scancscode 1.0.37 → 1.0.38

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.
@@ -0,0 +1,11 @@
1
+ *- [x] originalIndex 字段有定义注释
2
+ - [x] originalContext 字段有定义注释
3
+ - [x] originalCode 字段有定义注释
4
+ - [x] convertedCode 字段有定义注释
5
+ - [x] literals 字段有定义注释
6
+ - [x] unexpects 字段有定义注释
7
+ - [x] isChanged 访问器属性有定义注释
8
+ - [x] 所有 124 个测试用例都通过
9
+ - [x] 注释使用 JSDoc 格式
10
+ - [x] 注释准确描述每个字段的用途
11
+
@@ -0,0 +1,59 @@
1
+ # 给 CodeSnippet 类的公开字段添加定义注释 - Product Requirement Document
2
+
3
+ ## Overview
4
+ - **Summary**: 给 CodeSnippet 类的所有公开字段添加清晰的定义注释
5
+ - **Purpose**: 提高代码的可读性和可维护性,让开发者更容易理解每个字段的用途
6
+ - **Target Users**: 使用 CSharpStringExtractor 和 CodeSnippet 类的开发者
7
+
8
+ ## Goals
9
+ - 给 CodeSnippet 类的所有公开字段添加 JSDoc 格式的定义注释
10
+ - 确保注释清晰、准确地描述每个字段的用途
11
+ - 保持代码风格一致
12
+
13
+ ## Non-Goals (Out of Scope)
14
+ - 不修改字段的功能或实现
15
+ - 不修改私有字段
16
+ - 不添加新的功能
17
+
18
+ ## Background & Context
19
+ CodeSnippet 类是 CSharpStringExtractor 的核心数据结构,用于存储提取的字符串片段信息。目前该类的公开字段缺少详细的定义注释,影响了代码的可读性。
20
+
21
+ ## Functional Requirements
22
+ - **FR-1**: 给 CodeSnippet 类的所有公开字段添加定义注释
23
+ - **FR-2**: 注释使用 JSDoc 格式,便于 IDE 提示和文档生成
24
+
25
+ ## Non-Functional Requirements
26
+ - **NFR-1**: 所有现有测试用例必须通过
27
+ - **NFR-2**: 代码风格保持一致
28
+
29
+ ## Constraints
30
+ - **Technical**: 使用 TypeScript 语言,遵循 JSDoc 注释风格
31
+ - **Business**: 不影响现有功能
32
+
33
+ ## Assumptions
34
+ - 公开字段包括:originalIndex, originalContext, originalCode, convertedCode, literals, unexpects
35
+ - 公开字段还包括:isChanged 访问器属性
36
+ - addLiteral 和 finalizeLiterals 方法已经有适当的实现
37
+
38
+ ## Acceptance Criteria
39
+
40
+ ### AC-1: 所有公开字段都有定义注释
41
+ - **Given**: CodeSnippet 类的代码
42
+ - **When**: 检查每个公开字段
43
+ - **Then**: 每个公开字段都有清晰的 JSDoc 格式注释
44
+ - **Verification**: `human-judgment`
45
+
46
+ ### AC-2: 所有现有测试通过
47
+ - **Given**: 修改后的代码
48
+ - **When**: 运行完整测试套件
49
+ - **Then**: 所有测试用例都应该通过
50
+ - **Verification**: `programmatic`
51
+
52
+ ### AC-3: 注释准确描述字段用途
53
+ - **Given**: 字段的注释
54
+ - **When**: 与实际代码使用情况对比
55
+ - **Then**: 注释准确反映字段的实际用途
56
+ - **Verification**: `human-judgment`
57
+
58
+ ## Open Questions
59
+ - 无
@@ -0,0 +1,14 @@
1
+ # 给 CodeSnippet 类的公开字段添加定义注释 - The Implementation Plan (Decomposed and Prioritized Task List)
2
+
3
+ ## [x] Task 1: 给 CodeSnippet 类的公开字段添加定义注释
4
+ - **Priority**: P0
5
+ - **Depends On**: None
6
+ - **Description**:
7
+ - 给 CodeSnippet 类的所有公开字段添加 JSDoc 格式的定义注释
8
+ - 字段包括:originalIndex, originalContext, originalCode, convertedCode, literals, unexpects
9
+ - 还包括:isChanged 访问器属性
10
+ - **Acceptance Criteria Addressed**: [AC-1, AC-2, AC-3]
11
+ - **Test Requirements**:
12
+ - `programmatic` TR-1.1: 所有 124 个测试用例都通过
13
+ - `human-judgement` TR-1.2: 所有公开字段都有清晰、准确的 JSDoc 注释
14
+ - **Notes**: 在 2-22 行进行修改,根据测试用例和实际使用情况确定每个字段的准确含义
@@ -0,0 +1,4 @@
1
+ - [x] "should handle return string" 测试用例通过
2
+ - [x] 所有 124 个测试用例都通过
3
+ - [x] originalIndex 的值等于 fullCode.indexOf(originalCode)
4
+ - [x] 代码修改遵循 TypeScript 语法和现有风格
@@ -0,0 +1,74 @@
1
+ # 修复 return 语句的 originalIndex 计算 - Product Requirement Document
2
+
3
+ ## Overview
4
+
5
+ ### **Summary**: 修复 CSharpStringExtractor 中处理 return 语句时 originalIndex 的计算错误问题
6
+
7
+ ### **Purpose**: 确保提取的字符串片段的 originalIndex 正确对应代码中实际位置
8
+
9
+ ### **Target Users**: 使用 CSharpStringExtractor 的开发者
10
+
11
+ ### Goals
12
+
13
+ ### 修复 "should handle return string" 测试用例失败的问题
14
+
15
+ ### 确保 originalIndex = code.indexOf(originalCode) 的正确性
16
+
17
+ ### Non-Goals (Out of Scope)
18
+
19
+ * 不修改其他功能的实现逻辑
20
+
21
+ * 不添加新的测试用例
22
+
23
+ ## Background & Context
24
+
25
+ 当前测试用例 "should handle return string" 失败,原因是 originalIndex 的值不正确(期望 130,实际 199)。问题出在 extractValueExpression 方法中处理 return 语句时,没有像处理赋值语句那样在 fullCode 中查找实际的 valueExpression 位置。
26
+
27
+ ## Functional Requirements
28
+
29
+ * **FR-1**: 对于 return 语句,正确计算 valueExpression 的 originalIndex
30
+
31
+ * **FR-2**: originalIndex 应该等于 fullCode.indexOf(originalCode)
32
+
33
+ ## Non-Functional Requirements
34
+
35
+ * **NFR-1**: 所有现有测试用例必须通过
36
+
37
+ ## Constraints
38
+
39
+ * **Technical**: 使用 TypeScript 语言,遵循现有代码风格
40
+
41
+ * **Business**: 不影响现有功能
42
+
43
+ ## Assumptions
44
+
45
+ * 测试用例的预期结果是正确的
46
+
47
+ * 其他语句类型(如赋值语句)的 originalIndex 计算是正确的
48
+
49
+ ## Acceptance Criteria
50
+
51
+ ### AC-1: return 语句的 originalIndex 正确
52
+
53
+ * **Given**: 包含 return 语句的 C# 代码
54
+
55
+ * **When**: extractStrings 方法被调用
56
+
57
+ * **Then**: 返回的 snippet 的 originalIndex 应该等于 code.indexOf(originalCode)
58
+
59
+ * **Verification**: `programmatic`
60
+
61
+ ### AC-2: 所有现有测试通过
62
+
63
+ * **Given**: 修改后的代码
64
+
65
+ * **When**: 运行完整测试套件
66
+
67
+ * **Then**: 所有测试用例都应该通过
68
+
69
+ * **Verification**: `programmatic`
70
+
71
+ ## Open Questions
72
+
73
+ * 无
74
+
@@ -0,0 +1,14 @@
1
+ # 修复 return 语句的 originalIndex 计算 - The Implementation Plan (Decomposed and Prioritized Task List)
2
+
3
+ ## [x] Task 1: 修复 extractValueExpression 方法中 return 语句的 originalIndex 计算
4
+ - **Priority**: P0
5
+ - **Depends On**: None
6
+ - **Description**:
7
+ - 修改 extractValueExpression 方法中处理 return 语句的代码
8
+ - 在计算 finalIndex 时,添加在 fullCode 中查找 valueExpression 实际位置的逻辑
9
+ - 参考赋值语句的处理方式
10
+ - **Acceptance Criteria Addressed**: [AC-1, AC-2]
11
+ - **Test Requirements**:
12
+ - `programmatic` TR-1.1: "should handle return string" 测试用例通过
13
+ - `programmatic` TR-1.2: 所有 123 个测试用例都通过
14
+ - **Notes**: 在 2396-2431 行进行修改,参考赋值语句的处理(2434-2451 行)
@@ -2,11 +2,29 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CSharpStringExtractor = exports.CodeSnippet = void 0;
4
4
  class CodeSnippet {
5
+ /**
6
+ * 代码片段在原始源代码中的起始位置索引
7
+ */
5
8
  originalIndex;
9
+ /**
10
+ * 原始上下文,与 originalCode 相同,保存原始的代码片段内容
11
+ */
6
12
  originalContext;
13
+ /**
14
+ * 从源代码中提取的原始代码片段
15
+ */
7
16
  originalCode;
17
+ /**
18
+ * 经过国际化处理后的代码片段,可能添加了 .TR() 调用或转换为 Tr.Format(...) 形式
19
+ */
8
20
  convertedCode;
21
+ /**
22
+ * 从代码片段中提取出的字符串字面量数组,已进行去重和标准化处理
23
+ */
9
24
  literals;
25
+ /**
26
+ * 意外情况记录数组
27
+ */
10
28
  unexpects;
11
29
  literalPositions;
12
30
  constructor() {
@@ -18,6 +36,10 @@ class CodeSnippet {
18
36
  this.unexpects = [];
19
37
  this.literalPositions = new Map();
20
38
  }
39
+ /**
40
+ * 判断代码片段是否经过修改
41
+ * @returns 如果 convertedCode 与 originalCode 不同则返回 true,否则返回 false
42
+ */
21
43
  get isChanged() {
22
44
  return this.originalCode !== this.convertedCode;
23
45
  }
@@ -2156,29 +2178,10 @@ class CSharpStringExtractor {
2156
2178
  const valuePart = trimmedStatement.substring('return '.length);
2157
2179
  const value = this.extractValueUntilSemicolon(valuePart);
2158
2180
  const valueExpression = value.trim();
2159
- let finalIndex = statementIndex;
2160
- const returnKeywordInStatement = statement.indexOf('return ');
2161
- if (returnKeywordInStatement !== -1) {
2162
- const afterReturnInStatement = returnKeywordInStatement + 'return '.length;
2163
- let startInStatement = -1;
2164
- for (let i = afterReturnInStatement; i < statement.length - 1; i++) {
2165
- if (statement[i] === '$' && statement[i + 1] === '"') {
2166
- startInStatement = i;
2167
- break;
2168
- }
2169
- }
2170
- if (startInStatement === -1) {
2171
- for (let i = afterReturnInStatement; i < statement.length; i++) {
2172
- if (statement[i] === '"') {
2173
- startInStatement = i;
2174
- break;
2175
- }
2176
- }
2177
- }
2178
- if (startInStatement !== -1) {
2179
- finalIndex = statementIndex + startInStatement;
2180
- }
2181
- }
2181
+ const valueStartInStatement = statement.indexOf(value);
2182
+ const valueExpressionIndex = statementIndex + (valueStartInStatement !== -1 ? valueStartInStatement : 'return '.length);
2183
+ const actualValueStart = fullCode.indexOf(valueExpression, valueExpressionIndex);
2184
+ const finalIndex = actualValueStart !== -1 ? actualValueStart : valueExpressionIndex;
2182
2185
  return {
2183
2186
  valueExpression: valueExpression,
2184
2187
  valueExpressionIndex: finalIndex
@@ -13,9 +13,8 @@ function isNullOrEmpty(arr) {
13
13
  class CmdExecutor {
14
14
  static testConvert() {
15
15
  let cwd = "E:/DATA/Projects/ZhiYou/ProjectFClient/GameClient/";
16
- let cscodeFolders =
17
- // [cwd + "Assets/Bundles/FGUI/"]
18
- [
16
+ let cscodeFolders = [
17
+ cwd + "Assets/Bundles/FGUI/",
19
18
  "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Bundles/UI/",
20
19
  "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Bundles/Battle/",
21
20
  "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Scripts/",
@@ -2252,7 +2252,7 @@ namespace FaBao.UI.MainUI
2252
2252
  }
2253
2253
  });
2254
2254
  // 测试处理 `xxx.text = yyy` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
2255
- test('should handle .text = format 1', () => {
2255
+ test('should handle .text = format 2', () => {
2256
2256
  const code = `m_text_info.text = info1.member1 + info2.member2;`;
2257
2257
  const snippets = extractor.extractStrings(code);
2258
2258
  {
@@ -2438,4 +2438,36 @@ namespace FaBao.UI.MainUI
2438
2438
  const snippets = extractor.extractStrings(code);
2439
2439
  expect(snippets.length).toBe(0);
2440
2440
  });
2441
+ // 需要正确获取 `originalIndex`, `originalIndex` 的定义为 `originalIndex=code.indexOf(originalCode)`, 其中 `code` 表示原始代码, `originalCode` 表示匹配出的原始代码中的字符串表达式
2442
+ test('should handle calculate originalIndex', () => {
2443
+ const code = `
2444
+ namespace FaBao
2445
+ {
2446
+ public partial class UserInfoModel
2447
+ {
2448
+ public bool HasJoinedGuild()
2449
+ {
2450
+ return !string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0";
2451
+ }
2452
+
2453
+ public void UpdateBargain(BargainShopData bargainShopData)
2454
+ {
2455
+ MyGuildInfo.IsBargainShopBargain = true;
2456
+ }
2457
+
2458
+ }
2459
+ }
2460
+ `;
2461
+ const snippets = extractor.extractStrings(code);
2462
+ {
2463
+ let targetSnippet = snippets[0];
2464
+ expect(targetSnippet.originalCode).toBe(`!string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0"`);
2465
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`!string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0"`));
2466
+ expect(targetSnippet.convertedCode).toBe(`!string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0"`);
2467
+ expect(targetSnippet.literals).toEqual([
2468
+ '0'
2469
+ ]);
2470
+ expect(targetSnippet.isChanged).toBe(false);
2471
+ }
2472
+ });
2441
2473
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scancscode",
3
- "version": "1.0.37",
3
+ "version": "1.0.38",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {
@@ -1,9 +1,27 @@
1
1
  export class CodeSnippet {
2
+ /**
3
+ * 代码片段在原始源代码中的起始位置索引
4
+ */
2
5
  originalIndex: number;
6
+ /**
7
+ * 原始上下文,与 originalCode 相同,保存原始的代码片段内容
8
+ */
3
9
  originalContext: string;
10
+ /**
11
+ * 从源代码中提取的原始代码片段
12
+ */
4
13
  originalCode: string;
14
+ /**
15
+ * 经过国际化处理后的代码片段,可能添加了 .TR() 调用或转换为 Tr.Format(...) 形式
16
+ */
5
17
  convertedCode: string;
18
+ /**
19
+ * 从代码片段中提取出的字符串字面量数组,已进行去重和标准化处理
20
+ */
6
21
  literals: string[];
22
+ /**
23
+ * 意外情况记录数组
24
+ */
7
25
  unexpects: string[];
8
26
  private literalPositions: Map<number, string>;
9
27
 
@@ -17,6 +35,10 @@ export class CodeSnippet {
17
35
  this.literalPositions = new Map();
18
36
  }
19
37
 
38
+ /**
39
+ * 判断代码片段是否经过修改
40
+ * @returns 如果 convertedCode 与 originalCode 不同则返回 true,否则返回 false
41
+ */
20
42
  get isChanged(): boolean {
21
43
  return this.originalCode !== this.convertedCode;
22
44
  }
@@ -2398,32 +2420,11 @@ private findMatchingParenthesis(str: string, openIndex: number): number {
2398
2420
  const value = this.extractValueUntilSemicolon(valuePart);
2399
2421
  const valueExpression = value.trim();
2400
2422
 
2401
- let finalIndex = statementIndex;
2402
- const returnKeywordInStatement = statement.indexOf('return ');
2403
- if (returnKeywordInStatement !== -1) {
2404
- const afterReturnInStatement = returnKeywordInStatement + 'return '.length;
2405
- let startInStatement = -1;
2406
-
2407
- for (let i = afterReturnInStatement; i < statement.length - 1; i++) {
2408
- if (statement[i] === '$' && statement[i + 1] === '"') {
2409
- startInStatement = i;
2410
- break;
2411
- }
2412
- }
2413
-
2414
- if (startInStatement === -1) {
2415
- for (let i = afterReturnInStatement; i < statement.length; i++) {
2416
- if (statement[i] === '"') {
2417
- startInStatement = i;
2418
- break;
2419
- }
2420
- }
2421
- }
2422
-
2423
- if (startInStatement !== -1) {
2424
- finalIndex = statementIndex + startInStatement;
2425
- }
2426
- }
2423
+ const valueStartInStatement = statement.indexOf(value);
2424
+ const valueExpressionIndex = statementIndex + (valueStartInStatement !== -1 ? valueStartInStatement : 'return '.length);
2425
+
2426
+ const actualValueStart = fullCode.indexOf(valueExpression, valueExpressionIndex);
2427
+ const finalIndex = actualValueStart !== -1 ? actualValueStart : valueExpressionIndex;
2427
2428
 
2428
2429
  return {
2429
2430
  valueExpression: valueExpression,
@@ -10,13 +10,12 @@ export class CmdExecutor {
10
10
  static testConvert() {
11
11
  let cwd = "E:/DATA/Projects/ZhiYou/ProjectFClient/GameClient/"
12
12
  let cscodeFolders =
13
- // [cwd + "Assets/Bundles/FGUI/"]
14
-
15
- [
16
- "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Bundles/UI/",
17
- "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Bundles/Battle/",
18
- "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Scripts/",
19
- ]
13
+ [
14
+ cwd + "Assets/Bundles/FGUI/",
15
+ "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Bundles/UI/",
16
+ "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Bundles/Battle/",
17
+ "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Scripts/",
18
+ ]
20
19
 
21
20
  let gameConfigFolders = [cwd + "Assets/Bundles/GameConfigs/"]
22
21
  let outCsvFile = "E:/DATA/Projects/e-gbl-client/client/Assets/Bundles/GameConfigs/Translation/hello.csv"
@@ -2477,7 +2477,7 @@ namespace FaBao.UI.MainUI
2477
2477
  });
2478
2478
 
2479
2479
  // 测试处理 `xxx.text = yyy` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
2480
- test('should handle .text = format 1', () => {
2480
+ test('should handle .text = format 2', () => {
2481
2481
  const code = `m_text_info.text = info1.member1 + info2.member2;`;
2482
2482
  const snippets = extractor.extractStrings(code);
2483
2483
  {
@@ -2676,4 +2676,37 @@ namespace FaBao.UI.MainUI
2676
2676
  expect(snippets.length).toBe(0);
2677
2677
  });
2678
2678
 
2679
+ // 需要正确获取 `originalIndex`, `originalIndex` 的定义为 `originalIndex=code.indexOf(originalCode)`, 其中 `code` 表示原始代码, `originalCode` 表示匹配出的原始代码中的字符串表达式
2680
+ test('should handle calculate originalIndex', () => {
2681
+ const code = `
2682
+ namespace FaBao
2683
+ {
2684
+ public partial class UserInfoModel
2685
+ {
2686
+ public bool HasJoinedGuild()
2687
+ {
2688
+ return !string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0";
2689
+ }
2690
+
2691
+ public void UpdateBargain(BargainShopData bargainShopData)
2692
+ {
2693
+ MyGuildInfo.IsBargainShopBargain = true;
2694
+ }
2695
+
2696
+ }
2697
+ }
2698
+ `;
2699
+ const snippets = extractor.extractStrings(code);
2700
+ {
2701
+ let targetSnippet = snippets[0];
2702
+ expect(targetSnippet.originalCode).toBe(`!string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0"`);
2703
+ expect(targetSnippet.originalIndex).toBe(code.indexOf(`!string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0"`));
2704
+ expect(targetSnippet.convertedCode).toBe(`!string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0"`);
2705
+ expect(targetSnippet.literals).toEqual([
2706
+ '0'
2707
+ ]);
2708
+ expect(targetSnippet.isChanged).toBe(false);
2709
+ }
2710
+ });
2711
+
2679
2712
  });