diff options
| author | William Boman <william@redwill.se> | 2021-10-10 19:20:16 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-10-10 19:20:16 +0200 |
| commit | 5f54148153a878cc9cbe370adccda0f706251d89 (patch) | |
| tree | 28ae0436232486323dfce3444e1d25657c34d75e /lua | |
| parent | jdtls: fix jar argument (diff) | |
| download | mason-5f54148153a878cc9cbe370adccda0f706251d89.tar mason-5f54148153a878cc9cbe370adccda0f706251d89.tar.gz mason-5f54148153a878cc9cbe370adccda0f706251d89.tar.bz2 mason-5f54148153a878cc9cbe370adccda0f706251d89.tar.lz mason-5f54148153a878cc9cbe370adccda0f706251d89.tar.xz mason-5f54148153a878cc9cbe370adccda0f706251d89.tar.zst mason-5f54148153a878cc9cbe370adccda0f706251d89.zip | |
add keybindings to UI window (#140)
- Allows for expanding servers to view more information about it.
- Allows for installing/reinstalling/uninstalling servers.
The default keybindings is an attempt to mimic vim-fugitive's :Git
maps, and these can be overriden.
The keybinding implementation in display.lua is a bit hacky, but it
works and the "public" API is at least manageable. This will also
open up for adding more metadata in the future, such as filetype
information, currently installed version, latest available version,
etc.
Also there's Cowth Vader.
Diffstat (limited to 'lua')
61 files changed, 527 insertions, 104 deletions
diff --git a/lua/nvim-lsp-installer/data.lua b/lua/nvim-lsp-installer/data.lua index 901c293a..b5689142 100644 --- a/lua/nvim-lsp-installer/data.lua +++ b/lua/nvim-lsp-installer/data.lua @@ -33,6 +33,21 @@ function Data.list_map(fn, list) return result end +function Data.table_pack(...) + return { n = select("#", ...), ... } +end + +function Data.list_not_nil(...) + local result = {} + local args = Data.table_pack(...) + for i = 1, args.n do + if args[i] ~= nil then + result[#result + 1] = args[i] + end + end + return result +end + function Data.tbl_pack(...) return { n = select("#", ...), ... } end @@ -41,6 +56,10 @@ function Data.when(condition, value) return condition and value or nil end +function Data.lazy(condition, fn) + return condition and fn() or nil +end + function Data.coalesce(...) local args = Data.tbl_pack(...) for i = 1, args.n do diff --git a/lua/nvim-lsp-installer/server.lua b/lua/nvim-lsp-installer/server.lua index 4c8d4e65..1240d349 100644 --- a/lua/nvim-lsp-installer/server.lua +++ b/lua/nvim-lsp-installer/server.lua @@ -17,6 +17,9 @@ M.Server.__index = M.Server --@param opts table -- @field name (string) The name of the LSP server. This MUST correspond with lspconfig's naming. -- +-- @field homepage (string) A URL to the homepage of this server. This is for example where users can +-- report issues and receive support. +-- -- @field installer (function) The function that installs the LSP (see the .installers module). The function signature should be `function (server, callback)`, where -- `server` is the Server instance being installed, and `callback` is a function that must be called upon completion. The `callback` function -- has the signature `function (success, result)`, where `success` is a boolean and `result` is of any type (similar to `pcall`). @@ -37,6 +40,7 @@ function M.Server:new(opts) return setmetatable({ name = opts.name, root_dir = opts.root_dir, + homepage = opts.homepage, _root_dir = opts.root_dir, -- @deprecated Use the `root_dir` property instead. _installer = type(opts.installer) == "function" and opts.installer or installers.pipe(opts.installer), _default_options = opts.default_options, diff --git a/lua/nvim-lsp-installer/servers/angularls/init.lua b/lua/nvim-lsp-installer/servers/angularls/init.lua index 89c62741..5a545cd2 100644 --- a/lua/nvim-lsp-installer/servers/angularls/init.lua +++ b/lua/nvim-lsp-installer/servers/angularls/init.lua @@ -18,6 +18,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://angular.io/guide/language-service", installer = npm.packages { "@angular/language-server" }, default_options = { cmd = { diff --git a/lua/nvim-lsp-installer/servers/ansiblels/init.lua b/lua/nvim-lsp-installer/servers/ansiblels/init.lua index a3903146..46afb9a1 100644 --- a/lua/nvim-lsp-installer/servers/ansiblels/init.lua +++ b/lua/nvim-lsp-installer/servers/ansiblels/init.lua @@ -7,6 +7,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/ansible/ansible-language-server", installer = { std.git_clone "https://github.com/ansible/ansible-language-server", npm.install { "npm@latest" }, -- ansiblels has quite a strict npm version requirement diff --git a/lua/nvim-lsp-installer/servers/bashls/init.lua b/lua/nvim-lsp-installer/servers/bashls/init.lua index 7839131e..a670d6fc 100644 --- a/lua/nvim-lsp-installer/servers/bashls/init.lua +++ b/lua/nvim-lsp-installer/servers/bashls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/bash-lsp/bash-language-server", installer = npm.packages { "bash-language-server" }, default_options = { cmd = { npm.executable(root_dir, "bash-language-server"), "start" }, diff --git a/lua/nvim-lsp-installer/servers/bicep/init.lua b/lua/nvim-lsp-installer/servers/bicep/init.lua index 3e92e700..28e010b0 100644 --- a/lua/nvim-lsp-installer/servers/bicep/init.lua +++ b/lua/nvim-lsp-installer/servers/bicep/init.lua @@ -7,6 +7,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/Azure/bicep", installer = { std.ensure_executables { { "dotnet", "dotnet is required to run the bicep language server." }, diff --git a/lua/nvim-lsp-installer/servers/clangd/init.lua b/lua/nvim-lsp-installer/servers/clangd/init.lua index bf8494e7..63b17b5f 100644 --- a/lua/nvim-lsp-installer/servers/clangd/init.lua +++ b/lua/nvim-lsp-installer/servers/clangd/init.lua @@ -12,6 +12,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://clangd.llvm.org", installer = { context.github_release_file("clangd/clangd", function(version) return Data.coalesce( diff --git a/lua/nvim-lsp-installer/servers/clojure_lsp/init.lua b/lua/nvim-lsp-installer/servers/clojure_lsp/init.lua index bbdde873..8fb9d92b 100644 --- a/lua/nvim-lsp-installer/servers/clojure_lsp/init.lua +++ b/lua/nvim-lsp-installer/servers/clojure_lsp/init.lua @@ -9,6 +9,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://clojure-lsp.io", installer = { context.github_release_file( "clojure-lsp/clojure-lsp", diff --git a/lua/nvim-lsp-installer/servers/cmake/init.lua b/lua/nvim-lsp-installer/servers/cmake/init.lua index 9fe9de23..38df47b3 100644 --- a/lua/nvim-lsp-installer/servers/cmake/init.lua +++ b/lua/nvim-lsp-installer/servers/cmake/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/regen100/cmake-language-server", installer = pip3.packages { "cmake-language-server" }, default_options = { cmd = { pip3.executable(root_dir, "cmake-language-server") }, diff --git a/lua/nvim-lsp-installer/servers/denols/init.lua b/lua/nvim-lsp-installer/servers/denols/init.lua index 85245cf1..d09d3957 100644 --- a/lua/nvim-lsp-installer/servers/denols/init.lua +++ b/lua/nvim-lsp-installer/servers/denols/init.lua @@ -11,6 +11,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://deno.land/x/deno/cli/lsp", installer = { context.github_release_file( "denoland/deno", diff --git a/lua/nvim-lsp-installer/servers/diagnosticls/init.lua b/lua/nvim-lsp-installer/servers/diagnosticls/init.lua index 9a80eadc..3a48d13a 100644 --- a/lua/nvim-lsp-installer/servers/diagnosticls/init.lua +++ b/lua/nvim-lsp-installer/servers/diagnosticls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/iamcco/diagnostic-languageserver", installer = npm.packages { "diagnostic-languageserver" }, default_options = { cmd = { npm.executable(root_dir, "diagnostic-languageserver"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/dockerls/init.lua b/lua/nvim-lsp-installer/servers/dockerls/init.lua index 7ea76d2e..621eb8ed 100644 --- a/lua/nvim-lsp-installer/servers/dockerls/init.lua +++ b/lua/nvim-lsp-installer/servers/dockerls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/rcjsuen/dockerfile-language-server-nodejs", installer = npm.packages { "dockerfile-language-server-nodejs" }, default_options = { cmd = { npm.executable(root_dir, "docker-langserver"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/dotls/init.lua b/lua/nvim-lsp-installer/servers/dotls/init.lua index 438f6f88..bd2deedf 100644 --- a/lua/nvim-lsp-installer/servers/dotls/init.lua +++ b/lua/nvim-lsp-installer/servers/dotls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/nikeee/dot-language-server", installer = npm.packages { "dot-language-server" }, default_options = { cmd = { npm.executable(root_dir, "dot-language-server"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/efm/init.lua b/lua/nvim-lsp-installer/servers/efm/init.lua index 56037305..875b3884 100644 --- a/lua/nvim-lsp-installer/servers/efm/init.lua +++ b/lua/nvim-lsp-installer/servers/efm/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/mattn/efm-langserver", installer = go.packages { "github.com/mattn/efm-langserver" }, default_options = { cmd = { go.executable(root_dir, "efm-langserver") }, diff --git a/lua/nvim-lsp-installer/servers/elixirls/init.lua b/lua/nvim-lsp-installer/servers/elixirls/init.lua index ecb0cb00..4cd941f0 100644 --- a/lua/nvim-lsp-installer/servers/elixirls/init.lua +++ b/lua/nvim-lsp-installer/servers/elixirls/init.lua @@ -7,6 +7,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/elixir-lsp/elixir-ls", installer = { context.github_release_file("elixir-lsp/elixir-ls", "elixir-ls.zip"), context.capture(function(ctx) diff --git a/lua/nvim-lsp-installer/servers/elmls/init.lua b/lua/nvim-lsp-installer/servers/elmls/init.lua index 30e69055..676beddd 100644 --- a/lua/nvim-lsp-installer/servers/elmls/init.lua +++ b/lua/nvim-lsp-installer/servers/elmls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/elm-tooling/elm-language-server", installer = npm.packages { "@elm-tooling/elm-language-server", "elm", "elm-test", "elm-format" }, default_options = { cmd = { npm.executable(root_dir, "elm-language-server") }, diff --git a/lua/nvim-lsp-installer/servers/ember/init.lua b/lua/nvim-lsp-installer/servers/ember/init.lua index 11b64a17..095503ef 100644 --- a/lua/nvim-lsp-installer/servers/ember/init.lua +++ b/lua/nvim-lsp-installer/servers/ember/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/lifeart/ember-language-server", installer = npm.packages { "@lifeart/ember-language-server" }, default_options = { cmd = { npm.executable(root_dir, "ember-language-server"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/emmet_ls/init.lua b/lua/nvim-lsp-installer/servers/emmet_ls/init.lua index ee5fda7a..2588bcc5 100644 --- a/lua/nvim-lsp-installer/servers/emmet_ls/init.lua +++ b/lua/nvim-lsp-installer/servers/emmet_ls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/aca/emmet-ls", installer = npm.packages { "emmet-ls" }, pre_setup = function() local lspconfig = require "lspconfig" diff --git a/lua/nvim-lsp-installer/servers/fortls/init.lua b/lua/nvim-lsp-installer/servers/fortls/init.lua index cee618e1..d9c771fb 100644 --- a/lua/nvim-lsp-installer/servers/fortls/init.lua +++ b/lua/nvim-lsp-installer/servers/fortls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/hansec/fortran-language-server", installer = pip3.packages { "fortran-language-server" }, default_options = { cmd = { pip3.executable(root_dir, "fortls") }, diff --git a/lua/nvim-lsp-installer/servers/gopls/init.lua b/lua/nvim-lsp-installer/servers/gopls/init.lua index 9534673b..26e0d45e 100644 --- a/lua/nvim-lsp-installer/servers/gopls/init.lua +++ b/lua/nvim-lsp-installer/servers/gopls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://pkg.go.dev/golang.org/x/tools/gopls", installer = go.packages { "golang.org/x/tools/gopls" }, default_options = { cmd = { go.executable(root_dir, "gopls") }, diff --git a/lua/nvim-lsp-installer/servers/graphql/init.lua b/lua/nvim-lsp-installer/servers/graphql/init.lua index d11f77ef..5be801e6 100644 --- a/lua/nvim-lsp-installer/servers/graphql/init.lua +++ b/lua/nvim-lsp-installer/servers/graphql/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://www.npmjs.com/package/graphql-language-service-cli", installer = npm.packages { "graphql-language-service-cli", "graphql" }, default_options = { cmd = { npm.executable(root_dir, "graphql-lsp"), "server", "-m", "stream" }, diff --git a/lua/nvim-lsp-installer/servers/groovyls/init.lua b/lua/nvim-lsp-installer/servers/groovyls/init.lua index 614d196b..a895f566 100644 --- a/lua/nvim-lsp-installer/servers/groovyls/init.lua +++ b/lua/nvim-lsp-installer/servers/groovyls/init.lua @@ -6,6 +6,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/GroovyLanguageServer/groovy-language-server", installer = { std.ensure_executables { { "javac", "javac was not found in path." } }, std.git_clone "https://github.com/GroovyLanguageServer/groovy-language-server", diff --git a/lua/nvim-lsp-installer/servers/hls/init.lua b/lua/nvim-lsp-installer/servers/hls/init.lua index b3197ed7..a6ad0ef4 100644 --- a/lua/nvim-lsp-installer/servers/hls/init.lua +++ b/lua/nvim-lsp-installer/servers/hls/init.lua @@ -11,6 +11,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://haskell-language-server.readthedocs.io/en/latest/", installer = { context.github_release_file("haskell/haskell-language-server", function(version) return Data.coalesce( diff --git a/lua/nvim-lsp-installer/servers/intelephense/init.lua b/lua/nvim-lsp-installer/servers/intelephense/init.lua index 835aa124..1d4ddb74 100644 --- a/lua/nvim-lsp-installer/servers/intelephense/init.lua +++ b/lua/nvim-lsp-installer/servers/intelephense/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://intelephense.com", installer = npm.packages { "intelephense" }, default_options = { cmd = { npm.executable(root_dir, "intelephense"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/jdtls/init.lua b/lua/nvim-lsp-installer/servers/jdtls/init.lua index 806babd8..2e9a88bd 100644 --- a/lua/nvim-lsp-installer/servers/jdtls/init.lua +++ b/lua/nvim-lsp-installer/servers/jdtls/init.lua @@ -42,6 +42,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/eclipse/eclipse.jdt.ls", installer = { context.capture(function(ctx) local version = ctx.requested_server_version or "latest" diff --git a/lua/nvim-lsp-installer/servers/jedi_language_server/init.lua b/lua/nvim-lsp-installer/servers/jedi_language_server/init.lua index f3d53e8c..a91b34ba 100644 --- a/lua/nvim-lsp-installer/servers/jedi_language_server/init.lua +++ b/lua/nvim-lsp-installer/servers/jedi_language_server/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/pappasam/jedi-language-server", installer = pip3.packages { "jedi-language-server" }, default_options = { cmd = { pip3.executable(root_dir, "jedi-language-server") }, diff --git a/lua/nvim-lsp-installer/servers/kotlin_language_server/init.lua b/lua/nvim-lsp-installer/servers/kotlin_language_server/init.lua index baff307f..e21766c8 100644 --- a/lua/nvim-lsp-installer/servers/kotlin_language_server/init.lua +++ b/lua/nvim-lsp-installer/servers/kotlin_language_server/init.lua @@ -8,6 +8,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/fwcd/kotlin-language-server", installer = { context.github_release_file("fwcd/kotlin-language-server", "server.zip"), context.capture(function(ctx) diff --git a/lua/nvim-lsp-installer/servers/lemminx/init.lua b/lua/nvim-lsp-installer/servers/lemminx/init.lua index b82f775c..d88a3005 100644 --- a/lua/nvim-lsp-installer/servers/lemminx/init.lua +++ b/lua/nvim-lsp-installer/servers/lemminx/init.lua @@ -18,6 +18,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/eclipse/lemminx", installer = { function(_, callback, ctx) if not unzipped_file then diff --git a/lua/nvim-lsp-installer/servers/ltex/init.lua b/lua/nvim-lsp-installer/servers/ltex/init.lua index 4bbdf69a..a1553976 100644 --- a/lua/nvim-lsp-installer/servers/ltex/init.lua +++ b/lua/nvim-lsp-installer/servers/ltex/init.lua @@ -14,6 +14,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://valentjn.github.io/vscode-ltex", installer = { context.github_release_file("valentjn/ltex-ls", function(version) return coalesce( diff --git a/lua/nvim-lsp-installer/servers/ocamlls/init.lua b/lua/nvim-lsp-installer/servers/ocamlls/init.lua index 7de21479..8fbc8dc7 100644 --- a/lua/nvim-lsp-installer/servers/ocamlls/init.lua +++ b/lua/nvim-lsp-installer/servers/ocamlls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/ocaml-lsp/ocaml-language-server", installer = npm.packages { "ocaml-language-server" }, default_options = { cmd = { npm.executable(root_dir, "ocaml-language-server"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/omnisharp/init.lua b/lua/nvim-lsp-installer/servers/omnisharp/init.lua index ae9594c0..294c1d02 100644 --- a/lua/nvim-lsp-installer/servers/omnisharp/init.lua +++ b/lua/nvim-lsp-installer/servers/omnisharp/init.lua @@ -11,6 +11,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/OmniSharp/omnisharp-roslyn", installer = { context.github_release_file( "OmniSharp/omnisharp-roslyn", diff --git a/lua/nvim-lsp-installer/servers/phpactor/init.lua b/lua/nvim-lsp-installer/servers/phpactor/init.lua index 67a84238..16823520 100644 --- a/lua/nvim-lsp-installer/servers/phpactor/init.lua +++ b/lua/nvim-lsp-installer/servers/phpactor/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://phpactor.readthedocs.io/en/master/", installer = composer.packages { "phpactor/phpactor" }, default_options = { cmd = { composer.executable(root_dir, "phpactor"), "language-server" }, diff --git a/lua/nvim-lsp-installer/servers/prismals/init.lua b/lua/nvim-lsp-installer/servers/prismals/init.lua index 4bca4a9a..97ad49d9 100644 --- a/lua/nvim-lsp-installer/servers/prismals/init.lua +++ b/lua/nvim-lsp-installer/servers/prismals/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/prisma/language-tools", installer = npm.packages { "@prisma/language-server" }, default_options = { cmd = { npm.executable(root_dir, "prisma-language-server"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/puppet/init.lua b/lua/nvim-lsp-installer/servers/puppet/init.lua index f69757c5..a1642d62 100644 --- a/lua/nvim-lsp-installer/servers/puppet/init.lua +++ b/lua/nvim-lsp-installer/servers/puppet/init.lua @@ -7,6 +7,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/puppetlabs/puppet-editor-services", installer = { context.github_release_file("puppetlabs/puppet-editor-services", function(version) return ("puppet_editor_services_%s.zip"):format(version) diff --git a/lua/nvim-lsp-installer/servers/purescriptls/init.lua b/lua/nvim-lsp-installer/servers/purescriptls/init.lua index 8d04537c..63981df0 100644 --- a/lua/nvim-lsp-installer/servers/purescriptls/init.lua +++ b/lua/nvim-lsp-installer/servers/purescriptls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/nwolverson/purescript-language-server", installer = npm.packages { "purescript-language-server" }, default_options = { cmd = { npm.executable(root_dir, "purescript-language-server"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/pylsp/init.lua b/lua/nvim-lsp-installer/servers/pylsp/init.lua index 42537411..8a14111d 100644 --- a/lua/nvim-lsp-installer/servers/pylsp/init.lua +++ b/lua/nvim-lsp-installer/servers/pylsp/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/python-lsp/python-lsp-server", installer = pip3.packages { "python-lsp-server[all]" }, default_options = { cmd = { pip3.executable(root_dir, "pylsp") }, diff --git a/lua/nvim-lsp-installer/servers/pyright/init.lua b/lua/nvim-lsp-installer/servers/pyright/init.lua index 1df23958..521830f6 100644 --- a/lua/nvim-lsp-installer/servers/pyright/init.lua +++ b/lua/nvim-lsp-installer/servers/pyright/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/microsoft/pyright", installer = npm.packages { "pyright" }, default_options = { cmd = { npm.executable(root_dir, "pyright-langserver"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/rescriptls/init.lua b/lua/nvim-lsp-installer/servers/rescriptls/init.lua index caec9d53..590eafc5 100644 --- a/lua/nvim-lsp-installer/servers/rescriptls/init.lua +++ b/lua/nvim-lsp-installer/servers/rescriptls/init.lua @@ -7,6 +7,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/rescript-lang/rescript-vscode", installer = { context.github_release_file("rescript-lang/rescript-vscode", function(version) return ("rescript-vscode-%s.vsix"):format(version) diff --git a/lua/nvim-lsp-installer/servers/rome/init.lua b/lua/nvim-lsp-installer/servers/rome/init.lua index e4e61ab7..17564b9e 100644 --- a/lua/nvim-lsp-installer/servers/rome/init.lua +++ b/lua/nvim-lsp-installer/servers/rome/init.lua @@ -7,6 +7,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://rome.tools", installer = { context.set(function(ctx) ctx.requested_server_version = Data.coalesce( diff --git a/lua/nvim-lsp-installer/servers/rust_analyzer/init.lua b/lua/nvim-lsp-installer/servers/rust_analyzer/init.lua index fc843964..7e96a54d 100644 --- a/lua/nvim-lsp-installer/servers/rust_analyzer/init.lua +++ b/lua/nvim-lsp-installer/servers/rust_analyzer/init.lua @@ -35,6 +35,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://rust-analyzer.github.io", installer = { context.github_release_file("rust-analyzer/rust-analyzer", target), context.capture(function(ctx) diff --git a/lua/nvim-lsp-installer/servers/serve_d/init.lua b/lua/nvim-lsp-installer/servers/serve_d/init.lua index 719d9d5f..f7c01bcf 100644 --- a/lua/nvim-lsp-installer/servers/serve_d/init.lua +++ b/lua/nvim-lsp-installer/servers/serve_d/init.lua @@ -9,6 +9,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/Pure-D/serve-d", installer = { context.set(function(ctx) -- Consider the latest (as of writing) beta release as "latest", instead of 0.6.0. diff --git a/lua/nvim-lsp-installer/servers/solang/init.lua b/lua/nvim-lsp-installer/servers/solang/init.lua index b1405f46..6b3fac2c 100644 --- a/lua/nvim-lsp-installer/servers/solang/init.lua +++ b/lua/nvim-lsp-installer/servers/solang/init.lua @@ -47,6 +47,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://solang.readthedocs.io/en/latest/", installer = { solang_executable_installer, llvm_installer, diff --git a/lua/nvim-lsp-installer/servers/solargraph/init.lua b/lua/nvim-lsp-installer/servers/solargraph/init.lua index 4e0e4598..9bd3afc2 100644 --- a/lua/nvim-lsp-installer/servers/solargraph/init.lua +++ b/lua/nvim-lsp-installer/servers/solargraph/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://solargraph.org", installer = gem.packages { "solargraph" }, default_options = { cmd = { gem.executable(root_dir, "solargraph"), "stdio" }, diff --git a/lua/nvim-lsp-installer/servers/sqlls/init.lua b/lua/nvim-lsp-installer/servers/sqlls/init.lua index e8ccd56e..29b0449e 100644 --- a/lua/nvim-lsp-installer/servers/sqlls/init.lua +++ b/lua/nvim-lsp-installer/servers/sqlls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/joe-re/sql-language-server", installer = npm.packages { "sql-language-server" }, default_options = { cmd = { npm.executable(root_dir, "sql-language-server") }, diff --git a/lua/nvim-lsp-installer/servers/sqls/init.lua b/lua/nvim-lsp-installer/servers/sqls/init.lua index d58e9353..5e3f4e1c 100644 --- a/lua/nvim-lsp-installer/servers/sqls/init.lua +++ b/lua/nvim-lsp-installer/servers/sqls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/lighttiger2505/sqls", installer = go.packages { "github.com/lighttiger2505/sqls" }, default_options = { cmd = { go.executable(root_dir, "sqls") }, diff --git a/lua/nvim-lsp-installer/servers/stylelint_lsp/init.lua b/lua/nvim-lsp-installer/servers/stylelint_lsp/init.lua index 113f7329..ea72db8f 100644 --- a/lua/nvim-lsp-installer/servers/stylelint_lsp/init.lua +++ b/lua/nvim-lsp-installer/servers/stylelint_lsp/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/bmatcuk/stylelint-lsp", installer = npm.packages { "stylelint-lsp" }, default_options = { cmd = { npm.executable(root_dir, "stylelint-lsp"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/sumneko_lua/init.lua b/lua/nvim-lsp-installer/servers/sumneko_lua/init.lua index 008a2e4f..19b64649 100644 --- a/lua/nvim-lsp-installer/servers/sumneko_lua/init.lua +++ b/lua/nvim-lsp-installer/servers/sumneko_lua/init.lua @@ -15,6 +15,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/sumneko/lua-language-server", installer = { context.github_release_file("sumneko/vscode-lua", function(version) return ("lua-%s.vsix"):format(version:gsub("^v", "")) diff --git a/lua/nvim-lsp-installer/servers/svelte/init.lua b/lua/nvim-lsp-installer/servers/svelte/init.lua index c94b1f89..2415de63 100644 --- a/lua/nvim-lsp-installer/servers/svelte/init.lua +++ b/lua/nvim-lsp-installer/servers/svelte/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/sveltejs/language-tools", installer = npm.packages { "svelte-language-server" }, default_options = { cmd = { npm.executable(root_dir, "svelteserver"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/terraformls/init.lua b/lua/nvim-lsp-installer/servers/terraformls/init.lua index e400e2ce..a584c940 100644 --- a/lua/nvim-lsp-installer/servers/terraformls/init.lua +++ b/lua/nvim-lsp-installer/servers/terraformls/init.lua @@ -11,6 +11,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/hashicorp/terraform-ls", installer = { context.github_release_file("hashicorp/terraform-ls", function(version) return Data.coalesce( diff --git a/lua/nvim-lsp-installer/servers/texlab/init.lua b/lua/nvim-lsp-installer/servers/texlab/init.lua index 1a38be26..425ba78a 100644 --- a/lua/nvim-lsp-installer/servers/texlab/init.lua +++ b/lua/nvim-lsp-installer/servers/texlab/init.lua @@ -11,6 +11,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/latex-lsp/texlab", installer = { std.ensure_executables { { "pdflatex", "A TeX distribution is not installed. Refer to https://www.latex-project.org/get/." }, diff --git a/lua/nvim-lsp-installer/servers/tflint/init.lua b/lua/nvim-lsp-installer/servers/tflint/init.lua index 35f00918..97f76217 100644 --- a/lua/nvim-lsp-installer/servers/tflint/init.lua +++ b/lua/nvim-lsp-installer/servers/tflint/init.lua @@ -23,6 +23,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/terraform-linters/tflint", installer = { context.github_release_file("terraform-linters/tflint", target), context.capture(function(ctx) diff --git a/lua/nvim-lsp-installer/servers/tsserver/init.lua b/lua/nvim-lsp-installer/servers/tsserver/init.lua index ec66e1c1..d4008933 100644 --- a/lua/nvim-lsp-installer/servers/tsserver/init.lua +++ b/lua/nvim-lsp-installer/servers/tsserver/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/typescript-language-server/typescript-language-server", installer = npm.packages { "typescript-language-server", "typescript" }, default_options = { cmd = { npm.executable(root_dir, "typescript-language-server"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/vimls/init.lua b/lua/nvim-lsp-installer/servers/vimls/init.lua index 756e32aa..4ea07465 100644 --- a/lua/nvim-lsp-installer/servers/vimls/init.lua +++ b/lua/nvim-lsp-installer/servers/vimls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/iamcco/vim-language-server", installer = npm.packages { "vim-language-server" }, default_options = { cmd = { npm.executable(root_dir, "vim-language-server"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/volar/init.lua b/lua/nvim-lsp-installer/servers/volar/init.lua index d7f2a44b..26af7d5d 100644 --- a/lua/nvim-lsp-installer/servers/volar/init.lua +++ b/lua/nvim-lsp-installer/servers/volar/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/johnsoncodehk/volar", installer = npm.packages { "@volar/server" }, default_options = { cmd = { npm.executable(root_dir, "volar-server"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/vuels/init.lua b/lua/nvim-lsp-installer/servers/vuels/init.lua index ff251e6b..d208c0a3 100644 --- a/lua/nvim-lsp-installer/servers/vuels/init.lua +++ b/lua/nvim-lsp-installer/servers/vuels/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/vlang/vls", installer = npm.packages { "vls" }, default_options = { cmd = { npm.executable(root_dir, "vls"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/yamlls/init.lua b/lua/nvim-lsp-installer/servers/yamlls/init.lua index f02d7642..ed65d03f 100644 --- a/lua/nvim-lsp-installer/servers/yamlls/init.lua +++ b/lua/nvim-lsp-installer/servers/yamlls/init.lua @@ -5,6 +5,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/redhat-developer/yaml-language-server", installer = npm.packages { "yaml-language-server" }, default_options = { cmd = { npm.executable(root_dir, "yaml-language-server"), "--stdio" }, diff --git a/lua/nvim-lsp-installer/servers/zls/init.lua b/lua/nvim-lsp-installer/servers/zls/init.lua index c9d32790..e6cd5928 100644 --- a/lua/nvim-lsp-installer/servers/zls/init.lua +++ b/lua/nvim-lsp-installer/servers/zls/init.lua @@ -20,6 +20,7 @@ return function(name, root_dir) return server.Server:new { name = name, root_dir = root_dir, + homepage = "https://github.com/zigtools/zls", installer = { context.github_release_file("zigtools/zls", ("%s.tar.xz"):format(archive_name)), context.capture(function(ctx) diff --git a/lua/nvim-lsp-installer/settings.lua b/lua/nvim-lsp-installer/settings.lua index 1d7fa283..7a61510f 100644 --- a/lua/nvim-lsp-installer/settings.lua +++ b/lua/nvim-lsp-installer/settings.lua @@ -8,6 +8,16 @@ local DEFAULT_SETTINGS = { -- The list icon to use for servers that are not installed. server_uninstalled = "◍", }, + keymaps = { + -- Keymap to expand a server in the UI + toggle_server_expand = "<CR>", + -- Keymap to install a server + install_server = "i", + -- Keymap to reinstall/update a server + update_server = "u", + -- Keymap to uninstall a server + uninstall_server = "X", + }, }, -- Controls to which degree logs are written to the log file. It's useful to set this to vim.log.levels.DEBUG when @@ -19,6 +29,10 @@ local DEFAULT_SETTINGS = { -- example, installing `cssls` will also install both `jsonls` and `html` (and the other ways around), as these all -- share the same underlying package. allow_federated_servers = true, + + -- Limit for the maximum amount of servers to be installed at the same time. Once this limit is reached, any further + -- servers that are requested to be installed will be put in a queue. + max_concurrent_installers = 4, } local M = {} diff --git a/lua/nvim-lsp-installer/ui/display.lua b/lua/nvim-lsp-installer/ui/display.lua index 27aca167..e675d715 100644 --- a/lua/nvim-lsp-installer/ui/display.lua +++ b/lua/nvim-lsp-installer/ui/display.lua @@ -3,6 +3,20 @@ local log = require "nvim-lsp-installer.log" local process = require "nvim-lsp-installer.process" local state = require "nvim-lsp-installer.ui.state" +local M = {} + +local function from_hex(str) + return (str:gsub("..", function(cc) + return string.char(tonumber(cc, 16)) + end)) +end + +local function to_hex(str) + return (str:gsub(".", function(c) + return string.format("%02X", string.byte(c)) + end)) +end + local function get_styles(line, render_context) local indentation = 0 @@ -29,11 +43,13 @@ local function render_node(context, node, _render_context, _output) context = context, applied_block_styles = {}, } - local output = _output or { - lines = {}, - virt_texts = {}, - highlights = {}, - } + local output = _output + or { + lines = {}, + virt_texts = {}, + highlights = {}, + keybinds = {}, + } if node.type == Ui.NodeType.VIRTUAL_TEXT then output.virt_texts[#output.virt_texts + 1] = { @@ -81,6 +97,13 @@ local function render_node(context, node, _render_context, _output) if node.type == Ui.NodeType.CASCADING_STYLE then render_context.applied_block_styles[#render_context.applied_block_styles] = nil end + elseif node.type == Ui.NodeType.KEYBIND_HANDLER then + output.keybinds[#output.keybinds + 1] = { + line = node.is_global and -1 or #output.lines, + key = node.key, + effect = node.effect, + payload = node.payload, + } end return output @@ -102,10 +125,35 @@ local function create_popup_window_opts() return popup_layout end -local M = {} - +local registered_effect_handlers_by_bufnr = {} +local active_keybinds_by_bufnr = {} +local registered_keymaps_by_bufnr = {} local redraw_by_win_id = {} +local function call_effect_handler(bufnr, line, key) + local line_keybinds = active_keybinds_by_bufnr[bufnr][line] + if line_keybinds then + local keybind = line_keybinds[key] + if keybind then + local effect_handler = registered_effect_handlers_by_bufnr[bufnr][keybind.effect] + if effect_handler then + log.fmt_trace("Calling handler for effect %s on line %d for key %s", keybind.effect, line, key) + effect_handler { payload = keybind.payload } + return true + end + end + end + return false +end + +M.dispatch_effect = vim.schedule_wrap(function(bufnr, hex_key) + local key = from_hex(hex_key) + local line = vim.api.nvim_win_get_cursor(0)[1] + log.fmt_trace("Dispatching effect on line %d, key %s, bufnr %s", line, key, bufnr) + call_effect_handler(bufnr, line, key) -- line keybinds + call_effect_handler(bufnr, -1, key) -- global keybinds +end) + function M.redraw_win(win_id) local fn = redraw_by_win_id[win_id] if fn then @@ -129,6 +177,15 @@ function M.delete_win_buf(win_id, bufnr) if redraw_by_win_id[win_id] then redraw_by_win_id[win_id] = nil end + if active_keybinds_by_bufnr[bufnr] then + active_keybinds_by_bufnr[bufnr] = nil + end + if registered_effect_handlers_by_bufnr[bufnr] then + registered_effect_handlers_by_bufnr[bufnr] = nil + end + if registered_keymaps_by_bufnr[bufnr] then + registered_keymaps_by_bufnr[bufnr] = nil + end end) end @@ -143,6 +200,10 @@ function M.new_view_only_win(name) bufnr = vim.api.nvim_create_buf(false, true) win_id = vim.api.nvim_open_win(bufnr, true, create_popup_window_opts()) + registered_effect_handlers_by_bufnr[bufnr] = {} + active_keybinds_by_bufnr[bufnr] = {} + registered_keymaps_by_bufnr[bufnr] = {} + local buf_opts = { modifiable = false, swapfile = false, @@ -185,8 +246,6 @@ function M.new_view_only_win(name) ):format(win_id, bufnr) ) - vim.api.nvim_buf_set_keymap(bufnr, "n", "<esc>", "<cmd>bd<CR>", { noremap = true }) - if highlight_groups then for i = 1, #highlight_groups do vim.cmd(highlight_groups[i]) @@ -214,8 +273,10 @@ function M.new_view_only_win(name) win_width = win_width, } local output = render_node(context, view) - local lines, virt_texts, highlights = output.lines, output.virt_texts, output.highlights + local lines, virt_texts, highlights, keybinds = + output.lines, output.virt_texts, output.highlights, output.keybinds + -- set line contents vim.api.nvim_buf_clear_namespace(0, namespace, 0, -1) vim.api.nvim_buf_set_option(bufnr, "modifiable", true) vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) @@ -227,6 +288,8 @@ function M.new_view_only_win(name) virt_text = virt_text.content, }) end + + -- set highlights for i = 1, #highlights do local highlight = highlights[i] vim.api.nvim_buf_add_highlight( @@ -238,6 +301,32 @@ function M.new_view_only_win(name) highlight.col_end ) end + + -- set keybinds + local buf_keybinds = {} + active_keybinds_by_bufnr[bufnr] = buf_keybinds + for i = 1, #keybinds do + local keybind = keybinds[i] + if not buf_keybinds[keybind.line] then + buf_keybinds[keybind.line] = {} + end + buf_keybinds[keybind.line][keybind.key] = keybind + if not registered_keymaps_by_bufnr[bufnr][keybind.key] then + vim.api.nvim_buf_set_keymap( + bufnr, + "n", + keybind.key, + ("<cmd>lua require('nvim-lsp-installer.ui.display').dispatch_effect(%d, %q)<cr>"):format( + bufnr, + -- We transfer the keybinding as hex to avoid issues with (neo)vim interpreting the key as a + -- literal input to the command. For example, "<CR>" would cause vim to issue an actual carriage + -- return - even if it's quoted as a string. + to_hex(keybind.key) + ), + { nowait = true, silent = true, noremap = true } + ) + end + end end) return { @@ -267,6 +356,7 @@ function M.new_view_only_win(name) unsubscribe(false) local opened_win_id = open(opts) draw(renderer(get_state())) + registered_effect_handlers_by_bufnr[bufnr] = opts.effects redraw_by_win_id[opened_win_id] = function() if vim.api.nvim_win_is_valid(opened_win_id) then draw(renderer(get_state())) @@ -274,16 +364,19 @@ function M.new_view_only_win(name) end end end), - -- This is probably not needed. - -- destroy = vim.schedule_wrap(function() - -- assert(has_initiated, "Display has not been initiated, cannot destroy.") - -- TODO: what happens with the state container, etc? - -- unsubscribe(true) - -- redraw_by_winnr[win_id] = nil - -- if win_id then - -- vim.api.nvim_win_close(win_id, true) - -- end - -- end), + close = vim.schedule_wrap(function() + assert(has_initiated, "Display has not been initiated, cannot close.") + unsubscribe(true) + M.delete_win_buf(win_id, bufnr) + end), + set_cursor = function(pos) + assert(win_id ~= nil, "Window has not been opened, cannot set cursor.") + return vim.api.nvim_win_set_cursor(win_id, pos) + end, + get_cursor = function() + assert(win_id ~= nil, "Window has not been opened, cannot get cursor.") + return vim.api.nvim_win_get_cursor(win_id) + end, } end diff --git a/lua/nvim-lsp-installer/ui/init.lua b/lua/nvim-lsp-installer/ui/init.lua index 8f5d86a6..4f8d6935 100644 --- a/lua/nvim-lsp-installer/ui/init.lua +++ b/lua/nvim-lsp-installer/ui/init.lua @@ -6,6 +6,7 @@ M.NodeType = Data.enum { "CASCADING_STYLE", "VIRTUAL_TEXT", "HL_TEXT", + "KEYBIND_HANDLER", } function M.Node(children) @@ -63,8 +64,41 @@ function M.When(condition, a) return M.Node {} end +function M.Keybind(key, effect, payload, is_global) + return { + type = M.NodeType.KEYBIND_HANDLER, + key = key, + effect = effect, + payload = payload, + is_global = is_global or false, + } +end + function M.EmptyLine() return M.Text { "" } end +function M.Table(rows) + local col_maxwidth = {} + for i = 1, #rows do + local row = rows[i] + for j = 1, #row do + local col = row[j] + local content = col[1] + col_maxwidth[j] = math.max(#content, col_maxwidth[j] or 0) + end + end + + for i = 1, #rows do + local row = rows[i] + for j = 1, #row do + local col = row[j] + local content = col[1] + col[1] = content .. string.rep(" ", (col_maxwidth[j] - #content) + 1) -- +1 for default minimum padding + end + end + + return M.HlTextNode(rows) +end + return M diff --git a/lua/nvim-lsp-installer/ui/status-win/init.lua b/lua/nvim-lsp-installer/ui/status-win/init.lua index b82aef0a..d448cd9c 100644 --- a/lua/nvim-lsp-installer/ui/status-win/init.lua +++ b/lua/nvim-lsp-installer/ui/status-win/init.lua @@ -4,6 +4,11 @@ local log = require "nvim-lsp-installer.log" local Data = require "nvim-lsp-installer.data" local display = require "nvim-lsp-installer.ui.display" local settings = require "nvim-lsp-installer.settings" +local lsp_servers = require "nvim-lsp-installer.servers" + +local HELP_KEYMAP = "?" +local CLOSE_WINDOW_KEYMAP_1 = "<Esc>" +local CLOSE_WINDOW_KEYMAP_2 = "q" local function ServerGroupHeading(props) return Ui.HlTextNode { @@ -15,11 +20,90 @@ local function Indent(children) return Ui.CascadingStyleNode({ Ui.CascadingStyle.INDENT }, children) end +-- stylua: ignore start +local very_reasonable_cow = { + { { [[ _______________________________________________________________________ ]], "LspInstallerMuted" } }, + { { [[ < Help sponsor Neovim development! ]], "LspInstallerMuted" }, { "https://github.com/sponsors/neovim", "LspInstallerHighlighted"}, {[[ > ]], "LspInstallerMuted" } }, + { { [[ ----------------------------------------------------------------------- ]], "LspInstallerMuted" } }, + { { [[ \ ,-^-. ]], "LspInstallerMuted" } }, + { { [[ \ !oYo! ]], "LspInstallerMuted" } }, + { { [[ \ /./=\.\______ ]], "LspInstallerMuted" } }, + { { [[ ## )\/\ ]], "LspInstallerMuted" } }, + { { [[ ||-----w|| ]], "LspInstallerMuted" } }, + { { [[ || || ]], "LspInstallerMuted" } }, + { { [[ ]], "LspInstallerMuted" } }, + { { [[ Cowth Vader (alleged Neovim user) ]], "LspInstallerMuted" } }, + { { [[ ]], "LspInstallerMuted" } }, +} +-- stylua: ignore end + +local function Help(is_current_settings_expanded) + local keymap_tuples = { + { "Toggle help", HELP_KEYMAP }, + { "Toggle server info", settings.current.ui.keymaps.toggle_server_expand }, + { "Update server", settings.current.ui.keymaps.update_server }, + { "Uninstall server", settings.current.ui.keymaps.uninstall_server }, + { "Install server", settings.current.ui.keymaps.install_server }, + { "Close window", CLOSE_WINDOW_KEYMAP_1 }, + { "Close window", CLOSE_WINDOW_KEYMAP_2 }, + } + + return Ui.Node { + Ui.EmptyLine(), + Ui.Table(vim.list_extend( + { + { + { "Keyboard shortcuts", "LspInstallerLabel" }, + }, + }, + Data.list_map(function(keymap_tuple) + return { { keymap_tuple[1], "LspInstallerMuted" }, { keymap_tuple[2], "LspInstallerHighlighted" } } + end, keymap_tuples) + )), + Ui.EmptyLine(), + Ui.HlTextNode { + { { "Problems installing/uninstalling servers", "LspInstallerLabel" } }, + { + { + "Make sure you meet the minimum requirements to install servers. For debugging, refer to ", + "LspInstallerMuted", + }, + { ":help nvim-lsp-installer-debugging", "LspInstallerHighlighted" }, + }, + }, + Ui.EmptyLine(), + Ui.HlTextNode { + { { "Problems with server functionality", "LspInstallerLabel" } }, + { { "Please refer to each language server's own homepage for further assistance.", "LspInstallerMuted" } }, + }, + Ui.EmptyLine(), + Ui.HlTextNode { + { + { + ("%s Current settings"):format(is_current_settings_expanded and "v" or ">"), + "LspInstallerLabel", + }, + { " :help nvim-lsp-installer-settings", "LspInstallerHighlighted" }, + }, + }, + Ui.Keybind("<CR>", "TOGGLE_EXPAND_CURRENT_SETTINGS", nil), + Ui.When(is_current_settings_expanded, function() + local settings_split_by_newline = vim.split(vim.inspect(settings.current), "\n") + local current_settings = Data.list_map(function(line) + return { { line, "LspInstallerMuted" } } + end, settings_split_by_newline) + return Ui.HlTextNode(current_settings) + end), + Ui.EmptyLine(), + Ui.HlTextNode(very_reasonable_cow), + } +end + local function Header() return Ui.CascadingStyleNode({ Ui.CascadingStyle.CENTERED }, { Ui.HlTextNode { - { { "nvim-lsp-installer", "LspInstallerHeader" } }, - { { "https://github.com/williamboman/nvim-lsp-installer", "LspInstallerLink" } }, + { { ":help ", "LspInstallerMuted" }, { "nvim-lsp-installer", "LspInstallerHeader" } }, + { { "https://github.com/williamboman/nvim-lsp-installer", "Comment" } }, }, }) end @@ -49,26 +133,54 @@ local function get_relative_install_time(time) end end -local function InstalledServers(servers) +local function ServerMetadata(server) + return Ui.Table(Data.list_not_nil( + Data.lazy(server.metadata.install_timestamp_seconds, function() + return { + { "last updated", "LspInstallerMuted" }, + { get_relative_install_time(server.metadata.install_timestamp_seconds), "" }, + } + end), + Data.when(server.is_installed, { + { "path", "LspInstallerMuted" }, + { server.metadata.install_dir, "" }, + }), + Data.when(server.is_installed, { + { "startup command", "LspInstallerMuted" }, + { server.metadata.cmd, "" }, + }), + { + { "homepage", "LspInstallerMuted" }, + { server.metadata.homepage or "-", "" }, + } + )) +end + +local function InstalledServers(servers, expanded_server) return Ui.Node(Data.list_map(function(server) + local is_expanded = expanded_server == server.name return Ui.Node { Ui.HlTextNode { - { + Data.list_not_nil( { settings.current.ui.icons.server_installed, "LspInstallerGreen" }, - { " " .. server.name, "" }, - { - (" installed %s"):format(get_relative_install_time(server.creation_time)), - "Comment", - }, - }, + { " " .. server.name, "" } + ), }, + Ui.Keybind(settings.current.ui.keymaps.toggle_server_expand, "EXPAND_SERVER", { server.name }), + Ui.Keybind(settings.current.ui.keymaps.update_server, "INSTALL_SERVER", { server.name }), + Ui.Keybind(settings.current.ui.keymaps.uninstall_server, "UNINSTALL_SERVER", { server.name }), + Ui.When(is_expanded, function() + return Indent { + ServerMetadata(server), + } + end), } end, servers)) end local function TailedOutput(server) return Ui.HlTextNode(Data.list_map(function(line) - return { { line, "LspInstallerGray" } } + return { { line, "LspInstallerMuted" } } end, server.installer.tailed_output)) end @@ -85,22 +197,23 @@ end local function PendingServers(servers) return Ui.Node(Data.list_map(function(server) local has_failed = server.installer.has_run or server.uninstaller.has_run - local note = has_failed and "(failed)" or (server.installer.is_queued and "(queued)" or "(running)") + local note = has_failed and "(failed)" or (server.installer.is_queued and "(queued)" or "(installing)") return Ui.Node { Ui.HlTextNode { - { + Data.list_not_nil( { settings.current.ui.icons.server_pending, has_failed and "LspInstallerError" or "LspInstallerOrange", }, - { " " .. server.name, server.installer.is_running and "" or "LspInstallerGray" }, + { " " .. server.name, server.installer.is_running and "" or "LspInstallerMuted" }, { " " .. note, "Comment" }, - { - has_failed and "" or (" " .. get_last_non_empty_line(server.installer.tailed_output)), + Data.when(not has_failed, { + (" " .. get_last_non_empty_line(server.installer.tailed_output)), "Comment", - }, - }, + }) + ), }, + Ui.Keybind(settings.current.ui.keymaps.install_server, "INSTALL_SERVER", { server.name }), Ui.When(has_failed, function() return Indent { Indent { TailedOutput(server) } } end), @@ -114,16 +227,24 @@ local function PendingServers(servers) end, servers)) end -local function UninstalledServers(servers) +local function UninstalledServers(servers, expanded_server) return Ui.Node(Data.list_map(function(server) + local is_expanded = expanded_server == server.name return Ui.Node { Ui.HlTextNode { - { - { settings.current.ui.icons.server_uninstalled, "LspInstallerGray" }, - { " " .. server.name, "Comment" }, - { server.uninstaller.has_run and " (just uninstalled)" or "", "Comment" }, - }, + Data.list_not_nil( + { settings.current.ui.icons.server_uninstalled, "LspInstallerMuted" }, + { " " .. server.name, "LspInstallerMuted" }, + Data.when(server.uninstaller.has_run, { " (just uninstalled)", "Comment" }) + ), }, + Ui.Keybind(settings.current.ui.keymaps.toggle_server_expand, "EXPAND_SERVER", { server.name }), + Ui.Keybind(settings.current.ui.keymaps.install_server, "INSTALL_SERVER", { server.name }), + Ui.When(is_expanded, function() + return Indent { + ServerMetadata(server), + } + end), } end, servers)) end @@ -144,13 +265,13 @@ local function ServerGroup(props) count = total_server_count, }, Indent(Data.list_map(function(servers) - return props.renderer(servers) + return props.renderer(servers, props.expanded_server) end, props.servers)), } end) end -local function Servers(servers) +local function Servers(servers, expanded_server) local grouped_servers = { installed = {}, queued = {}, @@ -192,6 +313,7 @@ local function Servers(servers) title = "Installed servers", renderer = InstalledServers, servers = { grouped_servers.session_installed, grouped_servers.installed }, + expanded_server = expanded_server, }, ServerGroup { title = "Pending servers", @@ -203,26 +325,27 @@ local function Servers(servers) grouped_servers.install_failed, grouped_servers.uninstall_failed, }, + expanded_server = expanded_server, }, ServerGroup { title = "Available servers", renderer = UninstalledServers, servers = { grouped_servers.session_uninstalled, grouped_servers.uninstalled }, + expanded_server = expanded_server, }, } end -local function create_server_state(server) - local ok, fstat = pcall(fs.fstat, server.root_dir) - local creation_time - if ok then - creation_time = fstat.mtime.sec - end - +local function create_initial_server_state(server) return { name = server.name, is_installed = server:is_installed(), - creation_time = creation_time, + metadata = { + cmd = table.concat(server._default_options.cmd, " "), + homepage = server.homepage, + install_timestamp_seconds = nil, -- lazy + install_dir = server.root_dir, + }, installer = { is_queued = false, is_running = false, @@ -246,34 +369,54 @@ local function init(all_servers) window.view(function(state) return Indent { + Ui.Keybind(HELP_KEYMAP, "TOGGLE_HELP", nil, true), + Ui.Keybind(CLOSE_WINDOW_KEYMAP_1, "CLOSE_WINDOW", nil, true), + Ui.Keybind(CLOSE_WINDOW_KEYMAP_2, "CLOSE_WINDOW", nil, true), Header(), - Servers(state.servers), + Ui.When(state.is_showing_help, function() + return Help(state.is_current_settings_expanded) + end), + Ui.When(not state.is_showing_help, function() + return Servers(state.servers, state.expanded_server) + end), } end) local servers = {} for i = 1, #all_servers do local server = all_servers[i] - servers[server.name] = create_server_state(server) + servers[server.name] = create_initial_server_state(server) end local mutate_state, get_state = window.init { servers = servers, + is_showing_help = false, + expanded_server = nil, } - local function open() - window.open { - win_width = 95, - highlight_groups = { - "hi def LspInstallerHeader gui=bold guifg=#ebcb8b", - "hi def link LspInstallerLink Comment", - "hi def LspInstallerHeading gui=bold", - "hi def LspInstallerGreen guifg=#a3be8c", - "hi def LspInstallerOrange ctermfg=222 guifg=#ebcb8b", - "hi def LspInstallerGray guifg=#888888 ctermfg=144", - "hi def LspInstallerError ctermfg=203 guifg=#f44747", - }, - } + -- TODO: memoize or throttle.. or cache. Do something. Also, as opposed to what the naming currently suggests, this + -- is not really doing anything async stuff, but will very likely do so in the future :tm:. + local async_populate_server_metadata = vim.schedule_wrap(function(server_name) + local ok, server = lsp_servers.get_server(server_name) + if not ok then + return log.warn("Unable to get server when populating metadata.", server_name) + end + local fstat_ok, fstat = pcall(fs.fstat, server.root_dir) + mutate_state(function(state) + if fstat_ok then + state.servers[server.name].metadata.install_timestamp_seconds = fstat.mtime.sec + end + end) + end) + + local function expand_server(server_name) + mutate_state(function(state) + local should_expand = state.expanded_server ~= server_name + state.expanded_server = should_expand and server_name or nil + if should_expand then + async_populate_server_metadata(server_name) + end + end) end local function start_install(server_tuple, on_complete) @@ -309,10 +452,10 @@ local function init(all_servers) state.servers[server.name].installer.tailed_output = {} end state.servers[server.name].is_installed = success - state.servers[server.name].creation_time = os.time() state.servers[server.name].installer.is_running = false state.servers[server.name].installer.has_run = true end) + expand_server(server.name) on_complete() end) end @@ -320,7 +463,7 @@ local function init(all_servers) -- We have a queue because installers have a tendency to hog resources. local queue do - local max_running = 2 + local max_running = settings.current.max_concurrent_installers local q = {} local r = 0 @@ -342,39 +485,101 @@ local function init(all_servers) end end - return { - open = open, - install_server = function(server, version) - log.debug("Installing server", server, version) - local server_state = get_state().servers[server.name] - if server_state and (server_state.installer.is_running or server_state.installer.is_queued) then - log.debug("Installer is already queued/running", server.name) - return - end - mutate_state(function(state) - -- reset state - state.servers[server.name] = create_server_state(server) - state.servers[server.name].installer.is_queued = true - end) - queue(server, version) - end, - uninstall_server = function(server) - local server_state = get_state().servers[server.name] - if server_state and (server_state.installer.is_running or server_state.installer.is_queued) then - log.debug("Installer is already queued/running", server.name) - return + local function install_server(server, version) + log.debug("Installing server", server, version) + local server_state = get_state().servers[server.name] + if server_state and (server_state.installer.is_running or server_state.installer.is_queued) then + log.debug("Installer is already queued/running", server.name) + return + end + mutate_state(function(state) + -- reset state + state.servers[server.name] = create_initial_server_state(server) + state.servers[server.name].installer.is_queued = true + end) + queue(server, version) + end + + local function uninstall_server(server) + local server_state = get_state().servers[server.name] + if server_state and (server_state.installer.is_running or server_state.installer.is_queued) then + log.debug("Installer is already queued/running", server.name) + return + end + + local is_uninstalled, err = pcall(server.uninstall, server) + mutate_state(function(state) + -- reset state + state.servers[server.name] = create_initial_server_state(server) + if is_uninstalled then + state.servers[server.name].is_installed = false end + state.servers[server.name].uninstaller.has_run = true + state.servers[server.name].uninstaller.error = err + end) + end - local is_uninstalled, err = pcall(server.uninstall, server) - mutate_state(function(state) - state.servers[server.name] = create_server_state(server) - if is_uninstalled then - state.servers[server.name].is_installed = false - end - state.servers[server.name].uninstaller.has_run = true - state.servers[server.name].uninstaller.error = err - end) - end, + local function open() + mutate_state(function(state) + state.is_showing_help = false + end) + + window.open { + win_width = 95, + highlight_groups = { + "hi def LspInstallerHeader gui=bold guifg=#ebcb8b", + "hi def LspInstallerServerExpanded gui=italic", + "hi def LspInstallerHeading gui=bold", + "hi def LspInstallerGreen guifg=#a3be8c", + "hi def LspInstallerOrange ctermfg=222 guifg=#ebcb8b", + "hi def LspInstallerMuted guifg=#888888 ctermfg=144", + "hi def LspInstallerLabel gui=bold", + "hi def LspInstallerError ctermfg=203 guifg=#f44747", + "hi def LspInstallerHighlighted guifg=#56B6C2", + }, + effects = { + ["TOGGLE_HELP"] = function() + if not get_state().is_showing_help then + window.set_cursor { 1, 1 } + end + mutate_state(function(state) + state.is_showing_help = not state.is_showing_help + end) + end, + ["CLOSE_WINDOW"] = function() + window.close() + end, + ["TOGGLE_EXPAND_CURRENT_SETTINGS"] = function() + mutate_state(function(state) + state.is_current_settings_expanded = not state.is_current_settings_expanded + end) + end, + ["EXPAND_SERVER"] = function(e) + local server_name = e.payload[1] + expand_server(server_name) + end, + ["INSTALL_SERVER"] = function(e) + local server_name = e.payload[1] + local ok, server = lsp_servers.get_server(server_name) + if ok then + install_server(server, nil) + end + end, + ["UNINSTALL_SERVER"] = function(e) + local server_name = e.payload[1] + local ok, server = lsp_servers.get_server(server_name) + if ok then + uninstall_server(server) + end + end, + }, + } + end + + return { + open = open, + install_server = install_server, + uninstall_server = uninstall_server, } end @@ -383,7 +588,6 @@ return function() if win then return win end - local servers = require "nvim-lsp-installer.servers" - win = init(servers.get_available_servers()) + win = init(lsp_servers.get_available_servers()) return win end |
