Merge branch 'master' of ssh://git@git.john.me.tz:223/jpm/.dotfiles.git

Conflicts:
	bash/bashrc.host
	sway/autostart
This commit is contained in:
John Mertz 2023-07-16 14:46:10 -04:00
commit 914a95d622
1320 changed files with 564732 additions and 30 deletions

View File

@ -2,16 +2,16 @@
## Configure static SSH Agent
export SSH_AUTH_SOCK="$HOME/.spool/ssh-agent.sock"
if [ -e $SSH_AUTH_SOCK ]; then
rm $SSH_AUTH_SOCK
if [ -e "$SSH_AUTH_SOCK" ]; then
rm "$SSH_AUTH_SOCK"
fi
if [ -e "$HOME/.spool/ssh-agent.env "]; then
if [ -e "$HOME/.spool/ssh-agent.env" ]; then
rm "$HOME/.spool/ssh-agent.env"
fi
if [[ "$(pgrep ssh-agent)" ]]; then
pkill ssh-agent
fi
. "$HOME/scripts/ssh-agent.sh"
. ${HOME}/scripts/ssh-agent.sh
# Static/predictable Sway socket
export SWAYSOCK="$HOME/.spool/sway-ipc.sock"

View File

@ -1,18 +1,8 @@
# vim: ft=sh
export TERMINAL="alacritty"
# Setup editor
export EDITOR="flatpak run io.neovim.nvim"
export SSH_AUTH_SOCK="$HOME/.spool/ssh-agent.sock"
if [ -z "$SSH_AGENT_PID" ]; then
if [ -f "$HOME/.spool/ssh-agent.env" ]; then
source "$HOME/.spool/ssh-agent.env" >/dev/null
else
if [ ! -f "$SSH_AUTH_SOCK" ]; then
touch "$SSH_AUTH_SOCK"
fi
$HOME/scripts/ssh-agent.sh
fi
fi
echo "This is the host OS. You probably want to open a Distrobox."

21
nvim.bk/init.lua Normal file
View File

@ -0,0 +1,21 @@
local impatient_ok, impatient = pcall(require, "impatient")
if impatient_ok then impatient.enable_profile() end
for _, source in ipairs {
"core.utils",
"core.options",
"core.bootstrap",
"core.diagnostics",
"core.autocmds",
"core.mappings",
"configs.which-key-register",
} do
local status_ok, fault = pcall(require, source)
if not status_ok then vim.api.nvim_err_writeln("Failed to load " .. source .. "\n\n" .. fault) end
end
astronvim.conditional_func(astronvim.user_plugin_opts("polish", nil, false))
if vim.fn.has "nvim-0.8" ~= 1 or vim.version().prerelease then
vim.schedule(function() astronvim.notify("Unsupported Neovim Version! Please check the requirements", "error") end)
end

194
nvim.bk/init.vim.old Normal file
View File

@ -0,0 +1,194 @@
let plug_install = 0
let autoload_plug_path = stdpath('config') . '/autoload/plug.vim'
if !filereadable(autoload_plug_path)
silent exe '!curl -fL --create-dirs -o ' . autoload_plug_path .
\ ' https://raw.github.com/junegunn/vim-plug/master/plug.vim'
execute 'source ' . fnameescape(autoload_plug_path)
let plug_install = 1
endif
unlet autoload_plug_path
call plug#begin('~/.dotfiles/nvim/plugins')
" Plugins here
" LSP - Language Server Protocol
" Plug 'neovim/nvim-lspconfig'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'hrsh7th/cmp-buffer'
Plug 'hrsh7th/cmp-path'
Plug 'hrsh7th/cmp-cmdline'
Plug 'hrsh7th/nvim-cmp'
" TreeSitter - Syntax highlighting
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
" Fold-Cycle - Better management of foldable blocks
Plug 'jghauser/fold-cycle.nvim'
" Git Gutter - Git diff markers
Plug 'airblade/vim-gitgutter'
" Lightline - lightweight status/tabline beautification
Plug 'itchyny/lightline.vim'
" Nerdtree - Tree explorer
Plug 'preservim/nerdtree'
" Conqueror of Completion
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'bmeneg/coc-perl', {'do': 'yarn install && yarn build'}
" Dart lsp
Plug 'dart-lang/dart-vim-plugin'
call plug#end()
let g:coc_global_extensions = ['coc-json', 'coc-html', 'coc-python', 'coc-php', 'coc-perl', 'coc-flutter', 'coc-git']
if plug_install
PlugInstall --sync
endif
unlet plug_install
source $HOME/.dotfiles/vim/generic
lua <<EOF
require'nvim-treesitter.configs'.setup {
ensure_installed = {
"bash",
"c",
"cmake",
"cpp",
"css",
"dart",
"html",
"javascript",
"json",
"lua",
"make",
"markdown",
"perl",
"php",
"rasi",
"vim",
"yaml",
},
-- Install languages synchronously (only applied to `ensure_installed`)
sync_install = true,
-- List of parsers to ignore installing
ignore_install = { },
highlight = {
-- `false` will disable the whole extension
enable = true,
-- list of language that will be disabled
disable = { "" },
-- Setting this to true will run `:h syntax` and tree-sitter at the same time.
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
-- Using this option may slow down your editor, and you may see some duplicate highlights.
-- Instead of true it can also be a list of languages
additional_vim_regex_highlighting = false,
},
indent = {
-- dont enable this, messes up python indentation
enable = false,
disable = {},
},
}
-- local nvim_lsp = require('lspconfig')
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
buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')
-- Mappings
local opts = { noremap=true, silent=false }
local opts2 = { focusable = false,
close_events = { "BufLeave", "CursorMoved", "InsertEnter", "FocusLost" },
border = 'rounded',
source = 'always', -- show source in diagnostic popup window
prefix = ' '}
buf_set_keymap('n', 'gD', '<Cmd>lua vim.lsp.buf.declaration()<CR>', opts)
buf_set_keymap('n', 'gd', '<Cmd>tab split | 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', '<leader>t', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
buf_set_keymap('n', '<leader>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
buf_set_keymap('n', '<leader>e', '<cmd>lua vim.diagnostic.open_float(0, {{opts2}, scope="line", border="rounded"})<CR>', opts)
buf_set_keymap('n', '[d', '<cmd>lua vim.diagnostic.goto_prev({ float = { border = "rounded" }})<CR>', opts)
buf_set_keymap('n', ']d', '<cmd>lua vim.diagnostic.goto_next({ float = { border = "rounded" }})<CR>', opts)
buf_set_keymap("n", "<leader>q", "<cmd>lua vim.diagnostic.setloclist({open = true})<CR>", opts)
-- Set some keybinds conditional on server capabilities
if client.resolved_capabilities.document_formatting then
buf_set_keymap("n", "<leader>lf", "<cmd>lua vim.lsp.buf.formatting()<CR>", opts)
end
if client.resolved_capabilities.document_range_formatting then
buf_set_keymap("n", "<leader>lf", "<cmd>lua vim.lsp.buf.formatting()<CR>", opts)
end
end
-- NOTE: Don't use more than 1 servers otherwise nvim is unstable
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)
capabilities.textDocument.completion.completionItem.snippetSupport = true
-- Use pylsp
-- nvim_lsp.pylsp.setup({
-- on_attach = on_attach,
-- settings = {
-- pylsp = {
-- plugins = {
-- pylint = { enabled = true, executable = "pylint" },
-- pyflakes = { enabled = true },
-- pycodestyle = { enabled = false },
-- jedi_completion = { fuzzy = true },
-- pyls_isort = { enabled = true },
-- pylsp_mypy = { enabled = true },
-- },
-- }, },
-- flags = {
-- debounce_text_changes = 200,
-- },
-- capabilities = capabilities,
-- })
-- Use pyright or jedi_language_server
--local servers = {'jedi_language_server'}
--local servers = {'pyright'}
--for _, lsp in ipairs(servers) do
-- nvim_lsp[lsp].setup({
-- on_attach = on_attach,
-- capabilities = capabilities
--})
--end
-- Change diagnostic signs.
vim.fn.sign_define("DiagnosticSignError", { text = "✗", texthl = "DiagnosticSignError" })
vim.fn.sign_define("DiagnosticSignWarn", { text = "!", texthl = "DiagnosticSignWarn" })
vim.fn.sign_define("DiagnosticSignInformation", { text = "", texthl = "DiagnosticSignInfo" })
vim.fn.sign_define("DiagnosticSignHint", { text = "", texthl = "DiagnosticSignHint" })
-- global config for diagnostic
vim.diagnostic.config({
underline = false,
virtual_text = true,
signs = true,
severity_sort = true,
})
-- Change border of documentation hover window, See https://github.com/neovim/neovim/pull/13998.
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {
border = "rounded",
})
-- require'lspconfig'.perlpls.setup()
require('fold-cycle').setup({
open_if_closed = true,
close_if_opened = true,
softwrap_movement_fix = true,
})
vim.keymap.set('n', '<tab>',
function() return require('fold-cycle').open() end,
{silent = true, desc = 'Fold-cycle: open folds'})
vim.keymap.set('n', '<s-tab>',
function() return require('fold-cycle').close() end,
{silent = true, desc = 'Fold-cycle: close folds'})
vim.keymap.set('n', 'zC',
function() return require('fold-cycle').close_all() end,
{remap = true, silent = true, desc = 'Fold-cycle: close all folds'})
EOF
set foldmethod=expr
set cursorline
set foldexpr=nvim_treesitter#foldexpr()

View File

@ -0,0 +1,16 @@
local utils = require "Comment.utils"
require("Comment").setup(astronvim.user_plugin_opts("plugins.Comment", {
pre_hook = function(ctx)
local location = nil
if ctx.ctype == utils.ctype.blockwise then
location = require("ts_context_commentstring.utils").get_cursor_location()
elseif ctx.cmotion == utils.cmotion.v or ctx.cmotion == utils.cmotion.V then
location = require("ts_context_commentstring.utils").get_visual_start_location()
end
return require("ts_context_commentstring.internal").calculate_commentstring {
key = ctx.ctype == utils.ctype.linewise and "__default" or "__multiline",
location = location,
}
end,
}))

View File

@ -0,0 +1,25 @@
require("aerial").setup(astronvim.user_plugin_opts("plugins.aerial", {
attach_mode = "global",
backends = { "lsp", "treesitter", "markdown", "man" },
layout = {
min_width = 28,
},
show_guides = true,
filter_kind = false,
guides = {
mid_item = "",
last_item = "",
nested_top = "",
whitespace = " ",
},
keymaps = {
["[y"] = "actions.prev",
["]y"] = "actions.next",
["[Y"] = "actions.prev_up",
["]Y"] = "actions.next_up",
["{"] = false,
["}"] = false,
["[["] = false,
["]]"] = false,
},
}))

View File

@ -0,0 +1,36 @@
require("alpha").setup(astronvim.user_plugin_opts("plugins.alpha", {
layout = {
{ type = "padding", val = vim.fn.max { 2, vim.fn.floor(vim.fn.winheight(0) * 0.2) } },
{
type = "text",
val = astronvim.user_plugin_opts("header", {
" █████ ███████ ████████ ██████ ██████",
"██ ██ ██ ██ ██ ██ ██ ██",
"███████ ███████ ██ ██████ ██ ██",
"██ ██ ██ ██ ██ ██ ██ ██",
"██ ██ ███████ ██ ██ ██ ██████",
" ",
" ███  ██ ██  ██ ██ ███  ███",
" ████  ██ ██  ██ ██ ████  ████",
" ██ ██  ██ ██  ██ ██ ██ ████ ██",
" ██  ██ ██  ██  ██  ██ ██  ██  ██",
" ██   ████   ████   ██ ██  ██",
}, false),
opts = { position = "center", hl = "DashboardHeader" },
},
{ type = "padding", val = 5 },
{
type = "group",
val = {
astronvim.alpha_button("LDR f F", " Find File "),
astronvim.alpha_button("LDR f o", " Recents "),
astronvim.alpha_button("LDR f w", " Find Word "),
astronvim.alpha_button("LDR f n", " New File "),
astronvim.alpha_button("LDR f m", " Bookmarks "),
astronvim.alpha_button("LDR S l", " Last Session "),
},
opts = { spacing = 1 },
},
},
opts = { noautocmd = true },
}))

View File

@ -0,0 +1,28 @@
local npairs = require "nvim-autopairs"
npairs.setup(astronvim.user_plugin_opts("plugins.nvim-autopairs", {
check_ts = true,
ts_config = { java = false },
fast_wrap = {
map = "<M-e>",
chars = { "{", "[", "(", '"', "'" },
pattern = string.gsub([[ [%'%"%)%>%]%)%}%,] ]], "%s+", ""),
offset = 0,
end_key = "$",
keys = "qwertyuiopzxcvbnmasdfghjkl",
check_comma = true,
highlight = "PmenuSel",
highlight_grey = "LineNr",
},
}))
if not vim.g.autopairs_enabled then npairs.disable() end
local rules = astronvim.user_plugin_opts("nvim-autopairs").add_rules
if vim.tbl_contains({ "function", "table" }, type(rules)) then
npairs.add_rules(type(rules) == "function" and rules(npairs) or rules)
end
local cmp_status_ok, cmp = pcall(require, "cmp")
if cmp_status_ok then
cmp.event:on("confirm_done", require("nvim-autopairs.completion.cmp").on_confirm_done { tex = false })
end

View File

@ -0,0 +1 @@
require("better_escape").setup(astronvim.user_plugin_opts "plugins.better_escape")

View File

@ -0,0 +1,36 @@
local close_func = function(bufnum)
local bufdelete_avail, bufdelete = pcall(require, "bufdelete")
if bufdelete_avail then
bufdelete.bufdelete(bufnum, true)
else
vim.cmd.bdelete { args = { bufnum }, bang = true }
end
end
require("bufferline").setup(astronvim.user_plugin_opts("plugins.bufferline", {
options = {
offsets = {
{ filetype = "NvimTree", text = "", padding = 1 },
{ filetype = "neo-tree", text = "", padding = 1 },
{ filetype = "Outline", text = "", padding = 1 },
},
buffer_close_icon = astronvim.get_icon "BufferClose",
modified_icon = astronvim.get_icon "FileModified",
close_icon = astronvim.get_icon "NeovimClose",
close_command = close_func,
right_mouse_command = close_func,
max_name_length = 14,
max_prefix_length = 13,
tab_size = 20,
separator_style = "thin",
},
}))
local highlights = require "bufferline.highlights"
vim.api.nvim_create_autocmd("User", {
pattern = "AstroColorScheme",
group = "BufferlineCmds",
desc = "Bufferline apply colors after astronvim colorscheme change",
callback = function()
highlights.reset_icon_hl_cache()
highlights.set_all(require("bufferline.config").update_highlights())
end,
})

View File

@ -0,0 +1,92 @@
local cmp = require "cmp"
local snip_status_ok, luasnip = pcall(require, "luasnip")
local lspkind_status_ok, lspkind = pcall(require, "lspkind")
if not snip_status_ok then return end
local setup = cmp.setup
local border_opts =
{ border = "single", winhighlight = "Normal:Normal,FloatBorder:FloatBorder,CursorLine:Visual,Search:None" }
local function has_words_before()
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match "%s" == nil
end
setup(astronvim.user_plugin_opts("plugins.cmp", {
enabled = function()
if vim.api.nvim_buf_get_option(0, "buftype") == "prompt" then return false end
return vim.g.cmp_enabled
end,
preselect = cmp.PreselectMode.None,
formatting = {
fields = { "kind", "abbr", "menu" },
format = lspkind_status_ok and lspkind.cmp_format(astronvim.lspkind) or nil,
},
snippet = {
expand = function(args) luasnip.lsp_expand(args.body) end,
},
duplicates = {
nvim_lsp = 1,
luasnip = 1,
cmp_tabnine = 1,
buffer = 1,
path = 1,
},
confirm_opts = {
behavior = cmp.ConfirmBehavior.Replace,
select = false,
},
window = {
completion = cmp.config.window.bordered(border_opts),
documentation = cmp.config.window.bordered(border_opts),
},
mapping = {
["<Up>"] = cmp.mapping.select_prev_item { behavior = cmp.SelectBehavior.Select },
["<Down>"] = cmp.mapping.select_next_item { behavior = cmp.SelectBehavior.Select },
["<C-p>"] = cmp.mapping.select_prev_item { behavior = cmp.SelectBehavior.Insert },
["<C-n>"] = cmp.mapping.select_next_item { behavior = cmp.SelectBehavior.Insert },
["<C-k>"] = cmp.mapping.select_prev_item { behavior = cmp.SelectBehavior.Insert },
["<C-j>"] = cmp.mapping.select_next_item { behavior = cmp.SelectBehavior.Insert },
["<C-d>"] = cmp.mapping(cmp.mapping.scroll_docs(-1), { "i", "c" }),
["<C-f>"] = cmp.mapping(cmp.mapping.scroll_docs(1), { "i", "c" }),
["<C-Space>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
["<C-y>"] = cmp.config.disable,
["<C-e>"] = cmp.mapping {
i = cmp.mapping.abort(),
c = cmp.mapping.close(),
},
["<CR>"] = cmp.mapping.confirm { select = false },
["<Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expandable() then
luasnip.expand()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
elseif has_words_before() then
cmp.complete()
else
fallback()
end
end, {
"i",
"s",
}),
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, {
"i",
"s",
}),
},
}))
for setup_opt, setup_table in pairs(astronvim.user_plugin_opts("cmp.setup", {})) do
for pattern, options in pairs(setup_table) do
setup[setup_opt](pattern, options)
end
end

View File

@ -0,0 +1,3 @@
require("colorizer").setup(
astronvim.user_plugin_opts("plugins.colorizer", { user_default_options = { names = false } })
)

View File

@ -0,0 +1,3 @@
local dap = require "dap"
dap.adapters = astronvim.user_plugin_opts("dap.adapters", dap.adapters)
dap.configurations = astronvim.user_plugin_opts("dap.configurations", dap.configurations)

View File

@ -0,0 +1,5 @@
local dap, dapui = require "dap", require "dapui"
dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open() end
dap.listeners.before.event_terminated["dapui_config"] = function() dapui.close() end
dap.listeners.before.event_exited["dapui_config"] = function() dapui.close() end
dapui.setup(astronvim.user_plugin_opts("plugins.dapui", { floating = { border = "rounded" } }))

View File

@ -0,0 +1,10 @@
require("dressing").setup(astronvim.user_plugin_opts("plugins.dressing", {
input = {
default_prompt = "",
win_options = { winhighlight = "Normal:Normal,NormalNC:Normal" },
},
select = {
backend = { "telescope", "builtin" },
builtin = { win_options = { winhighlight = "Normal:Normal,NormalNC:Normal" } },
},
}))

View File

@ -0,0 +1,10 @@
require("gitsigns").setup(astronvim.user_plugin_opts("plugins.gitsigns", {
signs = {
add = { text = "" },
change = { text = "" },
delete = { text = "" },
topdelete = { text = "" },
changedelete = { text = "" },
untracked = { text = "" },
},
}))

View File

@ -0,0 +1,309 @@
local heirline = require "heirline"
if not astronvim.status then return end
local C = require "default_theme.colors"
local function setup_colors()
local Normal = astronvim.get_hlgroup("Normal", { fg = C.fg, bg = C.bg })
local Comment = astronvim.get_hlgroup("Comment", { fg = C.grey_2, bg = C.bg })
local Error = astronvim.get_hlgroup("Error", { fg = C.red, bg = C.bg })
local StatusLine = astronvim.get_hlgroup("StatusLine", { fg = C.fg, bg = C.grey_4 })
local TabLine = astronvim.get_hlgroup("TabLine", { fg = C.grey, bg = C.none })
local TabLineSel = astronvim.get_hlgroup("TabLineSel", { fg = C.fg, bg = C.none })
local WinBar = astronvim.get_hlgroup("WinBar", { fg = C.grey_2, bg = C.bg })
local WinBarNC = astronvim.get_hlgroup("WinBarNC", { fg = C.grey, bg = C.bg })
local Conditional = astronvim.get_hlgroup("Conditional", { fg = C.purple_1, bg = C.grey_4 })
local String = astronvim.get_hlgroup("String", { fg = C.green, bg = C.grey_4 })
local TypeDef = astronvim.get_hlgroup("TypeDef", { fg = C.yellow, bg = C.grey_4 })
local GitSignsAdd = astronvim.get_hlgroup("GitSignsAdd", { fg = C.green, bg = C.grey_4 })
local GitSignsChange = astronvim.get_hlgroup("GitSignsChange", { fg = C.orange_1, bg = C.grey_4 })
local GitSignsDelete = astronvim.get_hlgroup("GitSignsDelete", { fg = C.red_1, bg = C.grey_4 })
local DiagnosticError = astronvim.get_hlgroup("DiagnosticError", { fg = C.red_1, bg = C.grey_4 })
local DiagnosticWarn = astronvim.get_hlgroup("DiagnosticWarn", { fg = C.orange_1, bg = C.grey_4 })
local DiagnosticInfo = astronvim.get_hlgroup("DiagnosticInfo", { fg = C.white_2, bg = C.grey_4 })
local DiagnosticHint = astronvim.get_hlgroup("DiagnosticHint", { fg = C.yellow_1, bg = C.grey_4 })
local HeirlineInactive = astronvim.get_hlgroup("HeirlineInactive", { fg = nil }).fg
or astronvim.status.hl.lualine_mode("inactive", C.grey_7)
local HeirlineNormal = astronvim.get_hlgroup("HeirlineNormal", { fg = nil }).fg
or astronvim.status.hl.lualine_mode("normal", C.blue)
local HeirlineInsert = astronvim.get_hlgroup("HeirlineInsert", { fg = nil }).fg
or astronvim.status.hl.lualine_mode("insert", C.green)
local HeirlineVisual = astronvim.get_hlgroup("HeirlineVisual", { fg = nil }).fg
or astronvim.status.hl.lualine_mode("visual", C.purple)
local HeirlineReplace = astronvim.get_hlgroup("HeirlineReplace", { fg = nil }).fg
or astronvim.status.hl.lualine_mode("replace", C.red_1)
local HeirlineCommand = astronvim.get_hlgroup("HeirlineCommand", { fg = nil }).fg
or astronvim.status.hl.lualine_mode("command", C.yellow_1)
local HeirlineTerminal = astronvim.get_hlgroup("HeirlineTerminal", { fg = nil }).fg
or astronvim.status.hl.lualine_mode("inactive", HeirlineInsert)
local colors = astronvim.user_plugin_opts("heirline.colors", {
close_fg = Error.fg,
fg = StatusLine.fg,
bg = StatusLine.bg,
section_fg = StatusLine.fg,
section_bg = StatusLine.bg,
git_branch_fg = Conditional.fg,
mode_fg = StatusLine.bg,
treesitter_fg = String.fg,
scrollbar = TypeDef.fg,
git_added = GitSignsAdd.fg,
git_changed = GitSignsChange.fg,
git_removed = GitSignsDelete.fg,
diag_ERROR = DiagnosticError.fg,
diag_WARN = DiagnosticWarn.fg,
diag_INFO = DiagnosticInfo.fg,
diag_HINT = DiagnosticHint.fg,
winbar_fg = WinBar.fg,
winbar_bg = WinBar.bg,
winbarnc_fg = WinBarNC.fg,
winbarnc_bg = WinBarNC.bg,
tabline_bg = StatusLine.bg,
tabline_fg = StatusLine.bg,
buffer_fg = Comment.fg,
buffer_path_fg = WinBarNC.fg,
buffer_close_fg = Comment.fg,
buffer_bg = StatusLine.bg,
buffer_active_fg = Normal.fg,
buffer_active_path_fg = WinBarNC.fg,
buffer_active_close_fg = Error.fg,
buffer_active_bg = Normal.bg,
buffer_visible_fg = Normal.fg,
buffer_visible_path_fg = WinBarNC.fg,
buffer_visible_close_fg = Error.fg,
buffer_visible_bg = Normal.bg,
buffer_overflow_fg = Comment.fg,
buffer_overflow_bg = StatusLine.bg,
buffer_picker_fg = Error.fg,
tab_close_fg = Error.fg,
tab_close_bg = StatusLine.bg,
tab_fg = TabLine.fg,
tab_bg = TabLine.bg,
tab_active_fg = TabLineSel.fg,
tab_active_bg = TabLineSel.bg,
inactive = HeirlineInactive,
normal = HeirlineNormal,
insert = HeirlineInsert,
visual = HeirlineVisual,
replace = HeirlineReplace,
command = HeirlineCommand,
terminal = HeirlineTerminal,
})
for _, section in ipairs {
"git_branch",
"file_info",
"git_diff",
"diagnostics",
"lsp",
"macro_recording",
"mode",
"cmd_info",
"treesitter",
"nav",
} do
if not colors[section .. "_bg"] then colors[section .. "_bg"] = colors["section_bg"] end
if not colors[section .. "_fg"] then colors[section .. "_fg"] = colors["section_fg"] end
end
return colors
end
--- a submodule of heirline specific functions and aliases
astronvim.status.heirline = {}
--- A helper function to get the type a tab or buffer is
-- @param self the self table from a heirline component function
-- @param prefix the prefix of the type, either "tab" or "buffer" (Default: "buffer")
-- @return the string of prefix with the type (i.e. "_active" or "_visible")
function astronvim.status.heirline.tab_type(self, prefix)
local tab_type = ""
if self.is_active then
tab_type = "_active"
elseif self.is_visible then
tab_type = "_visible"
end
return (prefix or "buffer") .. tab_type
end
--- Make a list of buffers, rendering each buffer with the provided component
---@param component table
---@return table
astronvim.status.heirline.make_buflist = function(component)
local overflow_hl = astronvim.status.hl.get_attributes("buffer_overflow", true)
return require("heirline.utils").make_buflist(
astronvim.status.utils.surround(
"tab",
function(self)
return {
main = astronvim.status.heirline.tab_type(self) .. "_bg",
left = "tabline_bg",
right = "tabline_bg",
}
end,
{ -- bufferlist
init = function(self) self.tab_type = astronvim.status.heirline.tab_type(self) end,
on_click = { -- add clickable component to each buffer
callback = function(_, minwid) vim.api.nvim_win_set_buf(0, minwid) end,
minwid = function(self) return self.bufnr end,
name = "heirline_tabline_buffer_callback",
},
{ -- add buffer picker functionality to each buffer
condition = function(self) return self._show_picker end,
update = false,
init = function(self)
local bufname = astronvim.status.provider.filename { fallback = "empty_file" }(self)
local label = bufname:sub(1, 1)
local i = 2
while label ~= " " and self._picker_labels[label] do
if i > #bufname then break end
label = bufname:sub(i, i)
i = i + 1
end
self._picker_labels[label] = self.bufnr
self.label = label
end,
provider = function(self)
return astronvim.status.provider.str { str = self.label, padding = { left = 1, right = 1 } }
end,
hl = astronvim.status.hl.get_attributes "buffer_picker",
},
component, -- create buffer component
},
false -- disable surrounding
),
{ provider = astronvim.get_icon "ArrowLeft" .. " ", hl = overflow_hl },
{ provider = astronvim.get_icon "ArrowRight" .. " ", hl = overflow_hl },
function() return vim.t.bufs end, -- use astronvim bufs variable
false -- disable internal caching
)
end
--- Alias to require("heirline.utils").make_tablist
astronvim.status.heirline.make_tablist = require("heirline.utils").make_tablist
--- Run the buffer picker and execute the callback function on the selected buffer
-- @param callback function with a single parameter of the buffer number
function astronvim.status.heirline.buffer_picker(callback)
local tabline = require("heirline").tabline
local buflist = tabline and tabline._buflist[1]
if buflist then
local prev_showtabline = vim.opt.showtabline
buflist._picker_labels = {}
buflist._show_picker = true
vim.opt.showtabline = 2
vim.cmd.redrawtabline()
local char = vim.fn.getcharstr()
local bufnr = buflist._picker_labels[char]
if bufnr then callback(bufnr) end
buflist._show_picker = false
vim.opt.showtabline = prev_showtabline
vim.cmd.redrawtabline()
end
end
heirline.load_colors(setup_colors())
local heirline_opts = astronvim.user_plugin_opts("plugins.heirline", {
{ -- statusline
hl = { fg = "fg", bg = "bg" },
astronvim.status.component.mode(),
astronvim.status.component.git_branch(),
-- TODO: REMOVE THIS WITH v3
astronvim.status.component.file_info(
(astronvim.is_available "bufferline.nvim" or vim.g.heirline_bufferline)
and { filetype = {}, filename = false, file_modified = false }
or nil
),
-- astronvim.status.component.file_info { filetype = {}, filename = false, file_modified = false },
astronvim.status.component.git_diff(),
astronvim.status.component.diagnostics(),
astronvim.status.component.fill(),
astronvim.status.component.cmd_info(),
astronvim.status.component.fill(),
astronvim.status.component.lsp(),
astronvim.status.component.treesitter(),
astronvim.status.component.nav(),
astronvim.status.component.mode { surround = { separator = "right" } },
},
{ -- winbar
static = {
disabled = {
buftype = { "terminal", "prompt", "nofile", "help", "quickfix" },
filetype = { "NvimTree", "neo%-tree", "dashboard", "Outline", "aerial" },
},
},
init = function(self) self.bufnr = vim.api.nvim_get_current_buf() end,
fallthrough = false,
{
condition = function(self)
return vim.opt.diff:get() or astronvim.status.condition.buffer_matches(self.disabled or {})
end,
init = function() vim.opt_local.winbar = nil end,
},
astronvim.status.component.file_info {
condition = function() return not astronvim.status.condition.is_active() end,
unique_path = {},
file_icon = { hl = astronvim.status.hl.file_icon "winbar" },
file_modified = false,
file_read_only = false,
hl = astronvim.status.hl.get_attributes("winbarnc", true),
surround = false,
update = "BufEnter",
},
astronvim.status.component.breadcrumbs { hl = astronvim.status.hl.get_attributes("winbar", true) },
},
vim.g.heirline_bufferline -- TODO v3: remove this option and make bufferline default
and { -- bufferline
{ -- file tree padding
condition = function(self)
self.winid = vim.api.nvim_tabpage_list_wins(0)[1]
return astronvim.status.condition.buffer_matches(
{ filetype = { "neo%-tree", "NvimTree" } },
vim.api.nvim_win_get_buf(self.winid)
)
end,
provider = function(self) return string.rep(" ", vim.api.nvim_win_get_width(self.winid)) end,
hl = { bg = "tabline_bg" },
},
astronvim.status.heirline.make_buflist(astronvim.status.component.tabline_file_info()), -- component for each buffer tab
astronvim.status.component.fill { hl = { bg = "tabline_bg" } }, -- fill the rest of the tabline with background color
{ -- tab list
condition = function() return #vim.api.nvim_list_tabpages() >= 2 end, -- only show tabs if there are more than one
astronvim.status.heirline.make_tablist { -- component for each tab
provider = astronvim.status.provider.tabnr(),
hl = function(self)
return astronvim.status.hl.get_attributes(astronvim.status.heirline.tab_type(self, "tab"), true)
end,
},
{ -- close button for current tab
provider = astronvim.status.provider.close_button { kind = "TabClose", padding = { left = 1, right = 1 } },
hl = astronvim.status.hl.get_attributes("tab_close", true),
on_click = { callback = astronvim.close_tab, name = "heirline_tabline_close_tab_callback" },
},
},
}
or nil,
})
heirline.setup(heirline_opts[1], heirline_opts[2], heirline_opts[3])
local augroup = vim.api.nvim_create_augroup("Heirline", { clear = true })
vim.api.nvim_create_autocmd("User", {
pattern = "AstroColorScheme",
group = augroup,
desc = "Refresh heirline colors",
callback = function() require("heirline.utils").on_colorscheme(setup_colors()) end,
})
vim.api.nvim_create_autocmd("User", {
pattern = "HeirlineInitWinbar",
group = augroup,
desc = "Disable winbar for some filetypes",
callback = function()
if
vim.opt.diff:get()
or astronvim.status.condition.buffer_matches(require("heirline").winbar.disabled or {
buftype = { "terminal", "prompt", "nofile", "help", "quickfix" },
filetype = { "NvimTree", "neo%-tree", "dashboard", "Outline", "aerial" },
}) -- TODO v3: remove the default fallback here
then
vim.opt_local.winbar = nil
end
end,
})

View File

@ -0,0 +1,45 @@
require("indent_blankline").setup(astronvim.user_plugin_opts("plugins.indent_blankline", {
buftype_exclude = {
"nofile",
"terminal",
},
filetype_exclude = {
"help",
"startify",
"aerial",
"alpha",
"dashboard",
"packer",
"neogitstatus",
"NvimTree",
"neo-tree",
"Trouble",
},
context_patterns = {
"class",
"return",
"function",
"method",
"^if",
"^while",
"jsx_element",
"^for",
"^object",
"^table",
"block",
"arguments",
"if_statement",
"else_clause",
"jsx_element",
"jsx_self_closing_element",
"try_statement",
"catch_clause",
"import_statement",
"operation_type",
},
show_trailing_blankline_indent = false,
use_treesitter = true,
char = "",
context_char = "",
show_current_context = true,
}))

View File

@ -0,0 +1,3 @@
local indent_o_matic = require "indent-o-matic"
indent_o_matic.setup(astronvim.user_plugin_opts "plugins.indent-o-matic")
indent_o_matic.detect()

View File

@ -0,0 +1,13 @@
if vim.g.lsp_handlers_enabled then
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { border = "rounded" })
vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, { border = "rounded" })
end
local setup_servers = function()
vim.tbl_map(astronvim.lsp.setup, astronvim.user_plugin_opts "lsp.servers")
vim.cmd "silent! do FileType"
end
if astronvim.is_available "mason-lspconfig.nvim" then
vim.api.nvim_create_autocmd("User", { pattern = "AstroLspSetup", once = true, callback = setup_servers })
else
setup_servers()
end

View File

@ -0,0 +1,23 @@
astronvim.lspkind = astronvim.user_plugin_opts("plugins.lspkind", {
mode = "symbol",
symbol_map = {
NONE = "",
Array = "",
Boolean = "",
Class = "",
Constructor = "",
Key = "",
Namespace = "",
Null = "NULL",
Number = "#",
Object = "⦿",
Package = "",
Property = "",
Reference = "",
Snippet = "",
String = "𝓐",
TypeParameter = "",
Unit = "",
},
})
require("lspkind").init(astronvim.lspkind)

View File

@ -0,0 +1,17 @@
local user_settings = astronvim.user_plugin_opts "luasnip"
local luasnip = require "luasnip"
if user_settings.config then luasnip.config.setup(user_settings.config) end
for _, load_type in ipairs { "vscode", "snipmate", "lua" } do
local loader = require("luasnip.loaders.from_" .. load_type)
loader.lazy_load()
-- TODO: DEPRECATE _snippet_paths option in next major version release
local paths = user_settings[load_type .. "_snippet_paths"]
if paths then loader.lazy_load { paths = paths } end
local loader_settings = user_settings[load_type]
if loader_settings then loader.lazy_load(loader_settings) end
end
if type(user_settings.filetype_extend) == "table" then
for filetype, snippets in pairs(user_settings.filetype_extend) do
luasnip.filetype_extend(filetype, snippets)
end
end

View File

@ -0,0 +1,6 @@
local mason_lspconfig = require "mason-lspconfig"
mason_lspconfig.setup(astronvim.user_plugin_opts "plugins.mason-lspconfig")
mason_lspconfig.setup_handlers(
astronvim.user_plugin_opts("mason-lspconfig.setup_handlers", { function(server) astronvim.lsp.setup(server) end })
)
astronvim.event "LspSetup"

View File

@ -0,0 +1,3 @@
local mason_null_ls = require "mason-null-ls"
mason_null_ls.setup(astronvim.user_plugin_opts("plugins.mason-null-ls", { automatic_setup = true }))
mason_null_ls.setup_handlers(astronvim.user_plugin_opts("mason-null-ls.setup_handlers", {}))

View File

@ -0,0 +1,3 @@
local mason_nvim_dap = require "mason-nvim-dap"
mason_nvim_dap.setup(astronvim.user_plugin_opts("plugins.mason-nvim-dap", { automatic_setup = true }))
mason_nvim_dap.setup_handlers(astronvim.user_plugin_opts("mason-nvim-dap.setup_handlers", {}))

View File

@ -0,0 +1,13 @@
require("mason").setup(astronvim.user_plugin_opts("plugins.mason", {
ui = {
icons = {
package_installed = "",
package_uninstalled = "",
package_pending = "",
},
},
}))
local cmd = vim.api.nvim_create_user_command
cmd("MasonUpdateAll", function() astronvim.mason.update_all() end, { desc = "Update Mason Packages" })
cmd("MasonUpdate", function(opts) astronvim.mason.update(opts.args) end, { nargs = 1, desc = "Update Mason Package" })

View File

@ -0,0 +1,62 @@
require("neo-tree").setup(astronvim.user_plugin_opts("plugins.neo-tree", {
close_if_last_window = true,
enable_diagnostics = false,
source_selector = {
winbar = true,
content_layout = "center",
tab_labels = {
filesystem = astronvim.get_icon "FolderClosed" .. " File",
buffers = astronvim.get_icon "DefaultFile" .. " Bufs",
git_status = astronvim.get_icon "Git" .. " Git",
diagnostics = astronvim.get_icon "Diagnostic" .. " Diagnostic",
},
},
default_component_configs = {
indent = { padding = 0 },
icon = {
folder_closed = astronvim.get_icon "FolderClosed",
folder_open = astronvim.get_icon "FolderOpen",
folder_empty = astronvim.get_icon "FolderEmpty",
default = astronvim.get_icon "DefaultFile",
},
git_status = {
symbols = {
added = astronvim.get_icon "GitAdd",
deleted = astronvim.get_icon "GitDelete",
modified = astronvim.get_icon "GitChange",
renamed = astronvim.get_icon "GitRenamed",
untracked = astronvim.get_icon "GitUntracked",
ignored = astronvim.get_icon "GitIgnored",
unstaged = astronvim.get_icon "GitUnstaged",
staged = astronvim.get_icon "GitStaged",
conflict = astronvim.get_icon "GitConflict",
},
},
},
window = {
width = 30,
mappings = {
["<space>"] = false, -- disable space until we figure out which-key disabling
o = "open",
H = "prev_source",
L = "next_source",
},
},
filesystem = {
follow_current_file = true,
hijack_netrw_behavior = "open_current",
use_libuv_file_watcher = true,
window = {
mappings = {
O = "system_open",
h = "toggle_hidden",
},
},
commands = {
system_open = function(state) astronvim.system_open(state.tree:get_node():get_id()) end,
},
},
event_handlers = {
{ event = "neo_tree_buffer_enter", handler = function(_) vim.opt_local.signcolumn = "auto" end },
},
}))

View File

@ -0,0 +1,3 @@
local notify = require "notify"
notify.setup(astronvim.user_plugin_opts("plugins.notify", { stages = "fade" }))
vim.notify = notify

View File

@ -0,0 +1 @@
require("null-ls").setup(astronvim.user_plugin_opts("plugins.null-ls", { on_attach = astronvim.lsp.on_attach }))

View File

@ -0,0 +1,15 @@
require("nvim-web-devicons").set_default_icon(astronvim.get_icon "DefaultFile", "#6d8086", "66")
require("nvim-web-devicons").set_icon(astronvim.user_plugin_opts("plugins.nvim-web-devicons", {
deb = { icon = "", name = "Deb" },
lock = { icon = "", name = "Lock" },
mp3 = { icon = "", name = "Mp3" },
mp4 = { icon = "", name = "Mp4" },
out = { icon = "", name = "Out" },
["robots.txt"] = { icon = "", name = "Robots" },
ttf = { icon = "", name = "TrueTypeFont" },
rpm = { icon = "", name = "Rpm" },
woff = { icon = "", name = "WebOpenFontFormat" },
woff2 = { icon = "", name = "WebOpenFontFormat2" },
xz = { icon = "", name = "Xz" },
zip = { icon = "", name = "Zip" },
}))

View File

@ -0,0 +1,8 @@
return {
settings = {
json = {
schemas = require("schemastore").json.schemas(),
validate = { enable = true },
},
},
}

View File

@ -0,0 +1,16 @@
return {
settings = {
Lua = {
telemetry = { enable = false },
runtime = { version = "LuaJIT" },
diagnostics = { globals = { "vim", "astronvim", "astronvim_installation", "packer_plugins", "bit" } },
workspace = {
library = {
vim.fn.expand "$VIMRUNTIME/lua",
astronvim.install.home .. "/lua",
astronvim.install.config .. "/lua",
},
},
},
},
}

View File

@ -0,0 +1 @@
require("session_manager").setup(astronvim.user_plugin_opts "plugins.session_manager")

View File

@ -0,0 +1,9 @@
require("smart-splits").setup(astronvim.user_plugin_opts("plugins.smart-splits", {
ignored_filetypes = {
"nofile",
"quickfix",
"qf",
"prompt",
},
ignored_buftypes = { "nofile" },
}))

View File

@ -0,0 +1,38 @@
local telescope = require "telescope"
local actions = require "telescope.actions"
telescope.setup(astronvim.user_plugin_opts("plugins.telescope", {
defaults = {
prompt_prefix = string.format("%s ", astronvim.get_icon "Search"),
selection_caret = string.format("%s ", astronvim.get_icon "Selected"),
path_display = { "truncate" },
sorting_strategy = "ascending",
layout_config = {
horizontal = {
prompt_position = "top",
preview_width = 0.55,
results_width = 0.8,
},
vertical = {
mirror = false,
},
width = 0.87,
height = 0.80,
preview_cutoff = 120,
},
mappings = {
i = {
["<C-n>"] = actions.cycle_history_next,
["<C-p>"] = actions.cycle_history_prev,
["<C-j>"] = actions.move_selection_next,
["<C-k>"] = actions.move_selection_previous,
},
n = { ["q"] = actions.close },
},
},
}))
astronvim.conditional_func(telescope.load_extension, pcall(require, "notify"), "notify")
astronvim.conditional_func(telescope.load_extension, pcall(require, "aerial"), "aerial")

View File

@ -0,0 +1,13 @@
require("toggleterm").setup(astronvim.user_plugin_opts("plugins.toggleterm", {
size = 10,
open_mapping = [[<F7>]],
shading_factor = 2,
direction = "float",
float_opts = {
border = "curved",
highlights = {
border = "Normal",
background = "Normal",
},
},
}))

View File

@ -0,0 +1,19 @@
require("nvim-treesitter.configs").setup(astronvim.user_plugin_opts("plugins.treesitter", {
highlight = {
enable = true,
additional_vim_regex_highlighting = false,
},
context_commentstring = {
enable = true,
enable_autocmd = false,
},
rainbow = {
enable = true,
disable = { "html" },
extended_mode = false,
max_file_lines = nil,
},
autotag = { enable = true },
incremental_selection = { enable = true },
indent = { enable = false },
}))

View File

@ -0,0 +1,55 @@
local is_available = astronvim.is_available
local user_plugin_opts = astronvim.user_plugin_opts
local mappings = {
n = {
["<leader>"] = {
f = { name = "File" },
p = { name = "Packages" },
l = { name = "LSP" },
u = { name = "UI" },
},
},
}
local extra_sections = {
b = "Buffers",
D = "Debugger",
g = "Git",
s = "Search",
S = "Session",
t = "Terminal",
}
local function init_table(mode, prefix, idx)
if not mappings[mode][prefix][idx] then mappings[mode][prefix][idx] = { name = extra_sections[idx] } end
end
-- TODO v3: remove vim.g.heirline_bufferline check
if is_available "heirline.nvim" and vim.g.heirline_bufferline then init_table("n", "<leader>", "b") end
if is_available "neovim-session-manager" then init_table("n", "<leader>", "S") end
if is_available "gitsigns.nvim" then init_table("n", "<leader>", "g") end
if is_available "toggleterm.nvim" then
init_table("n", "<leader>", "g")
init_table("n", "<leader>", "t")
end
if is_available "telescope.nvim" then
init_table("n", "<leader>", "s")
init_table("n", "<leader>", "g")
end
if is_available "nvim-dap" then init_table("n", "<leader>", "D") end
if is_available "Comment.nvim" then
for _, mode in ipairs { "n", "v" } do
if not mappings[mode] then mappings[mode] = {} end
if not mappings[mode].g then mappings[mode].g = {} end
mappings[mode].g.c = "Comment toggle linewise"
mappings[mode].g.b = "Comment toggle blockwise"
end
end
astronvim.which_key_register(user_plugin_opts("which-key.register", mappings))

View File

@ -0,0 +1,11 @@
require("which-key").setup(astronvim.user_plugin_opts("plugins.which-key", {
plugins = {
spelling = { enabled = true },
presets = { operators = false },
},
window = {
border = "rounded",
padding = { 2, 2, 2, 2 },
},
disable = { filetypes = { "TelescopePrompt" } },
}))

View File

@ -0,0 +1,4 @@
local colors = require "default_theme.colors"
require("window-picker").setup(
astronvim.user_plugin_opts("plugins.window-picker", { use_winbar = "smart", other_win_hl_color = colors.grey_4 })
)

View File

@ -0,0 +1,213 @@
local is_available = astronvim.is_available
local user_plugin_opts = astronvim.user_plugin_opts
local namespace = vim.api.nvim_create_namespace
local cmd = vim.api.nvim_create_autocmd
local augroup = vim.api.nvim_create_augroup
local create_command = vim.api.nvim_create_user_command
vim.on_key(function(char)
if vim.fn.mode() == "n" then
local new_hlsearch = vim.tbl_contains({ "<CR>", "n", "N", "*", "#", "?", "/" }, vim.fn.keytrans(char))
if vim.opt.hlsearch:get() ~= new_hlsearch then vim.opt.hlsearch = new_hlsearch end
end
end, namespace "auto_hlsearch")
if vim.g.heirline_bufferline then
local bufferline_group = augroup("bufferline", { clear = true })
cmd({ "BufAdd", "BufEnter" }, {
desc = "Update buffers when adding new buffers",
group = bufferline_group,
callback = function(args)
if not vim.t.bufs then vim.t.bufs = {} end
local bufs = vim.t.bufs
if not vim.tbl_contains(bufs, args.buf) then
table.insert(bufs, args.buf)
vim.t.bufs = bufs
end
vim.t.bufs = vim.tbl_filter(astronvim.is_valid_buffer, vim.t.bufs)
end,
})
cmd("BufDelete", {
desc = "Update buffers when deleting buffers",
group = bufferline_group,
callback = function(args)
for _, tab in ipairs(vim.api.nvim_list_tabpages()) do
local bufs = vim.t[tab].bufs
if bufs then
for i, bufnr in ipairs(bufs) do
if bufnr == args.buf then
table.remove(bufs, i)
vim.t[tab].bufs = bufs
break
end
end
end
end
vim.t.bufs = vim.tbl_filter(astronvim.is_valid_buffer, vim.t.bufs)
vim.cmd.redrawtabline()
end,
})
end
cmd({ "VimEnter", "FileType", "BufEnter", "WinEnter" }, {
desc = "URL Highlighting",
group = augroup("highlighturl", { clear = true }),
pattern = "*",
callback = function() astronvim.set_url_match() end,
})
cmd("TextYankPost", {
desc = "Highlight yanked text",
group = augroup("highlightyank", { clear = true }),
pattern = "*",
callback = function() vim.highlight.on_yank() end,
})
cmd("FileType", {
desc = "Unlist quickfist buffers",
group = augroup("unlist_quickfist", { clear = true }),
pattern = "qf",
callback = function() vim.opt_local.buflisted = false end,
})
cmd("BufEnter", {
desc = "Quit AstroNvim if more than one window is open and only sidebar windows are list",
group = augroup("auto_quit", { clear = true }),
callback = function()
local wins = vim.api.nvim_tabpage_list_wins(0)
-- Both neo-tree and aerial will auto-quit if there is only a single window left
if #wins <= 1 then return end
local sidebar_fts = { aerial = true, ["neo-tree"] = true }
for _, winid in ipairs(wins) do
if vim.api.nvim_win_is_valid(winid) then
local bufnr = vim.api.nvim_win_get_buf(winid)
local filetype = vim.api.nvim_buf_get_option(bufnr, "filetype")
-- If any visible windows are not sidebars, early return
if not sidebar_fts[filetype] then
return
-- If the visible window is a sidebar
else
-- only count filetypes once, so remove a found sidebar from the detection
sidebar_fts[filetype] = nil
end
end
end
if #vim.api.nvim_list_tabpages() > 1 then
vim.cmd.tabclose()
else
vim.cmd.qall()
end
end,
})
if is_available "alpha-nvim" then
local group_name = augroup("alpha_settings", { clear = true })
cmd("User", {
desc = "Disable status and tablines for alpha",
group = group_name,
pattern = "AlphaReady",
callback = function()
local prev_showtabline = vim.opt.showtabline
local prev_status = vim.opt.laststatus
vim.opt.laststatus = 0
vim.opt.showtabline = 0
vim.opt_local.winbar = nil
cmd("BufUnload", {
pattern = "<buffer>",
callback = function()
vim.opt.laststatus = prev_status
vim.opt.showtabline = prev_showtabline
end,
})
end,
})
cmd("VimEnter", {
desc = "Start Alpha when vim is opened with no arguments",
group = group_name,
callback = function()
local should_skip = false
if vim.fn.argc() > 0 or vim.fn.line2byte "$" ~= -1 or not vim.o.modifiable then
should_skip = true
else
for _, arg in pairs(vim.v.argv) do
if arg == "-b" or arg == "-c" or vim.startswith(arg, "+") or arg == "-S" then
should_skip = true
break
end
end
end
if not should_skip then
if is_available "bufferline.nvim" then pcall(require, "bufferline") end -- TODO v3: remove this line
require("alpha").start(true)
end
end,
})
end
if is_available "neo-tree.nvim" then
cmd("BufEnter", {
desc = "Open Neo-Tree on startup with directory",
group = augroup("neotree_start", { clear = true }),
callback = function()
local stats = vim.loop.fs_stat(vim.api.nvim_buf_get_name(0))
if stats and stats.type == "directory" then require("neo-tree.setup.netrw").hijack() end
end,
})
end
if is_available "nvim-dap-ui" then
vim.api.nvim_create_autocmd("FileType", {
desc = "Make q close dap floating windows",
group = vim.api.nvim_create_augroup("dapui", { clear = true }),
pattern = "dap-float",
callback = function() vim.keymap.set("n", "q", "<cmd>close!<cr>") end,
})
end
cmd({ "VimEnter", "ColorScheme" }, {
desc = "Load custom highlights from user configuration",
group = augroup("astronvim_highlights", { clear = true }),
callback = function()
if vim.g.colors_name then
for _, module in ipairs { "init", vim.g.colors_name } do
for group, spec in pairs(user_plugin_opts("highlights." .. module)) do
vim.api.nvim_set_hl(0, group, spec)
end
end
end
astronvim.event "ColorScheme"
end,
})
vim.api.nvim_create_autocmd("BufRead", {
group = vim.api.nvim_create_augroup("git_plugin_lazy_load", { clear = true }),
callback = function()
vim.fn.system("git -C " .. vim.fn.expand "%:p:h" .. " rev-parse")
if vim.v.shell_error == 0 then
vim.api.nvim_del_augroup_by_name "git_plugin_lazy_load"
local packer = require "packer"
vim.tbl_map(function(plugin) packer.loader(plugin) end, astronvim.git_plugins)
end
end,
})
vim.api.nvim_create_autocmd({ "BufRead", "BufWinEnter", "BufNewFile" }, {
group = vim.api.nvim_create_augroup("file_plugin_lazy_load", { clear = true }),
callback = function(args)
if not (vim.fn.expand "%" == "" or vim.api.nvim_buf_get_option(args.buf, "buftype") == "nofile") then
vim.api.nvim_del_augroup_by_name "file_plugin_lazy_load"
local packer = require "packer"
vim.tbl_map(function(plugin) packer.loader(plugin) end, astronvim.file_plugins)
end
end,
})
create_command(
"AstroUpdatePackages",
function() astronvim.updater.update_packages() end,
{ desc = "Update Packer and Mason" }
)
create_command("AstroUpdate", function() astronvim.updater.update() end, { desc = "Update AstroNvim" })
create_command("AstroReload", function() astronvim.updater.reload() end, { desc = "Reload AstroNvim" })
create_command("AstroVersion", function() astronvim.updater.version() end, { desc = "Check AstroNvim Version" })
create_command("AstroChangelog", function() astronvim.updater.changelog() end, { desc = "Check AstroNvim Changelog" })
create_command("ToggleHighlightURL", function() astronvim.ui.toggle_url_match() end, { desc = "Toggle URL Highlights" })

View File

@ -0,0 +1,4 @@
astronvim.initialize_packer()
local colorscheme = astronvim.user_plugin_opts("colorscheme", nil, false)
vim.cmd.colorscheme(vim.tbl_contains(vim.fn.getcompletion("", "color"), colorscheme) and colorscheme or "default_theme")

View File

@ -0,0 +1,43 @@
local signs = {
{ name = "DiagnosticSignError", text = astronvim.get_icon "DiagnosticError" },
{ name = "DiagnosticSignWarn", text = astronvim.get_icon "DiagnosticWarn" },
{ name = "DiagnosticSignHint", text = astronvim.get_icon "DiagnosticHint" },
{ name = "DiagnosticSignInfo", text = astronvim.get_icon "DiagnosticInfo" },
{ name = "DiagnosticSignError", text = astronvim.get_icon "DiagnosticError" },
{ name = "DapStopped", text = astronvim.get_icon "DapStopped", texthl = "DiagnosticWarn" },
{ name = "DapBreakpoint", text = astronvim.get_icon "DapBreakpoint", texthl = "DiagnosticInfo" },
{ name = "DapBreakpointRejected", text = astronvim.get_icon "DapBreakpointRejected", texthl = "DiagnosticError" },
{ name = "DapBreakpointCondition", text = astronvim.get_icon "DapBreakpointCondition", texthl = "DiagnosticInfo" },
{ name = "DapLogPoint", text = astronvim.get_icon "DapLogPoint", texthl = "DiagnosticInfo" },
}
for _, sign in ipairs(signs) do
if not sign.texthl then sign.texthl = sign.name end
vim.fn.sign_define(sign.name, sign)
end
astronvim.lsp.diagnostics = {
off = {
underline = false,
virtual_text = false,
signs = false,
update_in_insert = false,
},
on = astronvim.user_plugin_opts("diagnostics", {
virtual_text = true,
signs = { active = signs },
update_in_insert = true,
underline = true,
severity_sort = true,
float = {
focused = false,
style = "minimal",
border = "rounded",
source = "always",
header = "",
prefix = "",
},
}),
}
vim.diagnostic.config(astronvim.lsp.diagnostics[vim.g.diagnostics_enabled and "on" or "off"])

View File

@ -0,0 +1,46 @@
return {
ActiveLSP = "",
ActiveTS = "",
ArrowLeft = "",
ArrowRight = "",
BufferClose = "",
DapBreakpoint = "",
DapBreakpointCondition = "",
DapBreakpointRejected = "",
DapLogPoint = ".>",
DapStopped = "",
DefaultFile = "",
Diagnostic = "",
DiagnosticError = "",
DiagnosticHint = "",
DiagnosticInfo = "",
DiagnosticWarn = "",
Ellipsis = "",
FileModified = "",
FileReadOnly = "",
FolderClosed = "",
FolderEmpty = "",
FolderOpen = "",
Git = "",
GitAdd = "",
GitBranch = "",
GitChange = "",
GitConflict = "",
GitDelete = "",
GitIgnored = "",
GitRenamed = "",
GitStaged = "",
GitUnstaged = "",
GitUntracked = "",
LSPLoaded = "",
LSPLoading1 = "",
LSPLoading2 = "",
LSPLoading3 = "",
MacroRecording = "",
NeovimClose = "", -- TODO v3: remove this icon
Paste = "",
Search = "",
Selected = "",
Spellcheck = "",
TabClose = "",
}

View File

@ -0,0 +1,38 @@
return {
ActiveLSP = "LSP:",
ArrowLeft = "<",
ArrowRight = ">",
BufferClose = "x",
DapBreakpoint = "B",
DapBreakpointCondition = "C",
DapBreakpointRejected = "R",
DapLogPoint = "L",
DapStopped = ">",
DefaultFile = "[F]",
DiagnosticError = "X",
DiagnosticHint = "?",
DiagnosticInfo = "i",
DiagnosticWarn = "!",
Ellipsis = "...",
FileModified = "*",
FileReadOnly = "[lock]",
FolderClosed = "[D]",
FolderEmpty = "[E]",
FolderOpen = "[O]",
GitAdd = "[+]",
GitChange = "[/]",
GitConflict = "[!]",
GitDelete = "[-]",
GitIgnored = "[I]",
GitRenamed = "[R]",
GitStaged = "[S]",
GitUnstaged = "[U]",
GitUntracked = "[?]",
MacroRecording = "Recording:",
NeovimClose = "X", -- TODO v3: remove this icon
Paste = "[PASTE]",
Search = "?",
Selected = "*",
Spellcheck = "[SPELL]",
TabClose = "X",
}

View File

@ -0,0 +1,317 @@
local is_available = astronvim.is_available
local maps = { i = {}, n = {}, v = {}, t = {} }
-- Normal --
-- Standard Operations
maps.n["<leader>w"] = { "<cmd>w<cr>", desc = "Save" }
maps.n["<leader>q"] = { "<cmd>q<cr>", desc = "Quit" }
maps.n["<leader>h"] = { "<cmd>nohlsearch<cr>", desc = "No Highlight" } -- TODO: REMOVE IN v3
maps.n["<leader>fn"] = { "<cmd>enew<cr>", desc = "New File" }
maps.n["gx"] = { function() astronvim.system_open() end, desc = "Open the file under cursor with system app" }
maps.n["<C-s>"] = { "<cmd>w!<cr>", desc = "Force write" }
maps.n["<C-q>"] = { "<cmd>q!<cr>", desc = "Force quit" }
maps.n["Q"] = "<Nop>"
maps.n["|"] = { "<cmd>vsplit<cr>", desc = "Vertical Split" }
maps.n["\\"] = { "<cmd>split<cr>", desc = "Horizontal Split" }
-- Packer
maps.n["<leader>pc"] = { "<cmd>PackerCompile<cr>", desc = "Packer Compile" }
maps.n["<leader>pi"] = { "<cmd>PackerInstall<cr>", desc = "Packer Install" }
maps.n["<leader>ps"] = { "<cmd>PackerSync<cr>", desc = "Packer Sync" }
maps.n["<leader>pS"] = { "<cmd>PackerStatus<cr>", desc = "Packer Status" }
maps.n["<leader>pu"] = { "<cmd>PackerUpdate<cr>", desc = "Packer Update" }
-- AstroNvim
maps.n["<leader>pa"] = { "<cmd>AstroUpdatePackages<cr>", desc = "Update Packer and Mason" }
maps.n["<leader>pA"] = { "<cmd>AstroUpdate<cr>", desc = "AstroNvim Update" }
maps.n["<leader>pv"] = { "<cmd>AstroVersion<cr>", desc = "AstroNvim Version" }
maps.n["<leader>pl"] = { "<cmd>AstroChangelog<cr>", desc = "AstroNvim Changelog" }
-- Alpha
if is_available "alpha-nvim" then
maps.n["<leader>d"] = { function() require("alpha").start() end, desc = "Alpha Dashboard" }
end
if vim.g.heirline_bufferline then
-- Manage Buffers
maps.n["<leader>c"] = { function() astronvim.close_buf(0) end, desc = "Close buffer" }
maps.n["<leader>C"] = { function() astronvim.close_buf(0, true) end, desc = "Force close buffer" }
maps.n["<S-l>"] = { function() astronvim.nav_buf(vim.v.count > 0 and vim.v.count or 1) end, desc = "Next buffer" }
maps.n["<S-h>"] =
{ function() astronvim.nav_buf(-(vim.v.count > 0 and vim.v.count or 1)) end, desc = "Previous buffer" }
maps.n[">b"] =
{ function() astronvim.move_buf(vim.v.count > 0 and vim.v.count or 1) end, desc = "Move buffer tab right" }
maps.n["<b"] =
{ function() astronvim.move_buf(-(vim.v.count > 0 and vim.v.count or 1)) end, desc = "Move buffer tab left" }
maps.n["<leader>bb"] = {
function()
astronvim.status.heirline.buffer_picker(function(bufnr) vim.api.nvim_win_set_buf(0, bufnr) end)
end,
desc = "Select buffer from tabline",
}
maps.n["<leader>bd"] = {
function()
astronvim.status.heirline.buffer_picker(function(bufnr) astronvim.close_buf(bufnr) end)
end,
desc = "Delete buffer from tabline",
}
maps.n["<leader>b\\"] = {
function()
astronvim.status.heirline.buffer_picker(function(bufnr)
vim.cmd.split()
vim.api.nvim_win_set_buf(0, bufnr)
end)
end,
desc = "Horizontal split buffer from tabline",
}
maps.n["<leader>b|"] = {
function()
astronvim.status.heirline.buffer_picker(function(bufnr)
vim.cmd.vsplit()
vim.api.nvim_win_set_buf(0, bufnr)
end)
end,
desc = "Vertical split buffer from tabline",
}
else -- TODO v3: remove this else block
-- Bufdelete
if is_available "bufdelete.nvim" then
maps.n["<leader>c"] = { function() require("bufdelete").bufdelete(0, false) end, desc = "Close buffer" }
maps.n["<leader>C"] = { function() require("bufdelete").bufdelete(0, true) end, desc = "Force close buffer" }
else
maps.n["<leader>c"] = { "<cmd>bdelete<cr>", desc = "Close buffer" }
maps.n["<leader>C"] = { "<cmd>bdelete!<cr>", desc = "Force close buffer" }
end
-- Navigate buffers
if is_available "bufferline.nvim" then
maps.n["<S-l>"] = { "<cmd>BufferLineCycleNext<cr>", desc = "Next buffer tab" }
maps.n["<S-h>"] = { "<cmd>BufferLineCyclePrev<cr>", desc = "Previous buffer tab" }
maps.n[">b"] = { "<cmd>BufferLineMoveNext<cr>", desc = "Move buffer tab right" }
maps.n["<b"] = { "<cmd>BufferLineMovePrev<cr>", desc = "Move buffer tab left" }
else
maps.n["<S-l>"] = { "<cmd>bnext<cr>", desc = "Next buffer" }
maps.n["<S-h>"] = { "<cmd>bprevious<cr>", desc = "Previous buffer" }
end
end
-- Navigate tabs
maps.n["]t"] = { function() vim.cmd.tabnext() end, desc = "Next tab" }
maps.n["[t"] = { function() vim.cmd.tabprevious() end, desc = "Previous tab" }
-- Comment
if is_available "Comment.nvim" then
maps.n["<leader>/"] = { function() require("Comment.api").toggle.linewise.current() end, desc = "Comment line" }
maps.v["<leader>/"] = {
"<esc><cmd>lua require('Comment.api').toggle.linewise(vim.fn.visualmode())<cr>",
desc = "Toggle comment line",
}
end
-- GitSigns
if is_available "gitsigns.nvim" then
maps.n["<leader>gj"] = { function() require("gitsigns").next_hunk() end, desc = "Next Git hunk" }
maps.n["<leader>gk"] = { function() require("gitsigns").prev_hunk() end, desc = "Previous Git hunk" }
maps.n["<leader>gl"] = { function() require("gitsigns").blame_line() end, desc = "View Git blame" }
maps.n["<leader>gp"] = { function() require("gitsigns").preview_hunk() end, desc = "Preview Git hunk" }
maps.n["<leader>gh"] = { function() require("gitsigns").reset_hunk() end, desc = "Reset Git hunk" }
maps.n["<leader>gr"] = { function() require("gitsigns").reset_buffer() end, desc = "Reset Git buffer" }
maps.n["<leader>gs"] = { function() require("gitsigns").stage_hunk() end, desc = "Stage Git hunk" }
maps.n["<leader>gu"] = { function() require("gitsigns").undo_stage_hunk() end, desc = "Unstage Git hunk" }
maps.n["<leader>gd"] = { function() require("gitsigns").diffthis() end, desc = "View Git diff" }
end
-- NeoTree
if is_available "neo-tree.nvim" then
maps.n["<leader>e"] = { "<cmd>Neotree toggle<cr>", desc = "Toggle Explorer" }
maps.n["<leader>o"] = { "<cmd>Neotree focus<cr>", desc = "Focus Explorer" }
end
-- Session Manager
if is_available "neovim-session-manager" then
maps.n["<leader>Sl"] = { "<cmd>SessionManager! load_last_session<cr>", desc = "Load last session" }
maps.n["<leader>Ss"] = { "<cmd>SessionManager! save_current_session<cr>", desc = "Save this session" }
maps.n["<leader>Sd"] = { "<cmd>SessionManager! delete_session<cr>", desc = "Delete session" }
maps.n["<leader>Sf"] = { "<cmd>SessionManager! load_session<cr>", desc = "Search sessions" }
maps.n["<leader>S."] =
{ "<cmd>SessionManager! load_current_dir_session<cr>", desc = "Load current directory session" }
end
-- Package Manager
if is_available "mason.nvim" then
maps.n["<leader>pI"] = { "<cmd>Mason<cr>", desc = "Mason Installer" }
maps.n["<leader>pU"] = { "<cmd>MasonUpdateAll<cr>", desc = "Mason Update" }
end
-- Smart Splits
if is_available "smart-splits.nvim" then
-- Better window navigation
maps.n["<C-h>"] = { function() require("smart-splits").move_cursor_left() end, desc = "Move to left split" }
maps.n["<C-j>"] = { function() require("smart-splits").move_cursor_down() end, desc = "Move to below split" }
maps.n["<C-k>"] = { function() require("smart-splits").move_cursor_up() end, desc = "Move to above split" }
maps.n["<C-l>"] = { function() require("smart-splits").move_cursor_right() end, desc = "Move to right split" }
-- Resize with arrows
maps.n["<C-Up>"] = { function() require("smart-splits").resize_up() end, desc = "Resize split up" }
maps.n["<C-Down>"] = { function() require("smart-splits").resize_down() end, desc = "Resize split down" }
maps.n["<C-Left>"] = { function() require("smart-splits").resize_left() end, desc = "Resize split left" }
maps.n["<C-Right>"] = { function() require("smart-splits").resize_right() end, desc = "Resize split right" }
else
maps.n["<C-h>"] = { "<C-w>h", desc = "Move to left split" }
maps.n["<C-j>"] = { "<C-w>j", desc = "Move to below split" }
maps.n["<C-k>"] = { "<C-w>k", desc = "Move to above split" }
maps.n["<C-l>"] = { "<C-w>l", desc = "Move to right split" }
maps.n["<C-Up>"] = { "<cmd>resize -2<CR>", desc = "Resize split up" }
maps.n["<C-Down>"] = { "<cmd>resize +2<CR>", desc = "Resize split down" }
maps.n["<C-Left>"] = { "<cmd>vertical resize -2<CR>", desc = "Resize split left" }
maps.n["<C-Right>"] = { "<cmd>vertical resize +2<CR>", desc = "Resize split right" }
end
-- SymbolsOutline
if is_available "aerial.nvim" then
maps.n["<leader>lS"] = { function() require("aerial").toggle() end, desc = "Symbols outline" }
end
-- Telescope
if is_available "telescope.nvim" then
maps.n["<leader>fw"] = { function() require("telescope.builtin").live_grep() end, desc = "Search words" }
maps.n["<leader>fW"] = {
function()
require("telescope.builtin").live_grep {
additional_args = function(args) return vim.list_extend(args, { "--hidden", "--no-ignore" }) end,
}
end,
desc = "Search words in all files",
}
maps.n["<leader>gt"] = { function() require("telescope.builtin").git_status() end, desc = "Git status" }
maps.n["<leader>gb"] = { function() require("telescope.builtin").git_branches() end, desc = "Git branches" }
maps.n["<leader>gc"] = { function() require("telescope.builtin").git_commits() end, desc = "Git commits" }
maps.n["<leader>ff"] = { function() require("telescope.builtin").find_files( { hidden = true } ) end, desc = "Search files" }
maps.n["<leader>fF"] = {
function() require("telescope.builtin").find_files { hidden = true, no_ignore = true } end,
desc = "Search all files",
}
maps.n["<leader>fb"] = { function() require("telescope.builtin").buffers() end, desc = "Search buffers" }
maps.n["<leader>fh"] = { function() require("telescope.builtin").help_tags() end, desc = "Search help" }
maps.n["<leader>fm"] = { function() require("telescope.builtin").marks() end, desc = "Search marks" }
maps.n["<leader>fo"] = { function() require("telescope.builtin").oldfiles() end, desc = "Search history" }
maps.n["<leader>fc"] =
{ function() require("telescope.builtin").grep_string() end, desc = "Search for word under cursor" }
maps.n["<leader>sb"] = { function() require("telescope.builtin").git_branches() end, desc = "Git branches" }
maps.n["<leader>sh"] = { function() require("telescope.builtin").help_tags() end, desc = "Search help" }
maps.n["<leader>sm"] = { function() require("telescope.builtin").man_pages() end, desc = "Search man" }
maps.n["<leader>sr"] = { function() require("telescope.builtin").registers() end, desc = "Search registers" }
maps.n["<leader>sk"] = { function() require("telescope.builtin").keymaps() end, desc = "Search keymaps" }
maps.n["<leader>sc"] = { function() require("telescope.builtin").commands() end, desc = "Search commands" }
if astronvim.is_available "nvim-notify" then
maps.n["<leader>sn"] =
{ function() require("telescope").extensions.notify.notify() end, desc = "Search notifications" }
end
maps.n["<leader>ls"] = {
function()
local aerial_avail, _ = pcall(require, "aerial")
if aerial_avail then
require("telescope").extensions.aerial.aerial()
else
require("telescope.builtin").lsp_document_symbols()
end
end,
desc = "Search symbols",
}
maps.n["<leader>lD"] = { function() require("telescope.builtin").diagnostics() end, desc = "Search diagnostics" }
end
-- Terminal
if is_available "toggleterm.nvim" then
local toggle_term_cmd = astronvim.toggle_term_cmd
if vim.fn.executable "lazygit" == 1 then
maps.n["<leader>gg"] = { function() toggle_term_cmd "lazygit" end, desc = "ToggleTerm lazygit" }
maps.n["<leader>tl"] = { function() toggle_term_cmd "lazygit" end, desc = "ToggleTerm lazygit" }
end
if vim.fn.executable "node" == 1 then
maps.n["<leader>tn"] = { function() toggle_term_cmd "node" end, desc = "ToggleTerm node" }
end
if vim.fn.executable "gdu" == 1 then
maps.n["<leader>tu"] = { function() toggle_term_cmd "gdu" end, desc = "ToggleTerm gdu" }
end
if vim.fn.executable "btm" == 1 then
maps.n["<leader>tt"] = { function() toggle_term_cmd "btm" end, desc = "ToggleTerm btm" }
end
if vim.fn.executable "python" == 1 then
maps.n["<leader>tp"] = { function() toggle_term_cmd "python" end, desc = "ToggleTerm python" }
end
maps.n["<leader>tf"] = { "<cmd>ToggleTerm direction=float<cr>", desc = "ToggleTerm float" }
maps.n["<leader>th"] = { "<cmd>ToggleTerm size=10 direction=horizontal<cr>", desc = "ToggleTerm horizontal split" }
maps.n["<leader>tv"] = { "<cmd>ToggleTerm size=80 direction=vertical<cr>", desc = "ToggleTerm vertical split" }
maps.n["<F7>"] = { "<cmd>ToggleTerm<cr>", desc = "Toggle terminal" }
maps.t["<F7>"] = maps.n["<F7>"]
maps.n["<C-'>"] = maps.n["<F7>"]
maps.t["<C-'>"] = maps.n["<F7>"]
end
if is_available "nvim-dap" then
-- modified function keys found with `showkey -a` in the terminal to get key code
-- run `nvim -V3log +quit` and search through the "Terminal info" in the `log` file for the correct keyname
maps.n["<F5>"] = { function() require("dap").continue() end, desc = "Debugger: Start" }
maps.n["<F17>"] = { function() require("dap").terminate() end, desc = "Debugger: Stop" } -- Shift+F5
maps.n["<F29>"] = { function() require("dap").restart_frame() end, desc = "Debugger: Restart" } -- Control+F5
maps.n["<F6>"] = { function() require("dap").pause() end, desc = "Debugger: Pause" }
maps.n["<F9>"] = { function() require("dap").toggle_breakpoint() end, desc = "Debugger: Toggle Breakpoint" }
maps.n["<F10>"] = { function() require("dap").step_over() end, desc = "Debugger: Step Over" }
maps.n["<F11>"] = { function() require("dap").step_into() end, desc = "Debugger: Step Into" }
maps.n["<F23>"] = { function() require("dap").step_out() end, desc = "Debugger: Step Out" } -- Shift+F11
maps.n["<leader>Db"] = { function() require("dap").toggle_breakpoint() end, desc = "Toggle Breakpoint (F9)" }
maps.n["<leader>DB"] = { function() require("dap").clear_breakpoints() end, desc = "Clear Breakpoints" }
maps.n["<leader>Dc"] = { function() require("dap").continue() end, desc = "Start/Continue (F5)" }
maps.n["<leader>Di"] = { function() require("dap").step_into() end, desc = "Step Into (F11)" }
maps.n["<leader>Do"] = { function() require("dap").step_over() end, desc = "Step Over (F10)" }
maps.n["<leader>DO"] = { function() require("dap").step_out() end, desc = "Step Out (S-F11)" }
maps.n["<leader>Dq"] = { function() require("dap").close() end, desc = "Close Session" }
maps.n["<leader>DQ"] = { function() require("dap").terminate() end, desc = "Terminate Session (S-F5)" }
maps.n["<leader>Dp"] = { function() require("dap").pause() end, desc = "Pause (F6)" }
maps.n["<leader>Dr"] = { function() require("dap").restart_frame() end, desc = "Restart (C-F5)" }
maps.n["<leader>DR"] = { function() require("dap").repl.toggle() end, desc = "Toggle REPL" }
if is_available "nvim-dap-ui" then
maps.n["<leader>Du"] = { function() require("dapui").toggle() end, desc = "Toggle Debugger UI" }
maps.n["<leader>Dh"] = { function() require("dap.ui.widgets").hover() end, desc = "Debugger Hover" }
end
end
-- Stay in indent mode
maps.v["<"] = { "<gv", desc = "unindent line" }
maps.v[">"] = { ">gv", desc = "indent line" }
-- Improved Terminal Navigation
maps.t["<C-h>"] = { "<c-\\><c-n><c-w>h", desc = "Terminal left window navigation" }
maps.t["<C-j>"] = { "<c-\\><c-n><c-w>j", desc = "Terminal down window navigation" }
maps.t["<C-k>"] = { "<c-\\><c-n><c-w>k", desc = "Terminal up window navigation" }
maps.t["<C-l>"] = { "<c-\\><c-n><c-w>l", desc = "Terminal right window navigation" }
-- Custom menu for modification of the user experience
if is_available "nvim-autopairs" then
maps.n["<leader>ua"] = { function() astronvim.ui.toggle_autopairs() end, desc = "Toggle autopairs" }
end
maps.n["<leader>ub"] = { function() astronvim.ui.toggle_background() end, desc = "Toggle background" }
if is_available "nvim-cmp" then
maps.n["<leader>uc"] = { function() astronvim.ui.toggle_cmp() end, desc = "Toggle autocompletion" }
end
if is_available "nvim-colorizer.lua" then
maps.n["<leader>uC"] = { "<cmd>ColorizerToggle<cr>", desc = "Toggle color highlight" }
end
maps.n["<leader>uS"] = { function() astronvim.ui.toggle_conceal() end, desc = "Toggle conceal" }
maps.n["<leader>ud"] = { function() astronvim.ui.toggle_diagnostics() end, desc = "Toggle diagnostics" }
maps.n["<leader>ug"] = { function() astronvim.ui.toggle_signcolumn() end, desc = "Toggle signcolumn" }
maps.n["<leader>ui"] = { function() astronvim.ui.set_indent() end, desc = "Change indent setting" }
maps.n["<leader>ul"] = { function() astronvim.ui.toggle_statusline() end, desc = "Toggle statusline" }
maps.n["<leader>un"] = { function() astronvim.ui.change_number() end, desc = "Change line numbering" }
maps.n["<leader>us"] = { function() astronvim.ui.toggle_spell() end, desc = "Toggle spellcheck" }
maps.n["<leader>up"] = { function() astronvim.ui.toggle_paste() end, desc = "Toggle paste mode" }
maps.n["<leader>ut"] = { function() astronvim.ui.toggle_tabline() end, desc = "Toggle tabline" }
maps.n["<leader>uu"] = { function() astronvim.ui.toggle_url_match() end, desc = "Toggle URL highlight" }
maps.n["<leader>uw"] = { function() astronvim.ui.toggle_wrap() end, desc = "Toggle wrap" }
maps.n["<leader>uy"] = { function() astronvim.ui.toggle_syntax() end, desc = "Toggle syntax highlight" }
maps.n["<leader>uN"] = { function() astronvim.ui.toggle_ui_notifications() end, desc = "Toggle UI notifications" }
astronvim.set_mappings(astronvim.user_plugin_opts("mappings", maps))

View File

@ -0,0 +1,72 @@
vim.opt.shortmess:append { s = true, I = true } -- disable startup message
astronvim.vim_opts(astronvim.user_plugin_opts("options", {
opt = {
backspace = vim.opt.backspace + { "nostop" }, -- Don't stop backspace at insert
clipboard = "unnamedplus", -- Connection to the system clipboard
cmdheight = 0, -- hide command line unless needed
completeopt = { "menuone", "noselect" }, -- Options for insert mode completion
copyindent = true, -- Copy the previous indentation on autoindenting
cursorline = true, -- Highlight the text line of the cursor
expandtab = true, -- Enable the use of space in tab
fileencoding = "utf-8", -- File content encoding for the buffer
fillchars = { eob = " " }, -- Disable `~` on nonexistent lines
history = 100, -- Number of commands to remember in a history table
ignorecase = true, -- Case insensitive searching
laststatus = 3, -- globalstatus
mouse = "a", -- Enable mouse support
number = true, -- Show numberline
preserveindent = true, -- Preserve indent structure as much as possible
pumheight = 10, -- Height of the pop up menu
relativenumber = true, -- Show relative numberline
scrolloff = 8, -- Number of lines to keep above and below the cursor
shiftwidth = 2, -- Number of space inserted for indentation
showmode = false, -- Disable showing modes in command line
showtabline = 2, -- always display tabline
sidescrolloff = 8, -- Number of columns to keep at the sides of the cursor
signcolumn = "yes", -- Always show the sign column
smartcase = true, -- Case sensitivie searching
splitbelow = true, -- Splitting a new window below the current one
splitright = true, -- Splitting a new window at the right of the current one
tabstop = 2, -- Number of space in a tab
termguicolors = true, -- Enable 24-bit RGB color in the TUI
timeoutlen = 300, -- Length of time to wait for a mapped sequence
undofile = true, -- Enable persistent undo
updatetime = 300, -- Length of time to wait before triggering the plugin
wrap = false, -- Disable wrapping of lines longer than the width of window
writebackup = false, -- Disable making a backup before overwriting a file
},
g = {
highlighturl_enabled = true, -- highlight URLs by default
mapleader = " ", -- set leader key
zipPlugin = false, -- disable zip
load_black = false, -- disable black
loaded_2html_plugin = true, -- disable 2html
loaded_getscript = true, -- disable getscript
loaded_getscriptPlugin = true, -- disable getscript
loaded_gzip = true, -- disable gzip
loaded_logipat = true, -- disable logipat
loaded_matchit = true, -- disable matchit
loaded_netrwFileHandlers = true, -- disable netrw
loaded_netrwPlugin = true, -- disable netrw
loaded_netrwSettngs = true, -- disable netrw
loaded_remote_plugins = true, -- disable remote plugins
loaded_tar = true, -- disable tar
loaded_tarPlugin = true, -- disable tar
loaded_zip = true, -- disable zip
loaded_zipPlugin = true, -- disable zip
loaded_vimball = true, -- disable vimball
loaded_vimballPlugin = true, -- disable vimball
autoformat_enabled = true, -- enable or disable auto formatting at start (lsp.formatting.format_on_save must be enabled)
lsp_handlers_enabled = true, -- enable or disable default vim.lsp.handlers (hover and signatureHelp)
cmp_enabled = true, -- enable completion at start
autopairs_enabled = true, -- enable autopairs at start
diagnostics_enabled = true, -- enable diagnostics at start
status_diagnostics_enabled = true, -- enable diagnostics in statusline
icons_enabled = true, -- disable icons in the UI (disable if no nerd font is available)
ui_notifications_enabled = true, -- disable notifications when toggling UI elements
heirline_bufferline = false, -- enable heirline bufferline (TODO v3: remove this option and make it default)
},
t = {
bufs = vim.tbl_filter(astronvim.is_valid_buffer, vim.api.nvim_list_bufs()), -- buffers in tab
},
}))

View File

@ -0,0 +1,375 @@
local astro_plugins = {
-- Plugin manager
["wbthomason/packer.nvim"] = {
setup = function()
astronvim.lazy_load_commands("packer.nvim", {
"PackerSnapshot",
"PackerSnapshotRollback",
"PackerSnapshotDelete",
"PackerInstall",
"PackerUpdate",
"PackerSync",
"PackerClean",
"PackerCompile",
"PackerStatus",
"PackerProfile",
"PackerLoad",
})
end,
config = function() require "core.plugins" end,
},
-- Optimiser
["lewis6991/impatient.nvim"] = {},
-- Lua functions
["nvim-lua/plenary.nvim"] = { module = "plenary" },
-- Indent detection
["Darazaki/indent-o-matic"] = {
opt = true,
setup = function() table.insert(astronvim.file_plugins, "indent-o-matic") end,
config = function() require "configs.indent-o-matic" end,
},
-- Notification Enhancer
["rcarriga/nvim-notify"] = {
module = "notify",
setup = function() astronvim.load_plugin_with_func("nvim-notify", vim, "notify") end,
config = function() require "configs.notify" end,
},
-- Neovim UI Enhancer
["stevearc/dressing.nvim"] = {
opt = true,
setup = function() astronvim.load_plugin_with_func("dressing.nvim", vim.ui, { "input", "select" }) end,
config = function() require "configs.dressing" end,
},
-- Smarter Splits
["mrjones2014/smart-splits.nvim"] = {
module = "smart-splits",
config = function() require "configs.smart-splits" end,
},
-- Icons
["nvim-tree/nvim-web-devicons"] = {
disable = not vim.g.icons_enabled,
module = "nvim-web-devicons",
config = function() require "configs.nvim-web-devicons" end,
},
-- LSP Icons
["onsails/lspkind.nvim"] = {
disable = not vim.g.icons_enabled,
module = "lspkind",
config = function() require "configs.lspkind" end,
},
-- Bufferline
["akinsho/bufferline.nvim"] = { -- TODO v3: remove this plugin
disable = vim.g.heirline_bufferline,
module = "bufferline",
event = "UIEnter",
config = function() require "configs.bufferline" end,
},
-- Better buffer closing
["famiu/bufdelete.nvim"] = {
module = "bufdelete",
setup = function() astronvim.lazy_load_commands("bufdelete.nvim", { "Bdelete", "Bwipeout" }) end,
},
["s1n7ax/nvim-window-picker"] = {
tag = "v1.*",
module = "window-picker",
config = function() require "configs.window-picker" end,
},
-- File explorer
["nvim-neo-tree/neo-tree.nvim"] = {
branch = "v2.x",
module = "neo-tree",
requires = { { "MunifTanjim/nui.nvim", module = "nui" } },
setup = function()
astronvim.lazy_load_commands("neo-tree.nvim", "Neotree")
vim.g.neo_tree_remove_legacy_commands = true
end,
config = function() require "configs.neo-tree" end,
},
-- Statusline
["rebelot/heirline.nvim"] = { event = "VimEnter", config = function() require "configs.heirline" end },
-- Syntax highlighting
["nvim-treesitter/nvim-treesitter"] = {
module = "nvim-treesitter",
setup = function()
table.insert(astronvim.file_plugins, "nvim-treesitter")
astronvim.lazy_load_commands("nvim-treesitter", {
"TSBufDisable",
"TSBufEnable",
"TSBufToggle",
"TSDisable",
"TSEnable",
"TSToggle",
"TSInstall",
"TSInstallInfo",
"TSInstallSync",
"TSModuleInfo",
"TSUninstall",
"TSUpdate",
"TSUpdateSync",
})
end,
run = function() require("nvim-treesitter.install").update { with_sync = true }() end,
config = function() require "configs.treesitter" end,
},
-- Parenthesis highlighting
["p00f/nvim-ts-rainbow"] = { after = "nvim-treesitter" },
-- Autoclose tags
["windwp/nvim-ts-autotag"] = { after = "nvim-treesitter" },
-- Context based commenting
["JoosepAlviste/nvim-ts-context-commentstring"] = { after = "nvim-treesitter" },
-- Snippet collection
["rafamadriz/friendly-snippets"] = { opt = true },
-- Snippet engine
["L3MON4D3/LuaSnip"] = {
module = "luasnip",
wants = "friendly-snippets",
config = function() require "configs.luasnip" end,
},
-- Completion engine
["hrsh7th/nvim-cmp"] = { event = "InsertEnter", config = function() require "configs.cmp" end },
-- Snippet completion source
["saadparwaiz1/cmp_luasnip"] = {
after = "nvim-cmp",
config = function() astronvim.add_user_cmp_source "luasnip" end,
},
-- Buffer completion source
["hrsh7th/cmp-buffer"] = { after = "nvim-cmp", config = function() astronvim.add_user_cmp_source "buffer" end },
-- Path completion source
["hrsh7th/cmp-path"] = { after = "nvim-cmp", config = function() astronvim.add_user_cmp_source "path" end },
-- LSP completion source
["hrsh7th/cmp-nvim-lsp"] = { after = "nvim-cmp", config = function() astronvim.add_user_cmp_source "nvim_lsp" end },
-- Built-in LSP
["neovim/nvim-lspconfig"] = {
module = "lspconfig",
setup = function() table.insert(astronvim.file_plugins, "nvim-lspconfig") end,
config = function() require "configs.lspconfig" end,
},
-- Formatting and linting
["jose-elias-alvarez/null-ls.nvim"] = {
module = "null-ls",
setup = function() table.insert(astronvim.file_plugins, "null-ls.nvim") end,
config = function() require "configs.null-ls" end,
},
-- Debugger
["mfussenegger/nvim-dap"] = {
disable = vim.fn.has "win32" == 1,
module = "dap",
config = function() require "configs.dap" end,
},
-- Debugger UI
["rcarriga/nvim-dap-ui"] = {
disable = vim.fn.has "win32" == 1,
after = "nvim-dap",
config = function() require "configs.dapui" end,
},
-- Package Manager
["williamboman/mason.nvim"] = {
module = "mason",
cmd = {
"Mason",
"MasonInstall",
"MasonUninstall",
"MasonUninstallAll",
"MasonLog",
"MasonUpdate", -- astronvim command
"MasonUpdateAll", -- astronvim command
},
config = function()
require "configs.mason"
vim.tbl_map(function(plugin) pcall(require, plugin) end, { "lspconfig", "null-ls", "dap" })
end,
},
-- LSP manager
["williamboman/mason-lspconfig.nvim"] = {
after = "nvim-lspconfig",
config = function() require "configs.mason-lspconfig" end,
},
-- null-ls manager
["jayp0521/mason-null-ls.nvim"] = { after = "null-ls.nvim", config = function() require "configs.mason-null-ls" end },
-- dap manager
["jayp0521/mason-nvim-dap.nvim"] = {
disable = vim.fn.has "win32" == 1,
after = "nvim-dap",
config = function() require "configs.mason-nvim-dap" end,
},
-- LSP symbols
["stevearc/aerial.nvim"] = {
module = "aerial",
after = { "nvim-treesitter", "nvim-lspconfig" },
ft = { "man", "markdown" },
config = function() require "configs.aerial" end,
},
-- Fuzzy finder
["nvim-telescope/telescope.nvim"] = {
module = "telescope",
setup = function() astronvim.lazy_load_commands("telescope.nvim", "Telescope") end,
config = function() require "configs.telescope" end,
},
-- Fuzzy finder syntax support
["nvim-telescope/telescope-fzf-native.nvim"] = {
after = "telescope.nvim",
disable = vim.fn.executable "make" == 0,
run = "make",
config = function() require("telescope").load_extension "fzf" end,
},
-- Git integration
["lewis6991/gitsigns.nvim"] = {
disable = vim.fn.executable "git" == 0,
ft = "gitcommit",
setup = function() table.insert(astronvim.git_plugins, "gitsigns.nvim") end,
config = function() require "configs.gitsigns" end,
},
-- Start screen
["goolord/alpha-nvim"] = {
module = "alpha",
setup = function() astronvim.lazy_load_commands("alpha-nvim", "Alpha") end,
config = function() require "configs.alpha" end,
},
-- Color highlighting
["NvChad/nvim-colorizer.lua"] = {
opt = true,
setup = function()
astronvim.lazy_load_commands(
"nvim-colorizer.lua",
{ "ColorizerToggle", "ColorizerAttachToBuffer", "ColorizerDetachFromBuffer", "ColorizerReloadAllBuffers" }
)
table.insert(astronvim.file_plugins, "nvim-colorizer.lua")
end,
config = function() require "configs.colorizer" end,
},
-- Autopairs
["windwp/nvim-autopairs"] = { event = "InsertEnter", config = function() require "configs.autopairs" end },
-- Terminal
["akinsho/toggleterm.nvim"] = {
module = "toggleterm",
setup = function() astronvim.lazy_load_commands("toggleterm.nvim", { "ToggleTerm", "TermExec" }) end,
config = function() require "configs.toggleterm" end,
},
-- Commenting
["numToStr/Comment.nvim"] = {
module = "Comment",
keys = { "gc", "gb" },
config = function() require "configs.Comment" end,
},
-- Indentation
["lukas-reineke/indent-blankline.nvim"] = {
opt = true,
setup = function() table.insert(astronvim.file_plugins, "indent-blankline.nvim") end,
config = function() require "configs.indent-line" end,
},
-- Keymaps popup
["folke/which-key.nvim"] = { module = "which-key", config = function() require "configs.which-key" end },
-- Smooth escaping
["max397574/better-escape.nvim"] = {
event = "InsertCharPre",
config = function() require "configs.better_escape" end,
},
-- Get extra JSON schemas
["b0o/SchemaStore.nvim"] = { module = "schemastore" },
-- Session manager
["Shatur/neovim-session-manager"] = {
module = "session_manager",
event = "BufWritePost",
setup = function() astronvim.lazy_load_commands("neovim-session-manager", "SessionManager") end,
config = function() require "configs.session_manager" end,
},
}
if astronvim.updater.snapshot then
for plugin, options in pairs(astro_plugins) do
local pin = astronvim.updater.snapshot[plugin:match "/([^/]*)$"]
if pin and pin.commit then
options.commit = pin.commit
options.tag = nil
end
end
end
local user_plugin_opts = astronvim.user_plugin_opts
local status_ok, packer = pcall(require, "packer")
if status_ok then
packer.startup {
function(use)
local plugins = user_plugin_opts("plugins.init", astro_plugins)
for key, plugin in pairs(plugins) do
if type(key) == "string" and not plugin[1] then plugin[1] = key end
if key == "williamboman/mason.nvim" and plugin.cmd then
for mason_plugin, commands in pairs { -- lazy load mason plugin commands with Mason
["jayp0521/mason-null-ls.nvim"] = { "NullLsInstall", "NullLsUninstall" },
["williamboman/mason-lspconfig.nvim"] = { "LspInstall", "LspUninstall" },
["jayp0521/mason-nvim-dap.nvim"] = { "DapInstall", "DapUninstall" },
} do
if plugins[mason_plugin] and not plugins[mason_plugin].disable then
vim.list_extend(plugin.cmd, commands)
end
end
end
use(plugin)
end
end,
config = user_plugin_opts("plugins.packer", {
compile_path = astronvim.default_compile_path,
display = {
open_fn = function() return require("packer.util").float { border = "rounded" } end,
},
profile = {
enable = true,
threshold = 0.0001,
},
git = {
clone_timeout = 300,
subcommands = {
update = "pull --rebase",
},
},
auto_clean = true,
compile_on_sync = true,
}),
}
end

View File

@ -0,0 +1,157 @@
--- ### Git LUA API
--
-- This module can be loaded with `local git = require "core.utils.git"`
--
-- @module core.utils.git
-- @copyright 2022
-- @license GNU General Public License v3.0
local git = { url = "https://github.com/" }
--- Run a git command from the AstroNvim installation directory
-- @param args the git arguments
-- @return the result of the command or nil if unsuccessful
function git.cmd(args, ...) return astronvim.cmd("git -C " .. astronvim.install.home .. " " .. args, ...) end
--- Check if the AstroNvim is able to reach the `git` command
-- @return the result of running `git --help`
function git.available() return vim.fn.executable "git" == 1 end
--- Check if the AstroNvim home is a git repo
-- @return the result of the command
function git.is_repo() return git.cmd("rev-parse --is-inside-work-tree", false) end
--- Fetch git remote
-- @param remote the remote to fetch
-- @return the result of the command
function git.fetch(remote, ...) return git.cmd("fetch " .. remote, ...) end
--- Pull the git repo
-- @return the result of the command
function git.pull(...) return git.cmd("pull --rebase", ...) end
--- Checkout git target
-- @param dest the target to checkout
-- @return the result of the command
function git.checkout(dest, ...) return git.cmd("checkout " .. dest, ...) end
--- Hard reset to a git target
-- @param dest the target to hard reset to
-- @return the result of the command
function git.hard_reset(dest, ...) return git.cmd("reset --hard " .. dest, ...) end
--- Check if a branch contains a commit
-- @param remote the git remote to check
-- @param branch the git branch to check
-- @param commit the git commit to check for
-- @return the result of the command
function git.branch_contains(remote, branch, commit, ...)
return git.cmd("merge-base --is-ancestor " .. commit .. " " .. remote .. "/" .. branch, ...) ~= nil
end
--- Add a git remote
-- @param remote the remote to add
-- @param url the url of the remote
-- @return the result of the command
function git.remote_add(remote, url, ...) return git.cmd("remote add " .. remote .. " " .. url, ...) end
--- Update a git remote URL
-- @param remote the remote to update
-- @param url the new URL of the remote
-- @return the result of the command
function git.remote_update(remote, url, ...) return git.cmd("remote set-url " .. remote .. " " .. url, ...) end
--- Get the URL of a given git remote
-- @param remote the remote to get the URL of
-- @return the url of the remote
function git.remote_url(remote, ...) return astronvim.trim_or_nil(git.cmd("remote get-url " .. remote, ...)) end
--- Get the current version with git describe including tags
-- @return the current git describe string
function git.current_version(...) return astronvim.trim_or_nil(git.cmd("describe --tags", ...)) end
--- Get the current branch
-- @return the branch of the AstroNvim installation
function git.current_branch(...) return astronvim.trim_or_nil(git.cmd("rev-parse --abbrev-ref HEAD", ...)) end
--- Get the current head of the git repo
-- @return the head string
function git.local_head(...) return astronvim.trim_or_nil(git.cmd("rev-parse HEAD", ...)) end
--- Get the current head of a git remote
-- @param remote the remote to check
-- @param branch the branch to check
-- @return the head string of the remote branch
function git.remote_head(remote, branch, ...)
return astronvim.trim_or_nil(git.cmd("rev-list -n 1 " .. remote .. "/" .. branch, ...))
end
--- Get the commit hash of a given tag
-- @param tag the tag to resolve
-- @return the commit hash of a git tag
function git.tag_commit(tag, ...) return astronvim.trim_or_nil(git.cmd("rev-list -n 1 " .. tag, ...)) end
--- Get the commit log between two commit hashes
-- @param start_hash the start commit hash
-- @param end_hash the end commit hash
-- @return an array like table of commit messages
function git.get_commit_range(start_hash, end_hash, ...)
local range = ""
if start_hash and end_hash then range = start_hash .. ".." .. end_hash end
local log = git.cmd('log --no-merges --pretty="format:[%h] %s" ' .. range, ...)
return log and vim.fn.split(log, "\n") or {}
end
--- Get a list of all tags with a regex filter
-- @param search a regex to search the tags with (defaults to "v*" for version tags)
-- @return an array like table of tags that match the search
function git.get_versions(search, ...)
local tags = git.cmd('tag -l --sort=version:refname "' .. (search == "latest" and "v*" or search) .. '"', ...)
return tags and vim.fn.split(tags, "\n") or {}
end
--- Get the latest version of a list of versions
-- @param versions a list of versions to search (defaults to all versions available)
-- @return the latest version from the array
function git.latest_version(versions, ...)
if not versions then versions = git.get_versions(...) end
return versions[#versions]
end
--- Parse a remote url
-- @param str the remote to parse to a full git url
-- @return the full git url for the given remote string
function git.parse_remote_url(str)
return vim.fn.match(str, astronvim.url_matcher) == -1
and git.url .. str .. (vim.fn.match(str, "/") == -1 and "/AstroNvim.git" or ".git")
or str
end
--- Check if a Conventional Commit commit message is breaking or not
-- @param commit a commit message
-- @return boolean true if the message is breaking, false if the commit message is not breaking
function git.is_breaking(commit) return vim.fn.match(commit, "\\[.*\\]\\s\\+\\w\\+\\((\\w\\+)\\)\\?!:") ~= -1 end
--- Get a list of breaking commits from commit messages using Conventional Commit standard
-- @param commits an array like table of commit messages
-- @return an array like table of commits that are breaking
function git.breaking_changes(commits) return vim.tbl_filter(git.is_breaking, commits) end
--- Generate a table of commit messages for neovim's echo API with highlighting
-- @param commits an array like table of commit messages
-- @return an array like table of echo messages to provide to nvim_echo or astronvim.echo
function git.pretty_changelog(commits)
local changelog = {}
for _, commit in ipairs(commits) do
local hash, type, msg = commit:match "(%[.*%])(.*:)(.*)"
if hash and type and msg then
vim.list_extend(
changelog,
{ { hash, "DiffText" }, { type, git.is_breaking(commit) and "DiffDelete" or "DiffChange" }, { msg }, { "\n" } }
)
end
end
return changelog
end
return git

View File

@ -0,0 +1,606 @@
--- ### AstroNvim Utilities
--
-- This module is automatically loaded by AstroNvim on during it's initialization into global variable `astronvim`
--
-- This module can also be manually loaded with `local astronvim = require "core.utils"`
--
-- @module core.utils
-- @copyright 2022
-- @license GNU General Public License v3.0
_G.astronvim = {}
local stdpath = vim.fn.stdpath
local tbl_insert = table.insert
local map = vim.keymap.set
--- installation details from external installers
astronvim.install = astronvim_installation or { home = stdpath "config" }
--- external astronvim configuration folder
astronvim.install.config = stdpath("config"):gsub("nvim$", "astronvim")
vim.opt.rtp:append(astronvim.install.config)
local supported_configs = { astronvim.install.home, astronvim.install.config }
--- Looks to see if a module path references a lua file in a configuration folder and tries to load it. If there is an error loading the file, write an error and continue
-- @param module the module path to try and load
-- @return the loaded module if successful or nil
local function load_module_file(module)
-- placeholder for final return value
local found_module = nil
-- search through each of the supported configuration locations
for _, config_path in ipairs(supported_configs) do
-- convert the module path to a file path (example user.init -> user/init.lua)
local module_path = config_path .. "/lua/" .. module:gsub("%.", "/") .. ".lua"
-- check if there is a readable file, if so, set it as found
if vim.fn.filereadable(module_path) == 1 then found_module = module_path end
end
-- if we found a readable lua file, try to load it
if found_module then
-- try to load the file
local status_ok, loaded_module = pcall(require, module)
-- if successful at loading, set the return variable
if status_ok then
found_module = loaded_module
-- if unsuccessful, throw an error
else
vim.api.nvim_err_writeln("Error loading file: " .. found_module .. "\n\n" .. loaded_module)
end
end
-- return the loaded module or nil if no file found
return found_module
end
--- user settings from the base `user/init.lua` file
astronvim.user_settings = load_module_file "user.init"
--- default packer compilation location to be used in bootstrapping and packer setup call
astronvim.default_compile_path = stdpath "data" .. "/packer_compiled.lua"
--- table of user created terminals
astronvim.user_terminals = {}
--- table of plugins to load with git
astronvim.git_plugins = {}
--- table of plugins to load when file opened
astronvim.file_plugins = {}
--- regex used for matching a valid URL/URI string
astronvim.url_matcher =
"\\v\\c%(%(h?ttps?|ftp|file|ssh|git)://|[a-z]+[@][a-z]+[.][a-z]+:)%([&:#*@~%_\\-=?!+;/0-9a-z]+%(%([.;/?]|[.][.]+)[&:#*@~%_\\-=?!+/0-9a-z]+|:\\d+|,%(%(%(h?ttps?|ftp|file|ssh|git)://|[a-z]+[@][a-z]+[.][a-z]+:)@![0-9a-z]+))*|\\([&:#*@~%_\\-=?!+;/.0-9a-z]*\\)|\\[[&:#*@~%_\\-=?!+;/.0-9a-z]*\\]|\\{%([&:#*@~%_\\-=?!+;/.0-9a-z]*|\\{[&:#*@~%_\\-=?!+;/.0-9a-z]*})\\})+"
--- Main configuration engine logic for extending a default configuration table with either a function override or a table to merge into the default option
-- @function astronvim.func_or_extend
-- @param overrides the override definition, either a table or a function that takes a single parameter of the original table
-- @param default the default configuration table
-- @param extend boolean value to either extend the default or simply overwrite it if an override is provided
-- @return the new configuration table
local function func_or_extend(overrides, default, extend)
-- if we want to extend the default with the provided override
if extend then
-- if the override is a table, use vim.tbl_deep_extend
if type(overrides) == "table" then
default = astronvim.default_tbl(overrides, default)
-- if the override is a function, call it with the default and overwrite default with the return value
elseif type(overrides) == "function" then
default = overrides(default)
end
-- if extend is set to false and we have a provided override, simply override the default
elseif overrides ~= nil then
default = overrides
end
-- return the modified default table
return default
end
--- Merge extended options with a default table of options
-- @param opts the new options that should be merged with the default table
-- @param default the default table that you want to merge into
-- @return the merged table
function astronvim.default_tbl(opts, default)
opts = opts or {}
return default and vim.tbl_deep_extend("force", default, opts) or opts
end
--- Call function if a condition is met
-- @param func the function to run
-- @param condition a boolean value of whether to run the function or not
function astronvim.conditional_func(func, condition, ...)
-- if the condition is true or no condition is provided, evaluate the function with the rest of the parameters and return the result
if (condition == nil or condition) and type(func) == "function" then return func(...) end
end
--- Get highlight properties for a given highlight name
-- @param name highlight group name
-- @return table of highlight group properties
function astronvim.get_hlgroup(name, fallback)
if vim.fn.hlexists(name) == 1 then
local hl = vim.api.nvim_get_hl_by_name(name, vim.o.termguicolors)
if not hl["foreground"] then hl["foreground"] = "NONE" end
if not hl["background"] then hl["background"] = "NONE" end
hl.fg, hl.bg, hl.sp = hl.foreground, hl.background, hl.special
hl.ctermfg, hl.ctermbg = hl.foreground, hl.background
return hl
end
return fallback
end
--- Trim a string or return nil
-- @param str the string to trim
-- @return a trimmed version of the string or nil if the parameter isn't a string
function astronvim.trim_or_nil(str) return type(str) == "string" and vim.trim(str) or nil end
--- Add left and/or right padding to a string
-- @param str the string to add padding to
-- @param padding a table of the format `{ left = 0, right = 0}` that defines the number of spaces to include to the left and the right of the string
-- @return the padded string
function astronvim.pad_string(str, padding)
padding = padding or {}
return str and str ~= "" and string.rep(" ", padding.left or 0) .. str .. string.rep(" ", padding.right or 0) or ""
end
--- Initialize icons used throughout the user interface
function astronvim.initialize_icons()
astronvim.icons = astronvim.user_plugin_opts("icons", require "core.icons.nerd_font")
astronvim.text_icons = astronvim.user_plugin_opts("text_icons", require "core.icons.text")
end
--- Get an icon from `lspkind` if it is available and return it
-- @param kind the kind of icon in `lspkind` to retrieve
-- @return the icon
function astronvim.get_icon(kind)
local icon_pack = vim.g.icons_enabled and "icons" or "text_icons"
if not astronvim[icon_pack] then astronvim.initialize_icons() end
return astronvim[icon_pack] and astronvim[icon_pack][kind] or ""
end
--- Serve a notification with a title of AstroNvim
-- @param msg the notification body
-- @param type the type of the notification (:help vim.log.levels)
-- @param opts table of nvim-notify options to use (:help notify-options)
function astronvim.notify(msg, type, opts)
vim.schedule(function() vim.notify(msg, type, astronvim.default_tbl(opts, { title = "AstroNvim" })) end)
end
--- Trigger an AstroNvim user event
-- @param event the event name to be appended to Astro
function astronvim.event(event)
vim.schedule(function() vim.api.nvim_exec_autocmds("User", { pattern = "Astro" .. event }) end)
end
--- Wrapper function for neovim echo API
-- @param messages an array like table where each item is an array like table of strings to echo
function astronvim.echo(messages)
-- if no parameter provided, echo a new line
messages = messages or { { "\n" } }
if type(messages) == "table" then vim.api.nvim_echo(messages, false, {}) end
end
--- Echo a message and prompt the user for yes or no response
-- @param messages the message to echo
-- @return True if the user responded y, False for any other response
function astronvim.confirm_prompt(messages)
if messages then astronvim.echo(messages) end
local confirmed = string.lower(vim.fn.input "(y/n) ") == "y"
astronvim.echo()
astronvim.echo()
return confirmed
end
--- Search the user settings (user/init.lua table) for a table with a module like path string
-- @param module the module path like string to look up in the user settings table
-- @return the value of the table entry if exists or nil
local function user_setting_table(module)
-- get the user settings table
local settings = astronvim.user_settings or {}
-- iterate over the path string split by '.' to look up the table value
for tbl in string.gmatch(module, "([^%.]+)") do
settings = settings[tbl]
-- if key doesn't exist, keep the nil value and stop searching
if settings == nil then break end
end
-- return the found settings
return settings
end
--- Check if packer is installed and loadable, if not then install it and make sure it loads
function astronvim.initialize_packer()
-- try loading packer
local packer_path = stdpath "data" .. "/site/pack/packer/opt/packer.nvim"
local packer_avail = vim.fn.empty(vim.fn.glob(packer_path)) == 0
-- if packer isn't availble, reinstall it
if not packer_avail then
-- set the location to install packer
-- delete the old packer install if one exists
vim.fn.delete(packer_path, "rf")
-- clone packer
vim.fn.system {
"git",
"clone",
"--depth",
"1",
"https://github.com/wbthomason/packer.nvim",
packer_path,
}
-- add packer and try loading it
vim.cmd.packadd "packer.nvim"
local packer_loaded, _ = pcall(require, "packer")
packer_avail = packer_loaded
-- if packer didn't load, print error
if not packer_avail then vim.api.nvim_err_writeln("Failed to load packer at:" .. packer_path) end
end
-- if packer is available, check if there is a compiled packer file
if packer_avail then
-- try to load the packer compiled file
local run_me, _ = loadfile(
astronvim.user_plugin_opts("plugins.packer", { compile_path = astronvim.default_compile_path }).compile_path
)
if run_me then
-- if the file loads, run the compiled function
run_me()
else
-- if there is no compiled file, ask user to sync packer
require "core.plugins"
vim.api.nvim_create_autocmd("User", {
once = true,
pattern = "PackerComplete",
callback = function()
vim.cmd.bw()
vim.tbl_map(require, { "nvim-treesitter", "mason" })
astronvim.notify "Mason is installing packages if configured, check status with :Mason"
end,
})
vim.opt.cmdheight = 1
vim.notify "Please wait while plugins are installed..."
vim.cmd.PackerSync()
end
end
end
function astronvim.lazy_load_commands(plugin, commands)
if type(commands) == "string" then commands = { commands } end
if astronvim.is_available(plugin) and not packer_plugins[plugin].loaded then
for _, command in ipairs(commands) do
pcall(
vim.cmd,
string.format(
'command -nargs=* -range -bang -complete=file %s lua require("packer.load")({"%s"}, { cmd = "%s", l1 = <line1>, l2 = <line2>, bang = <q-bang>, args = <q-args>, mods = "<mods>" }, _G.packer_plugins)',
command,
plugin,
command
)
)
end
end
end
--- Set vim options with a nested table like API with the format vim.<first_key>.<second_key>.<value>
-- @param options the nested table of vim options
function astronvim.vim_opts(options)
for scope, table in pairs(options) do
for setting, value in pairs(table) do
vim[scope][setting] = value
end
end
end
--- User configuration entry point to override the default options of a configuration table with a user configuration file or table in the user/init.lua user settings
-- @param module the module path of the override setting
-- @param default the default settings that will be overridden
-- @param extend boolean value to either extend the default settings or overwrite them with the user settings entirely (default: true)
-- @param prefix a module prefix for where to search (default: user)
-- @return the new configuration settings with the user overrides applied
function astronvim.user_plugin_opts(module, default, extend, prefix)
-- default to extend = true
if extend == nil then extend = true end
-- if no default table is provided set it to an empty table
default = default or {}
-- try to load a module file if it exists
local user_settings = load_module_file((prefix or "user") .. "." .. module)
-- if no user module file is found, try to load an override from the user settings table from user/init.lua
if user_settings == nil and prefix == nil then user_settings = user_setting_table(module) end
-- if a user override was found call the configuration engine
if user_settings ~= nil then default = func_or_extend(user_settings, default, extend) end
-- return the final configuration table with any overrides applied
return default
end
--- Open a URL under the cursor with the current operating system (Supports Mac OS X and *nix)
-- @param path the path of the file to open with the system opener
function astronvim.system_open(path)
path = path or vim.fn.expand "<cfile>"
if vim.fn.has "mac" == 1 then
-- if mac use the open command
vim.fn.jobstart({ "open", path }, { detach = true })
elseif vim.fn.has "unix" == 1 then
-- if unix then use xdg-open
vim.fn.jobstart({ "xdg-open", path }, { detach = true })
else
-- if any other operating system notify the user that there is currently no support
astronvim.notify("System open is not supported on this OS!", "error")
end
end
-- term_details can be either a string for just a command or
-- a complete table to provide full access to configuration when calling Terminal:new()
--- Toggle a user terminal if it exists, if not then create a new one and save it
-- @param term_details a terminal command string or a table of options for Terminal:new() (Check toggleterm.nvim documentation for table format)
function astronvim.toggle_term_cmd(opts)
local terms = astronvim.user_terminals
-- if a command string is provided, create a basic table for Terminal:new() options
if type(opts) == "string" then opts = { cmd = opts, hidden = true } end
local num = vim.v.count > 0 and vim.v.count or 1
-- if terminal doesn't exist yet, create it
if not terms[opts.cmd] then terms[opts.cmd] = {} end
if not terms[opts.cmd][num] then
if not opts.count then opts.count = vim.tbl_count(terms) * 100 + num end
terms[opts.cmd][num] = require("toggleterm.terminal").Terminal:new(opts)
end
-- toggle the terminal
astronvim.user_terminals[opts.cmd][num]:toggle()
end
--- Add a source to cmp
-- @param source the cmp source string or table to add (see cmp documentation for source table format)
function astronvim.add_cmp_source(source)
-- load cmp if available
local cmp_avail, cmp = pcall(require, "cmp")
if cmp_avail then
-- get the current cmp config
local config = cmp.get_config()
-- add the source to the list of sources
tbl_insert(config.sources, source)
-- call the setup function again
cmp.setup(config)
end
end
--- Get the priority of a cmp source
-- @param source the cmp source string or table (see cmp documentation for source table format)
-- @return a cmp source table with the priority set from the user configuration
function astronvim.get_user_cmp_source(source)
-- if the source is a string, convert it to a cmp source table
source = type(source) == "string" and { name = source } or source
-- get the priority of the source name from the user configuration
local priority = astronvim.user_plugin_opts("cmp.source_priority", {
nvim_lsp = 1000,
luasnip = 750,
buffer = 500,
path = 250,
})[source.name]
-- if a priority is found, set it in the source
if priority then source.priority = priority end
-- return the source table
return source
end
--- add a source to cmp with the user configured priority
-- @param source a cmp source string or table (see cmp documentation for source table format)
function astronvim.add_user_cmp_source(source) astronvim.add_cmp_source(astronvim.get_user_cmp_source(source)) end
--- register mappings table with which-key
-- @param mappings nested table of mappings where the first key is the mode, the second key is the prefix, and the value is the mapping table for which-key
-- @param opts table of which-key options when setting the mappings (see which-key documentation for possible values)
function astronvim.which_key_register(mappings, opts)
local status_ok, which_key = pcall(require, "which-key")
if not status_ok then return end
for mode, prefixes in pairs(mappings) do
for prefix, mapping_table in pairs(prefixes) do
which_key.register(
mapping_table,
astronvim.default_tbl(opts, {
mode = mode,
prefix = prefix,
buffer = nil,
silent = true,
noremap = true,
nowait = true,
})
)
end
end
end
--- Get a list of registered null-ls providers for a given filetype
-- @param filetype the filetype to search null-ls for
-- @return a list of null-ls sources
function astronvim.null_ls_providers(filetype)
local registered = {}
-- try to load null-ls
local sources_avail, sources = pcall(require, "null-ls.sources")
if sources_avail then
-- get the available sources of a given filetype
for _, source in ipairs(sources.get_available(filetype)) do
-- get each source name
for method in pairs(source.methods) do
registered[method] = registered[method] or {}
tbl_insert(registered[method], source.name)
end
end
end
-- return the found null-ls sources
return registered
end
--- Get the null-ls sources for a given null-ls method
-- @param filetype the filetype to search null-ls for
-- @param method the null-ls method (check null-ls documentation for available methods)
-- @return the available sources for the given filetype and method
function astronvim.null_ls_sources(filetype, method)
local methods_avail, methods = pcall(require, "null-ls.methods")
return methods_avail and astronvim.null_ls_providers(filetype)[methods.internal[method]] or {}
end
--- Create a button entity to use with the alpha dashboard
-- @param sc the keybinding string to convert to a button
-- @param txt the explanation text of what the keybinding does
-- @return a button entity table for an alpha configuration
function astronvim.alpha_button(sc, txt)
-- replace <leader> in shortcut text with LDR for nicer printing
local sc_ = sc:gsub("%s", ""):gsub("LDR", "<leader>")
-- if the leader is set, replace the text with the actual leader key for nicer printing
if vim.g.mapleader then sc = sc:gsub("LDR", vim.g.mapleader == " " and "SPC" or vim.g.mapleader) end
-- return the button entity to display the correct text and send the correct keybinding on press
return {
type = "button",
val = txt,
on_press = function()
local key = vim.api.nvim_replace_termcodes(sc_, true, false, true)
vim.api.nvim_feedkeys(key, "normal", false)
end,
opts = {
position = "center",
text = txt,
shortcut = sc,
cursor = 5,
width = 36,
align_shortcut = "right",
hl = "DashboardCenter",
hl_shortcut = "DashboardShortcut",
},
}
end
--- Check if a plugin is defined in packer. Useful with lazy loading when a plugin is not necessarily loaded yet
-- @param plugin the plugin string to search for
-- @return boolean value if the plugin is available
function astronvim.is_available(plugin) return packer_plugins ~= nil and packer_plugins[plugin] ~= nil end
--- A helper function to wrap a module function to require a plugin before running
-- @param plugin the plugin string to call `require("packer").laoder` with
-- @param module the system module where the functions live (e.g. `vim.ui`)
-- @param func_names a string or a list like table of strings for functions to wrap in the given moduel (e.g. `{ "ui", "select }`)
function astronvim.load_plugin_with_func(plugin, module, func_names)
if type(func_names) == "string" then func_names = { func_names } end
for _, func in ipairs(func_names) do
local old_func = module[func]
module[func] = function(...)
module[func] = old_func
require("packer").loader(plugin)
module[func](...)
end
end
end
--- Table based API for setting keybindings
-- @param map_table A nested table where the first key is the vim mode, the second key is the key to map, and the value is the function to set the mapping to
-- @param base A base set of options to set on every keybinding
function astronvim.set_mappings(map_table, base)
-- iterate over the first keys for each mode
for mode, maps in pairs(map_table) do
-- iterate over each keybinding set in the current mode
for keymap, options in pairs(maps) do
-- build the options for the command accordingly
if options then
local cmd = options
local keymap_opts = base or {}
if type(options) == "table" then
cmd = options[1]
keymap_opts = vim.tbl_deep_extend("force", options, keymap_opts)
keymap_opts[1] = nil
end
-- extend the keybinding options with the base provided and set the mapping
map(mode, keymap, cmd, keymap_opts)
end
end
end
end
--- Delete the syntax matching rules for URLs/URIs if set
function astronvim.delete_url_match()
for _, match in ipairs(vim.fn.getmatches()) do
if match.group == "HighlightURL" then vim.fn.matchdelete(match.id) end
end
end
--- Add syntax matching rules for highlighting URLs/URIs
function astronvim.set_url_match()
astronvim.delete_url_match()
if vim.g.highlighturl_enabled then vim.fn.matchadd("HighlightURL", astronvim.url_matcher, 15) end
end
--- Run a shell command and capture the output and if the command succeeded or failed
-- @param cmd the terminal command to execute
-- @param show_error boolean of whether or not to show an unsuccessful command as an error to the user
-- @return the result of a successfully executed command or nil
function astronvim.cmd(cmd, show_error)
if vim.fn.has "win32" == 1 then cmd = { "cmd.exe", "/C", cmd } end
local result = vim.fn.system(cmd)
local success = vim.api.nvim_get_vvar "shell_error" == 0
if not success and (show_error == nil and true or show_error) then
vim.api.nvim_err_writeln("Error running command: " .. cmd .. "\nError message:\n" .. result)
end
return success and result:gsub("[\27\155][][()#;?%d]*[A-PRZcf-ntqry=><~]", "") or nil
end
--- Check if a buffer is valid
-- @param bufnr the buffer to check
-- @return true if the buffer is valid or false
function astronvim.is_valid_buffer(bufnr)
if not bufnr or bufnr < 1 then return false end
return vim.bo[bufnr].buflisted and vim.api.nvim_buf_is_valid(bufnr)
end
--- Move the current buffer tab n places in the bufferline
-- @param n numer of tabs to move the current buffer over by (positive = right, negative = left)
function astronvim.move_buf(n)
if n == 0 then return end -- if n = 0 then no shifts are needed
local bufs = vim.t.bufs -- make temp variable
for i, bufnr in ipairs(bufs) do -- loop to find current buffer
if bufnr == vim.api.nvim_get_current_buf() then -- found index of current buffer
for _ = 0, (n % #bufs) - 1 do -- calculate number of right shifts
local new_i = i + 1 -- get next i
if i == #bufs then -- if at end, cycle to beginning
new_i = 1 -- next i is actually 1 if at the end
local val = bufs[i] -- save value
table.remove(bufs, i) -- remove from end
table.insert(bufs, new_i, val) -- insert at beginning
else -- if not at the end,then just do an in place swap
bufs[i], bufs[new_i] = bufs[new_i], bufs[i]
end
i = new_i -- iterate i to next value
end
break
end
end
vim.t.bufs = bufs -- set buffers
vim.cmd.redrawtabline() -- redraw tabline
end
--- Navigate left and right by n places in the bufferline
-- @param n the number of tabs to navigate to (positive = right, negative = left)
function astronvim.nav_buf(n)
local current = vim.api.nvim_get_current_buf()
for i, v in ipairs(vim.t.bufs) do
if current == v then
vim.cmd.b(vim.t.bufs[(i + n - 1) % #vim.t.bufs + 1])
break
end
end
end
--- Close a given buffer
-- @param bufnr? the buffer number to close or the current buffer if not provided
function astronvim.close_buf(bufnr, force)
if force == nil then force = false end
local current = vim.api.nvim_get_current_buf()
if not bufnr or bufnr == 0 then bufnr = current end
if bufnr == current then astronvim.nav_buf(-1) end
if astronvim.is_available "bufdelete.nvim" then
require("bufdelete").bufdelete(bufnr, force)
else
vim.cmd((force and "bd!" or "confirm bd") .. bufnr)
end
end
--- Close the current tab
function astronvim.close_tab()
if #vim.api.nvim_list_tabpages() > 1 then
vim.t.bufs = nil
vim.cmd.tabclose()
end
end
require "core.utils.ui"
require "core.utils.status"
require "core.utils.updater"
require "core.utils.mason"
require "core.utils.lsp"
return astronvim

View File

@ -0,0 +1,243 @@
--- ### AstroNvim LSP
--
-- This module is automatically loaded by AstroNvim on during it's initialization into global variable `astronvim.lsp`
--
-- This module can also be manually loaded with `local updater = require("core.utils").lsp`
--
-- @module core.utils.lsp
-- @see core.utils
-- @copyright 2022
-- @license GNU General Public License v3.0
astronvim.lsp = {}
local tbl_contains = vim.tbl_contains
local tbl_isempty = vim.tbl_isempty
local user_plugin_opts = astronvim.user_plugin_opts
local conditional_func = astronvim.conditional_func
local is_available = astronvim.is_available
local user_registration = user_plugin_opts("lsp.server_registration", nil, false)
local skip_setup = user_plugin_opts "lsp.skip_setup"
astronvim.lsp.formatting =
astronvim.user_plugin_opts("lsp.formatting", { format_on_save = { enabled = true }, disabled = {} })
if type(astronvim.lsp.formatting.format_on_save) == "boolean" then
astronvim.lsp.formatting.format_on_save = { enabled = astronvim.lsp.formatting.format_on_save }
end
astronvim.lsp.format_opts = vim.deepcopy(astronvim.lsp.formatting)
astronvim.lsp.format_opts.disabled = nil
astronvim.lsp.format_opts.format_on_save = nil
astronvim.lsp.format_opts.filter = function(client)
local filter = astronvim.lsp.formatting.filter
local disabled = astronvim.lsp.formatting.disabled or {}
-- check if client is fully disabled or filtered by function
return not (vim.tbl_contains(disabled, client.name) or (type(filter) == "function" and not filter(client)))
end
--- Helper function to set up a given server with the Neovim LSP client
-- @param server the name of the server to be setup
astronvim.lsp.setup = function(server)
if not tbl_contains(skip_setup, server) then
-- if server doesn't exist, set it up from user server definition
if not pcall(require, "lspconfig.server_configurations." .. server) then
local server_definition = user_plugin_opts("lsp.server-settings." .. server)
if server_definition.cmd then require("lspconfig.configs")[server] = { default_config = server_definition } end
end
local opts = astronvim.lsp.server_settings(server)
if type(user_registration) == "function" then
user_registration(server, opts)
else
require("lspconfig")[server].setup(opts)
end
end
end
--- The `on_attach` function used by AstroNvim
-- @param client the LSP client details when attaching
-- @param bufnr the number of the buffer that the LSP client is attaching to
astronvim.lsp.on_attach = function(client, bufnr)
local capabilities = client.server_capabilities
local lsp_mappings = {
n = {
["<leader>ld"] = { function() vim.diagnostic.open_float() end, desc = "Hover diagnostics" },
["[d"] = { function() vim.diagnostic.goto_prev() end, desc = "Previous diagnostic" },
["]d"] = { function() vim.diagnostic.goto_next() end, desc = "Next diagnostic" },
["gl"] = { function() vim.diagnostic.open_float() end, desc = "Hover diagnostics" },
},
v = {},
}
if is_available "mason-lspconfig.nvim" then
lsp_mappings.n["<leader>li"] = { "<cmd>LspInfo<cr>", desc = "LSP information" }
end
if is_available "null-ls.nvim" then
lsp_mappings.n["<leader>lI"] = { "<cmd>NullLsInfo<cr>", desc = "Null-ls information" }
end
if capabilities.codeActionProvider then
lsp_mappings.n["<leader>la"] = { function() vim.lsp.buf.code_action() end, desc = "LSP code action" }
lsp_mappings.v["<leader>la"] = lsp_mappings.n["<leader>la"]
end
if capabilities.codeLensProvider then
lsp_mappings.n["<leader>ll"] = { function() vim.lsp.codelens.refresh() end, desc = "LSP codelens refresh" }
lsp_mappings.n["<leader>lL"] = { function() vim.lsp.codelens.run() end, desc = "LSP codelens run" }
end
if capabilities.declarationProvider then
lsp_mappings.n["gD"] = { function() vim.lsp.buf.declaration() end, desc = "Declaration of current symbol" }
end
if capabilities.definitionProvider then
lsp_mappings.n["gd"] = { function() vim.lsp.buf.definition() end, desc = "Show the definition of current symbol" }
end
if capabilities.documentFormattingProvider and not tbl_contains(astronvim.lsp.formatting.disabled, client.name) then
lsp_mappings.n["<leader>lf"] = {
function() vim.lsp.buf.format(astronvim.lsp.format_opts) end,
desc = "Format buffer",
}
lsp_mappings.v["<leader>lf"] = lsp_mappings.n["<leader>lf"]
vim.api.nvim_buf_create_user_command(
bufnr,
"Format",
function() vim.lsp.buf.format(astronvim.lsp.format_opts) end,
{ desc = "Format file with LSP" }
)
local autoformat = astronvim.lsp.formatting.format_on_save
local filetype = vim.api.nvim_buf_get_option(bufnr, "filetype")
if
autoformat.enabled
and (tbl_isempty(autoformat.allow_filetypes or {}) or tbl_contains(autoformat.allow_filetypes, filetype))
and (tbl_isempty(autoformat.ignore_filetypes or {}) or not tbl_contains(autoformat.ignore_filetypes, filetype))
then
local autocmd_group = "auto_format_" .. bufnr
vim.api.nvim_create_augroup(autocmd_group, { clear = true })
vim.api.nvim_create_autocmd("BufWritePre", {
group = autocmd_group,
buffer = bufnr,
desc = "Auto format buffer " .. bufnr .. " before save",
callback = function()
if vim.g.autoformat_enabled then
vim.lsp.buf.format(astronvim.default_tbl({ bufnr = bufnr }, astronvim.lsp.format_opts))
end
end,
})
lsp_mappings.n["<leader>uf"] = {
function() astronvim.ui.toggle_autoformat() end,
desc = "Toggle autoformatting",
}
end
end
if capabilities.documentHighlightProvider then
local highlight_name = vim.fn.printf("lsp_document_highlight_%d", bufnr)
vim.api.nvim_create_augroup(highlight_name, {})
vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
group = highlight_name,
buffer = bufnr,
callback = function() vim.lsp.buf.document_highlight() end,
})
vim.api.nvim_create_autocmd("CursorMoved", {
group = highlight_name,
buffer = bufnr,
callback = function() vim.lsp.buf.clear_references() end,
})
end
if capabilities.hoverProvider then
lsp_mappings.n["K"] = { function() vim.lsp.buf.hover() end, desc = "Hover symbol details" }
end
if capabilities.implementationProvider then
lsp_mappings.n["gI"] = { function() vim.lsp.buf.implementation() end, desc = "Implementation of current symbol" }
end
if capabilities.referencesProvider then
lsp_mappings.n["gr"] = { function() vim.lsp.buf.references() end, desc = "References of current symbol" }
lsp_mappings.n["<leader>lR"] = { function() vim.lsp.buf.references() end, desc = "Search references" }
end
if capabilities.renameProvider then
lsp_mappings.n["<leader>lr"] = { function() vim.lsp.buf.rename() end, desc = "Rename current symbol" }
end
if capabilities.signatureHelpProvider then
lsp_mappings.n["<leader>lh"] = { function() vim.lsp.buf.signature_help() end, desc = "Signature help" }
end
if capabilities.typeDefinitionProvider then
lsp_mappings.n["gT"] = { function() vim.lsp.buf.type_definition() end, desc = "Definition of current type" }
end
if capabilities.workspaceSymbolProvider then
lsp_mappings.n["<leader>lG"] = { function() vim.lsp.buf.workspace_symbol() end, desc = "Search workspace symbols" }
end
if is_available "telescope.nvim" then -- setup telescope mappings if available
if lsp_mappings.n.gd then lsp_mappings.n.gd[1] = function() require("telescope.builtin").lsp_definitions() end end
if lsp_mappings.n.gI then
lsp_mappings.n.gI[1] = function() require("telescope.builtin").lsp_implementations() end
end
if lsp_mappings.n.gr then lsp_mappings.n.gr[1] = function() require("telescope.builtin").lsp_references() end end
if lsp_mappings.n["<leader>lR"] then
lsp_mappings.n["<leader>lR"][1] = function() require("telescope.builtin").lsp_references() end
end
if lsp_mappings.n.gT then
lsp_mappings.n.gT[1] = function() require("telescope.builtin").lsp_type_definitions() end
end
if lsp_mappings.n["<leader>lG"] then
lsp_mappings.n["<leader>lG"][1] = function() require("telescope.builtin").lsp_workspace_symbols() end
end
end
astronvim.set_mappings(user_plugin_opts("lsp.mappings", lsp_mappings), { buffer = bufnr })
if not vim.tbl_isempty(lsp_mappings.v) then
astronvim.which_key_register({ v = { ["<leader>"] = { l = { name = "LSP" } } } }, { buffer = bufnr })
end
local on_attach_override = user_plugin_opts("lsp.on_attach", nil, false)
conditional_func(on_attach_override, true, client, bufnr)
end
--- The default AstroNvim LSP capabilities
astronvim.lsp.capabilities = vim.lsp.protocol.make_client_capabilities()
astronvim.lsp.capabilities.textDocument.completion.completionItem.documentationFormat = { "markdown", "plaintext" }
astronvim.lsp.capabilities.textDocument.completion.completionItem.snippetSupport = true
astronvim.lsp.capabilities.textDocument.completion.completionItem.preselectSupport = true
astronvim.lsp.capabilities.textDocument.completion.completionItem.insertReplaceSupport = true
astronvim.lsp.capabilities.textDocument.completion.completionItem.labelDetailsSupport = true
astronvim.lsp.capabilities.textDocument.completion.completionItem.deprecatedSupport = true
astronvim.lsp.capabilities.textDocument.completion.completionItem.commitCharactersSupport = true
astronvim.lsp.capabilities.textDocument.completion.completionItem.tagSupport = { valueSet = { 1 } }
astronvim.lsp.capabilities.textDocument.completion.completionItem.resolveSupport = {
properties = { "documentation", "detail", "additionalTextEdits" },
}
astronvim.lsp.capabilities = user_plugin_opts("lsp.capabilities", astronvim.lsp.capabilities)
astronvim.lsp.flags = user_plugin_opts "lsp.flags"
--- Get the server settings for a given language server to be provided to the server's `setup()` call
-- @param server_name the name of the server
-- @return the table of LSP options used when setting up the given language server
function astronvim.lsp.server_settings(server_name)
local server = require("lspconfig")[server_name]
local opts = user_plugin_opts( -- get user server-settings
"lsp.server-settings." .. server_name, -- TODO: RENAME lsp.server-settings to lsp.config in v3
user_plugin_opts("server-settings." .. server_name, { -- get default server-settings
capabilities = vim.tbl_deep_extend("force", astronvim.lsp.capabilities, server.capabilities or {}),
flags = vim.tbl_deep_extend("force", astronvim.lsp.flags, server.flags or {}),
}, true, "configs")
)
local old_on_attach = server.on_attach
local user_on_attach = opts.on_attach
opts.on_attach = function(client, bufnr)
conditional_func(old_on_attach, true, client, bufnr)
astronvim.lsp.on_attach(client, bufnr)
conditional_func(user_on_attach, true, client, bufnr)
end
return opts
end
return astronvim.lsp

View File

@ -0,0 +1,95 @@
--- ### AstroNvim Mason Utils
--
-- This module is automatically loaded by AstroNvim on during it's initialization into global variable `astronvim.mason`
--
-- This module can also be manually loaded with `local updater = require("core.utils").mason`
--
-- @module core.utils.mason
-- @see core.utils
-- @copyright 2022
-- @license GNU General Public License v3.0
astronvim.mason = {}
--- Update a mason package
-- @param pkg_name string of the name of the package as defined in Mason (Not mason-lspconfig or mason-null-ls)
-- @param auto_install boolean of whether or not to install a package that is not currently installed (default: True)
function astronvim.mason.update(pkg_name, auto_install)
if auto_install == nil then auto_install = true end
local registry_avail, registry = pcall(require, "mason-registry")
if not registry_avail then
vim.api.nvim_err_writeln "Unable to access mason registry"
return
end
local pkg_avail, pkg = pcall(registry.get_package, pkg_name)
if not pkg_avail then
astronvim.notify(("Mason: %s is not available"):format(pkg_name), "error")
else
if not pkg:is_installed() then
if auto_install then
astronvim.notify(("Mason: Installing %s"):format(pkg.name))
pkg:install()
else
astronvim.notify(("Mason: %s not installed"):format(pkg.name), "warn")
end
else
pkg:check_new_version(function(update_available, version)
if update_available then
astronvim.notify(("Mason: Updating %s to %s"):format(pkg.name, version.latest_version))
pkg:install():on("closed", function() astronvim.notify(("Mason: Updated %s"):format(pkg.name)) end)
else
astronvim.notify(("Mason: No updates available for %s"):format(pkg.name))
end
end)
end
end
end
--- Update all packages in Mason
function astronvim.mason.update_all()
local registry_avail, registry = pcall(require, "mason-registry")
if not registry_avail then
vim.api.nvim_err_writeln "Unable to access mason registry"
return
end
local installed_pkgs = registry.get_installed_packages()
local running = #installed_pkgs
local no_pkgs = running == 0
astronvim.notify "Mason: Checking for package updates..."
if no_pkgs then
astronvim.notify "Mason: No updates available"
astronvim.event "MasonUpdateComplete"
else
local updated = false
for _, pkg in ipairs(installed_pkgs) do
pkg:check_new_version(function(update_available, version)
if update_available then
updated = true
astronvim.notify(("Mason: Updating %s to %s"):format(pkg.name, version.latest_version))
pkg:install():on("closed", function()
running = running - 1
if running == 0 then
astronvim.notify "Mason: Update Complete"
astronvim.event "MasonUpdateComplete"
end
end)
else
running = running - 1
if running == 0 then
if updated then
astronvim.notify "Mason: Update Complete"
else
astronvim.notify "Mason: No updates available"
end
astronvim.event "MasonUpdateComplete"
end
end
end)
end
end
end
return astronvim.mason

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,188 @@
--- ### AstroNvim UI Options
--
-- This module is automatically loaded by AstroNvim on during it's initialization into global variable `astronvim.ui`
--
-- This module can also be manually loaded with `local updater = require("core.utils").ui`
--
-- @module core.utils.ui
-- @see core.utils
-- @copyright 2022
-- @license GNU General Public License v3.0
astronvim.ui = {}
local function bool2str(bool) return bool and "on" or "off" end
local function ui_notify(str)
if vim.g.ui_notifications_enabled then astronvim.notify(str) end
end
--- Toggle notifications for UI toggles
function astronvim.ui.toggle_ui_notifications()
vim.g.ui_notifications_enabled = not vim.g.ui_notifications_enabled
ui_notify(string.format("ui notifications %s", bool2str(vim.g.ui_notifications_enabled)))
end
--- Toggle autopairs
function astronvim.ui.toggle_autopairs()
local ok, autopairs = pcall(require, "nvim-autopairs")
if ok then
if autopairs.state.disabled then
autopairs.enable()
else
autopairs.disable()
end
vim.g.autopairs_enabled = autopairs.state.disabled
ui_notify(string.format("autopairs %s", bool2str(not autopairs.state.disabled)))
else
ui_notify "autopairs not available"
end
end
--- Toggle diagnostics
function astronvim.ui.toggle_diagnostics()
local status = "on"
if vim.g.status_diagnostics_enabled then
if vim.g.diagnostics_enabled then
vim.g.diagnostics_enabled = false
status = "virtual text off"
else
vim.g.status_diagnostics_enabled = false
status = "fully off"
end
else
vim.g.diagnostics_enabled = true
vim.g.status_diagnostics_enabled = true
end
vim.diagnostic.config(astronvim.lsp.diagnostics[bool2str(vim.g.diagnostics_enabled)])
ui_notify(string.format("diagnostics %s", status))
end
--- Toggle background="dark"|"light"
function astronvim.ui.toggle_background()
vim.go.background = vim.go.background == "light" and "dark" or "light"
ui_notify(string.format("background=%s", vim.go.background))
end
--- Toggle cmp entrirely
function astronvim.ui.toggle_cmp()
vim.g.cmp_enabled = not vim.g.cmp_enabled
local ok, _ = pcall(require, "cmp")
ui_notify(ok and string.format("completion %s", bool2str(vim.g.cmp_enabled)) or "completion not available")
end
--- Toggle auto format
function astronvim.ui.toggle_autoformat()
vim.g.autoformat_enabled = not vim.g.autoformat_enabled
ui_notify(string.format("Autoformatting %s", bool2str(vim.g.autoformat_enabled)))
end
--- Toggle showtabline=2|0
function astronvim.ui.toggle_tabline()
vim.opt.showtabline = vim.opt.showtabline:get() == 0 and 2 or 0
ui_notify(string.format("tabline %s", bool2str(vim.opt.showtabline:get() == 2)))
end
--- Toggle conceal=2|0
function astronvim.ui.toggle_conceal()
vim.opt.conceallevel = vim.opt.conceallevel:get() == 0 and 2 or 0
ui_notify(string.format("conceal %s", bool2str(vim.opt.conceallevel:get() == 2)))
end
--- Toggle laststatus=3|2|0
function astronvim.ui.toggle_statusline()
local laststatus = vim.opt.laststatus:get()
local status
if laststatus == 0 then
vim.opt.laststatus = 2
status = "local"
elseif laststatus == 2 then
vim.opt.laststatus = 3
status = "global"
elseif laststatus == 3 then
vim.opt.laststatus = 0
status = "off"
end
ui_notify(string.format("statusline %s", status))
end
--- Toggle signcolumn="auto"|"no"
function astronvim.ui.toggle_signcolumn()
if vim.wo.signcolumn == "no" then
vim.wo.signcolumn = "yes"
elseif vim.wo.signcolumn == "yes" then
vim.wo.signcolumn = "auto"
else
vim.wo.signcolumn = "no"
end
ui_notify(string.format("signcolumn=%s", vim.wo.signcolumn))
end
--- Set the indent and tab related numbers
function astronvim.ui.set_indent()
local input_avail, input = pcall(vim.fn.input, "Set indent value (>0 expandtab, <=0 noexpandtab): ")
if input_avail then
local indent = tonumber(input)
if not indent or indent == 0 then return end
vim.bo.expandtab = (indent > 0) -- local to buffer
indent = math.abs(indent)
vim.bo.tabstop = indent -- local to buffer
vim.bo.softtabstop = indent -- local to buffer
vim.bo.shiftwidth = indent -- local to buffer
ui_notify(string.format("indent=%d %s", indent, vim.bo.expandtab and "expandtab" or "noexpandtab"))
end
end
--- Change the number display modes
function astronvim.ui.change_number()
local number = vim.wo.number -- local to window
local relativenumber = vim.wo.relativenumber -- local to window
if not number and not relativenumber then
vim.wo.number = true
elseif number and not relativenumber then
vim.wo.relativenumber = true
elseif number and relativenumber then
vim.wo.number = false
else -- not number and relativenumber
vim.wo.relativenumber = false
end
ui_notify(string.format("number %s, relativenumber %s", bool2str(vim.wo.number), bool2str(vim.wo.relativenumber)))
end
--- Toggle spell
function astronvim.ui.toggle_spell()
vim.wo.spell = not vim.wo.spell -- local to window
ui_notify(string.format("spell %s", bool2str(vim.wo.spell)))
end
--- Toggle paste
function astronvim.ui.toggle_paste()
vim.opt.paste = not vim.opt.paste:get() -- local to window
ui_notify(string.format("paste %s", bool2str(vim.opt.paste:get())))
end
--- Toggle wrap
function astronvim.ui.toggle_wrap()
vim.wo.wrap = not vim.wo.wrap -- local to window
ui_notify(string.format("wrap %s", bool2str(vim.wo.wrap)))
end
--- Toggle syntax highlighting and treesitter
function astronvim.ui.toggle_syntax()
local ts_avail, parsers = pcall(require, "nvim-treesitter.parsers")
if vim.g.syntax_on then -- global var for on//off
if ts_avail and parsers.has_parser() then vim.cmd.TSBufDisable "highlight" end
vim.cmd.syntax "off" -- set vim.g.syntax_on = false
else
if ts_avail and parsers.has_parser() then vim.cmd.TSBufEnable "highlight" end
vim.cmd.syntax "on" -- set vim.g.syntax_on = true
end
ui_notify(string.format("syntax %s", bool2str(vim.g.syntax_on)))
end
--- Toggle URL/URI syntax highlighting rules
function astronvim.ui.toggle_url_match()
vim.g.highlighturl_enabled = not vim.g.highlighturl_enabled
astronvim.set_url_match()
end

View File

@ -0,0 +1,296 @@
--- ### AstroNvim Updater
--
-- This module is automatically loaded by AstroNvim on during it's initialization into global variable `astronvim.updater`
--
-- This module can also be manually loaded with `local updater = require("core.utils").updater`
--
-- @module core.utils.updater
-- @see core.utils
-- @copyright 2022
-- @license GNU General Public License v3.0
local fn = vim.fn
local git = require "core.utils.git"
--- Updater settings overridden with any user provided configuration
local options = astronvim.user_plugin_opts("updater", {
remote = "origin",
channel = "stable",
show_changelog = true,
auto_reload = true,
auto_quit = true,
})
-- set the install channel
if options.branch then options.channel = "nightly" end
if astronvim.install.is_stable ~= nil then options.channel = astronvim.install.is_stable and "stable" or "nightly" end
astronvim.updater = { options = options }
-- if the channel is stable or the user has chosen to pin the system plugins
if options.pin_plugins == nil and options.channel == "stable" or options.pin_plugins then
-- load the current packer snapshot from the installation home location
local loaded, snapshot = pcall(fn.readfile, astronvim.install.home .. "/packer_snapshot")
if loaded then
-- decode the snapshot JSON and save it to a variable
loaded, snapshot = pcall(fn.json_decode, snapshot)
astronvim.updater.snapshot = type(snapshot) == "table" and snapshot or nil
end
-- if there is an error loading the snapshot, print an error
if not loaded then vim.api.nvim_err_writeln "Error loading packer snapshot" end
end
--- Get the current AstroNvim version
-- @param quiet boolean to quietly execute or send a notification
-- @return the current AstroNvim version string
function astronvim.updater.version(quiet)
local version = astronvim.install.version or git.current_version(false)
if version and not quiet then astronvim.notify("Version: " .. version) end
return version
end
--- Get the full AstroNvim changelog
-- @param quiet boolean to quietly execute or display the changelog
-- @return the current AstroNvim changelog table of commit messages
function astronvim.updater.changelog(quiet)
local summary = {}
vim.list_extend(summary, git.pretty_changelog(git.get_commit_range()))
if not quiet then astronvim.echo(summary) end
return summary
end
--- Attempt an update of AstroNvim
-- @param target the target if checking out a specific tag or commit or nil if just pulling
local function attempt_update(target)
-- if updating to a new stable version or a specific commit checkout the provided target
if options.channel == "stable" or options.commit then
return git.checkout(target, false)
-- if no target, pull the latest
else
return git.pull(false)
end
end
--- Cancelled update message
local cancelled_message = { { "Update cancelled", "WarningMsg" } }
--- Reload the AstroNvim configuration live (Experimental)
-- @param quiet boolean to quietly execute or send a notification
function astronvim.updater.reload(quiet)
-- stop LSP if it is running
if vim.fn.exists ":LspStop" ~= 0 then vim.cmd.LspStop() end
local reload_module = require("plenary.reload").reload_module
-- unload AstroNvim configuration files
reload_module "user"
reload_module "configs"
reload_module "default_theme"
reload_module "core"
-- manual unload some plugins that need it if they exist
reload_module "cmp"
reload_module "which-key"
-- source the AstroNvim configuration
local reloaded, _ = pcall(dofile, vim.fn.expand "$MYVIMRC")
-- if successful reload and not quiet, display a notification
if reloaded and not quiet then astronvim.notify "Reloaded AstroNvim" end
end
--- Sync Packer and then update Mason
function astronvim.updater.update_packages()
vim.api.nvim_create_autocmd("User", {
once = true,
desc = "Update Mason with Packer",
group = vim.api.nvim_create_augroup("astro_sync", { clear = true }),
pattern = "PackerComplete",
callback = function()
if astronvim.is_available "mason.nvim" then
vim.api.nvim_create_autocmd("User", {
pattern = "AstroMasonUpdateComplete",
once = true,
callback = function() astronvim.event "UpdatePackagesComplete" end,
})
astronvim.mason.update_all()
else
astronvim.event "UpdatePackagesComplete"
end
end,
})
vim.cmd.PackerSync()
end
--- AstroNvim's updater function
function astronvim.updater.update()
-- if the git command is not available, then throw an error
if not git.available() then
astronvim.notify(
"git command is not available, please verify it is accessible in a command line. This may be an issue with your PATH",
"error"
)
return
end
-- if installed with an external package manager, disable the internal updater
if not git.is_repo() then
astronvim.notify("Updater not available for non-git installations", "error")
return
end
-- set up any remotes defined by the user if they do not exist
for remote, entry in pairs(options.remotes and options.remotes or {}) do
local url = git.parse_remote_url(entry)
local current_url = git.remote_url(remote, false)
local check_needed = false
if not current_url then
git.remote_add(remote, url)
check_needed = true
elseif
current_url ~= url
and astronvim.confirm_prompt {
{ "Remote " },
{ remote, "Title" },
{ " is currently set to " },
{ current_url, "WarningMsg" },
{ "\nWould you like us to set it to " },
{ url, "String" },
{ "?" },
}
then
git.remote_update(remote, url)
check_needed = true
end
if check_needed and git.remote_url(remote, false) ~= url then
vim.api.nvim_err_writeln("Error setting up remote " .. remote .. " to " .. url)
return
end
end
local is_stable = options.channel == "stable"
if is_stable then
options.branch = "main"
elseif not options.branch then
options.branch = "nightly"
end
-- fetch the latest remote
if not git.fetch(options.remote) then
vim.api.nvim_err_writeln("Error fetching remote: " .. options.remote)
return
end
-- switch to the necessary branch only if not on the stable channel
if not is_stable then
local local_branch = (options.remote == "origin" and "" or (options.remote .. "_")) .. options.branch
if git.current_branch() ~= local_branch then
astronvim.echo {
{ "Switching to branch: " },
{ options.remote .. "/" .. options.branch .. "\n\n", "String" },
}
if not git.checkout(local_branch, false) then
git.checkout("-b " .. local_branch .. " " .. options.remote .. "/" .. options.branch, false)
end
end
-- check if the branch was switched to successfully
if git.current_branch() ~= local_branch then
vim.api.nvim_err_writeln("Error checking out branch: " .. options.remote .. "/" .. options.branch)
return
end
end
local source = git.local_head() -- calculate current commit
local target -- calculate target commit
if is_stable then -- if stable get tag commit
local version_search = options.version or "latest"
options.version = git.latest_version(git.get_versions(version_search))
if not options.version then -- continue only if stable version is found
vim.api.nvim_err_writeln("Error finding version: " .. version_search)
return
end
target = git.tag_commit(options.version)
elseif options.commit then -- if commit specified use it
target = git.branch_contains(options.remote, options.branch, options.commit) and options.commit or nil
else -- get most recent commit
target = git.remote_head(options.remote, options.branch)
end
if not source or not target then -- continue if current and target commits were found
vim.api.nvim_err_writeln "Error checking for updates"
return
elseif source == target then
astronvim.echo { { "No updates available", "String" } }
return
elseif -- prompt user if they want to accept update
not options.skip_prompts
and not astronvim.confirm_prompt {
{ "Update available to ", "Title" },
{ is_stable and options.version or target, "String" },
{ "\nUpdating requires a restart, continue?" },
}
then
astronvim.echo(cancelled_message)
return
else -- perform update
-- calculate and print the changelog
local changelog = git.get_commit_range(source, target)
local breaking = git.breaking_changes(changelog)
local breaking_prompt = { { "Update contains the following breaking changes:\n", "WarningMsg" } }
vim.list_extend(breaking_prompt, git.pretty_changelog(breaking))
vim.list_extend(breaking_prompt, { { "\nWould you like to continue?" } })
if #breaking > 0 and not options.skip_prompts and not astronvim.confirm_prompt(breaking_prompt) then
astronvim.echo(cancelled_message)
return
end
-- attempt an update
local updated = attempt_update(target)
-- check for local file conflicts and prompt user to continue or abort
if
not updated
and not options.skip_prompts
and not astronvim.confirm_prompt {
{ "Unable to pull due to local modifications to base files.\n", "ErrorMsg" },
{ "Reset local files and continue?" },
}
then
astronvim.echo(cancelled_message)
return
-- if continued and there were errors reset the base config and attempt another update
elseif not updated then
git.hard_reset(source)
updated = attempt_update(target)
end
-- if update was unsuccessful throw an error
if not updated then
vim.api.nvim_err_writeln "Error ocurred performing update"
return
end
-- print a summary of the update with the changelog
local summary = {
{ "AstroNvim updated successfully to ", "Title" },
{ git.current_version(), "String" },
{ "!\n", "Title" },
{
options.auto_reload and "AstroNvim will now sync packer and quit.\n\n"
or "Please restart and run :PackerSync.\n\n",
"WarningMsg",
},
}
if options.show_changelog and #changelog > 0 then
vim.list_extend(summary, { { "Changelog:\n", "Title" } })
vim.list_extend(summary, git.pretty_changelog(changelog))
end
astronvim.echo(summary)
-- if the user wants to auto quit, create an autocommand to quit AstroNvim on the update completing
if options.auto_quit then
vim.api.nvim_create_autocmd("User", { pattern = "AstroUpdateComplete", command = "quitall" })
end
-- if the user wants to reload and sync packer
if options.auto_reload then
-- perform a reload
vim.opt.modifiable = true
astronvim.updater.reload(true) -- run quiet to not show notification on reload
vim.api.nvim_create_autocmd("User", {
once = true,
pattern = "AstroUpdatePackagesComplete",
callback = function() astronvim.event "UpdateComplete" end,
})
require "core.plugins"
astronvim.updater.update_packages()
-- if packer isn't available send successful update event
else
-- send user event of successful update
astronvim.event "UpdateComplete"
end
end
end

363
nvim.bk/lua/user/init.lua Normal file
View File

@ -0,0 +1,363 @@
-- AstroNvim Configuration Table
-- All configuration changes should go inside of the table below
-- You can think of a Lua "table" as a dictionary like data structure the
-- normal format is "key = value". These also handle array like data structures
-- where a value with no key simply has an implicit numeric key
local config = {
-- Configure AstroNvim updates
updater = {
remote = "origin", -- remote to use
channel = "nightly", -- "stable" or "nightly"
version = "latest", -- "latest", tag name, or regex search like "v1.*" to only do updates before v2 (STABLE ONLY)
branch = "main", -- branch name (NIGHTLY ONLY)
commit = nil, -- commit hash (NIGHTLY ONLY)
pin_plugins = nil, -- nil, true, false (nil will pin plugins on stable only)
skip_prompts = false, -- skip prompts about breaking changes
show_changelog = true, -- show the changelog after performing an update
auto_reload = false, -- automatically reload and sync packer after a successful update
auto_quit = false, -- automatically quit the current session after a successful update
-- remotes = { -- easily add new remotes to track
-- ["remote_name"] = "https://remote_url.come/repo.git", -- full remote url
-- ["remote2"] = "github_user/repo", -- GitHub user/repo shortcut,
-- ["remote3"] = "github_user", -- GitHub user assume AstroNvim fork
-- },
},
-- Set colorscheme to use
colorscheme = "default_theme",
-- Add highlight groups in any theme
highlights = {
-- init = { -- this table overrides highlights in all themes
-- Normal = { bg = "#000000" },
-- }
-- duskfox = { -- a table of overrides/changes to the duskfox theme
-- Normal = { bg = "#000000" },
-- },
},
-- set vim options here (vim.<first_key>.<second_key> = value)
options = {
opt = {
-- set to true or false etc.
relativenumber = true, -- sets vim.opt.relativenumber
number = true, -- sets vim.opt.number
spell = false, -- sets vim.opt.spell
signcolumn = "auto", -- sets vim.opt.signcolumn to auto
wrap = false, -- sets vim.opt.wrap
},
g = {
mapleader = " ", -- sets vim.g.mapleader
autoformat_enabled = true, -- enable or disable auto formatting at start (lsp.formatting.format_on_save must be enabled)
cmp_enabled = true, -- enable completion at start
autopairs_enabled = true, -- enable autopairs at start
diagnostics_enabled = true, -- enable diagnostics at start
status_diagnostics_enabled = true, -- enable diagnostics in statusline
icons_enabled = true, -- disable icons in the UI (disable if no nerd font is available, requires :PackerSync after changing)
ui_notifications_enabled = true, -- disable notifications when toggling UI elements
heirline_bufferline = false, -- enable new heirline based bufferline (requires :PackerSync after changing)
},
},
-- If you need more control, you can use the function()...end notation
-- options = function(local_vim)
-- local_vim.opt.relativenumber = true
-- local_vim.g.mapleader = " "
-- local_vim.opt.whichwrap = vim.opt.whichwrap - { 'b', 's' } -- removing option from list
-- local_vim.opt.shortmess = vim.opt.shortmess + { I = true } -- add to option list
--
-- return local_vim
-- end,
-- Set dashboard header
header = {
" █████ ███████ ████████ ██████ ██████",
"██ ██ ██ ██ ██ ██ ██ ██",
"███████ ███████ ██ ██████ ██ ██",
"██ ██ ██ ██ ██ ██ ██ ██",
"██ ██ ███████ ██ ██ ██ ██████",
" ",
" ███  ██ ██  ██ ██ ███  ███",
" ████  ██ ██  ██ ██ ████  ████",
" ██ ██  ██ ██  ██ ██ ██ ████ ██",
" ██  ██ ██  ██  ██  ██ ██  ██  ██",
" ██   ████   ████   ██ ██  ██",
},
-- Default theme configuration
default_theme = {
-- Modify the color palette for the default theme
colors = {
fg = "#abb2bf",
bg = "#1e222a",
},
highlights = function(hl) -- or a function that returns a new table of colors to set
local C = require "default_theme.colors"
hl.Normal = { fg = C.fg, bg = C.bg }
-- New approach instead of diagnostic_style
hl.DiagnosticError.italic = true
hl.DiagnosticHint.italic = true
hl.DiagnosticInfo.italic = true
hl.DiagnosticWarn.italic = true
return hl
end,
-- enable or disable highlighting for extra plugins
plugins = {
aerial = true,
beacon = false,
bufferline = true,
cmp = true,
dashboard = true,
highlighturl = true,
hop = false,
indent_blankline = true,
lightspeed = false,
["neo-tree"] = true,
notify = true,
["nvim-tree"] = false,
["nvim-web-devicons"] = true,
rainbow = true,
symbols_outline = false,
telescope = true,
treesitter = true,
vimwiki = false,
["which-key"] = true,
},
},
-- Diagnostics configuration (for vim.diagnostics.config({...})) when diagnostics are on
diagnostics = {
virtual_text = true,
underline = true,
},
-- Extend LSP configuration
lsp = {
-- enable servers that you already have installed without mason
servers = {
-- "pyright"
},
formatting = {
-- control auto formatting on save
format_on_save = {
enabled = true, -- enable or disable format on save globally
allow_filetypes = { -- enable format on save for specified filetypes only
-- "go",
},
ignore_filetypes = { -- disable format on save for specified filetypes
-- "python",
},
},
disabled = { -- disable formatting capabilities for the listed language servers
-- "sumneko_lua",
},
timeout_ms = 1000, -- default format timeout
-- filter = function(client) -- fully override the default formatting function
-- return true
-- end
},
-- easily add or disable built in mappings added during LSP attaching
mappings = {
n = {
-- ["<leader>lf"] = false -- disable formatting keymap
},
},
-- add to the global LSP on_attach function
-- on_attach = function(client, bufnr)
-- end,
-- override the mason server-registration function
-- server_registration = function(server, opts)
-- require("lspconfig")[server].setup(opts)
-- end,
-- Add overrides for LSP server settings, the keys are the name of the server
["server-settings"] = {
-- example for addings schemas to yamlls
-- yamlls = { -- override table for require("lspconfig").yamlls.setup({...})
-- settings = {
-- yaml = {
-- schemas = {
-- ["http://json.schemastore.org/github-workflow"] = ".github/workflows/*.{yml,yaml}",
-- ["http://json.schemastore.org/github-action"] = ".github/action.{yml,yaml}",
-- ["http://json.schemastore.org/ansible-stable-2.9"] = "roles/tasks/*.{yml,yaml}",
-- },
-- },
-- },
-- },
},
},
-- Mapping data with "desc" stored directly by vim.keymap.set().
--
-- Please use this mappings table to set keyboard mapping since this is the
-- lower level configuration and more robust one. (which-key will
-- automatically pick-up stored data by this setting.)
mappings = {
-- first key is the mode
n = {
-- second key is the lefthand side of the map
-- mappings seen under group name "Buffer"
["<leader>bb"] = { "<cmd>tabnew<cr>", desc = "New tab" },
["<leader>bc"] = { "<cmd>BufferLinePickClose<cr>", desc = "Pick to close" },
["<leader>bj"] = { "<cmd>BufferLinePick<cr>", desc = "Pick to jump" },
["<leader>bt"] = { "<cmd>BufferLineSortByTabs<cr>", desc = "Sort by tabs" },
-- quick save
-- ["<C-s>"] = { ":w!<cr>", desc = "Save File" }, -- change description but the same command
},
t = {
-- setting a mapping to false will disable it
-- ["<esc>"] = false,
},
},
-- Configure plugins
plugins = {
init = {
-- You can disable default plugins as follows:
-- ["goolord/alpha-nvim"] = { disable = true },
-- You can also add new plugins here as well:
-- Add plugins, the packer syntax without the "use"
-- { "andweeb/presence.nvim" },
-- {
-- "ray-x/lsp_signature.nvim",
-- event = "BufRead",
-- config = function()
-- require("lsp_signature").setup()
-- end,
-- },
-- We also support a key value style plugin definition similar to NvChad:
-- ["ray-x/lsp_signature.nvim"] = {
-- event = "BufRead",
-- config = function()
-- require("lsp_signature").setup()
-- end,
-- },
},
-- All other entries override the require("<key>").setup({...}) call for default plugins
["null-ls"] = function(config) -- overrides `require("null-ls").setup(config)`
-- config variable is the default configuration table for the setup function call
-- local null_ls = require "null-ls"
-- Check supported formatters and linters
-- https://github.com/jose-elias-alvarez/null-ls.nvim/tree/main/lua/null-ls/builtins/formatting
-- https://github.com/jose-elias-alvarez/null-ls.nvim/tree/main/lua/null-ls/builtins/diagnostics
config.sources = {
-- Set a formatter
-- null_ls.builtins.formatting.stylua,
-- null_ls.builtins.formatting.prettier,
}
return config -- return final config table
end,
treesitter = { -- overrides `require("treesitter").setup(...)`
-- ensure_installed = { "lua" },
},
-- use mason-lspconfig to configure LSP installations
["mason-lspconfig"] = { -- overrides `require("mason-lspconfig").setup(...)`
-- ensure_installed = { "sumneko_lua" },
},
-- use mason-null-ls to configure Formatters/Linter installation for null-ls sources
["mason-null-ls"] = { -- overrides `require("mason-null-ls").setup(...)`
-- ensure_installed = { "prettier", "stylua" },
},
["mason-nvim-dap"] = { -- overrides `require("mason-nvim-dap").setup(...)`
-- ensure_installed = { "python" },
},
},
-- LuaSnip Options
luasnip = {
-- Extend filetypes
filetype_extend = {
-- javascript = { "javascriptreact" },
},
-- Configure luasnip loaders (vscode, lua, and/or snipmate)
vscode = {
-- Add paths for including more VS Code style snippets in luasnip
paths = {},
},
},
-- CMP Source Priorities
-- modify here the priorities of default cmp sources
-- higher value == higher priority
-- The value can also be set to a boolean for disabling default sources:
-- false == disabled
-- true == 1000
cmp = {
source_priority = {
nvim_lsp = 1000,
luasnip = 750,
buffer = 500,
path = 250,
},
},
-- Customize Heirline options
heirline = {
-- -- Customize different separators between sections
-- separators = {
-- tab = { "", "" },
-- },
-- -- Customize colors for each element each element has a `_fg` and a `_bg`
-- colors = function(colors)
-- colors.git_branch_fg = astronvim.get_hlgroup "Conditional"
-- return colors
-- end,
-- -- Customize attributes of highlighting in Heirline components
-- attributes = {
-- -- styling choices for each heirline element, check possible attributes with `:h attr-list`
-- git_branch = { bold = true }, -- bold the git branch statusline component
-- },
-- -- Customize if icons should be highlighted
-- icon_highlights = {
-- breadcrumbs = false, -- LSP symbols in the breadcrumbs
-- file_icon = {
-- winbar = false, -- Filetype icon in the winbar inactive windows
-- statusline = true, -- Filetype icon in the statusline
-- },
-- },
},
-- Modify which-key registration (Use this with mappings table in the above.)
["which-key"] = {
-- Add bindings which show up as group name
register = {
-- first key is the mode, n == normal mode
n = {
-- second key is the prefix, <leader> prefixes
["<leader>"] = {
-- third key is the key to bring up next level and its displayed
-- group name in which-key top level menu
["b"] = { name = "Buffer" },
},
},
},
},
-- This function is run last and is a good place to configuring
-- augroups/autocommands and custom filetypes also this just pure lua so
-- anything that doesn't fit in the normal config locations above can go here
polish = function()
-- Set up custom filetypes
-- vim.filetype.add {
-- extension = {
-- foo = "fooscript",
-- },
-- filename = {
-- ["Foofile"] = "fooscript",
-- },
-- pattern = {
-- ["~/%.config/foo/.*"] = "fooscript",
-- },
-- }
end,
}
return config

View File

@ -0,0 +1,21 @@
FROM archlinux:base-devel
WORKDIR /setup
RUN pacman -Sy git neovim python --noconfirm
RUN useradd -m test
USER test
RUN git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
RUN mkdir -p /home/test/.cache/nvim/packer.nvim
RUN touch /home/test/.cache/nvim/packer.nvim/test_completion{,1,2,3}
USER test
RUN mkdir -p /home/test/.local/share/nvim/site/pack/packer/start/packer.nvim/
WORKDIR /home/test/.local/share/nvim/site/pack/packer/start/packer.nvim/
COPY . ./
USER root
RUN chmod 777 -R /home/test/.local/share/nvim/site/pack/packer/start/packer.nvim
RUN touch /home/test/.cache/nvim/packer.nvim/not_writeable
USER test
ENTRYPOINT make test

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Wil Thomason
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,6 @@
test:
nvim --headless --noplugin -u tests/minimal.vim -c "PlenaryBustedDirectory tests/ { minimal_init = './tests/minimal.vim' }"
run:
docker build . -t neovim-stable:latest && docker run --rm -it --entrypoint bash neovim-stable:latest
run-test:
docker build . -t neovim-stable:latest && docker run --rm neovim-stable:latest

View File

@ -0,0 +1,676 @@
# packer.nvim
[![Gitter](https://badges.gitter.im/packer-nvim/community.svg)](https://gitter.im/packer-nvim/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[`use-package`](https://github.com/jwiegley/use-package) inspired plugin/package management for
Neovim.
Have questions? Start a [discussion](https://github.com/wbthomason/packer.nvim/discussions).
Have a problem or idea? Make an [issue](https://github.com/wbthomason/packer.nvim/issues) or a [PR](https://github.com/wbthomason/packer.nvim/pulls).
**Packer is built on native packages. You may wish to read `:h packages` before continuing**
## Table of Contents
1. [Features](#features)
2. [Requirements](#requirements)
3. [Quickstart](#quickstart)
4. [Bootstrapping](#bootstrapping)
5. [Usage](#usage)
1. [The startup function](#the-startup-function)
2. [Custom Initialization](#custom-initialization)
3. [Specifying Plugins](#specifying-plugins)
4. [Performing plugin management operations](#performing-plugin-management-operations)
5. [Extending packer](#extending-packer)
6. [Compiling Lazy-Loaders](#compiling-lazy-loaders)
7. [User autocommands](#user-autocommands)
8. [Using a floating window](#using-a-floating-window)
6. [Profiling](#profiling)
7. [Debugging](#debugging)
8. [Compatibility and known issues](#compatibility-and-known-issues)
9. [Contributors](#contributors)
## Features
- Declarative plugin specification
- Support for dependencies
- Support for Luarocks dependencies
- Expressive configuration and lazy-loading options
- Automatically compiles efficient lazy-loading code to improve startup time
- Uses native packages
- Extensible
- Written in Lua, configured in Lua
- Post-install/update hooks
- Uses jobs for async installation
- Support for `git` tags, branches, revisions, submodules
- Support for local plugins
## Requirements
- You need to be running **Neovim v0.5.0+**
- If you are on Windows 10, you need developer mode enabled in order to use local plugins (creating
symbolic links requires admin privileges on Windows - credit to @TimUntersberger for this note)
## Quickstart
To get started, first clone this repository to somewhere on your `packpath`, e.g.:
> Unix, Linux Installation
```shell
git clone --depth 1 https://github.com/wbthomason/packer.nvim\
~/.local/share/nvim/site/pack/packer/start/packer.nvim
```
If you use Arch Linux, there is also [an AUR
package](https://aur.archlinux.org/packages/nvim-packer-git/).
> Windows Powershell Installation
```shell
git clone https://github.com/wbthomason/packer.nvim "$env:LOCALAPPDATA\nvim-data\site\pack\packer\start\packer.nvim"
```
Then you can write your plugin specification in Lua, e.g. (in `~/.config/nvim/lua/plugins.lua`):
```lua
-- This file can be loaded by calling `lua require('plugins')` from your init.vim
-- Only required if you have packer configured as `opt`
vim.cmd [[packadd packer.nvim]]
return require('packer').startup(function(use)
-- Packer can manage itself
use 'wbthomason/packer.nvim'
-- Simple plugins can be specified as strings
use 'rstacruz/vim-closer'
-- Lazy loading:
-- Load on specific commands
use {'tpope/vim-dispatch', opt = true, cmd = {'Dispatch', 'Make', 'Focus', 'Start'}}
-- Load on an autocommand event
use {'andymass/vim-matchup', event = 'VimEnter'}
-- Load on a combination of conditions: specific filetypes or commands
-- Also run code after load (see the "config" key)
use {
'w0rp/ale',
ft = {'sh', 'zsh', 'bash', 'c', 'cpp', 'cmake', 'html', 'markdown', 'racket', 'vim', 'tex'},
cmd = 'ALEEnable',
config = 'vim.cmd[[ALEEnable]]'
}
-- Plugins can have dependencies on other plugins
use {
'haorenW1025/completion-nvim',
opt = true,
requires = {{'hrsh7th/vim-vsnip', opt = true}, {'hrsh7th/vim-vsnip-integ', opt = true}}
}
-- Plugins can also depend on rocks from luarocks.org:
use {
'my/supercoolplugin',
rocks = {'lpeg', {'lua-cjson', version = '2.1.0'}}
}
-- You can specify rocks in isolation
use_rocks 'penlight'
use_rocks {'lua-resty-http', 'lpeg'}
-- Local plugins can be included
use '~/projects/personal/hover.nvim'
-- Plugins can have post-install/update hooks
use {'iamcco/markdown-preview.nvim', run = 'cd app && yarn install', cmd = 'MarkdownPreview'}
-- Post-install/update hook with neovim command
use { 'nvim-treesitter/nvim-treesitter', run = ':TSUpdate' }
-- Post-install/update hook with call of vimscript function with argument
use { 'glacambre/firenvim', run = function() vim.fn['firenvim#install'](0) end }
-- Use specific branch, dependency and run lua file after load
use {
'glepnir/galaxyline.nvim', branch = 'main', config = function() require'statusline' end,
requires = {'kyazdani42/nvim-web-devicons'}
}
-- Use dependency and run lua function after load
use {
'lewis6991/gitsigns.nvim', requires = { 'nvim-lua/plenary.nvim' },
config = function() require('gitsigns').setup() end
}
-- You can specify multiple plugins in a single call
use {'tjdevries/colorbuddy.vim', {'nvim-treesitter/nvim-treesitter', opt = true}}
-- You can alias plugin names
use {'dracula/vim', as = 'dracula'}
end)
```
Note that if you get linter complaints about `use` being an undefined global, these errors are
spurious - `packer` injects `use` into the scope of the function passed to `startup`.
If these errors bother you, the easiest fix is to simply specify `use` as an argument to the
function you pass to `startup`, e.g.
```lua
packer.startup(function(use)
...your config...
end)
```
`packer` provides the following commands after you've run and configured `packer` with `require('packer').startup(...)`:
```
-- You must run this or `PackerSync` whenever you make changes to your plugin configuration
-- Regenerate compiled loader file
:PackerCompile
-- Remove any disabled or unused plugins
:PackerClean
-- Clean, then install missing plugins
:PackerInstall
-- Clean, then update and install plugins
-- supports the `--preview` flag as an optional first argument to preview updates
:PackerUpdate
-- Perform `PackerUpdate` and then `PackerCompile`
-- supports the `--preview` flag as an optional first argument to preview updates
:PackerSync
-- Show list of installed plugins
:PackerStatus
-- Loads opt plugin immediately
:PackerLoad completion-nvim ale
```
You can configure Neovim to automatically run `:PackerCompile` whenever `plugins.lua` is updated with
[an autocommand](https://neovim.io/doc/user/autocmd.html#:autocmd):
```
augroup packer_user_config
autocmd!
autocmd BufWritePost plugins.lua source <afile> | PackerCompile
augroup end
```
This autocommand can be placed in your `init.vim`, or any other startup file as per your setup.
Placing this in `plugins.lua` could look like this:
```lua
vim.cmd([[
augroup packer_user_config
autocmd!
autocmd BufWritePost plugins.lua source <afile> | PackerCompile
augroup end
]])
```
## Bootstrapping
If you want to automatically install and set up `packer.nvim` on any machine you clone your configuration to,
add the following snippet (which is due to @Iron-E and @khuedoan) somewhere in your config **before** your first usage of `packer`:
```lua
local ensure_packer = function()
local fn = vim.fn
local install_path = fn.stdpath('data')..'/site/pack/packer/start/packer.nvim'
if fn.empty(fn.glob(install_path)) > 0 then
fn.system({'git', 'clone', '--depth', '1', 'https://github.com/wbthomason/packer.nvim', install_path})
vim.cmd [[packadd packer.nvim]]
return true
end
return false
end
local packer_bootstrap = ensure_packer()
return require('packer').startup(function(use)
use 'wbthomason/packer.nvim'
-- My plugins here
-- use 'foo1/bar1.nvim'
-- use 'foo2/bar2.nvim'
-- Automatically set up your configuration after cloning packer.nvim
-- Put this at the end after all plugins
if packer_bootstrap then
require('packer').sync()
end
end)
```
You can also use the following command (with `packer` bootstrapped) to have `packer` setup your
configuration (or simply run updates) and close once all operations are completed:
```sh
$ nvim --headless -c 'autocmd User PackerComplete quitall' -c 'PackerSync'
```
## Usage
The above snippets give some examples of `packer` features and use. Examples include:
- My dotfiles:
- [Specification file](https://github.com/wbthomason/dotfiles/blob/linux/neovim/.config/nvim/lua/plugins.lua)
- [Loading file](https://github.com/wbthomason/dotfiles/blob/linux/neovim/.config/nvim/lua/plugins.lua)
- [Generated lazy-loader file](https://github.com/wbthomason/dotfiles/blob/linux/neovim/.config/nvim/plugin/packer_compiled.lua)
- An example using the `startup` method: [tjdevries](https://github.com/tjdevries/config_manager/blob/master/xdg_config/nvim/lua/tj/plugins.lua)
- Using this method, you do not require a "loading" file. You can simply `lua require('plugins')` from your `init.vim`
The following is a more in-depth explanation of `packer`'s features and use.
### The `startup` function
`packer` provides `packer.startup(spec)`, which is used in the above examples.
`startup` is a convenience function for simple setup and can be invoked as follows:
- `spec` can be a function: `packer.startup(function() use 'tjdevries/colorbuddy.vim' end)`
- `spec` can be a table with a function as its first element and config overrides as another element:
`packer.startup({function() use 'tjdevries/colorbuddy.vim' end, config = { ... }})`
- `spec` can be a table with a table of plugin specifications as its first element, config overrides as another element, and optional rock specifications as another element:
`packer.startup({{'tjdevries/colorbuddy.vim'}, config = { ... }, rocks = { ... }})`
### Custom Initialization
You are not required to use `packer.startup` if you prefer a more manual setup with finer control
over configuration and loading.
To take this approach, load `packer` like any other Lua module. You must call `packer.init()` before
performing any operations; it is recommended to call `packer.reset()` if you may be re-running your
specification code (e.g. by sourcing your plugin specification file with `luafile`).
You may pass a table of configuration values to `packer.init()` to customize its operation. The
default configuration values (and structure of the configuration table) are:
```lua
{
ensure_dependencies = true, -- Should packer install plugin dependencies?
snapshot = nil, -- Name of the snapshot you would like to load at startup
snapshot_path = join_paths(stdpath 'cache', 'packer.nvim'), -- Default save directory for snapshots
package_root = util.join_paths(vim.fn.stdpath('data'), 'site', 'pack'),
compile_path = util.join_paths(vim.fn.stdpath('config'), 'plugin', 'packer_compiled.lua'),
plugin_package = 'packer', -- The default package for plugins
max_jobs = nil, -- Limit the number of simultaneous jobs. nil means no limit
auto_clean = true, -- During sync(), remove unused plugins
compile_on_sync = true, -- During sync(), run packer.compile()
disable_commands = false, -- Disable creating commands
opt_default = false, -- Default to using opt (as opposed to start) plugins
transitive_opt = true, -- Make dependencies of opt plugins also opt by default
transitive_disable = true, -- Automatically disable dependencies of disabled plugins
auto_reload_compiled = true, -- Automatically reload the compiled file after creating it.
preview_updates = false, -- If true, always preview updates before choosing which plugins to update, same as `PackerUpdate --preview`.
git = {
cmd = 'git', -- The base command for git operations
subcommands = { -- Format strings for git subcommands
update = 'pull --ff-only --progress --rebase=false',
install = 'clone --depth %i --no-single-branch --progress',
fetch = 'fetch --depth 999999 --progress',
checkout = 'checkout %s --',
update_branch = 'merge --ff-only @{u}',
current_branch = 'branch --show-current',
diff = 'log --color=never --pretty=format:FMT --no-show-signature HEAD@{1}...HEAD',
diff_fmt = '%%h %%s (%%cr)',
get_rev = 'rev-parse --short HEAD',
get_msg = 'log --color=never --pretty=format:FMT --no-show-signature HEAD -n 1',
submodules = 'submodule update --init --recursive --progress'
},
depth = 1, -- Git clone depth
clone_timeout = 60, -- Timeout, in seconds, for git clones
default_url_format = 'https://github.com/%s' -- Lua format string used for "aaa/bbb" style plugins
},
display = {
non_interactive = false, -- If true, disable display windows for all operations
compact = false, -- If true, fold updates results by default
open_fn = nil, -- An optional function to open a window for packer's display
open_cmd = '65vnew \\[packer\\]', -- An optional command to open a window for packer's display
working_sym = '⟳', -- The symbol for a plugin being installed/updated
error_sym = '✗', -- The symbol for a plugin with an error in installation/updating
done_sym = '✓', -- The symbol for a plugin which has completed installation/updating
removed_sym = '-', -- The symbol for an unused plugin which was removed
moved_sym = '→', -- The symbol for a plugin which was moved (e.g. from opt to start)
header_sym = '━', -- The symbol for the header line in packer's display
show_all_info = true, -- Should packer show all update details automatically?
prompt_border = 'double', -- Border style of prompt popups.
keybindings = { -- Keybindings for the display window
quit = 'q',
toggle_update = 'u', -- only in preview
continue = 'c', -- only in preview
toggle_info = '<CR>',
diff = 'd',
prompt_revert = 'r',
}
},
luarocks = {
python_cmd = 'python' -- Set the python command to use for running hererocks
},
log = { level = 'warn' }, -- The default print log level. One of: "trace", "debug", "info", "warn", "error", "fatal".
profile = {
enable = false,
threshold = 1, -- integer in milliseconds, plugins which load faster than this won't be shown in profile output
},
autoremove = false, -- Remove disabled or unused plugins without prompting the user
}
```
### Specifying plugins
`packer` is based around declarative specification of plugins. You can declare a plugin using the
function `packer.use`, which I highly recommend locally binding to `use` for conciseness.
`use` takes either a string or a table. If a string is provided, it is treated as a plugin location
for a non-optional plugin with no additional configuration. Plugin locations may be specified as
1. Absolute paths to a local plugin
2. Full URLs (treated as plugins managed with `git`)
3. `username/repo` paths (treated as Github `git` plugins)
A table given to `use` can take two forms:
1. A list of plugin specifications (strings or tables)
2. A table specifying a single plugin. It must have a plugin location string as its first element,
and may additionally have a number of optional keyword elements, shown below:
```lua
use {
'myusername/example', -- The plugin location string
-- The following keys are all optional
disable = boolean, -- Mark a plugin as inactive
as = string, -- Specifies an alias under which to install the plugin
installer = function, -- Specifies custom installer. See "custom installers" below.
updater = function, -- Specifies custom updater. See "custom installers" below.
after = string or list, -- Specifies plugins to load before this plugin. See "sequencing" below
rtp = string, -- Specifies a subdirectory of the plugin to add to runtimepath.
opt = boolean, -- Manually marks a plugin as optional.
bufread = boolean, -- Manually specifying if a plugin needs BufRead after being loaded
branch = string, -- Specifies a git branch to use
tag = string, -- Specifies a git tag to use. Supports '*' for "latest tag"
commit = string, -- Specifies a git commit to use
lock = boolean, -- Skip updating this plugin in updates/syncs. Still cleans.
run = string, function, or table, -- Post-update/install hook. See "update/install hooks".
requires = string or list, -- Specifies plugin dependencies. See "dependencies".
rocks = string or list, -- Specifies Luarocks dependencies for the plugin
config = string or function, -- Specifies code to run after this plugin is loaded.
-- The setup key implies opt = true
setup = string or function, -- Specifies code to run before this plugin is loaded. The code is ran even if
-- the plugin is waiting for other conditions (ft, cond...) to be met.
-- The following keys all imply lazy-loading and imply opt = true
cmd = string or list, -- Specifies commands which load this plugin. Can be an autocmd pattern.
ft = string or list, -- Specifies filetypes which load this plugin.
keys = string or list, -- Specifies maps which load this plugin. See "Keybindings".
event = string or list, -- Specifies autocommand events which load this plugin.
fn = string or list -- Specifies functions which load this plugin.
cond = string, function, or list of strings/functions, -- Specifies a conditional test to load this plugin
module = string or list -- Specifies Lua module names for require. When requiring a string which starts
-- with one of these module names, the plugin will be loaded.
module_pattern = string/list -- Specifies Lua pattern of Lua module names for require. When
-- requiring a string which matches one of these patterns, the plugin will be loaded.
}
```
For the `cmd` option, the command may be a full command, or an autocommand pattern. If the command contains any
non-alphanumeric characters, it is assumed to be a pattern, and instead of creating a stub command, it creates
a CmdUndefined autocmd to load the plugin when a command that matches the pattern is invoked.
#### Checking plugin statuses
You can check whether or not a particular plugin is installed with `packer` as well as if that plugin is loaded.
To do this you can check for the plugin's name in the `packer_plugins` global table.
Plugins in this table are saved using only the last section of their names
e.g. `tpope/vim-fugitive` if installed will be under the key `vim-fugitive`.
```lua
if packer_plugins["vim-fugitive"] and packer_plugins["vim-fugitive"].loaded then
print("Vim fugitive is loaded")
-- other custom logic
end
```
**NOTE:** this table is only available *after* `packer_compiled.vim` is loaded so cannot be used till *after* plugins
have been loaded.
#### Luarocks support
You may specify that a plugin requires one or more Luarocks packages using the `rocks` key. This key
takes either a string specifying the name of a package (e.g. `rocks=lpeg`), or a list specifying one or more packages.
Entries in the list may either be strings, a list of strings or a table --- the latter case is used to specify arguments such as the
particular version of a package.
all supported luarocks keys are allowed except: `tree` and `local`. Environment variables for the luarocks command can also be
specified using the `env` key which takes a table as the value as shown below.
```lua
rocks = {'lpeg', {'lua-cjson', version = '2.1.0'}}
use_rocks {'lua-cjson', 'lua-resty-http'}
use_rocks {'luaformatter', server = 'https://luarocks.org/dev'}
use_rocks {'openssl' env = {OPENSSL_DIR = "/path/to/dir"}}
```
Currently, `packer` only supports equality constraints on package versions.
`packer` also provides the function `packer.luarocks.install_commands()`, which creates the
`PackerRocks <cmd> <packages...>` command. `<cmd>` must be one of "install" or "remove";
`<packages...>` is one or more package names (currently, version restrictions are not supported with
this command). Running `PackerRocks` will install or remove the given packages. You can use this
command even if you don't use `packer` to manage your plugins. However, please note that (1)
packages installed through `PackerRocks` **will** be removed by calls to `packer.luarocks.clean()`
(unless they are also part of a `packer` plugin specification), and (2) you will need to manually
invoke `packer.luarocks.setup_paths` (or otherwise modify your `package.path`) to ensure that Neovim
can find the installed packages.
Finally, `packer` provides the function `packer.use_rocks`, which takes a string or table specifying
one or more Luarocks packages as in the `rocks` key. You can use this to ensure that `packer`
downloads and manages some rocks which you want to use, but which are not associated with any
particular plugin.
#### Custom installers
You may specify a custom installer & updater for a plugin using the `installer` and `updater` keys.
Note that either both or none of these keys are required. These keys should be functions which take
as an argument a `display` object (from `lua/packer/display.lua`) and return an async function (per
`lua/packer/async.lua`) which (respectively) installs/updates the given plugin.
Providing the `installer`/`updater` keys overrides plugin type detection, but you still need to
provide a location string for the name of the plugin.
#### Update/install hooks
You may specify operations to be run after successful installs/updates of a plugin with the `run`
key. This key may either be a Lua function, which will be called with the `plugin` table for this
plugin (containing the information passed to `use` as well as output from the installation/update
commands, the installation path of the plugin, etc.), a string, or a table of functions and strings.
If an element of `run` is a string, then either:
1. If the first character of `run` is ":", it is treated as a Neovim command and executed.
2. Otherwise, `run` is treated as a shell command and run in the installation directory of the
plugin via `$SHELL -c '<run>'`.
#### Dependencies
Plugins may specify dependencies via the `requires` key. This key can be a string or a list (table).
If `requires` is a string, it is treated as specifying a single plugin. If a plugin with the name
given in `requires` is already known in the managed set, nothing happens. Otherwise, the string is
treated as a plugin location string and the corresponding plugin is added to the managed set.
If `requires` is a list, it is treated as a list of plugin specifications following the format given
above.
If `ensure_dependencies` is true, the plugins specified in `requires` will be installed.
Plugins specified in `requires` are removed when no active plugins require them.
#### Sequencing
You may specify a loading order for plugins using the `after` key. This key can be a string or a
list (table).
If `after` is a string, it must be the name of another plugin managed by `packer` (e.g. the final segment of a plugin's path - for a Github plugin `FooBar/Baz`, the name would be just `Baz`). If `after` is a table, it must be a list of plugin names. If a plugin has an alias (i.e. uses the `as` key), this alias is its name.
The set of plugins specified in a plugin's `after` key must **all** be loaded before the plugin
using `after` will be loaded. For example, in the specification
```lua
use {'FooBar/Baz', ft = 'bax'}
use {'Something/Else', after = 'Baz'}
```
the plugin `Else` will only be loaded after the plugin `Baz`, which itself is only loaded for files
with `bax` filetype.
#### Keybindings
Plugins may be lazy-loaded on the use of keybindings/maps. Individual keybindings are specified either as a string (in which case they are treated as normal mode maps) or a table in the format `{mode, map}`.
### Performing plugin management operations
`packer` exposes the following functions for common plugin management operations. In all of the
below, `plugins` is an optional table of plugin names; if not provided, the default is "all managed
plugins":
- `packer.install(plugins)`: Install the specified plugins if they are not already installed
- `packer.update(plugins)`: Update the specified plugins, installing any that are missing
- `packer.update(opts, plugins)`: First argument can be a table specifying options, such as `{preview_updates = true}` to preview potential changes before updating (same as `PackerUpdate --preview`).
- `packer.clean()`: Remove any disabled or no longer managed plugins
- `packer.sync(plugins)`: Perform a `clean` followed by an `update`.
- `packer.sync(opts, plugins)`: Can take same optional options as `update`.
- `packer.compile(path)`: Compile lazy-loader code and save to `path`.
- `packer.snapshot(snapshot_name, ...)`: Creates a snapshot file that will live under `config.snapshot_path/<snapshot_name>`. If `snapshot_name` is an absolute path, then that will be the location where the snapshot will be taken. Optionally, a list of plugins name can be provided to selectively choose the plugins to snapshot.
- `packer.rollback(snapshot_name, ...)`: Rollback plugins status a snapshot file that will live under `config.snapshot_path/<snapshot_name>`. If `snapshot_name` is an absolute path, then that will be the location where the snapshot will be taken. Optionally, a list of plugins name can be provided to selectively choose which plugins to revert.
- `packer.delete(snapshot_name)`: Deletes a snapshot file under `config.snapshot_path/<snapshot_name>`. If `snapshot_name` is an absolute path, then that will be the location where the snapshot will be deleted.
### Extending `packer`
You can add custom key handlers to `packer` by calling `packer.set_handler(name, func)` where `name`
is the key you wish to handle and `func` is a function with the signature `func(plugins, plugin,
value)` where `plugins` is the global table of managed plugins, `plugin` is the table for a specific
plugin, and `value` is the value associated with key `name` in `plugin`.
### Compiling Lazy-Loaders
To optimize startup time, `packer.nvim` compiles code to perform the lazy-loading operations you
specify. This means that you do not need to load `packer.nvim` unless you want to perform some
plugin management operations.
To generate the compiled code, call `packer.compile(path)`, where `path` is some file path on your
`runtimepath`, with a `.vim` extension. This will generate a blend of Lua and Vimscript to load and
configure all your lazy-loaded plugins (e.g. generating commands, autocommands, etc.) and save it to
`path`. Then, when you start vim, the file at `path` is loaded (because `path` must be on your
`runtimepath`), and lazy-loading works.
If `path` is not provided to `packer.compile`, the output file will default to the value of
`config.compile_path`.
The option `compile_on_sync`, which defaults to `true`, will run `packer.compile()` during
`packer.sync()`, if set to `true`. Note that otherwise, you **must** run `packer.compile` yourself
to generate the lazy-loader file!
**NOTE:** If you use a function value for `config` or `setup` keys in any plugin specifications, it
**must not** have any upvalues (i.e. captures). We currently use Lua's `string.dump` to compile
config/setup functions to bytecode, which has this limitation.
Additionally, if functions are given for these keys, the functions will be passed the plugin
name and information table as arguments.
### User autocommands
`packer` runs most of its operations asyncronously. If you would like to implement automations that
require knowing when the operations are complete, you can use the following `User` autocmds (see
`:help User` for more info on how to use):
- `PackerComplete`: Fires after install, update, clean, and sync asynchronous operations finish.
- `PackerCompileDone`: Fires after compiling (see [the section on compilation](#compiling-lazy-loaders))
### Using a floating window
You can configure Packer to use a floating window for command outputs by passing a utility
function to `packer`'s config:
```lua
packer.startup({function()
-- Your plugins here
end,
config = {
display = {
open_fn = require('packer.util').float,
}
}})
```
By default, this floating window will show doubled borders. If you want to customize the window
appearance, you can pass a configuration to `float`, which is the same configuration that would be
passed to `nvim_open_win`:
```lua
packer.startup({function()
-- Your plugins here
end,
config = {
display = {
open_fn = function()
return require('packer.util').float({ border = 'single' })
end
}
}})
```
## Profiling
Packer has built in functionality that can allow you to profile the time taken loading your plugins.
In order to use this functionality you must either enable profiling in your config, or pass in an argument
when running packer compile.
#### Setup via config
```lua
config = {
profile = {
enable = true,
threshold = 1 -- the amount in ms that a plugin's load time must be over for it to be included in the profile
}
}
```
#### Using the packer compile command
```vim
:PackerCompile profile=true
" or
:PackerCompile profile=false
```
#### Profiling usage
This will rebuild your `packer_compiled.vim` with profiling code included. In order to visualise the output of the profile
restart your neovim and run `PackerProfile`. This will open a window with the output of your profiling.
## Debugging
`packer.nvim` logs to `stdpath(cache)/packer.nvim.log`. Looking at this file is usually a good start
if something isn't working as expected.
## Compatibility and known issues
- **2021-07-31:** If you're on macOS, note that building Neovim with the version of `luv` from `homebrew` [will cause any `packer` command to crash](https://github.com/wbthomason/packer.nvim/issues/496#issuecomment-890371022). More about this issue at [neovim/neovim#15054](https://github.com/neovim/neovim/issues/15054).
- **2021-07-28:** `packer` will now highlight commits/plugin names with potentially breaking changes
(determined by looking for `breaking change` or `breaking_change`, case insensitive, in the update
commit bodies and headers) as `WarningMsg` in the status window.
- **2021-06-06**: Your Neovim must include https://github.com/neovim/neovim/pull/14659; `packer` uses the `noautocmd` key.
- **2021-04-19**: `packer` now provides built-in profiling for your config via the `packer_compiled`
file. Take a look at [the docs](#profiling) for more information!
- **2021-02-18**: Having trouble with Luarocks on macOS? See [this issue](https://github.com/wbthomason/packer.nvim/issues/180).
- **2021-01-19**: Basic Luarocks support has landed! Use the `rocks` key with a string or table to specify packages to install.
- **2020-12-10**: The `disable_commands` configuration flag now affects non-`startup` use as well. This means that, by default, `packer` will create commands for basic operations for you.
- **2020-11-13**: There is now a default implementation for a floating window `open_fn` in `packer.util`.
- **2020-09-04:** Due to changes to the Neovim `extmark` api (see: https://github.com/neovim/neovim/commit/3853276d9cacc99a2698117e904475dbf7033383), users will need to update to a version of Neovim **after** the aforementioned PR was merged. There are currently shims around the changed functions which should maintain support for earlier versions of Neovim, but these are intended to be temporary and will be removed by **2020-10-04**. Therefore Packer will not work with Neovim v0.4.4, which was released before the `extmark` change.
## Contributors
Many thanks to those who have contributed to the project! PRs and issues are always welcome. This
list is infrequently updated; please feel free to bug me if you're not listed here and you would
like to be.
- [@akinsho](https://github.com/akinsho)
- [@nanotee](https://github.com/nanotee)
- [@weilbith](https://github.com/weilbith)
- [@Iron-E](https://github.com/Iron-E)
- [@tjdevries](https://github.com/tjdevries)
- [@numToStr](https://github.com/numToStr)
- [@fsouza](https://github.com/fsouza)
- [@gbrlsnchs](https://github.com/gbrlsnchs)
- [@lewis6991](https://github.com/lewis6991)
- [@TimUntersberger](https://github.com/TimUntersberger)
- [@bfredl](https://github.com/bfredl)
- [@sunjon](https://github.com/sunjon)
- [@gwerbin](https://github.com/gwerbin)
- [@shadmansaleh](https://github.com/shadmansaleh)
- [@ur4ltz](https://github.com/ur4ltz)
- [@EdenEast](https://github.com/EdenEast)
- [@khuedoan](https://github.com/khuedoan)
- [@kevinhwang91](https://github.com/kevinhwang91)
- [@runiq](https://github.com/runiq)
- [@n3wborn](https://github.com/n3wborn)
- [@deathlyfrantic](https://github.com/deathlyfrantic)
- [@doctoromer](https://github.com/doctoromer)
- [@elianiva](https://github.com/elianiva)
- [@dundargoc](https://github.com/dundargoc)
- [@jdelkins](https://github.com/jdelkins)
- [@dsully](https://github.com/dsully)

View File

@ -0,0 +1,618 @@
*packer.txt* A use-package inspired Neovim plugin manager
*packer.nvim*
Author: Wil Thomason <wil.thomason@gmail.com>
CONTENTS *packer-contents*
Introduction |packer-introduction|
Features |packer-intro-features|
Requirements |packer-intro-requirements|
Quickstart |packer-intro-quickstart|
Usage |packer-usage|
API |packer-api|
==============================================================================
INTRODUCTION *packer-introduction*
This is a Neovim plugin manager. It is written in Lua, uses the native
|packages| feature, and has features for declarative plugin configuration
inspired by the `use-package` library from Emacs.
==============================================================================
REQUIREMENTS *packer-intro-requirements*
- You need to be running Neovim v0.5.0+; `packer` makes use of extmarks and
other newly-added Neovim features.
- Your version of Neovim must be compiled with LuaJIT support; `packer` relies
on this to detect whether you are running Windows or a Unix-like OS (for path
separators)
- If you are on Windows 10, you need developer mode enabled in order to use
local plugins (creating symbolic links requires admin privileges on Windows
- credit to @TimUntersberger for this note)
==============================================================================
FEATURES *packer-intro-features*
- Declarative plugin specification
- Support for dependencies
- Support for Luarocks dependencies
- Expressive configuration and lazy-loading options
- Automatically compiles efficient lazy-loading code to improve startup time
- Uses native packages
- Extensible
- Written in Lua, configured in Lua
- Post-install/update hooks
- Uses jobs for async installation
- Support for `git` tags, branches, revisions, submodules
- Support for local plugins
- Support for saving/restoring snapshots for plugin versions (`git` only)
==============================================================================
QUICKSTART *packer-intro-quickstart*
To get started, first clone this repository to somewhere on your `packpath`, e.g.: >sh
git clone https://github.com/wbthomason/packer.nvim\
~/.local/share/nvim/site/pack/packer/opt/packer.nvim
Then you can write your plugin specification in Lua, e.g. (in `~/.config/nvim/lua/plugins.lua`): >lua
-- This file can be loaded by calling `lua require('plugins')` from your init.vim
-- Only required if you have packer in your `opt` pack
vim.cmd [[packadd packer.nvim]]
-- Only if your version of Neovim doesn't have https://github.com/neovim/neovim/pull/12632 merged
vim._update_package_paths()
return require('packer').startup(function()
-- Packer can manage itself as an optional plugin
use {'wbthomason/packer.nvim', opt = true}
-- Simple plugins can be specified as strings
use '9mm/vim-closer'
-- Lazy loading:
-- Load on specific commands
use {'tpope/vim-dispatch', opt = true, cmd = {'Dispatch', 'Make', 'Focus', 'Start'}}
-- Load on an autocommand event
use {'andymass/vim-matchup', event = 'VimEnter *'}
-- Load on a combination of conditions: specific filetypes or commands
-- Also run code after load (see the "config" key)
use {
'w0rp/ale',
ft = {'sh', 'zsh', 'bash', 'c', 'cpp', 'cmake', 'html', 'markdown', 'racket', 'vim', 'tex'},
cmd = 'ALEEnable',
config = 'vim.cmd[[ALEEnable]]'
}
-- Plugins can have dependencies on other plugins
use {
'haorenW1025/completion-nvim',
opt = true,
requires = {{'hrsh7th/vim-vsnip', opt = true}, {'hrsh7th/vim-vsnip-integ', opt = true}}
}
-- Local plugins can be included
use '~/projects/personal/hover.nvim'
-- Plugins can have post-install/update hooks
use {'iamcco/markdown-preview.nvim', run = 'cd app && yarn install', cmd = 'MarkdownPreview'}
-- You can specify multiple plugins in a single call
use {'tjdevries/colorbuddy.vim', {'nvim-treesitter/nvim-treesitter', opt = true}}
-- You can alias plugin names
use {'dracula/vim', as = 'dracula'}
end)
`packer` provides the following commands after you've run and configured `packer` with `require('packer').startup(...)`: *packer-default-commands* *packer-commands*
`PackerClean` *packer-commands-clean*
Remove any disabled or unused plugins.
`PackerCompile` *packer-commands-compile*
You must run this or `PackerSync` whenever you make changes to your plugin
configuration. Regenerate compiled loader file.
`PackerInstall` *packer-commands-install*
Clean, then install missing plugins.
`PackerUpdate` *packer-commands-update*
Clean, then update and install plugins.
Supports the `--preview` flag as an optional first argument to preview
updates.
`PackerSync` *packer-commands-sync*
Perform `PackerUpdate` and then `PackerCompile`.
Supports the `--preview` flag as an optional first argument to preview
updates.
`PackerLoad` *packer-commands-load*
Loads opt plugin immediately
`PackerSnapshot` *packer-commands-snapshot*
Snapshots your plugins to a file
`PackerSnapshotDelete` *packer-commands-delete*
Deletes a snapshot
`PackerSnapshotRollback` *packer-commands-rollback*
Rolls back plugins' commit specified by the snapshot
==============================================================================
USAGE *packer-usage*
Although the example in |packer-intro-quickstart| will be enough to get you
going for basic usage, `packer` has a number of other features and options
detailed in this section.
STARTUP *packer-startup*
The easiest way to use `packer` is via the |packer.startup()| function. In
short, `startup` is a convenience function for simple setup, and is invoked as
`packer.startup(spec)`, where:
- `spec` can be a function: >lua
packer.startup(function() use 'tjdevries/colorbuddy.vim' end)
- `spec` can be a table with a function as its first element and config
overrides as another element: >lua
packer.startup({
function() use 'tjdevries/colorbuddy.vim' end, config = { ... }
})
- `spec` can be a table with a table of plugin specifications as its first
element, config overrides as another element, and optional rock
specifications as another element: >lua
packer.startup({{'tjdevries/colorbuddy.vim'}, config = { ... }, rocks = { ... }})
See |packer-configuration| for the allowed configuration keys.
`startup` will handle calling |packer.init()| and |packer.reset()| for you, as
well as creating the commands given in |packer-commands|.
CUSTOM INITIALIZATION *packer-custom-initialization*
If you prefer a more manual setup with finer control over configuration and
loading, you may use custom initialization for `packer`. This approach has the
benefit of not requiring that the `packer` plugin be loaded unless you want to
perform plugin management operations, but it is more involved to use.
To take this approach, load `packer` like any other Lua module. You must call
|packer.init()| before performing any operations; it is recommended to call
|packer.reset()| if you may be re-running your specification code (e.g. by
sourcing your plugin specification file with `luafile`).
See |packer.init()| for more details on initialization; in short, `init` takes
a table of configuration values like that which can be passed to `startup`.
If you use custom initialization, you'll probably want to define commands to
load `packer` and perform common package management operations. The following
commands work well for this purpose: >vim
command! -nargs=* -complete=customlist,v:lua.require'packer'.plugin_complete PackerInstall lua require('packer').install(<f-args>)
command! -nargs=* -complete=customlist,v:lua.require'packer'.plugin_complete PackerUpdate lua require('packer').update(<f-args>)
command! -nargs=* -complete=customlist,v:lua.require'packer'.plugin_complete PackerSync lua require('packer').sync(<f-args>)
command! PackerClean packadd packer.nvim | lua require('plugins').clean()
command! PackerCompile packadd packer.nvim | lua require('plugins').compile('~/.config/nvim/plugin/packer_load.vim')
command! -bang -nargs=+ -complete=customlist,v:lua.require'packer'.loader_complete PackerLoad lua require('packer').loader(<f-args>, '<bang>')
CONFIGURATION *packer-configuration*
`packer` provides the following configuration variables, presented in the
structure of the `config` table expected by `startup` or `init`, with their
default values: >lua
{
ensure_dependencies = true, -- Should packer install plugin dependencies?
package_root = util.join_paths(vim.fn.stdpath('data'), 'site', 'pack'),
compile_path = util.join_paths(vim.fn.stdpath('config'), 'plugin', 'packer_compiled.lua'),
plugin_package = 'packer', -- The default package for plugins
max_jobs = nil, -- Limit the number of simultaneous jobs. nil means no limit
auto_clean = true, -- During sync(), remove unused plugins
compile_on_sync = true, -- During sync(), run packer.compile()
disable_commands = false, -- Disable creating commands
opt_default = false, -- Default to using opt (as opposed to start) plugins
transitive_opt = true, -- Make dependencies of opt plugins also opt by default
transitive_disable = true, -- Automatically disable dependencies of disabled plugins
auto_reload_compiled = true, -- Automatically reload the compiled file after creating it.
preview_updates = false, -- If true, always preview updates before choosing which plugins to update, same as `PackerUpdate --preview`.
git = {
cmd = 'git', -- The base command for git operations
subcommands = { -- Format strings for git subcommands
update = 'pull --ff-only --progress --rebase=false',
install = 'clone --depth %i --no-single-branch --progress',
fetch = 'fetch --depth 999999 --progress',
checkout = 'checkout %s --',
update_branch = 'merge --ff-only @{u}',
current_branch = 'branch --show-current',
diff = 'log --color=never --pretty=format:FMT --no-show-signature HEAD@{1}...HEAD',
diff_fmt = '%%h %%s (%%cr)',
get_rev = 'rev-parse --short HEAD',
get_msg = 'log --color=never --pretty=format:FMT --no-show-signature HEAD -n 1',
submodules = 'submodule update --init --recursive --progress'
},
depth = 1, -- Git clone depth
clone_timeout = 60, -- Timeout, in seconds, for git clones
default_url_format = 'https://github.com/%s' -- Lua format string used for "aaa/bbb" style plugins
},
log = { level = 'warn' }, -- The default print log level. One of: "trace", "debug", "info", "warn", "error", "fatal".
display = {
non_interactive = false, -- If true, disable display windows for all operations
compact = false, -- If true, fold updates results by default
open_fn = nil, -- An optional function to open a window for packer's display
open_cmd = '65vnew \\[packer\\]', -- An optional command to open a window for packer's display
working_sym = '⟳', -- The symbol for a plugin being installed/updated
error_sym = '✗', -- The symbol for a plugin with an error in installation/updating
done_sym = '✓', -- The symbol for a plugin which has completed installation/updating
removed_sym = '-', -- The symbol for an unused plugin which was removed
moved_sym = '→', -- The symbol for a plugin which was moved (e.g. from opt to start)
header_sym = '━', -- The symbol for the header line in packer's display
show_all_info = true, -- Should packer show all update details automatically?
prompt_border = 'double', -- Border style of prompt popups.
keybindings = { -- Keybindings for the display window
quit = 'q',
toggle_update = 'u', -- only in preview
continue = 'c', -- only in preview
toggle_info = '<CR>',
diff = 'd',
prompt_revert = 'r',
}
}
autoremove = false, -- Remove disabled or unused plugins without prompting the user
}
SPECIFYING PLUGINS *packer-specifying-plugins*
`packer` is based around declarative specification of plugins. You can declare
a plugin using the function |packer.use()|, which I highly recommend locally
binding to `use` for conciseness.
`use` takes either a string or a table. If a string is provided, it is treated
as a plugin location for a non-optional plugin with no additional
configuration. Plugin locations may be specified as:
1. Absolute paths to a local plugin
2. Full URLs (treated as plugins managed with `git`)
3. `username/repo` paths (treated as Github `git` plugins)
A table given to `use` can take two forms:
1. A list of plugin specifications (strings or tables)
2. A table specifying a single plugin. It must have a plugin location string
as its first element, and may additionally have a number of optional keyword
elements, detailed in |packer.use()|
CONFIGURING PLUGINS *packer-plugin-configuration*
`packer` allows you to configure plugins either before they are loaded (the
`setup` key described in |packer.use()|) or after they are loaded (the
`config` key described in |packer.use()|).
If functions are given for these keys, the functions will be passed the plugin
name and information table as arguments.
PLUGIN STATUSES *packer-plugin-status*
You can check whether or not a particular plugin is installed with `packer` as
well as if that plugin is loaded. To do this you can check for the plugin's
name in the `packer_plugins` global table. Plugins in this table are saved
using only the last section of their names e.g. `tpope/vim-fugitive` if
installed will be under the key `vim-fugitive`.
>lua
if packer_plugins["vim-fugitive"] and packer_plugins["vim-fugitive"].loaded then
print("Vim fugitive is loaded")
-- other custom logic
end
CUSTOM INSTALLERS *packer-custom-installers*
You may specify a custom installer & updater for a plugin using the
`installer` and `updater` keys in a plugin specification. Note that either
both or none of these keys are required. These keys should be functions which
take as an argument a `display` object (from `lua/packer/display.lua`) and
return an async function (per `lua/packer/async.lua`) which (respectively)
installs/updates the given plugin.
Providing the `installer`/`updater` keys overrides plugin type detection, but
you still need to provide a location string for the name of the plugin.
POST-UPDATE HOOKS *packer-plugin-hooks*
You may specify operations to be run after successful installs/updates of a
plugin with the `run` key. This key may either be a Lua function, which will be
called with the `plugin` table for this plugin (containing the information
passed to `use` as well as output from the installation/update commands, the
installation path of the plugin, etc.), a string, or a table of functions and
strings.
If an element of `run` is a string, then either:
1. If the first character of `run` is ":", it is treated as a Neovim command and
executed.
2. Otherwise, `run` is treated as a shell command and run in the installation
directory of the plugin via `$SHELL -c '<run>'`.
DEPENDENCIES *packer-plugin-dependencies*
Plugins may specify dependencies via the `requires` key in their specification
table. This key can be a string or a list (table).
If `requires` is a string, it is treated as specifying a single plugin. If a
plugin with the name given in `requires` is already known in the managed set,
nothing happens. Otherwise, the string is treated as a plugin location string
and the corresponding plugin is added to the managed set.
If `requires` is a list, it is treated as a list of plugin specifications
following the format given above.
If `ensure_dependencies` is true, the plugins specified in `requires` will be
installed.
Plugins specified in `requires` are removed when no active plugins require
them.
LUAROCKS *packer-plugin-luarocks*
You may specify that a plugin requires one or more Luarocks packages using the
`rocks` key. This key takes either a string specifying the name of a package
(e.g. `rocks=lpeg`), or a list specifying one or more packages. Entries in the
list may either be strings or lists --- the latter case is used to specify a
particular version of a package, e.g. `rocks = {'lpeg', {'lua-cjson',
'2.1.0'}}`.
Currently, `packer` only supports equality constraints on package versions.
`packer` also provides the function `packer.luarocks.install_commands()`, which
creates the `PackerRocks <cmd> <packages...>` command. `<cmd>` must be one of
"install" or "remove"; `<packages...>` is one or more package names (currently,
version restrictions are not supported with this command). Running `PackerRocks`
will install or remove the given packages. You can use this command even if you
don't use `packer` to manage your plugins. However, please note that (1)
packages installed through `PackerRocks` **will** be removed by calls to
`packer.luarocks.clean()` (unless they are also part of a `packer` plugin
specification), and (2) you will need to manually invoke
`packer.luarocks.setup_paths` (or otherwise modify your `package.path`) to
ensure that Neovim can find the installed packages.
Finally, `packer` provides the function `packer.use_rocks`, which takes a string
or table specifying one or more Luarocks packages as in the `rocks` key. You can
use this to ensure that `packer` downloads and manages some rocks which you want
to use, but which are not associated with any particular plugin.
SEQUENCING *packer-plugin-sequencing*
You may specify a loading order for plugins using the `after` key. This key can
be a string or a list (table).
If `after` is a string, it must be the name of another plugin managed by
`packer` (e.g. the final segment of a plugin's path - for a Github plugin
`FooBar/Baz`, the name would be just `Baz`). If `after` is a table, it must be a
list of plugin names. If a plugin has an alias (i.e. uses the `as` key), this
alias is its name.
The set of plugins specified in a plugin's `after` key must *all* be loaded
before the plugin using `after` will be loaded. For example, in the
specification >lua
use {'FooBar/Baz', ft = 'bax'}
use {'Something/Else', after = 'Baz'}
the plugin `Else` will only be loaded after the plugin `Baz`, which itself is
only loaded for files with `bax` filetype.
KEYBINDINGS *packer-plugin-keybindings*
Plugins may be lazy-loaded on the use of keybindings/maps. Individual
keybindings are specified under the `keys` key in a plugin specification
either as a string (in which case they are treated as normal mode maps) or a
table in the format `{mode, map}`.
LAZY-LOADING *packer-lazy-load*
To optimize startup time, `packer.nvim` compiles code to perform the
lazy-loading operations you specify. This means that you do not need to load
`packer.nvim` unless you want to perform some plugin management operations.
To generate the compiled code, call `packer.compile(path)`, where `path` is
some file path on your `runtimepath`, with a `.vim` extension. This will
generate a blend of Lua and Vimscript to load and configure all your
lazy-loaded plugins (e.g. generating commands, autocommands, etc.) and save it
to `path`. Then, when you start vim, the file at `path` is loaded (because
`path` must be on your `runtimepath`), and lazy-loading works.
If `path` is not provided to |packer.compile()|, the output file will default
to the value of `config.compile_path`.
The option `compile_on_sync`, which defaults to `true`, will run
`packer.compile()` during `packer.sync()`, if set to `true`.
Note that otherwise, you **must** run `packer.compile` yourself to generate
the lazy-loader file!
USING A FLOATING WINDOW *packer-floating-window*
You can configure Packer to use a floating window for command outputs by
passing a utility function to `packer`'s config: >lua
packer.startup({function()
-- Your plugins here
end,
config = {
display = {
open_fn = require('packer.util').float,
}
}})
<
By default, this floating window will show doubled borders. If you want to
customize the window appearance, you can pass a configuration to `float`,
which is the same configuration that would be passed to |nvim_open_win|: >lua
packer.startup({function()
-- Your plugins here
end,
config = {
display = {
open_fn = function()
return require('packer.util').float({ border = 'single' })
end
}
}})
<
PROFILING PLUGINS *packer-profiling*
You can measure how long it takes your plugins to load using packer's builtin
profiling functionality.
In order to use this functionality you must either enable profiling in your config, or pass in an argument
when running packer compile.
Setup via config >lua
config = {
profile = {
enable = true,
threshold = 1 -- the amount in ms that a plugin's load time must be over for it to be included in the profile
}
}
<
Using the packer compile command
>vim
:PackerCompile profile=true
" or
:PackerCompile profile=false
<
NOTE you can also set a `threshold` in your profile config which is a number
in `ms` above which plugin load times will be show e.g. if you set a threshold
value of `3` then any plugin that loads slower than `3ms` will not be included in
the output window.
This will rebuild your `packer_compiled.vim` with profiling code included. In order to visualise the output of the profile
Restart your neovim and run `PackerProfile`. This will open a window with the output of your profiling.
EXTENDING PACKER *packer-extending*
You can add custom key handlers to `packer` by calling
`packer.set_handler(name, func)` where `name` is the key you wish to handle
and `func` is a function with the signature `func(plugins, plugin, value)`
where `plugins` is the global table of managed plugins, `plugin` is the table
for a specific plugin, and `value` is the value associated with key `name` in
`plugin`.
RESULTS WINDOW KEYBINDINGS *packer-results-keybindings*
Once an operation completes, the results are shown in the display window.
`packer` sets up default keybindings for this window:
q close the display window
<CR> toggle information about a particular plugin
r revert an update
They can be configured by changing the value of `config.display.keybindings`
(see |packer-configuration|). Setting it to `false` will disable all keybindings.
Setting any of its keys to `false` will disable the corresponding keybinding.
USER AUTOCMDS *packer-user-autocmds*
`packer` runs most of its operations asyncronously. If you would like to
implement automations that require knowing when the operations are complete,
you can use the following User autocmds (see |User| for more info on how to
use):
`PackerComplete` Fires after install, update, clean, and sync
asynchronous operations finish.
`PackerCompileDone` Fires after compiling (see |packer-lazy-load|)
==============================================================================
API *packer-api*
clean() *packer.clean()*
`clean` scans for and removes all disabled or no longer managed plugins. It is
invoked without arguments.
compile() *packer.compile()*
`compile` builds lazy-loader code from your plugin specification and saves it
to either `config.compile_path` if it is invoked with no argument, or to the
path it is invoked with if it is given a single argument. This path should end
in `.vim` and be on your |runtimepath| in order for lazy-loading to work. You
**must** call `compile` to update lazy-loaders after your configuration
changes.
init() *packer.init()*
Initializes `packer`; must be called before any calls to any other `packer`
function. Takes an optional table of configuration values as described in
|packer-configuration|.
install() *packer.install()*
`install` installs any missing plugins, runs post-update hooks, and updates
rplugins (|remote-plugin|) and helptags.
It can be invoked with no arguments or with a list of plugin names to install.
These plugin names must already be managed by `packer` via a call to
|packer.use()|.
reset() *packer.reset()*
`reset` empties the set of managed plugins. Called with no arguments; used to
ensure plugin specifications are reinitialized if the specification file is
reloaded. Called by |packer.startup()| or manually before calling
|packer.use()|.
set_handler() *packer.set_handler()*
`set_handler` allows custom extension of `packer`. See |packer-extending| for
details.
startup() *packer.startup()*
`startup` is a convenience function for simple setup. See |packer-startup| for
details.
sync() *packer.sync()*
`sync` runs |packer.clean()| followed by |packer.update()|.
Supports options as the first argument, see |packer.update()|.
update() *packer.update()*
`update` installs any missing plugins, updates all installed plugins, runs
post-update hooks, and updates rplugins (|remote-plugin|) and helptags.
It can be invoked with no arguments or with a list of plugin names to update.
These plugin names must already be managed by `packer` via a call to
|packer.use()|.
Additionally, the first argument can be a table specifying options,
such as `update({preview_updates = true}, ...)` to preview potential changes before updating
(same as `PackerUpdate --preview`).
snapshot(snapshot_name, ...) *packer.snapshot()*
`snapshot` takes the rev of all the installed plugins and serializes them into a Lua table which will be saved under `config.snapshot_path` (which is the directory that will hold all the snapshots files) as `config.snapshot_path/<snapshot_name>` or an absolute path provided by the users.
Optionally plugins name can be specified so that only those plugins will be
snapshotted.
Snapshot files can be loaded manually via `dofile` which will return a table with the plugins name as keys the commit short hash as value.
delete(snapshot_name) *packer.delete()*
`delete` deletes a snapshot given the name or the absolute path.
rollback(snapshot_name, ...) *packer.rollback()*
`rollback` reverts all plugins or only the specified as extra arguments to the commit specified in the snapshot file
use() *packer.use()*
`use` allows you to add one or more plugins to the managed set. It can be
invoked as follows:
- With a single plugin location string, e.g. `use <STRING>`
- With a single plugin specification table, e.g. >lua
use {
'myusername/example', -- The plugin location string
-- The following keys are all optional
disable = boolean, -- Mark a plugin as inactive
as = string, -- Specifies an alias under which to install the plugin
installer = function, -- Specifies custom installer. See |packer-custom-installers|
updater = function, -- Specifies custom updater. See |packer-custom-installers|
after = string or list, -- Specifies plugins to load before this plugin.
rtp = string, -- Specifies a subdirectory of the plugin to add to runtimepath.
opt = boolean, -- Manually marks a plugin as optional.
bufread = boolean, -- Manually specifying if a plugin needs BufRead after being loaded
branch = string, -- Specifies a git branch to use
tag = string, -- Specifies a git tag to use. Supports '*' for "latest tag"
commit = string, -- Specifies a git commit to use
lock = boolean, -- Skip updating this plugin in updates/syncs. Still cleans.
run = string, function, or table -- Post-update/install hook. See |packer-plugin-hooks|
requires = string or list -- Specifies plugin dependencies. See |packer-plugin-dependencies|
config = string or function, -- Specifies code to run after this plugin is loaded.
rocks = string or list, -- Specifies Luarocks dependencies for the plugin
-- The following keys all imply lazy-loading
cmd = string or list, -- Specifies commands which load this plugin. Can be an autocmd pattern.
ft = string or list, -- Specifies filetypes which load this plugin.
keys = string or list, -- Specifies maps which load this plugin. See |packer-plugin-keybindings|
event = string or list, -- Specifies autocommand events which load this plugin.
fn = string or list -- Specifies functions which load this plugin.
cond = string, function, or list of strings/functions, -- Specifies a conditional test to load this plugin
setup = string or function, -- Specifies code to run before this plugin is loaded. The code is ran even if
-- the plugin is waiting for other conditions (ft, cond...) to be met.
module = string or list -- Specifies Lua module names for require. When requiring a string which starts
-- with one of these module names, the plugin will be loaded.
module_pattern = string/list -- Specifies Lua pattern of Lua module names for require. When requiring a string
-- which matches one of these patterns, the plugin will be loaded.
}
- With a list of plugins specified in either of the above two forms
For the *cmd* option, the command may be a full command, or an autocommand pattern. If the command contains any
non-alphanumeric characters, it is assumed to be a pattern, and instead of creating a stub command, it creates
a CmdUndefined autocmd to load the plugin when a command that matches the pattern is invoked.
vim:tw=78:ts=2:ft=help:norl:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,132 @@
-- Adapted from https://ms-jpq.github.io/neovim-async-tutorial/
local log = require 'packer.log'
local yield = coroutine.yield
local resume = coroutine.resume
local thread_create = coroutine.create
local function EMPTY_CALLBACK() end
local function step(func, callback)
local thread = thread_create(func)
local tick = nil
tick = function(...)
local ok, val = resume(thread, ...)
if ok then
if type(val) == 'function' then
val(tick)
else
(callback or EMPTY_CALLBACK)(val)
end
else
log.error('Error in coroutine: ' .. val);
(callback or EMPTY_CALLBACK)(nil)
end
end
tick()
end
local function wrap(func)
return function(...)
local params = { ... }
return function(tick)
params[#params + 1] = tick
return func(unpack(params))
end
end
end
local function join(...)
local thunks = { ... }
local thunk_all = function(s)
if #thunks == 0 then
return s()
end
local to_go = #thunks
local results = {}
for i, thunk in ipairs(thunks) do
local callback = function(...)
results[i] = { ... }
if to_go == 1 then
s(unpack(results))
else
to_go = to_go - 1
end
end
thunk(callback)
end
end
return thunk_all
end
local function wait_all(...)
return yield(join(...))
end
local function pool(n, interrupt_check, ...)
local thunks = { ... }
return function(s)
if #thunks == 0 then
return s()
end
local remaining = { select(n + 1, unpack(thunks)) }
local results = {}
local to_go = #thunks
local make_callback = nil
make_callback = function(idx, left)
local i = (left == nil) and idx or (idx + left)
return function(...)
results[i] = { ... }
to_go = to_go - 1
if to_go == 0 then
s(unpack(results))
elseif not interrupt_check or not interrupt_check() then
if remaining and #remaining > 0 then
local next_task = table.remove(remaining)
next_task(make_callback(n, #remaining + 1))
end
end
end
end
for i = 1, math.min(n, #thunks) do
local thunk = thunks[i]
thunk(make_callback(i))
end
end
end
local function wait_pool(limit, ...)
return yield(pool(limit, false, ...))
end
local function interruptible_wait_pool(limit, interrupt_check, ...)
return yield(pool(limit, interrupt_check, ...))
end
local function main(f)
vim.schedule(f)
end
local M = {
--- Wrapper for functions that do not take a callback to make async functions
sync = wrap(step),
--- Alias for yielding to await the result of an async function
wait = yield,
--- Await the completion of a full set of async functions
wait_all = wait_all,
--- Await the completion of a full set of async functions, with a limit on how many functions can
-- run simultaneously
wait_pool = wait_pool,
--- Like wait_pool, but additionally checks at every function completion to see if a condition is
-- met indicating that it should keep running the remaining tasks
interruptible_wait_pool = interruptible_wait_pool,
--- Wrapper for functions that do take a callback to make async functions
wrap = wrap,
--- Convenience function to ensure a function runs on the main "thread" (i.e. for functions which
-- use Neovim functions, etc.)
main = main,
}
return M

View File

@ -0,0 +1,92 @@
local plugin_utils = require 'packer.plugin_utils'
local a = require 'packer.async'
local display = require 'packer.display'
local log = require 'packer.log'
local util = require 'packer.util'
local await = a.wait
local async = a.sync
local config
local PLUGIN_OPTIONAL_LIST = 1
local PLUGIN_START_LIST = 2
local function is_dirty(plugin, typ)
return (plugin.opt and typ == PLUGIN_START_LIST) or (not plugin.opt and typ == PLUGIN_OPTIONAL_LIST)
end
-- Find and remove any plugins not currently configured for use
local clean_plugins = function(_, plugins, fs_state, results)
return async(function()
log.debug 'Starting clean'
local dirty_plugins = {}
results = results or {}
results.removals = results.removals or {}
local opt_plugins = vim.deepcopy(fs_state.opt)
local start_plugins = vim.deepcopy(fs_state.start)
local missing_plugins = fs_state.missing
-- test for dirty / 'missing' plugins
for _, plugin_config in pairs(plugins) do
local path = plugin_config.install_path
local plugin_source = nil
if opt_plugins[path] then
plugin_source = PLUGIN_OPTIONAL_LIST
opt_plugins[path] = nil
elseif start_plugins[path] then
plugin_source = PLUGIN_START_LIST
start_plugins[path] = nil
end
-- We don't want to report paths which don't exist for removal; that will confuse people
local path_exists = false
if missing_plugins[plugin_config.short_name] or plugin_config.disable then
path_exists = vim.loop.fs_stat(path) ~= nil
end
local plugin_missing = path_exists and missing_plugins[plugin_config.short_name]
local disabled_but_installed = path_exists and plugin_config.disable
if plugin_missing or is_dirty(plugin_config, plugin_source) or disabled_but_installed then
dirty_plugins[#dirty_plugins + 1] = path
end
end
-- Any path which was not set to `nil` above will be set to dirty here
local function mark_remaining_as_dirty(plugin_list)
for path, _ in pairs(plugin_list) do
dirty_plugins[#dirty_plugins + 1] = path
end
end
mark_remaining_as_dirty(opt_plugins)
mark_remaining_as_dirty(start_plugins)
if next(dirty_plugins) then
local lines = {}
for _, path in ipairs(dirty_plugins) do
table.insert(lines, ' - ' .. path)
end
await(a.main)
if config.autoremove or await(display.ask_user('Removing the following directories. OK? (y/N)', lines)) then
results.removals = dirty_plugins
log.debug('Removed ' .. vim.inspect(dirty_plugins))
for _, path in ipairs(dirty_plugins) do
local result = vim.fn.delete(path, 'rf')
if result == -1 then
log.warn('Could not remove ' .. path)
end
end
else
log.warn 'Cleaning cancelled!'
end
else
log.info 'Already clean!'
end
end)
end
local function cfg(_config)
config = _config
end
local clean = setmetatable({ cfg = cfg }, { __call = clean_plugins })
return clean

View File

@ -0,0 +1,824 @@
-- Compiling plugin specifications to Lua for lazy-loading
local util = require 'packer.util'
local log = require 'packer.log'
local fmt = string.format
local luarocks = require 'packer.luarocks'
local config
local function cfg(_config)
config = _config.profile
end
local feature_guard = [[
if !has('nvim-0.5')
echohl WarningMsg
echom "Invalid Neovim version for packer.nvim!"
echohl None
finish
endif
packadd packer.nvim
try
]]
local feature_guard_lua = [[
if vim.api.nvim_call_function('has', {'nvim-0.5'}) ~= 1 then
vim.api.nvim_command('echohl WarningMsg | echom "Invalid Neovim version for packer.nvim! | echohl None"')
return
end
vim.api.nvim_command('packadd packer.nvim')
local no_errors, error_msg = pcall(function()
]]
local enter_packer_compile = [[
_G._packer = _G._packer or {}
_G._packer.inside_compile = true
]]
local exit_packer_compile = [[
_G._packer.inside_compile = false
if _G._packer.needs_bufread == true then
vim.cmd("doautocmd BufRead")
end
_G._packer.needs_bufread = false
]]
local catch_errors = [[
catch
echohl ErrorMsg
echom "Error in packer_compiled: " .. v:exception
echom "Please check your config for correctness"
echohl None
endtry
]]
local catch_errors_lua = [[
end)
if not no_errors then
error_msg = error_msg:gsub('"', '\\"')
vim.api.nvim_command('echohl ErrorMsg | echom "Error in packer_compiled: '..error_msg..'" | echom "Please check your config for correctness" | echohl None')
end
]]
---@param should_profile boolean
---@return string
local profile_time = function(should_profile)
return fmt(
[[
local time
local profile_info
local should_profile = %s
if should_profile then
local hrtime = vim.loop.hrtime
profile_info = {}
time = function(chunk, start)
if start then
profile_info[chunk] = hrtime()
else
profile_info[chunk] = (hrtime() - profile_info[chunk]) / 1e6
end
end
else
time = function(chunk, start) end
end
]],
vim.inspect(should_profile)
)
end
local profile_output = [[
local function save_profiles(threshold)
local sorted_times = {}
for chunk_name, time_taken in pairs(profile_info) do
sorted_times[#sorted_times + 1] = {chunk_name, time_taken}
end
table.sort(sorted_times, function(a, b) return a[2] > b[2] end)
local results = {}
for i, elem in ipairs(sorted_times) do
if not threshold or threshold and elem[2] > threshold then
results[i] = elem[1] .. ' took ' .. elem[2] .. 'ms'
end
end
if threshold then
table.insert(results, '(Only showing plugins that took longer than ' .. threshold .. ' ms ' .. 'to load)')
end
_G._packer.profile_output = results
end
]]
---@param threshold number
---@return string
local conditionally_output_profile = function(threshold)
if threshold then
return fmt(
[[
if should_profile then save_profiles(%d) end
]],
threshold
)
else
return [[
if should_profile then save_profiles() end
]]
end
end
local try_loadstring = [[
local function try_loadstring(s, component, name)
local success, result = pcall(loadstring(s), name, _G.packer_plugins[name])
if not success then
vim.schedule(function()
vim.api.nvim_notify('packer.nvim: Error running ' .. component .. ' for ' .. name .. ': ' .. result, vim.log.levels.ERROR, {})
end)
end
return result
end
]]
local module_loader = [[
local lazy_load_called = {['packer.load'] = true}
local function lazy_load_module(module_name)
local to_load = {}
if lazy_load_called[module_name] then return nil end
lazy_load_called[module_name] = true
for module_pat, plugin_name in pairs(module_lazy_loads) do
if not _G.packer_plugins[plugin_name].loaded and string.match(module_name, module_pat) then
to_load[#to_load + 1] = plugin_name
end
end
if #to_load > 0 then
require('packer.load')(to_load, {module = module_name}, _G.packer_plugins)
local loaded_mod = package.loaded[module_name]
if loaded_mod then
return function(modname) return loaded_mod end
end
end
end
if not vim.g.packer_custom_loader_enabled then
table.insert(package.loaders, 1, lazy_load_module)
vim.g.packer_custom_loader_enabled = true
end
]]
local function timed_chunk(chunk, name, output_table)
output_table = output_table or {}
output_table[#output_table + 1] = 'time([[' .. name .. ']], true)'
if type(chunk) == 'string' then
output_table[#output_table + 1] = chunk
else
vim.list_extend(output_table, chunk)
end
output_table[#output_table + 1] = 'time([[' .. name .. ']], false)'
return output_table
end
local function dump_loaders(loaders)
local result = vim.deepcopy(loaders)
for k, _ in pairs(result) do
if result[k].only_setup or result[k].only_sequence then
result[k].loaded = true
end
result[k].only_setup = nil
result[k].only_sequence = nil
end
return vim.inspect(result)
end
local function make_try_loadstring(item, chunk, name)
local bytecode = string.dump(item, true)
local executable_string = 'try_loadstring(' .. vim.inspect(bytecode) .. ', "' .. chunk .. '", "' .. name .. '")'
return executable_string, bytecode
end
local after_plugin_pattern = table.concat({ 'after', 'plugin', [[**/*.\(vim\|lua\)]] }, util.get_separator())
local function detect_after_plugin(name, plugin_path)
local path = plugin_path .. util.get_separator() .. after_plugin_pattern
local glob_ok, files = pcall(vim.fn.glob, path, false, true)
if not glob_ok then
if string.find(files, 'E77') then
return { path }
else
log.error('Error compiling ' .. name .. ': ' .. vim.inspect(files))
error(files)
end
elseif #files > 0 then
return files
end
return nil
end
local ftdetect_patterns = {
table.concat({ 'ftdetect', [[**/*.\(vim\|lua\)]] }, util.get_separator()),
table.concat({ 'after', 'ftdetect', [[**/*.\(vim\|lua\)]] }, util.get_separator()),
}
local function detect_ftdetect(name, plugin_path)
local paths = {
plugin_path .. util.get_separator() .. ftdetect_patterns[1],
plugin_path .. util.get_separator() .. ftdetect_patterns[2],
}
local source_paths = {}
for i = 1, 2 do
local path = paths[i]
local glob_ok, files = pcall(vim.fn.glob, path, false, true)
if not glob_ok then
if string.find(files, 'E77') then
source_paths[#source_paths + 1] = path
else
log.error('Error compiling ' .. name .. ': ' .. vim.inspect(files))
error(files)
end
elseif #files > 0 then
vim.list_extend(source_paths, files)
end
end
return source_paths
end
local source_dirs = { 'ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin' }
local function detect_bufread(plugin_path)
local path = plugin_path
for i = 1, 4 do
if #vim.fn.finddir(source_dirs[i], path) > 0 then
return true
end
end
return false
end
local function make_loaders(_, plugins, output_lua, should_profile)
local loaders = {}
local configs = {}
local rtps = {}
local setup = {}
local fts = {}
local events = {}
local condition_ids = {}
local commands = {}
local keymaps = {}
local after = {}
local fns = {}
local ftdetect_paths = {}
local module_lazy_loads = {}
for name, plugin in pairs(plugins) do
if not plugin.disable then
plugin.simple_load = true
local quote_name = "'" .. name .. "'"
if plugin.config and not plugin.executable_config then
plugin.simple_load = false
plugin.executable_config = {}
if type(plugin.config) ~= 'table' then
plugin.config = { plugin.config }
end
for i, config_item in ipairs(plugin.config) do
local executable_string = config_item
if type(config_item) == 'function' then
local bytecode
executable_string, bytecode = make_try_loadstring(config_item, 'config', name)
plugin.config[i] = bytecode
end
table.insert(plugin.executable_config, executable_string)
end
end
local path = plugin.install_path
if plugin.rtp then
path = util.join_paths(plugin.install_path, plugin.rtp)
table.insert(rtps, path)
end
loaders[name] = {
loaded = not plugin.opt,
config = plugin.config,
path = path,
only_sequence = plugin.manual_opt == nil,
only_setup = false,
}
if plugin.opt then
plugin.simple_load = false
loaders[name].after_files = detect_after_plugin(name, loaders[name].path)
if plugin.bufread ~= nil then
loaders[name].needs_bufread = plugin.bufread
else
loaders[name].needs_bufread = detect_bufread(loaders[name].path)
end
end
if plugin.setup then
plugin.simple_load = false
if type(plugin.setup) ~= 'table' then
plugin.setup = { plugin.setup }
end
for i, setup_item in ipairs(plugin.setup) do
if type(setup_item) == 'function' then
plugin.setup[i], _ = make_try_loadstring(setup_item, 'setup', name)
end
end
loaders[name].only_setup = plugin.manual_opt == nil
setup[name] = plugin.setup
end
-- Keep this as first opt loader to maintain only_cond ?
if plugin.cond ~= nil then
plugin.simple_load = false
loaders[name].only_sequence = false
loaders[name].only_setup = false
loaders[name].only_cond = true
if type(plugin.cond) ~= 'table' then
plugin.cond = { plugin.cond }
end
for _, condition in ipairs(plugin.cond) do
loaders[name].cond = {}
if type(condition) == 'function' then
_, condition = make_try_loadstring(condition, 'condition', name)
elseif type(condition) == 'string' then
condition = 'return ' .. condition
end
condition_ids[condition] = condition_ids[condition] or {}
table.insert(loaders[name].cond, condition)
table.insert(condition_ids[condition], name)
end
end
-- Add the git URL for displaying in PackerStatus and PackerSync. https://github.com/wbthomason/packer.nvim/issues/542
loaders[name].url = plugin.url
if plugin.ft then
plugin.simple_load = false
loaders[name].only_sequence = false
loaders[name].only_setup = false
loaders[name].only_cond = false
vim.list_extend(ftdetect_paths, detect_ftdetect(name, loaders[name].path))
if type(plugin.ft) == 'string' then
plugin.ft = { plugin.ft }
end
for _, ft in ipairs(plugin.ft) do
fts[ft] = fts[ft] or {}
table.insert(fts[ft], quote_name)
end
end
if plugin.event then
plugin.simple_load = false
loaders[name].only_sequence = false
loaders[name].only_setup = false
loaders[name].only_cond = false
if type(plugin.event) == 'string' then
if not plugin.event:find '%s' then
plugin.event = { plugin.event .. ' *' }
else
plugin.event = { plugin.event }
end
end
for _, event in ipairs(plugin.event) do
if event:sub(#event, -1) ~= '*' and not event:find '%s' then
event = event .. ' *'
end
events[event] = events[event] or {}
table.insert(events[event], quote_name)
end
end
if plugin.cmd then
plugin.simple_load = false
loaders[name].only_sequence = false
loaders[name].only_setup = false
loaders[name].only_cond = false
if type(plugin.cmd) == 'string' then
plugin.cmd = { plugin.cmd }
end
loaders[name].commands = {}
for _, command in ipairs(plugin.cmd) do
commands[command] = commands[command] or {}
table.insert(loaders[name].commands, command)
table.insert(commands[command], quote_name)
end
end
if plugin.keys then
plugin.simple_load = false
loaders[name].only_sequence = false
loaders[name].only_setup = false
loaders[name].only_cond = false
if type(plugin.keys) == 'string' then
plugin.keys = { plugin.keys }
end
loaders[name].keys = {}
for _, keymap in ipairs(plugin.keys) do
if type(keymap) == 'string' then
keymap = { '', keymap }
end
keymaps[keymap] = keymaps[keymap] or {}
table.insert(loaders[name].keys, keymap)
table.insert(keymaps[keymap], quote_name)
end
end
if plugin.after then
plugin.simple_load = false
loaders[name].only_setup = false
if type(plugin.after) == 'string' then
plugin.after = { plugin.after }
end
for _, other_plugin in ipairs(plugin.after) do
after[other_plugin] = after[other_plugin] or {}
table.insert(after[other_plugin], name)
end
end
if plugin.wants then
plugin.simple_load = false
if type(plugin.wants) == 'string' then
plugin.wants = { plugin.wants }
end
loaders[name].wants = plugin.wants
end
if plugin.fn then
plugin.simple_load = false
loaders[name].only_sequence = false
loaders[name].only_setup = false
if type(plugin.fn) == 'string' then
plugin.fn = { plugin.fn }
end
for _, fn in ipairs(plugin.fn) do
fns[fn] = fns[fn] or {}
table.insert(fns[fn], quote_name)
end
end
if plugin.module or plugin.module_pattern then
plugin.simple_load = false
loaders[name].only_sequence = false
loaders[name].only_setup = false
loaders[name].only_cond = false
if plugin.module then
if type(plugin.module) == 'string' then
plugin.module = { plugin.module }
end
for _, module_name in ipairs(plugin.module) do
module_lazy_loads['^' .. vim.pesc(module_name)] = name
end
else
if type(plugin.module_pattern) == 'string' then
plugin.module_pattern = { plugin.module_pattern }
end
for _, module_pattern in ipairs(plugin.module_pattern) do
module_lazy_loads[module_pattern] = name
end
end
end
if plugin.config and (not plugin.opt or loaders[name].only_setup) then
plugin.simple_load = false
plugin.only_config = true
configs[name] = plugin.executable_config
end
end
end
local ft_aucmds = {}
for ft, names in pairs(fts) do
table.insert(
ft_aucmds,
fmt(
'vim.cmd [[au FileType %s ++once lua require("packer.load")({%s}, { ft = "%s" }, _G.packer_plugins)]]',
ft,
table.concat(names, ', '),
ft
)
)
end
local event_aucmds = {}
for event, names in pairs(events) do
table.insert(
event_aucmds,
fmt(
'vim.cmd [[au %s ++once lua require("packer.load")({%s}, { event = "%s" }, _G.packer_plugins)]]',
event,
table.concat(names, ', '),
event:gsub([[\]], [[\\]])
)
)
end
local config_lines = {}
for name, plugin_config in pairs(configs) do
local lines = { '-- Config for: ' .. name }
timed_chunk(plugin_config, 'Config for ' .. name, lines)
vim.list_extend(config_lines, lines)
end
local rtp_line = ''
for _, rtp in ipairs(rtps) do
rtp_line = rtp_line .. ' .. ",' .. vim.fn.escape(rtp, '\\,') .. '"'
end
if rtp_line ~= '' then
rtp_line = 'vim.o.runtimepath = vim.o.runtimepath' .. rtp_line
end
local setup_lines = {}
for name, plugin_setup in pairs(setup) do
local lines = { '-- Setup for: ' .. name }
timed_chunk(plugin_setup, 'Setup for ' .. name, lines)
if loaders[name].only_setup then
timed_chunk('vim.cmd [[packadd ' .. name .. ']]', 'packadd for ' .. name, lines)
end
vim.list_extend(setup_lines, lines)
end
local conditionals = {}
for _, names in pairs(condition_ids) do
for _, name in ipairs(names) do
if loaders[name].only_cond then
timed_chunk(
fmt(' require("packer.load")({"%s"}, {}, _G.packer_plugins)', name),
'Conditional loading of ' .. name,
conditionals
)
end
end
end
local command_defs = {}
for command, names in pairs(commands) do
local command_line
if string.match(command, '^%w+$') then
-- Better command completions here are due to @folke and @lewis6991
command_line = fmt(
[[pcall(vim.api.nvim_create_user_command, '%s', function(cmdargs)
require('packer.load')({%s}, { cmd = '%s', l1 = cmdargs.line1, l2 = cmdargs.line2, bang = cmdargs.bang, args = cmdargs.args, mods = cmdargs.mods }, _G.packer_plugins)
end,
{nargs = '*', range = true, bang = true, complete = function()
require('packer.load')({%s}, { cmd = '%s' }, _G.packer_plugins)
return vim.fn.getcompletion('%s ', 'cmdline')
end})]],
command,
table.concat(names, ', '),
command,
table.concat(names, ', '),
command,
command
)
else
command_line = fmt(
'pcall(vim.cmd, [[au CmdUndefined %s ++once lua require"packer.load"({%s}, {}, _G.packer_plugins)]])',
command,
table.concat(names, ', ')
)
end
command_defs[#command_defs + 1] = command_line
end
local keymap_defs = {}
for keymap, names in pairs(keymaps) do
local prefix = nil
if keymap[1] ~= 'i' then
prefix = ''
end
local escaped_map_lt = string.gsub(keymap[2], '<', '<lt>')
local escaped_map = string.gsub(escaped_map_lt, '([\\"])', '\\%1')
local keymap_line = fmt(
'vim.cmd [[%snoremap <silent> %s <cmd>lua require("packer.load")({%s}, { keys = "%s"%s }, _G.packer_plugins)<cr>]]',
keymap[1],
keymap[2],
table.concat(names, ', '),
escaped_map,
prefix == nil and '' or (', prefix = "' .. prefix .. '"')
)
table.insert(keymap_defs, keymap_line)
end
local sequence_loads = {}
for pre, posts in pairs(after) do
if plugins[pre] == nil then
error(string.format('Dependency %s for %s not found', pre, vim.inspect(posts)))
end
if plugins[pre].opt then
loaders[pre].after = posts
elseif plugins[pre].only_config then
loaders[pre].after = posts
loaders[pre].only_sequence = true
loaders[pre].only_config = true
end
if plugins[pre].simple_load or plugins[pre].opt or plugins[pre].only_config then
for _, name in ipairs(posts) do
loaders[name].load_after = {}
sequence_loads[name] = sequence_loads[name] or {}
table.insert(sequence_loads[name], pre)
end
end
end
local fn_aucmds = {}
for fn, names in pairs(fns) do
table.insert(
fn_aucmds,
fmt(
'vim.cmd[[au FuncUndefined %s ++once lua require("packer.load")({%s}, {}, _G.packer_plugins)]]',
fn,
table.concat(names, ', ')
)
)
end
local sequence_lines = {}
local graph = {}
for name, precedents in pairs(sequence_loads) do
graph[name] = graph[name] or { in_links = {}, out_links = {} }
for _, pre in ipairs(precedents) do
graph[pre] = graph[pre] or { in_links = {}, out_links = {} }
graph[name].in_links[pre] = true
table.insert(graph[pre].out_links, name)
end
end
local frontier = {}
for name, links in pairs(graph) do
if next(links.in_links) == nil then
table.insert(frontier, name)
end
end
while next(frontier) ~= nil do
local plugin = table.remove(frontier)
if loaders[plugin].only_sequence and not (loaders[plugin].only_setup or loaders[plugin].only_config) then
table.insert(sequence_lines, 'vim.cmd [[ packadd ' .. plugin .. ' ]]')
if plugins[plugin].config then
local lines = { '', '-- Config for: ' .. plugin }
vim.list_extend(lines, plugins[plugin].executable_config)
table.insert(lines, '')
vim.list_extend(sequence_lines, lines)
end
end
for _, name in ipairs(graph[plugin].out_links) do
if not loaders[plugin].only_sequence then
loaders[name].only_sequence = false
loaders[name].load_after[plugin] = true
end
graph[name].in_links[plugin] = nil
if next(graph[name].in_links) == nil then
table.insert(frontier, name)
end
end
graph[plugin] = nil
end
if next(graph) then
log.warn 'Cycle detected in sequenced loads! Load order may be incorrect'
-- TODO: This should actually just output the cycle, then continue with toposort. But I'm too
-- lazy to do that right now, so.
for plugin, _ in pairs(graph) do
table.insert(sequence_lines, 'vim.cmd [[ packadd ' .. plugin .. ' ]]')
if plugins[plugin].config then
local lines = { '-- Config for: ' .. plugin }
vim.list_extend(lines, plugins[plugin].config)
vim.list_extend(sequence_lines, lines)
end
end
end
-- Output everything:
-- First, the Lua code
local result = { (output_lua and '--' or '"') .. ' Automatically generated packer.nvim plugin loader code\n' }
if output_lua then
table.insert(result, feature_guard_lua)
else
table.insert(result, feature_guard)
table.insert(result, 'lua << END')
end
table.insert(result, enter_packer_compile)
table.insert(result, profile_time(should_profile))
table.insert(result, profile_output)
timed_chunk(luarocks.generate_path_setup(), 'Luarocks path setup', result)
timed_chunk(try_loadstring, 'try_loadstring definition', result)
timed_chunk(fmt('_G.packer_plugins = %s\n', dump_loaders(loaders)), 'Defining packer_plugins', result)
-- Then the runtimepath line
if rtp_line ~= '' then
table.insert(result, '-- Runtimepath customization')
timed_chunk(rtp_line, 'Runtimepath customization', result)
end
-- Then the module lazy loads
if next(module_lazy_loads) then
table.insert(result, 'local module_lazy_loads = ' .. vim.inspect(module_lazy_loads))
table.insert(result, module_loader)
end
-- Then setups, configs, and conditionals
if next(setup_lines) then
vim.list_extend(result, setup_lines)
end
if next(config_lines) then
vim.list_extend(result, config_lines)
end
if next(conditionals) then
table.insert(result, '-- Conditional loads')
vim.list_extend(result, conditionals)
end
-- The sequenced loads
if next(sequence_lines) then
table.insert(result, '-- Load plugins in order defined by `after`')
timed_chunk(sequence_lines, 'Sequenced loading', result)
end
-- The command and keymap definitions
if next(command_defs) then
table.insert(result, '\n-- Command lazy-loads')
timed_chunk(command_defs, 'Defining lazy-load commands', result)
table.insert(result, '')
end
if next(keymap_defs) then
table.insert(result, '-- Keymap lazy-loads')
timed_chunk(keymap_defs, 'Defining lazy-load keymaps', result)
table.insert(result, '')
end
-- The filetype, event and function autocommands
local some_ft = next(ft_aucmds) ~= nil
local some_event = next(event_aucmds) ~= nil
local some_fn = next(fn_aucmds) ~= nil
if some_ft or some_event or some_fn then
table.insert(result, 'vim.cmd [[augroup packer_load_aucmds]]\nvim.cmd [[au!]]')
end
if some_ft then
table.insert(result, ' -- Filetype lazy-loads')
timed_chunk(ft_aucmds, 'Defining lazy-load filetype autocommands', result)
end
if some_event then
table.insert(result, ' -- Event lazy-loads')
timed_chunk(event_aucmds, 'Defining lazy-load event autocommands', result)
end
if some_fn then
table.insert(result, ' -- Function lazy-loads')
timed_chunk(fn_aucmds, 'Defining lazy-load function autocommands', result)
end
if some_ft or some_event or some_fn then
table.insert(result, 'vim.cmd("augroup END")')
end
if next(ftdetect_paths) then
table.insert(result, 'vim.cmd [[augroup filetypedetect]]')
for _, path in ipairs(ftdetect_paths) do
local escaped_path = vim.fn.escape(path, ' ')
timed_chunk('vim.cmd [[source ' .. escaped_path .. ']]', 'Sourcing ftdetect script at: ' .. escaped_path, result)
end
table.insert(result, 'vim.cmd("augroup END")')
end
table.insert(result, exit_packer_compile)
table.insert(result, conditionally_output_profile(config.threshold))
if output_lua then
table.insert(result, catch_errors_lua)
else
table.insert(result, 'END\n')
table.insert(result, catch_errors)
end
return table.concat(result, '\n')
end
local compile = setmetatable({ cfg = cfg }, { __call = make_loaders })
compile.opt_keys = { 'after', 'cmd', 'ft', 'keys', 'event', 'cond', 'setup', 'fn', 'module', 'module_pattern' }
return compile

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
local config = nil
local function cfg(_config)
config = _config
end
local handlers = {
cfg = cfg,
}
return handlers

View File

@ -0,0 +1,58 @@
local a = require 'packer.async'
local log = require 'packer.log'
local util = require 'packer.util'
local display = require 'packer.display'
local plugin_utils = require 'packer.plugin_utils'
local fmt = string.format
local async = a.sync
local await = a.wait
local config = nil
local function install_plugin(plugin, display_win, results)
local plugin_name = util.get_plugin_full_name(plugin)
return async(function()
display_win:task_start(plugin_name, 'installing...')
-- TODO: If the user provided a custom function as an installer, we would like to use pcall
-- here. Need to figure out how that integrates with async code
local r = await(plugin.installer(display_win))
r = r:and_then(await, plugin_utils.post_update_hook(plugin, display_win))
if r.ok then
display_win:task_succeeded(plugin_name, 'installed')
log.debug('Installed ' .. plugin_name)
else
display_win:task_failed(plugin_name, 'failed to install')
log.debug(fmt('Failed to install %s: %s', plugin_name, vim.inspect(r.err)))
end
results.installs[plugin_name] = r
results.plugins[plugin_name] = plugin
end)
end
local function do_install(_, plugins, missing_plugins, results)
results = results or {}
results.installs = results.installs or {}
results.plugins = results.plugins or {}
local display_win = nil
local tasks = {}
if #missing_plugins > 0 then
display_win = display.open(config.display.open_fn or config.display.open_cmd)
for _, v in ipairs(missing_plugins) do
if not plugins[v].disable then
table.insert(tasks, install_plugin(plugins[v], display_win, results))
end
end
end
return tasks, display_win
end
local function cfg(_config)
config = _config
end
local install = setmetatable({ cfg = cfg }, { __call = do_install })
return install

View File

@ -0,0 +1,216 @@
-- Interface with Neovim job control and provide a simple job sequencing structure
local split = vim.split
local loop = vim.loop
local a = require 'packer.async'
local log = require 'packer.log'
local result = require 'packer.result'
--- Utility function to make a "standard" logging callback for a given set of tables
-- Arguments:
-- - err_tbl: table to which err messages will be logged
-- - data_tbl: table to which data (non-err messages) will be logged
-- - pipe: the pipe for which this callback will be used. Passed in so that we can make sure all
-- output flushes before finishing reading
-- - disp: optional packer.display object for updating task status. Requires `name`
-- - name: optional string name for a current task. Used to update task status
local function make_logging_callback(err_tbl, data_tbl, pipe, disp, name)
return function(err, data)
if err then
table.insert(err_tbl, vim.trim(err))
end
if data ~= nil then
local trimmed = vim.trim(data)
table.insert(data_tbl, trimmed)
if disp then
disp:task_update(name, split(trimmed, '\n')[1])
end
else
loop.read_stop(pipe)
loop.close(pipe)
end
end
end
--- Utility function to make a table for capturing output with "standard" structure
local function make_output_table()
return { err = { stdout = {}, stderr = {} }, data = { stdout = {}, stderr = {} } }
end
--- Utility function to merge stdout and stderr from two tables with "standard" structure (either
-- the err or data subtables, specifically)
local function extend_output(to, from)
vim.list_extend(to.stdout, from.stdout)
vim.list_extend(to.stderr, from.stderr)
return to
end
--- Wrapper for vim.loop.spawn. Takes a command, options, and callback just like vim.loop.spawn, but
-- (1) makes an async function and (2) ensures that all output from the command has been flushed
-- before calling the callback
local spawn = a.wrap(function(cmd, options, callback)
local handle = nil
local timer = nil
handle = loop.spawn(cmd, options, function(exit_code, signal)
handle:close()
if timer ~= nil then
timer:stop()
timer:close()
end
loop.close(options.stdio[1])
local check = loop.new_check()
loop.check_start(check, function()
for _, pipe in pairs(options.stdio) do
if not loop.is_closing(pipe) then
return
end
end
loop.check_stop(check)
callback(exit_code, signal)
end)
end)
if options.stdio then
for i, pipe in pairs(options.stdio) do
if options.stdio_callbacks[i] then
loop.read_start(pipe, options.stdio_callbacks[i])
end
end
end
if options.timeout then
timer = loop.new_timer()
timer:start(options.timeout, 0, function()
timer:stop()
timer:close()
if loop.is_active(handle) then
log.warn('Killing ' .. cmd .. ' due to timeout!')
loop.process_kill(handle, 'sigint')
handle:close()
for _, pipe in pairs(options.stdio) do
loop.close(pipe)
end
callback(-9999, 'sigint')
end
end)
end
end)
--- Utility function to perform a common check for process success and return a result object
local function was_successful(r)
if r.exit_code == 0 and (not r.output or not r.output.err or #r.output.err == 0) then
return result.ok(r)
else
return result.err(r)
end
end
--- Main exposed function for the jobs module. Takes a task and options and returns an async
-- function that will run the task with the given opts via vim.loop.spawn
-- Arguments:
-- - task: either a string or table. If string, split, and the first component is treated as the
-- command. If table, first element is treated as the command. All subsequent elements are passed
-- as args
-- - opts: table of options. Can include the keys "options" (like the options table passed to
-- vim.loop.spawn), "success_test" (a function, called like `was_successful` (above)),
-- "capture_output" (either a boolean, in which case default output capture is set up and the
-- resulting tables are included in the result, or a set of tables, in which case output is logged
-- to the given tables)
local run_job = function(task, opts)
return a.sync(function()
local options = opts.options or { hide = true }
local stdout = nil
local stderr = nil
local job_result = { exit_code = -1, signal = -1 }
local success_test = opts.success_test or was_successful
local uv_err
local output = make_output_table()
local callbacks = {}
local output_valid = false
if opts.capture_output then
if type(opts.capture_output) == 'boolean' then
stdout, uv_err = loop.new_pipe(false)
if uv_err then
log.error('Failed to open stdout pipe: ' .. uv_err)
return result.err()
end
stderr, uv_err = loop.new_pipe(false)
if uv_err then
log.error('Failed to open stderr pipe: ' .. uv_err)
return job_result
end
callbacks.stdout = make_logging_callback(output.err.stdout, output.data.stdout, stdout)
callbacks.stderr = make_logging_callback(output.err.stderr, output.data.stderr, stderr)
output_valid = true
elseif type(opts.capture_output) == 'table' then
if opts.capture_output.stdout then
stdout, uv_err = loop.new_pipe(false)
if uv_err then
log.error('Failed to open stdout pipe: ' .. uv_err)
return job_result
end
callbacks.stdout = function(err, data)
if data ~= nil then
opts.capture_output.stdout(err, data)
else
loop.read_stop(stdout)
loop.close(stdout)
end
end
end
if opts.capture_output.stderr then
stderr, uv_err = loop.new_pipe(false)
if uv_err then
log.error('Failed to open stderr pipe: ' .. uv_err)
return job_result
end
callbacks.stderr = function(err, data)
if data ~= nil then
opts.capture_output.stderr(err, data)
else
loop.read_stop(stderr)
loop.close(stderr)
end
end
end
end
end
if type(task) == 'string' then
local split_pattern = '%s+'
task = split(task, split_pattern)
end
local cmd = task[1]
if opts.timeout then
options.timeout = 1000 * opts.timeout
end
options.cwd = opts.cwd
local stdin = loop.new_pipe(false)
options.args = { unpack(task, 2) }
options.stdio = { stdin, stdout, stderr }
options.stdio_callbacks = { nil, callbacks.stdout, callbacks.stderr }
local exit_code, signal = a.wait(spawn(cmd, options))
job_result = { exit_code = exit_code, signal = signal }
if output_valid then
job_result.output = output
end
return success_test(job_result)
end)
end
local jobs = {
run = run_job,
logging_callback = make_logging_callback,
output_table = make_output_table,
extend_output = extend_output,
}
return jobs

View File

@ -0,0 +1,185 @@
local packer_load = nil
local cmd = vim.api.nvim_command
local fmt = string.format
local function verify_conditions(conds, name)
if conds == nil then
return true
end
for _, cond in ipairs(conds) do
local success, result
if type(cond) == 'boolean' then
result = cond
elseif type(cond) == 'string' then
success, result = pcall(loadstring(cond))
if not success then
vim.schedule(function()
vim.api.nvim_notify(
'packer.nvim: Error running cond for ' .. name .. ': ' .. result,
vim.log.levels.ERROR,
{}
)
end)
return false
end
end
if result == false then
return false
end
end
return true
end
local function loader_clear_loaders(plugin)
if plugin.commands then
for _, del_cmd in ipairs(plugin.commands) do
cmd('silent! delcommand ' .. del_cmd)
end
end
if plugin.keys then
for _, key in ipairs(plugin.keys) do
cmd(fmt('silent! %sunmap %s', key[1], key[2]))
end
end
end
local function loader_apply_config(plugin, name)
if plugin.config then
for _, config_line in ipairs(plugin.config) do
local success, err = pcall(loadstring(config_line), name, plugin)
if not success then
vim.schedule(function()
vim.api.nvim_notify('packer.nvim: Error running config for ' .. name .. ': ' .. err, vim.log.levels.ERROR, {})
end)
end
end
end
end
local function loader_apply_wants(plugin, plugins)
if plugin.wants then
for _, wanted_name in ipairs(plugin.wants) do
packer_load({ wanted_name }, {}, plugins)
end
end
end
local function loader_apply_after(plugin, plugins, name)
if plugin.after then
for _, after_name in ipairs(plugin.after) do
local after_plugin = plugins[after_name]
after_plugin.load_after[name] = nil
if next(after_plugin.load_after) == nil then
packer_load({ after_name }, {}, plugins)
end
end
end
end
local function apply_cause_side_effects(cause)
if cause.cmd then
local lines = cause.l1 == cause.l2 and '' or (cause.l1 .. ',' .. cause.l2)
-- This is a hack to deal with people who haven't recompiled after updating to the new command
-- creation logic
local bang = ''
if type(cause.bang) == 'string' then
bang = cause.bang
elseif type(cause.bang) == 'boolean' and cause.bang then
bang = '!'
end
cmd(fmt('%s %s%s%s %s', cause.mods or '', lines, cause.cmd, bang, cause.args))
elseif cause.keys then
local extra = ''
while true do
local c = vim.fn.getchar(0)
if c == 0 then
break
end
extra = extra .. vim.fn.nr2char(c)
end
if cause.prefix then
local prefix = vim.v.count ~= 0 and vim.v.count or ''
prefix = prefix .. '"' .. vim.v.register .. cause.prefix
if vim.fn.mode 'full' == 'no' then
if vim.v.operator == 'c' then
prefix = '' .. prefix
end
prefix = prefix .. vim.v.operator
end
vim.fn.feedkeys(prefix, 'n')
end
local escaped_keys = vim.api.nvim_replace_termcodes(cause.keys .. extra, true, true, true)
vim.api.nvim_feedkeys(escaped_keys, 'm', true)
elseif cause.event then
cmd(fmt('doautocmd <nomodeline> %s', cause.event))
elseif cause.ft then
cmd(fmt('doautocmd <nomodeline> %s FileType %s', 'filetypeplugin', cause.ft))
cmd(fmt('doautocmd <nomodeline> %s FileType %s', 'filetypeindent', cause.ft))
cmd(fmt('doautocmd <nomodeline> %s FileType %s', 'syntaxset', cause.ft))
end
end
packer_load = function(names, cause, plugins, force)
local some_unloaded = false
local needs_bufread = false
local num_names = #names
for i = 1, num_names do
local plugin = plugins[names[i]]
if not plugin then
local err_message = 'Error: attempted to load ' .. names[i] .. ' which is not present in plugins table!'
vim.notify(err_message, vim.log.levels.ERROR, { title = 'packer.nvim' })
error(err_message)
end
if not plugin.loaded then
loader_clear_loaders(plugin)
if force or verify_conditions(plugin.cond, names[i]) then
-- Set the plugin as loaded before config is run in case something in the config tries to load
-- this same plugin again
plugin.loaded = true
some_unloaded = true
needs_bufread = needs_bufread or plugin.needs_bufread
loader_apply_wants(plugin, plugins)
cmd('packadd ' .. names[i])
if plugin.after_files then
for _, file in ipairs(plugin.after_files) do
cmd('silent source ' .. file)
end
end
loader_apply_config(plugin, names[i])
loader_apply_after(plugin, plugins, names[i])
end
end
end
if not some_unloaded then
return
end
if needs_bufread then
if _G._packer and _G._packer.inside_compile == true then
-- delaying BufRead to end of packer_compiled
_G._packer.needs_bufread = true
else
cmd 'doautocmd BufRead'
end
end
-- Retrigger cmd/keymap...
apply_cause_side_effects(cause)
end
local function load_wrapper(names, cause, plugins, force)
local success, err_msg = pcall(packer_load, names, cause, plugins, force)
if not success then
vim.cmd 'echohl ErrorMsg'
vim.cmd('echomsg "Error in packer_compiled: ' .. vim.fn.escape(err_msg, '"') .. '"')
vim.cmd 'echomsg "Please check your config for correctness"'
vim.cmd 'echohl None'
end
end
return load_wrapper

View File

@ -0,0 +1,163 @@
-- log.lua
--
-- Inspired by rxi/log.lua
-- Modified by tjdevries and can be found at github.com/tjdevries/vlog.nvim
--
-- This library is free software; you can redistribute it and/or modify it
-- under the terms of the MIT license. See LICENSE for details.
-- User configuration section
local default_config = {
-- Name of the plugin. Prepended to log messages
plugin = 'packer.nvim',
-- Should print the output to neovim while running
use_console = true,
-- Should highlighting be used in console (using echohl)
highlights = true,
-- Should write to a file
use_file = true,
-- Any messages above this level will be logged.
level = 'debug',
-- Level configuration
modes = {
{ name = 'trace', hl = 'Comment' },
{ name = 'debug', hl = 'Comment' },
{ name = 'info', hl = 'None' },
{ name = 'warn', hl = 'WarningMsg' },
{ name = 'error', hl = 'ErrorMsg' },
{ name = 'fatal', hl = 'ErrorMsg' },
},
-- Which levels should be logged?
active_levels = { [1] = true, [2] = true, [3] = true, [4] = true, [5] = true, [6] = true },
-- Can limit the number of decimals displayed for floats
float_precision = 0.01,
}
-- {{{ NO NEED TO CHANGE
local log = {}
local unpack = unpack or table.unpack
local level_ids = { trace = 1, debug = 2, info = 3, warn = 4, error = 5, fatal = 6 }
log.cfg = function(_config)
local min_active_level = level_ids[_config.log.level]
local config = { active_levels = {} }
if min_active_level then
for i = min_active_level, 6 do
config.active_levels[i] = true
end
end
log.new(config, true)
end
log.new = function(config, standalone)
config = vim.tbl_deep_extend('force', default_config, config)
local outfile = string.format('%s/%s.log', vim.fn.stdpath 'cache', config.plugin)
vim.fn.mkdir(vim.fn.stdpath 'cache', 'p')
local obj
if standalone then
obj = log
else
obj = {}
end
local levels = {}
for i, v in ipairs(config.modes) do
levels[v.name] = i
end
local round = function(x, increment)
increment = increment or 1
x = x / increment
return (x > 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)) * increment
end
local make_string = function(...)
local t = {}
for i = 1, select('#', ...) do
local x = select(i, ...)
if type(x) == 'number' and config.float_precision then
x = tostring(round(x, config.float_precision))
elseif type(x) == 'table' then
x = vim.inspect(x)
else
x = tostring(x)
end
t[#t + 1] = x
end
return table.concat(t, ' ')
end
local console_output = vim.schedule_wrap(function(level_config, info, nameupper, msg)
local console_lineinfo = vim.fn.fnamemodify(info.short_src, ':t') .. ':' .. info.currentline
local console_string = string.format('[%-6s%s] %s: %s', nameupper, os.date '%H:%M:%S', console_lineinfo, msg)
-- Heuristic to check for nvim-notify
local is_fancy_notify = type(vim.notify) == 'table'
vim.notify(
string.format([[%s%s]], is_fancy_notify and '' or ('[' .. config.plugin .. '] '), console_string),
vim.log.levels[level_config.name:upper()],
{ title = config.plugin }
)
end)
local log_at_level = function(level, level_config, message_maker, ...)
-- Return early if we're below the config.level
if level < levels[config.level] then
return
end
local nameupper = level_config.name:upper()
local msg = message_maker(...)
local info = debug.getinfo(2, 'Sl')
local lineinfo = info.short_src .. ':' .. info.currentline
-- Output to console
if config.use_console and config.active_levels[level] then
console_output(level_config, info, nameupper, msg)
end
-- Output to log file
if config.use_file and config.active_levels[level] then
local fp, err = io.open(outfile, 'a')
if not fp then
print(err)
return
end
local str = string.format('[%-6s%s %s] %s: %s\n', nameupper, os.date(), vim.loop.hrtime(), lineinfo, msg)
fp:write(str)
fp:close()
end
end
for i, x in ipairs(config.modes) do
obj[x.name] = function(...)
return log_at_level(i, x, make_string, ...)
end
obj[('fmt_%s'):format(x.name)] = function()
return log_at_level(i, x, function(...)
local passed = { ... }
local fmt = table.remove(passed, 1)
local inspected = {}
for _, v in ipairs(passed) do
table.insert(inspected, vim.inspect(v))
end
return string.format(fmt, unpack(inspected))
end)
end
end
end
log.new(default_config, true)
-- }}}
return log

View File

@ -0,0 +1,577 @@
-- Add support for installing and cleaning Luarocks dependencies
-- Based off of plenary/neorocks/init.lua in https://github.com/nvim-lua/plenary.nvim
local a = require 'packer.async'
local jobs = require 'packer.jobs'
local log = require 'packer.log'
local result = require 'packer.result'
local util = require 'packer.util'
local fmt = string.format
local async = a.sync
local await = a.wait
local config = nil
local function cfg(_config)
config = _config.luarocks
end
local function warn_need_luajit()
log.error 'LuaJIT is required for Luarocks functionality!'
end
local lua_version = nil
if jit then
local jit_version = string.gsub(jit.version, 'LuaJIT ', '')
lua_version = { lua = string.gsub(_VERSION, 'Lua ', ''), jit = jit_version, dir = jit_version }
else
return {
handle_command = warn_need_luajit,
install_commands = warn_need_luajit,
list = warn_need_luajit,
install_hererocks = warn_need_luajit,
setup_paths = warn_need_luajit,
uninstall = warn_need_luajit,
clean = warn_need_luajit,
install = warn_need_luajit,
ensure = warn_need_luajit,
generate_path_setup = function()
return ''
end,
cfg = cfg,
}
end
local cache_path = vim.fn.stdpath 'cache'
local rocks_path = util.join_paths(cache_path, 'packer_hererocks')
local hererocks_file = util.join_paths(rocks_path, 'hererocks.py')
local hererocks_install_dir = util.join_paths(rocks_path, lua_version.dir)
local shell_hererocks_dir = vim.fn.shellescape(hererocks_install_dir)
local _hererocks_setup_done = false
local function hererocks_is_setup()
if _hererocks_setup_done then
return true
end
local path_info = vim.loop.fs_stat(util.join_paths(hererocks_install_dir, 'lib'))
_hererocks_setup_done = (path_info ~= nil) and (path_info['type'] == 'directory')
return _hererocks_setup_done
end
local function hererocks_installer(disp)
return async(function()
local hererocks_url = 'https://raw.githubusercontent.com/luarocks/hererocks/master/hererocks.py'
local hererocks_cmd
await(a.main)
vim.fn.mkdir(rocks_path, 'p')
if vim.fn.executable 'curl' > 0 then
hererocks_cmd = 'curl ' .. hererocks_url .. ' -o ' .. hererocks_file
elseif vim.fn.executable 'wget' > 0 then
hererocks_cmd = 'wget ' .. hererocks_url .. ' -O ' .. hererocks_file .. ' --verbose'
else
return result.err '"curl" or "wget" is required to install hererocks'
end
if disp ~= nil then
disp:task_start('luarocks-hererocks', 'installing hererocks...')
end
local output = jobs.output_table()
local callbacks = {
stdout = jobs.logging_callback(output.err.stdout, output.data.stdout, nil, disp, 'luarocks-hererocks'),
stderr = jobs.logging_callback(output.err.stderr, output.data.stderr),
}
local opts = { capture_output = callbacks }
local r = await(jobs.run(hererocks_cmd, opts)):map_err(function(err)
return { msg = 'Error installing hererocks', data = err, output = output }
end)
local luarocks_cmd = config.python_cmd
.. ' '
.. hererocks_file
.. ' --verbose -j '
.. lua_version.jit
.. ' -r latest '
.. hererocks_install_dir
r:and_then(await, jobs.run(luarocks_cmd, opts))
:map_ok(function()
if disp then
disp:task_succeeded('luarocks-hererocks', 'installed hererocks!')
end
end)
:map_err(function(err)
if disp then
disp:task_failed('luarocks-hererocks', 'failed to install hererocks!')
end
log.error('Failed to install hererocks: ' .. vim.inspect(err))
return { msg = 'Error installing luarocks', data = err, output = output }
end)
return r
end)
end
local function package_patterns(dir)
local sep = util.get_separator()
return fmt('%s%s?.lua;%s%s?%sinit.lua', dir, sep, dir, sep, sep)
end
local package_paths = (function()
local install_path = util.join_paths(hererocks_install_dir, 'lib', 'luarocks', fmt('rocks-%s', lua_version.lua))
local share_path = util.join_paths(hererocks_install_dir, 'share', 'lua', lua_version.lua)
return package_patterns(share_path) .. ';' .. package_patterns(install_path)
end)()
local nvim_paths_are_setup = false
local function setup_nvim_paths()
if not hererocks_is_setup() then
log.warn 'Tried to setup Neovim Lua paths before hererocks was setup!'
return
end
if nvim_paths_are_setup then
log.warn 'Tried to setup Neovim Lua paths redundantly!'
return
end
if not string.find(package.path, package_paths, 1, true) then
package.path = package.path .. ';' .. package_paths
end
local install_cpath = util.join_paths(hererocks_install_dir, 'lib', 'lua', lua_version.lua)
local install_cpath_pattern = fmt('%s%s?.so', install_cpath, util.get_separator())
if not string.find(package.cpath, install_cpath_pattern, 1, true) then
package.cpath = package.cpath .. ';' .. install_cpath_pattern
end
nvim_paths_are_setup = true
end
local function generate_path_setup_code()
local package_path_str = vim.inspect(package_paths)
local install_cpath = util.join_paths(hererocks_install_dir, 'lib', 'lua', lua_version.lua)
local install_cpath_pattern = fmt('"%s%s?.so"', install_cpath, util.get_separator())
install_cpath_pattern = vim.fn.escape(install_cpath_pattern, [[\]])
return [[
local package_path_str = ]] .. package_path_str .. [[
local install_cpath_pattern = ]] .. install_cpath_pattern .. [[
if not string.find(package.path, package_path_str, 1, true) then
package.path = package.path .. ';' .. package_path_str
end
if not string.find(package.cpath, install_cpath_pattern, 1, true) then
package.cpath = package.cpath .. ';' .. install_cpath_pattern
end
]]
end
local function activate_hererocks_cmd(install_path)
local activate_file = 'activate'
local user_shell = os.getenv 'SHELL'
local shell = user_shell:gmatch '([^/]*)$'()
if shell == 'fish' then
activate_file = 'activate.fish'
elseif shell == 'csh' then
activate_file = 'activate.csh'
end
return fmt('source %s', util.join_paths(install_path, 'bin', activate_file))
end
local function run_luarocks(args, disp, operation_name)
local cmd = {
os.getenv 'SHELL',
'-c',
fmt('%s && luarocks --tree=%s %s', activate_hererocks_cmd(hererocks_install_dir), shell_hererocks_dir, args),
}
return async(function()
local output = jobs.output_table()
local callbacks = {
stdout = jobs.logging_callback(output.err.stdout, output.data.stdout, nil, disp, operation_name),
stderr = jobs.logging_callback(output.err.stderr, output.data.stderr),
}
local opts = { capture_output = callbacks }
return await(jobs.run(cmd, opts))
:map_err(function(err)
return { msg = fmt('Error running luarocks %s', args), data = err, output = output }
end)
:map_ok(function(data)
return { data = data, output = output }
end)
end)
end
local luarocks_keys = { only_server = 'only-server', only_source = 'only-sources' }
local function is_valid_luarock_key(key)
return not (key == 'tree' or key == 'local')
end
local function format_luarocks_args(package)
if type(package) ~= 'table' then
return ''
end
local args = {}
for key, value in pairs(package) do
if type(key) == 'string' and is_valid_luarock_key(key) then
local luarock_key = luarocks_keys[key] and luarocks_keys[key] or key
if luarock_key and type(value) == 'string' then
table.insert(args, string.format('--%s=%s', key, value))
elseif key == 'env' and type(value) == 'table' then
for name, env_value in pairs(value) do
table.insert(args, string.format('%s=%s', name, env_value))
end
end
end
end
return ' ' .. table.concat(args, ' ')
end
local function luarocks_install(package, results, disp)
return async(function()
local package_name = type(package) == 'table' and package[1] or package
if disp then
disp:task_update('luarocks-install', 'installing ' .. package_name)
end
local args = format_luarocks_args(package)
local version = package.version and ' ' .. package.version .. ' ' or ''
local install_result = await(run_luarocks('install ' .. package_name .. version .. args, disp, 'luarocks-install'))
if results then
results.luarocks.installs[package_name] = install_result
end
return install_result
end)
end
local function install_packages(packages, results, disp)
return async(function()
local r = result.ok()
if not hererocks_is_setup() then
r:and_then(await, hererocks_installer(disp))
end
if disp then
disp:task_start('luarocks-install', 'installing rocks...')
end
if results then
results.luarocks.installs = {}
end
for _, package in ipairs(packages) do
r:and_then(await, luarocks_install(package, results, disp))
end
r:map_ok(function()
if disp then
disp:task_succeeded('luarocks-install', 'rocks installed!')
end
end):map_err(function()
if disp then
disp:task_failed('luarocks-install', 'installing rocks failed!')
end
end)
return r
end)
end
--- Install the packages specified with `packages` synchronously
local function install_sync(packages)
return async(function()
return await(install_packages(packages))
end)()
end
local function chunk_output(output)
-- Merge the output to a single line, then split again. Helps to deal with inconsistent
-- chunking in the output collection
local res = table.concat(output, '\n')
return vim.split(res, '\n')
end
local function luarocks_list(disp)
return async(function()
local r = result.ok()
if not hererocks_is_setup() then
r:and_then(await, hererocks_installer(disp))
end
r:and_then(await, run_luarocks 'list --porcelain')
return r:map_ok(function(data)
local results = {}
local output = chunk_output(data.output.data.stdout)
for _, line in ipairs(output) do
for l_package, version, status, install_path in string.gmatch(line, '([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)') do
table.insert(results, {
name = l_package,
version = version,
status = status,
install_path = install_path,
})
end
end
return results
end)
end)
end
local function luarocks_show(package, disp)
return async(function()
local r = result.ok()
if not hererocks_is_setup() then
r:and_then(await, hererocks_installer(disp))
end
r:and_then(await, run_luarocks('show --porcelain ' .. package))
return r:map_ok(function(data)
local output = chunk_output(data.output.data.stdout)
local dependencies = {}
for _, line in ipairs(output) do
local components = {}
for component in string.gmatch(line, '([^%s]+)') do
components[#components + 1] = component
end
if (components[1] == 'dependency' or components[1] == 'indirect_dependency') and (components[2] ~= 'lua') then
dependencies[components[2]] = components[2]
end
end
return dependencies
end)
end)
end
local function luarocks_remove(package, results, disp)
return async(function()
if disp then
disp:task_update('luarocks-remove', 'removing ' .. package)
end
local remove_result = await(run_luarocks('remove ' .. package, disp, 'luarocks-remove'))
if results then
results.luarocks.removals[package] = remove_result
end
return remove_result
end)
end
local function uninstall_packages(packages, results, disp)
return async(function()
local r = result.ok()
if not hererocks_is_setup() then
r:and_then(await, hererocks_installer(disp))
end
if disp then
disp:task_start('luarocks-remove', 'uninstalling rocks...')
end
if results then
results.luarocks.removals = {}
end
for _, package in ipairs(packages) do
local name = type(package) == 'table' and package[1] or package
r:and_then(await, luarocks_remove(name, results, disp))
end
r:map_ok(function()
if disp then
disp:task_succeeded('luarocks-remove', 'rocks cleaned!')
end
end):map_err(function()
if disp then
disp:task_failed('luarocks-remove', 'cleaning rocks failed!')
end
end)
return r
end)
end
--- Uninstall the packages specified with `packages` synchronously
local function uninstall_sync(packages)
return async(function()
return await(uninstall_packages(packages))
end)()
end
local function clean_rocks(rocks, results, disp)
return async(function()
local r = result.ok()
if not hererocks_is_setup() then
return r
end
r:and_then(await, luarocks_list(disp))
local installed_packages
if r.ok then
installed_packages = r.ok
else
return r
end
local dependency_info = {}
for _, package in ipairs(installed_packages) do
r:and_then(await, luarocks_show(package.name, disp))
if r.ok then
dependency_info[package.name] = r.ok
end
end
r = r:map_ok(function()
local to_remove = {}
for _, package in ipairs(installed_packages) do
to_remove[package.name] = package
end
for _, rock in pairs(rocks) do
if type(rock) == 'table' then
if to_remove[rock[1]] and (not rock.version or to_remove[rock[1]].version == rock.version) then
to_remove[rock[1]] = nil
end
else
to_remove[rock] = nil
end
end
for rock, dependencies in pairs(dependency_info) do
if rocks[rock] ~= nil then
for _, dependency in pairs(dependencies) do
to_remove[dependency] = nil
end
end
end
-- Toposort to ensure that we remove packages before their dependencies
local removal_order = {}
local frontier = {}
for rock, _ in pairs(to_remove) do
if next(dependency_info[rock]) == nil then
frontier[#frontier + 1] = rock
dependency_info[rock] = nil
end
end
local inverse_dependencies = {}
for rock, depends in pairs(dependency_info) do
for d, _ in pairs(depends) do
inverse_dependencies[d] = inverse_dependencies[d] or {}
inverse_dependencies[d][rock] = true
end
end
while #frontier > 0 do
local rock = table.remove(frontier)
removal_order[#removal_order + 1] = rock
local inv_depends = inverse_dependencies[rock]
if inv_depends ~= nil then
for depends, _ in pairs(inverse_dependencies[rock]) do
table.remove(dependency_info[depends])
if #dependency_info[depends] == 0 then
frontier[#frontier + 1] = depends
end
end
end
end
local reverse_order = {}
for i = #removal_order, 1, -1 do
reverse_order[#reverse_order + 1] = removal_order[i]
end
return reverse_order
end)
if results ~= nil then
results.luarocks = results.luarocks or {}
end
return r:and_then(await, uninstall_packages(r.ok, results, disp))
end)
end
local function ensure_rocks(rocks, results, disp)
return async(function()
local to_install = {}
for _, rock in pairs(rocks) do
if type(rock) == 'table' then
to_install[rock[1]:lower()] = rock
else
to_install[rock:lower()] = true
end
end
local r = result.ok()
if next(to_install) == nil then
return r
end
if disp == nil then
disp = require('packer.display').open(config.display.open_fn or config.display.open_cmd)
end
if not hererocks_is_setup() then
r = r:and_then(await, hererocks_installer(disp))
end
r:and_then(await, luarocks_list(disp))
r:map_ok(function(installed_packages)
for _, package in ipairs(installed_packages) do
local spec = to_install[package.name]
if spec then
if type(spec) == 'table' then
-- if the package is on the system and the spec has no version
-- or it has a version and that is the version on the system do not install it again
if not spec.version or (spec.version and spec.version == package.version) then
to_install[package.name] = nil
end
else
to_install[package.name] = nil
end
end
end
local package_specs = {}
for name, spec in pairs(to_install) do
if type(spec) == 'table' then
table.insert(package_specs, spec)
else
table.insert(package_specs, { name })
end
end
return package_specs
end)
results.luarocks = results.luarocks or {}
return r:and_then(await, install_packages(r.ok, results, disp))
end)
end
local function handle_command(cmd, ...)
local task
local packages = { ... }
if cmd == 'install' then
task = install_packages(packages)
elseif cmd == 'remove' then
task = uninstall_packages(packages)
else
log.warn 'Unrecognized command!'
return result.err 'Unrecognized command'
end
return async(function()
local r = await(task)
await(a.main)
local package_names = vim.fn.escape(vim.inspect(packages), '"')
return r:map_ok(function(data)
local operation_name = cmd:sub(1, 1):upper() .. cmd:sub(2)
log.info(fmt('%sed packages %s', operation_name, package_names))
return data
end):map_err(function(err)
log.error(fmt('Failed to %s packages %s: %s', cmd, package_names, vim.fn.escape(vim.inspect(err), '"\n')))
return err
end)
end)()
end
local function make_commands()
vim.cmd [[ command! -nargs=+ PackerRocks lua require('packer.luarocks').handle_command(<f-args>) ]]
end
return {
handle_command = handle_command,
install_commands = make_commands,
list = luarocks_list,
install_hererocks = hererocks_installer,
setup_paths = setup_nvim_paths,
uninstall = uninstall_sync,
clean = clean_rocks,
install = install_sync,
ensure = ensure_rocks,
generate_path_setup = generate_path_setup_code,
cfg = cfg,
}

View File

@ -0,0 +1,16 @@
local config
local function cfg(_config)
config = _config
end
local plugin_types = setmetatable({ cfg = cfg }, {
__index = function(self, k)
local v = require('packer.plugin_types.' .. k)
v.cfg(config)
self[k] = v
return v
end,
})
return plugin_types

View File

@ -0,0 +1,565 @@
local util = require 'packer.util'
local jobs = require 'packer.jobs'
local a = require 'packer.async'
local result = require 'packer.result'
local log = require 'packer.log'
local await = a.wait
local async = a.sync
local fmt = string.format
local vim = vim
local git = {}
local blocked_env_vars = {
GIT_DIR = true,
GIT_INDEX_FILE = true,
GIT_OBJECT_DIRECTORY = true,
GIT_TERMINAL_PROMPT = true,
GIT_WORK_TREE = true,
GIT_COMMON_DIR = true,
}
local function ensure_git_env()
if git.job_env == nil then
local job_env = {}
for k, v in pairs(vim.fn.environ()) do
if not blocked_env_vars[k] then
table.insert(job_env, k .. '=' .. v)
end
end
table.insert(job_env, 'GIT_TERMINAL_PROMPT=0')
git.job_env = job_env
end
end
local function has_wildcard(tag)
if not tag then
return false
end
return string.match(tag, '*') ~= nil
end
local break_tag_pattern = [=[[bB][rR][eE][aA][kK]!?:]=]
local breaking_change_pattern = [=[[bB][rR][eE][aA][kK][iI][nN][gG][ _][cC][hH][aA][nN][gG][eE]]=]
local type_exclam_pattern = [=[[a-zA-Z]+!:]=]
local type_scope_exclam_pattern = [=[[a-zA-Z]+%([^)]+%)!:]=]
local function mark_breaking_commits(plugin, commit_bodies)
local commits = vim.gsplit(table.concat(commit_bodies, '\n'), '===COMMIT_START===', true)
for commit in commits do
local commit_parts = vim.split(commit, '===BODY_START===')
local body = commit_parts[2]
local lines = vim.split(commit_parts[1], '\n')
local is_breaking = (
body ~= nil
and (
(string.match(body, breaking_change_pattern) ~= nil)
or (string.match(body, break_tag_pattern) ~= nil)
or (string.match(body, type_exclam_pattern) ~= nil)
or (string.match(body, type_scope_exclam_pattern) ~= nil)
)
)
or (
lines[2] ~= nil
and (
(string.match(lines[2], breaking_change_pattern) ~= nil)
or (string.match(lines[2], break_tag_pattern) ~= nil)
or (string.match(lines[2], type_exclam_pattern) ~= nil)
or (string.match(lines[2], type_scope_exclam_pattern) ~= nil)
)
)
if is_breaking then
plugin.breaking_commits[#plugin.breaking_commits + 1] = lines[1]
end
end
end
local config = nil
git.cfg = function(_config)
config = _config.git
config.base_dir = _config.package_root
config.default_base_dir = util.join_paths(config.base_dir, _config.plugin_package)
config.exec_cmd = config.cmd .. ' '
ensure_git_env()
end
---Resets a git repo `dest` to `commit`
---@param dest string @ path to the local git repo
---@param commit string @ commit hash
---@return function @ async function
local function reset(dest, commit)
local reset_cmd = fmt(config.exec_cmd .. config.subcommands.revert_to, commit)
local opts = { capture_output = true, cwd = dest, options = { env = git.job_env } }
return async(function()
return await(jobs.run(reset_cmd, opts))
end)
end
local handle_checkouts = function(plugin, dest, disp, opts)
local plugin_name = util.get_plugin_full_name(plugin)
return async(function()
if disp ~= nil then
disp:task_update(plugin_name, 'fetching reference...')
end
local output = jobs.output_table()
local callbacks = {
stdout = jobs.logging_callback(output.err.stdout, output.data.stdout, nil, disp, plugin_name),
stderr = jobs.logging_callback(output.err.stderr, output.data.stderr),
}
local job_opts = { capture_output = callbacks, cwd = dest, options = { env = git.job_env } }
local r = result.ok()
if plugin.tag and has_wildcard(plugin.tag) then
disp:task_update(plugin_name, fmt('getting tag for wildcard %s...', plugin.tag))
local fetch_tags = config.exec_cmd .. fmt(config.subcommands.tags_expand_fmt, plugin.tag)
r:and_then(await, jobs.run(fetch_tags, job_opts))
local data = output.data.stdout[1]
if data then
plugin.tag = vim.split(data, '\n')[1]
else
log.warn(
fmt('Wildcard expansion did not found any tag for plugin %s: defaulting to latest commit...', plugin.name)
)
plugin.tag = nil -- Wildcard is not found, then we bypass the tag
end
end
if plugin.branch or (plugin.tag and not opts.preview_updates) then
local branch_or_tag = plugin.branch and plugin.branch or plugin.tag
if disp ~= nil then
disp:task_update(plugin_name, fmt('checking out %s %s...', plugin.branch and 'branch' or 'tag', branch_or_tag))
end
r:and_then(await, jobs.run(config.exec_cmd .. fmt(config.subcommands.checkout, branch_or_tag), job_opts))
:map_err(function(err)
return {
msg = fmt(
'Error checking out %s %s for %s',
plugin.branch and 'branch' or 'tag',
branch_or_tag,
plugin_name
),
data = err,
output = output,
}
end)
end
if plugin.commit then
if disp ~= nil then
disp:task_update(plugin_name, fmt('checking out %s...', plugin.commit))
end
r:and_then(await, jobs.run(config.exec_cmd .. fmt(config.subcommands.checkout, plugin.commit), job_opts))
:map_err(function(err)
return {
msg = fmt('Error checking out commit %s for %s', plugin.commit, plugin_name),
data = err,
output = output,
}
end)
end
return r:map_ok(function(ok)
return { status = ok, output = output }
end):map_err(function(err)
if not err.msg then
return {
msg = fmt('Error updating %s: %s', plugin_name, table.concat(err, '\n')),
data = err,
output = output,
}
end
err.output = output
return err
end)
end)
end
local get_rev = function(plugin)
local plugin_name = util.get_plugin_full_name(plugin)
local rev_cmd = config.exec_cmd .. config.subcommands.get_rev
return async(function()
local rev = await(jobs.run(rev_cmd, { cwd = plugin.install_path, options = { env = git.job_env }, capture_output = true }))
:map_ok(function(ok)
local _, r = next(ok.output.data.stdout)
return r
end)
:map_err(function(err)
local _, msg = fmt('%s: %s', plugin_name, next(err.output.data.stderr))
return msg
end)
return rev
end)
end
local split_messages = function(messages)
local lines = {}
for _, message in ipairs(messages) do
vim.list_extend(lines, vim.split(message, '\n'))
table.insert(lines, '')
end
return lines
end
git.setup = function(plugin)
local plugin_name = util.get_plugin_full_name(plugin)
local install_to = plugin.install_path
local install_cmd =
vim.split(config.exec_cmd .. fmt(config.subcommands.install, plugin.commit and 999999 or config.depth), '%s+')
local submodule_cmd = config.exec_cmd .. config.subcommands.submodules
local rev_cmd = config.exec_cmd .. config.subcommands.get_rev
local update_cmd = config.exec_cmd .. config.subcommands.update
local update_head_cmd = config.exec_cmd .. config.subcommands.update_head
local fetch_cmd = config.exec_cmd .. config.subcommands.fetch
if plugin.commit or plugin.tag then
update_cmd = fetch_cmd
end
local branch_cmd = config.exec_cmd .. config.subcommands.current_branch
local current_commit_cmd = vim.split(config.exec_cmd .. config.subcommands.get_header, '%s+')
for i, arg in ipairs(current_commit_cmd) do
current_commit_cmd[i] = string.gsub(arg, 'FMT', config.subcommands.diff_fmt)
end
if plugin.branch or (plugin.tag and not has_wildcard(plugin.tag)) then
install_cmd[#install_cmd + 1] = '--branch'
install_cmd[#install_cmd + 1] = plugin.branch and plugin.branch or plugin.tag
end
install_cmd[#install_cmd + 1] = plugin.url
install_cmd[#install_cmd + 1] = install_to
local needs_checkout = plugin.tag ~= nil or plugin.commit ~= nil or plugin.branch ~= nil
plugin.installer = function(disp)
local output = jobs.output_table()
local callbacks = {
stdout = jobs.logging_callback(output.err.stdout, output.data.stdout),
stderr = jobs.logging_callback(output.err.stderr, output.data.stderr, nil, disp, plugin_name),
}
local installer_opts = {
capture_output = callbacks,
timeout = config.clone_timeout,
options = { env = git.job_env },
}
return async(function()
disp:task_update(plugin_name, 'cloning...')
local r = await(jobs.run(install_cmd, installer_opts))
installer_opts.cwd = install_to
r:and_then(await, jobs.run(submodule_cmd, installer_opts))
if plugin.commit then
disp:task_update(plugin_name, fmt('checking out %s...', plugin.commit))
r:and_then(await, jobs.run(config.exec_cmd .. fmt(config.subcommands.checkout, plugin.commit), installer_opts))
:map_err(function(err)
return {
msg = fmt('Error checking out commit %s for %s', plugin.commit, plugin_name),
data = { err, output },
}
end)
end
r:and_then(await, jobs.run(current_commit_cmd, installer_opts))
:map_ok(function(_)
plugin.messages = output.data.stdout
end)
:map_err(function(err)
plugin.output = { err = output.data.stderr }
if not err.msg then
return {
msg = fmt('Error installing %s: %s', plugin_name, table.concat(output.data.stderr, '\n')),
data = { err, output },
}
end
end)
return r
end)
end
plugin.remote_url = function()
return async(function()
return await(
jobs.run(
fmt('%s remote get-url origin', config.exec_cmd),
{ capture_output = true, cwd = plugin.install_path, options = { env = git.job_env } }
)
):map_ok(function(data)
return { remote = data.output.data.stdout[1] }
end)
end)
end
plugin.updater = function(disp, opts)
return async(function()
local update_info = { err = {}, revs = {}, output = {}, messages = {} }
local function exit_ok(r)
if #update_info.err > 0 or r.exit_code ~= 0 then
return result.err(r)
end
return result.ok(r)
end
local rev_onread = jobs.logging_callback(update_info.err, update_info.revs)
local rev_callbacks = { stdout = rev_onread, stderr = rev_onread }
disp:task_update(plugin_name, 'checking current commit...')
local r = await(
jobs.run(
rev_cmd,
{ success_test = exit_ok, capture_output = rev_callbacks, cwd = install_to, options = { env = git.job_env } }
)
):map_err(function(err)
plugin.output = { err = vim.list_extend(update_info.err, update_info.revs), data = {} }
return {
msg = fmt('Error getting current commit for %s: %s', plugin_name, table.concat(update_info.revs, '\n')),
data = err,
}
end)
local current_branch
disp:task_update(plugin_name, 'checking current branch...')
r:and_then(
await,
jobs.run(
branch_cmd,
{ success_test = exit_ok, capture_output = true, cwd = install_to, options = { env = git.job_env } }
)
)
:map_ok(function(ok)
current_branch = ok.output.data.stdout[1]
end)
:map_err(function(err)
plugin.output = { err = vim.list_extend(update_info.err, update_info.revs), data = {} }
return {
msg = fmt('Error checking current branch for %s: %s', plugin_name, table.concat(update_info.revs, '\n')),
data = err,
}
end)
if not needs_checkout then
local origin_branch = ''
disp:task_update(plugin_name, 'checking origin branch...')
local origin_refs_path = util.join_paths(install_to, '.git', 'refs', 'remotes', 'origin', 'HEAD')
local origin_refs_file = vim.loop.fs_open(origin_refs_path, 'r', 438)
if origin_refs_file ~= nil then
local origin_refs_stat = vim.loop.fs_fstat(origin_refs_file)
-- NOTE: This should check for errors
local origin_refs = vim.split(vim.loop.fs_read(origin_refs_file, origin_refs_stat.size, 0), '\n')
vim.loop.fs_close(origin_refs_file)
if #origin_refs > 0 then
origin_branch = string.match(origin_refs[1], [[^ref: refs/remotes/origin/(.*)]])
end
end
if current_branch ~= origin_branch then
needs_checkout = true
plugin.branch = origin_branch
end
end
local update_callbacks = {
stdout = jobs.logging_callback(update_info.err, update_info.output),
stderr = jobs.logging_callback(update_info.err, update_info.output, nil, disp, plugin_name),
}
local update_opts = {
success_test = exit_ok,
capture_output = update_callbacks,
cwd = install_to,
options = { env = git.job_env },
}
if needs_checkout then
r:and_then(await, jobs.run(config.exec_cmd .. config.subcommands.fetch, update_opts))
r:and_then(await, handle_checkouts(plugin, install_to, disp, opts))
local function merge_output(res)
if res.output ~= nil then
vim.list_extend(update_info.err, res.output.err.stderr)
vim.list_extend(update_info.err, res.output.err.stdout)
vim.list_extend(update_info.output, res.output.data.stdout)
vim.list_extend(update_info.output, res.output.data.stderr)
end
end
r:map_ok(merge_output)
r:map_err(function(err)
merge_output(err)
plugin.output = { err = vim.list_extend(update_info.err, update_info.output), data = {} }
local errmsg = '<unknown error>'
if err ~= nil and err.msg ~= nil then
errmsg = err.msg
end
return { msg = errmsg .. ' ' .. table.concat(update_info.output, '\n'), data = err.data }
end)
end
if opts.preview_updates then
disp:task_update(plugin_name, 'fetching updates...')
r:and_then(await, jobs.run(fetch_cmd, update_opts))
elseif opts.pull_head then
disp:task_update(plugin_name, 'pulling updates from head...')
r:and_then(await, jobs.run(update_head_cmd, update_opts))
else
disp:task_update(plugin_name, 'pulling updates...')
r:and_then(await, jobs.run(update_cmd, update_opts)):and_then(await, jobs.run(submodule_cmd, update_opts))
end
r:map_err(function(err)
plugin.output = { err = vim.list_extend(update_info.err, update_info.output), data = {} }
return {
msg = fmt('Error getting updates for %s: %s', plugin_name, table.concat(update_info.output, '\n')),
data = err,
}
end)
local post_rev_cmd
if plugin.tag ~= nil then
-- NOTE that any tag wildcard should already been expanded to a specific commit at this point
post_rev_cmd = string.gsub(rev_cmd, 'HEAD', string.format('%s^{}', plugin.tag))
elseif opts.preview_updates then
post_rev_cmd = string.gsub(rev_cmd, 'HEAD', 'FETCH_HEAD')
else
post_rev_cmd = rev_cmd
end
disp:task_update(plugin_name, 'checking updated commit...')
r:and_then(
await,
jobs.run(post_rev_cmd, {
success_test = exit_ok,
capture_output = rev_callbacks,
cwd = install_to,
options = { env = git.job_env },
})
):map_err(function(err)
plugin.output = { err = vim.list_extend(update_info.err, update_info.revs), data = {} }
return {
msg = fmt('Error checking updated commit for %s: %s', plugin_name, table.concat(update_info.revs, '\n')),
data = err,
}
end)
if r.ok then
if update_info.revs[1] ~= update_info.revs[2] then
local commit_headers_onread = jobs.logging_callback(update_info.err, update_info.messages)
local commit_headers_callbacks = { stdout = commit_headers_onread, stderr = commit_headers_onread }
local diff_cmd = string.format(config.subcommands.diff, update_info.revs[1], update_info.revs[2])
local commit_headers_cmd = vim.split(config.exec_cmd .. diff_cmd, '%s+')
for i, arg in ipairs(commit_headers_cmd) do
commit_headers_cmd[i] = string.gsub(arg, 'FMT', config.subcommands.diff_fmt)
end
disp:task_update(plugin_name, 'getting commit messages...')
r:and_then(
await,
jobs.run(commit_headers_cmd, {
success_test = exit_ok,
capture_output = commit_headers_callbacks,
cwd = install_to,
options = { env = git.job_env },
})
)
plugin.output = { err = update_info.err, data = update_info.output }
if r.ok then
plugin.messages = update_info.messages
plugin.revs = update_info.revs
end
if config.mark_breaking_changes then
local commit_bodies = { err = {}, output = {} }
local commit_bodies_onread = jobs.logging_callback(commit_bodies.err, commit_bodies.output)
local commit_bodies_callbacks = { stdout = commit_bodies_onread, stderr = commit_bodies_onread }
local commit_bodies_cmd = config.exec_cmd .. config.subcommands.get_bodies
if opts.preview_updates then
commit_bodies_cmd = config.exec_cmd .. config.subcommands.get_fetch_bodies
end
disp:task_update(plugin_name, 'checking for breaking changes...')
r:and_then(
await,
jobs.run(commit_bodies_cmd, {
success_test = exit_ok,
capture_output = commit_bodies_callbacks,
cwd = install_to,
options = { env = git.job_env },
})
):map_ok(function(ok)
plugin.breaking_commits = {}
mark_breaking_commits(plugin, commit_bodies.output)
return ok
end)
end
else
plugin.revs = update_info.revs
plugin.messages = update_info.messages
end
else
plugin.output.err = vim.list_extend(plugin.output.err, update_info.messages)
end
r.info = update_info
return r
end)
end
plugin.diff = function(commit, callback)
async(function()
local diff_cmd = config.exec_cmd .. fmt(config.subcommands.git_diff_fmt, commit)
local diff_info = { err = {}, output = {}, messages = {} }
local diff_onread = jobs.logging_callback(diff_info.err, diff_info.messages)
local diff_callbacks = { stdout = diff_onread, stderr = diff_onread }
return await(jobs.run(diff_cmd, { capture_output = diff_callbacks, cwd = install_to, options = { env = git.job_env } }))
:map_ok(function(_)
return callback(split_messages(diff_info.messages))
end)
:map_err(function(err)
return callback(nil, err)
end)
end)()
end
plugin.revert_last = function()
local r = result.ok()
async(function()
local revert_cmd = config.exec_cmd .. config.subcommands.revert
r:and_then(
await,
jobs.run(revert_cmd, { capture_output = true, cwd = install_to, options = { env = git.job_env } })
)
if needs_checkout then
r:and_then(await, handle_checkouts(plugin, install_to, nil, {}))
end
return r
end)()
return r
end
---Reset the plugin to `commit`
---@param commit string
plugin.revert_to = function(commit)
assert(type(commit) == 'string', fmt("commit: string expected but '%s' provided", type(commit)))
return async(function()
require('packer.log').debug(fmt("Reverting '%s' to commit '%s'", plugin.name, commit))
return await(reset(install_to, commit))
end)
end
---Returns HEAD's short hash
---@return string
plugin.get_rev = function()
return get_rev(plugin)
end
end
return git

View File

@ -0,0 +1,69 @@
local a = require 'packer.async'
local log = require 'packer.log'
local util = require 'packer.util'
local result = require 'packer.result'
local async = a.sync
local await = a.wait
local fmt = string.format
local config = nil
local function cfg(_config)
config = _config
end
-- Due to #679, we know that fs_symlink requires admin privileges on Windows. This is a workaround,
-- as suggested by @nonsleepr.
local symlink_fn
if util.is_windows then
symlink_fn = function(path, new_path, flags, callback)
flags = flags or {}
flags.junction = true
return vim.loop.fs_symlink(path, new_path, flags, callback)
end
else
symlink_fn = vim.loop.fs_symlink
end
local symlink = a.wrap(symlink_fn)
local unlink = a.wrap(vim.loop.fs_unlink)
local function setup_local(plugin)
local from = vim.loop.fs_realpath(util.strip_trailing_sep(plugin.path))
local to = util.strip_trailing_sep(plugin.install_path)
local plugin_name = util.get_plugin_full_name(plugin)
plugin.installer = function(disp)
return async(function()
disp:task_update(plugin_name, 'making symlink...')
local err, success = await(symlink(from, to, { dir = true }))
if not success then
plugin.output = { err = { err } }
return result.err(err)
end
return result.ok()
end)
end
plugin.updater = function(disp)
return async(function()
local r = result.ok()
disp:task_update(plugin_name, 'checking symlink...')
local resolved_path = vim.loop.fs_realpath(to)
if resolved_path ~= from then
disp:task_update(plugin_name, 'updating symlink...')
r = await(unlink(to)):and_then(symlink(from, to, { dir = true }))
end
return r
end)
end
plugin.revert_last = function(_)
log.warn "Can't revert a local plugin!"
return result.ok()
end
end
return { setup = setup_local, cfg = cfg }

View File

@ -0,0 +1,287 @@
local a = require 'packer.async'
local jobs = require 'packer.jobs'
local util = require 'packer.util'
local result = require 'packer.result'
local log = require 'packer.log'
local await = a.wait
local config = nil
local plugin_utils = {}
plugin_utils.cfg = function(_config)
config = _config
end
plugin_utils.custom_plugin_type = 'custom'
plugin_utils.local_plugin_type = 'local'
plugin_utils.git_plugin_type = 'git'
plugin_utils.guess_type = function(plugin)
if plugin.installer then
plugin.type = plugin_utils.custom_plugin_type
elseif vim.fn.isdirectory(plugin.path) ~= 0 then
plugin.url = plugin.path
plugin.type = plugin_utils.local_plugin_type
elseif
string.sub(plugin.path, 1, 6) == 'git://'
or string.sub(plugin.path, 1, 4) == 'http'
or string.match(plugin.path, '@')
then
plugin.url = plugin.path
plugin.type = plugin_utils.git_plugin_type
else
local path = table.concat(vim.split(plugin.path, '\\', true), '/')
plugin.url = string.format(config.git.default_url_format, path)
plugin.type = plugin_utils.git_plugin_type
end
end
plugin_utils.guess_dir_type = function(dir)
local globdir = vim.fn.glob(dir)
local dir_type = (vim.loop.fs_lstat(globdir) or { type = 'noexist' }).type
--[[ NOTE: We're assuming here that:
1. users only create custom plugins for non-git repos;
2. custom plugins don't use symlinks to install;
otherwise, there's no consistent way to tell from a dir alone… ]]
if dir_type == 'link' then
return plugin_utils.local_plugin_type
elseif vim.loop.fs_stat(globdir .. '/.git') then
return plugin_utils.git_plugin_type
elseif dir_type ~= 'noexist' then
return plugin_utils.custom_plugin_type
end
end
plugin_utils.helptags_stale = function(dir)
-- Adapted directly from minpac.vim
local txts = vim.fn.glob(util.join_paths(dir, '*.txt'), true, true)
vim.list_extend(txts, vim.fn.glob(util.join_paths(dir, '*.[a-z][a-z]x'), true, true))
local tags = vim.fn.glob(util.join_paths(dir, 'tags'), true, true)
vim.list_extend(tags, vim.fn.glob(util.join_paths(dir, 'tags-[a-z][a-z]'), true, true))
local txt_ftimes = util.map(vim.fn.getftime, txts)
local tag_ftimes = util.map(vim.fn.getftime, tags)
if #txt_ftimes == 0 then
return false
end
if #tag_ftimes == 0 then
return true
end
local txt_newest = math.max(unpack(txt_ftimes))
local tag_oldest = math.min(unpack(tag_ftimes))
return txt_newest > tag_oldest
end
plugin_utils.update_helptags = vim.schedule_wrap(function(...)
for _, dir in ipairs(...) do
local doc_dir = util.join_paths(dir, 'doc')
if plugin_utils.helptags_stale(doc_dir) then
log.info('Updating helptags for ' .. doc_dir)
vim.cmd('silent! helptags ' .. vim.fn.fnameescape(doc_dir))
end
end
end)
plugin_utils.update_rplugins = vim.schedule_wrap(function()
if vim.fn.exists ':UpdateRemotePlugins' == 2 then
vim.cmd [[silent UpdateRemotePlugins]]
end
end)
plugin_utils.ensure_dirs = function()
if vim.fn.isdirectory(config.opt_dir) == 0 then
vim.fn.mkdir(config.opt_dir, 'p')
end
if vim.fn.isdirectory(config.start_dir) == 0 then
vim.fn.mkdir(config.start_dir, 'p')
end
end
plugin_utils.list_installed_plugins = function()
local opt_plugins = {}
local start_plugins = {}
local opt_dir_handle = vim.loop.fs_opendir(config.opt_dir, nil, 50)
if opt_dir_handle then
local opt_dir_items = vim.loop.fs_readdir(opt_dir_handle)
while opt_dir_items do
for _, item in ipairs(opt_dir_items) do
opt_plugins[util.join_paths(config.opt_dir, item.name)] = true
end
opt_dir_items = vim.loop.fs_readdir(opt_dir_handle)
end
end
local start_dir_handle = vim.loop.fs_opendir(config.start_dir, nil, 50)
if start_dir_handle then
local start_dir_items = vim.loop.fs_readdir(start_dir_handle)
while start_dir_items do
for _, item in ipairs(start_dir_items) do
start_plugins[util.join_paths(config.start_dir, item.name)] = true
end
start_dir_items = vim.loop.fs_readdir(start_dir_handle)
end
end
return opt_plugins, start_plugins
end
plugin_utils.find_missing_plugins = function(plugins, opt_plugins, start_plugins)
return a.sync(function()
if opt_plugins == nil or start_plugins == nil then
opt_plugins, start_plugins = plugin_utils.list_installed_plugins()
end
-- NOTE/TODO: In the case of a plugin gaining/losing an alias, this will force a clean and
-- reinstall
local missing_plugins = {}
for _, plugin_name in ipairs(vim.tbl_keys(plugins)) do
local plugin = plugins[plugin_name]
if not plugin.disable then
local plugin_path = util.join_paths(config[plugin.opt and 'opt_dir' or 'start_dir'], plugin.short_name)
local plugin_installed = (plugin.opt and opt_plugins or start_plugins)[plugin_path]
await(a.main)
local guessed_type = plugin_utils.guess_dir_type(plugin_path)
if not plugin_installed or plugin.type ~= guessed_type then
missing_plugins[plugin_name] = true
elseif guessed_type == plugin_utils.git_plugin_type then
local r = await(plugin.remote_url())
local remote = r.ok and r.ok.remote or nil
if remote then
-- Form a Github-style user/repo string
local parts = vim.split(remote, '[:/]')
local repo_name = parts[#parts - 1] .. '/' .. parts[#parts]
repo_name = repo_name:gsub('%.git', '')
-- Also need to test for "full URL" plugin names, but normalized to get rid of the
-- protocol
local normalized_remote = remote:gsub('https://', ''):gsub('ssh://git@', '')
local normalized_plugin_name = plugin.name:gsub('https://', ''):gsub('ssh://git@', ''):gsub('\\', '/')
if (normalized_remote ~= normalized_plugin_name) and (repo_name ~= normalized_plugin_name) then
missing_plugins[plugin_name] = true
end
end
end
end
end
return missing_plugins
end)
end
plugin_utils.get_fs_state = function(plugins)
log.debug 'Updating FS state'
local opt_plugins, start_plugins = plugin_utils.list_installed_plugins()
return a.sync(function()
local missing_plugins = await(plugin_utils.find_missing_plugins(plugins, opt_plugins, start_plugins))
return { opt = opt_plugins, start = start_plugins, missing = missing_plugins }
end)
end
plugin_utils.load_plugin = function(plugin)
if plugin.opt then
vim.cmd('packadd ' .. plugin.short_name)
else
vim.o.runtimepath = vim.o.runtimepath .. ',' .. plugin.install_path
for _, pat in ipairs {
table.concat({ 'plugin', '**/*.vim' }, util.get_separator()),
table.concat({ 'after', 'plugin', '**/*.vim' }, util.get_separator()),
} do
local path = util.join_paths(plugin.install_path, pat)
local glob_ok, files = pcall(vim.fn.glob, path, false, true)
if not glob_ok then
if string.find(files, 'E77') then
vim.cmd('silent exe "source ' .. path .. '"')
else
error(files)
end
elseif #files > 0 then
for _, file in ipairs(files) do
file = file:gsub('\\', '/')
vim.cmd('silent exe "source ' .. file .. '"')
end
end
end
end
end
plugin_utils.post_update_hook = function(plugin, disp)
local plugin_name = util.get_plugin_full_name(plugin)
return a.sync(function()
if plugin.run or not plugin.opt then
await(a.main)
plugin_utils.load_plugin(plugin)
end
if plugin.run then
if type(plugin.run) ~= 'table' then
plugin.run = { plugin.run }
end
disp:task_update(plugin_name, 'running post update hooks...')
local hook_result = result.ok()
for _, task in ipairs(plugin.run) do
if type(task) == 'function' then
local success, err = pcall(task, plugin, disp)
if not success then
return result.err {
msg = 'Error running post update hook: ' .. vim.inspect(err),
}
end
elseif type(task) == 'string' then
if string.sub(task, 1, 1) == ':' then
await(a.main)
vim.cmd(string.sub(task, 2))
else
local hook_output = { err = {}, output = {} }
local hook_callbacks = {
stderr = jobs.logging_callback(hook_output.err, hook_output.output, nil, disp, plugin_name),
stdout = jobs.logging_callback(hook_output.err, hook_output.output, nil, disp, plugin_name),
}
local cmd
local shell = os.getenv 'SHELL' or vim.o.shell
if shell:find 'cmd.exe$' then
cmd = { shell, '/c', task }
else
cmd = { shell, '-c', task }
end
hook_result = await(jobs.run(cmd, { capture_output = hook_callbacks, cwd = plugin.install_path })):map_err(
function(err)
return {
msg = string.format('Error running post update hook: %s', table.concat(hook_output.output, '\n')),
data = err,
}
end
)
if hook_result.err then
return hook_result
end
end
else
-- TODO/NOTE: This case should also capture output in case of error. The minor difficulty is
-- what to do if the plugin's run table (i.e. this case) already specifies output handling.
hook_result = await(jobs.run(task)):map_err(function(err)
return {
msg = string.format('Error running post update hook: %s', vim.inspect(err)),
data = err,
}
end)
if hook_result.err then
return hook_result
end
end
end
return hook_result
else
return result.ok()
end
end)
end
return plugin_utils

View File

@ -0,0 +1,74 @@
-- A simple Result<V, E> type to simplify control flow with installers and updaters
local result = {}
local ok_result_mt = {
and_then = function(self, f, ...)
local r = f(...)
if r == nil then
return result.err('Nil result in and_then! ' .. vim.inspect(debug.traceback()))
end
self.ok = r.ok
self.err = r.err
setmetatable(self, getmetatable(r))
return self
end,
or_else = function(self)
return self
end,
map_ok = function(self, f)
self.ok = f(self.ok) or self.ok
return self
end,
map_err = function(self)
return self
end,
}
ok_result_mt.__index = ok_result_mt
local err_result_mt = {
and_then = function(self)
return self
end,
or_else = function(self, f, ...)
local r = f(...)
if r == nil then
return result.err('Nil result in or_else! ' .. vim.inspect(debug.traceback()))
end
self.ok = r.ok
self.err = r.err
setmetatable(self, getmetatable(r))
return self
end,
map_ok = function(self)
return self
end,
map_err = function(self, f)
self.err = f(self.err) or self.err
return self
end,
}
err_result_mt.__index = err_result_mt
result.ok = function(val)
if val == nil then
val = true
end
local r = setmetatable({}, ok_result_mt)
r.ok = val
return r
end
result.err = function(err)
if err == nil then
err = true
end
local r = setmetatable({}, err_result_mt)
r.err = err
return r
end
return result

View File

@ -0,0 +1,212 @@
local a = require 'packer.async'
local util = require 'packer.util'
local log = require 'packer.log'
local plugin_utils = require 'packer.plugin_utils'
local plugin_complete = require('packer').plugin_complete
local result = require 'packer.result'
local async = a.sync
local await = a.wait
local fmt = string.format
local config = {}
local snapshot = {
completion = {},
}
snapshot.cfg = function(_config)
config = _config
end
--- Completion for listing snapshots in `config.snapshot_path`
--- Intended to provide completion for PackerSnapshotDelete command
snapshot.completion.snapshot = function(lead, cmdline, pos)
local completion_list = {}
if config.snapshot_path == nil then
return completion_list
end
local dir = vim.loop.fs_opendir(config.snapshot_path)
if dir ~= nil then
local res = vim.loop.fs_readdir(dir)
while res ~= nil do
for _, entry in ipairs(res) do
if entry.type == 'file' and vim.startswith(entry.name, lead) then
completion_list[#completion_list + 1] = entry.name
end
end
res = vim.loop.fs_readdir(dir)
end
end
vim.loop.fs_closedir(dir)
return completion_list
end
--- Completion for listing single plugins before taking snapshot
--- Intended to provide completion for PackerSnapshot command
snapshot.completion.create = function(lead, cmdline, pos)
local cmd_args = (vim.fn.split(cmdline, ' '))
if #cmd_args > 1 then
return plugin_complete(lead, cmdline, pos)
end
return {}
end
--- Completion for listing snapshots in `config.snapshot_path` and single plugins after
--- the first argument is provided
--- Intended to provide completion for PackerSnapshotRollback command
snapshot.completion.rollback = function(lead, cmdline, pos)
local cmd_args = vim.split(cmdline, ' ')
if #cmd_args > 2 then
return plugin_complete(lead)
else
return snapshot.completion.snapshot(lead, cmdline, pos)
end
end
--- Creates a with with `completed` and `failed` keys, each containing a map with plugin name as key and commit hash/error as value
--- @param plugins list
--- @return { ok: { failed : table<string, string>, completed : table<string, string>}}
local function generate_snapshot(plugins)
local completed = {}
local failed = {}
local opt, start = plugin_utils.list_installed_plugins()
local installed = vim.tbl_extend('error', start, opt)
plugins = vim.tbl_filter(function(plugin)
if installed[plugin.install_path] and plugin.type == plugin_utils.git_plugin_type then -- this plugin is installed
return plugin
end
end, plugins)
return async(function()
for _, plugin in pairs(plugins) do
local rev = await(plugin.get_rev())
if rev.err then
failed[plugin.short_name] =
fmt("Snapshotting %s failed because of error '%s'", plugin.short_name, vim.inspect(rev.err))
else
completed[plugin.short_name] = { commit = rev.ok }
end
end
return result.ok { failed = failed, completed = completed }
end)
end
---Serializes a table of git-plugins with `short_name` as table key and another
---table with `commit`; the serialized tables will be written in the path `snapshot_path`
---provided, if there is already a snapshot it will be overwritten
---Snapshotting work only with `plugin_utils.git_plugin_type` type of plugins,
---other will be ignored.
---@param snapshot_path string realpath for snapshot file
---@param plugins table<string, any>[]
snapshot.create = function(snapshot_path, plugins)
assert(type(snapshot_path) == 'string', fmt("filename needs to be a string but '%s' provided", type(snapshot_path)))
assert(type(plugins) == 'table', fmt("plugins needs to be an array but '%s' provided", type(plugins)))
return async(function()
local commits = await(generate_snapshot(plugins))
await(a.main)
local snapshot_content = vim.fn.json_encode(commits.ok.completed)
local status, res = pcall(function()
return vim.fn.writefile({ snapshot_content }, snapshot_path) == 0
end)
if status and res then
return result.ok {
message = fmt("Snapshot '%s' complete", snapshot_path),
completed = commits.ok.completed,
failed = commits.ok.failed,
}
else
return result.err { message = fmt("Error on creation of snapshot '%s': '%s'", snapshot_path, res) }
end
end)
end
local function fetch(plugin)
local git = require 'packer.plugin_types.git'
local opts = { capture_output = true, cwd = plugin.install_path, options = { env = git.job_env } }
return async(function()
return await(require('packer.jobs').run('git ' .. config.git.subcommands.fetch, opts))
end)
end
---Rollbacks `plugins` to the hash specified in `snapshot_path` if exists.
---It automatically runs `git fetch --depth 999999 --progress` to retrieve the history
---@param snapshot_path string @ realpath to the snapshot file
---@param plugins list @ of `plugin_utils.git_plugin_type` type of plugins
---@return {ok: {completed: table<string, string>, failed: table<string, string[]>}}
snapshot.rollback = function(snapshot_path, plugins)
assert(type(snapshot_path) == 'string', 'snapshot_path: expected string but got ' .. type(snapshot_path))
assert(type(plugins) == 'table', 'plugins: expected table but got ' .. type(snapshot_path))
log.debug('Rolling back to ' .. snapshot_path)
local content = vim.fn.readfile(snapshot_path)
---@type string
local plugins_snapshot = vim.fn.json_decode(content)
if plugins_snapshot == nil then -- not valid snapshot file
return result.err(fmt("Couldn't load '%s' file", snapshot_path))
end
local completed = {}
local failed = {}
return async(function()
for _, plugin in pairs(plugins) do
local function err_handler(err)
failed[plugin.short_name] = failed[plugin.short_name] or {}
failed[plugin.short_name][#failed[plugin.short_name] + 1] = err
end
if plugins_snapshot[plugin.short_name] then
local commit = plugins_snapshot[plugin.short_name].commit
if commit ~= nil then
await(fetch(plugin))
:map_err(err_handler)
:and_then(await, plugin.revert_to(commit))
:map_ok(function(ok)
completed[plugin.short_name] = ok
end)
:map_err(err_handler)
end
end
end
return result.ok { completed = completed, failed = failed }
end)
end
---Deletes the snapshot provided
---@param snapshot_name string absolute path or just a snapshot name
snapshot.delete = function(snapshot_name)
assert(type(snapshot_name) == 'string', fmt('Expected string, got %s', type(snapshot_name)))
---@type string
local snapshot_path = vim.loop.fs_realpath(snapshot_name)
or vim.loop.fs_realpath(util.join_paths(config.snapshot_path, snapshot_name))
if snapshot_path == nil then
local warn = fmt("Snapshot '%s' is wrong or doesn't exist", snapshot_name)
log.warn(warn)
return
end
log.debug('Deleting ' .. snapshot_path)
if vim.loop.fs_unlink(snapshot_path) then
local info = 'Deleted ' .. snapshot_path
log.info(info)
else
local warn = "Couldn't delete " .. snapshot_path
log.warn(warn)
end
end
return snapshot

View File

@ -0,0 +1,141 @@
local util = require 'packer.util'
local result = require 'packer.result'
local display = require 'packer.display'
local a = require 'packer.async'
local log = require 'packer.log'
local plugin_utils = require 'packer.plugin_utils'
local fmt = string.format
local async = a.sync
local await = a.wait
local config = nil
local function get_plugin_status(plugins, plugin_name, start_plugins, opt_plugins)
local status = {}
local plugin = plugins[plugin_name]
status.wrong_type = (plugin.opt and vim.tbl_contains(start_plugins, util.join_paths(config.start_dir, plugin_name)))
or vim.tbl_contains(opt_plugins, util.join_paths(config.opt_dir, plugin_name))
return status
end
local function cfg(_config)
config = _config
end
local function fix_plugin_type(plugin, results, fs_state)
local from
local to
if plugin.opt then
from = util.join_paths(config.start_dir, plugin.short_name)
to = util.join_paths(config.opt_dir, plugin.short_name)
fs_state.opt[to] = true
fs_state.start[from] = nil
fs_state.missing[plugin.short_name] = nil
else
from = util.join_paths(config.opt_dir, plugin.short_name)
to = util.join_paths(config.start_dir, plugin.short_name)
fs_state.start[to] = true
fs_state.opt[from] = nil
fs_state.missing[plugin.short_name] = nil
end
-- NOTE: If we stored all plugins somewhere off-package-path and used symlinks to put them in the
-- right directories, this could be lighter-weight
local success, msg = os.rename(from, to)
if not success then
log.error('Failed to move ' .. from .. ' to ' .. to .. ': ' .. msg)
results.moves[plugin.short_name] = { from = from, to = to, result = result.err(success) }
else
log.debug('Moved ' .. plugin.short_name .. ' from ' .. from .. ' to ' .. to)
results.moves[plugin.short_name] = { from = from, to = to, result = result.ok(success) }
end
end
local function fix_plugin_types(plugins, plugin_names, results, fs_state)
log.debug 'Fixing plugin types'
results = results or {}
results.moves = results.moves or {}
-- NOTE: This function can only be run on plugins already installed
for _, v in ipairs(plugin_names) do
local plugin = plugins[v]
local install_dir = util.join_paths(plugin.opt and config.start_dir or config.opt_dir, plugin.short_name)
if vim.loop.fs_stat(install_dir) ~= nil then
fix_plugin_type(plugin, results, fs_state)
end
end
log.debug 'Done fixing plugin types'
end
local function update_plugin(plugin, display_win, results, opts)
local plugin_name = util.get_plugin_full_name(plugin)
-- TODO: This will have to change when separate packages are implemented
local install_path = util.join_paths(config.pack_dir, plugin.opt and 'opt' or 'start', plugin.short_name)
plugin.install_path = install_path
return async(function()
if plugin.lock or plugin.disable then
return
end
display_win:task_start(plugin_name, 'updating...')
local r = await(plugin.updater(display_win, opts))
if r ~= nil and r.ok then
local msg = 'up to date'
if plugin.type == plugin_utils.git_plugin_type then
local info = r.info
local actual_update = info.revs[1] ~= info.revs[2]
msg = actual_update and ('updated: ' .. info.revs[1] .. '...' .. info.revs[2]) or 'already up to date'
if actual_update and not opts.preview_updates then
log.debug(fmt('Updated %s: %s', plugin_name, vim.inspect(info)))
r = r:and_then(await, plugin_utils.post_update_hook(plugin, display_win))
end
end
if r.ok then
display_win:task_succeeded(plugin_name, msg)
end
else
display_win:task_failed(plugin_name, 'failed to update')
local errmsg = '<unknown error>'
if r ~= nil and r.err ~= nil then
errmsg = r.err
end
log.debug(fmt('Failed to update %s: %s', plugin_name, vim.inspect(errmsg)))
end
results.updates[plugin_name] = r
results.plugins[plugin_name] = plugin
end)
end
local function do_update(_, plugins, update_plugins, display_win, results, opts)
results = results or {}
results.updates = results.updates or {}
results.plugins = results.plugins or {}
local tasks = {}
for _, v in ipairs(update_plugins) do
local plugin = plugins[v]
if plugin == nil then
log.error(fmt('Unknown plugin: %s', v))
end
if plugin and not plugin.frozen then
if display_win == nil then
display_win = display.open(config.display.open_fn or config.display.open_cmd)
end
table.insert(tasks, update_plugin(plugin, display_win, results, opts))
end
end
if #tasks == 0 then
log.info 'Nothing to update!'
end
return tasks, display_win
end
local update = setmetatable({ cfg = cfg }, { __call = do_update })
update.get_plugin_status = get_plugin_status
update.fix_plugin_types = fix_plugin_types
return update

View File

@ -0,0 +1,168 @@
local util = {}
util.map = function(func, seq)
local result = {}
for _, v in ipairs(seq) do
table.insert(result, func(v))
end
return result
end
util.partition = function(sub, seq)
local sub_vals = {}
for _, val in ipairs(sub) do
sub_vals[val] = true
end
local result = { {}, {} }
for _, val in ipairs(seq) do
if sub_vals[val] then
table.insert(result[1], val)
else
table.insert(result[2], val)
end
end
return unpack(result)
end
util.nonempty_or = function(opt, alt)
if #opt > 0 then
return opt
else
return alt
end
end
if jit ~= nil then
util.is_windows = jit.os == 'Windows'
else
util.is_windows = package.config:sub(1, 1) == '\\'
end
if util.is_windows and vim.o.shellslash then
util.use_shellslash = true
else
util.use_shallslash = false
end
util.get_separator = function()
if util.is_windows and not util.use_shellslash then
return '\\'
end
return '/'
end
util.strip_trailing_sep = function(path)
local res, _ = string.gsub(path, util.get_separator() .. '$', '', 1)
return res
end
util.join_paths = function(...)
local separator = util.get_separator()
return table.concat({ ... }, separator)
end
util.get_plugin_short_name = function(plugin)
local path = vim.fn.expand(plugin[1])
local name_segments = vim.split(path, util.get_separator())
local segment_idx = #name_segments
local name = plugin.as or name_segments[segment_idx]
while name == '' and segment_idx > 0 do
name = name_segments[segment_idx]
segment_idx = segment_idx - 1
end
return name, path
end
util.get_plugin_full_name = function(plugin)
local plugin_name = plugin.name
if plugin.branch and plugin.branch ~= 'master' then
-- NOTE: maybe have to change the seperator here too
plugin_name = plugin_name .. '/' .. plugin.branch
end
if plugin.rev then
plugin_name = plugin_name .. '@' .. plugin.rev
end
return plugin_name
end
util.remove_ending_git_url = function(url)
return vim.endswith(url, '.git') and url:sub(1, -5) or url
end
util.deep_extend = function(policy, ...)
local result = {}
local function helper(policy, k, v1, v2)
if type(v1) ~= 'table' or type(v2) ~= 'table' then
if policy == 'error' then
error('Key ' .. vim.inspect(k) .. ' is already present with value ' .. vim.inspect(v1))
elseif policy == 'force' then
return v2
else
return v1
end
else
return util.deep_extend(policy, v1, v2)
end
end
for _, t in ipairs { ... } do
for k, v in pairs(t) do
if result[k] ~= nil then
result[k] = helper(policy, k, result[k], v)
else
result[k] = v
end
end
end
return result
end
-- Credit to @crs for the original function
util.float = function(opts)
local last_win = vim.api.nvim_get_current_win()
local last_pos = vim.api.nvim_win_get_cursor(last_win)
local columns = vim.o.columns
local lines = vim.o.lines
local width = math.ceil(columns * 0.8)
local height = math.ceil(lines * 0.8 - 4)
local left = math.ceil((columns - width) * 0.5)
local top = math.ceil((lines - height) * 0.5 - 1)
--- TODO: this is an impromptu fix for
--- https://github.com/wbthomason/packer.nvim/pull/325#issuecomment-832874005
--- ideally we should decide if the string argument passed to display openers is
--- required or not
if type(opts) ~= 'table' then
opts = {}
end
opts = vim.tbl_deep_extend('force', {
relative = 'editor',
style = 'minimal',
border = 'double',
width = width,
height = height,
col = left,
row = top,
}, opts or {})
local buf = vim.api.nvim_create_buf(false, true)
local win = vim.api.nvim_open_win(buf, true, opts)
function _G.__packer_restore_cursor()
vim.api.nvim_set_current_win(last_win)
vim.api.nvim_win_set_cursor(last_win, last_pos)
end
vim.cmd 'autocmd! BufWipeout <buffer> lua __packer_restore_cursor()'
return true, win, buf
end
return util

View File

@ -0,0 +1 @@
std="vim"

View File

@ -0,0 +1,4 @@
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferSingle"
no_call_parentheses = true

View File

@ -0,0 +1,27 @@
local util = require 'packer.util'
local M = { base_dir = '/tmp/__packer_tests__' }
---Create a fake git repository
---@param name string
---@param base string
function M.create_git_dir(name, base)
base = base or M.base_dir
local repo_path = util.join_paths(base, name)
local path = util.join_paths(repo_path, '.git')
if vim.fn.isdirectory(path) > 0 then
M.cleanup_dirs(path)
end
vim.fn.mkdir(path, 'p')
return repo_path
end
---Remove directories created for test purposes
---@vararg string
function M.cleanup_dirs(...)
for _, dir in ipairs { ... } do
vim.fn.delete(dir, 'rf')
end
end
return M

View File

@ -0,0 +1,33 @@
local a = require('plenary.async_lib.tests')
local await = require('packer.async').wait
local local_plugin = require('packer.plugin_types.local')
local packer_path = vim.fn.stdpath('data') .. '/site/pack/packer/start/'
local helpers = require('tests.helpers')
a.describe('Local plugin -', function()
a.describe('installer', function()
local local_plugin_path
local repo_name = 'test.nvim'
local plugin_install_path = packer_path .. repo_name
before_each(function()
vim.fn.mkdir(packer_path, 'p')
local_plugin_path = helpers.create_git_dir(repo_name)
end)
after_each(function() helpers.cleanup_dirs(local_plugin_path, plugin_install_path) end)
a.it('should create a symlink', function()
local plugin_spec = {
name = local_plugin_path,
path = local_plugin_path,
install_path = plugin_install_path
}
local_plugin.setup(plugin_spec)
await(plugin_spec.installer({task_update = function() end}))
assert.equal('link', vim.loop.fs_lstat(plugin_install_path).type)
end)
end)
end)

View File

@ -0,0 +1,3 @@
set rtp+=.
set rtp+=../plenary.nvim
runtime! plugin/plenary.vim

View File

@ -0,0 +1,25 @@
local a = require('plenary.async_lib.tests')
local await = require('packer.async').wait
local plugin_utils = require("packer.plugin_utils")
local packer_path = vim.fn.stdpath("data") .. "/site/pack/packer/start/"
a.describe("Packer post update hooks", function()
local test_plugin_path = packer_path .. "test_plugin/"
local run_hook = plugin_utils.post_update_hook
before_each(function() vim.fn.mkdir(test_plugin_path, "p") end)
after_each(function() vim.fn.delete(test_plugin_path, "rf") end)
a.it("should run the command in the correct folder", function()
local plugin_spec = {
name = "test/test_plugin",
install_path = test_plugin_path,
run = "touch 'this_file_should_exist'"
}
await(run_hook(plugin_spec, {task_update = function() end}))
assert.truthy(vim.loop.fs_stat(test_plugin_path .. "this_file_should_exist"))
end)
end)

View File

@ -0,0 +1,29 @@
local packer = require("packer")
local use = packer.use
local packer_path = vim.fn.stdpath("data").."/site/pack/packer/start/"
describe("Packer use tests", function()
after_each(function()
packer.reset()
end)
it("should set the correct install path", function ()
local spec = {"test/plugin1"}
packer.startup(function()
use(spec)
end)
packer.__manage_all()
assert.truthy(spec.install_path)
assert.equal(spec.install_path, packer_path .. spec.short_name)
end)
it("should add metadata to a plugin from a spec", function ()
local spec = {"test/plugin1"}
packer.startup(function()
use(spec)
end)
packer.__manage_all()
assert.equal(spec.name, "test/plugin1")
assert.equal(spec.path, "test/plugin1")
end)
end)

View File

@ -0,0 +1,81 @@
local a = require('plenary.async_lib.tests')
local await = require('packer.async').wait
local async = require('packer.async').sync
local plugin_utils = require('packer.plugin_utils')
local helpers = require("tests.helpers")
local fmt = string.format
a.describe('Plugin utils -', function()
a.describe('find_missing_plugins', function()
local repo_name = "test.nvim"
local path
plugin_utils.cfg({start_dir = helpers.base_dir})
before_each(function() path = helpers.create_git_dir(repo_name) end)
after_each(function() helpers.cleanup_dirs("tmp/packer") end)
a.it('should pick up plugins with a different remote URL', function()
local test_repo_name = fmt('user2/%s', repo_name)
local plugins = {
[repo_name] = {
opt = false,
type = "git",
name = fmt("user1/%s", repo_name),
short_name = repo_name,
remote_url = function()
return async(function()
return {ok = {remote = fmt('https://github.com/%s', test_repo_name)}}
end)
end
}
}
local result = await(plugin_utils.find_missing_plugins(plugins, {}, {[path] = true}))
assert.truthy(result)
assert.equal(1, #vim.tbl_keys(result))
end)
a.it('should not pick up plugins with the same remote URL', function()
local test_repo_name = fmt('user1/%s', repo_name)
local plugins = {
[repo_name] = {
opt = false,
type = "git",
name = test_repo_name,
short_name = repo_name,
remote_url = function()
return async(function()
return {ok = {remote = fmt('https://github.com/%s', test_repo_name)}}
end)
end
}
}
local result = await(plugin_utils.find_missing_plugins(plugins, {}, {[path] = true}))
assert.truthy(result)
assert.equal(0, #result)
end)
a.it('should handle ssh git urls', function()
local test_repo_name = fmt('user2/%s', repo_name)
local plugins = {
[repo_name] = {
opt = false,
type = "git",
name = fmt("user1/%s", repo_name),
short_name = repo_name,
remote_url = function()
return async(function()
return {ok = {remote = fmt('git@github.com:%s.git', test_repo_name)}}
end)
end
}
}
local result = await(plugin_utils.find_missing_plugins(plugins, {}, {[path] = true}))
assert.truthy(result)
assert.equal(1, #vim.tbl_keys(result))
end)
end)
end)

View File

@ -0,0 +1,176 @@
local before_each = require('plenary.busted').before_each
local a = require 'plenary.async_lib.tests'
local util = require 'packer.util'
local mocked_plugin_utils = require 'packer.plugin_utils'
local log = require 'packer.log'
local async = require('packer.async').sync
local await = require('packer.async').wait
local wait_all = require('packer.async').wait_all
local main = require('packer.async').main
local packer = require 'packer'
local jobs = require 'packer.jobs'
local git = require 'packer.plugin_types.git'
local join_paths = util.join_paths
local stdpath = vim.fn.stdpath
local fmt = string.format
local config = {
ensure_dependencies = true,
snapshot = nil,
snapshot_path = join_paths(stdpath 'cache', 'packer.nvim'),
package_root = join_paths(stdpath 'data', 'site', 'pack'),
compile_path = join_paths(stdpath 'config', 'plugin', 'packer_compiled.lua'),
plugin_package = 'packer',
max_jobs = nil,
auto_clean = true,
compile_on_sync = true,
disable_commands = false,
opt_default = false,
transitive_opt = true,
transitive_disable = true,
auto_reload_compiled = true,
git = {
mark_breaking_changes = true,
cmd = 'git',
subcommands = {
update = 'pull --ff-only --progress --rebase=false',
install = 'clone --depth %i --no-single-branch --progress',
fetch = 'fetch --depth 999999 --progress',
checkout = 'checkout %s --',
update_branch = 'merge --ff-only @{u}',
current_branch = 'rev-parse --abbrev-ref HEAD',
diff = 'log --color=never --pretty=format:FMT --no-show-signature HEAD@{1}...HEAD',
diff_fmt = '%%h %%s (%%cr)',
git_diff_fmt = 'show --no-color --pretty=medium %s',
get_rev = 'rev-parse --short HEAD',
get_header = 'log --color=never --pretty=format:FMT --no-show-signature HEAD -n 1',
get_bodies = 'log --color=never --pretty=format:"===COMMIT_START===%h%n%s===BODY_START===%b" --no-show-signature HEAD@{1}...HEAD',
submodules = 'submodule update --init --recursive --progress',
revert = 'reset --hard HEAD@{1}',
revert_to = 'reset --hard %s --',
},
depth = 1,
clone_timeout = 60,
default_url_format = 'https://github.com/%s.git',
},
display = {
non_interactive = false,
open_fn = nil,
open_cmd = '65vnew',
working_sym = '',
error_sym = '',
done_sym = '',
removed_sym = '-',
moved_sym = '',
header_sym = '',
header_lines = 2,
title = 'packer.nvim',
show_all_info = true,
prompt_border = 'double',
keybindings = { quit = 'q', toggle_info = '<CR>', diff = 'd', prompt_revert = 'r' },
},
luarocks = { python_cmd = 'python' },
log = { level = 'trace' },
profile = { enable = false },
}
git.cfg(config)
--[[ For testing purposes the spec file is made up so that when running `packer`
it could manage itself as if it was in `~/.local/share/nvim/site/pack/packer/start/` --]]
local install_path = vim.fn.getcwd()
mocked_plugin_utils.list_installed_plugins = function()
return { [install_path] = true }, {}
end
local old_require = _G.require
_G.require = function(modname)
if modname == 'plugin_utils' then
return mocked_plugin_utils
end
return old_require(modname)
end
local spec = { 'wbthomason/packer.nvim' }
local function exec_cmd(cmd, opts)
return async(function()
local r = await(jobs.run(cmd, opts))
if r.err then
print(fmt("Failed on command '%s': %s", cmd, vim.inspect(r.err)))
end
assert.is_not_nil(r.ok)
local _, result = next(r.ok.output.data.stdout)
return result
end)
end
local snapshotted_plugins = {}
a.describe('Packer testing ', function()
local snapshot_name = 'test'
local test_path = join_paths(config.snapshot_path, snapshot_name)
local snapshot = require 'packer.snapshot'
snapshot.cfg(config)
before_each(function()
packer.reset()
packer.init(config)
packer.use(spec)
packer.__manage_all()
end)
after_each(function()
spec = { 'wbthomason/packer.nvim' }
spec.install_path = install_path
end)
a.describe('snapshot.create()', function()
a.it(fmt("create snapshot in '%s'", test_path), function()
local result = await(snapshot.create(test_path, { spec }))
local stat = vim.loop.fs_stat(test_path)
assert.truthy(stat)
end)
a.it("checking if snapshot content corresponds to plugins'", function()
async(function()
local file_content = vim.fn.readfile(test_path)
snapshotted_plugins = vim.fn.json_decode(file_content)
local expected_rev = await(spec.get_rev())
assert.are.equals(expected_rev.ok, snapshotted_plugins['packer.nvim'].commit)
end)()
end)
end)
a.describe('packer.delete()', function()
a.it(fmt("delete '%s' snapshot", snapshot_name), function()
snapshot.delete(snapshot_name)
local stat = vim.loop.fs_stat(test_path)
assert.falsy(stat)
end)
end)
a.describe('packer.rollback()', function()
local rollback_snapshot_name = 'rollback_test'
local rollback_test_path = join_paths(config.snapshot_path, rollback_snapshot_name)
local prev_commit_cmd = 'git rev-parse --short HEAD~5'
local get_rev_cmd = 'git rev-parse --short HEAD'
local opts = { capture_output = true, cwd = spec.install_path, options = { env = git.job_env } }
a.it("restore 'packer' to the commit hash HEAD~5", function()
async(function()
local commit = await(exec_cmd(prev_commit_cmd, opts))
snapshotted_plugins['packer.nvim'] = { commit = commit }
await(main)
local encoded_json = vim.fn.json_encode(snapshotted_plugins)
vim.fn.writefile({ encoded_json }, rollback_test_path)
await(snapshot.rollback(rollback_test_path, { spec }))
local rev = await(exec_cmd(get_rev_cmd, opts))
assert.are.equals(snapshotted_plugins['packer.nvim'].commit, rev)
end)()
end)
end)
end)

View File

@ -0,0 +1,49 @@
[selene]
base = "lua51"
name = "vim"
[vim]
any = true
[jit]
any = true
[[describe.args]]
type = "string"
[[describe.args]]
type = "function"
[[it.args]]
type = "string"
[[it.args]]
type = "function"
[[before_each.args]]
type = "function"
[[after_each.args]]
type = "function"
[assert.is_not]
any = true
[[assert.equals.args]]
type = "any"
[[assert.equals.args]]
type = "any"
[[assert.equals.args]]
type = "any"
required = false
[[assert.same.args]]
type = "any"
[[assert.same.args]]
type = "any"
[[assert.truthy.args]]
type = "any"
[[assert.spy.args]]
type = "any"
[[assert.stub.args]]
type = "any"

View File

@ -0,0 +1,169 @@
-- Automatically generated packer.nvim plugin loader code
if vim.api.nvim_call_function('has', {'nvim-0.5'}) ~= 1 then
vim.api.nvim_command('echohl WarningMsg | echom "Invalid Neovim version for packer.nvim! | echohl None"')
return
end
vim.api.nvim_command('packadd packer.nvim')
local no_errors, error_msg = pcall(function()
_G._packer = _G._packer or {}
_G._packer.inside_compile = true
local time
local profile_info
local should_profile = false
if should_profile then
local hrtime = vim.loop.hrtime
profile_info = {}
time = function(chunk, start)
if start then
profile_info[chunk] = hrtime()
else
profile_info[chunk] = (hrtime() - profile_info[chunk]) / 1e6
end
end
else
time = function(chunk, start) end
end
local function save_profiles(threshold)
local sorted_times = {}
for chunk_name, time_taken in pairs(profile_info) do
sorted_times[#sorted_times + 1] = {chunk_name, time_taken}
end
table.sort(sorted_times, function(a, b) return a[2] > b[2] end)
local results = {}
for i, elem in ipairs(sorted_times) do
if not threshold or threshold and elem[2] > threshold then
results[i] = elem[1] .. ' took ' .. elem[2] .. 'ms'
end
end
if threshold then
table.insert(results, '(Only showing plugins that took longer than ' .. threshold .. ' ms ' .. 'to load)')
end
_G._packer.profile_output = results
end
time([[Luarocks path setup]], true)
local package_path_str = "/home/jpm/.cache/nvim/packer_hererocks/2.1.0-beta3/share/lua/5.1/?.lua;/home/jpm/.cache/nvim/packer_hererocks/2.1.0-beta3/share/lua/5.1/?/init.lua;/home/jpm/.cache/nvim/packer_hererocks/2.1.0-beta3/lib/luarocks/rocks-5.1/?.lua;/home/jpm/.cache/nvim/packer_hererocks/2.1.0-beta3/lib/luarocks/rocks-5.1/?/init.lua"
local install_cpath_pattern = "/home/jpm/.cache/nvim/packer_hererocks/2.1.0-beta3/lib/lua/5.1/?.so"
if not string.find(package.path, package_path_str, 1, true) then
package.path = package.path .. ';' .. package_path_str
end
if not string.find(package.cpath, install_cpath_pattern, 1, true) then
package.cpath = package.cpath .. ';' .. install_cpath_pattern
end
time([[Luarocks path setup]], false)
time([[try_loadstring definition]], true)
local function try_loadstring(s, component, name)
local success, result = pcall(loadstring(s), name, _G.packer_plugins[name])
if not success then
vim.schedule(function()
vim.api.nvim_notify('packer.nvim: Error running ' .. component .. ' for ' .. name .. ': ' .. result, vim.log.levels.ERROR, {})
end)
end
return result
end
time([[try_loadstring definition]], false)
time([[Defining packer_plugins]], true)
_G.packer_plugins = {
["cmp-buffer"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/cmp-buffer",
url = "https://github.com/hrsh7th/cmp-buffer"
},
["cmp-cmdline"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/cmp-cmdline",
url = "https://github.com/hrsh7th/cmp-cmdline"
},
["cmp-nvim-lsp"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/cmp-nvim-lsp",
url = "https://github.com/hrsh7th/cmp-nvim-lsp"
},
["cmp-path"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/cmp-path",
url = "https://github.com/hrsh7th/cmp-path"
},
["coc-perl"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/coc-perl",
url = "https://github.com/bmeneg/coc-perl"
},
["coc.nvim"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/coc.nvim",
url = "https://github.com/neoclide/coc.nvim"
},
["fold-cycle.nvim"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/fold-cycle.nvim",
url = "https://github.com/jghauser/fold-cycle.nvim"
},
["lightline.vim"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/lightline.vim",
url = "https://github.com/itchyny/lightline.vim"
},
nerdtree = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/nerdtree",
url = "https://github.com/preservim/nerdtree"
},
["nvim-cmp"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/nvim-cmp",
url = "https://github.com/hrsh7th/nvim-cmp"
},
["nvim-cursorline"] = {
config = { "\27LJ\2\nÛ\1\0\0\5\0\v\0\0156\0\0\0'\2\1\0B\0\2\0029\0\2\0005\2\6\0005\3\3\0005\4\4\0=\4\5\3=\3\a\0025\3\b\0005\4\t\0=\4\5\3=\3\n\2B\0\2\1K\0\1\0\15cursorword\1\0\2\14underline\1\tbold\2\1\0\2\15min_length\3\3\venable\2\15cursorline\1\0\0\ahl\1\0\2\14underline\1\tbold\2\1\0\3\ftimeout\3è\a\vnumber\1\venable\2\nsetup\20nvim-cursorline\frequire\0" },
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/nvim-cursorline",
url = "https://github.com/yamatsum/nvim-cursorline"
},
["nvim-treesitter"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/nvim-treesitter",
url = "https://github.com/nvim-treesitter/nvim-treesitter"
},
["packer.nvim"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/packer.nvim",
url = "https://github.com/wbthomason/packer.nvim"
},
["vim-gitgutter"] = {
loaded = true,
path = "/home/jpm/.local/share/nvim/site/pack/packer/start/vim-gitgutter",
url = "https://github.com/airblade/vim-gitgutter"
}
}
time([[Defining packer_plugins]], false)
-- Config for: nvim-cursorline
time([[Config for nvim-cursorline]], true)
try_loadstring("\27LJ\2\nÛ\1\0\0\5\0\v\0\0156\0\0\0'\2\1\0B\0\2\0029\0\2\0005\2\6\0005\3\3\0005\4\4\0=\4\5\3=\3\a\0025\3\b\0005\4\t\0=\4\5\3=\3\n\2B\0\2\1K\0\1\0\15cursorword\1\0\2\14underline\1\tbold\2\1\0\2\15min_length\3\3\venable\2\15cursorline\1\0\0\ahl\1\0\2\14underline\1\tbold\2\1\0\3\ftimeout\3è\a\vnumber\1\venable\2\nsetup\20nvim-cursorline\frequire\0", "config", "nvim-cursorline")
time([[Config for nvim-cursorline]], false)
_G._packer.inside_compile = false
if _G._packer.needs_bufread == true then
vim.cmd("doautocmd BufRead")
end
_G._packer.needs_bufread = false
if should_profile then save_profiles() end
end)
if not no_errors then
error_msg = error_msg:gsub('"', '\\"')
vim.api.nvim_command('echohl ErrorMsg | echom "Error in packer_compiled: '..error_msg..'" | echom "Please check your config for correctness" | echohl None')
end

BIN
nvim.bk/shada/main.shada Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More