# lsp-bridge
**Repository Path**: manateelazycat/lsp-bridge
## Basic Information
- **Project Name**: lsp-bridge
- **Description**: Fastest LSP client for Emacs
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 6
- **Forks**: 0
- **Created**: 2022-05-17
- **Last Updated**: 2022-08-24
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
[English](./README.md) | 简体中文
# lsp-bridge
lsp-bridge的目标是实现Emacs生态中性能最快的LSP客户端。
lsp-bridge使用Python多线程技术在Emacs和LSP服务器之间构建高速缓存,在提供行云流水的代码补全体验的前提下,保证永远不会卡住Emacs。
## 安装
1. 安装Python依赖: [python-epc](https://github.com/tkf/python-epc)
2. 安装Elisp依赖:
+ [posframe](https://github.com/tumashu/posframe)
+ [markdown-mode](https://github.com/jrblevin/markdown-mode)
+ [yasnippet](https://github.com/joaotavora/yasnippet)
3. 用 `git clone` 下载此仓库,并替换下面配置中的 load-path 路径
4. 把下面代码加入到你的配置文件 ~/.emacs 中:
```elisp
(add-to-list 'load-path "")
(require 'yasnippet)
(require 'lsp-bridge)
(require 'lsp-bridge-jdtls) ;; 提供Java第三方库跳转和-data目录支持, Java用户必选
(yas-global-mode 1)
(global-lsp-bridge-mode)
```
## 使用
lsp-bridge开箱即用, 安装好文件对应的LSP服务器命令以后, 直接写代码即可, 不需要额外的设置。
需要注意的是 lsp-bridge 有两种模式:
1. 检测到.git目录时(通过命令 `git rev-parse --is-inside-work-tree` 来判断), lsp-bridge会扫描整个目录文件来提供补全
2. 没有检测到.git目录时, lsp-bridge只会对打开的文件提供单文件补全
如果你期望lsp-bridge能够自动扫描整个项目的文件, 请在项目根目录执行`git init`命令。
## 命令列表
* `lsp-bridge-find-def`: 跳转到定义位置
* `lsp-bridge-find-def-other-window`: 在其他窗口跳转到定义位置
* `lsp-bridge-find-impl`: 跳转到接口实现位置
* `lsp-bridge-find-impl-other-window`: 在其他窗口跳转到接口实现位置
* `lsp-bridge-return-from-def`: 返回跳转之前的位置
* `lsp-bridge-find-references`: 查看代码引用
* `lsp-bridge-lookup-documentation`: 查看光标处的文档
* `lsp-bridge-popup-documentation-scroll-up`: 文档窗口向上滚动
* `lsp-bridge-popup-documentation-scroll-down`: 文档窗口向下滚动
* `lsp-bridge-rename`: 重命名
* `lsp-bridge-jump-to-next-diagnostic`: 跳转到下一个诊断位置
* `lsp-bridge-jump-to-prev-diagnostic`: 跳转到上一个诊断位置
* `lsp-bridge-list-diagnostics`: 列出所有诊断信息
* `lsp-bridge-ignore-current-diagnostic`: 插入注视忽略当前诊断
* `lsp-bridge-signature-help-fetch`: 在minibuffer显示参数信息
* `lsp-bridge-insert-common-prefix`: 插入补全后选词的公共前缀
* `lsp-bridge-restart-process`: 重启lsp-bridge进程 (一般只有开发者才需要这个功能)
* `acm-doc-scroll-up`: API文档窗口向上滚动
* `acm-doc-scroll-down`: API文档窗口向下滚动
## 选项
* `lsp-bridge-completion-popup-predicates`: 补全菜单显示的检查函数, 这个选项包括的所有函数都检查过以后, 补全菜单才能显示
* `lsp-bridge-completion-stop-commands`: 这些命令执行以后,不再弹出补全菜单
* `lsp-bridge-completion-hide-characters`: 这些字符的后面不再弹出补全菜单
* `lsp-bridge-diagnostics-fetch-idle`: 诊断延迟,默认是停止敲键盘后1秒开始拉取诊断信息
* `lsp-bridge-enable-diagnostics`: 代码诊断, 默认打开
* `lsp-bridge-enable-candidate-doc-preview`: 支持后选词文档预览, 默认打开
* `lsp-bridge-enable-signature-help`: 支持函数参数显示, 默认关闭
* `lsp-bridge-org-babel-lang-list`: 支持org-mode代码块补全的语言列表
* `lsp-bridge-disable-backup`: 禁止emacs对文件做版本管理, 默认打开
* `lsp-bridge-enable-log`: 启用LSP消息日志, 默认关闭
* `lsp-bridge-enable-debug`: 启用程序调试, 默认关闭
* `lsp-bridge-python-command`: Python命令的路径, 如果你用 `conda`, 你也许会定制这个选项
* `acm-backend-lsp-enable-auto-import`: 支持自动导入, 默认打开
* `acm-candidate-match-function`: 补全菜单匹配算法, orderless-* 开头的算法需要额外安装 [orderless](https://github.com/oantolin/orderless)
* `acm-enable-doc`: 补全菜单是否显示帮助文档
* `acm-enable-icon`: 补全菜单是否显示图标
* `acm-fetch-candidate-doc-delay`: 补全菜单弹出文档的延时, 不建议设置成0, 会降低菜单选择性能
* `acm-snippet-insert-index`: 代码模板后选词在补全菜单中的显示位置
## 自定义语言服务器配置
lsp-bridge每种语言的服务器配置存储在[lsp-bridge/langserver](https://github.com/manateelazycat/lsp-bridge/tree/master/langserver).
你可以根据以下优先级顺序来自定义服务器配置:
1. ```lsp-bridge-get-lang-server-by-project```: 编写自己的自定义函数,根据工程目录和文件路径来返回自定义配置的路径, 默认这个函数为nil
2. ```lsp-bridge-lang-server-extension-list```: 根据文件的扩展名来返回服务器,比如打开*.vue文件时,我们会使用 ```volar``` 服务器替代 javascript-mode 匹配的 ```javascript``` 服务器
3. ```lsp-bridge-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-lang-server-mode-list` 选项中, 比如 `(python-mode . "pyright")`。
3. 添加新的 mode-hook 到 `lsp-bridge.el` 文件中的 `lsp-bridge-default-mode-hooks` 选项中。
欢迎发送补丁帮助我们支持更多的LSP服务器,感谢你的帮助!
## 已经支持的语言服务器
| 序号 | LSP服务器 | 语言 | 备注 |
| :--- | :--- | :--- | :--- |
| 1 | [clangd](https://github.com/clangd/clangd) | c, 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 | make sure gopls in PATH, please do `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://www.npmjs.com/package/typescript) | 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 | |
| 16 | [bash-language-server](https://github.com/bash-lsp/bash-language-server) | bash | |
| 17 | [volar](https://github.com/johnsoncodehk/volar) | vue | |
| 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` |
### 需要完成的功能:
- [ ] Code Action: 代码动作, 比如自动修复代码
### 不会支持的特性:
lsp-bridge的目标是实现Emacs生态中性能最快的LSP客户端, 但不是实现LSP协议最全的LSP客户端。
下面的功能用Emacs现有生态做更好:
1. 代码格式化: 每个LSP服务器都有自己的格式配置,使用Emacs内置的格式化工具,我们可以获得更细腻一致的格式化风格
2. 语法高亮: [Tree-sitter](https://tree-sitter.github.io/tree-sitter/) 是一个静态高性能的语法分析库,比LSP更适合完成语法高亮
2. Xref: Xref的机制是同步等待, lsp-bridge是完全异步的, 两个机制无法融合, 建议自己编写包装函数来统一按键
## 加入开发
下图是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.py | lsp-bridge的Python主逻辑部分,提供事件循环、消息调度和状态管理 |
| acm/acm.el | 异步补全菜单, 专门为 lsp-bridge 后端而设计, 支持lsp, elisp, words等后端 |
| 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-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*` 的内容
## 贡献者