scancscode 1.0.33 → 1.0.35

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.
@@ -404,10 +404,12 @@ class CSharpStringExtractor {
404
404
  let stringStartPos = -1;
405
405
  let inComment = false;
406
406
  let commentType = '';
407
+ let inAnonymousFunction = false;
408
+ let anonymousFunctionBraceDepth = 0;
407
409
  for (j = braceOpenIndex + 1; j < code.length; j++) {
408
410
  const char = code[j];
409
411
  const nextChar = j + 1 < code.length ? code[j + 1] : '';
410
- if (braceDepth > 1 || parenthesesDepth > 0) {
412
+ if (!inAnonymousFunction && (braceDepth > 1 || parenthesesDepth > 0)) {
411
413
  afterEqual = false;
412
414
  }
413
415
  if (!inString && !inComment && char === '(') {
@@ -416,6 +418,12 @@ class CSharpStringExtractor {
416
418
  if (!inString && !inComment && char === ')') {
417
419
  parenthesesDepth--;
418
420
  }
421
+ if (!inString && !inComment && char === '=' && nextChar === '>') {
422
+ inAnonymousFunction = true;
423
+ anonymousFunctionBraceDepth = braceDepth;
424
+ j++;
425
+ continue;
426
+ }
419
427
  if (!inString && !inComment && char === '/' && nextChar === '/') {
420
428
  inComment = true;
421
429
  commentType = '//';
@@ -452,7 +460,7 @@ class CSharpStringExtractor {
452
460
  if (char === stringDelimiter) {
453
461
  inString = false;
454
462
  stringDelimiter = '';
455
- if (afterEqual && braceDepth === 1) {
463
+ if (afterEqual || inAnonymousFunction) {
456
464
  const stringLiteral = code.substring(stringStartPos, j + 1);
457
465
  let alreadyExists = false;
458
466
  for (const snippet of snippets) {
@@ -474,7 +482,7 @@ class CSharpStringExtractor {
474
482
  }
475
483
  continue;
476
484
  }
477
- if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '$') {
485
+ if (!inString && (afterEqual || inAnonymousFunction) && j + 1 < code.length && char === '$') {
478
486
  if (code[j + 1] === '"') {
479
487
  inString = true;
480
488
  stringDelimiter = '"';
@@ -490,7 +498,7 @@ class CSharpStringExtractor {
490
498
  continue;
491
499
  }
492
500
  }
493
- else if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '@' && code[j + 1] === '$' && j + 2 < code.length && code[j + 2] === '"') {
501
+ else if (!inString && (afterEqual || inAnonymousFunction) && j + 1 < code.length && char === '@' && code[j + 1] === '$' && j + 2 < code.length && code[j + 2] === '"') {
494
502
  inString = true;
495
503
  stringDelimiter = '"';
496
504
  stringStartPos = j;
@@ -498,7 +506,7 @@ class CSharpStringExtractor {
498
506
  continue;
499
507
  }
500
508
  else if (!inString && (char === '"' || char === "'")) {
501
- if (afterEqual && braceDepth === 1) {
509
+ if (afterEqual || inAnonymousFunction) {
502
510
  inString = true;
503
511
  stringDelimiter = char;
504
512
  stringStartPos = j;
@@ -507,7 +515,7 @@ class CSharpStringExtractor {
507
515
  }
508
516
  if (char === '{') {
509
517
  braceDepth++;
510
- if (braceDepth > 1) {
518
+ if (!inAnonymousFunction && braceDepth > 1) {
511
519
  afterEqual = false;
512
520
  }
513
521
  continue;
@@ -517,7 +525,10 @@ class CSharpStringExtractor {
517
525
  if (braceDepth === 0) {
518
526
  break;
519
527
  }
520
- if (braceDepth > 1 || parenthesesDepth > 0) {
528
+ if (inAnonymousFunction && braceDepth === anonymousFunctionBraceDepth) {
529
+ inAnonymousFunction = false;
530
+ }
531
+ if (!inAnonymousFunction && (braceDepth > 1 || parenthesesDepth > 0)) {
521
532
  afterEqual = false;
522
533
  }
523
534
  continue;
@@ -528,6 +539,7 @@ class CSharpStringExtractor {
528
539
  }
529
540
  if (char === ',' && braceDepth === 1) {
530
541
  afterEqual = false;
542
+ inAnonymousFunction = false;
531
543
  continue;
532
544
  }
533
545
  }
@@ -984,10 +996,12 @@ class CSharpStringExtractor {
984
996
  let stringStartPos = -1;
985
997
  let inComment = false;
986
998
  let commentType = '';
999
+ let inAnonymousFunction = false;
1000
+ let anonymousFunctionBraceDepth = 0;
987
1001
  for (let i = braceOpenIndex + 1; i < fullCode.length; i++) {
988
1002
  const char = fullCode[i];
989
1003
  const nextChar = i + 1 < fullCode.length ? fullCode[i + 1] : '';
990
- if (braceDepth > 1 || parenthesesDepth > 0) {
1004
+ if (!inAnonymousFunction && (braceDepth > 1 || parenthesesDepth > 0)) {
991
1005
  afterEqual = false;
992
1006
  }
993
1007
  if (!inString && !inComment && char === '(') {
@@ -996,6 +1010,12 @@ class CSharpStringExtractor {
996
1010
  if (!inString && !inComment && char === ')') {
997
1011
  parenthesesDepth--;
998
1012
  }
1013
+ if (!inString && !inComment && char === '=' && nextChar === '>') {
1014
+ inAnonymousFunction = true;
1015
+ anonymousFunctionBraceDepth = braceDepth;
1016
+ i++;
1017
+ continue;
1018
+ }
999
1019
  if (!inString && !inComment && char === '/' && nextChar === '/') {
1000
1020
  inComment = true;
1001
1021
  commentType = '//';
@@ -1032,20 +1052,29 @@ class CSharpStringExtractor {
1032
1052
  if (char === stringDelimiter) {
1033
1053
  inString = false;
1034
1054
  stringDelimiter = '';
1035
- if (afterEqual && braceDepth === 1) {
1055
+ if (afterEqual || inAnonymousFunction) {
1036
1056
  const stringLiteral = fullCode.substring(stringStartPos, i + 1);
1037
- const snippet = new CodeSnippet();
1038
- snippet.originalIndex = stringStartPos;
1039
- snippet.originalCode = stringLiteral;
1040
- snippet.originalContext = stringLiteral;
1041
- this.variableIndex = 0;
1042
- this.processSingleArgument(snippet, stringLiteral, trClass, trFormatMethod, trMethod);
1043
- snippets.push(snippet);
1057
+ let alreadyExists = false;
1058
+ for (const snippet of snippets) {
1059
+ if (snippet.originalIndex === stringStartPos) {
1060
+ alreadyExists = true;
1061
+ break;
1062
+ }
1063
+ }
1064
+ if (!alreadyExists) {
1065
+ const snippet = new CodeSnippet();
1066
+ snippet.originalIndex = stringStartPos;
1067
+ snippet.originalCode = stringLiteral;
1068
+ snippet.originalContext = stringLiteral;
1069
+ this.variableIndex = 0;
1070
+ this.processSingleArgument(snippet, stringLiteral, trClass, trFormatMethod, trMethod);
1071
+ snippets.push(snippet);
1072
+ }
1044
1073
  }
1045
1074
  }
1046
1075
  continue;
1047
1076
  }
1048
- if (!inString && afterEqual && braceDepth === 1 && i + 1 < fullCode.length && char === '$') {
1077
+ if (!inString && (afterEqual || inAnonymousFunction) && i + 1 < fullCode.length && char === '$') {
1049
1078
  if (fullCode[i + 1] === '"') {
1050
1079
  inString = true;
1051
1080
  stringDelimiter = '"';
@@ -1061,7 +1090,7 @@ class CSharpStringExtractor {
1061
1090
  continue;
1062
1091
  }
1063
1092
  }
1064
- else if (!inString && afterEqual && braceDepth === 1 && i + 1 < fullCode.length && char === '@' && fullCode[i + 1] === '$' && i + 2 < fullCode.length && fullCode[i + 2] === '"') {
1093
+ else if (!inString && (afterEqual || inAnonymousFunction) && i + 1 < fullCode.length && char === '@' && fullCode[i + 1] === '$' && i + 2 < fullCode.length && fullCode[i + 2] === '"') {
1065
1094
  inString = true;
1066
1095
  stringDelimiter = '"';
1067
1096
  stringStartPos = i;
@@ -1069,7 +1098,7 @@ class CSharpStringExtractor {
1069
1098
  continue;
1070
1099
  }
1071
1100
  else if (!inString && (char === '"' || char === "'")) {
1072
- if (afterEqual && braceDepth === 1) {
1101
+ if (afterEqual || inAnonymousFunction) {
1073
1102
  inString = true;
1074
1103
  stringDelimiter = char;
1075
1104
  stringStartPos = i;
@@ -1078,6 +1107,9 @@ class CSharpStringExtractor {
1078
1107
  }
1079
1108
  if (char === '{') {
1080
1109
  braceDepth++;
1110
+ if (!inAnonymousFunction && braceDepth > 1) {
1111
+ afterEqual = false;
1112
+ }
1081
1113
  continue;
1082
1114
  }
1083
1115
  if (char === '}') {
@@ -1085,7 +1117,10 @@ class CSharpStringExtractor {
1085
1117
  if (braceDepth === 0) {
1086
1118
  break;
1087
1119
  }
1088
- if (braceDepth > 1 || parenthesesDepth > 0) {
1120
+ if (inAnonymousFunction && braceDepth === anonymousFunctionBraceDepth) {
1121
+ inAnonymousFunction = false;
1122
+ }
1123
+ if (!inAnonymousFunction && (braceDepth > 1 || parenthesesDepth > 0)) {
1089
1124
  afterEqual = false;
1090
1125
  }
1091
1126
  continue;
@@ -1096,6 +1131,7 @@ class CSharpStringExtractor {
1096
1131
  }
1097
1132
  if (char === ',' && braceDepth === 1) {
1098
1133
  afterEqual = false;
1134
+ inAnonymousFunction = false;
1099
1135
  continue;
1100
1136
  }
1101
1137
  }
@@ -47,7 +47,7 @@ class LiteralCollector {
47
47
  }
48
48
  let files = glob_1.glob.sync("**/*.cs", { cwd: folder });
49
49
  let testFullPath = "@";
50
- // let testFullPath = "E:/DATA/Projects/ZhiYou/ProjectFClient/GameClient/Assets/Bundles/GameConfigs/Main/UnitAttributeTable-UnitAttributeTable.cs";
50
+ // let testFullPath = "E:/DATA/Projects/ZhiYou/ProjectFClient/GameClient/Assets/Bundles/FGUI/Basics/Comp/InfoToastComp.cs";
51
51
  // 限制并行处理的文件数量,避免内存占用过高
52
52
  const batchSize = 10;
53
53
  for (let i = 0; i < files.length; i += batchSize) {
@@ -63,7 +63,6 @@ class LiteralCollector {
63
63
  try {
64
64
  const content = await fs.promises.readFile(fullPath, "utf-8");
65
65
  const snippets = CSCodeScanner_1.CSCodeScanner.scanFile(fullPath, content, trmethod);
66
- CSCodeScanner_1.CSCodeScanner.filterSnippets(snippets);
67
66
  if (snippets.length > 0) {
68
67
  if (!scanonly) {
69
68
  let convertedContent = CSCodeScanner_1.CSCodeScanner.replaceInFile(content, snippets);
@@ -71,9 +70,11 @@ class LiteralCollector {
71
70
  await fs.promises.writeFile(fullPath, convertedContent, "utf-8");
72
71
  }
73
72
  }
73
+ let filteredSnippets = [...snippets];
74
+ CSCodeScanner_1.CSCodeScanner.filterSnippets(filteredSnippets);
74
75
  const fileLiterals = [];
75
76
  const fileUnexpects = [];
76
- for (const snippet of snippets) {
77
+ for (const snippet of filteredSnippets) {
77
78
  fileLiterals.push(...snippet.literals);
78
79
  fileUnexpects.push(...snippet.unexpects);
79
80
  }
@@ -0,0 +1 @@
1
+ "use strict";
@@ -1211,32 +1211,19 @@ describe('CSharpStringExtractor', () => {
1211
1211
  ]);
1212
1212
  expect(targetSnippet.isChanged).toBe(true);
1213
1213
  });
1214
- // // 测试处理C#字符串表达式中包含各种特殊字符的情况
1215
- // test('should handle special characters in string expression 1', () => {
1216
- // const code = `return "abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\";`;
1217
- // const snippets = extractor.extractStrings(code);
1218
- // let targetSnippet = snippets[0];
1219
- // expect(targetSnippet.originalIndex).toBe(code.indexOf(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`));
1220
- // expect(targetSnippet.originalCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1221
- // expect(targetSnippet.convertedCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1222
- // expect(targetSnippet.literals).toEqual([
1223
- // 'abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\'
1224
- // ]);
1225
- // expect(targetSnippet.isChanged).toBe(false);
1226
- // });
1227
- // // 测试处理C#字符串表达式中包含各种特殊字符的情况
1228
- // test('should handle special characters in string expression 2', () => {
1229
- // const code = readFileSync("test/TestSpecialString.cs", "utf8");
1230
- // const snippets = extractor.extractStrings(code);
1231
- // let targetSnippet = snippets[0];
1232
- // expect(targetSnippet.originalIndex).toBe(code.indexOf(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`));
1233
- // expect(targetSnippet.originalCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1234
- // expect(targetSnippet.convertedCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1235
- // expect(targetSnippet.literals).toEqual([
1236
- // 'abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\'
1237
- // ]);
1238
- // expect(targetSnippet.isChanged).toBe(false);
1239
- // });
1214
+ // 测试处理C#字符串表达式中包含各种特殊字符的情况
1215
+ test('should handle special characters in string expression 1', () => {
1216
+ const code = `return "abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\";`;
1217
+ const snippets = extractor.extractStrings(code);
1218
+ let targetSnippet = snippets[0];
1219
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`));
1220
+ expect(targetSnippet.originalCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1221
+ expect(targetSnippet.convertedCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
1222
+ expect(targetSnippet.literals).toEqual([
1223
+ 'abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\'
1224
+ ]);
1225
+ expect(targetSnippet.isChanged).toBe(false);
1226
+ });
1240
1227
  // 测试处理在C#类成员初始赋值语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
1241
1228
  test('should handle member initial assignment', () => {
1242
1229
  const code = (0, fs_1.readFileSync)("test/TestSpecialString.cs", "utf8");
@@ -2111,54 +2098,170 @@ namespace FaBao.UI.MainUI
2111
2098
  }
2112
2099
  expect(snippets.length).toBe(1);
2113
2100
  });
2114
- // // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2115
- // test('should handle class member initialization assignment with anonymous function', () => {
2116
- // const code = `
2117
- // FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2118
- // {
2119
- // Title = "提示",
2120
- // ConfirmCallback = async () =>
2121
- // {
2122
- // //灵纹一键下阵
2123
- // if (await this.GetModel<CardModel>().DispatchAction(new Rune_Action_CleanRune(_sutraCardData.Card.Id)))
2124
- // {
2125
- // Toast.Info("卸载成功");
2126
- // }
2127
- // },
2128
- // CancelText = "取消",
2129
- // }).Forget();
2130
- // `;
2131
- // const snippets = extractor.extractStrings(code);
2132
- // {
2133
- // let targetSnippet = snippets[0];
2134
- // expect(targetSnippet.originalIndex).toBe(code.indexOf(`提示`));
2135
- // expect(targetSnippet.originalCode).toBe(`提示`);
2136
- // expect(targetSnippet.convertedCode).toBe(`提示`);
2137
- // expect(targetSnippet.literals).toEqual([
2138
- // '提示'
2139
- // ]);
2140
- // expect(targetSnippet.isChanged).toBe(false);
2141
- // }
2142
- // {
2143
- // let targetSnippet = snippets[0];
2144
- // // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2145
- // expect(targetSnippet.originalIndex).toBe(code.indexOf(`卸载成功`));
2146
- // expect(targetSnippet.originalCode).toBe(`卸载成功`);
2147
- // expect(targetSnippet.convertedCode).toBe(`卸载成功`);
2148
- // expect(targetSnippet.literals).toEqual([
2149
- // '卸载成功'
2150
- // ]);
2151
- // expect(targetSnippet.isChanged).toBe(false);
2152
- // }
2153
- // {
2154
- // let targetSnippet = snippets[0];
2155
- // expect(targetSnippet.originalIndex).toBe(code.indexOf(`取消`));
2156
- // expect(targetSnippet.originalCode).toBe(`取消`);
2157
- // expect(targetSnippet.convertedCode).toBe(`取消`);
2158
- // expect(targetSnippet.literals).toEqual([
2159
- // '取消'
2160
- // ]);
2161
- // expect(targetSnippet.isChanged).toBe(false);
2162
- // }
2163
- // });
2101
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2102
+ test('should handle class member initialization assignment with anonymous function 1', () => {
2103
+ const code = `
2104
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2105
+ {
2106
+ Title = "提示",
2107
+ ConfirmCallback = async () =>
2108
+ {
2109
+ //灵纹一键下阵
2110
+ if (await this.GetModel<CardModel>().DispatchAction(new Rune_Action_CleanRune(_sutraCardData.Card.Id)))
2111
+ {
2112
+ Toast.Info("卸载成功");
2113
+ }
2114
+ },
2115
+ CancelText = "取消",
2116
+ }).Forget();
2117
+ `;
2118
+ const snippets = extractor.extractStrings(code);
2119
+ {
2120
+ let targetSnippet = snippets[0];
2121
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"提示"`));
2122
+ expect(targetSnippet.originalCode).toBe(`"提示"`);
2123
+ expect(targetSnippet.convertedCode).toBe(`"提示"`);
2124
+ expect(targetSnippet.literals).toEqual([
2125
+ '提示'
2126
+ ]);
2127
+ expect(targetSnippet.isChanged).toBe(false);
2128
+ }
2129
+ {
2130
+ let targetSnippet = snippets[1];
2131
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2132
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2133
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2134
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2135
+ expect(targetSnippet.literals).toEqual([
2136
+ '卸载成功'
2137
+ ]);
2138
+ expect(targetSnippet.isChanged).toBe(false);
2139
+ }
2140
+ {
2141
+ let targetSnippet = snippets[2];
2142
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"取消"`));
2143
+ expect(targetSnippet.originalCode).toBe(`"取消"`);
2144
+ expect(targetSnippet.convertedCode).toBe(`"取消"`);
2145
+ expect(targetSnippet.literals).toEqual([
2146
+ '取消'
2147
+ ]);
2148
+ expect(targetSnippet.isChanged).toBe(false);
2149
+ }
2150
+ });
2151
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2152
+ test('should handle class member initialization assignment with anonymous function 2', () => {
2153
+ const code = `
2154
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2155
+ {
2156
+ ConfirmCallback = async () =>
2157
+ {
2158
+ Toast.Info("卸载成功");
2159
+ },
2160
+ }).Forget();
2161
+ `;
2162
+ const snippets = extractor.extractStrings(code);
2163
+ {
2164
+ let targetSnippet = snippets[0];
2165
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2166
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2167
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2168
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2169
+ expect(targetSnippet.literals).toEqual([
2170
+ '卸载成功'
2171
+ ]);
2172
+ expect(targetSnippet.isChanged).toBe(false);
2173
+ }
2174
+ });
2175
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2176
+ test('should handle class member initialization assignment with anonymous function 2', () => {
2177
+ const code = `
2178
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2179
+ {
2180
+ ConfirmCallback = (int qwfewe) =>
2181
+ {
2182
+ Toast.Info($"卸载成功: {qwfewe}");
2183
+ },
2184
+ }).Forget();
2185
+ `;
2186
+ const snippets = extractor.extractStrings(code);
2187
+ {
2188
+ let targetSnippet = snippets[0];
2189
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2190
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"卸载成功: {qwfewe}"`));
2191
+ expect(targetSnippet.originalCode).toBe(`$"卸载成功: {qwfewe}"`);
2192
+ expect(targetSnippet.convertedCode).toBe(`Tr.Format("卸载成功: {0}", qwfewe)`);
2193
+ expect(targetSnippet.literals).toEqual([
2194
+ '卸载成功: {0}'
2195
+ ]);
2196
+ expect(targetSnippet.isChanged).toBe(true);
2197
+ }
2198
+ });
2199
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2200
+ test('should handle class member initialization assignment with anonymous function 3', () => {
2201
+ const code = `
2202
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2203
+ {
2204
+ ConfirmCallback = async () => Toast.Info("卸载成功"),
2205
+ }).Forget();
2206
+ `;
2207
+ const snippets = extractor.extractStrings(code);
2208
+ {
2209
+ let targetSnippet = snippets[0];
2210
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2211
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2212
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2213
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2214
+ expect(targetSnippet.literals).toEqual([
2215
+ '卸载成功'
2216
+ ]);
2217
+ expect(targetSnippet.isChanged).toBe(false);
2218
+ }
2219
+ });
2220
+ // 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
2221
+ test('should handle class member initialization assignment with anonymous function 4', () => {
2222
+ const code = `
2223
+ FUISys.Instance.ShowLayer<MainConfirmDialog>(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
2224
+ {
2225
+ ConfirmCallback = () => Toast.Info("卸载成功"),
2226
+ }).Forget();
2227
+ `;
2228
+ const snippets = extractor.extractStrings(code);
2229
+ {
2230
+ let targetSnippet = snippets[0];
2231
+ // 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
2232
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
2233
+ expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
2234
+ expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
2235
+ expect(targetSnippet.literals).toEqual([
2236
+ '卸载成功'
2237
+ ]);
2238
+ expect(targetSnippet.isChanged).toBe(false);
2239
+ }
2240
+ });
2241
+ // 测试处理 `xxx.text = yyy` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
2242
+ test('should handle .text = format 1', () => {
2243
+ const code = `m_text_iwff.text = info ;`;
2244
+ const snippets = extractor.extractStrings(code);
2245
+ {
2246
+ let targetSnippet = snippets[0];
2247
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`info`));
2248
+ expect(targetSnippet.originalCode).toBe(`info`);
2249
+ expect(targetSnippet.convertedCode).toBe(`info.TR()`);
2250
+ expect(targetSnippet.literals).toEqual([]);
2251
+ expect(targetSnippet.isChanged).toBe(true);
2252
+ }
2253
+ });
2254
+ // 测试处理 `xxx.text = yyy` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
2255
+ test('should handle .text = format 1', () => {
2256
+ const code = `m_text_info.text = info1.member1 + info2.member2;`;
2257
+ const snippets = extractor.extractStrings(code);
2258
+ {
2259
+ let targetSnippet = snippets[0];
2260
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`info1.member1 + info2.member2`));
2261
+ expect(targetSnippet.originalCode).toBe(`info1.member1 + info2.member2`);
2262
+ expect(targetSnippet.convertedCode).toBe(`info1.member1.TR() + info2.member2.TR()`);
2263
+ expect(targetSnippet.literals).toEqual([]);
2264
+ expect(targetSnippet.isChanged).toBe(true);
2265
+ }
2266
+ });
2164
2267
  });
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ // 测试我们的逻辑
3
+ const trimmedFullStatement = "case (int)State.Lock: Toast.Info($\"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁\"); break;";
4
+ const trimmedProcessedStatement = "case (int)State.Lock: Toast.Info(Tr.Format(\"灵田{0}级解锁\", _model.GetFarmLandUnlockLevel(_landId))); break;";
5
+ console.log('=== trimmedFullStatement ===');
6
+ console.log(trimmedFullStatement);
7
+ console.log('=== trimmedProcessedStatement ===');
8
+ console.log(trimmedProcessedStatement);
9
+ let colonIndex = -1;
10
+ let inString = false;
11
+ let escapeNext = false;
12
+ let stringDelimiter = '';
13
+ let searchStart = 0;
14
+ console.log('\n=== Looking for colon in trimmedFullStatement ===');
15
+ for (let i = 0; i < trimmedFullStatement.length; i++) {
16
+ const char = trimmedFullStatement[i];
17
+ if (escapeNext) {
18
+ escapeNext = false;
19
+ continue;
20
+ }
21
+ if (char === '\\') {
22
+ escapeNext = true;
23
+ continue;
24
+ }
25
+ if (inString) {
26
+ if (char === stringDelimiter) {
27
+ inString = false;
28
+ stringDelimiter = '';
29
+ }
30
+ continue;
31
+ }
32
+ if (char === '"' || char === "'") {
33
+ inString = true;
34
+ stringDelimiter = char;
35
+ continue;
36
+ }
37
+ if (char === ':') {
38
+ colonIndex = i;
39
+ console.log(`Found colon at index ${i}`);
40
+ break;
41
+ }
42
+ }
43
+ if (colonIndex !== -1) {
44
+ searchStart = colonIndex + 1;
45
+ }
46
+ console.log(`searchStart = ${searchStart}`);
47
+ let lastParenOpenIndex = -1;
48
+ inString = false;
49
+ escapeNext = false;
50
+ stringDelimiter = '';
51
+ console.log('\n=== Looking for last paren in trimmedProcessedStatement from searchStart ===');
52
+ for (let i = searchStart; i < trimmedProcessedStatement.length; i++) {
53
+ const char = trimmedProcessedStatement[i];
54
+ if (escapeNext) {
55
+ escapeNext = false;
56
+ continue;
57
+ }
58
+ if (char === '\\') {
59
+ escapeNext = true;
60
+ continue;
61
+ }
62
+ if (inString) {
63
+ if (char === stringDelimiter) {
64
+ inString = false;
65
+ stringDelimiter = '';
66
+ }
67
+ continue;
68
+ }
69
+ if (char === '"' || char === "'") {
70
+ inString = true;
71
+ stringDelimiter = char;
72
+ continue;
73
+ }
74
+ if (char === '(') {
75
+ lastParenOpenIndex = i;
76
+ console.log(`Found ( at index ${i}`);
77
+ }
78
+ }
79
+ console.log(`lastParenOpenIndex = ${lastParenOpenIndex}`);
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ const code = `infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";`;
3
+ const templateRegex = /(\$@?|@\$)"((?:[^"\\]|\\.)*)"/gs;
4
+ console.log('Testing regex...');
5
+ console.log('Code:', code);
6
+ let match;
7
+ while ((match = templateRegex.exec(code)) !== null) {
8
+ console.log('Match found:');
9
+ console.log('Full match:', match[0]);
10
+ console.log('Content:', match[2]);
11
+ console.log('Content length:', match[2].length);
12
+ console.log('---');
13
+ }