@px-lsp/server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +334 -0
  3. package/THIRD-PARTY-NOTICES.md +83 -0
  4. package/data/ck3/dataTypes.json +2195 -0
  5. package/data/ck3/data_types/data_types_common.txt +2040 -0
  6. package/data/ck3/data_types/data_types_gui.txt +5264 -0
  7. package/data/ck3/data_types/data_types_internalclausewitzgui.txt +14843 -0
  8. package/data/ck3/data_types/data_types_script.txt +4251 -0
  9. package/data/ck3/data_types/data_types_uncategorized.txt +109984 -0
  10. package/data/ck3/freqs.json +1 -0
  11. package/data/ck3/guiSchema.json +6344 -0
  12. package/data/ck3/script_docs/effects.log +16059 -0
  13. package/data/ck3/script_docs/event_targets.log +2098 -0
  14. package/data/ck3/script_docs/modifiers.log +2228 -0
  15. package/data/ck3/script_docs/on_actions.log +5275 -0
  16. package/data/ck3/script_docs/triggers.log +11991 -0
  17. package/data/ck3/structures.json +9743 -0
  18. package/data/ck3/wikidocs/ATTRIBUTION.md +18 -0
  19. package/data/ck3/wikidocs/Data_types.md +2568 -0
  20. package/data/ck3/wikidocs/Effects_list.md +1176 -0
  21. package/data/ck3/wikidocs/Scopes_list.md +341 -0
  22. package/data/ck3/wikidocs/Triggers_list.md +1097 -0
  23. package/data/eu5/data_types/data_types_common.txt +2087 -0
  24. package/data/eu5/data_types/data_types_gui.txt +6732 -0
  25. package/data/eu5/data_types/data_types_internalclausewitzgui.txt +19276 -0
  26. package/data/eu5/data_types/data_types_script.txt +5688 -0
  27. package/data/eu5/data_types/data_types_uncategorized.txt +135569 -0
  28. package/data/vic3/data_types/data_types_common.txt +2021 -0
  29. package/data/vic3/data_types/data_types_gui.txt +5592 -0
  30. package/data/vic3/data_types/data_types_internalclausewitzgui.txt +17304 -0
  31. package/data/vic3/data_types/data_types_script.txt +2817 -0
  32. package/data/vic3/data_types/data_types_uncategorized.txt +84354 -0
  33. package/data/vic3/freqs.json +1 -0
  34. package/data/vic3/guiSchema.json +5578 -0
  35. package/data/vic3/script_docs/effects.log +38135 -0
  36. package/data/vic3/script_docs/event_targets.log +2028 -0
  37. package/data/vic3/script_docs/modifiers.log +18954 -0
  38. package/data/vic3/script_docs/on_actions.log +1561 -0
  39. package/data/vic3/script_docs/triggers.log +15738 -0
  40. package/data/vic3/structures.json +10189 -0
  41. package/dist/server.js +63668 -0
  42. package/media/px-lsp.svg +12 -0
  43. package/package.json +50 -0
  44. package/src/clientMode.ts +60 -0
  45. package/src/coa/coa.ts +184 -0
  46. package/src/coa/coaParse.ts +267 -0
  47. package/src/context.ts +78 -0
  48. package/src/contextKeywords.ts +224 -0
  49. package/src/data/dataBindingMacros.ts +82 -0
  50. package/src/data/dataFnDocs.ts +152 -0
  51. package/src/data/dataFnUsage.ts +431 -0
  52. package/src/data/dataTypes.ts +279 -0
  53. package/src/data/defines.ts +123 -0
  54. package/src/data/docsParser.ts +453 -0
  55. package/src/data/keywordDocs.ts +98 -0
  56. package/src/data/modifierTemplates.ts +143 -0
  57. package/src/data/textFormatting.ts +165 -0
  58. package/src/data/wikiDocs.ts +187 -0
  59. package/src/dds/decoder.ts +1007 -0
  60. package/src/dds/encode.ts +235 -0
  61. package/src/dds/index.ts +58 -0
  62. package/src/dds/png.ts +96 -0
  63. package/src/dds/tga.ts +62 -0
  64. package/src/documents.ts +35 -0
  65. package/src/features/assetPaths.ts +169 -0
  66. package/src/features/codeActions.ts +148 -0
  67. package/src/features/colors.ts +244 -0
  68. package/src/features/completion.ts +961 -0
  69. package/src/features/datafunction.ts +729 -0
  70. package/src/features/definition.ts +84 -0
  71. package/src/features/diagnostics.ts +244 -0
  72. package/src/features/folding.ts +106 -0
  73. package/src/features/formatting.ts +60 -0
  74. package/src/features/guiLanguage.ts +366 -0
  75. package/src/features/guiNavigation.ts +140 -0
  76. package/src/features/guiTree.ts +97 -0
  77. package/src/features/hover.ts +817 -0
  78. package/src/features/hoverRender.ts +222 -0
  79. package/src/features/inlayHints.ts +147 -0
  80. package/src/features/locFormatting.ts +127 -0
  81. package/src/features/references.ts +70 -0
  82. package/src/features/rename.ts +135 -0
  83. package/src/features/scopeAt.ts +65 -0
  84. package/src/features/semanticTokens.ts +188 -0
  85. package/src/features/signatureHelp.ts +72 -0
  86. package/src/features/symbols.ts +241 -0
  87. package/src/features/textureHover.ts +143 -0
  88. package/src/features/workspaceSymbols.ts +69 -0
  89. package/src/games/active.ts +19 -0
  90. package/src/games/ck3/ambientScopes.ts +273 -0
  91. package/src/games/ck3/index.ts +38 -0
  92. package/src/games/ck3/meta.ts +28 -0
  93. package/src/games/ck3/modifierPlaceholders.ts +61 -0
  94. package/src/games/ck3/saveSchema.ts +134 -0
  95. package/src/games/ck3/scaffolds.ts +197 -0
  96. package/src/games/ck3/schema.ts +422 -0
  97. package/src/games/ck3/structures.ts +887 -0
  98. package/src/games/eu5/index.ts +75 -0
  99. package/src/games/eu5/meta.ts +44 -0
  100. package/src/games/eu5/scaffolds.ts +49 -0
  101. package/src/games/eu5/schema.generated.ts +1043 -0
  102. package/src/games/jomini/variables.ts +134 -0
  103. package/src/games/profile.ts +205 -0
  104. package/src/games/registry.ts +27 -0
  105. package/src/games/vic3/index.ts +52 -0
  106. package/src/games/vic3/meta.ts +55 -0
  107. package/src/games/vic3/saveSchema.ts +77 -0
  108. package/src/games/vic3/scaffolds.ts +135 -0
  109. package/src/games/vic3/schema.ts +650 -0
  110. package/src/games/vic3/structures.ts +33 -0
  111. package/src/gui/anchorSpec.ts +66 -0
  112. package/src/gui/declMarkers.ts +30 -0
  113. package/src/gui/fillGeometry.ts +101 -0
  114. package/src/gui/guiDefs.ts +386 -0
  115. package/src/gui/guiDependencies.ts +352 -0
  116. package/src/gui/guiLinks.ts +64 -0
  117. package/src/gui/layoutEngine.ts +1998 -0
  118. package/src/gui/layoutService.ts +221 -0
  119. package/src/gui/measuredMetrics.ts +21 -0
  120. package/src/gui/previewService.ts +89 -0
  121. package/src/gui/saveSchema.ts +220 -0
  122. package/src/gui/saveValues.ts +399 -0
  123. package/src/gui/saveZip.ts +60 -0
  124. package/src/gui/sourceEdit.ts +535 -0
  125. package/src/gui/sourceEditService.ts +439 -0
  126. package/src/gui/sourceModel.ts +603 -0
  127. package/src/gui/textResolve.ts +145 -0
  128. package/src/gui/textureInfo.ts +106 -0
  129. package/src/gui/vocabulary.ts +149 -0
  130. package/src/gui/widgetEdit.ts +52 -0
  131. package/src/gui/widgetInfo.ts +245 -0
  132. package/src/index/docComments.ts +103 -0
  133. package/src/index/extract.ts +252 -0
  134. package/src/index/indexer.ts +369 -0
  135. package/src/index/intern.ts +101 -0
  136. package/src/index/lazyRefs.ts +145 -0
  137. package/src/index/modOrigin.ts +69 -0
  138. package/src/index/references.ts +534 -0
  139. package/src/overview/dependencies.ts +240 -0
  140. package/src/overview/eventBanner.ts +95 -0
  141. package/src/overview/eventDetail.ts +482 -0
  142. package/src/overview/eventGraph.ts +617 -0
  143. package/src/overview/eventVocabulary.ts +214 -0
  144. package/src/overview/locCoverage.ts +138 -0
  145. package/src/overview/modOverview.ts +29 -0
  146. package/src/overview/overrides.ts +89 -0
  147. package/src/parseCache.ts +81 -0
  148. package/src/parser/cst.ts +257 -0
  149. package/src/parser/encoding.ts +106 -0
  150. package/src/parser/index.ts +7 -0
  151. package/src/parser/lexer.ts +245 -0
  152. package/src/parser/locParser.ts +276 -0
  153. package/src/parser/parser.ts +360 -0
  154. package/src/schema/freqs.ts +70 -0
  155. package/src/schema/loader.ts +113 -0
  156. package/src/schema/types.ts +142 -0
  157. package/src/scopes/inference.ts +478 -0
  158. package/src/scopes/model.ts +148 -0
  159. package/src/scopes/varTypes.ts +290 -0
  160. package/src/server.ts +1894 -0
  161. package/src/serverData.ts +98 -0
  162. package/src/structure.ts +56 -0
  163. package/src/wordAt.ts +49 -0
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Variable value-type resolution: what scope does `var:x` produce?
3
+ *
4
+ * Set-sites indexed by references.ts carry the raw value expression
5
+ * (`set_variable = { name = x value = <expr> }` → Definition.value = expr).
6
+ * This module resolves those expressions STATICALLY — literals, flags and
7
+ * link chains anchored at root or a global data link — and merges the types
8
+ * across all set-sites of a name. Anything anchored in a runtime scope
9
+ * (`scope:…`, `this`, bare links) stays unknown: annotate, never guess (AD-5).
10
+ *
11
+ * The full map is rebuilt lazily once per index revision (variables only —
12
+ * cheap even for AGOT-sized mods) and consumed by scope inference
13
+ * (`ctx.varTypes`), hover and completion detail.
14
+ *
15
+ * No `vscode` imports: unit-tested in plain Node.
16
+ */
17
+ import type { Definition, Reference } from "@px-lsp/protocol/types";
18
+ import type { SchemaEntry } from "../schema/types";
19
+ import type { ServerData } from "../serverData";
20
+ import { resolveKeyChainScopes, type InferenceContext } from "./inference";
21
+ import type { Scope, ScopeModel } from "./model";
22
+
23
+ const VAR_KIND_PREFIX: Record<string, string> = {
24
+ variable: "var",
25
+ local_variable: "local_var",
26
+ global_variable: "global_var",
27
+ };
28
+
29
+ /** Variable-list item types, keyed `var:x`-style by the LIST's storage class. */
30
+ const LIST_KIND_PREFIX: Record<string, string> = {
31
+ variable_list: "var",
32
+ local_variable_list: "local_var",
33
+ global_variable_list: "global_var",
34
+ };
35
+
36
+ const NUMBER = /^-?\d+(?:\.\d+)?$/;
37
+
38
+ export interface VariableTypeInfo {
39
+ /** `var:x` / `local_var:x` / `global_var:x` → merged value types (null = unknown). */
40
+ types: Map<string, Set<Scope> | null>;
41
+ /** `var:x`-keyed ITEM types of variable lists (for in-list iterator hovers). */
42
+ listItemTypes: Map<string, Set<Scope> | null>;
43
+ /** Plain-named ITEM types of ad-hoc lists (add_to_list/add_to_temporary_list
44
+ * sites): the set-site's enclosing key chain resolved statically. */
45
+ adhocListItemTypes: Map<string, Set<Scope> | null>;
46
+ /** Saved-scope types merged across ALL indexed save sites of the workspace
47
+ * mods — the cross-file fallback when a scope: name is not saved in the
48
+ * current file. Sites carry a discriminated hint in Definition.value
49
+ * (index/references.ts): "chain:" key chains, "expr:" value expressions,
50
+ * "type:value" script-value saves. */
51
+ savedScopeTypes: Map<string, Set<Scope> | null>;
52
+ }
53
+
54
+ interface Cache {
55
+ revision: number;
56
+ info: VariableTypeInfo;
57
+ }
58
+
59
+ const cache = new WeakMap<ServerData, Cache>();
60
+
61
+ /**
62
+ * PdxDoc `@scope` tag of a definition (`# @scope character` above a scripted
63
+ * effect/trigger/value): the calling scope the author declared. Null when the
64
+ * definition carries no tag.
65
+ */
66
+ export function defScopeTag(data: ServerData, name: string): Set<Scope> | null {
67
+ for (const def of data.index.lookup(name)) {
68
+ const tag = def.tags?.find((t) => t.tag === "scope");
69
+ if (tag && tag.text.trim()) {
70
+ const scopes = tag.text
71
+ .split(/[|,\s/]+/)
72
+ .map((x) => x.trim().toLowerCase())
73
+ .filter((x) => x !== "");
74
+ if (scopes.length > 0) return new Set(scopes);
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+
80
+ /**
81
+ * The one canonical InferenceContext for a file. Every feature (completion,
82
+ * hover, inlay hints, audit script) MUST build its context through here:
83
+ * getSavedScopes caches its result per document version, so callers passing
84
+ * divergent contexts would make hover/completion results depend on which
85
+ * request happened to arrive first.
86
+ */
87
+ export function inferenceContextFor(data: ServerData, entry: SchemaEntry | null): InferenceContext {
88
+ const varInfo = variableTypes(data, data.rootScopesForFile);
89
+ return {
90
+ entry,
91
+ onActionScopes: data.onActionScopes,
92
+ varTypes: varInfo.types,
93
+ varListItemTypes: varInfo.listItemTypes,
94
+ adhocListItemTypes: varInfo.adhocListItemTypes,
95
+ savedScopeTypes: varInfo.savedScopeTypes,
96
+ callSiteScopes: callSiteScopes(data, data.rootScopesForFile),
97
+ defScopeTag: (name) => defScopeTag(data, name),
98
+ };
99
+ }
100
+
101
+ interface CallSiteCache {
102
+ revision: string;
103
+ map: Map<string, Set<Scope>>;
104
+ }
105
+
106
+ const callSiteCache = new WeakMap<ServerData, CallSiteCache>();
107
+
108
+ /**
109
+ * Calling scopes of scripted effects/triggers/modifiers aggregated from their
110
+ * indexed call sites (rebuilt when either index changes). The root-scope
111
+ * fallback for definitions without a PdxDoc `@scope` tag.
112
+ */
113
+ export function callSiteScopes(
114
+ data: ServerData,
115
+ rootScopesForFile: (file: string) => Set<Scope> | null
116
+ ): Map<string, Set<Scope>> {
117
+ const revision = `${data.index.revision}:${data.refIndex.revision}`;
118
+ const cached = callSiteCache.get(data);
119
+ if (cached && cached.revision === revision) return cached.map;
120
+ const map = buildCallSiteScopes(data.refIndex.all(), data.scopeModel, rootScopesForFile);
121
+ callSiteCache.set(data, { revision, map });
122
+ return map;
123
+ }
124
+
125
+ /**
126
+ * Union of the statically resolved scopes at every call site of a name.
127
+ * Unresolved sites (unknown-root files, dynamic anchors) contribute NOTHING —
128
+ * unlike the variable/list maps this does not poison, because an unresolved
129
+ * call site carries no information about the calling scope. One level only:
130
+ * calls inside other untyped scripted effects stay unresolved (no transitive
131
+ * closure). Ranking/annotation input only, never a diagnostic (AD-5).
132
+ */
133
+ export function buildCallSiteScopes(
134
+ refs: Iterable<Reference>,
135
+ model: ScopeModel,
136
+ rootScopesForFile: (file: string) => Set<Scope> | null
137
+ ): Map<string, Set<Scope>> {
138
+ const map = new Map<string, Set<Scope>>();
139
+ for (const ref of refs) {
140
+ if (!ref.call || ref.chain === undefined) continue;
141
+ const resolved = resolveKeyChainScopes(ref.chain.split("."), model, rootScopesForFile(ref.file));
142
+ if (!resolved || resolved.size === 0) continue;
143
+ const prev = map.get(ref.name);
144
+ if (prev) for (const s of resolved) prev.add(s);
145
+ else map.set(ref.name, new Set(resolved));
146
+ }
147
+ return map;
148
+ }
149
+
150
+ /** The variable type map for the current index revision (cached). */
151
+ export function variableTypes(
152
+ data: ServerData,
153
+ rootScopesForFile: (file: string) => Set<Scope> | null
154
+ ): VariableTypeInfo {
155
+ const cached = cache.get(data);
156
+ if (cached && cached.revision === data.index.revision) return cached.info;
157
+ const info = buildVariableTypes(
158
+ data.index.entries(
159
+ (d) =>
160
+ d.kind in VAR_KIND_PREFIX ||
161
+ d.kind in LIST_KIND_PREFIX ||
162
+ d.kind === "list" ||
163
+ d.kind === "saved_scope"
164
+ ),
165
+ data.scopeModel,
166
+ rootScopesForFile
167
+ );
168
+ cache.set(data, { revision: data.index.revision, info });
169
+ return info;
170
+ }
171
+
172
+ export function buildVariableTypes(
173
+ defs: Iterable<Definition>,
174
+ model: ScopeModel,
175
+ rootScopesForFile: (file: string) => Set<Scope> | null
176
+ ): VariableTypeInfo {
177
+ const types = new Map<string, Set<Scope> | null>();
178
+ const listItemTypes = new Map<string, Set<Scope> | null>();
179
+ const adhocListItemTypes = new Map<string, Set<Scope> | null>();
180
+ const savedScopeTypes = new Map<string, Set<Scope> | null>();
181
+ const merge = (map: Map<string, Set<Scope> | null>, key: string, resolved: Set<Scope> | null) => {
182
+ const prev = map.get(key);
183
+ if (prev === undefined) {
184
+ map.set(key, resolved ? new Set(resolved) : null);
185
+ } else if (prev === null || resolved === null) {
186
+ map.set(key, null);
187
+ } else {
188
+ for (const s of resolved) prev.add(s);
189
+ }
190
+ };
191
+ for (const def of defs) {
192
+ // Ad-hoc lists: def.value is the set-site's enclosing key chain (dotted,
193
+ // outermost first — see index/references.ts), resolved from the file root.
194
+ if (def.kind === "list") {
195
+ if (def.value === undefined) continue;
196
+ const resolved = resolveKeyChainScopes(def.value.split("."), model, rootScopesForFile(def.file));
197
+ merge(adhocListItemTypes, def.name, resolved);
198
+ continue;
199
+ }
200
+ // Saved scopes: typed per save form via the discriminated value hint.
201
+ if (def.kind === "saved_scope") {
202
+ merge(savedScopeTypes, def.name, resolveSavedScopeHint(def, model, rootScopesForFile));
203
+ continue;
204
+ }
205
+ const varPrefix = VAR_KIND_PREFIX[def.kind];
206
+ const listPrefix = LIST_KIND_PREFIX[def.kind];
207
+ if (!varPrefix && !listPrefix) continue;
208
+ // Defs without an expression (change_variable sites, dual-indexed list
209
+ // shadows) contribute nothing — they evidence existence, not type.
210
+ if (def.value === undefined) continue;
211
+ const resolved = resolveValueExpr(def.value, def.file, model, rootScopesForFile);
212
+ if (varPrefix) merge(types, `${varPrefix}:${def.name}`, resolved);
213
+ else merge(listItemTypes, `${listPrefix}:${def.name}`, resolved);
214
+ }
215
+ return { types, listItemTypes, adhocListItemTypes, savedScopeTypes };
216
+ }
217
+
218
+ /** Resolve one save site's discriminated type hint; null = unknown. Sites
219
+ * without a hint (older index rows) contribute unknown, poisoning the merge —
220
+ * annotate, never guess (AD-5). */
221
+ function resolveSavedScopeHint(
222
+ def: Definition,
223
+ model: ScopeModel,
224
+ rootScopesForFile: (file: string) => Set<Scope> | null
225
+ ): Set<Scope> | null {
226
+ const hint = def.value;
227
+ if (hint === undefined) return null;
228
+ if (hint === "type:value") return new Set(["value"]);
229
+ if (hint.startsWith("expr:")) {
230
+ return resolveValueExpr(hint.slice(5), def.file, model, rootScopesForFile);
231
+ }
232
+ if (hint.startsWith("chain:")) {
233
+ return resolveKeyChainScopes(hint.slice(6).split("."), model, rootScopesForFile(def.file));
234
+ }
235
+ return null;
236
+ }
237
+
238
+ /**
239
+ * Statically resolve a set_variable value expression to the scope type(s) it
240
+ * produces; null = unknown. Handles literals (value/boolean/flag) and link
241
+ * chains anchored at `root` (the set-file's root scope) or a global data link
242
+ * (`culture:x.head_of_faith` …). Runtime anchors resolve to null.
243
+ */
244
+ export function resolveValueExpr(
245
+ expr: string,
246
+ file: string,
247
+ model: ScopeModel,
248
+ rootScopesForFile: (file: string) => Set<Scope> | null
249
+ ): Set<Scope> | null {
250
+ if (NUMBER.test(expr)) return new Set(["value"]);
251
+ if (expr === "yes" || expr === "no") return new Set(["boolean"]);
252
+ if (expr.startsWith("flag:")) return new Set(["flag"]);
253
+ if (expr.includes("$")) return null; // macro parameter
254
+
255
+ const parts = expr.split(".");
256
+ let current: Set<Scope> | null;
257
+
258
+ const first = parts[0];
259
+ if (first === "root") {
260
+ current = rootScopesForFile(file);
261
+ } else if (
262
+ first.startsWith("scope:") ||
263
+ first.startsWith("var:") ||
264
+ first.startsWith("local_var:") ||
265
+ first.startsWith("global_var:") ||
266
+ first === "this" ||
267
+ first === "prev"
268
+ ) {
269
+ return null; // runtime anchor — unknowable statically
270
+ } else {
271
+ // Global/data link anchor: culture:x, faith:x, character:123, title:k_x …
272
+ const colon = first.indexOf(":");
273
+ const linkName = colon > 0 ? first.slice(0, colon) : first;
274
+ const link = model.links.get(linkName.toLowerCase());
275
+ if (!link || !link.outputs) return null;
276
+ // A bare link (no data argument) needs an input scope — unknowable here.
277
+ if (colon <= 0 && link.inputs !== null) return null;
278
+ current = new Set(link.outputs);
279
+ }
280
+ if (!current) return null;
281
+
282
+ for (let i = 1; i < parts.length; i++) {
283
+ const part = parts[i].toLowerCase();
284
+ const colon = part.indexOf(":");
285
+ const link = model.links.get(colon > 0 ? part.slice(0, colon) : part);
286
+ if (!link || !link.outputs) return null;
287
+ current = new Set(link.outputs);
288
+ }
289
+ return current;
290
+ }