@chengzhao-dev/dsh-cpp-workflow 0.6.5 → 0.6.6
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 +82 -25
- package/lib/comment-policy.js +16 -2
- package/lib/index.js +3 -3
- package/package.json +4 -2
- package/skills/developing-cpp/references/comments.md +3 -3
- 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.6
|
|
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.6
|
|
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, `@brief` is written by default, and `@author`, `@date`, `@copyright`, `@version` stay off and are written only when they hold a non-empty value; `@param
|
|
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, `@brief` is written by default, and `@author`, `@date`, `@copyright`, `@version` stay off and are written only when they hold a non-empty value; `@param` is required for every parameter; `@return` is written by default but may be omitted when the condition is self-evident; `@throws` only when the function throws. 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
|
## Workspace gate
|
|
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.6
|
|
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.6
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
桌面应用独占 `desktop` profile。命令行 `add` 被拒绝时,在应用设置里添加插件,地址填同一个 npm 版本。Git 标签安装使用仓库内已构建的 `lib/`,不在目标机器上重新构建;包内也不含需要授权执行的安装脚本。
|
|
32
32
|
|
|
33
33
|
### 设置
|
|
34
34
|
|
|
35
|
-
插件页的「C++ 工程流程」是唯一入口,只管文件头标签:`@file` 固定为文件名,`@brief` 默认写出,`@author`、`@date`、`@copyright`、`@version` 默认关闭且只在填了非空值时才写出;有参数就要求 `@param
|
|
35
|
+
插件页的「C++ 工程流程」是唯一入口,只管文件头标签:`@file` 固定为文件名,`@brief` 默认写出,`@author`、`@date`、`@copyright`、`@version` 默认关闭且只在填了非空值时才写出;有参数就要求 `@param`,有返回值默认要求 `@return`(成立条件自明时可省),会抛才要求 `@throws`。预览随开关即时更新,保存后下一次读取技能即生效,已有源码不会被改写。设置导航不再重复放一份。
|
|
36
36
|
|
|
37
37
|
## 工作区门禁
|
|
38
38
|
|
package/lib/client.js
CHANGED
|
@@ -79,11 +79,25 @@ const DEFAULT_CONFIG = {
|
|
|
79
79
|
tparam: false,
|
|
80
80
|
retval: false,
|
|
81
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* 宿主把 volatile 字段交给插件时给的是可读写单元(`.get()`),设置页快照给的是普通值;
|
|
84
|
+
* 两种形状都要按同一个值读取。
|
|
85
|
+
*/
|
|
86
|
+
function unwrap(value) {
|
|
87
|
+
if (value !== null &&
|
|
88
|
+
typeof value === 'object' &&
|
|
89
|
+
typeof value.get === 'function') {
|
|
90
|
+
return value.get();
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
82
94
|
function text(value) {
|
|
83
|
-
|
|
95
|
+
const plain = unwrap(value);
|
|
96
|
+
return typeof plain === 'string' ? plain.trim() : '';
|
|
84
97
|
}
|
|
85
98
|
function flag(value, fallback) {
|
|
86
|
-
|
|
99
|
+
const plain = unwrap(value);
|
|
100
|
+
return typeof plain === 'boolean' ? plain : fallback;
|
|
87
101
|
}
|
|
88
102
|
/**
|
|
89
103
|
* 把宿主给的值补齐成完整配置;缺字段、旧版本或手工改坏的配置都回到出厂值。
|
|
@@ -468,36 +482,69 @@ function Preview(props) {
|
|
|
468
482
|
function diffOperations(draft, base) {
|
|
469
483
|
const operations = []
|
|
470
484
|
for (const key of Object.keys(DEFAULT_CONFIG)) {
|
|
471
|
-
const next =
|
|
472
|
-
const previous =
|
|
473
|
-
if (next !==
|
|
485
|
+
const next = draft[key]
|
|
486
|
+
const previous = base[key]
|
|
487
|
+
if (next !== null && typeof next === 'object') {
|
|
488
|
+
// 文件头字段整体不是 volatile,宿主只接受叶子路径(brief.enabled / brief.value)。
|
|
489
|
+
const before = previous !== null && typeof previous === 'object' ? previous : {}
|
|
490
|
+
for (const field of Object.keys(next)) {
|
|
491
|
+
if (JSON.stringify(next[field]) !== JSON.stringify(before[field])) {
|
|
492
|
+
operations.push({ op: 'set', path: [key, field], value: next[field] })
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
continue
|
|
496
|
+
}
|
|
497
|
+
if (JSON.stringify(next) !== JSON.stringify(previous)) {
|
|
498
|
+
operations.push({ op: 'set', path: [key], value: next })
|
|
499
|
+
}
|
|
474
500
|
}
|
|
475
501
|
return operations
|
|
476
502
|
}
|
|
477
503
|
|
|
504
|
+
function snapshotOf(form) {
|
|
505
|
+
if (!form) return { status: 'missing', writable: false }
|
|
506
|
+
if (typeof form.getSnapshot === 'function') return form.getSnapshot()
|
|
507
|
+
return form.state || { status: 'missing', writable: false }
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// 宿主的 ConfigFormController 是可订阅的外部存储;旧调用方仍可能直接给 form.state。
|
|
511
|
+
function useFormSnapshot(form) {
|
|
512
|
+
const [snapshot, setSnapshot] = React.useState(() => snapshotOf(form))
|
|
513
|
+
React.useEffect(() => {
|
|
514
|
+
if (!form || typeof form.subscribe !== 'function' || typeof form.getSnapshot !== 'function') {
|
|
515
|
+
setSnapshot(snapshotOf(form))
|
|
516
|
+
return undefined
|
|
517
|
+
}
|
|
518
|
+
setSnapshot(form.getSnapshot())
|
|
519
|
+
return form.subscribe(() => setSnapshot(form.getSnapshot()))
|
|
520
|
+
}, [form])
|
|
521
|
+
return snapshot
|
|
522
|
+
}
|
|
523
|
+
|
|
478
524
|
function ConfigPage(props) {
|
|
479
525
|
const s = t()
|
|
480
526
|
ensureStyles()
|
|
481
527
|
const form = props.form
|
|
482
|
-
const
|
|
528
|
+
const state = useFormSnapshot(form)
|
|
529
|
+
const initial = React.useMemo(() => normalizeConfig(state.value), [])
|
|
483
530
|
const [draft, setDraft] = React.useState(initial)
|
|
484
531
|
const [base, setBase] = React.useState(initial)
|
|
485
532
|
const [dirty, setDirty] = React.useState(false)
|
|
486
533
|
const [busy, setBusy] = React.useState(false)
|
|
487
534
|
const [message, setMessage] = React.useState('')
|
|
488
535
|
const [tone, setTone] = React.useState('')
|
|
489
|
-
const revision =
|
|
536
|
+
const revision = state.revision
|
|
490
537
|
// 快照只有 status / writable / revision;旧版本多出来的 mode 字段已不存在,
|
|
491
538
|
// 再按它判断会把所有可写表单误判成只读。
|
|
492
|
-
const ready = Boolean(
|
|
493
|
-
const status =
|
|
539
|
+
const ready = Boolean(state.status === 'ready' && state.writable)
|
|
540
|
+
const status = state.status
|
|
494
541
|
|
|
495
542
|
React.useEffect(() => {
|
|
496
|
-
if (dirty ||
|
|
497
|
-
const next = normalizeConfig(
|
|
543
|
+
if (dirty || state.status !== 'ready') return
|
|
544
|
+
const next = normalizeConfig(state.value)
|
|
498
545
|
setBase(next)
|
|
499
546
|
setDraft(next)
|
|
500
|
-
}, [revision, status, dirty, form])
|
|
547
|
+
}, [revision, status, dirty, form, state.value])
|
|
501
548
|
|
|
502
549
|
const update = (patch) => {
|
|
503
550
|
setDraft((current) => ({ ...current, ...patch }))
|
|
@@ -666,26 +713,36 @@ function ConfigPage(props) {
|
|
|
666
713
|
)
|
|
667
714
|
}
|
|
668
715
|
|
|
669
|
-
|
|
670
|
-
|
|
716
|
+
// 组合包 cordis.patch.yml 插入的 profile 条目 id;设置服务按它定位表单。
|
|
717
|
+
const ENTRY_ID = 'cpp-workflow'
|
|
718
|
+
|
|
719
|
+
function page(ownerProps, form) {
|
|
720
|
+
return h(ConfigPage, { ...(ownerProps || {}), form })
|
|
671
721
|
}
|
|
672
722
|
|
|
673
723
|
function apply(ctx) {
|
|
674
|
-
ctx.
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
724
|
+
const form = ctx.configForms ? ctx.configForms.get(ENTRY_ID) : undefined
|
|
725
|
+
const mount = () =>
|
|
726
|
+
ctx.slots.inject('plugins.bundle.config', () =>
|
|
727
|
+
ctx.slots.register(
|
|
728
|
+
{
|
|
729
|
+
name: 'plugins.bundle.config',
|
|
730
|
+
key: PACKAGE_NAME,
|
|
731
|
+
label: () => t().nav,
|
|
732
|
+
},
|
|
733
|
+
(ownerProps) => (ownerProps && ownerProps.view === 'summary' ? null : page(ownerProps, form)),
|
|
734
|
+
),
|
|
735
|
+
)
|
|
736
|
+
if (ctx.configForms && typeof ctx.configForms.whileServed === 'function') {
|
|
737
|
+
ctx.effect(() => ctx.configForms.whileServed([ENTRY_ID], mount), 'dsh-cpp-workflow: 配置挂点')
|
|
738
|
+
} else {
|
|
739
|
+
mount()
|
|
740
|
+
}
|
|
684
741
|
}
|
|
685
742
|
|
|
686
743
|
module.exports = {
|
|
687
744
|
name: PACKAGE_NAME,
|
|
688
|
-
inject: ['slots'],
|
|
745
|
+
inject: ['slots', 'configForms'],
|
|
689
746
|
apply,
|
|
690
747
|
ConfigPage,
|
|
691
748
|
diffOperations,
|
package/lib/comment-policy.js
CHANGED
|
@@ -72,11 +72,25 @@ export const DEFAULT_CONFIG = {
|
|
|
72
72
|
tparam: false,
|
|
73
73
|
retval: false,
|
|
74
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* 宿主把 volatile 字段交给插件时给的是可读写单元(`.get()`),设置页快照给的是普通值;
|
|
77
|
+
* 两种形状都要按同一个值读取。
|
|
78
|
+
*/
|
|
79
|
+
function unwrap(value) {
|
|
80
|
+
if (value !== null &&
|
|
81
|
+
typeof value === 'object' &&
|
|
82
|
+
typeof value.get === 'function') {
|
|
83
|
+
return value.get();
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
75
87
|
function text(value) {
|
|
76
|
-
|
|
88
|
+
const plain = unwrap(value);
|
|
89
|
+
return typeof plain === 'string' ? plain.trim() : '';
|
|
77
90
|
}
|
|
78
91
|
function flag(value, fallback) {
|
|
79
|
-
|
|
92
|
+
const plain = unwrap(value);
|
|
93
|
+
return typeof plain === 'boolean' ? plain : fallback;
|
|
80
94
|
}
|
|
81
95
|
/**
|
|
82
96
|
* 把宿主给的值补齐成完整配置;缺字段、旧版本或手工改坏的配置都回到出厂值。
|
package/lib/index.js
CHANGED
|
@@ -223,11 +223,11 @@ async function getSkill(candidate, options = {}, commentConfig = undefined) {
|
|
|
223
223
|
};
|
|
224
224
|
}
|
|
225
225
|
export function apply(ctx, config = undefined) {
|
|
226
|
-
//
|
|
227
|
-
|
|
226
|
+
// 配置对象由宿主在 volatile 保存时原地更新,所以每次 get() 都重新读取,
|
|
227
|
+
// 不能在这里缓存成一份快照,否则设置页改了标签开关,技能正文仍是旧值。
|
|
228
228
|
ctx.skills.registerProvider(() => ({
|
|
229
229
|
name: PROVIDER,
|
|
230
230
|
list: listSkills,
|
|
231
|
-
get: (candidate, options) => getSkill(candidate, options,
|
|
231
|
+
get: (candidate, options) => getSkill(candidate, options, normalizeConfig(config)),
|
|
232
232
|
}));
|
|
233
233
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chengzhao-dev/dsh-cpp-workflow",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "写 C++ 源码、配 CMake、用 Clang 构建;头文件用 /// Doxygen 文档注释,函数体用 // 分段说明,文件头标签可在设置里改。 · Write C++ sources, configure CMake, and build with Clang; /// Doxygen on declarations, // section notes in bodies, and configurable file-header tags.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
"client": {
|
|
33
33
|
"platform": "web",
|
|
34
34
|
"inject": [
|
|
35
|
-
"@deepseek-ai/dsh-client-ui-slots"
|
|
35
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
36
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
37
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager"
|
|
36
38
|
]
|
|
37
39
|
},
|
|
38
40
|
"catalog": {
|
|
@@ -67,7 +67,7 @@ GameResult result_ = GameResult::kInProgress;
|
|
|
67
67
|
有参数或有返回值的函数写两段:第一段写结果、失败方式或检查项;第二段按签名把标签写齐。
|
|
68
68
|
|
|
69
69
|
1. 第一段:普通函数写正常返回什么、失败时返回什么或抛什么;布尔查询与 `operator==` 写「检查…是否…」。
|
|
70
|
-
2. 第二段:每个参数一条 `@param
|
|
70
|
+
2. 第二段:每个参数一条 `@param`;有返回值默认写 `@return`,成立条件已由函数名与返回类型说清时可省略;会抛才写 `@throws`。
|
|
71
71
|
|
|
72
72
|
```cpp
|
|
73
73
|
/// 检查坐标能否落子,不修改棋盘。
|
|
@@ -78,9 +78,9 @@ GameResult result_ = GameResult::kInProgress;
|
|
|
78
78
|
[[nodiscard]] PlaceCellResult CheckPlaceable(CellPosition position, CellType cell_type) const;
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
- 布尔查询与 `operator==`:第一句写「检查…是否…」;`@return` 只写「什么情况下为 true
|
|
81
|
+
- 布尔查询与 `operator==`:第一句写「检查…是否…」;`@return` 只写「什么情况下为 true」,再补一句「不检查什么」。`Is`、`Has` 开头的名字与 `bool` 返回类型已经把成立条件说清时可省略 `@return`,不为了形式补一句同义反复。
|
|
82
82
|
- `@param` 每个参数都写,写范围、单位或所有权,不写「索引」这类同义反复;没有参数就不写空的 `@param`。
|
|
83
|
-
- `@return`
|
|
83
|
+
- `@return` 默认写,写返回值怎么读或失败时返回什么;返回类型与函数名已把条件说清时可省略,不要用「见上」或重复第一段。
|
|
84
84
|
- `@throws` 必须与 `references/exceptions.md` 一致;不抛异常的函数不写。
|
|
85
85
|
- 私有函数同样写契约,不能默认调用者知道前置条件;输出参数标 `[out]`,既读又写标 `[in,out]`。
|
|
86
86
|
|
|
@@ -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
|
|
17
|
+
- 声明注释按 `references/comments.md` 的四节写:写作只写声明里看不出的事实——类型注释的名词短语(纯数据与枚举一句,类只在不变量不重复领域尺寸时加第二段)、特殊成员的标签与后置条件(例如 `默认构造函数:得到空棋盘、每个格子都是 kEmpty`)、函数注释的第一句结果或失败方式或检查项与契约。有参数就每个参数一条 `@param`,有返回值默认写 `@return`(成立条件自明时可省),会抛才写 `@throws`;构造、析构和 `void` 无参数函数写两段后置条件,不写空标签。数据成员的主注释放在声明上一行并用 `///`,先说明这个成员是什么,再用全角冒号补名字和类型看不出的轴向、哨兵、所有权与改写时机,行尾不写补充;布局定文件头、位置与行尾;排版定符号、宽度与标签;润色删复述。类型注释不罗列成员功能、不写领域尺寸、不写方案对比与设计论证;全项目的风格理由写进项目文档,不写进注释。
|
|
18
18
|
- `std::format` 用于组合消息;声明里不要承诺 `std::print`(C++23)。
|
|
19
19
|
|
|
20
20
|
## 情况
|