@webgal/language-server 0.0.2-alpha.0 → 0.0.2-alpha.2

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/build/utils.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  import { n as getDiagnosticInformation, r as warningConfig, t as getState } from "./providerState-CLCXHs37.mjs";
2
- import { GlobalMap, cleartGlobalMapAll, fsAccessor, runCode, source } from "@webgal/language-core";
3
- import { DiagnosticSeverity, Position, Range } from "@volar/language-server";
2
+ import { fsAccessor, source } from "@webgal/language-core";
3
+ import { DiagnosticSeverity, FoldingRangeKind, Range } from "@volar/language-server";
4
4
  import { FileType } from "@volar/language-service";
5
+ import { URI } from "vscode-uri";
5
6
 
6
7
  //#region src/utils/index.ts
7
8
  function bindCoreFileAccessorToClientVfs(connection) {
@@ -22,20 +23,258 @@ function bindCoreFileAccessorToClientVfs(connection) {
22
23
  });
23
24
  };
24
25
  }
25
- function getVariableTypeDesc(ALL_ARR, _start_line) {
26
- let _desc_arr = [];
27
- for (let _d_index = _start_line - 2; _d_index > 0; _d_index--) {
28
- const _data = ALL_ARR[_d_index];
29
- if (_data.startsWith(";") && _data.length > 0) _desc_arr.unshift(_data.substring(1));
30
- else if (_data.length > 0) break;
31
- else continue;
26
+ const fullCodeInformation = {
27
+ completion: true,
28
+ semantic: true,
29
+ navigation: true,
30
+ structure: true,
31
+ format: true,
32
+ verification: true
33
+ };
34
+ const emptyDefinitionMap = {
35
+ label: {},
36
+ setVar: {},
37
+ choose: {}
38
+ };
39
+ const getVariableDesc = (lines, startLine) => {
40
+ const desc = [];
41
+ for (let index = startLine - 2; index > 0; index--) {
42
+ const line = lines[index];
43
+ if (line.startsWith(";") && line.length > 0) desc.unshift(line.substring(1));
44
+ else if (line.length > 0) break;
32
45
  }
33
- return _desc_arr.join("\n");
34
- }
35
- function getVariableType(expr) {
36
- if (expr.includes("$stage") || expr.includes("$userData")) return "expression";
37
- return typeof runCode(expr)();
38
- }
46
+ return desc.join("\n");
47
+ };
48
+ const analyzeWebgalText = (text) => {
49
+ const map = {
50
+ label: {},
51
+ setVar: {},
52
+ choose: {}
53
+ };
54
+ const lines = text.split(/\r?\n/);
55
+ for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) {
56
+ const currentLine = lines[lineNumber];
57
+ const setVarExec = /setVar:\s*(\w+)\s*=\s*([^;]*\S+);?/g.exec(currentLine);
58
+ const labelExec = /label:\s*(\S+);/g.exec(currentLine);
59
+ const getUserInputExec = /getUserInput:\s*([^\s;]+)/g.exec(currentLine);
60
+ const chooseExec = /choose:\s*([^\s;]+)/g.exec(currentLine);
61
+ if (setVarExec !== null) {
62
+ const currentVariablePool = map.setVar[setVarExec[1]] ??= [];
63
+ const isGlobal = currentLine.indexOf("-global") !== -1;
64
+ const currentToken = {
65
+ word: setVarExec[1],
66
+ value: setVarExec[2],
67
+ input: setVarExec.input,
68
+ isGlobal,
69
+ isGetUserInput: false,
70
+ position: {
71
+ line: lineNumber,
72
+ character: setVarExec.index + 7
73
+ },
74
+ desc: getVariableDesc(lines, lineNumber)
75
+ };
76
+ currentVariablePool.push(currentToken);
77
+ } else if (labelExec !== null) (map.label[labelExec[1]] ??= []).push({
78
+ word: labelExec[1],
79
+ value: labelExec.input,
80
+ input: labelExec.input,
81
+ position: {
82
+ line: lineNumber,
83
+ character: 6
84
+ }
85
+ });
86
+ else if (getUserInputExec !== null) (map.setVar[getUserInputExec[1]] ??= []).push({
87
+ word: getUserInputExec[1],
88
+ value: getUserInputExec.input,
89
+ input: getUserInputExec.input,
90
+ isGetUserInput: true,
91
+ position: {
92
+ line: lineNumber,
93
+ character: 13
94
+ }
95
+ });
96
+ else if (chooseExec !== null) {
97
+ const options = [];
98
+ const text = chooseExec[1];
99
+ for (const machChooseOption of text.split("|")) {
100
+ const sliceArray = machChooseOption.split(":");
101
+ options.push({
102
+ text: sliceArray[0]?.trim(),
103
+ value: sliceArray[1]?.trim()
104
+ });
105
+ }
106
+ map.choose[lineNumber] = {
107
+ options,
108
+ line: lineNumber
109
+ };
110
+ }
111
+ }
112
+ return map;
113
+ };
114
+ const getSnapshotText = (snapshot) => snapshot.getText(0, snapshot.getLength());
115
+ const buildLineStarts = (text) => {
116
+ const result = [0];
117
+ for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) result.push(i + 1);
118
+ return result;
119
+ };
120
+ const getLineFromOffset = (lineStarts, offset) => {
121
+ let low = 0;
122
+ let high = lineStarts.length - 1;
123
+ while (low <= high) {
124
+ const mid = low + high >> 1;
125
+ const start = lineStarts[mid];
126
+ const nextStart = lineStarts[mid + 1] ?? Number.POSITIVE_INFINITY;
127
+ if (offset < start) high = mid - 1;
128
+ else if (offset >= nextStart) low = mid + 1;
129
+ else return mid;
130
+ }
131
+ return 0;
132
+ };
133
+ const analyzeWebgalDocumentLinks = (lines) => {
134
+ const candidates = [];
135
+ const regex = /\$?\{?(\w+)\.(\w+)\}?/g;
136
+ for (let i = 0; i < lines.length; i++) {
137
+ const currentLine = lines[i];
138
+ const commandType = getLineCommandType(currentLine);
139
+ let match;
140
+ while (match = regex.exec(currentLine)) {
141
+ if (match[0].startsWith("$")) continue;
142
+ const matchText = match[0];
143
+ candidates.push({
144
+ line: i,
145
+ start: match.index,
146
+ end: match.index + matchText.length,
147
+ text: matchText,
148
+ command: commandType
149
+ });
150
+ if (regex.lastIndex === match.index) regex.lastIndex++;
151
+ }
152
+ }
153
+ return candidates;
154
+ };
155
+ const analyzeWebgalFoldingRanges = (text) => {
156
+ const foldingRanges = [];
157
+ const regex = /label:([\s\S]*?)(?=(?:\r?\n|^)end|(?:\r?\n|^)label:|$)/g;
158
+ const lineStarts = buildLineStarts(text);
159
+ let match;
160
+ while (match = regex.exec(text)) {
161
+ const startLine = getLineFromOffset(lineStarts, match.index);
162
+ const endOffset = match.index + match[0].length;
163
+ let endLine = getLineFromOffset(lineStarts, endOffset);
164
+ if (endOffset - lineStarts[endLine] === 0) endLine = endLine - 1;
165
+ if (endLine > startLine) foldingRanges.push({
166
+ startLine,
167
+ endLine,
168
+ collapsedText: match[1].split("\n")[0].replace(/;/g, "").trim() || "...",
169
+ kind: FoldingRangeKind.Region
170
+ });
171
+ }
172
+ return foldingRanges;
173
+ };
174
+ const createWebgalVirtualCode = (scriptId, languageId, snapshot) => {
175
+ const length = snapshot.getLength();
176
+ const text = getSnapshotText(snapshot);
177
+ const lines = text.split(/\r?\n/);
178
+ const lineCommandTypes = lines.map(getLineCommandType);
179
+ const map = languageId === "webgal" ? analyzeWebgalText(text) : emptyDefinitionMap;
180
+ const linkCandidates = analyzeWebgalDocumentLinks(lines);
181
+ const foldingRanges = analyzeWebgalFoldingRanges(text);
182
+ return {
183
+ id: scriptId.toString(),
184
+ languageId,
185
+ snapshot,
186
+ mappings: [{
187
+ sourceOffsets: [0],
188
+ generatedOffsets: [0],
189
+ lengths: [length],
190
+ data: fullCodeInformation
191
+ }],
192
+ webgalDefinitionMap: map,
193
+ webgalDocumentLinkCandidates: linkCandidates,
194
+ webgalFoldingRanges: foldingRanges,
195
+ webgalLines: lines,
196
+ webgalLineCommandTypes: lineCommandTypes
197
+ };
198
+ };
199
+ const updateWebgalVirtualCode = (virtualCode, newSnapshot) => {
200
+ const length = newSnapshot.getLength();
201
+ const mapping = virtualCode.mappings[0];
202
+ if (mapping) {
203
+ mapping.sourceOffsets[0] = 0;
204
+ mapping.generatedOffsets[0] = 0;
205
+ mapping.lengths[0] = length;
206
+ mapping.data = fullCodeInformation;
207
+ } else virtualCode.mappings = [{
208
+ sourceOffsets: [0],
209
+ generatedOffsets: [0],
210
+ lengths: [length],
211
+ data: fullCodeInformation
212
+ }];
213
+ virtualCode.snapshot = newSnapshot;
214
+ if (virtualCode.languageId === "webgal") {
215
+ const text = getSnapshotText(newSnapshot);
216
+ const lines = text.split(/\r?\n/);
217
+ virtualCode.webgalLines = lines;
218
+ virtualCode.webgalLineCommandTypes = lines.map(getLineCommandType);
219
+ virtualCode.webgalDefinitionMap = analyzeWebgalText(text);
220
+ virtualCode.webgalDocumentLinkCandidates = analyzeWebgalDocumentLinks(lines);
221
+ virtualCode.webgalFoldingRanges = analyzeWebgalFoldingRanges(text);
222
+ } else {
223
+ virtualCode.webgalDefinitionMap = emptyDefinitionMap;
224
+ virtualCode.webgalDocumentLinkCandidates = [];
225
+ virtualCode.webgalFoldingRanges = [];
226
+ virtualCode.webgalLines = [];
227
+ virtualCode.webgalLineCommandTypes = [];
228
+ }
229
+ return virtualCode;
230
+ };
231
+ const getSourceUri = (context, document) => {
232
+ const uri = URI.parse(document.uri);
233
+ const decoded = context.decodeEmbeddedDocumentUri?.(uri);
234
+ return decoded ? decoded[0] : uri;
235
+ };
236
+ const getSourceVirtualCode = (context, document) => {
237
+ const sourceUri = getSourceUri(context, document);
238
+ const script = context.language.scripts.get(sourceUri);
239
+ return {
240
+ script,
241
+ virtualCode: script?.generated?.root
242
+ };
243
+ };
244
+ const getWebgalDefinitionMap = (context, document) => {
245
+ const { virtualCode } = getSourceVirtualCode(context, document);
246
+ return virtualCode?.webgalDefinitionMap ?? emptyDefinitionMap;
247
+ };
248
+ const getWebgalDocumentLinkCandidates = (context, document) => {
249
+ const { virtualCode } = getSourceVirtualCode(context, document);
250
+ return virtualCode?.webgalDocumentLinkCandidates ?? [];
251
+ };
252
+ const getWebgalFoldingRanges = (context, document) => {
253
+ const { virtualCode } = getSourceVirtualCode(context, document);
254
+ return virtualCode?.webgalFoldingRanges ?? [];
255
+ };
256
+ const getWebgalVirtualCodeLines = (context, document) => {
257
+ const { virtualCode } = getSourceVirtualCode(context, document);
258
+ if (virtualCode?.webgalLines) return virtualCode.webgalLines;
259
+ return document.getText().split(/\r?\n/);
260
+ };
261
+ const getWebgalLineCommandTypes = (context, document) => {
262
+ const { virtualCode } = getSourceVirtualCode(context, document);
263
+ if (virtualCode?.webgalLineCommandTypes) return virtualCode.webgalLineCommandTypes;
264
+ return getWebgalVirtualCodeLines(context, document).map(getLineCommandType);
265
+ };
266
+ const getLineCommandType = (line) => {
267
+ return line.substring(0, line.indexOf(":") !== -1 ? line.indexOf(":") : line.indexOf(";"));
268
+ };
269
+ const getWebgalSourceUriString = (context, document) => {
270
+ return getSourceUri(context, document).toString();
271
+ };
272
+ const getWebgalVirtualCodeText = (context, document) => {
273
+ const uri = URI.parse(document.uri);
274
+ const snapshot = context.language.scripts.get(uri)?.snapshot;
275
+ if (snapshot) return snapshot.getText(0, snapshot.getLength());
276
+ return document.getText();
277
+ };
39
278
  /** 获取位置的指令单词 */
40
279
  function getWordAtPosition(doc, pos, charRegex) {
41
280
  const text = doc.getText();
@@ -200,61 +439,6 @@ function getStageCompletionContext(document, cursorPos, match) {
200
439
  prefix
201
440
  };
202
441
  }
203
- /** 更新全局映射表 */
204
- function updateGlobalMap(documentTextArray) {
205
- cleartGlobalMapAll();
206
- for (let lineNumber = 0; lineNumber < documentTextArray.length; lineNumber++) {
207
- const currentLine = documentTextArray[lineNumber];
208
- const setVarExec = /setVar:\s*(\w+)\s*=\s*([^;]*\S+);?/g.exec(currentLine);
209
- const labelExec = /label:\s*(\S+);/g.exec(currentLine);
210
- const getUserInputExec = /getUserInput:\s*([^\s;]+)/g.exec(currentLine);
211
- const chooseExec = /choose:\s*([^\s;]+)/g.exec(currentLine);
212
- if (setVarExec !== null) {
213
- const currentVariablePool = GlobalMap.setVar[setVarExec[1]] ??= [];
214
- const isGlobal = currentLine.indexOf("-global") === -1 ? false : true;
215
- currentVariablePool.push({
216
- word: setVarExec[1],
217
- value: setVarExec[2],
218
- input: setVarExec.input,
219
- isGlobal,
220
- isGetUserInput: false,
221
- position: Position.create(lineNumber, setVarExec.index + 7)
222
- });
223
- const currentVariableLatest = currentVariablePool[currentVariablePool.length - 1];
224
- if (currentVariableLatest && currentVariableLatest?.position) {
225
- const _v_pos = currentVariableLatest.position;
226
- currentVariableLatest.desc = getVariableTypeDesc(documentTextArray, _v_pos?.line ? _v_pos.line : -1);
227
- }
228
- } else if (labelExec !== null) (GlobalMap.label[labelExec[1]] ??= []).push({
229
- word: labelExec[1],
230
- value: labelExec.input,
231
- input: labelExec.input,
232
- position: Position.create(lineNumber, 6)
233
- });
234
- else if (getUserInputExec !== null) (GlobalMap.setVar[getUserInputExec[1]] ??= []).push({
235
- word: getUserInputExec[1],
236
- value: getUserInputExec.input,
237
- input: getUserInputExec.input,
238
- isGetUserInput: true,
239
- position: Position.create(lineNumber, 13)
240
- });
241
- else if (chooseExec !== null) {
242
- const options = [];
243
- const text = chooseExec[1];
244
- for (const machChooseOption of text.split("|")) {
245
- const sliceArray = machChooseOption.split(":");
246
- options.push({
247
- text: sliceArray[0]?.trim(),
248
- value: sliceArray[1]?.trim()
249
- });
250
- }
251
- GlobalMap.choose[lineNumber] = {
252
- options,
253
- line: lineNumber
254
- };
255
- }
256
- }
257
- }
258
442
  const defaultSettings = {
259
443
  maxNumberOfProblems: 1e3,
260
444
  isShowWarning: true,
@@ -282,10 +466,10 @@ function getDocumentSettings(connection, url) {
282
466
  }
283
467
  return result;
284
468
  }
285
- async function validateTextDocument(connection, textDocument) {
469
+ async function validateTextDocument(connection, textDocument, textOverride) {
286
470
  const settings = await getDocumentSettings(connection, textDocument.uri);
287
471
  if (!settings?.isShowWarning) return [];
288
- const text = textDocument.getText();
472
+ const text = textOverride ?? textDocument.getText();
289
473
  let m;
290
474
  let problems = 0;
291
475
  const diagnostics = [];
@@ -361,28 +545,21 @@ async function validateTextDocument(connection, textDocument) {
361
545
  return diagnostics;
362
546
  }
363
547
  function createVolarFileSystemFromVirtualFileSystem(vfs, options) {
364
- const uriToPath = options?.uriToPath ?? ((uri) => uri.scheme === "file" ? uri.fsPath : uri.path);
548
+ let cached = null;
549
+ const load = async () => {
550
+ if (cached) return cached;
551
+ cached = (await import("@webgal/language-service")).createVolarFileSystem(vfs, options);
552
+ return cached;
553
+ };
365
554
  return {
366
555
  stat: async (uri) => {
367
- const pathValue = uriToPath(uri);
368
- const info = await vfs.stat(pathValue);
369
- if (!info) return;
370
- return {
371
- type: info.isDirectory ? FileType.Directory : info.isFile ? FileType.File : FileType.Unknown,
372
- ctime: 0,
373
- mtime: 0,
374
- size: 0
375
- };
556
+ return (await load()).stat(uri);
376
557
  },
377
558
  readFile: async (uri) => {
378
- const pathValue = uriToPath(uri);
379
- return await vfs.readFile(pathValue) ?? void 0;
559
+ return (await load()).readFile(uri);
380
560
  },
381
561
  readDirectory: async (uri) => {
382
- const pathValue = uriToPath(uri);
383
- const entries = await vfs.readDirectory(pathValue);
384
- if (!entries) return [];
385
- return entries.map((entry) => [entry.name, entry.isDirectory ? FileType.Directory : FileType.File]);
562
+ return (await load()).readDirectory(uri);
386
563
  }
387
564
  };
388
565
  }
@@ -420,4 +597,4 @@ function createClientVfsFileSystem(connection, options) {
420
597
  }
421
598
 
422
599
  //#endregion
423
- export { StateConfig, bindCoreFileAccessorToClientVfs, createClientVfsFileSystem, createVolarFileSystemFromVirtualFileSystem, defaultSettings, documentSettings, findTokenRange, getDocumentSettings, getPatternAtPosition, getStageCompletionContext, getState, getTokenOrPatternAtPosition, getVariableType, getVariableTypeDesc, getWordAtPosition, globalSettings, isPathChar, listPathCandidates, setGlobalSettings, updateGlobalMap, validateTextDocument };
600
+ export { StateConfig, bindCoreFileAccessorToClientVfs, createClientVfsFileSystem, createVolarFileSystemFromVirtualFileSystem, createWebgalVirtualCode, defaultSettings, documentSettings, findTokenRange, getDocumentSettings, getPatternAtPosition, getStageCompletionContext, getState, getTokenOrPatternAtPosition, getWebgalDefinitionMap, getWebgalDocumentLinkCandidates, getWebgalFoldingRanges, getWebgalLineCommandTypes, getWebgalSourceUriString, getWebgalVirtualCodeLines, getWebgalVirtualCodeText, getWordAtPosition, globalSettings, isPathChar, listPathCandidates, setGlobalSettings, updateWebgalVirtualCode, validateTextDocument };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webgal/language-server",
3
- "version": "0.0.2-alpha.0",
3
+ "version": "0.0.2-alpha.2",
4
4
  "main": "build/index.cjs",
5
5
  "module": "build/index.mjs",
6
6
  "types": "build/index.d.mts",
@@ -54,8 +54,8 @@
54
54
  "@volar/language-server": "~2.4.0",
55
55
  "@volar/language-service": "~2.4.0",
56
56
  "@volar/typescript": "~2.4.0",
57
- "@webgal/language-core": "0.0.2-alpha.0",
58
- "@webgal/language-service": "0.0.2-alpha.0",
57
+ "@webgal/language-core": "0.0.2-alpha.1",
58
+ "@webgal/language-service": "0.0.2-alpha.3",
59
59
  "tsc-alias": "^1.8.16",
60
60
  "vscode-languageserver": "^9.0.1",
61
61
  "vscode-languageserver-textdocument": "^1.0.11",
@@ -68,5 +68,5 @@
68
68
  "tsdown": "^0.20.3",
69
69
  "tsx": "^4.21.0"
70
70
  },
71
- "gitHead": "2efa929ed7f0f62a003ab621e15e279651362d2d"
71
+ "gitHead": "ebff8c309c7b7940bdebed1dc096d99a318af3cc"
72
72
  }