0.1.1
This commit is contained in:
+72
-5
@@ -3,7 +3,7 @@ import { existsSync } from "node:fs";
|
||||
import { join, dirname } from "node:path";
|
||||
import { CachedDirectoryWalker } from "./indexer/fileScanner";
|
||||
import { ModIndexer } from "./indexer/indexer";
|
||||
import { DocumentCache, ShallowScanCache } from "./indexer/caches";
|
||||
import { DocumentCache, IncludeResolveCache, IndexRecordsCache } from "./indexer/caches";
|
||||
import type { ModIndex } from "./indexer/types";
|
||||
import { readSettings, type ExtensionSettings } from "./settings";
|
||||
|
||||
@@ -20,13 +20,17 @@ export class ModWorkspace {
|
||||
// only re-reads files whose stat changed (crucial for the ~2.6 GB of .w3x
|
||||
// art assets in a project like Corona).
|
||||
private documentCache = new DocumentCache();
|
||||
private shallowCache = new ShallowScanCache();
|
||||
private recordsCache = new IndexRecordsCache();
|
||||
private resolveCache = new IncludeResolveCache();
|
||||
private context: vscode.ExtensionContext;
|
||||
private watchers: vscode.FileSystemWatcher[] = [];
|
||||
private statusBar: vscode.StatusBarItem;
|
||||
private rebuildTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private building = false;
|
||||
private dirty = false;
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
this.context = context;
|
||||
this.settings = readSettings();
|
||||
this.statusBar = vscode.window.createStatusBarItem(
|
||||
vscode.StatusBarAlignment.Left,
|
||||
@@ -57,11 +61,69 @@ export class ModWorkspace {
|
||||
this.statusBar.hide();
|
||||
return;
|
||||
}
|
||||
this.startWatching();
|
||||
this.statusBar.text = "$(sync~spin) RA3 XML: indexing…";
|
||||
this.statusBar.show();
|
||||
await this.rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates cached documents for a path (called by the file watcher and
|
||||
* on document save), so the next rebuild re-reads it instead of trusting
|
||||
* the cached copy.
|
||||
*/
|
||||
invalidate(path: string): void {
|
||||
if (!path) return;
|
||||
this.documentCache.invalidate(path);
|
||||
this.recordsCache.invalidate(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when files are created or deleted: include-resolution results
|
||||
* (which encode file existence) are no longer trustworthy.
|
||||
*/
|
||||
invalidateExistence(): void {
|
||||
this.resolveCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Watches the project, SDK and extra DATA roots for file changes and
|
||||
* invalidates the corresponding cache entries. With caches invalidated
|
||||
* precisely, rebuilds can trust every other cached file and skip per-file
|
||||
* stats (huge win on mechanical drives; Corona rebuild dropped from ~38s
|
||||
* to a few seconds).
|
||||
*/
|
||||
private startWatching(): void {
|
||||
if (!this.projectRoot) return;
|
||||
const roots = new Set([
|
||||
this.projectRoot,
|
||||
this.settings.sdkPath,
|
||||
...this.settings.additionalDataSearchPaths,
|
||||
]);
|
||||
for (const root of roots) {
|
||||
if (!existsSync(root)) continue;
|
||||
try {
|
||||
const watcher = vscode.workspace.createFileSystemWatcher(
|
||||
new vscode.RelativePattern(root, "**/*"),
|
||||
);
|
||||
watcher.onDidCreate((uri) => {
|
||||
this.invalidate(uri.fsPath);
|
||||
this.invalidateExistence();
|
||||
});
|
||||
watcher.onDidChange((uri) => this.invalidate(uri.fsPath));
|
||||
watcher.onDidDelete((uri) => {
|
||||
this.invalidate(uri.fsPath);
|
||||
this.invalidateExistence();
|
||||
});
|
||||
this.watchers.push(watcher);
|
||||
this.context.subscriptions.push(watcher);
|
||||
} catch {
|
||||
// The root may be temporarily unavailable (e.g. removable drive);
|
||||
// indexing still works, just without watcher-based invalidation.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduleRebuild(): void {
|
||||
if (!this.projectRoot) return;
|
||||
if (this.rebuildTimer) clearTimeout(this.rebuildTimer);
|
||||
@@ -70,12 +132,13 @@ export class ModWorkspace {
|
||||
}, REBUILD_DEBOUNCE_MS);
|
||||
}
|
||||
|
||||
async rebuild(): Promise<void> {
|
||||
async rebuild(force = false): Promise<void> {
|
||||
if (!this.projectRoot) return;
|
||||
if (this.building) {
|
||||
this.dirty = true;
|
||||
return;
|
||||
}
|
||||
if (force) this.resolveCache.clear();
|
||||
this.building = true;
|
||||
this.settings = readSettings();
|
||||
try {
|
||||
@@ -88,7 +151,11 @@ export class ModWorkspace {
|
||||
additionalDataSearchPaths: this.settings.additionalDataSearchPaths,
|
||||
walker: this.walker,
|
||||
documentCache: this.documentCache,
|
||||
shallowCache: this.shallowCache,
|
||||
recordsCache: this.recordsCache,
|
||||
resolveCache: this.resolveCache,
|
||||
// Trust cache entries unless the user explicitly asked for a full
|
||||
// verification (ra3modxml.reindex).
|
||||
trustUnchanged: !force,
|
||||
});
|
||||
const started = Date.now();
|
||||
this.index = await indexer.build();
|
||||
@@ -121,7 +188,7 @@ export class ModWorkspace {
|
||||
return {
|
||||
file: { path, stat: null },
|
||||
parse,
|
||||
shallow: null,
|
||||
records: null,
|
||||
lineMap: new LineMap(text),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user