Game Content (Exor) — VS Code language support
Language server for the Exor PTREE/KVP data formats (.ent, .material, .gui, .td,
.ag, .att, .logic, .pu, ...).
Completion, hover, go-to-definition, find-references, rename and diagnostics are driven by
the engine's TypeRegistry export, so they follow the real reflection data rather than a
hand-maintained schema. Hovering a .dds path or a material name shows the texture.
Open the builds directory as the workspace root — data roots and mods are discovered
automatically.
Testing
npm test runs the suites inside a real VS Code (@vscode/test-electron), against the
fixture workspace in src/test/fixtures/workspace. It reuses the VS Code already installed
and links in the installed sumneko.lua, so nothing is downloaded and no real profile is
touched.
npm test
Running against a real game build
The fixture proves a mechanism works. It cannot prove it works in a particular working copy —
the config can be right, the files can be on disk, and the server can still be serving
nothing. GAMECONTENT_TEST_WORKSPACE points the same host at a real build, and
GAMECONTENT_TEST_GREP narrows the run to one suite so nothing writes scratch files into
someone's working copy:
GAMECONTENT_TEST_WORKSPACE=D:/riftbreaker/rbs_monorepo/rbs/builds GAMECONTENT_TEST_GREP="lua probe" npm run test:workspace
test:workspace skips the type-check for a faster loop; use npm test before committing.
Only run read-only suites this way. lua probe is written for it: it opens files the
workspace already has, writes nothing, and reports what the server answers rather than
asserting a verdict on someone's local state. Suites that create scratch documents (the
completion and blueprint-table suites) belong on the fixture.
Writing a test that means something
Two traps this suite has already fallen into, both of which produce a green test that proves
nothing:
- Asking for "any answer". sumneko replies
Workspace loading: 0 / 0 while indexing, and
that is a hover — so waiting for a non-empty hover returns the placeholder and every
assertion after it reads empty. Wait for an answer that is not the placeholder.
- Matching a name against itself. A hover of
(global) AnimationService: unknown contains
the string AnimationService, so asserting the hover "mentions" the symbol passes whether or
not the definitions were ever loaded. Assert that go-to-definition lands inside the
generated defs instead.
The same shape applies to our own server: reuse one scratch URI across cases and VS Code hands
back the cached document, so the server answers against the previous case's text while the
position comes from the new one. Give each case its own file.