mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-13 04:34:21 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d596c7943 |
No files matched your search
@@ -56,7 +56,7 @@ async function applyEdit(view: EditorView, edit: WorkspaceEdit) {
|
|||||||
async function prepareIndexes(view: EditorView, path: string): Promise<boolean> {
|
async function prepareIndexes(view: EditorView, path: string): Promise<boolean> {
|
||||||
const before = view.state.doc;
|
const before = view.state.doc;
|
||||||
try {
|
try {
|
||||||
await synchronizeDocumentIndexes(path);
|
await synchronizeDocumentIndexes(path, "refactor");
|
||||||
return view.state.doc === before;
|
return view.state.doc === before;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reportLspError(view, e, true);
|
reportLspError(view, e, true);
|
||||||
@@ -269,7 +269,7 @@ async function finishRename(view: EditorView, doc: EditorDocContext, pos: Pos, o
|
|||||||
let renamed;
|
let renamed;
|
||||||
const before = view.state.doc;
|
const before = view.state.doc;
|
||||||
try {
|
try {
|
||||||
await synchronizeDocumentIndexes(doc.path);
|
await synchronizeDocumentIndexes(doc.path, "refactor");
|
||||||
if (renameRequest !== request || view.state.doc !== before) return;
|
if (renameRequest !== request || view.state.doc !== before) return;
|
||||||
renamed = await lspRequest(() => commands.lspRename(request, doc.doc, pos, newName));
|
renamed = await lspRequest(() => commands.lspRename(request, doc.doc, pos, newName));
|
||||||
if (renameRequest !== request) return;
|
if (renameRequest !== request) return;
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ describe("findUsages", () => {
|
|||||||
const pending = findUsages();
|
const pending = findUsages();
|
||||||
const at = (line: number) => ({ path: "/p/b.rs", range: { start: { line, col: 4 }, end: { line, col: 8 } } });
|
const at = (line: number) => ({ path: "/p/b.rs", range: { start: { line, col: 4 }, end: { line, col: 8 } } });
|
||||||
try {
|
try {
|
||||||
await vi.waitFor(() => expect(sync).toHaveBeenCalledWith("/p/a.rs"));
|
await vi.waitFor(() => expect(sync).toHaveBeenCalledWith("/p/a.rs", "usages"));
|
||||||
expect(lspReferences).not.toHaveBeenCalled();
|
expect(lspReferences).not.toHaveBeenCalled();
|
||||||
expect(channels).toHaveLength(allocated);
|
expect(channels).toHaveLength(allocated);
|
||||||
release();
|
release();
|
||||||
@@ -181,6 +181,23 @@ describe("findUsages", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("findUsages with a contributed index that is not up to date", () => {
|
||||||
|
it("still searches, and says plugin usages may be missing", async () => {
|
||||||
|
const off = registerDocumentIndexSync(async () => false);
|
||||||
|
const at = { path: "/p/b.rs", range: { start: { line: 1, col: 4 }, end: { line: 1, col: 8 } } };
|
||||||
|
referenceResponse.mockResolvedValueOnce({ locations: [at], incomplete: null });
|
||||||
|
try {
|
||||||
|
await findUsages();
|
||||||
|
const search = activeUsageSearch(useUsages.getState())!;
|
||||||
|
expect(search.groups).toHaveLength(1);
|
||||||
|
expect(search.note).toBe("May be incomplete: plugin indexes are still updating");
|
||||||
|
} finally {
|
||||||
|
off();
|
||||||
|
resetUsages();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("usage tabs", () => {
|
describe("usage tabs", () => {
|
||||||
const search = (id: number) => ({
|
const search = (id: number) => ({
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { clearLineTokens } from "@/editor/lineTokens";
|
|||||||
import { showPendingHint } from "@/editor/lsp/hint";
|
import { showPendingHint } from "@/editor/lsp/hint";
|
||||||
import { incompleteNote } from "@/editor/lsp/lsp";
|
import { incompleteNote } from "@/editor/lsp/lsp";
|
||||||
import { commands, IpcError, isCancelled, nextRequestId } from "@/platform/ipc";
|
import { commands, IpcError, isCancelled, nextRequestId } from "@/platform/ipc";
|
||||||
|
import { tx } from "@/platform/i18n";
|
||||||
import { shortcutLabel } from "@/platform/keymap";
|
import { shortcutLabel } from "@/platform/keymap";
|
||||||
import { synchronizeDocumentIndexes, toolWindowService } from "@/workbench/contributions";
|
import { synchronizeDocumentIndexes, toolWindowService } from "@/workbench/contributions";
|
||||||
import {
|
import {
|
||||||
@@ -230,7 +231,7 @@ export async function fetchUsages(
|
|||||||
let note: string | null = null;
|
let note: string | null = null;
|
||||||
let librariesSkipped = false;
|
let librariesSkipped = false;
|
||||||
await caretSynced(c);
|
await caretSynced(c);
|
||||||
await synchronizeDocumentIndexes(c.path);
|
const indexed = await synchronizeDocumentIndexes(c.path, "usages");
|
||||||
if (c.view && c.view.state.doc !== before) throw new IpcError({ kind: "cancelled", message: "", data: null });
|
if (c.view && c.view.state.doc !== before) throw new IpcError({ kind: "cancelled", message: "", data: null });
|
||||||
let settled = false;
|
let settled = false;
|
||||||
const channel = new Channel<References>();
|
const channel = new Channel<References>();
|
||||||
@@ -268,6 +269,7 @@ export async function fetchUsages(
|
|||||||
locs = fb.locs;
|
locs = fb.locs;
|
||||||
note = fb.note;
|
note = fb.note;
|
||||||
}
|
}
|
||||||
|
if (!indexed) note ??= tx("May be incomplete: plugin indexes are still updating");
|
||||||
const files = await readManyFileLines(pathsToRead(locs, classified));
|
const files = await readManyFileLines(pathsToRead(locs, classified));
|
||||||
return { groups: groupUsages(locs, files, classified), note, librariesSkipped };
|
return { groups: groupUsages(locs, files, classified), note, librariesSkipped };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ it("flushes pending changes before usages or rename without waiting for the debo
|
|||||||
await vi.advanceTimersByTimeAsync(250);
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
mocks.index.mockClear();
|
mocks.index.mockClear();
|
||||||
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
||||||
await synchronizeDocumentIndexes("/project/A.xml");
|
await synchronizeDocumentIndexes("/project/A.xml", "refactor");
|
||||||
expect(mocks.index).toHaveBeenCalledExactlyOnceWith(["/project/A.xml"], expect.any(Number));
|
expect(mocks.index).toHaveBeenCalledExactlyOnceWith(["/project/A.xml"], expect.any(Number));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ it("waits for an active batch, then indexes changes queued while it was running"
|
|||||||
await vi.advanceTimersByTimeAsync(40);
|
await vi.advanceTimersByTimeAsync(40);
|
||||||
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.java"], removed: [] });
|
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.java"], removed: [] });
|
||||||
let settled = false;
|
let settled = false;
|
||||||
const waiting = synchronizeDocumentIndexes("/project/A.xml").then(() => {
|
const waiting = synchronizeDocumentIndexes("/project/A.xml", "refactor").then(() => {
|
||||||
settled = true;
|
settled = true;
|
||||||
});
|
});
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
@@ -168,7 +168,33 @@ it("refuses refactoring if a queued index batch failed", async () => {
|
|||||||
await vi.advanceTimersByTimeAsync(250);
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
mocks.index.mockResolvedValueOnce({ status: "error", error: { kind: "unavailable", message: "indexer failed" } });
|
mocks.index.mockResolvedValueOnce({ status: "error", error: { kind: "unavailable", message: "indexer failed" } });
|
||||||
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
||||||
await expect(synchronizeDocumentIndexes("/project/A.xml")).rejects.toThrow("Plugin indexes are still updating");
|
await expect(synchronizeDocumentIndexes("/project/A.xml", "refactor")).rejects.toThrow(
|
||||||
|
"Plugin indexes could not be updated",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lets usages go ahead, marked incomplete, while a failed batch keeps failing", async () => {
|
||||||
|
off = startPluginNavigation();
|
||||||
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
|
const failure = { status: "error", error: { kind: "unavailable", message: "indexer failed" } };
|
||||||
|
mocks.index.mockResolvedValue(failure);
|
||||||
|
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
||||||
|
await vi.advanceTimersByTimeAsync(40);
|
||||||
|
// Every later search used to fail with "still updating" although nothing was running.
|
||||||
|
await expect(synchronizeDocumentIndexes("/project/A.java", "usages")).resolves.toBe(false);
|
||||||
|
await expect(synchronizeDocumentIndexes("/project/A.java", "usages")).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("retries a failed batch before the next usages or refactoring", async () => {
|
||||||
|
off = startPluginNavigation();
|
||||||
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
|
mocks.index.mockResolvedValueOnce({ status: "error", error: { kind: "unavailable", message: "indexer failed" } });
|
||||||
|
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
||||||
|
await vi.advanceTimersByTimeAsync(40);
|
||||||
|
mocks.index.mockClear();
|
||||||
|
await expect(synchronizeDocumentIndexes("/project/A.java", "refactor")).resolves.toBe(true);
|
||||||
|
expect(mocks.index).toHaveBeenCalledWith(["/project/A.xml"], expect.any(Number));
|
||||||
|
await expect(synchronizeDocumentIndexes("/project/A.java", "usages")).resolves.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each(["/project/notes.txt", "/external/Other.xml", "jdt://contents/Other.xml"])(
|
it.each(["/project/notes.txt", "/external/Other.xml", "jdt://contents/Other.xml"])(
|
||||||
@@ -179,13 +205,13 @@ it.each(["/project/notes.txt", "/external/Other.xml", "jdt://contents/Other.xml"
|
|||||||
mocks.index.mockImplementationOnce(() => new Promise(() => {}));
|
mocks.index.mockImplementationOnce(() => new Promise(() => {}));
|
||||||
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
||||||
await vi.advanceTimersByTimeAsync(40);
|
await vi.advanceTimersByTimeAsync(40);
|
||||||
await synchronizeDocumentIndexes(path);
|
await synchronizeDocumentIndexes(path, "refactor");
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
it("keeps unrelated language-server queries available when plugin discovery fails", async () => {
|
it("keeps unrelated language-server queries available when plugin discovery fails", async () => {
|
||||||
mocks.files.mockRejectedValue(new Error("Plugin file inventory is unavailable"));
|
mocks.files.mockRejectedValue(new Error("Plugin file inventory is unavailable"));
|
||||||
off = startPluginNavigation();
|
off = startPluginNavigation();
|
||||||
await expect(synchronizeDocumentIndexes("/project/notes.txt")).resolves.toBeUndefined();
|
await expect(synchronizeDocumentIndexes("/project/notes.txt", "refactor")).resolves.toBe(true);
|
||||||
expect(mocks.index).not.toHaveBeenCalled();
|
expect(mocks.index).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@@ -22,9 +22,12 @@ export function startPluginNavigation(): () => void {
|
|||||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||||
let pending = new Set<string>();
|
let pending = new Set<string>();
|
||||||
const dirty = new Set<string>();
|
const dirty = new Set<string>();
|
||||||
|
/** Paths whose last index batch failed: retried on the next usages or refactoring. */
|
||||||
|
const failed = new Set<string>();
|
||||||
const views = new Map<EditorView, string>();
|
const views = new Map<EditorView, string>();
|
||||||
let discovered = false;
|
let discovered = false;
|
||||||
let discovering = false;
|
let discovering = false;
|
||||||
|
let discoveryFailed = false;
|
||||||
const known = new Set<string>();
|
const known = new Set<string>();
|
||||||
let signature = "";
|
let signature = "";
|
||||||
let reportedError = false;
|
let reportedError = false;
|
||||||
@@ -79,13 +82,22 @@ export function startPluginNavigation(): () => void {
|
|||||||
await call(commands.pluginNavigationIndex(paths, id));
|
await call(commands.pluginNavigationIndex(paths, id));
|
||||||
if (at === generation) {
|
if (at === generation) {
|
||||||
paths.forEach((p) => {
|
paths.forEach((p) => {
|
||||||
|
failed.delete(p);
|
||||||
if (!pending.has(p)) dirty.delete(p);
|
if (!pending.has(p)) dirty.delete(p);
|
||||||
});
|
});
|
||||||
notify();
|
notify();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (at === generation && isCancelled(error)) paths.forEach((p) => pending.add(p));
|
if (at === generation && isCancelled(error)) paths.forEach((p) => pending.add(p));
|
||||||
else if (at === generation) report(error);
|
else if (at === generation) {
|
||||||
|
// Not "still updating": the batch is done, its paths just have no fresh index.
|
||||||
|
paths.forEach((p) => {
|
||||||
|
if (pending.has(p)) return;
|
||||||
|
dirty.delete(p);
|
||||||
|
failed.add(p);
|
||||||
|
});
|
||||||
|
report(error);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
request = null;
|
request = null;
|
||||||
running = false;
|
running = false;
|
||||||
@@ -120,12 +132,14 @@ export function startPluginNavigation(): () => void {
|
|||||||
if (at !== generation) return;
|
if (at !== generation) return;
|
||||||
fileExtensions = new Set(inventory.extensions);
|
fileExtensions = new Set(inventory.extensions);
|
||||||
discovered = !inventory.indexing;
|
discovered = !inventory.indexing;
|
||||||
|
discoveryFailed = false;
|
||||||
const fresh = inventory.paths.filter((p) => !known.has(p));
|
const fresh = inventory.paths.filter((p) => !known.has(p));
|
||||||
inventory.paths.forEach((p) => known.add(p));
|
inventory.paths.forEach((p) => known.add(p));
|
||||||
queue(fresh);
|
queue(fresh);
|
||||||
queue([...views.values()], true);
|
queue([...views.values()], true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
discovered = false;
|
discovered = false;
|
||||||
|
discoveryFailed = true;
|
||||||
report(error);
|
report(error);
|
||||||
} finally {
|
} finally {
|
||||||
if (at === generation) discovering = false;
|
if (at === generation) discovering = false;
|
||||||
@@ -139,9 +153,11 @@ export function startPluginNavigation(): () => void {
|
|||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
pending.clear();
|
pending.clear();
|
||||||
dirty.clear();
|
dirty.clear();
|
||||||
|
failed.clear();
|
||||||
known.clear();
|
known.clear();
|
||||||
discovered = false;
|
discovered = false;
|
||||||
discovering = false;
|
discovering = false;
|
||||||
|
discoveryFailed = false;
|
||||||
discovery = null;
|
discovery = null;
|
||||||
reportedError = false;
|
reportedError = false;
|
||||||
notify();
|
notify();
|
||||||
@@ -151,32 +167,41 @@ export function startPluginNavigation(): () => void {
|
|||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
const offs = [
|
const offs = [
|
||||||
registerDocumentIndexSync(async (path) => {
|
registerDocumentIndexSync(async (path, purpose) => {
|
||||||
if (!ready()) return;
|
if (!ready()) return;
|
||||||
const root = useProject.getState().project!.root;
|
const root = useProject.getState().project!.root;
|
||||||
if (!path.startsWith(root.endsWith("/") ? root : `${root}/`)) return;
|
if (!path.startsWith(root.endsWith("/") ? root : `${root}/`)) return;
|
||||||
const at = generation;
|
const at = generation;
|
||||||
await discover();
|
await discover();
|
||||||
if (!eligible(path)) return;
|
if (!eligible(path)) return;
|
||||||
|
if (failed.size) queue([...failed]);
|
||||||
while (ready() && at === generation && (pending.size || flushing)) {
|
while (ready() && at === generation && (pending.size || flushing)) {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
await flush();
|
await flush();
|
||||||
}
|
}
|
||||||
if (at !== generation || !discovered || dirty.size) {
|
const complete = at === generation && discovered && !dirty.size && !failed.size;
|
||||||
throw new Error(tx`Plugin indexes are still updating; try again shortly`);
|
if (complete) return true;
|
||||||
|
if (purpose === "usages") return false;
|
||||||
|
if (at === generation && (failed.size || discoveryFailed)) {
|
||||||
|
throw new Error(tx`Plugin indexes could not be updated; see the plugin notification`);
|
||||||
}
|
}
|
||||||
|
throw new Error(tx`Plugin indexes are still updating; try again shortly`);
|
||||||
}),
|
}),
|
||||||
pluginsBackend().onEvent((event) => {
|
pluginsBackend().onEvent((event) => {
|
||||||
if (event.type === "documentIndexesChanged") reset();
|
if (event.type === "documentIndexesChanged") reset();
|
||||||
}),
|
}),
|
||||||
registerNavigationProvider(async (view, offset) => {
|
registerNavigationProvider(async (view, offset) => {
|
||||||
const doc = docOf(view);
|
const doc = docOf(view);
|
||||||
if (!ready() || !doc || doc.virtual || !eligible(doc.path) || dirty.has(doc.path)) return null;
|
if (!ready() || !doc || doc.virtual || !eligible(doc.path) || dirty.has(doc.path) || failed.has(doc.path))
|
||||||
|
return null;
|
||||||
const before = view.state.doc;
|
const before = view.state.doc;
|
||||||
await whenSynced(view);
|
await whenSynced(view);
|
||||||
const result = await call(commands.pluginNavigationGoto(doc.doc, offsetToPos(before, offset)));
|
const result = await call(commands.pluginNavigationGoto(doc.doc, offsetToPos(before, offset)));
|
||||||
if (!result || view.state.doc !== before) return null;
|
if (!result || view.state.doc !== before) return null;
|
||||||
return { ...rangeToOffsets(before, result.range), targets: result.targets.filter((t) => !dirty.has(t.path)) };
|
return {
|
||||||
|
...rangeToOffsets(before, result.range),
|
||||||
|
targets: result.targets.filter((t) => !dirty.has(t.path) && !failed.has(t.path)),
|
||||||
|
};
|
||||||
}),
|
}),
|
||||||
registerEditorContribution({
|
registerEditorContribution({
|
||||||
id: "plugins.navigation",
|
id: "plugins.navigation",
|
||||||
|
|||||||
@@ -1344,5 +1344,7 @@
|
|||||||
"Close Agent Task": "Close Agent Task",
|
"Close Agent Task": "Close Agent Task",
|
||||||
"Close and New Agent Task": "Close and New Agent Task",
|
"Close and New Agent Task": "Close and New Agent Task",
|
||||||
"Closes the agent task tab in the editor; the task keeps running in the Agents tool window.": "Closes the agent task tab in the editor; the task keeps running in the Agents tool window.",
|
"Closes the agent task tab in the editor; the task keeps running in the Agents tool window.": "Closes the agent task tab in the editor; the task keeps running in the Agents tool window.",
|
||||||
"Closes the agent task tab in the editor, then opens the New Task page.": "Closes the agent task tab in the editor, then opens the New Task page."
|
"Closes the agent task tab in the editor, then opens the New Task page.": "Closes the agent task tab in the editor, then opens the New Task page.",
|
||||||
|
"May be incomplete: plugin indexes are still updating": "May be incomplete: plugin indexes are still updating",
|
||||||
|
"Plugin indexes could not be updated; see the plugin notification": "Plugin indexes could not be updated; see the plugin notification"
|
||||||
}
|
}
|
||||||
@@ -7731,5 +7731,7 @@
|
|||||||
"Download Remote Files Into Folder": "将远程文件下载到文件夹",
|
"Download Remote Files Into Folder": "将远程文件下载到文件夹",
|
||||||
"Download Remote Host files into a local folder (arg: { server, paths, target })": "将远程主机中的文件下载到本地文件夹(参数:{ server, paths, target })",
|
"Download Remote Host files into a local folder (arg: { server, paths, target })": "将远程主机中的文件下载到本地文件夹(参数:{ server, paths, target })",
|
||||||
"Delegate IDE build/run actions to Maven": "将 IDE 构建/运行操作委托给 Maven",
|
"Delegate IDE build/run actions to Maven": "将 IDE 构建/运行操作委托给 Maven",
|
||||||
"Run and Debug build Maven modules with Maven (the project's javac) instead of the Java language server. Editor problems still come from the language server.": "运行和调试时用 Maven(项目的 javac)构建 Maven 模块,而不是用 Java 语言服务器。编辑器中的问题仍来自语言服务器。"
|
"Run and Debug build Maven modules with Maven (the project's javac) instead of the Java language server. Editor problems still come from the language server.": "运行和调试时用 Maven(项目的 javac)构建 Maven 模块,而不是用 Java 语言服务器。编辑器中的问题仍来自语言服务器。",
|
||||||
|
"May be incomplete: plugin indexes are still updating": "可能不完整:插件索引仍在更新",
|
||||||
|
"Plugin indexes could not be updated; see the plugin notification": "插件索引更新失败,详见插件通知"
|
||||||
}
|
}
|
||||||
@@ -1825,16 +1825,23 @@ export function addContributedFileTemplate(t: import("@/platform/fileTemplates")
|
|||||||
return () => useContributedFileTemplates.setState((s) => ({ templates: s.templates.filter((x) => x !== t) }));
|
return () => useContributedFileTemplates.setState((s) => ({ templates: s.templates.filter((x) => x !== t) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Flush contributed document indexes before workspace usages or refactoring. */
|
/**
|
||||||
const documentIndexSync = new Set<(path: string) => Promise<void>>();
|
* Flush contributed document indexes before workspace usages or refactoring. A refactoring
|
||||||
export function registerDocumentIndexSync(sync: (path: string) => Promise<void>): () => void {
|
* refuses (throws) while an index is incomplete; usages go ahead and resolve `false`, so the
|
||||||
|
* results can say they may be incomplete.
|
||||||
|
*/
|
||||||
|
export type DocumentIndexPurpose = "usages" | "refactor";
|
||||||
|
type DocumentIndexSync = (path: string, purpose: DocumentIndexPurpose) => Promise<boolean | void>;
|
||||||
|
const documentIndexSync = new Set<DocumentIndexSync>();
|
||||||
|
export function registerDocumentIndexSync(sync: DocumentIndexSync): () => void {
|
||||||
documentIndexSync.add(sync);
|
documentIndexSync.add(sync);
|
||||||
return () => {
|
return () => {
|
||||||
documentIndexSync.delete(sync);
|
documentIndexSync.delete(sync);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export async function synchronizeDocumentIndexes(path: string): Promise<void> {
|
export async function synchronizeDocumentIndexes(path: string, purpose: DocumentIndexPurpose): Promise<boolean> {
|
||||||
await Promise.all([...documentIndexSync].map((sync) => sync(path)));
|
const complete = await Promise.all([...documentIndexSync].map((sync) => sync(path, purpose)));
|
||||||
|
return complete.every((c) => c !== false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Font zoom for non-CodeMirror surfaces, owned and released by their features. */
|
/** Font zoom for non-CodeMirror surfaces, owned and released by their features. */
|
||||||
|
|||||||
Reference in new issue
Block a user