English | 简体中文
lsp-bridge
Lsp-bridge's goal is to become the fastest LSP client in Emacs.
Lsp-bridge uses python's threading technology to build caches that bridge Emacs and LSP server. Lsp-bridge will provide smooth completion experience without compromise to slow down emacs' performance.
The video explains the principle of lsp-bridge
EmacsConf 2022 talk page |
---|
Installation
- Install Emacs 28 and above versions
- Install Python dependencies:
pip3 install epc orjson sexpdata six
(orjson is optional, orjson is based on Rust, providing faster JSON parsing performance) - Install Elisp dependencies:
- posframe
- markdown-mode
- yasnippet
- acm-terminal (optional, only for terminal user)
- Clone or download this repository (path of the folder is the
<path-to-lsp-bridge>
used below). - Add following code in your ~/.emacs:
(add-to-list 'load-path "<path-to-lsp-bridge>")
(require 'yasnippet)
(yas-global-mode 1)
(require 'lsp-bridge)
(global-lsp-bridge-mode)
NOTE:
- When use lsp-bridge, please disable other completion plugin first, such as lsp-mode, eglot, company, corfu, etc. lsp-bridge provides a full set from completion backend, completion frontend to multi-backend integration solution.
- When the completion menu pops up,
acm-mode
will be automatically enabled, and when the completion menu disappears,acm-mode
will be automatically disabled, please do not manually addacm-mode
to any mode-hook, also don't manually executeacm-mode
Usage
lsp-bridge is design for out the box. After installing the LSP server and mode plugin corresponding to the open file, you can write the code directly without additional settings.
It should be noted that there are three scan modes of lsp-bridge:
- When detecting the
.git
directory (check by commandgit rev-parse-is-inside-work-tree
), lsp-bridge scan the entire directory file to provide completion - When the
.git
directory was not detected, lsp-bridge only scan opened file to provide completion - Custom
lsp-bridge-get-project-path-by-filepath
function, the input parameter is the path string of opened file, the output parameter is the project directory path, lsp-bridge will scan project directory path to provide provide completion
Keymap
Key | Command | Description |
---|---|---|
Alt + n | acm-select-next | Select next candidate |
Down | acm-select-next | Select next candidate |
Alt + p | acm-select-prev | Select previous candidate |
Up | acm-select-prev | Select previous candidate |
Alt + , | acm-select-last | Select last candidate |
Alt + . | acm-select-first | Select first candidate |
Ctrl + m | acm-complete | Complete completion |
Return | acm-complete | Complete completion |
Tab | acm-complete | Complete completion |
Alt + h | acm-complete | Complete completion |
Alt + H | acm-insert-common | Insert common part of candidates |
Alt + u | acm-filter | Filter candidates with overlay string |
Alt + d | acm-doc-toggle | Enable or disable candidate documentation |
Alt + j | acm-doc-scroll-up | Scroll up candidate documentation |
Alt + k | acm-doc-scroll-down | Scroll down candidate documentation |
Alt + l | acm-hide | Hide completion menu |
Ctrl + g | acm-hide | Hide completion menu |
Alt + Number | acm-complete-quick-access | Selecting candidate quickly, you need enable acm-enable-quick-access first |
Commands
lsp-bridge-find-def
: jump to the definitionlsp-bridge-find-def-other-window
: jump to the definition in other-windowlsp-bridge-find-def-return
: return to the location before callinglsp-bridge-find-def
lsp-bridge-find-impl
: jump to the implementationlsp-bridge-find-impl-other-window
: jump to the implementation in other-windowlsp-bridge-find-references
: traverse across code references (forked from color-rg.el)lsp-bridge-popup-documentation
: lookup documentation of symbol under the cursorlsp-bridge-popup-documentation-scroll-up
: scroll up popup document.lsp-bridge-popup-documentation-scroll-down
: scroll down popup document.lsp-bridge-rename
: rename symbol under the cursorlsp-bridge-diagnostic-jump-next
: Jump to the next diagnostic positionlsp-bridge-diagnostic-jump-prev
: Jump to the previous diagnostic positionlsp-bridge-diagnostic-list
: List all diagnostic informationlsp-bridge-diagnostic-copy
: Copy the current diagnostic information to the clipboardlsp-bridge-diagnostic-ignore
: Insert comment to ignore the current diagnosislsp-bridge-workspace-list-symbols
: List all symbols in workspace and jump to the symbol definitionlsp-bridge-signature-help-fetch
: show signature help in minibuffer manually (move cursor to parameters area will show signature help automatically)lsp-bridge-popup-complete-menu
: Manually popup the completion menu, you only need this command when turn on optionlsp-bride-complete-manually
lsp-bridge-restart-process
: restart lsp-bridge process (only used for development)lsp-bridge-toggle-sdcv-helper
: Switch dictionary completion assistantacm-insert-common
: insert common prefix of candidatesacm-doc-scroll-up
: API document window scroll upacm-doc-scroll-down
: API document window scroll down
LSP server options
lsp-bridge-c-lsp-server
: C language server, you can chooseclangd
orccls
lsp-bridge-python-lsp-server
: Python language server, you can choosepyright
,jedi
,python-ms
,pylsp
lsp-bridge-php-lsp-server
: PHP language server, you can chooseintelephense
orphpactor
lsp-bridge-tex-lsp-server
: LaTeX language server, you can choosetaxlab
ordigestif
lsp-bridge-csharp-lsp-server
: C# language server, you can chooseomnisharp-mono
oromnisharp-dotnet
, note that you need to give execute permissions to the OmniSharp file
Options
lsp-bridge-python-command
: The path of the python command, if you useconda
, you may customize this option. Windows platform usingpython.exe
rather thanpython3
, if lsp-bridge can't work, try set topython3
lsp-bridge-complete-manually
: Only popup completion menu when user calllsp-bridge-popup-complete-menu
command, default is nillsp-bridge-get-workspace-folder
: You need to put multiple project in aworkspace
directory in Java before you can jump function defintion normally. This function can be customized, the function input is the project path and returns theworkspace
directory correspondinglsp-bridge-org-babel-lang-list
: list of language to support org-mode code block completion, need move cursor to code block and execute commandorg-edit-special
, then lsp-bridge can show completion menu in popup windowlsp-bridge-enable-diagnostics
: code diagnostic, enable by defaultlsp-bridge-enable-hover-diagnostic
: show diagnostic tooltip when cursor hover diagnostic place, disable by defaultlsp-bridge-enable-search-words
: index the word of the file, enable by defaultlsp-bridge-enable-auto-format-code
: automatic format code, disable by defaultlsp-bridge-enable-signature-help
: show function parameter in minibufer, enable by defaultlsp-bridge-enable-log
: enable LSP message log, disable by default, only enable this option for development purposes, usually do not turn on this option to avoid affecting performancelsp-bridge-enable-debug
: enable program debugging, disable by defaultlsp-bridge-disable-backup
: forbidden version manage of emacs, enable by defaultlsp-bridge-diagnostic-fetch-idle
: diagnostic delay, start pulling diagnostic information 0.5 second after stopping typinglsp-bridge-signature-show-function
: The function used for displaying signature info, default show message in minibuffer, setlsp-bridge-signature-posframe
to show signature info in framelsp-bridge-completion-popup-predicates
: the predicate function for completion menu, completion menu popup after all the functions passlsp-bridge-completion-stop-commands
: completion menu will not popup if these commands are executedlsp-bridge-completion-hide-characters
: completion menu will not popup when cursor after those charactersacm-markdown-render-font-height
: The font height of function documentation, default is 130acm-enable-doc
: Whether the complete menu display the help documentacm-enable-doc-markdown-render
: Richly render Markdown for completion popups, you can choose'async
,t
ornil
. When set to'async
, styles are applied asynchronously, chooset
, styles are applied synchronously and will slow down the completion speed, default isasync
acm-enable-icon
: Whether the complete menu shows the icon, macOS users need to add option--with-rsvg
to the brew command to install emacs to display SVG iconacm-enable-tabnine
: Enable tabnine support, enable by default,when enable need executelsp-bridge-install-tabnine
command to install TabNine, and it can be used. TabNine will consume huge CPUs, causing your entire computer to be slow. If the computer performance is not good, it is not recommended to enable this optionacm-enable-search-file-words
: Whether the complete menu display the word of the file, enable by defaultacm-enable-quick-access
: Whether the index is displayed behind the icon, you can quickly select the candidate through Alt + Number, disable by defaultacm-enable-yas
: yasnippet completion, enable by defaultacm-enable-citre
: Integration with citre(ctags). Enable this to add citre (ctags) backend (disabled by default)acm-doc-frame-max-lines
: Max line number of help documentation, default is 20acm-snippet-insert-index
: The display position of snippet candidate in the complementary menuacm-candidate-match-function
: The complete menu matching algorithm, the algorithm prefix of orderless-* needs to be installed additional orderlessacm-backend-lsp-candidate-min-length
: The minimum characters to trigger completion, default is 0acm-backend-lsp-enable-auto-import
: automatic insert import code, enable by defaultacm-backend-lsp-candidate-max-length
: Maximum length of LSP candidate, some language, such as Java, argument list is very long, you can increase the value of this option to see clear argument listacm-backend-yas-candidates-number
: yasnippet display number,2 by defaultacm-backend-citre-keyword-complete
: Completion is performed according to the keywords of each mode defined byacm-backend-citre-keywords-alist
, which takes effect only after citre is enabled.acm-backend-search-sdcv-words-dictionary
: StarDict dictionary for word completion, default iskdic-ec-11w
, you can replace it with StarDict dictionary path, example, if you have dictionary/usr/share/stardict/dic/stardict-oxford-gb-formated-2.4.2/oxford-gb-formated.ifo
, you need set this value to/usr/share/stardict/dic/stardict-oxford-gb-formated-2.4.2/oxford-gb-formated
, not include.ifo
extension.
Customize language server configuration
The default configuration of for each language server is stored at lsp-bridge/langserver.
In most cases, you can customize server configuration with the following priority:
lsp-bridge-get-single-lang-server-by-project
: The user custom function, the input parameter isproject-path
andfile-path
, return the corresponding LSP server string, you can query all LSP servers in the list oflsp-bridge-single-lang-server-mode-list
option, this function return nil defaultlsp-bridge-single-lang-server-extension-list
: load server configuration based on file extension, such as, we launchwxml
server when open *.wxml filelsp-bridge-single-lang-server-mode-list
: load server configuration based on major-mode
If you are writing JavaScript code, you may need to customize multi-server configuration:
lsp-bridge-get-multi-lang-server-by-project
: The user custom function, the input parameter isproject-path
andfile-path
, return the multi-server configuration name, you can find configuration name in the subdirectory lsp-bridge/multiserverlsp-bridge-multi-lang-server-extension-list
: Return multi-server configuration name according to the expansion of the file, for example, when opening the *.vue file, we will use thevolar
andemmet-ls
to provide completion servicelsp-bridge-multi-lang-server-mode-list
: Return the corresponding multi-server configuration name according to Emacs's major-mode
For example, we can enable the Deno LSP server for the Deno script with the following configuration:
(setq lsp-bridge-get-single-lang-server-by-project
(lambda (project-path filepath)
;; If typescript file include deno.land url, then use Deno LSP server.
(save-excursion
(when (string-equal (file-name-extension filepath) "ts")
(dolist (buf (buffer-list))
(when (string-equal (buffer-file-name buf) filepath)
(with-current-buffer buf
(goto-char (point-min))
(when (search-forward-regexp (regexp-quote "from \"https://deno.land") nil t)
(return "deno")))))))))
Add support for new language?
- Create configuration file under lsp-bridge/langserver, such as
pyright.json
for pyright (windows user please usespyright_windows.json
, macOS user please usespyright_darwin.json
). - Add
(mode . server_name)
tolsp-bridge-single-lang-server-mode-list
inlsp-bridge.el
, such as(python-mode . "pyright")
. - Add new mode-hook to
lsp-bridge-default-mode-hooks
inlsp-bridge.el
. - Add new mode indent to
lsp-bridge-formatting-indent-alist
inlsp-bridge.el
.
Welcome to send PR to help us improve support for LSP servers, thanks for your contribution!
Supported language servers
You need to install the LSP server corresponding to each programming language, then lsp-bridge can provide code completion service.
LSP Server | Language | Note |
---|---|---|
clangd | C, C++, Object-C | |
ccls | C, C++, Object-C | lsp-bridge-c-lsp-server set to ccls |
[pyright](https://github.com/microsoft/pyright | Python | lsp-bridge-python-lsp-server set to pyright , pyright-background-analysis is faster sometimes, but it can't response diagnostic informations |
jedi | Python | lsp-bridge-python-lsp-server set to jedi |
python-ms | Python | Legacy language server for Python2 |
pylsp | Python | lsp-bridge-python-lsp-server set to pylsp |
solargraph | Ruby | |
rust-analyzer | Rust | |
elixirLS | Elixir | Please ensure that the elixir-ls release directory is in your system PATH at first |
gopls | Go | Make sure install go-mode and gopls in PATH, please do ln -s ~/go/bin/gopls ~/.local/bin , and do go mod init first |
hls | Haskell | |
dart-analysis-server | Dart | |
metals | Scala | |
typescript | Typescript, Javascript | |
ocamllsp | Ocaml | |
erlang-ls | Erlang | |
texlab | Latex | |
eclipse.jdt.ls | Java | Please ensure that org.eclipse.jdt.ls.product/target/repository/bin is in your system PATH at first |
clojure-lsp | Clojure | If you use homebrew , please ensure install clojure-lsp/brew/clojure-lsp-native clojure-lsp-native |
bash-language-server | Bash | |
volar | Vue | npm install -g typescript @volar/vue-language-server |
sumneko | Lua | Please ensure bin under sumneko installation is in your system PATH at first |
wxml-language-server | Wxml | |
vscode-html-language-server | HTML | npm i -g vscode-langservers-extracted |
vscode-css-language-server | CSS | npm i -g vscode-langservers-extracted |
vscode-eslint-language-server | JavaScript | npm i -g vscode-langservers-extracted |
vscode-json-language-server | JSON | npm i -g vscode-langservers-extracted |
elm-language-server | Elm | |
intelephense | PHP | |
Phpactor | PHP | lsp-brige-php-lsp-server set to phpactor |
yaml-language-server | Yaml | npm install -g yaml-language-server |
zls | Zig | Execute zls config to generate configuration for zls. see Configuration Options |
groovy-language-server | Groovy | Create a script "groovy-language-server" in PATH, with $JAVA_HOME/bin/java -jar <path>/groovy-language-server-all.jar |
docker-language-server | Dockerfiles | |
serve-d | D | serve-d does not support single file mode, please init .git repository under project root at first or custom lsp-bridge-get-project-path-by-filepath function |
fortls | Fortran | |
emmet-ls | HTML, JavaScript, CSS, SASS, SCSS, LESS | |
rnix-lsp | Nix | |
taxlab | Latex | lsp-bridge-tex-lsp-server set to taxlab |
digestif | Latex | lsp-bridge-tex-lsp-server set to digestif |
rlanguageserver | R | |
graphql-lsp | GraphQL | |
cmake-language-server | Cmake | pip install cmake-language-server |
Wen | Org-mode | pip install pygls pypinyin |
sourcekit-lsp | Swift | The SourceKit-LSP server is included with the Swift toolchain. |
omnisharp-mono | C# | OmniSharp is a .NET development platform based on Roslyn workspaces. use M-x lsp-bridge-install-omnisharp to install it. lsp-bridge-csharp-lsp-server set to omnisharp-mono |
omnisharp-dotnet | C# | OmniSharp is a .NET development platform based on Roslyn workspaces. use M-x lsp-bridge-install-omnisharp to install it. lsp-bridge-csharp-lsp-server set to omnisharp-dotnet (6.0) |
deno | Deno | Deno runtime use TypeScript as source code, you need customize option lsp-bridge-get-single-lang-server-by-project that return result "deno" when project-path match Deno project. |
ansible-language-server | Ansible | Ansible uses YAML as source code, you'll need to customize lsp-bridge-get-single-lang-server-by-project to return "ansible-language-server". |
Features that won't be supported
The goal of lsp-bridge is to become the fastest LSP client in Emacs, not a complete implementation of LSP protocol.
Emacs can do better for the following tasks, we will not reinvent the wheel in lsp-bridge:
- Syntax highlighting: Tree-sitter is a wonderful incremental parsing library for syntax highlighting.
- Xref: Xref's mechanism is synchronous, but lsp-bridge is completely asynchronous. Please use the
lsp-bridge-find-references
to view the code references.
Join development
The following is the framework of lsp-bridge:
The following is the directory structure of the lsp-bridge project:
File | Explanation |
---|---|
lsp-bridge.el | Elisp main logic part that provides custom options and elisp functions for python sub-process calls like code jumping, renaming, etc. |
lsp-bridge-epc.el | Communicating with lsp-bridge python sub-process, which mainly implements elisp IPC to connect to python EPC for data serialization, sending, receiving, and deserialization |
lsp-bridge-call-hierarchy.el | Show call hierarchy in popup frame. |
lsp-bridge-code-action.el | Code action functions |
lsp-bridge-diagnostic.el | Diagnostic functions |
lsp-bridge-ref.el | Framework of code referencing, providing references viewing, batch renames, regex filtering of reference results, etc. The core code is forked from color-rg.el |
lsp-bridge-jdtls.el | Provide java language third-party library jumping function |
lsp-bridge-lsp-installer.el | Install TabNine and Omnisharp |
lsp-bridge.py | Python main logic part that provides event loop, message scheduling and status management |
acm/acm.el | Asynchronous completion menu, specially designed for lsp-bridge backend, supports LSP, elisp, words, TabNine and other backend |
core/fileaction.py | Tracking the status of each file, processing LSP response messages, calling Emacs elisp function |
core/lspserver.py | LSP message processing module, mainly to analyze, send and receive LSP messages, and ensure that the sequence of LSP requests conforms with the LSP protocol specification |
core/utils.py | Utility functions of convenience for each module call |
core/mergedeep.py | JSON information merger is mainly used to send custom options to LSP server |
core/hanlder/ | The implementation of sending and receiving LSP message, where init.py is a base class |
langserver | The configurations of the LSP servers, each server corresponding to a JSON file that defines the name of the server, language ID, starting command, options, etc. |
Please read below articles first:
Then turn on develop option lsp-bridge-enable-log
and happy hacking! ;)
Develop a multi-threaded asynchronous completion backend
lsp-bridge builds the completion backend based on Python's multi-threading technology. With the support of multi-threading technology, no matter how much data you search, lsp-bridge will ensure that the completion experience smooth as butter. Please refer to the design of the existing backend (lsp-bridge/acm/acm-backend-*.el) for complicated backend.
For some small scenarios, such as a language that needs to add additional keyword completion, lsp-bridge provides some scaffolding code to help you quickly build your own asynchronous completion backend:
1. Cache keyword list
(lsp-bridge-call-async "search_list_update" "example" (list "keyword_a" "keyword_b" "keyword_c") 100 "lsp-bridge-example-record")
We can quickly cache the keyword list to the Python process of lsp-bridge through the interface function search_list_update
, where example
is the name of the completion backend, (list "keyword_a" "keyword_b" "keyword_c")
is the keyword list, 100
is the maximum number of search candidates, lsp-bridge-example-record
is the name of the callback function called after the search is completed.
2. Multi-threaded search and filter
(lsp-bridge-call-async "search_list_search" "example" "current_symbol")
After completing the keyword cache, search through the interface function search_list_search
, where example
is the name of the completion backend, and current_symbol
is the search keyword, which is generally the symbol at the cursor. When calling search_list_search
, lsp-bridge will automatically use sub-threads to search and filter, and automatically detect whether the search results have expired? If the search result is not expired, call the callback function lsp-bridge-example-record
to record the search result.
3. Asynchronous data pop-up completion
(defun lsp-bridge-example-record (candidates)
(setq-local acm-backend-example-items candidates)
(lsp-bridge-try-completion))
Generally, lsp-bridge-example-record
is defined in this way. After receiving candidates
returned by the asynchronous backend, first save the search results in the buffer, here is acm-backend-example-items
local variable (you need to define this variable yourself), and then call the function lsp-bridge-try-completion
, try to popup the completion menu.
Optimize Python performance
- Enable the profiler option: (setq lsp-bridge-enable-profile t)
- Restart lsp-bridge:
lsp-bridge-restart-process
- Write the code normally and complete the operation, the longer the better
- Output profiling log:
lsp-bridge-profile-dump
- Install snakeviz: sudo pip3 install snakeviz
- Show performance bottlenecks: snakeviz ~/lsp-bridge.prof
Report bug
Please use emacs -q
and load a minimal setup with only lsp-bridge to verify that the bug is reproducible. If emacs -q
works fine, probably something is wrong with your Emacs config.
If the problem still exists, please report it here with *lsp-bridge*
buffer content, it contains many clues that can help us locate the problem faster.
-
If you get a segfault error, please use the following way to collect crash information:
- Install gdb and turn on option
lsp-bridge-enable-debug
- Use the command
lsp-bridge-restart-process
to restart theLSP-BRIDGE
process - Send issue with
*lsp-bridge*
buffer content when next crash
- Install gdb and turn on option
-
If you get other errors, please use the following way to collection information:
- turn on option
lsp-bridge-enable-log
- Use the command
lsp-bridge-restart-process
to restart theLSP-BRIDGE
process - Send issue with
*lsp-bridge*
buffer content
- turn on option