дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
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]>
98 рядки
2.9 KiB
TypeScript
98 рядки
2.9 KiB
TypeScript
import { afterEach, describe, expect, it } from "vitest";
|
|
import { EditorState } from "@codemirror/state";
|
|
import { EditorView } from "@codemirror/view";
|
|
import {
|
|
contributedNavigation,
|
|
invalidateNavigationProviders,
|
|
registerNavigationProvider,
|
|
type NavigationMatch,
|
|
} from "./navigationProviders";
|
|
|
|
const disposers: (() => void)[] = [];
|
|
const views: EditorView[] = [];
|
|
afterEach(() => {
|
|
disposers.splice(0).forEach((f) => f());
|
|
views.splice(0).forEach((v) => v.destroy());
|
|
});
|
|
function view() {
|
|
const v = new EditorView({ state: EditorState.create({ doc: "demo.UserMapper" }) });
|
|
views.push(v);
|
|
return v;
|
|
}
|
|
const hit: NavigationMatch = {
|
|
from: 0,
|
|
to: 15,
|
|
targets: [{ path: "/src/UserMapper.java", range: { start: { line: 0, col: 0 }, end: { line: 0, col: 10 } } }],
|
|
};
|
|
|
|
describe("navigation providers", () => {
|
|
it("shares pending hover/click requests and whole-name results", async () => {
|
|
let calls = 0;
|
|
let finish!: (value: NavigationMatch) => void;
|
|
disposers.push(
|
|
registerNavigationProvider(() => {
|
|
calls++;
|
|
return new Promise((r) => {
|
|
finish = r;
|
|
});
|
|
}),
|
|
);
|
|
const v = view();
|
|
const hover = contributedNavigation(v, 0);
|
|
const click = contributedNavigation(v, 0);
|
|
finish(hit);
|
|
expect(await hover).toEqual(hit);
|
|
expect(await click).toEqual(hit);
|
|
expect(await contributedNavigation(v, 8)).toEqual(hit);
|
|
expect(calls).toBe(1);
|
|
});
|
|
it("drops results after edits and re-queries the new document", async () => {
|
|
let finish!: (value: NavigationMatch) => void;
|
|
const remove = registerNavigationProvider(
|
|
() =>
|
|
new Promise((r) => {
|
|
finish = r;
|
|
}),
|
|
);
|
|
disposers.push(remove);
|
|
const v = view();
|
|
const request = contributedNavigation(v, 0);
|
|
v.dispatch({ changes: { from: 0, insert: "x" } });
|
|
finish(hit);
|
|
expect(await request).toBeNull();
|
|
});
|
|
it("invalidates misses after background indexing and removes disabled providers", async () => {
|
|
let ready = false;
|
|
let calls = 0;
|
|
const off = registerNavigationProvider(async () => {
|
|
calls++;
|
|
return ready ? hit : null;
|
|
});
|
|
disposers.push(off);
|
|
const v = view();
|
|
expect(await contributedNavigation(v, 0)).toBeNull();
|
|
expect(await contributedNavigation(v, 0)).toBeNull();
|
|
expect(calls).toBe(1);
|
|
ready = true;
|
|
invalidateNavigationProviders();
|
|
expect(await contributedNavigation(v, 0)).toEqual(hit);
|
|
off();
|
|
expect(await contributedNavigation(v, 0)).toBeNull();
|
|
});
|
|
it("drops old results after project/provider invalidation", async () => {
|
|
let finish!: (value: NavigationMatch) => void;
|
|
disposers.push(
|
|
registerNavigationProvider(
|
|
() =>
|
|
new Promise((r) => {
|
|
finish = r;
|
|
}),
|
|
),
|
|
);
|
|
const request = contributedNavigation(view(), 0);
|
|
invalidateNavigationProviders();
|
|
finish(hit);
|
|
expect(await request).toBeNull();
|
|
});
|
|
});
|