diff --git a/init.lua b/init.lua index 9db19c0..6ca8ece 100644 --- a/init.lua +++ b/init.lua @@ -1,5 +1,4 @@ if #vim.api.nvim_list_uis() == 0 then - print('--> Running neovim in headless mode') require('setup') return end diff --git a/lua/core/events.lua b/lua/core/events.lua index a8299e4..b1c8ea6 100644 --- a/lua/core/events.lua +++ b/lua/core/events.lua @@ -94,7 +94,7 @@ au('InsertEnter', { end, }) - require('modules.theme') +require('modules.theme') au('UIEnter', { group = group, once = true, @@ -102,6 +102,7 @@ au('UIEnter', { vim.schedule(function() require('modules.navigation') require('plugins.language-manager').setup() + require('modules.diagnostics') end) end, }) diff --git a/lua/plugins/language-manager.lua b/lua/plugins/language-manager.lua index 4d8c555..fc7f826 100644 --- a/lua/plugins/language-manager.lua +++ b/lua/plugins/language-manager.lua @@ -1,6 +1,7 @@ +local utils = require('utils') local M = { - lsp = {}, ts = {}, + lsp = {}, lint = {}, format = {}, } @@ -138,14 +139,20 @@ end -- ======== Spec Generation ======== +local function get_mason_registry() + vim.cmd.packadd('mason.nvim') + require('mason').setup() + + local registry = require('mason-registry') + registry.refresh() + + return registry +end + function M.generate_specs(specs_raw) local install_spec = create_spec() local specs = create_spec() - - -- ensure Mason is available - vim.cmd.packadd('mason.nvim') - require('mason').setup() - local registry = require('mason-registry') + local registry = get_mason_registry() local lsp_map = {} @@ -157,17 +164,21 @@ function M.generate_specs(specs_raw) spec.ts = spec.ts or spec.ft specs.add(spec.ts, 'ts_parsers') - -- resolve LSP name using Mason registry - install_spec.add(spec.lsp, 'language_servers') + install_spec.add(spec.lsp, 'code_tools') local resolved_lsps = {} - for _, lsp in ipairs(wrap(spec.lsp)) do - if registry.has_package(lsp) then - local pkg = registry.get_package(lsp) - local lspconfig = pkg.spec and pkg.spec.neovim and pkg.spec.neovim.lspconfig or lsp - table.insert(resolved_lsps, lspconfig) - lsp_map[lspconfig] = lsp + for _, lsp_name in ipairs(wrap(spec.lsp)) do + if registry and registry.has_package(lsp_name) then + local pkg = registry.get_package(lsp_name) + if pkg.spec and pkg.spec.neovim and pkg.spec.neovim.lspconfig then + local lspconfig = pkg.spec and pkg.spec.neovim and pkg.spec.neovim.lspconfig + lsp_name = lspconfig + lsp_map[lspconfig] = lsp_name + table.insert(resolved_lsps, lsp_name) + else + print('Package found but not lsp name: ' .. lsp_name) + end else - vim.notify('Unknown LSP: ' .. lsp, vim.log.levels.WARN) + print('Package not found: ' .. lsp_name) end end specs.add(resolved_lsps, 'language_servers') @@ -185,19 +196,7 @@ function M.generate_specs(specs_raw) return result, install_spec.get() end --- ======== Cache Interface ======== - -function M.load_or_generate(specs_raw) - local hash = hash_spec(specs_raw) - local cache = load_cache() - if cache and cache.hash == hash then - M.general = cache.spec - else - M.general, M.mason = M.generate_specs(specs_raw) - save_cache({ hash = hash, spec = M.general }) - end - return M.general, M.mason -end +-- ======== Cache ======== function M.load_specs() local cache = load_cache() @@ -205,9 +204,8 @@ function M.load_specs() M.general = cache.spec else local specs_raw = require('modules.language-specs').get() - local hash = hash_spec(specs_raw) M.general, M.mason = M.generate_specs(specs_raw) - save_cache({ hash = hash, spec = M.general }) + save_cache({ hash = hash_spec(specs_raw), spec = M.general }) end return M.general, M.mason end @@ -223,103 +221,53 @@ function M.invalidate_cache() M.mason = nil end --- ======== Setup Functions ======== - -local function on_ts_installed(parsers, cb) - local info = require('nvim-treesitter.info') - local timer = vim.loop.new_timer() - timer:start(0, 500, vim.schedule_wrap(function() - for _, lang in ipairs(parsers) do - if not vim.tbl_contains(info.installed_parsers(), lang) then - return - end - end - timer:stop() - timer:close() - cb() - end)) -end - function M.ts.install() - local install = require('nvim-treesitter.install') - local parsers = M.general.ts_parsers or {} - install.ensure_installed(parsers) -- async - return parsers -end - --- ======== Orchestration ======== - -function M.install() - local parsers = M.ts.install() - on_ts_installed(parsers, function() - M.mason_install() - end) -end - -local function ensure_registry_ready() - local registry = require('mason-registry') - if not registry.is_installed() then - registry.update() - vim.wait(10000, function() - return registry.is_installed() - end, 200) - end - return registry + vim.cmd.packadd('nvim-treesitter') + local ts_install = require('nvim-treesitter.install').ensure_installed_sync + ts_install(M.general.ts_parsers) end function M.mason_install() - vim.cmd.packadd('mason.nvim') - require('mason').setup() - local registry = ensure_registry_ready() - local list = vim.list_extend(M.mason.language_servers, M.mason.code_tools) - print(vim.inspect(list)) + local packages = M.mason.code_tools + local registry = get_mason_registry() - local function install_and_wait(name) - local pkg = registry.get_package(name) - if pkg:is_installed() then - vim.notify('Already installed ' .. name, vim.log.levels.INFO) - return + local pending = #packages + + local result = utils.await(function(resolve) + for _, name in ipairs(packages) do + print('Mason package installing: ' .. name) + local pkg = registry.get_package(name) + pkg:install({}, function(success, error) + if success then + print('Mason package installed: ' .. name) + else + print('Mason package failed: ' .. name) + print(' > Error: ' .. vim.inspect(error)) + end + + pending = pending - 1 + if pending == 0 then + resolve(true) + end + end) end + end, 5 * 60 * 1000, 200) - vim.notify('Installing ' .. name, vim.log.levels.INFO) - local done = false - pkg:install():once('closed', function() - done = true - end) - - local ok = vim.wait(60 * 60 * 1000, function() - return done or pkg:is_installed() - end, 200) - - if not ok or not pkg:is_installed() then - vim.notify('Install failed: ' .. name, vim.log.levels.ERROR) - else - vim.notify('Installed ' .. name, vim.log.levels.INFO) - end + if not result.ok or pending ~= 0 then + print('\n!! >> Exited timeout, possible clean up needed!') + print(' > status: ' .. result.ok) + print(' > pending: ' .. pending) end - - for _, name in ipairs(list) do - if registry.has_package(name) then - install_and_wait(name) - else - vim.notify('Package not found in registry: ' .. name, vim.log.levels.WARN) - end - end - - -- force event loop to process any pending Mason async teardown - local settled = false - vim.defer_fn(function() - settled = true - end, 3000) - vim.wait(10 * 1000, function() - return settled - end, 100) end -function M.lsp.setup() - for _, lsp_name in ipairs(M.general.language_servers or {}) do - vim.lsp.enable(lsp_name) - end +-- ======== Public API ======== + +function M.install() + print('\n> Starting ts parsers install') + M.ts.install() + + print('\n> Starting mason install: lsp, lint, format') + M.mason_install() end function M.ts.setup() @@ -329,12 +277,18 @@ function M.ts.setup() }) end +function M.lsp.setup() + for _, lsp_name in ipairs((M.general and M.general.language_servers) or {}) do + vim.lsp.enable(lsp_name) + end +end + function M.lint.setup() vim.api.nvim_create_autocmd({ 'BufReadPre', 'BufNewFile' }, { group = M.group, callback = function() local lint = require('lint') - lint.linters_by_ft = M.general.linters_by_ft or {} + lint.linters_by_ft = (M.general and M.general.linters_by_ft) or {} vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, { group = vim.api.nvim_create_augroup('language-manager.lint', { clear = true }), callback = function() @@ -353,7 +307,7 @@ function M.format.setup() require('conform').setup({ format_on_save = { timeout_ms = 500, lsp_format = 'fallback' }, default_format_opts = { stop_after_first = true }, - formatters_by_ft = M.general.formatters_by_ft or {}, + formatters_by_ft = (M.general and M.general.formatters_by_ft) or {}, }) end, }) diff --git a/lua/setup/init.lua b/lua/setup/init.lua index 5ced9a5..1e6b1a0 100644 --- a/lua/setup/init.lua +++ b/lua/setup/init.lua @@ -1,53 +1,52 @@ -local M = {} - -function M.install_pm() +local function clone_package_manager() local path = vim.fn.stdpath('data') .. '/site/pack/paqs/opt/paq-nvim' if not vim.uv.fs_stat(path) then local repo = 'https://github.com/savq/paq-nvim.git' local cmd = { 'git', 'clone', '--depth=1', repo, path } - vim.system(cmd):wait() - print("Installed: paq") + local result = vim.system(cmd):wait() + if result.code == 0 then + print('Package manager installed correctly') + end end end -function M.install_packages() - print("Installing packages...") - vim.cmd.packadd('paq-nvim') - local packages = require('setup.packages').get() - local paq = require('paq') - - paq(packages) - - -- capture list of jobs before starting - local install_list = paq.install() - - -- if nothing to install, skip wait - if not install_list or vim.tbl_isempty(install_list) then - vim.cmd('packloadall') - vim.cmd('silent! helptags ALL') - return - end - +local function install_packages() local done = false vim.api.nvim_create_autocmd('User', { pattern = 'PaqDoneInstall', once = true, callback = function() - vim.cmd('packloadall') - vim.cmd('silent! helptags ALL') done = true end, }) - -- wait until done or timeout - vim.wait(300000, function() + vim.cmd.packadd('paq-nvim') + local paq = require('paq') + local packages = require('setup.packages').get() + + paq(packages) + paq.install() + + local to_install = paq.query('to_install') + if #to_install == 0 then + return + end + + vim.wait(60000, function() return done end, 200) + + if not done then + print('Paq installation timeout or failed') + else + print('Paq installation completed') + end end +local function install_languages() + vim.cmd.packadd('mason.nvim') + require('mason').setup() - -function M.install_languages() local lm = require('plugins.language-manager') lm.invalidate_cache() lm.load_specs() @@ -55,9 +54,13 @@ function M.install_languages() end vim.api.nvim_create_user_command('InstallAll', function() - M.install_pm() - M.install_packages() - M.install_languages() + print('> Starting clone package manager...') + clone_package_manager() + print('\n> Starting installing packages...') + install_packages() + print('\n> Starting installing languages: ts parsers, language servers, linters, formatters...') + install_languages() + print('\n=== Install Finished ===\n\n') end, {}) vim.api.nvim_create_user_command('FetchLspConfigs', function() @@ -70,7 +73,7 @@ vim.api.nvim_create_user_command('FetchLspConfigs', function() local lsp_dir = vim.fs.joinpath(vim.fn.getcwd(), 'lsp') vim.fn.mkdir(lsp_dir, 'p') - for _, name in ipairs(general.language_servers) do + for _, name in ipairs(general.language_servers or {}) do local file = vim.fs.joinpath(lsp_dir, name .. '.lua') if vim.fn.filereadable(file) == 0 then local url = base_url .. name .. '.lua' @@ -87,5 +90,3 @@ vim.api.nvim_create_user_command('FetchLspConfigs', function() end end end, { desc = 'Fetch default LSP configs into ./lsp in cwd' }) - -return M diff --git a/lua/utils.lua b/lua/utils.lua new file mode 100644 index 0000000..3799130 --- /dev/null +++ b/lua/utils.lua @@ -0,0 +1,24 @@ +-- utils.lua +local M = {} + +function M.await(fn, timeout, interval) + local done = false + local ok, data + + -- Wrap resolve in vim.schedule so it runs on main loop + fn(function(success, result) + vim.schedule(function() + done = true + ok = success + data = result + end) + end) + + vim.wait(timeout, function() + return done + end, interval) + + return { ok = ok or false, data = data } +end + +return M diff --git a/nvim-pack-lock.json b/nvim-pack-lock.json deleted file mode 100644 index 951e18a..0000000 --- a/nvim-pack-lock.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "plugins": { - "nvim-tree.lua": { - "version": "'master'" - } - } -} \ No newline at end of file diff --git a/scripts/install_all b/scripts/install_all deleted file mode 100755 index 93357b0..0000000 --- a/scripts/install_all +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -nvim --headless '+lua =require("setup").install_pm()' +qa -nvim --headless '+lua =require("setup").install_packages()' +qa -nvim --headless '+lua =require("setup").install_languages()' +qa