diff --git a/main.ts b/main.ts index 50b75f3..3addc91 100644 --- a/main.ts +++ b/main.ts @@ -1,137 +1,51 @@ -import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; - -// Remember to rename these classes and interfaces! - -interface MyPluginSettings { - mySetting: string; -} - -const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: 'default' -} - -export default class MyPlugin extends Plugin { - settings: MyPluginSettings; - - async onload() { - await this.loadSettings(); - - // This creates an icon in the left ribbon. - const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => { - // Called when the user clicks the icon. - new Notice('This is a notice!'); - }); - // Perform additional things with the ribbon - ribbonIconEl.addClass('my-plugin-ribbon-class'); - - // This adds a status bar item to the bottom of the app. Does not work on mobile apps. - const statusBarItemEl = this.addStatusBarItem(); - statusBarItemEl.setText('Status Bar Text'); - - // This adds a simple command that can be triggered anywhere - this.addCommand({ - id: 'open-sample-modal-simple', - name: 'Open sample modal (simple)', - callback: () => { - new SampleModal(this.app).open(); - } - }); - // This adds an editor command that can perform some operation on the current editor instance - this.addCommand({ - id: 'sample-editor-command', - name: 'Sample editor command', - editorCallback: (editor: Editor, view: MarkdownView) => { - console.log(editor.getSelection()); - editor.replaceSelection('Sample Editor Command'); - } - }); - // This adds a complex command that can check whether the current state of the app allows execution of the command - this.addCommand({ - id: 'open-sample-modal-complex', - name: 'Open sample modal (complex)', - checkCallback: (checking: boolean) => { - // Conditions to check - const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); - if (markdownView) { - // If checking is true, we're simply "checking" if the command can be run. - // If checking is false, then we want to actually perform the operation. - if (!checking) { - new SampleModal(this.app).open(); - } - - // This command will only show up in Command Palette when the check function returns true - return true; - } - } - }); - - // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SampleSettingTab(this.app, this)); - - // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) - // Using this function will automatically remove the event listener when this plugin is disabled. - this.registerDomEvent(document, 'click', (evt: MouseEvent) => { - console.log('click', evt); - }); - - // When registering intervals, this function will automatically clear the interval when the plugin is disabled. - this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000)); - } - - onunload() { - - } - - async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); - } - - async saveSettings() { - await this.saveData(this.settings); - } -} - -class SampleModal extends Modal { - constructor(app: App) { - super(app); - } - - onOpen() { - const {contentEl} = this; - contentEl.setText('Woah!'); - } - - onClose() { - const {contentEl} = this; - contentEl.empty(); - } -} - -class SampleSettingTab extends PluginSettingTab { - plugin: MyPlugin; - - constructor(app: App, plugin: MyPlugin) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const {containerEl} = this; - - containerEl.empty(); - - containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'}); - - new Setting(containerEl) - .setName('Setting #1') - .setDesc('It\'s a secret') - .addText(text => text - .setPlaceholder('Enter your secret') - .setValue(this.plugin.settings.mySetting) - .onChange(async (value) => { - console.log('Secret: ' + value); - this.plugin.settings.mySetting = value; - await this.plugin.saveSettings(); - })); - } +// https://marcus.se.net/obsidian-plugin-docs/reference/typescript +import { Plugin, parseFrontMatterAliases } from 'obsidian'; +import type { Editor, MarkdownView } from 'obsidian'; + +import { isNeedleAtIndex } from './util'; + +export default class NoteAliases extends Plugin { + public onload (): void { + this.addCommand({ + id: 'save-alias', + name: 'Save alias of the link under cursor in the target note frontmatter', + + editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView) => { + const linkRe = /\[\[(?[^[|]*)\|(?[^\]]*)\]\]/u; + + const position = editor.getCursor(); + const line = editor.getLine(position.line); + const links = line.match(new RegExp(linkRe, 'gu')) ?? []; + const link = links.find(match => isNeedleAtIndex(line, match, position.ch)); + + if (!link) return false; + if (checking) return true; + + const { alias, target } = linkRe.exec(link)?.groups ?? {}; + const fromPath = view.file.path; + + (async (): Promise => { + let targetFile = app.metadataCache.getFirstLinkpathDest(target, fromPath); + if (!targetFile) { + console.info('target file not exists', { checking, editor, view }); + const targetPath = `${app.fileManager.getNewFileParent(fromPath).path}/${target}.md`; + targetFile = await app.vault.create(targetPath, ''); + console.info(targetFile); + } + await app.fileManager.processFrontMatter( + targetFile, + (metadata: { aliases?: unknown }) => { + const aliases = parseFrontMatterAliases(metadata) ?? []; + const exists = aliases.some(item => item.toLowerCase() === alias.toLowerCase()); + + if (exists) return; + + metadata.aliases = [...aliases, alias]; + } + ); + })().catch(error => { console.error(error); }); + return true; + }, + }); + } } diff --git a/util/index.ts b/util/index.ts new file mode 100644 index 0000000..57f9f48 --- /dev/null +++ b/util/index.ts @@ -0,0 +1 @@ +export * from './string'; diff --git a/util/string/index.ts b/util/string/index.ts new file mode 100644 index 0000000..4aa447d --- /dev/null +++ b/util/string/index.ts @@ -0,0 +1 @@ +export * from './isNeedleAtIndex'; diff --git a/util/string/isNeedleAtIndex.ts b/util/string/isNeedleAtIndex.ts new file mode 100644 index 0000000..f695c5c --- /dev/null +++ b/util/string/isNeedleAtIndex.ts @@ -0,0 +1,9 @@ +export function isNeedleAtIndex (haystack: string, needle: string, index: number): boolean { + let start = haystack.indexOf(needle); + while (start !== -1) { + if (start > index) return false; + if ((start + needle.length) > index) return true; + start = haystack.indexOf(needle, start + 1); + } + return false; +}