summaryrefslogtreecommitdiff
path: root/hosts/muhammed/home/neovim/lsp.nix
blob: 134c5e6ae729cdb1a75d6705b35f966c86cbe817 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# This module sets up LSP server configurations for Neovim.
{pkgs, ...}: {
  programs.neovim.plugins = [
    {
      plugin = pkgs.vimPlugins.nvim-lspconfig;
      type = "lua";
      config = ''
        local lspconfig = require("lspconfig")
        local util = require("lspconfig.util")

        -- Mappings.
        -- See `:help vim.diagnostic.*` for documentation on any of the below functions
        local opts = { noremap=true, silent=true }
        vim.keymap.set('n', '<leader>e', vim.diagnostic.open_float, opts)
        vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts)
        vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts)
        vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, opts)

        -- Use an on_attach function to only map the following keys
        -- after the language server attaches to the current buffer
        local on_attach = function(client, bufnr)
          -- Enable completion triggered by <c-x><c-o>
          vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')

          -- Mappings.
          -- See `:help vim.lsp.*` for documentation on any of the below functions
          local bufopts = { noremap=true, silent=true, buffer=bufnr }
          vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, bufopts)
          vim.keymap.set('n', 'gd', vim.lsp.buf.definition, bufopts)
          vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts)
          vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, bufopts)
          vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, bufopts)
          vim.keymap.set('n', '<leader>wa', vim.lsp.buf.add_workspace_folder, bufopts)
          vim.keymap.set('n', '<leader>wr', vim.lsp.buf.remove_workspace_folder, bufopts)
          vim.keymap.set('n', '<leader>wl', function()
            print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
          end, bufopts)
          vim.keymap.set('n', '<leader>D', vim.lsp.buf.type_definition, bufopts)
          vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, bufopts)
          vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, bufopts)
          vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts)
          vim.keymap.set('n', '<leader>f', function() vim.lsp.buf.format { async = true } end, bufopts)
          vim.keymap.set('n', '<leader>s', function() vim.cmd[[ClangdSwitchSourceHeader]] end, bufopts)

          -- Rust-analyzer supports inlay hints
          vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
        end

        -- Use a loop to conveniently call 'setup' on multiple servers and
        -- map buffer local keybindings when the language server attaches
        local servers = {
          pyright = {
            cmd = { "${pkgs.pyright}/bin/pyright-langserver", "--stdio" },
          },
          nixd = {
            cmd = { "${pkgs.nixd}/bin/nixd" },
          },
          denols = {
            init_options = {
              enable = true,
              unstable = true,
              lint = true,
              nodeModulesDir = true,
            },
            cmd = { "${pkgs.unstable.deno}/bin/deno", "lsp" },
            root_dir = function(startpath)
              if util.find_package_json_ancestor(startpath) then
                -- This is a Node project; let tsserver handle this one.
                -- This exactly mirrors how typescript-langauge-server yields to this server for Deno projects.
                return nil
              else
                -- Otherwise, we try to find the root or
                -- default to the current directory.
                return util.root_pattern("deno.json", "deno.jsonc", ".git")(startpath)
                    or util.path.dirname(startpath)
              end
            end,
          },
          -- NOTE: Will be renamed to ts_ls shortly
          -- See: https://github.com/neovim/nvim-lspconfig/commit/bdbc65aadc708ce528efb22bca5f82a7cca6b54d
          tsserver = {
            cmd = { "${pkgs.nodePackages_latest.typescript-language-server}/bin/typescript-language-server", "--stdio" },
            root_dir = function(startpath)
              local find_deno_root_dir = util.root_pattern("deno.json", "deno.jsonc")
              if find_deno_root_dir(startpath) then
                -- This is a Deno project; let deno-lsp handle this one.
                -- This exactly mirrors how deno-lsp yields to this server for Node projects.
                return nil
              else
                -- Otherwise fall back to the usual resolution method.
                -- See: https://github.com/neovim/nvim-lspconfig/blob/056f569f71e4b726323b799b9cfacc53653bceb3/lua/lspconfig/server_configurations/ts_ls.lua#L15
                return util.root_pattern("tsconfig.json", "jsconfig.json", "package.json", ".git")(startpath)
              end
            end,
            -- We also have to disallow starting in without a root directory, as otherwise returning
            -- nil from find_root will just cause the LSP to be spawned in single file mode instead of yielding to deno-lsp.
            --
            -- This has the side effect that Deno LSP will be preferred in a single file context which is what we want!
            --
            -- See: https://github.com/neovim/nvim-lspconfig/blob/056f569f71e4b726323b799b9cfacc53653bceb3/lua/lspconfig/manager.lua#L281-L286
            single_file_support = false,
          },
          clangd = {
            cmd = { "${pkgs.clang-tools}/bin/clangd" },
          },
          nimls = {
            cmd = { "${pkgs.nimlsp}/bin/nimlsp" },
          },
          rust_analyzer = {
            cmd = { "${pkgs.rust-analyzer}/bin/rust-analyzer" },
          },
          rust_analyzer = {
            cmd = { "${pkgs.rust-analyzer}/bin/rust-analyzer" },
          },
          gleam = {
            cmd = { "${pkgs.unstable.gleam}/bin/gleam", "lsp" },
          },
          svelte = {
            cmd = { "${pkgs.nodePackages_latest.svelte-language-server}/bin/svelteserver", "--stdio" },
            root_dir = util.root_pattern("package.json", ".git", "deno.json", "deno.jsonc"),
          },
        };
        for server, config in pairs(servers) do
          -- set common options
          config.on_attach = on_attach;
          config.debounce_text_changes = 150;

          lspconfig[server].setup(config)
        end
      '';
    }
  ];
}
# I spent like an hour writing this, only to find it was a pretty bad idea.
#
#    nixToLua = s:
#      if builtins.isAttrs s then
#        let
#          renderAttr = name: value: "[ [==========[" + name + "]==========] ] = " + (nixToLua value);
#          attrsList = map (name: renderAttr name s.${name}) (lib.attrNames s);
#          attrsListStr = lib.concatStringsSep ", " attrsList;
#        in
#        "{ ${attrsListStr} }"
#      else if builtins.isList s then
#        "{ " + (lib.concatStringsSep ", " (map nixToLua s)) + " }"
#      else if builtins.isString s then
#        # Oh boy I sure hope `s` doesn't contain "]==========]".
#        "[==========[" + s + "]==========]"
#      else if builtins.isInt s || builtins.isFloat s then
#        toString s
#      else
#        throw "Cannot convert ${builtins.typeOf s} to Lua value!";