feat: use prettier

This commit is contained in:
Daniel Bauer
2024-12-12 19:14:52 +01:00
parent 8eb4da6381
commit 8beb4ac67d
23 changed files with 6411 additions and 5706 deletions

View File

@@ -1,10 +1,17 @@
# top-most EditorConfig file root = true
root = true
# Unix-style newlines with a newline ending every file
[*] [*]
charset = utf-8 end_of_line = lf
end_of_line = lf insert_final_newline = true
insert_final_newline = true trim_trailing_whitespace = true
indent_style = tab
indent_size = 4 [*.json]
tab_width = 4 charset = utf-8
indent_style = space
indent_size = 2
[*.{ts,js}]
charset = utf-8
indent_style = space
indent_size = 4

1
.husky/pre-commit Normal file
View File

@@ -0,0 +1 @@
npx lint-staged

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
# Ignore artifacts:
build
coverage
test-vault

1
.prettierrc Normal file
View File

@@ -0,0 +1 @@
{}

8
.prettierrc.json Normal file
View File

@@ -0,0 +1,8 @@
{
"tabWidth": 4,
"semi": true,
"embeddedLanguageFormatting": "off",
"parser": "typescript",
"printWidth": 120,
"arrowParens": "avoid"
}

View File

@@ -6,6 +6,7 @@ This project uses TypeScript to provide type checking and documentation.
The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does. The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does.
This sample plugin demonstrates some of the basic functionality the plugin API can do. This sample plugin demonstrates some of the basic functionality the plugin API can do.
- Adds a ribbon icon, which shows a Notice when clicked. - Adds a ribbon icon, which shows a Notice when clicked.
- Adds a command "Open Sample Modal" which opens a Modal. - Adds a command "Open Sample Modal" which opens a Modal.
- Adds a plugin setting tab to the settings page. - Adds a plugin setting tab to the settings page.
@@ -56,7 +57,8 @@ Quick starting guide for new plugin devs:
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`. - Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`.
## Improve code quality with eslint (optional) ## Improve code quality with eslint (optional)
- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code.
- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code.
- To use eslint with this project, make sure to install eslint from terminal: - To use eslint with this project, make sure to install eslint from terminal:
- `npm install -g eslint` - `npm install -g eslint`
- To use eslint to analyze this project use this command: - To use eslint to analyze this project use this command:
@@ -73,7 +75,7 @@ The simple way is to set the `fundingUrl` field to your link in your `manifest.j
```json ```json
{ {
"fundingUrl": "https://buymeacoffee.com" "fundingUrl": "https://buymeacoffee.com"
} }
``` ```
@@ -81,11 +83,11 @@ If you have multiple URLs, you can also do:
```json ```json
{ {
"fundingUrl": { "fundingUrl": {
"Buy Me a Coffee": "https://buymeacoffee.com", "Buy Me a Coffee": "https://buymeacoffee.com",
"GitHub Sponsor": "https://github.com/sponsors", "GitHub Sponsor": "https://github.com/sponsors",
"Patreon": "https://www.patreon.com/" "Patreon": "https://www.patreon.com/"
} }
} }
``` ```

View File

@@ -2,48 +2,48 @@ import esbuild from "esbuild";
import process from "process"; import process from "process";
import builtins from "builtin-modules"; import builtins from "builtin-modules";
const banner = const banner = `/*
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin if you want to view the source, please visit the github repository of this plugin
*/ */
`; `;
const prod = (process.argv[2] === "production"); const prod = process.argv[2] === "production";
const context = await esbuild.context({ const context = await esbuild.context({
banner: { banner: {
js: banner, js: banner,
}, },
entryPoints: ["main.ts"], entryPoints: ["main.ts"],
bundle: true, bundle: true,
external: [ external: [
"obsidian", "obsidian",
"electron", "electron",
"@codemirror/autocomplete", "@codemirror/autocomplete",
"@codemirror/collab", "@codemirror/collab",
"@codemirror/commands", "@codemirror/commands",
"@codemirror/language", "@codemirror/language",
"@codemirror/lint", "@codemirror/lint",
"@codemirror/search", "@codemirror/search",
"@codemirror/state", "@codemirror/state",
"@codemirror/view", "@codemirror/view",
"@lezer/common", "@lezer/common",
"@lezer/highlight", "@lezer/highlight",
"@lezer/lr", "@lezer/lr",
...builtins], ...builtins,
format: "cjs", ],
target: "es2018", format: "cjs",
logLevel: "info", target: "es2018",
sourcemap: prod ? false : "inline", logLevel: "info",
treeShaking: true, sourcemap: prod ? false : "inline",
outfile: "main.js", treeShaking: true,
minify: prod, outfile: "main.js",
minify: prod,
}); });
if (prod) { if (prod) {
await context.rebuild(); await context.rebuild();
process.exit(0); process.exit(0);
} else { } else {
await context.watch(); await context.watch();
} }

View File

@@ -1,11 +1,11 @@
{ {
"id": "sample-plugin", "id": "sample-plugin",
"name": "Sample Plugin", "name": "Sample Plugin",
"version": "1.0.0", "version": "1.0.0",
"minAppVersion": "0.15.0", "minAppVersion": "0.15.0",
"description": "Demonstrates some of the capabilities of the Obsidian API.", "description": "Demonstrates some of the capabilities of the Obsidian API.",
"author": "Obsidian", "author": "Obsidian",
"authorUrl": "https://obsidian.md", "authorUrl": "https://obsidian.md",
"fundingUrl": "https://obsidian.md/pricing", "fundingUrl": "https://obsidian.md/pricing",
"isDesktopOnly": false "isDesktopOnly": false
} }

11130
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,47 @@
{ {
"name": "obsidian-sample-plugin", "name": "obsidian-sample-plugin",
"version": "1.0.0", "version": "1.0.0",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)", "description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"dev": "npx rollup --config rollup.config.js -w", "dev": "npx rollup --config rollup.config.js -w",
"build": "npx rollup --config rollup.config.js --environment BUILD:production", "build": "npx rollup --config rollup.config.js --environment BUILD:production",
"test": "npx jest", "test": "npx jest",
"version": "node version-bump.mjs && git add manifest.json versions.json" "version": "node version-bump.mjs && git add manifest.json versions.json",
}, "prepare": "husky"
"keywords": [], },
"author": "", "keywords": [],
"license": "MIT", "author": "",
"devDependencies": { "license": "MIT",
"@types/jest": "^29.5.14", "devDependencies": {
"@types/node": "^16.11.6", "@types/jest": "^29.5.14",
"@typescript-eslint/eslint-plugin": "5.29.0", "@types/node": "^16.11.6",
"@typescript-eslint/parser": "5.29.0", "@typescript-eslint/eslint-plugin": "5.29.0",
"babel-jest": "^29.7.0", "@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0", "babel-jest": "^29.7.0",
"esbuild": "0.17.3", "builtin-modules": "3.3.0",
"obsidian": "latest", "esbuild": "0.17.3",
"ts-jest": "^29.2.5", "husky": "^9.1.7",
"tslib": "2.4.0", "lint-staged": "^15.2.11",
"typescript": "4.7.4" "obsidian": "latest",
}, "prettier": "3.4.2",
"dependencies": { "ts-jest": "^29.2.5",
"@rollup/plugin-commonjs": "^28.0.1", "tslib": "2.4.0",
"@rollup/plugin-node-resolve": "^15.3.0", "typescript": "4.7.4"
"rollup": "^2.79.2", },
"rollup-plugin-copy": "^3.5.0", "dependencies": {
"rollup-plugin-typescript2": "^0.36.0" "@rollup/plugin-commonjs": "^28.0.1",
}, "@rollup/plugin-node-resolve": "^15.3.0",
"jest": { "rollup": "^2.79.2",
"transform": { "rollup-plugin-copy": "^3.5.0",
"^.+\\.(ts|tsx|js|jsx)$": "ts-jest" "rollup-plugin-typescript2": "^0.36.0"
} },
} "jest": {
"transform": {
"^.+\\.(ts|tsx|js|jsx)$": "ts-jest"
}
},
"lint-staged": {
"**/*": "prettier --write --ignore-unknown"
}
} }

View File

@@ -5,15 +5,13 @@ import typescript2 from "rollup-plugin-typescript2";
const BASE_CONFIG = { const BASE_CONFIG = {
input: "src/main.ts", input: "src/main.ts",
external: ["obsidian"] external: ["obsidian"],
}; };
const getRollupPlugins = (tsconfig, ...plugins) => const getRollupPlugins = (tsconfig, ...plugins) =>
[ [typescript2(tsconfig), nodeResolve({ browser: true }), commonjs()].concat(
typescript2(tsconfig), plugins,
nodeResolve({ browser: true }), );
commonjs(),
].concat(plugins);
const DEV_PLUGIN_CONFIG = { const DEV_PLUGIN_CONFIG = {
...BASE_CONFIG, ...BASE_CONFIG,
@@ -28,10 +26,16 @@ const DEV_PLUGIN_CONFIG = {
undefined, undefined,
copy({ copy({
targets: [ targets: [
{ src: "manifest.json", dest: "test-vault/.obsidian/plugins/dataview-suggester/" }, {
{ src: "styles.css", dest: "test-vault/.obsidian/plugins/dataview-suggester/" }, src: "manifest.json",
dest: "test-vault/.obsidian/plugins/dataview-suggester/",
},
{
src: "styles.css",
dest: "test-vault/.obsidian/plugins/dataview-suggester/",
},
], ],
}) }),
), ),
}; };

View File

@@ -1,72 +1,84 @@
import { App, Editor, Plugin, EditorPosition, EditorSuggest, EditorSuggestContext, EditorSuggestTriggerInfo, TFile } from 'obsidian'; import {
import { getTriggerText } from './trigger'; App,
Editor,
Plugin,
EditorPosition,
EditorSuggest,
EditorSuggestContext,
EditorSuggestTriggerInfo,
TFile,
} from "obsidian";
import { getTriggerText } from "./trigger";
export class DataviewSuggester extends EditorSuggest<String> { export class DataviewSuggester extends EditorSuggest<String> {
constructor(plugin: Plugin) {
super(plugin.app);
}
constructor(plugin: Plugin) { onTrigger(
super(plugin.app) cursor: EditorPosition,
} editor: Editor,
file: TFile,
): EditorSuggestTriggerInfo | null {
const line = editor.getLine(cursor.line);
onTrigger( let trigger = getTriggerText(line, cursor.ch);
cursor: EditorPosition, if (trigger !== null) {
editor: Editor, return {
file: TFile, query: trigger[0],
): EditorSuggestTriggerInfo | null { start: { line: cursor.line, ch: trigger[1] },
const line = editor.getLine(cursor.line); end: { line: cursor.line, ch: trigger[2] },
};
let trigger = getTriggerText(line, cursor.ch) }
if (trigger !== null) { return null;
return { }
query: trigger[0],
start: { line: cursor.line, ch: trigger[1] },
end: { line: cursor.line, ch: trigger[2] },
}
}
return null
}
getSuggestions(context: EditorSuggestContext): string[] | Promise<string[]> { getSuggestions(
const suggestions: string[] = [] context: EditorSuggestContext,
// TODO use official dv api? ): string[] | Promise<string[]> {
// @ts-ignore const suggestions: string[] = [];
for (const page of this.app.plugins.plugins.dataview.index.pages) { // TODO use official dv api?
const fields = page[1].fields // @ts-ignore
for (let [key, val] of fields) { for (const page of this.app.plugins.plugins.dataview.index.pages) {
let arrayVal const fields = page[1].fields;
if (!Array.isArray(val)) { for (let [key, val] of fields) {
arrayVal = [val] let arrayVal;
} else { if (!Array.isArray(val)) {
arrayVal = val arrayVal = [val];
} } else {
arrayVal = val;
}
for (const value of arrayVal) { for (const value of arrayVal) {
// TODO whitespace in match // TODO whitespace in match
// Fuzzy matching? // Fuzzy matching?
const suggestion = key + ":: " + value const suggestion = key + ":: " + value;
if (suggestion.includes(context.query) && !suggestions.includes(suggestion)) { if (
suggestions.push(key+ ":: " + value) suggestion.includes(context.query) &&
} !suggestions.includes(suggestion)
} ) {
} suggestions.push(key + ":: " + value);
}
}
}
}
console.log(suggestions);
return suggestions;
}
} renderSuggestion(value: string, el: HTMLElement): void {
console.log(suggestions) el.setText(value);
return suggestions }
}
renderSuggestion(value: string, el: HTMLElement): void { selectSuggestion(value: string, evt: MouseEvent | KeyboardEvent): void {
el.setText(value) console.log("selected", value);
} const { editor, start, end } = this.context!;
editor.replaceRange(value, start, end);
selectSuggestion(value: string, evt: MouseEvent | KeyboardEvent): void { const newCursorPos = {
console.log("selected", value) line: end.line + 1,
const { editor, start, end } = this.context!; ch: 0,
editor.replaceRange(value, start, end); };
editor.setCursor(newCursorPos);
const newCursorPos = { }
line: end.line + 1,
ch: 0,
};
editor.setCursor(newCursorPos);
}
} }

View File

@@ -1,35 +1,37 @@
import { App, Plugin, PluginSettingTab, Setting } from 'obsidian'; import { App, Plugin, PluginSettingTab, Setting } from "obsidian";
import { DataviewSuggester } from './DataviewSuggester'; import { DataviewSuggester } from "./DataviewSuggester";
interface DataviewAutocompleteSettings { interface DataviewAutocompleteSettings {
// mySetting: string; // mySetting: string;
} }
const DEFAULT_SETTINGS: DataviewAutocompleteSettings = { const DEFAULT_SETTINGS: DataviewAutocompleteSettings = {
// mySetting: 'default' // mySetting: 'default'
} };
export default class DataviewAutocompletePlugin extends Plugin { export default class DataviewAutocompletePlugin extends Plugin {
settings: DataviewAutocompleteSettings; settings: DataviewAutocompleteSettings;
suggester: DataviewSuggester suggester: DataviewSuggester;
async onload() { async onload() {
await this.loadSettings(); await this.loadSettings();
// register suggester
this.suggester = new DataviewSuggester(this);
this.registerEditorSuggest(this.suggester);
}
onunload() { // register suggester
this.suggester = new DataviewSuggester(this);
this.registerEditorSuggest(this.suggester);
}
} onunload() {}
async loadSettings() { async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); this.settings = Object.assign(
} {},
DEFAULT_SETTINGS,
await this.loadData(),
);
}
async saveSettings() { async saveSettings() {
await this.saveData(this.settings); await this.saveData(this.settings);
} }
} }

View File

@@ -1,139 +1,262 @@
import { getTriggerText } from "./trigger" import { getTriggerText } from "./trigger";
describe("trigger", () => { describe("trigger", () => {
describe("empty", () => { describe("empty", () => {
test("empty parantheses", () => { test("empty parantheses", () => {
expect(getTriggerText("() testing", 1)).toEqual(["", 1, 1]) expect(getTriggerText("() testing", 1)).toEqual(["", 1, 1]);
}) });
test("empty square brackets", () => { test("empty square brackets", () => {
expect(getTriggerText("[] testing", 1)).toEqual(["", 1, 1]) expect(getTriggerText("[] testing", 1)).toEqual(["", 1, 1]);
}) });
test("empty square brackets in text", () => { test("empty square brackets in text", () => {
expect(getTriggerText("test [] testing", 6)).toEqual(["", 6, 6]) expect(getTriggerText("test [] testing", 6)).toEqual(["", 6, 6]);
}) });
// test("not match empty double square brackets", () => { // test("not match empty double square brackets", () => {
// expect(getTriggerText("[[]] testing", 2)).toEqual(null) // expect(getTriggerText("[[]] testing", 2)).toEqual(null)
// }) // })
test("multiple empty brackets in text", () => { test("multiple empty brackets in text", () => {
expect(getTriggerText("test [] and [] testing", 13)).toEqual(["", 13, 13]) expect(getTriggerText("test [] and [] testing", 13)).toEqual([
}) "",
}) 13,
13,
]);
});
});
describe("text in parantheses", () => { describe("text in parantheses", () => {
test("text behind", () => { test("text behind", () => {
expect(getTriggerText("(capture) testing", 1)).toEqual(["capture", 1, 8]) expect(getTriggerText("(capture) testing", 1)).toEqual([
}) "capture",
1,
8,
]);
});
test("text before", () => { test("text before", () => {
expect(getTriggerText("testing (capture)", 12)).toEqual(["capture", 9, 16]) expect(getTriggerText("testing (capture)", 12)).toEqual([
}) "capture",
9,
16,
]);
});
test("text behind no whitespace", () => { test("text behind no whitespace", () => {
expect(getTriggerText("(capture)testing", 1)).toEqual(["capture", 1, 8]) expect(getTriggerText("(capture)testing", 1)).toEqual([
}) "capture",
1,
8,
]);
});
test("text before no whitespace", () => { test("text before no whitespace", () => {
expect(getTriggerText("test(capture)", 5)).toEqual(["capture", 5, 12]) expect(getTriggerText("test(capture)", 5)).toEqual([
}) "capture",
5,
12,
]);
});
test("text before and behind", () => { test("text before and behind", () => {
expect(getTriggerText("test (capture) testing", 6)).toEqual(["capture", 6, 13]) expect(getTriggerText("test (capture) testing", 6)).toEqual([
}) "capture",
6,
13,
]);
});
test("text and colon", () => { test("text and colon", () => {
expect(getTriggerText("(capture:) testing", 1)).toEqual(["capture:", 1, 9]) expect(getTriggerText("(capture:) testing", 1)).toEqual([
}) "capture:",
1,
9,
]);
});
test("text and double colon", () => { test("text and double colon", () => {
expect(getTriggerText("(capture::) testing", 1)).toEqual(["capture::", 1, 10]) expect(getTriggerText("(capture::) testing", 1)).toEqual([
}) "capture::",
1,
10,
]);
});
test("metadata field", () => { test("metadata field", () => {
expect(getTriggerText("(capture::Bob) testing", 1)).toEqual(["capture::Bob", 1, 13]) expect(getTriggerText("(capture::Bob) testing", 1)).toEqual([
}) "capture::Bob",
1,
13,
]);
});
test("metadata field with whitespace", () => { test("metadata field with whitespace", () => {
expect(getTriggerText("(capture:: Bob) testing", 1)).toEqual(["capture:: Bob", 1, 14]) expect(getTriggerText("(capture:: Bob) testing", 1)).toEqual([
}) "capture:: Bob",
1,
14,
]);
});
test("metadata field with more white space", () => { test("metadata field with more white space", () => {
expect(getTriggerText("(capture:: Bob) testing", 1)).toEqual(["capture:: Bob", 1, 16]) expect(getTriggerText("(capture:: Bob) testing", 1)).toEqual([
}) "capture:: Bob",
1,
16,
]);
});
test("text with markdown link", () => { test("text with markdown link", () => {
expect(getTriggerText("([[test]]) testing", 1)).toEqual(["[[test]]", 1, 9]) expect(getTriggerText("([[test]]) testing", 1)).toEqual([
}) "[[test]]",
1,
9,
]);
});
test("text with aliased markdown link", () => { test("text with aliased markdown link", () => {
expect(getTriggerText("([[test|display]]) testing", 1)).toEqual(["[[test|display]]", 1, 17]) expect(getTriggerText("([[test|display]]) testing", 1)).toEqual([
}) "[[test|display]]",
1,
17,
]);
});
test("text with wiki link", () => { test("text with wiki link", () => {
expect(getTriggerText("([[test](https://example.com)]) testing", 1)).toEqual(["[[test](https://example.com)]", 1, 30]) expect(
}) getTriggerText("([[test](https://example.com)]) testing", 1),
).toEqual(["[[test](https://example.com)]", 1, 30]);
});
test("text with wiki link local", () => { test("text with wiki link local", () => {
expect(getTriggerText("([[test](test)]) testing", 1)).toEqual(["[[test](test)]", 1, 15]) expect(getTriggerText("([[test](test)]) testing", 1)).toEqual([
}) "[[test](test)]",
}) 1,
15,
]);
});
});
describe("text in square brackets", () => { describe("text in square brackets", () => {
test("text behind", () => { test("text behind", () => {
expect(getTriggerText("[capture] testing", 1)).toEqual(["capture", 1, 8]) expect(getTriggerText("[capture] testing", 1)).toEqual([
}) "capture",
1,
8,
]);
});
test("text before", () => { test("text before", () => {
expect(getTriggerText("testing [capture]", 12)).toEqual(["capture", 9, 16]) expect(getTriggerText("testing [capture]", 12)).toEqual([
}) "capture",
9,
16,
]);
});
test("text behind no whitespace", () => { test("text behind no whitespace", () => {
expect(getTriggerText("[capture]testing", 1)).toEqual(["capture", 1, 8]) expect(getTriggerText("[capture]testing", 1)).toEqual([
}) "capture",
1,
8,
]);
});
test("text before no whitespace", () => { test("text before no whitespace", () => {
expect(getTriggerText("test[capture]", 5)).toEqual(["capture", 5, 12]) expect(getTriggerText("test[capture]", 5)).toEqual([
}) "capture",
5,
12,
]);
});
test("text before and behind", () => { test("text before and behind", () => {
expect(getTriggerText("test [capture] testing", 6)).toEqual(["capture", 6, 13]) expect(getTriggerText("test [capture] testing", 6)).toEqual([
}) "capture",
6,
13,
]);
});
test("text and colon", () => { test("text and colon", () => {
expect(getTriggerText("[capture:] testing", 1)).toEqual(["capture:", 1, 9]) expect(getTriggerText("[capture:] testing", 1)).toEqual([
}) "capture:",
1,
9,
]);
});
test("text and double colon", () => { test("text and double colon", () => {
expect(getTriggerText("[capture::] testing", 1)).toEqual(["capture::", 1, 10]) expect(getTriggerText("[capture::] testing", 1)).toEqual([
}) "capture::",
1,
10,
]);
});
test("metadata field", () => { test("metadata field", () => {
expect(getTriggerText("[capture::Bob] testing", 1)).toEqual(["capture::Bob", 1, 13]) expect(getTriggerText("[capture::Bob] testing", 1)).toEqual([
}) "capture::Bob",
1,
13,
]);
});
test("metadata field with whitespace", () => { test("metadata field with whitespace", () => {
expect(getTriggerText("[capture:: Bob] testing", 1)).toEqual(["capture:: Bob", 1, 14]) expect(getTriggerText("[capture:: Bob] testing", 1)).toEqual([
}) "capture:: Bob",
1,
14,
]);
});
test("metadata field with more white space", () => { test("metadata field with more white space", () => {
expect(getTriggerText("[capture:: Bob] testing", 1)).toEqual(["capture:: Bob", 1, 16]) expect(getTriggerText("[capture:: Bob] testing", 1)).toEqual([
}) "capture:: Bob",
1,
16,
]);
});
test("text with markdown link", () => { test("text with markdown link", () => {
expect(getTriggerText("[[[test]]] testing", 1)).toEqual(["[[test]]", 1, 9]) expect(getTriggerText("[[[test]]] testing", 1)).toEqual([
}) "[[test]]",
1,
9,
]);
});
test("text with aliased markdown link", () => { test("text with aliased markdown link", () => {
expect(getTriggerText("[[[test|display]]] testing", 1)).toEqual(["[[test|display]]", 1, 17]) expect(getTriggerText("[[[test|display]]] testing", 1)).toEqual([
}) "[[test|display]]",
1,
17,
]);
});
test("text with wiki link", () => { test("text with wiki link", () => {
expect(getTriggerText("[[[test](https://example.com)]] testing", 1)).toEqual(["[[test](https://example.com)]", 1, 30]) expect(
}) getTriggerText("[[[test](https://example.com)]] testing", 1),
).toEqual(["[[test](https://example.com)]", 1, 30]);
});
test("text with wiki link local", () => { test("text with wiki link local", () => {
expect(getTriggerText("[[[test](test)]] testing", 1)).toEqual(["[[test](test)]", 1, 15]) expect(getTriggerText("[[[test](test)]] testing", 1)).toEqual([
}) "[[test](test)]",
}) 1,
15,
]);
});
});
describe("cursor position", () => { describe("cursor position", () => {
test("cursor in first field", () => { test("cursor in first field", () => {
expect(getTriggerText("test (test) string (test2) testing", 9)).toEqual(["test", 6, 10]) expect(
}) getTriggerText("test (test) string (test2) testing", 9),
).toEqual(["test", 6, 10]);
});
test("cursor in second field", () => { test("cursor in second field", () => {
expect(getTriggerText("test (test) string (test2) testing", 21)).toEqual(["test2", 20, 25]) expect(
}) getTriggerText("test (test) string (test2) testing", 21),
).toEqual(["test2", 20, 25]);
});
test("ignore cursor out of field", () => { test("ignore cursor out of field", () => {
expect(getTriggerText("test (test) string (test2) testing", 2)).toEqual(null) expect(
}) getTriggerText("test (test) string (test2) testing", 2),
}) ).toEqual(null);
});
});
describe("ignore plain markdown links", () => { describe("ignore plain markdown links", () => {
test("cursor in link text", () => { test("cursor in link text", () => {
expect(getTriggerText("test [test](https://example.com)", 8)).toEqual(null) expect(
}) getTriggerText("test [test](https://example.com)", 8),
).toEqual(null);
});
test("cursor in link url", () => { test("cursor in link url", () => {
expect(getTriggerText("test [test](https://example.com)", 15)).toEqual(null) expect(
}) getTriggerText("test [test](https://example.com)", 15),
).toEqual(null);
});
test("cursor in empty link text", () => { test("cursor in empty link text", () => {
expect(getTriggerText("test [](https://example.com)", 6)).toEqual(null) expect(getTriggerText("test [](https://example.com)", 6)).toEqual(
}) null,
);
});
test("cursor in empty link url", () => { test("cursor in empty link url", () => {
expect(getTriggerText("test [test]()", 12)).toEqual(null) expect(getTriggerText("test [test]()", 12)).toEqual(null);
}) });
}) });
});
})

View File

@@ -1,20 +1,20 @@
/** /**
* Matches single square bracket pair [] or parahtheses (), but not a double square bracket pair [[]] * Matches single square bracket pair [] or parahtheses (), but not a double square bracket pair [[]]
* (?<!\[) # Negative lookbehind to make sure there is no second [ * (?<!\[) # Negative lookbehind to make sure there is no second [
* \[] # Matches [] * \[] # Matches []
* (?![\]\(]) # Negative lookahead to make sure there is no second closing ] from wiki link or opening ( from a markdown link * (?![\]\(]) # Negative lookahead to make sure there is no second closing ] from wiki link or opening ( from a markdown link
* | # OR * | # OR
* (?<!\]) # Negative lookbehind to make sure there is no closing ] from a markdown link * (?<!\]) # Negative lookbehind to make sure there is no closing ] from a markdown link
* \(\) # Matches () * \(\) # Matches ()
*/ */
const emptyRegex = /(?<!\[)\[](?![\]\(])|(?<!\])\(\)/g const emptyRegex = /(?<!\[)\[](?![\]\(])|(?<!\])\(\)/g;
/** /**
* Maches single square brackets or parantheses with some text in them * Maches single square brackets or parantheses with some text in them
* and captures the text * and captures the text
* If the brackets start a wiki link or markdown link, they are ignored * If the brackets start a wiki link or markdown link, they are ignored
* but links inside another layer of brackets are returned: * but links inside another layer of brackets are returned:
* *
* ( * (
* (?<=^\(|\s\() # Pattern for parantheses * (?<=^\(|\s\() # Pattern for parantheses
* .+?:{0,2}.*? * .+?:{0,2}.*?
@@ -25,7 +25,8 @@ const emptyRegex = /(?<!\[)\[](?![\]\(])|(?<!\])\(\)/g
* (?=\]$|\]\s) * (?=\]$|\]\s)
* ) * )
*/ */
const filledRegex = /((?<=^\(|\s\().+?:{0,2}.*?(?=\)$|\)\s)|(?<=^\[|\s\[).+?:{0,2}.*?(?=\]$|\]\s))/g const filledRegex =
/((?<=^\(|\s\().+?:{0,2}.*?(?=\)$|\)\s)|(?<=^\[|\s\[).+?:{0,2}.*?(?=\]$|\]\s))/g;
/** /**
* Finds the dataview metadata field the user is currently inside. * Finds the dataview metadata field the user is currently inside.
@@ -33,38 +34,44 @@ const filledRegex = /((?<=^\(|\s\().+?:{0,2}.*?(?=\)$|\)\s)|(?<=^\[|\s\[).+?:{0,
* and start and end are the cursor positions of the start and end of the field. * and start and end are the cursor positions of the start and end of the field.
* If the user is not inside a field, it returns null. * If the user is not inside a field, it returns null.
*/ */
export function getTriggerText(line: string, cursorPos: number): [string, number, number] | null { export function getTriggerText(
line: string,
cursorPos: number,
): [string, number, number] | null {
// Check for empty [] or (). // Check for empty [] or ().
// If the user starts typing with no text in the field, this is the fastest way to find it. // If the user starts typing with no text in the field, this is the fastest way to find it.
let match = getEmptyTrigger(line, cursorPos) let match = getEmptyTrigger(line, cursorPos);
if (match) { if (match) {
return match return match;
} }
// If the user types inside an existing field, this is the function to find it. // If the user types inside an existing field, this is the function to find it.
match = getTriggerTextFromRegex(line, cursorPos, filledRegex) match = getTriggerTextFromRegex(line, cursorPos, filledRegex);
if (match !== null) { if (match !== null) {
return match return match;
} }
return null return null;
} }
/** /**
* Matches an empty [] or empty (), but not [[]] * Matches an empty [] or empty (), but not [[]]
* Has its own function since there is no capture group resulting in a different index calculation * Has its own function since there is no capture group resulting in a different index calculation
*/ */
function getEmptyTrigger(line: string, cursorPos: number): [string, number, number] | null { function getEmptyTrigger(
let matches = Array.from(line.matchAll(emptyRegex)) line: string,
cursorPos: number,
): [string, number, number] | null {
let matches = Array.from(line.matchAll(emptyRegex));
for (const match of matches) { for (const match of matches) {
if (match.index === undefined) { if (match.index === undefined) {
continue continue;
} }
if (cursorPos === match.index+1) { if (cursorPos === match.index + 1) {
return ["", match.index+1, match.index+1] return ["", match.index + 1, match.index + 1];
} }
} }
return null return null;
} }
/** /**
@@ -74,20 +81,24 @@ function getEmptyTrigger(line: string, cursorPos: number): [string, number, numb
* start is the position of the start of the match, and end is the position of the end of the match. * start is the position of the start of the match, and end is the position of the end of the match.
* Otherwise, returns null. * Otherwise, returns null.
*/ */
function getTriggerTextFromRegex(line: string, cursorPos: number, regex: RegExp): [string, number, number] | null { function getTriggerTextFromRegex(
let matches = Array.from(line.matchAll(regex)) line: string,
cursorPos: number,
regex: RegExp,
): [string, number, number] | null {
let matches = Array.from(line.matchAll(regex));
for (const match of matches) { for (const match of matches) {
if (match.index === undefined) { if (match.index === undefined) {
continue continue;
} }
const matchStart = match!.index const matchStart = match!.index;
const matchEnd = matchStart + match[1].length const matchEnd = matchStart + match[1].length;
const cursorInMatch = cursorPos >= matchStart && cursorPos <= matchEnd const cursorInMatch = cursorPos >= matchStart && cursorPos <= matchEnd;
if (cursorInMatch) { if (cursorInMatch) {
return [match[1], matchStart, matchEnd] return [match[1], matchStart, matchEnd];
} }
} }
return null return null;
} }

View File

@@ -1,4 +0,0 @@
[
"dataview",
"sample-plugin"
]

View File

@@ -1,11 +0,0 @@
{
"id": "sample-plugin",
"name": "Sample Plugin",
"version": "1.0.0",
"minAppVersion": "0.15.0",
"description": "Demonstrates some of the capabilities of the Obsidian API.",
"author": "Obsidian",
"authorUrl": "https://obsidian.md",
"fundingUrl": "https://obsidian.md/pricing",
"isDesktopOnly": false
}

View File

@@ -1,8 +0,0 @@
/*
This CSS file will be included with your plugin, and
available in the app when your plugin is enabled.
If your plugin does not need CSS, delete this file.
*/

View File

@@ -1,11 +0,0 @@
{
"id": "dataview",
"name": "Dataview",
"version": "0.5.67",
"minAppVersion": "0.13.11",
"description": "Complex data views for the data-obsessed.",
"author": "Michael Brenan <blacksmithgu@gmail.com>",
"authorUrl": "https://github.com/blacksmithgu",
"helpUrl": "https://blacksmithgu.github.io/obsidian-dataview/",
"isDesktopOnly": false
}

View File

@@ -1,146 +0,0 @@
/** Live Preview padding fixes, specifically for DataviewJS custom HTML elements. */
.is-live-preview .block-language-dataviewjs > p, .is-live-preview .block-language-dataviewjs > span {
line-height: 1.0;
}
.block-language-dataview {
overflow-y: auto;
}
/*****************/
/** Table Views **/
/*****************/
/* List View Default Styling; rendered internally as a table. */
.table-view-table {
width: 100%;
}
.table-view-table > thead > tr, .table-view-table > tbody > tr {
margin-top: 1em;
margin-bottom: 1em;
text-align: left;
}
.table-view-table > tbody > tr:hover {
background-color: var(--table-row-background-hover);
}
.table-view-table > thead > tr > th {
font-weight: 700;
font-size: larger;
border-top: none;
border-left: none;
border-right: none;
border-bottom: solid;
max-width: 100%;
}
.table-view-table > tbody > tr > td {
text-align: left;
border: none;
font-weight: 400;
max-width: 100%;
}
.table-view-table ul, .table-view-table ol {
margin-block-start: 0.2em !important;
margin-block-end: 0.2em !important;
}
/** Rendered value styling for any view. */
.dataview-result-list-root-ul {
padding: 0em !important;
margin: 0em !important;
}
.dataview-result-list-ul {
margin-block-start: 0.2em !important;
margin-block-end: 0.2em !important;
}
/** Generic grouping styling. */
.dataview.result-group {
padding-left: 8px;
}
/*******************/
/** Inline Fields **/
/*******************/
.dataview.inline-field-key {
padding-left: 8px;
padding-right: 8px;
font-family: var(--font-monospace);
background-color: var(--background-primary-alt);
color: var(--text-nav-selected);
}
.dataview.inline-field-value {
padding-left: 8px;
padding-right: 8px;
font-family: var(--font-monospace);
background-color: var(--background-secondary-alt);
color: var(--text-nav-selected);
}
.dataview.inline-field-standalone-value {
padding-left: 8px;
padding-right: 8px;
font-family: var(--font-monospace);
background-color: var(--background-secondary-alt);
color: var(--text-nav-selected);
}
/***************/
/** Task View **/
/***************/
.dataview.task-list-item, .dataview.task-list-basic-item {
margin-top: 3px;
margin-bottom: 3px;
transition: 0.4s;
}
.dataview.task-list-item:hover, .dataview.task-list-basic-item:hover {
background-color: var(--text-selection);
box-shadow: -40px 0 0 var(--text-selection);
cursor: pointer;
}
/*****************/
/** Error Views **/
/*****************/
div.dataview-error-box {
width: 100%;
min-height: 150px;
display: flex;
align-items: center;
justify-content: center;
border: 4px dashed var(--background-secondary);
}
.dataview-error-message {
color: var(--text-muted);
text-align: center;
}
/*************************/
/** Additional Metadata **/
/*************************/
.dataview.small-text {
font-size: smaller;
color: var(--text-muted);
margin-left: 3px;
}
.dataview.small-text::before {
content: "(";
}
.dataview.small-text::after {
content: ")";
}

View File

@@ -1,11 +1,10 @@
(test:: test) (test:: test)
(test::test) (test::test)
(test:: test) (test:: test)
(test::) (test::)
[test:: test] [test:: test]
[test:: test] [test:: test]
[test:: [[Moods]]] [test:: [[Moods]]]
[test:: [[Moods|Wiki link display]]] [test:: [[Moods|Wiki link display]]]
[test:: [example link](https://example.com)] [test:: [example link](https://example.com)]
[test::Text with [[Moods|wiki link]] and more text] [test::Text with [[Moods|wiki link]] and more text]

View File

@@ -1,8 +1,8 @@
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"sourceMap": true, "sourceMap": true,
"inlineSourceMap": false, "inlineSourceMap": false,
"inlineSources": true, "inlineSources": true,
"module": "ESNext", "module": "ESNext",
"target": "ES6", "target": "ES6",
@@ -12,15 +12,7 @@
"importHelpers": true, "importHelpers": true,
"isolatedModules": true, "isolatedModules": true,
"strictNullChecks": true, "strictNullChecks": true,
"lib": [ "lib": ["DOM", "ES5", "ES6", "ES7", "es2020.string"]
"DOM",
"ES5",
"ES6",
"ES7",
"es2020.string"
]
}, },
"include": [ "include": ["**/*.ts"]
"**/*.ts"
]
} }

View File

@@ -1,3 +1,3 @@
{ {
"1.0.0": "0.15.0" "1.0.0": "0.15.0"
} }