text editor link autocomplete

This commit is contained in:
2026-06-12 13:57:14 -04:00
parent 0adb980cf7
commit 4655008154
3 changed files with 146 additions and 42 deletions

View File

@@ -18,6 +18,7 @@
var codeMirrorContainer = document.querySelector("[data-codemirror-mount]");
var codeMirrorReadyPromise = null;
var codeMirrorModules = null;
var documentCompletionOptionsPromise = null;
if (!form || !textarea || statusNodes.length === 0) {
return;
@@ -507,6 +508,105 @@
]));
}
function normalizeCompletionPath(doc) {
var docPath = doc && doc.path ? String(doc.path) : "";
return docPath.replace(/\.md$/, "");
}
function buildDocumentCompletionOptions(documents) {
var seen = new Set();
return (documents || [])
.map(function (doc) {
var label = normalizeCompletionPath(doc);
if (!label || seen.has(label) || label === path.replace(/\.md$/, "")) {
return null;
}
seen.add(label);
return {
label: label,
detail: doc.title && doc.title !== label ? doc.title : doc.path,
type: "file",
apply: function (view, completion, from, to) {
var insert = completion.label;
var after = view.state.doc.sliceString(to, Math.min(view.state.doc.length, to + 2));
if (after !== "]]") {
insert += "]]";
}
view.dispatch({
changes: { from: from, to: to, insert: insert },
selection: { anchor: from + insert.length },
});
},
};
})
.filter(Boolean)
.sort(function (a, b) {
return a.label.localeCompare(b.label);
});
}
function fetchDocumentCompletionOptions() {
return fetch("/api/documents", { credentials: "same-origin" })
.then(function (response) {
if (!response.ok) {
throw new Error("Could not load documents");
}
return response.json();
})
.then(function (data) {
var documents = data.documents || [];
if (window.MDHubCache && window.MDHubCache.cacheDocuments) {
window.MDHubCache.cacheDocuments(documents).catch(function () {});
}
return buildDocumentCompletionOptions(documents);
});
}
function loadDocumentCompletionOptions() {
if (documentCompletionOptionsPromise) {
return documentCompletionOptionsPromise;
}
documentCompletionOptionsPromise = Promise.resolve()
.then(function () {
if (window.MDHubCache && window.MDHubCache.getCachedDocuments) {
return window.MDHubCache.getCachedDocuments().then(function (documents) {
if (documents && documents.length > 0) {
return buildDocumentCompletionOptions(documents);
}
return fetchDocumentCompletionOptions();
});
}
return fetchDocumentCompletionOptions();
})
.catch(function () {
return [];
});
return documentCompletionOptionsPromise;
}
function wikiLinkCompletions(context) {
var before = context.matchBefore(/\[\[([^\]\n]*)$/);
if (!before) {
return null;
}
return loadDocumentCompletionOptions().then(function (options) {
if (!options.length) {
return null;
}
return {
from: before.from + 2,
to: before.to,
options: options,
validFor: /^[^\]\n]*$/,
};
});
}
function createCodeMirrorEditor(mount, initialValue, options) {
var cm = codeMirrorModules.cm;
var markdownModule = codeMirrorModules.markdown;
@@ -521,6 +621,10 @@
cm.EditorView.lineWrapping,
codeMirrorTheme(),
codeMirrorHighlighting(),
cm.autocompletion({
override: [wikiLinkCompletions],
activateOnTyping: true,
}),
cm.EditorView.updateListener.of(function (update) {
if (update.docChanged) {
onChange(update.state.doc.toString());