= Kakoune Language Server Protocol Client

kak-lsp is a https://microsoft.github.io/language-server-protocol/[Language Server Protocol] client for http://kakoune.org[Kakoune] implemented in https://www.rust-lang.org[Rust].

== Quick Start

  1. <>
  2. <>
  3. <>
  4. <>

=== Install kak-lsp

==== Pre-built binaries

===== MacOS

====== Homebrew

[source,sh]

brew install kak-lsp/kak-lsp/kak-lsp

====== Manual

[source,sh]

curl -O -L https://github.com/kak-lsp/kak-lsp/releases/download/v14.2.0/kak-lsp-v14.2.0-x8664-apple-darwin.tar.gz tar xzvf kak-lsp-v14.2.0-x8664-apple-darwin.tar.gz

replace ~/.local/bin/ with something on your $PATH

mv kak-lsp ~/.local/bin/

optional: if you want to use specific language servers

mkdir -p ~/.config/kak-lsp

mv kak-lsp.toml ~/.config/kak-lsp/

===== Linux

====== Package managers

====== Others

[source,sh]

wget https://github.com/kak-lsp/kak-lsp/releases/download/v14.2.0/kak-lsp-v14.2.0-x8664-unknown-linux-musl.tar.gz tar xzvf kak-lsp-v14.2.0-x8664-unknown-linux-musl.tar.gz

replace ~/.local/bin/ with something on your $PATH

mv kak-lsp ~/.local/bin/

optional: if you want to use specific language servers

mkdir -p ~/.config/kak-lsp

mv kak-lsp.toml ~/.config/kak-lsp/

==== From source

Generally, you need the latest stable version of Rust to build kak-lsp.

[source,sh]

git clone https://github.com/kak-lsp/kak-lsp cd kak-lsp

this installs the kak-lsp binary to ~/.cargo/bin, which must be in your $PATH

cargo install --locked --force --path .

optional: if you want to use specific language servers

mkdir -p ~/.config/kak-lsp

cp kak-lsp.toml ~/.config/kak-lsp/

==== With plug.kak

If you don't mind using a plugin manager, you can install kak-lsp via https://github.com/andreyorst/plug.kak[plug.kak]. Add this code to your kakrc:

[source,kak]

plug "kak-lsp/kak-lsp" do %{ cargo install --locked --force --path . # optional: if you want to use specific language servers mkdir -p ~/.config/kak-lsp cp -n kak-lsp.toml ~/.config/kak-lsp/

}

cargo install will install the kak-lsp binary to ~/.cargo/bin, which must be in your $PATH. Alternatively, you can replace cargo install with cargo build --release && ln -sf $PWD/target/release/kak-lsp ~/.local/bin/ where ~/.local/bin/ can be replaced with something in your $PATH.

Examples of configuration with plug.kak can be found at https://github.com/kak-lsp/kak-lsp/wiki/Usage-with-plug.kak[Wiki].

=== Install language servers for your desired languages

kak-lsp doesn't manage installation of language servers, please install them by yourself for the languages you plan to use kak-lsp with. Please consult the https://github.com/kak-lsp/kak-lsp/wiki/How-to-install-servers[How to install servers] wiki page for quick installation of language servers supported by kak-lsp out of the box.

=== Configure Kakoune to enable kak-lsp

To enable LSP support for configured languages (see <>) just add the following commands to your kakrc:

[source,kak]

eval %sh{kak-lsp --kakoune -s $kak_session} # Not needed if you load it with plug.kak.

lsp-enable

A bit more involved but recommended way is to enable kak-lsp only for specific filetypes you need via lsp-enable-window, e.g.:

[source,kak]

eval %sh{kak-lsp --kakoune -s $kak_session} # Not needed if you load it with plug.kak. hook global WinSetOption filetype=(rust|python|go|javascript|typescript|c|cpp) %{ lsp-enable-window

}

=== Configure mappings

There are three default mappings in goto-mode: gd (lsp-definition), gy (lsp-type-definition) and gr (lsp-references). You can override them in your kakrc after this plugin is loaded.

Here are additional recommended mappings. See below for the meaning of each command.

[source,kak]

map global user l %{:enter-user-mode lsp} -docstring "LSP mode" map global insert 'tab> }' -docstring 'Select next snippet placeholder' map global object a 'lsp-object' -docstring 'LSP any symbol' map global object 'lsp-object' -docstring 'LSP any symbol' map global object e 'lsp-object Function Method' -docstring 'LSP function or method' map global object k 'lsp-object Class Interface Struct' -docstring 'LSP class interface or struct' map global object d 'lsp-diagnostic-object --include-warnings' -docstring 'LSP errors and warnings'

map global object D 'lsp-diagnostic-object' -docstring 'LSP errors'

== Usage

NOTE: Contents below corresponds to the master branch HEAD and could be slightly out-of-sync with the version installed from pre-built binaries. The most common case is new commands being in a pre-release testing stage. Please refer to the README.asciidoc revision tagged with the version you use or the README.asciidoc from the release archive.

If you have followed above steps you get

[source,kak]

hook global WinSetOption filetype=rust %{ hook window BufWritePre .* lsp-formatting-sync

}

NOTE: By default, kak-lsp exits when it doesn't receive any request from Kakoune for 30 minutes, even if the Kakoune session is still up and running. Change server.timeout in kak-lsp.toml to tweak this duration, or set it to 0 to disable this behavior. In any scenario, a new request would spin up a fresh server if it is down.

|=== | Binding | Command

| a | lsp-code-actions | c | lsp-capabilities | d | lsp-definition | e | lsp-diagnostics | f | lsp-formatting | h | lsp-hover | i | lsp-implementation | j | lsp-outgoing-calls | k | lsp-incoming-calls | l | lsp-code-lens | r | lsp-references | R | lsp-rename-prompt | s | lsp-goto-document-symbol | S | lsp-document-symbol | o | lsp-workspace-symbol-incr | n | lsp-find-error | p | lsp-find-error --previous | v | lsp-selection-range | y | lsp-type-definition | 9 | lsp-hover-previous-function | 0 | lsp-hover-next-function | & | lsp-highlight-references | ( | lsp-previous-function | ) | lsp-next-function | [ | lsp-hover-previous-symbol | ] | lsp-hover-next-symbol | { | lsp-previous-symbol | } | lsp-next-symbol |===

To know which subset of kak-lsp commands is backed by the current buffer's filetype's language server use lsp-capabilities command.

== Configuration

kak-lsp itself has configuration, but it also adds configuration options to Kakoune that affect the Kakoune integration.

=== Configuring kak-lsp

kak-lsp is configured via a configuration file in https://github.com/toml-lang/toml[TOML] format. By default kak-lsp tries to read $XDG_CONFIG_HOME/kak-lsp/kak-lsp.toml (which defaults to ~/.config/kak-lsp/kak-lsp.toml) but you can override it with command-line option --config. Look into the default link:kak-lsp.toml[kak-lsp.toml], it should be quite self-explanatory. If you don't need to change configuration then feel free to skip copying it anywhere as the default configuration is embedded into the kak-lsp binary.

Important: The configuration file does not extend the default configuration, but rather overrides it. This means that if you want to customize any of the configuration, you must copy the entire default configuration and then edit it.

In the language section of kak-lsp.toml, the roots parameter is a list of file globs. Whenever your editor session wants to send an LSP request, the first glob that matches a file in any of the current buffer's parent directories will cause kak-lsp to set the project root to that parent directory.

You can define an environment variable like KAK_LSP_PROJECT_ROOT_RUST=/my/project to always use /my/project as root for Rust files inside /my/project. Substitute RUST with another language ID to do the same for other file types.

The environment variable KAK_LSP_FORCE_PROJECT_ROOT=/my/project will make kak-lsp always use /my/project as project root, even for files outside this directory. This avoids starting separate language servers for files outside /my/project, and ensures that your language server is aware of your project's build configuration even when navigating library code.

If you are setting any server options via cli, do not forget to append them to %sh{kak-lsp --kakoune ...} in your kakrc. It's not needed if you change options in ~/.config/kak-lsp/kak-lsp.toml.

Please let us know if you have any ideas about how to make the default config more sensible.

==== Server-specific configuration

Many servers accept configuration options that are not part of the LSP spec. The TOML table [language.<filetype>.settings] holds those configuration options. It has the same structure as the corresponding fragments from VSCode's settings.json. For example:

[source,toml]

[language.go] ... settings_section = "gopls" [language.go.settings.gopls]

"formatting.gofumpt" = true

During server initialization, kak-lsp sends the section specified by settings_section; in this case {"formatting.gofumpt":true}. Whenever you change the Kakoune option lsp_config, the same section is sent via workspace/didChangeConfiguration. Additionally, kak-lsp will send arbitrary sections that are requested by the server in workspace/configuration.

=== Configuring Kakoune

kak-lsp's Kakoune integration declares the following options:

For example, you can toggle an option dynamically with a command like this:

[source,kak]

set-option global lsp_config %{ [language.go.settings.gopls] "formatting.gofumpt" = true

}

== Inlay hints

Inlay hints are a feature proposed for LSP 3.17 to show inferred types, parameter names in function calls, and the types of chained calls inline in the code. To enable support for it in kak-lsp, add the following to your kakrc:

[source,kak]

lsp-inlay-hints-enable global

You can change the hints' face with set-face global InlayHint <face>.

== Semantic Tokens

kak-lsp supports the semanticTokens feature for semantic highlighting. If the language server supports it, you can enable it with:

[source,kak]

hook global WinSetOption filetype= %{ hook window -group semantic-tokens BufReload .* lsp-semantic-tokens hook window -group semantic-tokens NormalIdle .* lsp-semantic-tokens hook window -group semantic-tokens InsertIdle .* lsp-semantic-tokens hook -once -always window WinSetOption filetype=.* %{ remove-hooks window semantic-tokens }

}

The faces used for semantic tokens and modifiers can be modified in kak-lsp.toml, using the semantic_tokens.faces array, for example:

[source,toml]

[semantictokens] faces = [ {face="constvariable_declaration", token="variable", modifiers=["constant", "declaration"]},

]

where face is the face that will be applied in Kakoune (you'll want to define these in your theme/config), token is the token's name as reported by the language server (see lsp-capabilities) and modifiers is an array of modifier names (also reported by the language server). modifiers may be omitted, but token and face are required.

You may create any arbitrary number of definitions with permutations between the token names and modifiers reported by the server. For an entry to match a token, all the entry's modifiers must exist on the token. However, the token may have additional modifiers not assigned in the config entry. + kak-lsp will find the most specific matching configuration to apply, where specificity is defined as the number of matching modifiers. If multiple matching entries have the same number of modifiers, the one that was defined last in the configuration wins.

Example:

Assuming the following configuration,

[source,toml]

[semantictokens] faces = [ {face="constvariabledeclaration", token="variable", modifiers=["constant","declaration"]}, {face="constvariable", token="variable", modifiers=["constant"]}, {face="variable", token="variable"},

]

kak-lsp will perform these mappings:

[cols="1,1,2,5"] |=== | Token | Modifiers | Face | Comment

| variable | constant, declaration | const_variable_declaration | First entry matches with 2 modifiers.

| variable | constant | const_variable | First and second entry match with 1 modifier, second wins.

| variable | declaration | variable | Only third entry matches. First entry doesn't match, because constant is missing.

| variable | | variable | Third entry matches.

| function | | | No entries match and no face is applied.

|===

== Inlay Diagnostics

kak-lsp supports showing diagnostics inline after their respective line, but this behavior can be somewhat buggy and must be enabled explicitly:

[source,kak]

lsp-inlay-diagnostics-enable global

== Markdown rendering in info box

kak-lsp shows some additional information provided by the language server in an info box. This information includes documentation for the token under the cursor (lsp-hover) and documentation for completion candidates. In both cases, the Language Server Protocol allows for both plain text and Markdown, and most servers do implement Markdown.

To make use of Markdown, kak-lsp transpiles it into Kakoune's markup language, utilizing various faces for styling. These faces all default to the Information face, to ensure that the text in the info box works with any color scheme.

To enable Markdown highlighting, define some of the following faces in your theme or kakrc:

[cols="1a,3a"] |=== | Face | Usage

| InfoDefault | The default text color. You'll likely want to leave this at the default Information.

| InfoBlock | The face used for code blocks. Language specific syntax highlighting for code blocks is not supported.

| InfoBlockQuote | The face used for block quotes. The > Markdown syntax is still rendered.

| InfoBullet | The face used to highlight the list symbol for both ordered and unordered lists. For list items' text, InfoDefault is used.

| InfoHeader | The face used for headings. There is currently no distinction between different heading levels.

| InfoLink | The face used to highlight link titles. Maybe some classic blue+u for this one?

| InfoLinkMono | This face is assigned to inline code spans within link titles, such as in the following Markdown snippet. Here, the word format will receive the InfoLinkMono face.


the format function

| InfoMono | The face used for inline code spans (backtick strings).

| InfoRule | The face used for horizontal lines (rules).

| InfoDiagnosticError | Used for error messages in the diagnostics inside hover info. This defaults to Kakoune's built-in Error face.

| InfoDiagnosticHint | Used for hints in the diagnostics inside hover info.

| InfoDiagnosticInformation | Used for informational messages in the diagnostics inside hover info.

| InfoDiagnosticWarning | Used for warnings in the diagnostics inside hover info.

|===

For convenience, here is a snippet to paste into your theme/config:

[source,kak]

face global InfoDefault Information face global InfoBlock Information face global InfoBlockQuote Information face global InfoBullet Information face global InfoHeader Information face global InfoLink Information face global InfoLinkMono Information face global InfoMono Information face global InfoRule Information face global InfoDiagnosticError Information face global InfoDiagnosticHint Information face global InfoDiagnosticInformation Information

face global InfoDiagnosticWarning Information

Current limitations of this feature are:

== Snippets

Snippets are completions that come with placeholders ("tabstops") in the places you likely want to insert text (for example function call arguments). The placeholders are highlighted with the two faces SnippetsNextPlaceholders and SnippetsOtherPlaceholders.

The lsp-snippets-select-next-placeholders command allows to jump to the next tabstop (like function call arguments). The suggested mapping uses <tab> (see <>). Here's a way to bind it to <c-n> instead (might need to hide the completion menu with Kakoune's <c-o> command):

[source,kak]

map global insert '' -docstring 'Select next snippet placeholder' hook global InsertCompletionShow .* %{ unmap global insert '' } hook global InsertCompletionHide .* %{ map global insert '' -docstring 'Select next snippet placeholder'

}

Snippet support can be disabled by setting snippet_support = false at the top level of the config.

== Limitations

=== Encoding

kak-lsp works best with UTF-8 documents.

=== Position.character interpretation

The LSP spec says that column offsets (Position.character) are to be interpreted as UTF-16 code units. Many servers violate the spec. Please refer to https://github.com/Microsoft/language-server-protocol/issues/376 for some background.

kak-lsp adheres to the spec but will prefer UTF-8 offsets if the server advertises support for UTF-8 offsets via client capabilities general.positionEncodings or https://clangd.llvm.org/extensions.html#utf-8-offsets[clangd protocol extension].

== Troubleshooting

If kak-lsp fails try to put this line in your kakrc after kak-lsp --kakoune invocation:

[source,kak]

set global lsp_cmd "kak-lsp -s %val{session} -vvv --log /tmp/kak-lsp.log"

to enable debug logging.

If it does not give enough insight to fix the problem or if the problem is a bug in kak-lsp itself please don't hesitate to raise an issue.

=== Default configuration

Please also try to reproduce the issue with a minimal configuration. Sometimes the problem occurs only with specific settings in your ~/.config/kak-lsp/kak-lsp.toml and/or ~/.config/kak/. Use this command to start Kakoune with kak-lsp enabled, both with pristine settings.

[source,sh]

HOME=$(mktemp -d) kak -e ' eval %sh{kak-lsp --kakoune -s $kaksession} set global lspcmd "kak-lsp -s %val{session} -vvvv --log /tmp/kak-lsp.log"

lsp-enable'

NOTE: Some Kakoune plugins could interfere with kak-lsp, particularly completions providers. E.g. racer.kak competes for autocompletion in Rust files.

=== Crashes

For troubleshooting crashes, you might like to run kak-lsp outside of Kakoune.

To do this:

. Before launching Kakoune, run kak-lsp with an arbitrary session ID (here foobar):

kak-lsp -s foobar

. In a second terminal, run Kakoune with the same session ID:

kak -s foobar

== Versioning

kak-lsp follows https://semver.org/[SemVer] with one notable difference from common practice: we don't use 0 major version to indicate that product is not yet reached stability. Even for non-stable and not feature-complete product user should be clearly informed about breaking change. Therefore we start with major version 1 and increment it each time when upgrade requires user's attention.