aboutsummaryrefslogtreecommitdiffstats
path: root/lua/nvim-lsp-installer/ui/status-win/init.lua
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2021-09-07 02:44:09 +0200
committerGitHub <noreply@github.com>2021-09-07 02:44:09 +0200
commit00294b84031711013a385f18c0fb0e8db84ebaf9 (patch)
treee45de668229c6b41643c5d1fa0fdb5beb0ff60fa /lua/nvim-lsp-installer/ui/status-win/init.lua
parentlazily require servers for faster startup times (#77) (diff)
downloadmason-00294b84031711013a385f18c0fb0e8db84ebaf9.tar
mason-00294b84031711013a385f18c0fb0e8db84ebaf9.tar.gz
mason-00294b84031711013a385f18c0fb0e8db84ebaf9.tar.bz2
mason-00294b84031711013a385f18c0fb0e8db84ebaf9.tar.lz
mason-00294b84031711013a385f18c0fb0e8db84ebaf9.tar.xz
mason-00294b84031711013a385f18c0fb0e8db84ebaf9.tar.zst
mason-00294b84031711013a385f18c0fb0e8db84ebaf9.zip
add direct integration with libuv instead of going through termopen, also implement a UI (#79)
* add direct integration with libuv instead of going through termopen, also implement a UI * alleged free perf boosts yo that's free cycles
Diffstat (limited to 'lua/nvim-lsp-installer/ui/status-win/init.lua')
-rw-r--r--lua/nvim-lsp-installer/ui/status-win/init.lua334
1 files changed, 334 insertions, 0 deletions
diff --git a/lua/nvim-lsp-installer/ui/status-win/init.lua b/lua/nvim-lsp-installer/ui/status-win/init.lua
new file mode 100644
index 00000000..aef7060f
--- /dev/null
+++ b/lua/nvim-lsp-installer/ui/status-win/init.lua
@@ -0,0 +1,334 @@
+local Ui = require "nvim-lsp-installer.ui"
+local log = require "nvim-lsp-installer.log"
+local Data = require "nvim-lsp-installer.data"
+local display = require "nvim-lsp-installer.ui.display"
+
+local function ServerGroupHeading(props)
+ return Ui.HlTextNode {
+ { { props.title, props.highlight or "LspInstallerHeading" }, { (" (%d)"):format(props.count), "Comment" } },
+ }
+end
+
+local function Indent(children)
+ return Ui.CascadingStyleNode({ Ui.CascadingStyle.INDENT }, children)
+end
+
+local function Header()
+ return Ui.CascadingStyleNode({ Ui.CascadingStyle.CENTERED }, {
+ Ui.HlTextNode {
+ { { "nvim-lsp-installer", "LspInstallerHeader" } },
+ { { "https://github.com/williamboman/nvim-lsp-installer", "LspInstallerLink" } },
+ },
+ })
+end
+
+-- TODO make configurable
+local LIST_ICON = "◍"
+
+local function InstalledServers(servers)
+ return Ui.Node(Data.list_map(function(server)
+ return Ui.Node {
+ Ui.HlTextNode {
+ {
+ { LIST_ICON, "LspInstallerGreen" },
+ { " " .. server.name, "Normal" },
+ { (server.installer.has_run and " (new)" or ""), "Comment" },
+ },
+ },
+ }
+ end, servers))
+end
+
+local function TailedOutput(server)
+ return Ui.HlTextNode(Data.list_map(function(line)
+ return { { line, "LspInstallerGray" } }
+ end, server.installer.tailed_output))
+end
+
+local function get_last_non_empty_line(output)
+ for i = #output, 1, -1 do
+ local line = output[i]
+ if #line > 0 then
+ return line
+ end
+ end
+ return ""
+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)")
+ return Ui.Node {
+ Ui.HlTextNode {
+ {
+ { LIST_ICON, has_failed and "LspInstallerError" or "LspInstallerOrange" },
+ { " " .. server.name, server.installer.is_running and "Normal" or "LspInstallerGray" },
+ { " " .. note, "Comment" },
+ { has_failed and "" or (" " .. get_last_non_empty_line(server.installer.tailed_output)), "Comment" },
+ },
+ },
+ Ui.When(has_failed, function()
+ return Indent { Indent { TailedOutput(server) } }
+ end),
+ Ui.When(
+ server.uninstaller.error,
+ Indent {
+ Ui.HlTextNode { server.uninstaller.error, "Comment" },
+ }
+ ),
+ }
+ end, servers))
+end
+
+local function UninstalledServers(servers)
+ return Ui.Node(Data.list_map(function(server)
+ return Ui.Node {
+ Ui.HlTextNode {
+ {
+ { LIST_ICON, "LspInstallerGray" },
+ { " " .. server.name, "Comment" },
+ { server.uninstaller.has_run and " (just uninstalled)" or "", "Comment" },
+ },
+ },
+ }
+ end, servers))
+end
+
+local function ServerGroup(props)
+ local total_server_count = 0
+ local chunks = props.servers
+ for i = 1, #chunks do
+ local servers = chunks[i]
+ total_server_count = total_server_count + #servers
+ end
+
+ return Ui.When(total_server_count > 0 or not props.hide_when_empty, function()
+ return Ui.Node {
+ Ui.EmptyLine(),
+ ServerGroupHeading {
+ title = props.title,
+ count = total_server_count,
+ },
+ Indent(Data.list_map(function(servers)
+ return props.renderer(servers)
+ end, props.servers)),
+ }
+ end)
+end
+
+local function Servers(servers)
+ local grouped_servers = {
+ installed = {},
+ queued = {},
+ session_installed = {},
+ uninstall_failed = {},
+ installing = {},
+ install_failed = {},
+ uninstalled = {},
+ session_uninstalled = {},
+ }
+
+ -- giggity
+ for _, server in pairs(servers) do
+ if server.installer.is_running then
+ grouped_servers.installing[#grouped_servers.installing + 1] = server
+ elseif server.installer.is_queued then
+ grouped_servers.queued[#grouped_servers.queued + 1] = server
+ elseif server.uninstaller.has_run then
+ if server.uninstaller.error then
+ grouped_servers.uninstall_failed[#grouped_servers.uninstall_failed + 1] = server
+ else
+ grouped_servers.session_uninstalled[#grouped_servers.session_uninstalled + 1] = server
+ end
+ elseif server.is_installed then
+ if server.installer.has_run then
+ grouped_servers.session_installed[#grouped_servers.session_installed + 1] = server
+ else
+ grouped_servers.installed[#grouped_servers.installed + 1] = server
+ end
+ elseif server.installer.has_run then
+ grouped_servers.install_failed[#grouped_servers.install_failed + 1] = server
+ else
+ grouped_servers.uninstalled[#grouped_servers.uninstalled + 1] = server
+ end
+ end
+
+ return Ui.Node {
+ ServerGroup {
+ title = "Installed servers",
+ renderer = InstalledServers,
+ servers = { grouped_servers.session_installed, grouped_servers.installed },
+ },
+ ServerGroup {
+ title = "Pending servers",
+ hide_when_empty = true,
+ renderer = PendingServers,
+ servers = {
+ grouped_servers.installing,
+ grouped_servers.queued,
+ grouped_servers.install_failed,
+ grouped_servers.uninstall_failed,
+ },
+ },
+ ServerGroup {
+ title = "Available servers",
+ renderer = UninstalledServers,
+ servers = { grouped_servers.session_uninstalled, grouped_servers.uninstalled },
+ },
+ }
+end
+
+local function create_server_state(server)
+ return {
+ name = server.name,
+ is_installed = server:is_installed(),
+ installer = {
+ is_queued = false,
+ is_running = false,
+ has_run = false,
+ tailed_output = {},
+ },
+ uninstaller = { has_run = false, error = nil },
+ }
+end
+
+local function init(all_servers)
+ local window = display.new_view_only_win "LSP servers"
+
+ window.view(function(state)
+ return Ui.Node {
+ Header(),
+ Servers(state.servers),
+ }
+ end)
+
+ local servers = {}
+ for i = 1, #all_servers do
+ local server = all_servers[i]
+ servers[server.name] = create_server_state(server)
+ end
+
+ local mutate_state, get_state = window.init {
+ servers = servers,
+ }
+
+ 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",
+ },
+ }
+ end
+
+ local function start_install(server, on_complete)
+ mutate_state(function(state)
+ state.servers[server.name].installer.is_queued = false
+ state.servers[server.name].installer.is_running = true
+ end)
+
+ server:install_attached({
+ stdio_sink = {
+ stdout = function(line)
+ mutate_state(function(state)
+ local tailed_output = state.servers[server.name].installer.tailed_output
+ tailed_output[#tailed_output + 1] = line
+ end)
+ end,
+ stderr = function(line)
+ mutate_state(function(state)
+ local tailed_output = state.servers[server.name].installer.tailed_output
+ tailed_output[#tailed_output + 1] = line
+ end)
+ end,
+ },
+ }, function(success)
+ mutate_state(function(state)
+ if success then
+ -- release stdout/err output table.. hopefully ¯\_(ツ)_/¯
+ state.servers[server.name].installer.tailed_output = {}
+ end
+ state.servers[server.name].is_installed = success
+ state.servers[server.name].installer.is_running = false
+ state.servers[server.name].installer.has_run = true
+ end)
+ on_complete()
+ end)
+ end
+
+ -- We have a queue because installers have a tendency to hog resources.
+ local queue = (function()
+ local max_running = 2
+ local q = {}
+ local r = 0
+
+ local check_queue
+ check_queue = vim.schedule_wrap(function()
+ if #q > 0 and r < max_running then
+ local dequeued_server = table.remove(q, 1)
+ r = r + 1
+ start_install(dequeued_server, function()
+ r = r - 1
+ check_queue()
+ end)
+ end
+ end)
+
+ return function(server)
+ q[#q + 1] = server
+ check_queue()
+ end
+ end)()
+
+ return {
+ open = open,
+ install_server = function(server)
+ log.debug { "installing 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
+ 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)
+ 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
+ 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,
+ }
+end
+
+local win
+return function()
+ if win then
+ return win
+ end
+ local servers = require "nvim-lsp-installer.servers"
+ win = init(servers.get_available_servers())
+ return win
+end