@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,650 @@
1
+ /**
2
+ * The Victoria 3 schema table.
3
+ *
4
+ * Every entry was verified folder-by-folder against a real Victoria 3 install
5
+ * (F:\SteamLibrary\...\Victoria 3\game, `common/` with 135 subfolders plus a
6
+ * loose achievement_groups.txt, 2026-08-01 and re-swept 2026-08-15): the folder
7
+ * was listed, at least one file opened, and the top-level key confirmed to be
8
+ * the name script actually references. Folders whose on-disk layout fits none
9
+ * of the extraction modes (see schema/types.ts) are left out rather than
10
+ * indexed with wrong names, see the trailing "Not covered" block. All 135
11
+ * subfolders are accounted for: 125 in the table, 10 in that block.
12
+ *
13
+ * The community CWT config (cwtools-vic3-config) was used as a *checklist*
14
+ * only; where it disagreed with the install, the install won (the disagreements
15
+ * are noted below).
16
+ *
17
+ * requiredLoc: every claim below was MEASURED against vanilla
18
+ * localization/english (101 467 keys), the folder's definition names were
19
+ * expanded through the pattern and looked up. Only patterns satisfied by ≥95%
20
+ * of vanilla definitions are claimed, and each carries its measured hit rate.
21
+ *
22
+ * rootScopes: claimed only where the CWT config's type-level `replace_scope`
23
+ * and the folder's own file contents agree. Omitted when unsure.
24
+ *
25
+ * No structure/ambientScopes layers: the Vic3 tree ships no `_*.info` docs
26
+ * (see games/vic3/index.ts). Engine tokens come from the user's own script_docs
27
+ * logs (same log format as CK3).
28
+ */
29
+ import type { SchemaEntry, RefField } from "../../schema/types";
30
+ import { JOMINI_VARIABLE_BLOCK_REFS } from "../jomini/variables";
31
+
32
+ export const VIC3_SCHEMA: SchemaEntry[] = [
33
+ // --- Core script surfaces ---
34
+ // Events use `namespace = x` declarations and ns.N ids like CK3. Root scope
35
+ // varies by event type (country/state/character), so none is claimed.
36
+ { path: "events", kind: "event", extraction: "event-id" },
37
+ { path: "localization", kind: "loc_key", ext: ".yml", extraction: "loc-key" },
38
+ { path: "common/scripted_effects", kind: "scripted_effect" },
39
+ { path: "common/scripted_triggers", kind: "scripted_trigger" },
40
+ { path: "common/script_values", kind: "script_value" },
41
+ // Vanilla ships ZERO .txt files here (only scripted_modifiers.md, which
42
+ // documents the `name = { <factor blocks> }` layout). Indexed anyway so mod
43
+ // definitions resolve; the folder is a normal top-level-key folder.
44
+ { path: "common/scripted_modifiers", kind: "scripted_modifier" },
45
+ // Vic3 reads the PLURAL folder (unlike CK3's common/on_action).
46
+ { path: "common/on_actions", kind: "on_action" },
47
+ // Each scripted_gui declares its own `scope = X`, so no fixed root scope.
48
+ { path: "common/scripted_guis", kind: "scripted_gui" },
49
+ { path: "common/scripted_lists", kind: "scripted_list" },
50
+ { path: "common/scripted_rules", kind: "scripted_rule" },
51
+ { path: "common/scripted_buttons", kind: "scripted_button", rootScopes: ["country"] },
52
+ { path: "common/scripted_progress_bars", kind: "scripted_progress_bar", rootScopes: ["country"] },
53
+ { path: "common/customizable_localization", kind: "customizable_localization" },
54
+ // $ 100% (612/612), $_desc 100% (612/612). Vic3 names already carry the
55
+ // `concept_` prefix, so the key is the bare name (no `game_concept_` prefix
56
+ // as in CK3, measured 0%).
57
+ { path: "common/game_concepts", kind: "game_concept", requiredLoc: ["$", "$_desc"] },
58
+ { path: "common/trigger_localization", kind: "trigger_localization" },
59
+ { path: "common/effect_localization", kind: "effect_localization" },
60
+
61
+ // --- Decisions & journal ---
62
+ // $ 100% (60/60), $_desc 100% (60/60).
63
+ {
64
+ path: "common/decisions",
65
+ kind: "decision",
66
+ rootScopes: ["country"],
67
+ requiredLoc: ["$", "$_desc"],
68
+ },
69
+ // $ 100% (419/419), $_reason 100% (419/419), `_reason` is the journal
70
+ // entry's goal tooltip and the CWT config marks it required too.
71
+ {
72
+ path: "common/journal_entries",
73
+ kind: "journal_entry",
74
+ rootScopes: ["country"],
75
+ requiredLoc: ["$", "$_reason"],
76
+ },
77
+ // $ 96.3% (26/27).
78
+ { path: "common/journal_entry_groups", kind: "journal_entry_group", requiredLoc: ["$"] },
79
+
80
+ // --- Objectives (the 1.6 "play objective" system) ---
81
+ // Plain `name = { … }` folders, re-verified 2026-08-15. `objectives` names the
82
+ // playthrough goal, `objective_subgoals` the individual tasks it lists in
83
+ // `objective_subgoals = { … }`, and the categories group those. No
84
+ // requiredLoc claimed anywhere in the 2026-08-15 sweep: the hit rates were
85
+ // not measured, and an unmeasured claim would fire bogus missing-loc hints.
86
+ { path: "common/objectives", kind: "objective" },
87
+ { path: "common/objective_subgoals", kind: "objective_subgoal" },
88
+ { path: "common/objective_subgoal_categories", kind: "objective_subgoal_category" },
89
+
90
+ // --- Economy ---
91
+ // $ 100% (115/115). No `$_desc` (0%) and no `building_` prefix (names carry it).
92
+ {
93
+ path: "common/buildings",
94
+ kind: "building",
95
+ rootScopes: ["building"],
96
+ requiredLoc: ["$"],
97
+ },
98
+ // $ 100% (69/69).
99
+ { path: "common/building_groups", kind: "building_group", requiredLoc: ["$"] },
100
+ // $ 100% (436/436).
101
+ { path: "common/production_methods", kind: "production_method", requiredLoc: ["$"] },
102
+ // $ 100% (197/197).
103
+ { path: "common/production_method_groups", kind: "production_method_group", requiredLoc: ["$"] },
104
+ // $ 100% (53/53).
105
+ { path: "common/goods", kind: "good", rootScopes: ["goods"], requiredLoc: ["$"] },
106
+ // $ 100% (72/72).
107
+ { path: "common/prestige_goods", kind: "prestige_good", requiredLoc: ["$"] },
108
+ // $ 100% (15/15).
109
+ { path: "common/pop_needs", kind: "pop_need", requiredLoc: ["$"] },
110
+ // $ 100% (221/221).
111
+ { path: "common/company_types", kind: "company_type", requiredLoc: ["$"] },
112
+ // $ 100% (5/5), $_desc 100% (5/5).
113
+ {
114
+ path: "common/company_charter_types",
115
+ kind: "company_charter_type",
116
+ rootScopes: ["company"],
117
+ requiredLoc: ["$", "$_desc"],
118
+ },
119
+ // Weighted naming tables for procedurally named companies, keyed by name.
120
+ { path: "common/dynamic_company_names", kind: "dynamic_company_name" },
121
+ // Droughts, floods, locust swarms… referenced by `incompatible_with = flood`
122
+ // and by harvest-condition effects.
123
+ { path: "common/harvest_condition_types", kind: "harvest_condition_type" },
124
+
125
+ // --- Pops & society ---
126
+ // $ 100% (15/15), $_desc 100% (15/15).
127
+ {
128
+ path: "common/pop_types",
129
+ kind: "pop_type",
130
+ rootScopes: ["pop"],
131
+ requiredLoc: ["$", "$_desc"],
132
+ },
133
+ // $ 100% (14/14), $_desc 100% (14/14).
134
+ { path: "common/social_classes", kind: "social_class", requiredLoc: ["$", "$_desc"] },
135
+ // $ 100% (3/3), only three vanilla definitions, so a weak sample.
136
+ {
137
+ path: "common/social_hierarchies",
138
+ kind: "social_hierarchy",
139
+ rootScopes: ["pop"],
140
+ requiredLoc: ["$"],
141
+ },
142
+ // $ 100% (324/324). Covers heritages, languages and religious traditions.
143
+ { path: "common/discrimination_traits", kind: "discrimination_trait", requiredLoc: ["$"] },
144
+ // $ 100% (89/89).
145
+ {
146
+ path: "common/discrimination_trait_groups",
147
+ kind: "discrimination_trait_group",
148
+ requiredLoc: ["$"],
149
+ },
150
+ // $ 100% (317/317).
151
+ { path: "common/cultures", kind: "culture", requiredLoc: ["$"] },
152
+ // $ 100% (17/17).
153
+ { path: "common/religions", kind: "religion", requiredLoc: ["$"] },
154
+ // The five discrimination tiers a pop can sit in (`violent_hostility` …
155
+ // `full_acceptance`); each is a `name = { threshold = … }` block.
156
+ { path: "common/acceptance_statuses", kind: "acceptance_status" },
157
+
158
+ // --- Portraits & culture graphics ---
159
+ // Re-checked 2026-08-15: unlike CK3's equivalents these three ARE plain
160
+ // named-definition folders, not `@macro`/texture-path config.
161
+ // `graphics = european` on a culture picks a culture_graphics entry (330
162
+ // vanilla `graphics =` sites).
163
+ { path: "common/culture_graphics", kind: "culture_graphics" },
164
+ // 36 named ethnicities (plus `ethnicity_template`, referenced by the others
165
+ // through `template = …`). Cultures pick them through a WEIGHTED block
166
+ // (`ethnicity = { 10 = arab }`), so no scalar ref field resolves them.
167
+ { path: "common/ethnicities", kind: "ethnicity" },
168
+ // 583 one-off historical portraits, one `dna_<name> = { portrait_info = … }`
169
+ // per file; `dna = dna_x` has 589 vanilla sites. Indexed for navigation only:
170
+ // 6.2 MB / 584 files is the largest single folder in common/, and the names
171
+ // are historical one-offs, so they stay out of completion.
172
+ { path: "common/dna_data", kind: "dna", completable: false },
173
+
174
+ // --- Politics ---
175
+ // Unlike CK3, Vic3 splits laws and law groups into two folders, so BOTH the
176
+ // individual law and its group are ordinary top-level keys.
177
+ // $ 100% (138/138), $_desc 100% (138/138).
178
+ {
179
+ path: "common/laws",
180
+ kind: "law",
181
+ rootScopes: ["country"],
182
+ requiredLoc: ["$", "$_desc"],
183
+ },
184
+ // $ 100% (26/26), $_desc 100% (26/26).
185
+ {
186
+ path: "common/law_groups",
187
+ kind: "law_group",
188
+ rootScopes: ["country"],
189
+ requiredLoc: ["$", "$_desc"],
190
+ },
191
+ // $ 100% (172/172), $_desc 100% (172/172).
192
+ { path: "common/ideologies", kind: "ideology", requiredLoc: ["$", "$_desc"] },
193
+ // $ 100% (7/7), $_desc 100% (7/7).
194
+ { path: "common/institutions", kind: "institution", requiredLoc: ["$", "$_desc"] },
195
+ // $ 100% (8/8), $_desc 100% (8/8).
196
+ {
197
+ path: "common/interest_groups",
198
+ kind: "interest_group",
199
+ rootScopes: ["interest_group"],
200
+ requiredLoc: ["$", "$_desc"],
201
+ },
202
+ // $ 100% (99/99), $_desc 100% (99/99).
203
+ { path: "common/interest_group_traits", kind: "interest_group_trait", requiredLoc: ["$", "$_desc"] },
204
+ // Party names are built from `name = { first_valid = … }`, not from a `$`
205
+ // key, so no requiredLoc.
206
+ { path: "common/parties", kind: "party", rootScopes: ["country"] },
207
+ // $ 100% (39/39).
208
+ { path: "common/political_movements", kind: "political_movement", requiredLoc: ["$"] },
209
+ { path: "common/political_movement_categories", kind: "political_movement_category" },
210
+ // Named support sources a movement draws on (`movement_support_high_literacy
211
+ // = { name = … }`); the folder's own header comment declares root =
212
+ // political_movement, but the CWT config has no matching type, so no
213
+ // rootScopes claim.
214
+ { path: "common/political_movement_pop_support", kind: "political_movement_pop_support" },
215
+ // Named appeasement events a lobby reacts to (`appeasement_alliance_formed`).
216
+ { path: "common/political_lobby_appeasement", kind: "political_lobby_appeasement" },
217
+ // Constitutional amendments; each names its `parent` law and allowed_laws.
218
+ { path: "common/amendments", kind: "amendment" },
219
+ // Threshold tiers (`legitimacy_level_contested = { threshold = … }`).
220
+ { path: "common/legitimacy_levels", kind: "legitimacy_level" },
221
+ // $ 100% (4/4), four vanilla definitions, weak sample but unambiguous.
222
+ {
223
+ path: "common/political_lobbies",
224
+ kind: "political_lobby",
225
+ rootScopes: ["country"],
226
+ requiredLoc: ["$"],
227
+ },
228
+ // $ 100% (444/444), $_desc 99.8% (443/444).
229
+ {
230
+ path: "common/government_types",
231
+ kind: "government_type",
232
+ rootScopes: ["country"],
233
+ requiredLoc: ["$", "$_desc"],
234
+ },
235
+
236
+ // --- Characters ---
237
+ // $ 100% (121/121), $_desc 100% (121/121). Bare name, no `trait_` prefix (0%).
238
+ { path: "common/character_traits", kind: "character_trait", requiredLoc: ["$", "$_desc"] },
239
+ // $ 100% (21/21); $_desc only 61.9%, so not claimed.
240
+ {
241
+ path: "common/character_interactions",
242
+ kind: "character_interaction",
243
+ rootScopes: ["character"],
244
+ requiredLoc: ["$"],
245
+ },
246
+ // $ 100% (10/10).
247
+ {
248
+ path: "common/character_roles",
249
+ kind: "character_role",
250
+ rootScopes: ["character"],
251
+ requiredLoc: ["$"],
252
+ },
253
+ // 2011 vanilla templates, almost all historical one-offs referenced only from
254
+ // their own history file → indexed for navigation, kept out of completion.
255
+ { path: "common/character_templates", kind: "character_template", completable: false },
256
+
257
+ // --- Country & diplomacy ---
258
+ // 830 country tags; noisy in completion (`D00`… placeholder tags) and only
259
+ // 88% have a `$` loc key, so neither completable nor a requiredLoc claim.
260
+ { path: "common/country_definitions", kind: "country_definition", completable: false },
261
+ // $ 100% (5/5).
262
+ { path: "common/country_types", kind: "country_type", requiredLoc: ["$"] },
263
+ // $ 100% (9/9).
264
+ { path: "common/subject_types", kind: "subject_type", requiredLoc: ["$"] },
265
+ // $ 100% (55/55), $_desc 100% (55/55).
266
+ {
267
+ path: "common/diplomatic_actions",
268
+ kind: "diplomatic_action",
269
+ rootScopes: ["country"],
270
+ requiredLoc: ["$", "$_desc"],
271
+ },
272
+ // $ 98.1% (51/52); $_desc 86.5%, not claimed.
273
+ {
274
+ path: "common/diplomatic_plays",
275
+ kind: "diplomatic_play",
276
+ rootScopes: ["country"],
277
+ requiredLoc: ["$"],
278
+ },
279
+ // $ 100% (77/77), $_desc 100% (77/77).
280
+ {
281
+ path: "common/diplomatic_catalysts",
282
+ kind: "diplomatic_catalyst",
283
+ rootScopes: ["country"],
284
+ requiredLoc: ["$", "$_desc"],
285
+ },
286
+ // War goals take their display name from the play that carries them: only
287
+ // 17.6% (6/34) have a `$` key, so no requiredLoc.
288
+ { path: "common/war_goal_types", kind: "war_goal_type", rootScopes: ["country"] },
289
+ // $ 100% (34/34), $_desc 100% (34/34).
290
+ {
291
+ path: "common/treaty_articles",
292
+ kind: "treaty_article",
293
+ rootScopes: ["country"],
294
+ requiredLoc: ["$", "$_desc"],
295
+ },
296
+ // Ranks a country can hold (`great_power = { rank_value = 7 … }`).
297
+ { path: "common/country_ranks", kind: "country_rank" },
298
+ // Cooldown/relevance categories a diplomatic catalyst declares via
299
+ // `category = cc_relations_change`.
300
+ { path: "common/diplomatic_catalyst_categories", kind: "diplomatic_catalyst_category" },
301
+ // Subject liberty-desire tiers (`ld_level_low = { threshold = … }`).
302
+ { path: "common/liberty_desire_levels", kind: "liberty_desire_level" },
303
+ // The popup/notification shapes a proposal uses (`proposal_treaty = { … }`).
304
+ { path: "common/proposal_types", kind: "proposal_type" },
305
+ // Weighted naming tables for generated treaty names, keyed by name.
306
+ { path: "common/dynamic_treaty_names", kind: "dynamic_treaty_name" },
307
+
308
+ // --- Power blocs ---
309
+ // Principles display their GROUP's name, so `$` is 0% while $_desc is
310
+ // 100% (69/69), measured, not assumed.
311
+ {
312
+ path: "common/power_bloc_principles",
313
+ kind: "power_bloc_principle",
314
+ rootScopes: ["country"],
315
+ requiredLoc: ["$_desc"],
316
+ },
317
+ // $ 100% (23/23), $_desc 100% (23/23).
318
+ {
319
+ path: "common/power_bloc_principle_groups",
320
+ kind: "power_bloc_principle_group",
321
+ requiredLoc: ["$", "$_desc"],
322
+ },
323
+ // $ 100% (6/6), $_desc 100% (6/6).
324
+ {
325
+ path: "common/power_bloc_identities",
326
+ kind: "power_bloc_identity",
327
+ rootScopes: ["country"],
328
+ requiredLoc: ["$", "$_desc"],
329
+ },
330
+ // $ 100% (200/200).
331
+ {
332
+ path: "common/power_bloc_names",
333
+ kind: "power_bloc_name",
334
+ rootScopes: ["country"],
335
+ requiredLoc: ["$"],
336
+ },
337
+ // Cohesion tiers (`cohesion_level_high = { threshold = … }`).
338
+ { path: "common/cohesion_levels", kind: "cohesion_level" },
339
+ // Sphere-of-influence interest tiers (`interest_tier_engaged = { rank = … }`).
340
+ { path: "common/interest_tier_types", kind: "interest_tier_type" },
341
+ // Named map patterns a bloc paints with (`pb_pattern_01 = { texture = … }`).
342
+ { path: "common/power_bloc_map_textures", kind: "power_bloc_map_texture" },
343
+
344
+ // --- Technology, states, map ---
345
+ // The folder is `technology/` with two definition subfolders; the researchable
346
+ // technologies live in technologies/. $ 100% (179/179), $_desc 100% (179/179).
347
+ {
348
+ path: "common/technology/technologies",
349
+ kind: "technology",
350
+ requiredLoc: ["$", "$_desc"],
351
+ },
352
+ // Eras have no loc keys of their own at all (measured 0%).
353
+ { path: "common/technology/eras", kind: "technology_era" },
354
+ // $ 100% (239/239).
355
+ {
356
+ path: "common/state_traits",
357
+ kind: "state_trait",
358
+ rootScopes: ["state"],
359
+ requiredLoc: ["$"],
360
+ },
361
+ // $ 100% (11/11), $_desc 100% (11/11).
362
+ {
363
+ path: "common/decrees",
364
+ kind: "decree",
365
+ rootScopes: ["state"],
366
+ requiredLoc: ["$", "$_desc"],
367
+ },
368
+ // $ 100% (142/142).
369
+ { path: "common/strategic_regions", kind: "strategic_region", requiredLoc: ["$"] },
370
+ // $ 100% (165/165).
371
+ { path: "common/geographic_regions", kind: "geographic_region", requiredLoc: ["$"] },
372
+ // Re-checked 2026-08-15: the three map folders below are NOT texture-path
373
+ // config, each is a plain `name = { … }` folder whose keys script uses.
374
+ // 25 terrain types (`plains`, `mountain`…), the value of `has_terrain = x`.
375
+ { path: "common/terrain", kind: "terrain" },
376
+ // Terrain conversion rules, each naming its `created_terrain`.
377
+ { path: "common/terrain_manipulators", kind: "terrain_manipulator" },
378
+ // Named canals and straits (`canal_suez = { type = artificial … }`).
379
+ { path: "common/strait_definitions", kind: "strait_definition" },
380
+ // Terrain/travel labels a state or province carries (`label_forested`).
381
+ { path: "common/labels", kind: "label" },
382
+ // Alternative country map colours chosen by trigger, keyed by name.
383
+ { path: "common/dynamic_country_map_colors", kind: "dynamic_country_map_color" },
384
+ // Map click behaviours (`build_building = { mapmode = … }`).
385
+ { path: "common/map_interaction_types", kind: "map_interaction_type" },
386
+ // On-map notification widgets. The file also holds one `@three_dee_map_zoom`
387
+ // macro, which the extractor drops (names must start with a word character).
388
+ { path: "common/map_notification_types", kind: "map_notification_type" },
389
+
390
+ // --- Modifiers & rules ---
391
+ // The `add_modifier = x` targets. 6127 vanilla definitions but every name is
392
+ // unique and hand-written, so they stay completable. Only 67.7% have a `$`
393
+ // loc key (code-applied containers have none), so no requiredLoc.
394
+ { path: "common/static_modifiers", kind: "static_modifier" },
395
+ // Formatting/metadata for code-defined modifier types (CK3's
396
+ // modifier_definition_formats equivalent): names mirror engine modifiers, so
397
+ // indexed for hover/navigation but not offered in completion.
398
+ { path: "common/modifier_type_definitions", kind: "modifier_type", completable: false },
399
+ { path: "common/opinion_modifiers", kind: "opinion_modifier" },
400
+ // NOTE: top-level keys here are game rule *categories* (achievements,
401
+ // ai_behavior); the individual settings that `has_game_rule = x` references
402
+ // are second-level and are NOT extractable with the current modes, the same
403
+ // situation as CK3's common/laws. rule_$ 100% (15/15) confirms the top-level
404
+ // key is the category (the settings use `setting_$`).
405
+ { path: "common/game_rules", kind: "game_rule_category", requiredLoc: ["rule_$"] },
406
+
407
+ // --- Military ---
408
+ // Plain top-level-key folders, each re-checked against the install for 0.4.0
409
+ // because the mod corpus edits them (they were skipped in 0.3.0 as
410
+ // low-traffic). No requiredLoc claimed: the hit rates were never measured.
411
+ { path: "common/combat_unit_types", kind: "combat_unit_type" },
412
+ { path: "common/mobilization_options", kind: "mobilization_option" },
413
+ { path: "common/mobilization_option_groups", kind: "mobilization_option_group" },
414
+ { path: "common/ship_types", kind: "ship_type" },
415
+ { path: "common/ship_modifications", kind: "ship_modification" },
416
+ { path: "common/ship_modification_slots", kind: "ship_modification_slot" },
417
+ // Named fleet-naming tables (`ship_names_historical_brazilian_capital_ships`),
418
+ // not the ship names themselves.
419
+ { path: "common/ship_name_definitions", kind: "ship_name_definition" },
420
+ // Added 2026-08-15, same plain `name = { … }` shape as the rows above.
421
+ // Land and naval battle modifiers (`battle_condition_mud = { modifier = … }`).
422
+ { path: "common/battle_conditions", kind: "battle_condition" },
423
+ { path: "common/naval_battle_conditions", kind: "naval_battle_condition" },
424
+ // `combat_unit_group_infantry` etc.; combat unit types name their group.
425
+ { path: "common/combat_unit_groups", kind: "combat_unit_group" },
426
+ { path: "common/combat_unit_experience_levels", kind: "combat_unit_experience_level" },
427
+ { path: "common/ship_groups", kind: "ship_group" },
428
+ { path: "common/ship_veterancy_levels", kind: "ship_veterancy_level" },
429
+ // Commander order buttons (`advance`, `defend_dig_in`).
430
+ { path: "common/commander_orders", kind: "commander_order" },
431
+ { path: "common/commander_ranks", kind: "commander_rank" },
432
+ // Naval mission buttons (`naval_mission_type_blockade`).
433
+ { path: "common/naval_mission_types", kind: "naval_mission_type" },
434
+ // Formation banner icons (`army_01 = { icon = … type = army }`).
435
+ { path: "common/military_formation_flags", kind: "military_formation_flag" },
436
+
437
+ // --- AI, economy & tooling ---
438
+ { path: "common/ai_strategies", kind: "ai_strategy" },
439
+ { path: "common/buy_packages", kind: "buy_package" },
440
+ { path: "common/console_command_macros", kind: "console_command_macro" },
441
+ // AI stance towards a strategic region (`stance_conquer_region = { … }`).
442
+ { path: "common/ai_strategic_region_stance_types", kind: "ai_strategic_region_stance_type" },
443
+
444
+ // --- Alerts, notifications & UI shells ---
445
+ // Added 2026-08-15. These are the named definitions gui/ and code look up;
446
+ // all four are plain `name = { … }` folders.
447
+ { path: "common/alert_types", kind: "alert_type" },
448
+ // `isolated_states = {}` … empty bodies, but the name is what alert_types
449
+ // and the GUI reference.
450
+ { path: "common/alert_groups", kind: "alert_group" },
451
+ // 469 notification definitions, the `post_notification = x` targets.
452
+ { path: "common/messages", kind: "message" },
453
+ // UI/papermap skins (`gui_skin_base = { category = ui_skin_theme … }`).
454
+ { path: "common/themes", kind: "theme" },
455
+
456
+ // --- Achievements & tutorial ---
457
+ // Added 2026-08-15, plain `name = { possible = … happened = … }` folders.
458
+ // (common/achievement_groups.txt is a LOOSE FILE in common/, not a folder,
459
+ // so the folder-keyed table cannot reach it, see "Not covered".)
460
+ { path: "common/achievements", kind: "achievement" },
461
+ // Top-level keys are lesson names; the `lesson_x_1 = { … }` steps nest one
462
+ // level down and are correctly not extracted.
463
+ { path: "common/tutorial_lessons", kind: "tutorial_lesson" },
464
+ // Only 3 of the 9 top-level keys are chains; the other 6 are `@timer` macros
465
+ // the extractor drops.
466
+ { path: "common/tutorial_lesson_chains", kind: "tutorial_lesson_chain" },
467
+
468
+ // --- GUI ---
469
+ { path: "gui", kind: "gui_type", ext: ".gui", extraction: "gui-type" },
470
+ ];
471
+
472
+ // Not covered. Re-swept folder by folder on 2026-08-15: the previous
473
+ // "deliberate low-traffic skip" list is GONE, every folder on it has a plain
474
+ // `name = { … }` layout, so all 46 moved into the table above. What stays out
475
+ // stays out because the extraction would produce wrong names, not because the
476
+ // folder is quiet.
477
+ // - Individual game rule settings: second-level keys under common/game_rules
478
+ // (only the category is indexed, see above).
479
+ // - common/history/*: 22 subfolders. Corrected 2026-08-15, the top-level key
480
+ // is NOT a date or a tag but a single SHOUTY section marker per file
481
+ // (`COUNTRIES = { c:ABS ?= { … } }`, `STATES`, `POPS`, `AI`…), repeated in
482
+ // every file of the folder. Indexing it would yield one bogus "definition"
483
+ // named COUNTRIES per file; the real identities are the scoped tags inside.
484
+ // - common/coat_of_arms/*: `coat_of_arms/` keys are country TAGS plus `@macro`
485
+ // soup (see the tag rule below), `options/` has one `atlas` key per file and
486
+ // `template_lists/` five list containers (`color_lists` …).
487
+ // - common/power_bloc_coa_pieces: top-level keys are .dds FILENAMES
488
+ // (`pb_center_00.dds`), not identifiers.
489
+ // - common/named_colors: every file's single top-level key is `colors`.
490
+ // - common/defines: top-level keys are the NDefines GROUPS (`NGame`,
491
+ // `NMilitary`); the settings script reads are second-level.
492
+ // - common/genes: top-level keys are the four gene CONTAINERS (`color_genes`,
493
+ // `special_genes`…); gene names sit two to three levels down.
494
+ // - Country-TAG folders, common/country_creation (394 keys),
495
+ // common/country_formation (74), common/dynamic_country_names (147) and
496
+ // common/flag_definitions (446, mostly `@macro`): all fit the mode, but
497
+ // their top-level keys are country tags that collide with
498
+ // country_definitions. Four "definitions" per tag would make navigation
499
+ // worse, not better. Revisit if a tag-aware mode lands.
500
+ // - common/achievement_groups.txt: a loose FILE directly in common/, not a
501
+ // folder. The table is keyed by folder, so no entry can name it; it would
502
+ // need a file-level schema path.
503
+ // - gfx/: unlike CK3 no gfx/ subfolder holds script-referenced names,
504
+ // gfx/portraits, gfx/map/* etc. are pure art config. gui/ IS indexed (the
505
+ // gui_type row): `type`/`template` names are what .gui files reference.
506
+ //
507
+ // Where the CWT config disagreed with the install (install wins):
508
+ // - CWT declares type[old_combat_unit_type] at common/old_combat_unit_types
509
+ // and type[history_interest] at common/history/interests. Neither folder
510
+ // exists in the install.
511
+ // - CWT's type[achievement_group] points at bare "game/common"; there is no
512
+ // such folder (achievement groups live inside common/achievements).
513
+ // - CWT models common/history/* as game-root `history/`; in Vic3 it really is
514
+ // common/history/ (verified on disk). Either way it is not indexed.
515
+
516
+ /**
517
+ * Assignment keys whose values reference other definitions. Every entry was
518
+ * counted across the vanilla common/ + events/ tree with the same rules the
519
+ * reference extractor uses (unquoted scalar values, no `prefix:`); the counts
520
+ * are the vanilla usage sites.
521
+ */
522
+ export const VIC3_REF_FIELDS: RefField[] = [
523
+ // Events & on_actions
524
+ { key: "trigger_event", kinds: ["event"] }, // 57
525
+ { key: "on_action", kinds: ["on_action"] }, // 1 (Vic3 mostly fires on_actions by name only)
526
+ { key: "on_actions", kinds: ["on_action"], form: "list" }, // 7
527
+ { key: "events", kinds: ["event"], form: "list" }, // 97
528
+ // Vic3's random_events is WEIGHTED (`10 = ns.1`), so only the rare bare-list
529
+ // form resolves through this field; kept because the bare form is still legal.
530
+ { key: "random_events", kinds: ["event"], form: "list", weighted: true },
531
+ // NOT `first_valid`. common/on_actions/_on_actions.md documents it as an
532
+ // on_action's "pick the first valid event" list, but all 405 vanilla sites
533
+ // are the LOCALIZATION construct `desc = { first_valid = { triggered_desc
534
+ // … } }`, zero are event lists. Claiming it would turn every localized
535
+ // description into bogus event references. Same for `random_on_actions`,
536
+ // `first_valid_on_action` and `fallback`: documented in that .md, zero
537
+ // script sites, so nothing to resolve.
538
+
539
+ // Technology
540
+ { key: "has_technology_researched", kinds: ["technology"] }, // 1509
541
+ { key: "add_technology_researched", kinds: ["technology"] }, // 220
542
+ { key: "unlocking_technologies", kinds: ["technology"], form: "list" }, // 644
543
+ { key: "era", kinds: ["technology_era"] }, // 179
544
+
545
+ // Buildings & production
546
+ { key: "has_building", kinds: ["building"] }, // 456
547
+ { key: "building_types", kinds: ["building"], form: "list" }, // 395
548
+ { key: "building_group", kinds: ["building_group"] }, // 115
549
+ { key: "production_method", kinds: ["production_method"] }, // 105
550
+ { key: "production_methods", kinds: ["production_method"], form: "list" }, // 591
551
+ { key: "unlocking_production_methods", kinds: ["production_method"], form: "list" }, // 11
552
+ { key: "goods", kinds: ["good"] }, // 62 scalar (the block form is a weighted table)
553
+
554
+ // Politics
555
+ { key: "unlocking_laws", kinds: ["law"], form: "list" }, // 64
556
+ { key: "ideology", kinds: ["ideology"] }, // 2051
557
+ { key: "ideologies", kinds: ["ideology"], form: "list" }, // 26
558
+ { key: "character_ideologies", kinds: ["ideology"], form: "list" }, // 123
559
+ { key: "institution", kinds: ["institution"] }, // 218
560
+ { key: "pop_type", kinds: ["pop_type"] }, // 1097
561
+ // `group = X` is a journal entry group on journal entries and a law group on
562
+ // laws; both kinds ride the reference so either can resolve. 596 sites.
563
+ { key: "group", kinds: ["journal_entry_group", "law_group"] },
564
+
565
+ // Characters
566
+ { key: "add_trait", kinds: ["character_trait"] }, // 237
567
+ { key: "remove_trait", kinds: ["character_trait"] }, // 48
568
+ { key: "has_trait", kinds: ["character_trait"] }, // 1014
569
+ { key: "traits", kinds: ["character_trait"], form: "list" }, // 4493
570
+
571
+ // Modifiers & misc
572
+ { key: "add_modifier", kinds: ["static_modifier"] }, // 83
573
+ { key: "remove_modifier", kinds: ["static_modifier"] }, // 477
574
+ { key: "has_modifier", kinds: ["static_modifier"] }, // 1259
575
+ { key: "war_goal", kinds: ["war_goal_type"] }, // 52
576
+ { key: "subject_type", kinds: ["subject_type"] }, // 9
577
+ ];
578
+
579
+ /**
580
+ * Scalar-value prefixes that reference definitions (`cu:czech` → culture).
581
+ * These are Vic3's dominant cross-reference idiom; counts are vanilla
582
+ * occurrences of `<prefix>:<name>` across common/ + events/.
583
+ *
584
+ * Re-measured 2026-08-15 by sweeping EVERY `<lowercase_prefix>:<name>` in
585
+ * vanilla instead of checking a hand-written list, then resolving each name
586
+ * against the real folder. The rule is now mechanical: a prefix is wired iff
587
+ * its target kind is in the schema table AND 100% of its vanilla names resolve
588
+ * to a definition of that kind. Twelve prefixes newly qualified (the three the
589
+ * 0.3.1 note flagged as unmeasured, plus four unlocked by the folders added
590
+ * this sweep and five that were simply never found); each carries its measured
591
+ * "occurrences, distinct names" below.
592
+ *
593
+ * Still out, because no folder produces their names: `s:` (state regions live
594
+ * in map_data/, not common/), `region_state:`, `p:` (province colours),
595
+ * `define:` (common/defines is not indexed), and the engine enums
596
+ * `relations_threshold:`, `infamy_threshold:`, `get_ruler_for:`. `scope:`,
597
+ * `var:`, `global_var:` and `local_var:` are the variables layer
598
+ * (games/jomini/variables.ts), not definition references.
599
+ */
600
+ export const VIC3_PREFIX_REFS: Record<string, string[]> = {
601
+ c: ["country_definition"], // 23543
602
+ cu: ["culture"], // 12766
603
+ law_type: ["law"], // 9009
604
+ rel: ["religion"], // 5141
605
+ ideology: ["ideology"], // 4111
606
+ ig: ["interest_group"], // 2356
607
+ sr: ["strategic_region"], // 1474
608
+ unit_type: ["combat_unit_type"], // NEW 903, 18/18 resolve
609
+ g: ["good"], // 654
610
+ je: ["journal_entry"], // 613
611
+ rank_value: ["country_rank"], // NEW 577, 7/7, needed common/country_ranks
612
+ ig_trait: ["interest_group_trait"], // 201
613
+ modifier: ["modifier_type"], // 178
614
+ identity: ["power_bloc_identity"], // 158
615
+ company_type: ["company_type"], // 144
616
+ ship_type: ["ship_type"], // NEW 121, 10/10 resolve
617
+ mg: ["good"], // 105, market goods, keyed by the goods name
618
+ amendment_type: ["amendment"], // NEW 94, 60/60, needed common/amendments
619
+ py: ["party"], // 91
620
+ active_law: ["law_group"], // 77, `active_law:lawgroup_x.type…`
621
+ movement_type: ["political_movement"], // 59
622
+ sg: ["good"], // 59, state goods, keyed by the goods name
623
+ bt: ["building"], // 55
624
+ mobilization_option: ["mobilization_option"], // NEW 38, 10/10 resolve
625
+ principle_group: ["power_bloc_principle_group"], // 32
626
+ institution: ["institution"], // 31
627
+ b: ["building"], // NEW 29, 14/14, the in-state building scope, `b:building_x`
628
+ bg: ["building_group"], // 28
629
+ principle: ["power_bloc_principle"], // 17
630
+ pop_type: ["pop_type"], // 16
631
+ cd: ["country_definition"], // NEW 16, 4/4, `country_definition = cd:LIP`
632
+ company: ["company_type"], // NEW 13, 7/7, the company instance, keyed by type
633
+ lobby_type: ["political_lobby"], // 8
634
+ play_type: ["diplomatic_play"], // NEW 7, 5/5 resolve
635
+ strait_type: ["strait_definition"], // NEW 6, 3/3, needed common/strait_definitions
636
+ i: ["ideology"], // NEW 4, 3/3, short form of `ideology:`
637
+ ship_group: ["ship_group"], // NEW 2, 1/1, needed common/ship_groups
638
+ };
639
+
640
+ /**
641
+ * Keys too generic for a global ref field, resolved by their enclosing block:
642
+ * `id` means an event only inside `trigger_event = { … }`, and `template` names
643
+ * a character template only inside `create_character = { … }` (elsewhere it is
644
+ * an ethnicity or coat-of-arms template).
645
+ */
646
+ export const VIC3_BLOCK_REF_FIELDS: Record<string, Record<string, string[]>> = {
647
+ trigger_event: { id: ["event"] },
648
+ create_character: { template: ["character_template"] },
649
+ ...JOMINI_VARIABLE_BLOCK_REFS,
650
+ };