@px-lsp/protocol 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.
- package/LICENSE +674 -0
- package/README.md +25 -0
- package/dist/arrays.d.ts +13 -0
- package/dist/arrays.js +19 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.js +10 -0
- package/dist/descriptorMetadata.d.ts +51 -0
- package/dist/descriptorMetadata.js +98 -0
- package/dist/descriptorMod.d.ts +66 -0
- package/dist/descriptorMod.js +335 -0
- package/dist/errorLogParser.d.ts +33 -0
- package/dist/errorLogParser.js +125 -0
- package/dist/fsWalk.d.ts +20 -0
- package/dist/fsWalk.js +159 -0
- package/dist/locProperties.d.ts +13 -0
- package/dist/locProperties.js +46 -0
- package/dist/locRefs.d.ts +11 -0
- package/dist/locRefs.js +31 -0
- package/dist/modName.d.ts +6 -0
- package/dist/modName.js +53 -0
- package/dist/protocol.d.ts +1462 -0
- package/dist/protocol.js +201 -0
- package/dist/regex.d.ts +13 -0
- package/dist/regex.js +21 -0
- package/dist/suppression.d.ts +52 -0
- package/dist/suppression.js +173 -0
- package/dist/tigerParser.d.ts +28 -0
- package/dist/tigerParser.js +72 -0
- package/dist/translationCore.d.ts +26 -0
- package/dist/translationCore.js +162 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.js +3 -0
- package/package.json +39 -0
- package/src/arrays.ts +16 -0
- package/src/constants.ts +12 -0
- package/src/descriptorMetadata.ts +101 -0
- package/src/descriptorMod.ts +354 -0
- package/src/errorLogParser.ts +136 -0
- package/src/fsWalk.ts +126 -0
- package/src/locProperties.ts +43 -0
- package/src/locRefs.ts +38 -0
- package/src/modName.ts +18 -0
- package/src/protocol.ts +1459 -0
- package/src/regex.ts +19 -0
- package/src/suppression.ts +178 -0
- package/src/tigerParser.ts +79 -0
- package/src/translationCore.ts +140 -0
- package/src/types.ts +90 -0
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scopeAtRequest = exports.dependenciesRequest = exports.eventValueOptionsRequest = exports.EVENT_VOCABULARY_MAX_VALUES = exports.EVENT_VOCABULARY_MAX_TOKENS = exports.eventVocabularyRequest = exports.eventBannerRequest = exports.eventGraphRequest = exports.guiWidgetEditRequest = exports.guiSourceEditRequest = exports.guiSaveValuesRequest = exports.GUI_PREVIEW_MAX = exports.guiPreviewRequest = exports.guiVocabularyRequest = exports.guiDependenciesRequest = exports.guiWidgetInfoRequest = exports.guiLayoutRequest = exports.guiTreeRequest = exports.eventDetailRequest = exports.overridesRequest = exports.locCoverageRequest = exports.modOverviewRequest = exports.progressNotification = exports.indexChangedNotification = exports.statusNotification = exports.lookupLocRequest = exports.indexStatsRequest = exports.reloadDocsRequest = exports.modFileChangedNotification = exports.configChangedNotification = exports.allClientCommandIds = exports.clientCommands = void 0;
|
|
4
|
+
// ---- client command ids ----------------------------------------------------
|
|
5
|
+
/**
|
|
6
|
+
* Client commands the server references in code actions and hover links (part
|
|
7
|
+
* of the wire contract: a client that implements one must register exactly
|
|
8
|
+
* this id and list it in {@link ParadoxClientCapabilities.commands}). They
|
|
9
|
+
* carry the "px." prefix: these are public extension command ids with shipped
|
|
10
|
+
* default keybindings. The prefix was renamed in the Paradox Toolkit rebrand
|
|
11
|
+
* and no fallback to the old ids is registered.
|
|
12
|
+
*/
|
|
13
|
+
exports.clientCommands = {
|
|
14
|
+
editLocalization: "px.editLocalization",
|
|
15
|
+
openLocalizationSideBySide: "px.openLocalizationSideBySide",
|
|
16
|
+
showReferences: "px.showReferences",
|
|
17
|
+
};
|
|
18
|
+
/** Every id in {@link clientCommands}: what a fully capable client registers. */
|
|
19
|
+
exports.allClientCommandIds = Object.values(exports.clientCommands);
|
|
20
|
+
// ---- client -> server ------------------------------------------------------
|
|
21
|
+
/** Notification: settings changed; payload {@link ParadoxSettings}. */
|
|
22
|
+
exports.configChangedNotification = "paradox/configChanged";
|
|
23
|
+
/** Notification: a mod file changed on disk; payload {@link ModFileChangeParams}. */
|
|
24
|
+
exports.modFileChangedNotification = "paradox/modFileChanged";
|
|
25
|
+
/** Request: re-parse script_docs logs; payload {@link ReloadDocsParams} -> {@link ReloadDocsResult}. */
|
|
26
|
+
exports.reloadDocsRequest = "paradox/reloadDocs";
|
|
27
|
+
/** Request: index statistics; no payload -> {@link IndexStats}. */
|
|
28
|
+
exports.indexStatsRequest = "paradox/indexStats";
|
|
29
|
+
/** Request: look up localization entries for a key; {@link LookupLocParams} -> {@link LocEntryInfo}[].
|
|
30
|
+
* Mod entries shadow vanilla ones (the full list is returned, mod first). */
|
|
31
|
+
exports.lookupLocRequest = "paradox/lookupLoc";
|
|
32
|
+
// ---- server -> client ------------------------------------------------------
|
|
33
|
+
/** Notification: data health for the status bar; payload {@link StatusPayload}. */
|
|
34
|
+
exports.statusNotification = "paradox/status";
|
|
35
|
+
/** Notification: the definition index changed (debounced server-side); no payload.
|
|
36
|
+
* Overview views re-query on this signal. */
|
|
37
|
+
exports.indexChangedNotification = "paradox/indexChanged";
|
|
38
|
+
/** Notification: a long-running server phase started or finished; payload
|
|
39
|
+
* {@link ProgressPayload}. The status bar lists what is still loading, so a
|
|
40
|
+
* cold workspace says which step it is on instead of looking idle. No
|
|
41
|
+
* percentages: the phases are coarse and the client only shows their state. */
|
|
42
|
+
exports.progressNotification = "paradox/progress";
|
|
43
|
+
/** Request: mod content inventory; {@link ModScopedParams} -> {@link ModOverview}. */
|
|
44
|
+
exports.modOverviewRequest = "paradox/modOverview";
|
|
45
|
+
/** Request: localization coverage; {@link ModScopedParams} -> {@link LocCoverage}[]. */
|
|
46
|
+
exports.locCoverageRequest = "paradox/locCoverage";
|
|
47
|
+
/** Request: override/conflict map; {@link ModScopedParams} -> {@link OverrideInfo}[]. */
|
|
48
|
+
exports.overridesRequest = "paradox/overrides";
|
|
49
|
+
/** Request: full event detail for the graph inspector; {@link EventDetailParams} -> {@link EventDetail} | null. */
|
|
50
|
+
exports.eventDetailRequest = "paradox/eventDetail";
|
|
51
|
+
/** Request: GUI widget tree for a .gui document; {@link GuiTreeParams} -> {@link GuiTree}. */
|
|
52
|
+
exports.guiTreeRequest = "paradox/guiTree";
|
|
53
|
+
/**
|
|
54
|
+
* Request: rendered GUI layout for a .gui document;
|
|
55
|
+
* {@link GuiLayoutParams} -> {@link GuiLayoutResult}. Rectangles come from
|
|
56
|
+
* the measured layout engine (docs/gui-designer/calibration/spec.md), with
|
|
57
|
+
* templates/types resolved against the vanilla + mod gui tree.
|
|
58
|
+
*/
|
|
59
|
+
exports.guiLayoutRequest = "paradox/guiLayout";
|
|
60
|
+
/**
|
|
61
|
+
* Request: the properties of ONE widget, as the layout engine resolved them;
|
|
62
|
+
* {@link GuiWidgetInfoParams} -> {@link GuiWidgetInfo}, null when the line
|
|
63
|
+
* carries no widget of its own (a node spliced in from a template or a type has
|
|
64
|
+
* no source here, the same answer `guiSourceEdit` refuses with).
|
|
65
|
+
*
|
|
66
|
+
* This is the designer inspector's READ side. It is a separate request rather
|
|
67
|
+
* than a field on {@link GuiLayoutNode} because it is per-SELECTION data: a
|
|
68
|
+
* vanilla window lays out 500+ widgets and carrying every widget's expanded
|
|
69
|
+
* property list on every layout push would multiply the payload for rows one
|
|
70
|
+
* widget at a time is ever shown.
|
|
71
|
+
*/
|
|
72
|
+
exports.guiWidgetInfoRequest = "paradox/guiWidgetInfo";
|
|
73
|
+
/**
|
|
74
|
+
* Request: what a `.gui` document reaches on the SCRIPT side;
|
|
75
|
+
* {@link GuiDependenciesParams} -> {@link GuiDependenciesResult}. The forward
|
|
76
|
+
* half of the dependency surface; the reverse (script definition -> the .gui
|
|
77
|
+
* paths using it) is `paradox/dependencies` with `guiUses: true`, so both
|
|
78
|
+
* directions come out of the same scripted_gui link.
|
|
79
|
+
*/
|
|
80
|
+
exports.guiDependenciesRequest = "paradox/guiDependencies";
|
|
81
|
+
/**
|
|
82
|
+
* Request: the widget names a designer palette may offer for THIS document;
|
|
83
|
+
* {@link GuiVocabularyParams} -> {@link GuiVocabularyResult}.
|
|
84
|
+
*
|
|
85
|
+
* Every name is harvested, never listed by hand: the bundled per-game widget
|
|
86
|
+
* schema (`data/<game>/guiSchema.json`, built from the vanilla `gui/` tree)
|
|
87
|
+
* plus the requested document's own `template` and `type` declarations. A
|
|
88
|
+
* palette entry is therefore always a widget the game knows.
|
|
89
|
+
*/
|
|
90
|
+
exports.guiVocabularyRequest = "paradox/guiVocabulary";
|
|
91
|
+
/**
|
|
92
|
+
* Render-ready previews of palette entries: one instance of each entry laid
|
|
93
|
+
* out in a synthetic document that keeps the requested document's own
|
|
94
|
+
* declarations (so a local template previews with its real base). The
|
|
95
|
+
* result is an ordinary node tree the client draws with the same painter as
|
|
96
|
+
* the canvas. Entries a synthetic document cannot stand up (nothing to show,
|
|
97
|
+
* zero size, a type the store lacks) come back with `node: null` and a
|
|
98
|
+
* `reason`. Capped per request (GUI_PREVIEW_MAX); ask for the visible page.
|
|
99
|
+
*/
|
|
100
|
+
exports.guiPreviewRequest = "paradox/guiPreview";
|
|
101
|
+
exports.GUI_PREVIEW_MAX = 48;
|
|
102
|
+
/**
|
|
103
|
+
* Preview values read out of a save game, so a designer draws
|
|
104
|
+
* `[GetPlayer.GetName]` as "Great Britain" instead of a placeholder chip.
|
|
105
|
+
*
|
|
106
|
+
* `values` is keyed by datafunction chain WITHOUT brackets, exactly the shape
|
|
107
|
+
* {@link GuiLayoutParams.previewValues} takes, so a client hands the answer
|
|
108
|
+
* straight back to the next layout request. A chain the save has no field for
|
|
109
|
+
* is absent: a preview shows what is knowable and never invents a value.
|
|
110
|
+
*
|
|
111
|
+
* The server streams the file and parses only the few blocks it needs (a big
|
|
112
|
+
* campaign runs ~115 MB), and caches the answer per file and mtime.
|
|
113
|
+
* Ironman and binary saves are refused with `error` set; melting them is a
|
|
114
|
+
* different tool.
|
|
115
|
+
*/
|
|
116
|
+
exports.guiSaveValuesRequest = "paradox/guiSaveValues";
|
|
117
|
+
/**
|
|
118
|
+
* Request: source edits for a `.gui` designer gesture;
|
|
119
|
+
* {@link GuiSourceEditParams} -> {@link GuiSourceEditResult}, null when the
|
|
120
|
+
* request itself makes no sense (an unknown op). The server never writes: it
|
|
121
|
+
* returns offsets into the text it was handed and the host applies them, which
|
|
122
|
+
* keeps undo, dirty state and the live preview in the editor (EMBEDDING.md,
|
|
123
|
+
* host-owns-text).
|
|
124
|
+
*
|
|
125
|
+
* Every edit is surgical, over the exact span the source model recorded, so
|
|
126
|
+
* untouched bytes stay byte-identical: comments, CRLF, tabs-vs-spaces and
|
|
127
|
+
* single-line bodies all survive a write.
|
|
128
|
+
*/
|
|
129
|
+
exports.guiSourceEditRequest = "paradox/guiSourceEdit";
|
|
130
|
+
/**
|
|
131
|
+
* Request: text edit for a preview interaction (drag / property change);
|
|
132
|
+
* {@link GuiWidgetEditParams} -> {@link GuiWidgetEditResult} (null when the
|
|
133
|
+
* widget or property cannot be edited). The client applies the offsets via
|
|
134
|
+
* WorkspaceEdit so undo and the live preview loop stay in the editor.
|
|
135
|
+
*
|
|
136
|
+
* @deprecated Use {@link guiSourceEditRequest} with a `setProperties` op. This
|
|
137
|
+
* is a thin alias over the same core, kept for hosts already wired to it: it
|
|
138
|
+
* can only write the `position`/`size` pair and returns one edit or null, so a
|
|
139
|
+
* refusal reaches the caller as a bare null with no reason attached.
|
|
140
|
+
*/
|
|
141
|
+
exports.guiWidgetEditRequest = "paradox/guiWidgetEdit";
|
|
142
|
+
/** Request: event graph; {@link EventGraphParams} -> {@link EventGraph}. */
|
|
143
|
+
exports.eventGraphRequest = "paradox/eventGraph";
|
|
144
|
+
/**
|
|
145
|
+
* Request: the value sets an event editor may offer; {@link EventVocabularyParams}
|
|
146
|
+
* to {@link EventVocabularyResult}.
|
|
147
|
+
*
|
|
148
|
+
* Everything in the answer is DERIVED: the key lists come from the active
|
|
149
|
+
* profile's structure table, the field value sets from the schema's reference
|
|
150
|
+
* fields resolved through the definition index, and the effect/trigger lists
|
|
151
|
+
* from the user's script_docs (or the bundled wiki fallback). Nothing here is a
|
|
152
|
+
* hand-written name list, so a game patch that adds a theme or an effect shows
|
|
153
|
+
* up without a release.
|
|
154
|
+
*/
|
|
155
|
+
/**
|
|
156
|
+
* Request: the illustration an event theme puts behind its window;
|
|
157
|
+
* {@link EventBannerParams} to {@link EventBannerResult}.
|
|
158
|
+
*
|
|
159
|
+
* Resolved through the game's own two hops (event_themes -> event_backgrounds),
|
|
160
|
+
* taking the last `background` block that carries no `trigger`, which is the
|
|
161
|
+
* file's own unconditional fallback. `texture` is the engine's mod-relative
|
|
162
|
+
* path, exactly as a `.gui` file would spell it, so a client resolves it with
|
|
163
|
+
* the same mod-then-game lookup it uses for any other texture. A theme that
|
|
164
|
+
* resolves to nothing answers `reason` instead: the caller is expected to say
|
|
165
|
+
* so rather than draw a picture that is not the event's.
|
|
166
|
+
*/
|
|
167
|
+
exports.eventBannerRequest = "paradox/eventBanner";
|
|
168
|
+
exports.eventVocabularyRequest = "paradox/eventVocabulary";
|
|
169
|
+
/** Caps: an editor lists a page at a time, and these ride on every open. */
|
|
170
|
+
exports.EVENT_VOCABULARY_MAX_TOKENS = 600;
|
|
171
|
+
exports.EVENT_VOCABULARY_MAX_VALUES = 400;
|
|
172
|
+
/**
|
|
173
|
+
* Request: the value set a VALUE belongs to, resolved through the definition
|
|
174
|
+
* index; {@link EventValueOptionsParams} -> {@link EventValueOptionsResult} |
|
|
175
|
+
* null. The static vocabulary maps a KEY to its values, which only works where
|
|
176
|
+
* the schema knows the key's context (an event's or option's own fields). Deep
|
|
177
|
+
* inside an effect tree the same key name means something else (`type` in
|
|
178
|
+
* `random_secret` is a secret, not an event type), so there the editor asks
|
|
179
|
+
* about the value it already has: `secret_cultivator` is an indexed `secret`,
|
|
180
|
+
* and the answer is every secret the index knows. Null = the value resolves to
|
|
181
|
+
* nothing enumerable; the editor falls back to a free input.
|
|
182
|
+
*/
|
|
183
|
+
exports.eventValueOptionsRequest = "paradox/eventValueOptions";
|
|
184
|
+
/**
|
|
185
|
+
* Request: dependency explorer for any indexed definition;
|
|
186
|
+
* {@link DependenciesParams} -> {@link DependenciesResult}. Cursor-driven
|
|
187
|
+
* (uri + position) or by name (optionally disambiguated by kind).
|
|
188
|
+
*/
|
|
189
|
+
exports.dependenciesRequest = "paradox/dependencies";
|
|
190
|
+
/**
|
|
191
|
+
* Request: the inferred scope chain at a cursor position;
|
|
192
|
+
* {@link ScopeAtParams} -> {@link ScopeAtResult} | null. Answers for OPEN
|
|
193
|
+
* script documents only (the server reads the client's text, not the disk);
|
|
194
|
+
* null means "not open / not a script document", which a status bar renders as
|
|
195
|
+
* nothing rather than as an error.
|
|
196
|
+
*
|
|
197
|
+
* This is a read-out of the same inference completion, hover and inlay hints
|
|
198
|
+
* run at a position: it ranks and annotates, never diagnoses, and never
|
|
199
|
+
* asserts more than the derived link tables actually say.
|
|
200
|
+
*/
|
|
201
|
+
exports.scopeAtRequest = "paradox/scopeAt";
|
package/dist/regex.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One correct copy of the regex-escape both sides need. It lived inline at five
|
|
3
|
+
* call sites and one of them had an extra backslash, which silently turned the
|
|
4
|
+
* escape into a no-op (it matched a metacharacter followed by two literal
|
|
5
|
+
* backslashes, so nothing was ever escaped).
|
|
6
|
+
*/
|
|
7
|
+
/** Escape every regex metacharacter in `literal` so it matches itself. */
|
|
8
|
+
export declare function escapeRegExp(literal: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* A pattern matching `name` only as a whole script identifier: not when it is
|
|
11
|
+
* a substring of a longer name, and not across a dot-chain segment boundary.
|
|
12
|
+
*/
|
|
13
|
+
export declare function wholeNamePattern(name: string): string;
|
package/dist/regex.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* One correct copy of the regex-escape both sides need. It lived inline at five
|
|
4
|
+
* call sites and one of them had an extra backslash, which silently turned the
|
|
5
|
+
* escape into a no-op (it matched a metacharacter followed by two literal
|
|
6
|
+
* backslashes, so nothing was ever escaped).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.escapeRegExp = escapeRegExp;
|
|
10
|
+
exports.wholeNamePattern = wholeNamePattern;
|
|
11
|
+
/** Escape every regex metacharacter in `literal` so it matches itself. */
|
|
12
|
+
function escapeRegExp(literal) {
|
|
13
|
+
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A pattern matching `name` only as a whole script identifier: not when it is
|
|
17
|
+
* a substring of a longer name, and not across a dot-chain segment boundary.
|
|
18
|
+
*/
|
|
19
|
+
function wholeNamePattern(name) {
|
|
20
|
+
return `(?<![A-Za-z0-9_.\\-])${escapeRegExp(name)}(?![A-Za-z0-9_.\\-])`;
|
|
21
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostic suppression, shared by the server (own structural/loc diagnostics)
|
|
3
|
+
* and the client (tiger-forwarded reports) so one habit works across both tools.
|
|
4
|
+
*
|
|
5
|
+
* No `vscode` imports: plain data in, plain predicates out. Everything here is
|
|
6
|
+
* fail-soft — bad setting values or malformed comments are ignored, never thrown.
|
|
7
|
+
*
|
|
8
|
+
* Two mechanisms:
|
|
9
|
+
* 1. Settings: the diagnostics.ignore setting (diagnostic codes) and
|
|
10
|
+
* the diagnostics.ignorePatterns setting (globs on the workspace-relative path).
|
|
11
|
+
* 2. Inline comments: `# px:ignore <code…>` (same line) and
|
|
12
|
+
* `# px:ignore-next-line <code…>` (following line); a bare form with no
|
|
13
|
+
* codes suppresses every diagnostic on the target line. A trailing
|
|
14
|
+
* `-- <rationale>` is allowed and ignored.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Settings-driven filter. `ignore` matches a diagnostic's code (our stable
|
|
18
|
+
* codes, or tiger's `key`); `ignorePatterns` matches globs against the
|
|
19
|
+
* workspace-relative file path.
|
|
20
|
+
*/
|
|
21
|
+
export interface DiagnosticIgnoreConfig {
|
|
22
|
+
/** Diagnostic codes to drop everywhere. */
|
|
23
|
+
ignore: string[];
|
|
24
|
+
/** Glob patterns matched against the workspace-relative (forward-slash) path. */
|
|
25
|
+
ignorePatterns: string[];
|
|
26
|
+
}
|
|
27
|
+
/** Normalize a raw settings array: strings only, trimmed, empties dropped. */
|
|
28
|
+
export declare function sanitizeStringList(value: unknown): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Tiny `*`/`**` glob matcher (no dependency). `*` matches within a path segment,
|
|
31
|
+
* `**` matches across segments (including `/`). Matching is done on
|
|
32
|
+
* forward-slash paths and is case-insensitive (Windows-friendly). A pattern with
|
|
33
|
+
* no slash also matches against the basename, so `*.txt` works like a gitignore
|
|
34
|
+
* entry. Returns false on any malformed pattern.
|
|
35
|
+
*/
|
|
36
|
+
export declare function globMatch(pattern: string, filePath: string): boolean;
|
|
37
|
+
/** True when a diagnostic with `code` in `filePath` should be dropped by settings. */
|
|
38
|
+
export declare function isIgnoredByConfig(cfg: DiagnosticIgnoreConfig, code: string | undefined, relPath: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Inline suppression map for a file, keyed by 0-based line number. A `null`
|
|
41
|
+
* value means "suppress every code on this line"; an array means "suppress only
|
|
42
|
+
* these codes". Built by scanning comment lines once when publishing.
|
|
43
|
+
*/
|
|
44
|
+
export type InlineSuppressions = Map<number, string[] | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Scan a document's text for `# px:ignore[-next-line] <code…>` comments.
|
|
47
|
+
* Cheap: only lines containing `px:ignore` are parsed. `-next-line` targets
|
|
48
|
+
* the following line; the plain form targets its own line.
|
|
49
|
+
*/
|
|
50
|
+
export declare function scanInlineSuppressions(text: string): InlineSuppressions;
|
|
51
|
+
/** True when line `line` has an inline suppression covering `code`. */
|
|
52
|
+
export declare function isSuppressedInline(map: InlineSuppressions, line: number, code: string | undefined): boolean;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Diagnostic suppression, shared by the server (own structural/loc diagnostics)
|
|
4
|
+
* and the client (tiger-forwarded reports) so one habit works across both tools.
|
|
5
|
+
*
|
|
6
|
+
* No `vscode` imports: plain data in, plain predicates out. Everything here is
|
|
7
|
+
* fail-soft — bad setting values or malformed comments are ignored, never thrown.
|
|
8
|
+
*
|
|
9
|
+
* Two mechanisms:
|
|
10
|
+
* 1. Settings: the diagnostics.ignore setting (diagnostic codes) and
|
|
11
|
+
* the diagnostics.ignorePatterns setting (globs on the workspace-relative path).
|
|
12
|
+
* 2. Inline comments: `# px:ignore <code…>` (same line) and
|
|
13
|
+
* `# px:ignore-next-line <code…>` (following line); a bare form with no
|
|
14
|
+
* codes suppresses every diagnostic on the target line. A trailing
|
|
15
|
+
* `-- <rationale>` is allowed and ignored.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.sanitizeStringList = sanitizeStringList;
|
|
19
|
+
exports.globMatch = globMatch;
|
|
20
|
+
exports.isIgnoredByConfig = isIgnoredByConfig;
|
|
21
|
+
exports.scanInlineSuppressions = scanInlineSuppressions;
|
|
22
|
+
exports.isSuppressedInline = isSuppressedInline;
|
|
23
|
+
/** Normalize a raw settings array: strings only, trimmed, empties dropped. */
|
|
24
|
+
function sanitizeStringList(value) {
|
|
25
|
+
if (!Array.isArray(value))
|
|
26
|
+
return [];
|
|
27
|
+
const out = [];
|
|
28
|
+
for (const v of value) {
|
|
29
|
+
if (typeof v !== "string")
|
|
30
|
+
continue;
|
|
31
|
+
const t = v.trim();
|
|
32
|
+
if (t !== "")
|
|
33
|
+
out.push(t);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Tiny `*`/`**` glob matcher (no dependency). `*` matches within a path segment,
|
|
39
|
+
* `**` matches across segments (including `/`). Matching is done on
|
|
40
|
+
* forward-slash paths and is case-insensitive (Windows-friendly). A pattern with
|
|
41
|
+
* no slash also matches against the basename, so `*.txt` works like a gitignore
|
|
42
|
+
* entry. Returns false on any malformed pattern.
|
|
43
|
+
*/
|
|
44
|
+
function globMatch(pattern, filePath) {
|
|
45
|
+
const p = pattern.replace(/\\/g, "/").toLowerCase();
|
|
46
|
+
const f = filePath.replace(/\\/g, "/").replace(/^\/+/, "").toLowerCase();
|
|
47
|
+
if (p === "")
|
|
48
|
+
return false;
|
|
49
|
+
try {
|
|
50
|
+
const re = new RegExp("^" + globToRegExpSource(p) + "$");
|
|
51
|
+
if (re.test(f))
|
|
52
|
+
return true;
|
|
53
|
+
// Slash-free patterns also match the basename (gitignore-style convenience).
|
|
54
|
+
if (!p.includes("/")) {
|
|
55
|
+
const base = f.slice(f.lastIndexOf("/") + 1);
|
|
56
|
+
return re.test(base);
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Translate a glob (already lowercased, forward-slashed) into a regex source. */
|
|
65
|
+
function globToRegExpSource(glob) {
|
|
66
|
+
let out = "";
|
|
67
|
+
for (let i = 0; i < glob.length; i++) {
|
|
68
|
+
const c = glob[i];
|
|
69
|
+
if (c === "*") {
|
|
70
|
+
if (glob[i + 1] === "*") {
|
|
71
|
+
// `**` — cross segments, optionally swallowing a trailing slash.
|
|
72
|
+
i++;
|
|
73
|
+
if (glob[i + 1] === "/") {
|
|
74
|
+
i++;
|
|
75
|
+
out += "(?:.*/)?";
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
out += ".*";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
out += "[^/]*";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if (c === "?") {
|
|
86
|
+
out += "[^/]";
|
|
87
|
+
}
|
|
88
|
+
else if ("\\^$.|+()[]{}".includes(c)) {
|
|
89
|
+
out += "\\" + c;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
out += c;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
/** True when a diagnostic with `code` in `filePath` should be dropped by settings. */
|
|
98
|
+
function isIgnoredByConfig(cfg, code, relPath) {
|
|
99
|
+
if (code !== undefined && cfg.ignore.includes(code))
|
|
100
|
+
return true;
|
|
101
|
+
for (const pattern of cfg.ignorePatterns) {
|
|
102
|
+
if (globMatch(pattern, relPath))
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const IGNORE_RE = /#\s*px:ignore(-next-line)?\b([^\n]*)/i;
|
|
108
|
+
/**
|
|
109
|
+
* Scan a document's text for `# px:ignore[-next-line] <code…>` comments.
|
|
110
|
+
* Cheap: only lines containing `px:ignore` are parsed. `-next-line` targets
|
|
111
|
+
* the following line; the plain form targets its own line.
|
|
112
|
+
*/
|
|
113
|
+
function scanInlineSuppressions(text) {
|
|
114
|
+
const map = new Map();
|
|
115
|
+
if (!text.includes("px:ignore"))
|
|
116
|
+
return map;
|
|
117
|
+
const lines = text.split(/\r?\n/);
|
|
118
|
+
for (let i = 0; i < lines.length; i++) {
|
|
119
|
+
const line = lines[i];
|
|
120
|
+
// A comment can trail script on the same line; only look after the `#`.
|
|
121
|
+
const hash = line.indexOf("#");
|
|
122
|
+
if (hash < 0)
|
|
123
|
+
continue;
|
|
124
|
+
const m = IGNORE_RE.exec(line.slice(hash));
|
|
125
|
+
if (!m)
|
|
126
|
+
continue;
|
|
127
|
+
const target = m[1] ? i + 1 : i;
|
|
128
|
+
const codes = parseCodes(m[2]);
|
|
129
|
+
mergeSuppression(map, target, codes.length === 0 ? null : codes);
|
|
130
|
+
}
|
|
131
|
+
return map;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The codes following the marker, stopping at a `--` rationale. Writing a
|
|
135
|
+
* reason is the natural instinct, and without the cut-off every word of it
|
|
136
|
+
* parsed as a code — turning a suppression that matched everything into one
|
|
137
|
+
* that matched nothing, silently. Codes are kebab-case slugs
|
|
138
|
+
* (`unclosed-brace`, `loc-no-header`), so a leading `-` can only be the
|
|
139
|
+
* separator.
|
|
140
|
+
*/
|
|
141
|
+
function parseCodes(rest) {
|
|
142
|
+
const out = [];
|
|
143
|
+
for (const token of rest.trim().split(/\s+/)) {
|
|
144
|
+
if (token === "")
|
|
145
|
+
continue;
|
|
146
|
+
if (token.startsWith("-"))
|
|
147
|
+
break; // `-- because the game allows it`
|
|
148
|
+
out.push(token);
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
function mergeSuppression(map, line, codes) {
|
|
153
|
+
const existing = map.get(line);
|
|
154
|
+
if (existing === undefined) {
|
|
155
|
+
map.set(line, codes);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// `null` (suppress-all) wins; otherwise union the code lists.
|
|
159
|
+
if (existing === null || codes === null) {
|
|
160
|
+
map.set(line, null);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
map.set(line, [...existing, ...codes]);
|
|
164
|
+
}
|
|
165
|
+
/** True when line `line` has an inline suppression covering `code`. */
|
|
166
|
+
function isSuppressedInline(map, line, code) {
|
|
167
|
+
if (!map.has(line))
|
|
168
|
+
return false;
|
|
169
|
+
const codes = map.get(line) ?? null;
|
|
170
|
+
if (codes === null)
|
|
171
|
+
return true; // bare `# px:ignore` suppresses all
|
|
172
|
+
return code !== undefined && codes.includes(code);
|
|
173
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for tiger `--json` reports (the Paradox script validator family).
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from the process management in tiger.ts so it stays free of
|
|
5
|
+
* `vscode` imports and defensively tolerant of format drift between tiger
|
|
6
|
+
* releases: unknown fields are ignored, malformed entries are skipped.
|
|
7
|
+
*/
|
|
8
|
+
export interface TigerLocation {
|
|
9
|
+
path: string;
|
|
10
|
+
fullpath?: string;
|
|
11
|
+
/** 1-based, may be missing for file-level reports. */
|
|
12
|
+
linenr?: number;
|
|
13
|
+
/** 1-based. */
|
|
14
|
+
column?: number;
|
|
15
|
+
length?: number;
|
|
16
|
+
tag?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface TigerReport {
|
|
19
|
+
severity: string;
|
|
20
|
+
/** tiger also rates how sure it is: weak | reasonable | strong. */
|
|
21
|
+
confidence?: string;
|
|
22
|
+
key: string;
|
|
23
|
+
message: string;
|
|
24
|
+
info?: string;
|
|
25
|
+
locations: TigerLocation[];
|
|
26
|
+
}
|
|
27
|
+
/** Parse tiger's JSON output. Returns null if no JSON array can be found at all. */
|
|
28
|
+
export declare function parseTigerJson(stdout: string): TigerReport[] | null;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for tiger `--json` reports (the Paradox script validator family).
|
|
4
|
+
*
|
|
5
|
+
* Kept separate from the process management in tiger.ts so it stays free of
|
|
6
|
+
* `vscode` imports and defensively tolerant of format drift between tiger
|
|
7
|
+
* releases: unknown fields are ignored, malformed entries are skipped.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.parseTigerJson = parseTigerJson;
|
|
11
|
+
/** Parse tiger's JSON output. Returns null if no JSON array can be found at all. */
|
|
12
|
+
function parseTigerJson(stdout) {
|
|
13
|
+
let raw;
|
|
14
|
+
try {
|
|
15
|
+
raw = JSON.parse(stdout);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// tiger may print progress noise before the JSON; try from the first '['.
|
|
19
|
+
const start = stdout.indexOf("[");
|
|
20
|
+
if (start < 0)
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
raw = JSON.parse(stdout.slice(start));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (!Array.isArray(raw))
|
|
30
|
+
return null;
|
|
31
|
+
const reports = [];
|
|
32
|
+
for (const entry of raw) {
|
|
33
|
+
if (typeof entry !== "object" || entry === null)
|
|
34
|
+
continue;
|
|
35
|
+
const e = entry;
|
|
36
|
+
const message = typeof e.message === "string" ? e.message : null;
|
|
37
|
+
const locationsRaw = Array.isArray(e.locations) ? e.locations : [];
|
|
38
|
+
if (message === null)
|
|
39
|
+
continue;
|
|
40
|
+
const locations = [];
|
|
41
|
+
for (const locRaw of locationsRaw) {
|
|
42
|
+
if (typeof locRaw !== "object" || locRaw === null)
|
|
43
|
+
continue;
|
|
44
|
+
const l = locRaw;
|
|
45
|
+
const p = typeof l.fullpath === "string" ? l.fullpath : typeof l.path === "string" ? l.path : null;
|
|
46
|
+
if (p === null)
|
|
47
|
+
continue;
|
|
48
|
+
const loc = { path: typeof l.path === "string" ? l.path : p };
|
|
49
|
+
if (typeof l.fullpath === "string")
|
|
50
|
+
loc.fullpath = l.fullpath;
|
|
51
|
+
const linenr = l.linenr ?? l.line;
|
|
52
|
+
if (typeof linenr === "number")
|
|
53
|
+
loc.linenr = linenr;
|
|
54
|
+
if (typeof l.column === "number")
|
|
55
|
+
loc.column = l.column;
|
|
56
|
+
if (typeof l.length === "number")
|
|
57
|
+
loc.length = l.length;
|
|
58
|
+
if (typeof l.tag === "string")
|
|
59
|
+
loc.tag = l.tag;
|
|
60
|
+
locations.push(loc);
|
|
61
|
+
}
|
|
62
|
+
reports.push({
|
|
63
|
+
severity: typeof e.severity === "string" ? e.severity : "warning",
|
|
64
|
+
confidence: typeof e.confidence === "string" ? e.confidence : undefined,
|
|
65
|
+
key: typeof e.key === "string" ? e.key : "unknown",
|
|
66
|
+
message,
|
|
67
|
+
info: typeof e.info === "string" ? e.info : undefined,
|
|
68
|
+
locations,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return reports;
|
|
72
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const LOC_LANGUAGES: string[];
|
|
2
|
+
/** Language of a loc file, from its `_l_<lang>.yml` suffix or a path segment. */
|
|
3
|
+
export declare function detectLocFileLanguage(filePath: string): string | null;
|
|
4
|
+
/**
|
|
5
|
+
* Where the translated counterpart of `srcFile` lives: language path segments
|
|
6
|
+
* and the `_l_<lang>` filename marker are retargeted. Returns null when the
|
|
7
|
+
* path carries no language marker at all.
|
|
8
|
+
*/
|
|
9
|
+
export declare function retargetLocPath(srcFile: string, sourceLang: string, targetLang: string): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* A translation skeleton: the source file's structure (comments and blank
|
|
12
|
+
* lines preserved — they are context for the translator) with the language
|
|
13
|
+
* header switched and every value BLANKED; the source text stays visible as
|
|
14
|
+
* an inline `# english: …` comment so the translator sees it right there
|
|
15
|
+
* without it leaking into the game as a fake translation.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildTranslation(sourceContent: string, targetLang: string, sourceLang?: string): string;
|
|
18
|
+
export interface MergeResult {
|
|
19
|
+
content: string;
|
|
20
|
+
added: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Add entries that exist in the source but not yet in the target, appended at
|
|
24
|
+
* the end under a marker comment. Existing target lines are never touched.
|
|
25
|
+
*/
|
|
26
|
+
export declare function mergeTranslation(targetContent: string, sourceContent: string, sourceLang: string): MergeResult;
|