- Language Server Protocol Support for Emacs
- ❤️ Community Driven
- 💎 Fully featured - supports LSP core and Language Server non-standard extensions
- 🚀 Easy to configure - works out of the box and automatically upgrades if additional packages are present
- 🌟 Flexible - could be configured as full-blown IDE with flashy UI or minimal distraction free
Client for Language Server Protocol (v3.14). lsp-mode aims to provide IDE-like experience by providing optional integration with the most popular Emacs packages like company
, flycheck
and projectile
.
- Non-blocking asynchronous calls
- Real-time Diagnostics/linting (via builtin
flymake
when Emacs > 26 or flycheck/lsp-ui) - Code completion - using company-lsp or builtin
completion-at-point
- Hovers - using lsp-ui
- Code actions - using
lsp-execute-code-action
or lsp-ui - Code outline - using builtin imenu or
helm-imenu
- Code navigation - using builtin xref
- Code lens (references/implementations) - using builtin xref
- Highlights
- Formatting
- Debugger - dap-mode
- Helm integration - helm-lsp
- Treemacs integration - lsp-treemacs
- Semantic highlighting (as currently implemented by JDT LS and unreleased builds of clangd, cf. Semantic highlighting spec)
The recommended way to install lsp-mode
is via package.el
- the built-in package manager in Emacs. lsp-mode
is available on the two major package.el
community maintained repos - MELPA Stable and MELPA.
M-x package-install
[RET] lsp-mode
[RET]
Check the table bellow with the list of supported servers and the corresponding instructions on how to install the server.
You could go minimal and use lsp-mode
as it is without external packages with the built-in flymake
and completion-at-point
or you could install the following extensions for better experience:
- install lsp-ui for flycheck integration and higher level UI modules.
- install company-lsp if you want to use
company-mode
for completion. - install lsp-treemacs for project wide error overview.
- install helm-lsp provides on type completion for
xref-apropos
. - install dap-mode if your language is supported by the debugger.
(require 'lsp-mode)
(add-hook 'XXX-mode-hook #'lsp)
where XXX
could be major mode like python
, java
, c++
. Alternatively, if you want to minimize you configuration you may use prog-mode-hook
. In case you do that, lsp
will try to start for each programming mode and echo a message when there is no client registered for the current mode or if the corresponding server is not present. In addition, lsp-mode
will automatically detect and configure lsp-ui and company-lsp. To turn off that behavior you could set lsp-auto-configure
to nil
.
To defer LSP server startup (and DidOpen notifications) until the buffer is visible you can use lsp-deferred
instead of lsp
:
(add-hook 'XXX-mode-hook #'lsp-deferred)
lsp-mode is included in spacemacs develop branch. Add lsp
to dotspacemacs-configuration-layers
and configure the language that you want to use to be backed by lsp
backend.
Replace (require 'lsp-mode)
with the following if you use use-package.
(use-package lsp-mode
:hook (XXX-mode . lsp)
:commands lsp)
;; optionally
(use-package lsp-ui :commands lsp-ui-mode)
(use-package company-lsp :commands company-lsp)
(use-package helm-lsp :commands helm-lsp-workspace-symbol)
(use-package lsp-treemacs :commands lsp-treemacs-errors-list)
;; optionally if you want to use debugger
(use-package dap-mode)
;; (use-package dap-LANGUAGE) to load the dap adapter for your language
To defer LSP server startup (and DidOpen notifications) until the buffer is visible you can use lsp-deferred
instead of lsp
:
(use-package lsp-mode
:hook (XXX-mode . lsp-deferred)
:commands (lsp lsp-deferred))
Refer to lsp-docker README which provide a guide how you can run lsp-mode
in docker
container.
lsp-mode
has predefined list of server configurations (loaded in lsp-clients.el
) containing a mapping from major-mode
to the server configuration or by using activation function. In addition to the default server configuration located in lsp-clients.el
there are few languages servers which require separate package(check Supported languages). When you open a file from a particular project lsp-mode
and call lsp
command lsp-mode
will look for server registrations able to handle current file. If there is such client lsp-mode
will look for the project root. If you open a file from the project for the first time you will be prompted to define the current project root. Once the project root is selected it is saved in lsp-session
file and it will be loaded the next time you start Emacs so you no longer will be asked for a project root when you open a file from that project. Later if you want to change the project root you may use lsp-workspace-folder-remove
to remove the project and call lsp-workspace-folder-add
to add the root. If you want to force starting a particular language server in a file you may use C-u
M-x
lsp
which will prompt you to select language server to start.
Some of the servers are directly supported by lsp-mode
by requiring
lsp-clients.el
while others require installing additional packages which provide
server specific functionality.
Language | Language Server | Built-in | Installation command | Debugger |
---|---|---|---|---|
Angular | vscode-ng-language-service | Yes | Installation instructions | Not relevant |
Bash | bash-language-server | Yes | npm i -g bash-language-server | |
C++ | ccls | emacs-ccls | ccls | Yes (gdb or lldb) |
C++ | clangd | Yes | clangd | Yes (gdb or lldb) |
C++ | cquery | emacs-cquery | cquery | Yes (gdb or lldb) |
C# | OmniSharp-Roslyn | Yes | OmniSharp-Roslyn | No |
Clojure | clojure-lsp | Yes | clojure-lisp | |
CSS/LessCSS/SASS/SCSS | css | Yes | npm install -g vscode-css-languageserver-bin | |
Dart | dart_analysis_server | Yes | built into dart-sdk | |
Dockerfile | dockerfile-language-server-nodejs | Yes | npm install -g dockerfile-language-server-nodejs | |
Dart | dart_language_server | Yes | pub global activate dart_language_server | |
Elixir | elixir-ls | Yes | elixir-ls | Yes |
Elm | elmLS | Yes | npm i -g @elm-tooling/elm-language-server, or clone the repository and follow installation instructions | No |
Erlang | erlang_ls | Yes | erlang_ls | |
F# | fsautocomplete | Yes | Automatic by lsp-fsharp | No |
Fortran | fortran-language-server | Yes | pip install fortran-language-server | Yes |
Go | gopls | Yes | gopls go get golang.org/x/tools/gopls@latest | Yes |
Go | bingo | Yes | bingo | Yes |
Groovy | groovy-language-server | Yes | groovy-language-server | |
Hack | hhvm | Yes | hhvm | |
HTML | html | Yes | npm install -g vscode-html-languageserver-bin | |
Haskell | IDE engine | lsp-haskell | IDE engine | |
Lua | EmmyLua | Yes | Installation | |
Java | Eclipse JDT LS | lsp-java | Automatic by lsp-java | Yes |
JavaScript/TypeScript | typescript-language-server (recommended) | Yes | npm i -g typescript-language-server; npm i -g typescript | Yes (Firefox/Chrome) |
JavaScript/TypeScript | javascript-typescript-stdio | Yes | npm i -g javascript-typescript-langserver | Yes (Firefox/Chrome) |
JavaScript Flow | flow (add-on if working on a Flow file) | Yes | flow | Yes (Firefox/Chrome) |
Julia | lsp-julia | lsp-julia | LanguageServer.jl | |
Kotlin | kotlin-language-server | Yes | kotlin-language-server | |
Ocaml | ocaml-language-server | Yes | ocaml-language-server | |
PHP(recommended) | intelephense | Yes | npm i intelephense -g | Yes |
PHP | php-language-server | Yes | php-language-server | Yes |
Powershell | PowerShellEditorServices | lsp-powershell | Automatic by lsp-powershell | |
Python | pyls | Yes | pip install ‘python-language-server[all]’ | Yes |
Python(Microsoft) | Microsoft Python Language Server | lsp-python-ms | lsp-python-ms | Yes |
Ruby | solargraph | Yes | gem install solargraph | Yes |
Rust | rls | Yes | rls | Yes |
Scala | Metals | Yes | Metals | |
Swift | sourcekit-LSP | lsp-sourcekit | sourcekit-LSP | Yes (via llvm debug adapter) |
TeX, LaTeX, etc. | Digestif | Yes | luarocks install –server=http://luarocks.org/dev digestif | |
Vue | vue-language-server | Yes | npm install -g vue-language-server | Yes (Firefox/Chrome) |
XML | lsp4xml | Yes | Download from lsp4xml releases |
lsp-describe-session
- Display session folders and running servers.lsp-describe-thing-at-point
- Display help for the thing at point.lsp-execute-code-action
- Execute code actionlsp-format-buffer
- Format current bufferlsp-organize-imports
- Organize library importslsp-goto-implementation
- Go to implementationlsp-goto-type-definition
- Go to type definitionlsp-rename
- Rename symbol at pointlsp-restart-workspace
- Restart projectlsp-symbol-highlight
- Highlight all relevant references to the symbol under point.lsp-workspace-folders-add
- Add workspace folderlsp-workspace-folders-remove
- Remove workspace folderlsp-workspace-folders-switch
- Switch workspace folderimenu
orhelm-imenu
- display document structure.completion-at-point
- display completion using built-in emacscompletion-at-point
framework.lsp-find-definition
- to find the definition for the symbol under point.lsp-find-references
- Find references for the symbol under point.lsp-disconnect
- Disconnect the buffer from the language server.lsp-lens-show
- Show lenses in the current filelsp-lens-hide
- Hide lenses in the current filelsp-lens-mode
(experimental) - Turn on/off lenses in the current file.
lsp-log-io
- If non-nil, print all messages to and from the language server to*lsp-log*
.lsp-print-performance
- If non-nil, print performance info. to*lsp-log*
.lsp-inhibit-message
- If non-nil, inhibit the message echo viainhibit-message
.lsp-report-if-no-buffer
- If non nil the errors will be reported even when the file is not open.lsp-keep-workspace-alive
- If non nil keep workspace alive when the last workspace buffer is closed.lsp-enable-snippet
- Enable/disable snippet completion support.lsp-auto-guess-root
- Automatically guess the project root using projectile/project.lsp-restart
- Defines how server exited event must be handled.lsp-session-file
- File where session information is stored.lsp-auto-configure
- Auto configurelsp-mode
. When set to tlsp-mode
will auto-configurelsp-ui
andcompany-lsp
.lsp-document-sync-method
- How to sync the document with the language server.lsp-auto-execute-action
- Auto-execute single action.lsp-eldoc-render-all
- Display all of the info returned bydocument/onHover
. If this is nil,eldoc
will show only the symbol information.lsp-signature-render-all
- Display all of the info returned bytextDocument/signatureHelp
. If this is nil,eldoc
will show only the active signature.lsp-enable-completion-at-point
- Enablecompletion-at-point
integration.lsp-enable-xref
- Enable xref integration.lsp-prefer-flymake
- If you prefer flycheck andlsp-ui-flycheck
is available,(setq lsp-prefer-flymake nil)
. If set to:none
neither of two will be enabled.lsp-enable-indentation
- Indent regions using the file formatting functionality provided by the language server.lsp-enable-on-type-formatting
- EnabletextDocument/onTypeFormatting
integration.lsp-before-save-edits
- If non-nil,lsp-mode
will apply edits suggested by the language server before saving a document.lsp-imenu-show-container-name
- Display the symbol’s container name in an imenu entry.lsp-imenu-container-name-separator
- Separator string to use to separate the container name from the symbol while displaying imenu entries.lsp-imenu-sort-methods
- How to sort the imenu items. The value is a list ofkind
,name
orposition
. Priorities are determined by the index of the element.lsp-response-timeout
- Number of seconds to wait for a response from the language server before timing out.lsp-enable-file-watchers
- If non-nil lsp-mode will watch the files in the workspace if the server has requested that.lsp-server-trace
- Request trace mode on the language server.lsp-enable-semantic-highlighting
- Enable experimental semantic highlighting support
- RUST Completion with company-lsp
- Typescript references using lsp-ui
- Debugging Python using dap-mode
- Call hierarchy via ccls
- Metals Doctor
- Semantic highlighting as provided by clangd (built from unreleased 10.0 branch). In this screenshot, all other font-locking has been disabled (hence no syntax highlighting of comments or basic keywords such as
auto
)
LSP mode has support for tramp buffers with the following requirements:
- The language server has to be present on the remote server.
- Having multi folder language server (like Eclipse JDT LS) cannot have local and remote workspace folders.
lsp-mode
detects whether a particular file is located on remote machine and looks for a client which matches current file and it is marked as :remote?
t. Then lsp-mode
starts the client through tramp.
Here it is example how you can configure python language server to work when using TRAMP
. Note that if you are trying to convert existing language server configuration you should copy all of it’s properties(e. g. :request-handlers
, activation-fn
, etc).
(lsp-register-client
(make-lsp-client :new-connection (lsp-tramp-connection "binary-or-full-path")
:major-modes '(python-mode)
:remote? t
:server-id 'pyls-remote))
With TRAMP, Emacs does not have an easy way to distinguish stdout and stderr, so when the underlying LSP process writes to stderr, it breaks the lsp-mode
parser. As a workaround, lsp-mode
is redirecting stderr to /tmp/<process-name>-<id>~stderr
.
When some of the workspaces that are active in the current project requests file notifications via workspace/didChangeWatchedFiles
lsp-mode
will start monitoring each of the folders in the workspace for changes. In case your project contains a lot of files you might want to disable file monitoring via lsp-enable-file-watchers
(you may use dir-locals).
Contributions are very much welcome.
Here is a throughput graph of the repository for the last few weeks:
- set
lsp-log-io
tot
to inspect communication between client and the server. lsp-describe-session
will show the current projects roots + the started severs and allows inspecting the server capabilities.
Here it is the minimal configuration that is needed for new language server registration. Refer to the documentation of lsp-client.el
for the additional settings supported on registration time. lsp-language-id-configuration
must be updated to contain the corresponding mode -> language id - in this case (python-mode . "python")
(defvar lsp-language-id-configuration
'(...
(python-mode . "python")
...))
;; if you are adding the support for your language server in separate repo use
;; (add-to-list 'lsp-language-id-configuration '(python-mode . "python"))
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection "pyls")
:major-modes '(python-mode)
:server-id 'pyls))
lsp-mode
provides tools to bridge emacs defcustom
as a language configuration sections properties(see specification workspace/configuration). In addition you may use lsp-generate-settings
from Generate Settings script to generate defcustom
from package.json
VScode plugin manifest. Example:
(defcustom lsp-foo-language-server-property "bar"
"Demo property."
:group 'foo-ls
:risky t)
(lsp-register-custom-settings '(("foo.section.property" lsp-foo-language-server-property)))
(lsp-configuration-section "foo")
;; => (("foo" ("settings" ("property" . "bar"))))
- How to configure a server with local variables?
- Add
lsp
server call tohack-local-variables-hook
which runs right after the local variables are loaded.(add-hook 'hack-local-variables-hook (lambda () (when (derived-mode-p 'XXX-mode) (lsp))))
- Add
- I have multiple language servers registered for language FOO. Which one will be used when opening a project?
- The one with highest priority wins.
lsp-clients.el
predefined servers have priority -1, lower than external packages (priority 0 if unspecified). If a server is registered with:add-on?
flag set tot
it will be started in parallel to the other servers that are registered for the current mode.
- The one with highest priority wins.
- I have multiple language servers for language
FOO
and I want to select the server per project, what can I do?- You may create
dir-local
for each of the projects and specify list oflsp-enabled-clients
. This will narrow the list of the clients that are going to be tested for the project.
- You may create
- The completion does not work fine and inserts arguments and placeholders, what I am doing wrong?
- Snippet support works only with
company-lsp
so if you are usingcompletion-at-point
the snippets won’t be expanded and you should either disable them by settinglsp-enable-snippet
tonil
or you should switch tocompany-lsp
. Note also thatcompany-tng
frontend does not support snippet expansion(see company-mode#891)
- Snippet support works only with
- How to automatically follow
lsp-ui-log
?- Go into the log buffer and execute the following snippet(source: Emacs auto scrolling log buffer)
(set (make-local-variable 'window-point-insertion-type) t)
- lsp-docker - provide docker image with preconfigured language servers with corresponding emacs configuration.
- company-box -
company
frontend with icons. - dap-mode - Debugger integration for
lsp-mode
. - eglot - An alternative minimal LSP implementation.