Search code examples
node.jselectronobsidian

Is there a way to insert custom data in Obsidian Internal Link result?


I'm developing an Obsidian plugin and want to augment the default internal link suggestions (the ones that appear when I type [[ ) with some custom data. Specifically, I'd like to merge my custom results with Obsidian's suggestions.

I tried listening to editor-change and get the query

this.app.workspace.on('editor-change', (editor: Editor) => {
 const cursor = editor.getCursor();
 const line = editor.getLine(cursor.line);
 const linkMatch = line.match(/\[\[(.*?)(?:\]\])?$/);
 
 if (linkMatch) {
   const query = linkMatch[1];
   console.log('Link query:', query);
 }
})

That work, and I can capture the query then call my API but I can't find documentation on how to insert my data in the default Suggest UI.

I can create my own EditorSuggest with custom trigger but the requirement is to use [[


Solution

  • Update: Check end, for an alternative(abit more robust/easy) injection solution

    Disclaimer: I'm new to obsidian, so my answer should be taken with a "grain of salt"

    I think this is not really easy possible. If this is only for you or in an environment that you can control, you could:

    • "deactivate" the default EditorSuggest, and write/register your own EditorSuggest

      default class EnhancedLinkSuggestions extends Plugin {
      async onload() {
          let defaultSuggestions = this.app.workspace.editorSuggest.suggests[0];
          defaultSuggestions.onTrigger = ( _ => undefined);
          ...
          this.registerEditorSuggest(...)
          ...
      
    • Or attach a html mutation observer to the html element of the current EditorSuggest and inject html code into the popup.

        export default class EnhancedLinkSuggestions extends Plugin {
            async onload() {
                let defaultSuggestions = this.app.workspace.editorSuggest.suggests[0];
                window._me = this;
                console.info(defaultSuggestions);
                this.attachObserver(defaultSuggestions.suggestEl);
                ...
            }
      
            attachObserver(element) {
                var observer = new MutationObserver(function (changes) {
                    if (changes[0].target === element) {    
                        if (element.checkVisibility()) {
                            let hasEntries = element.querySelector("#_my");
                            if (!hasEntries) {
                                let parent = element.querySelector(".suggestion");
                                let wrapper = document.createElement("div");
                                wrapper.id = "_my";
                                wrapper.innerHTML = this.generateEntry("TEST DATA");
                                parent.prepend(wrapper);
                            }
                        }
                    }
                });
      
                var options = { attributes: true,  attributeFilter: ["style"] };
                observer.observe(element, options);
            }
            // Example
            generateEntry(value) {
                return `<div class="suggestion-item mod-complex">
                <div class="suggestion-content">
                    <div class="suggestion-title">${value}</div>
                    <div class="suggestion-note"></div>
                </div>
                <div class="suggestion-aux"></div>
                </div>`;
            }
        }
      

    But I think these solutions, are just workaround and a bit brittle, that said I currently couldn't find any better solution.

    Update:
    This solution is also a bit hacky, but you could simply write a wrapper function around the getSuggestions function of the default EditorSuggest.

    Example Code:
    (This is all the code needed, but could be tweaked abit)
    Only tested on Win11, Obsidian Version 1.8.7, with not community plugins, except this one

    export default class EnhancedLinkSuggestions extends Plugin {
            async onload() {
                var defaultPopUp = this.app.workspace.editorSuggest.suggests[0];
                var _getSuggestions = defaultPopUp.getSuggestions;
                var _wrapperGetSuggestions = async function (...args) {
                    // get original results
                    let result = await _getSuggestions.call(this, ...args);
                    // return Promise, to keep things the same
                    // add in your values with the array "concat" 
                    return Promise.resolve(
                        [
                            // Here you add your data
                            {
                                type: "linktext",
                                path: "injected " + Date.now(),
                                score: Infinity,
                                matches: null,
                            },
                        ].concat(result)
                    );
                };
                defaultPopUp.getSuggestions = _wrapperGetSuggestions;
            }
        }
    

    And It looks like this:

    ScreenShot Injected Entry in Link EditorSuggest Popup (Obsidian v 1.8.7) ScreenShot Injected Entry in Link EditorSuggest Popup