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
@@ -193,9 +193,16 @@ export class CSharpStringExtractor {
193
193
  const trimmedStatement = statement.trim();
194
194
  const isStringFormatCall = trimmedStatement.startsWith('string.Format(');
195
195
  const isTextAssignment = /\w+\.text\s*=/.test(trimmedStatement);
196
- const isRegularAssignment = /^\s*[\w\.]+\s*=/.test(trimmedStatement) && !isTextAssignment;
196
+ const isRegularAssignment = /^[\s\S]*?=/.test(trimmedStatement);
197
197
  const isCaseOrDefault = trimmedStatement.startsWith('case ') || trimmedStatement.startsWith('default:');
198
198
  const isFunctionCall = /^\s*[\w\.<>]+[\s\w\.<>]*\(/.test(trimmedStatement) && !isStringFormatCall && !isCaseOrDefault;
199
+
200
+ if ((isRegularAssignment && !isTextAssignment) && !isFunctionCall) {
201
+ const assignmentIndex = statement.indexOf('=');
202
+ if (assignmentIndex !== -1) {
203
+ this.processAllFunctionCallsInRange(originalIndex, originalIndex + assignmentIndex, code, snippets, trClass, trFormatMethod, trMethod);
204
+ }
205
+ }
199
206
 
200
207
  if (isFunctionCall && !isStringFormatCall) {
201
208
  this.processFunctionCallArguments(statement, originalIndex, code, snippets, trClass, trFormatMethod, trMethod);
@@ -203,6 +210,8 @@ export class CSharpStringExtractor {
203
210
  const extractionResult = this.extractValueExpression(statement, originalIndex, code);
204
211
  const { valueExpression, valueExpressionIndex } = extractionResult;
205
212
 
213
+ const hasStringInValue = /"(?:[^"\\]|\\.)*"|'[^']*'|\$"[\s\S]*?"|\$@"[\s\S]*?"|@\$"[\s\S]*?"/.test(valueExpression);
214
+
206
215
  let alreadyExists = false;
207
216
  for (const snippet of snippets) {
208
217
  if (snippet.originalIndex === valueExpressionIndex) {
@@ -211,7 +220,7 @@ export class CSharpStringExtractor {
211
220
  }
212
221
  }
213
222
 
214
- if (!alreadyExists) {
223
+ if (!alreadyExists && (hasStringInValue || isTextAssignment)) {
215
224
  const snippet = new CodeSnippet();
216
225
  snippet.originalIndex = valueExpressionIndex;
217
226
  snippet.originalCode = valueExpression;
@@ -219,7 +228,11 @@ export class CSharpStringExtractor {
219
228
 
220
229
  this.variableIndex = 0;
221
230
 
222
- this.processStatementAndExtractValue(snippet, statement, valueExpression, trClass, trFormatMethod, trMethod);
231
+ if (isCaseOrDefault) {
232
+ this.processSingleArgument(snippet, valueExpression, trClass, trFormatMethod, trMethod);
233
+ } else {
234
+ this.processStatementAndExtractValue(snippet, statement, valueExpression, trClass, trFormatMethod, trMethod);
235
+ }
223
236
 
224
237
  snippets.push(snippet);
225
238
  }
@@ -265,6 +278,8 @@ export class CSharpStringExtractor {
265
278
  let inClassBody = true;
266
279
  let inComment = false;
267
280
  let commentType = '';
281
+ let isInterpolatedString = false;
282
+ let interpolatedBraceDepth = 0;
268
283
 
269
284
  for (j = braceOpenIndex + 1; j < code.length; j++) {
270
285
  const char = code[j];
@@ -316,9 +331,11 @@ export class CSharpStringExtractor {
316
331
  }
317
332
 
318
333
  if (inString) {
319
- if (char === stringDelimiter) {
334
+ if (char === stringDelimiter && (!isInterpolatedString || interpolatedBraceDepth === 0)) {
320
335
  inString = false;
321
336
  stringDelimiter = '';
337
+ isInterpolatedString = false;
338
+ interpolatedBraceDepth = 0;
322
339
 
323
340
  if (afterEqual && braceDepth === 1) {
324
341
  const stringLiteral = code.substring(stringStartPos, j + 1);
@@ -344,6 +361,12 @@ export class CSharpStringExtractor {
344
361
  snippets.push(snippet);
345
362
  }
346
363
  }
364
+ } else if (isInterpolatedString) {
365
+ if (char === '{' && !escapeNext) {
366
+ interpolatedBraceDepth++;
367
+ } else if (char === '}' && !escapeNext && interpolatedBraceDepth > 0) {
368
+ interpolatedBraceDepth--;
369
+ }
347
370
  }
348
371
  continue;
349
372
  }
@@ -351,12 +374,14 @@ export class CSharpStringExtractor {
351
374
  if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '$') {
352
375
  if (code[j + 1] === '"') {
353
376
  inString = true;
377
+ isInterpolatedString = true;
354
378
  stringDelimiter = '"';
355
379
  stringStartPos = j;
356
380
  j++;
357
381
  continue;
358
382
  } else if (code[j + 1] === '@' && j + 2 < code.length && code[j + 2] === '"') {
359
383
  inString = true;
384
+ isInterpolatedString = true;
360
385
  stringDelimiter = '"';
361
386
  stringStartPos = j;
362
387
  j += 2;
@@ -364,13 +389,15 @@ export class CSharpStringExtractor {
364
389
  }
365
390
  } else if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '@' && code[j + 1] === '$' && j + 2 < code.length && code[j + 2] === '"') {
366
391
  inString = true;
392
+ isInterpolatedString = true;
367
393
  stringDelimiter = '"';
368
394
  stringStartPos = j;
369
395
  j += 2;
370
396
  continue;
371
- } else if (!inString && (char === '"' || char === "'")) {
397
+ } else if (!inString && char === '"') {
372
398
  if (afterEqual && braceDepth === 1) {
373
399
  inString = true;
400
+ isInterpolatedString = false;
374
401
  stringDelimiter = char;
375
402
  stringStartPos = j;
376
403
  }
@@ -445,6 +472,8 @@ export class CSharpStringExtractor {
445
472
  let commentType = '';
446
473
  let inAnonymousFunction = false;
447
474
  let anonymousFunctionBraceDepth = 0;
475
+ let isInterpolatedString = false;
476
+ let interpolatedBraceDepth = 0;
448
477
 
449
478
  for (j = braceOpenIndex + 1; j < code.length; j++) {
450
479
  const char = code[j];
@@ -503,9 +532,11 @@ export class CSharpStringExtractor {
503
532
  }
504
533
 
505
534
  if (inString) {
506
- if (char === stringDelimiter) {
535
+ if (char === stringDelimiter && (!isInterpolatedString || interpolatedBraceDepth === 0)) {
507
536
  inString = false;
508
537
  stringDelimiter = '';
538
+ isInterpolatedString = false;
539
+ interpolatedBraceDepth = 0;
509
540
 
510
541
  if (afterEqual || inAnonymousFunction) {
511
542
  const stringLiteral = code.substring(stringStartPos, j + 1);
@@ -531,6 +562,12 @@ export class CSharpStringExtractor {
531
562
  snippets.push(snippet);
532
563
  }
533
564
  }
565
+ } else if (isInterpolatedString) {
566
+ if (char === '{' && !escapeNext) {
567
+ interpolatedBraceDepth++;
568
+ } else if (char === '}' && !escapeNext && interpolatedBraceDepth > 0) {
569
+ interpolatedBraceDepth--;
570
+ }
534
571
  }
535
572
  continue;
536
573
  }
@@ -538,12 +575,14 @@ export class CSharpStringExtractor {
538
575
  if (!inString && (afterEqual || inAnonymousFunction) && j + 1 < code.length && char === '$') {
539
576
  if (code[j + 1] === '"') {
540
577
  inString = true;
578
+ isInterpolatedString = true;
541
579
  stringDelimiter = '"';
542
580
  stringStartPos = j;
543
581
  j++;
544
582
  continue;
545
583
  } else if (code[j + 1] === '@' && j + 2 < code.length && code[j + 2] === '"') {
546
584
  inString = true;
585
+ isInterpolatedString = true;
547
586
  stringDelimiter = '"';
548
587
  stringStartPos = j;
549
588
  j += 2;
@@ -551,13 +590,15 @@ export class CSharpStringExtractor {
551
590
  }
552
591
  } else if (!inString && (afterEqual || inAnonymousFunction) && j + 1 < code.length && char === '@' && code[j + 1] === '$' && j + 2 < code.length && code[j + 2] === '"') {
553
592
  inString = true;
593
+ isInterpolatedString = true;
554
594
  stringDelimiter = '"';
555
595
  stringStartPos = j;
556
596
  j += 2;
557
597
  continue;
558
- } else if (!inString && (char === '"' || char === "'")) {
598
+ } else if (!inString && char === '"') {
559
599
  if (afterEqual || inAnonymousFunction) {
560
600
  inString = true;
601
+ isInterpolatedString = false;
561
602
  stringDelimiter = char;
562
603
  stringStartPos = j;
563
604
  }
@@ -611,12 +652,10 @@ private extractCommentStrings(code: string, snippets: CodeSnippet[], trClass: st
611
652
  if (i + 1 < code.length && code[i] === '/' && code[i + 1] === '/') {
612
653
  commentType = '//';
613
654
  if (i + 2 < code.length && code[i + 2] === '/') {
614
- // 这是文档注释(三个或更多连续 /),跳过它
615
655
  isDocComment = true;
616
656
  inComment = true;
617
657
  i += 3;
618
658
  } else {
619
- // 普通注释,正常处理
620
659
  inComment = true;
621
660
  i += 2;
622
661
  }
@@ -630,7 +669,6 @@ private extractCommentStrings(code: string, snippets: CodeSnippet[], trClass: st
630
669
  }
631
670
 
632
671
  if (isDocComment) {
633
- // 对于文档注释,我们只需要跳过,不处理任何内容!
634
672
  while (i < code.length && inComment) {
635
673
  const char = code[i];
636
674
  if (char === '\n') {
@@ -647,10 +685,12 @@ private extractCommentStrings(code: string, snippets: CodeSnippet[], trClass: st
647
685
  let stringDelimiter = '';
648
686
  let stringStartPos = -1;
649
687
  let interpolatedBraceDepth = 0;
688
+ let isInterpolatedString = false;
650
689
 
651
690
  while (i < code.length && inComment) {
652
691
  const char = code[i];
653
692
  const nextChar = i + 1 < code.length ? code[i + 1] : '';
693
+ const nextNextChar = i + 2 < code.length ? code[i + 2] : '';
654
694
 
655
695
  if (commentType === '//') {
656
696
  if (char === '\n') {
@@ -678,9 +718,11 @@ private extractCommentStrings(code: string, snippets: CodeSnippet[], trClass: st
678
718
  }
679
719
 
680
720
  if (inString) {
681
- if (char === stringDelimiter) {
721
+ if (char === stringDelimiter && (!isInterpolatedString || interpolatedBraceDepth === 0)) {
682
722
  inString = false;
683
723
  stringDelimiter = '';
724
+ isInterpolatedString = false;
725
+ interpolatedBraceDepth = 0;
684
726
 
685
727
  let alreadyExists = false;
686
728
  for (const snippet of snippets) {
@@ -703,6 +745,12 @@ private extractCommentStrings(code: string, snippets: CodeSnippet[], trClass: st
703
745
 
704
746
  snippets.push(snippet);
705
747
  }
748
+ } else if (isInterpolatedString) {
749
+ if (char === '{' && !escapeNext) {
750
+ interpolatedBraceDepth++;
751
+ } else if (char === '}' && !escapeNext && interpolatedBraceDepth > 0) {
752
+ interpolatedBraceDepth--;
753
+ }
706
754
  }
707
755
  i++;
708
756
  continue;
@@ -711,12 +759,14 @@ private extractCommentStrings(code: string, snippets: CodeSnippet[], trClass: st
711
759
  if (!inString && i + 1 < code.length && char === '$') {
712
760
  if (code[i + 1] === '"') {
713
761
  inString = true;
762
+ isInterpolatedString = true;
714
763
  stringDelimiter = '"';
715
764
  stringStartPos = i;
716
765
  i += 2;
717
766
  continue;
718
767
  } else if (code[i + 1] === '@' && i + 2 < code.length && code[i + 2] === '"') {
719
768
  inString = true;
769
+ isInterpolatedString = true;
720
770
  stringDelimiter = '"';
721
771
  stringStartPos = i;
722
772
  i += 3;
@@ -724,12 +774,14 @@ private extractCommentStrings(code: string, snippets: CodeSnippet[], trClass: st
724
774
  }
725
775
  } else if (!inString && i + 1 < code.length && char === '@' && code[i + 1] === '$' && i + 2 < code.length && code[i + 2] === '"') {
726
776
  inString = true;
777
+ isInterpolatedString = true;
727
778
  stringDelimiter = '"';
728
779
  stringStartPos = i;
729
780
  i += 3;
730
781
  continue;
731
- } else if (!inString && (char === '"' || char === "'")) {
782
+ } else if (!inString && char === '"') {
732
783
  inString = true;
784
+ isInterpolatedString = false;
733
785
  stringDelimiter = char;
734
786
  stringStartPos = i;
735
787
  i++;
@@ -1872,12 +1924,15 @@ private findMatchingParenthesis(str: string, openIndex: number): number {
1872
1924
  const textAssignmentRegex = /([\s\S]*?\.text\s*=\s*)([\s\S]*?)(?=;|$)/;
1873
1925
  const match = textAssignmentRegex.exec(statement);
1874
1926
 
1875
- if (!match) {
1876
- return statement;
1877
- }
1927
+ let prefix: string | null = null;
1928
+ let value: string;
1878
1929
 
1879
- const prefix = match[1];
1880
- const value = match[2];
1930
+ if (match) {
1931
+ prefix = match[1];
1932
+ value = match[2];
1933
+ } else {
1934
+ value = statement;
1935
+ }
1881
1936
 
1882
1937
  let processedValue = value;
1883
1938
 
@@ -1929,7 +1984,7 @@ private findMatchingParenthesis(str: string, openIndex: number): number {
1929
1984
  } else {
1930
1985
  const trimmedValue = value.trim();
1931
1986
  if (!trimmedValue.includes(`${trClass}.${trFormatMethod}(`) && !trimmedValue.endsWith(`.${trMethod}()`)) {
1932
- if (trimmedValue.startsWith('"') || /^\w+/.test(trimmedValue) || trimmedValue.startsWith('(')) {
1987
+ if (trimmedValue !== 'null' && (trimmedValue.startsWith('"') || /^\w+/.test(trimmedValue) || trimmedValue.startsWith('('))) {
1933
1988
  const whitespaceBefore = value.substring(0, value.search(/\S/));
1934
1989
  const actualValue = value.substring(value.search(/\S/));
1935
1990
  const whitespaceAfter = actualValue.search(/\s*$/) === 0 ? actualValue : actualValue.substring(actualValue.search(/\S/) + trimmedValue.length);
@@ -1938,13 +1993,16 @@ private findMatchingParenthesis(str: string, openIndex: number): number {
1938
1993
  }
1939
1994
  }
1940
1995
 
1941
- if (processedValue !== value) {
1942
- const hasSemicolon = statement.trim().endsWith(';');
1943
- const result = prefix + processedValue;
1944
- return hasSemicolon ? result + ';' : result;
1996
+ if (prefix) {
1997
+ if (processedValue !== value) {
1998
+ const hasSemicolon = statement.trim().endsWith(';');
1999
+ const result = prefix + processedValue;
2000
+ return hasSemicolon ? result + ';' : result;
2001
+ }
2002
+ return statement;
2003
+ } else {
2004
+ return processedValue;
1945
2005
  }
1946
-
1947
- return statement;
1948
2006
  }
1949
2007
 
1950
2008
  private processStringConcatenation(statement: string, snippet: CodeSnippet, trClass: string, trFormatMethod: string, trMethod: string): string {
@@ -2011,12 +2069,62 @@ private findMatchingParenthesis(str: string, openIndex: number): number {
2011
2069
  const hasSemicolon = statement.endsWith(';');
2012
2070
  let statementWithoutSemicolon = hasSemicolon ? statement.slice(0, -1) : statement;
2013
2071
 
2014
- const assignmentMatch = statementWithoutSemicolon.match(/(.*=\s*)([\s\S]*)/);
2072
+ // Find actual assignment operator = that's not inside string, parentheses, etc.
2073
+ let assignmentIndex = -1;
2074
+ let tempInString = false;
2075
+ let tempEscapeNext = false;
2076
+ let tempStringDelimiter = '';
2077
+ let parenDepth = 0;
2078
+ let braceDepth = 0;
2079
+ let bracketDepth = 0;
2080
+ for (let i = 0; i < statementWithoutSemicolon.length; i++) {
2081
+ const char = statementWithoutSemicolon[i];
2082
+ if (tempEscapeNext) {
2083
+ tempEscapeNext = false;
2084
+ continue;
2085
+ }
2086
+ if (char === '\\') {
2087
+ tempEscapeNext = true;
2088
+ continue;
2089
+ }
2090
+ if (tempInString) {
2091
+ if (char === tempStringDelimiter) {
2092
+ tempInString = false;
2093
+ tempStringDelimiter = '';
2094
+ }
2095
+ continue;
2096
+ }
2097
+ if (char === '"' || char === "'") {
2098
+ tempInString = true;
2099
+ tempStringDelimiter = char;
2100
+ continue;
2101
+ }
2102
+ if (char === '(') {
2103
+ parenDepth++;
2104
+ } else if (char === ')') {
2105
+ parenDepth--;
2106
+ } else if (char === '{') {
2107
+ braceDepth++;
2108
+ } else if (char === '}') {
2109
+ braceDepth--;
2110
+ } else if (char === '[') {
2111
+ bracketDepth++;
2112
+ } else if (char === ']') {
2113
+ bracketDepth--;
2114
+ } else if (char === '=' && parenDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
2115
+ assignmentIndex = i;
2116
+ break;
2117
+ }
2118
+ }
2015
2119
 
2016
- if (assignmentMatch) {
2017
- const leftSide = assignmentMatch[1];
2018
- const rightSide = assignmentMatch[2];
2120
+ let leftSide = '';
2121
+ let rightSide = '';
2122
+ if (assignmentIndex !== -1) {
2123
+ leftSide = statementWithoutSemicolon.substring(0, assignmentIndex + 1); // Include =
2124
+ rightSide = statementWithoutSemicolon.substring(assignmentIndex + 1);
2125
+ }
2019
2126
 
2127
+ if (assignmentIndex !== -1) {
2020
2128
  const parts = this.splitExpression(rightSide);
2021
2129
  const processedParts: string[] = [];
2022
2130
 
@@ -2058,7 +2166,7 @@ private findMatchingParenthesis(str: string, openIndex: number): number {
2058
2166
  const actualPart = part.substring(part.search(/\S/));
2059
2167
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
2060
2168
  processedParts.push(whitespaceBefore + trimmedPart + `.${trMethod}()` + whitespaceAfter);
2061
- } else if (trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
2169
+ } else if (trimmedPart !== 'null' && trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
2062
2170
  const whitespaceBefore = part.substring(0, part.search(/\S/));
2063
2171
  const actualPart = part.substring(part.search(/\S/));
2064
2172
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
@@ -2111,7 +2219,7 @@ private findMatchingParenthesis(str: string, openIndex: number): number {
2111
2219
  const actualPart = part.substring(part.search(/\S/));
2112
2220
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
2113
2221
  processedParts.push(whitespaceBefore + trimmedPart + `.${trMethod}()` + whitespaceAfter);
2114
- } else if (trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
2222
+ } else if (trimmedPart !== 'null' && trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
2115
2223
  if (trimmedPart.includes(`${trClass}.${trFormatMethod}(`)) {
2116
2224
  processedParts.push(part);
2117
2225
  continue;
@@ -2456,6 +2564,92 @@ private findMatchingParenthesis(str: string, openIndex: number): number {
2456
2564
  }
2457
2565
  }
2458
2566
 
2567
+ let startInStatement = -1;
2568
+
2569
+ for (let i = 0; i < statement.length - 1; i++) {
2570
+ const char = statement[i];
2571
+ const nextChar = i + 1 < statement.length ? statement[i + 1] : '';
2572
+ const nextNextChar = i + 2 < statement.length ? statement[i + 2] : '';
2573
+
2574
+ if ((char === '$' && nextChar === '"') ||
2575
+ (char === '$' && nextChar === '@' && nextNextChar === '"') ||
2576
+ (char === '@' && nextChar === '$' && nextNextChar === '"')) {
2577
+ startInStatement = i;
2578
+ break;
2579
+ }
2580
+ }
2581
+
2582
+ if (startInStatement === -1) {
2583
+ for (let i = 0; i < statement.length; i++) {
2584
+ if (statement[i] === '"' || statement[i] === "'") {
2585
+ startInStatement = i;
2586
+ break;
2587
+ }
2588
+ }
2589
+ }
2590
+
2591
+ if (startInStatement !== -1) {
2592
+ const finalIndex = statementIndex + startInStatement;
2593
+
2594
+ let stringEnd = startInStatement;
2595
+ let inString = false;
2596
+ let escapeNext = false;
2597
+ let stringDelimiter = '';
2598
+ let interpolatedBraceDepth = 0;
2599
+
2600
+ let j = startInStatement;
2601
+ const char = statement[j];
2602
+ const nextChar = j + 1 < statement.length ? statement[j + 1] : '';
2603
+ const nextNextChar = j + 2 < statement.length ? statement[j + 2] : '';
2604
+
2605
+ if ((char === '$' && nextChar === '"')) {
2606
+ inString = true;
2607
+ stringDelimiter = '"';
2608
+ j += 2;
2609
+ } else if (char === '$' && nextChar === '@' && nextNextChar === '"') {
2610
+ inString = true;
2611
+ stringDelimiter = '"';
2612
+ j += 3;
2613
+ } else if (char === '@' && nextChar === '$' && nextNextChar === '"') {
2614
+ inString = true;
2615
+ stringDelimiter = '"';
2616
+ j += 3;
2617
+ } else if (char === '"' || char === "'") {
2618
+ inString = true;
2619
+ stringDelimiter = char;
2620
+ j++;
2621
+ }
2622
+
2623
+ while (j < statement.length && inString) {
2624
+ const currentChar = statement[j];
2625
+
2626
+ if (escapeNext) {
2627
+ escapeNext = false;
2628
+ } else if (currentChar === '\\') {
2629
+ escapeNext = true;
2630
+ } else if (currentChar === stringDelimiter && interpolatedBraceDepth === 0) {
2631
+ inString = false;
2632
+ j++;
2633
+ stringEnd = j;
2634
+ break;
2635
+ } else if (currentChar === '{' && !escapeNext) {
2636
+ interpolatedBraceDepth++;
2637
+ } else if (currentChar === '}' && !escapeNext && interpolatedBraceDepth > 0) {
2638
+ interpolatedBraceDepth--;
2639
+ }
2640
+
2641
+ j++;
2642
+ }
2643
+
2644
+ if (!inString) {
2645
+ const valueExpression = statement.substring(startInStatement, stringEnd).trim();
2646
+ return {
2647
+ valueExpression,
2648
+ valueExpressionIndex: finalIndex
2649
+ };
2650
+ }
2651
+ }
2652
+
2459
2653
  return {
2460
2654
  valueExpression: trimmedStatement,
2461
2655
  valueExpressionIndex: statementIndex
@@ -2536,113 +2730,384 @@ private findMatchingParenthesis(str: string, openIndex: number): number {
2536
2730
  }
2537
2731
 
2538
2732
  private processStatementAndExtractValue(snippet: CodeSnippet, fullStatement: string, valueExpression: string, trClass: string, trFormatMethod: string, trMethod: string): void {
2539
- let processedFullStatement = fullStatement;
2733
+ let processedValueExpression = valueExpression;
2734
+ const isTextAssignment = /\w+\.text\s*=/.test(fullStatement.trim());
2540
2735
 
2541
- this.extractTrFormatStrings(processedFullStatement, snippet, trClass, trFormatMethod);
2542
- processedFullStatement = this.processStringTemplates(processedFullStatement, snippet, trClass, trFormatMethod);
2543
- processedFullStatement = this.processStringFormat(processedFullStatement, snippet, trClass, trFormatMethod);
2544
- processedFullStatement = this.processStringConcatenation(processedFullStatement, snippet, trClass, trFormatMethod, trMethod);
2545
- processedFullStatement = this.processTextAssignments(processedFullStatement, snippet, trClass, trFormatMethod, trMethod);
2546
- this.extractPlainStrings(processedFullStatement, snippet, trClass, trFormatMethod);
2736
+ this.extractTrFormatStrings(processedValueExpression, snippet, trClass, trFormatMethod);
2737
+ processedValueExpression = this.processStringTemplates(processedValueExpression, snippet, trClass, trFormatMethod);
2738
+ processedValueExpression = this.processStringFormat(processedValueExpression, snippet, trClass, trFormatMethod);
2739
+ processedValueExpression = this.processStringConcatenation(processedValueExpression, snippet, trClass, trFormatMethod, trMethod);
2740
+ if (isTextAssignment) {
2741
+ processedValueExpression = this.processTextAssignments(processedValueExpression, snippet, trClass, trFormatMethod, trMethod);
2742
+ }
2743
+ this.extractPlainStrings(processedValueExpression, snippet, trClass, trFormatMethod);
2547
2744
 
2548
2745
  const regex = new RegExp(`${trClass}\\.${trFormatMethod}\\(([^)]*)\\)\\.${trMethod}\\(\\)`, 'g');
2549
- processedFullStatement = processedFullStatement.replace(regex, `${trClass}.${trFormatMethod}($1)`);
2746
+ processedValueExpression = processedValueExpression.replace(regex, `${trClass}.${trFormatMethod}($1)`);
2550
2747
  snippet.finalizeLiterals();
2551
2748
 
2552
- let convertedValueExpression = valueExpression;
2749
+ let convertedValueExpression = processedValueExpression;
2553
2750
 
2554
- const trimmedFullStatement = fullStatement.trim();
2555
- const trimmedProcessedStatement = processedFullStatement.trim();
2751
+ if (convertedValueExpression.endsWith(';')) {
2752
+ convertedValueExpression = convertedValueExpression.slice(0, -1).trim();
2753
+ }
2556
2754
 
2557
- const isCaseOrDefault = trimmedFullStatement.startsWith('case ') || trimmedFullStatement.startsWith('default:');
2558
-
2559
- if (/^return\s/.test(trimmedFullStatement)) {
2560
- // 检查 processedFullStatement 是否真的被修改过
2561
- // 如果没有被修改过,直接使用原始 valueExpression 即可
2562
- if (fullStatement === processedFullStatement) {
2563
- convertedValueExpression = valueExpression;
2564
- } else {
2565
- // 对于 return 语句,我们不需要复杂地使用 extractValueUntilSemicolon 来重新解析!
2566
- // 我们已经处理完整个语句了,直接截取 return 后面的所有内容,然后去掉末尾的分号即可!
2567
- let returnIndex = trimmedProcessedStatement.indexOf('return');
2568
- let valuePart = trimmedProcessedStatement.substring(returnIndex + 'return'.length);
2569
- valuePart = valuePart.trim();
2570
- if (valuePart.endsWith(';')) {
2571
- valuePart = valuePart.slice(0, -1).trim();
2755
+ snippet.convertedCode = convertedValueExpression;
2756
+ }
2757
+
2758
+ private processAllFunctionCallsInRange(startIndex: number, endIndex: number, fullCode: string, snippets: CodeSnippet[], trClass: string, trFormatMethod: string, trMethod: string): void {
2759
+ let i = startIndex;
2760
+ while (i < endIndex) {
2761
+ let actualParenOpenIndex = -1;
2762
+ let tempDepth = 0;
2763
+ let tempInString = false;
2764
+ let tempEscapeNext = false;
2765
+ let tempStringDelimiter = '';
2766
+ let isStringFormat = false;
2767
+ let functionNameStart = -1;
2768
+
2769
+ for (let j = i; j < endIndex; j++) {
2770
+ const char = fullCode[j];
2771
+ const nextChar = j + 1 < fullCode.length ? fullCode[j + 1] : '';
2772
+ const nextNextChar = j + 2 < fullCode.length ? fullCode[j + 2] : '';
2773
+
2774
+ if (tempEscapeNext) {
2775
+ tempEscapeNext = false;
2776
+ continue;
2572
2777
  }
2573
- convertedValueExpression = valuePart;
2574
- }
2575
- } else {
2576
- const textAssignmentIndex = trimmedFullStatement.indexOf('.text =');
2577
- if (textAssignmentIndex !== -1) {
2578
- if (fullStatement === processedFullStatement) {
2579
- convertedValueExpression = valueExpression;
2580
- } else {
2581
- const prefix = trimmedFullStatement.substring(0, textAssignmentIndex + '.text ='.length);
2582
- const valuePart = trimmedProcessedStatement.substring(textAssignmentIndex + '.text ='.length);
2583
- const value = this.extractValueUntilSemicolon(valuePart);
2584
- convertedValueExpression = value.trim();
2778
+
2779
+ if (char === '\\') {
2780
+ tempEscapeNext = true;
2781
+ continue;
2585
2782
  }
2586
- } else {
2587
- const assignmentIndex = trimmedFullStatement.indexOf('=');
2588
- if (assignmentIndex !== -1) {
2589
- if (fullStatement === processedFullStatement) {
2590
- convertedValueExpression = valueExpression;
2783
+
2784
+ if (tempInString) {
2785
+ if (char === tempStringDelimiter) {
2786
+ tempInString = false;
2787
+ tempStringDelimiter = '';
2788
+ }
2789
+ continue;
2790
+ }
2791
+
2792
+ if ((char === '$' && nextChar === '"') ||
2793
+ (char === '$' && nextChar === '@' && nextNextChar === '"') ||
2794
+ (char === '@' && nextChar === '$' && nextNextChar === '"')) {
2795
+ tempInString = true;
2796
+ tempStringDelimiter = '"';
2797
+ if (nextChar === '@') {
2798
+ j += 2;
2591
2799
  } else {
2592
- const prefix = trimmedFullStatement.substring(0, assignmentIndex + 1);
2593
- const valuePart = trimmedProcessedStatement.substring(assignmentIndex + 1);
2594
- const value = this.extractValueUntilSemicolon(valuePart);
2595
- convertedValueExpression = value.trim();
2800
+ j += 1;
2596
2801
  }
2597
- } else if (valueExpression.startsWith('string.Format(')) {
2598
- const processedMatch = trimmedProcessedStatement.match(/^(Tr\.Format[\s\S]*?)(;|$)/);
2599
- if (processedMatch) {
2600
- convertedValueExpression = processedMatch[1].trim();
2802
+ continue;
2803
+ }
2804
+
2805
+ if (char === '"' || char === "'") {
2806
+ tempInString = true;
2807
+ tempStringDelimiter = char;
2808
+ continue;
2809
+ }
2810
+
2811
+ if (char === '(') {
2812
+ tempDepth++;
2813
+ if (actualParenOpenIndex === -1) {
2814
+ actualParenOpenIndex = j;
2815
+ if (functionNameStart !== -1) {
2816
+ const funcName = fullCode.substring(functionNameStart, j).trim();
2817
+ isStringFormat = funcName === 'string.Format';
2818
+ }
2601
2819
  }
2602
- } else if (isCaseOrDefault) {
2603
- if (fullStatement === processedFullStatement) {
2604
- convertedValueExpression = valueExpression;
2605
- } else {
2606
- const trFormatIndex = trimmedProcessedStatement.indexOf('Tr.Format(');
2607
- if (trFormatIndex !== -1) {
2608
- const parenOpenIndex = trFormatIndex + 'Tr.Format('.length - 1;
2609
- const parenCloseIndex = this.findMatchingParenthesis(trimmedProcessedStatement, parenOpenIndex);
2610
- if (parenCloseIndex !== -1) {
2611
- convertedValueExpression = trimmedProcessedStatement.substring(trFormatIndex, parenCloseIndex + 1);
2612
- } else {
2613
- convertedValueExpression = valueExpression;
2820
+ } else if (char === ')') {
2821
+ tempDepth--;
2822
+ if (tempDepth === 0 && actualParenOpenIndex !== -1 && !isStringFormat) {
2823
+ const actualParenCloseIndex = j;
2824
+ let currentSnippetCount = 0;
2825
+ const stringLiteralPositions: number[] = [];
2826
+
2827
+ let k = actualParenOpenIndex;
2828
+ let inString3 = false;
2829
+ let escapeNext3 = false;
2830
+ let stringDelimiter3 = '';
2831
+ let stringStart = -1;
2832
+
2833
+ while (k < fullCode.length && k <= actualParenCloseIndex) {
2834
+ const char = fullCode[k];
2835
+ const nextChar = k + 1 < fullCode.length ? fullCode[k + 1] : '';
2836
+ const nextNextChar = k + 2 < fullCode.length ? fullCode[k + 2] : '';
2837
+
2838
+ if (escapeNext3) {
2839
+ escapeNext3 = false;
2840
+ k++;
2841
+ continue;
2842
+ }
2843
+
2844
+ if (char === '\\') {
2845
+ escapeNext3 = true;
2846
+ k++;
2847
+ continue;
2848
+ }
2849
+
2850
+ if (inString3) {
2851
+ if (char === stringDelimiter3) {
2852
+ stringLiteralPositions.push(stringStart);
2853
+ inString3 = false;
2854
+ stringDelimiter3 = '';
2855
+ }
2856
+ k++;
2857
+ continue;
2614
2858
  }
2615
- } else {
2616
- const parenOpenIndex = trimmedFullStatement.indexOf('(');
2617
- if (parenOpenIndex !== -1) {
2618
- const parenCloseIndex = this.findMatchingParenthesis(trimmedProcessedStatement, parenOpenIndex);
2619
- if (parenCloseIndex !== -1) {
2620
- convertedValueExpression = trimmedProcessedStatement.substring(parenOpenIndex + 1, parenCloseIndex).trim();
2859
+
2860
+ if ((char === '$' && nextChar === '"') ||
2861
+ (char === '$' && nextChar === '@' && nextNextChar === '"') ||
2862
+ (char === '@' && nextChar === '$' && nextNextChar === '"')) {
2863
+ inString3 = true;
2864
+ stringDelimiter3 = '"';
2865
+ stringStart = k;
2866
+ if (nextChar === '@') {
2867
+ k += 2;
2621
2868
  } else {
2622
- convertedValueExpression = valueExpression;
2869
+ k += 1;
2623
2870
  }
2624
- } else {
2625
- convertedValueExpression = valueExpression;
2871
+ continue;
2626
2872
  }
2873
+
2874
+ if (char === '"' || char === "'") {
2875
+ inString3 = true;
2876
+ stringDelimiter3 = char;
2877
+ stringStart = k;
2878
+ k++;
2879
+ continue;
2880
+ }
2881
+
2882
+ if (char === ')' && k === actualParenCloseIndex) {
2883
+ break;
2884
+ }
2885
+
2886
+ k++;
2627
2887
  }
2628
- }
2629
- } else if (trimmedFullStatement.match(/^[\s\S]*?\(/)) {
2630
- const parenOpenIndex = trimmedFullStatement.indexOf('(');
2631
- if (parenOpenIndex !== -1) {
2632
- const parenCloseIndex = this.findMatchingParenthesis(trimmedProcessedStatement, parenOpenIndex);
2633
- if (parenCloseIndex !== -1) {
2634
- convertedValueExpression = trimmedProcessedStatement.substring(parenOpenIndex + 1, parenCloseIndex).trim();
2888
+
2889
+ let argStartPos = actualParenOpenIndex + 1;
2890
+
2891
+ const trimmedCode = fullCode.substring(actualParenOpenIndex + 1, actualParenCloseIndex);
2892
+ const args = this.splitArguments(trimmedCode);
2893
+
2894
+ for (const arg of args) {
2895
+ const trimmedArg = arg.trim();
2896
+ if (!trimmedArg) {
2897
+ argStartPos = this.updateArgStartPos(argStartPos, fullCode, actualParenCloseIndex);
2898
+ continue;
2899
+ }
2900
+
2901
+ const hasStringLiteral = /"(?:[^"\\]|\\.)*"/.test(trimmedArg) ||
2902
+ /\$"[\s\S]*?"/.test(trimmedArg) ||
2903
+ /\$@"[\s\S]*?"/.test(trimmedArg) ||
2904
+ /@\$"[\s\S]*?"/.test(trimmedArg);
2905
+
2906
+ while (argStartPos < fullCode.length && /\s/.test(fullCode[argStartPos])) {
2907
+ argStartPos++;
2908
+ }
2909
+
2910
+ let foundArgEnd = argStartPos;
2911
+ let inString4 = false;
2912
+ let escapeNext4 = false;
2913
+ let stringDelimiter4 = '';
2914
+ let parenthesesDepth = 0;
2915
+ let braceDepth = 0;
2916
+ let bracketDepth = 0;
2917
+
2918
+ while (foundArgEnd < fullCode.length && foundArgEnd <= actualParenCloseIndex) {
2919
+ const char = fullCode[foundArgEnd];
2920
+ const nextChar = foundArgEnd + 1 < fullCode.length ? fullCode[foundArgEnd + 1] : '';
2921
+ const nextNextChar = foundArgEnd + 2 < fullCode.length ? fullCode[foundArgEnd + 2] : '';
2922
+
2923
+ if (escapeNext4) {
2924
+ escapeNext4 = false;
2925
+ foundArgEnd++;
2926
+ continue;
2927
+ }
2928
+
2929
+ if (char === '\\') {
2930
+ escapeNext4 = true;
2931
+ foundArgEnd++;
2932
+ continue;
2933
+ }
2934
+
2935
+ if (inString4) {
2936
+ if (char === stringDelimiter4) {
2937
+ inString4 = false;
2938
+ stringDelimiter4 = '';
2939
+ }
2940
+ foundArgEnd++;
2941
+ continue;
2942
+ }
2943
+
2944
+ if ((char === '$' && nextChar === '"') ||
2945
+ (char === '$' && nextChar === '@' && nextNextChar === '"') ||
2946
+ (char === '@' && nextChar === '$' && nextNextChar === '"')) {
2947
+ inString4 = true;
2948
+ stringDelimiter4 = '"';
2949
+ if (nextChar === '@') {
2950
+ foundArgEnd += 2;
2951
+ } else {
2952
+ foundArgEnd += 1;
2953
+ }
2954
+ continue;
2955
+ }
2956
+
2957
+ if (char === '"' || char === "'") {
2958
+ inString4 = true;
2959
+ stringDelimiter4 = char;
2960
+ foundArgEnd++;
2961
+ continue;
2962
+ }
2963
+
2964
+ if (char === '(') {
2965
+ parenthesesDepth++;
2966
+ } else if (char === ')') {
2967
+ if (foundArgEnd === actualParenCloseIndex) {
2968
+ break;
2969
+ }
2970
+ parenthesesDepth--;
2971
+ } else if (char === '{') {
2972
+ braceDepth++;
2973
+ } else if (char === '}') {
2974
+ braceDepth--;
2975
+ } else if (char === '[') {
2976
+ bracketDepth++;
2977
+ } else if (char === ']') {
2978
+ bracketDepth--;
2979
+ } else if (char === ',' && parenthesesDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
2980
+ break;
2981
+ }
2982
+
2983
+ foundArgEnd++;
2984
+ }
2985
+
2986
+ if (!hasStringLiteral) {
2987
+ argStartPos = foundArgEnd + 1;
2988
+ continue;
2989
+ }
2990
+
2991
+ const actualArg = fullCode.substring(argStartPos, foundArgEnd).trim();
2992
+
2993
+ const isObjectInitializer = actualArg.startsWith('new ') && actualArg.includes('{') && actualArg.includes('}');
2994
+
2995
+ if (isObjectInitializer) {
2996
+ this.processObjectInitializerStringsOnly(argStartPos, fullCode, snippets, trClass, trFormatMethod, trMethod);
2997
+ } else {
2998
+ let finalOriginalIndex = argStartPos;
2999
+
3000
+ if (currentSnippetCount < stringLiteralPositions.length) {
3001
+ finalOriginalIndex = stringLiteralPositions[currentSnippetCount];
3002
+ }
3003
+
3004
+ let alreadyExists = false;
3005
+ for (const snippet of snippets) {
3006
+ if (snippet.originalIndex === finalOriginalIndex) {
3007
+ alreadyExists = true;
3008
+ break;
3009
+ }
3010
+ }
3011
+
3012
+ if (!alreadyExists) {
3013
+ let finalArg = actualArg;
3014
+
3015
+ if (currentSnippetCount < stringLiteralPositions.length) {
3016
+ const stringStart = stringLiteralPositions[currentSnippetCount];
3017
+ let stringEnd = stringStart;
3018
+
3019
+ let j = stringStart;
3020
+ let inString = false;
3021
+ let escapeNext = false;
3022
+ let stringDelimiter = '';
3023
+ let braceDepth = 0;
3024
+ let interpolatedBraceDepth = 0;
3025
+
3026
+ const char = fullCode[j];
3027
+ const nextChar = j + 1 < fullCode.length ? fullCode[j + 1] : '';
3028
+ const nextNextChar = j + 2 < fullCode.length ? fullCode[j + 2] : '';
3029
+
3030
+ if ((char === '$' && nextChar === '"')) {
3031
+ inString = true;
3032
+ stringDelimiter = '"';
3033
+ j += 2;
3034
+ } else if (char === '$' && nextChar === '@' && j + 2 < fullCode.length && fullCode[j + 2] === '"') {
3035
+ inString = true;
3036
+ stringDelimiter = '"';
3037
+ j += 3;
3038
+ } else if (char === '@' && nextChar === '$' && j + 2 < fullCode.length && fullCode[j + 2] === '"') {
3039
+ inString = true;
3040
+ stringDelimiter = '"';
3041
+ j += 3;
3042
+ } else if (char === '"' || char === "'") {
3043
+ inString = true;
3044
+ stringDelimiter = char;
3045
+ j++;
3046
+ }
3047
+
3048
+ while (j < fullCode.length && inString) {
3049
+ const currentChar = fullCode[j];
3050
+
3051
+ if (escapeNext) {
3052
+ escapeNext = false;
3053
+ } else if (currentChar === '\\') {
3054
+ escapeNext = true;
3055
+ } else if (currentChar === stringDelimiter && interpolatedBraceDepth === 0) {
3056
+ inString = false;
3057
+ j++;
3058
+ stringEnd = j;
3059
+ break;
3060
+ } else if (currentChar === '{' && !escapeNext) {
3061
+ interpolatedBraceDepth++;
3062
+ } else if (currentChar === '}' && !escapeNext && interpolatedBraceDepth > 0) {
3063
+ interpolatedBraceDepth--;
3064
+ }
3065
+
3066
+ j++;
3067
+ }
3068
+
3069
+ if (!inString) {
3070
+ finalArg = fullCode.substring(stringStart, stringEnd);
3071
+ }
3072
+ }
3073
+
3074
+ const snippet = new CodeSnippet();
3075
+ snippet.originalIndex = finalOriginalIndex;
3076
+ snippet.originalCode = finalArg;
3077
+ snippet.originalContext = finalArg;
3078
+
3079
+ this.variableIndex = 0;
3080
+
3081
+ this.processSingleArgument(snippet, finalArg, trClass, trFormatMethod, trMethod);
3082
+
3083
+ snippets.push(snippet);
3084
+ currentSnippetCount++;
3085
+ }
3086
+ }
3087
+
3088
+ argStartPos = foundArgEnd + 1;
2635
3089
  }
3090
+
3091
+ i = actualParenCloseIndex + 1;
3092
+ actualParenOpenIndex = -1;
3093
+ isStringFormat = false;
3094
+ functionNameStart = -1;
3095
+ } else if (tempDepth === 0 && actualParenOpenIndex !== -1 && isStringFormat) {
3096
+ i = j + 1;
3097
+ actualParenOpenIndex = -1;
3098
+ isStringFormat = false;
3099
+ functionNameStart = -1;
2636
3100
  }
2637
- } else {
2638
- convertedValueExpression = trimmedProcessedStatement;
2639
- if (convertedValueExpression.endsWith(';')) {
2640
- convertedValueExpression = convertedValueExpression.slice(0, -1).trim();
2641
- }
3101
+ } else if (!tempInString && char === '.' && tempDepth === 0) {
3102
+ functionNameStart = -1;
3103
+ } else if (!tempInString && /[a-zA-Z_]/.test(char) && tempDepth === 0 && functionNameStart === -1) {
3104
+ functionNameStart = j;
2642
3105
  }
2643
3106
  }
3107
+
3108
+ if (actualParenOpenIndex === -1) {
3109
+ break;
3110
+ }
2644
3111
  }
2645
-
2646
- snippet.convertedCode = convertedValueExpression;
2647
3112
  }
2648
3113
  }