дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-12 12:45:39 +02:00
Порівняти коміти
4
Коміти
| Автор | SHA1 | Дата | |
|---|---|---|---|
|
|
d0e7558b1c | ||
|
|
3bd7adda08 | ||
|
|
8bd417edaa | ||
|
|
fefd60206d |
No files matched your search
@@ -11,12 +11,4 @@
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<!-- Win32 calls in dependencies (gitoxide's ownership check of a submodule's
|
||||
`<worktree>/../../.git/modules/…` path) accept paths past MAX_PATH when
|
||||
the system enables long paths. -->
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
@@ -56,7 +56,7 @@ async function applyEdit(view: EditorView, edit: WorkspaceEdit) {
|
||||
async function prepareIndexes(view: EditorView, path: string): Promise<boolean> {
|
||||
const before = view.state.doc;
|
||||
try {
|
||||
await synchronizeDocumentIndexes(path, "refactor");
|
||||
await synchronizeDocumentIndexes(path);
|
||||
return view.state.doc === before;
|
||||
} catch (e) {
|
||||
reportLspError(view, e, true);
|
||||
@@ -269,7 +269,7 @@ async function finishRename(view: EditorView, doc: EditorDocContext, pos: Pos, o
|
||||
let renamed;
|
||||
const before = view.state.doc;
|
||||
try {
|
||||
await synchronizeDocumentIndexes(doc.path, "refactor");
|
||||
await synchronizeDocumentIndexes(doc.path);
|
||||
if (renameRequest !== request || view.state.doc !== before) return;
|
||||
renamed = await lspRequest(() => commands.lspRename(request, doc.doc, pos, newName));
|
||||
if (renameRequest !== request) return;
|
||||
|
||||
@@ -160,7 +160,7 @@ describe("findUsages", () => {
|
||||
const pending = findUsages();
|
||||
const at = (line: number) => ({ path: "/p/b.rs", range: { start: { line, col: 4 }, end: { line, col: 8 } } });
|
||||
try {
|
||||
await vi.waitFor(() => expect(sync).toHaveBeenCalledWith("/p/a.rs", "usages"));
|
||||
await vi.waitFor(() => expect(sync).toHaveBeenCalledWith("/p/a.rs"));
|
||||
expect(lspReferences).not.toHaveBeenCalled();
|
||||
expect(channels).toHaveLength(allocated);
|
||||
release();
|
||||
@@ -181,23 +181,6 @@ 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", () => {
|
||||
const search = (id: number) => ({
|
||||
id,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { clearLineTokens } from "@/editor/lineTokens";
|
||||
import { showPendingHint } from "@/editor/lsp/hint";
|
||||
import { incompleteNote } from "@/editor/lsp/lsp";
|
||||
import { commands, IpcError, isCancelled, nextRequestId } from "@/platform/ipc";
|
||||
import { tx } from "@/platform/i18n";
|
||||
import { shortcutLabel } from "@/platform/keymap";
|
||||
import { synchronizeDocumentIndexes, toolWindowService } from "@/workbench/contributions";
|
||||
import {
|
||||
@@ -231,7 +230,7 @@ export async function fetchUsages(
|
||||
let note: string | null = null;
|
||||
let librariesSkipped = false;
|
||||
await caretSynced(c);
|
||||
const indexed = await synchronizeDocumentIndexes(c.path, "usages");
|
||||
await synchronizeDocumentIndexes(c.path);
|
||||
if (c.view && c.view.state.doc !== before) throw new IpcError({ kind: "cancelled", message: "", data: null });
|
||||
let settled = false;
|
||||
const channel = new Channel<References>();
|
||||
@@ -269,7 +268,6 @@ export async function fetchUsages(
|
||||
locs = fb.locs;
|
||||
note = fb.note;
|
||||
}
|
||||
if (!indexed) note ??= tx("May be incomplete: plugin indexes are still updating");
|
||||
const files = await readManyFileLines(pathsToRead(locs, classified));
|
||||
return { groups: groupUsages(locs, files, classified), note, librariesSkipped };
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { EmptyState, IconButton, showContextMenu, T, type MenuItem } from "@/ui"
|
||||
import { formatBinding, hasPrimaryModifier, hostBinding, registerKeyClaim } from "@/platform/keymap";
|
||||
import { tx } from "@/platform/i18n";
|
||||
import { useWorkflowSettings } from "@/platform/workflowSettings";
|
||||
import { useConsoleFont } from "@/platform/consoleFont";
|
||||
import { spanStyle } from "@/ui/ansi";
|
||||
import { ConsoleFindBar } from "@/ui/ConsoleFindBar";
|
||||
import { DEFAULT_FIND_OPTIONS, findInLines, type FindOptions, type FindResult } from "@/ui/consoleFind";
|
||||
@@ -117,6 +118,10 @@ export function DebugConsole({ session, interactive = true }: { session: DebugSe
|
||||
const [follow, setFollow] = useState(true);
|
||||
const stick = useRef(true);
|
||||
stick.current = follow;
|
||||
// The debug console uses the same font as the Run console.
|
||||
const consoleFont = useConsoleFont();
|
||||
const { rowHeight } = consoleFont;
|
||||
const fontStyle = { fontFamily: consoleFont.family, fontSize: `${consoleFont.size}px` };
|
||||
// Settings › Console › Use soft wraps is the default; the toggle overrides it for this console.
|
||||
const defaultWrap = useWorkflowSettings((s) => s.console.softWrap);
|
||||
const [wrapOverride, setWrapOverride] = useState<boolean | null>(null);
|
||||
@@ -140,13 +145,13 @@ export function DebugConsole({ session, interactive = true }: { session: DebugSe
|
||||
const virtualizer = useVirtualizer({
|
||||
count: lines?.length ?? 0,
|
||||
getScrollElement: () => scroller.current,
|
||||
estimateSize: () => 18,
|
||||
estimateSize: () => rowHeight,
|
||||
overscan: 30,
|
||||
getItemKey: (i) => lines?.[i]?.id ?? i,
|
||||
});
|
||||
|
||||
// Wrapping changes every row's height; the end is pinned again below.
|
||||
useLayoutEffect(() => virtualizer.measure?.(), [softWrap, virtualizer]);
|
||||
useLayoutEffect(() => virtualizer.measure?.(), [softWrap, rowHeight, virtualizer]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const el = scroller.current;
|
||||
@@ -429,6 +434,7 @@ export function DebugConsole({ session, interactive = true }: { session: DebugSe
|
||||
<div
|
||||
ref={scroller}
|
||||
className={clsx(styles.consoleOutput, !softWrap && styles.noWrap)}
|
||||
style={{ ...fontStyle, lineHeight: `${rowHeight}px` }}
|
||||
onScroll={(e) => {
|
||||
if (programmatic.current) return;
|
||||
const el = e.currentTarget;
|
||||
@@ -465,12 +471,13 @@ export function DebugConsole({ session, interactive = true }: { session: DebugSe
|
||||
<div className={styles.repl}>
|
||||
{interactive && (
|
||||
<>
|
||||
<span className={styles.prompt} aria-hidden>
|
||||
<span className={styles.prompt} style={fontStyle} aria-hidden>
|
||||
›
|
||||
</span>
|
||||
<input
|
||||
ref={inputRef}
|
||||
className={styles.evalInput}
|
||||
style={fontStyle}
|
||||
onFocus={() => setConsoleHistoryBrowser(browseHistory)}
|
||||
onBlur={() => {
|
||||
setConsoleHistoryBrowser(null);
|
||||
|
||||
@@ -135,7 +135,7 @@ it("flushes pending changes before usages or rename without waiting for the debo
|
||||
await vi.advanceTimersByTimeAsync(250);
|
||||
mocks.index.mockClear();
|
||||
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
||||
await synchronizeDocumentIndexes("/project/A.xml", "refactor");
|
||||
await synchronizeDocumentIndexes("/project/A.xml");
|
||||
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);
|
||||
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.java"], removed: [] });
|
||||
let settled = false;
|
||||
const waiting = synchronizeDocumentIndexes("/project/A.xml", "refactor").then(() => {
|
||||
const waiting = synchronizeDocumentIndexes("/project/A.xml").then(() => {
|
||||
settled = true;
|
||||
});
|
||||
await Promise.resolve();
|
||||
@@ -168,33 +168,7 @@ it("refuses refactoring if a queued index batch failed", async () => {
|
||||
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 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);
|
||||
await expect(synchronizeDocumentIndexes("/project/A.xml")).rejects.toThrow("Plugin indexes are still updating");
|
||||
});
|
||||
|
||||
it.each(["/project/notes.txt", "/external/Other.xml", "jdt://contents/Other.xml"])(
|
||||
@@ -205,13 +179,13 @@ it.each(["/project/notes.txt", "/external/Other.xml", "jdt://contents/Other.xml"
|
||||
mocks.index.mockImplementationOnce(() => new Promise(() => {}));
|
||||
mocks.events.get("fsChanged")?.({ created: [], modified: ["/project/A.xml"], removed: [] });
|
||||
await vi.advanceTimersByTimeAsync(40);
|
||||
await synchronizeDocumentIndexes(path, "refactor");
|
||||
await synchronizeDocumentIndexes(path);
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps unrelated language-server queries available when plugin discovery fails", async () => {
|
||||
mocks.files.mockRejectedValue(new Error("Plugin file inventory is unavailable"));
|
||||
off = startPluginNavigation();
|
||||
await expect(synchronizeDocumentIndexes("/project/notes.txt", "refactor")).resolves.toBe(true);
|
||||
await expect(synchronizeDocumentIndexes("/project/notes.txt")).resolves.toBeUndefined();
|
||||
expect(mocks.index).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -22,12 +22,9 @@ export function startPluginNavigation(): () => void {
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
let pending = 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>();
|
||||
let discovered = false;
|
||||
let discovering = false;
|
||||
let discoveryFailed = false;
|
||||
const known = new Set<string>();
|
||||
let signature = "";
|
||||
let reportedError = false;
|
||||
@@ -82,22 +79,13 @@ export function startPluginNavigation(): () => void {
|
||||
await call(commands.pluginNavigationIndex(paths, id));
|
||||
if (at === generation) {
|
||||
paths.forEach((p) => {
|
||||
failed.delete(p);
|
||||
if (!pending.has(p)) dirty.delete(p);
|
||||
});
|
||||
notify();
|
||||
}
|
||||
} catch (error) {
|
||||
if (at === generation && isCancelled(error)) paths.forEach((p) => pending.add(p));
|
||||
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);
|
||||
}
|
||||
else if (at === generation) report(error);
|
||||
} finally {
|
||||
request = null;
|
||||
running = false;
|
||||
@@ -132,14 +120,12 @@ export function startPluginNavigation(): () => void {
|
||||
if (at !== generation) return;
|
||||
fileExtensions = new Set(inventory.extensions);
|
||||
discovered = !inventory.indexing;
|
||||
discoveryFailed = false;
|
||||
const fresh = inventory.paths.filter((p) => !known.has(p));
|
||||
inventory.paths.forEach((p) => known.add(p));
|
||||
queue(fresh);
|
||||
queue([...views.values()], true);
|
||||
} catch (error) {
|
||||
discovered = false;
|
||||
discoveryFailed = true;
|
||||
report(error);
|
||||
} finally {
|
||||
if (at === generation) discovering = false;
|
||||
@@ -153,11 +139,9 @@ export function startPluginNavigation(): () => void {
|
||||
clearTimeout(timer);
|
||||
pending.clear();
|
||||
dirty.clear();
|
||||
failed.clear();
|
||||
known.clear();
|
||||
discovered = false;
|
||||
discovering = false;
|
||||
discoveryFailed = false;
|
||||
discovery = null;
|
||||
reportedError = false;
|
||||
notify();
|
||||
@@ -167,41 +151,32 @@ export function startPluginNavigation(): () => void {
|
||||
.catch(() => {});
|
||||
}
|
||||
const offs = [
|
||||
registerDocumentIndexSync(async (path, purpose) => {
|
||||
registerDocumentIndexSync(async (path) => {
|
||||
if (!ready()) return;
|
||||
const root = useProject.getState().project!.root;
|
||||
if (!path.startsWith(root.endsWith("/") ? root : `${root}/`)) return;
|
||||
const at = generation;
|
||||
await discover();
|
||||
if (!eligible(path)) return;
|
||||
if (failed.size) queue([...failed]);
|
||||
while (ready() && at === generation && (pending.size || flushing)) {
|
||||
clearTimeout(timer);
|
||||
await flush();
|
||||
}
|
||||
const complete = at === generation && discovered && !dirty.size && !failed.size;
|
||||
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`);
|
||||
if (at !== generation || !discovered || dirty.size) {
|
||||
throw new Error(tx`Plugin indexes are still updating; try again shortly`);
|
||||
}
|
||||
throw new Error(tx`Plugin indexes are still updating; try again shortly`);
|
||||
}),
|
||||
pluginsBackend().onEvent((event) => {
|
||||
if (event.type === "documentIndexesChanged") reset();
|
||||
}),
|
||||
registerNavigationProvider(async (view, offset) => {
|
||||
const doc = docOf(view);
|
||||
if (!ready() || !doc || doc.virtual || !eligible(doc.path) || dirty.has(doc.path) || failed.has(doc.path))
|
||||
return null;
|
||||
if (!ready() || !doc || doc.virtual || !eligible(doc.path) || dirty.has(doc.path)) return null;
|
||||
const before = view.state.doc;
|
||||
await whenSynced(view);
|
||||
const result = await call(commands.pluginNavigationGoto(doc.doc, offsetToPos(before, offset)));
|
||||
if (!result || view.state.doc !== before) return null;
|
||||
return {
|
||||
...rangeToOffsets(before, result.range),
|
||||
targets: result.targets.filter((t) => !dirty.has(t.path) && !failed.has(t.path)),
|
||||
};
|
||||
return { ...rangeToOffsets(before, result.range), targets: result.targets.filter((t) => !dirty.has(t.path)) };
|
||||
}),
|
||||
registerEditorContribution({
|
||||
id: "plugins.navigation",
|
||||
|
||||
@@ -14,8 +14,7 @@ import {
|
||||
import clsx from "clsx";
|
||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||
import { useWorkflowSettings } from "@/platform/workflowSettings";
|
||||
import { useProject } from "@/platform/project";
|
||||
import { resolveMonoFont } from "@/platform/fonts";
|
||||
import { useConsoleFont } from "@/platform/consoleFont";
|
||||
import { Icon, showContextMenu } from "@/ui";
|
||||
import { spanStyle } from "@/ui/ansi";
|
||||
import type { ConsoleLine, ConsoleModel } from "./console";
|
||||
@@ -185,10 +184,8 @@ export function ConsoleView({
|
||||
const patterns = useWorkflowSettings((s) => s.console.foldPatterns);
|
||||
// The Run console uses Tools › Terminal › Font (which can follow Editor › Font),
|
||||
// so the font and fallback configured there apply to process output too.
|
||||
const terminalFont = useWorkflowSettings((s) => s.terminal);
|
||||
const editorFont = useProject((s) => s.settings?.editor);
|
||||
const consoleFont = resolveMonoFont(terminalFont, editorFont);
|
||||
const rowHeight = Math.max(16, Math.round(consoleFont.size * 1.5));
|
||||
const consoleFont = useConsoleFont();
|
||||
const { rowHeight } = consoleFont;
|
||||
/** A row's box: at its virtual offset, and never shorter than one line — a blank line has no text. */
|
||||
const rowStyle = (start: number) => ({
|
||||
transform: `translateY(${start}px)`,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useProject } from "./project";
|
||||
import { resolveMonoFont } from "./fonts";
|
||||
import { useWorkflowSettings } from "./workflowSettings";
|
||||
|
||||
/**
|
||||
* The font of the Run and Debug consoles: Tools › Terminal › Font (which can follow
|
||||
* Editor › Font), with the row height both consoles lay their lines out with.
|
||||
*/
|
||||
export function useConsoleFont(): { family: string; size: number; rowHeight: number } {
|
||||
const terminalFont = useWorkflowSettings((s) => s.terminal);
|
||||
const editorFont = useProject((s) => s.settings?.editor);
|
||||
const { family, size } = resolveMonoFont(terminalFont, editorFont);
|
||||
return { family, size, rowHeight: Math.max(16, Math.round(size * 1.5)) };
|
||||
}
|
||||
@@ -1344,7 +1344,5 @@
|
||||
"Close Agent Task": "Close 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, 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"
|
||||
"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."
|
||||
}
|
||||
@@ -7731,7 +7731,5 @@
|
||||
"Download Remote Files Into Folder": "将远程文件下载到文件夹",
|
||||
"Download Remote Host files into a local folder (arg: { server, paths, target })": "将远程主机中的文件下载到本地文件夹(参数:{ server, paths, target })",
|
||||
"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 语言服务器。编辑器中的问题仍来自语言服务器。",
|
||||
"May be incomplete: plugin indexes are still updating": "可能不完整:插件索引仍在更新",
|
||||
"Plugin indexes could not be updated; see the plugin notification": "插件索引更新失败,详见插件通知"
|
||||
"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 语言服务器。编辑器中的问题仍来自语言服务器。"
|
||||
}
|
||||
@@ -1825,23 +1825,16 @@ export function addContributedFileTemplate(t: import("@/platform/fileTemplates")
|
||||
return () => useContributedFileTemplates.setState((s) => ({ templates: s.templates.filter((x) => x !== t) }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush contributed document indexes before workspace usages or refactoring. A refactoring
|
||||
* 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 {
|
||||
/** Flush contributed document indexes before workspace usages or refactoring. */
|
||||
const documentIndexSync = new Set<(path: string) => Promise<void>>();
|
||||
export function registerDocumentIndexSync(sync: (path: string) => Promise<void>): () => void {
|
||||
documentIndexSync.add(sync);
|
||||
return () => {
|
||||
documentIndexSync.delete(sync);
|
||||
};
|
||||
}
|
||||
export async function synchronizeDocumentIndexes(path: string, purpose: DocumentIndexPurpose): Promise<boolean> {
|
||||
const complete = await Promise.all([...documentIndexSync].map((sync) => sync(path, purpose)));
|
||||
return complete.every((c) => c !== false);
|
||||
export async function synchronizeDocumentIndexes(path: string): Promise<void> {
|
||||
await Promise.all([...documentIndexSync].map((sync) => sync(path)));
|
||||
}
|
||||
|
||||
/** Font zoom for non-CodeMirror surfaces, owned and released by their features. */
|
||||
|
||||
Reference in new issue
Заблокувати користувача