dsh-vscode-mode 0.14.1 → 0.14.2
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 +10 -9
- package/lib/client.js +33 -7
- package/lib/client.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/sidebar/panels/SearchPanel.ts +37 -9
- package/src/search/content.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vscode-mode",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "DSH 上的类 VSCode 编码体验:Monaco 编辑器(文件页签/QuickOpen/状态栏,可驻 DSH 0.1.5+ 官方右侧 Sidebar 与对话同屏)+ 命令栏(Ctrl+Shift+P)与可扩展指令系统 + VS Code 兼容代码片段(.code-snippets 全局/项目,IntelliSense 展开)+ Agent 编辑差异审查(整文件差异/采纳/拒绝/归档/回滚)+ SVN 集成(侧栏变更面板/日志弹窗/并排差异)+ 语言服务器(LSP)智能(跳转定义/引用/hover/大纲 + VSIX 扩展市场安装),状态持久化到工作区旁车,界面状态(页签/光标/侧栏/编辑 Tab/导航历史)镜像到 DSH home 跨重启恢复",
|
|
5
5
|
"icon": "assets/icon.svg",
|
|
6
6
|
"keywords": [
|
|
@@ -26,6 +26,19 @@ export function splitGlobs(text) {
|
|
|
26
26
|
return String(text ?? '').split(',').map((item) => item.trim()).filter(Boolean)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* 计算包含文件范围;仅当前文件已开启但无活动文件时返回 null,禁止退化为全工作区搜索。
|
|
31
|
+
* @author ddj 2026年09月29号
|
|
32
|
+
* @param onlyActive 是否仅搜索当前文件
|
|
33
|
+
* @param activePath 当前文件路径
|
|
34
|
+
* @param includeText 用户输入的包含模式
|
|
35
|
+
* @returns 包含 glob;null 表示当前搜索范围不可用
|
|
36
|
+
*/
|
|
37
|
+
export function searchIncludes(onlyActive: boolean, activePath: string | null, includeText: string): string[] | null {
|
|
38
|
+
if (!onlyActive) return splitGlobs(includeText)
|
|
39
|
+
return activePath ? [activePath] : null
|
|
40
|
+
}
|
|
41
|
+
|
|
29
42
|
/**
|
|
30
43
|
* 读取按作用域持久化的搜索条件(损坏/缺失返回 null)。
|
|
31
44
|
* 抽成函数而非内联:恢复 effect 内有两处消费者(填状态、同步 requestRef),
|
|
@@ -93,7 +106,7 @@ export function SearchPanel(props) {
|
|
|
93
106
|
const requestRef = React.useRef({ matchCase, wholeWord, regex, include: [], exclude: [] })
|
|
94
107
|
requestRef.current = {
|
|
95
108
|
matchCase, wholeWord, regex,
|
|
96
|
-
include: onlyActive
|
|
109
|
+
include: searchIncludes(onlyActive, activePath, includeText),
|
|
97
110
|
exclude: excludeOn ? splitGlobs(excludeText) : [],
|
|
98
111
|
}
|
|
99
112
|
|
|
@@ -117,9 +130,8 @@ export function SearchPanel(props) {
|
|
|
117
130
|
matchCase: saved.matchCase === true,
|
|
118
131
|
wholeWord: saved.wholeWord === true,
|
|
119
132
|
regex: saved.regex === true,
|
|
120
|
-
include: saved.onlyActive === true
|
|
121
|
-
|
|
122
|
-
: splitGlobs(typeof saved.includeText === 'string' ? saved.includeText : ''),
|
|
133
|
+
include: searchIncludes(saved.onlyActive === true, activePathRef.current,
|
|
134
|
+
typeof saved.includeText === 'string' ? saved.includeText : ''),
|
|
123
135
|
exclude: saved.excludeOn === true
|
|
124
136
|
? splitGlobs(typeof saved.excludeText === 'string' ? saved.excludeText : '')
|
|
125
137
|
: [],
|
|
@@ -146,6 +158,14 @@ export function SearchPanel(props) {
|
|
|
146
158
|
const clean = String(text ?? '').trim()
|
|
147
159
|
if (clean.length < 2) { setMatches(null); setStatus('idle'); return }
|
|
148
160
|
const req = requestRef.current
|
|
161
|
+
if (req.include === null) {
|
|
162
|
+
setMatches([])
|
|
163
|
+
setTruncated(false)
|
|
164
|
+
setError('')
|
|
165
|
+
setWarning('')
|
|
166
|
+
setStatus('done')
|
|
167
|
+
return
|
|
168
|
+
}
|
|
149
169
|
setStatus('searching')
|
|
150
170
|
setError('')
|
|
151
171
|
setWarning('')
|
|
@@ -195,13 +215,13 @@ export function SearchPanel(props) {
|
|
|
195
215
|
}
|
|
196
216
|
|
|
197
217
|
/**
|
|
198
|
-
*
|
|
218
|
+
* 立即刷新搜索(短查询清空旧结果);先同步 ref 避免本次调用读到旧请求参数。
|
|
199
219
|
* @param next 新的请求参数字段
|
|
200
220
|
*/
|
|
201
221
|
const rerun = (next) => {
|
|
202
222
|
requestRef.current = Object.assign({}, requestRef.current, next)
|
|
203
223
|
if (timerRef.current) clearTimeout(timerRef.current)
|
|
204
|
-
|
|
224
|
+
runSearch(queryRef.current)
|
|
205
225
|
}
|
|
206
226
|
|
|
207
227
|
/** 选项开关切换(大小写/全词/正则/书本/设置):更新状态 + 立即重搜。 */
|
|
@@ -215,11 +235,19 @@ export function SearchPanel(props) {
|
|
|
215
235
|
base[key] = value
|
|
216
236
|
rerun({
|
|
217
237
|
matchCase: base.matchCase, wholeWord: base.wholeWord, regex: base.regex,
|
|
218
|
-
include: base.onlyActive
|
|
238
|
+
include: searchIncludes(base.onlyActive, activePathRef.current, includeText),
|
|
219
239
|
exclude: base.excludeOn ? splitGlobs(excludeText) : [],
|
|
220
240
|
})
|
|
221
241
|
}
|
|
222
242
|
|
|
243
|
+
// 仅当前文件模式下切换/关闭活动文件,立即更新搜索范围并作废旧请求。
|
|
244
|
+
const previousPathRef = React.useRef(activePath)
|
|
245
|
+
React.useEffect(() => {
|
|
246
|
+
if (previousPathRef.current === activePath) return
|
|
247
|
+
previousPathRef.current = activePath
|
|
248
|
+
if (onlyActive) rerun({ include: searchIncludes(true, activePath, includeText) })
|
|
249
|
+
}, [activePath])
|
|
250
|
+
|
|
223
251
|
/**
|
|
224
252
|
* 取用一次性选区种子(需求 1:Ctrl+Shift+F 时把编辑器选中文本填入搜索框)。
|
|
225
253
|
* 有种子即填入并立即搜索(不再等 250ms 防抖),同时全选便于直接改写;无种子为 no-op。
|
|
@@ -307,8 +335,8 @@ export function SearchPanel(props) {
|
|
|
307
335
|
onChange: (e) => onChangeDebounced(setIncludeText, e.target.value),
|
|
308
336
|
}),
|
|
309
337
|
React.createElement('button', {
|
|
310
|
-
className: 'edrv-search-toggle' + (onlyActive ? ' on' : ''), title: activePath ? '仅在当前打开的文件中搜索' : '无打开的文件(需先打开文件)',
|
|
311
|
-
'aria-pressed': onlyActive, disabled: !activePath,
|
|
338
|
+
className: 'edrv-search-toggle' + (onlyActive ? ' on' : ''), title: activePath ? '仅在当前打开的文件中搜索' : onlyActive ? '无打开的文件(点击退出仅当前文件)' : '无打开的文件(需先打开文件)',
|
|
339
|
+
'aria-pressed': onlyActive, disabled: !activePath && !onlyActive,
|
|
312
340
|
onClick: () => toggleOpt('onlyActive', !onlyActive),
|
|
313
341
|
}, '📖')),
|
|
314
342
|
React.createElement('div', { className: 'edrv-search-row' },
|
package/src/search/content.ts
CHANGED
|
@@ -157,7 +157,7 @@ export function applyCaps(matches: ContentMatch[], maxMatches: number, maxFiles:
|
|
|
157
157
|
*/
|
|
158
158
|
export function contentArgv(binary: string, root: string, input: ContentSearchInput): string[] {
|
|
159
159
|
const argv = [binary, '--no-config', '--json', '--line-number', '--no-heading', '--color', 'never', '--hidden', '--no-ignore']
|
|
160
|
-
|
|
160
|
+
argv.push(input.matchCase === true ? '--case-sensitive' : '--ignore-case')
|
|
161
161
|
if (input.wholeWord === true) argv.push('--word-regexp')
|
|
162
162
|
if (input.regex !== true) argv.push('--fixed-strings')
|
|
163
163
|
for (const excluded of EXCLUDES) argv.push('--glob', '!**/' + excluded + '/**')
|