@chengzhao-dev/dsh-cpp-workflow 0.6.11 → 0.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -19,13 +19,13 @@ The exception material follows the C++ Core Guidelines' Error handling section a
19
19
  Pinned npm install:
20
20
 
21
21
  ```bash
22
- dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.11
22
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.12
23
23
  ```
24
24
 
25
25
  The matching Git tag:
26
26
 
27
27
  ```bash
28
- dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.11
28
+ dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.12
29
29
  ```
30
30
 
31
31
  The desktop app owns the `desktop` profile. If the command-line `add` is rejected, add the plugin in the app settings and use the same pinned npm version. A Git tag install uses the prebuilt `lib/` committed in the repository and runs no build on the target machine; the package has no install script that needs approval.
package/README.md CHANGED
@@ -19,13 +19,13 @@
19
19
  npm 安装钉死版本:
20
20
 
21
21
  ```bash
22
- dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.11
22
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.12
23
23
  ```
24
24
 
25
25
  同版本的 Git 标签:
26
26
 
27
27
  ```bash
28
- dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.11
28
+ dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.12
29
29
  ```
30
30
 
31
31
  桌面应用独占 `desktop` profile。命令行 `add` 被拒绝时,在应用设置里添加插件,地址填同一个 npm 版本。Git 标签安装使用仓库内已构建的 `lib/`,不在目标机器上重新构建;包内也不含需要授权执行的安装脚本。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chengzhao-dev/dsh-cpp-workflow",
3
- "version": "0.6.11",
3
+ "version": "0.6.12",
4
4
  "description": "写 C++ 源码、配 CMake、用 Clang 构建;头文件用 /// Doxygen 文档注释,函数体用 // 分段说明,文件头标签可在设置里改。",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -14,10 +14,10 @@
14
14
  **好**:
15
15
 
16
16
  ```cpp
17
- /// @file board.h
18
- /// @brief 定义井字棋棋盘、格子坐标与格子类型。
17
+ /// @file cell.h
18
+ /// @brief 定义格子状态与格子。
19
19
  ///
20
- /// 棋盘保存九个棋盘格子的状态,提供查询、落子与清空棋盘。
20
+ /// 包含格子状态 `CellState` 与格子 `Cell`。
21
21
  ```
22
22
 
23
23
  文件头只介绍这个文件里有什么,不写作者和变更史,也不把读者指去另一个文件。
@@ -56,7 +56,7 @@ class Board {
56
56
  class Board {
57
57
  ```
58
58
 
59
- **好**(类有不变量,第二句才写限制):
59
+ **好**(类有不变量,第二段直接写不变量):
60
60
 
61
61
  ```cpp
62
62
  /// 一局井字棋。
@@ -65,7 +65,7 @@ class Board {
65
65
  class Game {
66
66
  ```
67
67
 
68
- 纯数据结构和枚举只写一句名词短语;类有不变量,且第二句不是重复 `cells_`、`kBoardRows` 这类领域尺寸时,才保留第二段。函数各自的契约下沉到函数注释,设计理由与分层写进书稿。
68
+ 纯数据结构和枚举只写一句名词短语;类有不变量,且第二段不是重复 `cells_`、`kBoardRows` 这类领域尺寸时,才保留第二段。第二段直接写不变量,不加「限制:」这类标签。函数各自的契约下沉到函数注释,设计理由与分层写进书稿。
69
69
 
70
70
  ## 构造函数与析构函数:标签 + 职责
71
71
 
@@ -24,19 +24,24 @@ for (const auto& position : empty_cells) {
24
24
  **好**:
25
25
 
26
26
  ```cpp
27
- // 先找能立刻赢的格子:启发式只在「马上赢」和「马上挡」之间做选择,
28
- // 不在这里做更深的前瞻,深搜留给 MinimaxAi。
29
- for (const auto& position : empty_cells) {
30
- if (game.PreviewWin(position, CellType::kX) == WinPreviewResult::kWouldWin) {
31
- return position;
27
+ Cell::PlaceResult Cell::Place(CellState state) {
28
+ // 传入状态为 kEmpty,不更新格子状态。
29
+ if (state == CellState::kEmpty) {
30
+ return PlaceResult::kEmptyState;
32
31
  }
33
- }
34
32
 
35
- // 没有必胜格,交给下一层策略;这里不猜后续几步。
36
- return std::nullopt;
33
+ // 格子已有状态,不进行覆盖。
34
+ if (state_ != CellState::kEmpty) {
35
+ return PlaceResult::kOccupied;
36
+ }
37
+
38
+ // 格子状态为空,更新为传入的状态。
39
+ state_ = state;
40
+ return PlaceResult::kPlaced;
41
+ }
37
42
  ```
38
43
 
39
- 空行分开的阶段各一句,写这一阶段为什么停在这里。空行与注释的边界:
44
+ 每一支的上一行一句,写这一支的后果;`//` 里的枚举值不包反引号。空行与注释的边界:
40
45
 
41
46
  - 多条独立拒绝:每个 `if` 上一行一句,写拒绝后棋盘怎样。
42
47
  - 成功写入:上一行一句,写为什么到这里才改棋盘。
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## 写作:只写声明里看不出的事实
8
8
 
9
- - **第一句先写结果、限制或检查项**:类型用名词短语写它是什么;普通函数写正常返回什么、失败时返回什么或抛什么;布尔查询与 `operator==` 写「检查…是否…」,不写「返回 true/false」。条件、参数与实现理由放第二句以后。
9
+ - **第一句先写结果、限制或检查项**:类型用名词短语写它是什么;普通函数写正常返回什么、失败时返回什么或抛什么;布尔查询与 `operator==` 写「检查…是否…」。第一句不写「返回 true/false」;`@return` 仍要写明成立时返回 true、不成立时返回 false。条件、参数与实现理由放第二句以后。
10
10
  - 公共注释回答「这是什么」和「调用方必须遵守什么」;实现注释回答「为什么这样写」和「边界在哪里」。设计取舍、方案对比与演进历史写到书稿、README 或提交说明。
11
11
  - 用中文散文写完整句子,句号结尾;标识符、标准库类型、`@param` 这类标签保持原文。
12
12
  - 写之前三问:删掉这句,读者会误解设计或做错事吗?这句只是复述签名或代码吗?这个限制能不能由类型、`const` 或 `[[nodiscard]]` 表达?第一问为否就删,第二问为是就删,第三问为是就改代码。
@@ -17,7 +17,7 @@
17
17
 
18
18
  对象是类、结构体、枚举和类型别名。第一句是名词短语:类写「这个类型是什么」,纯数据结构和枚举写「这个取值表示什么」。
19
19
 
20
- 只有类有不变量,而且不重复领域尺寸时,才加第二段「限制」。纯数据结构和枚举不写第二句。
20
+ 只有类有不变量,且不重复领域尺寸时,才加第二段。第二段直接写不变量,不加「限制:」这类标签,也不写「这一层不负责什么」。纯数据结构和枚举不写第二句。
21
21
 
22
22
  ```cpp
23
23
  /// 井字棋棋盘。
@@ -32,20 +32,18 @@ class Board {
32
32
  - 不写尚未发生的用法、方案对比与取舍论证、广告句。
33
33
  - 纯数据结构不写「只保存…,不拥有…,不含…」这类否定式边界;这些由成员类型和所有权表达。
34
34
  - 领域尺寸不写进类型注释:格子数由 `cells_` 的声明表达,棋盘边长由 `kBoardRows`、`kBoardCols` 表达。
35
- - 枚举与只承载数据的结构体:第一句是名词短语,写这个取值表示什么;行尾 `///<` 的两种句式见「枚举项与 `struct` 短字段:两种句式」。
35
+ - 枚举与只承载数据的结构体:第一句是名词短语,写这个取值表示什么。状态枚举上一行写「格子的状态。」,结果枚举写「表示…结果。」;行尾 `///<` 的两种句式见「枚举项与 `struct` 短字段:两种句式」。
36
36
 
37
37
  ### 构造函数与析构函数:先写标签,再写职责
38
38
 
39
39
  特殊成员用标签开头:`默认构造函数:`、`拷贝构造函数:`、`移动构造函数:`、`析构函数:`;其他构造函数写 `构造函数:`。标签后写构造完成后的后置条件,或析构函数负责什么。`= default` 配合成员初始化器建立的不变量,签名上看不见,必须写成后置条件。只写读者能观察到的事实,不写「创建对象」「初始化成员」这类复述。
40
40
 
41
41
  ```cpp
42
- /// 默认构造函数:得到空棋盘,每个格子都是 kEmpty。
43
- ///
44
- /// 构造后的状态与调用 `Clear()` 相同,不抛异常。
45
- Board() noexcept;
42
+ /// 默认构造函数:每一格都是 `kEmpty`。
43
+ Board() = default;
46
44
  ```
47
45
 
48
- 默认构造、析构和 `void` 且无参数的函数写两段:第一段写后置条件或职责,第二段写调用方还要知道的边界;不写空的 `@param`、`@return`。有参数或有返回值的函数按下一节写标签,构造与析构不写 `@return`。标签说明这是哪一类特殊成员,不重复类名;非默认构造同样写建立的不变量与失败方式。
46
+ 默认构造与析构可以只写一行:标签后直接写后置条件或职责。`= default` 的后置条件写在标签那一行就够,不再另起一段写「不抛异常」,也不为了注释加 `noexcept`。无参数、无返回值的动作只在签名看不出结果时再写一句,不把「不抛异常」当成这句;不写 `@throws` 就表示不抛。不写空的 `@param`、`@return`。有参数或有返回值的函数按下一节写标签,构造与析构不写 `@return`。标签说明这是哪一类特殊成员,不重复类名;非默认构造同样写建立的不变量与失败方式。
49
47
 
50
48
  ### 数据成员:上一行 `///`,先写名词再写约定
51
49
 
@@ -66,10 +64,10 @@ GameResult result_ = GameResult::kInProgress;
66
64
 
67
65
  枚举项与只承载数据的 `struct` 短字段用行尾 `///<`,同一枚举各行的 `///<` 对齐。只写两种句式:
68
66
 
69
- - 语义本身已经完整:`///< 空格子。`
67
+ - 语义本身已经完整,只写一句:`///< 状态为空,尚未设置`
70
68
  - 语义之外还要补一条能核实的条件或结果:`///< 非法参数:传入空棋盘格子类型。`
71
69
 
72
- 冒号用全角 `:`;冒号前写类别,冒号后写说明,两侧都不能为空。禁止空冒号(`///< 落子成功:。`)、禁止用逗号把类别和说明连起来(`///< 传入空棋盘格子类型,非法参数。`)、禁止把说明写在类别前面。同一枚举要么每一项都写 `///<`,要么都不写;都写时两种句式可以混用。
70
+ 两类都不收句号。冒号用全角 `:`;冒号前写类别,冒号后写说明,两侧都不能为空。禁止空冒号(`///< 落子成功:。`)、禁止把说明写在类别前面(`///< 传入空棋盘格子类型,非法参数。`)。无冒号的行尾是一句完整的话,可以有句内逗号;有冒号时不得用逗号代替冒号。同一枚举要么每一项都写 `///<`,要么都不写;都写时两种句式可以混用。
73
71
 
74
72
  ### 函数:两段式契约
75
73
 
@@ -87,15 +85,17 @@ GameResult result_ = GameResult::kInProgress;
87
85
  [[nodiscard]] PlaceCellResult CheckPlaceable(CellPosition position, CellType cell_type) const;
88
86
  ```
89
87
 
90
- - 布尔查询与 `operator==`:第一句写「检查…是否…」;`@return` 只写「什么情况下为 true」,再补一句「不检查什么」。`Is`、`Has` 开头的名字与 `bool` 返回类型已经把成立条件说清时可省略 `@return`,不为了形式补一句同义反复。
88
+ - 布尔查询与 `operator==`:第一句写「检查…是否…」;`@return` 写成「成立时返回 true;不成立时返回 false」,不写「否则」省略后半句。`Is`、`Has` 开头的名字与 `bool` 返回类型已经把成立条件说清时可省略 `@return`,不为了形式补一句同义反复。
91
89
  - `@param` 每个参数都写,写范围、单位或所有权,不写「索引」这类同义反复;没有参数就不写空的 `@param`。
92
90
  - `@return` 默认写,写返回值怎么读或失败时返回什么;返回类型与函数名已把条件说清时可省略,不要用「见上」或重复第一段。
91
+ - 查询函数(`Get` 开头):第一句写「获取…」;`@return` 列出可能取到的值,枚举值在 `///` 里用反引号,例如 `kEmpty`、`kX` 或 `kO`。
92
+ - 更新函数:第一段写动作,第二段写清楚何时更新、什么时候不改;多个结果用 `@retval`,已变更的写在最前,其余按失败次序;标签组之间空一行,`@note` 写判断次序。
93
93
  - `@throws` 必须与 `references/exceptions.md` 一致;不抛异常的函数不写。
94
94
  - 私有函数同样写契约,不能默认调用者知道前置条件;输出参数标 `[out]`,既读又写标 `[in,out]`。
95
95
 
96
96
  ### 实现:阶段边界与为什么
97
97
 
98
- `.cpp` / `.cc` 里,函数体用 `//`,不用 `///`:体内的 `///` 会接到该函数的 Doxygen 详细说明后面,成为误导性文档。重要函数按空行分段,每一段上方写一句 `//`。句子写边界、失败后果或为什么停在这里,不把下一行条件翻译一遍;`IsFull` 这种单表达式函数不配注释。只写删掉会让下一次修改得到错误结果、而代码本身看不出的内容。
98
+ `.cpp` / `.cc` 里,函数体用 `//`,不用 `///`:体内的 `///` 会接到该函数的 Doxygen 详细说明后面,成为误导性文档。重要函数按空行分段,每一段上方写一句 `//`。句子写这一支的后果,可以点明分支处理了哪些枚举值,不把下一行条件逐词翻译一遍;`//` 里的枚举值不包反引号,反引号只留给声明注释;一句赋值或一个表达式不写注释,`IsFull` 这种单表达式函数也不配注释。只写删掉会让下一次修改得到错误结果、而代码本身看不出的内容。
99
99
 
100
100
  什么时候在 `if` 上方加一句:
101
101
 
@@ -121,10 +121,10 @@ GameResult result_ = GameResult::kInProgress;
121
121
 
122
122
  | 层 | 行数 | 第一句 | 写什么 |
123
123
  | --- | --- | --- | --- |
124
- | 文件头 | 3–7 | `@file` 文件名 + `@brief` 一句话 | 本文件由哪几部分组成 |
124
+ | 文件头 | 3–7(实现文件 2) | `@file` 文件名 + `@brief` 一句话 | 本文件由哪几部分组成 |
125
125
  | 类型注释 | 2–14 | 名词短语:这个类型或取值是什么 | 只有类的不变量 |
126
126
  | 特殊成员 | 1–3 | 标签(默认/拷贝/移动构造函数、析构函数) | 后置条件或职责 |
127
- | 函数注释 | 3–10 | 正常返回什么、失败时返回或抛什么、检查什么 | 每个参数、返回值与失败方式 |
127
+ | 函数注释 | 3–10(按标签数放宽) | 正常返回什么、失败时返回或抛什么、检查什么 | 每个参数、返回值与失败方式 |
128
128
  | 无标签函数 | 2 | 两段:后置条件/职责,再补边界 | 仅限构造、析构、`void` 无参数 |
129
129
  | 数据成员 | 上一行 1 句(`/// 名词:约定`) | 名字和类型看不出的范围、初值含义、轴向或哨兵 | 什么时候变 |
130
130
  | 实现注释 | 每个空行阶段 1 句 | 边界、失败后果或为什么停在这里 | `//`,只给重要函数 |
@@ -142,29 +142,29 @@ GameResult result_ = GameResult::kInProgress;
142
142
 
143
143
  ### 文件头
144
144
 
145
- 每个手写源文件开头写一段 doxygen 文件头:第一行写 `@file <文件名>`,第二行写 `@brief` 一句话说明这个文件定义或实现什么,用一行空注释隔开后,只写本文件自己的组成部分或边界。`@file` 的参数必须与文件名逐字相同,检查器会核对。`@brief` 始终写出;`@author`、`@date`、`@copyright`、`@version` 的开关出厂打开,只有填了非空值才写出,空值整条省略;作者、年份和变更史按团队约定填写,不填就不出现在源码里。删掉把读者指去另一个头文件、另一个目录或另一层的句子,也删掉「这一层不负责什么」。不写作者、版权年份、变更日志和外部资料路径。
145
+ 每个手写源文件开头写一段 doxygen 文件头:第一行写 `@file <文件名>`,第二行写 `@brief` 一句话说明这个文件定义或实现什么,用一行空注释隔开后,只写本文件自己的组成部分或边界。`@brief` 在头文件以「定义」起句,在实现文件以「实现」收句。空行后以「包含」起句,逐项写「中文概念 `类型名`」,顺序与声明一致;不写「文件包含」这类标签,也不写行为清单。`@file` 的参数必须与文件名逐字相同,检查器会核对。`@brief` 始终写出;`@author`、`@date`、`@copyright`、`@version` 的开关出厂打开,只有填了非空值才写出,空值整条省略;作者、年份和变更史按团队约定填写,不填就不出现在源码里。删掉把读者指去另一个头文件、另一个目录或另一层的句子,也删掉「这一层不负责什么」。不写作者、版权年份、变更日志和外部资料路径。
146
146
 
147
147
  ```cpp
148
- /// @file board.h
149
- /// @brief 定义井字棋棋盘、格子坐标与格子类型。
148
+ /// @file cell.h
149
+ /// @brief 定义格子状态与格子。
150
150
  ///
151
- /// 棋盘保存九个棋盘格子的状态,提供查询、落子与清空棋盘。
151
+ /// 包含格子状态 `CellState` 与格子 `Cell`。
152
152
  ```
153
153
 
154
154
  选择块注释时,同一段写成:
155
155
 
156
156
  ```cpp
157
157
  /**
158
- * @file board.h
159
- * @brief 定义井字棋棋盘、格子坐标与格子类型。
158
+ * @file cell.h
159
+ * @brief 定义格子状态与格子。
160
160
  *
161
- * 棋盘保存九个棋盘格子的状态,提供查询、落子与清空棋盘。
161
+ * 包含格子状态 `CellState` 与格子 `Cell`。
162
162
  */
163
163
  ```
164
164
 
165
165
  - C++ 源文件用以上格式;CMake 用 `#`,YAML/JSON 用 `#`。
166
166
  - 超过约 200 行的文件用空行分成几节,每节的说明写在一行 `///` 上,不画装饰线。
167
- - 文件头 3–7 行;`@file` 与 `@brief` 各占一行,后面只写本文件的范围与组成,不写实现步骤、作者和变更史。
167
+ - 文件头 3–7 行,实现文件可只有 `@file` 与 `@brief` 两行;`@file` 与 `@brief` 各占一行,后面只写本文件的范围与组成,不写实现步骤、作者和变更史。
168
168
 
169
169
  ## 排版:符号、宽度与标签
170
170
 
@@ -189,3 +189,15 @@ GameResult result_ = GameResult::kInProgress;
189
189
  - 只改注释时仍走第 3、4 步,然后回读:文件头 → 类型(名词短语 + 只有类的不变量)→ 特殊成员(标签 + 后置条件/职责)→ 函数(结果/失败方式/检查项 + 参数)→ 数据成员(只读上一行 `///`:名词 + 全角冒号 + 约定;行尾不应有注释)→ 实现(每个空行阶段的边界或理由)→ 术语与新符号是否真实存在 → 运行 `checking-comments.mjs` 兜底。
190
190
  - 注释写给维护者,正文写给读者;设计理由、方案比较与算法取舍写在正文或实现注释里,公开接口只留契约。
191
191
  - 禁止用注释代替类型、`const` 或 `[[nodiscard]]`。
192
+
193
+ ### 例:格子状态这一对文件的用词
194
+
195
+ `cell.h` 与 `cell.cpp` 用下面这套词,只约束这一对文件,不是全项目禁令:
196
+
197
+ - 说状态与格子状态,不说「取值」。
198
+ - 说尚未设置、状态为空、空白格子,不说「空格子」「尚未放置」「占据」。
199
+ - 说已设置状态、已有状态,不说「已放置」「有棋子」。
200
+ - 说更新,不说「写入」「放置」「落子」。
201
+ - 说未变更、已变更,不说「失败」「成功」。
202
+ - 说「不进行覆盖」「不做修改」「保持不变」;「不进行覆盖」只出现在 `.cpp`。
203
+ - 「设置」只管有没有状态,「更新」只管这次动作,「已有状态」只管拒绝覆盖,三者不互换。
@@ -81,7 +81,7 @@ int main() {
81
81
 
82
82
  ## 消息
83
83
 
84
- `what()` 是给程序员看的诊断文本,句式是「标签: 条件 (值); 限度」,四个槽位不动。标签是标识符,成员写 `Type::Function`,自由函数只写函数名,冒号后一个空格。先写清哪一步失败、失败条件是什么、手上有什么值,再写允许的限度;语言由项目约定,下面的示例用英文。条件不写 Error / Failed,不写空泛的「失败」;有分号时条件必须是带动词的小句,写 `board position is out of range`,不写无动词片段。括号只放导致失败的实际值,写 `name=value`,逗号后一个空格,等号两侧不空格;没有实际值就不写括号。分号后只放限度,且必须自成小句,如 `board is {}x{}`;没有限度就不写分号。
84
+ `what()` 是给程序员看的诊断文本,句式是「标签: 条件 (值); 限度」,四个槽位不动。标签是标识符,成员写 `Type::Function`,自由函数只写函数名,冒号后一个空格;默认参数用 `std::source_location::current()` 在调用点求值时,标签写 `function_name()`,条件、值与限度三槽不变。先写清哪一步失败、失败条件是什么、手上有什么值,再写允许的限度;语言由项目约定,下面的示例用英文。条件不写 Error / Failed,不写空泛的「失败」;有分号时条件必须是带动词的小句,写 `board position is out of range`,不写无动词片段。括号只放导致失败的实际值,写 `name=value`,逗号后一个空格,等号两侧不空格;没有实际值就不写括号。分号后只放限度,且必须自成小句,如 `board is {}x{}`;没有限度就不写分号。
85
85
 
86
86
  下面的英文示例只用 `:`、`()`、`,`、`=`、`;` 五个 ASCII 符号:冒号紧贴标签、后接一个空格;左括号前一个空格、内侧不空;逗号前不空、后一个空格;等号两侧不空;分号写在右括号外、前不空、后一个空格。不用第二个冒号、句号、问号、感叹号、破折号或 `×`;尺寸写 `3x3`,`out of range` 不加连字符。中文消息按中文标点,但槽位顺序和「值只放实际值」不变。
87
87
 
@@ -132,6 +132,7 @@ class TaskBoard {
132
132
  | 无下标 | `set`、`unordered_set`、`multimap` | 无 `operator[]` / `at` | — |
133
133
 
134
134
  - 只在调用点已经证明下标合法时用 `[]`;否则用 `at` 或先做边界检查。坐标类型不拥有容器区间;区间判断属于持有尺寸的类型(如 `Board::IsInRange`)。`std::span` 在 C++20 没有 `at`,`span::at` 到 C++26 才进入标准。
135
+ - 已经证明非负的坐标在 `[]` 前收成 `std::size_t`:写 `static_cast<std::size_t>(index)`,不要先转成 `int`,也不要改用 `at()`。
135
136
  - `map::operator[]` 会插入,不是“读取可能缺失的键”;只读查询用 `find` / `contains` / `at`。
136
137
  - 契约失败的消息用 `std::format`,见 `references/exceptions.md`。
137
138
 
@@ -487,14 +487,8 @@ export function checkText(text, options = {}) {
487
487
  if (marker === -1) continue
488
488
  const content = lines[index].slice(marker + 4).trim().replace(/。$/, '').trim()
489
489
  const colon = content.indexOf(':')
490
- if (colon !== -1) {
491
- if (content.slice(colon + 1).trim() === '') {
492
- add(index + 1, 'enum-trailing-empty', '行尾注释冒号后为空:写「类别:说明」,冒号两侧都不能为空')
493
- }
494
- continue
495
- }
496
- if (/[,,]/.test(content)) {
497
- add(index + 1, 'enum-trailing-comma', '行尾注释用逗号连接类别与说明:改成「类别:说明」,类别在前')
490
+ if (colon !== -1 && content.slice(colon + 1).trim() === '') {
491
+ add(index + 1, 'enum-trailing-empty', '行尾注释冒号后为空:写「类别:说明」,冒号两侧都不能为空')
498
492
  }
499
493
  }
500
494
 
@@ -14,7 +14,7 @@
14
14
  - 头文件只就地定义大约 10 行以内的短函数;更长或需要完整定义的普通函数只在 `.cpp` 定义,模板和 `constexpr` 例外。
15
15
  - 失败是调用方的正常结果时,用结果类型、`bool` + 输出参数或 `std::optional` 表达;函数无法完成自己的任务、或构造函数无法建立 invariant 时,才在声明注释里写清抛出的异常类型;越界用 `std::out_of_range`,前置条件被破坏用 `std::logic_error`,只有调用方必须区分时才新增派生类型;Debug/Release 的抛出条件相同。消息句式见 `references/exceptions.md` 的「消息」节。
16
16
  - 一次操作有多种必须区分的正常结果时,用 `enum class` 返回(如落子:成功、越界、类型非法、已占),不用 `bool` 把原因压掉;成功还需要附带数据时再考虑 `std::expected` 或结果结构,成功无数据时不用。纯查询谓词仍用 `bool`,不要把 `IsEmpty` 改成三态。
17
- - 声明注释按 `references/comments.md` 的四节写:写作只写声明里看不出的事实——类型注释的名词短语(纯数据与枚举一句,类只在不变量不重复领域尺寸时加第二段)、特殊成员的标签与后置条件(例如 `默认构造函数:得到空棋盘、每个格子都是 kEmpty`)、函数注释的第一句结果或失败方式或检查项与契约。有参数就每个参数一条 `@param`,有返回值默认写 `@return`(成立条件自明时可省),会抛才写 `@throws`;构造、析构和 `void` 无参数函数写两段后置条件,不写空标签。数据成员的主注释放在声明上一行并用 `///`,先说明这个成员是什么,再用全角冒号补名字和类型看不出的轴向、哨兵、所有权与改写时机,行尾不写补充;布局定文件头、位置与行尾;排版定符号、宽度与标签;润色删复述。类型注释不罗列成员功能、不写领域尺寸、不写方案对比与设计论证;全项目的风格理由写进项目文档,不写进注释。
17
+ - 声明注释按 `references/comments.md` 的四节写:写作只写声明里看不出的事实——类型注释的名词短语(纯数据与枚举一句,类只在不变量不重复领域尺寸时加第二段)、特殊成员的标签与后置条件(默认构造只写一行,如 `默认构造函数:格子的初始状态为 kEmpty`)、函数注释的第一句结果或失败方式或检查项与契约。有参数就每个参数一条 `@param`,有返回值默认写 `@return`(成立条件自明时可省),会抛才写 `@throws`;标签与句号规则以 `references/comments.md` 为准,不写空标签。数据成员的主注释放在声明上一行并用 `///`,先说明这个成员是什么,再用全角冒号补名字和类型看不出的轴向、哨兵、所有权与改写时机,行尾不写补充;布局定文件头、位置与行尾;排版定符号、宽度与标签;润色删复述。类型注释不罗列成员功能、不写领域尺寸、不写方案对比与设计论证;全项目的风格理由写进项目文档,不写进注释。
18
18
  - `std::format` 用于组合消息;声明里不要承诺 `std::print`(C++23)。
19
19
 
20
20
  ## 情况
@@ -25,7 +25,7 @@
25
25
 
26
26
  **异常声明**:构造函数、`Get` 类查询、解析函数最容易抛;在声明注释里写「当……时抛出 `std::out_of_range`」这样的具体类型,不要只写「可能抛异常」,也不要给每个参数错误都新建类型。类型选择见 `references/exceptions.md`。
27
27
 
28
- **注释**:声明注释用 `///` 写中文散文,只写调用方需要知道的;实现细节留给 `.cpp`。`@param` 每个参数都写,`@return` 每个非 `void` 函数都写,`@throws` 会抛才写;构造、析构与 `void` 无参数函数不写空标签。标识符和标签保持英文。文件头默认写 `@file` 与 `@brief` 两行,`@file` 必须与文件名相同;作者、日期、版权、版本开关出厂打开,填了非空值才写出。标签开关在插件设置页,技能正文末尾附带当前配置的快照。写完运行 `node "<skill-dir>/scripts/checking-comments.mjs" --root <项目根>`;关闭 `@brief` 时加 `--no-brief`,配置里打开了隐私标签并填了值时加 `--allow-tag <标签>`。文件头、标签、参数完整性、成员清单与设计论证由脚本兜底。文件头与 TODO 的格式见 `references/comments.md`。
28
+ **注释**:声明注释用 `///` 写中文散文,只写调用方需要知道的;实现细节留给 `.cpp`。`@param` 每个参数都写,`@return` 每个非 `void` 函数都写,`@throws` 会抛才写;构造、析构与 `void` 无参数函数不写空标签。标识符和标签保持英文。文件头写 `@file`、`@brief` 与一句「包含…」的范围,实现文件可只写前两行;`@file` 必须与文件名相同;作者、日期、版权、版本开关出厂打开,填了非空值才写出。标签开关在插件设置页,技能正文末尾附带当前配置的快照。写完运行 `node "<skill-dir>/scripts/checking-comments.mjs" --root <项目根>`;关闭 `@brief` 时加 `--no-brief`,配置里打开了隐私标签并填了值时加 `--allow-tag <标签>`。文件头、标签、参数完整性、成员清单与设计论证由脚本兜底。文件头与 TODO 的格式见 `references/comments.md`。
29
29
 
30
30
  ## 正反例指针
31
31
 
@@ -9,7 +9,7 @@
9
9
  - 函数保持短小;一个函数只做一件事。超过约 40 行时先找可以命名的中间步骤,不要用注释把长函数切成几段。
10
10
  - 按「准备 → 操作 → 收尾」排列语句,用空行分组;组与组之间的空行表达阶段,不用大段叙述。
11
11
  - `.cpp` 的第一个 include 是自己的配对头,然后直接包含本文件用到的标准库头;不要依赖公共头替本文件转发声明。包含顺序见 `references/google-cpp-style.md`;文件槽位见 `references/file-template.md`。
12
- - `.cpp` / `.cc` 的函数体用 `//`,不用 `///`:体内 `///` 会接到该函数的 Doxygen 详细说明后面。重要函数按空行分阶段,每个阶段上方写一句 `//`,写边界、失败后果或为什么停在这里,不把下一行的条件翻译一遍。`IsFull` 这种单表达式函数不配注释;只写删掉会让下一次修改得到错误结果、而代码本身看不出的内容,变更史留给提交说明。
12
+ - `.cpp` / `.cc` 的函数体用 `//`,不用 `///`:体内 `///` 会接到该函数的 Doxygen 详细说明后面。重要函数按空行分阶段,每个阶段上方写一句 `//`,写这一支的后果,可以点明分支处理了哪些枚举值,不把下一行的条件逐词翻译一遍;`//` 里的枚举值不包反引号。`IsFull` 这种单表达式函数不配注释;只写删掉会让下一次修改得到错误结果、而代码本身看不出的内容,变更史留给提交说明。
13
13
  - 什么时候在 `if` 上方加一句:多条独立拒绝(`CheckPlaceable`)每个 `if` 一句;成功写入前一句;只有次序依赖的一对 `if` 在第一个上方说明;循环上方已有阶段说明时,循环里的 `if` 不再加,单表达式谓词不加。
14
14
  - 错误处理:失败是调用方正常结果时返回结果类型、`bool` 或 `std::optional`;函数无法完成自己的任务时抛异常,不拿异常做正常分支。Debug 与 Release 用同一套返回值与抛出条件,只有诊断详略不同。两种模式都要守的 invariant 用 `Check`(项目保留时),只在开发期做的自检用 `DebugCheck`;没有这两个宏就分别用 `assert` 与 `std::terminate`。捕获时按 `const` 引用,只处理能处理的类型,其余继续抛。消息按 `Type::Function: 条件 (值); 限度` 组织,见 `references/exceptions.md` 的「消息」节。
15
15
  - 声明 `noexcept` 前核对函数体:出现 `at()` 或其他可能抛出的调用就去掉。越界用 `at()` 抛 `std::out_of_range`,这个函数就不能标 `noexcept`。
@@ -17,8 +17,8 @@
17
17
  - 不写 C 风格强制转换;用 `static_cast` / `std::bit_cast` 等具名转换。
18
18
  - 所有权用 `std::unique_ptr`;没有共享所有权的理由时不用 `std::shared_ptr`。
19
19
  - 只改注释时,不改可观察行为;只改实现时,不改公共声明。两者同时需要时,先改声明,再改实现。
20
- - 清空成员数组:标准支持 ranges 时用 `std::ranges::fill(cells_ | std::views::join, CellState::kEmpty)`;不支持时用 `for` 循环逐格赋 `kEmpty`。两种都不写 `cells_ = {}`,那依赖枚举零值恰好是 `kEmpty`。
21
- - 默认构造只做「建立初值」且与 `Clear()` 同义时,构造函数调用 `Clear()`,不另设 `Init()`;类内 `cells_{}` 仍保留,让成员先有确定初值。
20
+ - 清空成员数组:元素类型提供收回空取值的函数时(如 `Cell::Clear`),容器的 `Clear` 逐个调用它;没有这种函数时才用 `std::ranges::fill(cells_ | std::views::join, kEmpty)` 或 `for` 循环逐格赋空值。两种都不写 `cells_ = {}`,也不从外面把枚举赋成空值。
21
+ - 默认构造只做「建立初值」时用成员初值(如 `cells_{}`),不在构造函数里再调一遍 `Clear()`;`Clear()` 只负责把已经写入的元素收回空。
22
22
 
23
23
  ## 情况
24
24
 
@@ -37,6 +37,8 @@
37
37
  ## 正反例指针
38
38
 
39
39
  - 实现注释复述代码、分组混乱:`examples/comments-implementation.md`「实现注释」。
40
+ - 已经证明非负的坐标把下标收成 `std::size_t` 再 `[]`,不要转成 `int`:`references/types.md`「下标与越界」。
41
+ - 越界消息的标签取自调用点:`references/exceptions.md`「消息」的 `std::source_location` 一句。
40
42
  - 为了保住 `noexcept` 再包一层只转发下标的私有函数:`references/exceptions.md`「不抛的边界」。
41
43
  - 异常类型与结果类型混用:`examples/exceptions.md`「什么时候抛」。
42
44
  - `std::format` 与流输出混用得当:`examples/exceptions.md`「异常类型与消息」。