dsh-vscode-mode 0.5.2 → 0.6.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.
- package/README.md +78 -5
- package/lib/client.js +5579 -236
- package/lib/client.js.map +1 -1
- package/lib/index.js +2358 -126
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/compat.ts +1 -1
- package/src/client/dap/BpWidget.ts +262 -0
- package/src/client/dap/bpMenu.ts +91 -0
- package/src/client/dap/bpRowMenu.ts +53 -0
- package/src/client/dap/breakpoints.ts +222 -0
- package/src/client/dap/decorate.ts +128 -0
- package/src/client/dap/hintLine.ts +29 -0
- package/src/client/dap/hover.ts +166 -0
- package/src/client/dap/hoverMode.ts +159 -0
- package/src/client/dap/hoverTree.ts +722 -0
- package/src/client/dap/launchInsert.ts +229 -0
- package/src/client/dap/launchSnippetProvider.ts +206 -0
- package/src/client/dap/modelPath.ts +23 -0
- package/src/client/dap/panelSplit.ts +105 -0
- package/src/client/dap/pidPick.ts +24 -0
- package/src/client/dap/store.ts +571 -0
- package/src/client/dap/toolbarDrag.ts +51 -0
- package/src/client/dap/trace.ts +22 -0
- package/src/client/dap/variableTree.ts +62 -0
- package/src/client/editorLimit.ts +31 -0
- package/src/client/index.ts +13 -0
- package/src/client/markdownPreview.ts +24 -0
- package/src/client/md/componentType.ts +28 -0
- package/src/client/md/mdPanel.ts +98 -0
- package/src/client/monaco/lsp/index.ts +15 -0
- package/src/client/monaco/lsp/providers.ts +2 -0
- package/src/client/monaco/theme.ts +15 -0
- package/src/client/searchSeed.ts +64 -0
- package/src/client/sidebar/panels/DebugPanel.ts +613 -0
- package/src/client/sidebar/panels/SearchPanel.ts +70 -15
- package/src/client/sidebar/panels/SvnPanel.ts +167 -23
- package/src/client/sidebar/panels/index.ts +19 -0
- package/src/client/sidebar/types.ts +2 -0
- package/src/client/styles/editor.css +186 -1
- package/src/client/svnActions.ts +18 -1
- package/src/client/svnLog.ts +3 -3
- package/src/client/svnStatus.ts +140 -2
- package/src/client/tabActions.ts +54 -0
- package/src/client/ui/EditorView.ts +711 -29
- package/src/client/ui/McpSettings.ts +29 -0
- package/src/client/ui/SideBySideDiff.tsx +214 -29
- package/src/client/ui/SvnDiffPanel.ts +21 -8
- package/src/client/ui/SvnLogDialog.ts +44 -2
- package/src/client/ui/SvnPatchDialog.ts +63 -0
- package/src/client/ui/SvnSumDialog.ts +77 -0
- package/src/client/ui/commandCatalog.ts +54 -0
- package/src/client/ui/horizontalWheel.ts +85 -0
- package/src/client/ui/svnExport.ts +71 -0
- package/src/client-primitives.d.ts +34 -0
- package/src/dap/configSnippets.ts +269 -0
- package/src/dap/discovery.ts +198 -0
- package/src/dap/launchConfig.ts +127 -0
- package/src/dap/manager.ts +588 -0
- package/src/dap/processList.ts +76 -0
- package/src/dap/protocol.ts +80 -0
- package/src/dap/provider.ts +49 -0
- package/src/dap/rpc.ts +192 -0
- package/src/dap/sourcePath.ts +82 -0
- package/src/fileOpenSettings.ts +3 -0
- package/src/index.ts +7 -3
- package/src/lsp/providers.ts +3 -2
- package/src/reveal.ts +31 -20
- package/src/rpc.ts +11 -3
- package/src/search/ripgrep.ts +127 -11
- package/src/shared/dap.ts +262 -0
- package/src/shared/editorLimit.ts +46 -0
- package/src/shared/keybindings.ts +10 -0
- package/src/shared/rpc.ts +40 -2
- package/src/shared/svn.ts +147 -0
- package/src/shared/svnActions.ts +20 -0
- package/src/svn.ts +320 -11
- package/src/workspace.ts +0 -75
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
@author ddj 2026-09-10 */
|
|
6
6
|
[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; }
|
|
7
7
|
|
|
8
|
-
[data-edrv-view] .edrv-tabs { display: flex; align-items: center; gap: 2px; padding: 2px 8px 0; background: var(--dsw-alias-bg-layer-1, transparent); overflow-x: auto; scrollbar-width: thin; flex-shrink: 0; }
|
|
8
|
+
[data-edrv-view] .edrv-tabs { display: flex; align-items: center; gap: 2px; padding: 2px 8px 0; background: var(--dsw-alias-bg-layer-1, transparent); overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; flex-shrink: 0; overscroll-behavior-x: contain; } /* overscroll-behavior-x: 横滚到边界不把滚动链传给页面(滚轮横滚接管更可控)@author ddj 2026年09月18号 */
|
|
9
9
|
[data-edrv-view] .edrv-tab { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 8px 0 12px; border: none; border-radius: 6px 6px 0 0; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 12px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }
|
|
10
10
|
[data-edrv-view] .edrv-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }
|
|
11
11
|
[data-edrv-view] .edrv-tab.edrv-tab-active { background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); box-shadow: inset 0 -2px 0 var(--dsw-alias-brand-primary, #4f8cff); }
|
|
@@ -84,6 +84,15 @@
|
|
|
84
84
|
[data-edrv-view] .edrv-pdf-mode-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-brand-primary, #0f9d58); border-color: var(--dsw-alias-brand-primary, #0f9d58); }
|
|
85
85
|
[data-edrv-view] .edrv-pdf-dirty { color: var(--dsw-alias-state-warn-primary, #d97706); border-color: var(--dsw-alias-state-warn-primary, #d97706); }
|
|
86
86
|
[data-edrv-view] .edrv-pdf-stage { position: absolute; top: 34px; left: 0; right: 0; bottom: 0; overflow: auto; }
|
|
87
|
+
/* Markdown 预览(需求 4):工具条 + 居中阅读栏;正文排版由官方 MarkdownText 自带的 --dsw-* 样式负责 */
|
|
88
|
+
[data-edrv-view] .edrv-mdview { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
89
|
+
[data-edrv-view] .edrv-mdview-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }
|
|
90
|
+
[data-edrv-view] .edrv-mdview-meta { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; }
|
|
91
|
+
[data-edrv-view] .edrv-mdview-stage { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 14px 20px 28px; }
|
|
92
|
+
[data-edrv-view] .edrv-mdview-doc { max-width: 880px; margin: 0 auto; width: 100%; }
|
|
93
|
+
[data-edrv-view] .edrv-mdview-fallback { font-size: 12px; line-height: 1.7; color: var(--dsw-alias-label-secondary, #52606d); }
|
|
94
|
+
[data-edrv-view] .edrv-mdview-pre { margin: 8px 0 0; padding: 10px 12px; border-radius: 8px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-primary, #1f2933); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
|
|
95
|
+
[data-edrv-view] .edrv-pb-mdbtn { height: 20px; padding: 0 8px; font-size: 11px; flex-shrink: 0; }
|
|
87
96
|
[data-edrv-view] .edrv-pdf-stage .edrv-pdf-viewer { margin: 0 auto; }
|
|
88
97
|
[data-edrv-view] .edrv-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; height: 100%; color: var(--dsw-alias-label-tertiary, #777); font-size: 13px; }
|
|
89
98
|
[data-edrv-view] .edrv-diffbar { position: absolute; left: 0; right: 0; bottom: 10px; margin: 0 auto; z-index: 25; width: fit-content; max-width: calc(100% - 20px); border-radius: 12px; background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); box-shadow: none; overflow: visible; display: flex; flex-direction: column; }
|
|
@@ -321,6 +330,44 @@
|
|
|
321
330
|
[data-edrv-view] .edrv-svndiff-tag { padding: 1px 6px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); }
|
|
322
331
|
[data-edrv-view] .edrv-svndiff-host { flex: 1 1 auto; min-height: 0; }
|
|
323
332
|
[data-edrv-view] .edrv-svndiff-empty { display: flex; flex: 1 1 auto; align-items: center; justify-content: center; padding: 24px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 12px; text-align: center; }
|
|
333
|
+
/* 差异整行色块与行首 +/- 指示符:vendored Monaco(diffEditorWidget2)静态 CSS 无
|
|
334
|
+
.line-insert/.line-delete 背景规则,整行色块靠主题键 + 此处固定色兜底保证可见。
|
|
335
|
+
注意不要用 --dsw-alias-interactive-bg-hover 类 token:实测为 92% 透明中性色,肉眼不可见。
|
|
336
|
+
固定色取自研差异 UI 同色相(绿 #0f9d58 / 红 #d9534f),alpha 提到肉眼可辨。 */
|
|
337
|
+
[data-edrv-view] .monaco-editor .line-insert { background: rgba(15, 157, 88, 0.18); }
|
|
338
|
+
[data-edrv-view] .monaco-editor .line-delete { background: rgba(217, 83, 79, 0.16); }
|
|
339
|
+
[data-edrv-view] .monaco-editor .gutter-insert { background: rgba(15, 157, 88, 0.35); }
|
|
340
|
+
[data-edrv-view] .monaco-editor .gutter-delete { background: rgba(217, 83, 79, 0.35); }
|
|
341
|
+
[data-edrv-view] .monaco-editor .insert-sign { color: var(--dsw-alias-state-success-primary, #0f9d58); }
|
|
342
|
+
[data-edrv-view] .monaco-editor .delete-sign { color: var(--dsw-alias-state-error-primary, #d9534f); }
|
|
343
|
+
/* 差异条快速跳转簇(对齐自研审查条 ↑ i/N ↓ 的形态) */
|
|
344
|
+
[data-edrv-view] .edrv-svndiff-nav { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
|
|
345
|
+
[data-edrv-view] .edrv-svndiff-nav-count { min-width: 36px; text-align: center; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); }
|
|
346
|
+
/* W1-3 差异统计徽标(+新增/修改 −删除/修改;色相与行块绿红一致) @author ddj 2026-09-20 */
|
|
347
|
+
[data-edrv-view] .edrv-svndiff-stat { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; font-variant-numeric: tabular-nums; }
|
|
348
|
+
[data-edrv-view] .edrv-svndiff-stat-add { color: var(--dsw-alias-state-success-primary, #0f9d58); }
|
|
349
|
+
[data-edrv-view] .edrv-svndiff-stat-del { color: var(--dsw-alias-state-error-primary, #d9534f); }
|
|
350
|
+
/* W1-1 差异视图选项条(右端开关组;复用 .edrv-svn-check 的 label 形态) */
|
|
351
|
+
[data-edrv-view] .edrv-svndiff-opts { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
|
|
352
|
+
/* W1-2 变更面板名称过滤输入框(工具条行内,紧凑) @author ddj 2026-09-20 */
|
|
353
|
+
[data-edrv-view] .edrv-svn-name-filter { width: 168px; padding: 1px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; line-height: 14px; }
|
|
354
|
+
[data-edrv-view] .edrv-svn-name-filter:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #0f9d58); }
|
|
355
|
+
[data-edrv-view] .edrv-svn-name-filter::placeholder { color: var(--dsw-alias-label-tertiary, #9aa5b1); }
|
|
356
|
+
/* W2-3 远端更新提示条(工具条上方;warn = 失败降级) @author ddj 2026-09-20 */
|
|
357
|
+
[data-edrv-view] .edrv-svn-hintbar { display: flex; align-items: center; gap: 6px; margin: 0 10px 4px 12px; padding: 3px 8px; border-radius: 5px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 11px; flex-shrink: 0; }
|
|
358
|
+
[data-edrv-view] .edrv-svn-hintbar-warn { background: rgba(217, 83, 79, 0.12); color: var(--dsw-alias-state-error-primary, #d9534f); }
|
|
359
|
+
/* W2-5 疑似改名标记(行内 ⇄;hover 见 tooltip) */
|
|
360
|
+
[data-edrv-view] .edrv-svn-pair-mark { flex-shrink: 0; color: var(--dsw-alias-state-warn-primary, #7c3aed); font-size: 11px; cursor: help; }
|
|
361
|
+
/* W2-1 补丁对话框 / W2-2 目录对比对话框 @author ddj 2026-09-20 */
|
|
362
|
+
[data-edrv-view] .edrv-svn-patch-dialog { max-width: calc(100vw - 48px); }
|
|
363
|
+
[data-edrv-view] .edrv-svn-patch-head { display: flex; align-items: center; gap: 8px; padding-bottom: 8px; }
|
|
364
|
+
[data-edrv-view] .edrv-svn-patch-notes { display: flex; flex-direction: column; gap: 4px; padding-bottom: 6px; }
|
|
365
|
+
[data-edrv-view] .edrv-svn-patch-warn { color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }
|
|
366
|
+
[data-edrv-view] .edrv-svn-patch-pre { max-height: 46vh; overflow: auto; margin: 0 0 8px; padding: 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f7f9f9); color: var(--dsw-alias-label-primary, #1f2933); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.5; white-space: pre; user-select: text; }
|
|
367
|
+
[data-edrv-view] .edrv-svn-patch-foot { display: flex; align-items: center; gap: 8px; }
|
|
368
|
+
[data-edrv-view] .edrv-svn-sum-form { display: flex; align-items: center; gap: 10px; padding-bottom: 8px; }
|
|
369
|
+
[data-edrv-view] .edrv-svn-sum-list { max-height: 44vh; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; padding: 2px; margin-bottom: 8px; }
|
|
370
|
+
[data-edrv-view] .edrv-svnlog-path-kind { flex-shrink: 0; padding: 0 5px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; }
|
|
324
371
|
|
|
325
372
|
/* ===== 通用弹窗外壳(ModalShell)+ SVN 日志弹窗 @author ddj 2026-09-16 ===== */
|
|
326
373
|
/* z-index 190:落在既有阶梯内(命令栏 200 > 片段二级 197 > 片段一级 195 > 190),
|
|
@@ -614,3 +661,141 @@
|
|
|
614
661
|
|
|
615
662
|
|
|
616
663
|
|
|
664
|
+
|
|
665
|
+
/* 调试(DAP):断点圆点 / 停帧行 / 浮动调试工具条。
|
|
666
|
+
圆点挂在 Monaco glyphMargin(glyphMarginClassName),行背景为 isWholeLine className;
|
|
667
|
+
工具条绝对定位于编辑区右上(editorArea relative 由 .edrv-editor-area 自身定位)。 */
|
|
668
|
+
/* 断点圆点:装饰类占满整行,圆点经 ::before 在本行盒子内居中(行归属明确、不跨行界) */
|
|
669
|
+
[data-edrv-view] .edrv-dap-bp { cursor: pointer; }
|
|
670
|
+
[data-edrv-view] .edrv-dap-bp::before { content: ''; display: block; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; background: var(--dsw-alias-state-error-primary, #e51400); }
|
|
671
|
+
[data-edrv-view] .edrv-dap-bp-off { cursor: pointer; }
|
|
672
|
+
[data-edrv-view] .edrv-dap-bp-off::before { content: ''; display: block; box-sizing: border-box; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; border: 1px solid var(--dsw-alias-label-tertiary, #9aa5b1); opacity: .6; }
|
|
673
|
+
/* 未验证断点(适配器真实回执 verified:false):空心红圈,对齐 VS Code 空心圆语义 */
|
|
674
|
+
[data-edrv-view] .edrv-dap-bp-unverified { cursor: pointer; }
|
|
675
|
+
[data-edrv-view] .edrv-dap-bp-unverified::before { content: ''; display: block; box-sizing: border-box; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; border: 1px solid var(--dsw-alias-state-error-primary, #e51400); opacity: .75; }
|
|
676
|
+
[data-edrv-view] .edrv-dap-paused-line { background: var(--dsw-alias-state-warn-tertiary, rgba(229, 185, 63, .22)); }
|
|
677
|
+
[data-edrv-view] .edrv-dap-paused-glyph { cursor: pointer; }
|
|
678
|
+
[data-edrv-view] .edrv-dap-paused-glyph::before { content: ''; display: block; width: 5px; height: 12px; margin: 4px auto 0; border-radius: 2px; background: var(--dsw-alias-state-warn-primary, #e5b93f); }
|
|
679
|
+
[data-edrv-view] .edrv-editor-area { position: relative; }
|
|
680
|
+
/* launch.json 右下角「添加配置」按钮(VSCode 同款形态):仅 launch.json 打开时渲染 */
|
|
681
|
+
[data-edrv-view] .edrv-addcfg { position: absolute; right: 14px; bottom: 10px; z-index: 13; padding: 4px 12px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #f7f9fa); color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; line-height: 18px; cursor: pointer; box-shadow: 0 1px 4px rgba(0, 0, 0, .1); }
|
|
682
|
+
[data-edrv-view] .edrv-addcfg:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); color: var(--dsw-alias-label-primary, #1f2933); }
|
|
683
|
+
[data-edrv-view] .edrv-dapbar { position: absolute; top: 6px; right: 14px; z-index: 12; display: none; align-items: center; gap: 3px; padding: 3px 5px; border-radius: 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, #f7f9fa); box-shadow: 0 2px 8px rgba(0, 0, 0, .12); }
|
|
684
|
+
[data-edrv-view] .edrv-dapbar.on { display: flex; border-color: var(--dsw-alias-border-l2, #cdd6da); }
|
|
685
|
+
[data-edrv-view] .edrv-dapbar-grip { flex: 0 0 auto; width: 16px; height: 22px; display: inline-flex; align-items: center; justify-content: center; cursor: grab; user-select: none; color: var(--dsw-alias-label-tertiary, #7b8794); font-size: 12px; }
|
|
686
|
+
[data-edrv-view] .edrv-dapbar-grip:active, [data-edrv-view] .edrv-dapbar.dragging .edrv-dapbar-grip { cursor: grabbing; }
|
|
687
|
+
[data-edrv-view] .edrv-dapbar.dragging { user-select: none; }
|
|
688
|
+
[data-edrv-view] .edrv-dapbar-cfg { max-width: 220px; height: 22px; font-size: 11px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }
|
|
689
|
+
[data-edrv-view] .edrv-dapbar-btn { min-width: 24px; height: 22px; padding: 0 6px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, inherit); font-size: 12px; line-height: 1; cursor: pointer; }
|
|
690
|
+
[data-edrv-view] .edrv-dapbar-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); }
|
|
691
|
+
[data-edrv-view] .edrv-dapbar-btn:disabled { opacity: .35; cursor: default; }
|
|
692
|
+
[data-edrv-view] .edrv-dapbar-phase { padding: 0 4px; font-size: 11px; white-space: nowrap; color: var(--dsw-alias-label-secondary, #52606d); }
|
|
693
|
+
[data-edrv-view] .edrv-dapbar-phase.paused { color: var(--dsw-alias-state-warn-primary, #b7791f); font-weight: 600; }
|
|
694
|
+
[data-edrv-view] .edrv-dapbar-err { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--dsw-alias-state-error-primary, #e05252); }
|
|
695
|
+
|
|
696
|
+
/* 调试面板(活动栏「调试」页签):四段折叠区 + 断点列表 + REPL。 */
|
|
697
|
+
[data-edrv-view] .edrv-dappanel { display: flex; flex-direction: column; gap: 10px; height: 100%; min-height: 0; overflow: auto; padding: 2px 2px 8px; }
|
|
698
|
+
[data-edrv-view] .edrv-dappanel-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; row-gap: 4px; font-size: 12px; color: var(--dsw-alias-label-secondary, #52606d); }
|
|
699
|
+
[data-edrv-view] .edrv-dappanel-phase { padding: 2px 8px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }
|
|
700
|
+
[data-edrv-view] .edrv-dappanel-phase.paused { background: var(--dsw-alias-state-warn-tertiary, rgba(229, 185, 63, .18)); color: var(--dsw-alias-state-warn-primary, #b7791f); font-weight: 600; }
|
|
701
|
+
[data-edrv-view] .edrv-dappanel-cfg { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
702
|
+
[data-edrv-view] .edrv-dappanel-pid { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }
|
|
703
|
+
[data-edrv-view] .edrv-dappanel-err { color: var(--dsw-alias-state-error-primary, #e05252); }
|
|
704
|
+
[data-edrv-view] .edrv-dapseg { display: flex; flex-direction: column; gap: 2px; min-height: 0; }
|
|
705
|
+
[data-edrv-view] .edrv-dapseg-body { min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; }
|
|
706
|
+
/* 段间分隔条(splitview sash):拖动调整相邻两段高度占比;各段高度由 DebugPanel 内联接管。 */
|
|
707
|
+
[data-edrv-view] .edrv-dapseg-sash { flex: 0 0 auto; height: 4px; margin: 2px 0; border-radius: 2px; background: transparent; cursor: row-resize; }
|
|
708
|
+
[data-edrv-view] .edrv-dapseg-sash:hover, [data-edrv-view] .edrv-dapseg-sash.dragging { background: var(--dsw-alias-border-l2, #cdd6da); }
|
|
709
|
+
/* 折叠段头:chevron + 标题 + 悬浮动作区(动作 hover 显隐;REPL 头不参与折叠)。 */
|
|
710
|
+
[data-edrv-view] .edrv-dapseg-head { display: flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-tertiary, #7b8794); padding: 2px 2px; cursor: pointer; border-radius: 4px; }
|
|
711
|
+
[data-edrv-view] .edrv-dapseg-head:hover { color: var(--dsw-alias-label-secondary, #52606d); }
|
|
712
|
+
[data-edrv-view] .edrv-daprepl .edrv-dapseg-head { cursor: default; padding: 2px 0; }
|
|
713
|
+
[data-edrv-view] .edrv-dapseg-chevron { flex: 0 0 auto; width: 14px; text-align: center; font-size: 10px; }
|
|
714
|
+
[data-edrv-view] .edrv-dapseg-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
715
|
+
[data-edrv-view] .edrv-dapseg-act { flex: 0 0 auto; min-width: 20px; height: 18px; padding: 0 4px; border: none; border-radius: 4px; background: transparent; color: var(--dsw-alias-label-tertiary, #7b8794); font-size: 11px; line-height: 1; cursor: pointer; opacity: 0; }
|
|
716
|
+
[data-edrv-view] .edrv-dapseg-head:hover .edrv-dapseg-act { opacity: 1; }
|
|
717
|
+
[data-edrv-view] .edrv-dapseg-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); color: var(--dsw-alias-label-primary, inherit); }
|
|
718
|
+
[data-edrv-view] .edrv-dapempty { font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 4px 2px; }
|
|
719
|
+
[data-edrv-view] .edrv-dapframe { display: flex; align-items: baseline; gap: 8px; padding: 3px 4px; border-radius: 6px; cursor: pointer; font-size: 12px; }
|
|
720
|
+
[data-edrv-view] .edrv-dapframe:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }
|
|
721
|
+
[data-edrv-view] .edrv-dapframe-name { color: var(--dsw-alias-label-primary, inherit); font-weight: 600; }
|
|
722
|
+
[data-edrv-view] .edrv-dapframe-loc { color: var(--dsw-alias-label-tertiary, #9aa5b1); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
723
|
+
[data-edrv-view] .edrv-dapscope { font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary, #52606d); padding: 4px 2px 1px; }
|
|
724
|
+
[data-edrv-view] .edrv-dapvar { display: flex; align-items: baseline; gap: 6px; width: 100%; box-sizing: border-box; font-size: 12px; padding: 1px 2px; min-width: 0; }
|
|
725
|
+
[data-edrv-view] .edrv-dapvar-tw { flex: 0 0 auto; width: 14px; border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; font-size: 10px; padding: 0; }
|
|
726
|
+
[data-edrv-view] span.edrv-dapvar-tw { cursor: default; }
|
|
727
|
+
[data-edrv-view] .edrv-dapvar-name { flex: 0 1 34%; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-primary, inherit); font-family: var(--ds-font-family-code, ui-monospace, monospace); white-space: nowrap; }
|
|
728
|
+
[data-edrv-view] .edrv-dapvar-val { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #52606d); }
|
|
729
|
+
[data-edrv-view] .edrv-dapvar-type { flex: 0 0 auto; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }
|
|
730
|
+
[data-edrv-view] .edrv-dapframe.selected { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .16)); outline: 1px solid var(--dsw-alias-border-l2, #cdd6da); }
|
|
731
|
+
[data-edrv-view] .edrv-dapwatch { display: flex; align-items: baseline; gap: 6px; width: 100%; box-sizing: border-box; min-width: 0; font-size: 12px; padding: 1px 2px; }
|
|
732
|
+
[data-edrv-view] .edrv-dapwatch-x { border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; width: 14px; }
|
|
733
|
+
[data-edrv-view] .edrv-dapwatch-x:hover { color: var(--dsw-alias-state-error-primary, #e05252); }
|
|
734
|
+
[data-edrv-view] .edrv-dapwatch-add { padding: 2px 0; }
|
|
735
|
+
[data-edrv-view] .edrv-dapbp { display: flex; align-items: center; gap: 6px; font-size: 12px; padding: 2px 2px; border-radius: 6px; cursor: pointer; }
|
|
736
|
+
[data-edrv-view] .edrv-dapbp:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }
|
|
737
|
+
[data-edrv-view] .edrv-dapbp:active { background: var(--dsw-alias-interactive-bg-pressed, rgba(127, 127, 127, .2)); }
|
|
738
|
+
[data-edrv-view] .edrv-dapbp-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; background: var(--dsw-alias-state-error-primary, #e51400); }
|
|
739
|
+
[data-edrv-view] .edrv-dapbp-dot.off { background: transparent; border: 1.5px solid var(--dsw-alias-label-tertiary, #9aa5b1); opacity: .6; }
|
|
740
|
+
[data-edrv-view] .edrv-dapbp-file { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border: none; background: transparent; color: var(--dsw-alias-label-primary, inherit); cursor: pointer; padding: 0; font-size: 12px; }
|
|
741
|
+
[data-edrv-view] .edrv-dapbp-dir { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; text-align: right; }
|
|
742
|
+
[data-edrv-view] .edrv-dapbp-line { flex: 0 0 auto; font-size: 10px; padding: 1px 6px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }
|
|
743
|
+
[data-edrv-view] .edrv-dapbp-x { flex: 0 0 auto; border: none; background: transparent; color: transparent; cursor: pointer; width: 14px; }
|
|
744
|
+
[data-edrv-view] .edrv-dapbp:hover .edrv-dapbp-x { color: var(--dsw-alias-label-tertiary, #9aa5b1); }
|
|
745
|
+
[data-edrv-view] .edrv-dapbp-x:hover { color: var(--dsw-alias-state-error-primary, #e05252) !important; }
|
|
746
|
+
[data-edrv-view] .edrv-daprepl { display: flex; flex-direction: column; gap: 4px; }
|
|
747
|
+
[data-edrv-view] .edrv-daprepl-log { display: flex; flex-direction: column; gap: 1px; max-height: 180px; overflow: auto; }
|
|
748
|
+
[data-edrv-view] .edrv-daprepl-line { font-size: 11px; font-family: var(--ds-font-family-code, ui-monospace, monospace); color: var(--dsw-alias-label-secondary, #52606d); white-space: pre-wrap; word-break: break-all; display: flex; gap: 6px; }
|
|
749
|
+
[data-edrv-view] .edrv-daprepl-line.out { color: var(--dsw-alias-label-tertiary, #9aa5b1); }
|
|
750
|
+
[data-edrv-view] .edrv-daprepl-in { width: 100%; box-sizing: border-box; height: 26px; padding: 0 8px; font-size: 12px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }
|
|
751
|
+
[data-edrv-view] .edrv-dappanel-run { flex: 0 0 auto; width: 24px; height: 24px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, inherit); font-size: 13px; line-height: 1; cursor: pointer; }
|
|
752
|
+
[data-edrv-view] .edrv-dappanel-run:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); }
|
|
753
|
+
[data-edrv-view] .edrv-dappanel-run:disabled { opacity: .35; cursor: default; background: transparent; }
|
|
754
|
+
/* 面板顶部配置下拉(与编辑器浮动条 .edrv-dapbar-cfg 同视觉)。 */
|
|
755
|
+
[data-edrv-view] .edrv-dappanel-cfg-select { max-width: 190px; height: 24px; padding: 0 4px; font-size: 11px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }
|
|
756
|
+
/* 断点行内编辑行(面板内展开编辑,控件复用 .edrv-bpwidget-* 视觉;左缩进对齐断点行文字列)。 */
|
|
757
|
+
[data-edrv-view] .edrv-dapbp-edit { display: flex; align-items: center; gap: 6px; padding: 3px 2px 3px 20px; border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }
|
|
758
|
+
[data-edrv-view] .edrv-dapbp-edit .edrv-bpwidget-mode { height: 20px; }
|
|
759
|
+
[data-edrv-view] .edrv-dapbp-edit .edrv-bpwidget-input { height: 20px; }
|
|
760
|
+
/* 断点回执:未验证空心红圈(须排在 .off 之后,禁用态优先展示灰圈语义)。 */
|
|
761
|
+
[data-edrv-view] .edrv-dapbp-dot.unverified { background: transparent; border: 1.5px solid var(--dsw-alias-state-error-primary, #e51400); opacity: .75; }
|
|
762
|
+
/* 断点行内编辑浮层(Monaco content widget,锚定断点行上方;对齐 VS Code breakpointWidget 形态)。 */
|
|
763
|
+
/* View Zone 浮层:宽度/左移由 BpWidget.syncWidth 按 layout 内联设置
|
|
764
|
+
(Monaco .view-zones 仅内容区宽,需补偿 contentLeft 与滚动条),此处只管视觉。 */
|
|
765
|
+
[data-edrv-view] .edrv-bpwidget { position: relative; box-sizing: border-box; display: flex; align-items: center; gap: 6px; padding: 3px 8px; border-top: 1px solid var(--dsw-alias-border-l2, #cdd6da); border-bottom: 1px solid var(--dsw-alias-border-l2, #cdd6da); background: var(--dsw-alias-bg-layer-1, #f7f9fa); }
|
|
766
|
+
/* 断点编辑中的目标行高亮(浮层打开期间) */
|
|
767
|
+
[data-edrv-view] .edrv-dap-editing-line { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }
|
|
768
|
+
[data-edrv-view] .edrv-bpwidget-mode { flex: 0 0 auto; height: 22px; padding: 0 4px; font-size: 12px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 3px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); cursor: pointer; }
|
|
769
|
+
[data-edrv-view] .edrv-bpwidget-input { flex: 1 1 auto !important; min-width: 0; width: auto !important; box-sizing: border-box; height: 22px; padding: 0 6px; font-size: 12px; font-family: var(--ds-font-family-code, ui-monospace, monospace); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 3px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }
|
|
770
|
+
[data-edrv-view] .edrv-bpwidget-input:focus { outline: 1px solid var(--dsw-alias-border-l2, #cdd6da); }
|
|
771
|
+
|
|
772
|
+
/* 断点 hover 预览(glyph 区):半透明小红点 + pointer 光标(对齐 VS Code .codicon-debug-hint)。 */
|
|
773
|
+
[data-edrv-view] .edrv-dap-bp-hint { cursor: pointer; }
|
|
774
|
+
[data-edrv-view] .edrv-dap-bp-hint::before { content: ''; display: block; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; background: var(--dsw-alias-state-error-primary, #e51400); opacity: .4; }
|
|
775
|
+
/* 行号区也给出可点提示(与 VS Code 一致:glyph 区与行号区都可点/可 hover);
|
|
776
|
+
不拦截折叠箭头等自有控件的光标。 */
|
|
777
|
+
[data-edrv-view] .edrv-editor-row .monaco-editor .margin-view-overlays .line-numbers:hover { cursor: pointer; }
|
|
778
|
+
|
|
779
|
+
/* 暂停态 hover 调试块 + 变量树(成员全量直显 + table 成员折叠展开)。
|
|
780
|
+
⚠️ 选择器一律用 data-*:本仓 vendored Monaco 的 markdown→DOM 管线会剥掉我们 HTML 里的
|
|
781
|
+
class(supportHtml/isTrusted 都不豁免),只有 data-* 存活;用 class 会静默失效。 */
|
|
782
|
+
[data-edrv-view] [data-edrv-dap] { display: block; min-width: 0; }
|
|
783
|
+
[data-edrv-view] [data-edrv-head] { display: flex; align-items: baseline; gap: 2px; }
|
|
784
|
+
[data-edrv-view] [data-edrv-head] [data-edrv-expr] { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); }
|
|
785
|
+
[data-edrv-view] [data-edrv-head] [data-edrv-val] { font-family: var(--ds-font-family-code, ui-monospace, monospace); word-break: break-all; }
|
|
786
|
+
[data-edrv-view] [data-edrv-type] { margin-top: 2px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #8a949b); }
|
|
787
|
+
[data-edrv-view] [data-edrv-tree] { margin: 2px 0 0; padding: 0; list-style: none; font-variant-numeric: tabular-nums; }
|
|
788
|
+
[data-edrv-view] [data-edrv-line] { display: flex; align-items: baseline; gap: 4px; padding: 1px 2px; border-radius: 3px; white-space: nowrap; }
|
|
789
|
+
[data-edrv-view] [data-edrv-line]:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }
|
|
790
|
+
/* 折叠按钮(对齐 CodeBuddy 调试浮窗变量树的 twist):热区 14px + hover 反馈,未可展开时留空占位 */
|
|
791
|
+
[data-edrv-view] [data-edrv-twist] { flex: 0 0 auto; box-sizing: border-box; width: 14px; height: 14px; line-height: 13px; text-align: center; border-radius: 3px; color: var(--dsw-alias-label-tertiary, #8a949b); }
|
|
792
|
+
[data-edrv-view] [data-edrv-twist]:not(:empty) { cursor: pointer; user-select: none; }
|
|
793
|
+
[data-edrv-view] [data-edrv-twist]:not(:empty):hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .2)); color: var(--dsw-alias-label-primary, #1f2933); }
|
|
794
|
+
[data-edrv-view] [data-edrv-name] { flex: 0 0 auto; color: var(--dsw-alias-label-primary, #1f2933); }
|
|
795
|
+
[data-edrv-view] [data-edrv-line] [data-edrv-eq] { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary, #8a949b); }
|
|
796
|
+
[data-edrv-view] [data-edrv-line] [data-edrv-val] { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #52606d); }
|
|
797
|
+
[data-edrv-view] [data-edrv-note] { color: var(--dsw-alias-label-tertiary, #8a949b); font-style: italic; }
|
|
798
|
+
/* Alt 提示层:由 hoverTree 注入到 .monaco-hover(滚动区之外)贴底常驻;容器预留同高内边距避免遮挡末行。
|
|
799
|
+
不再用 sticky / `:has()`:滚动链在 .monaco-hover-content 上,sticky 会被滚走。 */
|
|
800
|
+
[data-edrv-view] .monaco-hover[data-edrv-hint-host="1"] { padding-bottom: 22px; }
|
|
801
|
+
[data-edrv-view] [data-edrv-hint="1"] { position: absolute; left: 0; right: 0; bottom: 0; box-sizing: border-box; height: 22px; padding: 2px 8px; font-size: 11px; line-height: 18px; color: var(--dsw-alias-label-tertiary, #8a949b); background: var(--dsw-alias-bg-overlay, #ffffff); border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }
|
package/src/client/svnActions.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { SvnActionDef } from '../shared/svnActions.js'
|
|
13
13
|
import { svnCleanup } from './svnLog.js'
|
|
14
|
-
import { refreshSvnChanges, svnAdd, svnDiffBase, svnRevert, svnTortoise, svnUpdate } from './svnStatus.js'
|
|
14
|
+
import { refreshSvnChanges, svnAdd, svnDiffBase, svnPatchText, svnRevert, svnTortoise, svnUpdate } from './svnStatus.js'
|
|
15
15
|
|
|
16
16
|
/** 动作执行上下文(各入口把自身能力投影进来)。 */
|
|
17
17
|
export interface SvnActionRunCtx {
|
|
@@ -29,6 +29,10 @@ export interface SvnActionRunCtx {
|
|
|
29
29
|
openSvnDiffRev?: (path: string, revision: number) => void
|
|
30
30
|
/** 打开日志弹窗(log 动作)。 */
|
|
31
31
|
openSvnLog?: (path?: string) => void
|
|
32
|
+
/** 打开补丁对话框(create-patch 动作;执行器完成 RPC 后把载荷交回 UI)。 */
|
|
33
|
+
openPatchDialog?: (payload: { path: string; text: string; truncated: boolean; binary: boolean }) => void
|
|
34
|
+
/** 打开目录对比对话框(diff-summarize 动作;目标目录相对路径,'' = 工作副本根)。 */
|
|
35
|
+
openSumDialog?: (path: string) => void
|
|
32
36
|
/** 重查变更清单(写操作后)。 */
|
|
33
37
|
refreshChanges?: () => void
|
|
34
38
|
/** 就地打开文件(日志面板点击变更文件)。 */
|
|
@@ -92,6 +96,19 @@ export const SVN_ACTION_RUNNERS: Record<string, SvnActionRunner> = {
|
|
|
92
96
|
}, log: (ctx) => {
|
|
93
97
|
ctx.openSvnLog?.(ctx.path || undefined)
|
|
94
98
|
},
|
|
99
|
+
// W2-1:补丁生成(只读,仅生成不落盘应用);RPC 后把载荷交给 UI 对话框
|
|
100
|
+
'create-patch': (ctx) => {
|
|
101
|
+
if (!ctx.path) { ctx.notify?.('无活动文件'); return }
|
|
102
|
+
const target = ctx.path
|
|
103
|
+
void svnPatchText(ctx.sessionId, target).then((outcome) => {
|
|
104
|
+
if (!outcome.ok || typeof outcome.text !== 'string') { ctx.notify?.(outcome.message); return }
|
|
105
|
+
ctx.openPatchDialog?.({ path: target, text: outcome.text, truncated: outcome.truncated === true, binary: outcome.binary === true })
|
|
106
|
+
})
|
|
107
|
+
},
|
|
108
|
+
// W2-2:目录对比(RPC 在对话框内做,执行器只负责打开并传入默认目标目录)
|
|
109
|
+
'diff-summarize': (ctx) => {
|
|
110
|
+
ctx.openSumDialog?.(ctx.path ?? '')
|
|
111
|
+
},
|
|
95
112
|
cleanup: (ctx) => {
|
|
96
113
|
const options = ctx.cleanupOptions ?? {}
|
|
97
114
|
const destructive = options.removeUnversioned === true || options.removeIgnored === true
|
package/src/client/svnLog.ts
CHANGED
|
@@ -258,7 +258,7 @@ export async function svnDiffRev(sessionId: string | undefined, path: string, re
|
|
|
258
258
|
try {
|
|
259
259
|
const res = await rpc('svn.diffRev', { sessionId, path, revision })
|
|
260
260
|
if (!res.ok) return { ok: false, left: null, right: null, error: res.error }
|
|
261
|
-
return { ok: true, left: res.left ?? null, right: res.right ?? null, reason: res.reason, error: res.error }
|
|
261
|
+
return { ok: true, left: res.left ?? null, right: res.right ?? null, reason: res.reason, error: res.error, binary: res.binary === true, encodingHint: res.encodingHint === true }
|
|
262
262
|
} catch (error) {
|
|
263
263
|
return { ok: false, left: null, right: null, error: String(error) }
|
|
264
264
|
}
|
|
@@ -278,7 +278,7 @@ export async function svnDiffPair(sessionId: string | undefined, path: string, r
|
|
|
278
278
|
try {
|
|
279
279
|
const res = await rpc('svn.diffPair', { sessionId, path, revA, revB })
|
|
280
280
|
if (!res.ok) return { ok: false, left: null, right: null, error: res.error }
|
|
281
|
-
return { ok: true, left: res.left ?? null, right: res.right ?? null, reason: res.reason, error: res.error }
|
|
281
|
+
return { ok: true, left: res.left ?? null, right: res.right ?? null, reason: res.reason, error: res.error, binary: res.binary === true, encodingHint: res.encodingHint === true }
|
|
282
282
|
} catch (error) {
|
|
283
283
|
return { ok: false, left: null, right: null, error: String(error) }
|
|
284
284
|
}
|
|
@@ -297,7 +297,7 @@ export async function svnDiffWorking(sessionId: string | undefined, path: string
|
|
|
297
297
|
try {
|
|
298
298
|
const res = await rpc('svn.diffWorking', { sessionId, path, revision })
|
|
299
299
|
if (!res.ok) return { ok: false, left: null, right: null, error: res.error }
|
|
300
|
-
return { ok: true, left: res.left ?? null, right: res.right ?? null, reason: res.reason, error: res.error }
|
|
300
|
+
return { ok: true, left: res.left ?? null, right: res.right ?? null, reason: res.reason, error: res.error, binary: res.binary === true, encodingHint: res.encodingHint === true }
|
|
301
301
|
} catch (error) {
|
|
302
302
|
return { ok: false, left: null, right: null, error: String(error) }
|
|
303
303
|
}
|
package/src/client/svnStatus.ts
CHANGED
|
@@ -211,6 +211,10 @@ export interface SvnDiffBaseOutcome {
|
|
|
211
211
|
reason?: string
|
|
212
212
|
/** 失败提示文案(ok=false,或 ok=true 时的补充说明)。 */
|
|
213
213
|
message: string
|
|
214
|
+
/** W2-6 护栏:内容含 NUL(二进制)。 */
|
|
215
|
+
binary?: boolean
|
|
216
|
+
/** W2-6 护栏:内容可能非 UTF-8。 */
|
|
217
|
+
encodingHint?: boolean
|
|
214
218
|
}
|
|
215
219
|
|
|
216
220
|
/**
|
|
@@ -228,9 +232,9 @@ export async function svnDiffBase(sessionId: string | undefined, path: string):
|
|
|
228
232
|
const message = res.reason === 'no-pristine'
|
|
229
233
|
? '该文件尚无基线版本(未提交的新增文件)'
|
|
230
234
|
: '读取基线失败' + (res.error ? ':' + res.error : '')
|
|
231
|
-
return { ok: true, base: null, working: res.working, message }
|
|
235
|
+
return { ok: true, base: null, working: res.working, message, binary: res.binary === true, encodingHint: res.encodingHint === true }
|
|
232
236
|
}
|
|
233
|
-
return { ok: true, base: res.base, working: res.working, message: '已打开基线差异' }
|
|
237
|
+
return { ok: true, base: res.base, working: res.working, message: '已打开基线差异', binary: res.binary === true, encodingHint: res.encodingHint === true }
|
|
234
238
|
} catch (error) {
|
|
235
239
|
return { ok: false, base: null, working: '', message: '读取基线失败:' + String(error) }
|
|
236
240
|
}
|
|
@@ -270,6 +274,140 @@ export async function svnAdd(sessionId: string | undefined, paths: string[]): Pr
|
|
|
270
274
|
}
|
|
271
275
|
}
|
|
272
276
|
|
|
277
|
+
// --region W2 差异管理补全(补丁/目录对比/远端检查/冲突/配对)
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* 生成统一格式补丁文本(W2-1,只读不落盘)。
|
|
281
|
+
* @author ddj 2026年09月20号
|
|
282
|
+
* @param sessionId 会话 id
|
|
283
|
+
* @param path 工作区相对路径(文件/目录)
|
|
284
|
+
* @param opts 扩展选项(空白语义/EOL/上下文行数;缺省 = svn 默认)
|
|
285
|
+
* @returns 补丁文本 + 截断/二进制标记,或错误文案
|
|
286
|
+
*/
|
|
287
|
+
export async function svnPatchText(
|
|
288
|
+
sessionId: string | undefined,
|
|
289
|
+
path: string,
|
|
290
|
+
opts: { whitespace?: 'none' | 'b' | 'w'; ignoreEol?: boolean; unified?: number } = {},
|
|
291
|
+
): Promise<{ ok: boolean; text?: string; truncated?: boolean; binary?: boolean; message: string }> {
|
|
292
|
+
try {
|
|
293
|
+
const res = await rpc('svn.patchText', { sessionId, path, ...opts })
|
|
294
|
+
if (!res.ok) return { ok: false, message: '生成补丁失败:' + res.error }
|
|
295
|
+
return { ok: true, text: res.text, truncated: res.truncated, binary: res.binary, message: '已生成补丁' }
|
|
296
|
+
} catch (error) {
|
|
297
|
+
return { ok: false, message: '生成补丁失败:' + String(error) }
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 目录对比(W2-2,任意两修订的 summarize 变更列表)。
|
|
303
|
+
* @author ddj 2026年09月20号
|
|
304
|
+
* @param sessionId 会话 id
|
|
305
|
+
* @param revA 起始修订(较早)
|
|
306
|
+
* @param revB 结束修订(较晚)
|
|
307
|
+
* @param path 目标相对路径('' = 工作副本根)
|
|
308
|
+
* @returns 变更条目或错误文案
|
|
309
|
+
*/
|
|
310
|
+
export async function svnDiffSum(
|
|
311
|
+
sessionId: string | undefined,
|
|
312
|
+
revA: number,
|
|
313
|
+
revB: number,
|
|
314
|
+
path = '',
|
|
315
|
+
): Promise<{ ok: boolean; entries?: Array<{ path: string; item: string; kind: string; props: string }>; message: string }> {
|
|
316
|
+
try {
|
|
317
|
+
const res = await rpc('svn.diffSum', { sessionId, path, revA, revB })
|
|
318
|
+
if (!res.ok) return { ok: false, message: '目录对比失败:' + res.error }
|
|
319
|
+
return { ok: true, entries: res.entries, message: '已读取 ' + res.entries.length + ' 条变更' }
|
|
320
|
+
} catch (error) {
|
|
321
|
+
return { ok: false, message: '目录对比失败:' + String(error) }
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 远端更新检查(W2-3,`svn status -u`;host 侧 15s 短超时,离线/超时走本函数错误分支)。
|
|
327
|
+
* @author ddj 2026年09月20号
|
|
328
|
+
* @param sessionId 会话 id
|
|
329
|
+
* @param path 目标相对路径('' = 工作副本根)
|
|
330
|
+
* @returns 落后条目 + against 修订,或错误文案
|
|
331
|
+
*/
|
|
332
|
+
export async function svnRemoteStatus(
|
|
333
|
+
sessionId: string | undefined,
|
|
334
|
+
path = '',
|
|
335
|
+
): Promise<{ ok: boolean; outdated?: Array<{ path: string; item: string }>; againstRev?: number | null; message: string }> {
|
|
336
|
+
try {
|
|
337
|
+
const res = await rpc('svn.remoteStatus', { sessionId, path })
|
|
338
|
+
if (!res.ok) return { ok: false, message: '远端检查失败:' + res.error }
|
|
339
|
+
return { ok: true, outdated: res.outdated, againstRev: res.againstRev, message: '远端检查完成' }
|
|
340
|
+
} catch (error) {
|
|
341
|
+
return { ok: false, message: '远端检查失败:' + String(error) }
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* 扫描冲突副本文件(W2-4,`.mine`/`.working`/`.rN`,纯本地只读)。
|
|
347
|
+
* @author ddj 2026年09月20号
|
|
348
|
+
* @param sessionId 会话 id
|
|
349
|
+
* @param path 冲突文件的工作区相对路径
|
|
350
|
+
* @returns 副本清单或错误文案
|
|
351
|
+
*/
|
|
352
|
+
export async function svnConflictArtifacts(
|
|
353
|
+
sessionId: string | undefined,
|
|
354
|
+
path: string,
|
|
355
|
+
): Promise<{ ok: boolean; artifacts?: Array<{ path: string; name: string; kind: 'mine' | 'working' | 'rev'; rev?: number }>; message: string }> {
|
|
356
|
+
try {
|
|
357
|
+
const res = await rpc('svn.conflictArtifacts', { sessionId, path })
|
|
358
|
+
if (!res.ok) return { ok: false, message: '扫描冲突副本失败:' + res.error }
|
|
359
|
+
return { ok: true, artifacts: res.artifacts, message: '发现 ' + res.artifacts.length + ' 个冲突副本' }
|
|
360
|
+
} catch (error) {
|
|
361
|
+
return { ok: false, message: '扫描冲突副本失败:' + String(error) }
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* 双侧本地文件差异(W2-4,冲突副本 `.rN` ↔ `.mine` 对比)。
|
|
367
|
+
* @author ddj 2026年09月20号
|
|
368
|
+
* @param sessionId 会话 id
|
|
369
|
+
* @param left 左侧工作区相对路径
|
|
370
|
+
* @param right 右侧工作区相对路径
|
|
371
|
+
* @returns 双侧内容(含护栏标记)或错误文案
|
|
372
|
+
*/
|
|
373
|
+
export async function svnDiffLocalPair(
|
|
374
|
+
sessionId: string | undefined,
|
|
375
|
+
left: string,
|
|
376
|
+
right: string,
|
|
377
|
+
): Promise<{ ok: boolean; leftText?: string | null; rightText?: string | null; binary?: boolean; encodingHint?: boolean; message: string }> {
|
|
378
|
+
try {
|
|
379
|
+
const res = await rpc('svn.diffLocalPair', { sessionId, left, right })
|
|
380
|
+
if (!res.ok) return { ok: false, message: '读取冲突副本失败:' + res.error }
|
|
381
|
+
return { ok: true, leftText: res.left, rightText: res.right, binary: res.binary === true, encodingHint: res.encodingHint === true, message: '已读取双侧内容' }
|
|
382
|
+
} catch (error) {
|
|
383
|
+
return { ok: false, message: '读取冲突副本失败:' + String(error) }
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* 批量取文件大小(W2-5,改名配对启发式用;未知/失败按 null)。
|
|
389
|
+
* @author ddj 2026年09月20号
|
|
390
|
+
* @param sessionId 会话 id
|
|
391
|
+
* @param paths 工作区相对路径列表(host 上限 200)
|
|
392
|
+
* @returns 路径 → 大小表
|
|
393
|
+
*/
|
|
394
|
+
export async function svnFileSizes(
|
|
395
|
+
sessionId: string | undefined,
|
|
396
|
+
paths: string[],
|
|
397
|
+
): Promise<Record<string, number | null>> {
|
|
398
|
+
try {
|
|
399
|
+
const res = await rpc('svn.fileSizes', { sessionId, paths })
|
|
400
|
+
if (!res.ok) return {}
|
|
401
|
+
const table: Record<string, number | null> = {}
|
|
402
|
+
for (const item of res.sizes) table[item.path] = item.size
|
|
403
|
+
return table
|
|
404
|
+
} catch (error) {
|
|
405
|
+
return {}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// --endregion
|
|
410
|
+
|
|
273
411
|
/**
|
|
274
412
|
* Monaco 右键菜单 SVN 动作条目(编辑区动态注册用)。
|
|
275
413
|
*
|
package/src/client/tabActions.ts
CHANGED
|
@@ -255,6 +255,60 @@ export function pickActive(tabs: TabLike[], wanted: unknown): string | null {
|
|
|
255
255
|
}
|
|
256
256
|
// --endregion
|
|
257
257
|
|
|
258
|
+
// --region 上限淘汰
|
|
259
|
+
|
|
260
|
+
/** 淘汰判定输入:活动页签、使用序、额外保留集。 */
|
|
261
|
+
export interface EvictOptions {
|
|
262
|
+
/** 当前活动页签路径(**绝不被淘汰**)。 */
|
|
263
|
+
active: string | null
|
|
264
|
+
/** 路径 → 最近使用序号(单调递增;越大越新)。缺记录按最久未用处理。 */
|
|
265
|
+
used: Record<string, number>
|
|
266
|
+
/** 额外保留集(调用方按需保护,如「恢复中刚载入的页签」)。 */
|
|
267
|
+
keep?: ReadonlySet<string>
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* 计算超限时应淘汰的页签路径(最久未使用者优先)。
|
|
272
|
+
*
|
|
273
|
+
* 保护规则(不满足则允许溢出而非强关,调用方据此保持「溢出态」:
|
|
274
|
+
* 用户显式固定过、或正在查看的页签,都不该被上限悄悄夺走):
|
|
275
|
+
* - `limit <= 0` → 上限关闭,返回空数组;
|
|
276
|
+
* - 未超限 → 返回空数组;
|
|
277
|
+
* - 固定页签(`pinned === true`)不是候选;
|
|
278
|
+
* - 活动页签不是候选;
|
|
279
|
+
* - `keep` 命中的路径不是候选。
|
|
280
|
+
*
|
|
281
|
+
* 脏页签**仍是候选**:其未保存内容的落盘由调用方在关闭前完成(EditorView 走
|
|
282
|
+
* closeTabs → persistDirty),本函数不感知磁盘,避免把 IO 语义掺进纯判定。
|
|
283
|
+
*
|
|
284
|
+
* @author ddj 2026年09月18号
|
|
285
|
+
* @param tabs 当前页签
|
|
286
|
+
* @param limit 页签上限(0 = 不限制)
|
|
287
|
+
* @param options 活动页签、使用序与额外保留集
|
|
288
|
+
* @returns 待淘汰路径(按最久未用在前);无需淘汰时为 `[]`
|
|
289
|
+
*/
|
|
290
|
+
export function evictPlan(tabs: TabLike[], limit: number, options: EvictOptions): string[] {
|
|
291
|
+
if (!Array.isArray(tabs) || !Number.isFinite(limit) || limit <= 0) return []
|
|
292
|
+
const excess = tabs.length - Math.floor(limit)
|
|
293
|
+
if (excess <= 0) return []
|
|
294
|
+
const active = options?.active ?? null
|
|
295
|
+
const used = options?.used ?? {}
|
|
296
|
+
const keep = options?.keep
|
|
297
|
+
const candidates: Array<{ path: string; at: number; at0: number }> = []
|
|
298
|
+
for (let i = 0; i < tabs.length; i += 1) {
|
|
299
|
+
const tab = tabs[i]
|
|
300
|
+
if (tab.pinned === true) continue
|
|
301
|
+
if (tab.path === active) continue
|
|
302
|
+
if (keep?.has(tab.path) === true) continue
|
|
303
|
+
const order = used[tab.path]
|
|
304
|
+
candidates.push({ path: tab.path, at: Number.isFinite(order) ? order : -1, at0: i })
|
|
305
|
+
}
|
|
306
|
+
// 使用序升序 = 最久未用在前;同序(含全部无记录)按页签原下标,保证确定性
|
|
307
|
+
candidates.sort((a, b) => (a.at === b.at ? a.at0 - b.at0 : a.at - b.at))
|
|
308
|
+
return candidates.slice(0, excess).map((item) => item.path)
|
|
309
|
+
}
|
|
310
|
+
// --endregion
|
|
311
|
+
|
|
258
312
|
// --region 路径推导
|
|
259
313
|
|
|
260
314
|
/** Windows 盘符 / UNC / POSIX 根:视为工作区外的绝对路径。 */
|