# lsp-bridge
**Repository Path**: emacs-hub/lsp-bridge
## Basic Information
- **Project Name**: lsp-bridge
- **Description**: 此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库:
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: https://github.com/manateelazycat/lsp-bridge.git
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 1
- **Created**: 2022-05-27
- **Last Updated**: 2024-01-16
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
[English](./README.md) | 简体中文
# lsp-bridge
lsp-bridge 的目标是实现 Emacs 生态中性能最快的 LSP 客户端。
lsp-bridge 使用 Python 多线程技术在 Emacs 和 LSP 服务器之间构建高速缓存,在提供行云流水的代码补全体验的前提下,保证永远不会卡住 Emacs。
## 安装
1. 安装 Emacs 28 及以上版本
2. 安装 Python 依赖:
+ [python-epc](https://github.com/tkf/python-epc)
+ [orjson](https://github.com/ijl/orjson)
3. 安装 Elisp 依赖:
+ [posframe](https://github.com/tumashu/posframe)
+ [markdown-mode](https://github.com/jrblevin/markdown-mode)
+ [yasnippet](https://github.com/joaotavora/yasnippet)
4. 用 `git clone` 下载此仓库,并替换下面配置中的 load-path 路径
5. 把下面代码加入到你的配置文件 ~/.emacs 中:
```elisp
(add-to-list 'load-path "")
(require 'yasnippet)
(yas-global-mode 1)
(require 'lsp-bridge)
(global-lsp-bridge-mode)
```
## 使用
lsp-bridge 开箱即用, 安装好语言对应的[LSP 服务器](https://github.com/manateelazycat/lsp-bridge/blob/master/README.zh-CN.md#%E5%B7%B2%E7%BB%8F%E6%94%AF%E6%8C%81%E7%9A%84%E8%AF%AD%E8%A8%80%E6%9C%8D%E5%8A%A1%E5%99%A8)和模式插件以后, 直接写代码即可, 不需要额外的设置。
需要注意的是 lsp-bridge 有三种扫描模式:
1. 检测到 `.git` 目录时(通过命令 `git rev-parse --is-inside-work-tree` 来判断), lsp-bridge 会扫描整个目录文件来提供补全
2. 没有检测到 `.git` 目录时, lsp-bridge 只会对打开的文件提供单文件补全
3. 自定义 `lsp-bridge-get-project-path-by-filepath` 函数, 输入参数是打开文件的路径字符串, 输出参数是项目目录路径, lsp-bridge 会根据输出目录路径来提供补全
## 按键
| 按键 | 命令 | 备注 |
| :--- | :--- | :--- |
| Alt + n | acm-select-next | 选择下一个后选词 |
| Down | acm-select-next | 选择下一个后选词 |
| Alt + p | acm-select-prev | 选择上一个后选词 |
| Up | acm-select-prev | 选择上一个后选词 |
| Alt + . | acm-select-last | 选择最后一个后选词 |
| Alt + , | acm-select-first | 选择第一个后选词 |
| Ctrl + m | acm-complete | 完成补全 |
| Return | acm-complete | 完成补全 |
| Tab | acm-complete | 完成补全 |
| Alt + h | acm-complete | 完成补全 |
| Alt + H | acm-insert-common | 插入后选词共有部分 |
| Alt + d | acm-doc-toggle | 开启或关闭后选词文档 |
| Alt + j | acm-doc-scroll-up | 向下滚动后选词文档 |
| Alt + k | acm-doc-scroll-down | 向上滚动后选词文档 |
| Alt + l | acm-hide | 隐藏补全窗口 |
| Ctrl + g | acm-hide | 隐藏补全窗口 |
| 数字键 | acm-complete-quick-access | 快速选择后选词, 需要开启 `acm-enable-quick-access` 选项 |
## 命令列表
* `lsp-bridge-find-def`: 跳转到定义位置
* `lsp-bridge-find-def-other-window`: 在其他窗口跳转到定义位置
* `lsp-bridge-find-def-return`: 返回跳转之前的位置
* `lsp-bridge-find-impl`: 跳转到接口实现位置
* `lsp-bridge-find-impl-other-window`: 在其他窗口跳转到接口实现位置
* `lsp-bridge-find-references`: 查看代码引用
* `lsp-bridge-popup-documentation`: 查看光标处的文档
* `lsp-bridge-popup-documentation-scroll-up`: 文档窗口向上滚动
* `lsp-bridge-popup-documentation-scroll-down`: 文档窗口向下滚动
* `lsp-bridge-rename`: 重命名
* `lsp-bridge-diagnostic-jump-next`: 跳转到下一个诊断位置
* `lsp-bridge-diagnostic-jump-prev`: 跳转到上一个诊断位置
* `lsp-bridge-diagnostic-list`: 列出所有诊断信息
* `lsp-bridge-diagnostic-ignore`: 插入注视忽略当前诊断
* `lsp-bridge-workspace-list-symbols`: 列出工作区所有符号,并跳转到符号定义
* `lsp-bridge-signature-help-fetch`: 在 minibuffer 显示参数信息
* `lsp-bridge-popup-complete-menu`: 手动弹出补全菜单, 只有当打开 `lsp-bridge-complete-manually` 选项才需要使用这个命令
* `lsp-bridge-restart-process`: 重启 lsp-bridge 进程 (一般只有开发者才需要这个功能)
* `acm-insert-common`: 插入补全后选词的公共前缀
* `acm-doc-scroll-up`: API 文档窗口向上滚动
* `acm-doc-scroll-down`: API 文档窗口向下滚动
## 选项
* `lsp-bridge-python-command`: Python 命令的路径, 如果你用 `conda`, 你也许会定制这个选项
* `lsp-bridge-complete-manually`: 只有当用户手动调用 `lsp-bridge-popup-complete-menu` 命令的时候才弹出补全菜单, 默认关闭
* `lsp-bridge-get-workspace-folder`: 在 Java 中需要把多个项目放到一个 Workspace 目录下, 才能正常进行定义跳转, 可以自定义这个函数, 函数输入是项目路径, 返回对应的 Workspace 目录
* `lsp-bridge-c-lsp-server`: C 语言的服务器,可以选择`clangd`或者`ccls`
* `lsp-bridge-python-lsp-server`: Python 语言的服务器,可以选择`pyright`或者`jedi`
* `lsp-bridge-tex-lsp-server`: LaTeX 语言的服务器,可以选择`texlab`或者`digestif`
* `lsp-bridge-org-babel-lang-list`: 支持 org-mode 代码块补全的语言列表
* `lsp-bridge-enable-diagnostics`: 代码诊断, 默认打开
* `lsp-bridge-enable-search-words`: 索引打开文件的单词, 默认打开
* `lsp-bridge-enable-auto-format-code`: 自动格式化代码, 默认关闭
* `lsp-bridge-enable-signature-help`: 支持函数参数显示, 默认打开
* `lsp-bridge-enable-log`: 启用 LSP 消息日志, 默认关闭
* `lsp-bridge-enable-debug`: 启用程序调试, 默认关闭
* `lsp-bridge-disable-backup`: 禁止 emacs 对文件做版本管理, 默认打开
* `lsp-bridge-diagnostic-fetch-idle`: 诊断延迟,默认是停止敲键盘后 0.5 秒开始拉取诊断信息
* `lsp-bridge-signature-show-function`: 用于显示签名信息的函数
* `lsp-bridge-completion-popup-predicates`: 补全菜单显示的检查函数, 这个选项包括的所有函数都检查过以后, 补全菜单才能显示
* `lsp-bridge-completion-stop-commands`: 这些命令执行以后,不再弹出补全菜单
* `lsp-bridge-completion-hide-characters`: 这些字符的后面不再弹出补全菜单
* `acm-enable-doc`: 补全菜单是否显示帮助文档
* `acm-enable-icon`: 补全菜单是否显示图标, macOS 用户需要给 brew 命令增加选项 `--with-rsvg` 来安装 Emacs 才能显示 SVG 图片
* `acm-enable-tabnine`: 是否打开 tabnine 补全支持,默认打开,打开后需要运行命令 `lsp-bridge-install-tabnine` 来安装 tabnine 后就可以使用了。 TabNine 会消耗巨大的 CPU, 导致你整个电脑都卡顿, 如果电脑性能不好, 不建议开启此选项
* `acm-enable-search-words`: 补全菜单是否显示打开文件的单词, 默认打开
* `acm-enable-quick-access`: 是否在图标后面显示索引, 可以通过 Alt + Number 来快速选择后选词, 默认关闭
* `acm-enable-yas`: yasnippet 补全,默认打开
* `acm-enable-citre`: citre 补全,默认关闭
* `acm-doc-frame-max-lines`: 帮助窗口的最大行数, 默认是 20
* `acm-snippet-insert-index`: 代码模板候选词在补全菜单中的显示位置
* `acm-candidate-match-function`: 补全菜单匹配算法, orderless-* 开头的算法需要额外安装 [orderless](https://github.com/oantolin/orderless)
* `acm-backend-lsp-enable-auto-import`: 支持自动导入, 默认打开
* `acm-backend-yas-candidates-number`: yasnippet 显示个数,默认 2 个
* `acm-backend-citre-keyword-complete`: 根据`acm-backend-citre-keywords-alist`定义的各个模式的关键字进行补全,需要使能 citre 后才生效
## 自定义语言服务器配置
lsp-bridge 每种语言的服务器配置存储在[lsp-bridge/langserver](https://github.com/manateelazycat/lsp-bridge/tree/master/langserver).
大多数情况, 你可以根据以下优先级顺序来自定义服务器配置:
1. ```lsp-bridge-get-single-lang-server-by-project```: 用户自定义函数, 输入参数是 `project-path` 和 `file-path`, 返回对应的 LSP 服务器字符串, 可以在 `lsp-bridge-single-lang-server-mode-list` 列表中查询所有 LSP 服务器的名称, 默认这个函数返回 nil
2. ```lsp-bridge-single-lang-server-extension-list```: 根据文件的扩展名来返回服务器,比如打开*.wxml 文件时,我们会使用 ```wxml``` LSP 服务器提供补全
3. ```lsp-bridge-single-lang-server-mode-list```: 根据 Emacs 的 major-mode 来返回对应的服务器
如果你在编写 JavaScript 代码, 你可能需要自定义多服务器配置:
1. ```lsp-bridge-get-multi-lang-server-by-project```: 用户自定义函数, 输入参数是 `project-path` 和 `file-path`, 返回多服务器配置名, 可以在子目录 [lsp-bridge/multiserver](https://github.com/manateelazycat/lsp-bridge/tree/master/multiserver) 中查找
2. ```lsp-bridge-multi-lang-server-extension-list```: 根据文件的扩展名来返回多服务器配置名, 比如打开*.vue 文件时,我们会使用 ```volar_emmet``` 来同时利用 `volar` 和 `emmet-ls` 两种 LSP 服务器提供补全
3. ```lsp-bridge-multi-lang-server-mode-list```: 根据 Emacs 的 major-mode 来返回对应的多服务器配置名
## 添加新的编程语言支持?
1. 在 lsp-bridge/langserver 目录下创建配置文件, 比如`pyright.json`就是 pyright 服务器的配置文件 (windows 平台用`pyright_windows.json`, macOS 平台用`pyright_darwin.json`)。
2. 添加 `(mode . server_name)` 到 `lsp-bridge.el` 文件中的 `lsp-bridge-single-lang-server-mode-list` 选项中, 比如 `(python-mode . "pyright")`。
3. 添加新的 mode-hook 到 `lsp-bridge.el` 文件中的 `lsp-bridge-default-mode-hooks` 选项中。
4. 添加新的缩进变量到 `lsp-bridge.el` 文件中的 `lsp-bridge-formatting-indent-alist` 选项中。
欢迎发送补丁帮助我们支持更多的 LSP 服务器,感谢你的帮助!
## 已经支持的语言服务器
你需要安装每个编程语言对应的 LSP 服务器, lsp-bridge 才能提供代码补全服务。
| 序号 | LSP 服务器 | 语言 | 备注 |
| :--- | :--- | :--- | :--- |
| 1 | [clangd](https://github.com/clangd/clangd) | c, c++, object-c | |
| 2 | [pyright](https://github.com/microsoft/pyright) | python | `pip install pyright` |
| 3 | [solargraph](https://github.com/castwide/solargraph) | ruby | |
| 4 | [rust-analyzer](https://github.com/rust-lang/rust-analyzer) | rust | |
| 5 | [elixirLS](https://github.com/elixir-lsp/elixir-ls) | elixir | 请确保导出 `elixir-ls` 目录到你系统的 PATH 路径 |
| 6 | [gopls](https://github.com/golang/tools/tree/master/gopls) | go | 确保安装 [go-mode](https://github.com/dominikh/go-mode.el), 同时确保 gopls 在 PATH 环境变量中, 执行命令 `ln -s ~/go/bin/gopls ~/.local/bin`, 还要在补全之前执行 `go mod init` 命令 |
| 7 | [hls](https://github.com/haskell/haskell-language-server) | haskell | |
| 8 | [dart-analysis-server](https://github.com/dart-lang/sdk/tree/master/pkg/analysis_server) | dart | |
| 9 | [metals](https://scalameta.org/metals/) | scala | |
| 10 | [typescript](https://github.com/typescript-language-server/typescript-language-server) | typescript, javascript | |
| 11 | [ocamllsp](https://github.com/ocaml/ocaml-lsp) | ocaml | |
| 12 | [erlang-ls](https://github.com/erlang-ls/erlang_ls) | erlang | |
| 13 | [texlab](https://github.com/latex-lsp/texlab) | latex | |
| 14 | [eclipse.jdt.ls](https://projects.eclipse.org/projects/eclipse.jdt.ls) | java | 请确保导出 `org.eclipse.jdt.ls.product/target/repository/bin` 到你系统的 PATH 路径 |
| 15 | [clojure-lsp](https://github.com/clojure-lsp/clojure-lsp) | clojure | 如果使用 `homebrew` 安装的,请确保安装的是 `clojure-lsp/brew/clojure-lsp-native` [clojure-lsp-native](https://clojure-lsp.io/installation/#homebrew-macos-and-linux) |
| 16 | [bash-language-server](https://github.com/bash-lsp/bash-language-server) | bash | |
| 17 | [volar](https://github.com/johnsoncodehk/volar) | vue | npm install typescript volar -g |
| 18 | [sumneko](https://github.com/sumneko/lua-language-server) | lua | 请确保导出 sumneko 的 `bin` 目录到你系统的 PATH 路径 |
| 19 | [wxml-language-server](https://github.com/chemzqm/wxml-languageserver) | wxml | |
| 20 | [vscode-html-language-server](https://github.com/hrsh7th/vscode-langservers-extracted) | html | |
| 21 | [vscode-css-language-server](https://github.com/hrsh7th/vscode-langservers-extracted) | css | |
| 22 | [elm-language-server](https://github.com/elm-tooling/elm-language-server) | elm | |
| 23 | [intelephense](https://github.com/bmewburn/vscode-intelephense) | php | |
| 24 | [yaml-language-server](https://github.com/redhat-developer/yaml-language-server) | yaml | `npm install -g yaml-language-server` |
| 25 | [zls](https://github.com/zigtools/zls) | zig | 运行 `zls config` 来生成 zls 的配置。参考 [Configuration Options](https://github.com/zigtools/zls#configuration-options) |
| 26 | [groovy-language-server](https://github.com/GroovyLanguageServer/groovy-language-server) | groovy | 在 PATH 中创建一个名为 "groovy-language-server" 的脚本, 内容为 `$JAVA_HOME/bin/java -jar /groovy-language-server-all.jar` |
| 27 | [docker-language-server](https://github.com/rcjsuen/dockerfile-language-server-nodejs) | Dockerfiles | |
| 28 | [serve-d](https://github.com/Pure-D/serve-d) | d | serve-d 不支持单文件模式, 使用前请先在项目目录下初始 git 仓库或者自定义 `lsp-bridge-get-project-path-by-filepath` 返回项目目录 |
| 29 | [fortls](https://github.com/gnikit/fortls) | Fortran | |
| 30 | [ccls](https://github.com/MaskRay/ccls) | c, c++, object-c | `lsp-bridge-c-lsp-server` 设置成 `ccls` |
| 31 | [jedi](https://github.com/pappasam/jedi-language-server) | python | `lsp-bridge-python-lsp-server` 设置成 `jedi` |
| 32 | [emmet-ls](https://github.com/aca/emmet-ls) | html, js, css, sass, scss, less | |
| 33 | [rnix-lsp](https://github.com/nix-community/rnix-lsp) | nix | |
| 34 | [digestif](https://github.com/astoff/digestif) | latex | `lsp-bridge-tex-lsp-server` 设置成 `digestif` |
| 35 | [rlanguageserver](https://github.com/REditorSupport/languageserver) | R | |
| 36 | [graphql-lsp](https://github.com/graphql/graphiql/tree/main/packages/graphql-language-service-cli) | GraphQL | |
| 37 | [microsoft-python-language-server](https://github.com/microsoft/python-language-server) | Python | 支持 Python2 的 lsp |
| 38 | [cmake-language-server](https://github.com/regen100/cmake-language-server) | cmake | `pip install cmake-language-server` |
| 39 | [Wen](https://github.com/metaescape/Wen) | org-mode | `pip install pygls pypinyin` |
| 40 | [sourcekit-lsp](https://github.com/apple/sourcekit-lsp) | swift | Sourcekit-lsp 包含在 swift toolchain 中。 |
| 41 | [omnisharp](https://github.com/OmniSharp/omnisharp-roslyn) | c# | OmniSharp dotnet 平台的 lsp. 使用 `M-x lsp-bridge-install-omnisharp` 安装 |
### 不会支持的特性:
lsp-bridge 的目标是实现 Emacs 生态中性能最快的 LSP 客户端, 但不是实现 LSP 协议最全的 LSP 客户端。
下面的功能用 Emacs 现有生态做更好:
1. 语法高亮: [Tree-sitter](https://tree-sitter.github.io/tree-sitter/) 是一个静态高性能的语法分析库,比 LSP 更适合完成语法高亮
2. Xref: Xref 的机制是同步等待, lsp-bridge 是完全异步的, 两个机制无法融合, 请使用 `lsp-bridge-find-references` 来查看代码引用。
## 加入开发
下图是 lsp-bridge 的架构设计:
下面是 lsp-bridge 项目的目录结构:
| 文件名 | 作用 |
| :--------------------- | :------------------- |
| lsp-bridge.el | lsp-bridge 的 Elisp 主逻辑部分,提供自定义选项和 Elisp 函数供 python 子进程调用,比如代码跳转、重命名等 |
| lsp-bridge-epc.el | 和 lsp-bridge python 子进程通讯的代码,主要实现 Elisp IPC 来对接 Python EPC, 实现数据序列化、发送、接收和反序列化 |
| lsp-bridge-ref.el | 代码引用查看框架,提供引用查看、批量重命名、引用结果正则过滤等,核心代码 fork 自 color-rg.el |
| lsp-bridge-jdtls.el | 提供 Java 语言第三方库跳转功能 |
| lsp-bridge-lsp-installer.el | 安装 TabNine 和 Omnisharp |
| lsp-bridge.py | lsp-bridge 的 Python 主逻辑部分,提供事件循环、消息调度和状态管理 |
| acm/acm.el | 异步补全菜单, 专门为 lsp-bridge 后端而设计, 支持 lsp, elisp, words, TabNine 等后端 |
| core/fileaction.py | 主要记录每个文件状态,处理 LSP 响应消息,调用 Emacs Elisp 函数 |
| core/lspserver.py | LSP 消息处理模块,主要是解析、发送和接受 LSP 消息,并保证 LSP 请求顺序符合 LSP 协议规范 |
| core/utils.py | 一些全局工具函数,方便各模块调用 |
| core/mergedeep.py | JSON 信息合并, 主要用于发送自定义选项给 LSP 服务器 |
| core/hanlder/ | LSP 消息发送和接受的实现,其中 __init__.py 是基类 |
| langserver | 主要放置 LSP 服务器的配置,每一个服务器一个 json 文件,分别定义服务器的名称、语言 ID、启动命令和设置选项等 |
请先阅读下面的文章:
* [LSP 协议规范](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/)
* [lsp-bridge 架构设计](https://manateelazycat.github.io/emacs/2022/05/12/lsp-bridge.html)
* [为什么 lsp-bridge 不用 capf](https://manateelazycat.github.io/emacs/2022/06/26/why-lsp-bridge-not-use-capf.html)
接着打开开发选项 ```lsp-bridge-enable-log``` , happy hacking! ;)
## 反馈问题
请用命令 `emacs -q` 并只添加 lsp-bridge 配置做一个对比测试,如果 `emacs -q` 可以正常工作,请检查你个人的配置文件。
如果`emacs -q`环境下问题依旧,请到[这里](https://github.com/manateelazycat/lsp-bridge/issues/new)反馈, 并附带 `*lsp-bridge*` 窗口的内容给我们提交 issue,那里面有很多线索可以帮助我们排查问题。。
如果你遇到崩溃的问题, 请用下面的方式来收集崩溃信息:
1. 先安装 gdb 并打开选项 `(setq lsp-bridge-enable-debug t)`
2. 使用命令 `lsp-bridge-stop-process` 停止 LSP-BRIDGE 进程
3. 重新打开 lsp-bridge, 并在下次崩溃时发送 `*lsp-bridge*` 的内容
## 贡献者