commit 2e982f1aad9f1a03562b7a451d642f76b04c37cb parent 51702e0aea99fddba74e299e2640dd350a348df3 Author: dundargoc <gocdundar@gmail.com> Date: Mon, 22 Jan 2024 18:23:28 +0100 refactor: create function for deferred loading The benefit of this is that users only pay for what they use. If e.g. only `vim.lsp.buf_get_clients()` is called then they don't need to load all modules under `vim.lsp` which could lead to significant startuptime saving. Also `vim.lsp.module` is a bit nicer to user compared to `require("vim.lsp.module")`. This isn't used for some nested modules such as `filetype` as it breaks tests with error messages such as "attempt to index field 'detect'". It's not entirely certain the reason for this, but it is likely it is due to filetype being precompiled which would imply deferred loading isn't needed for performance reasons. Diffstat:
23 files changed, 119 insertions(+), 117 deletions(-)
diff --git a/runtime/autoload/provider/perl.vim b/runtime/autoload/provider/perl.vim @@ -3,13 +3,13 @@ if exists('g:loaded_perl_provider') endif function! provider#perl#Call(method, args) abort - return v:lua.require'vim.provider.perl'.call(a:method, a:args) + return v:lua.vim.provider.perl.call(a:method, a:args) endfunction function! provider#perl#Require(host) abort - return v:lua.require'vim.provider.perl'.require(a:host, s:prog) + return v:lua.vim.provider.perl.require(a:host, s:prog) endfunction -let s:prog = v:lua.require'vim.provider.perl'.detect() +let s:prog = v:lua.vim.provider.perl.detect() let g:loaded_perl_provider = empty(s:prog) ? 1 : 2 call v:lua.require'vim.provider.perl'.start() diff --git a/runtime/autoload/provider/python3.vim b/runtime/autoload/provider/python3.vim @@ -3,13 +3,13 @@ if exists('g:loaded_python3_provider') endif function! provider#python3#Call(method, args) abort - return v:lua.require'vim.provider.python'.call(a:method, a:args) + return v:lua.vim.provider.python.call(a:method, a:args) endfunction function! provider#python3#Require(host) abort - return v:lua.require'vim.provider.python'.require(a:host) + return v:lua.vim.provider.python.require(a:host) endfunction -let s:prog = v:lua.require'vim.provider.python'.detect_by_module('neovim') +let s:prog = v:lua.vim.provider.python.detect_by_module('neovim') let g:loaded_python3_provider = empty(s:prog) ? 1 : 2 call v:lua.require'vim.provider.python'.start() diff --git a/runtime/autoload/provider/ruby.vim b/runtime/autoload/provider/ruby.vim @@ -3,14 +3,14 @@ if exists('g:loaded_ruby_provider') endif function! provider#ruby#Require(host) abort - return v:lua.require'vim.provider.ruby'.require(a:host) + return v:lua.vim.provider.ruby.require(a:host) endfunction function! provider#ruby#Call(method, args) abort - return v:lua.require'vim.provider.ruby'.call(a:method, a:args) + return v:lua.vim.provider.ruby.call(a:method, a:args) endfunction -let s:prog = v:lua.require'vim.provider.ruby'.detect() +let s:prog = v:lua.vim.provider.ruby.detect() let g:loaded_ruby_provider = empty(s:prog) ? 1 : 2 let s:plugin_path = expand('<sfile>:p:h') . '/script_host.rb' call v:lua.require'vim.provider.ruby'.start(s:plugin_path) diff --git a/runtime/lua/provider/perl/health.lua b/runtime/lua/provider/perl/health.lua @@ -9,7 +9,7 @@ function M.check() return end - local perl_exec, perl_warnings = require('vim.provider.perl').detect() + local perl_exec, perl_warnings = vim.provider.perl.detect() if not perl_exec then health.warn(assert(perl_warnings), { diff --git a/runtime/lua/provider/python/health.lua b/runtime/lua/provider/python/health.lua @@ -238,7 +238,7 @@ function M.check() end local pythonx_warnings - pyname, pythonx_warnings = require('vim.provider.python').detect_by_module('neovim') + pyname, pythonx_warnings = vim.provider.python.detect_by_module('neovim') if not pyname then health.warn( @@ -363,7 +363,7 @@ function M.check() -- can import 'pynvim'. If so, that Python failed to import 'neovim' as -- well, which is most probably due to a failed pip upgrade: -- https://github.com/neovim/neovim/wiki/Following-HEAD#20181118 - local pynvim_exe = require('vim.provider.python').detect_by_module('pynvim') + local pynvim_exe = vim.provider.python.detect_by_module('pynvim') if pynvim_exe then local message = 'Detected pip upgrade failure: Python executable can import "pynvim" but not "neovim": ' .. pynvim_exe diff --git a/runtime/lua/provider/ruby/health.lua b/runtime/lua/provider/ruby/health.lua @@ -20,7 +20,7 @@ function M.check() end health.info('Ruby: ' .. health.system({ 'ruby', '-v' })) - local ruby_detect_table = require('vim.provider.ruby').detect() + local ruby_detect_table = vim.provider.ruby.detect() local host = ruby_detect_table[1] if (not host) or host:find('^%s*$') then health.warn('`neovim-ruby-host` not found.', { diff --git a/runtime/lua/vim/_init_packages.lua b/runtime/lua/vim/_init_packages.lua @@ -60,6 +60,7 @@ vim._submodules = { iter = true, re = true, text = true, + provider = true, } -- These are for loading runtime modules in the vim namespace lazily. diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua @@ -1732,7 +1732,7 @@ function M.open_float(opts, ...) if not opts.focus_id then opts.focus_id = scope end - local float_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts) + local float_bufnr, winnr = vim.lsp.util.open_floating_preview(lines, 'plaintext', opts) for i, hl in ipairs(highlights) do local line = lines[i] local prefix_len = hl.prefix and hl.prefix.length or 0 diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua @@ -1,13 +1,4 @@ ---@diagnostic disable: invisible -local default_handlers = require('vim.lsp.handlers') -local log = require('vim.lsp.log') -local lsp_rpc = require('vim.lsp.rpc') -local protocol = require('vim.lsp.protocol') -local ms = protocol.Methods -local util = require('vim.lsp.util') -local changetracking = require('vim.lsp._changetracking') -local semantic_tokens = require('vim.lsp.semantic_tokens') - local api = vim.api local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_exec_autocmds = api.nvim_err_writeln, api.nvim_buf_get_lines, api.nvim_command, api.nvim_exec_autocmds @@ -16,24 +7,34 @@ local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend local validate = vim.validate local if_nil = vim.F.if_nil -local lsp = { - protocol = protocol, - - handlers = default_handlers, - - buf = require('vim.lsp.buf'), - diagnostic = require('vim.lsp.diagnostic'), - codelens = require('vim.lsp.codelens'), - inlay_hint = require('vim.lsp.inlay_hint'), - semantic_tokens = semantic_tokens, - util = util, +local lsp = vim._defer_require('vim.lsp', { + _changetracking = ..., --- @module 'vim.lsp._changetracking' + _completion = ..., --- @module 'vim.lsp._completion' + _dynamic = ..., --- @module 'vim.lsp._dynamic' + _snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar' + _watchfiles = ..., --- @module 'vim.lsp._watchfiles' + buf = ..., --- @module 'vim.lsp.buf' + codelens = ..., --- @module 'vim.lsp.codelens' + diagnostic = ..., --- @module 'vim.lsp.diagnostic' + handlers = ..., --- @module 'vim.lsp.handlers' + inlay_hint = ..., --- @module 'vim.lsp.inlay_hint' + log = ..., --- @module 'vim.lsp.log' + protocol = ..., --- @module 'vim.lsp.protocol' + rpc = ..., --- @module 'vim.lsp.rpc' + semantic_tokens = ..., --- @module 'vim.lsp.semantic_tokens' + tagfunc = ..., --- @module 'vim.lsp.tagfunc' + util = ..., --- @module 'vim.lsp.util' +}) - -- Allow raw RPC access. - rpc = lsp_rpc, +local log = lsp.log +local protocol = lsp.protocol +local ms = protocol.Methods +local util = lsp.util +local changetracking = lsp._changetracking - -- Export these directly from rpc. - rpc_response_error = lsp_rpc.rpc_response_error, -} +-- Export these directly from rpc. +---@nodoc +lsp.rpc_response_error = lsp.rpc.rpc_response_error -- maps request name to the required server_capability in the client. lsp._request_name_to_capability = { @@ -189,11 +190,11 @@ end --- @nodoc lsp.client_errors = tbl_extend( 'error', - lsp_rpc.client_errors, + lsp.rpc.client_errors, vim.tbl_add_reverse_lookup({ - BEFORE_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1, - ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 2, - ON_ATTACH_ERROR = table.maxn(lsp_rpc.client_errors) + 3, + BEFORE_INIT_CALLBACK_ERROR = table.maxn(lsp.rpc.client_errors) + 1, + ON_INIT_CALLBACK_ERROR = table.maxn(lsp.rpc.client_errors) + 2, + ON_ATTACH_ERROR = table.maxn(lsp.rpc.client_errors) + 3, }) ) @@ -800,7 +801,7 @@ function lsp.start_client(config) ---@param method (string) LSP method name ---@return lsp.Handler|nil handler for the given method, if defined, or the default from |vim.lsp.handlers| local function resolve_handler(method) - return handlers[method] or default_handlers[method] + return handlers[method] or lsp.handlers[method] end ---@private @@ -958,7 +959,7 @@ function lsp.start_client(config) if type(cmd) == 'function' then rpc = cmd(dispatch) else - rpc = lsp_rpc.start(cmd, cmd_args, dispatch, { + rpc = lsp.rpc.start(cmd, cmd_args, dispatch, { cwd = config.cmd_cwd, env = config.cmd_env, detached = config.detached, @@ -999,7 +1000,7 @@ function lsp.start_client(config) ---@deprecated use client.progress instead messages = { name = name, messages = {}, progress = {}, status = {} }, - dynamic_capabilities = require('vim.lsp._dynamic').new(client_id), + dynamic_capabilities = vim.lsp._dynamic.new(client_id), } ---@type table<string|integer, string> title of unfinished progress sequences by token @@ -1412,7 +1413,7 @@ function lsp.start_client(config) -- opt-out (deleting the semanticTokensProvider from capabilities) vim.schedule(function() if vim.tbl_get(client.server_capabilities, 'semanticTokensProvider', 'full') then - semantic_tokens.start(bufnr, client.id) + lsp.semantic_tokens.start(bufnr, client.id) end end) @@ -1969,7 +1970,7 @@ function lsp.omnifunc(findstart, base) if log.debug() then log.debug('omnifunc.findstart', { findstart = findstart, base = base }) end - return require('vim.lsp._completion').omnifunc(findstart, base) + return vim.lsp._completion.omnifunc(findstart, base) end --- Provides an interface between the built-in client and a `formatexpr` function. @@ -2039,7 +2040,7 @@ end --- ---@return table[] tags A list of matching tags function lsp.tagfunc(pattern, flags) - return require('vim.lsp.tagfunc')(pattern, flags) + return vim.lsp.tagfunc(pattern, flags) end ---Checks whether a client is stopped. diff --git a/runtime/lua/vim/lsp/_completion.lua b/runtime/lua/vim/lsp/_completion.lua @@ -8,7 +8,7 @@ local ms = protocol.Methods ---@return string parsed snippet local function parse_snippet(input) local ok, parsed = pcall(function() - return require('vim.lsp._snippet_grammar').parse(input) + return vim.lsp._snippet_grammar.parse(input) end) return ok and tostring(parsed) or input end @@ -206,7 +206,7 @@ function M.omnifunc(findstart, base) local params = util.make_position_params(win, client.offset_encoding) client.request(ms.textDocument_completion, params, function(err, result) if err then - require('vim.lsp.log').warn(err.message) + vim.lsp.log.warn(err.message) end if result and vim.fn.mode() == 'i' then local matches diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua @@ -1,4 +1,4 @@ -local glob = require('vim.glob') +local glob = vim.glob --- @class lsp.DynamicCapabilities --- @field capabilities table<string, lsp.Registration[]> diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua @@ -1,6 +1,6 @@ local bit = require('bit') -local glob = require('vim.glob') -local watch = require('vim._watch') +local glob = vim.glob +local watch = vim._watch local protocol = require('vim.lsp.protocol') local ms = protocol.Methods local lpeg = vim.lpeg diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua @@ -1,8 +1,6 @@ ---@brief lsp-diagnostic -local util = require('vim.lsp.util') local protocol = require('vim.lsp.protocol') -local log = require('vim.lsp.log') local ms = protocol.Methods local api = vim.api @@ -95,7 +93,7 @@ local function tags_lsp_to_vim(diagnostic, client_id) tags = tags or {} tags.deprecated = true else - log.info(string.format('Unknown DiagnosticTag %d from LSP client %d', tag, client_id)) + vim.lsp.log.info(string.format('Unknown DiagnosticTag %d from LSP client %d', tag, client_id)) end end return tags @@ -425,7 +423,7 @@ end local function _refresh(bufnr, opts) opts = opts or {} opts['bufnr'] = bufnr - util._refresh(ms.textDocument_diagnostic, opts) + vim.lsp.util._refresh(ms.textDocument_diagnostic, opts) end --- Enable pull diagnostics for a buffer diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua @@ -120,7 +120,7 @@ M[ms.client_registerCapability] = function(_, result, ctx) local unsupported = {} for _, reg in ipairs(result.registrations) do if reg.method == ms.workspace_didChangeWatchedFiles then - require('vim.lsp._watchfiles').register(reg, ctx) + vim.lsp._watchfiles.register(reg, ctx) elseif not client.dynamic_capabilities:supports_registration(reg.method) then unsupported[#unsupported + 1] = reg.method end @@ -144,7 +144,7 @@ M[ms.client_unregisterCapability] = function(_, result, ctx) for _, unreg in ipairs(result.unregisterations) do if unreg.method == ms.workspace_didChangeWatchedFiles then - require('vim.lsp._watchfiles').unregister(unreg, ctx) + vim.lsp._watchfiles.unregister(unreg, ctx) end end return vim.NIL @@ -223,19 +223,19 @@ M[ms.workspace_workspaceFolders] = function(_, _, ctx) end M[ms.textDocument_publishDiagnostics] = function(...) - return require('vim.lsp.diagnostic').on_publish_diagnostics(...) + return vim.lsp.diagnostic.on_publish_diagnostics(...) end M[ms.textDocument_diagnostic] = function(...) - return require('vim.lsp.diagnostic').on_diagnostic(...) + return vim.lsp.diagnostic.on_diagnostic(...) end M[ms.textDocument_codeLens] = function(...) - return require('vim.lsp.codelens').on_codelens(...) + return vim.lsp.codelens.on_codelens(...) end M[ms.textDocument_inlayHint] = function(...) - return require('vim.lsp.inlay_hint').on_inlayhint(...) + return vim.lsp.inlay_hint.on_inlayhint(...) end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references @@ -643,7 +643,7 @@ end ---@see https://microsoft.github.io/language-server-protocol/specification/#workspace_inlayHint_refresh M[ms.workspace_inlayHint_refresh] = function(err, result, ctx, config) - return require('vim.lsp.inlay_hint').on_refresh(err, result, ctx, config) + return vim.lsp.inlay_hint.on_refresh(err, result, ctx, config) end -- Add boilerplate error validation and logging for all of these. diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua @@ -5,7 +5,7 @@ function M.check() local report_info = vim.health.info local report_warn = vim.health.warn - local log = require('vim.lsp.log') + local log = vim.lsp.log local current_log_level = log.get_level() local log_level_string = log.levels[current_log_level] report_info(string.format('LSP log level : %s', log_level_string)) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua @@ -3,7 +3,7 @@ local snippet = require('vim.lsp._snippet_grammar') local validate = vim.validate local api = vim.api local list_extend = vim.list_extend -local highlight = require('vim.highlight') +local highlight = vim.highlight local uv = vim.uv local npcall = vim.F.npcall @@ -636,7 +636,7 @@ end ---@see complete-items function M.text_document_completion_list_to_complete_items(result, prefix) vim.deprecate('vim.lsp.util.text_document_completion_list_to_complete_items()', nil, '0.11') - return require('vim.lsp._completion')._lsp_to_complete_items(result, prefix) + return vim.lsp._completion._lsp_to_complete_items(result, prefix) end --- Like vim.fn.bufwinid except it works across tabpages. diff --git a/runtime/lua/vim/provider.lua b/runtime/lua/vim/provider.lua @@ -0,0 +1,7 @@ +local M = vim._defer_require('vim.provider', { + perl = ..., --- @module 'vim.provider.perl' + python = ..., --- @module 'vim.provider.python' + ruby = ..., --- @module 'vim.provider.ruby' +}) + +return M diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua @@ -983,4 +983,24 @@ do end end +--- @private +--- @generic T +--- @param root string +--- @param mod T +--- @return T +function vim._defer_require(root, mod) + return setmetatable({}, { + ---@param t table<string, any> + ---@param k string + __index = function(t, k) + if not mod[k] then + return + end + local name = string.format('%s.%s', root, k) + t[k] = require(name) + return t[k] + end, + }) +end + return vim diff --git a/runtime/lua/vim/snippet.lua b/runtime/lua/vim/snippet.lua @@ -1,4 +1,4 @@ -local G = require('vim.lsp._snippet_grammar') +local G = vim.lsp._snippet_grammar local snippet_group = vim.api.nvim_create_augroup('vim/snippet', {}) local snippet_ns = vim.api.nvim_create_namespace('vim/snippet') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua @@ -1,44 +1,21 @@ local api = vim.api -local LanguageTree = require('vim.treesitter.languagetree') -local Range = require('vim.treesitter._range') ---@type table<integer,LanguageTree> local parsers = setmetatable({}, { __mode = 'v' }) ----@class vim.treesitter ----@field highlighter vim.treesitter.highlighter ----@field query vim.treesitter.query ----@field language vim.treesitter.language -local M = setmetatable({}, { - __index = function(t, k) - ---@diagnostic disable:no-unknown - if k == 'highlighter' then - t[k] = require('vim.treesitter.highlighter') - return t[k] - elseif k == 'language' then - t[k] = require('vim.treesitter.language') - return t[k] - elseif k == 'query' then - t[k] = require('vim.treesitter.query') - return t[k] - end - - local query = require('vim.treesitter.query') - if query[k] then - vim.deprecate('vim.treesitter.' .. k .. '()', 'vim.treesitter.query.' .. k .. '()', '0.10') - t[k] = query[k] - return t[k] - end - - local language = require('vim.treesitter.language') - if language[k] then - vim.deprecate('vim.treesitter.' .. k .. '()', 'vim.treesitter.language.' .. k .. '()', '0.10') - t[k] = language[k] - return t[k] - end - end, +local M = vim._defer_require('vim.treesitter', { + _fold = ..., --- @module 'vim.treesitter._fold' + _query_linter = ..., --- @module 'vim.treesitter._query_linter' + _range = ..., --- @module 'vim.treesitter._range' + dev = ..., --- @module 'vim.treesitter.dev' + highlighter = ..., --- @module 'vim.treesitter.highlighter' + language = ..., --- @module 'vim.treesitter.language' + languagetree = ..., --- @module 'vim.treesitter.languagetree' + query = ..., --- @module 'vim.treesitter.query' }) +local LanguageTree = M.languagetree + --- @nodoc M.language_version = vim._ts_get_language_version() @@ -200,7 +177,7 @@ end function M.get_range(node, source, metadata) if metadata and metadata.range then assert(source) - return Range.add_bytes(source, metadata.range) + return M._range.add_bytes(source, metadata.range) end return { node:range(true) } end @@ -209,7 +186,7 @@ end ---@param range Range ---@returns string local function buf_range_get_text(buf, range) - local start_row, start_col, end_row, end_col = Range.unpack4(range) + local start_row, start_col, end_row, end_col = M._range.unpack4(range) if end_col == 0 then if start_row == end_row then start_col = -1 @@ -237,7 +214,7 @@ function M.get_node_text(node, source, opts) if metadata.text then return metadata.text elseif type(source) == 'number' then - local range = vim.treesitter.get_range(node, source, metadata) + local range = M.get_range(node, source, metadata) return buf_range_get_text(source, range) end @@ -266,9 +243,9 @@ function M.node_contains(node, range) vim.validate({ -- allow a table so nodes can be mocked node = { node, { 'userdata', 'table' } }, - range = { range, Range.validate, 'integer list with 4 or 6 elements' }, + range = { range, M._range.validate, 'integer list with 4 or 6 elements' }, }) - return Range.contains({ node:range() }, range) + return M._range.contains({ node:range() }, range) end --- Returns a list of highlight captures at the given position @@ -502,7 +479,7 @@ end --- argument and should return a string. function M.inspect_tree(opts) ---@diagnostic disable-next-line: invisible - require('vim.treesitter.dev').inspect_tree(opts) + M.dev.inspect_tree(opts) end --- Returns the fold level for {lnum} in the current buffer. Can be set directly to 'foldexpr': @@ -514,7 +491,7 @@ end ---@param lnum integer|nil Line number to calculate fold level for ---@return string function M.foldexpr(lnum) - return require('vim.treesitter._fold').foldexpr(lnum) + return M._fold.foldexpr(lnum) end return M diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua @@ -1,6 +1,6 @@ local M = {} local ts = vim.treesitter -local health = require('vim.health') +local health = vim.health --- Performs a healthcheck for treesitter integration function M.check() diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua @@ -1,6 +1,5 @@ local api = vim.api ----@class vim.treesitter.language local M = {} ---@type table<string,string> diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua @@ -12,7 +12,6 @@ Query.__index = Query ---@field captures table ---@field patterns table<string,any[][]> ----@class vim.treesitter.query local M = {} ---@param files string[] @@ -799,9 +798,9 @@ end --- - clear (boolean) if `true`, just clear current lint errors function M.lint(buf, opts) if opts and opts.clear then - require('vim.treesitter._query_linter').clear(buf) + vim.treesitter._query_linter.clear(buf) else - require('vim.treesitter._query_linter').lint(buf, opts) + vim.treesitter._query_linter.lint(buf, opts) end end @@ -814,7 +813,7 @@ end --- ``` --- function M.omnifunc(findstart, base) - return require('vim.treesitter._query_linter').omnifunc(findstart, base) + return vim.treesitter._query_linter.omnifunc(findstart, base) end --- Opens a live editor to query the buffer you started from. @@ -827,7 +826,7 @@ end --- --- @param lang? string language to open the query editor for. If omitted, inferred from the current buffer's filetype. function M.edit(lang) - require('vim.treesitter.dev').edit_query(lang) + vim.treesitter.dev.edit_query(lang) end return M