williamboman / nvim-lsp-installer

Further development has moved to https://github.com/williamboman/mason.nvim!
https://github.com/williamboman/mason.nvim
Apache License 2.0
2k stars 123 forks source link

Lazy Loading support #89

Closed TeoDev1611 closed 3 years ago

TeoDev1611 commented 3 years ago

Hi πŸ˜ƒ I'm here again

image

I just found this with the plugi takes sooome of time any idea how improve the lazy loading?

TeoDev1611 commented 3 years ago

I just found this solution but if if have other idea πŸ‘πŸΌ https://github.com/TeoDev1611/AstroVim/blob/astro/lua/plugins.lua#L99

williamboman commented 3 years ago

Hm what exactly is being profiled here? All I see is just numbers! In my next todo I was actually planning on taking a look at performance and loading times, so would be interesting to know what exactly happens inside "nvim-lsp-installer config"

TeoDev1611 commented 3 years ago

Is the nvim-lsp-installer plugin and config @williamboman the numbers are the startup time of the plugin

williamboman commented 3 years ago

So no interactions with any Lua APIs? Just neovim loading the plugin itself? I have profiled that particular scenario before and there is no bottlenecks that would cause a 100ms load time (it's consistently one of my fastest plugins to load, averaging <0.04ms). Are you sure you're not calling on_server_ready() or get_installed_servers() etc? These are highly i/o-bound and I'd not be surprised if they produce such high numbers

TeoDev1611 commented 3 years ago

No this use some functions and configurations:

local lsp_installer = require 'nvim-lsp-installer'
local lsp_servers = require 'nvim-lsp-installer.servers'
local M = {}
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)

-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
local on_attach = function(client, bufnr)
  local function buf_set_keymap(...)
    vim.api.nvim_buf_set_keymap(bufnr, ...)
  end
  local function buf_set_option(...)
    vim.api.nvim_buf_set_option(bufnr, ...)
  end

  -- Enable completion triggered by <c-x><c-o>
  buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')

  -- Mappings.
  local opts = { noremap = true, silent = true }

  -- See `:help vim.lsp.*` for documentation on any of the below functions
  buf_set_keymap('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
  buf_set_keymap('n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', opts)
  buf_set_keymap('n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', opts)
  buf_set_keymap('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts)
  buf_set_keymap('n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
  buf_set_keymap('n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
  buf_set_keymap('n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
  buf_set_keymap('n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
  buf_set_keymap('n', '<space>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
  buf_set_keymap('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
  buf_set_keymap('n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts)
  buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
  buf_set_keymap('n', '<space>e', '<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>', opts)
  buf_set_keymap('n', '[d', '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>', opts)
  buf_set_keymap('n', ']d', '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>', opts)
  buf_set_keymap('n', '<space>q', '<cmd>lua vim.lsp.diagnostic.set_loclist()<CR>', opts)
  buf_set_keymap('n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts)
end

local function setup_handlers()
  vim.lsp.handlers['textDocument/publishDiagnostics'] = vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
    virtual_text = {
      spacing = 5,
      prefix = '',
    },
    signs = false, -- rely on highlight styles instead, don't want to clobber signcolumn
  })
end

function M.setup()
  setup_handlers()
  vim.cmd [[ command! LspLog tabnew|lua vim.cmd('e'..vim.lsp.get_log_path()) ]]

  local ok, sumneko_lua = lsp_servers.get_server 'sumneko_lua'
  if ok then
    if not sumneko_lua:is_installed() then
      sumneko_lua:install()
    end
  end

  lsp_installer.on_server_ready(function(server)
    local opts = {
      on_attach = on_attach,
      capabilities = capabilities,
    }

    if server.name == 'eslintls' then
      opts.settings = {
        format = { enable = true },
      }
    end

    server:setup(opts)
    vim.cmd [[ do User LspAttachBuffers ]]
  end)
end

return M
TeoDev1611 commented 3 years ago

Ah and with the configuration shown above takes this: image

williamboman commented 3 years ago

Yeah the heavy stuff is actually scheduled already, so just measuring that code it'll be blazing fast :P. I'm currently experimenting a bit with speeding up the parts that are scheduled

williamboman commented 3 years ago

So I've done some work to defer require calls as much as possible. These are by far the biggest culprits in terms of performance. On my Windows machine a single require took ~5ms 🀒 (multiply that for each available server...). The problem today is that all the metadata for a given server exist exclusively inside each server's Lua module - which forces us to require all servers to do things like identify which ones are installed etc. I've started separating this a bit and colocating all the essential metadata in one location, and deferring all the "ad-hoc" metadata to the server modules themselves. This allows us to very quickly identify which servers are installed by scanning the root install directory once (via libuv), and then only require the installed servers.

The important metric is what's called when a user registers a on_server_ready callback, because that's the first touchpoint. Here are some profiling results I did across 3 different machines and operating systems:

macOS (11 servers installed)
37ms -> 10ms (~73% faster)

macOS (1 server installed)
37ms -> 1ms (~97% faster)

Windows (11 servers installed)
230ms -> 48ms (~80% faster)

Low-end Ubuntu machine (11 servers installed)
80ms -> 24ms (~70% faster)

I went a bit too far down the rabbit hole however and started looking at some other unrelated things at the same time (which resulted in for example https://github.com/neovim/neovim/issues/15648). Now it's all tangled together, will have to do work to extract the parts that are not ready yet πŸ™ˆ. What makes this all more difficult is that I want to maintain backwards compatibility (for custom servers), which sometimes complicates things

TeoDev1611 commented 3 years ago

:o that's interesting i have a idea is possible call only one server when y call a lua function this could be make the startup time more faster like this:

:lua require('nvim-lsp-installer.lazy').load('sumneko_lua') 

or something like this but can be have a setup option for if donΒ΄t want call to the server manually can be made this automatically example:

require('nvim-lsp-installer').setup{ 
       lazy = {
                  enable = true
       }
}

False by default for disable de lazy loading :p

williamboman commented 3 years ago

No need to make this opt-in behavior (would probably be a headache to even implement two different code paths in the first place)! It'll be default going forward, just finishing up the last bits and pieces at the moment

:lua require('nvim-lsp-installer.lazy').load('sumneko_lua')

This is pretty much what will happen - although it will be internalized in a non-public module

williamboman commented 3 years ago

@TeoDev1611 Would you mind trying out the io-speedups-new branch (https://github.com/williamboman/nvim-lsp-installer/pull/93) and see if it runs fine for you?

TeoDev1611 commented 3 years ago

Yeaaaaaaah This amazing works image

mjlbach commented 3 years ago

Re: https://github.com/neovim/neovim/issues/15648 I have looked into it, I just haven't decided what to do about it yet :)

williamboman commented 3 years ago

Re: neovim/neovim#15648 I have looked into it, I just haven't decided what to do about it yet :)

Hey! Thanks for the update! I posted another comment with how I could imagine things working in https://github.com/neovim/neovim/issues/15648.

williamboman commented 3 years ago

Lazy loading added in #93