@zzzen/pyright-internal 1.2.0-dev.20251228 → 1.2.0-dev.20260111

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 (107) hide show
  1. package/dist/analyzer/backgroundAnalysisProgram.d.ts +3 -3
  2. package/dist/analyzer/backgroundAnalysisProgram.js +18 -5
  3. package/dist/analyzer/backgroundAnalysisProgram.js.map +1 -1
  4. package/dist/analyzer/binder.d.ts +1 -0
  5. package/dist/analyzer/binder.js +22 -0
  6. package/dist/analyzer/binder.js.map +1 -1
  7. package/dist/analyzer/declaration.d.ts +1 -0
  8. package/dist/analyzer/declaration.js.map +1 -1
  9. package/dist/analyzer/docStringUtils.d.ts +1 -0
  10. package/dist/analyzer/docStringUtils.js +36 -0
  11. package/dist/analyzer/docStringUtils.js.map +1 -1
  12. package/dist/analyzer/importStatementUtils.d.ts +2 -1
  13. package/dist/analyzer/importStatementUtils.js +4 -3
  14. package/dist/analyzer/importStatementUtils.js.map +1 -1
  15. package/dist/analyzer/parseTreeUtils.js +2 -2
  16. package/dist/analyzer/parseTreeUtils.js.map +1 -1
  17. package/dist/analyzer/program.d.ts +5 -0
  18. package/dist/analyzer/program.js +39 -14
  19. package/dist/analyzer/program.js.map +1 -1
  20. package/dist/analyzer/pythonPathUtils.d.ts +1 -1
  21. package/dist/analyzer/service.d.ts +3 -2
  22. package/dist/analyzer/service.js +42 -12
  23. package/dist/analyzer/service.js.map +1 -1
  24. package/dist/analyzer/sourceMapper.d.ts +1 -1
  25. package/dist/analyzer/typeDocStringUtils.d.ts +4 -1
  26. package/dist/analyzer/typeDocStringUtils.js +9 -6
  27. package/dist/analyzer/typeDocStringUtils.js.map +1 -1
  28. package/dist/analyzer/typeEvaluator.js +16 -0
  29. package/dist/analyzer/typeEvaluator.js.map +1 -1
  30. package/dist/analyzer/types.d.ts +1 -1
  31. package/dist/backgroundAnalysisBase.d.ts +2 -0
  32. package/dist/backgroundAnalysisBase.js.map +1 -1
  33. package/dist/commands/dumpFileDebugInfoCommand.d.ts +0 -96
  34. package/dist/commands/dumpFileDebugInfoCommand.js +6 -966
  35. package/dist/commands/dumpFileDebugInfoCommand.js.map +1 -1
  36. package/dist/common/console.d.ts +16 -3
  37. package/dist/common/console.js +25 -4
  38. package/dist/common/console.js.map +1 -1
  39. package/dist/common/core.d.ts +2 -0
  40. package/dist/common/core.js +11 -0
  41. package/dist/common/core.js.map +1 -1
  42. package/dist/common/docStringService.d.ts +5 -2
  43. package/dist/common/docStringService.js +6 -2
  44. package/dist/common/docStringService.js.map +1 -1
  45. package/dist/common/languageInfoUtils.d.ts +114 -0
  46. package/dist/common/languageInfoUtils.js +1004 -0
  47. package/dist/common/languageInfoUtils.js.map +1 -0
  48. package/dist/common/logTracker.d.ts +6 -4
  49. package/dist/common/logTracker.js +9 -6
  50. package/dist/common/logTracker.js.map +1 -1
  51. package/dist/common/pathConsts.d.ts +1 -0
  52. package/dist/common/pathConsts.js +2 -1
  53. package/dist/common/pathConsts.js.map +1 -1
  54. package/dist/common/realFileSystem.js +16 -10
  55. package/dist/common/realFileSystem.js.map +1 -1
  56. package/dist/common/serviceProvider.d.ts +2 -0
  57. package/dist/common/serviceProvider.js +23 -1
  58. package/dist/common/serviceProvider.js.map +1 -1
  59. package/dist/common/serviceProviderExtensions.js +5 -0
  60. package/dist/common/serviceProviderExtensions.js.map +1 -1
  61. package/dist/common/uri/baseUri.d.ts +1 -1
  62. package/dist/common/uri/uri.d.ts +2 -50
  63. package/dist/common/uri/uri.js.map +1 -1
  64. package/dist/common/uri/uriInterface.d.ts +51 -0
  65. package/dist/common/uri/uriInterface.js +10 -0
  66. package/dist/common/uri/uriInterface.js.map +1 -0
  67. package/dist/languageServerBase.js +2 -2
  68. package/dist/languageServerBase.js.map +1 -1
  69. package/dist/localization/package.nls.cs.json +18 -18
  70. package/dist/localization/package.nls.de.json +18 -18
  71. package/dist/localization/package.nls.es.json +18 -18
  72. package/dist/localization/package.nls.fr.json +18 -18
  73. package/dist/localization/package.nls.it.json +18 -18
  74. package/dist/localization/package.nls.ja.json +18 -18
  75. package/dist/localization/package.nls.ko.json +18 -18
  76. package/dist/localization/package.nls.pl.json +18 -18
  77. package/dist/localization/package.nls.pt-br.json +18 -18
  78. package/dist/localization/package.nls.ru.json +18 -18
  79. package/dist/localization/package.nls.tr.json +18 -18
  80. package/dist/localization/package.nls.zh-cn.json +18 -18
  81. package/dist/localization/package.nls.zh-tw.json +18 -18
  82. package/dist/tests/completions.test.js +47 -0
  83. package/dist/tests/completions.test.js.map +1 -1
  84. package/dist/tests/fourSlashParser.test.js +259 -12
  85. package/dist/tests/fourSlashParser.test.js.map +1 -1
  86. package/dist/tests/fourslash/findDefinitions.typedDict.keys.fourslash.js +9 -0
  87. package/dist/tests/fourslash/findDefinitions.typedDict.keys.fourslash.js.map +1 -1
  88. package/dist/tests/harness/fourslash/fourSlashParser.js +484 -27
  89. package/dist/tests/harness/fourslash/fourSlashParser.js.map +1 -1
  90. package/dist/tests/harness/fourslash/fourSlashRawUtils.d.ts +12 -0
  91. package/dist/tests/harness/fourslash/fourSlashRawUtils.js +151 -0
  92. package/dist/tests/harness/fourslash/fourSlashRawUtils.js.map +1 -0
  93. package/dist/tests/harness/fourslash/fourSlashTypes.d.ts +73 -2
  94. package/dist/tests/harness/fourslash/fourSlashTypes.js +27 -1
  95. package/dist/tests/harness/fourslash/fourSlashTypes.js.map +1 -1
  96. package/dist/tests/harness/fourslash/testState.js +1 -1
  97. package/dist/tests/harness/fourslash/testState.js.map +1 -1
  98. package/dist/tests/harness/vfs/factory.js +3 -3
  99. package/dist/tests/harness/vfs/factory.js.map +1 -1
  100. package/dist/tests/languageServer.test.js +3 -3
  101. package/dist/tests/languageServer.test.js.map +1 -1
  102. package/dist/tests/lsp/languageServer.js +10 -1
  103. package/dist/tests/lsp/languageServer.js.map +1 -1
  104. package/dist/tests/lsp/languageServerTestUtils.d.ts +3 -1
  105. package/dist/tests/lsp/languageServerTestUtils.js +24 -2
  106. package/dist/tests/lsp/languageServerTestUtils.js.map +1 -1
  107. package/package.json +3 -3
@@ -14,6 +14,7 @@ const pathUtils_1 = require("../../../common/pathUtils");
14
14
  const uriUtils_1 = require("../../../common/uri/uriUtils");
15
15
  const factory_1 = require("../vfs/factory");
16
16
  const fourSlashTypes_1 = require("./fourSlashTypes");
17
+ const fourSlashRawUtils_1 = require("./fourSlashRawUtils");
17
18
  /**
18
19
  * Parse given fourslash markup code and return content with markup/range data
19
20
  *
@@ -24,17 +25,21 @@ const fourSlashTypes_1 = require("./fourSlashTypes");
24
25
  */
25
26
  function parseTestData(basePath, contents, fileName) {
26
27
  const normalizedBasePath = (0, pathUtils_1.normalizeSlashes)(basePath);
27
- // Regex for parsing options in the format "@Alpha: Value of any sort"
28
- const optionRegex = /^\s*@(\w+):\s*(.*)\s*/;
28
+ // Historically, many fourslash strings ended with a trailing newline (often from a closing backtick on its own
29
+ // line). Some parsing logic assumes line feeds exist for line-to-offset mapping. Normalize to ensure the
30
+ // input always ends with an LF, so callers don't need to add a trailing empty line.
31
+ const rawText = contents.endsWith('\n') ? contents : `${contents}\n`;
32
+ const rawTokens = [];
29
33
  // List of all the subfiles we've parsed out
30
34
  const files = [];
31
35
  // Global options
32
36
  const globalOptions = {};
37
+ const globalOptionsRawData = {};
33
38
  // Marker positions
34
39
  // Split up the input file by line
35
40
  // Note: IE JS engine incorrectly handles consecutive delimiters here when using RegExp split, so
36
41
  // we have to string-based splitting instead and try to figure out the delimiting chars
37
- const lines = contents.split('\n');
42
+ const lines = rawText.split('\n');
38
43
  let i = 0;
39
44
  const markerPositions = new Map();
40
45
  const markers = [];
@@ -43,6 +48,10 @@ function parseTestData(basePath, contents, fileName) {
43
48
  let currentFileContent;
44
49
  let currentFileName = (0, pathUtils_1.normalizeSlashes)(fileName);
45
50
  let currentFileOptions = {};
51
+ let currentFileOptionsRawData = {};
52
+ let currentFileTokenRanges = [];
53
+ let currentFileContentToRawSegments = [];
54
+ let lastFourSlashLineLfOffset;
46
55
  let normalizedProjectRoot = normalizedBasePath;
47
56
  function nextFile() {
48
57
  if (currentFileContent === undefined) {
@@ -55,38 +64,92 @@ function parseTestData(basePath, contents, fileName) {
55
64
  currentFileName = (0, pathUtils_1.normalizePath)((0, pathUtils_1.combinePaths)(factory_1.distlibFolder.getFilePath(), (0, pathUtils_1.getRelativePath)(currentFileName, normalizedBasePath)));
56
65
  }
57
66
  const ignoreCase = (0, core_1.toBoolean)(globalOptions["ignorecase" /* GlobalMetadataOptionNames.ignoreCase */]);
58
- const file = parseFileContent(currentFileContent, currentFileName, ignoreCase, markerPositions, markers, ranges);
67
+ const file = parseFileContent(currentFileContent, currentFileContentToRawSegments, rawTokens, currentFileName, ignoreCase, markerPositions, markers, ranges);
59
68
  file.fileOptions = currentFileOptions;
69
+ const mappingRawData = file.rawData;
70
+ file.rawData = {
71
+ tokenRanges: currentFileTokenRanges,
72
+ fileOptionsRawData: currentFileOptionsRawData,
73
+ rawToContent: mappingRawData?.rawToContent,
74
+ contentToRaw: mappingRawData?.contentToRaw,
75
+ };
60
76
  // Store result file
61
77
  files.push(file);
62
78
  currentFileContent = undefined;
63
79
  currentFileOptions = {};
80
+ currentFileOptionsRawData = {};
81
+ currentFileTokenRanges = [];
82
+ currentFileContentToRawSegments = [];
83
+ lastFourSlashLineLfOffset = undefined;
64
84
  currentFileName = fileName;
65
85
  }
66
- for (let line of lines) {
86
+ let rawOffset = 0;
87
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
88
+ const lineWithPotentialCr = lines[lineIndex];
67
89
  i++;
90
+ const lineStartRawOffset = rawOffset;
91
+ const hasLf = lineIndex < lines.length - 1;
92
+ const lineTokenStart = rawTokens.length;
93
+ const tokenizeResult = tokenizeRawLine(rawText, rawTokens, lineStartRawOffset, lineWithPotentialCr, hasLf);
94
+ const lineTokenEnd = rawTokens.length;
95
+ // Maintain legacy parsing behavior: treat CRLF as LF-delimited lines with a trailing '\r' in the line text.
96
+ let line = lineWithPotentialCr;
68
97
  if (line.length > 0 && line.charAt(line.length - 1) === '\r') {
69
98
  line = line.substr(0, line.length - 1);
70
99
  }
71
100
  if (line.substr(0, 4) === '////') {
72
101
  const text = line.substr(4);
73
- currentFileContent = currentFileContent === undefined ? text : currentFileContent + '\n' + text;
102
+ currentFileTokenRanges.push({ startToken: lineTokenStart, endToken: lineTokenEnd });
103
+ if (currentFileContent === undefined) {
104
+ currentFileContent = text;
105
+ currentFileContentToRawSegments = [
106
+ {
107
+ contentStart: 0,
108
+ contentEnd: text.length,
109
+ rawStart: lineStartRawOffset + 4,
110
+ },
111
+ ];
112
+ }
113
+ else {
114
+ const newlineContentOffset = currentFileContent.length;
115
+ currentFileContent = currentFileContent + '\n' + text;
116
+ if (lastFourSlashLineLfOffset === undefined) {
117
+ throw new Error(`Missing line feed mapping for four-slash line ending at line ${i - 1}`);
118
+ }
119
+ currentFileContentToRawSegments.push({
120
+ contentStart: newlineContentOffset,
121
+ contentEnd: newlineContentOffset + 1,
122
+ rawStart: lastFourSlashLineLfOffset,
123
+ });
124
+ const textContentStart = newlineContentOffset + 1;
125
+ currentFileContentToRawSegments.push({
126
+ contentStart: textContentStart,
127
+ contentEnd: textContentStart + text.length,
128
+ rawStart: lineStartRawOffset + 4,
129
+ });
130
+ }
131
+ // Record the raw offset of the '\n' for this line (used if another four-slash line follows).
132
+ lastFourSlashLineLfOffset = hasLf ? lineStartRawOffset + lineWithPotentialCr.length : undefined;
74
133
  }
75
134
  else if (line.substr(0, 3) === '///' && currentFileContent !== undefined) {
76
135
  throw new Error(`Three-slash line in the middle of four-slash region at line ${i}`);
77
136
  }
78
137
  else if (line.substr(0, 2) === '//') {
79
138
  // Comment line, check for global/file @options and record them
80
- const match = optionRegex.exec(line.substr(2));
81
- if (match) {
82
- const key = match[1].toLowerCase();
83
- const value = match[2];
139
+ const directive = tryParseOptionDirective(line.substr(2));
140
+ if (directive) {
141
+ const key = directive.key.toLowerCase();
142
+ const value = directive.value;
143
+ const directiveRawData = tokenizeResult.directiveRawData;
84
144
  if (!(0, collectionUtils_1.contains)(fourSlashTypes_1.fileMetadataNames, key)) {
85
145
  // Check if the match is already existed in the global options
86
146
  if (globalOptions[key] !== undefined) {
87
147
  throw new Error(`Global option '${key}' already exists`);
88
148
  }
89
149
  globalOptions[key] = value;
150
+ if (directiveRawData) {
151
+ globalOptionsRawData[key] = directiveRawData;
152
+ }
90
153
  if (key === "projectroot" /* GlobalMetadataOptionNames.projectRoot */) {
91
154
  normalizedProjectRoot = (0, pathUtils_1.combinePaths)(normalizedBasePath, value);
92
155
  }
@@ -101,11 +164,17 @@ function parseTestData(basePath, contents, fileName) {
101
164
  ? normalizedPath
102
165
  : (0, pathUtils_1.combinePaths)(normalizedProjectRoot, normalizedPath);
103
166
  currentFileOptions[key] = value;
167
+ if (directiveRawData) {
168
+ currentFileOptionsRawData[key] = directiveRawData;
169
+ }
104
170
  break;
105
171
  }
106
172
  default:
107
173
  // Add other fileMetadata flag
108
174
  currentFileOptions[key] = value;
175
+ if (directiveRawData) {
176
+ currentFileOptionsRawData[key] = directiveRawData;
177
+ }
109
178
  }
110
179
  }
111
180
  }
@@ -117,14 +186,265 @@ function parseTestData(basePath, contents, fileName) {
117
186
  // Code line, terminate current subfile if there is one
118
187
  nextFile();
119
188
  }
189
+ rawOffset += lineWithPotentialCr.length + (hasLf ? 1 : 0);
120
190
  }
121
191
  return {
122
192
  markerPositions,
123
193
  markers,
124
194
  globalOptions,
195
+ globalOptionsRawData,
125
196
  files,
126
197
  ranges,
198
+ rawText,
199
+ rawTokens,
200
+ };
201
+ }
202
+ function tryParseOptionDirective(textAfterTwoSlash) {
203
+ // Matches the legacy behavior of: /^\s*@(\w+):\s*(.*)\s*/
204
+ let i = 0;
205
+ while (i < textAfterTwoSlash.length && /\s/.test(textAfterTwoSlash[i])) {
206
+ i++;
207
+ }
208
+ if (i >= textAfterTwoSlash.length || textAfterTwoSlash[i] !== '@') {
209
+ return undefined;
210
+ }
211
+ i++;
212
+ const nameStart = i;
213
+ while (i < textAfterTwoSlash.length && /\w/.test(textAfterTwoSlash[i])) {
214
+ i++;
215
+ }
216
+ if (i === nameStart) {
217
+ return undefined;
218
+ }
219
+ const key = textAfterTwoSlash.substring(nameStart, i);
220
+ while (i < textAfterTwoSlash.length && /\s/.test(textAfterTwoSlash[i])) {
221
+ i++;
222
+ }
223
+ if (i >= textAfterTwoSlash.length || textAfterTwoSlash[i] !== ':') {
224
+ return undefined;
225
+ }
226
+ i++;
227
+ while (i < textAfterTwoSlash.length && /\s/.test(textAfterTwoSlash[i])) {
228
+ i++;
229
+ }
230
+ const valueStart = i;
231
+ let valueEnd = textAfterTwoSlash.length;
232
+ while (valueEnd > valueStart && /\s/.test(textAfterTwoSlash[valueEnd - 1])) {
233
+ valueEnd--;
234
+ }
235
+ const value = textAfterTwoSlash.substring(valueStart, valueEnd);
236
+ return { key, value };
237
+ }
238
+ function tokenizeRawLine(rawText, rawTokens, lineStartRawOffset, lineTextIncludingOptionalCR, hasLf) {
239
+ const lineTokenStart = rawTokens.length;
240
+ const hasCr = lineTextIncludingOptionalCR.length > 0 && lineTextIncludingOptionalCR.endsWith('\r');
241
+ const lineBody = hasCr
242
+ ? lineTextIncludingOptionalCR.substring(0, lineTextIncludingOptionalCR.length - 1)
243
+ : lineTextIncludingOptionalCR;
244
+ const lineBodyStart = lineStartRawOffset;
245
+ const lineBodyEnd = lineStartRawOffset + lineBody.length;
246
+ let directiveRawData;
247
+ const push = (kind, start, end) => {
248
+ if (start < end) {
249
+ rawTokens.push({ kind, start, end });
250
+ }
127
251
  };
252
+ const tokenizePlain = (start, end) => {
253
+ let pos = start;
254
+ while (pos < end) {
255
+ const ch = rawText[pos];
256
+ if (ch === ' ' || ch === '\t') {
257
+ const wsStart = pos;
258
+ pos++;
259
+ while (pos < end && (rawText[pos] === ' ' || rawText[pos] === '\t')) {
260
+ pos++;
261
+ }
262
+ push("whitespace" /* RawTokenKind.Whitespace */, wsStart, pos);
263
+ }
264
+ else {
265
+ const textStart = pos;
266
+ pos++;
267
+ while (pos < end && rawText[pos] !== ' ' && rawText[pos] !== '\t') {
268
+ pos++;
269
+ }
270
+ push("text" /* RawTokenKind.Text */, textStart, pos);
271
+ }
272
+ }
273
+ };
274
+ if (lineBody.startsWith('////')) {
275
+ push("fourSlashPrefix" /* RawTokenKind.FourSlashPrefix */, lineBodyStart, lineBodyStart + 4);
276
+ tokenizeFourSlashRemainder(rawText, rawTokens, lineBodyStart + 4, lineBodyEnd);
277
+ }
278
+ else if (lineBody.startsWith('//')) {
279
+ const prefixStartToken = rawTokens.length;
280
+ push("twoSlashPrefix" /* RawTokenKind.TwoSlashPrefix */, lineBodyStart, lineBodyStart + 2);
281
+ const afterPrefixStart = lineBodyStart + 2;
282
+ const afterPrefixText = rawText.substring(afterPrefixStart, lineBodyEnd);
283
+ const directive = tryParseOptionDirective(afterPrefixText);
284
+ if (!directive) {
285
+ tokenizePlain(afterPrefixStart, lineBodyEnd);
286
+ }
287
+ else {
288
+ const isWhitespaceNotNewline = (ch) => /\s/.test(ch) && ch !== '\r' && ch !== '\n';
289
+ const consumeWhitespaceTokens = (pos, stopChar) => {
290
+ while (pos < lineBodyEnd &&
291
+ isWhitespaceNotNewline(rawText[pos]) &&
292
+ (stopChar === undefined || rawText[pos] !== stopChar)) {
293
+ const wsStart = pos;
294
+ pos++;
295
+ while (pos < lineBodyEnd &&
296
+ isWhitespaceNotNewline(rawText[pos]) &&
297
+ (stopChar === undefined || rawText[pos] !== stopChar)) {
298
+ pos++;
299
+ }
300
+ push("whitespace" /* RawTokenKind.Whitespace */, wsStart, pos);
301
+ }
302
+ return pos;
303
+ };
304
+ // Tokenize with directive structure. This keeps token spans aligned to the stored value.
305
+ let pos = afterPrefixStart;
306
+ // Leading whitespace.
307
+ pos = consumeWhitespaceTokens(pos);
308
+ const atStartToken = rawTokens.length;
309
+ push("directiveAt" /* RawTokenKind.DirectiveAt */, pos, pos + 1);
310
+ pos++;
311
+ const nameStart = pos;
312
+ while (pos < lineBodyEnd && /\w/.test(rawText[pos])) {
313
+ pos++;
314
+ }
315
+ const nameTokenIndex = rawTokens.length;
316
+ push("directiveName" /* RawTokenKind.DirectiveName */, nameStart, pos);
317
+ // Whitespace before ':'
318
+ pos = consumeWhitespaceTokens(pos, ':');
319
+ const colonTokenIndex = rawTokens.length;
320
+ push("directiveColon" /* RawTokenKind.DirectiveColon */, pos, pos + 1);
321
+ pos++;
322
+ // Whitespace after ':'
323
+ pos = consumeWhitespaceTokens(pos);
324
+ const valueStart = pos;
325
+ let valueEnd = lineBodyEnd;
326
+ while (valueEnd > valueStart && /\s/.test(rawText[valueEnd - 1])) {
327
+ valueEnd--;
328
+ }
329
+ const valueTokenStart = rawTokens.length;
330
+ push("directiveValue" /* RawTokenKind.DirectiveValue */, valueStart, valueEnd);
331
+ const valueTokenEnd = rawTokens.length;
332
+ // Trailing whitespace.
333
+ tokenizePlain(valueEnd, lineBodyEnd);
334
+ directiveRawData = {
335
+ directiveLine: { startToken: lineTokenStart, endToken: -1 },
336
+ prefix: { startToken: prefixStartToken, endToken: prefixStartToken + 1 },
337
+ name: { startToken: atStartToken, endToken: nameTokenIndex + 1 },
338
+ colon: { startToken: colonTokenIndex, endToken: colonTokenIndex + 1 },
339
+ value: { startToken: valueTokenStart, endToken: valueTokenEnd },
340
+ };
341
+ }
342
+ }
343
+ else {
344
+ tokenizePlain(lineBodyStart, lineBodyEnd);
345
+ }
346
+ // CR and LF are tokenized separately.
347
+ if (hasCr) {
348
+ push("newlineCR" /* RawTokenKind.NewLineCR */, lineBodyEnd, lineBodyEnd + 1);
349
+ }
350
+ if (hasLf) {
351
+ const lfStart = lineStartRawOffset + lineTextIncludingOptionalCR.length;
352
+ push("newlineLF" /* RawTokenKind.NewLineLF */, lfStart, lfStart + 1);
353
+ }
354
+ const lineTokenEnd = rawTokens.length;
355
+ if (directiveRawData) {
356
+ directiveRawData.directiveLine.endToken = lineTokenEnd;
357
+ }
358
+ return { directiveRawData };
359
+ }
360
+ function tokenizeFourSlashRemainder(rawText, rawTokens, start, end) {
361
+ const push = (kind, s, e) => {
362
+ if (s < e) {
363
+ rawTokens.push({ kind, start: s, end: e });
364
+ }
365
+ };
366
+ const validMarkerChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz$1234567890_';
367
+ let pos = start;
368
+ while (pos < end) {
369
+ const ch = rawText[pos];
370
+ if (ch === ' ' || ch === '\t') {
371
+ const wsStart = pos;
372
+ pos++;
373
+ while (pos < end && (rawText[pos] === ' ' || rawText[pos] === '\t')) {
374
+ pos++;
375
+ }
376
+ push("whitespace" /* RawTokenKind.Whitespace */, wsStart, pos);
377
+ continue;
378
+ }
379
+ // Range delimiters.
380
+ if (pos + 1 < end && rawText[pos] === '[' && rawText[pos + 1] === '|') {
381
+ push("rangeStart" /* RawTokenKind.RangeStart */, pos, pos + 2);
382
+ pos += 2;
383
+ continue;
384
+ }
385
+ if (pos + 1 < end && rawText[pos] === '|' && rawText[pos + 1] === ']') {
386
+ push("rangeEnd" /* RawTokenKind.RangeEnd */, pos, pos + 2);
387
+ pos += 2;
388
+ continue;
389
+ }
390
+ // Object markers.
391
+ if (pos + 1 < end && rawText[pos] === '{' && rawText[pos + 1] === '|') {
392
+ const closeIndex = rawText.indexOf('|}', pos + 2);
393
+ if (closeIndex >= 0 && closeIndex + 2 <= end) {
394
+ push("objectMarkerStart" /* RawTokenKind.ObjectMarkerStart */, pos, pos + 2);
395
+ if (pos + 2 < closeIndex) {
396
+ push("objectMarkerText" /* RawTokenKind.ObjectMarkerText */, pos + 2, closeIndex);
397
+ }
398
+ push("objectMarkerEnd" /* RawTokenKind.ObjectMarkerEnd */, closeIndex, closeIndex + 2);
399
+ pos = closeIndex + 2;
400
+ continue;
401
+ }
402
+ }
403
+ // Slash-star markers.
404
+ if (pos + 1 < end && rawText[pos] === '/' && rawText[pos + 1] === '*') {
405
+ const closeIndex = rawText.indexOf('*/', pos + 2);
406
+ if (closeIndex >= 0 && closeIndex + 2 <= end) {
407
+ let isValidMarker = true;
408
+ for (let j = pos + 2; j < closeIndex; j++) {
409
+ if (validMarkerChars.indexOf(rawText[j]) < 0) {
410
+ isValidMarker = false;
411
+ break;
412
+ }
413
+ }
414
+ if (isValidMarker) {
415
+ push("markerStart" /* RawTokenKind.MarkerStart */, pos, pos + 2);
416
+ if (pos + 2 < closeIndex) {
417
+ push("markerName" /* RawTokenKind.MarkerName */, pos + 2, closeIndex);
418
+ }
419
+ push("markerEnd" /* RawTokenKind.MarkerEnd */, closeIndex, closeIndex + 2);
420
+ pos = closeIndex + 2;
421
+ continue;
422
+ }
423
+ }
424
+ }
425
+ // Plain text chunk until whitespace or a known delimiter.
426
+ const textStart = pos;
427
+ pos++;
428
+ while (pos < end) {
429
+ const c = rawText[pos];
430
+ if (c === ' ' || c === '\t') {
431
+ break;
432
+ }
433
+ if (pos + 1 < end) {
434
+ const c2 = rawText[pos + 1];
435
+ if ((c === '[' && c2 === '|') ||
436
+ (c === '|' && c2 === ']') ||
437
+ (c === '{' && c2 === '|') ||
438
+ (c === '|' && c2 === '}') ||
439
+ (c === '/' && c2 === '*') ||
440
+ (c === '*' && c2 === '/')) {
441
+ break;
442
+ }
443
+ }
444
+ pos++;
445
+ }
446
+ push("text" /* RawTokenKind.Text */, textStart, pos);
447
+ }
128
448
  }
129
449
  var State;
130
450
  (function (State) {
@@ -136,37 +456,43 @@ function reportError(fileName, line, col, message) {
136
456
  const errorMessage = `${fileName}(${line},${col}): ${message}`;
137
457
  throw new Error(errorMessage);
138
458
  }
139
- function recordObjectMarker(fileName, ignoreCase, location, text, markerMap, markers) {
459
+ function recordObjectMarker(fileName, ignoreCase, location, text, markerMap, markers, rawData) {
140
460
  let markerValue;
141
461
  try {
142
462
  // Attempt to parse the marker value as JSON
143
463
  markerValue = JSON.parse('{ ' + text + ' }');
144
464
  }
145
465
  catch (e) {
146
- reportError(fileName, location.sourceLine, location.sourceColumn, `Unable to parse marker text ${e.message}`);
466
+ const message = e instanceof Error ? e.message : String(e);
467
+ reportError(fileName, location.sourceLine, location.sourceColumn, `Unable to parse marker text ${message}`);
147
468
  }
148
- if (markerValue === undefined) {
469
+ if (markerValue === undefined || markerValue === null || typeof markerValue !== 'object') {
149
470
  reportError(fileName, location.sourceLine, location.sourceColumn, 'Object markers can not be empty');
150
471
  return undefined;
151
472
  }
473
+ const markerData = markerValue;
152
474
  const marker = {
153
475
  fileName,
154
476
  fileUri: uriUtils_1.UriEx.file(fileName, !ignoreCase),
155
477
  position: location.position,
156
- data: markerValue,
478
+ data: markerData,
479
+ rawData,
157
480
  };
158
481
  // Object markers can be anonymous
159
- if (markerValue.name) {
160
- markerMap.set(markerValue.name, marker);
482
+ const markerNameValue = markerValue.name;
483
+ if (markerNameValue) {
484
+ // Preserve legacy behavior: this may not be a string at runtime.
485
+ markerMap.set(markerNameValue, marker);
161
486
  }
162
487
  markers.push(marker);
163
488
  return marker;
164
489
  }
165
- function recordMarker(fileName, ignoreCase, location, name, markerMap, markers) {
490
+ function recordMarker(fileName, ignoreCase, location, name, markerMap, markers, rawData) {
166
491
  const marker = {
167
492
  fileName,
168
493
  fileUri: uriUtils_1.UriEx.file(fileName, !ignoreCase),
169
494
  position: location.position,
495
+ rawData,
170
496
  };
171
497
  // Verify markers for uniqueness
172
498
  if (markerMap.has(name)) {
@@ -180,12 +506,14 @@ function recordMarker(fileName, ignoreCase, location, name, markerMap, markers)
180
506
  return marker;
181
507
  }
182
508
  }
183
- function parseFileContent(content, fileName, ignoreCase, markerMap, markers, ranges) {
184
- content = chompLeadingSpace(content);
509
+ function parseFileContent(content, contentToRawSegments, rawTokens, fileName, ignoreCase, markerMap, markers, ranges) {
510
+ ({ content, segments: contentToRawSegments } = chompLeadingSpaceWithMapping(content, contentToRawSegments));
185
511
  // Any slash-star comment with a character not in this string is not a marker.
186
512
  const validMarkerChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz$1234567890_';
187
513
  /// The file content (minus metacharacters) so far
188
514
  let output = '';
515
+ // Mapping segments for the final FourSlashFile.content -> rawText offsets.
516
+ const contentToRawOutputSegments = [];
189
517
  /// The current marker (or maybe multi-line comment?) we're parsing, possibly
190
518
  let openMarker;
191
519
  /// A stack of the open range markers that are still unclosed
@@ -202,9 +530,13 @@ function parseFileContent(content, fileName, ignoreCase, markerMap, markers, ran
202
530
  let line = 1;
203
531
  let column = 1;
204
532
  const flush = (lastSafeCharIndex) => {
205
- output =
206
- output +
207
- content.substr(lastNormalCharPosition, lastSafeCharIndex === undefined ? undefined : lastSafeCharIndex - lastNormalCharPosition);
533
+ const safeIndex = lastSafeCharIndex ?? content.length;
534
+ if (safeIndex <= lastNormalCharPosition) {
535
+ return;
536
+ }
537
+ const outputStart = output.length;
538
+ output = output + content.substring(lastNormalCharPosition, safeIndex);
539
+ appendOutputMappingSegments(contentToRawSegments, lastNormalCharPosition, safeIndex, outputStart, contentToRawOutputSegments);
208
540
  };
209
541
  if (content.length > 0) {
210
542
  let previousChar = content.charAt(0);
@@ -214,11 +546,13 @@ function parseFileContent(content, fileName, ignoreCase, markerMap, markers, ran
214
546
  case 0 /* State.none */:
215
547
  if (previousChar === '[' && currentChar === '|') {
216
548
  // found a range start
549
+ const rawOpen = getRawSpanFromContentSpan(contentToRawSegments, i - 1, i + 1);
217
550
  openRanges.push({
218
551
  position: i - 1 - difference,
219
552
  sourcePosition: i - 1,
220
553
  sourceLine: line,
221
554
  sourceColumn: column,
555
+ rawOpen,
222
556
  });
223
557
  // copy all text up to marker position
224
558
  flush(i - 1);
@@ -231,12 +565,23 @@ function parseFileContent(content, fileName, ignoreCase, markerMap, markers, ran
231
565
  if (!rangeStart) {
232
566
  reportError(fileName, line, column, 'Found range end with no matching start.');
233
567
  }
568
+ const rawClose = getRawSpanFromContentSpan(contentToRawSegments, i - 1, i + 1);
569
+ const rawSelectedStart = rangeStart.rawOpen?.rawEnd ?? rawClose.rawStart;
570
+ const rawSelectedEnd = rawClose.rawStart;
571
+ const rawFullStart = rangeStart.rawOpen?.rawStart ?? rawClose.rawStart;
572
+ const rawFullEnd = rawClose.rawEnd;
234
573
  const range = {
235
574
  fileName,
236
575
  fileUri: uriUtils_1.UriEx.file(fileName, !ignoreCase),
237
576
  pos: rangeStart.position,
238
577
  end: i - 1 - difference,
239
578
  marker: rangeStart.marker,
579
+ rawData: {
580
+ full: getTokenRangeCoveringRawSpan(rawTokens, rawFullStart, rawFullEnd),
581
+ open: getTokenRangeCoveringRawSpan(rawTokens, rangeStart.rawOpen?.rawStart ?? rawClose.rawStart, rangeStart.rawOpen?.rawEnd ?? rawClose.rawStart),
582
+ selected: getTokenRangeCoveringRawSpan(rawTokens, rawSelectedStart, rawSelectedEnd),
583
+ close: getTokenRangeCoveringRawSpan(rawTokens, rawClose.rawStart, rawClose.rawEnd),
584
+ },
240
585
  };
241
586
  localRanges.push(range);
242
587
  // copy all text up to range marker position
@@ -269,9 +614,19 @@ function parseFileContent(content, fileName, ignoreCase, markerMap, markers, ran
269
614
  case 2 /* State.inObjectMarker */:
270
615
  // Object markers are only ever terminated by |} and have no content restrictions
271
616
  if (previousChar === '|' && currentChar === '}') {
617
+ const rawFull = getRawSpanFromContentSpan(contentToRawSegments, openMarker.sourcePosition, i + 1);
618
+ const rawStart = getRawSpanFromContentSpan(contentToRawSegments, openMarker.sourcePosition, openMarker.sourcePosition + 2);
619
+ const rawEnd = getRawSpanFromContentSpan(contentToRawSegments, i - 1, i + 1);
620
+ const rawTextSpan = getRawSpanFromContentSpan(contentToRawSegments, openMarker.sourcePosition + 2, i - 1);
272
621
  // Record the marker
273
622
  const objectMarkerNameText = content.substring(openMarker.sourcePosition + 2, i - 1).trim();
274
- const marker = recordObjectMarker(fileName, ignoreCase, openMarker, objectMarkerNameText, markerMap, markers);
623
+ const marker = recordObjectMarker(fileName, ignoreCase, openMarker, objectMarkerNameText, markerMap, markers, {
624
+ kind: 'object',
625
+ full: getTokenRangeCoveringRawSpan(rawTokens, rawFull.rawStart, rawFull.rawEnd),
626
+ start: getTokenRangeCoveringRawSpan(rawTokens, rawStart.rawStart, rawStart.rawEnd),
627
+ text: getTokenRangeCoveringRawSpan(rawTokens, rawTextSpan.rawStart, rawTextSpan.rawEnd),
628
+ end: getTokenRangeCoveringRawSpan(rawTokens, rawEnd.rawStart, rawEnd.rawEnd),
629
+ });
275
630
  if (openRanges.length > 0) {
276
631
  openRanges[openRanges.length - 1].marker = marker;
277
632
  }
@@ -285,10 +640,20 @@ function parseFileContent(content, fileName, ignoreCase, markerMap, markers, ran
285
640
  break;
286
641
  case 1 /* State.inSlashStarMarker */:
287
642
  if (previousChar === '*' && currentChar === '/') {
643
+ const rawFull = getRawSpanFromContentSpan(contentToRawSegments, openMarker.sourcePosition, i + 1);
644
+ const rawStart = getRawSpanFromContentSpan(contentToRawSegments, openMarker.sourcePosition, openMarker.sourcePosition + 2);
645
+ const rawEnd = getRawSpanFromContentSpan(contentToRawSegments, i - 1, i + 1);
646
+ const rawNameSpan = getRawSpanFromContentSpan(contentToRawSegments, openMarker.sourcePosition + 2, i - 1);
288
647
  // Record the marker
289
648
  // start + 2 to ignore the */, -1 on the end to ignore the * (/ is next)
290
649
  const markerNameText = content.substring(openMarker.sourcePosition + 2, i - 1).trim();
291
- const marker = recordMarker(fileName, ignoreCase, openMarker, markerNameText, markerMap, markers);
650
+ const marker = recordMarker(fileName, ignoreCase, openMarker, markerNameText, markerMap, markers, {
651
+ kind: 'slashStar',
652
+ full: getTokenRangeCoveringRawSpan(rawTokens, rawFull.rawStart, rawFull.rawEnd),
653
+ start: getTokenRangeCoveringRawSpan(rawTokens, rawStart.rawStart, rawStart.rawEnd),
654
+ name: getTokenRangeCoveringRawSpan(rawTokens, rawNameSpan.rawStart, rawNameSpan.rawEnd),
655
+ end: getTokenRangeCoveringRawSpan(rawTokens, rawEnd.rawStart, rawEnd.rawEnd),
656
+ });
292
657
  if (openRanges.length > 0) {
293
658
  openRanges[openRanges.length - 1].marker = marker;
294
659
  }
@@ -342,21 +707,113 @@ function parseFileContent(content, fileName, ignoreCase, markerMap, markers, ran
342
707
  localRanges.forEach((r) => {
343
708
  ranges.push(r);
344
709
  });
710
+ const contentToRaw = { segments: contentToRawOutputSegments };
711
+ const rawToContent = { segments: contentToRawOutputSegments };
345
712
  return {
346
713
  content: output,
347
714
  fileOptions: {},
348
715
  version: 0,
349
716
  fileName,
350
717
  fileUri: uriUtils_1.UriEx.file(fileName, !ignoreCase),
718
+ rawData: {
719
+ tokenRanges: [],
720
+ rawToContent,
721
+ contentToRaw,
722
+ },
351
723
  };
352
724
  }
353
- function chompLeadingSpace(content) {
725
+ function getRawSpanFromContentSpan(segments, contentStart, contentEnd) {
726
+ if (contentStart === contentEnd) {
727
+ const rawOffset = tryGetRawOffsetFromContentIndex(segments, contentStart) ?? 0;
728
+ return { rawStart: rawOffset, rawEnd: rawOffset };
729
+ }
730
+ const rawStart = tryGetRawOffsetFromContentIndex(segments, contentStart) ?? 0;
731
+ const rawLast = tryGetRawOffsetFromContentIndex(segments, contentEnd - 1) ?? rawStart;
732
+ return { rawStart, rawEnd: rawLast + 1 };
733
+ }
734
+ function tryGetRawOffsetFromContentIndex(segments, contentIndex) {
735
+ const seg = (0, fourSlashRawUtils_1.findItemContainingOffset)(segments, contentIndex, (s) => s.contentStart, (s) => s.contentEnd);
736
+ if (!seg) {
737
+ return undefined;
738
+ }
739
+ return seg.rawStart + (contentIndex - seg.contentStart);
740
+ }
741
+ function getTokenRangeCoveringRawSpan(rawTokens, rawStart, rawEnd) {
742
+ if (rawStart === rawEnd) {
743
+ const tokenIndex = (0, fourSlashRawUtils_1.findTokenIndexAtOrAfter)(rawTokens, rawStart);
744
+ return { startToken: tokenIndex, endToken: tokenIndex };
745
+ }
746
+ const startToken = (0, fourSlashRawUtils_1.findTokenIndexAtOrAfter)(rawTokens, rawStart);
747
+ const endToken = (0, fourSlashRawUtils_1.findTokenIndexAtOrAfter)(rawTokens, rawEnd);
748
+ return { startToken, endToken };
749
+ }
750
+ function appendOutputMappingSegments(sourceSegments, sourceStart, sourceEnd, outputStart, out) {
751
+ for (const seg of sourceSegments) {
752
+ const overlapStart = Math.max(sourceStart, seg.contentStart);
753
+ const overlapEnd = Math.min(sourceEnd, seg.contentEnd);
754
+ if (overlapStart >= overlapEnd) {
755
+ continue;
756
+ }
757
+ const overlapLen = overlapEnd - overlapStart;
758
+ const rawStart = seg.rawStart + (overlapStart - seg.contentStart);
759
+ const contentStart = outputStart + (overlapStart - sourceStart);
760
+ out.push({
761
+ rawStart,
762
+ rawEnd: rawStart + overlapLen,
763
+ contentStart,
764
+ contentEnd: contentStart + overlapLen,
765
+ });
766
+ }
767
+ }
768
+ function chompLeadingSpaceWithMapping(content, contentToRawSegments) {
354
769
  const lines = content.split('\n');
355
770
  for (const line of lines) {
356
771
  if (line.length !== 0 && line.charAt(0) !== ' ') {
357
- return content;
772
+ return { content, segments: contentToRawSegments };
773
+ }
774
+ }
775
+ // Remove one leading space from each line.
776
+ const newContent = lines.map((s) => s.substr(1)).join('\n');
777
+ // Rebuild mapping segments by walking line-by-line over the original content.
778
+ const newSegments = [];
779
+ let sourcePos = 0;
780
+ let outPos = 0;
781
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
782
+ const line = lines[lineIndex];
783
+ const lineStart = sourcePos;
784
+ const lineEnd = lineStart + line.length;
785
+ // Keep everything after the removed leading space.
786
+ if (line.length > 0) {
787
+ const keepStart = lineStart + 1;
788
+ const keepEnd = lineEnd;
789
+ appendChompedSegments(contentToRawSegments, keepStart, keepEnd, outPos, newSegments);
790
+ outPos += keepEnd - keepStart;
791
+ }
792
+ sourcePos = lineEnd;
793
+ // Keep newline except for the final line.
794
+ if (lineIndex < lines.length - 1) {
795
+ appendChompedSegments(contentToRawSegments, sourcePos, sourcePos + 1, outPos, newSegments);
796
+ sourcePos += 1;
797
+ outPos += 1;
798
+ }
799
+ }
800
+ return { content: newContent, segments: newSegments };
801
+ }
802
+ function appendChompedSegments(sourceSegments, sourceStart, sourceEnd, outputStart, out) {
803
+ for (const seg of sourceSegments) {
804
+ const overlapStart = Math.max(sourceStart, seg.contentStart);
805
+ const overlapEnd = Math.min(sourceEnd, seg.contentEnd);
806
+ if (overlapStart >= overlapEnd) {
807
+ continue;
358
808
  }
809
+ const overlapLen = overlapEnd - overlapStart;
810
+ const rawStart = seg.rawStart + (overlapStart - seg.contentStart);
811
+ const contentStart = outputStart + (overlapStart - sourceStart);
812
+ out.push({
813
+ contentStart,
814
+ contentEnd: contentStart + overlapLen,
815
+ rawStart,
816
+ });
359
817
  }
360
- return lines.map((s) => s.substr(1)).join('\n');
361
818
  }
362
819
  //# sourceMappingURL=fourSlashParser.js.map