aboutsummaryrefslogtreecommitdiffstats
path: root/lua/nvim-lsp-installer/ui
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2021-10-10 19:20:16 +0200
committerGitHub <noreply@github.com>2021-10-10 19:20:16 +0200
commit5f54148153a878cc9cbe370adccda0f706251d89 (patch)
tree28ae0436232486323dfce3444e1d25657c34d75e /lua/nvim-lsp-installer/ui
parentjdtls: fix jar argument (diff)
downloadmason-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/nvim-lsp-installer/ui')
-rw-r--r--lua/nvim-lsp-installer/ui/display.lua133
-rw-r--r--lua/nvim-lsp-installer/ui/init.lua34
-rw-r--r--lua/nvim-lsp-installer/ui/status-win/init.lua372
3 files changed, 435 insertions, 104 deletions
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