This commit is contained in:
2026-08-11 02:20:06 +02:00
parent 36eaaafa01
commit dbc2c99d8d
45 changed files with 3904 additions and 661 deletions
+107 -27
View File
@@ -17,6 +17,18 @@ class CodeLens {
this.command = command;
}
}
class EventEmitter {
constructor() {
this.listeners = [];
this.event = (listener) => {
this.listeners.push(listener);
return { dispose: () => {} };
};
}
fire() {
for (const listener of this.listeners) listener();
}
}
const require = createRequire(import.meta.url);
const Module = require("module");
@@ -32,6 +44,7 @@ require.cache["vscode-stub"] = {
exports: {
Range,
CodeLens,
EventEmitter,
},
};
@@ -72,6 +85,20 @@ function makeDocument(text = TEXT) {
}
function makeIndex() {
const tankDef = {
type: "GameObject",
id: "TestTank",
file: FILE,
line: 2,
origin: "project",
};
const baseDef = {
type: "GameObject",
id: "BaseVehicle",
file: FILE,
line: 3,
origin: "project",
};
const tankSite = {
file: "C:/mod/Data/Other.xml",
line: 7,
@@ -88,37 +115,33 @@ function makeIndex() {
};
const references = new Map();
references.set(
assetDefKey({
type: "GameObject",
id: "TestTank",
file: FILE,
line: 2,
}),
assetDefKey(tankDef),
[tankSite, secondSite],
);
references.set(
assetDefKey({
type: "GameObject",
id: "BaseVehicle",
file: FILE,
line: 3,
}),
[],
);
references.set(assetDefKey(baseDef), []);
return {
references,
assets: new Map(),
assetsById: new Map([
["testtank", [tankDef]],
["basevehicle", [baseDef]],
]),
stats: { indexedFiles: 10 },
sdkDir: SDK,
projectDir: PROJECT,
};
}
test("CodeLens shows counts on reference-target types only, including zero", () => {
test("CodeLens shows counts on reference-target types only, including zero", async () => {
const idx = makeIndex();
const provider = new Ra3CodeLensProvider({
isRa3Workspace: () => true,
index: makeIndex(),
index: idx,
getCodeLensScope: async () => ({ merged: idx }),
recordsSyncSurfaceFor: () => ({}),
log: () => {},
});
const lenses = provider.provideCodeLenses(makeDocument(), {});
const lenses = await provider.provideCodeLenses(makeDocument(), {});
assert.equal(lenses.length, 2, "no lens for auto-registered CameraSettings");
const tank = lenses.find((l) => l.command.arguments[0].id === "TestTank");
@@ -138,21 +161,69 @@ test("CodeLens shows counts on reference-target types only, including zero", ()
assert.ok(tank.range.start.character < tank.range.end.character);
});
test("CodeLens returns nothing without a workspace or index", () => {
test("CodeLens returns nothing without a workspace or index", async () => {
const noWorkspace = new Ra3CodeLensProvider({
isRa3Workspace: () => false,
index: makeIndex(),
});
assert.deepEqual(noWorkspace.provideCodeLenses(makeDocument(), {}), []);
assert.deepEqual(await noWorkspace.provideCodeLenses(makeDocument(), {}), []);
const noIndex = new Ra3CodeLensProvider({
isRa3Workspace: () => true,
index: null,
getCodeLensScope: async () => ({ merged: null }),
recordsSyncSurfaceFor: () => ({}),
log: () => {},
});
assert.deepEqual(noIndex.provideCodeLenses(makeDocument(), {}), []);
assert.deepEqual(await noIndex.provideCodeLenses(makeDocument(), {}), []);
});
test("CodeLens counts references attached to a manifest definition with the same SageXml source", () => {
test("CodeLens hides lenses before the first global snapshot", async () => {
const localOnly = {
complete: false,
stats: { indexedFiles: 0 },
references: new Map(),
};
const logs = [];
const provider = new Ra3CodeLensProvider({
isRa3Workspace: () => true,
getCodeLensScope: async () => ({ merged: localOnly }),
recordsSyncSurfaceFor: () => ({}),
log: (m) => logs.push(m),
});
assert.deepEqual(await provider.provideCodeLenses(makeDocument(), {}), []);
assert.deepEqual(await provider.provideCodeLenses(makeDocument(), {}), []);
assert.equal(
logs.filter((m) => m.includes("suppressed")).length,
1,
"suppression is logged once per document",
);
provider.resetSuppressionLog();
await provider.provideCodeLenses(makeDocument(), {});
assert.equal(
logs.filter((m) => m.includes("suppressed")).length,
2,
"reset allows re-logging after a new snapshot",
);
});
test("CodeLens refresh fires onDidChangeCodeLenses", () => {
const provider = new Ra3CodeLensProvider({
isRa3Workspace: () => true,
getCodeLensScope: async () => ({ merged: null }),
recordsSyncSurfaceFor: () => ({}),
log: () => {},
});
let fired = 0;
const subscription = provider.onDidChangeCodeLenses(() => {
fired++;
});
provider.refresh();
assert.equal(fired, 1);
subscription.dispose();
});
test("CodeLens counts references attached to a manifest definition with the same SageXml source", async () => {
const manifestDef = {
type: "GameObject",
id: "TestTank",
@@ -175,30 +246,39 @@ test("CodeLens counts references attached to a manifest definition with the same
assets: new Map([
["GameObject", new Map([["testtank", [manifestDef]]])],
]),
assetsById: new Map([["testtank", [manifestDef]]]),
stats: { indexedFiles: 10 },
sdkDir: SDK,
projectDir: PROJECT,
};
const provider = new Ra3CodeLensProvider({
isRa3Workspace: () => true,
index: idx,
getCodeLensScope: async () => ({ merged: idx }),
recordsSyncSurfaceFor: () => ({}),
log: () => {},
});
const lenses = provider.provideCodeLenses(makeDocument(), {});
const lenses = await provider.provideCodeLenses(makeDocument(), {});
const tank = lenses.find((l) => l.command.arguments[0].id === "TestTank");
assert.ok(tank, "lens is shown for the SageXml-backed definition");
assert.equal(tank.command.title, "1 reference");
});
test("CodeLens schedules a targeted rebuild when the open document desyncs from the snapshot", () => {
test("CodeLens schedules a targeted rebuild when the open document desyncs from the snapshot", async () => {
const idx = makeIndex();
idx.recordsHashes = new Map([[normKey(FILE), "stale-hash"]]);
const calls = [];
const provider = new Ra3CodeLensProvider({
const ws = {
isRa3Workspace: () => true,
index: idx,
invalidate: (p) => calls.push(["invalidate", p]),
scheduleRebuild: (r) => calls.push(["schedule", r]),
});
provider.provideCodeLenses(makeDocument(), {});
getCodeLensScope: async () => ({ merged: idx }),
recordsSyncSurfaceFor: () => ws,
log: () => {},
};
const provider = new Ra3CodeLensProvider(ws);
await provider.provideCodeLenses(makeDocument(), {});
assert.ok(
calls.some(([kind]) => kind === "invalidate"),
"the stale file is invalidated",