scancscode 1.0.35 → 1.0.36

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 (31) hide show
  1. package/.trae/specs/fix-string-expression-capture-range/checklist.md +7 -0
  2. package/.trae/specs/fix-string-expression-capture-range/spec.md +96 -0
  3. package/.trae/specs/fix-string-expression-capture-range/tasks.md +25 -0
  4. package/.trae/specs/handle-null-value/checklist.md +10 -0
  5. package/.trae/specs/handle-null-value/spec.md +57 -0
  6. package/.trae/specs/handle-null-value/tasks.md +40 -0
  7. package/.trae/specs/handle-right-value-expression/checklist.md +9 -0
  8. package/.trae/specs/handle-right-value-expression/spec.md +72 -0
  9. package/.trae/specs/handle-right-value-expression/tasks.md +53 -0
  10. package/.trae/specs/process-function-call-interpolated-strings/checklist.md +12 -0
  11. package/.trae/specs/process-function-call-interpolated-strings/spec.md +80 -0
  12. package/.trae/specs/process-function-call-interpolated-strings/tasks.md +73 -0
  13. package/dist/src/CSharpStringExtractor.js +547 -117
  14. package/dist/test/CSharpStringExtractor.test.js +174 -0
  15. package/package.json +1 -1
  16. package/src/CSharpStringExtractor.ts +581 -116
  17. package/test/CSharpStringExtractor.test.ts +185 -0
  18. package/dist/debug-arg.js +0 -30
  19. package/dist/debug-args.js +0 -34
  20. package/dist/debug-comment-5.js +0 -25
  21. package/dist/debug-comment-strings.js +0 -24
  22. package/dist/debug-full.js +0 -14
  23. package/dist/debug-template-issue.js +0 -33
  24. package/dist/debug-test-5.js +0 -23
  25. package/dist/debug-test.js +0 -21
  26. package/dist/debug.js +0 -15
  27. package/dist/simple-debug.js +0 -27
  28. package/dist/simple-test.js +0 -61
  29. package/dist/temp-original-source.js +0 -1
  30. package/dist/test-logic.js +0 -79
  31. package/dist/test-regex.js +0 -13
@@ -181,15 +181,22 @@ class CSharpStringExtractor {
181
181
  const trimmedStatement = statement.trim();
182
182
  const isStringFormatCall = trimmedStatement.startsWith('string.Format(');
183
183
  const isTextAssignment = /\w+\.text\s*=/.test(trimmedStatement);
184
- const isRegularAssignment = /^\s*[\w\.]+\s*=/.test(trimmedStatement) && !isTextAssignment;
184
+ const isRegularAssignment = /^[\s\S]*?=/.test(trimmedStatement);
185
185
  const isCaseOrDefault = trimmedStatement.startsWith('case ') || trimmedStatement.startsWith('default:');
186
186
  const isFunctionCall = /^\s*[\w\.<>]+[\s\w\.<>]*\(/.test(trimmedStatement) && !isStringFormatCall && !isCaseOrDefault;
187
+ if ((isRegularAssignment && !isTextAssignment) && !isFunctionCall) {
188
+ const assignmentIndex = statement.indexOf('=');
189
+ if (assignmentIndex !== -1) {
190
+ this.processAllFunctionCallsInRange(originalIndex, originalIndex + assignmentIndex, code, snippets, trClass, trFormatMethod, trMethod);
191
+ }
192
+ }
187
193
  if (isFunctionCall && !isStringFormatCall) {
188
194
  this.processFunctionCallArguments(statement, originalIndex, code, snippets, trClass, trFormatMethod, trMethod);
189
195
  }
190
196
  else {
191
197
  const extractionResult = this.extractValueExpression(statement, originalIndex, code);
192
198
  const { valueExpression, valueExpressionIndex } = extractionResult;
199
+ const hasStringInValue = /"(?:[^"\\]|\\.)*"|'[^']*'|\$"[\s\S]*?"|\$@"[\s\S]*?"|@\$"[\s\S]*?"/.test(valueExpression);
193
200
  let alreadyExists = false;
194
201
  for (const snippet of snippets) {
195
202
  if (snippet.originalIndex === valueExpressionIndex) {
@@ -197,13 +204,18 @@ class CSharpStringExtractor {
197
204
  break;
198
205
  }
199
206
  }
200
- if (!alreadyExists) {
207
+ if (!alreadyExists && (hasStringInValue || isTextAssignment)) {
201
208
  const snippet = new CodeSnippet();
202
209
  snippet.originalIndex = valueExpressionIndex;
203
210
  snippet.originalCode = valueExpression;
204
211
  snippet.originalContext = valueExpression;
205
212
  this.variableIndex = 0;
206
- this.processStatementAndExtractValue(snippet, statement, valueExpression, trClass, trFormatMethod, trMethod);
213
+ if (isCaseOrDefault) {
214
+ this.processSingleArgument(snippet, valueExpression, trClass, trFormatMethod, trMethod);
215
+ }
216
+ else {
217
+ this.processStatementAndExtractValue(snippet, statement, valueExpression, trClass, trFormatMethod, trMethod);
218
+ }
207
219
  snippets.push(snippet);
208
220
  }
209
221
  }
@@ -243,6 +255,8 @@ class CSharpStringExtractor {
243
255
  let inClassBody = true;
244
256
  let inComment = false;
245
257
  let commentType = '';
258
+ let isInterpolatedString = false;
259
+ let interpolatedBraceDepth = 0;
246
260
  for (j = braceOpenIndex + 1; j < code.length; j++) {
247
261
  const char = code[j];
248
262
  const nextChar = j + 1 < code.length ? code[j + 1] : '';
@@ -288,9 +302,11 @@ class CSharpStringExtractor {
288
302
  continue;
289
303
  }
290
304
  if (inString) {
291
- if (char === stringDelimiter) {
305
+ if (char === stringDelimiter && (!isInterpolatedString || interpolatedBraceDepth === 0)) {
292
306
  inString = false;
293
307
  stringDelimiter = '';
308
+ isInterpolatedString = false;
309
+ interpolatedBraceDepth = 0;
294
310
  if (afterEqual && braceDepth === 1) {
295
311
  const stringLiteral = code.substring(stringStartPos, j + 1);
296
312
  let alreadyExists = false;
@@ -311,11 +327,20 @@ class CSharpStringExtractor {
311
327
  }
312
328
  }
313
329
  }
330
+ else if (isInterpolatedString) {
331
+ if (char === '{' && !escapeNext) {
332
+ interpolatedBraceDepth++;
333
+ }
334
+ else if (char === '}' && !escapeNext && interpolatedBraceDepth > 0) {
335
+ interpolatedBraceDepth--;
336
+ }
337
+ }
314
338
  continue;
315
339
  }
316
340
  if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '$') {
317
341
  if (code[j + 1] === '"') {
318
342
  inString = true;
343
+ isInterpolatedString = true;
319
344
  stringDelimiter = '"';
320
345
  stringStartPos = j;
321
346
  j++;
@@ -323,6 +348,7 @@ class CSharpStringExtractor {
323
348
  }
324
349
  else if (code[j + 1] === '@' && j + 2 < code.length && code[j + 2] === '"') {
325
350
  inString = true;
351
+ isInterpolatedString = true;
326
352
  stringDelimiter = '"';
327
353
  stringStartPos = j;
328
354
  j += 2;
@@ -331,14 +357,16 @@ class CSharpStringExtractor {
331
357
  }
332
358
  else if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '@' && code[j + 1] === '$' && j + 2 < code.length && code[j + 2] === '"') {
333
359
  inString = true;
360
+ isInterpolatedString = true;
334
361
  stringDelimiter = '"';
335
362
  stringStartPos = j;
336
363
  j += 2;
337
364
  continue;
338
365
  }
339
- else if (!inString && (char === '"' || char === "'")) {
366
+ else if (!inString && char === '"') {
340
367
  if (afterEqual && braceDepth === 1) {
341
368
  inString = true;
369
+ isInterpolatedString = false;
342
370
  stringDelimiter = char;
343
371
  stringStartPos = j;
344
372
  }
@@ -406,6 +434,8 @@ class CSharpStringExtractor {
406
434
  let commentType = '';
407
435
  let inAnonymousFunction = false;
408
436
  let anonymousFunctionBraceDepth = 0;
437
+ let isInterpolatedString = false;
438
+ let interpolatedBraceDepth = 0;
409
439
  for (j = braceOpenIndex + 1; j < code.length; j++) {
410
440
  const char = code[j];
411
441
  const nextChar = j + 1 < code.length ? code[j + 1] : '';
@@ -457,9 +487,11 @@ class CSharpStringExtractor {
457
487
  continue;
458
488
  }
459
489
  if (inString) {
460
- if (char === stringDelimiter) {
490
+ if (char === stringDelimiter && (!isInterpolatedString || interpolatedBraceDepth === 0)) {
461
491
  inString = false;
462
492
  stringDelimiter = '';
493
+ isInterpolatedString = false;
494
+ interpolatedBraceDepth = 0;
463
495
  if (afterEqual || inAnonymousFunction) {
464
496
  const stringLiteral = code.substring(stringStartPos, j + 1);
465
497
  let alreadyExists = false;
@@ -480,11 +512,20 @@ class CSharpStringExtractor {
480
512
  }
481
513
  }
482
514
  }
515
+ else if (isInterpolatedString) {
516
+ if (char === '{' && !escapeNext) {
517
+ interpolatedBraceDepth++;
518
+ }
519
+ else if (char === '}' && !escapeNext && interpolatedBraceDepth > 0) {
520
+ interpolatedBraceDepth--;
521
+ }
522
+ }
483
523
  continue;
484
524
  }
485
525
  if (!inString && (afterEqual || inAnonymousFunction) && j + 1 < code.length && char === '$') {
486
526
  if (code[j + 1] === '"') {
487
527
  inString = true;
528
+ isInterpolatedString = true;
488
529
  stringDelimiter = '"';
489
530
  stringStartPos = j;
490
531
  j++;
@@ -492,6 +533,7 @@ class CSharpStringExtractor {
492
533
  }
493
534
  else if (code[j + 1] === '@' && j + 2 < code.length && code[j + 2] === '"') {
494
535
  inString = true;
536
+ isInterpolatedString = true;
495
537
  stringDelimiter = '"';
496
538
  stringStartPos = j;
497
539
  j += 2;
@@ -500,14 +542,16 @@ class CSharpStringExtractor {
500
542
  }
501
543
  else if (!inString && (afterEqual || inAnonymousFunction) && j + 1 < code.length && char === '@' && code[j + 1] === '$' && j + 2 < code.length && code[j + 2] === '"') {
502
544
  inString = true;
545
+ isInterpolatedString = true;
503
546
  stringDelimiter = '"';
504
547
  stringStartPos = j;
505
548
  j += 2;
506
549
  continue;
507
550
  }
508
- else if (!inString && (char === '"' || char === "'")) {
551
+ else if (!inString && char === '"') {
509
552
  if (afterEqual || inAnonymousFunction) {
510
553
  inString = true;
554
+ isInterpolatedString = false;
511
555
  stringDelimiter = char;
512
556
  stringStartPos = j;
513
557
  }
@@ -555,13 +599,11 @@ class CSharpStringExtractor {
555
599
  if (i + 1 < code.length && code[i] === '/' && code[i + 1] === '/') {
556
600
  commentType = '//';
557
601
  if (i + 2 < code.length && code[i + 2] === '/') {
558
- // 这是文档注释(三个或更多连续 /),跳过它
559
602
  isDocComment = true;
560
603
  inComment = true;
561
604
  i += 3;
562
605
  }
563
606
  else {
564
- // 普通注释,正常处理
565
607
  inComment = true;
566
608
  i += 2;
567
609
  }
@@ -576,7 +618,6 @@ class CSharpStringExtractor {
576
618
  continue;
577
619
  }
578
620
  if (isDocComment) {
579
- // 对于文档注释,我们只需要跳过,不处理任何内容!
580
621
  while (i < code.length && inComment) {
581
622
  const char = code[i];
582
623
  if (char === '\n') {
@@ -592,9 +633,11 @@ class CSharpStringExtractor {
592
633
  let stringDelimiter = '';
593
634
  let stringStartPos = -1;
594
635
  let interpolatedBraceDepth = 0;
636
+ let isInterpolatedString = false;
595
637
  while (i < code.length && inComment) {
596
638
  const char = code[i];
597
639
  const nextChar = i + 1 < code.length ? code[i + 1] : '';
640
+ const nextNextChar = i + 2 < code.length ? code[i + 2] : '';
598
641
  if (commentType === '//') {
599
642
  if (char === '\n') {
600
643
  inComment = false;
@@ -619,9 +662,11 @@ class CSharpStringExtractor {
619
662
  continue;
620
663
  }
621
664
  if (inString) {
622
- if (char === stringDelimiter) {
665
+ if (char === stringDelimiter && (!isInterpolatedString || interpolatedBraceDepth === 0)) {
623
666
  inString = false;
624
667
  stringDelimiter = '';
668
+ isInterpolatedString = false;
669
+ interpolatedBraceDepth = 0;
625
670
  let alreadyExists = false;
626
671
  for (const snippet of snippets) {
627
672
  if (snippet.originalIndex === stringStartPos) {
@@ -640,12 +685,21 @@ class CSharpStringExtractor {
640
685
  snippets.push(snippet);
641
686
  }
642
687
  }
688
+ else if (isInterpolatedString) {
689
+ if (char === '{' && !escapeNext) {
690
+ interpolatedBraceDepth++;
691
+ }
692
+ else if (char === '}' && !escapeNext && interpolatedBraceDepth > 0) {
693
+ interpolatedBraceDepth--;
694
+ }
695
+ }
643
696
  i++;
644
697
  continue;
645
698
  }
646
699
  if (!inString && i + 1 < code.length && char === '$') {
647
700
  if (code[i + 1] === '"') {
648
701
  inString = true;
702
+ isInterpolatedString = true;
649
703
  stringDelimiter = '"';
650
704
  stringStartPos = i;
651
705
  i += 2;
@@ -653,6 +707,7 @@ class CSharpStringExtractor {
653
707
  }
654
708
  else if (code[i + 1] === '@' && i + 2 < code.length && code[i + 2] === '"') {
655
709
  inString = true;
710
+ isInterpolatedString = true;
656
711
  stringDelimiter = '"';
657
712
  stringStartPos = i;
658
713
  i += 3;
@@ -661,13 +716,15 @@ class CSharpStringExtractor {
661
716
  }
662
717
  else if (!inString && i + 1 < code.length && char === '@' && code[i + 1] === '$' && i + 2 < code.length && code[i + 2] === '"') {
663
718
  inString = true;
719
+ isInterpolatedString = true;
664
720
  stringDelimiter = '"';
665
721
  stringStartPos = i;
666
722
  i += 3;
667
723
  continue;
668
724
  }
669
- else if (!inString && (char === '"' || char === "'")) {
725
+ else if (!inString && char === '"') {
670
726
  inString = true;
727
+ isInterpolatedString = false;
671
728
  stringDelimiter = char;
672
729
  stringStartPos = i;
673
730
  i++;
@@ -1684,11 +1741,15 @@ class CSharpStringExtractor {
1684
1741
  processTextAssignments(statement, snippet, trClass, trFormatMethod, trMethod) {
1685
1742
  const textAssignmentRegex = /([\s\S]*?\.text\s*=\s*)([\s\S]*?)(?=;|$)/;
1686
1743
  const match = textAssignmentRegex.exec(statement);
1687
- if (!match) {
1688
- return statement;
1744
+ let prefix = null;
1745
+ let value;
1746
+ if (match) {
1747
+ prefix = match[1];
1748
+ value = match[2];
1749
+ }
1750
+ else {
1751
+ value = statement;
1689
1752
  }
1690
- const prefix = match[1];
1691
- const value = match[2];
1692
1753
  let processedValue = value;
1693
1754
  if (value.includes('?') && value.includes(':')) {
1694
1755
  const trueBranchMatch = value.match(/\?\s*([^:]+)\s*:/);
@@ -1734,7 +1795,7 @@ class CSharpStringExtractor {
1734
1795
  else {
1735
1796
  const trimmedValue = value.trim();
1736
1797
  if (!trimmedValue.includes(`${trClass}.${trFormatMethod}(`) && !trimmedValue.endsWith(`.${trMethod}()`)) {
1737
- if (trimmedValue.startsWith('"') || /^\w+/.test(trimmedValue) || trimmedValue.startsWith('(')) {
1798
+ if (trimmedValue !== 'null' && (trimmedValue.startsWith('"') || /^\w+/.test(trimmedValue) || trimmedValue.startsWith('('))) {
1738
1799
  const whitespaceBefore = value.substring(0, value.search(/\S/));
1739
1800
  const actualValue = value.substring(value.search(/\S/));
1740
1801
  const whitespaceAfter = actualValue.search(/\s*$/) === 0 ? actualValue : actualValue.substring(actualValue.search(/\S/) + trimmedValue.length);
@@ -1742,12 +1803,17 @@ class CSharpStringExtractor {
1742
1803
  }
1743
1804
  }
1744
1805
  }
1745
- if (processedValue !== value) {
1746
- const hasSemicolon = statement.trim().endsWith(';');
1747
- const result = prefix + processedValue;
1748
- return hasSemicolon ? result + ';' : result;
1806
+ if (prefix) {
1807
+ if (processedValue !== value) {
1808
+ const hasSemicolon = statement.trim().endsWith(';');
1809
+ const result = prefix + processedValue;
1810
+ return hasSemicolon ? result + ';' : result;
1811
+ }
1812
+ return statement;
1813
+ }
1814
+ else {
1815
+ return processedValue;
1749
1816
  }
1750
- return statement;
1751
1817
  }
1752
1818
  processStringConcatenation(statement, snippet, trClass, trFormatMethod, trMethod) {
1753
1819
  if (!statement.includes('+')) {
@@ -1801,10 +1867,66 @@ class CSharpStringExtractor {
1801
1867
  }
1802
1868
  const hasSemicolon = statement.endsWith(';');
1803
1869
  let statementWithoutSemicolon = hasSemicolon ? statement.slice(0, -1) : statement;
1804
- const assignmentMatch = statementWithoutSemicolon.match(/(.*=\s*)([\s\S]*)/);
1805
- if (assignmentMatch) {
1806
- const leftSide = assignmentMatch[1];
1807
- const rightSide = assignmentMatch[2];
1870
+ // Find actual assignment operator = that's not inside string, parentheses, etc.
1871
+ let assignmentIndex = -1;
1872
+ let tempInString = false;
1873
+ let tempEscapeNext = false;
1874
+ let tempStringDelimiter = '';
1875
+ let parenDepth = 0;
1876
+ let braceDepth = 0;
1877
+ let bracketDepth = 0;
1878
+ for (let i = 0; i < statementWithoutSemicolon.length; i++) {
1879
+ const char = statementWithoutSemicolon[i];
1880
+ if (tempEscapeNext) {
1881
+ tempEscapeNext = false;
1882
+ continue;
1883
+ }
1884
+ if (char === '\\') {
1885
+ tempEscapeNext = true;
1886
+ continue;
1887
+ }
1888
+ if (tempInString) {
1889
+ if (char === tempStringDelimiter) {
1890
+ tempInString = false;
1891
+ tempStringDelimiter = '';
1892
+ }
1893
+ continue;
1894
+ }
1895
+ if (char === '"' || char === "'") {
1896
+ tempInString = true;
1897
+ tempStringDelimiter = char;
1898
+ continue;
1899
+ }
1900
+ if (char === '(') {
1901
+ parenDepth++;
1902
+ }
1903
+ else if (char === ')') {
1904
+ parenDepth--;
1905
+ }
1906
+ else if (char === '{') {
1907
+ braceDepth++;
1908
+ }
1909
+ else if (char === '}') {
1910
+ braceDepth--;
1911
+ }
1912
+ else if (char === '[') {
1913
+ bracketDepth++;
1914
+ }
1915
+ else if (char === ']') {
1916
+ bracketDepth--;
1917
+ }
1918
+ else if (char === '=' && parenDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
1919
+ assignmentIndex = i;
1920
+ break;
1921
+ }
1922
+ }
1923
+ let leftSide = '';
1924
+ let rightSide = '';
1925
+ if (assignmentIndex !== -1) {
1926
+ leftSide = statementWithoutSemicolon.substring(0, assignmentIndex + 1); // Include =
1927
+ rightSide = statementWithoutSemicolon.substring(assignmentIndex + 1);
1928
+ }
1929
+ if (assignmentIndex !== -1) {
1808
1930
  const parts = this.splitExpression(rightSide);
1809
1931
  const processedParts = [];
1810
1932
  for (let i = 0; i < parts.length; i++) {
@@ -1840,7 +1962,7 @@ class CSharpStringExtractor {
1840
1962
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
1841
1963
  processedParts.push(whitespaceBefore + trimmedPart + `.${trMethod}()` + whitespaceAfter);
1842
1964
  }
1843
- else if (trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
1965
+ else if (trimmedPart !== 'null' && trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
1844
1966
  const whitespaceBefore = part.substring(0, part.search(/\S/));
1845
1967
  const actualPart = part.substring(part.search(/\S/));
1846
1968
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
@@ -1886,7 +2008,7 @@ class CSharpStringExtractor {
1886
2008
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
1887
2009
  processedParts.push(whitespaceBefore + trimmedPart + `.${trMethod}()` + whitespaceAfter);
1888
2010
  }
1889
- else if (trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
2011
+ else if (trimmedPart !== 'null' && trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
1890
2012
  if (trimmedPart.includes(`${trClass}.${trFormatMethod}(`)) {
1891
2013
  processedParts.push(part);
1892
2014
  continue;
@@ -2172,6 +2294,87 @@ class CSharpStringExtractor {
2172
2294
  }
2173
2295
  }
2174
2296
  }
2297
+ let startInStatement = -1;
2298
+ for (let i = 0; i < statement.length - 1; i++) {
2299
+ const char = statement[i];
2300
+ const nextChar = i + 1 < statement.length ? statement[i + 1] : '';
2301
+ const nextNextChar = i + 2 < statement.length ? statement[i + 2] : '';
2302
+ if ((char === '$' && nextChar === '"') ||
2303
+ (char === '$' && nextChar === '@' && nextNextChar === '"') ||
2304
+ (char === '@' && nextChar === '$' && nextNextChar === '"')) {
2305
+ startInStatement = i;
2306
+ break;
2307
+ }
2308
+ }
2309
+ if (startInStatement === -1) {
2310
+ for (let i = 0; i < statement.length; i++) {
2311
+ if (statement[i] === '"' || statement[i] === "'") {
2312
+ startInStatement = i;
2313
+ break;
2314
+ }
2315
+ }
2316
+ }
2317
+ if (startInStatement !== -1) {
2318
+ const finalIndex = statementIndex + startInStatement;
2319
+ let stringEnd = startInStatement;
2320
+ let inString = false;
2321
+ let escapeNext = false;
2322
+ let stringDelimiter = '';
2323
+ let interpolatedBraceDepth = 0;
2324
+ let j = startInStatement;
2325
+ const char = statement[j];
2326
+ const nextChar = j + 1 < statement.length ? statement[j + 1] : '';
2327
+ const nextNextChar = j + 2 < statement.length ? statement[j + 2] : '';
2328
+ if ((char === '$' && nextChar === '"')) {
2329
+ inString = true;
2330
+ stringDelimiter = '"';
2331
+ j += 2;
2332
+ }
2333
+ else if (char === '$' && nextChar === '@' && nextNextChar === '"') {
2334
+ inString = true;
2335
+ stringDelimiter = '"';
2336
+ j += 3;
2337
+ }
2338
+ else if (char === '@' && nextChar === '$' && nextNextChar === '"') {
2339
+ inString = true;
2340
+ stringDelimiter = '"';
2341
+ j += 3;
2342
+ }
2343
+ else if (char === '"' || char === "'") {
2344
+ inString = true;
2345
+ stringDelimiter = char;
2346
+ j++;
2347
+ }
2348
+ while (j < statement.length && inString) {
2349
+ const currentChar = statement[j];
2350
+ if (escapeNext) {
2351
+ escapeNext = false;
2352
+ }
2353
+ else if (currentChar === '\\') {
2354
+ escapeNext = true;
2355
+ }
2356
+ else if (currentChar === stringDelimiter && interpolatedBraceDepth === 0) {
2357
+ inString = false;
2358
+ j++;
2359
+ stringEnd = j;
2360
+ break;
2361
+ }
2362
+ else if (currentChar === '{' && !escapeNext) {
2363
+ interpolatedBraceDepth++;
2364
+ }
2365
+ else if (currentChar === '}' && !escapeNext && interpolatedBraceDepth > 0) {
2366
+ interpolatedBraceDepth--;
2367
+ }
2368
+ j++;
2369
+ }
2370
+ if (!inString) {
2371
+ const valueExpression = statement.substring(startInStatement, stringEnd).trim();
2372
+ return {
2373
+ valueExpression,
2374
+ valueExpressionIndex: finalIndex
2375
+ };
2376
+ }
2377
+ }
2175
2378
  return {
2176
2379
  valueExpression: trimmedStatement,
2177
2380
  valueExpressionIndex: statementIndex
@@ -2238,121 +2441,348 @@ class CSharpStringExtractor {
2238
2441
  return result;
2239
2442
  }
2240
2443
  processStatementAndExtractValue(snippet, fullStatement, valueExpression, trClass, trFormatMethod, trMethod) {
2241
- let processedFullStatement = fullStatement;
2242
- this.extractTrFormatStrings(processedFullStatement, snippet, trClass, trFormatMethod);
2243
- processedFullStatement = this.processStringTemplates(processedFullStatement, snippet, trClass, trFormatMethod);
2244
- processedFullStatement = this.processStringFormat(processedFullStatement, snippet, trClass, trFormatMethod);
2245
- processedFullStatement = this.processStringConcatenation(processedFullStatement, snippet, trClass, trFormatMethod, trMethod);
2246
- processedFullStatement = this.processTextAssignments(processedFullStatement, snippet, trClass, trFormatMethod, trMethod);
2247
- this.extractPlainStrings(processedFullStatement, snippet, trClass, trFormatMethod);
2444
+ let processedValueExpression = valueExpression;
2445
+ const isTextAssignment = /\w+\.text\s*=/.test(fullStatement.trim());
2446
+ this.extractTrFormatStrings(processedValueExpression, snippet, trClass, trFormatMethod);
2447
+ processedValueExpression = this.processStringTemplates(processedValueExpression, snippet, trClass, trFormatMethod);
2448
+ processedValueExpression = this.processStringFormat(processedValueExpression, snippet, trClass, trFormatMethod);
2449
+ processedValueExpression = this.processStringConcatenation(processedValueExpression, snippet, trClass, trFormatMethod, trMethod);
2450
+ if (isTextAssignment) {
2451
+ processedValueExpression = this.processTextAssignments(processedValueExpression, snippet, trClass, trFormatMethod, trMethod);
2452
+ }
2453
+ this.extractPlainStrings(processedValueExpression, snippet, trClass, trFormatMethod);
2248
2454
  const regex = new RegExp(`${trClass}\\.${trFormatMethod}\\(([^)]*)\\)\\.${trMethod}\\(\\)`, 'g');
2249
- processedFullStatement = processedFullStatement.replace(regex, `${trClass}.${trFormatMethod}($1)`);
2455
+ processedValueExpression = processedValueExpression.replace(regex, `${trClass}.${trFormatMethod}($1)`);
2250
2456
  snippet.finalizeLiterals();
2251
- let convertedValueExpression = valueExpression;
2252
- const trimmedFullStatement = fullStatement.trim();
2253
- const trimmedProcessedStatement = processedFullStatement.trim();
2254
- const isCaseOrDefault = trimmedFullStatement.startsWith('case ') || trimmedFullStatement.startsWith('default:');
2255
- if (/^return\s/.test(trimmedFullStatement)) {
2256
- // 检查 processedFullStatement 是否真的被修改过
2257
- // 如果没有被修改过,直接使用原始 valueExpression 即可
2258
- if (fullStatement === processedFullStatement) {
2259
- convertedValueExpression = valueExpression;
2260
- }
2261
- else {
2262
- // 对于 return 语句,我们不需要复杂地使用 extractValueUntilSemicolon 来重新解析!
2263
- // 我们已经处理完整个语句了,直接截取 return 后面的所有内容,然后去掉末尾的分号即可!
2264
- let returnIndex = trimmedProcessedStatement.indexOf('return');
2265
- let valuePart = trimmedProcessedStatement.substring(returnIndex + 'return'.length);
2266
- valuePart = valuePart.trim();
2267
- if (valuePart.endsWith(';')) {
2268
- valuePart = valuePart.slice(0, -1).trim();
2269
- }
2270
- convertedValueExpression = valuePart;
2271
- }
2457
+ let convertedValueExpression = processedValueExpression;
2458
+ if (convertedValueExpression.endsWith(';')) {
2459
+ convertedValueExpression = convertedValueExpression.slice(0, -1).trim();
2272
2460
  }
2273
- else {
2274
- const textAssignmentIndex = trimmedFullStatement.indexOf('.text =');
2275
- if (textAssignmentIndex !== -1) {
2276
- if (fullStatement === processedFullStatement) {
2277
- convertedValueExpression = valueExpression;
2461
+ snippet.convertedCode = convertedValueExpression;
2462
+ }
2463
+ processAllFunctionCallsInRange(startIndex, endIndex, fullCode, snippets, trClass, trFormatMethod, trMethod) {
2464
+ let i = startIndex;
2465
+ while (i < endIndex) {
2466
+ let actualParenOpenIndex = -1;
2467
+ let tempDepth = 0;
2468
+ let tempInString = false;
2469
+ let tempEscapeNext = false;
2470
+ let tempStringDelimiter = '';
2471
+ let isStringFormat = false;
2472
+ let functionNameStart = -1;
2473
+ for (let j = i; j < endIndex; j++) {
2474
+ const char = fullCode[j];
2475
+ const nextChar = j + 1 < fullCode.length ? fullCode[j + 1] : '';
2476
+ const nextNextChar = j + 2 < fullCode.length ? fullCode[j + 2] : '';
2477
+ if (tempEscapeNext) {
2478
+ tempEscapeNext = false;
2479
+ continue;
2278
2480
  }
2279
- else {
2280
- const prefix = trimmedFullStatement.substring(0, textAssignmentIndex + '.text ='.length);
2281
- const valuePart = trimmedProcessedStatement.substring(textAssignmentIndex + '.text ='.length);
2282
- const value = this.extractValueUntilSemicolon(valuePart);
2283
- convertedValueExpression = value.trim();
2481
+ if (char === '\\') {
2482
+ tempEscapeNext = true;
2483
+ continue;
2284
2484
  }
2285
- }
2286
- else {
2287
- const assignmentIndex = trimmedFullStatement.indexOf('=');
2288
- if (assignmentIndex !== -1) {
2289
- if (fullStatement === processedFullStatement) {
2290
- convertedValueExpression = valueExpression;
2485
+ if (tempInString) {
2486
+ if (char === tempStringDelimiter) {
2487
+ tempInString = false;
2488
+ tempStringDelimiter = '';
2489
+ }
2490
+ continue;
2491
+ }
2492
+ if ((char === '$' && nextChar === '"') ||
2493
+ (char === '$' && nextChar === '@' && nextNextChar === '"') ||
2494
+ (char === '@' && nextChar === '$' && nextNextChar === '"')) {
2495
+ tempInString = true;
2496
+ tempStringDelimiter = '"';
2497
+ if (nextChar === '@') {
2498
+ j += 2;
2291
2499
  }
2292
2500
  else {
2293
- const prefix = trimmedFullStatement.substring(0, assignmentIndex + 1);
2294
- const valuePart = trimmedProcessedStatement.substring(assignmentIndex + 1);
2295
- const value = this.extractValueUntilSemicolon(valuePart);
2296
- convertedValueExpression = value.trim();
2501
+ j += 1;
2297
2502
  }
2503
+ continue;
2298
2504
  }
2299
- else if (valueExpression.startsWith('string.Format(')) {
2300
- const processedMatch = trimmedProcessedStatement.match(/^(Tr\.Format[\s\S]*?)(;|$)/);
2301
- if (processedMatch) {
2302
- convertedValueExpression = processedMatch[1].trim();
2303
- }
2505
+ if (char === '"' || char === "'") {
2506
+ tempInString = true;
2507
+ tempStringDelimiter = char;
2508
+ continue;
2304
2509
  }
2305
- else if (isCaseOrDefault) {
2306
- if (fullStatement === processedFullStatement) {
2307
- convertedValueExpression = valueExpression;
2510
+ if (char === '(') {
2511
+ tempDepth++;
2512
+ if (actualParenOpenIndex === -1) {
2513
+ actualParenOpenIndex = j;
2514
+ if (functionNameStart !== -1) {
2515
+ const funcName = fullCode.substring(functionNameStart, j).trim();
2516
+ isStringFormat = funcName === 'string.Format';
2517
+ }
2308
2518
  }
2309
- else {
2310
- const trFormatIndex = trimmedProcessedStatement.indexOf('Tr.Format(');
2311
- if (trFormatIndex !== -1) {
2312
- const parenOpenIndex = trFormatIndex + 'Tr.Format('.length - 1;
2313
- const parenCloseIndex = this.findMatchingParenthesis(trimmedProcessedStatement, parenOpenIndex);
2314
- if (parenCloseIndex !== -1) {
2315
- convertedValueExpression = trimmedProcessedStatement.substring(trFormatIndex, parenCloseIndex + 1);
2519
+ }
2520
+ else if (char === ')') {
2521
+ tempDepth--;
2522
+ if (tempDepth === 0 && actualParenOpenIndex !== -1 && !isStringFormat) {
2523
+ const actualParenCloseIndex = j;
2524
+ let currentSnippetCount = 0;
2525
+ const stringLiteralPositions = [];
2526
+ let k = actualParenOpenIndex;
2527
+ let inString3 = false;
2528
+ let escapeNext3 = false;
2529
+ let stringDelimiter3 = '';
2530
+ let stringStart = -1;
2531
+ while (k < fullCode.length && k <= actualParenCloseIndex) {
2532
+ const char = fullCode[k];
2533
+ const nextChar = k + 1 < fullCode.length ? fullCode[k + 1] : '';
2534
+ const nextNextChar = k + 2 < fullCode.length ? fullCode[k + 2] : '';
2535
+ if (escapeNext3) {
2536
+ escapeNext3 = false;
2537
+ k++;
2538
+ continue;
2316
2539
  }
2317
- else {
2318
- convertedValueExpression = valueExpression;
2540
+ if (char === '\\') {
2541
+ escapeNext3 = true;
2542
+ k++;
2543
+ continue;
2319
2544
  }
2320
- }
2321
- else {
2322
- const parenOpenIndex = trimmedFullStatement.indexOf('(');
2323
- if (parenOpenIndex !== -1) {
2324
- const parenCloseIndex = this.findMatchingParenthesis(trimmedProcessedStatement, parenOpenIndex);
2325
- if (parenCloseIndex !== -1) {
2326
- convertedValueExpression = trimmedProcessedStatement.substring(parenOpenIndex + 1, parenCloseIndex).trim();
2545
+ if (inString3) {
2546
+ if (char === stringDelimiter3) {
2547
+ stringLiteralPositions.push(stringStart);
2548
+ inString3 = false;
2549
+ stringDelimiter3 = '';
2550
+ }
2551
+ k++;
2552
+ continue;
2553
+ }
2554
+ if ((char === '$' && nextChar === '"') ||
2555
+ (char === '$' && nextChar === '@' && nextNextChar === '"') ||
2556
+ (char === '@' && nextChar === '$' && nextNextChar === '"')) {
2557
+ inString3 = true;
2558
+ stringDelimiter3 = '"';
2559
+ stringStart = k;
2560
+ if (nextChar === '@') {
2561
+ k += 2;
2327
2562
  }
2328
2563
  else {
2329
- convertedValueExpression = valueExpression;
2564
+ k += 1;
2330
2565
  }
2566
+ continue;
2567
+ }
2568
+ if (char === '"' || char === "'") {
2569
+ inString3 = true;
2570
+ stringDelimiter3 = char;
2571
+ stringStart = k;
2572
+ k++;
2573
+ continue;
2574
+ }
2575
+ if (char === ')' && k === actualParenCloseIndex) {
2576
+ break;
2577
+ }
2578
+ k++;
2579
+ }
2580
+ let argStartPos = actualParenOpenIndex + 1;
2581
+ const trimmedCode = fullCode.substring(actualParenOpenIndex + 1, actualParenCloseIndex);
2582
+ const args = this.splitArguments(trimmedCode);
2583
+ for (const arg of args) {
2584
+ const trimmedArg = arg.trim();
2585
+ if (!trimmedArg) {
2586
+ argStartPos = this.updateArgStartPos(argStartPos, fullCode, actualParenCloseIndex);
2587
+ continue;
2588
+ }
2589
+ const hasStringLiteral = /"(?:[^"\\]|\\.)*"/.test(trimmedArg) ||
2590
+ /\$"[\s\S]*?"/.test(trimmedArg) ||
2591
+ /\$@"[\s\S]*?"/.test(trimmedArg) ||
2592
+ /@\$"[\s\S]*?"/.test(trimmedArg);
2593
+ while (argStartPos < fullCode.length && /\s/.test(fullCode[argStartPos])) {
2594
+ argStartPos++;
2595
+ }
2596
+ let foundArgEnd = argStartPos;
2597
+ let inString4 = false;
2598
+ let escapeNext4 = false;
2599
+ let stringDelimiter4 = '';
2600
+ let parenthesesDepth = 0;
2601
+ let braceDepth = 0;
2602
+ let bracketDepth = 0;
2603
+ while (foundArgEnd < fullCode.length && foundArgEnd <= actualParenCloseIndex) {
2604
+ const char = fullCode[foundArgEnd];
2605
+ const nextChar = foundArgEnd + 1 < fullCode.length ? fullCode[foundArgEnd + 1] : '';
2606
+ const nextNextChar = foundArgEnd + 2 < fullCode.length ? fullCode[foundArgEnd + 2] : '';
2607
+ if (escapeNext4) {
2608
+ escapeNext4 = false;
2609
+ foundArgEnd++;
2610
+ continue;
2611
+ }
2612
+ if (char === '\\') {
2613
+ escapeNext4 = true;
2614
+ foundArgEnd++;
2615
+ continue;
2616
+ }
2617
+ if (inString4) {
2618
+ if (char === stringDelimiter4) {
2619
+ inString4 = false;
2620
+ stringDelimiter4 = '';
2621
+ }
2622
+ foundArgEnd++;
2623
+ continue;
2624
+ }
2625
+ if ((char === '$' && nextChar === '"') ||
2626
+ (char === '$' && nextChar === '@' && nextNextChar === '"') ||
2627
+ (char === '@' && nextChar === '$' && nextNextChar === '"')) {
2628
+ inString4 = true;
2629
+ stringDelimiter4 = '"';
2630
+ if (nextChar === '@') {
2631
+ foundArgEnd += 2;
2632
+ }
2633
+ else {
2634
+ foundArgEnd += 1;
2635
+ }
2636
+ continue;
2637
+ }
2638
+ if (char === '"' || char === "'") {
2639
+ inString4 = true;
2640
+ stringDelimiter4 = char;
2641
+ foundArgEnd++;
2642
+ continue;
2643
+ }
2644
+ if (char === '(') {
2645
+ parenthesesDepth++;
2646
+ }
2647
+ else if (char === ')') {
2648
+ if (foundArgEnd === actualParenCloseIndex) {
2649
+ break;
2650
+ }
2651
+ parenthesesDepth--;
2652
+ }
2653
+ else if (char === '{') {
2654
+ braceDepth++;
2655
+ }
2656
+ else if (char === '}') {
2657
+ braceDepth--;
2658
+ }
2659
+ else if (char === '[') {
2660
+ bracketDepth++;
2661
+ }
2662
+ else if (char === ']') {
2663
+ bracketDepth--;
2664
+ }
2665
+ else if (char === ',' && parenthesesDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
2666
+ break;
2667
+ }
2668
+ foundArgEnd++;
2669
+ }
2670
+ if (!hasStringLiteral) {
2671
+ argStartPos = foundArgEnd + 1;
2672
+ continue;
2673
+ }
2674
+ const actualArg = fullCode.substring(argStartPos, foundArgEnd).trim();
2675
+ const isObjectInitializer = actualArg.startsWith('new ') && actualArg.includes('{') && actualArg.includes('}');
2676
+ if (isObjectInitializer) {
2677
+ this.processObjectInitializerStringsOnly(argStartPos, fullCode, snippets, trClass, trFormatMethod, trMethod);
2331
2678
  }
2332
2679
  else {
2333
- convertedValueExpression = valueExpression;
2680
+ let finalOriginalIndex = argStartPos;
2681
+ if (currentSnippetCount < stringLiteralPositions.length) {
2682
+ finalOriginalIndex = stringLiteralPositions[currentSnippetCount];
2683
+ }
2684
+ let alreadyExists = false;
2685
+ for (const snippet of snippets) {
2686
+ if (snippet.originalIndex === finalOriginalIndex) {
2687
+ alreadyExists = true;
2688
+ break;
2689
+ }
2690
+ }
2691
+ if (!alreadyExists) {
2692
+ let finalArg = actualArg;
2693
+ if (currentSnippetCount < stringLiteralPositions.length) {
2694
+ const stringStart = stringLiteralPositions[currentSnippetCount];
2695
+ let stringEnd = stringStart;
2696
+ let j = stringStart;
2697
+ let inString = false;
2698
+ let escapeNext = false;
2699
+ let stringDelimiter = '';
2700
+ let braceDepth = 0;
2701
+ let interpolatedBraceDepth = 0;
2702
+ const char = fullCode[j];
2703
+ const nextChar = j + 1 < fullCode.length ? fullCode[j + 1] : '';
2704
+ const nextNextChar = j + 2 < fullCode.length ? fullCode[j + 2] : '';
2705
+ if ((char === '$' && nextChar === '"')) {
2706
+ inString = true;
2707
+ stringDelimiter = '"';
2708
+ j += 2;
2709
+ }
2710
+ else if (char === '$' && nextChar === '@' && j + 2 < fullCode.length && fullCode[j + 2] === '"') {
2711
+ inString = true;
2712
+ stringDelimiter = '"';
2713
+ j += 3;
2714
+ }
2715
+ else if (char === '@' && nextChar === '$' && j + 2 < fullCode.length && fullCode[j + 2] === '"') {
2716
+ inString = true;
2717
+ stringDelimiter = '"';
2718
+ j += 3;
2719
+ }
2720
+ else if (char === '"' || char === "'") {
2721
+ inString = true;
2722
+ stringDelimiter = char;
2723
+ j++;
2724
+ }
2725
+ while (j < fullCode.length && inString) {
2726
+ const currentChar = fullCode[j];
2727
+ if (escapeNext) {
2728
+ escapeNext = false;
2729
+ }
2730
+ else if (currentChar === '\\') {
2731
+ escapeNext = true;
2732
+ }
2733
+ else if (currentChar === stringDelimiter && interpolatedBraceDepth === 0) {
2734
+ inString = false;
2735
+ j++;
2736
+ stringEnd = j;
2737
+ break;
2738
+ }
2739
+ else if (currentChar === '{' && !escapeNext) {
2740
+ interpolatedBraceDepth++;
2741
+ }
2742
+ else if (currentChar === '}' && !escapeNext && interpolatedBraceDepth > 0) {
2743
+ interpolatedBraceDepth--;
2744
+ }
2745
+ j++;
2746
+ }
2747
+ if (!inString) {
2748
+ finalArg = fullCode.substring(stringStart, stringEnd);
2749
+ }
2750
+ }
2751
+ const snippet = new CodeSnippet();
2752
+ snippet.originalIndex = finalOriginalIndex;
2753
+ snippet.originalCode = finalArg;
2754
+ snippet.originalContext = finalArg;
2755
+ this.variableIndex = 0;
2756
+ this.processSingleArgument(snippet, finalArg, trClass, trFormatMethod, trMethod);
2757
+ snippets.push(snippet);
2758
+ currentSnippetCount++;
2759
+ }
2334
2760
  }
2761
+ argStartPos = foundArgEnd + 1;
2335
2762
  }
2763
+ i = actualParenCloseIndex + 1;
2764
+ actualParenOpenIndex = -1;
2765
+ isStringFormat = false;
2766
+ functionNameStart = -1;
2336
2767
  }
2337
- }
2338
- else if (trimmedFullStatement.match(/^[\s\S]*?\(/)) {
2339
- const parenOpenIndex = trimmedFullStatement.indexOf('(');
2340
- if (parenOpenIndex !== -1) {
2341
- const parenCloseIndex = this.findMatchingParenthesis(trimmedProcessedStatement, parenOpenIndex);
2342
- if (parenCloseIndex !== -1) {
2343
- convertedValueExpression = trimmedProcessedStatement.substring(parenOpenIndex + 1, parenCloseIndex).trim();
2344
- }
2768
+ else if (tempDepth === 0 && actualParenOpenIndex !== -1 && isStringFormat) {
2769
+ i = j + 1;
2770
+ actualParenOpenIndex = -1;
2771
+ isStringFormat = false;
2772
+ functionNameStart = -1;
2345
2773
  }
2346
2774
  }
2347
- else {
2348
- convertedValueExpression = trimmedProcessedStatement;
2349
- if (convertedValueExpression.endsWith(';')) {
2350
- convertedValueExpression = convertedValueExpression.slice(0, -1).trim();
2351
- }
2775
+ else if (!tempInString && char === '.' && tempDepth === 0) {
2776
+ functionNameStart = -1;
2777
+ }
2778
+ else if (!tempInString && /[a-zA-Z_]/.test(char) && tempDepth === 0 && functionNameStart === -1) {
2779
+ functionNameStart = j;
2352
2780
  }
2353
2781
  }
2782
+ if (actualParenOpenIndex === -1) {
2783
+ break;
2784
+ }
2354
2785
  }
2355
- snippet.convertedCode = convertedValueExpression;
2356
2786
  }
2357
2787
  }
2358
2788
  exports.CSharpStringExtractor = CSharpStringExtractor;