Skip to content

Adding support for languages#

Register a server#

Your language server must be registered with lsp-mode in order for the lsp command to figure out how to communicate with it. This section explains how to do that.

The language being used is identified by either an Emacs major mode or by a regular expression matching the buffer's filename. A language server matching that language is then started.

Here's a minimal example using Python. First we tell lsp-mode that buffers in python-mode correspond to the language "python":

(add-to-list 'lsp-language-id-configuration '(python-mode . "python"))

If there is no major mode corresponding to your language, or the major mode is not sufficient to determine the language (e.g. web-mode is used for javascript, html, and css) you can register it using a regular expression that matches the filename instead:

(add-to-list 'lsp-language-id-configuration '(".*\\.svelte$" . "svelte"))

Note: If adding support directly in the lsp-mode.el source obviously you should modify lsp-language-id-configuration directly instead of calling add-to-list.

Now we tell lsp-mode how to start the pyls language server when a buffer uses the "python" language:

(lsp-register-client (make-lsp-client
                      :new-connection (lsp-stdio-connection "pyls")
                      :activation-fn (lsp-activate-on "python")
                      :server-id 'pyls))

There are many more options for the client (see the lsp-client struct which documents each option) but what we've shown above is the minimal required configuration. One common case, setting environment variables for the server, is shown below.

Note: In the above example, when a new client is created using make-lsp-client, a new connection to the language server is created using lsp-stdio-connection. Please carefully check its documentation, as the function checks for various things (e.g. testing for the executable in PATH) and handles respective errors. Often while adding a server, the LSP client author might do these checks themselves, but without handling the errors correctly. This leads to features like lsp-install-server breaking for other users; e.g. see this issue. This is a common mistake that keeps reoccurring.

Here's an example of how to set up a custom language server in your init.el file:

;; Use shopify-cli / theme-check-language-server for Shopify's liquid syntax.
(with-eval-after-load 'lsp-mode
  (add-to-list 'lsp-language-id-configuration
               '(shopify-mode . "shopify"))

   (make-lsp-client :new-connection (lsp-stdio-connection "theme-check-language-server")
                    :activation-fn (lsp-activate-on "shopify")
                    :server-id 'theme-check)))

Note: This example assumes that you've already set up a major mode of your own either by deriving it from web-mode or perhaps by writing it yourself.

If the language server supports environment variables to control additional behavior, you can register that by using the :environment-fn option, like the Bash language client does:

 (make-lsp-client :new-connection (lsp-stdio-connection '("bash-language-server" "start"))
                  :activation-fn (lsp-activate-on "shellscript")
                  :priority -1
                  :environment-fn (lambda ()
                                    '(("EXPLAINSHELL_ENDPOINT" . lsp-bash-explainshell-endpoint)
                                      ("HIGHLIGHT_PARSING_ERRORS" . lsp-bash-highlight-parsing-errors)))
                  :server-id 'bash-ls))

lsp-bash-explainshell-endpoint and lsp-bash-highlight-parsing-errors are language client defcustom that expose supported server environment settings in a type-safe way. If you change any of those variables, restart the language server with lsp-workspace-restart for the changes to be applied.

Also, if your client supports customizing the language server path it's recommended to make a wrapper function so the user can customize the value even after the client has been loaded. For example:

(defcustom lsp-tex-executable "digestif"
  "Command to start the Digestif language server."
  :group 'lsp-tex
  :risky t
  :type 'file)

  ;; instead of `:new-connection (lsp-stdio-connection lsp-tex-executable)` use
  :new-connection (lsp-stdio-connection (lambda () lsp-tex-executable))
  :activation-fn (lsp-activate-on "plaintex" "latex")
  :priority -1
  :server-id 'digestif))


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 the Generate Settings script to generate lsp-defcustom from the package.json VScode plugin manifest. Example:

(lsp-defcustom lsp-foo-language-server-property "bar"
  "Demo property."
  :group 'foo-ls
  :lsp-path "")

(lsp-configuration-section "foo")
;; =>  (("foo" ("settings" ("property" . "bar"))))


  • Add the new language server to the lsp-clients.json file sorted by the full-name key alphabetically.
  • Create a new navigation entry in mkdocs.yml file.

Last update: June 10, 2024