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

View File

@@ -1,139 +1,262 @@
import { getTriggerText } from "./trigger"
import { getTriggerText } from "./trigger";
describe("trigger", () => {
describe("empty", () => {
test("empty parantheses", () => {
expect(getTriggerText("() testing", 1)).toEqual(["", 1, 1])
})
expect(getTriggerText("() testing", 1)).toEqual(["", 1, 1]);
});
test("empty square brackets", () => {
expect(getTriggerText("[] testing", 1)).toEqual(["", 1, 1])
})
expect(getTriggerText("[] testing", 1)).toEqual(["", 1, 1]);
});
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", () => {
// expect(getTriggerText("[[]] testing", 2)).toEqual(null)
// })
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", () => {
test("text behind", () => {
expect(getTriggerText("(capture) testing", 1)).toEqual(["capture", 1, 8])
})
expect(getTriggerText("(capture) testing", 1)).toEqual([
"capture",
1,
8,
]);
});
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", () => {
expect(getTriggerText("(capture)testing", 1)).toEqual(["capture", 1, 8])
})
expect(getTriggerText("(capture)testing", 1)).toEqual([
"capture",
1,
8,
]);
});
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", () => {
expect(getTriggerText("test (capture) testing", 6)).toEqual(["capture", 6, 13])
})
expect(getTriggerText("test (capture) testing", 6)).toEqual([
"capture",
6,
13,
]);
});
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", () => {
expect(getTriggerText("(capture::) testing", 1)).toEqual(["capture::", 1, 10])
})
expect(getTriggerText("(capture::) testing", 1)).toEqual([
"capture::",
1,
10,
]);
});
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", () => {
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", () => {
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", () => {
expect(getTriggerText("([[test]]) testing", 1)).toEqual(["[[test]]", 1, 9])
})
expect(getTriggerText("([[test]]) testing", 1)).toEqual([
"[[test]]",
1,
9,
]);
});
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", () => {
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", () => {
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", () => {
test("text behind", () => {
expect(getTriggerText("[capture] testing", 1)).toEqual(["capture", 1, 8])
})
expect(getTriggerText("[capture] testing", 1)).toEqual([
"capture",
1,
8,
]);
});
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", () => {
expect(getTriggerText("[capture]testing", 1)).toEqual(["capture", 1, 8])
})
expect(getTriggerText("[capture]testing", 1)).toEqual([
"capture",
1,
8,
]);
});
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", () => {
expect(getTriggerText("test [capture] testing", 6)).toEqual(["capture", 6, 13])
})
expect(getTriggerText("test [capture] testing", 6)).toEqual([
"capture",
6,
13,
]);
});
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", () => {
expect(getTriggerText("[capture::] testing", 1)).toEqual(["capture::", 1, 10])
})
expect(getTriggerText("[capture::] testing", 1)).toEqual([
"capture::",
1,
10,
]);
});
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", () => {
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", () => {
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", () => {
expect(getTriggerText("[[[test]]] testing", 1)).toEqual(["[[test]]", 1, 9])
})
expect(getTriggerText("[[[test]]] testing", 1)).toEqual([
"[[test]]",
1,
9,
]);
});
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", () => {
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", () => {
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", () => {
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", () => {
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", () => {
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", () => {
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", () => {
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", () => {
expect(getTriggerText("test [](https://example.com)", 6)).toEqual(null)
})
expect(getTriggerText("test [](https://example.com)", 6)).toEqual(
null,
);
});
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 [[]]
* (?<!\[) # 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
* | # OR
* (?<!\]) # Negative lookbehind to make sure there is no closing ] from a markdown link
* \(\) # Matches ()
*/
const emptyRegex = /(?<!\[)\[](?![\]\(])|(?<!\])\(\)/g
const emptyRegex = /(?<!\[)\[](?![\]\(])|(?<!\])\(\)/g;
/**
* Maches single square brackets or parantheses with some text in them
* and captures the text
* If the brackets start a wiki link or markdown link, they are ignored
* but links inside another layer of brackets are returned:
*
*
* (
* (?<=^\(|\s\() # Pattern for parantheses
* .+?:{0,2}.*?
@@ -25,7 +25,8 @@ const emptyRegex = /(?<!\[)\[](?![\]\(])|(?<!\])\(\)/g
* (?=\]$|\]\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.
@@ -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.
* 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 ().
// 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) {
return match
return match;
}
// 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) {
return match
return match;
}
return null
return null;
}
/**
* Matches an empty [] or empty (), but not [[]]
* 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 {
let matches = Array.from(line.matchAll(emptyRegex))
function getEmptyTrigger(
line: string,
cursorPos: number,
): [string, number, number] | null {
let matches = Array.from(line.matchAll(emptyRegex));
for (const match of matches) {
if (match.index === undefined) {
continue
continue;
}
if (cursorPos === match.index+1) {
return ["", match.index+1, match.index+1]
if (cursorPos === 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.
* Otherwise, returns null.
*/
function getTriggerTextFromRegex(line: string, cursorPos: number, regex: RegExp): [string, number, number] | null {
let matches = Array.from(line.matchAll(regex))
function getTriggerTextFromRegex(
line: string,
cursorPos: number,
regex: RegExp,
): [string, number, number] | null {
let matches = Array.from(line.matchAll(regex));
for (const match of matches) {
if (match.index === undefined) {
continue
continue;
}
const matchStart = match!.index
const matchEnd = matchStart + match[1].length
const cursorInMatch = cursorPos >= matchStart && cursorPos <= matchEnd
const matchStart = match!.index;
const matchEnd = matchStart + match[1].length;
const cursorInMatch = cursorPos >= matchStart && cursorPos <= matchEnd;
if (cursorInMatch) {
return [match[1], matchStart, matchEnd]
return [match[1], matchStart, matchEnd];
}
}
return null
return null;
}