dsh-vscode-mode 0.4.6 → 0.5.0

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 (44) hide show
  1. package/README.md +32 -2
  2. package/lib/client.js +8854 -4023
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +1842 -65
  5. package/lib/index.js.map +1 -1
  6. package/package.json +2 -2
  7. package/src/client/index.ts +3 -0
  8. package/src/client/monaco/lsp/providers.ts +28 -6
  9. package/src/client/navHighlight.ts +96 -0
  10. package/src/client/paths.ts +2 -0
  11. package/src/client/rpc.ts +32 -12
  12. package/src/client/sidebar/SidebarView.ts +6 -2
  13. package/src/client/sidebar/menuItems.ts +93 -2
  14. package/src/client/sidebar/panels/FileExplorer.ts +18 -1
  15. package/src/client/sidebar/panels/SvnPanel.ts +303 -0
  16. package/src/client/sidebar/panels/index.ts +44 -3
  17. package/src/client/sidebar/types.ts +20 -0
  18. package/src/client/styles/editor.css +177 -0
  19. package/src/client/svnActions.ts +153 -0
  20. package/src/client/svnLog.ts +356 -0
  21. package/src/client/svnStatus.ts +363 -0
  22. package/src/client/svnStore.ts +150 -0
  23. package/src/client/tabMenu.ts +51 -0
  24. package/src/client/ui/EditorView.ts +583 -24
  25. package/src/client/ui/LogDialog.ts +333 -0
  26. package/src/client/ui/McpSettings.ts +98 -0
  27. package/src/client/ui/ModalShell.ts +79 -0
  28. package/src/client/ui/SideBySideDiff.tsx +139 -0
  29. package/src/client/ui/SnippetsPicker.ts +70 -58
  30. package/src/client/ui/SvnDiffPanel.ts +50 -0
  31. package/src/client/ui/SvnLogDialog.ts +1141 -0
  32. package/src/client/ui/SvnLogStats.ts +243 -0
  33. package/src/client/ui/commandCatalog.ts +72 -0
  34. package/src/debugLog.ts +81 -12
  35. package/src/fileOpenSettings.ts +40 -3
  36. package/src/index.ts +5 -1
  37. package/src/rpc.ts +41 -4
  38. package/src/shared/rpc.ts +53 -1
  39. package/src/shared/svn.ts +377 -0
  40. package/src/shared/svnActions.ts +296 -0
  41. package/src/svn.ts +870 -0
  42. package/src/svnLog.ts +315 -0
  43. package/src/svnText.ts +175 -0
  44. package/src/svnXml.ts +110 -0
@@ -0,0 +1,296 @@
1
+ /**
2
+ * dsh-vscode-mode shared — SVN **动作目录**(单一事实源:纯元数据,无副作用)。
3
+ *
4
+ * 为什么要这层:P1/P2 每个 SVN 动作要在四处各写一遍(命令栏 / 文件树菜单 / 页签菜单 /
5
+ * EditorView 分派),加一个动作改四处、四处显隐规则还会各自漂移(P2 的「加入/还原」
6
+ * 状态矩阵就已经手写了两份)。这里把「有哪些动作、叫什么、出现在哪些入口、什么条件下可见、
7
+ * 需要什么目标类型/能力/状态」收敛成一份数据;各入口只做「过滤 + 映射」。
8
+ *
9
+ * 分工:本文件只放**纯元数据**(可被 host/client 双面引用、可单测);
10
+ * 执行器(RPC 副作用)放 client/svnActions.ts,避免 shared 引入 node/react 依赖。
11
+ * 作者 ddj 2026年09月16号
12
+ */
13
+ import type { SvnFeature, SvnItemStatus } from './svn.js'
14
+
15
+ /** 动作可出现的入口(一个动作可在多处出现)。 */
16
+ export type SvnSurface = 'palette' | 'tree' | 'tab'
17
+
18
+ /** 动作所需能力:cli = svn 命令可用;tortoise = TortoiseProc 可用;none = 无需 SVN 能力。 */
19
+ export type SvnCapability = 'cli' | 'tortoise' | 'none'
20
+
21
+ /** 动作适用的目标类型。 */
22
+ export type SvnTargetType = 'file' | 'directory' | 'root' | 'editor'
23
+
24
+ /** 动作可见性所需的条目状态条件(未列状态 = 不限制)。 */
25
+ export type SvnStateGate = 'versioned' | 'unversioned' | 'changed' | 'diffable'
26
+
27
+ /** 一条 SVN 动作定义(纯元数据)。 */
28
+ export interface SvnActionDef {
29
+ /** 稳定动作 id(同 id 在三个入口复用,如 'update' / 'diff-base' / 'log')。 */
30
+ id: string
31
+ /** 命令栏命令 id(`edrv.svn*`);仅 palette 入口需要。 */
32
+ commandId?: string
33
+ /** 展示文案(菜单与命令栏共用,避免两处漂移)。 */
34
+ label: string
35
+ /** 命令栏分组排序(小者靠前)。 */
36
+ order: number
37
+ /** 可出现的入口集合。 */
38
+ surfaces: readonly SvnSurface[]
39
+ /** 所需能力(缺省 'cli')。 */
40
+ capability?: SvnCapability
41
+ /** 适用目标类型(缺省全部)。 */
42
+ targets?: readonly SvnTargetType[]
43
+ /** 破坏性动作(danger 样式 + 调用方须 confirm)。 */
44
+ danger?: boolean
45
+ /** 菜单前置分隔线(分组首条)。 */
46
+ separator?: boolean
47
+ /** 状态条件(对目标文件的变更条目状态求值)。 */
48
+ gates?: readonly SvnStateGate[]
49
+ /** 命令栏可用性是否额外要求存在活动编辑器模型。 */
50
+ needsEditorModel?: boolean
51
+ /**
52
+ * 该动作已被**自研能力**替代时声明对应能力标志(仅 Tortoise 过渡项使用)。
53
+ * 一旦 `ctx.svnFeatures` 含该能力,本项即隐藏——落实「自研替换时间线」,
54
+ * 避免自研项与 Tortoise 同名项并列、用户误点到官方弹窗。
55
+ */
56
+ coveredBy?: SvnFeature
57
+ }
58
+
59
+ /**
60
+ * SVN 动作目录(顺序 = 命令栏展示顺序)。
61
+ *
62
+ * 显隐口径(详见各 gates):
63
+ * - update:受管理且 CLI 可用即可(文件/目录/根都合法)
64
+ * - diff-base:需「可比较」——文本文件(图片/PDF 无文本差异)
65
+ * - add:仅未纳入版本控制的目标
66
+ * - revert:仅受版本控制且有本地改动的目标
67
+ * - log:受版本控制的目标(含目录)
68
+ * - cleanup:不受目标状态限制(修工作副本锁)
69
+ * - Tortoise 组:仅 Windows 且 TortoiseProc 可用
70
+ */
71
+ export const SVN_ACTIONS: readonly SvnActionDef[] = [
72
+ {
73
+ id: 'update',
74
+ commandId: 'edrv.svnUpdate',
75
+ label: 'SVN 更新',
76
+ order: 10,
77
+ surfaces: ['palette', 'tree', 'tab'],
78
+ capability: 'cli',
79
+ },
80
+ {
81
+ id: 'refresh-changes',
82
+ commandId: 'edrv.svnRefreshChanges',
83
+ label: 'SVN 刷新变更(工作副本状态)',
84
+ order: 15,
85
+ surfaces: ['palette'],
86
+ capability: 'cli',
87
+ },
88
+ {
89
+ id: 'diff-base',
90
+ commandId: 'edrv.svnDiffBase',
91
+ label: '与基线比较',
92
+ order: 16,
93
+ surfaces: ['palette', 'tree', 'tab'],
94
+ capability: 'cli',
95
+ targets: ['file', 'editor'],
96
+ gates: ['diffable'],
97
+ needsEditorModel: true,
98
+ },
99
+ {
100
+ id: 'add',
101
+ commandId: 'edrv.svnAdd',
102
+ label: '加入版本控制',
103
+ order: 17,
104
+ surfaces: ['palette', 'tree', 'tab'],
105
+ capability: 'cli',
106
+ targets: ['file', 'directory'],
107
+ gates: ['unversioned'],
108
+ needsEditorModel: true,
109
+ },
110
+ {
111
+ // id 保持 'revert-cli'(P2 以来的菜单 id `svn-revert-cli` 与命令 id `edrv.svnRevertCli` 不变,
112
+ // 避免仅为重构而改动既有 id;'cli' 后缀用于与 Tortoise 还原区分)
113
+ id: 'revert-cli',
114
+ commandId: 'edrv.svnRevertCli',
115
+ label: 'SVN 还原',
116
+ order: 18,
117
+ surfaces: ['palette', 'tree', 'tab'],
118
+ capability: 'cli',
119
+ targets: ['file', 'directory'],
120
+ gates: ['versioned', 'changed'],
121
+ danger: true,
122
+ needsEditorModel: true,
123
+ },
124
+ {
125
+ id: 'log',
126
+ commandId: 'edrv.svnLog',
127
+ label: '查看日志',
128
+ order: 19,
129
+ surfaces: ['palette', 'tree', 'tab'],
130
+ capability: 'cli',
131
+ targets: ['file', 'directory'],
132
+ gates: ['versioned'],
133
+ },
134
+ {
135
+ id: 'cleanup',
136
+ commandId: 'edrv.svnCleanup',
137
+ label: '清理工作副本',
138
+ order: 22,
139
+ surfaces: ['palette'],
140
+ capability: 'cli',
141
+ },
142
+ {
143
+ id: 'tortoise-commit',
144
+ commandId: 'edrv.svnTortoiseCommit',
145
+ label: 'TortoiseSVN 提交',
146
+ order: 30,
147
+ surfaces: ['palette', 'tree', 'tab'],
148
+ capability: 'tortoise',
149
+ targets: ['file', 'directory', 'root', 'editor'],
150
+ separator: true,
151
+ },
152
+ {
153
+ id: 'tortoise-log',
154
+ commandId: 'edrv.svnTortoiseLog',
155
+ label: 'TortoiseSVN 日志',
156
+ order: 31,
157
+ surfaces: ['palette', 'tree', 'tab'],
158
+ capability: 'tortoise',
159
+ targets: ['file', 'directory'],
160
+ // 自研「查看日志」(P3)已提供等价能力 → 自研就绪后隐藏本项
161
+ coveredBy: 'log',
162
+ },
163
+ {
164
+ id: 'tortoise-diff',
165
+ commandId: 'edrv.svnTortoiseDiff',
166
+ label: 'TortoiseSVN 差异',
167
+ order: 32,
168
+ surfaces: ['palette', 'tree', 'tab'],
169
+ capability: 'tortoise',
170
+ targets: ['file', 'editor'],
171
+ // 自研「与基线比较」(P2)已提供等价能力 → 自研就绪后隐藏本项
172
+ coveredBy: 'diff',
173
+ },
174
+ {
175
+ id: 'tortoise-blame',
176
+ commandId: 'edrv.svnTortoiseBlame',
177
+ label: 'TortoiseSVN 追溯',
178
+ order: 33,
179
+ surfaces: ['palette', 'tree', 'tab'],
180
+ capability: 'tortoise',
181
+ targets: ['file', 'editor'],
182
+ // 追溯尚无自研实现(P5 另议)→ 不声明 coveredBy,保留官方能力
183
+ },
184
+ {
185
+ id: 'tortoise-revert',
186
+ commandId: 'edrv.svnTortoiseRevert',
187
+ label: 'TortoiseSVN 还原',
188
+ order: 34,
189
+ surfaces: ['palette', 'tree', 'tab'],
190
+ capability: 'tortoise',
191
+ targets: ['file', 'directory'],
192
+ danger: true,
193
+ // 自研「SVN 还原」(P2 revert -R)已提供等价能力 → 自研就绪后隐藏本项
194
+ coveredBy: 'revert',
195
+ },
196
+ ]
197
+
198
+ /** 动作 id → 定义(查表用)。 */
199
+ export const SVN_ACTION_BY_ID: Record<string, SvnActionDef> = Object.fromEntries(
200
+ SVN_ACTIONS.map((action) => [action.id, action]),
201
+ )
202
+
203
+ /** 某入口的动作表(按 order 升序)。 */
204
+ export function svnActionsFor(surface: SvnSurface): SvnActionDef[] {
205
+ return SVN_ACTIONS.filter((action) => action.surfaces.includes(surface))
206
+ .slice()
207
+ .sort((a, b) => a.order - b.order)
208
+ }
209
+
210
+ /** 动作可见性的求值上下文(各入口把自身状态投影到这一形状)。 */
211
+ export interface SvnActionContext {
212
+ /** 工作区是否受 SVN 管理。 */
213
+ managed: boolean
214
+ /** svn CLI 是否可用。 */
215
+ svnCli: boolean
216
+ /** TortoiseProc 是否可用。 */
217
+ tortoise: boolean
218
+ /** 已由自研界面提供等价能力的功能集合(落实「自研替换时间线」;缺省空 = 不隐藏)。 */
219
+ svnFeatures?: readonly SvnFeature[]
220
+ /** 目标类型(缺省视为 editor)。 */
221
+ target?: SvnTargetType
222
+ /** 目标是否为受版本控制(不在变更清单里视为受版本控制)。 */
223
+ versioned?: boolean
224
+ /** 目标当前的变更状态(不在清单里为 undefined)。 */
225
+ status?: SvnItemStatus
226
+ /** 目标是否为可比较的文本文件(按扩展名判定)。 */
227
+ diffable?: boolean
228
+ }
229
+
230
+ /**
231
+ * 该动作是否已被自研能力覆盖(用于隐藏 Tortoise 过渡项)。
232
+ * @author ddj 2026年09月16号
233
+ * @param action 动作定义
234
+ * @param features 已就绪的自研能力集合
235
+ * @returns 是否应因自研覆盖而隐藏
236
+ */
237
+ export function svnActionCovered(action: SvnActionDef, features: readonly SvnFeature[] | undefined): boolean {
238
+ if (!action.coveredBy) return false
239
+ return Array.isArray(features) && features.includes(action.coveredBy)
240
+ }
241
+
242
+ /**
243
+ * 动作在给定上下文下是否可用(能力 + 目标类型 + 状态门禁,全部满足才可用)。
244
+ *
245
+ * 这是三入口**唯一**的显隐判定实现:树菜单/页签菜单/命令栏都调它,
246
+ * 从根上消除「同一动作两处手写规则各自漂移」的历史问题。
247
+ *
248
+ * @author ddj 2026年09月16号
249
+ * @param action 动作定义
250
+ * @param ctx 求值上下文
251
+ * @returns 是否可用
252
+ */
253
+ export function svnActionOn(action: SvnActionDef, ctx: SvnActionContext): boolean {
254
+ if (!ctx.managed) return false
255
+ // 自研替换时间线:自研已有等价能力时隐藏对应 Tortoise 过渡项
256
+ if (svnActionCovered(action, ctx.svnFeatures)) return false
257
+ const capability = action.capability ?? 'cli'
258
+ if (capability === 'cli' && !ctx.svnCli) return false
259
+ if (capability === 'tortoise' && !ctx.tortoise) return false
260
+ if (action.targets && action.targets.length) {
261
+ const target = ctx.target ?? 'editor'
262
+ if (!action.targets.includes(target)) return false
263
+ }
264
+ for (const gate of action.gates ?? []) {
265
+ if (!gatePasses(gate, ctx)) return false
266
+ }
267
+ return true
268
+ }
269
+
270
+ /**
271
+ * 单条状态门禁求值。
272
+ * @author ddj 2026年09月16号
273
+ * @param gate 门禁
274
+ * @param ctx 求值上下文
275
+ * @returns 是否通过
276
+ */
277
+ function gatePasses(gate: SvnStateGate, ctx: SvnActionContext): boolean {
278
+ const status = ctx.status
279
+ if (gate === 'versioned') return ctx.versioned === true
280
+ if (gate === 'unversioned') return ctx.versioned === false
281
+ if (gate === 'diffable') return ctx.diffable === true && ctx.versioned !== false
282
+ // changed:在变更清单里且不是 normal/external(未版本控制也算有改动)
283
+ if (gate === 'changed') return status !== undefined && status !== 'normal' && status !== 'external'
284
+ return false
285
+ }
286
+
287
+ /**
288
+ * 过滤出某入口当前可见的动作(已排序)。
289
+ * @author ddj 2026年09月16号
290
+ * @param surface 入口
291
+ * @param ctx 求值上下文
292
+ * @returns 可见动作(保持 order)
293
+ */
294
+ export function visibleSvnActions(surface: SvnSurface, ctx: SvnActionContext): SvnActionDef[] {
295
+ return svnActionsFor(surface).filter((action) => svnActionOn(action, ctx))
296
+ }