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 [[
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