跳到主要内容
路径文档

LSP 代码智能

一句话版ctx.lsp语义代码导航能力缝:跳转定义、找引用、找实现、hover:通过语言服务器,且只暴露这四个语义操作,没有泛型 JSON-RPC 逃生口。dsh-tool-lsp 是它的模型可见工具。

让 agent 不只是 grep,而是真正"跳去定义 / 找所有引用 / 看实现"。

一、四个语义操作(没有逃生口)

ctx.lsp 恰好暴露四个:

goToDefinition 跳转定义
findReferences 找引用(总是包含声明)
goToImplementation 找实现
hover 悬停(内容或 null)

没有泛型 JSON-RPC 逃生口:所以不会有未经 review 的命令/突变经 ctx.lsp 到达 provider。这是刻意的安全边界。

二、包

角色
dsh-lsp服务定义:服务、按 brand id + 扩展名映射的 provider 注册表、按需选择、请求/结果词汇、LspError
dsh-lsp-stdio服务 provider:通用 stdio 语言服务器后端
dsh-tool-lsp消费者:模型面向的 lsp 工具

三、Service API

Member语义
registerProvider(provider)注册后端,原子保留它的 brand id + 每个归一化文件扩展名;输入非法/冲突不发布并抛 LspError(LSP_INVALID_PROVIDER/LSP_CONFLICT)
query(request, signal?)按文件最终扩展名选 provider、derive languageId、跑一次查询;无匹配抛 LSP_UNAVAILABLE

选择按查询、与顺序无关:provider 独家拥有一组扩展名,注册/HMR 顺序不影响路由。扩展名归一化成小写、带前导点形式;languageId 只同步瞬态文档,不参与选择。第一版没有 glob / language-id / 显式路由选择器。

Provider 注册的是能力,不是工具dsh-tool-lsp 是模型可见名/描述/提示/schema/呈现的唯一所有者。

四、词汇

LspQueryRequest: { operation, filePath, position, workspaceRoot } 全必填:没有字段需要实现默认、无 resolve() 步。position/range 是零基 UTF-16(对应协议);工具的 one-based 光标惯例由它转。

LspQueryResult封闭判别联合:

  • 导航:{ kind: 'locations', locations, resolvedWorkspaceUri }
  • hover:{ kind: 'hover', hover }(内容或 null)

resolvedWorkspaceUri 是 provider 的规范工作区 file: URI;调用方相对化 location URI 用它,而不是套宿主平台路径规则(可能 symlink)。

五、本地后端(lsp-stdio)

dsh-lsp-stdio通用 stdio 语言服务器后端:一个插件实例接受一个命名 server 表,每项注册一个隔离 provider。经 ctx.fs 读、ctx.subprocess 启动:server 与源码总在挂载的执行世界里。这是泛型 host,不是语言服务器目录或安装器;preset 放 cordis.yml overlay。

关键行为:

  • 每个 (server id, 规范工作区) 懒单飞一个 server 进程;live server 错误不重放,查询失败在 fresh 进程上重试一次
  • transient-open:每次查询 resolve+字节限定源码,didOpen(version 1 全文本)→ 请求 → finallydidClose。文档每次查询后关闭,第一版不需要 didChange/内容缓存/LRU
  • 每次 source-read/open/query/close 通过一个可 abort 的 per-workspace 队列串行;不同工作区并行
  • 协议 shutdown 失败 → 经 subprocess 终止 server 子孙树(POSIX group / Windows taskkill)
  • ctx.fs 规范 containment、file URI、流式文本校验;但不发 fs/observed:只 LSP 结果对模型可见

六、配置

- id: lsp
name: '@deepseek-ai/dsh-lsp-stdio'
config:
servers:
typescript:
command: typescript-language-server # 必填:spawn 的可执行
args: ['--stdio']
extensionToLanguage: # 必填:小写点前缀扩展名 → languageId
'.ts': typescript
'.tsx': typescript
python:
command: pyright-langserver
extensionToLanguage:
'.py': python

servers 的 key 是在 ctx.lsp 上保留的稳定 provider id;command 必填,启动不用 shell。

七、验证

# 看 lsp 是否装载 + 配了几个 server
dsh web --dump-config | grep -iE "lsp"
# 问 agent "goToDefinition 某个符号",会话里看结果
zstdcat ~/.dsh/sessions/*/*/session.jsonl.zstd | grep -E '"tool/result"' | tail

下一步