dsh-vscode-mode 0.1.49 → 0.1.51

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.
@@ -2,17 +2,38 @@
2
2
  /**
3
3
  * dsh-vscode-mode client — 「语言服务器」设置子 Tab。
4
4
  * 四视图:服务器(状态 + 每语言启用/命令/路径)|已安装(卸载/更新)|市场(Open VSX 搜索/安装 + 本地 vsix)|更新。
5
- * 状态经 edrv.lsp.status,配置经 edrv.lsp.configGet/Update,扩展经 edrv.lsp.ext.*。
6
- * 作者 ddj 2026-08-27
5
+ * 状态经 edrv.lsp.detect(检测结果驱动卡片动态显示),配置经 edrv.lsp.configGet/Update,扩展经 edrv.lsp.ext.*。
6
+ * 作者 ddj 2026-08-27 / 2026-09-03
7
7
  */
8
8
  import React from 'react'
9
9
  import { rpc } from '../rpc.js'
10
10
  import '../styles/mcp.css'
11
11
 
12
- const LANGUAGES = [
13
- { id: 'lua', label: 'Lua(EmmyLua / LuaLS)', hint: '优先使用已安装 EmmyLua;未安装时回退 LuaLS 或 PATH 中的 lua-language-server' },
14
- { id: 'csharp', label: 'C#(Roslyn / DotRush / OmniSharp)', hint: '优先自动发现 ms-dotnettools.csharp / DotRush 扩展(DotRush 需 .NET 10 运行时);也可手动指定' },
15
- ]
12
+ /** 支持语言的展示元数据(卡片显示由检测结果驱动;未知 id 走 langMeta 兜底)。 */
13
+ const LANG_META = {
14
+ lua: { label: 'Lua(EmmyLua / LuaLS)', hint: '优先使用已安装 EmmyLua;未安装时回退 LuaLS 或 PATH 中的 lua-language-server' },
15
+ csharp: { label: 'C#(Roslyn / DotRush / OmniSharp)', hint: '优先自动发现 ms-dotnettools.csharp / DotRush 扩展(DotRush 需 .NET 10 运行时);也可手动指定' },
16
+ }
17
+
18
+ /** 语言展示元数据(含未知 id 兜底)。 */
19
+ function langMeta(id) {
20
+ return LANG_META[id] || { label: id + '(语言服务器)', hint: '' }
21
+ }
22
+
23
+ /** 该语言是否已存配置(禁用开关或手动命令/路径)。 */
24
+ function hasStoredConfig(cfg) {
25
+ if (!cfg || typeof cfg !== 'object') return false
26
+ if (cfg.enabled === false) return true
27
+ return Boolean((cfg.command && cfg.command.trim()) || (cfg.path && cfg.path.trim()))
28
+ }
29
+
30
+ /** 该语言是否显示卡片:检测到可用 LSP、已存配置、或存在待安装的缺失环境。 */
31
+ function langVisible(status, config) {
32
+ if (!status) return false
33
+ if (status.source && status.source !== 'none') return true
34
+ if (hasStoredConfig(config[status.languageId])) return true
35
+ return Array.isArray(status.missingEnv) && status.missingEnv.length > 0
36
+ }
16
37
 
17
38
  const PHASE_LABEL = {
18
39
  idle: '未启动', starting: '启动中', ready: '就绪', indexing: '索引中', unavailable: '不可用', stopped: '已停止',
@@ -54,6 +75,11 @@ function LangCard({ lang, config, status, busy, envStates, onInstallEnv, onToggl
54
75
  status?.version ? ' · v' + status.version : '',
55
76
  (phase === 'ready' && status?.root) ? ' · ' + String(status.root).split(/[\\/]/).pop() : ''),
56
77
  status?.reason ? React.createElement('div', { className: 'vsm-mcp-error' }, status.reason) : null,
78
+ Array.isArray(status?.candidates) && status.candidates.length > 1
79
+ ? React.createElement('div', { className: 'vsm-lsp-hint' },
80
+ '候选(' + status.candidates.length + '):' + status.candidates.map((c) =>
81
+ c.name + (c.version ? ' v' + c.version : '') + (c.chosen ? '(当前)' : '')).join(' · '))
82
+ : null,
57
83
  Array.isArray(status?.missingEnv) && status.missingEnv.length
58
84
  ? React.createElement('div', { className: 'vsm-lsp-hint' },
59
85
  '缺少运行环境(一键安装后自动生效):',
@@ -125,10 +151,10 @@ export function LspSettings() {
125
151
  const [envLang, setEnvLang] = React.useState('')
126
152
 
127
153
  const refreshServers = React.useCallback(() => {
128
- Promise.all([rpc('edrv.lsp.configGet', {}), rpc('edrv.lsp.status', {})])
129
- .then(([cfg, st]) => {
154
+ Promise.all([rpc('edrv.lsp.configGet', {}), rpc('edrv.lsp.detect', {})])
155
+ .then(([cfg, det]) => {
130
156
  if (cfg?.ok) setConfig(cfg.config ?? {})
131
- if (st?.ok) setServers(st.servers ?? [])
157
+ if (det?.ok) setServers(det.servers ?? [])
132
158
  setError('')
133
159
  })
134
160
  .catch((e) => setError(String(e)))
@@ -282,6 +308,7 @@ export function LspSettings() {
282
308
  .then((res) => {
283
309
  if (!res?.ok) { setError(res?.error ?? '安装失败'); return }
284
310
  void refreshExt()
311
+ void refreshServers() // 新装 LSP 使对应语言卡片出现
285
312
  })
286
313
  .catch((e) => setError(String(e)))
287
314
  .finally(() => setBusy(''))
@@ -296,6 +323,7 @@ export function LspSettings() {
296
323
  if (!res?.ok) { setError(res?.error ?? '安装失败'); return }
297
324
  setVsixPath('')
298
325
  void refreshExt()
326
+ void refreshServers() // 新装 LSP 使对应语言卡片出现
299
327
  })
300
328
  .catch((e) => setError(String(e)))
301
329
  .finally(() => setBusy(''))
@@ -309,6 +337,7 @@ export function LspSettings() {
309
337
  .then((res) => {
310
338
  if (!res?.ok) setError(res?.error ?? '卸载失败')
311
339
  void refreshExt()
340
+ void refreshServers() // 卸载后对应语言卡片按最新检测隐匿
312
341
  })
313
342
  .catch((e) => setError(String(e)))
314
343
  .finally(() => setBusy(''))
@@ -321,6 +350,7 @@ export function LspSettings() {
321
350
  .then((res) => {
322
351
  if (!res?.ok) setError(res?.error ?? '更新失败')
323
352
  void refreshExt()
353
+ void refreshServers() // 更新后选用版本随之变化
324
354
  })
325
355
  .catch((e) => setError(String(e)))
326
356
  .finally(() => setBusy(''))
@@ -344,14 +374,24 @@ export function LspSettings() {
344
374
 
345
375
  let body = null
346
376
  if (tab === 'servers') {
377
+ // 动态显示:检测结果(detect 全量)驱动卡片可见性;未检出语言灰字列出
378
+ const detected = Array.isArray(servers) ? servers : []
379
+ const visible = detected.filter((s) => langVisible(s, config))
380
+ const hidden = detected.filter((s) => !visible.includes(s))
347
381
  body = panel('语言服务器', React.createElement(React.Fragment, null,
348
- LANGUAGES.map((lang) => React.createElement(LangCard, {
349
- key: lang.id, lang,
350
- config: config[lang.id] ?? {},
351
- status: servers.find((s) => s.languageId === lang.id),
352
- busy, envStates, onInstallEnv: installEnv, onToggle: toggleLang, onSave: saveLang, onRedetect: redetectLang,
353
- })),
354
- React.createElement('div', { className: 'vsm-lsp-hint' }, '提示:打开 Lua/C# 文件即自动(惰性)启动对应语言服务器;未配置时编辑器功能不受影响,大纲回退内置解析。')))
382
+ visible.length ? visible.map((status) => {
383
+ const meta = langMeta(status.languageId)
384
+ return React.createElement(LangCard, {
385
+ key: status.languageId, lang: { id: status.languageId, label: meta.label, hint: meta.hint },
386
+ config: config[status.languageId] ?? {},
387
+ status,
388
+ busy, envStates, onInstallEnv: installEnv, onToggle: toggleLang, onSave: saveLang, onRedetect: redetectLang,
389
+ })
390
+ }) : React.createElement('div', { className: 'vsm-mcp-empty' },
391
+ '未检测到已安装的语言服务器;可切换到「市场」搜索安装(如 sumneko.lua),或安装本地 .vsix 后刷新。'),
392
+ hidden.length ? React.createElement('div', { className: 'vsm-lsp-hint' },
393
+ '未检测到:' + hidden.map((s) => langMeta(s.languageId).label).join('、') + ' —— 可在「市场」安装后刷新。') : null,
394
+ React.createElement('div', { className: 'vsm-lsp-hint' }, '提示:打开对应语言的文件即自动(惰性)启动语言服务器;未配置时编辑器功能不受影响,大纲回退内置解析。')))
355
395
  } else if (tab === 'installed') {
356
396
  body = panel('已安装扩展', React.createElement(React.Fragment, null,
357
397
  React.createElement('div', { className: 'vsm-lsp-hint' }, '已安装的语言服务器扩展(存于 ~/.dsh/dsh-vscode-mode/extensions/)。语言服务器被自动发现为「扩展」源。'),
@@ -12,6 +12,7 @@ import { dirname, join, sep } from 'node:path'
12
12
  import { isAbsolute } from 'node:path'
13
13
  import { extensionsRoot } from './extmgr.js'
14
14
  import { dshHome, lspSpecCacheFile } from '../paths.js'
15
+ import type { LspCandidate } from '../shared/lsp.js'
15
16
 
16
17
  /** provider 来源标记(与 shared/lsp.ts 的 status.source 一致)。 */
17
18
  export type LspProviderKind = 'extension' | 'discover' | 'manual' | 'none'
@@ -237,26 +238,33 @@ export function candidateEmmyLua(home = dshHome()): { path: string; version?: st
237
238
  return candidates[0] ?? null
238
239
  }
239
240
 
240
- /** LuaLS 自动发现候选(VSCode 扩展目录 + extmgr 目录下 sumneko.lua-* 的 server/bin 可执行)。 */
241
- export function candidateLuaServers(home = dshHome()): string[] {
241
+ /**
242
+ * LuaLS 自动发现候选(VSCode 扩展目录 + extmgr 目录下 sumneko.lua-* 的 server/bin 可执行)。
243
+ * 多版本并存时按清单版本降序、路径升序排序(与 EmmyLua/DotRush 发现的排序策略一致)。
244
+ * @author ddj 2026年09月03号
245
+ * @param home DSH home(缺省真实;测试可注入临时目录)
246
+ * @returns 排序后的候选列表(首个为建议选用者)
247
+ */
248
+ export function candidateLuaServers(home = dshHome()): { path: string; version?: string }[] {
242
249
  const plat = platformServerDir()
243
250
  const suffix = exeSuffix()
244
- const out: string[] = []
251
+ const out: { path: string; version?: string }[] = []
245
252
  for (const dir of vscodeExtensionsDirs(home)) {
246
253
  for (const extDir of listMatchingDirs(dir, 'sumneko.lua-')) {
247
254
  const binDir = join(extDir, 'server', 'bin')
248
255
  const binName = 'lua-language-server' + suffix
249
256
  // 新布局(3.x 平台变体 vsix):server/bin/xxx.exe;旧布局:server/bin/<平台>/xxx.exe
250
257
  const bin = existsSync(join(binDir, binName)) ? join(binDir, binName) : join(binDir, plat, binName)
251
- if (existsSync(bin) && !out.includes(bin)) out.push(bin)
258
+ if (existsSync(bin) && !out.some((c) => c.path === bin)) out.push({ path: bin, version: manifestVersionOf(extDir) })
252
259
  }
253
260
  }
254
- return out
261
+ return out.sort((a, b) => (b.version ?? '').localeCompare(a.version ?? '', undefined, { numeric: true }) || a.path.localeCompare(b.path))
255
262
  }
256
263
 
257
- /** C# 服务器发现结果(ms-Roslyn / DotRush / OmniSharp + 系统 dotnet)。 */
264
+ /** C# 服务器发现结果(ms-Roslyn / DotRush / OmniSharp + 系统 dotnet;各取清单版本最高者)。 */
258
265
  export interface CSharpCandidates {
259
266
  roslynDll?: string
267
+ roslynVersion?: string
260
268
  dotrushDll?: string
261
269
  dotrushVersion?: string
262
270
  omnisharpExe?: string
@@ -266,31 +274,44 @@ export interface CSharpCandidates {
266
274
  /**
267
275
  * C# 自动发现候选:ms-dotnettools.csharp 的 Roslyn dll(需 dotnet)/ OmniSharp 可执行,
268
276
  * 以及 DotRush 扩展自带服务器(extension/bin/LanguageServer/DotRush.dll,需对应 .NET 运行时)。
277
+ * 同类多版本并存时取清单版本最高者(与 EmmyLua/DotRush 发现的排序策略一致)。
269
278
  * @author ddj 2026年09月03号
270
279
  * @param home DSH home(缺省真实;测试可注入临时目录)
271
280
  * @returns 发现结果
272
281
  */
273
282
  export function candidateCSharpServers(home = dshHome()): CSharpCandidates {
274
283
  const out: CSharpCandidates = {}
275
- const dotrushHits: { dll: string; version?: string }[] = []
284
+ /** 命中收集 → 清单版本降序取最高(平级按路径稳定排序)。 */
285
+ const bestOf = (hits: { entry: string; version?: string }[]): { entry: string; version?: string } | null =>
286
+ hits.sort((a, b) => (b.version ?? '').localeCompare(a.version ?? '', undefined, { numeric: true }) || a.entry.localeCompare(b.entry))[0] ?? null
287
+ const roslynHits: { entry: string; version?: string }[] = []
288
+ const omniHits: { entry: string; version?: string }[] = []
289
+ const dotrushHits: { entry: string; version?: string }[] = []
276
290
  for (const dir of vscodeExtensionsDirs(home)) {
277
291
  for (const extDir of listMatchingDirs(dir, 'ms-dotnettools.csharp-')) {
292
+ const version = manifestVersionOf(extDir)
278
293
  const roslyn = join(extDir, '.roslyn', 'Microsoft.CodeAnalysis.LanguageServer.dll')
279
- if (!out.roslynDll && existsSync(roslyn)) out.roslynDll = roslyn
294
+ if (existsSync(roslyn)) roslynHits.push({ entry: roslyn, version })
280
295
  const omni = join(extDir, '.omnisharp', 'OmniSharp' + exeSuffix())
281
- if (!out.omnisharpExe && existsSync(omni)) out.omnisharpExe = omni
296
+ if (existsSync(omni)) omniHits.push({ entry: omni, version })
282
297
  }
283
298
  for (const extDir of listMatchingDirs(dir, 'nromanov.dotrush-')) {
284
299
  const dll = join(extDir, 'extension', 'bin', 'LanguageServer', 'DotRush.dll')
285
300
  if (!existsSync(dll)) continue
286
- dotrushHits.push({ dll, version: manifestVersionOf(extDir) })
301
+ dotrushHits.push({ entry: dll, version: manifestVersionOf(extDir) })
287
302
  }
288
303
  }
289
- // 多版本并存时取清单版本最高者(与 EmmyLua 发现的排序策略一致)
290
- const best = dotrushHits.sort((a, b) => (b.version ?? '').localeCompare(a.version ?? '', undefined, { numeric: true }) || a.dll.localeCompare(b.dll))[0]
291
- if (best) {
292
- out.dotrushDll = best.dll
293
- out.dotrushVersion = best.version
304
+ const roslynBest = bestOf(roslynHits)
305
+ if (roslynBest) {
306
+ out.roslynDll = roslynBest.entry
307
+ out.roslynVersion = roslynBest.version
308
+ }
309
+ const omniBest = bestOf(omniHits)
310
+ if (omniBest) out.omnisharpExe = omniBest.entry
311
+ const dotrushBest = bestOf(dotrushHits)
312
+ if (dotrushBest) {
313
+ out.dotrushDll = dotrushBest.entry
314
+ out.dotrushVersion = dotrushBest.version
294
315
  }
295
316
  const dotnet = findDotnet()
296
317
  if (dotnet) out.dotnet = dotnet
@@ -367,7 +388,8 @@ export function resolveLuaProvider(manual?: { command?: string; path?: string },
367
388
  const emmy = candidateEmmyLua(h)
368
389
  if (emmy) return { ...base, kind: 'extension', argv: [emmy.path], ready: true, version: emmy.version, providerName: 'EmmyLua' }
369
390
  const candidates = candidateLuaServers(h)
370
- if (candidates.length) return { ...base, kind: 'discover', argv: [candidates[0]], cwd: undefined, ready: true }
391
+ const best = candidates[0]
392
+ if (best) return { ...base, kind: 'discover', argv: [best.path], cwd: undefined, ready: true, version: best.version, providerName: 'LuaLS' }
371
393
  const inPath = findInPath('lua-language-server' + exeSuffix())
372
394
  if (inPath) return { ...base, kind: 'discover', argv: [inPath], ready: true }
373
395
  return { ...base, kind: 'none', argv: [], ready: false, reason: manualResolved.reason ?? '未发现 lua-language-server(可安装 LuaLS 或手动指定路径)' }
@@ -429,6 +451,44 @@ export const PROVIDER_RESOLVERS: Record<string, ProviderResolver> = {
429
451
  csharp: resolveCSharpProvider,
430
452
  }
431
453
 
454
+ /**
455
+ * 汇总某语言当前检测到的候选服务器(每类服务器一条、取该类最高版本;设置页展示用)。
456
+ * chosen 以候选路径命中 spec.argv 判定;未选用(kind=none/manual 不匹配)时不标记。
457
+ * @author ddj 2026年09月03号
458
+ * @param languageId 语言 id
459
+ * @param spec 已解析的 provider 规格
460
+ * @param home DSH home(缺省真实;测试可注入临时目录)
461
+ * @returns 候选列表(不支持的语言/检测异常返回空)
462
+ */
463
+ export function candidatesFor(languageId: string, spec: LspProviderSpec, home = dshHome()): LspCandidate[] {
464
+ /** 候选路径命中 spec.argv 时补 chosen 标记。 */
465
+ const markChosen = (candidate: LspCandidate): LspCandidate =>
466
+ spec.ready && candidate.path && spec.argv.includes(candidate.path) ? { ...candidate, chosen: true } : candidate
467
+ try {
468
+ if (languageId === 'lua') {
469
+ const out: LspCandidate[] = []
470
+ const emmy = candidateEmmyLua(home)
471
+ if (emmy) out.push(markChosen({ name: 'EmmyLua', version: emmy.version, path: emmy.path }))
472
+ const lua = candidateLuaServers(home)[0]
473
+ if (lua) out.push(markChosen({ name: 'LuaLS(sumneko.lua)', version: lua.version, path: lua.path }))
474
+ const inPath = findInPath('lua-language-server' + exeSuffix())
475
+ if (inPath) out.push(markChosen({ name: 'lua-language-server(PATH)', path: inPath }))
476
+ return out
477
+ }
478
+ if (languageId === 'csharp') {
479
+ const out: LspCandidate[] = []
480
+ const found = candidateCSharpServers(home)
481
+ if (found.roslynDll) out.push(markChosen({ name: 'Roslyn(ms-dotnettools.csharp)', version: found.roslynVersion, path: found.roslynDll }))
482
+ if (found.dotrushDll) out.push(markChosen({ name: 'DotRush', version: found.dotrushVersion, path: found.dotrushDll }))
483
+ if (found.omnisharpExe) out.push(markChosen({ name: 'OmniSharp', path: found.omnisharpExe }))
484
+ return out
485
+ }
486
+ return []
487
+ } catch (error) {
488
+ return []
489
+ }
490
+ }
491
+
432
492
  /** 手动配置是否为绝对路径可执行(供设置页判断输入类型)。 */
433
493
  export function looksAbsolute(value: string): boolean {
434
494
  return isAbsolute(value)
package/src/lsp/rpc.ts CHANGED
@@ -7,7 +7,7 @@ import type { Ctx } from '../store.js'
7
7
  import { searchRoot } from '../search/ripgrep.js'
8
8
  import { sessionOf, cwdOf } from '../registry.js'
9
9
  import { resolveProviderSpec, langOfPath, configFromPlugin, configFromSettings, LSP_LANGUAGES, LSP_SETTINGS_NS, type LspConfig } from './config.js'
10
- import { clearProviderCache } from './providers.js'
10
+ import { clearProviderCache, candidatesFor } from './providers.js'
11
11
  import { onRuntimeProvisioned, envInstallStates } from './dotnetProvision.js'
12
12
  import { envRequirementsFor, installRequirement } from './envRequirements.js'
13
13
  import type { LspManager } from './manager.js'
@@ -147,10 +147,11 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
147
147
  const rootLocations = (locations: LspLocation[], root: string): LspLocation[] =>
148
148
  locations.map((location) => ({ ...location, root }))
149
149
 
150
- /** 当前 provider 检测结论 → 设置页 idle 状态(不启动 server);附带未满足的环境需求。 */
150
+ /** 当前 provider 检测结论 → 设置页 idle 状态(不启动 server);附带未满足的环境需求与候选服务器。 */
151
151
  const detectedStatus = (languageId: string, root?: string): LspServerStatus => {
152
152
  const spec = resolveProviderSpec(ctx, pluginConfig, languageId)
153
153
  const missingEnv = envRequirementsFor(languageId, dshHome())
154
+ const candidates = candidatesFor(languageId, spec)
154
155
  return {
155
156
  languageId,
156
157
  source: spec.kind,
@@ -160,6 +161,7 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
160
161
  providerName: spec.providerName,
161
162
  root,
162
163
  missingEnv: missingEnv.length ? missingEnv : undefined,
164
+ candidates: candidates.length ? candidates : undefined,
163
165
  }
164
166
  }
165
167
 
@@ -199,6 +201,11 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
199
201
  return { ok: true, servers: manager.statusAll().filter((status) => status.root === sc.root) }
200
202
  },
201
203
 
204
+ 'edrv.lsp.detect': async () => {
205
+ // 全支持语言的检测结果(不启动服务器):设置页动态渲染卡片 + 候选展示
206
+ return { ok: true, servers: LSP_LANGUAGES.map((lang) => detectedStatus(lang)) }
207
+ },
208
+
202
209
  'edrv.lsp.configGet': async () => {
203
210
  const settings = configFromSettings(ctx)
204
211
  const plugin = configFromPlugin(pluginConfig)
@@ -209,7 +216,7 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
209
216
 
210
217
  'edrv.lsp.configUpdate': async (args) => {
211
218
  const lang = args.languageId
212
- if (!lang || !resolveProviderSpec(ctx, pluginConfig, lang).languageId) {
219
+ if (!lang || !(LSP_LANGUAGES as readonly string[]).includes(lang)) {
213
220
  return { ok: false, error: '不支持的语言:' + lang }
214
221
  }
215
222
  const current = configFromSettings(ctx)
package/src/shared/lsp.ts CHANGED
@@ -164,6 +164,14 @@ export interface LspEnvInstallState {
164
164
  message?: string
165
165
  }
166
166
 
167
+ /** 同语言检测到的候选服务器(candidates 元素;chosen 标记当前选用者)。 */
168
+ export interface LspCandidate {
169
+ name: string // 显示名(如 EmmyLua / LuaLS / Roslyn)
170
+ version?: string // 扩展清单版本
171
+ path?: string // 服务器入口路径(argv 比对用)
172
+ chosen?: boolean // 是否为当前解析选用的服务器
173
+ }
174
+
167
175
  /** 单语言服务器状态(edrv.lsp.status 载荷元素)。 */
168
176
  export interface LspServerStatus {
169
177
  languageId: string
@@ -176,6 +184,7 @@ export interface LspServerStatus {
176
184
  root?: string // 绑定工作区根(相对显示)
177
185
  providerName?: string // provider 名称(如 EmmyLua / LuaLS)
178
186
  missingEnv?: LspMissingEnv[] // 未满足的环境需求(无则省略)
187
+ candidates?: LspCandidate[] // 同语言检测到的候选服务器(设置页展示用,无则省略)
179
188
  }
180
189
 
181
190
  /**
package/src/shared/rpc.ts CHANGED
@@ -137,6 +137,7 @@ export interface RpcRequestMap {
137
137
  'vscode.devFormSet': { enabled: boolean; path?: string }
138
138
  'compat': {}
139
139
  'edrv.lsp.status': { sessionId?: string }
140
+ 'edrv.lsp.detect': {}
140
141
  'edrv.lsp.sync': { sessionId?: string; path: string; text: string; version: number }
141
142
  'edrv.lsp.close': { sessionId?: string; path: string }
142
143
  'edrv.lsp.definition': { sessionId?: string; path: string; position: LspPosition }
@@ -208,6 +209,7 @@ export interface RpcOkMap {
208
209
  'vscode.devFormSet': { devForm: DevFormInfo; restart: boolean }
209
210
  'compat': { report: CompatReport }
210
211
  'edrv.lsp.status': { servers: LspServerStatus[] }
212
+ 'edrv.lsp.detect': { servers: LspServerStatus[] }
211
213
  'edrv.lsp.sync': object
212
214
  'edrv.lsp.close': object
213
215
  'edrv.lsp.definition': { locations: LspLocation[]; truncated?: boolean }