@chengzhao-dev/dsh-cpp-workflow 0.6.8 → 0.6.9
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 +3 -3
- package/README.md +3 -3
- package/lib/client.js +210 -188
- package/lib/comment-policy.js +15 -13
- package/lib/index.js +18 -14
- package/package.json +1 -1
- package/skills/developing-cpp/references/comments.md +12 -3
- package/skills/developing-cpp/references/google-cpp-style.md +1 -1
- package/skills/developing-cpp/references/types.md +1 -1
- package/skills/developing-cpp/scripts/checking-comments.mjs +17 -1
- package/skills/developing-cpp/steps/03-declaration.md +1 -1
package/README.en.md
CHANGED
|
@@ -19,20 +19,20 @@ 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.
|
|
22
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.9
|
|
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.
|
|
28
|
+
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.9
|
|
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.
|
|
32
32
|
|
|
33
33
|
### Settings
|
|
34
34
|
|
|
35
|
-
The plugin page's "C++ Workflow" card is the only entry point and manages file-header tags only: `@file` is always the file name,
|
|
35
|
+
The plugin page's "C++ Workflow" card is the only entry point and manages file-header tags only: `@file` is always the file name, and every file-header, type, and declaration tag is on by default except the fixed `//` body symbol; empty values are skipped. `@param` is required for every parameter; `@return` may be omitted when the condition is self-evident; `@throws` only when the function throws; `@pre`, `@post`, `@retval`, `@tparam`, `@note`, and `@see` are on by default and can be switched off individually. The preview follows the switches, saving takes effect on the next skill read, and existing sources are never rewritten. Settings navigation no longer duplicates the form.
|
|
36
36
|
|
|
37
37
|
If the page still reports that it is not connected to a configuration form after an upgrade, the window is still running the previous client bundle: restart DeepSeek Harness or select "Reload page" on the plugin page. The page retries the connection while open and switches to the editable form as soon as the host serves it; reinstalling is unnecessary. Discard changes, Restore defaults and Save are separate: discard returns to the last value read from the host, restore fills the draft, and only Save writes it.
|
|
38
38
|
|
package/README.md
CHANGED
|
@@ -19,20 +19,20 @@
|
|
|
19
19
|
npm 安装钉死版本:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.
|
|
22
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.9
|
|
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.
|
|
28
|
+
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.9
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
桌面应用独占 `desktop` profile。命令行 `add` 被拒绝时,在应用设置里添加插件,地址填同一个 npm 版本。Git 标签安装使用仓库内已构建的 `lib/`,不在目标机器上重新构建;包内也不含需要授权执行的安装脚本。
|
|
32
32
|
|
|
33
33
|
### 设置
|
|
34
34
|
|
|
35
|
-
插件页的「C++ 工程流程」是唯一入口:注释符号可选 `///` 或 `/** ... */`,页面按注释符号、文件头、类型、函数声明与只读预览分块。`@file`
|
|
35
|
+
插件页的「C++ 工程流程」是唯一入口:注释符号可选 `///` 或 `/** ... */`,页面按注释符号、文件头、类型、函数声明与只读预览分块。`@file` 固定为文件名;除它和函数体 `//` 外,文件头、类型与函数声明标签的开关出厂全开,空值跳过。函数参数要求 `@param`,返回值要求 `@return`(成立条件自明时可省),会抛才要求 `@throws`;`@pre`、`@post`、`@retval` 与类型块的 `@tparam`、`@note`、`@see` 也出厂开启,需要时可逐项关掉。函数体固定用 `//`。预览随改动即时更新,保存后下一次读取技能即生效,已有源码不会被改写。设置导航不再重复放一份。
|
|
36
36
|
|
|
37
37
|
升级插件后如果这一页暂时只显示出厂值,说明当前窗口还挂着旧的前端产物:页脚会显示写入的 `~/.dsh/profiles/<profile>/cordis.patch.yml`,并提示重载;重启 DeepSeek Harness,或在插件页点“重新载入页面”即可编辑,不需要重装插件。页面会在打开后自动重试连接,拿到表单就切换为可编辑。保存、撤销修改与恢复默认值分三个按钮:撤销只回到最近一次读到的值,恢复默认值只填草稿,点保存才写盘。
|
|
38
38
|
|
package/lib/client.js
CHANGED
|
@@ -19,9 +19,9 @@ window.__ModuleLoader__.load({
|
|
|
19
19
|
const HEADER_TAGS = ['brief', 'author', 'date', 'copyright', 'version'];
|
|
20
20
|
/** 函数声明标签:开关决定是否要求写出。 */
|
|
21
21
|
const CONTRACT_TAGS = ['param', 'returns', 'throws'];
|
|
22
|
-
/**
|
|
22
|
+
/** 函数声明里出厂开启的补充标签。 */
|
|
23
23
|
const FUNCTION_EXTRA_TAGS = ['pre', 'post', 'retval'];
|
|
24
|
-
/**
|
|
24
|
+
/** 类型标签:出厂开启,函数模板共用同一组键。 */
|
|
25
25
|
const TYPE_TAGS = ['tparam', 'note', 'see'];
|
|
26
26
|
/** 全部可选标签,快照与测试按它排序。 */
|
|
27
27
|
const EXTRA_TAGS = ['note', 'see', 'pre', 'post', 'tparam', 'retval'];
|
|
@@ -49,23 +49,25 @@ const TAG_LABEL = {
|
|
|
49
49
|
const SAMPLE_BRIEF = '<一句话说明本文件>';
|
|
50
50
|
/** 预览里的示例文件名:只是占位符,不对应任何工程的真实文件。 */
|
|
51
51
|
const SAMPLE_FILE = '<文件名>';
|
|
52
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* 出厂配置:声明用 `///`,除 @file 与函数体 // 外全部开启;空值仍不写文件头。
|
|
54
|
+
*/
|
|
53
55
|
const DEFAULT_CONFIG = {
|
|
54
56
|
commentStyle: 'slash',
|
|
55
57
|
brief: { enabled: true, value: '' },
|
|
56
|
-
author: { enabled:
|
|
57
|
-
date: { enabled:
|
|
58
|
-
copyright: { enabled:
|
|
59
|
-
version: { enabled:
|
|
58
|
+
author: { enabled: true, value: '' },
|
|
59
|
+
date: { enabled: true, value: '' },
|
|
60
|
+
copyright: { enabled: true, value: '' },
|
|
61
|
+
version: { enabled: true, value: '' },
|
|
60
62
|
param: true,
|
|
61
63
|
returns: true,
|
|
62
64
|
throws: true,
|
|
63
|
-
note:
|
|
64
|
-
see:
|
|
65
|
-
pre:
|
|
66
|
-
post:
|
|
67
|
-
tparam:
|
|
68
|
-
retval:
|
|
65
|
+
note: true,
|
|
66
|
+
see: true,
|
|
67
|
+
pre: true,
|
|
68
|
+
post: true,
|
|
69
|
+
tparam: true,
|
|
70
|
+
retval: true,
|
|
69
71
|
};
|
|
70
72
|
/**
|
|
71
73
|
* 宿主把 volatile 字段交给插件时给的是可读写单元(`.get()`),设置页快照给的是普通值;
|
|
@@ -236,28 +238,36 @@ const STRINGS = {
|
|
|
236
238
|
zh: {
|
|
237
239
|
nav: 'C++ 工程流程',
|
|
238
240
|
heading: '文档注释',
|
|
241
|
+
lede: '文件头、类型与函数声明各自决定默认写入哪些标签;预览随改动即时更新。',
|
|
239
242
|
style: '注释符号',
|
|
243
|
+
styleHint: '声明用哪种注释;函数体始终用 //。',
|
|
240
244
|
bodyRow: '函数体',
|
|
241
|
-
bodyPlaceholder: '
|
|
245
|
+
bodyPlaceholder: '固定 //',
|
|
242
246
|
header: '文件头',
|
|
247
|
+
headerHint: '开关打开后填写内容;空值不会写进文件。',
|
|
243
248
|
type: '类型',
|
|
249
|
+
typeHint: '类型块与函数模板里的补充标签。',
|
|
244
250
|
declaration: '函数声明',
|
|
251
|
+
declarationHint: '函数契约与前置、后置条件。',
|
|
245
252
|
preview: '预览',
|
|
253
|
+
previewHint: '代码块随开关和输入即时更新。',
|
|
246
254
|
filePlaceholder: '跟随文件名',
|
|
247
255
|
briefPlaceholder: '文件摘要:留空则各文件自写',
|
|
248
|
-
authorPlaceholder: '
|
|
249
|
-
datePlaceholder: '
|
|
250
|
-
copyrightPlaceholder: '
|
|
251
|
-
versionPlaceholder: '
|
|
252
|
-
paramPlaceholder: '
|
|
253
|
-
returnPlaceholder: '
|
|
254
|
-
throwsPlaceholder: '
|
|
255
|
-
prePlaceholder: '
|
|
256
|
-
postPlaceholder: '
|
|
257
|
-
retvalPlaceholder: '
|
|
258
|
-
tparamPlaceholder: '
|
|
259
|
-
notePlaceholder: '
|
|
260
|
-
seePlaceholder: '
|
|
256
|
+
authorPlaceholder: '作者署名',
|
|
257
|
+
datePlaceholder: '日期',
|
|
258
|
+
copyrightPlaceholder: '版权',
|
|
259
|
+
versionPlaceholder: '版本',
|
|
260
|
+
paramPlaceholder: '逐项说明参数',
|
|
261
|
+
returnPlaceholder: '说明返回值',
|
|
262
|
+
throwsPlaceholder: '列出可能抛出的异常',
|
|
263
|
+
prePlaceholder: '前置条件',
|
|
264
|
+
postPlaceholder: '后置条件',
|
|
265
|
+
retvalPlaceholder: '按枚举值说明返回值',
|
|
266
|
+
tparamPlaceholder: '说明模板参数',
|
|
267
|
+
notePlaceholder: '补充说明',
|
|
268
|
+
seePlaceholder: '相关接口',
|
|
269
|
+
styleSlashExample: '/// @brief 定义棋盘。',
|
|
270
|
+
styleBlockExample: '/** @brief 定义棋盘。 */',
|
|
261
271
|
save: '保存',
|
|
262
272
|
reset: '撤销修改',
|
|
263
273
|
defaults: '恢复默认值',
|
|
@@ -275,28 +285,36 @@ const STRINGS = {
|
|
|
275
285
|
en: {
|
|
276
286
|
nav: 'C++ Workflow',
|
|
277
287
|
heading: 'Doxygen comments',
|
|
288
|
+
lede: 'The file header, types, and declarations each decide which tags are written by default; the preview updates as you edit.',
|
|
278
289
|
style: 'Comment symbol',
|
|
290
|
+
styleHint: 'Symbol for declarations; function bodies always use //.',
|
|
279
291
|
bodyRow: 'Body',
|
|
280
|
-
bodyPlaceholder: '
|
|
292
|
+
bodyPlaceholder: 'Always //',
|
|
281
293
|
header: 'File header',
|
|
294
|
+
headerHint: 'Turn a tag on, then fill in its value; empty values are not written.',
|
|
282
295
|
type: 'Types',
|
|
296
|
+
typeHint: 'Extra tags for type blocks and function templates.',
|
|
283
297
|
declaration: 'Function declarations',
|
|
298
|
+
declarationHint: 'Function contracts plus pre- and postconditions.',
|
|
284
299
|
preview: 'Preview',
|
|
300
|
+
previewHint: 'The code block updates as you toggle switches and type.',
|
|
285
301
|
filePlaceholder: 'Follows the file name',
|
|
286
302
|
briefPlaceholder: 'File summary: leave empty for each file',
|
|
287
|
-
authorPlaceholder: 'Author
|
|
288
|
-
datePlaceholder: 'Date
|
|
289
|
-
copyrightPlaceholder: 'Copyright
|
|
290
|
-
versionPlaceholder: 'Version
|
|
291
|
-
paramPlaceholder: '
|
|
292
|
-
returnPlaceholder: '
|
|
293
|
-
throwsPlaceholder: '
|
|
294
|
-
prePlaceholder: '
|
|
295
|
-
postPlaceholder: '
|
|
296
|
-
retvalPlaceholder: '
|
|
297
|
-
tparamPlaceholder: '
|
|
298
|
-
notePlaceholder: '
|
|
299
|
-
seePlaceholder: '
|
|
303
|
+
authorPlaceholder: 'Author',
|
|
304
|
+
datePlaceholder: 'Date',
|
|
305
|
+
copyrightPlaceholder: 'Copyright',
|
|
306
|
+
versionPlaceholder: 'Version',
|
|
307
|
+
paramPlaceholder: 'Describe each parameter',
|
|
308
|
+
returnPlaceholder: 'Describe the return value',
|
|
309
|
+
throwsPlaceholder: 'List exceptions that may be thrown',
|
|
310
|
+
prePlaceholder: 'Precondition',
|
|
311
|
+
postPlaceholder: 'Postcondition',
|
|
312
|
+
retvalPlaceholder: 'Describe a named return value',
|
|
313
|
+
tparamPlaceholder: 'Describe template parameters',
|
|
314
|
+
notePlaceholder: 'Additional notes',
|
|
315
|
+
seePlaceholder: 'Related interfaces',
|
|
316
|
+
styleSlashExample: '/// @brief Defines the board.',
|
|
317
|
+
styleBlockExample: '/** @brief Defines the board. */',
|
|
300
318
|
save: 'Save',
|
|
301
319
|
reset: 'Discard changes',
|
|
302
320
|
defaults: 'Restore defaults',
|
|
@@ -326,68 +344,65 @@ const STYLE_ID = 'dsh-cpp-workflow-settings'
|
|
|
326
344
|
|
|
327
345
|
// 外观只用宿主变量与两档中性色;没有变量时回落到浅色主题的一组合法颜色。
|
|
328
346
|
const CSS = `
|
|
329
|
-
.dcw-page{--dcw-fg:var(--dsw-alias-label-primary,#1f2328);--dcw-muted:var(--dsw-alias-label-secondary,#69707d);--dcw-faint:var(--dsw-alias-label-tertiary,#8a919c);--dcw-border:var(--dsw-alias-border-l2,rgba(31,35,40,.14));--dcw-bg1:var(--dsw-alias-bg-layer-1,#fff);--dcw-bg2:var(--dsw-alias-bg-layer-2,rgba(31,35,40,.035));--dcw-accent:var(--dsw-alias-brand-primary,#2f6fed);--dcw-danger:var(--dsw-alias-state-error-primary,#c0392b);color:var(--dcw-fg);max-width:760px;padding:2px 0 12px;font-size:13px;line-height:1.6}
|
|
330
|
-
.dcw-head{margin:0 0
|
|
331
|
-
.dcw-
|
|
332
|
-
.dcw-
|
|
333
|
-
.dcw-lede{margin:0;color:var(--dcw-muted);font-size:12.5px;line-height:1.65;max-width:64ch}
|
|
347
|
+
.dcw-page{--dcw-fg:var(--dsw-alias-label-primary,#1f2328);--dcw-muted:var(--dsw-alias-label-secondary,#69707d);--dcw-faint:var(--dsw-alias-label-tertiary,#8a919c);--dcw-border:var(--dsw-alias-border-l2,rgba(31,35,40,.14));--dcw-bg1:var(--dsw-alias-bg-layer-1,#fff);--dcw-bg2:var(--dsw-alias-bg-layer-2,rgba(31,35,40,.035));--dcw-accent:var(--dsw-alias-brand-primary,#2f6fed);--dcw-danger:var(--dsw-alias-state-error-primary,#c0392b);--dcw-success:var(--dsw-alias-state-success-primary,#1a7f37);color:var(--dcw-fg);max-width:760px;padding:2px 0 12px;font-size:13px;line-height:1.6}
|
|
348
|
+
.dcw-head{margin:0 0 16px}
|
|
349
|
+
.dcw-title{margin:0 0 6px;font-size:17px;font-weight:620;letter-spacing:.005em}
|
|
350
|
+
.dcw-lede{margin:0;color:var(--dcw-muted);font-size:12.5px;line-height:1.65;max-width:66ch}
|
|
334
351
|
.dcw-note{display:flex;gap:8px;align-items:center;margin:12px 0 0;padding:9px 11px;border:1px solid var(--dcw-border);border-radius:8px;background:var(--dcw-bg2);color:var(--dcw-muted);font-size:12px;line-height:1.55}
|
|
335
352
|
.dcw-note[data-tone="warn"]{color:var(--dcw-danger);border-color:color-mix(in srgb,var(--dcw-danger) 35%,transparent)}
|
|
336
353
|
.dcw-note-dot{flex:none;width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.55}
|
|
337
|
-
.dcw-
|
|
338
|
-
.dcw-
|
|
339
|
-
.dcw-
|
|
340
|
-
.dcw-card
|
|
341
|
-
.dcw-
|
|
342
|
-
.dcw-
|
|
343
|
-
.dcw-
|
|
344
|
-
.dcw-
|
|
345
|
-
.dcw-row-
|
|
346
|
-
.dcw-
|
|
347
|
-
.dcw-
|
|
348
|
-
.dcw-
|
|
349
|
-
.dcw-
|
|
350
|
-
.dcw-
|
|
351
|
-
.dcw-
|
|
352
|
-
.dcw-
|
|
353
|
-
.dcw-
|
|
354
|
+
.dcw-note-code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;opacity:.8;margin-left:6px}
|
|
355
|
+
.dcw-note-action{margin-left:auto;flex:none;padding:3px 10px;border:1px solid currentColor;border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:11.5px;cursor:pointer;white-space:nowrap}
|
|
356
|
+
.dcw-note-action:hover{background:var(--dcw-bg1)}
|
|
357
|
+
.dcw-card{margin:14px 0 0;border:1px solid var(--dcw-border);border-radius:12px;background:var(--dcw-bg1);overflow:hidden}
|
|
358
|
+
.dcw-card-head{padding:12px 16px 10px;border-bottom:1px solid var(--dcw-border);background:var(--dcw-bg2)}
|
|
359
|
+
.dcw-card-title{margin:0;font-size:13px;font-weight:620;letter-spacing:.01em}
|
|
360
|
+
.dcw-card-hint{margin:2px 0 0;color:var(--dcw-muted);font-size:11.5px;line-height:1.5}
|
|
361
|
+
.dcw-card-body{padding:0}
|
|
362
|
+
.dcw-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:2px 14px;min-height:48px;padding:10px 16px;border-top:1px solid var(--dcw-border)}
|
|
363
|
+
.dcw-card-body>.dcw-row:first-child{border-top:0}
|
|
364
|
+
.dcw-row-main{display:flex;flex-direction:column;gap:2px;min-width:0}
|
|
365
|
+
.dcw-row-label{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;color:var(--dcw-fg)}
|
|
366
|
+
.dcw-row-hint{color:var(--dcw-muted);font-size:11.5px;line-height:1.5}
|
|
367
|
+
.dcw-row-value .dcw-input{grid-column:1/-1;margin-top:8px}
|
|
368
|
+
.dcw-fixed-value{color:var(--dcw-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;white-space:nowrap}
|
|
369
|
+
.dcw-row-flag,.dcw-row-value{cursor:pointer}
|
|
370
|
+
.dcw-style-options{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:14px 16px}
|
|
371
|
+
.dcw-style-option{display:flex;flex-direction:column;align-items:flex-start;gap:6px;padding:12px 14px;border:1px solid var(--dcw-border);border-radius:10px;background:var(--dcw-bg1);color:var(--dcw-fg);font:inherit;text-align:left;cursor:pointer}
|
|
372
|
+
.dcw-style-option:hover:enabled{border-color:color-mix(in srgb,var(--dcw-fg) 30%,transparent)}
|
|
373
|
+
.dcw-style-option[aria-checked="true"]{border-color:var(--dcw-accent);background:color-mix(in srgb,var(--dcw-accent) 7%,var(--dcw-bg1));box-shadow:inset 0 0 0 1px var(--dcw-accent)}
|
|
374
|
+
.dcw-style-option:focus-visible{outline:2px solid var(--dcw-accent);outline-offset:2px}
|
|
375
|
+
.dcw-style-option:disabled{opacity:.5;cursor:default}
|
|
376
|
+
.dcw-style-symbol{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;font-weight:620}
|
|
377
|
+
.dcw-style-example{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;line-height:1.55;color:var(--dcw-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
|
|
378
|
+
.dcw-switch{position:relative;display:inline-flex;align-items:center;flex:none;width:40px;height:22px;cursor:pointer}
|
|
354
379
|
.dcw-switch input{position:absolute;inset:0;margin:0;opacity:0;cursor:pointer}
|
|
355
|
-
.dcw-track{position:absolute;inset:0;border-radius:999px;background:color-mix(in srgb,var(--dcw-fg)
|
|
356
|
-
.dcw-knob{position:absolute;top:2px;left:2px;width:
|
|
380
|
+
.dcw-track{position:absolute;inset:0;border-radius:999px;background:color-mix(in srgb,var(--dcw-fg) 20%,transparent);transition:background .16s ease}
|
|
381
|
+
.dcw-knob{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:var(--dsw-alias-bg-layer-1,#fff);box-shadow:0 1px 2px rgba(0,0,0,.28);transition:transform .16s ease}
|
|
357
382
|
.dcw-switch input:checked + .dcw-track{background:var(--dcw-accent)}
|
|
358
|
-
.dcw-switch input:checked + .dcw-track .dcw-knob{transform:translateX(
|
|
383
|
+
.dcw-switch input:checked + .dcw-track .dcw-knob{transform:translateX(18px)}
|
|
359
384
|
.dcw-switch input:focus-visible + .dcw-track{outline:2px solid var(--dcw-accent);outline-offset:2px}
|
|
360
385
|
.dcw-switch input:disabled + .dcw-track{opacity:.4}
|
|
361
386
|
.dcw-switch input:disabled{cursor:default}
|
|
362
|
-
.dcw-
|
|
363
|
-
.dcw-input{flex:1 1 220px;min-width:0;height:28px;padding:0 9px;border:1px solid var(--dcw-border);border-radius:6px;background:transparent;color:var(--dcw-fg);font:inherit;font-size:12.5px}
|
|
387
|
+
.dcw-input{width:100%;min-width:0;height:36px;padding:0 11px;border:1px solid var(--dcw-border);border-radius:8px;background:var(--dcw-bg1);color:var(--dcw-fg);font:inherit;font-size:12.5px}
|
|
364
388
|
.dcw-input::placeholder{color:var(--dcw-faint)}
|
|
365
|
-
.dcw-input:focus{outline:none;border-color:var(--dcw-accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--dcw-accent)
|
|
366
|
-
.dcw-input:disabled{
|
|
367
|
-
.dcw-
|
|
368
|
-
.dcw-
|
|
369
|
-
.dcw-
|
|
370
|
-
.dcw-
|
|
371
|
-
.dcw-
|
|
372
|
-
.dcw-
|
|
373
|
-
.dcw-card>summary:hover{background:var(--dcw-bg2)}
|
|
374
|
-
.dcw-chevron{transition:transform .16s ease;color:var(--dcw-muted)}
|
|
375
|
-
.dcw-card[open] .dcw-chevron{transform:rotate(90deg)}
|
|
376
|
-
.dcw-preview{margin:0;padding:11px 14px 13px;border-top:1px solid var(--dcw-border);overflow-x:auto;background:var(--dcw-bg2);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;line-height:1.7;white-space:pre;color:var(--dcw-fg)}
|
|
377
|
-
.dcw-actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:14px;padding:11px 2px 4px;border-top:1px solid var(--dcw-border)}
|
|
378
|
-
.dcw-btn{height:30px;padding:0 13px;border:1px solid var(--dcw-border);border-radius:7px;background:var(--dcw-bg1);color:var(--dcw-fg);font:inherit;font-size:12.5px;cursor:pointer}
|
|
389
|
+
.dcw-input:focus{outline:none;border-color:var(--dcw-accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--dcw-accent) 16%,transparent)}
|
|
390
|
+
.dcw-input:disabled{opacity:.55}
|
|
391
|
+
.dcw-preview{margin:0;border-top:1px solid var(--dcw-border)}
|
|
392
|
+
.dcw-preview-head{display:flex;align-items:center;gap:7px;padding:8px 14px;border-bottom:1px solid var(--dcw-border);background:var(--dcw-bg2);color:var(--dcw-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px}
|
|
393
|
+
.dcw-preview-dot{width:6px;height:6px;border-radius:50%;background:var(--dcw-faint)}
|
|
394
|
+
.dcw-preview-body{margin:0;padding:12px 14px 14px;overflow-x:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;line-height:1.7;white-space:pre;color:var(--dcw-fg)}
|
|
395
|
+
.dcw-actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:16px;padding:12px 0 4px;border-top:1px solid var(--dcw-border)}
|
|
396
|
+
.dcw-btn{height:32px;padding:0 14px;border:1px solid var(--dcw-border);border-radius:8px;background:var(--dcw-bg1);color:var(--dcw-fg);font:inherit;font-size:12.5px;cursor:pointer}
|
|
379
397
|
.dcw-btn:hover:enabled{border-color:color-mix(in srgb,var(--dcw-fg) 32%,transparent)}
|
|
380
398
|
.dcw-btn:focus-visible{outline:2px solid var(--dcw-accent);outline-offset:1px}
|
|
381
399
|
.dcw-btn:disabled{opacity:.45;cursor:default}
|
|
382
400
|
.dcw-btn-primary{background:var(--dcw-accent);border-color:transparent;color:var(--dsw-alias-label-primary-foreground,#fff)}
|
|
383
401
|
.dcw-btn-primary:hover:enabled{filter:brightness(1.06)}
|
|
384
|
-
.dcw-status{flex:1 1
|
|
402
|
+
.dcw-status{flex:1 1 180px;margin:0;min-height:20px;color:var(--dcw-muted);font-size:12px}
|
|
385
403
|
.dcw-status[data-tone="warn"]{color:var(--dcw-danger)}
|
|
386
|
-
.dcw-status[data-tone="ok"]{color:var(--
|
|
387
|
-
.dcw-
|
|
388
|
-
.dcw-note-action{margin-left:auto;flex:none;padding:3px 10px;border:1px solid currentColor;border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:11.5px;cursor:pointer;white-space:nowrap}
|
|
389
|
-
.dcw-note-action:hover{background:var(--dcw-bg1)}
|
|
390
|
-
@media (max-width:640px){.dcw-row,.dcw-row-style{grid-template-columns:1fr 40px}.dcw-row-label{grid-column:1/-1}.dcw-row-style .dcw-segmented{grid-column:1/-1}}
|
|
404
|
+
.dcw-status[data-tone="ok"]{color:var(--dcw-success)}
|
|
405
|
+
@media (max-width:520px){.dcw-style-options{grid-template-columns:1fr}.dcw-row{grid-template-columns:minmax(0,1fr) auto}.dcw-row-value .dcw-input{grid-column:1/-1}}
|
|
391
406
|
`
|
|
392
407
|
|
|
393
408
|
function ensureStyles() {
|
|
@@ -401,7 +416,7 @@ function ensureStyles() {
|
|
|
401
416
|
|
|
402
417
|
function Switch(props) {
|
|
403
418
|
return h(
|
|
404
|
-
'
|
|
419
|
+
'label',
|
|
405
420
|
{ className: 'dcw-switch' },
|
|
406
421
|
h('input', {
|
|
407
422
|
type: 'checkbox',
|
|
@@ -414,131 +429,123 @@ function Switch(props) {
|
|
|
414
429
|
)
|
|
415
430
|
}
|
|
416
431
|
|
|
417
|
-
|
|
418
|
-
return h(
|
|
419
|
-
'svg',
|
|
420
|
-
{ width: 12, height: 12, viewBox: '0 0 16 16', 'aria-hidden': 'true', focusable: 'false' },
|
|
421
|
-
h('path', {
|
|
422
|
-
fill: 'currentColor',
|
|
423
|
-
d: 'M8 1a3 3 0 0 0-3 3v2H4.5A1.5 1.5 0 0 0 3 7.5v5A1.5 1.5 0 0 0 4.5 14h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 11.5 6H11V4a3 3 0 0 0-3-3Zm2 5H6V4a2 2 0 1 1 4 0v2Z',
|
|
424
|
-
}),
|
|
425
|
-
)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
function Chevron() {
|
|
429
|
-
return h(
|
|
430
|
-
'svg',
|
|
431
|
-
{ className: 'dcw-chevron', width: 12, height: 12, viewBox: '0 0 16 16', 'aria-hidden': 'true', focusable: 'false' },
|
|
432
|
-
h('path', { fill: 'currentColor', d: 'M6 3.5 10.5 8 6 12.5 4.9 11.4 8.3 8 4.9 4.6Z' }),
|
|
433
|
-
)
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/** 五个区块统一用默认展开的 details,标题在摘要行。 */
|
|
432
|
+
/** 设置页的分组卡片:标题下一句说明,正文按行排控件。 */
|
|
437
433
|
function Section(props) {
|
|
438
434
|
return h(
|
|
439
|
-
'
|
|
440
|
-
{ className: 'dcw-card'
|
|
441
|
-
h(
|
|
442
|
-
|
|
435
|
+
'section',
|
|
436
|
+
{ className: 'dcw-card' },
|
|
437
|
+
h(
|
|
438
|
+
'div',
|
|
439
|
+
{ className: 'dcw-card-head' },
|
|
440
|
+
h('h4', { className: 'dcw-card-title' }, props.title),
|
|
441
|
+
props.hint ? h('p', { className: 'dcw-card-hint' }, props.hint) : null,
|
|
442
|
+
),
|
|
443
|
+
h('div', { className: 'dcw-card-body' }, props.children),
|
|
443
444
|
)
|
|
444
445
|
}
|
|
445
446
|
|
|
446
|
-
/**
|
|
447
|
+
/** 文件头可填写的一行:标题与开关一行,输入框整行在下;关闭时收起输入框。 */
|
|
447
448
|
function ValueRow(props) {
|
|
448
449
|
const entry = props.entry
|
|
449
450
|
return h(
|
|
450
451
|
'div',
|
|
451
|
-
{
|
|
452
|
-
|
|
452
|
+
{
|
|
453
|
+
className: 'dcw-row dcw-row-value',
|
|
454
|
+
onClick: (event) => {
|
|
455
|
+
if (props.disabled) return
|
|
456
|
+
if (event.target.closest('.dcw-switch, .dcw-input')) return
|
|
457
|
+
props.onChange({ ...entry, enabled: !entry.enabled })
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
h(
|
|
461
|
+
'div',
|
|
462
|
+
{ className: 'dcw-row-main' },
|
|
463
|
+
h('span', { className: 'dcw-row-label' }, TAG_LABEL[props.tag]),
|
|
464
|
+
),
|
|
453
465
|
h(Switch, {
|
|
454
466
|
label: TAG_LABEL[props.tag],
|
|
455
467
|
checked: entry.enabled,
|
|
456
468
|
disabled: props.disabled,
|
|
457
469
|
onChange: (enabled) => props.onChange({ ...entry, enabled }),
|
|
458
470
|
}),
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
471
|
+
entry.enabled
|
|
472
|
+
? h('input', {
|
|
473
|
+
className: 'dcw-input',
|
|
474
|
+
type: 'text',
|
|
475
|
+
value: entry.value,
|
|
476
|
+
placeholder: props.placeholder,
|
|
477
|
+
disabled: props.disabled,
|
|
478
|
+
'aria-label': `${TAG_LABEL[props.tag]} 的值`,
|
|
479
|
+
onChange: (event) => props.onChange({ ...entry, value: event.target.value }),
|
|
480
|
+
})
|
|
481
|
+
: null,
|
|
468
482
|
)
|
|
469
483
|
}
|
|
470
484
|
|
|
471
|
-
/**
|
|
485
|
+
/** 只有开关的标签行;说明放在标题下面,不再放一个不可用的假输入框。 */
|
|
472
486
|
function FlagRow(props) {
|
|
473
487
|
return h(
|
|
474
488
|
'div',
|
|
475
|
-
{
|
|
476
|
-
|
|
489
|
+
{
|
|
490
|
+
className: 'dcw-row dcw-row-flag',
|
|
491
|
+
onClick: (event) => {
|
|
492
|
+
if (props.disabled) return
|
|
493
|
+
if (event.target.closest('.dcw-switch, .dcw-input')) return
|
|
494
|
+
props.onChange(!props.checked)
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
h(
|
|
498
|
+
'div',
|
|
499
|
+
{ className: 'dcw-row-main' },
|
|
500
|
+
h('span', { className: 'dcw-row-label' }, TAG_LABEL[props.tag]),
|
|
501
|
+
props.hint ? h('span', { className: 'dcw-row-hint' }, props.hint) : null,
|
|
502
|
+
),
|
|
477
503
|
h(Switch, {
|
|
478
504
|
label: TAG_LABEL[props.tag],
|
|
479
505
|
checked: props.checked,
|
|
480
506
|
disabled: props.disabled,
|
|
481
507
|
onChange: props.onChange,
|
|
482
508
|
}),
|
|
483
|
-
h('input', {
|
|
484
|
-
className: 'dcw-input',
|
|
485
|
-
type: 'text',
|
|
486
|
-
value: '',
|
|
487
|
-
placeholder: props.placeholder,
|
|
488
|
-
disabled: true,
|
|
489
|
-
readOnly: true,
|
|
490
|
-
tabIndex: -1,
|
|
491
|
-
'aria-label': props.placeholder,
|
|
492
|
-
}),
|
|
493
509
|
)
|
|
494
510
|
}
|
|
495
511
|
|
|
496
|
-
/**
|
|
512
|
+
/** 值由文件或语法固定的行:右侧写成普通文本,不放锁图标。 */
|
|
497
513
|
function LockRow(props) {
|
|
498
514
|
return h(
|
|
499
515
|
'div',
|
|
500
|
-
{ className: 'dcw-row dcw-row-
|
|
501
|
-
h(
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
className: 'dcw-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
placeholder: props.placeholder,
|
|
508
|
-
disabled: true,
|
|
509
|
-
readOnly: true,
|
|
510
|
-
tabIndex: -1,
|
|
511
|
-
'aria-label': props.placeholder,
|
|
512
|
-
}),
|
|
516
|
+
{ className: 'dcw-row dcw-row-fixed' },
|
|
517
|
+
h(
|
|
518
|
+
'div',
|
|
519
|
+
{ className: 'dcw-row-main' },
|
|
520
|
+
h('span', { className: 'dcw-row-label' }, props.label),
|
|
521
|
+
),
|
|
522
|
+
h('span', { className: 'dcw-fixed-value' }, props.value),
|
|
513
523
|
)
|
|
514
524
|
}
|
|
515
525
|
|
|
516
|
-
/**
|
|
526
|
+
/** 两种声明注释符号:两张等宽卡片,上面符号、下面示例。 */
|
|
517
527
|
function StyleRow(props) {
|
|
518
528
|
const options = [
|
|
519
|
-
{ value: 'slash',
|
|
520
|
-
{ value: 'block',
|
|
529
|
+
{ value: 'slash', symbol: '///', example: props.slashExample },
|
|
530
|
+
{ value: 'block', symbol: '/** */', example: props.blockExample },
|
|
521
531
|
]
|
|
522
532
|
return h(
|
|
523
533
|
'div',
|
|
524
|
-
{ className: 'dcw-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
'
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
},
|
|
540
|
-
option.label,
|
|
541
|
-
),
|
|
534
|
+
{ className: 'dcw-style-options', role: 'radiogroup', 'aria-label': props.label },
|
|
535
|
+
options.map((option) =>
|
|
536
|
+
h(
|
|
537
|
+
'button',
|
|
538
|
+
{
|
|
539
|
+
key: option.value,
|
|
540
|
+
type: 'button',
|
|
541
|
+
role: 'radio',
|
|
542
|
+
'aria-checked': props.value === option.value ? 'true' : 'false',
|
|
543
|
+
className: 'dcw-style-option',
|
|
544
|
+
disabled: props.disabled,
|
|
545
|
+
onClick: () => props.onChange(option.value),
|
|
546
|
+
},
|
|
547
|
+
h('span', { className: 'dcw-style-symbol' }, option.symbol),
|
|
548
|
+
h('span', { className: 'dcw-style-example' }, option.example),
|
|
542
549
|
),
|
|
543
550
|
),
|
|
544
551
|
)
|
|
@@ -548,8 +555,13 @@ function Preview(props) {
|
|
|
548
555
|
const s = props.strings
|
|
549
556
|
return h(
|
|
550
557
|
Section,
|
|
551
|
-
{ title: s.preview },
|
|
552
|
-
h(
|
|
558
|
+
{ title: s.preview, hint: s.previewHint },
|
|
559
|
+
h(
|
|
560
|
+
'div',
|
|
561
|
+
{ className: 'dcw-preview' },
|
|
562
|
+
h('div', { className: 'dcw-preview-head' }, h('span', { className: 'dcw-preview-dot' }), SAMPLE_FILE),
|
|
563
|
+
h('pre', { className: 'dcw-preview-body' }, previewText(props.config, SAMPLE_FILE)),
|
|
564
|
+
),
|
|
553
565
|
)
|
|
554
566
|
}
|
|
555
567
|
|
|
@@ -661,9 +673,11 @@ function ConfigPage(props) {
|
|
|
661
673
|
return () => clearTimeout(timer)
|
|
662
674
|
}, [form, attempt])
|
|
663
675
|
|
|
676
|
+
// 脏标记按与已保存值的差集算:把开关关了又开回原值时,页面不该再说“有未保存的改动”。
|
|
664
677
|
const update = (patch) => {
|
|
665
|
-
|
|
666
|
-
|
|
678
|
+
const next = { ...draft, ...patch }
|
|
679
|
+
setDraft(next)
|
|
680
|
+
setDirty(diffOperations(next, base).length > 0)
|
|
667
681
|
setMessage('')
|
|
668
682
|
setTone('')
|
|
669
683
|
}
|
|
@@ -678,8 +692,9 @@ function ConfigPage(props) {
|
|
|
678
692
|
|
|
679
693
|
// 恢复默认值:只填进草稿,点“保存”后才写盘,避免误触直接覆盖已保存的配置。
|
|
680
694
|
const restoreDefaults = () => {
|
|
681
|
-
|
|
682
|
-
|
|
695
|
+
const next = normalizeConfig(DEFAULT_CONFIG)
|
|
696
|
+
setDraft(next)
|
|
697
|
+
setDirty(diffOperations(next, base).length > 0)
|
|
683
698
|
setMessage(s.restored)
|
|
684
699
|
setTone('')
|
|
685
700
|
}
|
|
@@ -720,21 +735,26 @@ function ConfigPage(props) {
|
|
|
720
735
|
const editable = Boolean(form)
|
|
721
736
|
const disabled = !ready
|
|
722
737
|
|
|
723
|
-
/**
|
|
738
|
+
/** 一个标签一行:标题与说明,右侧开关。 */
|
|
724
739
|
const flag = (tag, checked) =>
|
|
725
740
|
h(FlagRow, {
|
|
726
741
|
key: tag,
|
|
727
742
|
tag,
|
|
728
743
|
checked,
|
|
729
744
|
disabled,
|
|
730
|
-
|
|
745
|
+
hint: s[FLAG_PLACEHOLDER[tag]],
|
|
731
746
|
onChange: (value) => update({ [tag]: value }),
|
|
732
747
|
})
|
|
733
748
|
|
|
734
749
|
return h(
|
|
735
750
|
'div',
|
|
736
751
|
{ className: 'dcw-page' },
|
|
737
|
-
h(
|
|
752
|
+
h(
|
|
753
|
+
'header',
|
|
754
|
+
{ className: 'dcw-head' },
|
|
755
|
+
h('h3', { className: 'dcw-title' }, s.heading),
|
|
756
|
+
h('p', { className: 'dcw-lede' }, s.lede),
|
|
757
|
+
),
|
|
738
758
|
missing
|
|
739
759
|
? h(
|
|
740
760
|
'div',
|
|
@@ -759,19 +779,21 @@ function ConfigPage(props) {
|
|
|
759
779
|
: null,
|
|
760
780
|
h(
|
|
761
781
|
Section,
|
|
762
|
-
{ title: s.style },
|
|
782
|
+
{ title: s.style, hint: s.styleHint },
|
|
763
783
|
h(StyleRow, {
|
|
764
784
|
label: s.style,
|
|
765
785
|
value: draft.commentStyle,
|
|
766
786
|
disabled,
|
|
787
|
+
slashExample: s.styleSlashExample,
|
|
788
|
+
blockExample: s.styleBlockExample,
|
|
767
789
|
onChange: (value) => update({ commentStyle: value }),
|
|
768
790
|
}),
|
|
769
|
-
h(LockRow, { label: s.bodyRow,
|
|
791
|
+
h(LockRow, { label: s.bodyRow, value: s.bodyPlaceholder }),
|
|
770
792
|
),
|
|
771
793
|
h(
|
|
772
794
|
Section,
|
|
773
|
-
{ title: s.header },
|
|
774
|
-
h(LockRow, { label: TAG_LABEL.file,
|
|
795
|
+
{ title: s.header, hint: s.headerHint },
|
|
796
|
+
h(LockRow, { label: TAG_LABEL.file, value: s.filePlaceholder }),
|
|
775
797
|
...HEADER_TAGS.map((tag) =>
|
|
776
798
|
h(ValueRow, {
|
|
777
799
|
key: tag,
|
|
@@ -783,10 +805,10 @@ function ConfigPage(props) {
|
|
|
783
805
|
}),
|
|
784
806
|
),
|
|
785
807
|
),
|
|
786
|
-
h(Section, { title: s.type }, ...TYPE_TAGS.map((tag) => flag(tag, draft[tag]))),
|
|
808
|
+
h(Section, { title: s.type, hint: s.typeHint }, ...TYPE_TAGS.map((tag) => flag(tag, draft[tag]))),
|
|
787
809
|
h(
|
|
788
810
|
Section,
|
|
789
|
-
{ title: s.declaration },
|
|
811
|
+
{ title: s.declaration, hint: s.declarationHint },
|
|
790
812
|
...[...CONTRACT_TAGS, ...FUNCTION_EXTRA_TAGS].map((tag) => flag(tag, draft[tag])),
|
|
791
813
|
),
|
|
792
814
|
h(Preview, { config: draft, strings: s }),
|
package/lib/comment-policy.js
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
export const HEADER_TAGS = ['brief', 'author', 'date', 'copyright', 'version'];
|
|
13
13
|
/** 函数声明标签:开关决定是否要求写出。 */
|
|
14
14
|
export const CONTRACT_TAGS = ['param', 'returns', 'throws'];
|
|
15
|
-
/**
|
|
15
|
+
/** 函数声明里出厂开启的补充标签。 */
|
|
16
16
|
export const FUNCTION_EXTRA_TAGS = ['pre', 'post', 'retval'];
|
|
17
|
-
/**
|
|
17
|
+
/** 类型标签:出厂开启,函数模板共用同一组键。 */
|
|
18
18
|
export const TYPE_TAGS = ['tparam', 'note', 'see'];
|
|
19
19
|
/** 全部可选标签,快照与测试按它排序。 */
|
|
20
20
|
export const EXTRA_TAGS = ['note', 'see', 'pre', 'post', 'tparam', 'retval'];
|
|
@@ -42,23 +42,25 @@ export const TAG_LABEL = {
|
|
|
42
42
|
export const SAMPLE_BRIEF = '<一句话说明本文件>';
|
|
43
43
|
/** 预览里的示例文件名:只是占位符,不对应任何工程的真实文件。 */
|
|
44
44
|
export const SAMPLE_FILE = '<文件名>';
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* 出厂配置:声明用 `///`,除 @file 与函数体 // 外全部开启;空值仍不写文件头。
|
|
47
|
+
*/
|
|
46
48
|
export const DEFAULT_CONFIG = {
|
|
47
49
|
commentStyle: 'slash',
|
|
48
50
|
brief: { enabled: true, value: '' },
|
|
49
|
-
author: { enabled:
|
|
50
|
-
date: { enabled:
|
|
51
|
-
copyright: { enabled:
|
|
52
|
-
version: { enabled:
|
|
51
|
+
author: { enabled: true, value: '' },
|
|
52
|
+
date: { enabled: true, value: '' },
|
|
53
|
+
copyright: { enabled: true, value: '' },
|
|
54
|
+
version: { enabled: true, value: '' },
|
|
53
55
|
param: true,
|
|
54
56
|
returns: true,
|
|
55
57
|
throws: true,
|
|
56
|
-
note:
|
|
57
|
-
see:
|
|
58
|
-
pre:
|
|
59
|
-
post:
|
|
60
|
-
tparam:
|
|
61
|
-
retval:
|
|
58
|
+
note: true,
|
|
59
|
+
see: true,
|
|
60
|
+
pre: true,
|
|
61
|
+
post: true,
|
|
62
|
+
tparam: true,
|
|
63
|
+
retval: true,
|
|
62
64
|
};
|
|
63
65
|
/**
|
|
64
66
|
* 宿主把 volatile 字段交给插件时给的是可读写单元(`.get()`),设置页快照给的是普通值;
|
package/lib/index.js
CHANGED
|
@@ -27,10 +27,14 @@ import { normalizeConfig, snapshotText } from './comment-policy.js';
|
|
|
27
27
|
export const name = 'dsh-cpp-workflow';
|
|
28
28
|
export const inject = ['skills'];
|
|
29
29
|
// 设置页保存的标签开关;字段即时生效,改配置不必重装插件。
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
// 每个文件头标签各建一个对象:四个标签共用一个 Schema 实例时,volatile 叶子会撞在同一条路径上,
|
|
31
|
+
// 保存 @author 会被宿主拒绝或写到别的标签。
|
|
32
|
+
function headerTag() {
|
|
33
|
+
return Schema.object({
|
|
34
|
+
enabled: Schema.boolean().default(true).volatile(),
|
|
35
|
+
value: Schema.string().default('').volatile(),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
34
38
|
export const Config = Schema.object({
|
|
35
39
|
// 声明注释符号:`///` 或 Doxygen 块注释;缺省值由 comment-policy 兜底。
|
|
36
40
|
commentStyle: Schema.string().default('slash').volatile(),
|
|
@@ -38,19 +42,19 @@ export const Config = Schema.object({
|
|
|
38
42
|
enabled: Schema.boolean().default(true).volatile(),
|
|
39
43
|
value: Schema.string().default('').volatile(),
|
|
40
44
|
}),
|
|
41
|
-
author:
|
|
42
|
-
date:
|
|
43
|
-
copyright:
|
|
44
|
-
version:
|
|
45
|
+
author: headerTag(),
|
|
46
|
+
date: headerTag(),
|
|
47
|
+
copyright: headerTag(),
|
|
48
|
+
version: headerTag(),
|
|
45
49
|
param: Schema.boolean().default(true).volatile(),
|
|
46
50
|
returns: Schema.boolean().default(true).volatile(),
|
|
47
51
|
throws: Schema.boolean().default(true).volatile(),
|
|
48
|
-
note: Schema.boolean().default(
|
|
49
|
-
see: Schema.boolean().default(
|
|
50
|
-
pre: Schema.boolean().default(
|
|
51
|
-
post: Schema.boolean().default(
|
|
52
|
-
tparam: Schema.boolean().default(
|
|
53
|
-
retval: Schema.boolean().default(
|
|
52
|
+
note: Schema.boolean().default(true).volatile(),
|
|
53
|
+
see: Schema.boolean().default(true).volatile(),
|
|
54
|
+
pre: Schema.boolean().default(true).volatile(),
|
|
55
|
+
post: Schema.boolean().default(true).volatile(),
|
|
56
|
+
tparam: Schema.boolean().default(true).volatile(),
|
|
57
|
+
retval: Schema.boolean().default(true).volatile(),
|
|
54
58
|
});
|
|
55
59
|
const PROVIDER = 'dsh-cpp-workflow';
|
|
56
60
|
const RANK = 550;
|
package/package.json
CHANGED
|
@@ -32,7 +32,7 @@ class Board {
|
|
|
32
32
|
- 不写尚未发生的用法、方案对比与取舍论证、广告句。
|
|
33
33
|
- 纯数据结构不写「只保存…,不拥有…,不含…」这类否定式边界;这些由成员类型和所有权表达。
|
|
34
34
|
- 领域尺寸不写进类型注释:格子数由 `cells_` 的声明表达,棋盘边长由 `kBoardRows`、`kBoardCols` 表达。
|
|
35
|
-
-
|
|
35
|
+
- 枚举与只承载数据的结构体:第一句是名词短语,写这个取值表示什么;行尾 `///<` 的两种句式见「枚举项与 `struct` 短字段:两种句式」。
|
|
36
36
|
|
|
37
37
|
### 构造函数与析构函数:先写标签,再写职责
|
|
38
38
|
|
|
@@ -62,12 +62,21 @@ GameResult result_ = GameResult::kInProgress;
|
|
|
62
62
|
|
|
63
63
|
`board_` 这类名字已经说明存放对象的成员不写「对局棋盘」;`cells_` 不写「存储…」这种把声明读一遍的句子,但上一行要先说它是棋盘格子数组,再补轴向、哨兵和改写次序。`clang-format` 不会把行尾注释搬到上一行,位置由本规则和检查器保证。
|
|
64
64
|
|
|
65
|
+
### 枚举项与 `struct` 短字段:两种句式
|
|
66
|
+
|
|
67
|
+
枚举项与只承载数据的 `struct` 短字段用行尾 `///<`,同一枚举各行的 `///<` 对齐。只写两种句式:
|
|
68
|
+
|
|
69
|
+
- 语义本身已经完整:`///< 空格子。`
|
|
70
|
+
- 语义之外还要补一条能核实的条件或结果:`///< 非法参数:传入空棋盘格子类型。`
|
|
71
|
+
|
|
72
|
+
冒号用全角 `:`;冒号前写类别,冒号后写说明,两侧都不能为空。禁止空冒号(`///< 落子成功:。`)、禁止用逗号把类别和说明连起来(`///< 传入空棋盘格子类型,非法参数。`)、禁止把说明写在类别前面。同一枚举要么每一项都写 `///<`,要么都不写;都写时两种句式可以混用。
|
|
73
|
+
|
|
65
74
|
### 函数:两段式契约
|
|
66
75
|
|
|
67
76
|
有参数或有返回值的函数写两段:第一段写结果、失败方式或检查项;第二段按签名把标签写齐。
|
|
68
77
|
|
|
69
78
|
1. 第一段:普通函数写正常返回什么、失败时返回什么或抛什么;布尔查询与 `operator==` 写「检查…是否…」。
|
|
70
|
-
2. 第二段:每个参数一条 `@param`;有返回值默认写 `@return`,成立条件已由函数名与返回类型说清时可省略;会抛才写 `@throws`;`@pre`、`@post`、`@retval`、`@tparam`、`@note`、`@see`
|
|
79
|
+
2. 第二段:每个参数一条 `@param`;有返回值默认写 `@return`,成立条件已由函数名与返回类型说清时可省略;会抛才写 `@throws`;`@pre`、`@post`、`@retval`、`@tparam`、`@note`、`@see` 出厂开关打开;只在确有需要时写,不需要可在设置里关掉。
|
|
71
80
|
|
|
72
81
|
```cpp
|
|
73
82
|
/// 检查坐标能否落子,不修改棋盘。
|
|
@@ -133,7 +142,7 @@ GameResult result_ = GameResult::kInProgress;
|
|
|
133
142
|
|
|
134
143
|
### 文件头
|
|
135
144
|
|
|
136
|
-
每个手写源文件开头写一段 doxygen 文件头:第一行写 `@file <文件名>`,第二行写 `@brief` 一句话说明这个文件定义或实现什么,用一行空注释隔开后,只写本文件自己的组成部分或边界。`@file` 的参数必须与文件名逐字相同,检查器会核对。`@brief`
|
|
145
|
+
每个手写源文件开头写一段 doxygen 文件头:第一行写 `@file <文件名>`,第二行写 `@brief` 一句话说明这个文件定义或实现什么,用一行空注释隔开后,只写本文件自己的组成部分或边界。`@file` 的参数必须与文件名逐字相同,检查器会核对。`@brief` 始终写出;`@author`、`@date`、`@copyright`、`@version` 的开关出厂打开,只有填了非空值才写出,空值整条省略;作者、年份和变更史按团队约定填写,不填就不出现在源码里。删掉把读者指去另一个头文件、另一个目录或另一层的句子,也删掉「这一层不负责什么」。不写作者、版权年份、变更日志和外部资料路径。
|
|
137
146
|
|
|
138
147
|
```cpp
|
|
139
148
|
/// @file board.h
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
- 混合优先级算术加显式括号,消除 `readability-math-missing-parentheses`。
|
|
78
78
|
- 内建整数默认用 `int`;需要确定宽度时用 `<cstdint>` 的 `std::int32_t`、`std::int64_t` 等(可省略 `std::` 前缀),可能到 2^31 的值用 64 位;`std::size_t` / `std::ptrdiff_t` 只用于大小和指针差。不要用无符号类型表达「这个值不会为负」。
|
|
79
79
|
- 封闭状态码用 `enum class`;底层类型默认 `int`,只有确实需要缩小存储时才写固定底层类型并说明理由。枚举值总是带作用域:`OrderResult::kSuccess`。
|
|
80
|
-
-
|
|
80
|
+
- 枚举的尾随注释要么全写要么全不写;两种句式与禁止项见 `references/comments.md`「枚举项与 `struct` 短字段:两种句式」。
|
|
81
81
|
- 浮点不用 `==` 比较计算结果;按量纲给容差,金额用最小货币单位的整数。
|
|
82
82
|
- 数值宽度、符号、枚举、强类型、`const` 成员和下标访问的完整规则见 `references/types.md`;初始化、RAII、移动和模板见 `references/language.md`。
|
|
83
83
|
|
|
@@ -68,7 +68,7 @@ std::array<TaskState, kItemCount> items_{};
|
|
|
68
68
|
|
|
69
69
|
- 新代码用 `enum class`;unscoped `enum` 只用于兼容 C 接口,不作为默认写法。
|
|
70
70
|
- 封闭状态码默认写 `: std::uint8_t`;普通枚举用默认底层类型。
|
|
71
|
-
-
|
|
71
|
+
- 枚举值的行尾注释要么全写要么全不写;两种句式与禁止项见 `references/comments.md`「枚举项与 `struct` 短字段:两种句式」。
|
|
72
72
|
- 需要“编号”与“名字”两套表示时,写显式映射函数,例如 `TaskStateToName` / `TaskStateFromName`,不要在枚举和整数之间来回隐式转换。
|
|
73
73
|
|
|
74
74
|
## 强类型包装
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// 只读注释审计:按 references/comments.md 的硬规则检查 C/C++ 源文件。
|
|
3
3
|
//
|
|
4
4
|
// 检查项:Doxygen 文件头、注释行宽度、标签、注释与声明的贴合、
|
|
5
|
-
// 类型注释存在性、成员功能清单、设计论证(方案对比)、否定式边界、领域尺寸、谓词首句、@param
|
|
5
|
+
// 类型注释存在性、成员功能清单、设计论证(方案对比)、否定式边界、领域尺寸、谓词首句、@param 完整性、枚举成员注释一致性、枚举行尾句式、数据成员主注释位置。
|
|
6
6
|
// 不重写任何文件。
|
|
7
7
|
//
|
|
8
8
|
// 用法:
|
|
@@ -482,6 +482,22 @@ export function checkText(text, options = {}) {
|
|
|
482
482
|
index = body
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
486
|
+
const marker = lines[index].indexOf('///<')
|
|
487
|
+
if (marker === -1) continue
|
|
488
|
+
const content = lines[index].slice(marker + 4).trim().replace(/。$/, '').trim()
|
|
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', '行尾注释用逗号连接类别与说明:改成「类别:说明」,类别在前')
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
485
501
|
for (let index = 0; index < lines.length; index += 1) {
|
|
486
502
|
const trimmed = lines[index].trim()
|
|
487
503
|
if (trimmed.startsWith('//') || trimmed.startsWith('///') || !/\bnoexcept\b/.test(trimmed)) continue
|
|
@@ -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`
|
|
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
|
|