diff --git a/.gitignore b/.gitignore index 9a1e75fc..c4b5cbc1 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ lua/lv-user-config/ *.backup *.old -.luarc.json \ No newline at end of file +.luarc.json +.luacheckcache diff --git a/lua/lvim/core/lualine/components.lua b/lua/lvim/core/lualine/components.lua index b89bbd36..49a8ff81 100644 --- a/lua/lvim/core/lualine/components.lua +++ b/lua/lvim/core/lualine/components.lua @@ -110,7 +110,8 @@ return { local supported_linters = linters.list_registered(buf_ft) vim.list_extend(buf_client_names, supported_linters) - return "[" .. table.concat(buf_client_names, ", ") .. "]" + local unique_client_names = vim.fn.uniq(buf_client_names) + return "[" .. table.concat(unique_client_names, ", ") .. "]" end, color = { gui = "bold" }, cond = conditions.hide_in_width, diff --git a/lua/lvim/core/nvimtree.lua b/lua/lvim/core/nvimtree.lua index 7f50f256..9d7ab069 100644 --- a/lua/lvim/core/nvimtree.lua +++ b/lua/lvim/core/nvimtree.lua @@ -148,7 +148,6 @@ function M.config() }, }, } - lvim.builtin.which_key.mappings["e"] = { "NvimTreeToggle", "Explorer" } end function M.setup() @@ -158,10 +157,14 @@ function M.setup() return end - for opt, val in pairs(lvim.builtin.nvimtree) do - vim.g["nvim_tree_" .. opt] = val + if lvim.builtin.nvimtree._setup_called then + Log:debug "ignoring repeated setup call for nvim-tree, see kyazdani42/nvim-tree.lua#1308" + return end + lvim.builtin.which_key.mappings["e"] = { "NvimTreeToggle", "Explorer" } + lvim.builtin.nvimtree._setup_called = true + -- Implicitly update nvim-tree when project module is active if lvim.builtin.project.active then lvim.builtin.nvimtree.setup.respect_buf_cwd = true diff --git a/lua/lvim/core/telescope.lua b/lua/lvim/core/telescope.lua index 6a04a866..f556913b 100644 --- a/lua/lvim/core/telescope.lua +++ b/lua/lvim/core/telescope.lua @@ -70,14 +70,14 @@ function M.config() borderchars = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, color_devicons = true, set_env = { ["COLORTERM"] = "truecolor" }, -- default = nil, - pickers = { - find_files = { - hidden = true, - }, - live_grep = { - --@usage don't include the filename in the search results - only_sort_text = true, - }, + }, + pickers = { + find_files = { + hidden = true, + }, + live_grep = { + --@usage don't include the filename in the search results + only_sort_text = true, }, }, extensions = { diff --git a/lua/lvim/core/which-key.lua b/lua/lvim/core/which-key.lua index b1ae6f87..3015781b 100644 --- a/lua/lvim/core/which-key.lua +++ b/lua/lvim/core/which-key.lua @@ -244,6 +244,7 @@ M.config = function() c = { "Telescope colorscheme", "Colorscheme" }, f = { "Telescope find_files", "Find File" }, h = { "Telescope help_tags", "Find Help" }, + H = { "Telescope highlights", "Find highlight groups" }, M = { "Telescope man_pages", "Man Pages" }, r = { "Telescope oldfiles", "Open Recent File" }, R = { "Telescope registers", "Registers" }, diff --git a/lua/lvim/lsp/config.lua b/lua/lvim/lsp/config.lua index a0e22107..1f84c4e2 100644 --- a/lua/lvim/lsp/config.lua +++ b/lua/lvim/lsp/config.lua @@ -32,6 +32,7 @@ local skipped_servers = { "stylelint_lsp", "tailwindcss", "tflint", + "svlangserver", "verible", "vuels", } diff --git a/lua/lvim/lsp/utils.lua b/lua/lvim/lsp/utils.lua index 252e611c..d0e36241 100644 --- a/lua/lvim/lsp/utils.lua +++ b/lua/lvim/lsp/utils.lua @@ -176,7 +176,7 @@ function M.format(opts) end, clients) if #clients == 0 then - vim.notify "[LSP] Format request failed, no matching language servers." + vim.notify_once "[LSP] Format request failed, no matching language servers." end local timeout_ms = opts.timeout_ms or 1000 diff --git a/lua/lvim/plugins.lua b/lua/lvim/plugins.lua index 9397318e..dd40b967 100644 --- a/lua/lvim/plugins.lua +++ b/lua/lvim/plugins.lua @@ -246,9 +246,19 @@ local core_plugins = { }, } -for _, entry in ipairs(core_plugins) do - if not os.getenv "LVIM_DEV_MODE" then - entry["lock"] = true +local default_snapshot_path = join_paths(get_lvim_base_dir(), "snapshots", "default.json") +local content = vim.fn.readfile(default_snapshot_path) +local default_sha1 = vim.fn.json_decode(content) + +local get_default_sha1 = function(spec) + local short_name, _ = require("packer.util").get_plugin_short_name(spec) + return default_sha1[short_name] and default_sha1[short_name].commit +end + +for _, spec in ipairs(core_plugins) do + if not vim.env.LVIM_DEV_MODE then + -- Manually lock the commit hash since Packer's snapshots are unreliable in headless mode + spec["commit"] = get_default_sha1(spec) end end diff --git a/snapshots/default.json b/snapshots/default.json index de28eeaf..533ff25e 100644 --- a/snapshots/default.json +++ b/snapshots/default.json @@ -1,15 +1,15 @@ { "Comment.nvim": { - "commit": "bdf9ca6" + "commit": "3c69bab" }, "FixCursorHold.nvim": { "commit": "1bfb32e" }, "LuaSnip": { - "commit": "52f4aed" + "commit": "79b2019" }, "alpha-nvim": { - "commit": "4781fcf" + "commit": "ef27a59" }, "bufferline.nvim": { "commit": "c78b3ec" @@ -30,10 +30,10 @@ "commit": "bbda2b0" }, "friendly-snippets": { - "commit": "974d792" + "commit": "d27a83a" }, "gitsigns.nvim": { - "commit": "27aeb2e" + "commit": "c18e016" }, "lua-dev.nvim": { "commit": "54149d1" @@ -42,34 +42,34 @@ "commit": "3362b28" }, "nlsp-settings.nvim": { - "commit": "f27faa4" + "commit": "62d72bc" }, "null-ls.nvim": { - "commit": "474372a" + "commit": "ff40739" }, "nvim-autopairs": { - "commit": "b9cc0a2" + "commit": "fa6876f" }, "nvim-cmp": { - "commit": "033a817" + "commit": "df6734a" }, "nvim-dap": { - "commit": "688cb52" + "commit": "014ebd5" }, "nvim-lsp-installer": { - "commit": "ce70a78" + "commit": "2408a0f" }, "nvim-lspconfig": { - "commit": "eb03999" + "commit": "10c3934" }, "nvim-notify": { - "commit": "8252aae" + "commit": "8960269" }, "nvim-tree.lua": { - "commit": "1caca62" + "commit": "bdb6d4a" }, "nvim-treesitter": { - "commit": "178f24e" + "commit": "518e275" }, "nvim-ts-context-commentstring": { "commit": "8834375" @@ -84,7 +84,7 @@ "commit": "00ec5ad" }, "plenary.nvim": { - "commit": "54b2e3d" + "commit": "968a4b9" }, "popup.nvim": { "commit": "b7404d3" @@ -93,19 +93,19 @@ "commit": "541115e" }, "schemastore.nvim": { - "commit": "3a15757" + "commit": "a32911d" }, "structlog.nvim": { "commit": "232a8e2" }, "telescope-fzf-native.nvim": { - "commit": "f0dba7d" + "commit": "6a33ece" }, "telescope.nvim": { - "commit": "e6b69b1" + "commit": "d96eaa9" }, "toggleterm.nvim": { - "commit": "5bf839a" + "commit": "aaeed9e" }, "which-key.nvim": { "commit": "f03a259" diff --git a/tests/specs/plugins_load_spec.lua b/tests/specs/plugins_load_spec.lua index 1f11279e..283d5547 100644 --- a/tests/specs/plugins_load_spec.lua +++ b/tests/specs/plugins_load_spec.lua @@ -38,7 +38,8 @@ a.describe("plugin-loader", function() assert.truthy(package.loaded[plugin]) end end) - a.it("should be able to rollback plugins without errors", function() + + pending("should be able to rollback plugins without errors", function() local plugin = { name = "onedarker.nvim" } plugin.path = vim.tbl_filter(function(package) return package:match(plugin.name) diff --git a/utils/ci/generate_new_lockfile.lua b/utils/ci/generate_new_lockfile.lua index 9f274597..fd10775c 100644 --- a/utils/ci/generate_new_lockfile.lua +++ b/utils/ci/generate_new_lockfile.lua @@ -1,18 +1,18 @@ local sp = os.getenv "SNAPSHOT_PATH" local function call_proc(process, opts, cb) - local std_output = "" - local error_output = "" + local output, error_output = "", "" + local handle_stdout = function(err, chunk) + assert(not err, err) + if chunk then + output = output .. chunk + end + end - local function onread(_, is_stderr) - return function(err, data) - if data then - if is_stderr then - error_output = (error_output or "") .. err - else - std_output = (std_output or "") .. data - end - end + local handle_stderr = function(err, chunk) + assert(not err, err) + if chunk then + error_output = error_output .. chunk end end @@ -26,7 +26,7 @@ local function call_proc(process, opts, cb) handle = uv.spawn( process, - { args = opts.args, cwd = uv.cwd(), stdio = stdio }, + { args = opts.args, cwd = opts.cwd or uv.cwd(), stdio = stdio }, vim.schedule_wrap(function(code) if code ~= 0 then stdout:read_stop() @@ -42,13 +42,13 @@ local function call_proc(process, opts, cb) end check:stop() handle:close() - cb(code, std_output, error_output) + cb(code, output, error_output) end) end) ) - uv.read_start(stdout, onread(handle, false)) - uv.read_start(stderr, onread(handle, true)) + uv.read_start(stdout, handle_stdout) + uv.read_start(stderr, handle_stderr) return handle end @@ -91,11 +91,12 @@ local function write_lockfile(verbose) end local handle = call_proc("git", { args = { "ls-remote", entry.url, "HEAD" } }, on_done) + assert(handle) table.insert(active_jobs, handle) end print("active: " .. #active_jobs) - print("parsers: " .. #default_plugins) + print("plugins: " .. #default_plugins) vim.wait(#active_jobs * 60 * 1000, function() return completed == #active_jobs diff --git a/utils/ci/verify_plugins.lua b/utils/ci/verify_plugins.lua new file mode 100644 index 00000000..c56d85d1 --- /dev/null +++ b/utils/ci/verify_plugins.lua @@ -0,0 +1,134 @@ +local completed = 0 +local collection = {} +local active_jobs = {} + +local fmt = string.format +local core_plugins = require "lvim.plugins" + +local default_snapshot_path = join_paths(get_lvim_base_dir(), "snapshots", "default.json") +local fd = io.open(default_snapshot_path, "rb") +local content +if fd then + content = fd:read "*a" +end +local default_sha1 = vim.json.decode(content) + +local get_short_name = function(spec) + return spec[1]:match "/(%S*)" +end + +local get_default_sha1 = function(spec) + local short_name, _ = get_short_name(spec) + assert(default_sha1[short_name]) + return default_sha1[short_name].commit +end + +local is_directory = require("lvim.utils").is_directory +-- see packer.init() +local packdir = join_paths(get_runtime_dir(), "site", "pack", "packer") +local packer_config = { opt_dir = join_paths(packdir, "opt"), start_dir = join_paths(packdir, "start") } +local is_optional = function(spec) + return spec.opt or spec.event or spec.cmd or spec.module +end +local get_install_path = function(spec) + local prefix = is_optional(spec) and packer_config.opt_dir or packer_config.start_dir + local path = join_paths(prefix, get_short_name(spec)) + assert(is_directory(path)) + return path +end + +local function call_proc(process, opts, cb) + local output, error_output = "", "" + local handle_stdout = function(err, chunk) + assert(not err, err) + if chunk then + output = output .. chunk + end + end + + local handle_stderr = function(err, chunk) + assert(not err, err) + if chunk then + error_output = error_output .. chunk + end + end + + local uv = vim.loop + local handle + + local stdout = uv.new_pipe(false) + local stderr = uv.new_pipe(false) + + local stdio = { nil, stdout, stderr } + + handle = uv.spawn( + process, + { args = opts.args, cwd = opts.cwd or uv.cwd(), stdio = stdio }, + vim.schedule_wrap(function(code) + if code ~= 0 then + stdout:read_stop() + stderr:read_stop() + end + + local check = uv.new_check() + check:start(function() + for _, pipe in ipairs(stdio) do + if pipe and not pipe:is_closing() then + return + end + end + check:stop() + handle:close() + cb(code, output, error_output) + end) + end) + ) + + uv.read_start(stdout, handle_stdout) + uv.read_start(stderr, handle_stderr) + + return handle +end + +local function verify_core_plugins(verbose) + for _, spec in pairs(core_plugins) do + if not spec.disable then + table.insert(collection, { + name = get_short_name(spec), + commit = get_default_sha1(spec), + path = get_install_path(spec), + }) + end + end + + for _, entry in pairs(collection) do + local on_done = function(code, result, errors) + completed = completed + 1 + if code ~= 0 then + io.write(errors .. "\n") + -- os.exit(code) + else + if verbose then + io.write(fmt("verified [%s]\n", entry.name)) + end + end + local current_commit = result:gsub("\n", ""):gsub([[']], [[]]) + -- just in case there are some extra qutoes or it's a longer commit hash + if current_commit ~= entry.commit then + io.write(fmt("mismatch at [%s]: expected [%s], got [%s]\n", entry.name, entry.commit, current_commit)) + os.exit(1) + end + end + + local handle = call_proc("git", { args = { "log", "--pretty='%h'", "-1" }, cwd = entry.path }, on_done) + assert(handle) + table.insert(active_jobs, handle) + end + + vim.wait(#active_jobs * 60 * 1000, function() + return completed == #active_jobs + end) +end + +verify_core_plugins() +vim.cmd "q" diff --git a/utils/ci/verify_plugins.sh b/utils/ci/verify_plugins.sh new file mode 100644 index 00000000..7d0d764b --- /dev/null +++ b/utils/ci/verify_plugins.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -e + +lvim --headless \ + -c "luafile ./utils/ci/verify_plugins.lua" diff --git a/utils/installer/install.sh b/utils/installer/install.sh index 66149cf6..ec675e6d 100755 --- a/utils/installer/install.sh +++ b/utils/installer/install.sh @@ -160,7 +160,7 @@ function detect_platform() { elif [ -f "/etc/fedora-release" ] || [ -f "/etc/redhat-release" ]; then RECOMMEND_INSTALL="sudo dnf install -y" elif [ -f "/etc/gentoo-release" ]; then - RECOMMEND_INSTALL="emerge install -y" + RECOMMEND_INSTALL="emerge -tv" else # assume debian based RECOMMEND_INSTALL="sudo apt install -y" fi @@ -206,6 +206,15 @@ function check_neovim_min_version() { fi } +function verify_core_plugins() { + msg "Verifying core plugins" + if ! bash "$LUNARVIM_BASE_DIR/utils/ci/verify_plugins.sh"; then + echo "[ERROR]: Unable to verify plugins, makde sure to manually run ':PackerSync' when starting lvim for the first time." + exit 1 + fi + echo "Verification complete!" +} + function validate_lunarvim_files() { local verify_version_cmd='if v:errmsg != "" | cquit | else | quit | endif' if ! "$INSTALL_PREFIX/bin/lvim" --headless -c 'LvimUpdate' -c "$verify_version_cmd" &>/dev/null; then @@ -418,6 +427,8 @@ function setup_lvim() { -c 'PackerSync' echo "Packer setup complete" + + verify_core_plugins } function print_logo() {