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.
This commit is contained in:
dundargoc 2024-01-22 18:23:28 +01:00 committed by dundargoc
parent 51702e0aea
commit 2e982f1aad
23 changed files with 119 additions and 117 deletions

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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), {

View File

@ -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

View File

@ -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.', {

View File

@ -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.

View File

@ -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

View File

@ -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,
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'
})
handlers = default_handlers,
local log = lsp.log
local protocol = lsp.protocol
local ms = protocol.Methods
local util = lsp.util
local changetracking = lsp._changetracking
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,
-- Allow raw RPC access.
rpc = lsp_rpc,
-- 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.

View File

@ -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

View File

@ -1,4 +1,4 @@
local glob = require('vim.glob')
local glob = vim.glob
--- @class lsp.DynamicCapabilities
--- @field capabilities table<string, lsp.Registration[]>

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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))

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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()

View File

@ -1,6 +1,5 @@
local api = vim.api
---@class vim.treesitter.language
local M = {}
---@type table<string,string>

View File

@ -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