feat(plugins): add background document-indexer API (#1021)

Plugin API 1.2 gains an optional document-indexer export (plugin-indexing world). A plugin returns per document the keys it defines and refers to, aliases between key prefixes and gutter markers; the host indexes a trusted project in cancellable background batches, keeps the project-wide graph, and answers Go to Declaration / Ctrl-click and multi-target gutter markers from memory without calling the plugin again. Edits and file changes invalidate the affected entries; reload, disable or a failing runtime drops the plugin's graph.

The interface is limited to cross-file associations: icons chosen by file content and embedded-language regions are separate concerns for later interfaces. The plugin navigation choosers' titles are translated.

Co-authored-by: qjlqyf <[email protected]>
Цей коміт міститься в:
qiaojianlong authored and GitHub committed 2026-11-06 10:07:53 +08:00
1 джерело 9069c322e8
коміт ed08b96382
79 files changed
+3039 -46

No files matched your search

+34
Переглянути файл
@@ -0,0 +1,34 @@
import { tx } from "@/platform/i18n";
import { navigateTo, locationItems } from "@/editor/lsp/navigation";
import { openPopup } from "@/editor/lsp/popupStore";
import { gutterIconMarkup, isIconName } from "@/ui/icons";
import type { Location } from "@/platform/ipc";
async function openTargets(targets: Location[], element: HTMLElement) {
if (targets.length === 1) {
await navigateTo(targets[0]!);
return;
}
const rect = element.getBoundingClientRect();
openPopup({
kind: "list",
title: tx`Choose Mapping Target`,
anchor: { x: rect.left, y: rect.bottom + 2 },
items: await locationItems(targets),
});
}
/** Shared ordinary-click and keyboard navigation for interactive and indexed markers. */
export function navigationButton(targets: Location[], icon: string): HTMLButtonElement {
const button = document.createElement("button");
button.type = "button";
button.className = "cm-plugin-mapping-button";
button.title = targets.map((t) => `${t.path.split("/").pop()}:${t.range.start.line + 1}`).join("\n");
button.setAttribute("aria-label", tx`Go to mapping: ${button.title}`);
button.innerHTML = gutterIconMarkup(isIconName(icon) ? icon : "arrow-right") ?? "";
button.onclick = (event) => {
event.preventDefault();
event.stopPropagation();
void openTargets(targets, button);
};
return button;
}