Files
Ra3ModXmlExt/test/referenceProvider.test.mjs
T
2026-08-11 02:20:06 +02:00

180 lines
4.7 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { createRequire } from "node:module";
// Minimal vscode shim for the semantic reference provider.
class Position {
constructor(line, character) {
this.line = line;
this.character = character;
}
}
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
}
class Location {
constructor(uri, range) {
this.uri = uri;
this.range = range;
}
}
const Uri = {
file: (p) => ({ fsPath: p }),
};
const require = createRequire(import.meta.url);
const Module = require("module");
const origResolve = Module._resolveFilename;
Module._resolveFilename = function (request, ...args) {
if (request === "vscode") return "vscode-stub";
return origResolve.call(this, request, ...args);
};
require.cache["vscode-stub"] = {
id: "vscode-stub",
filename: "vscode-stub",
loaded: true,
exports: {
Position,
Range,
Location,
Uri,
SymbolKind: {},
DocumentSymbol: class {},
},
};
const { Ra3ReferenceProvider } = require("../out/features/navigation.js");
const { LineMap, parseXml } = require("../out/language/xmlParser.js");
const { extractIndexRecords } = require("../out/indexer/records.js");
const { buildReferenceIndex } = require("../out/indexer/referenceIndex.js");
const FILE = "C:/mod/Data/Units.xml";
const TEXT = `<AssetDeclaration>
<GameObject id="Tank"/>
<ObjectCreationList id="OCL">
<CreateObject>
<CreateObject>Tank</CreateObject>
</CreateObject>
</ObjectCreationList>
</AssetDeclaration>`;
function makeDocument(text = TEXT) {
const lineStarts = [0];
for (let i = 0; i < text.length; i++) {
if (text.charCodeAt(i) === 10) lineStarts.push(i + 1);
}
return {
uri: { fsPath: FILE },
getText: () => text,
offsetAt: (pos) => lineStarts[pos.line] + pos.character,
positionAt: (offset) => {
let lo = 0;
let hi = lineStarts.length - 1;
while (lo < hi) {
const mid = (lo + hi + 1) >> 1;
if (lineStarts[mid] <= offset) lo = mid;
else hi = mid - 1;
}
return new Position(lo, offset - lineStarts[lo]);
},
};
}
function makeScope() {
const parse = parseXml(TEXT);
const lineMap = new LineMap(TEXT);
const records = extractIndexRecords(parse, lineMap, TEXT);
const def = {
type: "GameObject",
id: "Tank",
file: FILE,
line: 2,
origin: "project",
};
const lookup = {
assets: new Map([["GameObject", new Map([["tank", [def]]])]]),
assetsById: new Map([["tank", [def]]]),
};
const references = buildReferenceIndex([{ file: FILE, records }], lookup);
const idx = {
...lookup,
references,
complete: true,
phase: "art",
projectDir: "C:/mod",
sdkDir: "C:/sdk",
defines: new Map(),
files: new Map(),
streams: [],
manifests: new Map(),
sourceCandidates: [],
diagnostics: [],
stats: {},
};
return {
merged: idx,
};
}
function makeWs(scope) {
const parse = parseXml(TEXT);
const lineMap = new LineMap(TEXT);
const indexer = {
readDom: async (path) =>
path === FILE
? { file: { path: FILE }, parse, lineMap, records: null }
: null,
};
return {
isRa3Workspace: () => true,
getScope: async () => scope,
indexer,
indexerForFile: () => indexer,
activeIndexer: () => indexer,
recordsSyncSurfaceFor: () => ({
get index() {
return scope.merged;
},
invalidate: () => {},
scheduleRebuild: () => {},
}),
};
}
test("semantic Find All References excludes the definition even when includeDeclaration is set", async () => {
const scope = makeScope();
const provider = new Ra3ReferenceProvider(makeWs(scope));
const document = makeDocument();
const defLine = TEXT.split("\n")[1];
const defPos = new Position(1, defLine.indexOf('id="') + 4);
const refs = await provider.provideReferences(document, defPos, {
includeDeclaration: true,
}, {});
assert.ok(refs, "references are returned");
assert.equal(refs.length, 1, "only the typed content reference is returned");
assert.equal(refs[0].uri.fsPath, FILE);
assert.equal(refs[0].range.start.line, 4);
assert.equal(
TEXT.split("\n")[4].slice(refs[0].range.start.character, refs[0].range.end.character),
"Tank",
);
});
test("FAR from the reference site itself returns the same result", async () => {
const scope = makeScope();
const provider = new Ra3ReferenceProvider(makeWs(scope));
const document = makeDocument();
const contentLine = TEXT.split("\n")[4];
const contentPos = new Position(4, contentLine.indexOf("Tank") + 1);
const refs = await provider.provideReferences(document, contentPos, {
includeDeclaration: false,
}, {});
assert.equal(refs.length, 1);
assert.equal(refs[0].range.start.line, 4);
});