{ lib , writeTextDir , formats , kak-lsp , # LSP packages ccls , gopls , nil , nixpkgs-fmt , python311Packages , ltex-ls , nodePackages , tailwindcss-language-server , fsautocomplete , metals , texlab , marksman , rust-analyzer , ... }: let # Configuration for kak-lsp config = { languageIDs = { c = "c_cpp"; cpp = "c_cpp"; javascript = "javascriptreact"; typescript = "typescriptreact"; protobuf = "proto"; sh = "shellscript"; }; languageServers = let vscodeServerOf = name: { name = "vscode-${name}-language-server"; value = { args = [ "--stdio" ]; command = "vscode-${name}-language-server"; filetypes = [ name "templ" ]; roots = [ "package.json" ".git" ]; }; package = nodePackages.vscode-langservers-extracted; }; in { ccls = { args = [ "-v=2" "-log-file=/tmp/ccls.log" ]; package = ccls; command = "ccls"; filetypes = [ "c" "cpp" ]; roots = [ "compile_commands.json" ".cquery" ".git" ]; }; gopls = { command = "gopls"; package = gopls; filetypes = [ "go" ]; offset_encoding = "utf-8"; roots = [ "Gopkg.toml" "go.mod" ".git" ".hg" ]; settings = { gopls = { hoverKind = "SynopsisDocumentation"; semanticTokens = true; }; }; settings_section = "gopls"; }; haskell-language-server = { args = [ "--lsp" ]; command = "haskell-language-server-wrapper"; filetypes = [ "haskell" ]; roots = [ "Setup.hs" "stack.yaml" "*.cabal" "package.yaml" ]; settings_section = "haskell"; }; nil = { command = "nil"; package = nil; filetypes = [ "nix" ]; roots = [ "flake.nix" "shell.nix" ".git" ]; settings.nil = { formatting.command = [ "${lib.getExe nixpkgs-fmt}" ]; }; }; pylsp = { command = "pylsp"; package = python311Packages.python-lsp-server; filetypes = [ "python" ]; offset_encoding = "utf-8"; roots = [ "requirements.txt" "setup.py" ".git" ".hg" ]; }; # Spellchecking server ltex-ls = { command = "ltex-ls"; args = [ "--log-file=/tmp" ]; filetypes = [ "latex" "typst" ]; roots = [ "main.tex" "main.typ" ".git" ]; package = ltex-ls; }; tailwind = { command = "tailwindcss-language-server"; args = [ "--stdio" ]; filetypes = [ "html" "css" "javascript" "typescript" "templ" ]; roots = [ "tailwind.config.{js,cjs,mjs,ts}" "package.json" ".git" ]; settings_section = "tailwindCSS"; settings.tailwindCSS = { validate = "warning"; userLanguages.templ = "html"; }; package = tailwindcss-language-server; }; elixir-ls = { args = [ ]; command = "elixir-ls"; filetypes = [ "elixir" ]; roots = [ "mix.exs" ]; }; typescript-language-server = { args = [ "--stdio" ]; command = "typescript-language-server"; filetypes = [ "typescript" "javascript" ]; roots = [ "package.json" ]; package = nodePackages.typescript-language-server; }; fsautocomplete = { args = [ "--adaptive-lsp-server-enabled" "--project-graph-enabled" "--source-text-factory" "RoslynSourceText" ]; command = "fsautocomplete"; filetypes = [ "fsharp" ]; roots = [ "*.fsproj" ]; settings_section = "FSharp"; settings.FSharp = { AutomaticWorkspaceInit = true; }; package = fsautocomplete; }; metals = { command = "metals"; filetypes = [ "scala" ]; roots = [ "build.sbt" "build.sc" ]; settings_section = "metals"; settings.metals = { enableSemanticHighlighting = true; showInferredType = true; decorationProvider = true; inlineDecorationProvider = true; # From kakoune-lsp's own options icons = "unicode"; isHttpEnabled = true; statusBarProvider = "log-message"; compilerOptions = { overrideDefFormat = "unicode"; }; }; package = metals; }; texlab = { command = "texlab"; filetypes = [ "latex" ]; roots = [ "main.tex" "all.tex" ".git" ]; settings_section = "texlab"; settings.texlab = { build.executable = "latexmk"; build.args = [ "-pdf" "-shell-escape" "-interaction=nonstopmode" "-synctex=1" "%f" ]; build.forwardSearchAfter = true; build.onSave = true; # forwardSearch = # (if pkgs.stdenv.isDarwin then { # executable = "/Applications/Skim.app/Contents/SharedSupport/displayline"; # args = [ "-r" "-g" "%l" "%p" "%f" ]; # } else # { # executable = "${pkgs.zathura}/bin/zathura"; # args = [ "--synctex-forward" "%l:1:%f" "%p" "-x" "${./kaktex} jump %%{input} %%{line} %%{column}" ]; # }); }; package = texlab; }; typst-lsp = { command = "typst-lsp"; filetypes = [ "typst" ]; roots = [ "main.typ" ".git" ]; settings_section = "typst-lsp"; settings.typst-lsp = { experimentalFormatterMode = "on"; }; }; marksman = { command = "marksman"; filetypes = [ "markdown" ]; roots = [ ".marksman.toml" ".git" ]; package = marksman; }; rust-analyzer = { args = [ ]; command = "rust-analyzer"; filetypes = [ "rust" ]; roots = [ "Cargo.toml" ]; package = rust-analyzer; }; } // (builtins.listToAttrs (builtins.map vscodeServerOf [ "html" "css" "json" ])); faces = [ ## Items # (Rust) Macros { face = "attribute"; token = "attribute"; } { face = "attribute"; token = "derive"; } { face = "macro"; token = "macro"; } # Function-like Macro # Keyword and Fixed Tokens { face = "keyword"; token = "keyword"; } { face = "operator"; token = "operator"; } # Functions and Methods { face = "function"; token = "function"; } { face = "method"; token = "method"; } # Constants { face = "string"; token = "string"; } { face = "format_specifier"; token = "formatSpecifier"; } # Variables { face = "variable"; token = "variable"; modifiers = [ "readonly" ]; } { face = "mutable_variable"; token = "variable"; } { face = "module"; token = "namespace"; } { face = "variable"; token = "type_parameter"; } { face = "class"; token = "enum"; } { face = "class"; token = "struct"; } { face = "class"; token = "trait"; } { face = "class"; token = "union"; } { face = "class"; token = "class"; } ## Comments { face = "documentation"; token = "comment"; modifiers = [ "documentation" ]; } { face = "comment"; token = "comment"; } # Typst { face = "header"; token = "heading"; } { face = "ts_markup_link_url"; token = "link"; } { face = "ts_markup_link_uri"; token = "ref"; } { face = "ts_markup_link_label"; token = "label"; } { face = "ts_property"; token = "pol"; } { face = "ts_markup_list_checked"; token = "marker"; } { face = "ts_constant_builtin_boolean"; token = "bool"; } { face = "ts_keyword_control"; token = "delim"; } { face = "ts_number"; token = "text"; modifiers = [ "math" ]; } { face = "ts_markup_bold"; token = "text"; modifiers = [ "strong" ]; } { face = "ts_markup_italic"; token = "text"; modifiers = [ "emph" ]; } ]; raw = { server = { timeout = 1800; }; snippet_support = false; verbosity = 255; }; }; kak-lsp-config = let toml = formats.toml { }; toLspConfig = builtins.mapAttrs (_: attrs: builtins.removeAttrs attrs [ "package" ]); in toml.generate "kak-lsp.toml" ({ semantic_tokens.faces = config.faces; language_server = toLspConfig config.languageServers; language_ids = config.languageIDs; } // config.raw); serverPackages = builtins.filter (v: v != null) (lib.mapAttrsToList (_: serv: serv.package or null) config.languageServers); in { extraPaths = lib.makeBinPath serverPackages; plugin = writeTextDir "share/kak/autoload/kak-lsp.kak" '' hook global KakBegin .* %{ try %{ eval %sh{${lib.getExe kak-lsp} --kakoune --config ${kak-lsp-config} -s $kak_session} } lsp-enable map global lsp N -docstring "Display the next message request" ": lsp-show-message-request-next" map global normal ": enter-user-mode lsp" map global normal ": lsp-hover" map global normal ": lsp-hover-buffer" # lsp-auto-hover-insert-mode-enable set global lsp_hover_anchor true map global insert ':try lsp-snippets-select-next-placeholders catch %{ execute-keys -with-hooks 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 f 'lsp-object Function Method' -docstring 'LSP function or method' map global object t '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' hook global WinSetOption filetype=(racket|rust|python|go|javascript|typescript|c|cpp|tex|latex|haskell|nix|fsharp|templ) %{ # Format the document if possible hook window BufWritePre .* %{ lsp-formatting-sync } } hook global WinSetOption filetype=(rust|scala|fsharp) %{ # Enable inlay hints lsp-inlay-hints-enable window } hook global WinSetOption filetype=(rust|go|fsharp|typst|scala) %{ 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 } } } ''; }