diff --git a/main.js b/main.js index b5002fd1..ee3a2fd1 100644 --- a/main.js +++ b/main.js @@ -9664,6 +9664,7 @@ const DEFAULT_SETTINGS = { dotsColour: "#000000", dvWaitTime: 5000, enableAlphaSort: true, + enableRelationSuggestor: false, fieldSuggestor: true, filterImpliedSiblingsOfDifferentTypes: false, limitWriteBCCheckboxes: [], @@ -9688,6 +9689,7 @@ const DEFAULT_SETTINGS = { showNameOrType: true, showRelationType: true, regexNoteField: "", + relSuggestorTrigger: "\\", rlLeaf: true, showAllPathsIfNoneToIndexNote: false, showAllAliases: true, @@ -9834,6 +9836,10 @@ function strToRegex(input) { console.log(e); return null; } +} +// Source: https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript +function escapeRegex(string) { + return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); } /** @@ -31438,19 +31444,17 @@ class FieldSuggestor extends obsidian.EditorSuggest { } onTrigger(cursor, editor, _) { var _a; - if (this.plugin.settings.fieldSuggestor) { - const sub = editor.getLine(cursor.line).substring(0, cursor.ch); - const match = (_a = sub.match(/^BC-(.*)$/)) === null || _a === void 0 ? void 0 : _a[1]; - if (match !== undefined) { - return { - end: cursor, - start: { - ch: sub.lastIndexOf(match), - line: cursor.line, - }, - query: match, - }; - } + const sub = editor.getLine(cursor.line).substring(0, cursor.ch); + const match = (_a = sub.match(/^BC-(.*)$/)) === null || _a === void 0 ? void 0 : _a[1]; + if (match !== undefined) { + return { + end: cursor, + start: { + ch: sub.lastIndexOf(match), + line: cursor.line, + }, + query: match, + }; } return null; } @@ -33398,6 +33402,49 @@ async function refreshIndex(plugin) { new obsidian.Notice("Index refreshed"); } +class RelationSuggestor extends obsidian.EditorSuggest { + constructor(plugin) { + super(plugin.app); + this.getSuggestions = (context) => { + const { query } = context; + const { userHiers } = this.plugin.settings; + return getFields(userHiers).filter((sug) => sug.includes(query)); + }; + this.plugin = plugin; + } + onTrigger(cursor, editor, _) { + var _a; + const trig = this.plugin.settings.relSuggestorTrigger; + const sub = editor.getLine(cursor.line).substring(0, cursor.ch); + const regex = new RegExp(`.*?${escapeRegex(trig)}(.*)$`); + const match = (_a = regex.exec(sub)) === null || _a === void 0 ? void 0 : _a[1]; + if (match === undefined) + return null; + return { + start: { + ch: sub.lastIndexOf(trig), + line: cursor.line, + }, + end: cursor, + query: match, + }; + } + renderSuggestion(suggestion, el) { + el.createDiv({ + text: suggestion, + cls: "codeblock-suggestion", + }); + } + selectSuggestion(suggestion) { + const { context } = this; + if (context) { + const trig = this.plugin.settings.relSuggestorTrigger; + const { start, end, editor } = context; + editor.replaceRange(suggestion + ":", { ch: start.ch + 1 - trig.length, line: start.line }, end); + } + } +} + /* src\Components\KoFi.svelte generated by Svelte v3.35.0 */ function create_fragment$h(ctx) { @@ -37488,6 +37535,22 @@ class BCSettingTab extends obsidian.PluginSettingTab { settings.fieldSuggestor = value; await plugin.saveSettings(); })); + new obsidian.Setting(alternativeHierarchyDetails) + .setName("Enable Relation Suggestor") + .setDesc(fragWithHTML("Enable an editor suggestor which gets triggered by a custom string to show a list of relations from your hierarchies to insert.")) + .addToggle((toggle) => toggle + .setValue(settings.enableRelationSuggestor) + .onChange(async (value) => { + settings.enableRelationSuggestor = value; + await plugin.saveSettings(); + })); + new obsidian.Setting(alternativeHierarchyDetails) + .setName("Relation Suggestor Trigger") + .setDesc(fragWithHTML("The string used to trigger the relation suggestor. Default is \\.")) + .addText((text) => text.setValue(settings.relSuggestorTrigger).onChange(async (value) => { + settings.relSuggestorTrigger = value; + await plugin.saveSettings(); + })); addTagNoteSettings(plugin, alternativeHierarchyDetails); addRegexNoteSettings(plugin, alternativeHierarchyDetails); addNoSystemSettings(plugin, alternativeHierarchyDetails); @@ -61784,10 +61847,14 @@ class BCPlugin extends obsidian.Plugin { console.log("loading breadcrumbs plugin"); const { app } = this; await this.loadSettings(); - const { settings } = this; this.addSettingTab(new BCSettingTab(app, this)); this.db = new Debugger(this); - this.registerEditorSuggest(new FieldSuggestor(this)); + const { settings } = this; + const { fieldSuggestor, enableRelationSuggestor } = settings; + if (fieldSuggestor) + this.registerEditorSuggest(new FieldSuggestor(this)); + if (enableRelationSuggestor) + this.registerEditorSuggest(new RelationSuggestor(this)); const { openMatrixOnLoad, openStatsOnLoad, openDuckOnLoad, openDownOnLoad, showBCs, showBCsInEditLPMode, userHiers, } = settings; this.VIEWS = [ { diff --git a/src/FieldSuggestor.ts b/src/FieldSuggestor.ts index 51e6ac5f..e88f8345 100644 --- a/src/FieldSuggestor.ts +++ b/src/FieldSuggestor.ts @@ -22,20 +22,19 @@ export class FieldSuggestor extends EditorSuggest { editor: Editor, _: TFile ): EditorSuggestTriggerInfo | null { - if (this.plugin.settings.fieldSuggestor) { - const sub = editor.getLine(cursor.line).substring(0, cursor.ch); - const match = sub.match(/^BC-(.*)$/)?.[1]; - if (match !== undefined) { - return { - end: cursor, - start: { - ch: sub.lastIndexOf(match), - line: cursor.line, - }, - query: match, - }; - } + const sub = editor.getLine(cursor.line).substring(0, cursor.ch); + const match = sub.match(/^BC-(.*)$/)?.[1]; + if (match !== undefined) { + return { + end: cursor, + start: { + ch: sub.lastIndexOf(match), + line: cursor.line, + }, + query: match, + }; } + return null; } diff --git a/src/RelationSuggestor.ts b/src/RelationSuggestor.ts new file mode 100644 index 00000000..9a867e8b --- /dev/null +++ b/src/RelationSuggestor.ts @@ -0,0 +1,68 @@ +import { + Editor, + EditorPosition, + EditorSuggest, + EditorSuggestContext, + EditorSuggestTriggerInfo, + TFile, +} from "obsidian"; +import type BCPlugin from "./main"; +import { escapeRegex } from "./Utils/generalUtils"; +import { getFields } from "./Utils/HierUtils"; + +export class RelationSuggestor extends EditorSuggest { + plugin: BCPlugin; + + constructor(plugin: BCPlugin) { + super(plugin.app); + this.plugin = plugin; + } + + onTrigger( + cursor: EditorPosition, + editor: Editor, + _: TFile + ): EditorSuggestTriggerInfo | null { + const trig = this.plugin.settings.relSuggestorTrigger; + const sub = editor.getLine(cursor.line).substring(0, cursor.ch); + + const regex = new RegExp(`.*?${escapeRegex(trig)}(.*)$`); + const match = regex.exec(sub)?.[1]; + + if (match === undefined) return null; + return { + start: { + ch: sub.lastIndexOf(trig), + line: cursor.line, + }, + end: cursor, + query: match, + }; + } + + getSuggestions = (context: EditorSuggestContext) => { + const { query } = context; + const { userHiers } = this.plugin.settings; + return getFields(userHiers).filter((sug) => sug.includes(query)); + }; + + renderSuggestion(suggestion: string, el: HTMLElement): void { + el.createDiv({ + text: suggestion, + cls: "codeblock-suggestion", + }); + } + + selectSuggestion(suggestion: string): void { + const { context } = this; + if (context) { + const trig = this.plugin.settings.relSuggestorTrigger; + const { start, end, editor } = context; + editor.replaceRange( + suggestion + ":", + { ch: start.ch + 1 - trig.length, line: start.line }, + end + ); + } + } +} diff --git a/src/Settings/BreadcrumbsSettingTab.ts b/src/Settings/BreadcrumbsSettingTab.ts index eaa4f1a3..e54f837b 100644 --- a/src/Settings/BreadcrumbsSettingTab.ts +++ b/src/Settings/BreadcrumbsSettingTab.ts @@ -77,6 +77,34 @@ export class BCSettingTab extends PluginSettingTab { await plugin.saveSettings(); }) ); + new Setting(alternativeHierarchyDetails) + .setName("Enable Relation Suggestor") + .setDesc( + fragWithHTML( + "Enable an editor suggestor which gets triggered by a custom string to show a list of relations from your hierarchies to insert." + ) + ) + .addToggle((toggle) => + toggle + .setValue(settings.enableRelationSuggestor) + .onChange(async (value) => { + settings.enableRelationSuggestor = value; + await plugin.saveSettings(); + }) + ); + new Setting(alternativeHierarchyDetails) + .setName("Relation Suggestor Trigger") + .setDesc( + fragWithHTML( + "The string used to trigger the relation suggestor. Default is \\." + ) + ) + .addText((text) => + text.setValue(settings.relSuggestorTrigger).onChange(async (value) => { + settings.relSuggestorTrigger = value; + await plugin.saveSettings(); + }) + ); addTagNoteSettings(plugin, alternativeHierarchyDetails); addRegexNoteSettings(plugin, alternativeHierarchyDetails); diff --git a/src/Utils/generalUtils.ts b/src/Utils/generalUtils.ts index 892d8afc..5a53b1ba 100644 --- a/src/Utils/generalUtils.ts +++ b/src/Utils/generalUtils.ts @@ -1,5 +1,3 @@ -import type { App } from "obsidian"; -import { isInVault } from "obsidian-community-lib"; import { dropHeaderOrAlias, regNFlags, splitLinksRegex } from "../constants"; import type { BCSettings } from "../interfaces"; @@ -176,3 +174,8 @@ export function strToRegex(input: string) { return null; } } + +// Source: https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript +export function escapeRegex(string) { + return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); +} diff --git a/src/constants.ts b/src/constants.ts index b414f991..e5d185bc 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -252,6 +252,7 @@ export const DEFAULT_SETTINGS: BCSettings = { dotsColour: "#000000", dvWaitTime: 5000, enableAlphaSort: true, + enableRelationSuggestor: false, fieldSuggestor: true, filterImpliedSiblingsOfDifferentTypes: false, limitWriteBCCheckboxes: [], @@ -278,6 +279,7 @@ export const DEFAULT_SETTINGS: BCSettings = { showNameOrType: true, showRelationType: true, regexNoteField: "", + relSuggestorTrigger: "\\", rlLeaf: true, showAllPathsIfNoneToIndexNote: false, showAllAliases: true, diff --git a/src/interfaces.ts b/src/interfaces.ts index 48d22c90..0e06e6f6 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -28,6 +28,7 @@ export interface BCSettings { downViewWrap: boolean; dotsColour: string; enableAlphaSort: boolean; + enableRelationSuggestor: boolean; fieldSuggestor: boolean; filterImpliedSiblingsOfDifferentTypes: boolean; gridDots: boolean; @@ -76,6 +77,7 @@ export interface BCSettings { showWriteAllBCsCmd: boolean; sortByNameShowAlias: boolean; regexNoteField: string; + relSuggestorTrigger: string; rlLeaf: boolean; showBCs: boolean; showBCsInEditLPMode: boolean; diff --git a/src/main.ts b/src/main.ts index 937f2e3d..01f335ee 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,6 +26,7 @@ import { import { FieldSuggestor } from "./FieldSuggestor"; import type { BCSettings, Directions, MyView, ViewInfo } from "./interfaces"; import { buildClosedG, buildMainG, refreshIndex } from "./refreshIndex"; +import { RelationSuggestor } from "./RelationSuggestor"; import { BCSettingTab } from "./Settings/BreadcrumbsSettingTab"; import { getFields } from "./Utils/HierUtils"; import { waitForCache } from "./Utils/ObsidianUtils"; @@ -73,11 +74,16 @@ export default class BCPlugin extends Plugin { const { app } = this; await this.loadSettings(); - const { settings } = this; this.addSettingTab(new BCSettingTab(app, this)); this.db = new Debugger(this); - this.registerEditorSuggest(new FieldSuggestor(this)); + + const { settings } = this; + const { fieldSuggestor, enableRelationSuggestor } = settings; + + if (fieldSuggestor) this.registerEditorSuggest(new FieldSuggestor(this)); + if (enableRelationSuggestor) + this.registerEditorSuggest(new RelationSuggestor(this)); const { openMatrixOnLoad,