From 5a46cb5f69213f780b40dd40400fe7e11620be41 Mon Sep 17 00:00:00 2001 From: Tomas Mirchev Date: Fri, 24 Oct 2025 17:13:50 +0300 Subject: [PATCH 1/9] feat: remove lazy --- init.lua | 139 +++++++++++--- lazy-lock.json | 11 -- lsp/eslint.lua | 29 ++- lsp/tailwindcss.lua | 29 ++- lua/config/autocmds.lua | 77 -------- lua/config/options.lua | 109 ----------- lua/config/terminal.lua | 65 ------- lua/plugins/colorscheme.lua | 36 ---- lua/plugins/filetree.lua | 147 --------------- lua/{custom => plugins}/language-manager.lua | 62 +++++-- lua/{custom => plugins}/navigation.lua | 0 lua/plugins/syntax.lua | 67 ------- lua/{custom => plugins}/tabline.lua | 5 +- lua/setup/init.lua | 1 + lua/{custom => setup}/lm-cmds.lua | 0 lua/{config => triimd}/diagnostics.lua | 0 lua/triimd/filetree.lua | 127 +++++++++++++ lua/{config => triimd}/keymaps.lua | 1 + lua/triimd/syntax.lua | 26 +++ lua/utils.lua | 33 ---- nvim-pack-lock.json | 43 +++++ plugin/colorscheme.lua | 29 +++ plugin/events.lua | 180 +++++++++++++++++++ plugin/packages.lua | 8 + 24 files changed, 635 insertions(+), 589 deletions(-) delete mode 100644 lazy-lock.json delete mode 100644 lua/config/autocmds.lua delete mode 100644 lua/config/options.lua delete mode 100644 lua/config/terminal.lua delete mode 100644 lua/plugins/colorscheme.lua delete mode 100644 lua/plugins/filetree.lua rename lua/{custom => plugins}/language-manager.lua (65%) rename lua/{custom => plugins}/navigation.lua (100%) delete mode 100644 lua/plugins/syntax.lua rename lua/{custom => plugins}/tabline.lua (90%) create mode 100644 lua/setup/init.lua rename lua/{custom => setup}/lm-cmds.lua (100%) rename lua/{config => triimd}/diagnostics.lua (100%) create mode 100644 lua/triimd/filetree.lua rename lua/{config => triimd}/keymaps.lua (98%) create mode 100644 lua/triimd/syntax.lua delete mode 100644 lua/utils.lua create mode 100644 nvim-pack-lock.json create mode 100644 plugin/colorscheme.lua create mode 100644 plugin/events.lua create mode 100644 plugin/packages.lua diff --git a/init.lua b/init.lua index 93b7bfc..243a33a 100644 --- a/init.lua +++ b/init.lua @@ -1,29 +1,112 @@ -local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim' -if not (vim.uv or vim.loop).fs_stat(lazypath) then - local lazyrepo = 'https://github.com/folke/lazy.nvim.git' - local out = - vim.fn.system({ 'git', 'clone', '--filter=blob:none', '--branch=stable', lazyrepo, lazypath }) - if vim.v.shell_error ~= 0 then - vim.api.nvim_echo({ - { 'Failed to clone lazy.nvim:\n', 'ErrorMsg' }, - { out, 'WarningMsg' }, - { '\nPress any key to exit...' }, - }, true, {}) - vim.fn.getchar() - os.exit(1) - end -end -vim.opt.rtp:prepend(lazypath) +-- Map Leader +vim.g.mapleader = ' ' +vim.g.maplocalleader = ' ' -require('config.options') -require('config.keymaps') -require('config.autocmds') -require('config.terminal') -require('custom.navigation') -require('custom.tabline').setup() -require('lazy').setup({ - spec = { { import = 'plugins' } }, - install = { missing = false }, - change_detection = { notify = false }, - rocks = { enabled = false }, -}) +-- Disable built-in plugins +vim.loader.enable() +vim.g.loaded_gzip = 1 +vim.g.loaded_tar = 1 +vim.g.loaded_tarPlugin = 1 +vim.g.loaded_zip = 1 +vim.g.loaded_zipPlugin = 1 +vim.g.loaded_getscript = 1 +vim.g.loaded_getscriptPlugin = 1 +vim.g.loaded_vimball = 1 +vim.g.loaded_vimballPlugin = 1 +vim.g.loaded_matchit = 1 +vim.g.loaded_2html_plugin = 1 +vim.g.loaded_rrhelper = 1 +vim.g.loaded_matchparen = 1 +vim.g.loaded_tutor_mode_plugin = 1 +vim.g.loaded_spellfile_plugin = 1 +vim.g.loaded_logipat = 1 +vim.g.loaded_rplugin = 1 +vim.g.loaded_netrw = 1 -- use nvim-tree instead +vim.g.loaded_netrwPlugin = 1 + +-- UI +vim.g.have_nerd_font = true +vim.opt.termguicolors = false + +vim.opt.textwidth = 100 +vim.opt.colorcolumn = '+0' + +vim.opt.signcolumn = 'no' +vim.opt.number = true +vim.opt.relativenumber = true +vim.opt.cursorline = true +vim.opt.ruler = false +vim.opt.winborder = 'rounded' +vim.opt.guicursor = 'n-v-i-c:block' + +vim.opt.laststatus = 3 +vim.opt.statusline = '── %f %h%w%m%r %= [%l,%c-%L] ──' +vim.opt.fillchars = { + stl = '─', + stlnc = '─', + horiz = '─', + horizdown = '─', + horizup = '─', + vert = '│', + vertleft = '│', + vertright = '│', + verthoriz = '│', +} + +-- Wrap +vim.opt.wrap = false +vim.opt.linebreak = true +vim.opt.breakindent = true + +-- Indent +vim.opt.shiftwidth = 2 -- Number of spaces to use for (auto)indent +vim.opt.tabstop = 2 -- Number of spaces that a in file counts for +vim.opt.softtabstop = 2 -- Number of spaces when pressing in insert mode +vim.opt.expandtab = true -- Use spaces instead of literal tab characters +vim.opt.autoindent = true -- Copy indent from the current line when starting a new one +vim.opt.smartindent = true -- Automatically inserts indents in code blocks (for C-like languages) + +-- Scroll and mouse +vim.opt.scrolloff = 10 +vim.opt.sidescrolloff = 5 +vim.opt.mousescroll = 'hor:1,ver:5' +vim.opt.mouse = 'a' -- Enable mouse support + +-- Search +vim.opt.ignorecase = true +vim.opt.smartcase = true -- Override ignorecase if search contains upper case chars +vim.opt.inccommand = 'split' -- Live substitution preview +vim.opt.completeopt = { 'fuzzy', 'menuone', 'popup', 'noselect' } + +-- Splits +vim.opt.splitright = true +vim.opt.splitbelow = true + +-- Persistence +vim.opt.undofile = true +vim.opt.swapfile = false + +-- Tweaks +vim.opt.updatetime = 1000 +vim.opt.timeout = true +vim.opt.ttimeout = true +vim.opt.timeoutlen = 500 +vim.opt.ttimeoutlen = 10 + +-- Clipboard +if vim.env.CONTAINER then + vim.g.clipboard = { + name = 'osc52', + copy = { + ['+'] = require('vim.ui.clipboard.osc52').copy('+'), + ['*'] = require('vim.ui.clipboard.osc52').copy('*'), + }, + paste = { + ['+'] = require('vim.ui.clipboard.osc52').paste('+'), + ['*'] = require('vim.ui.clipboard.osc52').paste('*'), + }, + } +end + +-- require('custom.navigation') +-- require('custom.tabline').setup() diff --git a/lazy-lock.json b/lazy-lock.json deleted file mode 100644 index 2f44ad4..0000000 --- a/lazy-lock.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "conform.nvim": { "branch": "master", "commit": "9fd3d5e0b689ec1bf400c53cbbec72c6fdf24081" }, - "invero.nvim": { "branch": "main", "commit": "6acdefa2f2fdf544b53b5786e748be0ae8e9fd23" }, - "lazy.nvim": { "branch": "main", "commit": "1ea3c4085785f460fb0e46d2fe1ee895f5f9e7c1" }, - "mason.nvim": { "branch": "main", "commit": "ad7146aa61dcaeb54fa900144d768f040090bff0" }, - "nvim-autopairs": { "branch": "master", "commit": "7a2c97cccd60abc559344042fefb1d5a85b3e33b" }, - "nvim-lint": { "branch": "master", "commit": "9da1fb942dd0668d5182f9c8dee801b9c190e2bb" }, - "nvim-tree.lua": { "branch": "master", "commit": "7c0f7e906ab6f11b61eec52171eaf7dc06726ef1" }, - "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, - "nvim-ts-autotag": { "branch": "main", "commit": "c4ca798ab95b316a768d51eaaaee48f64a4a46bc" } -} diff --git a/lsp/eslint.lua b/lsp/eslint.lua index f80694c..4770e1f 100644 --- a/lsp/eslint.lua +++ b/lsp/eslint.lua @@ -1,3 +1,31 @@ +local utils = {} + +function utils.root_markers_with_field(root_files, new_names, field, fname) + local path = vim.fn.fnamemodify(fname, ':h') + local found = vim.fs.find(new_names, { path = path, upward = true }) + + for _, f in ipairs(found or {}) do + -- Match the given `field`. + for line in io.lines(f) do + if line:find(field) then + root_files[#root_files + 1] = vim.fs.basename(f) + break + end + end + end + + return root_files +end + +function utils.insert_package_json(root_files, field, fname) + return utils.root_markers_with_field( + root_files, + { 'package.json', 'package.json5' }, + field, + fname + ) +end + --- @brief --- --- https://github.com/hrsh7th/vscode-langservers-extracted @@ -39,7 +67,6 @@ --- --- /!\ When using flat config files, you need to use them across all your packages in your monorepo, as it's a global setting for the server. -local utils = require('utils') local lsp = vim.lsp local eslint_config_files = { diff --git a/lsp/tailwindcss.lua b/lsp/tailwindcss.lua index 3c432cd..1d523b6 100644 --- a/lsp/tailwindcss.lua +++ b/lsp/tailwindcss.lua @@ -1,10 +1,37 @@ +local utils = {} + +function utils.root_markers_with_field(root_files, new_names, field, fname) + local path = vim.fn.fnamemodify(fname, ':h') + local found = vim.fs.find(new_names, { path = path, upward = true }) + + for _, f in ipairs(found or {}) do + -- Match the given `field`. + for line in io.lines(f) do + if line:find(field) then + root_files[#root_files + 1] = vim.fs.basename(f) + break + end + end + end + + return root_files +end + +function utils.insert_package_json(root_files, field, fname) + return utils.root_markers_with_field( + root_files, + { 'package.json', 'package.json5' }, + field, + fname + ) +end + ---@brief --- https://github.com/tailwindlabs/tailwindcss-intellisense --- --- Tailwind CSS Language Server can be installed via npm: --- --- npm install -g @tailwindcss/language-server -local utils = require('utils') ---@type vim.lsp.Config return { diff --git a/lua/config/autocmds.lua b/lua/config/autocmds.lua deleted file mode 100644 index 53255d5..0000000 --- a/lua/config/autocmds.lua +++ /dev/null @@ -1,77 +0,0 @@ --- Automatically create a scratch buffer if Neovim starts with no files -vim.api.nvim_create_autocmd('VimEnter', { - callback = function() - -- Only trigger if no file arguments are passed - if vim.fn.argc() == 0 then - vim.cmd('enew') - vim.bo.buftype = 'nofile' - vim.bo.bufhidden = 'wipe' - vim.bo.swapfile = false - end - end, -}) - --- Highlight when yanking (copying) text -vim.api.nvim_create_autocmd('TextYankPost', { - callback = function() - vim.highlight.on_yank() - end, -}) - --- Disable comment continuation only when using 'o'/'O', but keep it for -vim.api.nvim_create_autocmd('FileType', { - pattern = '*', - callback = function() - vim.opt_local.formatoptions:remove('o') - end, -}) - --- Show cursor line only in active window -vim.api.nvim_create_autocmd({ 'WinEnter', 'InsertLeave' }, { - callback = function() - if vim.w.auto_cursorline then - vim.wo.cursorline = true - vim.w.auto_cursorline = nil - end - end, -}) - -vim.api.nvim_create_autocmd({ 'WinLeave', 'InsertEnter' }, { - callback = function() - if vim.bo.filetype == 'NvimTree' then - return - end - if vim.wo.cursorline then - vim.w.auto_cursorline = true - vim.wo.cursorline = false - end - end, -}) - --- Autocompletion -vim.api.nvim_create_autocmd('LspAttach', { - group = vim.api.nvim_create_augroup('minimal_lsp', { clear = true }), - callback = function(ev) - local client = vim.lsp.get_client_by_id(ev.data.client_id) - if not client then - return - end - - -- Enable native completion - if client:supports_method('textDocument/completion') then - vim.lsp.completion.enable(true, client.id, ev.buf, { autotrigger = true }) - end - end, -}) -vim.lsp.handlers['textDocument/completion'] = function(err, result, ctx, config) - if err or not result then - return - end - for _, item in ipairs(result.items or result) do - if item.kind then - local kind = vim.lsp.protocol.CompletionItemKind[item.kind] or '' - item.menu = '[' .. kind .. ']' - end - end - return vim.lsp.completion._on_completion_result(err, result, ctx, config) -end diff --git a/lua/config/options.lua b/lua/config/options.lua deleted file mode 100644 index 4c3d5ce..0000000 --- a/lua/config/options.lua +++ /dev/null @@ -1,109 +0,0 @@ --- Map Leader -vim.g.mapleader = ' ' -vim.g.maplocalleader = ' ' - --- Disable built-in plugins -vim.loader.enable() -vim.g.loaded_gzip = 1 -vim.g.loaded_tar = 1 -vim.g.loaded_tarPlugin = 1 -vim.g.loaded_zip = 1 -vim.g.loaded_zipPlugin = 1 -vim.g.loaded_getscript = 1 -vim.g.loaded_getscriptPlugin = 1 -vim.g.loaded_vimball = 1 -vim.g.loaded_vimballPlugin = 1 -vim.g.loaded_matchit = 1 -vim.g.loaded_2html_plugin = 1 -vim.g.loaded_rrhelper = 1 -vim.g.loaded_matchparen = 1 -vim.g.loaded_tutor_mode_plugin = 1 -vim.g.loaded_spellfile_plugin = 1 -vim.g.loaded_logipat = 1 -vim.g.loaded_rplugin = 1 -vim.g.loaded_netrw = 1 -- use nvim-tree instead -vim.g.loaded_netrwPlugin = 1 - --- UI -vim.g.have_nerd_font = true -vim.opt.termguicolors = false - -vim.opt.textwidth = 100 -vim.opt.colorcolumn = '+0' - -vim.opt.signcolumn = 'no' -vim.opt.number = true -vim.opt.relativenumber = true -vim.opt.cursorline = true -vim.opt.ruler = false -vim.opt.winborder = 'rounded' -vim.opt.guicursor = 'n-v-i-c:block' - -vim.opt.laststatus = 3 -vim.opt.statusline = '── %f %h%w%m%r %= [%l,%c-%L] ──' -vim.opt.fillchars = { - stl = '─', - stlnc = '─', - horiz = '─', - horizdown = '─', - horizup = '─', - vert = '│', - vertleft = '│', - vertright = '│', - verthoriz = '│', -} - --- Wrap -vim.opt.wrap = false -vim.opt.linebreak = true -vim.opt.breakindent = true - --- Indent -vim.opt.shiftwidth = 2 -- Number of spaces to use for (auto)indent -vim.opt.tabstop = 2 -- Number of spaces that a in file counts for -vim.opt.softtabstop = 2 -- Number of spaces when pressing in insert mode -vim.opt.expandtab = true -- Use spaces instead of literal tab characters -vim.opt.autoindent = true -- Copy indent from the current line when starting a new one -vim.opt.smartindent = true -- Automatically inserts indents in code blocks (for C-like languages) - --- Scroll and mouse -vim.opt.scrolloff = 10 -vim.opt.sidescrolloff = 5 -vim.opt.mousescroll = 'hor:1,ver:5' -vim.opt.mouse = 'a' -- Enable mouse support - --- Search -vim.opt.ignorecase = true -vim.opt.smartcase = true -- Override ignorecase if search contains upper case chars -vim.opt.inccommand = 'split' -- Live substitution preview -vim.opt.completeopt = { 'fuzzy', 'menuone', 'popup', 'noselect' } - --- Splits -vim.opt.splitright = true -vim.opt.splitbelow = true - --- Persistence -vim.opt.undofile = true -vim.opt.swapfile = false - --- Tweaks -vim.opt.updatetime = 1000 -vim.opt.timeout = true -vim.opt.ttimeout = true -vim.opt.timeoutlen = 500 -vim.opt.ttimeoutlen = 10 - --- Clipboard -if vim.env.CONTAINER then - vim.g.clipboard = { - name = 'osc52', - copy = { - ['+'] = require('vim.ui.clipboard.osc52').copy('+'), - ['*'] = require('vim.ui.clipboard.osc52').copy('*'), - }, - paste = { - ['+'] = require('vim.ui.clipboard.osc52').paste('+'), - ['*'] = require('vim.ui.clipboard.osc52').paste('*'), - }, - } -end diff --git a/lua/config/terminal.lua b/lua/config/terminal.lua deleted file mode 100644 index 675262e..0000000 --- a/lua/config/terminal.lua +++ /dev/null @@ -1,65 +0,0 @@ -local term_group = vim.api.nvim_create_augroup('custom-term', { clear = true }) - --- Custom terminal -vim.api.nvim_create_autocmd('TermOpen', { - group = term_group, - callback = function() - vim.opt_local.number = false - vim.opt_local.relativenumber = false - vim.opt_local.scrolloff = 0 - vim.bo.filetype = 'terminal' - vim.cmd.startinsert() - end, -}) - --- Insert when re-entering a terminal window (after switching back) -vim.api.nvim_create_autocmd('BufEnter', { - group = term_group, - pattern = 'term://*', - callback = function() - if vim.bo.buftype == 'terminal' and vim.fn.mode() ~= 'i' then - vim.cmd.startinsert() - end - end, -}) - --- Close all terminal buffers before quitting -vim.api.nvim_create_autocmd('QuitPre', { - group = term_group, - callback = function() - for _, buf in ipairs(vim.api.nvim_list_bufs()) do - if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].buftype == 'terminal' then - vim.api.nvim_buf_delete(buf, { force = true }) - end - end - end, -}) - -local commands = { - TermDefault = function() - vim.cmd('terminal') - end, - - TermRelative = function() - local shell = vim.o.shell or 'zsh' - local dir = vim.fn.expand('%:p:h') - vim.cmd(string.format('edit term://%s//%s', dir, shell)) - end, - - TermSplit = function() - vim.cmd('new') - vim.cmd('wincmd J') - vim.api.nvim_win_set_height(0, 12) - vim.wo.winfixheight = true - vim.cmd('term') - end, - - TermVSplit = function() - vim.cmd('vsplit') - vim.cmd('term') - end, -} - -for name, fn in pairs(commands) do - vim.api.nvim_create_user_command(name, fn, {}) -end diff --git a/lua/plugins/colorscheme.lua b/lua/plugins/colorscheme.lua deleted file mode 100644 index 65c3568..0000000 --- a/lua/plugins/colorscheme.lua +++ /dev/null @@ -1,36 +0,0 @@ -return { - 'triimdev/invero.nvim', - lazy = false, - priority = 1000, - config = function() - vim.api.nvim_create_user_command('ReloadInvero', function() - require('invero').invalidate_cache() - vim.cmd('Lazy reload invero.nvim') - end, {}) - - require('invero').setup({ - highlights = function(c, tool) - c.bg_float = tool(152) - return { - ModeMsg = { fg = c.yellow, bg = c.none, bold = true }, - WinSeparator = { fg = c.outline, bg = c.base }, - StatusLine = { fg = c.outline, bg = c.base }, - StatusLineNC = { fg = c.text, bg = c.base, bold = true }, - TabLine = { fg = c.muted, bg = c.none }, - TabLineSel = { fg = c.text, bg = c.none, bold = true }, - TabLineFill = { fg = c.outline_light, bg = c.none }, - - Pmenu = { fg = c.text, bg = c.surface }, - PmenuSel = { fg = c.text, bg = c.accent_light }, - QuickFixLine = { fg = c.accent, bg = c.none, bold = true }, - -- PmenuSbar = { bg = c.surface }, - -- PmenuThumb = { bg = c.outline }, - -- PmenuBorder = { fg = c.outline }, - } - end, - }) - - vim.o.background = 'light' - vim.cmd.colorscheme('invero') - end, -} diff --git a/lua/plugins/filetree.lua b/lua/plugins/filetree.lua deleted file mode 100644 index 01acb31..0000000 --- a/lua/plugins/filetree.lua +++ /dev/null @@ -1,147 +0,0 @@ -local function my_on_attach(bufnr) - local api = require('nvim-tree.api') - local opts = { buffer = bufnr } - - -- basics: copy/cut/paste/create/rename/remove - vim.keymap.set('n', 'c', api.fs.copy.node, opts) - vim.keymap.set('n', 'x', api.fs.cut, opts) - vim.keymap.set('n', 'p', api.fs.paste, opts) - vim.keymap.set('n', 'a', api.fs.create, opts) - vim.keymap.set('n', 'r', api.fs.rename, opts) - vim.keymap.set('n', 'R', api.fs.rename_basename, opts) - vim.keymap.set('n', 'd', api.fs.remove, opts) - - -- bulk mark and delete/move - vim.keymap.set('n', 's', api.marks.toggle, opts) - vim.keymap.set('n', 'S', api.marks.clear, opts) - vim.keymap.set('n', 'bd', api.marks.bulk.delete, opts) - vim.keymap.set('n', 'bm', api.marks.bulk.move, opts) - - -- copy filename/path - vim.keymap.set('n', 'y', api.fs.copy.filename, opts) - vim.keymap.set('n', 'Y', api.fs.copy.relative_path, opts) - vim.keymap.set('n', 'gy', api.fs.copy.absolute_path, opts) - vim.keymap.set('n', 'ge', api.fs.copy.basename, opts) - - -- expand/collapse - vim.keymap.set('n', 'e', api.tree.expand_all, opts) - vim.keymap.set('n', 'E', api.tree.collapse_all, opts) - - -- filter - vim.keymap.set('n', 'f', api.live_filter.start, opts) - vim.keymap.set('n', 'F', api.live_filter.clear, opts) - - -- navigate - vim.keymap.set('n', 'J', api.node.navigate.sibling.last, opts) - vim.keymap.set('n', 'K', api.node.navigate.sibling.first, opts) - vim.keymap.set('n', 'P', api.node.navigate.parent, opts) - - -- open - vim.keymap.set('n', '', api.node.open.edit, opts) - vim.keymap.set('n', 'o', api.node.open.edit, opts) - vim.keymap.set('n', 'O', api.node.open.no_window_picker, opts) - - -- miscellaneous - vim.keymap.set('n', 'K', api.node.show_info_popup, opts) - vim.keymap.set('n', 'U', api.tree.reload, opts) -end - -return { - 'https://gitea.tomastm.com/tomas.mirchev/nvim-tree.lua', - branch = 'master', - opts = { - on_attach = my_on_attach, - view = { signcolumn = 'no' }, - actions = { file_popup = { open_win_config = { border = 'rounded' } } }, - renderer = { - root_folder_label = false, - -- root_folder_label = function(path) - -- return '-- ' .. vim.fn.fnamemodify(path, ':t') .. ' --' - -- end, - special_files = {}, - - highlight_hidden = 'all', - highlight_clipboard = 'all', - - indent_markers = { - enable = true, - inline_arrows = false, - icons = { corner = '│', none = '│', bottom = ' ' }, - }, - icons = { - bookmarks_placement = 'after', - git_placement = 'after', - show = { - file = false, - folder = false, - folder_arrow = false, -- KEEP FALSE - git = true, - modified = false, - hidden = false, - diagnostics = false, - bookmarks = true, - }, - glyphs = { - -- default = '•', - default = ' ', - symlink = '', - bookmark = '󰆤', - modified = '●', - hidden = '󰜌', - folder = { - arrow_closed = '', - arrow_open = '', - default = '▸', - open = '▾', - empty = '', - empty_open = '', - symlink = '', - symlink_open = '', - }, - - git = { - unstaged = '◇', -- '✗', - staged = '', - unmerged = '', - renamed = '', - untracked = '', - deleted = '', -- '󰧧', - ignored = '', - }, - }, - }, - }, - - hijack_cursor = true, - prefer_startup_root = true, - update_focused_file = { - enable = true, - update_root = { enable = true, ignore_list = {} }, - exclude = false, - }, - modified = { enable = true, show_on_dirs = true, show_on_open_dirs = true }, - filters = { - enable = true, - git_ignored = true, - dotfiles = false, - git_clean = false, - no_buffer = false, - no_bookmark = false, - custom = {}, - exclude = {}, - }, - filesystem_watchers = { - enable = true, - debounce_delay = 50, - ignore_dirs = { - '/.git', - '/.DS_Store', - '/build', - '/dist', - '/public', - '/node_modules', - '/target', - }, - }, - }, -} diff --git a/lua/custom/language-manager.lua b/lua/plugins/language-manager.lua similarity index 65% rename from lua/custom/language-manager.lua rename to lua/plugins/language-manager.lua index 48d86f0..a65a6f2 100644 --- a/lua/custom/language-manager.lua +++ b/lua/plugins/language-manager.lua @@ -8,7 +8,16 @@ lm.delete_cache() lm.enable() --]] + +vim.pack.add({ 'https://github.com/mason-org/mason.nvim' }) + local M = {} +M.lsp = {} +M.ts = {} +M.lint = {} +M.format = {} + +M.group = vim.api.nvim_create_augroup('language-manager', { clear = true }) local function wrap(item) if type(item) == 'string' then @@ -35,19 +44,19 @@ local function create_set() end local function create_spec() - local M = {} + local S = {} local unique = create_set() local specs = {} - function M.set(item, group) + function S.set(item, group) specs[group] = item end - function M.get() + function S.get() return specs end - function M.add(list, ...) + function S.add(list, ...) local groups = { ... } local ref = table.concat(groups, '.') local pointer = specs @@ -74,7 +83,7 @@ local function create_spec() end end - return M + return S end function M.generate_specs(specs_raw) @@ -104,25 +113,58 @@ function M.generate_specs(specs_raw) return M.general end -M.lsp = {} -function M.lsp.enable() +function M.lsp.setup() + require('mason').setup() local registry = require('mason-registry') - local lsp_configs = {} for _, lsp in ipairs(M.general.language_servers) do if registry.has_package(lsp) then local pkg = registry.get_package(lsp) local spec = pkg.spec and pkg.spec.neovim local lsp_name = (spec and spec.lspconfig) or lsp - table.insert(lsp_configs, lsp_name) vim.lsp.enable(lsp_name) else vim.notify('Unknown LSP: ' .. lsp, vim.log.levels.WARN) end end +end - vim.notify('Enabled LSPs: ' .. table.concat(lsp_configs, ', '), vim.log.levels.INFO) +function M.ts.setup() + require('nvim-treesitter.configs').setup({ + highlight = { enable = true }, + incremental_selection = { enable = true }, + }) +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 + vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, { + group = vim.api.nvim_create_augroup('language-manager.lint', { clear = true }), + callback = function() + lint.try_lint() + end, + }) + end, + }) +end + +function M.format.setup() + vim.api.nvim_create_autocmd('BufWritePre', { + group = M.group, + once = true, + callback = function() + 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, + }) + end, + }) end return M diff --git a/lua/custom/navigation.lua b/lua/plugins/navigation.lua similarity index 100% rename from lua/custom/navigation.lua rename to lua/plugins/navigation.lua diff --git a/lua/plugins/syntax.lua b/lua/plugins/syntax.lua deleted file mode 100644 index 3ca2f6f..0000000 --- a/lua/plugins/syntax.lua +++ /dev/null @@ -1,67 +0,0 @@ -local lm = require('custom.language-manager') -local language_specs = { - { ts = { 'yaml', 'toml', 'sql', 'diff', 'dockerfile', 'gitcommit', 'gitignore' } }, - { ts = { 'c', 'cpp', 'go', 'rust', 'python' } }, - - { ft = 'markdown', ts = { 'markdown', 'markdown_inline' }, format = 'prettier' }, - { ft = 'bash', lsp = 'bash-language-server', lint = 'shellcheck', format = 'shfmt' }, - { ft = 'lua', lsp = 'lua-language-server', lint = 'luacheck', format = 'stylua' }, - { ft = { 'json', 'jsonc' }, lsp = 'json-lsp' }, - { ft = 'html', lsp = 'html-lsp' }, - { ft = 'css', lsp = { 'css-lsp', 'tailwindcss-language-server' } }, - { - ft = { 'javascript', 'typescript', 'javascriptreact', 'typescriptreact' }, - ts = { 'javascript', 'typescript', 'tsx' }, - lsp = { 'vtsls', 'eslint-lsp' }, - format = { 'prettierd', 'prettier' }, - }, -} - -local general = lm.generate_specs(language_specs) - -return { - { 'windwp/nvim-ts-autotag', config = true }, - { 'windwp/nvim-autopairs', event = 'InsertEnter', config = true }, - { - 'mason-org/mason.nvim', - config = function() - require('mason').setup() - lm.lsp.enable() - end, - }, - { - 'nvim-treesitter/nvim-treesitter', - build = ':TSUpdate', - main = 'nvim-treesitter.configs', - opts = { - highlight = { enable = true }, - incremental_selection = { enable = true }, - ensure_installed = general.ts_parsers, - }, - }, - { - 'mfussenegger/nvim-lint', - event = { 'BufReadPre', 'BufNewFile' }, - opts = { linters_by_ft = general.linters_by_ft }, - config = function(_, opts) - local lint = require('lint') - lint.linters_by_ft = opts.linters_by_ft - - vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, { - group = vim.api.nvim_create_augroup('lint_autocmd', { clear = true }), - callback = function() - lint.try_lint() - end, - }) - end, - }, - { - 'stevearc/conform.nvim', - event = { 'BufWritePre' }, - opts = { - format_on_save = { timeout_ms = 500, lsp_format = 'fallback' }, - default_format_opts = { stop_after_first = true }, - formatters_by_ft = general.formatters_by_ft, - }, - }, -} diff --git a/lua/custom/tabline.lua b/lua/plugins/tabline.lua similarity index 90% rename from lua/custom/tabline.lua rename to lua/plugins/tabline.lua index 411024a..129273e 100644 --- a/lua/custom/tabline.lua +++ b/lua/plugins/tabline.lua @@ -1,6 +1,3 @@ --- ~/.config/nvim/lua/custom/tabline.lua --- Custom Lua tabline with proper modified/unsaved and window count handling - local M = {} -- Helper to get label for each tab page @@ -80,7 +77,7 @@ end function M.setup() vim.o.showtabline = 1 - vim.o.tabline = "%!v:lua.require'custom.tabline'.tabline()" + vim.o.tabline = "%!v:lua.require'plugins.tabline'.tabline()" end return M diff --git a/lua/setup/init.lua b/lua/setup/init.lua new file mode 100644 index 0000000..8aa769a --- /dev/null +++ b/lua/setup/init.lua @@ -0,0 +1 @@ +vim.pack.add({ 'https://github.com/mason-org/mason.nvim' }) diff --git a/lua/custom/lm-cmds.lua b/lua/setup/lm-cmds.lua similarity index 100% rename from lua/custom/lm-cmds.lua rename to lua/setup/lm-cmds.lua diff --git a/lua/config/diagnostics.lua b/lua/triimd/diagnostics.lua similarity index 100% rename from lua/config/diagnostics.lua rename to lua/triimd/diagnostics.lua diff --git a/lua/triimd/filetree.lua b/lua/triimd/filetree.lua new file mode 100644 index 0000000..0daf633 --- /dev/null +++ b/lua/triimd/filetree.lua @@ -0,0 +1,127 @@ +vim.pack.add({ + { src = 'https://gitea.tomastm.com/tomas.mirchev/nvim-tree.lua', version = 'master' }, +}) + +local function my_on_attach(bufnr) + local api = require('nvim-tree.api') + local opts = { buffer = bufnr } + + -- basics: copy/cut/paste/create/rename/remove + vim.keymap.set('n', 'c', api.fs.copy.node, opts) + vim.keymap.set('n', 'x', api.fs.cut, opts) + vim.keymap.set('n', 'p', api.fs.paste, opts) + vim.keymap.set('n', 'a', api.fs.create, opts) + vim.keymap.set('n', 'r', api.fs.rename, opts) + vim.keymap.set('n', 'R', api.fs.rename_basename, opts) + vim.keymap.set('n', 'd', api.fs.remove, opts) + + -- bulk mark and delete/move + vim.keymap.set('n', 's', api.marks.toggle, opts) + vim.keymap.set('n', 'S', api.marks.clear, opts) + vim.keymap.set('n', 'bd', api.marks.bulk.delete, opts) + vim.keymap.set('n', 'bm', api.marks.bulk.move, opts) + + -- copy filename/path + vim.keymap.set('n', 'y', api.fs.copy.filename, opts) + vim.keymap.set('n', 'Y', api.fs.copy.relative_path, opts) + vim.keymap.set('n', 'gy', api.fs.copy.absolute_path, opts) + vim.keymap.set('n', 'ge', api.fs.copy.basename, opts) + + -- expand/collapse + vim.keymap.set('n', 'e', api.tree.expand_all, opts) + vim.keymap.set('n', 'E', api.tree.collapse_all, opts) + + -- filter + vim.keymap.set('n', 'f', api.live_filter.start, opts) + vim.keymap.set('n', 'F', api.live_filter.clear, opts) + + -- navigate + vim.keymap.set('n', 'J', api.node.navigate.sibling.last, opts) + vim.keymap.set('n', 'K', api.node.navigate.sibling.first, opts) + vim.keymap.set('n', 'P', api.node.navigate.parent, opts) + + -- open + vim.keymap.set('n', '', api.node.open.edit, opts) + vim.keymap.set('n', 'o', api.node.open.edit, opts) + vim.keymap.set('n', 'O', api.node.open.no_window_picker, opts) + + -- miscellaneous + vim.keymap.set('n', 'K', api.node.show_info_popup, opts) + vim.keymap.set('n', 'U', api.tree.reload, opts) +end + +require('nvim-tree').setup({ + on_attach = my_on_attach, + view = { signcolumn = 'no' }, + actions = { file_popup = { open_win_config = { border = 'rounded' } } }, + renderer = { + root_folder_label = false, + special_files = {}, + + highlight_hidden = 'all', + highlight_clipboard = 'all', + + indent_markers = { + enable = true, + inline_arrows = false, + icons = { corner = '│', none = '│', bottom = ' ' }, + }, + icons = { + bookmarks_placement = 'after', + git_placement = 'after', + show = { + file = false, + folder = false, + folder_arrow = false, + git = true, + modified = false, + hidden = false, + diagnostics = false, + bookmarks = true, + }, + glyphs = { + git = { + unstaged = '◇', + staged = '', + unmerged = '', + renamed = '', + untracked = '', + deleted = '', + ignored = '', + }, + }, + }, + }, + + hijack_cursor = true, + prefer_startup_root = true, + update_focused_file = { + enable = true, + update_root = { enable = true, ignore_list = {} }, + exclude = false, + }, + modified = { enable = true, show_on_dirs = true, show_on_open_dirs = true }, + filters = { + enable = true, + git_ignored = true, + dotfiles = false, + git_clean = false, + no_buffer = false, + no_bookmark = false, + custom = {}, + exclude = {}, + }, + filesystem_watchers = { + enable = true, + debounce_delay = 50, + ignore_dirs = { + '/.git', + '/.DS_Store', + '/build', + '/dist', + '/public', + '/node_modules', + '/target', + }, + }, +}) diff --git a/lua/config/keymaps.lua b/lua/triimd/keymaps.lua similarity index 98% rename from lua/config/keymaps.lua rename to lua/triimd/keymaps.lua index 837f9a4..54ef101 100644 --- a/lua/config/keymaps.lua +++ b/lua/triimd/keymaps.lua @@ -9,6 +9,7 @@ end -- QOL map('i', 'jk', '') +map('i', '', '') map('n', '', cmd('nohlsearch')) map('n', 'q:', '') diff --git a/lua/triimd/syntax.lua b/lua/triimd/syntax.lua new file mode 100644 index 0000000..c1a2a46 --- /dev/null +++ b/lua/triimd/syntax.lua @@ -0,0 +1,26 @@ +local language_specs = { + { ts = { 'yaml', 'toml', 'sql', 'diff', 'dockerfile', 'gitcommit', 'gitignore' } }, + { ts = { 'c', 'cpp', 'go', 'rust', 'python' } }, + + { ft = 'markdown', ts = { 'markdown', 'markdown_inline' }, format = 'prettier' }, + { ft = 'bash', lsp = 'bash-language-server', lint = 'shellcheck', format = 'shfmt' }, + { ft = 'lua', lsp = 'lua-language-server', lint = 'luacheck', format = 'stylua' }, + { ft = { 'json', 'jsonc' }, lsp = 'json-lsp' }, + { ft = 'html', lsp = 'html-lsp' }, + { ft = 'css', lsp = { 'css-lsp', 'tailwindcss-language-server' } }, + { + ft = { 'javascript', 'typescript', 'javascriptreact', 'typescriptreact' }, + ts = { 'javascript', 'typescript', 'tsx' }, + lsp = { 'vtsls', 'eslint-lsp' }, + format = { 'prettierd', 'prettier' }, + }, +} + +local lm = require('plugins.language-manager') + +lm.generate_specs(language_specs) + +lm.ts.setup() +lm.lsp.setup() +lm.lint.setup() +lm.format.setup() diff --git a/lua/utils.lua b/lua/utils.lua deleted file mode 100644 index 6105bde..0000000 --- a/lua/utils.lua +++ /dev/null @@ -1,33 +0,0 @@ -local M = {} - ---- Appends `new_names` to `root_files` if `field` is found in any such file in any ancestor of `fname`. ---- ---- NOTE: this does a "breadth-first" search, so is broken for multi-project workspaces: ---- https://github.com/neovim/nvim-lspconfig/issues/3818#issuecomment-2848836794 ---- ---- @param root_files string[] List of root-marker files to append to. ---- @param new_names string[] Potential root-marker filenames (e.g. `{ 'package.json', 'package.json5' }`) to inspect for the given `field`. ---- @param field string Field to search for in the given `new_names` files. ---- @param fname string Full path of the current buffer name to start searching upwards from. -function M.root_markers_with_field(root_files, new_names, field, fname) - local path = vim.fn.fnamemodify(fname, ':h') - local found = vim.fs.find(new_names, { path = path, upward = true }) - - for _, f in ipairs(found or {}) do - -- Match the given `field`. - for line in io.lines(f) do - if line:find(field) then - root_files[#root_files + 1] = vim.fs.basename(f) - break - end - end - end - - return root_files -end - -function M.insert_package_json(root_files, field, fname) - return M.root_markers_with_field(root_files, { 'package.json', 'package.json5' }, field, fname) -end - -return M diff --git a/nvim-pack-lock.json b/nvim-pack-lock.json new file mode 100644 index 0000000..2f2f13b --- /dev/null +++ b/nvim-pack-lock.json @@ -0,0 +1,43 @@ +{ + "plugins": { + "conform.nvim": { + "rev": "9fd3d5e", + "src": "https://github.com/stevearc/conform.nvim" + }, + "invero.nvim": { + "rev": "6acdefa", + "src": "https://github.com/triimd/invero.nvim" + }, + "mason.nvim": { + "rev": "ad7146a", + "src": "https://github.com/mason-org/mason.nvim" + }, + "nvim-autopairs": { + "rev": "7a2c97c", + "src": "https://github.com/windwp/nvim-autopairs" + }, + "nvim-lint": { + "rev": "9da1fb9", + "src": "https://github.com/mfussenegger/nvim-lint" + }, + "nvim-tree": { + "rev": "7c0f7e9", + "src": "https://gitea.tomastm.com/tomas.mirchev/nvim-tree.lua", + "version": "'master'" + }, + "nvim-tree.lua": { + "rev": "7c0f7e9", + "src": "https://gitea.tomastm.com/tomas.mirchev/nvim-tree.lua", + "version": "'master'" + }, + "nvim-treesitter": { + "rev": "42fc28ba", + "src": "https://github.com/nvim-treesitter/nvim-treesitter", + "version": "'master'" + }, + "nvim-ts-autotag": { + "rev": "c4ca798", + "src": "https://github.com/windwp/nvim-ts-autotag" + } + } +} \ No newline at end of file diff --git a/plugin/colorscheme.lua b/plugin/colorscheme.lua new file mode 100644 index 0000000..9eb9dcd --- /dev/null +++ b/plugin/colorscheme.lua @@ -0,0 +1,29 @@ +vim.pack.add({ 'https://github.com/triimd/invero.nvim' }) + +require('invero').setup({ + highlights = function(c, tool) + c.bg_float = tool(152) + return { + ModeMsg = { fg = c.yellow, bg = c.none, bold = true }, + WinSeparator = { fg = c.outline, bg = c.base }, + StatusLine = { fg = c.outline, bg = c.base }, + StatusLineNC = { fg = c.text, bg = c.base, bold = true }, + TabLine = { fg = c.muted, bg = c.none }, + TabLineSel = { fg = c.text, bg = c.none, bold = true }, + TabLineFill = { fg = c.outline_light, bg = c.none }, + Pmenu = { fg = c.text, bg = c.surface }, + PmenuSel = { fg = c.text, bg = c.accent_light }, + QuickFixLine = { fg = c.accent, bg = c.none, bold = true }, + } + end, +}) + +vim.o.background = 'light' +vim.cmd.colorscheme('invero') + +vim.api.nvim_create_user_command('ReloadInvero', function() + require('invero').invalidate_cache() + package.loaded['invero'] = nil + require('invero') + vim.cmd.colorscheme('invero') +end, {}) diff --git a/plugin/events.lua b/plugin/events.lua new file mode 100644 index 0000000..ff11e0d --- /dev/null +++ b/plugin/events.lua @@ -0,0 +1,180 @@ +local api = vim.api +local au = api.nvim_create_autocmd +local group = api.nvim_create_augroup('triimd.events', { clear = true }) + +-- Automatically create a scratch buffer if Neovim starts with no files +au('VimEnter', { + group = group, + callback = function() + -- Only trigger if no file arguments are passed + if vim.fn.argc() == 0 then + vim.cmd('enew') + vim.bo.buftype = 'nofile' + vim.bo.bufhidden = 'wipe' + vim.bo.swapfile = false + end + end, +}) + +-- Highlight when yanking (copying) text +au('TextYankPost', { + group = group, + callback = function() + vim.highlight.on_yank({ timeout = 400 }) + end, +}) + +-- Disable comment continuation only when using 'o'/'O', but keep it for +au('FileType', { + group = group, + pattern = '*', + callback = function() + vim.opt_local.formatoptions:remove('o') + end, +}) + +-- Show cursor line only in active window +au({ 'WinEnter', 'InsertLeave' }, { + group = group, + callback = function() + if vim.w.auto_cursorline then + vim.wo.cursorline = true + vim.w.auto_cursorline = nil + end + end, +}) + +au({ 'WinLeave', 'InsertEnter' }, { + group = group, + callback = function() + if vim.bo.filetype == 'NvimTree' then + return + end + if vim.wo.cursorline then + vim.w.auto_cursorline = true + vim.wo.cursorline = false + end + end, +}) + +-- Autocompletion +au('LspAttach', { + group = group, + callback = function(ev) + local client = vim.lsp.get_client_by_id(ev.data.client_id) + if not client then + return + end + + -- Enable native completion + if client:supports_method('textDocument/completion') then + vim.lsp.completion.enable(true, client.id, ev.buf, { autotrigger = true }) + end + end, +}) +vim.lsp.handlers['textDocument/completion'] = function(err, result, ctx, config) + if err or not result then + return + end + for _, item in ipairs(result.items or result) do + if item.kind then + local kind = vim.lsp.protocol.CompletionItemKind[item.kind] or '' + item.menu = '[' .. kind .. ']' + end + end + return vim.lsp.completion._on_completion_result(err, result, ctx, config) +end + +-- Custom terminal +au('TermOpen', { + group = group, + callback = function() + vim.opt_local.number = false + vim.opt_local.relativenumber = false + vim.opt_local.scrolloff = 0 + vim.bo.filetype = 'terminal' + vim.cmd.startinsert() + end, +}) + +-- Insert when re-entering a terminal window (after switching back) +au('BufEnter', { + group = group, + pattern = 'term://*', + callback = function() + if vim.bo.buftype == 'terminal' and vim.fn.mode() ~= 'i' then + vim.cmd.startinsert() + end + end, +}) + +-- Close all terminal buffers before quitting +au('QuitPre', { + group = group, + callback = function() + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].buftype == 'terminal' then + vim.api.nvim_buf_delete(buf, { force = true }) + end + end + end, +}) + +au('InsertEnter', { + group = group, + once = true, + callback = function() + require('nvim-ts-autotag').setup() + require('nvim-autopairs').setup() + end, +}) + +au('UIEnter', { + group = group, + once = true, + callback = function() + vim.schedule(function() + -- start ----- + + local commands = { + TermDefault = function() + vim.cmd('terminal') + end, + + TermRelative = function() + local shell = vim.o.shell or 'zsh' + local dir = vim.fn.expand('%:p:h') + vim.cmd(string.format('edit term://%s//%s', dir, shell)) + end, + + TermSplit = function() + vim.cmd('new') + vim.cmd('wincmd J') + vim.api.nvim_win_set_height(0, 12) + vim.wo.winfixheight = true + vim.cmd('term') + end, + + TermVSplit = function() + vim.cmd('vsplit') + vim.cmd('term') + end, + } + + for name, fn in pairs(commands) do + vim.api.nvim_create_user_command(name, fn, {}) + end + + require('triimd.keymaps') + require('triimd.filetree') + require('triimd.syntax') + require('triimd.diagnostics') + + require('plugins.navigation') + require('plugins.tabline').setup() + + + -- end ----- + end) + end, +}) diff --git a/plugin/packages.lua b/plugin/packages.lua new file mode 100644 index 0000000..ced32bd --- /dev/null +++ b/plugin/packages.lua @@ -0,0 +1,8 @@ +vim.pack.add({ + 'https://github.com/windwp/nvim-ts-autotag', + 'https://github.com/windwp/nvim-autopairs', + + { src = 'https://github.com/nvim-treesitter/nvim-treesitter', version = 'master' }, + 'https://github.com/mfussenegger/nvim-lint', + 'https://github.com/stevearc/conform.nvim', +}) -- 2.45.2 From 2692b9510908c0875a577751392224a6a42dd0e5 Mon Sep 17 00:00:00 2001 From: Tomas Mirchev Date: Sat, 25 Oct 2025 07:43:00 +0300 Subject: [PATCH 2/9] non-working checkpoint --- init.lua | 119 +------ {plugin => lua/core}/events.lua | 81 +---- lua/{triimd => core}/keymaps.lua | 3 +- lua/core/options.lua | 109 +++++++ lua/{triimd => modules}/diagnostics.lua | 0 .../syntax.lua => modules/language-specs.lua} | 15 +- lua/modules/navigation.lua | 2 + lua/modules/terminal.lua | 67 ++++ lua/modules/theme.lua | 32 ++ lua/{triimd => plugins}/filetree.lua | 4 - lua/plugins/{navigation.lua => finder.lua} | 0 lua/plugins/language-manager.lua | 306 +++++++++++++++--- lua/plugins/tabline.lua | 4 +- lua/setup/init.lua | 92 +++++- lua/setup/lm-cmds.lua | 98 ------ lua/setup/packages.lua | 20 ++ nvim-pack-lock.json | 36 --- plugin/colorscheme.lua | 29 -- plugin/packages.lua | 8 - scripts/install_all | 5 + 20 files changed, 601 insertions(+), 429 deletions(-) rename {plugin => lua/core}/events.lua (55%) rename lua/{triimd => core}/keymaps.lua (98%) create mode 100644 lua/core/options.lua rename lua/{triimd => modules}/diagnostics.lua (100%) rename lua/{triimd/syntax.lua => modules/language-specs.lua} (81%) create mode 100644 lua/modules/navigation.lua create mode 100644 lua/modules/terminal.lua create mode 100644 lua/modules/theme.lua rename lua/{triimd => plugins}/filetree.lua (96%) rename lua/plugins/{navigation.lua => finder.lua} (100%) delete mode 100644 lua/setup/lm-cmds.lua create mode 100644 lua/setup/packages.lua delete mode 100644 plugin/colorscheme.lua delete mode 100644 plugin/packages.lua create mode 100755 scripts/install_all diff --git a/init.lua b/init.lua index 243a33a..9db19c0 100644 --- a/init.lua +++ b/init.lua @@ -1,112 +1,11 @@ --- Map Leader -vim.g.mapleader = ' ' -vim.g.maplocalleader = ' ' - --- Disable built-in plugins -vim.loader.enable() -vim.g.loaded_gzip = 1 -vim.g.loaded_tar = 1 -vim.g.loaded_tarPlugin = 1 -vim.g.loaded_zip = 1 -vim.g.loaded_zipPlugin = 1 -vim.g.loaded_getscript = 1 -vim.g.loaded_getscriptPlugin = 1 -vim.g.loaded_vimball = 1 -vim.g.loaded_vimballPlugin = 1 -vim.g.loaded_matchit = 1 -vim.g.loaded_2html_plugin = 1 -vim.g.loaded_rrhelper = 1 -vim.g.loaded_matchparen = 1 -vim.g.loaded_tutor_mode_plugin = 1 -vim.g.loaded_spellfile_plugin = 1 -vim.g.loaded_logipat = 1 -vim.g.loaded_rplugin = 1 -vim.g.loaded_netrw = 1 -- use nvim-tree instead -vim.g.loaded_netrwPlugin = 1 - --- UI -vim.g.have_nerd_font = true -vim.opt.termguicolors = false - -vim.opt.textwidth = 100 -vim.opt.colorcolumn = '+0' - -vim.opt.signcolumn = 'no' -vim.opt.number = true -vim.opt.relativenumber = true -vim.opt.cursorline = true -vim.opt.ruler = false -vim.opt.winborder = 'rounded' -vim.opt.guicursor = 'n-v-i-c:block' - -vim.opt.laststatus = 3 -vim.opt.statusline = '── %f %h%w%m%r %= [%l,%c-%L] ──' -vim.opt.fillchars = { - stl = '─', - stlnc = '─', - horiz = '─', - horizdown = '─', - horizup = '─', - vert = '│', - vertleft = '│', - vertright = '│', - verthoriz = '│', -} - --- Wrap -vim.opt.wrap = false -vim.opt.linebreak = true -vim.opt.breakindent = true - --- Indent -vim.opt.shiftwidth = 2 -- Number of spaces to use for (auto)indent -vim.opt.tabstop = 2 -- Number of spaces that a in file counts for -vim.opt.softtabstop = 2 -- Number of spaces when pressing in insert mode -vim.opt.expandtab = true -- Use spaces instead of literal tab characters -vim.opt.autoindent = true -- Copy indent from the current line when starting a new one -vim.opt.smartindent = true -- Automatically inserts indents in code blocks (for C-like languages) - --- Scroll and mouse -vim.opt.scrolloff = 10 -vim.opt.sidescrolloff = 5 -vim.opt.mousescroll = 'hor:1,ver:5' -vim.opt.mouse = 'a' -- Enable mouse support - --- Search -vim.opt.ignorecase = true -vim.opt.smartcase = true -- Override ignorecase if search contains upper case chars -vim.opt.inccommand = 'split' -- Live substitution preview -vim.opt.completeopt = { 'fuzzy', 'menuone', 'popup', 'noselect' } - --- Splits -vim.opt.splitright = true -vim.opt.splitbelow = true - --- Persistence -vim.opt.undofile = true -vim.opt.swapfile = false - --- Tweaks -vim.opt.updatetime = 1000 -vim.opt.timeout = true -vim.opt.ttimeout = true -vim.opt.timeoutlen = 500 -vim.opt.ttimeoutlen = 10 - --- Clipboard -if vim.env.CONTAINER then - vim.g.clipboard = { - name = 'osc52', - copy = { - ['+'] = require('vim.ui.clipboard.osc52').copy('+'), - ['*'] = require('vim.ui.clipboard.osc52').copy('*'), - }, - paste = { - ['+'] = require('vim.ui.clipboard.osc52').paste('+'), - ['*'] = require('vim.ui.clipboard.osc52').paste('*'), - }, - } +if #vim.api.nvim_list_uis() == 0 then + print('--> Running neovim in headless mode') + require('setup') + return end --- require('custom.navigation') --- require('custom.tabline').setup() +vim.env.PATH = vim.fn.stdpath('data') .. '/mason/bin:' .. vim.env.PATH + +require('core.options') +require('core.keymaps') +require('core.events') diff --git a/plugin/events.lua b/lua/core/events.lua similarity index 55% rename from plugin/events.lua rename to lua/core/events.lua index ff11e0d..a8299e4 100644 --- a/plugin/events.lua +++ b/lua/core/events.lua @@ -1,6 +1,6 @@ local api = vim.api local au = api.nvim_create_autocmd -local group = api.nvim_create_augroup('triimd.events', { clear = true }) +local group = api.nvim_create_augroup('core.events', { clear = true }) -- Automatically create a scratch buffer if Neovim starts with no files au('VimEnter', { @@ -85,41 +85,6 @@ vim.lsp.handlers['textDocument/completion'] = function(err, result, ctx, config) return vim.lsp.completion._on_completion_result(err, result, ctx, config) end --- Custom terminal -au('TermOpen', { - group = group, - callback = function() - vim.opt_local.number = false - vim.opt_local.relativenumber = false - vim.opt_local.scrolloff = 0 - vim.bo.filetype = 'terminal' - vim.cmd.startinsert() - end, -}) - --- Insert when re-entering a terminal window (after switching back) -au('BufEnter', { - group = group, - pattern = 'term://*', - callback = function() - if vim.bo.buftype == 'terminal' and vim.fn.mode() ~= 'i' then - vim.cmd.startinsert() - end - end, -}) - --- Close all terminal buffers before quitting -au('QuitPre', { - group = group, - callback = function() - for _, buf in ipairs(vim.api.nvim_list_bufs()) do - if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].buftype == 'terminal' then - vim.api.nvim_buf_delete(buf, { force = true }) - end - end - end, -}) - au('InsertEnter', { group = group, once = true, @@ -129,52 +94,14 @@ au('InsertEnter', { end, }) + require('modules.theme') au('UIEnter', { group = group, once = true, callback = function() vim.schedule(function() - -- start ----- - - local commands = { - TermDefault = function() - vim.cmd('terminal') - end, - - TermRelative = function() - local shell = vim.o.shell or 'zsh' - local dir = vim.fn.expand('%:p:h') - vim.cmd(string.format('edit term://%s//%s', dir, shell)) - end, - - TermSplit = function() - vim.cmd('new') - vim.cmd('wincmd J') - vim.api.nvim_win_set_height(0, 12) - vim.wo.winfixheight = true - vim.cmd('term') - end, - - TermVSplit = function() - vim.cmd('vsplit') - vim.cmd('term') - end, - } - - for name, fn in pairs(commands) do - vim.api.nvim_create_user_command(name, fn, {}) - end - - require('triimd.keymaps') - require('triimd.filetree') - require('triimd.syntax') - require('triimd.diagnostics') - - require('plugins.navigation') - require('plugins.tabline').setup() - - - -- end ----- + require('modules.navigation') + require('plugins.language-manager').setup() end) end, }) diff --git a/lua/triimd/keymaps.lua b/lua/core/keymaps.lua similarity index 98% rename from lua/triimd/keymaps.lua rename to lua/core/keymaps.lua index 54ef101..906891a 100644 --- a/lua/triimd/keymaps.lua +++ b/lua/core/keymaps.lua @@ -81,9 +81,8 @@ map('n', '[d', function() end) map('n', 'q', vim.diagnostic.setloclist) map('n', 'd', vim.diagnostic.open_float) -map('n', 's', vim.lsp.buf.signature_help) +map('n', 'K', vim.lsp.buf.hover) map('n', 'gd', vim.lsp.buf.definition) map('n', 'gr', vim.lsp.buf.references) -map('n', 'K', vim.lsp.buf.hover) map('n', '', vim.lsp.buf.signature_help) diff --git a/lua/core/options.lua b/lua/core/options.lua new file mode 100644 index 0000000..4c3d5ce --- /dev/null +++ b/lua/core/options.lua @@ -0,0 +1,109 @@ +-- Map Leader +vim.g.mapleader = ' ' +vim.g.maplocalleader = ' ' + +-- Disable built-in plugins +vim.loader.enable() +vim.g.loaded_gzip = 1 +vim.g.loaded_tar = 1 +vim.g.loaded_tarPlugin = 1 +vim.g.loaded_zip = 1 +vim.g.loaded_zipPlugin = 1 +vim.g.loaded_getscript = 1 +vim.g.loaded_getscriptPlugin = 1 +vim.g.loaded_vimball = 1 +vim.g.loaded_vimballPlugin = 1 +vim.g.loaded_matchit = 1 +vim.g.loaded_2html_plugin = 1 +vim.g.loaded_rrhelper = 1 +vim.g.loaded_matchparen = 1 +vim.g.loaded_tutor_mode_plugin = 1 +vim.g.loaded_spellfile_plugin = 1 +vim.g.loaded_logipat = 1 +vim.g.loaded_rplugin = 1 +vim.g.loaded_netrw = 1 -- use nvim-tree instead +vim.g.loaded_netrwPlugin = 1 + +-- UI +vim.g.have_nerd_font = true +vim.opt.termguicolors = false + +vim.opt.textwidth = 100 +vim.opt.colorcolumn = '+0' + +vim.opt.signcolumn = 'no' +vim.opt.number = true +vim.opt.relativenumber = true +vim.opt.cursorline = true +vim.opt.ruler = false +vim.opt.winborder = 'rounded' +vim.opt.guicursor = 'n-v-i-c:block' + +vim.opt.laststatus = 3 +vim.opt.statusline = '── %f %h%w%m%r %= [%l,%c-%L] ──' +vim.opt.fillchars = { + stl = '─', + stlnc = '─', + horiz = '─', + horizdown = '─', + horizup = '─', + vert = '│', + vertleft = '│', + vertright = '│', + verthoriz = '│', +} + +-- Wrap +vim.opt.wrap = false +vim.opt.linebreak = true +vim.opt.breakindent = true + +-- Indent +vim.opt.shiftwidth = 2 -- Number of spaces to use for (auto)indent +vim.opt.tabstop = 2 -- Number of spaces that a in file counts for +vim.opt.softtabstop = 2 -- Number of spaces when pressing in insert mode +vim.opt.expandtab = true -- Use spaces instead of literal tab characters +vim.opt.autoindent = true -- Copy indent from the current line when starting a new one +vim.opt.smartindent = true -- Automatically inserts indents in code blocks (for C-like languages) + +-- Scroll and mouse +vim.opt.scrolloff = 10 +vim.opt.sidescrolloff = 5 +vim.opt.mousescroll = 'hor:1,ver:5' +vim.opt.mouse = 'a' -- Enable mouse support + +-- Search +vim.opt.ignorecase = true +vim.opt.smartcase = true -- Override ignorecase if search contains upper case chars +vim.opt.inccommand = 'split' -- Live substitution preview +vim.opt.completeopt = { 'fuzzy', 'menuone', 'popup', 'noselect' } + +-- Splits +vim.opt.splitright = true +vim.opt.splitbelow = true + +-- Persistence +vim.opt.undofile = true +vim.opt.swapfile = false + +-- Tweaks +vim.opt.updatetime = 1000 +vim.opt.timeout = true +vim.opt.ttimeout = true +vim.opt.timeoutlen = 500 +vim.opt.ttimeoutlen = 10 + +-- Clipboard +if vim.env.CONTAINER then + vim.g.clipboard = { + name = 'osc52', + copy = { + ['+'] = require('vim.ui.clipboard.osc52').copy('+'), + ['*'] = require('vim.ui.clipboard.osc52').copy('*'), + }, + paste = { + ['+'] = require('vim.ui.clipboard.osc52').paste('+'), + ['*'] = require('vim.ui.clipboard.osc52').paste('*'), + }, + } +end diff --git a/lua/triimd/diagnostics.lua b/lua/modules/diagnostics.lua similarity index 100% rename from lua/triimd/diagnostics.lua rename to lua/modules/diagnostics.lua diff --git a/lua/triimd/syntax.lua b/lua/modules/language-specs.lua similarity index 81% rename from lua/triimd/syntax.lua rename to lua/modules/language-specs.lua index c1a2a46..5aa31b5 100644 --- a/lua/triimd/syntax.lua +++ b/lua/modules/language-specs.lua @@ -1,4 +1,7 @@ -local language_specs = { +local M = {} + +function M.get() + return { { ts = { 'yaml', 'toml', 'sql', 'diff', 'dockerfile', 'gitcommit', 'gitignore' } }, { ts = { 'c', 'cpp', 'go', 'rust', 'python' } }, @@ -15,12 +18,6 @@ local language_specs = { format = { 'prettierd', 'prettier' }, }, } +end -local lm = require('plugins.language-manager') - -lm.generate_specs(language_specs) - -lm.ts.setup() -lm.lsp.setup() -lm.lint.setup() -lm.format.setup() +return M diff --git a/lua/modules/navigation.lua b/lua/modules/navigation.lua new file mode 100644 index 0000000..e291a6f --- /dev/null +++ b/lua/modules/navigation.lua @@ -0,0 +1,2 @@ +require('plugins.finder') +require('plugins.filetree') diff --git a/lua/modules/terminal.lua b/lua/modules/terminal.lua new file mode 100644 index 0000000..c44f88e --- /dev/null +++ b/lua/modules/terminal.lua @@ -0,0 +1,67 @@ +local api = vim.api +local au = api.nvim_create_autocmd +local group = api.nvim_create_augroup('triimd.term', { clear = true }) + +-- Custom terminal +au('TermOpen', { + group = group, + callback = function() + vim.opt_local.number = false + vim.opt_local.relativenumber = false + vim.opt_local.scrolloff = 0 + vim.bo.filetype = 'terminal' + vim.cmd.startinsert() + end, +}) + +-- Insert when re-entering a terminal window (after switching back) +au('BufEnter', { + group = group, + pattern = 'term://*', + callback = function() + if vim.bo.buftype == 'terminal' and vim.fn.mode() ~= 'i' then + vim.cmd.startinsert() + end + end, +}) + +-- Close all terminal buffers before quitting +au('QuitPre', { + group = group, + callback = function() + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].buftype == 'terminal' then + vim.api.nvim_buf_delete(buf, { force = true }) + end + end + end, +}) + +local commands = { + TermDefault = function() + vim.cmd('terminal') + end, + + TermRelative = function() + local shell = vim.o.shell or 'zsh' + local dir = vim.fn.expand('%:p:h') + vim.cmd(string.format('edit term://%s//%s', dir, shell)) + end, + + TermSplit = function() + vim.cmd('new') + vim.cmd('wincmd J') + vim.api.nvim_win_set_height(0, 12) + vim.wo.winfixheight = true + vim.cmd('term') + end, + + TermVSplit = function() + vim.cmd('vsplit') + vim.cmd('term') + end, +} + +for name, fn in pairs(commands) do + vim.api.nvim_create_user_command(name, fn, {}) +end diff --git a/lua/modules/theme.lua b/lua/modules/theme.lua new file mode 100644 index 0000000..9ee7cfc --- /dev/null +++ b/lua/modules/theme.lua @@ -0,0 +1,32 @@ + +local function load_theme() + require('invero').setup({ + highlights = function(c, tool) + c.bg_float = tool(152) + return { + ModeMsg = { fg = c.yellow, bg = c.none, bold = true }, + WinSeparator = { fg = c.outline, bg = c.base }, + StatusLine = { fg = c.outline, bg = c.base }, + StatusLineNC = { fg = c.text, bg = c.base, bold = true }, + TabLine = { fg = c.muted, bg = c.none }, + TabLineSel = { fg = c.text, bg = c.none, bold = true }, + TabLineFill = { fg = c.outline_light, bg = c.none }, + Pmenu = { fg = c.text, bg = c.surface }, + PmenuSel = { fg = c.text, bg = c.accent_light }, + QuickFixLine = { fg = c.accent, bg = c.none, bold = true }, + } + end, + }) + + vim.o.background = 'light' + vim.cmd.colorscheme('invero') +end + +vim.api.nvim_create_user_command('ReloadInvero', function() + require('invero').invalidate_cache() + load_theme() +end, {}) + +load_theme() + +require('plugins.tabline').setup() diff --git a/lua/triimd/filetree.lua b/lua/plugins/filetree.lua similarity index 96% rename from lua/triimd/filetree.lua rename to lua/plugins/filetree.lua index 0daf633..bc1ff69 100644 --- a/lua/triimd/filetree.lua +++ b/lua/plugins/filetree.lua @@ -1,7 +1,3 @@ -vim.pack.add({ - { src = 'https://gitea.tomastm.com/tomas.mirchev/nvim-tree.lua', version = 'master' }, -}) - local function my_on_attach(bufnr) local api = require('nvim-tree.api') local opts = { buffer = bufnr } diff --git a/lua/plugins/navigation.lua b/lua/plugins/finder.lua similarity index 100% rename from lua/plugins/navigation.lua rename to lua/plugins/finder.lua diff --git a/lua/plugins/language-manager.lua b/lua/plugins/language-manager.lua index a65a6f2..4d8c555 100644 --- a/lua/plugins/language-manager.lua +++ b/lua/plugins/language-manager.lua @@ -1,24 +1,16 @@ ---[[ -local lm = require('language_manager') -lm.install() or .install({'ts_parsers','language_servers','linters','formatters'}) - - treesitter.install specs.ts_parsers - - mason.install mason-registry.get_package(name):install() -lm.generate_specs() or {use_cache=true, only_installed=true} -lm.delete_cache() -lm.enable() ---]] - - -vim.pack.add({ 'https://github.com/mason-org/mason.nvim' }) - -local M = {} -M.lsp = {} -M.ts = {} -M.lint = {} -M.format = {} +local M = { + lsp = {}, + ts = {}, + lint = {}, + format = {}, +} M.group = vim.api.nvim_create_augroup('language-manager', { clear = true }) +-- ======== Helpers ======== + +local cache_path = vim.fn.stdpath('cache') .. '/language-manager.json' + local function wrap(item) if type(item) == 'string' then return { item } @@ -31,7 +23,7 @@ end local function create_set() local seen = {} - return function(ref, item) -- unique() + return function(ref, item) if not seen[ref] then seen[ref] = {} end @@ -43,6 +35,69 @@ local function create_set() end end +local function read_file(path) + local f = io.open(path, 'r') + if not f then + return nil + end + local content = f:read('*a') + f:close() + return content +end + +local function write_file(path, content) + local f = io.open(path, 'w') + if not f then + return false + end + f:write(content) + f:close() + return true +end + +local function to_json(tbl) + local ok, result = pcall(vim.json.encode, tbl) + return ok and result or nil +end + +local function from_json(str) + local ok, result = pcall(vim.json.decode, str) + return ok and result or nil +end + +local function hash_spec(tbl) + local encoded = to_json(tbl) or '' + if vim.fn.exists('*sha256') == 1 then + return vim.fn.sha256(encoded) + else + local tmp = vim.fn.tempname() + write_file(tmp, encoded) + local handle = io.popen('openssl dgst -sha256 ' .. tmp) + local result = handle and handle:read('*a') or '' + if handle then + handle:close() + end + return result:match('([a-f0-9]+)') or '' + end +end + +local function save_cache(data) + local encoded = to_json(data) + if encoded then + write_file(cache_path, encoded) + end +end + +local function load_cache() + local raw = read_file(cache_path) + if not raw then + return nil + end + return from_json(raw) +end + +-- ======== Spec Builder ======== + local function create_spec() local S = {} local unique = create_set() @@ -64,20 +119,15 @@ local function create_spec() for i = 1, last_i do local group = groups[i] - if i == last_i then for _, item in ipairs(wrap(list)) do if unique(ref, item) then - if not pointer[group] then - pointer[group] = {} - end + pointer[group] = pointer[group] or {} table.insert(pointer[group], item) end end else - if not pointer[group] then - pointer[group] = {} - end + pointer[group] = pointer[group] or {} pointer = pointer[group] end end @@ -86,47 +136,189 @@ local function create_spec() return S end +-- ======== Spec Generation ======== + 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 lsp_map = {} + for _, spec in ipairs(specs_raw) do - -- -> { ft = } if type(spec) == 'string' then spec = { ft = spec } end - -- Filetype = TS Parser - if not spec.ts then - spec.ts = spec.ft - end - + spec.ts = spec.ts or spec.ft specs.add(spec.ts, 'ts_parsers') - specs.add(spec.lsp, 'language_servers') - for _, filetype in ipairs(wrap(spec.ft)) do - specs.add(spec.lint, 'linters_by_ft', filetype) - specs.add(spec.format, 'formatters_by_ft', filetype) + -- resolve LSP name using Mason registry + install_spec.add(spec.lsp, 'language_servers') + 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 + else + vim.notify('Unknown LSP: ' .. lsp, vim.log.levels.WARN) + end + end + specs.add(resolved_lsps, 'language_servers') + + install_spec.add(spec.lint, 'code_tools') + install_spec.add(spec.format, 'code_tools') + for _, ft in ipairs(wrap(spec.ft)) do + specs.add(spec.lint, 'linters_by_ft', ft) + specs.add(spec.format, 'formatters_by_ft', ft) end end - M.general = specs.get() - return M.general + local result = specs.get() + result.lsp_map = lsp_map + 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 + +function M.load_specs() + local cache = load_cache() + if cache then + 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 }) + end + return M.general, M.mason +end + +function M.invalidate_cache() + local ok = os.remove(cache_path) + if ok then + vim.notify('Language manager cache invalidated', vim.log.levels.INFO) + else + vim.notify('No cache to invalidate or failed to delete', vim.log.levels.WARN) + end + M.general = nil + 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 +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 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 + end + + 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 + 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() - require('mason').setup() - local registry = require('mason-registry') - - for _, lsp in ipairs(M.general.language_servers) do - if registry.has_package(lsp) then - local pkg = registry.get_package(lsp) - local spec = pkg.spec and pkg.spec.neovim - local lsp_name = (spec and spec.lspconfig) or lsp - - vim.lsp.enable(lsp_name) - else - vim.notify('Unknown LSP: ' .. lsp, vim.log.levels.WARN) - end + for _, lsp_name in ipairs(M.general.language_servers or {}) do + vim.lsp.enable(lsp_name) end end @@ -142,7 +334,7 @@ function M.lint.setup() group = M.group, callback = function() local lint = require('lint') - lint.linters_by_ft = M.general.linters_by_ft + lint.linters_by_ft = 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() @@ -161,10 +353,18 @@ 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, + formatters_by_ft = M.general.formatters_by_ft or {}, }) end, }) end +function M.setup() + M.load_specs() + M.ts.setup() + M.lsp.setup() + M.lint.setup() + M.format.setup() +end + return M diff --git a/lua/plugins/tabline.lua b/lua/plugins/tabline.lua index 129273e..edefc5f 100644 --- a/lua/plugins/tabline.lua +++ b/lua/plugins/tabline.lua @@ -76,8 +76,8 @@ function M.tabline() end function M.setup() - vim.o.showtabline = 1 - vim.o.tabline = "%!v:lua.require'plugins.tabline'.tabline()" + vim.opt.showtabline = 1 + vim.opt.tabline = "%!v:lua.require'plugins.tabline'.tabline()" end return M diff --git a/lua/setup/init.lua b/lua/setup/init.lua index 8aa769a..5ced9a5 100644 --- a/lua/setup/init.lua +++ b/lua/setup/init.lua @@ -1 +1,91 @@ -vim.pack.add({ 'https://github.com/mason-org/mason.nvim' }) +local M = {} + +function M.install_pm() + 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") + 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 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() + return done + end, 200) +end + + + +function M.install_languages() + local lm = require('plugins.language-manager') + lm.invalidate_cache() + lm.load_specs() + lm.install() +end + +vim.api.nvim_create_user_command('InstallAll', function() + M.install_pm() + M.install_packages() + M.install_languages() +end, {}) + +vim.api.nvim_create_user_command('FetchLspConfigs', function() + local base_url = 'https://raw.githubusercontent.com/neovim/nvim-lspconfig/master/lsp/' + + local lm = require('plugins.language-manager') + lm.invalidate_cache() + local general = lm.load_specs() + + local lsp_dir = vim.fs.joinpath(vim.fn.getcwd(), 'lsp') + vim.fn.mkdir(lsp_dir, 'p') + + for _, name in ipairs(general.language_servers) do + local file = vim.fs.joinpath(lsp_dir, name .. '.lua') + if vim.fn.filereadable(file) == 0 then + local url = base_url .. name .. '.lua' + local cmd = string.format('curl -fsSL -o %q %q', file, url) + vim.fn.system(cmd) + if vim.v.shell_error ~= 0 then + vim.notify('Failed to fetch ' .. name .. '.lua', vim.log.levels.ERROR) + vim.fn.delete(file) + else + vim.notify('Fetched ' .. name .. '.lua', vim.log.levels.INFO) + end + else + vim.notify('Skipped existing ' .. name .. '.lua', vim.log.levels.INFO) + end + end +end, { desc = 'Fetch default LSP configs into ./lsp in cwd' }) + +return M diff --git a/lua/setup/lm-cmds.lua b/lua/setup/lm-cmds.lua deleted file mode 100644 index 868cc2f..0000000 --- a/lua/setup/lm-cmds.lua +++ /dev/null @@ -1,98 +0,0 @@ -local general = {} ---------------------------------------------------------------------- --- Mason Install Command ---------------------------------------------------------------------- -vim.api.nvim_create_user_command('MasonInstallAll', function() - local registry = require('mason-registry') - local list = vim.list_extend(vim.list_extend({}, general.lsps), {}) - - for _, ftmap in pairs({ general.linters_by_ft, general.formatters_by_ft }) do - for _, tools in pairs(ftmap) do - vim.list_extend(list, tools) - end - end - - list = uniq(list) - local installed = {} - for _, pkg in ipairs(registry.get_installed_packages()) do - installed[pkg.name] = true - end - - for _, name in ipairs(list) do - if registry.has_package(name) then - if not installed[name] then - vim.notify('Installing ' .. name, vim.log.levels.INFO) - registry.get_package(name):install() - else - vim.notify('Already installed ' .. name, vim.log.levels.INFO) - end - else - vim.notify('Package not found in registry: ' .. name, vim.log.levels.WARN) - end - end -end, { desc = 'Install all Mason LSPs, linters, and formatters' }) - ---------------------------------------------------------------------- --- Fetch LSP default configs from nvim-lspconfig ---------------------------------------------------------------------- -vim.api.nvim_create_user_command('FetchLspConfigs', function() - local registry = require('mason-registry') - local lspconfig_names = {} - - for _, lsp in ipairs(general.lsps) do - if registry.has_package(lsp) then - local pkg = registry.get_package(lsp) - local spec = pkg.spec and pkg.spec.neovim - if spec and spec.lspconfig then - table.insert(lspconfig_names, spec.lspconfig) - else - table.insert(lspconfig_names, lsp) - end - else - table.insert(lspconfig_names, lsp) - end - end - - lspconfig_names = uniq(lspconfig_names) - - -- base URL same as your original - local base_url = 'https://raw.githubusercontent.com/neovim/nvim-lspconfig/master/lsp/' - -- write to current working directory - local lsp_dir = vim.fs.joinpath(vim.fn.getcwd(), 'lsp') - vim.fn.mkdir(lsp_dir, 'p') - - for _, name in ipairs(lspconfig_names) do - local file = vim.fs.joinpath(lsp_dir, name .. '.lua') - if vim.fn.filereadable(file) == 0 then - local url = base_url .. name .. '.lua' - local cmd = string.format('curl -fsSL -o %q %q', file, url) - vim.fn.system(cmd) - if vim.v.shell_error ~= 0 then - vim.notify('Failed to fetch ' .. name .. '.lua', vim.log.levels.ERROR) - vim.fn.delete(file) - else - vim.notify('Fetched ' .. name .. '.lua', vim.log.levels.INFO) - end - else - vim.notify('Skipped existing ' .. name .. '.lua', vim.log.levels.INFO) - end - end -end, { desc = 'Fetch default LSP configs into ./lsp in cwd' }) - -vim.api.nvim_create_user_command('TreesitterInstallAll', function() - local parsers = require('nvim-treesitter.parsers') - local configs = require('nvim-treesitter.configs') - local langs = configs.get_module('ensure_installed') or {} - if type(langs) == 'string' and langs == 'all' then - vim.notify('Treesitter ensure_installed = "all" not supported here', vim.log.levels.WARN) - return - end - - for _, lang in ipairs(langs) do - if not parsers.has_parser(lang) then - vim.cmd('TSInstall ' .. lang) - else - vim.notify('Parser already installed: ' .. lang, vim.log.levels.INFO) - end - end -end, { desc = 'Install all Treesitter parsers defined in ensure_installed' }) diff --git a/lua/setup/packages.lua b/lua/setup/packages.lua new file mode 100644 index 0000000..ec36031 --- /dev/null +++ b/lua/setup/packages.lua @@ -0,0 +1,20 @@ +local M = {} + +function M.get() + return { + { 'savq/paq-nvim', opt = true }, + { 'https://github.com/mason-org/mason.nvim', opt = true }, + + { 'https://github.com/triimd/invero.nvim' }, + { 'https://gitea.tomastm.com/tomas.mirchev/nvim-tree.lua', version = 'master' }, + + { 'https://github.com/windwp/nvim-ts-autotag' }, + { 'https://github.com/windwp/nvim-autopairs' }, + + { 'https://github.com/nvim-treesitter/nvim-treesitter', version = 'master' }, + { 'https://github.com/mfussenegger/nvim-lint' }, + { 'https://github.com/stevearc/conform.nvim' }, + } +end + +return M diff --git a/nvim-pack-lock.json b/nvim-pack-lock.json index 2f2f13b..951e18a 100644 --- a/nvim-pack-lock.json +++ b/nvim-pack-lock.json @@ -1,43 +1,7 @@ { "plugins": { - "conform.nvim": { - "rev": "9fd3d5e", - "src": "https://github.com/stevearc/conform.nvim" - }, - "invero.nvim": { - "rev": "6acdefa", - "src": "https://github.com/triimd/invero.nvim" - }, - "mason.nvim": { - "rev": "ad7146a", - "src": "https://github.com/mason-org/mason.nvim" - }, - "nvim-autopairs": { - "rev": "7a2c97c", - "src": "https://github.com/windwp/nvim-autopairs" - }, - "nvim-lint": { - "rev": "9da1fb9", - "src": "https://github.com/mfussenegger/nvim-lint" - }, - "nvim-tree": { - "rev": "7c0f7e9", - "src": "https://gitea.tomastm.com/tomas.mirchev/nvim-tree.lua", - "version": "'master'" - }, "nvim-tree.lua": { - "rev": "7c0f7e9", - "src": "https://gitea.tomastm.com/tomas.mirchev/nvim-tree.lua", "version": "'master'" - }, - "nvim-treesitter": { - "rev": "42fc28ba", - "src": "https://github.com/nvim-treesitter/nvim-treesitter", - "version": "'master'" - }, - "nvim-ts-autotag": { - "rev": "c4ca798", - "src": "https://github.com/windwp/nvim-ts-autotag" } } } \ No newline at end of file diff --git a/plugin/colorscheme.lua b/plugin/colorscheme.lua deleted file mode 100644 index 9eb9dcd..0000000 --- a/plugin/colorscheme.lua +++ /dev/null @@ -1,29 +0,0 @@ -vim.pack.add({ 'https://github.com/triimd/invero.nvim' }) - -require('invero').setup({ - highlights = function(c, tool) - c.bg_float = tool(152) - return { - ModeMsg = { fg = c.yellow, bg = c.none, bold = true }, - WinSeparator = { fg = c.outline, bg = c.base }, - StatusLine = { fg = c.outline, bg = c.base }, - StatusLineNC = { fg = c.text, bg = c.base, bold = true }, - TabLine = { fg = c.muted, bg = c.none }, - TabLineSel = { fg = c.text, bg = c.none, bold = true }, - TabLineFill = { fg = c.outline_light, bg = c.none }, - Pmenu = { fg = c.text, bg = c.surface }, - PmenuSel = { fg = c.text, bg = c.accent_light }, - QuickFixLine = { fg = c.accent, bg = c.none, bold = true }, - } - end, -}) - -vim.o.background = 'light' -vim.cmd.colorscheme('invero') - -vim.api.nvim_create_user_command('ReloadInvero', function() - require('invero').invalidate_cache() - package.loaded['invero'] = nil - require('invero') - vim.cmd.colorscheme('invero') -end, {}) diff --git a/plugin/packages.lua b/plugin/packages.lua deleted file mode 100644 index ced32bd..0000000 --- a/plugin/packages.lua +++ /dev/null @@ -1,8 +0,0 @@ -vim.pack.add({ - 'https://github.com/windwp/nvim-ts-autotag', - 'https://github.com/windwp/nvim-autopairs', - - { src = 'https://github.com/nvim-treesitter/nvim-treesitter', version = 'master' }, - 'https://github.com/mfussenegger/nvim-lint', - 'https://github.com/stevearc/conform.nvim', -}) diff --git a/scripts/install_all b/scripts/install_all new file mode 100755 index 0000000..93357b0 --- /dev/null +++ b/scripts/install_all @@ -0,0 +1,5 @@ +#!/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 -- 2.45.2 From cd5025859c1c369d86485455b0b13198b0ba5745 Mon Sep 17 00:00:00 2001 From: Tomas Mirchev Date: Sun, 26 Oct 2025 02:53:13 +0300 Subject: [PATCH 3/9] feat: automated setup works finally --- init.lua | 1 - lua/core/events.lua | 3 +- lua/plugins/language-manager.lua | 192 ++++++++++++------------------- lua/setup/init.lua | 71 ++++++------ lua/utils.lua | 24 ++++ nvim-pack-lock.json | 7 -- scripts/install_all | 5 - 7 files changed, 135 insertions(+), 168 deletions(-) create mode 100644 lua/utils.lua delete mode 100644 nvim-pack-lock.json delete mode 100755 scripts/install_all 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 -- 2.45.2 From e07337d3ed3efca795dd3bf9b4c2f64e79bbf153 Mon Sep 17 00:00:00 2001 From: Tomas Mirchev Date: Sun, 26 Oct 2025 03:00:37 +0300 Subject: [PATCH 4/9] fix lspconfig map name --- lua/plugins/language-manager.lua | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lua/plugins/language-manager.lua b/lua/plugins/language-manager.lua index fc7f826..aefee8b 100644 --- a/lua/plugins/language-manager.lua +++ b/lua/plugins/language-manager.lua @@ -166,19 +166,18 @@ function M.generate_specs(specs_raw) install_spec.add(spec.lsp, 'code_tools') local resolved_lsps = {} - 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) + for _, language_server in ipairs(wrap(spec.lsp)) do + if registry.has_package(language_server) then + local pkg = registry.get_package(language_server) 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) + local lspconfig_name = pkg.spec and pkg.spec.neovim and pkg.spec.neovim.lspconfig + lsp_map[lspconfig_name] = language_server + table.insert(resolved_lsps, lspconfig_name) else - print('Package found but not lsp name: ' .. lsp_name) + print('Package found but not lspconfig name: ' .. language_server) end else - print('Package not found: ' .. lsp_name) + print('Package not found: ' .. language_server) end end specs.add(resolved_lsps, 'language_servers') -- 2.45.2 From 63e81da4900d6a7214fefc698a0d3c546a03626b Mon Sep 17 00:00:00 2001 From: Tomas Mirchev Date: Sun, 26 Oct 2025 03:05:28 +0300 Subject: [PATCH 5/9] feat: skip mason pkg install if already present --- lua/plugins/language-manager.lua | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/lua/plugins/language-manager.lua b/lua/plugins/language-manager.lua index aefee8b..a608daa 100644 --- a/lua/plugins/language-manager.lua +++ b/lua/plugins/language-manager.lua @@ -234,21 +234,29 @@ function M.mason_install() 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 - + if pkg:is_installed() then + print('Mason package already installed: ' .. name) pending = pending - 1 if pending == 0 then resolve(true) end - end) + else + print('Mason package installing: ' .. 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 end, 5 * 60 * 1000, 200) -- 2.45.2 From a4b5accc054d9068d41864ece7202530694fbfd4 Mon Sep 17 00:00:00 2001 From: Tomas Mirchev Date: Sun, 26 Oct 2025 03:35:28 +0200 Subject: [PATCH 6/9] fix: remove redundant autocmd for formatter --- lua/core/events.lua | 1 + lua/plugins/language-manager.lua | 19 ++++++++----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/lua/core/events.lua b/lua/core/events.lua index b1c8ea6..3a711de 100644 --- a/lua/core/events.lua +++ b/lua/core/events.lua @@ -47,6 +47,7 @@ au({ 'WinEnter', 'InsertLeave' }, { au({ 'WinLeave', 'InsertEnter' }, { group = group, callback = function() + -- Keep it on NvimTree to show current file if vim.bo.filetype == 'NvimTree' then return end diff --git a/lua/plugins/language-manager.lua b/lua/plugins/language-manager.lua index a608daa..5033c69 100644 --- a/lua/plugins/language-manager.lua +++ b/lua/plugins/language-manager.lua @@ -293,13 +293,16 @@ end function M.lint.setup() vim.api.nvim_create_autocmd({ 'BufReadPre', 'BufNewFile' }, { group = M.group, + once = true, callback = function() local lint = require('lint') 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() - lint.try_lint() + if vim.bo.modifiable then + lint.try_lint() + end end, }) end, @@ -307,16 +310,10 @@ function M.lint.setup() end function M.format.setup() - vim.api.nvim_create_autocmd('BufWritePre', { - group = M.group, - once = true, - callback = function() - require('conform').setup({ - format_on_save = { timeout_ms = 500, lsp_format = 'fallback' }, - default_format_opts = { stop_after_first = true }, - formatters_by_ft = (M.general and M.general.formatters_by_ft) or {}, - }) - end, + require('conform').setup({ + formatters_by_ft = (M.general and M.general.formatters_by_ft) or {}, + default_format_opts = { stop_after_first = true, lsp_format = 'fallback' }, + format_on_save = { timeout_ms = 500 }, }) end -- 2.45.2 From ae3d2d20ee42f68368862aae93104ff5b4531010 Mon Sep 17 00:00:00 2001 From: Tomas Mirchev Date: Sun, 26 Oct 2025 05:49:02 +0200 Subject: [PATCH 7/9] feat: improve lint logic --- lua/core/events.lua | 8 ++++---- lua/core/options.lua | 3 +-- lua/plugins/language-manager.lua | 28 +++++++++++++++++++++------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/lua/core/events.lua b/lua/core/events.lua index 3a711de..6ce7b18 100644 --- a/lua/core/events.lua +++ b/lua/core/events.lua @@ -20,7 +20,7 @@ au('VimEnter', { au('TextYankPost', { group = group, callback = function() - vim.highlight.on_yank({ timeout = 400 }) + vim.highlight.on_yank({ timeout = 200 }) end, }) @@ -61,15 +61,15 @@ au({ 'WinLeave', 'InsertEnter' }, { -- Autocompletion au('LspAttach', { group = group, - callback = function(ev) - local client = vim.lsp.get_client_by_id(ev.data.client_id) + callback = function(event) + local client = vim.lsp.get_client_by_id(event.data.client_id) if not client then return end -- Enable native completion if client:supports_method('textDocument/completion') then - vim.lsp.completion.enable(true, client.id, ev.buf, { autotrigger = true }) + vim.lsp.completion.enable(true, client.id, event.buf, { autotrigger = true }) end end, }) diff --git a/lua/core/options.lua b/lua/core/options.lua index 4c3d5ce..cac91c8 100644 --- a/lua/core/options.lua +++ b/lua/core/options.lua @@ -16,7 +16,6 @@ vim.g.loaded_vimballPlugin = 1 vim.g.loaded_matchit = 1 vim.g.loaded_2html_plugin = 1 vim.g.loaded_rrhelper = 1 -vim.g.loaded_matchparen = 1 vim.g.loaded_tutor_mode_plugin = 1 vim.g.loaded_spellfile_plugin = 1 vim.g.loaded_logipat = 1 @@ -87,7 +86,7 @@ vim.opt.undofile = true vim.opt.swapfile = false -- Tweaks -vim.opt.updatetime = 1000 +vim.opt.updatetime = 50 vim.opt.timeout = true vim.opt.ttimeout = true vim.opt.timeoutlen = 500 diff --git a/lua/plugins/language-manager.lua b/lua/plugins/language-manager.lua index 5033c69..76ea47c 100644 --- a/lua/plugins/language-manager.lua +++ b/lua/plugins/language-manager.lua @@ -296,14 +296,28 @@ function M.lint.setup() once = true, callback = function() local lint = require('lint') - lint.linters_by_ft = (M.general and M.general.linters_by_ft) or {} - vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, { + lint.linters_by_ft = M.general.linters_by_ft + + function M.debounce(ms, fn) + local timer = vim.uv.new_timer() + return function(...) + local argv = { ... } + timer:start(ms, 0, function() + timer:stop() + vim.schedule_wrap(fn)(unpack(argv)) + end) + end + end + + function M.lint() + if vim.bo.modifiable then + lint.try_lint() + end + end + + vim.api.nvim_create_autocmd({ 'BufReadPost', 'InsertLeave', 'TextChanged' }, { group = vim.api.nvim_create_augroup('language-manager.lint', { clear = true }), - callback = function() - if vim.bo.modifiable then - lint.try_lint() - end - end, + callback = M.debounce(100, M.lint), }) end, }) -- 2.45.2 From 0c13437b1122554ebdd196316ea4d030b1b3a653 Mon Sep 17 00:00:00 2001 From: Tomas Mirchev Date: Sun, 26 Oct 2025 06:00:37 +0200 Subject: [PATCH 8/9] feat: add lsp and lint info cmds --- lua/plugins/language-manager.lua | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lua/plugins/language-manager.lua b/lua/plugins/language-manager.lua index 76ea47c..c9ab9d9 100644 --- a/lua/plugins/language-manager.lua +++ b/lua/plugins/language-manager.lua @@ -8,8 +8,6 @@ local M = { M.group = vim.api.nvim_create_augroup('language-manager', { clear = true }) --- ======== Helpers ======== - local cache_path = vim.fn.stdpath('cache') .. '/language-manager.json' local function wrap(item) @@ -288,6 +286,10 @@ function M.lsp.setup() for _, lsp_name in ipairs((M.general and M.general.language_servers) or {}) do vim.lsp.enable(lsp_name) end + + vim.api.nvim_buf_create_user_command(0, 'LspInfo', function() + vim.cmd('checkhealth vim.lsp') + end, {}) end function M.lint.setup() @@ -319,6 +321,21 @@ function M.lint.setup() group = vim.api.nvim_create_augroup('language-manager.lint', { clear = true }), callback = M.debounce(100, M.lint), }) + + vim.api.nvim_create_autocmd('BufEnter', { + group = M.group, + callback = function(args) + local bufnr = args.buf + local ft = vim.bo[bufnr].filetype + local linters = lint.linters_by_ft[ft] + + if linters then + vim.api.nvim_buf_create_user_command(bufnr, 'LintInfo', function() + print('Linters for ' .. ft .. ': ' .. table.concat(linters, ', ')) + end, {}) + end + end, + }) end, }) end -- 2.45.2 From 1e3a8495ba2f5b3ad2e0ac19992820b869dc5228 Mon Sep 17 00:00:00 2001 From: Tomas Mirchev Date: Sun, 26 Oct 2025 06:17:49 +0200 Subject: [PATCH 9/9] feat: generate paq-lock --- lua/setup/init.lua | 1 + paq-lock.json | 1 + 2 files changed, 2 insertions(+) create mode 100644 paq-lock.json diff --git a/lua/setup/init.lua b/lua/setup/init.lua index 1e6b1a0..f990335 100644 --- a/lua/setup/init.lua +++ b/lua/setup/init.lua @@ -22,6 +22,7 @@ local function install_packages() vim.cmd.packadd('paq-nvim') local paq = require('paq') + paq:setup({ lock = vim.fn.stdpath("config") .. "/paq-lock.json" }) local packages = require('setup.packages').get() paq(packages) diff --git a/paq-lock.json b/paq-lock.json new file mode 100644 index 0000000..ab75310 --- /dev/null +++ b/paq-lock.json @@ -0,0 +1 @@ +{"nvim-ts-autotag":{"name":"nvim-ts-autotag","url":"https://github.com/windwp/nvim-ts-autotag","hash":"","status":1,"dir":"/home/tomas/.local/share/nvim/site/pack/paqs/start/nvim-ts-autotag"},"mason.nvim":{"name":"mason.nvim","url":"https://github.com/mason-org/mason.nvim","hash":"","status":1,"dir":"/home/tomas/.local/share/nvim/site/pack/paqs/opt/mason.nvim"},"paq-nvim":{"name":"paq-nvim","url":"https://github.com/savq/paq-nvim.git","hash":"971344d1fe1fd93580961815e7b7c8853c3605e4","status":0,"dir":"/home/tomas/.local/share/nvim/site/pack/paqs/opt/paq-nvim"},"nvim-autopairs":{"name":"nvim-autopairs","url":"https://github.com/windwp/nvim-autopairs","hash":"","status":1,"dir":"/home/tomas/.local/share/nvim/site/pack/paqs/start/nvim-autopairs"},"nvim-tree.lua":{"name":"nvim-tree.lua","url":"https://gitea.tomastm.com/tomas.mirchev/nvim-tree.lua","hash":"","status":1,"dir":"/home/tomas/.local/share/nvim/site/pack/paqs/start/nvim-tree.lua"},"nvim-treesitter":{"name":"nvim-treesitter","url":"https://github.com/nvim-treesitter/nvim-treesitter","hash":"","status":1,"dir":"/home/tomas/.local/share/nvim/site/pack/paqs/start/nvim-treesitter"},"conform.nvim":{"name":"conform.nvim","url":"https://github.com/stevearc/conform.nvim","hash":"","status":1,"dir":"/home/tomas/.local/share/nvim/site/pack/paqs/start/conform.nvim"},"invero.nvim":{"name":"invero.nvim","url":"https://github.com/triimd/invero.nvim","hash":"","status":1,"dir":"/home/tomas/.local/share/nvim/site/pack/paqs/start/invero.nvim"},"nvim-lint":{"name":"nvim-lint","url":"https://github.com/mfussenegger/nvim-lint","hash":"","status":1,"dir":"/home/tomas/.local/share/nvim/site/pack/paqs/start/nvim-lint"}} \ No newline at end of file -- 2.45.2