diff options
| author | William Boman <william@redwill.se> | 2021-09-07 02:44:09 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-09-07 02:44:09 +0200 |
| commit | 00294b84031711013a385f18c0fb0e8db84ebaf9 (patch) | |
| tree | e45de668229c6b41643c5d1fa0fdb5beb0ff60fa /lua/nvim-lsp-installer/ui/display.lua | |
| parent | lazily require servers for faster startup times (#77) (diff) | |
| download | mason-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/display.lua')
| -rw-r--r-- | lua/nvim-lsp-installer/ui/display.lua | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/lua/nvim-lsp-installer/ui/display.lua b/lua/nvim-lsp-installer/ui/display.lua new file mode 100644 index 00000000..1027afaa --- /dev/null +++ b/lua/nvim-lsp-installer/ui/display.lua @@ -0,0 +1,239 @@ +local Ui = require "nvim-lsp-installer.ui" +local log = require "nvim-lsp-installer.log" +local state = require "nvim-lsp-installer.ui.state" + +local M = {} + +local redraw_by_winnr = {} + +function _G.lsp_install_redraw(winnr) + local fn = redraw_by_winnr[winnr] + if fn then + fn() + end +end + +local function debounced(debounced_fn) + local queued = false + local last_arg = nil + return function(a) + last_arg = a + if queued then + return + end + queued = true + vim.schedule(function() + debounced_fn(last_arg) + queued = false + end) + end +end + +local function get_styles(line, render_context) + local indentation = 0 + + for i = 1, #render_context.applied_block_styles do + local styles = render_context.applied_block_styles[i] + for j = 1, #styles do + local style = styles[j] + if style == Ui.CascadingStyle.INDENT then + indentation = indentation + 2 + elseif style == Ui.CascadingStyle.CENTERED then + local padding = math.floor((render_context.context.win_width - #line) / 2) + indentation = math.max(0, padding) -- CENTERED overrides any already applied indentation + end + end + end + + return { + indentation = indentation, + } +end + +local function render_node(context, node, _render_context, _output) + local render_context = _render_context or { + context = context, + applied_block_styles = {}, + } + local output = _output or { + lines = {}, + virt_texts = {}, + highlights = {}, + } + + if node.type == Ui.NodeType.VIRTUAL_TEXT then + output.virt_texts[#output.virt_texts + 1] = { + line = #output.lines - 1, + content = node.virt_text, + } + elseif node.type == Ui.NodeType.HL_TEXT then + for i = 1, #node.lines do + local line = node.lines[i] + local line_highlights = {} + local full_line = "" + for j = 1, #line do + local span = line[j] + local content, hl_group = span[1], span[2] + local col_start = #full_line + full_line = full_line .. content + line_highlights[#line_highlights + 1] = { + hl_group = hl_group, + line = #output.lines, + col_start = col_start, + col_end = col_start + #content, + } + end + + local active_styles = get_styles(full_line, render_context) + + -- apply indentation + full_line = (" "):rep(active_styles.indentation) .. full_line + for i = 1, #line_highlights do + local highlight = line_highlights[i] + highlight.col_start = highlight.col_start + active_styles.indentation + highlight.col_end = highlight.col_end + active_styles.indentation + output.highlights[#output.highlights + 1] = highlight + end + + output.lines[#output.lines + 1] = full_line + end + elseif node.type == Ui.NodeType.NODE or node.type == Ui.NodeType.STYLE_BLOCK then + if node.type == Ui.NodeType.STYLE_BLOCK then + render_context.applied_block_styles[#render_context.applied_block_styles + 1] = node.styles + end + for i = 1, #node.children do + render_node(context, node.children[i], render_context, output) + end + if node.type == Ui.NodeType.STYLE_BLOCK then + render_context.applied_block_styles[#render_context.applied_block_styles] = nil + end + end + + return output +end + +function M.new_view_only_win(name) + local namespace = vim.api.nvim_create_namespace(("lsp_installer_%s"):format(name)) + local win, buf, renderer, mutate_state, get_state, unsubscribe + local has_initiated = false + + local function open(opts) + opts = opts or {} + local win_width, highlight_groups = opts.win_width, opts.highlight_groups + + if win_width then + vim.cmd(("%dvnew"):format(win_width)) + else + vim.cmd [[vnew]] + end + + win = vim.api.nvim_get_current_win() + buf = vim.api.nvim_get_current_buf() + + vim.api.nvim_buf_set_option(buf, "modifiable", false) + vim.api.nvim_buf_set_option(buf, "swapfile", false) + vim.api.nvim_buf_set_option(buf, "textwidth", 0) + vim.api.nvim_buf_set_option(buf, "buftype", "nofile") + vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe") + vim.api.nvim_buf_set_option(buf, "buflisted", false) + vim.api.nvim_buf_set_option(buf, "filetype", "lsp-installer") + + vim.api.nvim_win_set_option(win, "wrap", false) + vim.api.nvim_win_set_option(win, "spell", false) + vim.api.nvim_win_set_option(win, "number", false) + vim.api.nvim_win_set_option(win, "relativenumber", false) + vim.api.nvim_win_set_option(win, "foldenable", false) + vim.api.nvim_win_set_option(win, "signcolumn", "no") + vim.api.nvim_win_set_option(win, "colorcolumn", "") + + vim.cmd [[ syntax clear ]] + + for _, redraw_event in ipairs { "WinEnter", "WinLeave", "VimResized" } do + vim.cmd(("autocmd %s <buffer> call v:lua.lsp_install_redraw(%d)"):format(redraw_event, win)) + end + + if highlight_groups then + for i = 1, #highlight_groups do + vim.cmd(highlight_groups[i]) + end + end + end + + local draw = debounced(function(view) + if not win or not vim.api.nvim_win_is_valid(win) then + -- the window has been closed, e.g, by the user + unsubscribe(true) + return log.debug { "Window is no longer valid", name, win } + end + + local win_width = vim.api.nvim_win_get_width(win) + local context = { + win_width = win_width, + } + local output = render_node(context, view) + local lines, virt_texts, highlights = output.lines, output.virt_texts, output.highlights + + vim.api.nvim_buf_clear_namespace(0, namespace, 0, -1) + vim.api.nvim_buf_set_option(buf, "modifiable", true) + vim.api.nvim_buf_set_lines(buf, 0, -1, true, lines) + vim.api.nvim_buf_set_option(buf, "modifiable", false) + for i = 1, #virt_texts do + local virt_text = virt_texts[i] + vim.api.nvim_buf_set_extmark(buf, namespace, virt_text.line, 0, { + virt_text = virt_text.content, + }) + end + for i = 1, #highlights do + local highlight = highlights[i] + vim.api.nvim_buf_add_highlight( + buf, + namespace, + highlight.hl_group, + highlight.line, + highlight.col_start, + highlight.col_end + ) + end + end) + + return { + view = function(x) + renderer = x + end, + init = function(initial_state) + assert(renderer ~= nil, "No view function has been registered. Call .view() before .init().") + has_initiated = true + + mutate_state, get_state, unsubscribe = state.create_state_container(initial_state, function(new_state) + draw(renderer(new_state)) + end) + + return mutate_state, get_state + end, + open = vim.schedule_wrap(function(opts) + log.debug { "opening window" } + assert(has_initiated, "Display has not been initiated, cannot open.") + if win and vim.api.nvim_win_is_valid(win) then + return + end + unsubscribe(false) + open(opts) + draw(renderer(get_state())) + redraw_by_winnr[win] = function() + draw(renderer(get_state())) + 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] = nil + -- if win then + -- vim.api.nvim_win_close(win, true) + -- end + -- end), + } +end + +return M |
