mirror of
				https://github.com/NotAShelf/nvf.git
				synced 2025-11-03 20:22:21 +00:00 
			
		
		
		
	language/ts: migrate to vim.lsp.servers
This commit is contained in:
		
					parent
					
						
							
								a2ce78fa90
							
						
					
				
			
			
				commit
				
					
						76c031bbda
					
				
			
		
					 1 changed files with 169 additions and 63 deletions
				
			
		| 
						 | 
					@ -4,73 +4,177 @@
 | 
				
			||||||
  lib,
 | 
					  lib,
 | 
				
			||||||
  ...
 | 
					  ...
 | 
				
			||||||
}: let
 | 
					}: let
 | 
				
			||||||
  inherit (builtins) attrNames;
 | 
					  inherit (builtins) attrNames elem;
 | 
				
			||||||
  inherit (lib.options) mkEnableOption mkOption;
 | 
					  inherit (lib.options) mkEnableOption mkOption;
 | 
				
			||||||
  inherit (lib.modules) mkIf mkMerge;
 | 
					  inherit (lib.modules) mkIf mkMerge;
 | 
				
			||||||
  inherit (lib.lists) isList;
 | 
					 | 
				
			||||||
  inherit (lib.meta) getExe;
 | 
					  inherit (lib.meta) getExe;
 | 
				
			||||||
  inherit (lib.types) enum either listOf package str bool;
 | 
					  inherit (lib.types) enum listOf package bool;
 | 
				
			||||||
  inherit (lib.nvim.lua) expToLua toLuaObject;
 | 
					  inherit (lib.generators) mkLuaInline;
 | 
				
			||||||
 | 
					  inherit (lib.nvim.attrsets) mapListToAttrs;
 | 
				
			||||||
 | 
					  inherit (lib.nvim.lua) toLuaObject;
 | 
				
			||||||
  inherit (lib.nvim.types) mkGrammarOption diagnostics mkPluginSetupOption;
 | 
					  inherit (lib.nvim.types) mkGrammarOption diagnostics mkPluginSetupOption;
 | 
				
			||||||
  inherit (lib.nvim.dag) entryAnywhere;
 | 
					  inherit (lib.nvim.dag) entryAnywhere entryBefore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  cfg = config.vim.languages.ts;
 | 
					  cfg = config.vim.languages.ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  defaultServer = "ts_ls";
 | 
					  defaultServers = ["ts_ls"];
 | 
				
			||||||
  servers = {
 | 
					  servers = let
 | 
				
			||||||
    ts_ls = {
 | 
					    ts_ls = {
 | 
				
			||||||
      package = pkgs.typescript-language-server;
 | 
					      cmd = [(getExe pkgs.typescript-language-server) "--stdio"];
 | 
				
			||||||
      lspConfig = ''
 | 
					      init_options = {hostInfo = "neovim";};
 | 
				
			||||||
        lspconfig.ts_ls.setup {
 | 
					      filetypes = [
 | 
				
			||||||
          capabilities = capabilities,
 | 
					        "javascript"
 | 
				
			||||||
          on_attach = function(client, bufnr)
 | 
					        "javascriptreact"
 | 
				
			||||||
            attach_keymaps(client, bufnr);
 | 
					        "javascript.jsx"
 | 
				
			||||||
            client.server_capabilities.documentFormattingProvider = false;
 | 
					        "typescript"
 | 
				
			||||||
          end,
 | 
					        "typescriptreact"
 | 
				
			||||||
          cmd = ${
 | 
					        "typescript.tsx"
 | 
				
			||||||
          if isList cfg.lsp.package
 | 
					      ];
 | 
				
			||||||
          then expToLua cfg.lsp.package
 | 
					      root_markers = ["tsconfig.json" "jsconfig.json" "package.json" ".git"];
 | 
				
			||||||
          else ''{"${cfg.lsp.package}/bin/typescript-language-server", "--stdio"}''
 | 
					      handlers = {
 | 
				
			||||||
        }
 | 
					        # handle rename request for certain code actions like extracting functions / types
 | 
				
			||||||
        }
 | 
					        "_typescript.rename" = mkLuaInline ''
 | 
				
			||||||
 | 
					          function(_, result, ctx)
 | 
				
			||||||
 | 
					            local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
 | 
				
			||||||
 | 
					            vim.lsp.util.show_document({
 | 
				
			||||||
 | 
					              uri = result.textDocument.uri,
 | 
				
			||||||
 | 
					              range = {
 | 
				
			||||||
 | 
					                start = result.position,
 | 
				
			||||||
 | 
					                ['end'] = result.position,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            }, client.offset_encoding)
 | 
				
			||||||
 | 
					            vim.lsp.buf.rename()
 | 
				
			||||||
 | 
					            return vim.NIL
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
        '';
 | 
					        '';
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					      on_attach = mkLuaInline ''
 | 
				
			||||||
 | 
					        function(client, bufnr)
 | 
				
			||||||
 | 
					          default_on_attach(client, bufnr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    denols = {
 | 
					          -- ts_ls provides `source.*` code actions that apply to the whole file. These only appear in
 | 
				
			||||||
      package = pkgs.deno;
 | 
					          -- `vim.lsp.buf.code_action()` if specified in `context.only`.
 | 
				
			||||||
      lspConfig = ''
 | 
					          vim.api.nvim_buf_create_user_command(0, 'LspTypescriptSourceAction', function()
 | 
				
			||||||
        vim.g.markdown_fenced_languages = { "ts=typescript" }
 | 
					            local source_actions = vim.tbl_filter(function(action)
 | 
				
			||||||
        lspconfig.denols.setup {
 | 
					              return vim.startswith(action, 'source.')
 | 
				
			||||||
          capabilities = capabilities;
 | 
					            end, client.server_capabilities.codeActionProvider.codeActionKinds)
 | 
				
			||||||
          on_attach = attach_keymaps,
 | 
					
 | 
				
			||||||
          cmd = ${
 | 
					            vim.lsp.buf.code_action({
 | 
				
			||||||
          if isList cfg.lsp.package
 | 
					              context = {
 | 
				
			||||||
          then expToLua cfg.lsp.package
 | 
					                only = source_actions,
 | 
				
			||||||
          else ''{"${cfg.lsp.package}/bin/deno", "lsp"}''
 | 
					              },
 | 
				
			||||||
        }
 | 
					            })
 | 
				
			||||||
        }
 | 
					          end, {})
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
      '';
 | 
					      '';
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					  in {
 | 
				
			||||||
 | 
					    inherit ts_ls;
 | 
				
			||||||
    # Here for backwards compatibility. Still consider tsserver a valid
 | 
					    # Here for backwards compatibility. Still consider tsserver a valid
 | 
				
			||||||
    # configuration in the enum, but assert if it's set to *properly*
 | 
					    # configuration in the enum, but assert if it's set to *properly*
 | 
				
			||||||
    # redirect the user to the correct server.
 | 
					    # redirect the user to the correct server.
 | 
				
			||||||
    tsserver = {
 | 
					    tsserver = ts_ls;
 | 
				
			||||||
      package = pkgs.typescript-language-server;
 | 
					
 | 
				
			||||||
      lspConfig = ''
 | 
					    denols = {
 | 
				
			||||||
        lspconfig.ts_ls.setup {
 | 
					      cmd = [(getExe pkgs.deno) "lsp"];
 | 
				
			||||||
          capabilities = capabilities;
 | 
					      cmd_env = {NO_COLOR = true;};
 | 
				
			||||||
          on_attach = attach_keymaps,
 | 
					      filetypes = [
 | 
				
			||||||
          cmd = ${
 | 
					        "javascript"
 | 
				
			||||||
          if isList cfg.lsp.package
 | 
					        "javascriptreact"
 | 
				
			||||||
          then expToLua cfg.lsp.package
 | 
					        "javascript.jsx"
 | 
				
			||||||
          else ''{"${cfg.lsp.package}/bin/typescript-language-server", "--stdio"}''
 | 
					        "typescript"
 | 
				
			||||||
        }
 | 
					        "typescriptreact"
 | 
				
			||||||
        }
 | 
					        "typescript.tsx"
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					      root_markers = ["deno.json" "deno.jsonc" ".git"];
 | 
				
			||||||
 | 
					      settings = {
 | 
				
			||||||
 | 
					        deno = {
 | 
				
			||||||
 | 
					          enable = true;
 | 
				
			||||||
 | 
					          suggest = {
 | 
				
			||||||
 | 
					            imports = {
 | 
				
			||||||
 | 
					              hosts = {
 | 
				
			||||||
 | 
					                "https://deno.land" = true;
 | 
				
			||||||
 | 
					              };
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      handlers = {
 | 
				
			||||||
 | 
					        "textDocument/definition" = mkLuaInline "nvf_denols_handler";
 | 
				
			||||||
 | 
					        "textDocument/typeDefinition" = mkLuaInline "nvf_denols_handler";
 | 
				
			||||||
 | 
					        "textDocument/references" = mkLuaInline "nvf_denols_handler";
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      on_attach = mkLuaInline ''
 | 
				
			||||||
 | 
					        function(client, bufnr)
 | 
				
			||||||
 | 
					          default_on_attach(client, bufnr)
 | 
				
			||||||
 | 
					          vim.api.nvim_buf_create_user_command(0, 'LspDenolsCache', function()
 | 
				
			||||||
 | 
					            client:exec_cmd({
 | 
				
			||||||
 | 
					              command = 'deno.cache',
 | 
				
			||||||
 | 
					              arguments = { {}, vim.uri_from_bufnr(bufnr) },
 | 
				
			||||||
 | 
					            }, { bufnr = bufnr }, function(err, _result, ctx)
 | 
				
			||||||
 | 
					              if err then
 | 
				
			||||||
 | 
					                local uri = ctx.params.arguments[2]
 | 
				
			||||||
 | 
					                vim.api.nvim_err_writeln('cache command failed for ' .. vim.uri_to_fname(uri))
 | 
				
			||||||
 | 
					              end
 | 
				
			||||||
 | 
					            end)
 | 
				
			||||||
 | 
					          end, {
 | 
				
			||||||
 | 
					            desc = 'Cache a module and all of its dependencies.',
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
      '';
 | 
					      '';
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  denols_handlers = ''
 | 
				
			||||||
 | 
					    local function nvf_denols_virtual_text_document_handler(uri, res, client)
 | 
				
			||||||
 | 
					      if not res then
 | 
				
			||||||
 | 
					        return nil
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      local lines = vim.split(res.result, '\n')
 | 
				
			||||||
 | 
					      local bufnr = vim.uri_to_bufnr(uri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      local current_buf = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
 | 
				
			||||||
 | 
					      if #current_buf ~= 0 then
 | 
				
			||||||
 | 
					        return nil
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
 | 
				
			||||||
 | 
					      vim.api.nvim_set_option_value('readonly', true, { buf = bufnr })
 | 
				
			||||||
 | 
					      vim.api.nvim_set_option_value('modified', false, { buf = bufnr })
 | 
				
			||||||
 | 
					      vim.api.nvim_set_option_value('modifiable', false, { buf = bufnr })
 | 
				
			||||||
 | 
					      vim.lsp.buf_attach_client(bufnr, client.id)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function nvf_denols_virtual_text_document(uri, client)
 | 
				
			||||||
 | 
					      local params = {
 | 
				
			||||||
 | 
					        textDocument = {
 | 
				
			||||||
 | 
					          uri = uri,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      local result = client.request_sync('deno/virtualTextDocument', params)
 | 
				
			||||||
 | 
					      nvf_denols_virtual_text_document_handler(uri, result, client)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function nvf_denols_handler(err, result, ctx, config)
 | 
				
			||||||
 | 
					      if not result or vim.tbl_isempty(result) then
 | 
				
			||||||
 | 
					        return nil
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      local client = vim.lsp.get_client_by_id(ctx.client_id)
 | 
				
			||||||
 | 
					      for _, res in pairs(result) do
 | 
				
			||||||
 | 
					        local uri = res.uri or res.targetUri
 | 
				
			||||||
 | 
					        if uri:match '^deno:' then
 | 
				
			||||||
 | 
					          nvf_denols_virtual_text_document(uri, client)
 | 
				
			||||||
 | 
					          res['uri'] = uri
 | 
				
			||||||
 | 
					          res['targetUri'] = uri
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      vim.lsp.handlers[ctx.method](err, result, ctx, config)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # TODO: specify packages
 | 
					  # TODO: specify packages
 | 
				
			||||||
  defaultFormat = "prettier";
 | 
					  defaultFormat = "prettier";
 | 
				
			||||||
  formats = {
 | 
					  formats = {
 | 
				
			||||||
| 
						 | 
					@ -122,17 +226,10 @@ in {
 | 
				
			||||||
    lsp = {
 | 
					    lsp = {
 | 
				
			||||||
      enable = mkEnableOption "Typescript/Javascript LSP support" // {default = config.vim.lsp.enable;};
 | 
					      enable = mkEnableOption "Typescript/Javascript LSP support" // {default = config.vim.lsp.enable;};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      server = mkOption {
 | 
					      servers = mkOption {
 | 
				
			||||||
 | 
					        type = singleOrListOf (enum (attrNames servers));
 | 
				
			||||||
 | 
					        default = defaultServers;
 | 
				
			||||||
        description = "Typescript/Javascript LSP server to use";
 | 
					        description = "Typescript/Javascript LSP server to use";
 | 
				
			||||||
        type = enum (attrNames servers);
 | 
					 | 
				
			||||||
        default = defaultServer;
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      package = mkOption {
 | 
					 | 
				
			||||||
        description = "Typescript/Javascript LSP server package, or the command to run as a list of strings";
 | 
					 | 
				
			||||||
        example = ''[lib.getExe pkgs.jdt-language-server "-data" "~/.cache/jdtls/workspace"]'';
 | 
					 | 
				
			||||||
        type = either package (listOf str);
 | 
					 | 
				
			||||||
        default = servers.${cfg.lsp.server}.package;
 | 
					 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -190,8 +287,17 @@ in {
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (mkIf cfg.lsp.enable {
 | 
					    (mkIf cfg.lsp.enable {
 | 
				
			||||||
      vim.lsp.lspconfig.enable = true;
 | 
					      vim.lsp.servers =
 | 
				
			||||||
      vim.lsp.lspconfig.sources.ts-lsp = servers.${cfg.lsp.server}.lspConfig;
 | 
					        mapListToAttrs (name: {
 | 
				
			||||||
 | 
					          inherit name;
 | 
				
			||||||
 | 
					          value = servers.${name};
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        cfg.lsp.servers;
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (mkIf (cfg.lsp.enable && elem "denols" cfg.lsp.servers) {
 | 
				
			||||||
 | 
					      vim.globals.markdown_fenced_languages = ["ts=typescript"];
 | 
				
			||||||
 | 
					      vim.luaConfigRC.denols_handlers = entryBefore ["lsp-servers"] denols_handlers;
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (mkIf cfg.format.enable {
 | 
					    (mkIf cfg.format.enable {
 | 
				
			||||||
| 
						 | 
					@ -234,7 +340,7 @@ in {
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      assertions = [
 | 
					      assertions = [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          assertion = cfg.lsp.enable -> cfg.lsp.server != "tsserver";
 | 
					          assertion = cfg.lsp.enable -> !(elem "tsserver" cfg.lsp.servers);
 | 
				
			||||||
          message = ''
 | 
					          message = ''
 | 
				
			||||||
            As of a recent lspconfig update, the `tsserver` configuration has been renamed
 | 
					            As of a recent lspconfig update, the `tsserver` configuration has been renamed
 | 
				
			||||||
            to `ts_ls` to match upstream behaviour of `lspconfig`, and the name `tsserver`
 | 
					            to `ts_ls` to match upstream behaviour of `lspconfig`, and the name `tsserver`
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue