mirror of
https://github.com/neovim/neovim
synced 2025-07-16 01:01:49 +00:00
feat(pack): add built-in plugin manager vim.pack
Problem: No built-in plugin manager Solution: Add built-in plugin manager Co-authored-by: Lewis Russell <lewis6991@gmail.com>
This commit is contained in:
@ -39,6 +39,7 @@ for k, v in pairs({
|
||||
health = true,
|
||||
secure = true,
|
||||
snippet = true,
|
||||
pack = true,
|
||||
_watch = true,
|
||||
}) do
|
||||
vim._submodules[k] = v
|
||||
|
2
runtime/lua/vim/_meta/options.lua
generated
2
runtime/lua/vim/_meta/options.lua
generated
@ -2137,6 +2137,8 @@ vim.go.ei = vim.go.eventignore
|
||||
--- `MenuPopup`,
|
||||
--- `ModeChanged`,
|
||||
--- `OptionSet`,
|
||||
--- `PackChanged`,
|
||||
--- `PackChangedPre`,
|
||||
--- `QuickFixCmdPost`,
|
||||
--- `QuickFixCmdPre`,
|
||||
--- `QuitPre`,
|
||||
|
@ -409,6 +409,25 @@ local function check_external_tools()
|
||||
else
|
||||
health.warn('ripgrep not available')
|
||||
end
|
||||
|
||||
-- `vim.pack` requires `git` executable with version at least 2.36
|
||||
if vim.fn.executable('git') == 1 then
|
||||
local git = vim.fn.exepath('git')
|
||||
local out = vim.system({ 'git', 'version' }, {}):wait().stdout or ''
|
||||
local version = vim.version.parse(out)
|
||||
if version < vim.version.parse('2.36') then
|
||||
local msg = string.format(
|
||||
'git is available (%s), but needs at least version 2.36 (not %s) to work with `vim.pack`',
|
||||
git,
|
||||
tostring(version)
|
||||
)
|
||||
health.warn(msg)
|
||||
else
|
||||
health.ok(('%s (%s)'):format(vim.trim(out), git))
|
||||
end
|
||||
else
|
||||
health.warn('git not available (required by `vim.pack`)')
|
||||
end
|
||||
end
|
||||
|
||||
function M.check()
|
||||
|
1000
runtime/lua/vim/pack.lua
Normal file
1000
runtime/lua/vim/pack.lua
Normal file
File diff suppressed because it is too large
Load Diff
168
runtime/lua/vim/pack/_lsp.lua
Normal file
168
runtime/lua/vim/pack/_lsp.lua
Normal file
@ -0,0 +1,168 @@
|
||||
local M = {}
|
||||
|
||||
local capabilities = {
|
||||
codeActionProvider = true,
|
||||
documentSymbolProvider = true,
|
||||
hoverProvider = true,
|
||||
}
|
||||
--- @type table<string,function>
|
||||
local methods = {}
|
||||
|
||||
--- @param callback function
|
||||
function methods.initialize(_, callback)
|
||||
return callback(nil, { capabilities = capabilities })
|
||||
end
|
||||
|
||||
--- @param callback function
|
||||
function methods.shutdown(_, callback)
|
||||
return callback(nil, nil)
|
||||
end
|
||||
|
||||
local get_confirm_bufnr = function(uri)
|
||||
return tonumber(uri:match('^nvim%-pack://(%d+)/confirm%-update$'))
|
||||
end
|
||||
|
||||
--- @param params { textDocument: { uri: string } }
|
||||
--- @param callback function
|
||||
methods['textDocument/documentSymbol'] = function(params, callback)
|
||||
local bufnr = get_confirm_bufnr(params.textDocument.uri)
|
||||
if bufnr == nil then
|
||||
return callback(nil, {})
|
||||
end
|
||||
|
||||
--- @alias vim.pack.lsp.Position { line: integer, character: integer }
|
||||
--- @alias vim.pack.lsp.Range { start: vim.pack.lsp.Position, end: vim.pack.lsp.Position }
|
||||
--- @alias vim.pack.lsp.Symbol {
|
||||
--- name: string,
|
||||
--- kind: number,
|
||||
--- range: vim.pack.lsp.Range,
|
||||
--- selectionRange: vim.pack.lsp.Range,
|
||||
--- children: vim.pack.lsp.Symbol[]?,
|
||||
--- }
|
||||
|
||||
--- @return vim.pack.lsp.Symbol?
|
||||
local new_symbol = function(name, start_line, end_line, kind)
|
||||
if name == nil then
|
||||
return nil
|
||||
end
|
||||
local range = {
|
||||
start = { line = start_line, character = 0 },
|
||||
['end'] = { line = end_line, character = 0 },
|
||||
}
|
||||
return { name = name, kind = kind, range = range, selectionRange = range }
|
||||
end
|
||||
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
|
||||
--- @return vim.pack.lsp.Symbol[]
|
||||
local parse_headers = function(pattern, start_line, end_line, kind)
|
||||
local res, cur_match, cur_start = {}, nil, nil
|
||||
for i = start_line, end_line do
|
||||
local m = lines[i + 1]:match(pattern)
|
||||
if m ~= nil and m ~= cur_match then
|
||||
table.insert(res, new_symbol(cur_match, cur_start, i, kind))
|
||||
cur_match, cur_start = m, i
|
||||
end
|
||||
end
|
||||
table.insert(res, new_symbol(cur_match, cur_start, end_line, kind))
|
||||
return res
|
||||
end
|
||||
|
||||
local group_kind = vim.lsp.protocol.SymbolKind.Namespace
|
||||
local symbols = parse_headers('^# (%S+)', 0, #lines - 1, group_kind)
|
||||
|
||||
local plug_kind = vim.lsp.protocol.SymbolKind.Module
|
||||
for _, group in ipairs(symbols) do
|
||||
local start_line, end_line = group.range.start.line, group.range['end'].line
|
||||
group.children = parse_headers('^## (.+)$', start_line, end_line, plug_kind)
|
||||
end
|
||||
|
||||
return callback(nil, symbols)
|
||||
end
|
||||
|
||||
--- @param callback function
|
||||
methods['textDocument/codeAction'] = function(_, callback)
|
||||
-- TODO(echasnovski)
|
||||
-- Suggested actions for "plugin under cursor":
|
||||
-- - Delete plugin from disk.
|
||||
-- - Update only this plugin.
|
||||
-- - Exclude this plugin from update.
|
||||
return callback(_, {})
|
||||
end
|
||||
|
||||
--- @param params { textDocument: { uri: string }, position: { line: integer, character: integer } }
|
||||
--- @param callback function
|
||||
methods['textDocument/hover'] = function(params, callback)
|
||||
local bufnr = get_confirm_bufnr(params.textDocument.uri)
|
||||
if bufnr == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
local lnum = params.position.line + 1
|
||||
local commit = lines[lnum]:match('^[<>] (%x+) │') or lines[lnum]:match('^State.*:%s+(%x+)')
|
||||
local tag = lines[lnum]:match('^• (.+)$')
|
||||
if commit == nil and tag == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local path, path_lnum = nil, lnum - 1
|
||||
while path == nil and path_lnum >= 1 do
|
||||
path = lines[path_lnum]:match('^Path:%s+(.+)$')
|
||||
path_lnum = path_lnum - 1
|
||||
end
|
||||
if path == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local cmd = { 'git', 'show', '--no-color', commit or tag }
|
||||
--- @param sys_out vim.SystemCompleted
|
||||
local on_exit = function(sys_out)
|
||||
local markdown = '```diff\n' .. sys_out.stdout .. '\n```'
|
||||
local res = { contents = { kind = vim.lsp.protocol.MarkupKind.Markdown, value = markdown } }
|
||||
callback(nil, res)
|
||||
end
|
||||
vim.system(cmd, { cwd = path }, vim.schedule_wrap(on_exit))
|
||||
end
|
||||
|
||||
local dispatchers = {}
|
||||
|
||||
-- TODO: Simplify after `vim.lsp.server` is a thing
|
||||
-- https://github.com/neovim/neovim/pull/24338
|
||||
local cmd = function(disp)
|
||||
-- Store dispatchers to use for showing progress notifications
|
||||
dispatchers = disp
|
||||
local res, closing, request_id = {}, false, 0
|
||||
|
||||
function res.request(method, params, callback)
|
||||
local method_impl = methods[method]
|
||||
if method_impl ~= nil then
|
||||
method_impl(params, callback)
|
||||
end
|
||||
request_id = request_id + 1
|
||||
return true, request_id
|
||||
end
|
||||
|
||||
function res.notify(method, _)
|
||||
if method == 'exit' then
|
||||
dispatchers.on_exit(0, 15)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function res.is_closing()
|
||||
return closing
|
||||
end
|
||||
|
||||
function res.terminate()
|
||||
closing = true
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
M.client_id = assert(
|
||||
vim.lsp.start({ cmd = cmd, name = 'vim.pack', root_dir = vim.uv.cwd() }, { attach = false })
|
||||
)
|
||||
|
||||
return M
|
Reference in New Issue
Block a user