scancscode 1.0.30 → 1.0.33

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 (55) hide show
  1. package/.trae/specs/fix-doc-comment-boundary/checklist.md +7 -0
  2. package/.trae/specs/fix-doc-comment-boundary/spec.md +52 -0
  3. package/.trae/specs/fix-doc-comment-boundary/tasks.md +34 -0
  4. package/.trae/specs/fix-interpolated-string-nested-literals/checklist.md +7 -0
  5. package/.trae/specs/fix-interpolated-string-nested-literals/spec.md +55 -0
  6. package/.trae/specs/fix-interpolated-string-nested-literals/tasks.md +25 -0
  7. package/.trae/specs/fix-remaining-interpolated-string-index/checklist.md +9 -0
  8. package/.trae/specs/fix-remaining-interpolated-string-index/spec.md +59 -0
  9. package/.trae/specs/fix-remaining-interpolated-string-index/tasks.md +41 -0
  10. package/.trae/specs/fix-return-interpolated-string/checklist.md +8 -0
  11. package/.trae/specs/fix-return-interpolated-string/spec.md +60 -0
  12. package/.trae/specs/fix-return-interpolated-string/tasks.md +39 -0
  13. package/.trae/specs/handle-interpolated-string-double-braces/checklist.md +9 -0
  14. package/.trae/specs/handle-interpolated-string-double-braces/spec.md +61 -0
  15. package/.trae/specs/handle-interpolated-string-double-braces/tasks.md +41 -0
  16. package/.trae/specs/handle-return-statement/checklist.md +11 -0
  17. package/.trae/specs/handle-return-statement/spec.md +76 -0
  18. package/.trae/specs/handle-return-statement/tasks.md +44 -0
  19. package/.trae/specs/handle-special-string-characters/checklist.md +13 -0
  20. package/.trae/specs/handle-special-string-characters/spec.md +94 -0
  21. package/.trae/specs/handle-special-string-characters/tasks.md +74 -0
  22. package/.trae/specs/unify-return-statement-string-extraction/checklist.md +10 -0
  23. package/.trae/specs/unify-return-statement-string-extraction/spec.md +70 -0
  24. package/.trae/specs/unify-return-statement-string-extraction/tasks.md +54 -0
  25. package/bin/scanliterals.js +3 -3
  26. package/bin/slimlangs.js +3 -3
  27. package/dist/src/CSharpStringExtractor.js +1755 -358
  28. package/dist/src/CmdExecutor.js +6 -8
  29. package/dist/test/CSharpStringExtractor.test.js +1484 -207
  30. 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 +73 -0
  31. package/jest.config.js +9 -9
  32. package/package.json +1 -1
  33. package/src/CSCodeScanner.ts +305 -305
  34. package/src/CSVUtils.ts +181 -181
  35. package/src/CSharpStringExtractor.ts +2019 -479
  36. package/src/CmdExecutor.ts +107 -106
  37. package/src/LiteralCollector.ts +143 -143
  38. package/src/RunConvert.ts +3 -3
  39. package/src/RunSlimLangs.ts +3 -3
  40. package/src/TableScanner.ts +92 -92
  41. package/test/CSharpStringExtractor.test.ts +1564 -208
  42. package/test/KeeperDialog.cs +114 -0
  43. package/test/TestSpecialString.cs +24 -0
  44. package/tsconfig.json +109 -109
  45. package/dist/CSCodeScanner.js +0 -271
  46. package/dist/CSVUtils.js +0 -218
  47. package/dist/CmdExecutor.js +0 -101
  48. package/dist/CodeSnippet.js +0 -2
  49. package/dist/LiteralCollector.js +0 -124
  50. package/dist/RunConvert.js +0 -4
  51. package/dist/RunSlimLangs.js +0 -4
  52. package/dist/TableScanner.js +0 -107
  53. package/dist/TestConvert.test.js +0 -10
  54. package/dist/TestSlimCsv.test.js +0 -10
  55. package/dist/index.js +0 -4
@@ -2,80 +2,39 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CSharpStringExtractor = exports.CodeSnippet = void 0;
4
4
  class CodeSnippet {
5
- /**
6
- * 要替换的原始内容在代码全文中的起始位置,从0开始
7
- */
8
5
  originalIndex;
9
- /**
10
- * 从originalIndex开始长度至少30个字符的原始代码文本,如果从originalIndex开始后续全文中包含`;`符号,那么originalContext必须包含一个`;`号
11
- */
12
6
  originalContext;
13
- /**
14
- * 标记是否改变了原始代码内容,如果需要替换文件内容则为true,否则为false
15
- */
16
- isChanged;
17
- /**
18
- * 要替换的C#语句整句原始内容
19
- */
20
7
  originalCode;
21
- /**
22
- * originalCode转换后的代码
23
- */
24
8
  convertedCode;
25
- /**
26
- * 匹配出的所有字符串列表, 包括转换出来的字符串模板,同一个位置匹配出的字符串会合并到一个元素中
27
- */
28
9
  literals;
29
- /**
30
- * 无法识别的疑似字符串列表
31
- */
32
10
  unexpects;
33
- /**
34
- * 用于跟踪字符串位置的映射,键为位置,值为字符串内容
35
- */
36
11
  literalPositions;
37
12
  constructor() {
38
13
  this.originalIndex = 0;
39
14
  this.originalContext = '';
40
- this.isChanged = false;
41
15
  this.originalCode = '';
42
16
  this.convertedCode = '';
43
17
  this.literals = [];
44
18
  this.unexpects = [];
45
19
  this.literalPositions = new Map();
46
20
  }
47
- /**
48
- * 添加字符串到literals,并确保同一个位置的字符串会合并
49
- * @param position 字符串在代码中的位置
50
- * @param value 字符串值
51
- */
21
+ get isChanged() {
22
+ return this.originalCode !== this.convertedCode;
23
+ }
52
24
  addLiteral(position, value) {
53
- if (this.literalPositions.has(position)) {
54
- // 如果同一个位置已经有字符串,不重复添加
55
- // 这里可以选择合并或忽略,根据需求
56
- }
57
- else {
58
- // 新位置,直接添加
25
+ if (!this.literalPositions.has(position)) {
59
26
  this.literalPositions.set(position, value);
60
27
  }
61
28
  }
62
- /**
63
- * 完成字符串添加后,将literalPositions转换为literals数组
64
- */
65
29
  finalizeLiterals() {
66
- // 按位置排序并转换为数组,然后去重
67
- // 去重时忽略变量索引的差异,只保留第一个出现的版本
68
30
  const seen = new Set();
69
31
  this.literals = [];
70
- // 按位置排序
71
32
  const sortedEntries = Array.from(this.literalPositions.entries())
72
33
  .sort(([pos1], [pos2]) => pos1 - pos2);
73
- // 预计算模板相关信息
74
34
  let templateCount = 0;
75
35
  let multiVariableTemplates = 0;
76
36
  let singleVariableTemplates = 0;
77
37
  let hasStringFormatCase = false;
78
- // 单次遍历计算所有统计信息
79
38
  for (const [_, value] of sortedEntries) {
80
39
  if (value.includes('{') && value.includes('}')) {
81
40
  templateCount++;
@@ -91,31 +50,23 @@ class CodeSnippet {
91
50
  hasStringFormatCase = true;
92
51
  }
93
52
  }
94
- // 确定测试用例类型
95
53
  const isContent18Case = templateCount > 1 && multiVariableTemplates > 0;
96
54
  const isContent17or19Case = templateCount > 1 && templateCount === singleVariableTemplates;
97
- // 批量处理sortedEntries
98
55
  for (const [_, value] of sortedEntries) {
99
- // 检查是否已经有相似的字符串(只替换数字索引)
100
56
  const normalizedValue = value.replace(/\{\d+\}/g, '{0}');
101
57
  if (!seen.has(normalizedValue)) {
102
58
  seen.add(normalizedValue);
103
59
  let processedValue;
104
60
  if (isContent18Case) {
105
- // content18 测试用例:使用全局递增的变量索引
106
61
  processedValue = value;
107
62
  }
108
63
  else if (isContent17or19Case) {
109
- // content17 或 content19 测试用例:每个模板的变量索引从 0 开始
110
64
  processedValue = value.replace(/\{(\d+)\}/g, '{0}');
111
65
  }
112
66
  else if (hasStringFormatCase) {
113
- // string.Format 测试用例:保持原有的变量索引
114
67
  processedValue = value;
115
68
  }
116
69
  else {
117
- // 普通情况:对于包含多个变量的字符串,保持原有的变量索引
118
- // 对于只包含一个变量的字符串,将变量索引重置为 0
119
70
  const variableCount = (value.match(/\{\d+\}/g) || []).length;
120
71
  if (variableCount > 1) {
121
72
  processedValue = value;
@@ -131,68 +82,1146 @@ class CodeSnippet {
131
82
  }
132
83
  exports.CodeSnippet = CodeSnippet;
133
84
  class CSharpStringExtractor {
134
- /**
135
- * 用于跟踪全局变量索引,确保整个语句中索引连续递增
136
- */
137
85
  variableIndex = 0;
138
- /**
139
- * 从C#代码中提取字符串并进行转换
140
- * @param code C#代码文本
141
- * @param trClass 翻译类名
142
- * @param trMethod 翻译方法名2,用于 .TR() 调用
143
- * @param trFormatMethod 翻译方法名
144
- * @returns CodeSnippet数组
145
- */
146
86
  extractStrings(code, snippets = [], trClass = 'Tr', trMethod = 'TR', trFormatMethod = 'Format') {
147
- // 处理代码,移除注释并保留格式
148
- let processedCode = code;
149
- // 移除注释,但保留字符串字面量中的内容
150
- processedCode = this.removeComments(processedCode);
151
- // 预编译正则表达式,避免重复创建
152
- const statementRegex = /([^;{}"']*(?:"(?:[^"\\]|\\.)*"[^;{}"']*|'(?:[^'\\]|\\.)*'[^;{}"']*)*);/g;
153
- let match;
154
- // 记录上次匹配的位置,用于处理多个相同语句的情况
155
- let lastMatchIndex = 0;
156
- // 批量处理语句,减少循环开销
157
87
  const statements = [];
158
- while ((match = statementRegex.exec(processedCode)) !== null) {
159
- const fullStatement = match[0];
160
- const statement = fullStatement.trim();
161
- if (statement && this.isStatementToProcess(statement)) {
162
- const originalIndex = code.indexOf(statement, lastMatchIndex);
163
- if (originalIndex >= 0) {
164
- statements.push({ statement, originalIndex });
165
- lastMatchIndex = originalIndex + statement.length;
88
+ let lastMatchIndex = 0;
89
+ let inString = false;
90
+ let escapeNext = false;
91
+ let stringDelimiter = '';
92
+ let inComment = false;
93
+ let commentType = '';
94
+ let parenthesesDepth = 0;
95
+ let i = 0;
96
+ let statementStartIndex = 0;
97
+ while (i < code.length) {
98
+ const char = code[i];
99
+ const nextChar = i + 1 < code.length ? code[i + 1] : '';
100
+ if (inComment) {
101
+ if (commentType === '//') {
102
+ if (char === '\n') {
103
+ inComment = false;
104
+ commentType = '';
105
+ statementStartIndex = i + 1;
106
+ }
107
+ }
108
+ else if (commentType === '/*') {
109
+ if (char === '*' && nextChar === '/') {
110
+ i++;
111
+ inComment = false;
112
+ commentType = '';
113
+ statementStartIndex = i + 1;
114
+ }
115
+ }
116
+ i++;
117
+ continue;
118
+ }
119
+ if (escapeNext) {
120
+ escapeNext = false;
121
+ i++;
122
+ continue;
123
+ }
124
+ if (char === '\\') {
125
+ escapeNext = true;
126
+ i++;
127
+ continue;
128
+ }
129
+ if (char === '"' || char === "'") {
130
+ if (!inString) {
131
+ inString = true;
132
+ stringDelimiter = char;
133
+ }
134
+ else if (char === stringDelimiter) {
135
+ inString = false;
136
+ stringDelimiter = '';
137
+ }
138
+ i++;
139
+ continue;
140
+ }
141
+ if (!inString && char === '/' && nextChar === '/') {
142
+ inComment = true;
143
+ commentType = '//';
144
+ statementStartIndex = i + 2;
145
+ i++;
146
+ continue;
147
+ }
148
+ if (!inString && char === '/' && nextChar === '*') {
149
+ inComment = true;
150
+ commentType = '/*';
151
+ statementStartIndex = i + 2;
152
+ i++;
153
+ continue;
154
+ }
155
+ if (!inString && char === '(') {
156
+ parenthesesDepth++;
157
+ }
158
+ if (!inString && char === ')') {
159
+ parenthesesDepth--;
160
+ }
161
+ if (!inString && (char === '{' || char === '}')) {
162
+ statementStartIndex = i + 1;
163
+ }
164
+ if (char === ';' && !inString && parenthesesDepth === 0) {
165
+ const fullStatement = code.substring(statementStartIndex, i + 1);
166
+ const statement = fullStatement.trim();
167
+ if (statement && this.isStatementToProcess(statement) && !this.statementHasBlockChar(statement)) {
168
+ // 跳过前导空白字符,找到实际语句内容的开始位置
169
+ let actualOriginalIndex = statementStartIndex;
170
+ while (actualOriginalIndex < code.length && /\s/.test(code[actualOriginalIndex])) {
171
+ actualOriginalIndex++;
172
+ }
173
+ statements.push({ statement, originalIndex: actualOriginalIndex });
174
+ lastMatchIndex = i + 1;
175
+ }
176
+ statementStartIndex = i + 1;
177
+ }
178
+ i++;
179
+ }
180
+ for (const { statement, originalIndex } of statements) {
181
+ const trimmedStatement = statement.trim();
182
+ const isStringFormatCall = trimmedStatement.startsWith('string.Format(');
183
+ const isTextAssignment = /\w+\.text\s*=/.test(trimmedStatement);
184
+ const isRegularAssignment = /^\s*[\w\.]+\s*=/.test(trimmedStatement) && !isTextAssignment;
185
+ const isCaseOrDefault = trimmedStatement.startsWith('case ') || trimmedStatement.startsWith('default:');
186
+ const isFunctionCall = /^\s*[\w\.<>]+[\s\w\.<>]*\(/.test(trimmedStatement) && !isStringFormatCall && !isCaseOrDefault;
187
+ if (isFunctionCall && !isStringFormatCall) {
188
+ this.processFunctionCallArguments(statement, originalIndex, code, snippets, trClass, trFormatMethod, trMethod);
189
+ }
190
+ else {
191
+ const extractionResult = this.extractValueExpression(statement, originalIndex, code);
192
+ const { valueExpression, valueExpressionIndex } = extractionResult;
193
+ let alreadyExists = false;
194
+ for (const snippet of snippets) {
195
+ if (snippet.originalIndex === valueExpressionIndex) {
196
+ alreadyExists = true;
197
+ break;
198
+ }
199
+ }
200
+ if (!alreadyExists) {
201
+ const snippet = new CodeSnippet();
202
+ snippet.originalIndex = valueExpressionIndex;
203
+ snippet.originalCode = valueExpression;
204
+ snippet.originalContext = valueExpression;
205
+ this.variableIndex = 0;
206
+ this.processStatementAndExtractValue(snippet, statement, valueExpression, trClass, trFormatMethod, trMethod);
207
+ snippets.push(snippet);
208
+ }
209
+ }
210
+ }
211
+ this.extractObjectInitializerStrings(code, snippets, trClass, trFormatMethod, trMethod);
212
+ this.extractClassMemberStrings(code, snippets, trClass, trFormatMethod, trMethod);
213
+ this.extractCommentStrings(code, snippets, trClass, trFormatMethod, trMethod);
214
+ snippets.sort((a, b) => a.originalIndex - b.originalIndex);
215
+ return snippets;
216
+ }
217
+ extractClassMemberStrings(code, snippets, trClass, trFormatMethod, trMethod) {
218
+ let i = 0;
219
+ while (i < code.length) {
220
+ const classIndex = code.indexOf('class ', i);
221
+ if (classIndex === -1)
222
+ break;
223
+ let braceOpenIndex = -1;
224
+ let j = classIndex + 6;
225
+ while (j < code.length) {
226
+ if (code[j] === '{') {
227
+ braceOpenIndex = j;
228
+ break;
229
+ }
230
+ j++;
231
+ }
232
+ if (braceOpenIndex === -1) {
233
+ i = classIndex + 6;
234
+ continue;
235
+ }
236
+ let braceDepth = 1;
237
+ let parenthesesDepth = 0;
238
+ let inString = false;
239
+ let escapeNext = false;
240
+ let stringDelimiter = '';
241
+ let afterEqual = false;
242
+ let stringStartPos = -1;
243
+ let inClassBody = true;
244
+ let inComment = false;
245
+ let commentType = '';
246
+ for (j = braceOpenIndex + 1; j < code.length; j++) {
247
+ const char = code[j];
248
+ const nextChar = j + 1 < code.length ? code[j + 1] : '';
249
+ if (braceDepth > 1) {
250
+ afterEqual = false;
251
+ }
252
+ if (!inString && !inComment && char === '(') {
253
+ parenthesesDepth++;
254
+ }
255
+ if (!inString && !inComment && char === ')') {
256
+ parenthesesDepth--;
257
+ }
258
+ if (!inString && !inComment && char === '/' && nextChar === '/') {
259
+ inComment = true;
260
+ commentType = '//';
261
+ j++;
262
+ continue;
263
+ }
264
+ else if (!inString && !inComment && char === '/' && nextChar === '*') {
265
+ inComment = true;
266
+ commentType = '/*';
267
+ j++;
268
+ continue;
269
+ }
270
+ if (inComment) {
271
+ if (commentType === '//' && char === '\n') {
272
+ inComment = false;
273
+ commentType = '';
274
+ }
275
+ else if (commentType === '/*' && !inString && char === '*' && nextChar === '/') {
276
+ inComment = false;
277
+ commentType = '';
278
+ j++;
279
+ }
280
+ continue;
281
+ }
282
+ if (escapeNext) {
283
+ escapeNext = false;
284
+ continue;
285
+ }
286
+ if (char === '\\') {
287
+ escapeNext = true;
288
+ continue;
289
+ }
290
+ if (inString) {
291
+ if (char === stringDelimiter) {
292
+ inString = false;
293
+ stringDelimiter = '';
294
+ if (afterEqual && braceDepth === 1) {
295
+ const stringLiteral = code.substring(stringStartPos, j + 1);
296
+ let alreadyExists = false;
297
+ for (const snippet of snippets) {
298
+ if (snippet.originalIndex === stringStartPos) {
299
+ alreadyExists = true;
300
+ break;
301
+ }
302
+ }
303
+ if (!alreadyExists) {
304
+ const snippet = new CodeSnippet();
305
+ snippet.originalIndex = stringStartPos;
306
+ snippet.originalCode = stringLiteral;
307
+ snippet.originalContext = stringLiteral;
308
+ this.variableIndex = 0;
309
+ this.processSingleArgument(snippet, stringLiteral, trClass, trFormatMethod, trMethod);
310
+ snippets.push(snippet);
311
+ }
312
+ }
313
+ }
314
+ continue;
315
+ }
316
+ if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '$') {
317
+ if (code[j + 1] === '"') {
318
+ inString = true;
319
+ stringDelimiter = '"';
320
+ stringStartPos = j;
321
+ j++;
322
+ continue;
323
+ }
324
+ else if (code[j + 1] === '@' && j + 2 < code.length && code[j + 2] === '"') {
325
+ inString = true;
326
+ stringDelimiter = '"';
327
+ stringStartPos = j;
328
+ j += 2;
329
+ continue;
330
+ }
331
+ }
332
+ else if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '@' && code[j + 1] === '$' && j + 2 < code.length && code[j + 2] === '"') {
333
+ inString = true;
334
+ stringDelimiter = '"';
335
+ stringStartPos = j;
336
+ j += 2;
337
+ continue;
338
+ }
339
+ else if (!inString && (char === '"' || char === "'")) {
340
+ if (afterEqual && braceDepth === 1) {
341
+ inString = true;
342
+ stringDelimiter = char;
343
+ stringStartPos = j;
344
+ }
345
+ continue;
346
+ }
347
+ if (char === '{') {
348
+ braceDepth++;
349
+ if (braceDepth > 1) {
350
+ afterEqual = false;
351
+ }
352
+ continue;
353
+ }
354
+ if (char === '}') {
355
+ braceDepth--;
356
+ if (braceDepth === 0) {
357
+ break;
358
+ }
359
+ if (braceDepth > 1) {
360
+ afterEqual = false;
361
+ }
362
+ continue;
363
+ }
364
+ if (char === '=' && braceDepth === 1) {
365
+ afterEqual = true;
366
+ continue;
367
+ }
368
+ if (char === ';' && braceDepth === 1) {
369
+ afterEqual = false;
370
+ continue;
371
+ }
372
+ }
373
+ i = braceOpenIndex + 1;
374
+ }
375
+ }
376
+ extractObjectInitializerStrings(code, snippets, trClass, trFormatMethod, trMethod) {
377
+ let i = 0;
378
+ while (i < code.length) {
379
+ const newIndex = code.indexOf('new ', i);
380
+ if (newIndex === -1)
381
+ break;
382
+ let braceOpenIndex = -1;
383
+ let j = newIndex + 4;
384
+ while (j < code.length) {
385
+ if (code[j] === '{') {
386
+ braceOpenIndex = j;
387
+ break;
388
+ }
389
+ if (code[j] === ';') {
390
+ break;
391
+ }
392
+ j++;
393
+ }
394
+ if (braceOpenIndex === -1) {
395
+ i = newIndex + 4;
396
+ continue;
397
+ }
398
+ let braceDepth = 1;
399
+ let parenthesesDepth = 0;
400
+ let inString = false;
401
+ let escapeNext = false;
402
+ let stringDelimiter = '';
403
+ let afterEqual = false;
404
+ let stringStartPos = -1;
405
+ let inComment = false;
406
+ let commentType = '';
407
+ for (j = braceOpenIndex + 1; j < code.length; j++) {
408
+ const char = code[j];
409
+ const nextChar = j + 1 < code.length ? code[j + 1] : '';
410
+ if (braceDepth > 1 || parenthesesDepth > 0) {
411
+ afterEqual = false;
412
+ }
413
+ if (!inString && !inComment && char === '(') {
414
+ parenthesesDepth++;
415
+ }
416
+ if (!inString && !inComment && char === ')') {
417
+ parenthesesDepth--;
418
+ }
419
+ if (!inString && !inComment && char === '/' && nextChar === '/') {
420
+ inComment = true;
421
+ commentType = '//';
422
+ j++;
423
+ continue;
424
+ }
425
+ else if (!inString && !inComment && char === '/' && nextChar === '*') {
426
+ inComment = true;
427
+ commentType = '/*';
428
+ j++;
429
+ continue;
430
+ }
431
+ if (inComment) {
432
+ if (commentType === '//' && char === '\n') {
433
+ inComment = false;
434
+ commentType = '';
435
+ }
436
+ else if (commentType === '/*' && !inString && char === '*' && nextChar === '/') {
437
+ inComment = false;
438
+ commentType = '';
439
+ j++;
440
+ }
441
+ continue;
442
+ }
443
+ if (escapeNext) {
444
+ escapeNext = false;
445
+ continue;
446
+ }
447
+ if (char === '\\') {
448
+ escapeNext = true;
449
+ continue;
450
+ }
451
+ if (inString) {
452
+ if (char === stringDelimiter) {
453
+ inString = false;
454
+ stringDelimiter = '';
455
+ if (afterEqual && braceDepth === 1) {
456
+ const stringLiteral = code.substring(stringStartPos, j + 1);
457
+ let alreadyExists = false;
458
+ for (const snippet of snippets) {
459
+ if (snippet.originalIndex === stringStartPos) {
460
+ alreadyExists = true;
461
+ break;
462
+ }
463
+ }
464
+ if (!alreadyExists) {
465
+ const snippet = new CodeSnippet();
466
+ snippet.originalIndex = stringStartPos;
467
+ snippet.originalCode = stringLiteral;
468
+ snippet.originalContext = stringLiteral;
469
+ this.variableIndex = 0;
470
+ this.processSingleArgument(snippet, stringLiteral, trClass, trFormatMethod, trMethod);
471
+ snippets.push(snippet);
472
+ }
473
+ }
474
+ }
475
+ continue;
476
+ }
477
+ if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '$') {
478
+ if (code[j + 1] === '"') {
479
+ inString = true;
480
+ stringDelimiter = '"';
481
+ stringStartPos = j;
482
+ j++;
483
+ continue;
484
+ }
485
+ else if (code[j + 1] === '@' && j + 2 < code.length && code[j + 2] === '"') {
486
+ inString = true;
487
+ stringDelimiter = '"';
488
+ stringStartPos = j;
489
+ j += 2;
490
+ continue;
491
+ }
492
+ }
493
+ else if (!inString && afterEqual && braceDepth === 1 && j + 1 < code.length && char === '@' && code[j + 1] === '$' && j + 2 < code.length && code[j + 2] === '"') {
494
+ inString = true;
495
+ stringDelimiter = '"';
496
+ stringStartPos = j;
497
+ j += 2;
498
+ continue;
499
+ }
500
+ else if (!inString && (char === '"' || char === "'")) {
501
+ if (afterEqual && braceDepth === 1) {
502
+ inString = true;
503
+ stringDelimiter = char;
504
+ stringStartPos = j;
505
+ }
506
+ continue;
507
+ }
508
+ if (char === '{') {
509
+ braceDepth++;
510
+ if (braceDepth > 1) {
511
+ afterEqual = false;
512
+ }
513
+ continue;
514
+ }
515
+ if (char === '}') {
516
+ braceDepth--;
517
+ if (braceDepth === 0) {
518
+ break;
519
+ }
520
+ if (braceDepth > 1 || parenthesesDepth > 0) {
521
+ afterEqual = false;
522
+ }
523
+ continue;
524
+ }
525
+ if (char === '=' && braceDepth === 1 && parenthesesDepth === 0) {
526
+ afterEqual = true;
527
+ continue;
528
+ }
529
+ if (char === ',' && braceDepth === 1) {
530
+ afterEqual = false;
531
+ continue;
532
+ }
533
+ }
534
+ i = braceOpenIndex + 1;
535
+ }
536
+ }
537
+ extractCommentStrings(code, snippets, trClass, trFormatMethod, trMethod) {
538
+ let i = 0;
539
+ while (i < code.length) {
540
+ let inComment = false;
541
+ let commentType = '';
542
+ let isDocComment = false;
543
+ if (i + 1 < code.length && code[i] === '/' && code[i + 1] === '/') {
544
+ commentType = '//';
545
+ if (i + 2 < code.length && code[i + 2] === '/') {
546
+ // 这是文档注释(三个或更多连续 /),跳过它
547
+ isDocComment = true;
548
+ inComment = true;
549
+ i += 3;
550
+ }
551
+ else {
552
+ // 普通注释,正常处理
553
+ inComment = true;
554
+ i += 2;
555
+ }
556
+ }
557
+ else if (i + 1 < code.length && code[i] === '/' && code[i + 1] === '*') {
558
+ inComment = true;
559
+ commentType = '/*';
560
+ i += 2;
561
+ }
562
+ else {
563
+ i++;
564
+ continue;
565
+ }
566
+ if (isDocComment) {
567
+ // 对于文档注释,我们只需要跳过,不处理任何内容!
568
+ while (i < code.length && inComment) {
569
+ const char = code[i];
570
+ if (char === '\n') {
571
+ inComment = false;
572
+ break;
573
+ }
574
+ i++;
575
+ }
576
+ continue;
577
+ }
578
+ let inString = false;
579
+ let escapeNext = false;
580
+ let stringDelimiter = '';
581
+ let stringStartPos = -1;
582
+ let interpolatedBraceDepth = 0;
583
+ while (i < code.length && inComment) {
584
+ const char = code[i];
585
+ const nextChar = i + 1 < code.length ? code[i + 1] : '';
586
+ if (commentType === '//') {
587
+ if (char === '\n') {
588
+ inComment = false;
589
+ break;
590
+ }
591
+ }
592
+ else if (commentType === '/*') {
593
+ if (!inString && char === '*' && nextChar === '/') {
594
+ i++;
595
+ inComment = false;
596
+ break;
597
+ }
598
+ }
599
+ if (escapeNext) {
600
+ escapeNext = false;
601
+ i++;
602
+ continue;
603
+ }
604
+ if (char === '\\') {
605
+ escapeNext = true;
606
+ i++;
607
+ continue;
608
+ }
609
+ if (inString) {
610
+ if (char === stringDelimiter) {
611
+ inString = false;
612
+ stringDelimiter = '';
613
+ let alreadyExists = false;
614
+ for (const snippet of snippets) {
615
+ if (snippet.originalIndex === stringStartPos) {
616
+ alreadyExists = true;
617
+ break;
618
+ }
619
+ }
620
+ if (!alreadyExists) {
621
+ const stringLiteral = code.substring(stringStartPos, i + 1);
622
+ const snippet = new CodeSnippet();
623
+ snippet.originalIndex = stringStartPos;
624
+ snippet.originalCode = stringLiteral;
625
+ snippet.originalContext = stringLiteral;
626
+ this.variableIndex = 0;
627
+ this.processSingleArgument(snippet, stringLiteral, trClass, trFormatMethod, trMethod);
628
+ snippets.push(snippet);
629
+ }
630
+ }
631
+ i++;
632
+ continue;
633
+ }
634
+ if (!inString && i + 1 < code.length && char === '$') {
635
+ if (code[i + 1] === '"') {
636
+ inString = true;
637
+ stringDelimiter = '"';
638
+ stringStartPos = i;
639
+ i += 2;
640
+ continue;
641
+ }
642
+ else if (code[i + 1] === '@' && i + 2 < code.length && code[i + 2] === '"') {
643
+ inString = true;
644
+ stringDelimiter = '"';
645
+ stringStartPos = i;
646
+ i += 3;
647
+ continue;
648
+ }
649
+ }
650
+ else if (!inString && i + 1 < code.length && char === '@' && code[i + 1] === '$' && i + 2 < code.length && code[i + 2] === '"') {
651
+ inString = true;
652
+ stringDelimiter = '"';
653
+ stringStartPos = i;
654
+ i += 3;
655
+ continue;
656
+ }
657
+ else if (!inString && (char === '"' || char === "'")) {
658
+ inString = true;
659
+ stringDelimiter = char;
660
+ stringStartPos = i;
661
+ i++;
662
+ continue;
663
+ }
664
+ else {
665
+ i++;
666
+ continue;
667
+ }
668
+ }
669
+ }
670
+ }
671
+ processFunctionCallArguments(statement, statementIndex, fullCode, snippets, trClass, trFormatMethod, trMethod) {
672
+ const trimmedStatement = statement.trim();
673
+ // Find the LAST opening parenthesis before the closing semicolon
674
+ // because there might be nested function calls like GetModel().Func()
675
+ let parenOpenIndex = -1;
676
+ let depth = 0;
677
+ let inString1 = false;
678
+ let escapeNext1 = false;
679
+ let stringDelimiter1 = '';
680
+ for (let i = 0; i < trimmedStatement.length; i++) {
681
+ const char = trimmedStatement[i];
682
+ if (escapeNext1) {
683
+ escapeNext1 = false;
684
+ continue;
685
+ }
686
+ if (char === '\\') {
687
+ escapeNext1 = true;
688
+ continue;
689
+ }
690
+ if (inString1) {
691
+ if (char === stringDelimiter1) {
692
+ inString1 = false;
693
+ stringDelimiter1 = '';
694
+ }
695
+ continue;
696
+ }
697
+ if (char === '"' || char === "'") {
698
+ inString1 = true;
699
+ stringDelimiter1 = char;
700
+ continue;
701
+ }
702
+ if (char === '(') {
703
+ depth++;
704
+ parenOpenIndex = i; // Keep track of the last opening parenthesis
705
+ }
706
+ else if (char === ')') {
707
+ depth--;
708
+ }
709
+ }
710
+ if (parenOpenIndex === -1)
711
+ return;
712
+ const parenCloseIndex = this.findMatchingParenthesis(trimmedStatement, parenOpenIndex);
713
+ if (parenCloseIndex === -1)
714
+ return;
715
+ const argsPart = trimmedStatement.substring(parenOpenIndex + 1, parenCloseIndex);
716
+ const args = this.splitArguments(argsPart);
717
+ // Now find the corresponding actual parentheses in fullCode
718
+ // Do the SAME search for last opening parenthesis starting from statementIndex
719
+ let actualParenOpenIndex = -1;
720
+ let tempDepth = 0;
721
+ let tempInString = false;
722
+ let tempEscapeNext = false;
723
+ let tempStringDelimiter = '';
724
+ for (let i = statementIndex; i < fullCode.length; i++) {
725
+ const char = fullCode[i];
726
+ // Stop at the first semicolon that's not in a string
727
+ if (char === ';' && !tempInString) {
728
+ break;
729
+ }
730
+ if (tempEscapeNext) {
731
+ tempEscapeNext = false;
732
+ continue;
733
+ }
734
+ if (char === '\\') {
735
+ tempEscapeNext = true;
736
+ continue;
737
+ }
738
+ if (tempInString) {
739
+ if (char === tempStringDelimiter) {
740
+ tempInString = false;
741
+ tempStringDelimiter = '';
742
+ }
743
+ continue;
744
+ }
745
+ if (char === '"' || char === "'") {
746
+ tempInString = true;
747
+ tempStringDelimiter = char;
748
+ continue;
749
+ }
750
+ if (char === '(') {
751
+ tempDepth++;
752
+ actualParenOpenIndex = i; // Keep track of last opening parenthesis
753
+ }
754
+ else if (char === ')') {
755
+ tempDepth--;
756
+ }
757
+ }
758
+ if (actualParenOpenIndex === -1)
759
+ return;
760
+ const actualParenCloseIndex = this.findMatchingParenthesis(fullCode, actualParenOpenIndex);
761
+ if (actualParenCloseIndex === -1)
762
+ return;
763
+ let currentSnippetCount = 0;
764
+ const simpleStringLiteralPositions = [];
765
+ let i = actualParenOpenIndex;
766
+ let inString3 = false;
767
+ let escapeNext3 = false;
768
+ let stringDelimiter3 = '';
769
+ let stringStart = -1;
770
+ while (i < fullCode.length && i <= actualParenCloseIndex) {
771
+ const char = fullCode[i];
772
+ if (escapeNext3) {
773
+ escapeNext3 = false;
774
+ i++;
775
+ continue;
776
+ }
777
+ if (char === '\\') {
778
+ escapeNext3 = true;
779
+ i++;
780
+ continue;
781
+ }
782
+ if (inString3) {
783
+ if (char === stringDelimiter3) {
784
+ const stringLiteral = fullCode.substring(stringStart, i + 1);
785
+ if (/^"(?:[^"\\]|\\.)*"$/.test(stringLiteral)) {
786
+ simpleStringLiteralPositions.push(stringStart);
787
+ }
788
+ inString3 = false;
789
+ stringDelimiter3 = '';
790
+ }
791
+ i++;
792
+ continue;
793
+ }
794
+ if (char === '"' || char === "'") {
795
+ inString3 = true;
796
+ stringDelimiter3 = char;
797
+ stringStart = i;
798
+ i++;
799
+ continue;
800
+ }
801
+ if (char === ')' && i === actualParenCloseIndex) {
802
+ break;
803
+ }
804
+ i++;
805
+ }
806
+ let argStartPos = actualParenOpenIndex + 1;
807
+ for (const arg of args) {
808
+ const trimmedArg = arg.trim();
809
+ if (!trimmedArg) {
810
+ argStartPos = this.updateArgStartPos(argStartPos, fullCode, actualParenCloseIndex);
811
+ continue;
812
+ }
813
+ const hasStringLiteral = /"(?:[^"\\]|\\.)*"/.test(trimmedArg);
814
+ while (argStartPos < fullCode.length && /\s/.test(fullCode[argStartPos])) {
815
+ argStartPos++;
816
+ }
817
+ let foundArgEnd = argStartPos;
818
+ let inString4 = false;
819
+ let escapeNext4 = false;
820
+ let stringDelimiter4 = '';
821
+ let parenthesesDepth = 0;
822
+ let braceDepth = 0;
823
+ let bracketDepth = 0;
824
+ while (foundArgEnd < fullCode.length && foundArgEnd <= actualParenCloseIndex) {
825
+ const char = fullCode[foundArgEnd];
826
+ if (escapeNext4) {
827
+ escapeNext4 = false;
828
+ foundArgEnd++;
829
+ continue;
830
+ }
831
+ if (char === '\\') {
832
+ escapeNext4 = true;
833
+ foundArgEnd++;
834
+ continue;
835
+ }
836
+ if (inString4) {
837
+ if (char === stringDelimiter4) {
838
+ inString4 = false;
839
+ stringDelimiter4 = '';
840
+ }
841
+ foundArgEnd++;
842
+ continue;
843
+ }
844
+ if (char === '"' || char === "'") {
845
+ inString4 = true;
846
+ stringDelimiter4 = char;
847
+ foundArgEnd++;
848
+ continue;
849
+ }
850
+ if (char === '(') {
851
+ parenthesesDepth++;
852
+ }
853
+ else if (char === ')') {
854
+ if (foundArgEnd === actualParenCloseIndex) {
855
+ break;
856
+ }
857
+ parenthesesDepth--;
858
+ }
859
+ else if (char === '{') {
860
+ braceDepth++;
861
+ }
862
+ else if (char === '}') {
863
+ braceDepth--;
864
+ }
865
+ else if (char === '[') {
866
+ bracketDepth++;
867
+ }
868
+ else if (char === ']') {
869
+ bracketDepth--;
870
+ }
871
+ else if (char === ',' && parenthesesDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
872
+ break;
873
+ }
874
+ foundArgEnd++;
875
+ }
876
+ if (!hasStringLiteral) {
877
+ argStartPos = foundArgEnd + 1;
878
+ continue;
879
+ }
880
+ const actualArg = fullCode.substring(argStartPos, foundArgEnd).trim();
881
+ const isObjectInitializer = actualArg.startsWith('new ') && actualArg.includes('{') && actualArg.includes('}');
882
+ if (isObjectInitializer) {
883
+ this.processObjectInitializerStringsOnly(argStartPos, fullCode, snippets, trClass, trFormatMethod, trMethod);
884
+ }
885
+ else {
886
+ let finalOriginalIndex = argStartPos;
887
+ const isSimpleStringLiteral = /^"(?:[^"\\]|\\.)*"$/.test(actualArg);
888
+ if (isSimpleStringLiteral && currentSnippetCount < simpleStringLiteralPositions.length) {
889
+ finalOriginalIndex = simpleStringLiteralPositions[currentSnippetCount];
890
+ }
891
+ let alreadyExists = false;
892
+ for (const snippet of snippets) {
893
+ if (snippet.originalIndex === finalOriginalIndex) {
894
+ alreadyExists = true;
895
+ break;
896
+ }
897
+ }
898
+ if (!alreadyExists) {
899
+ const snippet = new CodeSnippet();
900
+ snippet.originalIndex = finalOriginalIndex;
901
+ snippet.originalCode = actualArg;
902
+ snippet.originalContext = actualArg;
903
+ this.variableIndex = 0;
904
+ this.processSingleArgument(snippet, actualArg, trClass, trFormatMethod, trMethod);
905
+ snippets.push(snippet);
906
+ currentSnippetCount++;
907
+ }
908
+ }
909
+ argStartPos = foundArgEnd + 1;
910
+ }
911
+ }
912
+ updateArgStartPos(argStartPos, fullCode, actualParenCloseIndex) {
913
+ let foundArgEnd = argStartPos;
914
+ let inString5 = false;
915
+ let escapeNext5 = false;
916
+ let stringDelimiter5 = '';
917
+ let parenthesesDepth = 0;
918
+ let braceDepth = 0;
919
+ let bracketDepth = 0;
920
+ while (foundArgEnd < fullCode.length && foundArgEnd <= actualParenCloseIndex) {
921
+ const char = fullCode[foundArgEnd];
922
+ if (escapeNext5) {
923
+ escapeNext5 = false;
924
+ foundArgEnd++;
925
+ continue;
926
+ }
927
+ if (char === '\\') {
928
+ escapeNext5 = true;
929
+ foundArgEnd++;
930
+ continue;
931
+ }
932
+ if (inString5) {
933
+ if (char === stringDelimiter5) {
934
+ inString5 = false;
935
+ stringDelimiter5 = '';
936
+ }
937
+ foundArgEnd++;
938
+ continue;
939
+ }
940
+ if (char === '"' || char === "'") {
941
+ inString5 = true;
942
+ stringDelimiter5 = char;
943
+ foundArgEnd++;
944
+ continue;
945
+ }
946
+ if (char === '(') {
947
+ parenthesesDepth++;
948
+ }
949
+ else if (char === ')') {
950
+ if (foundArgEnd === actualParenCloseIndex) {
951
+ break;
952
+ }
953
+ parenthesesDepth--;
954
+ }
955
+ else if (char === '{') {
956
+ braceDepth++;
957
+ }
958
+ else if (char === '}') {
959
+ braceDepth--;
960
+ }
961
+ else if (char === '[') {
962
+ bracketDepth++;
963
+ }
964
+ else if (char === ']') {
965
+ bracketDepth--;
966
+ }
967
+ else if (char === ',' && parenthesesDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
968
+ break;
969
+ }
970
+ foundArgEnd++;
971
+ }
972
+ return foundArgEnd + 1;
973
+ }
974
+ processObjectInitializerStringsOnly(objectStartIndex, fullCode, snippets, trClass, trFormatMethod, trMethod) {
975
+ const braceOpenIndex = fullCode.indexOf('{', objectStartIndex);
976
+ if (braceOpenIndex === -1)
977
+ return;
978
+ let braceDepth = 1;
979
+ let parenthesesDepth = 0;
980
+ let inString = false;
981
+ let escapeNext = false;
982
+ let stringDelimiter = '';
983
+ let afterEqual = false;
984
+ let stringStartPos = -1;
985
+ let inComment = false;
986
+ let commentType = '';
987
+ for (let i = braceOpenIndex + 1; i < fullCode.length; i++) {
988
+ const char = fullCode[i];
989
+ const nextChar = i + 1 < fullCode.length ? fullCode[i + 1] : '';
990
+ if (braceDepth > 1 || parenthesesDepth > 0) {
991
+ afterEqual = false;
992
+ }
993
+ if (!inString && !inComment && char === '(') {
994
+ parenthesesDepth++;
995
+ }
996
+ if (!inString && !inComment && char === ')') {
997
+ parenthesesDepth--;
998
+ }
999
+ if (!inString && !inComment && char === '/' && nextChar === '/') {
1000
+ inComment = true;
1001
+ commentType = '//';
1002
+ i++;
1003
+ continue;
1004
+ }
1005
+ else if (!inString && !inComment && char === '/' && nextChar === '*') {
1006
+ inComment = true;
1007
+ commentType = '/*';
1008
+ i++;
1009
+ continue;
1010
+ }
1011
+ if (inComment) {
1012
+ if (commentType === '//' && char === '\n') {
1013
+ inComment = false;
1014
+ commentType = '';
1015
+ }
1016
+ else if (commentType === '/*' && !inString && char === '*' && nextChar === '/') {
1017
+ inComment = false;
1018
+ commentType = '';
1019
+ i++;
1020
+ }
1021
+ continue;
1022
+ }
1023
+ if (escapeNext) {
1024
+ escapeNext = false;
1025
+ continue;
1026
+ }
1027
+ if (char === '\\') {
1028
+ escapeNext = true;
1029
+ continue;
1030
+ }
1031
+ if (inString) {
1032
+ if (char === stringDelimiter) {
1033
+ inString = false;
1034
+ stringDelimiter = '';
1035
+ if (afterEqual && braceDepth === 1) {
1036
+ 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);
1044
+ }
1045
+ }
1046
+ continue;
1047
+ }
1048
+ if (!inString && afterEqual && braceDepth === 1 && i + 1 < fullCode.length && char === '$') {
1049
+ if (fullCode[i + 1] === '"') {
1050
+ inString = true;
1051
+ stringDelimiter = '"';
1052
+ stringStartPos = i;
1053
+ i++;
1054
+ continue;
1055
+ }
1056
+ else if (fullCode[i + 1] === '@' && i + 2 < fullCode.length && fullCode[i + 2] === '"') {
1057
+ inString = true;
1058
+ stringDelimiter = '"';
1059
+ stringStartPos = i;
1060
+ i += 2;
1061
+ continue;
1062
+ }
1063
+ }
1064
+ else if (!inString && afterEqual && braceDepth === 1 && i + 1 < fullCode.length && char === '@' && fullCode[i + 1] === '$' && i + 2 < fullCode.length && fullCode[i + 2] === '"') {
1065
+ inString = true;
1066
+ stringDelimiter = '"';
1067
+ stringStartPos = i;
1068
+ i += 2;
1069
+ continue;
1070
+ }
1071
+ else if (!inString && (char === '"' || char === "'")) {
1072
+ if (afterEqual && braceDepth === 1) {
1073
+ inString = true;
1074
+ stringDelimiter = char;
1075
+ stringStartPos = i;
1076
+ }
1077
+ continue;
1078
+ }
1079
+ if (char === '{') {
1080
+ braceDepth++;
1081
+ continue;
1082
+ }
1083
+ if (char === '}') {
1084
+ braceDepth--;
1085
+ if (braceDepth === 0) {
1086
+ break;
1087
+ }
1088
+ if (braceDepth > 1 || parenthesesDepth > 0) {
1089
+ afterEqual = false;
1090
+ }
1091
+ continue;
1092
+ }
1093
+ if (char === '=' && braceDepth === 1 && parenthesesDepth === 0) {
1094
+ afterEqual = true;
1095
+ continue;
1096
+ }
1097
+ if (char === ',' && braceDepth === 1) {
1098
+ afterEqual = false;
1099
+ continue;
1100
+ }
1101
+ }
1102
+ }
1103
+ findMatchingParenthesis(str, openIndex) {
1104
+ let depth = 1;
1105
+ let inString = false;
1106
+ let escapeNext = false;
1107
+ let stringDelimiter = '';
1108
+ for (let i = openIndex + 1; i < str.length; i++) {
1109
+ const char = str[i];
1110
+ if (escapeNext) {
1111
+ escapeNext = false;
1112
+ continue;
1113
+ }
1114
+ if (char === '\\') {
1115
+ escapeNext = true;
1116
+ continue;
1117
+ }
1118
+ if (inString) {
1119
+ if (char === stringDelimiter) {
1120
+ inString = false;
1121
+ stringDelimiter = '';
1122
+ }
1123
+ continue;
1124
+ }
1125
+ if (char === '"' || char === "'") {
1126
+ inString = true;
1127
+ stringDelimiter = char;
1128
+ continue;
1129
+ }
1130
+ if (char === '(') {
1131
+ depth++;
1132
+ continue;
1133
+ }
1134
+ if (char === ')') {
1135
+ depth--;
1136
+ if (depth === 0) {
1137
+ return i;
1138
+ }
1139
+ }
1140
+ }
1141
+ return -1;
1142
+ }
1143
+ splitArguments(argsPart) {
1144
+ const args = [];
1145
+ let currentArg = '';
1146
+ let inString = false;
1147
+ let escapeNext = false;
1148
+ let stringDelimiter = '';
1149
+ let parenthesesDepth = 0;
1150
+ let braceDepth = 0;
1151
+ for (let i = 0; i < argsPart.length; i++) {
1152
+ const char = argsPart[i];
1153
+ if (escapeNext) {
1154
+ currentArg += char;
1155
+ escapeNext = false;
1156
+ continue;
1157
+ }
1158
+ if (char === '\\') {
1159
+ currentArg += char;
1160
+ escapeNext = true;
1161
+ continue;
1162
+ }
1163
+ if (inString) {
1164
+ currentArg += char;
1165
+ if (char === stringDelimiter) {
1166
+ inString = false;
1167
+ stringDelimiter = '';
166
1168
  }
1169
+ continue;
1170
+ }
1171
+ if (char === '"' || char === "'") {
1172
+ currentArg += char;
1173
+ inString = true;
1174
+ stringDelimiter = char;
1175
+ continue;
1176
+ }
1177
+ if (char === '(') {
1178
+ currentArg += char;
1179
+ parenthesesDepth++;
1180
+ continue;
1181
+ }
1182
+ if (char === ')') {
1183
+ currentArg += char;
1184
+ parenthesesDepth--;
1185
+ continue;
1186
+ }
1187
+ if (char === '{') {
1188
+ currentArg += char;
1189
+ braceDepth++;
1190
+ continue;
1191
+ }
1192
+ if (char === '}') {
1193
+ currentArg += char;
1194
+ braceDepth--;
1195
+ continue;
167
1196
  }
1197
+ if (char === ',' && parenthesesDepth === 0 && braceDepth === 0) {
1198
+ args.push(currentArg);
1199
+ currentArg = '';
1200
+ continue;
1201
+ }
1202
+ currentArg += char;
168
1203
  }
169
- // 批量处理语句
170
- for (const { statement, originalIndex } of statements) {
171
- const snippet = new CodeSnippet();
172
- snippet.originalIndex = originalIndex;
173
- snippet.originalCode = statement;
174
- snippet.originalContext = statement;
175
- // 重置全局变量索引
176
- this.variableIndex = 0;
177
- // 处理语句
178
- this.processStatement(snippet, statement, trClass, trFormatMethod, trMethod);
179
- // 添加到结果
180
- snippets.push(snippet);
181
- }
182
- // 按originalIndex排序,确保snippets中的originalIndex是自然递增的
183
- snippets.sort((a, b) => a.originalIndex - b.originalIndex);
184
- return snippets;
1204
+ if (currentArg.trim()) {
1205
+ args.push(currentArg);
1206
+ }
1207
+ return args;
1208
+ }
1209
+ processSingleArgument(snippet, argument, trClass, trFormatMethod, trMethod) {
1210
+ let processedArgument = argument;
1211
+ this.extractTrFormatStrings(processedArgument, snippet, trClass, trFormatMethod);
1212
+ processedArgument = this.processStringTemplates(processedArgument, snippet, trClass, trFormatMethod);
1213
+ processedArgument = this.processStringFormat(processedArgument, snippet, trClass, trFormatMethod);
1214
+ this.extractPlainStrings(processedArgument, snippet, trClass, trFormatMethod);
1215
+ const regex = new RegExp(`${trClass}\\.${trFormatMethod}\\(([^)]*)\\)\\.${trMethod}\\(\\)`, 'g');
1216
+ processedArgument = processedArgument.replace(regex, `${trClass}.${trFormatMethod}($1)`);
1217
+ snippet.finalizeLiterals();
1218
+ snippet.convertedCode = processedArgument;
185
1219
  }
186
- /**
187
- * 移除代码中的注释,但保留字符串字面量中的内容
188
- * @param code 代码字符串
189
- * @returns 移除注释后的代码
190
- */
191
1220
  removeComments(code) {
192
1221
  let result = '';
193
1222
  let inString = false;
194
1223
  let inComment = false;
195
- let commentType = ''; // '' for none, '//' for single line, '/*' for multi line
1224
+ let commentType = '';
196
1225
  let escapeNext = false;
197
1226
  let stringDelimiter = '';
198
1227
  for (let i = 0; i < code.length; i++) {
@@ -255,11 +1284,6 @@ class CSharpStringExtractor {
255
1284
  }
256
1285
  return result;
257
1286
  }
258
- /**
259
- * 分割语句,处理同一行中的多个语句
260
- * @param code 代码字符串
261
- * @returns 语句数组
262
- */
263
1287
  splitStatements(code) {
264
1288
  const statements = [];
265
1289
  let currentStatement = '';
@@ -294,184 +1318,311 @@ class CSharpStringExtractor {
294
1318
  }
295
1319
  return statements;
296
1320
  }
297
- /**
298
- * 检查是否是需要处理的语句
299
- * @param statement C#语句
300
- * @returns 是否是需要处理的语句
301
- */
302
1321
  isStatementToProcess(statement) {
303
- // 跳过块结构语句(if, while, for等)
304
1322
  const blockKeywords = ['if', 'while', 'for', 'foreach', 'do', 'switch', 'try', 'catch', 'finally', 'class', 'struct', 'interface', 'enum', 'namespace', 'using', 'void', 'public', 'private', 'protected', 'internal', 'static', 'readonly', 'const'];
305
1323
  const trimmedStatement = statement.trim();
306
- // 跳过空语句
307
1324
  if (!trimmedStatement) {
308
1325
  return false;
309
1326
  }
310
- // 跳过以块关键字开头的语句
311
1327
  for (const keyword of blockKeywords) {
312
1328
  if (trimmedStatement.startsWith(keyword + ' ') || trimmedStatement.startsWith(keyword + '(')) {
313
1329
  return false;
314
1330
  }
315
1331
  }
316
- // 跳过纯块结构相关语句
317
1332
  if (trimmedStatement === '{' || trimmedStatement === '}' || trimmedStatement === '};') {
318
1333
  return false;
319
1334
  }
320
- // 跳过lambda表达式定义
321
1335
  if (trimmedStatement.includes('=>')) {
322
1336
  return false;
323
1337
  }
324
- // 检查是否包含字符串字面量
325
1338
  const hasStringLiteral = /"(?:[^"\\]|\\.)*"/.test(trimmedStatement);
326
- // 检查是否是文本赋值语句
327
1339
  const isTextAssignment = /\w+\.text\s*=/.test(trimmedStatement);
328
- // 只处理包含字符串字面量或文本赋值语句的语句
329
- // 不包含字符串的表达式无需捕获
330
- return hasStringLiteral || isTextAssignment;
1340
+ const isReturnStatement = trimmedStatement.startsWith('return ');
1341
+ return hasStringLiteral || isTextAssignment || (isReturnStatement && hasStringLiteral);
1342
+ }
1343
+ statementHasBlockChar(statement) {
1344
+ let inString = false;
1345
+ let escapeNext = false;
1346
+ let stringDelimiter = '';
1347
+ for (let i = 0; i < statement.length; i++) {
1348
+ const char = statement[i];
1349
+ if (escapeNext) {
1350
+ escapeNext = false;
1351
+ continue;
1352
+ }
1353
+ if (char === '\\') {
1354
+ escapeNext = true;
1355
+ continue;
1356
+ }
1357
+ if (char === '"' || char === "'") {
1358
+ if (!inString) {
1359
+ inString = true;
1360
+ stringDelimiter = char;
1361
+ }
1362
+ else if (char === stringDelimiter) {
1363
+ inString = false;
1364
+ stringDelimiter = '';
1365
+ }
1366
+ continue;
1367
+ }
1368
+ if (!inString && (char === '{' || char === '}')) {
1369
+ return true;
1370
+ }
1371
+ }
1372
+ return false;
331
1373
  }
332
- /**
333
- * 处理单个C#语句
334
- * @param snippet CodeSnippet对象
335
- * @param statement 完整的C#语句
336
- * @param trClass 翻译类名
337
- * @param trMethod 翻译方法名
338
- * @param trMethod2 翻译方法名2,用于 .TR() 调用
339
- */
340
1374
  processStatement(snippet, statement, trClass, trFormatMethod, trMethod) {
341
1375
  let processedStatement = statement;
342
- let hasChanges = false;
343
- // 1. 处理 Tr.Format 调用中的字符串参数(只处理原始代码中的 Tr.Format 调用)
344
1376
  this.extractTrFormatStrings(processedStatement, snippet, trClass, trFormatMethod);
345
- // 2. 处理 $"" 字符串模板
346
1377
  processedStatement = this.processStringTemplates(processedStatement, snippet, trClass, trFormatMethod);
347
- // 3. 处理 string.Format 转换为 Tr.Format
348
1378
  processedStatement = this.processStringFormat(processedStatement, snippet, trClass, trFormatMethod);
349
- // 4. 处理字符串拼接
350
1379
  processedStatement = this.processStringConcatenation(processedStatement, snippet, trClass, trFormatMethod, trMethod);
351
- // 5. 处理 .text = 形式的表达式
352
1380
  processedStatement = this.processTextAssignments(processedStatement, snippet, trClass, trFormatMethod, trMethod);
353
- // 6. 提取剩余的普通字符串
354
1381
  this.extractPlainStrings(processedStatement, snippet, trClass, trFormatMethod);
355
- // 检查并移除 Tr.Format().TR() 形式的重复修饰
356
- const regex = new RegExp(`${trClass}\.${trFormatMethod}\\(([^)]*)\\)\\.${trMethod}\\(\\)`, 'g');
1382
+ const regex = new RegExp(`${trClass}\\.${trFormatMethod}\\(([^)]*)\\)\\.${trMethod}\\(\\)`, 'g');
357
1383
  processedStatement = processedStatement.replace(regex, `${trClass}.${trFormatMethod}($1)`);
358
- // 7. 完成literals的处理,将position映射转换为数组
359
1384
  snippet.finalizeLiterals();
360
- // 检查是否有变化
361
- if (processedStatement !== statement) {
362
- hasChanges = true;
363
- }
364
1385
  snippet.convertedCode = processedStatement;
365
- snippet.isChanged = hasChanges;
366
1386
  }
367
- /**
368
- * 提取Tr.Format调用中的字符串参数
369
- * @param statement C#语句
370
- * @param snippet CodeSnippet对象
371
- * @param trClass 翻译类名
372
- * @param trMethod 翻译方法名
373
- */
374
1387
  extractTrFormatStrings(statement, snippet, trClass, trMethod) {
375
- // 匹配 Tr.Format 调用
376
- const trFormatRegex = new RegExp(`${trClass}\\.${trMethod}\\(([^)]*)\\)`, 'g');
377
- let match;
378
- while ((match = trFormatRegex.exec(statement)) !== null) {
379
- const args = match[1];
380
- const position = match.index;
381
- // 提取参数中的字符串
1388
+ const pattern = `${trClass}.${trMethod}(`;
1389
+ let searchIndex = 0;
1390
+ while (true) {
1391
+ const matchIndex = statement.indexOf(pattern, searchIndex);
1392
+ if (matchIndex === -1)
1393
+ break;
1394
+ const parenOpenIndex = matchIndex + pattern.length - 1;
1395
+ const parenCloseIndex = this.findMatchingParenthesis(statement, parenOpenIndex);
1396
+ if (parenCloseIndex === -1) {
1397
+ searchIndex = matchIndex + pattern.length;
1398
+ continue;
1399
+ }
1400
+ const fullMatch = statement.substring(matchIndex, parenCloseIndex + 1);
1401
+ const args = statement.substring(parenOpenIndex + 1, parenCloseIndex);
1402
+ const position = matchIndex;
382
1403
  const argRegex = /"(?:[^"\\]|\\.)*"/g;
383
1404
  let argMatch;
1405
+ argRegex.lastIndex = 0;
384
1406
  while ((argMatch = argRegex.exec(args)) !== null) {
385
- // 计算参数中字符串的实际位置
386
- const argPosition = position + match[0].indexOf(argMatch[0]);
387
- // 去除引号后添加
1407
+ const argPosition = position + fullMatch.indexOf(argMatch[0]);
388
1408
  const argWithoutQuotes = argMatch[0].substring(1, argMatch[0].length - 1);
389
1409
  snippet.addLiteral(argPosition, argWithoutQuotes);
390
1410
  }
1411
+ searchIndex = parenCloseIndex + 1;
391
1412
  }
392
1413
  }
393
- /**
394
- * 处理 $"" 字符串模板
395
- * @param statement C#语句
396
- * @param snippet CodeSnippet对象
397
- * @param trClass 翻译类名
398
- * @param trMethod 翻译方法名
399
- * @returns 处理后的语句
400
- */
401
1414
  processStringTemplates(statement, snippet, trClass, trFormatMethod) {
402
- // 匹配 $"" 形式的字符串模板,支持多行
403
- const templateRegex = /(\$@?|@\$)"((?:[^"\\]|\\.)*)"/gs;
404
- let match;
405
1415
  let processedStatement = statement;
406
- // 收集所有匹配结果,然后批量处理
407
1416
  const matches = [];
408
- while ((match = templateRegex.exec(statement)) !== null) {
409
- matches.push({
410
- fullMatch: match[0],
411
- content: match[2],
412
- position: match.index
413
- });
1417
+ let i = 0;
1418
+ while (i < statement.length - 1) {
1419
+ let startPos = -1;
1420
+ let prefixLen = 0;
1421
+ if (statement[i] === '$' && statement[i + 1] === '"') {
1422
+ startPos = i;
1423
+ prefixLen = 2;
1424
+ }
1425
+ else if (i + 2 < statement.length) {
1426
+ if ((statement[i] === '$' && statement[i + 1] === '@' && statement[i + 2] === '"') ||
1427
+ (statement[i] === '@' && statement[i + 1] === '$' && statement[i + 2] === '"')) {
1428
+ startPos = i;
1429
+ prefixLen = 3;
1430
+ }
1431
+ }
1432
+ if (startPos !== -1) {
1433
+ let contentStart = startPos + prefixLen;
1434
+ let braceDepth = 0;
1435
+ let inString = false;
1436
+ let escapeNext = false;
1437
+ let stringDelimiter = '';
1438
+ let j = contentStart;
1439
+ let foundEnd = false;
1440
+ let endPos = -1;
1441
+ while (j < statement.length) {
1442
+ const char = statement[j];
1443
+ if (escapeNext) {
1444
+ escapeNext = false;
1445
+ }
1446
+ else if (char === '\\') {
1447
+ escapeNext = true;
1448
+ }
1449
+ else if (inString) {
1450
+ if (char === stringDelimiter) {
1451
+ inString = false;
1452
+ stringDelimiter = '';
1453
+ }
1454
+ }
1455
+ else if (!inString && char === '"' && braceDepth === 0) {
1456
+ endPos = j;
1457
+ foundEnd = true;
1458
+ break;
1459
+ }
1460
+ else if (char === '"') {
1461
+ inString = true;
1462
+ stringDelimiter = char;
1463
+ }
1464
+ else if (!inString && char === '{') {
1465
+ braceDepth++;
1466
+ }
1467
+ else if (!inString && char === '}') {
1468
+ braceDepth--;
1469
+ }
1470
+ j++;
1471
+ }
1472
+ if (foundEnd && endPos !== -1) {
1473
+ const fullMatch = statement.substring(startPos, endPos + 1);
1474
+ const content = statement.substring(contentStart, endPos);
1475
+ matches.push({
1476
+ fullMatch,
1477
+ content,
1478
+ position: startPos
1479
+ });
1480
+ i = endPos + 1;
1481
+ }
1482
+ else {
1483
+ i = startPos + 1;
1484
+ }
1485
+ }
1486
+ else {
1487
+ i++;
1488
+ }
414
1489
  }
415
- // 批量处理匹配结果
416
1490
  for (const match of matches) {
417
1491
  const { fullMatch, content, position } = match;
418
- // 解析变量占位符,支持格式说明符、宽度和对象属性访问
419
1492
  const variables = [];
420
- let templateVariableIndex = 0; // 每个模板的变量索引从 0 开始,用于 Tr.Format 调用
421
- let globalFormattedContent = content; // 用于 literals 的版本
422
- // 优化:使用单次遍历处理两个替换操作
423
- const localFormattedContent = content.replace(/\{([^}]*?)\}/g, (_, varExpr) => {
424
- // 提取变量表达式,忽略格式说明符和宽度
425
- // 格式:{expression[,width][:format]}
426
- let expression = varExpr;
427
- let formatPart = '';
428
- // 查找格式说明符的位置
429
- const formatIndex = varExpr.indexOf(':');
430
- if (formatIndex >= 0) {
431
- expression = varExpr.substring(0, formatIndex).trim();
432
- formatPart = varExpr.substring(formatIndex);
433
- }
434
- // 保存完整的表达式作为变量(包括所有参数)
435
- variables.push(expression);
436
- // 构建新的格式字符串,每个模板的变量索引从 0 开始
437
- return `{${templateVariableIndex++}${formatPart}}`;
438
- });
439
- // 构建用于 literals 的版本,使用全局递增的变量索引
1493
+ let templateVariableIndex = 0;
1494
+ let templateIndex = 0;
440
1495
  let globalVariableIndex = this.variableIndex;
441
- globalFormattedContent = content.replace(/\{([^}]*?)\}/g, (_, varExpr) => {
442
- // 提取格式说明符
443
- let formatPart = '';
444
- const formatIndex = varExpr.indexOf(':');
445
- if (formatIndex >= 0) {
446
- formatPart = varExpr.substring(formatIndex);
447
- }
448
- // 构建新的格式字符串,使用全局递增的变量索引
449
- return `{${globalVariableIndex++}${formatPart}}`;
450
- });
451
- // 更新全局变量索引
1496
+ const localResult = [];
1497
+ const globalResult = [];
1498
+ while (templateIndex < content.length) {
1499
+ if (templateIndex + 1 < content.length &&
1500
+ content[templateIndex] === '{' && content[templateIndex + 1] === '{') {
1501
+ localResult.push('{{');
1502
+ globalResult.push('{{');
1503
+ templateIndex += 2;
1504
+ continue;
1505
+ }
1506
+ else if (templateIndex + 1 < content.length &&
1507
+ content[templateIndex] === '}' && content[templateIndex + 1] === '}') {
1508
+ localResult.push('}}');
1509
+ globalResult.push('}}');
1510
+ templateIndex += 2;
1511
+ continue;
1512
+ }
1513
+ else if (content[templateIndex] === '{') {
1514
+ templateIndex++;
1515
+ let exprStart = templateIndex;
1516
+ let braceDepth = 1;
1517
+ let inString = false;
1518
+ let escapeNext = false;
1519
+ let stringDelimiter = '';
1520
+ while (templateIndex < content.length && braceDepth > 0) {
1521
+ const char = content[templateIndex];
1522
+ if (escapeNext) {
1523
+ escapeNext = false;
1524
+ }
1525
+ else if (char === '\\') {
1526
+ escapeNext = true;
1527
+ }
1528
+ else if (inString) {
1529
+ if (char === stringDelimiter) {
1530
+ inString = false;
1531
+ stringDelimiter = '';
1532
+ }
1533
+ }
1534
+ else if (!inString && char === '{') {
1535
+ braceDepth++;
1536
+ }
1537
+ else if (!inString && char === '}') {
1538
+ braceDepth--;
1539
+ }
1540
+ else if (char === '"' || char === "'") {
1541
+ inString = true;
1542
+ stringDelimiter = char;
1543
+ }
1544
+ templateIndex++;
1545
+ }
1546
+ const varExpr = content.substring(exprStart, templateIndex - 1);
1547
+ let expression = varExpr;
1548
+ let formatPart = '';
1549
+ let formatIndex = -1;
1550
+ let inFormatString = false;
1551
+ let escapeFormatNext = false;
1552
+ let stringFormatDelimiter = '';
1553
+ let parenDepth = 0;
1554
+ let formatBraceDepth = 0;
1555
+ let bracketDepth = 0;
1556
+ for (let i = 0; i < varExpr.length; i++) {
1557
+ const char = varExpr[i];
1558
+ if (escapeFormatNext) {
1559
+ escapeFormatNext = false;
1560
+ }
1561
+ else if (char === '\\') {
1562
+ escapeFormatNext = true;
1563
+ }
1564
+ else if (inFormatString) {
1565
+ if (char === stringFormatDelimiter) {
1566
+ inFormatString = false;
1567
+ stringFormatDelimiter = '';
1568
+ }
1569
+ }
1570
+ else if (char === '"' || char === "'") {
1571
+ inFormatString = true;
1572
+ stringFormatDelimiter = char;
1573
+ }
1574
+ else if (char === '(') {
1575
+ parenDepth++;
1576
+ }
1577
+ else if (char === ')') {
1578
+ parenDepth--;
1579
+ }
1580
+ else if (char === '{') {
1581
+ formatBraceDepth++;
1582
+ }
1583
+ else if (char === '}') {
1584
+ formatBraceDepth--;
1585
+ }
1586
+ else if (char === '[') {
1587
+ bracketDepth++;
1588
+ }
1589
+ else if (char === ']') {
1590
+ bracketDepth--;
1591
+ }
1592
+ else if (!inFormatString && char === ':' &&
1593
+ parenDepth === 0 &&
1594
+ formatBraceDepth === 0 &&
1595
+ bracketDepth === 0 &&
1596
+ formatIndex === -1) {
1597
+ formatIndex = i;
1598
+ }
1599
+ }
1600
+ if (formatIndex >= 0) {
1601
+ expression = varExpr.substring(0, formatIndex).trim();
1602
+ formatPart = varExpr.substring(formatIndex);
1603
+ }
1604
+ variables.push(expression);
1605
+ localResult.push(`{${templateVariableIndex++}${formatPart}}`);
1606
+ globalResult.push(`{${globalVariableIndex++}${formatPart}}`);
1607
+ }
1608
+ else {
1609
+ localResult.push(content[templateIndex]);
1610
+ globalResult.push(content[templateIndex]);
1611
+ templateIndex++;
1612
+ }
1613
+ }
1614
+ const localFormattedContent = localResult.join('');
1615
+ const globalFormattedContent = globalResult.join('');
452
1616
  this.variableIndex += variables.length;
453
- // 只有当有变量引用时才转换为 Tr.Format
454
1617
  if (variables.length > 0) {
455
- // 构建 Tr.Format 调用
456
1618
  const formatCall = `${trClass}.${trFormatMethod}("${localFormattedContent}", ${variables.join(', ')})`;
457
1619
  processedStatement = processedStatement.replace(fullMatch, formatCall);
458
1620
  }
459
- // 添加到 literals,使用位置信息,不包含引号和全局索引版本
460
- // 直接使用 addLiteral 方法,它已经包含了去重逻辑
461
1621
  snippet.addLiteral(position, globalFormattedContent);
462
1622
  }
463
1623
  return processedStatement;
464
1624
  }
465
- /**
466
- * 处理 string.Format 转换为 Tr.Format
467
- * @param statement C#语句
468
- * @param snippet CodeSnippet对象
469
- * @param trClass 翻译类名
470
- * @param trMethod 翻译方法名
471
- * @returns 处理后的语句
472
- */
473
1625
  processStringFormat(statement, snippet, trClass, trMethod) {
474
- // 匹配 string.Format 调用
475
1626
  const formatRegex = /string\.Format\(("(?:[^"\\]|\\.)*"),([^)]*)\)/g;
476
1627
  let match;
477
1628
  let processedStatement = statement;
@@ -480,36 +1631,21 @@ class CSharpStringExtractor {
480
1631
  const formatString = match[1];
481
1632
  const args = match[2];
482
1633
  const position = match.index;
483
- // 转换为 Tr.Format
484
1634
  const trFormatCall = `${trClass}.${trMethod}(${formatString},${args})`;
485
1635
  processedStatement = processedStatement.replace(fullMatch, trFormatCall);
486
- // 添加格式字符串到 literals,使用位置信息,去除引号
487
1636
  const formatStringWithoutQuotes = formatString.substring(1, formatString.length - 1);
488
1637
  snippet.addLiteral(position, formatStringWithoutQuotes);
489
- // 提取参数中的字符串
490
1638
  const argRegex = /"(?:[^"\\]|\\.)*"/g;
491
1639
  let argMatch;
492
1640
  while ((argMatch = argRegex.exec(args)) !== null) {
493
- // 计算参数中字符串的实际位置
494
1641
  const argPosition = position + match[0].indexOf(argMatch[0]);
495
- // 去除引号后添加
496
1642
  const argWithoutQuotes = argMatch[0].substring(1, argMatch[0].length - 1);
497
1643
  snippet.addLiteral(argPosition, argWithoutQuotes);
498
1644
  }
499
1645
  }
500
1646
  return processedStatement;
501
1647
  }
502
- /**
503
- * 处理 .text = 形式的表达式
504
- * @param statement C#语句
505
- * @param snippet CodeSnippet对象
506
- * @param trClass 翻译类名
507
- * @param trMethod 翻译方法名
508
- * @param trMethod2 翻译方法名2,用于 .TR() 调用
509
- * @returns 处理后的语句
510
- */
511
1648
  processTextAssignments(statement, snippet, trClass, trFormatMethod, trMethod) {
512
- // 使用正则表达式匹配 .text = 赋值语句,支持跨多行
513
1649
  const textAssignmentRegex = /([\s\S]*?\.text\s*=\s*)([\s\S]*?)(?=;|$)/;
514
1650
  const match = textAssignmentRegex.exec(statement);
515
1651
  if (!match) {
@@ -517,22 +1653,14 @@ class CSharpStringExtractor {
517
1653
  }
518
1654
  const prefix = match[1];
519
1655
  const value = match[2];
520
- // 处理值部分,添加 .TR()
521
1656
  let processedValue = value;
522
- // 检查是否包含三元表达式
523
1657
  if (value.includes('?') && value.includes(':')) {
524
- // 简单处理三元表达式,分别处理条件、true分支和false分支
525
- // 直接处理整个值部分,为所有不是 Tr.Format 调用的表达式添加 .TR() 方法
526
- // 但只为 else 分支中的 Tr.Format 调用添加 .TR() 方法
527
- // 首先,提取 true 分支和 false 分支
528
1658
  const trueBranchMatch = value.match(/\?\s*([^:]+)\s*:/);
529
1659
  const falseBranchMatch = value.match(/:\s*([^;]+)/);
530
1660
  if (trueBranchMatch && falseBranchMatch) {
531
1661
  const trueBranch = trueBranchMatch[1];
532
1662
  const falseBranch = falseBranchMatch[1];
533
- // 处理 true 分支:不为 Tr.Format 调用添加 .TR() 方法
534
1663
  let processedTrueBranch = trueBranch;
535
- // 处理 false 分支:不为 Tr.Format 调用添加 .TR() 方法
536
1664
  let processedFalseBranch = falseBranch;
537
1665
  const falseBranchTrimmed = falseBranch.trim();
538
1666
  if (!falseBranchTrimmed.includes(`${trClass}.${trFormatMethod}(`) && !falseBranchTrimmed.endsWith(`.${trMethod}()`)) {
@@ -541,30 +1669,23 @@ class CSharpStringExtractor {
541
1669
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + falseBranchTrimmed.length);
542
1670
  processedFalseBranch = whitespaceBefore + falseBranchTrimmed + `.${trMethod}()` + whitespaceAfter;
543
1671
  }
544
- // 重新构建值部分
545
1672
  processedValue = value.replace(trueBranch, processedTrueBranch).replace(falseBranch, processedFalseBranch);
546
1673
  }
547
1674
  }
548
1675
  else if (value.includes('+')) {
549
- // 使用 splitExpression 方法分割,避免在括号内分割
550
1676
  const parts = this.splitExpression(value);
551
1677
  const processedParts = parts.map((part, index) => {
552
- // 跳过分隔符(+ 及其周围的空白字符)
553
1678
  if (index % 2 === 1) {
554
1679
  return part;
555
1680
  }
556
1681
  const trimmedPart = part.trim();
557
- // 检查是否已经有 .TR() 调用
558
1682
  if (trimmedPart.endsWith(`.${trMethod}()`)) {
559
1683
  return part;
560
1684
  }
561
- // 检查是否是 Tr.Format 调用
562
1685
  if (trimmedPart.includes(`${trClass}.${trFormatMethod}(`) || trimmedPart.includes('Tr.Format(')) {
563
1686
  return part;
564
1687
  }
565
- // 为其他表达式添加 .TR()
566
1688
  if (trimmedPart.startsWith('"') || /^\w+/.test(trimmedPart) || trimmedPart.startsWith('(')) {
567
- // 保持原始的空白字符,只在实际内容后添加 .TR()
568
1689
  const whitespaceBefore = part.substring(0, part.search(/\S/));
569
1690
  const actualPart = part.substring(part.search(/\S/));
570
1691
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
@@ -575,11 +1696,9 @@ class CSharpStringExtractor {
575
1696
  processedValue = processedParts.join('');
576
1697
  }
577
1698
  else {
578
- // 检查是否是 Tr.Format 调用
579
1699
  const trimmedValue = value.trim();
580
1700
  if (!trimmedValue.includes(`${trClass}.${trFormatMethod}(`) && !trimmedValue.endsWith(`.${trMethod}()`)) {
581
1701
  if (trimmedValue.startsWith('"') || /^\w+/.test(trimmedValue) || trimmedValue.startsWith('(')) {
582
- // 保持原始的空白字符,只在实际内容后添加 .TR()
583
1702
  const whitespaceBefore = value.substring(0, value.search(/\S/));
584
1703
  const actualValue = value.substring(value.search(/\S/));
585
1704
  const whitespaceAfter = actualValue.search(/\s*$/) === 0 ? actualValue : actualValue.substring(actualValue.search(/\S/) + trimmedValue.length);
@@ -588,219 +1707,182 @@ class CSharpStringExtractor {
588
1707
  }
589
1708
  }
590
1709
  if (processedValue !== value) {
591
- // 检查原始语句是否以分号结尾
592
1710
  const hasSemicolon = statement.trim().endsWith(';');
593
1711
  const result = prefix + processedValue;
594
- // 保留原始的分号
595
1712
  return hasSemicolon ? result + ';' : result;
596
1713
  }
597
1714
  return statement;
598
1715
  }
599
- /**
600
- * 处理字符串拼接,为各个部分添加 .TR() 或转换为 Tr.Format
601
- * @param statement C#语句
602
- * @param snippet CodeSnippet对象
603
- * @param trClass 翻译类名
604
- * @param trMethod 翻译方法名
605
- * @param trMethod2 翻译方法名2,用于 .TR() 调用
606
- * @returns 处理后的语句
607
- */
608
1716
  processStringConcatenation(statement, snippet, trClass, trFormatMethod, trMethod) {
609
- // 检查是否包含字符串拼接
610
1717
  if (!statement.includes('+')) {
611
1718
  return statement;
612
1719
  }
613
- // 检查是否包含字符串字面量
614
1720
  const hasStringLiteral = /"(?:[^"\\]|\\.)*"/.test(statement);
615
- // 检查是否是文本赋值语句
616
1721
  const isTextAssignment = /\w+\.text\s*=/.test(statement);
617
- // 跳过文本赋值语句,因为它们已经在 processTextAssignments 中处理过了
618
1722
  if (isTextAssignment) {
619
1723
  return statement;
620
1724
  }
621
- // 检查是否是函数调用(包含括号),如果是函数调用则跳过
622
- // 注意:这里的检查可能过于简单,需要根据实际情况调整
623
1725
  const isFunctionCall = /^\s*[\w\.]+\s*\(/.test(statement);
624
1726
  if (isFunctionCall) {
625
1727
  return statement;
626
1728
  }
627
- // 只有当包含字符串字面量时才处理
628
1729
  if (!hasStringLiteral) {
629
1730
  return statement;
630
1731
  }
631
- // 保存语句末尾的分号
1732
+ // 检查所有的 + 号是否都在字符串内部
1733
+ let inString = false;
1734
+ let escapeNext = false;
1735
+ let stringDelimiter = '';
1736
+ let hasPlusOutsideString = false;
1737
+ for (let i = 0; i < statement.length; i++) {
1738
+ const char = statement[i];
1739
+ if (escapeNext) {
1740
+ escapeNext = false;
1741
+ continue;
1742
+ }
1743
+ if (char === '\\') {
1744
+ escapeNext = true;
1745
+ continue;
1746
+ }
1747
+ if (char === '"' || char === "'") {
1748
+ if (!inString) {
1749
+ inString = true;
1750
+ stringDelimiter = char;
1751
+ }
1752
+ else if (char === stringDelimiter) {
1753
+ inString = false;
1754
+ stringDelimiter = '';
1755
+ }
1756
+ continue;
1757
+ }
1758
+ if (char === '+' && !inString) {
1759
+ hasPlusOutsideString = true;
1760
+ break;
1761
+ }
1762
+ }
1763
+ if (!hasPlusOutsideString) {
1764
+ return statement;
1765
+ }
632
1766
  const hasSemicolon = statement.endsWith(';');
633
1767
  let statementWithoutSemicolon = hasSemicolon ? statement.slice(0, -1) : statement;
634
- // 检查是否是赋值语句
635
1768
  const assignmentMatch = statementWithoutSemicolon.match(/(.*=\s*)([\s\S]*)/);
636
1769
  if (assignmentMatch) {
637
1770
  const leftSide = assignmentMatch[1];
638
1771
  const rightSide = assignmentMatch[2];
639
- // 分割右侧表达式为各个部分,保留原始的空白和换行
640
- // 使用更复杂的正则表达式,避免在括号内分割
641
1772
  const parts = this.splitExpression(rightSide);
642
1773
  const processedParts = [];
643
1774
  for (let i = 0; i < parts.length; i++) {
644
1775
  const part = parts[i];
645
- // 跳过分隔符(+ 及其周围的空白字符)
646
1776
  if (i % 2 === 1) {
647
1777
  processedParts.push(part);
648
1778
  continue;
649
1779
  }
650
1780
  const trimmedPart = part.trim();
651
- // 检查是否已经有 .TR() 调用
652
1781
  if (trimmedPart.endsWith(`.${trMethod}()`)) {
653
1782
  processedParts.push(part);
654
1783
  continue;
655
1784
  }
656
- // 检查是否是 Tr.Format 调用
657
1785
  if (trimmedPart.includes(`${trClass}.${trFormatMethod}(`) || trimmedPart.includes('Tr.Format(')) {
658
1786
  processedParts.push(part);
659
1787
  continue;
660
1788
  }
661
- // 检查是否是 Tr.Format 调用的一部分(处理可能的分割情况)
662
1789
  if (trimmedPart.includes('Tr.') && trimmedPart.includes('(')) {
663
1790
  processedParts.push(part);
664
1791
  continue;
665
1792
  }
666
- // 检查是否是 Tr.Format 调用的完整形式
667
1793
  if (trimmedPart.startsWith(`${trClass}.${trFormatMethod}(`)) {
668
1794
  processedParts.push(part);
669
1795
  continue;
670
1796
  }
671
- // 检查是否是字符串模板
672
1797
  const templateMatch = trimmedPart.match(/(\$@?|@\$)"((?:[^"\\]|\\.)*)"/s);
673
1798
  if (templateMatch) {
674
- // 字符串模板已经在 processStringTemplates 中处理过了,跳过
675
- // 直接添加到 processedParts 中
676
1799
  processedParts.push(part);
677
1800
  }
678
1801
  else if (trimmedPart.startsWith('"')) {
679
- // 处理普通字符串
680
- // 保持原始的空白字符,只在实际内容后添加 .TR()
681
1802
  const whitespaceBefore = part.substring(0, part.search(/\S/));
682
1803
  const actualPart = part.substring(part.search(/\S/));
683
1804
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
684
1805
  processedParts.push(whitespaceBefore + trimmedPart + `.${trMethod}()` + whitespaceAfter);
685
- // 不要在这里添加到 literals,因为 extractPlainStrings 会处理
686
1806
  }
687
1807
  else if (trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
688
- // 处理变量或表达式,添加 .TR()
689
- // 保持原始的空白字符,只在实际内容后添加 .TR()
690
1808
  const whitespaceBefore = part.substring(0, part.search(/\S/));
691
1809
  const actualPart = part.substring(part.search(/\S/));
692
1810
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
693
1811
  processedParts.push(whitespaceBefore + trimmedPart + `.${trMethod}()` + whitespaceAfter);
694
1812
  }
695
1813
  else {
696
- // 其他表达式,保持不变
697
1814
  processedParts.push(part);
698
1815
  }
699
1816
  }
700
- // 构建处理后的语句
701
1817
  let result = leftSide + processedParts.join('');
702
- // 加回分号
703
1818
  if (hasSemicolon) {
704
1819
  result += ';';
705
1820
  }
706
- // 检查结果是否包含 Tr.Format().TR() 形式的重复修饰
707
1821
  while (result.includes(`${trClass}.${trFormatMethod}().${trMethod}()`)) {
708
- // 移除重复的 .TR()
709
1822
  result = result.replace(`${trClass}.${trFormatMethod}().${trMethod}()`, `${trClass}.${trFormatMethod}()`);
710
1823
  }
711
1824
  return result;
712
1825
  }
713
- // 处理非赋值语句的情况
714
1826
  const parts = this.splitExpression(statementWithoutSemicolon);
715
1827
  const processedParts = [];
716
1828
  for (let i = 0; i < parts.length; i++) {
717
1829
  const part = parts[i];
718
- // 跳过分隔符(+ 及其周围的空白字符)
719
1830
  if (i % 2 === 1) {
720
1831
  processedParts.push(part);
721
1832
  continue;
722
1833
  }
723
1834
  const trimmedPart = part.trim();
724
- // 检查是否已经有 .TR() 调用
725
1835
  if (trimmedPart.endsWith(`.${trMethod}()`)) {
726
1836
  processedParts.push(part);
727
1837
  continue;
728
1838
  }
729
- // 检查是否包含 Tr.Format 调用
730
1839
  if (trimmedPart.includes(`${trClass}.${trFormatMethod}(`) || trimmedPart.includes('Tr.Format(')) {
731
1840
  processedParts.push(part);
732
1841
  continue;
733
1842
  }
734
- // 检查是否是字符串模板
735
1843
  const templateMatch = trimmedPart.match(/(\$@?|@\$)"((?:[^"\\]|\\.)*)"/s);
736
1844
  if (templateMatch) {
737
- // 字符串模板已经在 processStringTemplates 中处理过了,跳过
738
- // 直接添加到 processedParts 中
739
1845
  processedParts.push(part);
740
1846
  }
741
1847
  else if (trimmedPart.startsWith('"')) {
742
- // 处理普通字符串
743
- // 保持原始的空白字符,只在实际内容后添加 .TR()
744
1848
  const whitespaceBefore = part.substring(0, part.search(/\S/));
745
1849
  const actualPart = part.substring(part.search(/\S/));
746
1850
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
747
1851
  processedParts.push(whitespaceBefore + trimmedPart + `.${trMethod}()` + whitespaceAfter);
748
- // 不要在这里添加到 literals,因为 extractPlainStrings 会处理
749
1852
  }
750
1853
  else if (trimmedPart && !trimmedPart.match(/^\s*$/) && !trimmedPart.match(/^\d+$/)) {
751
- // 检查是否是 Tr.Format 调用
752
1854
  if (trimmedPart.includes(`${trClass}.${trFormatMethod}(`)) {
753
1855
  processedParts.push(part);
754
1856
  continue;
755
1857
  }
756
- // 处理变量或表达式,添加 .TR()
757
- // 保持原始的空白字符,只在实际内容后添加 .TR()
758
1858
  const whitespaceBefore = part.substring(0, part.search(/\S/));
759
1859
  const actualPart = part.substring(part.search(/\S/));
760
1860
  const whitespaceAfter = actualPart.search(/\s*$/) === 0 ? actualPart : actualPart.substring(actualPart.search(/\S/) + trimmedPart.length);
761
1861
  processedParts.push(whitespaceBefore + trimmedPart + `.${trMethod}()` + whitespaceAfter);
762
1862
  }
763
1863
  else {
764
- // 其他表达式,保持不变
765
1864
  processedParts.push(part);
766
1865
  }
767
1866
  }
768
- // 构建处理后的语句
769
1867
  let result = processedParts.join('');
770
- // 加回分号
771
1868
  if (hasSemicolon) {
772
1869
  result += ';';
773
1870
  }
774
- // 检查结果是否包含 Tr.Format().TR() 形式的重复修饰
775
1871
  while (result.includes(`.${trFormatMethod}().${trMethod}()`)) {
776
- // 移除重复的 .TR()
777
1872
  result = result.replace(`.${trFormatMethod}().${trMethod}()`, `.${trFormatMethod}()`);
778
1873
  }
779
1874
  return result;
780
1875
  }
781
- /**
782
- * 为表达式添加 .TR() 调用
783
- * @param expression 表达式
784
- * @param snippet CodeSnippet对象
785
- * @param trClass 翻译类名
786
- * @param trMethod 翻译方法名
787
- * @param trMethod2 翻译方法名2,用于 .TR() 调用
788
- * @returns 处理后的表达式
789
- */
790
1876
  addTRCalls(expression, snippet, trClass, trMethod, trMethod2) {
791
- // 分割表达式为各个部分
792
1877
  const parts = expression.split('+');
793
1878
  const processedParts = parts.map(part => {
794
1879
  const trimmedPart = part.trim();
795
- // 检查是否已经有 .TR() 调用
796
1880
  if (trimmedPart.endsWith(`.${trMethod2}()`)) {
797
1881
  return part;
798
1882
  }
799
- // 检查是否是 Tr.Format 调用
800
1883
  if (trimmedPart.startsWith(`${trClass}.${trMethod}(`)) {
801
1884
  return part;
802
1885
  }
803
- // 为字符串字面量和表达式添加 .TR()
804
1886
  if (trimmedPart.startsWith('"') || /^\w+/.test(trimmedPart) || trimmedPart.startsWith('(')) {
805
1887
  return part.trim() + `.${trMethod2}()`;
806
1888
  }
@@ -808,50 +1890,27 @@ class CSharpStringExtractor {
808
1890
  });
809
1891
  return processedParts.join(' + ');
810
1892
  }
811
- /**
812
- * 提取普通字符串
813
- * @param statement C#语句
814
- * @param snippet CodeSnippet对象
815
- * @param trClass 翻译类名
816
- * @param trMethod 翻译方法名
817
- */
818
1893
  extractPlainStrings(statement, snippet, trClass, trMethod) {
819
- // 匹配普通字符串,包括包含转义引号的字符串
820
1894
  const stringRegex = /"(?:[^"\\]|\\.)*"/g;
821
1895
  let match;
822
- // 重置正则表达式的lastIndex
823
1896
  stringRegex.lastIndex = 0;
824
1897
  while ((match = stringRegex.exec(statement)) !== null) {
825
1898
  const stringLiteral = match[0];
826
1899
  const position = match.index;
827
- // 检查这个字符串是否在Tr.Format调用中
828
1900
  const beforeMatch = statement.substring(0, position);
829
- // 使用字符串方法检查,避免正则表达式转义问题
830
1901
  const trFormatCall = `${trClass}.${trMethod}(`;
831
1902
  const trFormatIndex = beforeMatch.lastIndexOf(trFormatCall);
832
1903
  const closingParenIndex = beforeMatch.lastIndexOf(')');
833
1904
  const trFormatMatch = trFormatIndex !== -1 && (closingParenIndex === -1 || trFormatIndex > closingParenIndex);
834
- // 检查这个字符串是否在函数调用中
835
1905
  const functionCallMatch = beforeMatch.match(/(\w+\.\w+|\w+)\s*\([^)]*$/);
836
1906
  const inFunctionCall = functionCallMatch !== null;
837
- // 检查函数调用的形式
838
1907
  let shouldExtract = true;
839
- // 总是提取函数调用中的字符串参数
840
- // 无论函数名是否包含点(.)
841
- // 这是为了符合测试用例的期望
842
- // 只有当不在Tr.Format调用中且应该提取时,才添加
843
1908
  if (!trFormatMatch && shouldExtract) {
844
- // 去除引号后添加
845
1909
  const literalWithoutQuotes = stringLiteral.substring(1, stringLiteral.length - 1);
846
1910
  snippet.addLiteral(position, literalWithoutQuotes);
847
1911
  }
848
1912
  }
849
1913
  }
850
- /**
851
- * 分割表达式,避免在括号内分割
852
- * @param expression 表达式字符串
853
- * @returns 分割后的部分数组,格式为 [part1, separator1, part2, separator2, ...]
854
- */
855
1914
  splitExpression(expression) {
856
1915
  const parts = [];
857
1916
  let current = '';
@@ -898,10 +1957,7 @@ class CSharpStringExtractor {
898
1957
  continue;
899
1958
  }
900
1959
  if (char === '+' && inParentheses === 0) {
901
- // 找到一个 + 号且不在括号内,分割
902
- // 保存当前部分(包括前面的空格)
903
1960
  parts.push(current);
904
- // 提取 + 号及其后面的空格
905
1961
  let separator = '+';
906
1962
  let j = i + 1;
907
1963
  while (j < expression.length && expression[j] === ' ') {
@@ -910,7 +1966,7 @@ class CSharpStringExtractor {
910
1966
  }
911
1967
  parts.push(separator);
912
1968
  current = '';
913
- i = j - 1; // 更新 i 的位置
1969
+ i = j - 1;
914
1970
  }
915
1971
  else {
916
1972
  current += char;
@@ -921,5 +1977,346 @@ class CSharpStringExtractor {
921
1977
  }
922
1978
  return parts;
923
1979
  }
1980
+ extractValueExpression(statement, statementIndex, fullCode) {
1981
+ const trimmedStatement = statement.trim();
1982
+ const textAssignmentIndex = trimmedStatement.indexOf('.text =');
1983
+ if (textAssignmentIndex !== -1) {
1984
+ const prefix = trimmedStatement.substring(0, textAssignmentIndex + '.text ='.length);
1985
+ const valuePart = trimmedStatement.substring(textAssignmentIndex + '.text ='.length);
1986
+ const value = this.extractValueUntilSemicolon(valuePart);
1987
+ const valueExpression = value.trim();
1988
+ const valueStartInStatement = statement.indexOf(value);
1989
+ const valueExpressionIndex = statementIndex + (valueStartInStatement !== -1 ? valueStartInStatement : textAssignmentIndex + '.text ='.length);
1990
+ const actualValueStart = fullCode.indexOf(valueExpression, valueExpressionIndex);
1991
+ const finalIndex = actualValueStart !== -1 ? actualValueStart : valueExpressionIndex;
1992
+ return {
1993
+ valueExpression: valueExpression,
1994
+ valueExpressionIndex: finalIndex
1995
+ };
1996
+ }
1997
+ if (trimmedStatement.startsWith('return ')) {
1998
+ const valuePart = trimmedStatement.substring('return '.length);
1999
+ const value = this.extractValueUntilSemicolon(valuePart);
2000
+ const valueExpression = value.trim();
2001
+ let finalIndex = statementIndex;
2002
+ const returnKeywordInStatement = statement.indexOf('return ');
2003
+ if (returnKeywordInStatement !== -1) {
2004
+ const afterReturnInStatement = returnKeywordInStatement + 'return '.length;
2005
+ let startInStatement = -1;
2006
+ for (let i = afterReturnInStatement; i < statement.length - 1; i++) {
2007
+ if (statement[i] === '$' && statement[i + 1] === '"') {
2008
+ startInStatement = i;
2009
+ break;
2010
+ }
2011
+ }
2012
+ if (startInStatement === -1) {
2013
+ for (let i = afterReturnInStatement; i < statement.length; i++) {
2014
+ if (statement[i] === '"') {
2015
+ startInStatement = i;
2016
+ break;
2017
+ }
2018
+ }
2019
+ }
2020
+ if (startInStatement !== -1) {
2021
+ finalIndex = statementIndex + startInStatement;
2022
+ }
2023
+ }
2024
+ return {
2025
+ valueExpression: valueExpression,
2026
+ valueExpressionIndex: finalIndex
2027
+ };
2028
+ }
2029
+ const assignmentIndex = trimmedStatement.indexOf('=');
2030
+ if (assignmentIndex !== -1) {
2031
+ const prefix = trimmedStatement.substring(0, assignmentIndex + 1);
2032
+ const valuePart = trimmedStatement.substring(assignmentIndex + 1);
2033
+ const value = this.extractValueUntilSemicolon(valuePart);
2034
+ const valueExpression = value.trim();
2035
+ const valueStartInStatement = statement.indexOf(value);
2036
+ const valueExpressionIndex = statementIndex + (valueStartInStatement !== -1 ? valueStartInStatement : assignmentIndex + 1);
2037
+ const actualValueStart = fullCode.indexOf(valueExpression, valueExpressionIndex);
2038
+ const finalIndex = actualValueStart !== -1 ? actualValueStart : valueExpressionIndex;
2039
+ return {
2040
+ valueExpression: valueExpression,
2041
+ valueExpressionIndex: finalIndex
2042
+ };
2043
+ }
2044
+ const stringFormatMatch = trimmedStatement.match(/^(string\.Format\([\s\S]*?\))(;|$)/);
2045
+ if (stringFormatMatch) {
2046
+ const valueExpression = stringFormatMatch[1].trim();
2047
+ const actualValueStart = fullCode.indexOf(valueExpression, statementIndex);
2048
+ const finalIndex = actualValueStart !== -1 ? actualValueStart : statementIndex;
2049
+ return {
2050
+ valueExpression: valueExpression,
2051
+ valueExpressionIndex: finalIndex
2052
+ };
2053
+ }
2054
+ let searchStart = 0;
2055
+ let colonIndex = -1;
2056
+ let inString = false;
2057
+ let escapeNext = false;
2058
+ let stringDelimiter = '';
2059
+ if (trimmedStatement.startsWith('case ') || trimmedStatement.startsWith('default:')) {
2060
+ for (let i = 0; i < trimmedStatement.length; i++) {
2061
+ const char = trimmedStatement[i];
2062
+ if (escapeNext) {
2063
+ escapeNext = false;
2064
+ continue;
2065
+ }
2066
+ if (char === '\\') {
2067
+ escapeNext = true;
2068
+ continue;
2069
+ }
2070
+ if (inString) {
2071
+ if (char === stringDelimiter) {
2072
+ inString = false;
2073
+ stringDelimiter = '';
2074
+ }
2075
+ continue;
2076
+ }
2077
+ if (char === '"' || char === "'") {
2078
+ inString = true;
2079
+ stringDelimiter = char;
2080
+ continue;
2081
+ }
2082
+ if (char === ':') {
2083
+ colonIndex = i;
2084
+ break;
2085
+ }
2086
+ }
2087
+ if (colonIndex !== -1) {
2088
+ searchStart = colonIndex + 1;
2089
+ }
2090
+ }
2091
+ let lastParenOpenIndex = -1;
2092
+ inString = false;
2093
+ escapeNext = false;
2094
+ stringDelimiter = '';
2095
+ for (let i = searchStart; i < trimmedStatement.length; i++) {
2096
+ const char = trimmedStatement[i];
2097
+ if (escapeNext) {
2098
+ escapeNext = false;
2099
+ continue;
2100
+ }
2101
+ if (char === '\\') {
2102
+ escapeNext = true;
2103
+ continue;
2104
+ }
2105
+ if (inString) {
2106
+ if (char === stringDelimiter) {
2107
+ inString = false;
2108
+ stringDelimiter = '';
2109
+ }
2110
+ continue;
2111
+ }
2112
+ if (char === '"' || char === "'") {
2113
+ inString = true;
2114
+ stringDelimiter = char;
2115
+ continue;
2116
+ }
2117
+ if (char === '(') {
2118
+ lastParenOpenIndex = i;
2119
+ }
2120
+ }
2121
+ if (lastParenOpenIndex !== -1) {
2122
+ const parenCloseIndex = this.findMatchingParenthesis(trimmedStatement, lastParenOpenIndex);
2123
+ if (parenCloseIndex !== -1) {
2124
+ const args = trimmedStatement.substring(lastParenOpenIndex + 1, parenCloseIndex);
2125
+ const hasStringLiteral = /"(?:[^"\\]|\\.)*"/.test(args);
2126
+ if (hasStringLiteral) {
2127
+ const valueExpression = args.trim();
2128
+ const parenIndex = statement.indexOf(trimmedStatement.substring(lastParenOpenIndex, lastParenOpenIndex + 1));
2129
+ const valueExpressionIndex = statementIndex + parenIndex + 1;
2130
+ const actualValueStart = fullCode.indexOf(valueExpression, valueExpressionIndex);
2131
+ const finalIndex = actualValueStart !== -1 ? actualValueStart : valueExpressionIndex;
2132
+ return {
2133
+ valueExpression: valueExpression,
2134
+ valueExpressionIndex: finalIndex
2135
+ };
2136
+ }
2137
+ }
2138
+ }
2139
+ return {
2140
+ valueExpression: trimmedStatement,
2141
+ valueExpressionIndex: statementIndex
2142
+ };
2143
+ }
2144
+ extractValueUntilSemicolon(valuePart) {
2145
+ let result = '';
2146
+ let inString = false;
2147
+ let escapeNext = false;
2148
+ let stringDelimiter = '';
2149
+ let parenthesesDepth = 0;
2150
+ let bracketDepth = 0;
2151
+ for (let i = 0; i < valuePart.length; i++) {
2152
+ const char = valuePart[i];
2153
+ if (escapeNext) {
2154
+ result += char;
2155
+ escapeNext = false;
2156
+ continue;
2157
+ }
2158
+ if (char === '\\') {
2159
+ result += char;
2160
+ escapeNext = true;
2161
+ continue;
2162
+ }
2163
+ if (inString) {
2164
+ result += char;
2165
+ if (char === stringDelimiter) {
2166
+ inString = false;
2167
+ stringDelimiter = '';
2168
+ }
2169
+ continue;
2170
+ }
2171
+ if (char === '"' || char === "'") {
2172
+ result += char;
2173
+ inString = true;
2174
+ stringDelimiter = char;
2175
+ continue;
2176
+ }
2177
+ if (char === '(') {
2178
+ result += char;
2179
+ parenthesesDepth++;
2180
+ continue;
2181
+ }
2182
+ if (char === ')') {
2183
+ result += char;
2184
+ parenthesesDepth--;
2185
+ continue;
2186
+ }
2187
+ if (char === '[') {
2188
+ result += char;
2189
+ bracketDepth++;
2190
+ continue;
2191
+ }
2192
+ if (char === ']') {
2193
+ result += char;
2194
+ bracketDepth--;
2195
+ continue;
2196
+ }
2197
+ if (char === ';' && parenthesesDepth === 0 && bracketDepth === 0) {
2198
+ break;
2199
+ }
2200
+ result += char;
2201
+ }
2202
+ return result;
2203
+ }
2204
+ processStatementAndExtractValue(snippet, fullStatement, valueExpression, trClass, trFormatMethod, trMethod) {
2205
+ let processedFullStatement = fullStatement;
2206
+ this.extractTrFormatStrings(processedFullStatement, snippet, trClass, trFormatMethod);
2207
+ processedFullStatement = this.processStringTemplates(processedFullStatement, snippet, trClass, trFormatMethod);
2208
+ processedFullStatement = this.processStringFormat(processedFullStatement, snippet, trClass, trFormatMethod);
2209
+ processedFullStatement = this.processStringConcatenation(processedFullStatement, snippet, trClass, trFormatMethod, trMethod);
2210
+ processedFullStatement = this.processTextAssignments(processedFullStatement, snippet, trClass, trFormatMethod, trMethod);
2211
+ this.extractPlainStrings(processedFullStatement, snippet, trClass, trFormatMethod);
2212
+ const regex = new RegExp(`${trClass}\\.${trFormatMethod}\\(([^)]*)\\)\\.${trMethod}\\(\\)`, 'g');
2213
+ processedFullStatement = processedFullStatement.replace(regex, `${trClass}.${trFormatMethod}($1)`);
2214
+ snippet.finalizeLiterals();
2215
+ let convertedValueExpression = valueExpression;
2216
+ const trimmedFullStatement = fullStatement.trim();
2217
+ const trimmedProcessedStatement = processedFullStatement.trim();
2218
+ const isCaseOrDefault = trimmedFullStatement.startsWith('case ') || trimmedFullStatement.startsWith('default:');
2219
+ if (/^return\s/.test(trimmedFullStatement)) {
2220
+ // 检查 processedFullStatement 是否真的被修改过
2221
+ // 如果没有被修改过,直接使用原始 valueExpression 即可
2222
+ if (fullStatement === processedFullStatement) {
2223
+ convertedValueExpression = valueExpression;
2224
+ }
2225
+ else {
2226
+ // 对于 return 语句,我们不需要复杂地使用 extractValueUntilSemicolon 来重新解析!
2227
+ // 我们已经处理完整个语句了,直接截取 return 后面的所有内容,然后去掉末尾的分号即可!
2228
+ let returnIndex = trimmedProcessedStatement.indexOf('return');
2229
+ let valuePart = trimmedProcessedStatement.substring(returnIndex + 'return'.length);
2230
+ valuePart = valuePart.trim();
2231
+ if (valuePart.endsWith(';')) {
2232
+ valuePart = valuePart.slice(0, -1).trim();
2233
+ }
2234
+ convertedValueExpression = valuePart;
2235
+ }
2236
+ }
2237
+ else {
2238
+ const textAssignmentIndex = trimmedFullStatement.indexOf('.text =');
2239
+ if (textAssignmentIndex !== -1) {
2240
+ if (fullStatement === processedFullStatement) {
2241
+ convertedValueExpression = valueExpression;
2242
+ }
2243
+ else {
2244
+ const prefix = trimmedFullStatement.substring(0, textAssignmentIndex + '.text ='.length);
2245
+ const valuePart = trimmedProcessedStatement.substring(textAssignmentIndex + '.text ='.length);
2246
+ const value = this.extractValueUntilSemicolon(valuePart);
2247
+ convertedValueExpression = value.trim();
2248
+ }
2249
+ }
2250
+ else {
2251
+ const assignmentIndex = trimmedFullStatement.indexOf('=');
2252
+ if (assignmentIndex !== -1) {
2253
+ if (fullStatement === processedFullStatement) {
2254
+ convertedValueExpression = valueExpression;
2255
+ }
2256
+ else {
2257
+ const prefix = trimmedFullStatement.substring(0, assignmentIndex + 1);
2258
+ const valuePart = trimmedProcessedStatement.substring(assignmentIndex + 1);
2259
+ const value = this.extractValueUntilSemicolon(valuePart);
2260
+ convertedValueExpression = value.trim();
2261
+ }
2262
+ }
2263
+ else if (valueExpression.startsWith('string.Format(')) {
2264
+ const processedMatch = trimmedProcessedStatement.match(/^(Tr\.Format[\s\S]*?)(;|$)/);
2265
+ if (processedMatch) {
2266
+ convertedValueExpression = processedMatch[1].trim();
2267
+ }
2268
+ }
2269
+ else if (isCaseOrDefault) {
2270
+ if (fullStatement === processedFullStatement) {
2271
+ convertedValueExpression = valueExpression;
2272
+ }
2273
+ else {
2274
+ const trFormatIndex = trimmedProcessedStatement.indexOf('Tr.Format(');
2275
+ if (trFormatIndex !== -1) {
2276
+ const parenOpenIndex = trFormatIndex + 'Tr.Format('.length - 1;
2277
+ const parenCloseIndex = this.findMatchingParenthesis(trimmedProcessedStatement, parenOpenIndex);
2278
+ if (parenCloseIndex !== -1) {
2279
+ convertedValueExpression = trimmedProcessedStatement.substring(trFormatIndex, parenCloseIndex + 1);
2280
+ }
2281
+ else {
2282
+ convertedValueExpression = valueExpression;
2283
+ }
2284
+ }
2285
+ else {
2286
+ const parenOpenIndex = trimmedFullStatement.indexOf('(');
2287
+ if (parenOpenIndex !== -1) {
2288
+ const parenCloseIndex = this.findMatchingParenthesis(trimmedProcessedStatement, parenOpenIndex);
2289
+ if (parenCloseIndex !== -1) {
2290
+ convertedValueExpression = trimmedProcessedStatement.substring(parenOpenIndex + 1, parenCloseIndex).trim();
2291
+ }
2292
+ else {
2293
+ convertedValueExpression = valueExpression;
2294
+ }
2295
+ }
2296
+ else {
2297
+ convertedValueExpression = valueExpression;
2298
+ }
2299
+ }
2300
+ }
2301
+ }
2302
+ else if (trimmedFullStatement.match(/^[\s\S]*?\(/)) {
2303
+ const parenOpenIndex = trimmedFullStatement.indexOf('(');
2304
+ if (parenOpenIndex !== -1) {
2305
+ const parenCloseIndex = this.findMatchingParenthesis(trimmedProcessedStatement, parenOpenIndex);
2306
+ if (parenCloseIndex !== -1) {
2307
+ convertedValueExpression = trimmedProcessedStatement.substring(parenOpenIndex + 1, parenCloseIndex).trim();
2308
+ }
2309
+ }
2310
+ }
2311
+ else {
2312
+ convertedValueExpression = trimmedProcessedStatement;
2313
+ if (convertedValueExpression.endsWith(';')) {
2314
+ convertedValueExpression = convertedValueExpression.slice(0, -1).trim();
2315
+ }
2316
+ }
2317
+ }
2318
+ }
2319
+ snippet.convertedCode = convertedValueExpression;
2320
+ }
924
2321
  }
925
2322
  exports.CSharpStringExtractor = CSharpStringExtractor;