@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
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Best-effort parsing of the game's logs/error.log lines. Pure (no vscode),
|
|
4
|
+
* so it stays unit-testable; the tailing/diagnostics wiring lives in the
|
|
5
|
+
* client.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.ErrorLogParser = void 0;
|
|
9
|
+
exports.parseErrorLogLine = parseErrorLogLine;
|
|
10
|
+
/** `... in file: events/x.txt line: 12` / `file: "gui/y.gui" near line: 3` */
|
|
11
|
+
const FILE_LINE = /file:\s*"?([^"\r\n]+?\.(?:txt|yml|gui|info|mod|gfx|asset))"?(?:\s+(?:near\s+)?line:?\s*(\d+))?/i;
|
|
12
|
+
/**
|
|
13
|
+
* Newer Jomini titles write the location with no `file:` keyword:
|
|
14
|
+
* `gui/x.gui:110 - Widget cannot have a position in a layout`. The ` - `
|
|
15
|
+
* separator is required; without it any "foo.txt:3" quoted inside a message
|
|
16
|
+
* would be read as a location.
|
|
17
|
+
*/
|
|
18
|
+
const FILE_LINE_BARE = /([\w./\\-]+\.(?:txt|yml|gui)):(\d+)\s+-\s+/;
|
|
19
|
+
/**
|
|
20
|
+
* Timestamp, with an OPTIONAL severity tag: older logs write
|
|
21
|
+
* `[18:33:24][E][x.cpp:1]:`, newer ones `[01:30:39][x.cpp:186]:` and leave the
|
|
22
|
+
* severity to the message text. Untagged entries count as errors.
|
|
23
|
+
*/
|
|
24
|
+
const TIMESTAMP = /^\[\d{2}:\d{2}:\d{2}\](?:\[([EW])\])?/;
|
|
25
|
+
/** The `[time][sev][source.cpp:N]: ` preamble, stripped from messages. */
|
|
26
|
+
const PREAMBLE = /^\[\d{2}:\d{2}:\d{2}\](?:\[[EW]\])?\[[^\]]*\]:\s*/;
|
|
27
|
+
/** The file/line a log line names, in either shape; null when it names none. */
|
|
28
|
+
function matchFile(line) {
|
|
29
|
+
const m = FILE_LINE.exec(line);
|
|
30
|
+
if (m) {
|
|
31
|
+
return {
|
|
32
|
+
relFile: m[1].replace(/\\/g, "/"),
|
|
33
|
+
line: m[2] !== undefined ? Math.max(0, parseInt(m[2], 10) - 1) : null,
|
|
34
|
+
matched: "",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const bare = FILE_LINE_BARE.exec(line);
|
|
38
|
+
if (!bare)
|
|
39
|
+
return null;
|
|
40
|
+
return {
|
|
41
|
+
relFile: bare[1].replace(/\\/g, "/"),
|
|
42
|
+
line: Math.max(0, parseInt(bare[2], 10) - 1),
|
|
43
|
+
matched: bare[0],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Parse one error.log line; null when it names no file. */
|
|
47
|
+
function parseErrorLogLine(raw) {
|
|
48
|
+
const line = raw.replace(/\r$/, "");
|
|
49
|
+
if (line.trim() === "")
|
|
50
|
+
return null;
|
|
51
|
+
const m = matchFile(line);
|
|
52
|
+
if (!m)
|
|
53
|
+
return null;
|
|
54
|
+
const sev = TIMESTAMP.exec(line);
|
|
55
|
+
let message = line.replace(PREAMBLE, "").trim();
|
|
56
|
+
// The bare shape puts the location in front of the text; the diagnostic
|
|
57
|
+
// already carries file and line, so it is redundant there.
|
|
58
|
+
if (m.matched !== "" && message.startsWith(m.matched))
|
|
59
|
+
message = message.slice(m.matched.length).trim() || message;
|
|
60
|
+
return {
|
|
61
|
+
message,
|
|
62
|
+
relFile: m.relFile,
|
|
63
|
+
line: m.line,
|
|
64
|
+
severity: sev?.[1] === "W" ? "warning" : "error",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Stateful line parser: same as `parseErrorLogLine`, but additionally stitches
|
|
69
|
+
* multi-line `Script system error!` blocks together, where the actual error
|
|
70
|
+
* text and the file location sit on separate indented continuation lines:
|
|
71
|
+
*
|
|
72
|
+
* [18:14:55][E][jomini_script_system.cpp:303]: Script system error!
|
|
73
|
+
* Error: is_cultivator trigger [ Scoped object ... is not valid ]
|
|
74
|
+
* Script location: file: common/script_values/x.txt line: 25 (name)
|
|
75
|
+
*
|
|
76
|
+
* Line-by-line, the location line would become the diagnostic message and the
|
|
77
|
+
* error text would be dropped. Feed EVERY line through `push` in order (state
|
|
78
|
+
* carries across reads); call `reset` when the log is cleared or replaced.
|
|
79
|
+
*/
|
|
80
|
+
class ErrorLogParser {
|
|
81
|
+
pendingSeverity = null;
|
|
82
|
+
pendingError = null;
|
|
83
|
+
push(raw) {
|
|
84
|
+
const line = raw.replace(/\r$/, "");
|
|
85
|
+
if (line.trim() === "") {
|
|
86
|
+
this.reset();
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const sev = TIMESTAMP.exec(line);
|
|
90
|
+
if (sev) {
|
|
91
|
+
// Timestamped entry: single-line entries parse as before; a header that
|
|
92
|
+
// names no file (e.g. "Script system error!") opens a block.
|
|
93
|
+
this.reset();
|
|
94
|
+
const single = parseErrorLogLine(line);
|
|
95
|
+
if (single)
|
|
96
|
+
return single;
|
|
97
|
+
this.pendingSeverity = sev[1] === "W" ? "warning" : "error"; // untagged = error
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
// Untimestamped continuation line of an open block.
|
|
101
|
+
if (this.pendingSeverity === null)
|
|
102
|
+
return null;
|
|
103
|
+
const err = /^\s*Error:\s*(.+)$/.exec(line);
|
|
104
|
+
if (err) {
|
|
105
|
+
this.pendingError = err[1].trim();
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const m = matchFile(line);
|
|
109
|
+
if (!m)
|
|
110
|
+
return null;
|
|
111
|
+
const parsed = {
|
|
112
|
+
message: this.pendingError ?? line.trim(),
|
|
113
|
+
relFile: m.relFile,
|
|
114
|
+
line: m.line,
|
|
115
|
+
severity: this.pendingSeverity,
|
|
116
|
+
};
|
|
117
|
+
this.reset();
|
|
118
|
+
return parsed;
|
|
119
|
+
}
|
|
120
|
+
reset() {
|
|
121
|
+
this.pendingSeverity = null;
|
|
122
|
+
this.pendingError = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.ErrorLogParser = ErrorLogParser;
|
package/dist/fsWalk.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Directory entries between two `null` ticks from `iterFiles`. Measured walk
|
|
3
|
+
* throughput on a real tree is 4.2k entries/s cold and 29k/s warm, so 500
|
|
4
|
+
* entries is tens of milliseconds of blocking at worst.
|
|
5
|
+
*/
|
|
6
|
+
export declare const WALK_TICK = 500;
|
|
7
|
+
/**
|
|
8
|
+
* Every file under `dir` (recursive) with the given extension (lowercase
|
|
9
|
+
* match), yielded as it is found, plus a `null` every WALK_TICK entries
|
|
10
|
+
* VISITED.
|
|
11
|
+
*
|
|
12
|
+
* The nulls are what lets an async caller pace the listing: a subtree with no
|
|
13
|
+
* match in it at all (a mod's `gfx/` under a `.txt` scan) still costs one
|
|
14
|
+
* readdirSync per directory, so a caller handed only paths would have nothing
|
|
15
|
+
* to pace itself against and would block for the whole traversal.
|
|
16
|
+
*/
|
|
17
|
+
export declare function iterFiles(dir: string, ext: string): Generator<string | null>;
|
|
18
|
+
/** All files under `dir` (recursive) with the given extension (lowercase match). */
|
|
19
|
+
export declare function listFiles(dir: string, ext: string): string[];
|
|
20
|
+
export declare function walkDir(dir: string, ext: string, out: string[]): void;
|
package/dist/fsWalk.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WALK_TICK = void 0;
|
|
37
|
+
exports.iterFiles = iterFiles;
|
|
38
|
+
exports.listFiles = listFiles;
|
|
39
|
+
exports.walkDir = walkDir;
|
|
40
|
+
/**
|
|
41
|
+
* Recursive file listing shared by the server indexer and client-side
|
|
42
|
+
* reference scans. No `vscode` imports.
|
|
43
|
+
*/
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
/**
|
|
47
|
+
* Directory entries between two `null` ticks from `iterFiles`. Measured walk
|
|
48
|
+
* throughput on a real tree is 4.2k entries/s cold and 29k/s warm, so 500
|
|
49
|
+
* entries is tens of milliseconds of blocking at worst.
|
|
50
|
+
*/
|
|
51
|
+
exports.WALK_TICK = 500;
|
|
52
|
+
/**
|
|
53
|
+
* Every file under `dir` (recursive) with the given extension (lowercase
|
|
54
|
+
* match), yielded as it is found, plus a `null` every WALK_TICK entries
|
|
55
|
+
* VISITED.
|
|
56
|
+
*
|
|
57
|
+
* The nulls are what lets an async caller pace the listing: a subtree with no
|
|
58
|
+
* match in it at all (a mod's `gfx/` under a `.txt` scan) still costs one
|
|
59
|
+
* readdirSync per directory, so a caller handed only paths would have nothing
|
|
60
|
+
* to pace itself against and would block for the whole traversal.
|
|
61
|
+
*/
|
|
62
|
+
function* iterFiles(dir, ext) {
|
|
63
|
+
// One visited-target set per walk: shared across sibling links so two links
|
|
64
|
+
// to the same tree cannot index it twice. The walk runs once per schema folder
|
|
65
|
+
// (tens of times per root), never per directory, so the Set is free.
|
|
66
|
+
yield* walk(dir, ext, new Set(), { count: 0 });
|
|
67
|
+
}
|
|
68
|
+
/** All files under `dir` (recursive) with the given extension (lowercase match). */
|
|
69
|
+
function listFiles(dir, ext) {
|
|
70
|
+
const out = [];
|
|
71
|
+
walkDir(dir, ext, out);
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
function walkDir(dir, ext, out) {
|
|
75
|
+
for (const file of iterFiles(dir, ext)) {
|
|
76
|
+
if (file !== null)
|
|
77
|
+
out.push(file);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function* walk(dir, ext, visited, tick) {
|
|
81
|
+
let entries;
|
|
82
|
+
try {
|
|
83
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
for (const entry of entries) {
|
|
89
|
+
// Dot-directories (.git, .claude worktrees, …) are never game content and
|
|
90
|
+
// can hold stale copies of the whole mod — indexing them pollutes results.
|
|
91
|
+
if (entry.name.startsWith("."))
|
|
92
|
+
continue;
|
|
93
|
+
if (++tick.count % exports.WALK_TICK === 0)
|
|
94
|
+
yield null;
|
|
95
|
+
const full = path.join(dir, entry.name);
|
|
96
|
+
if (entry.isDirectory())
|
|
97
|
+
yield* walk(full, ext, visited, tick);
|
|
98
|
+
else if (entry.isFile()) {
|
|
99
|
+
if (entry.name.toLowerCase().endsWith(ext))
|
|
100
|
+
yield full;
|
|
101
|
+
}
|
|
102
|
+
else if (entry.isSymbolicLink())
|
|
103
|
+
yield* followLink(full, ext, visited, tick);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Case-folded on Windows, trailing separator stripped. */
|
|
107
|
+
function norm(p) {
|
|
108
|
+
const n = process.platform === "win32" ? p.toLowerCase() : p;
|
|
109
|
+
return n.replace(/[\\/]+$/, "");
|
|
110
|
+
}
|
|
111
|
+
/** True when `inner` is `outer` itself or sits below it. */
|
|
112
|
+
function isSameOrBelow(outer, inner) {
|
|
113
|
+
const a = norm(outer);
|
|
114
|
+
const b = norm(inner);
|
|
115
|
+
return b === a || b.startsWith(a + path.sep);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A Dirent reports a symlink as neither file nor directory, so links need an
|
|
119
|
+
* explicit stat. Following them is not optional: symlinking a mod into the
|
|
120
|
+
* Paradox `mod/` folder is the standard Linux workflow, and Windows junctions
|
|
121
|
+
* do the same for a redirected Documents folder. Entries keep the LINK path so
|
|
122
|
+
* definitions stay attributed to the root the user configured.
|
|
123
|
+
*
|
|
124
|
+
* Two guards keep a malformed tree from looping or double-indexing: a link
|
|
125
|
+
* resolving to the directory it sits in (or one of its ancestors) is skipped
|
|
126
|
+
* outright, and every followed target is remembered for the rest of the walk.
|
|
127
|
+
*/
|
|
128
|
+
function* followLink(full, ext, visited, tick) {
|
|
129
|
+
let target;
|
|
130
|
+
let real;
|
|
131
|
+
try {
|
|
132
|
+
target = fs.statSync(full); // follows the link; throws when dangling
|
|
133
|
+
real = fs.realpathSync(full);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return; // dangling or unreadable link indexes nothing
|
|
137
|
+
}
|
|
138
|
+
if (target.isFile()) {
|
|
139
|
+
if (path.basename(full).toLowerCase().endsWith(ext))
|
|
140
|
+
yield full;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (!target.isDirectory())
|
|
144
|
+
return;
|
|
145
|
+
let host;
|
|
146
|
+
try {
|
|
147
|
+
host = fs.realpathSync(path.dirname(full));
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (isSameOrBelow(real, host))
|
|
153
|
+
return; // points back into the tree being walked
|
|
154
|
+
const key = norm(real);
|
|
155
|
+
if (visited.has(key))
|
|
156
|
+
return;
|
|
157
|
+
visited.add(key);
|
|
158
|
+
yield* walk(full, ext, visited, tick);
|
|
159
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Script properties whose right-hand side is a localization key.
|
|
3
|
+
*
|
|
4
|
+
* BROAD: properties that often hold a loc key; a resolved inlay hint is shown
|
|
5
|
+
* when the value exists in the loc index, silence otherwise (these keys also
|
|
6
|
+
* hold non-loc values, e.g. `name` on a title history entry).
|
|
7
|
+
*
|
|
8
|
+
* STRICT: properties that virtually always hold a loc key; an unresolved value
|
|
9
|
+
* here renders a `missing loc` hint.
|
|
10
|
+
*/
|
|
11
|
+
export declare const STRICT_LOC_PROPERTIES: Set<string>;
|
|
12
|
+
export declare const BROAD_LOC_PROPERTIES: Set<string>;
|
|
13
|
+
export declare function isLocProperty(prop: string): "strict" | "broad" | null;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Script properties whose right-hand side is a localization key.
|
|
4
|
+
*
|
|
5
|
+
* BROAD: properties that often hold a loc key; a resolved inlay hint is shown
|
|
6
|
+
* when the value exists in the loc index, silence otherwise (these keys also
|
|
7
|
+
* hold non-loc values, e.g. `name` on a title history entry).
|
|
8
|
+
*
|
|
9
|
+
* STRICT: properties that virtually always hold a loc key; an unresolved value
|
|
10
|
+
* here renders a `missing loc` hint.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.BROAD_LOC_PROPERTIES = exports.STRICT_LOC_PROPERTIES = void 0;
|
|
14
|
+
exports.isLocProperty = isLocProperty;
|
|
15
|
+
exports.STRICT_LOC_PROPERTIES = new Set([
|
|
16
|
+
"title",
|
|
17
|
+
"desc",
|
|
18
|
+
"flavor",
|
|
19
|
+
"custom_tooltip",
|
|
20
|
+
"confirm_text",
|
|
21
|
+
"confirm_title",
|
|
22
|
+
"prompt",
|
|
23
|
+
"failure_desc",
|
|
24
|
+
"success_desc",
|
|
25
|
+
]);
|
|
26
|
+
exports.BROAD_LOC_PROPERTIES = new Set([
|
|
27
|
+
...exports.STRICT_LOC_PROPERTIES,
|
|
28
|
+
"name",
|
|
29
|
+
"text",
|
|
30
|
+
"tooltip",
|
|
31
|
+
"first_valid",
|
|
32
|
+
"reason",
|
|
33
|
+
"format",
|
|
34
|
+
"header",
|
|
35
|
+
"opinion_text",
|
|
36
|
+
"what",
|
|
37
|
+
"who",
|
|
38
|
+
]);
|
|
39
|
+
function isLocProperty(prop) {
|
|
40
|
+
const p = prop.toLowerCase();
|
|
41
|
+
if (exports.STRICT_LOC_PROPERTIES.has(p))
|
|
42
|
+
return "strict";
|
|
43
|
+
if (exports.BROAD_LOC_PROPERTIES.has(p))
|
|
44
|
+
return "broad";
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface LocKeyRef {
|
|
2
|
+
prop: string;
|
|
3
|
+
key: string;
|
|
4
|
+
/** Character range of the key on the line. */
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
strictness: "strict" | "broad";
|
|
8
|
+
}
|
|
9
|
+
export declare function findLocKeyRefs(lineText: string): LocKeyRef[];
|
|
10
|
+
/** The loc key defined on the given line of a loc yml, if any. */
|
|
11
|
+
export declare function locKeyOnLine(lineText: string): string | null;
|
package/dist/locRefs.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findLocKeyRefs = findLocKeyRefs;
|
|
4
|
+
exports.locKeyOnLine = locKeyOnLine;
|
|
5
|
+
/**
|
|
6
|
+
* Line-level detection of localization-key references in script and of key
|
|
7
|
+
* definitions in loc yml. Used by the server (inlay hints, code actions) and
|
|
8
|
+
* the client (loc reference tracker), so it lives in shared/.
|
|
9
|
+
*
|
|
10
|
+
* No `vscode` imports here: this module is unit-tested in plain Node.
|
|
11
|
+
*/
|
|
12
|
+
const locProperties_1 = require("./locProperties");
|
|
13
|
+
const PROP_VALUE = /([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*("?)([A-Za-z_][A-Za-z0-9_.-]*)\2/g;
|
|
14
|
+
function findLocKeyRefs(lineText) {
|
|
15
|
+
const refs = [];
|
|
16
|
+
PROP_VALUE.lastIndex = 0;
|
|
17
|
+
let m;
|
|
18
|
+
while ((m = PROP_VALUE.exec(lineText)) !== null) {
|
|
19
|
+
const strictness = (0, locProperties_1.isLocProperty)(m[1]);
|
|
20
|
+
if (!strictness)
|
|
21
|
+
continue;
|
|
22
|
+
const end = m.index + m[0].length - (m[2] === '"' ? 1 : 0);
|
|
23
|
+
refs.push({ prop: m[1], key: m[3], start: end - m[3].length, end, strictness });
|
|
24
|
+
}
|
|
25
|
+
return refs;
|
|
26
|
+
}
|
|
27
|
+
/** The loc key defined on the given line of a loc yml, if any. */
|
|
28
|
+
function locKeyOnLine(lineText) {
|
|
29
|
+
const m = /^\s*([A-Za-z0-9_.\-']+):\d*\s*"/.exec(lineText.replace(/^/, ""));
|
|
30
|
+
return m ? m[1] : null;
|
|
31
|
+
}
|
package/dist/modName.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readModName = readModName;
|
|
37
|
+
/**
|
|
38
|
+
* One display name for a mod folder, whichever descriptor convention it uses.
|
|
39
|
+
* Every surface that names a mod (setup report, sidebar, pickers, the Project
|
|
40
|
+
* view, hover origins) goes through this, so a mod is called what its author
|
|
41
|
+
* called it instead of "3385002128".
|
|
42
|
+
*/
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const descriptorMod_1 = require("./descriptorMod");
|
|
45
|
+
const descriptorMetadata_1 = require("./descriptorMetadata");
|
|
46
|
+
/**
|
|
47
|
+
* The mod's display name: the launcher descriptor's `name=`, else
|
|
48
|
+
* `.metadata/metadata.json`'s `name`, else the folder's own name. Never null,
|
|
49
|
+
* so callers need no fallback of their own.
|
|
50
|
+
*/
|
|
51
|
+
function readModName(dir) {
|
|
52
|
+
return (0, descriptorMod_1.readDescriptorName)(dir) ?? (0, descriptorMetadata_1.readMetadataName)(dir) ?? path.basename(dir);
|
|
53
|
+
}
|