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.
- package/.trae/specs/handle-anonymous-function-strings/checklist.md +11 -0
- package/.trae/specs/handle-anonymous-function-strings/spec.md +137 -0
- package/.trae/specs/handle-anonymous-function-strings/tasks.md +65 -0
- package/dist/debug-arg.js +30 -0
- package/dist/debug-args.js +34 -0
- package/dist/debug-comment-5.js +25 -0
- package/dist/debug-comment-strings.js +24 -0
- package/dist/debug-full.js +14 -0
- package/dist/debug-template-issue.js +33 -0
- package/dist/debug-test-5.js +23 -0
- package/dist/debug-test.js +21 -0
- package/dist/debug.js +15 -0
- package/dist/simple-debug.js +27 -0
- package/dist/simple-test.js +61 -0
- package/dist/src/CSharpStringExtractor.js +56 -20
- package/dist/src/LiteralCollector.js +4 -3
- package/dist/temp-original-source.js +1 -0
- package/dist/test/CSharpStringExtractor.test.js +179 -76
- package/dist/test-logic.js +79 -0
- package/dist/test-regex.js +13 -0
- package/docs/CSharpStringExtractor/344/273/243/347/240/201/347/224/237/346/210/220/346/217/220/347/244/272/350/257/215.txt +72 -72
- package/package.json +1 -1
- package/src/CSharpStringExtractor.ts +59 -20
- package/src/LiteralCollector.ts +10 -9
- package/test/CSharpStringExtractor.test.ts +188 -79
- package/test/TestSpecialString.cs +24 -24
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
1055
|
+
if (afterEqual || inAnonymousFunction) {
|
|
1036
1056
|
const stringLiteral = fullCode.substring(stringStartPos, i + 1);
|
|
1037
|
-
|
|
1038
|
-
snippet
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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/
|
|
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
|
|
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
|
-
//
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
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
|
-
//
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
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
|
+
}
|