dsh-vscode-mode 0.1.35 → 0.1.42
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/lib/client.js +1117 -156
- package/lib/client.js.map +1 -1
- package/lib/index.js +447 -62
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/monaco/loader.ts +12 -1
- package/src/client/monaco/lsp/lspClient.ts +65 -11
- package/src/client/monaco/lsp/providers.ts +34 -1
- package/src/client/monaco/theme.ts +229 -0
- package/src/client/paths.ts +2 -0
- package/src/client/state/workspaceFoldCache.ts +95 -0
- package/src/client/styles/mcp.css +35 -12
- package/src/client/ui/EditorView.ts +21 -2
- package/src/client/ui/KeybindingsPanel.ts +8 -5
- package/src/client/ui/LspSettings.ts +53 -15
- package/src/client/ui/McpSettings.ts +110 -49
- package/src/client/ui/PerfSettings.ts +145 -34
- package/src/lsp/client.ts +29 -2
- package/src/lsp/manager.ts +15 -0
- package/src/lsp/providers.ts +203 -24
- package/src/lsp/rpc.ts +86 -4
- package/src/lsp/server.ts +80 -3
- package/src/paths.ts +7 -0
- package/src/rpc.ts +4 -1
- package/src/shared/lsp.ts +24 -0
- package/src/shared/rpc.ts +6 -2
package/src/lsp/rpc.ts
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
import type { Ctx } from '../store.js'
|
|
7
7
|
import { searchRoot } from '../search/ripgrep.js'
|
|
8
8
|
import { sessionOf, cwdOf } from '../registry.js'
|
|
9
|
-
import { resolveProviderSpec, langOfPath, configFromPlugin, configFromSettings, LSP_SETTINGS_NS, type LspConfig } from './config.js'
|
|
9
|
+
import { resolveProviderSpec, langOfPath, configFromPlugin, configFromSettings, LSP_LANGUAGES, LSP_SETTINGS_NS, type LspConfig } from './config.js'
|
|
10
|
+
import { clearProviderCache } from './providers.js'
|
|
10
11
|
import type { LspManager } from './manager.js'
|
|
11
12
|
import type { RpcHandlerMap } from '../shared/rpc.js'
|
|
12
|
-
import type { LspLocation } from '../shared/lsp.js'
|
|
13
|
+
import type { LspLocation, LspServerStatus } from '../shared/lsp.js'
|
|
13
14
|
import { installFromMarket, installVsixFile, listInstalled, marketGet, marketSearch, uninstall, updateExtension, type ExtInfo } from './extmgr.js'
|
|
14
15
|
|
|
15
16
|
export interface LspRpcDeps {
|
|
@@ -85,6 +86,21 @@ function createDocTracker() {
|
|
|
85
86
|
sessionDocs.delete(sessionId)
|
|
86
87
|
return [...toRelease]
|
|
87
88
|
},
|
|
89
|
+
/** 清除指定语言的文档归属,让当前客户端重新同步时重新 acquire。 */
|
|
90
|
+
resetLanguage(languageId: string): void {
|
|
91
|
+
for (const [docKey, owners] of openDocs) {
|
|
92
|
+
if (!docKey.includes('|' + languageId + '|')) continue
|
|
93
|
+
for (const sessionId of owners) forgetDoc(sessionId, docKey)
|
|
94
|
+
openDocs.delete(docKey)
|
|
95
|
+
}
|
|
96
|
+
for (const key of [...rootLangRefs.keys()]) if (key.endsWith('|' + languageId)) rootLangRefs.delete(key)
|
|
97
|
+
},
|
|
98
|
+
/** 返回指定会话已打开的文档路径,用于重检测后恢复同步。 */
|
|
99
|
+
pathsFor(sessionId: string, languageId: string): string[] {
|
|
100
|
+
const docs = sessionDocs.get(sessionId)
|
|
101
|
+
if (!docs) return []
|
|
102
|
+
return [...docs].filter((key) => key.includes('|' + languageId + '|')).map((key) => key.slice(key.lastIndexOf('|') + 1))
|
|
103
|
+
},
|
|
88
104
|
}
|
|
89
105
|
}
|
|
90
106
|
|
|
@@ -122,6 +138,41 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
|
|
|
122
138
|
const rootLocations = (locations: LspLocation[], root: string): LspLocation[] =>
|
|
123
139
|
locations.map((location) => ({ ...location, root }))
|
|
124
140
|
|
|
141
|
+
/** 当前 provider 检测结论 → 设置页 idle 状态(不启动 server)。 */
|
|
142
|
+
const detectedStatus = (languageId: string, root?: string): LspServerStatus => {
|
|
143
|
+
const spec = resolveProviderSpec(ctx, pluginConfig, languageId)
|
|
144
|
+
return {
|
|
145
|
+
languageId,
|
|
146
|
+
source: spec.kind,
|
|
147
|
+
phase: 'idle',
|
|
148
|
+
reason: spec.ready ? undefined : spec.reason,
|
|
149
|
+
version: spec.version,
|
|
150
|
+
providerName: spec.providerName,
|
|
151
|
+
root,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 清发现缓存并重置旧 server(保留 tracker 文档记录);
|
|
157
|
+
* 下一次模型同步会按最新配置重新 acquire,已打开文档无需重开。
|
|
158
|
+
* 作者 ddj 2026年09月02号
|
|
159
|
+
* @param languageId 语言 id
|
|
160
|
+
* @param sessionId 会话语境(缺省重置全部工作区该语言)
|
|
161
|
+
* @returns 检测后的 idle 状态列表
|
|
162
|
+
*/
|
|
163
|
+
const redetectLanguage = async (languageId: string, sessionId?: string): Promise<LspServerStatus[]> => {
|
|
164
|
+
clearProviderCache()
|
|
165
|
+
if (sessionId) {
|
|
166
|
+
const sc = await rootOf(sessionId)
|
|
167
|
+
if ('err' in sc) return []
|
|
168
|
+
manager.reset(sc.root, languageId)
|
|
169
|
+
return [detectedStatus(languageId, sc.root)]
|
|
170
|
+
}
|
|
171
|
+
const roots = [...new Set(manager.statusAll().filter((s) => s.languageId === languageId).map((s) => s.root))]
|
|
172
|
+
manager.resetLanguage(languageId)
|
|
173
|
+
return roots.length ? roots.map((root) => detectedStatus(languageId, root)) : [detectedStatus(languageId)]
|
|
174
|
+
}
|
|
175
|
+
|
|
125
176
|
const disposeSession = (sessionId: string): void => {
|
|
126
177
|
for (const rl of tracker.closeSession(sessionId)) {
|
|
127
178
|
const sep = rl.lastIndexOf('|')
|
|
@@ -160,10 +211,12 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
|
|
|
160
211
|
const settings = ctx.get('settings')
|
|
161
212
|
if (!settings?.update) return { ok: false, error: '设置服务不可用' }
|
|
162
213
|
await settings.update(LSP_SETTINGS_NS, { [lang]: target })
|
|
214
|
+
// 保存/切换后立即重新检测:清缓存 + 重置旧 server,旧状态不残留
|
|
215
|
+
const servers = await redetectLanguage(lang)
|
|
163
216
|
const descriptor = settings.describe?.({ redactSecrets: true })?.find((item: { ns?: string }) => item.ns === LSP_SETTINGS_NS)
|
|
164
217
|
const stored = descriptor?.value as Record<string, unknown> | undefined
|
|
165
218
|
const merged: LspConfig = { ...configFromPlugin(pluginConfig), ...(stored as LspConfig) }
|
|
166
|
-
return { ok: true, config: merged as unknown as Record<string, unknown
|
|
219
|
+
return { ok: true, config: merged as unknown as Record<string, unknown>, servers }
|
|
167
220
|
} catch (error) {
|
|
168
221
|
return { ok: false, error: '配置保存失败:' + String(error) }
|
|
169
222
|
}
|
|
@@ -274,6 +327,31 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
|
|
|
274
327
|
}
|
|
275
328
|
},
|
|
276
329
|
|
|
330
|
+
'edrv.lsp.semanticTokens': async (args) => {
|
|
331
|
+
const lang = langOfPath(args.path)
|
|
332
|
+
if (!lang) return { ok: true, tokens: undefined }
|
|
333
|
+
const sc = await rootOf(args.sessionId)
|
|
334
|
+
if ('err' in sc) return { ok: false, error: sc.err }
|
|
335
|
+
try {
|
|
336
|
+
const server = serverOf(sc.root, lang)
|
|
337
|
+
if (!server) return { ok: true, tokens: undefined }
|
|
338
|
+
return { ok: true, tokens: (await server.semanticTokens(args.path)) ?? undefined }
|
|
339
|
+
} catch (error) {
|
|
340
|
+
return { ok: false, error: 'LSP 语义分色查询失败:' + String(error) }
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
'edrv.lsp.redetect': async (args) => {
|
|
345
|
+
if (!(LSP_LANGUAGES as readonly string[]).includes(args.languageId)) {
|
|
346
|
+
return { ok: false, error: '不支持的语言:' + args.languageId }
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
return { ok: true, servers: await redetectLanguage(args.languageId) }
|
|
350
|
+
} catch (error) {
|
|
351
|
+
return { ok: false, error: '重新检测失败:' + String(error) }
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
|
|
277
355
|
// --region 扩展管理(edrv.lsp.ext.*:已装/市场/安装/卸载/更新)
|
|
278
356
|
'edrv.lsp.ext.list': async () => ({ ok: true, extensions: listInstalled() }),
|
|
279
357
|
|
|
@@ -292,6 +370,7 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
|
|
|
292
370
|
if (args.vsixPath) info = await installVsixFile(args.vsixPath)
|
|
293
371
|
else if (args.namespace && args.name) info = await installFromMarket(args.namespace, args.name, args.version)
|
|
294
372
|
else return { ok: false, error: '缺少安装源(vsixPath 或 namespace+name)' }
|
|
373
|
+
clearProviderCache() // 新扩展可能成为 Lua provider,立即丢弃旧发现结果
|
|
295
374
|
return { ok: true, extension: info }
|
|
296
375
|
} catch (error) {
|
|
297
376
|
return { ok: false, error: '安装失败:' + String(error) }
|
|
@@ -299,7 +378,9 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
|
|
|
299
378
|
},
|
|
300
379
|
|
|
301
380
|
'edrv.lsp.ext.uninstall': async (args) => {
|
|
302
|
-
|
|
381
|
+
const ok = uninstall(args.id)
|
|
382
|
+
if (ok) clearProviderCache()
|
|
383
|
+
return ok ? { ok: true } : { ok: false, error: '扩展未安装:' + args.id }
|
|
303
384
|
},
|
|
304
385
|
|
|
305
386
|
'edrv.lsp.ext.update': async (args) => {
|
|
@@ -309,6 +390,7 @@ export function createLspRpc(deps: LspRpcDeps): { handlers: Partial<RpcHandlerMa
|
|
|
309
390
|
const current = listInstalled().find((e) => e.id === args.id)
|
|
310
391
|
return current ? { ok: true, extension: current, updated: false } : { ok: false, error: '扩展未安装:' + args.id }
|
|
311
392
|
}
|
|
393
|
+
clearProviderCache()
|
|
312
394
|
return { ok: true, extension: updated, updated: true }
|
|
313
395
|
} catch (error) {
|
|
314
396
|
return { ok: false, error: '更新失败:' + String(error) }
|
package/src/lsp/server.ts
CHANGED
|
@@ -8,8 +8,10 @@ import { spawnServer, type Transport } from './transport.js'
|
|
|
8
8
|
import { createLspClient, type LspClient } from './client.js'
|
|
9
9
|
import { pathToFileUri } from './uri.js'
|
|
10
10
|
import type { LspProviderSpec } from './providers.js'
|
|
11
|
+
import { LSP_SEMANTIC_TOKEN_MODIFIERS, LSP_SEMANTIC_TOKEN_TYPES } from '../shared/lsp.js'
|
|
11
12
|
import type {
|
|
12
13
|
LspLocation,
|
|
14
|
+
LspSemanticTokens,
|
|
13
15
|
LspServerCapabilities,
|
|
14
16
|
LspServerPhase,
|
|
15
17
|
LspServerStatus,
|
|
@@ -31,6 +33,7 @@ export interface LspServer {
|
|
|
31
33
|
documentSymbol(path: string): Promise<LspSymbol[]>
|
|
32
34
|
workspaceSymbol(query: string): Promise<LspSymbol[]>
|
|
33
35
|
hover(path: string, line: number, character: number): Promise<{ contents: string[] } | null>
|
|
36
|
+
semanticTokens(path: string): Promise<LspSemanticTokens | null>
|
|
34
37
|
dispose(): Promise<void>
|
|
35
38
|
}
|
|
36
39
|
|
|
@@ -59,18 +62,39 @@ export function createLspServer(spec: LspProviderSpec, root: string, languageId:
|
|
|
59
62
|
let phase: LspServerPhase = 'idle'
|
|
60
63
|
let transport: Transport | null = null
|
|
61
64
|
let client: LspClient | null = null
|
|
62
|
-
let capabilities: LspServerCapabilities = {
|
|
65
|
+
let capabilities: LspServerCapabilities = {
|
|
66
|
+
definition: false,
|
|
67
|
+
references: false,
|
|
68
|
+
documentSymbol: false,
|
|
69
|
+
workspaceSymbol: false,
|
|
70
|
+
hover: false,
|
|
71
|
+
semanticTokens: false,
|
|
72
|
+
semanticTokenTypes: [...LSP_SEMANTIC_TOKEN_TYPES],
|
|
73
|
+
semanticTokenModifiers: [...LSP_SEMANTIC_TOKEN_MODIFIERS],
|
|
74
|
+
}
|
|
63
75
|
let disposed = false
|
|
64
76
|
let readyWait: Promise<boolean> = Promise.resolve(false)
|
|
65
77
|
let resolveReady: ((ready: boolean) => void) | null = null
|
|
66
78
|
let lastProgress: { value?: number; message?: string } = {}
|
|
67
|
-
let lastStatus: LspServerStatus = {
|
|
79
|
+
let lastStatus: LspServerStatus = {
|
|
80
|
+
languageId,
|
|
81
|
+
source: spec.kind,
|
|
82
|
+
phase: 'idle',
|
|
83
|
+
reason: spec.ready ? undefined : spec.reason,
|
|
84
|
+
root,
|
|
85
|
+
version: spec.version,
|
|
86
|
+
providerName: spec.providerName ?? (spec.version ? '语言服务器扩展' : undefined),
|
|
87
|
+
}
|
|
68
88
|
|
|
69
89
|
const log = (line: string): void => logger?.('[' + languageId + '@' + root + '] ' + line)
|
|
90
|
+
const providerInfo = {
|
|
91
|
+
version: spec.version,
|
|
92
|
+
providerName: spec.providerName ?? (spec.version ? 'EmmyLua' : undefined),
|
|
93
|
+
}
|
|
70
94
|
|
|
71
95
|
const setPhase = (next: LspServerPhase, reason?: string): void => {
|
|
72
96
|
phase = next
|
|
73
|
-
lastStatus = { languageId, source: spec.kind, phase: next, reason, root, ...progressFields() }
|
|
97
|
+
lastStatus = { languageId, source: spec.kind, phase: next, reason, root, ...providerInfo, ...progressFields() }
|
|
74
98
|
server.onStateChange?.(lastStatus)
|
|
75
99
|
}
|
|
76
100
|
|
|
@@ -254,6 +278,25 @@ export function createLspServer(spec: LspProviderSpec, root: string, languageId:
|
|
|
254
278
|
return { contents: stringifyHoverContents(result.contents) }
|
|
255
279
|
},
|
|
256
280
|
|
|
281
|
+
/** 查询全文 semantic tokens,并将服务器 legend 归一到插件固定 legend。 */
|
|
282
|
+
async semanticTokens(path: string): Promise<LspSemanticTokens | null> {
|
|
283
|
+
const doc = docs.get(path)
|
|
284
|
+
if (!doc || !(await readyWait) || !capabilities.semanticTokens) return null
|
|
285
|
+
const result = await ensureClient().request<{ data?: unknown; resultId?: unknown } | null>(
|
|
286
|
+
'textDocument/semanticTokens/full',
|
|
287
|
+
{ textDocument: { uri: doc.uri } },
|
|
288
|
+
)
|
|
289
|
+
if (!result || !Array.isArray(result.data)) return null
|
|
290
|
+
return {
|
|
291
|
+
data: normalizeSemanticData(
|
|
292
|
+
result.data,
|
|
293
|
+
capabilities.semanticTokenTypes ?? [],
|
|
294
|
+
capabilities.semanticTokenModifiers ?? [],
|
|
295
|
+
),
|
|
296
|
+
resultId: typeof result.resultId === 'string' ? result.resultId : undefined,
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
|
|
257
300
|
async dispose(): Promise<void> {
|
|
258
301
|
disposed = true
|
|
259
302
|
resolveReady?.(false)
|
|
@@ -344,3 +387,37 @@ function toInt(value: unknown): number {
|
|
|
344
387
|
const n = Number(value)
|
|
345
388
|
return Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 0
|
|
346
389
|
}
|
|
390
|
+
|
|
391
|
+
/** 把服务器 legend 下的 5 元组流转换为插件固定 legend 下的 5 元组流。 */
|
|
392
|
+
function normalizeSemanticData(data: unknown[], types: string[], modifiers: string[]): number[] {
|
|
393
|
+
const out: number[] = []
|
|
394
|
+
let previousLine = 0
|
|
395
|
+
let previousCharacter = 0
|
|
396
|
+
let outputLine = 0
|
|
397
|
+
let outputCharacter = 0
|
|
398
|
+
for (let i = 0; i + 4 < data.length; i += 5) {
|
|
399
|
+
const deltaLine = toInt(data[i])
|
|
400
|
+
const deltaCharacter = toInt(data[i + 1])
|
|
401
|
+
const length = toInt(data[i + 2])
|
|
402
|
+
const sourceType = types[toInt(data[i + 3])] ?? 'variable'
|
|
403
|
+
const sourceModifiers = toInt(data[i + 4])
|
|
404
|
+
const line = previousLine + deltaLine
|
|
405
|
+
const character = deltaLine === 0 ? previousCharacter + deltaCharacter : deltaCharacter
|
|
406
|
+
const typeIndex = Math.max(0, LSP_SEMANTIC_TOKEN_TYPES.indexOf(sourceType as never))
|
|
407
|
+
let modifierBits = 0
|
|
408
|
+
for (let bit = 0; bit < modifiers.length; bit++) {
|
|
409
|
+
if ((sourceModifiers & (1 << bit)) === 0) continue
|
|
410
|
+
const modifier = modifiers[bit]
|
|
411
|
+
const targetBit = LSP_SEMANTIC_TOKEN_MODIFIERS.indexOf(modifier as never)
|
|
412
|
+
if (targetBit >= 0) modifierBits |= 1 << targetBit
|
|
413
|
+
}
|
|
414
|
+
const outputDeltaLine = line - outputLine
|
|
415
|
+
const outputDeltaCharacter = outputDeltaLine === 0 ? character - outputCharacter : character
|
|
416
|
+
out.push(outputDeltaLine, outputDeltaCharacter, length, typeIndex, modifierBits)
|
|
417
|
+
previousLine = line
|
|
418
|
+
previousCharacter = character
|
|
419
|
+
outputLine = line
|
|
420
|
+
outputCharacter = character
|
|
421
|
+
}
|
|
422
|
+
return out
|
|
423
|
+
}
|
package/src/paths.ts
CHANGED
|
@@ -25,6 +25,8 @@ export const DSH_DIR_NAME = '.dsh'
|
|
|
25
25
|
export const DSH_HOME_ENV = 'DSH_HOME'
|
|
26
26
|
/** 树缓存 schema 版本(文件名携带;版本递增即触发旧版清理)。 */
|
|
27
27
|
export const TREE_CACHE_SCHEMA = 1
|
|
28
|
+
/** LSP provider 发现缓存 schema 版本(格式变更即递增,旧文件自动失效)。 */
|
|
29
|
+
export const LSP_SPEC_CACHE_SCHEMA = 4
|
|
28
30
|
/** 树缓存保留期:超期文件视为废弃(工作区搬迁/废弃残留)。 */
|
|
29
31
|
export const TREE_RETENTION_MS = 30 * 24 * 60 * 60 * 1000
|
|
30
32
|
/** 插件缓存目录总预算:超限按 mtime 从旧到新删文件,防任意增长撑爆磁盘(最后兜底)。 */
|
|
@@ -141,6 +143,11 @@ export function userCacheDir(home = dshHome()): string {
|
|
|
141
143
|
return join(pluginCacheRoot(home), 'user')
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
/** LSP provider 发现结果缓存文件(用户级:user/lsp-specs.v<schema>.json,跨会话/跨重启共享)。 */
|
|
147
|
+
export function lspSpecCacheFile(home = dshHome(), schema = LSP_SPEC_CACHE_SCHEMA): string {
|
|
148
|
+
return join(userCacheDir(home), 'lsp-specs.v' + schema + '.json')
|
|
149
|
+
}
|
|
150
|
+
|
|
144
151
|
/** 插件日志根(~/.dsh/dsh-vscode-mode/logs,用户级)。 */
|
|
145
152
|
export function pluginLogRoot(home = dshHome()): string {
|
|
146
153
|
return join(home, PLUGIN_ID, 'logs')
|
package/src/rpc.ts
CHANGED
|
@@ -284,7 +284,10 @@ export function buildHandlers(
|
|
|
284
284
|
try {
|
|
285
285
|
const target = await resolveTarget(ctx, sc.session, args.path)
|
|
286
286
|
const info = await fs.stat(target)
|
|
287
|
-
if (!info || info.type !== 'file')
|
|
287
|
+
if (!info || info.type !== 'file') {
|
|
288
|
+
// 带上解析后的真实路径:跳转失败时可直接看出是路径解析错还是目标本身不存在
|
|
289
|
+
return { ok: false, error: '文件不存在', resolvedPath: fs.processPath(target) }
|
|
290
|
+
}
|
|
288
291
|
if ((info.size ?? 0) > READ_CAP) return { ok: false, error: '文件过大(>8MB),不支持整文件预览' }
|
|
289
292
|
const content = await fs.readText(target)
|
|
290
293
|
return { ok: true, content, size: content.length }
|
package/src/shared/lsp.ts
CHANGED
|
@@ -49,6 +49,26 @@ export interface LspHover {
|
|
|
49
49
|
range?: LspRange
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/** LSP semantic token 标准类型(host 归一化后,client 与 Monaco 共用)。 */
|
|
53
|
+
export const LSP_SEMANTIC_TOKEN_TYPES = [
|
|
54
|
+
'namespace', 'type', 'class', 'enum', 'interface', 'struct', 'typeParameter',
|
|
55
|
+
'parameter', 'variable', 'property', 'enumMember', 'event', 'function', 'method',
|
|
56
|
+
'macro', 'label', 'comment', 'string', 'keyword', 'number', 'regexp', 'operator',
|
|
57
|
+
'decorator',
|
|
58
|
+
] as const
|
|
59
|
+
|
|
60
|
+
/** LSP semantic token 标准修饰符(host 归一化后,client 与 Monaco 共用)。 */
|
|
61
|
+
export const LSP_SEMANTIC_TOKEN_MODIFIERS = [
|
|
62
|
+
'declaration', 'definition', 'readonly', 'static', 'deprecated', 'abstract',
|
|
63
|
+
'async', 'modification', 'documentation', 'defaultLibrary', 'global', 'local', 'mutable',
|
|
64
|
+
] as const
|
|
65
|
+
|
|
66
|
+
/** 归一化后的 LSP semanticTokens/full 结果(data 使用 LSP delta 编码)。 */
|
|
67
|
+
export interface LspSemanticTokens {
|
|
68
|
+
data: number[]
|
|
69
|
+
resultId?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
52
72
|
/** LSP 服务器能力子集:本插件会用到的方法。 */
|
|
53
73
|
export interface LspServerCapabilities {
|
|
54
74
|
definition: boolean
|
|
@@ -56,6 +76,9 @@ export interface LspServerCapabilities {
|
|
|
56
76
|
documentSymbol: boolean
|
|
57
77
|
workspaceSymbol: boolean
|
|
58
78
|
hover: boolean
|
|
79
|
+
semanticTokens: boolean
|
|
80
|
+
semanticTokenTypes?: string[]
|
|
81
|
+
semanticTokenModifiers?: string[]
|
|
59
82
|
}
|
|
60
83
|
|
|
61
84
|
/** 服务器进程/会话状态。 */
|
|
@@ -77,6 +100,7 @@ export interface LspServerStatus {
|
|
|
77
100
|
progress?: number // LSP 工作区解析进度(0–100,未知时省略)
|
|
78
101
|
progressMessage?: string
|
|
79
102
|
root?: string // 绑定工作区根(相对显示)
|
|
103
|
+
providerName?: string // provider 名称(如 EmmyLua / LuaLS)
|
|
80
104
|
}
|
|
81
105
|
|
|
82
106
|
/**
|
package/src/shared/rpc.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
} from './types.js'
|
|
15
15
|
import type { MpcConfig, MpcProject, MpcProjectSaveInput, MpcServer } from './mcp.js'
|
|
16
16
|
import type { CompatReport, DevFormInfo } from './compat.js'
|
|
17
|
-
import type { LspExtInfo, LspExtUpdate, LspHover, LspLocation, LspMarketItem, LspPosition, LspServerStatus, LspSymbol } from './lsp.js'
|
|
17
|
+
import type { LspExtInfo, LspExtUpdate, LspHover, LspLocation, LspMarketItem, LspPosition, LspSemanticTokens, LspServerStatus, LspSymbol } from './lsp.js'
|
|
18
18
|
|
|
19
19
|
/** webServer 精确路由。 */
|
|
20
20
|
export const RPC_PATH = '/edrv/rpc'
|
|
@@ -143,6 +143,8 @@ export interface RpcRequestMap {
|
|
|
143
143
|
'edrv.lsp.documentSymbol': { sessionId?: string; path: string }
|
|
144
144
|
'edrv.lsp.workspaceSymbol': { sessionId?: string; query: string }
|
|
145
145
|
'edrv.lsp.hover': { sessionId?: string; path: string; position: LspPosition }
|
|
146
|
+
'edrv.lsp.semanticTokens': { sessionId?: string; path: string }
|
|
147
|
+
'edrv.lsp.redetect': { languageId: string }
|
|
146
148
|
'edrv.lsp.configGet': {}
|
|
147
149
|
'edrv.lsp.configUpdate': { languageId: string; enabled?: boolean; command?: string; path?: string }
|
|
148
150
|
'edrv.lsp.ext.list': {}
|
|
@@ -205,8 +207,10 @@ export interface RpcOkMap {
|
|
|
205
207
|
'edrv.lsp.documentSymbol': { symbols: LspSymbol[] }
|
|
206
208
|
'edrv.lsp.workspaceSymbol': { symbols: LspSymbol[]; truncated?: boolean }
|
|
207
209
|
'edrv.lsp.hover': { hover?: LspHover }
|
|
210
|
+
'edrv.lsp.semanticTokens': { tokens?: LspSemanticTokens }
|
|
211
|
+
'edrv.lsp.redetect': { servers: LspServerStatus[] }
|
|
208
212
|
'edrv.lsp.configGet': { config: Record<string, unknown> }
|
|
209
|
-
'edrv.lsp.configUpdate': { config: Record<string, unknown
|
|
213
|
+
'edrv.lsp.configUpdate': { config: Record<string, unknown>; servers?: LspServerStatus[] }
|
|
210
214
|
'edrv.lsp.ext.list': { extensions: LspExtInfo[] }
|
|
211
215
|
'edrv.lsp.ext.install': { extension: LspExtInfo; updated?: boolean }
|
|
212
216
|
'edrv.lsp.ext.uninstall': object
|