diff --git a/nvim/init.vim b/nvim/init.vim index 6c6a63c..c3ced33 100644 --- a/nvim/init.vim +++ b/nvim/init.vim @@ -19,9 +19,6 @@ if empty(glob(data_dir . '/autoload/plug.vim')) endif call plug#begin() -" Warn about plugin updates -Plug 'semanser/vim-outdated-plugins' - " Powerline replacement Plug 'vim-airline/vim-airline' Plug 'vim-airline/vim-airline-themes' @@ -76,6 +73,114 @@ Plug 'fatih/vim-go' call plug#end() +" Check for plugin updates after the editor is responsive. +lua << EOF +local function check_plugin_updates() + local plugins = {} + for name, plugin in pairs(vim.g.plugs or {}) do + if vim.fn.isdirectory(plugin.dir) == 1 then + table.insert(plugins, { name = name, dir = plugin.dir }) + end + end + table.sort(plugins, function(a, b) return a.name < b.name end) + + local next_plugin = 1 + local active = 0 + local finished = 0 + local failures = 0 + local updates = {} + local concurrency = 2 + + local function git_command(args) + local command = { 'ionice', '-c', '3', 'nice', '-n', '10', 'git' } + vim.list_extend(command, args) + return command + end + + local function report() + if #updates > 0 then + local message = string.format( + '%d plugin update%s available: %s', + #updates, + #updates == 1 and '' or 's', + table.concat(updates, ', ') + ) + if failures > 0 then + message = message .. string.format(' (%d check%s failed)', failures, failures == 1 and '' or 's') + end + vim.notify(message, failures > 0 and vim.log.levels.WARN or vim.log.levels.INFO) + elseif failures > 0 then + vim.notify( + string.format('Plugin update check incomplete: %d check%s failed', failures, failures == 1 and '' or 's'), + vim.log.levels.WARN + ) + else + vim.notify('All plugins up-to-date', vim.log.levels.INFO) + end + end + + local pump + local function complete_one() + active = active - 1 + finished = finished + 1 + if finished == #plugins then + report() + else + pump() + end + end + + local function check_one(plugin) + active = active + 1 + vim.system( + git_command({ '-C', plugin.dir, 'fetch', '--quiet', '--no-tags', 'origin' }), + { text = true, timeout = 20000 }, + vim.schedule_wrap(function(fetch_result) + if fetch_result.code ~= 0 then + failures = failures + 1 + complete_one() + return + end + + vim.system( + git_command({ '-C', plugin.dir, 'rev-list', '--count', 'HEAD..@{upstream}' }), + { text = true, timeout = 5000 }, + vim.schedule_wrap(function(count_result) + local count = tonumber(vim.trim(count_result.stdout or '')) + if count_result.code ~= 0 or count == nil then + failures = failures + 1 + elseif count > 0 then + table.insert(updates, plugin.name) + end + complete_one() + end) + ) + end) + ) + end + + pump = function() + while active < concurrency and next_plugin <= #plugins do + local plugin = plugins[next_plugin] + next_plugin = next_plugin + 1 + check_one(plugin) + end + end + + if #plugins > 0 then + pump() + end +end + +vim.api.nvim_create_autocmd('VimEnter', { + group = vim.api.nvim_create_augroup('plugin_update_check', { clear = true }), + once = true, + callback = function() + vim.defer_fn(check_plugin_updates, 1500) + end, +}) +EOF + " Run PlugInstall if there are missing plugins autocmd VimEnter * if len(filter(values(g:plugs), '!isdirectory(v:val.dir)')) \| PlugInstall --sync | source $MYVIMRC @@ -278,4 +383,3 @@ if filereadable(expand("~/python-envs/neovim/bin/python3")) else let g:python3_host_prog="/usr/bin/python3" endif -