scancscode 1.0.31 → 1.0.34

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