commit b8cad0f750b5115ea6977fcd0c0d596c2b0b2558 Author: Tomas Mirchev Date: Mon Oct 20 20:53:54 2025 +0300 init diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..a0aa6d0 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1 @@ +return { globals = { 'vim' } } diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..2aa780f --- /dev/null +++ b/.luarc.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", + "runtime": { + "version": "LuaJIT" + }, + "workspace": { + "library": [ + "lua", + "$VIMRUNTIME", + "${3rd}/luv/library" + ], + "checkThirdParty": false + } +} diff --git a/.stylua.toml b/.stylua.toml new file mode 100644 index 0000000..abdf68f --- /dev/null +++ b/.stylua.toml @@ -0,0 +1,7 @@ +column_width = 100 +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferSingle" +call_parentheses = "Always" +line_endings = "Unix" + diff --git a/docs/commands.md b/docs/commands.md new file mode 100644 index 0000000..137b984 --- /dev/null +++ b/docs/commands.md @@ -0,0 +1,109 @@ +#============================================================ +# Neovim Command Reference (Core Commands) +#============================================================ + + +#============================================================ +# FILE & SESSION MANAGEMENT +#============================================================ +:w (write) Save current buffer +:w Write to (does NOT rename buffer) +:sav (saveas) Save to and rename current buffer +:q (quit) Quit current window if no unsaved changes +:q! Force quit (discard changes) +:wq Write and quit current buffer +:wqa Write and quit all buffers +:qa Quit all windows +:qa! Quit all without saving +:e (edit) Edit in current window (new buffer) +:e! Revert buffer to last saved version (discard changes) +:enew Create new empty buffer +:r (read) Read into current buffer after cursor +:ter (terminal) Open terminal buffer +:clo (close) Close current window + + +#============================================================ +# BUFFERS +#============================================================ +:ls (buffers) List open buffers +:bn (bnext) Go to next buffer +:bp (bprev) Go to previous buffer +:b Go to buffer by number +:b Go to buffer by (partial) name +:bd (bdelete) [] Delete current (or given) buffer +:bw (bwipeout) [] Delete buffer and remove from memory +:bo (only) Close all windows except current +:e # Edit alternate (previous) buffer +:b# Switch to alternate buffer +:b% Re-edit current buffer (no-op) +:b$ Go to last buffer + + +#============================================================ +# WINDOWS (SPLITS) +#============================================================ +:sp (split) [] Split horizontally (optionally open ) +:vsp (vsplit) [] Split vertically (optionally open ) +:new Split with new empty buffer +:vnew Vertical split with new empty buffer +:close Close current window +:only Keep only current window open + + +#============================================================ +# TABS +#============================================================ +:tabnew [] Open new tab (optionally edit ) +:tabn (tabnext) Go to next tab +:tabp (tabprev) Go to previous tab +:tabm (tabmove) Move current tab to position +:tabfirst Jump to first tab +:tablast Jump to last tab +:tabc (tabclose) Close current tab +:tabo (tabonly) Close all other tabs +:tabdo Execute in all tabs (e.g. :tabdo q) + + +#============================================================ +# REGISTERS, MARKS & HISTORY +#============================================================ +:reg (registers) Show register contents +:marks Show all marks +:ju (jumps) Show jump list +:changes Show change list + + +#============================================================ +# SEARCH / GREP / QUICKFIX +#============================================================ +:g/{pattern}/d Delete all lines matching {pattern} +:g!/{pattern}/d Delete all lines NOT matching {pattern} +:vim /{pattern}/ **/* Search for {pattern} recursively in all files +:cn (cnext) Jump to next quickfix entry +:cp (cprevious) Jump to previous quickfix entry +:cope (copen) Open quickfix window +:ccl (cclose) Close quickfix window +:cl (clist) List quickfix entries + + +#============================================================ +# DIFF MODE +#============================================================ +:diffthis Start diff mode for current window +:diffo (diffoff) Turn off diff mode +:diffu (diffupdate) Recalculate diff +:diffg (diffget) Get changes from other window +:diffpu (diffput) Send changes to other window + + +#============================================================ +# NOTES & SYMBOLS +#============================================================ +% Current buffer +# Alternate (last visited) buffer +$ Last buffer in list ++ Next buffer +- Previous buffer +" Most recently edited buffer (context dependent) + diff --git a/docs/docs.md b/docs/docs.md new file mode 100644 index 0000000..a6a6ddd --- /dev/null +++ b/docs/docs.md @@ -0,0 +1,217 @@ +TODO: +- wrap up invero theme in separate repo and proper colors? + - check plugins logins + - cache / create final result +- simplify coding: ts, lsp, lint, format (check other repos) + - how to download parsers and plugins alternative +- telescope alternative +- keymaps +- wrap up everything + +```lua +--[[ + Neovim Lua config: ways to set things + + - vim.opt : preferred modern API for options (handles lists, cleaner syntax) + - vim.g : global variables (leader key, plugin settings, disable builtins, etc.) + - vim.o : global-only option (rarely needed directly) + - vim.wo : window-local option (applies to current window only, use in autocmds) + - vim.bo : buffer-local option (applies to current buffer only, use in autocmds) + - vim.env : set environment variables (like PATH, LANG) + - vim.fn : call Vimscript functions (e.g. vim.fn.getcwd()) + - vim.cmd : run raw Vimscript/Ex commands (e.g. vim.cmd("colorscheme desert")) + - vim.api : low-level Neovim API (create autocmds, keymaps, buffer/window ops, etc.) + + TL;DR -> use vim.opt + vim.g in options.lua for defaults. + Use vim.wo/vim.bo only in context-specific tweaks (autocmds). + Use vim.env, vim.fn, vim.cmd, vim.api as needed for scripting. +--]] +``` + +## check macos fileS: https://github.com/dsully/dotfiles/blob/main/.data/macos-defaults/globals.yaml + +# Used pacakges: +- rockspaces Metadata files describing how to build and install a Lua package. +- luarocks Package manager for Lua modules. (optional) +- tree-sitter Parser generator. Not needed except for using CLI. (optional) + +- fd-find (fd) Alternative to `find`. (optional) +- ripgrep (rg) Line-oriented search tool. (recommended) + +- git Revision control system. (requirement) +- lazygit Terminal UI for git commands. (optional) + +- fzf Command-line fuzzy finder. +- bat "cat" but with colors +- curl Command-line for transferring data specified with URL syntax. +- wget Utility for downloading files from the Web. +- make +- cc Collection of compilers. +- build-essential Meta-package that installs standard C/C++ libraries and headers. + These are needed to compile tree-sitter parsers. Run only on the first time. + cc (gcc, clang) C compiler. Usually it points to `clang` (on macos) or `gcc` (on linux). + g++ C++ compiler. + make Build automation tool from source code. + +- unzip Extraction utility for archives compressed in .zip. +- ca-certificates Provides a set of trusted Certificate Authority (CA) certificates. +- openssh-client Tools for connecting to remote servers securely over SSH. +- libssl-dev Development libraries and headers for OpenSSL. +- sudo +- tree +- jq +- man-db +- python3 +- volta Node manager +- ncurses ncurses-dev ncurses-libs ncurses-terminfo \ +- check: https://github.com/glepnir/nvim/blob/main/Dockerfile + + +# Currently installed +- plenary.nvim +- lazy.nvim + +- nvim-treesitter +- neovim/nvim-lspconfig + - williamboman/mason.nvim + - williamboman/mason-lspconfig.nvim + - j-hui/fidget.nvim + - hrsh7th/cmp-nvim-lsp + - b0o/schemastore.nvim + +- windwp/nvim-ts-autotag # auto close,rename tags +- nvim-autopairs # auto pair chars +- stevearc/conform.nvim # formatter +- nvim-cmp # completion + - cmp-path + - cmp-nvim-lsp + +- nvim-tree.lua # file explorer +- telescope.nvim + - telescope-fzf-native.nvim + - telescope-ui-select.nvim + - plenary.nvim +- harpoon # tags + + +# Notes: +- in lsp change tsserver to vtsls + +# New package definition +- Plugin and Package managers + - folke/lazy.nvim + - mason-org/mason.nvim +- TS + - nvim-treesitter + - nvim-treesitter-textobjects +- LSP + - neovim/nvim-lspconfig + +- nvim-ts-autotag tag elements (``) +- windwp/nvim-autopairs auto pairs +- blink.cmp autocompletion +- stevearc/conform.nvim autoformat +- mini.ai `a/i` motions +- mini.pairs +- mini.surround +- mfussenegger/nvim-lint +- nvim-lspconfig +- fzf-lua (replace telescope) +- ? indent guides + - lukas-reineke/indent-blankline.nvim + - snacks.indent + - mini.indentscope + +## Deps: +- SchemaStore.nvim +- mason-lspconfig.nvim +- mason.nvim + +## Maybe: +- folke/ts-comments.nvim better comments +- grug-far.nvim find and replace +- markdown-preview.nvim side by side md (disabled in folke) +- toppair/peek.nvim another markdown preview? +- render-markdown.nvim inline viewer +- markview.nvim +- diffview.nvim +- octo.nvim edit and review GH issues and pr +- yanky.nvim better yank+put. has history +- inc-rename.nvim LSP renaming with visual feedback +- mini.basics defaults options +- mini.test run tests under cursor +- mini.diff inline diff +- mini.hipatters hilight patters and hex colors +- mini.move move chunks (like vscode) +- undo tree (find a plugin) + +## AI help +- jackMort/ChatGPT.nvim +- MunifTanjim/nui.nvim (dep) +- nvim-lua/plenary.nvim (dep) +- nvim-telescope/telescope.nvim (dep) +- robitx/gp.nvim +- zbirenbaum/copilot.lua (folke) +- milanglacier/minuet-ai.nvim (folke) + +## Options +``` + +opt.backup = true +opt.backupdir = vim.fn(stdpath("state") .. "/backup" +opt.mousescroll = "vert:1,hor:4" +opt.winborder = "rounded" +opt.undofile = true +-- lazy + rtp = { + disabled_plugins = { "gzip", "tarPlugin", "zipPlugin", "tohtml", "tutor" }, + }, + +vim.keymap.set("n", "", "ciw") + +-- ts lsp + includeCompletionsWithSnippetText = true, + jsxAttributeCompletionStyle = "auto", + +-- tree-sitter parsers naming: + https://github.com/nvim-treesitter/nvim-treesitter/blob/master/lua/nvim-treesitter/parsers.lua +``` + +folke cmd +```lua +-- show cursor line only in active window +vim.api.nvim_create_autocmd({ "InsertLeave", "WinEnter" }, { + callback = function() + if vim.w.auto_cursorline then + vim.wo.cursorline = true + vim.w.auto_cursorline = nil + end + end, +}) +vim.api.nvim_create_autocmd({ "InsertEnter", "WinLeave" }, { + callback = function() + if vim.wo.cursorline then + vim.w.auto_cursorline = true + vim.wo.cursorline = false + end + end, +}) + +-- backups +vim.api.nvim_create_autocmd("BufWritePre", { + group = vim.api.nvim_create_augroup("better_backup", { clear = true }), + callback = function(event) + local file = vim.uv.fs_realpath(event.match) or event.match + local backup = vim.fn.fnamemodify(file, ":p:~:h") + backup = backup:gsub("[/\\]", "%%") + vim.go.backupext = backup + end, +}) +``` + +Enable folding with TS: +``` +vim.opt.foldmethod = "expr" +vim.opt.foldexpr = "nvim_treesitter#foldexpr()" +vim.opt.foldenable = false -- start with all folds open +``` diff --git a/docs/keymaps.md b/docs/keymaps.md new file mode 100644 index 0000000..04022ab --- /dev/null +++ b/docs/keymaps.md @@ -0,0 +1,119 @@ +#============================================================ +# NEOVIM KEYMAP REFERENCE (CORE & USEFUL) +#============================================================ + + +#============================================================ +# INSERT MODE (Ctrl + ...) +#============================================================ +Ctrl + h Delete character before cursor +Ctrl + w Delete word before cursor +Ctrl + j Insert line break (newline) at cursor +Ctrl + t Indent current line one 'shiftwidth' to the right +Ctrl + d De-indent current line one 'shiftwidth' to the left +Ctrl + n Insert next auto-completion match +Ctrl + p Insert previous auto-completion match +Ctrl + r x Insert contents of register x +Ctrl + o {cmd} Temporarily enter Normal mode to execute {cmd} +Esc / Ctrl + c Exit insert mode and return to Normal mode + + +#============================================================ +# NORMAL MODE — WINDOW & TAB MANAGEMENT +#============================================================ +C-w s Split current window horizontally +C-w v Split current window vertically +C-w c Close current window +C-w q Quit current window (same as close but exit if last) +C-w w Cycle through open windows +C-w x Exchange window with next one + +C-w = Equalize window sizes +C-w _ Maximize height of current window +C-w | Maximize width of current window + +C-w h Move to window left +C-w j Move to window below +C-w k Move to window above +C-w l Move to window right + +C-w H Move current window far left +C-w J Move current window to bottom +C-w K Move current window to top +C-w L Move current window far right + +C-w t Move current split into a new tab + +gt Go to next tab +gT Go to previous tab +gt Go to tab number + + +#============================================================ +# NORMAL MODE — TAGS, JUMPS & MOTIONS +#============================================================ +Ctrl + ] Jump to tag under cursor +gd Go to local declaration +gD Go to global declaration +{ Jump backward one paragraph/block +} Jump forward one paragraph/block +; Repeat last motion forward +, Repeat last motion backward +Ctrl + e Scroll window down (cursor stays put) +Ctrl + y Scroll window up (cursor stays put) +H Move cursor to top of screen +M Move cursor to middle of screen +L Move cursor to bottom of screen + + +#============================================================ +# NORMAL MODE — TEXT OBJECTS +#============================================================ +aw "A word" — select word + following space +ab "A block" — select around () +aB "A Block" — select around {} +at "A tag" — select around XML/HTML tag + +ib "Inner block" — inside () +iB "Inner Block" — inside {} +it "Inner tag" — inside tag (no delimiters) + + +#============================================================ +# NORMAL MODE — DIFF / CHANGES +#============================================================ +do Obtain (get) changes from other window +dp Put (send) changes to other window + + +#============================================================ +# REGISTERS & CLIPBOARD +#============================================================ +"xy Yank into register x +"xp Paste contents of register x +"+y Yank into system clipboard +"+p Paste from system clipboard + +#--- Common Registers --- +"0 Last yank +" Unnamed register (last delete or yank) +"% Current file name +"# Alternate file name +"* X11 primary selection clipboard +"+ X11 clipboard register +"/ Last search pattern +": Last command-line command +". Last inserted text +"- Last small (less than a line) delete +"= Expression register (evaluate expression) +"_ Black hole register (discard output) + + +#============================================================ +# TIPS & NOTES +#============================================================ +- Ctrl + o in Insert mode is a powerful way to run one Normal-mode command temporarily. +- Use “aw”, “iw”, “ab”, “ib”, etc. with operators (e.g., ‘daw’, ‘yib’) for precise text manipulation. +- The system clipboard is “+” (Windows/macOS/Linux), but some X11 systems also use “*”. +- When in diff mode, ‘do’ and ‘dp’ are complementary: “get” vs. “put” changes. + diff --git a/docs/neovim-actions.md b/docs/neovim-actions.md new file mode 100644 index 0000000..513afbc --- /dev/null +++ b/docs/neovim-actions.md @@ -0,0 +1,94 @@ +-- Window Creation/Closing +Ctrl-w s - Split window horizontally +Ctrl-w v - Split window vertically +Ctrl-w n - Create new window horizontally with empty buffer +Ctrl-w c - Close current window +Ctrl-w o - Close all windows except current one + +-- Window Navigation +Ctrl-h - Move to window on the left +Ctrl-j - Move to window below +Ctrl-k - Move to window above +Ctrl-l - Move to window on the right + +-- Window Moving/Rearranging +Ctrl-w H - Move current window to far left +Ctrl-w J - Move current window to bottom +Ctrl-w K - Move current window to top +Ctrl-w L - Move current window to far right + +-- Window Resizing +Ctrl-w = - Make all windows equal size +Ctrl-w _ - Maximize height of current window +Ctrl-w | - Maximize width of current window +Ctrl-w > - Increase width by 5 column +Ctrl-w < - Decrease width by 5 column +Ctrl-w + - Increase height by 5 row +Ctrl-w - - Decrease height by 5 row + +-- Window Special Commands +Ctrl-w T - Move current window to new tab +Ctrl-w } - Preview definition in new window +Ctrl-w z - Close preview window + +-- Tab +t] :tabnext - Go to next tab +t[ :tabprevious - Go to previous tab +tn :tabnew - Create a new tab +tc :tabclose - Close current tab +to :tabonly - Close all other tabs +{n} :{n}gt - Go to tab {n} + +-- Buffer +-- note: mainly using the telescope one +space telescope.buffers - open buffers with telescope. there can navigate and delete +bl :ls - List all buffers +bd :bdelete - Delete current buffer +bn :bnext - Go to next buffer +bp :bprevious - Go to previous buffer +b{n} :buffer {n} - Go to buffer {n} +bb :b - Start buffer selection +bo :bufdo bd|1bd - Delete all other buffers + +-- Telescope +ff telescope.find_files - Search Files +fg telescope.live_grep - Search by Grep +fb telescope.buffers - Search Buffers +fh telescope.help_tags - Search Help +fp telescope.projects - Search Projects +fm telescope.marks - Search Marks +fc telescope.commands - Search Commands +fk telescope.keymaps - Search Keymaps +fs telescope.git_status - Search Git Status +fw telescope.grep_string - Search current Word +fd telescope.diagnostics - Search Diagnostics +fr telescope.lsp_references - Search References + +-- Neo-tree +e :NvimTree toggle - Explorer Toggle +E :NvimTree focus - Explorer Focus + +-- Harpoon +h harpoon_ui.toggle_menu - Harpoon Menu +m harpoon_mark.add_file - Mark File +1 harpoon_ui.nav_file(1) - Harpoon File 1 +2 harpoon_ui.nav_file(2) - Harpoon File 2 +3 harpoon_ui.nav_file(3) - Harpoon File 3 +4 harpoon_ui.nav_file(4) - Harpoon File 4 +hn harpoon_ui.nav_next - Harpoon Next +hp harpoon_ui.nav_prev - Harpoon Previous + +-- Terminal +tet :terminal cd %:h - Terminal in This dir +ter :terminal - Terminal Regular +tec :!cd %:h && - Terminal Command + - Terminal Normal Mode + - Terminal Window Command + +-- LSP +gd vim.lsp.buf.definition - Goto Definition +gr vim.lsp.buf.references - Goto References +K vim.lsp.buf.hover - Hover Documentation +rn vim.lsp.buf.rename - Rename +ca vim.lsp.buf.code_action - Code Action +f vim.lsp.buf.format - Format diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..1c931a9 --- /dev/null +++ b/init.lua @@ -0,0 +1,29 @@ +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) + +require('config.options') +require('config.keymaps') +require('config.autocmds') +require('config.clipboard') +require('config.terminal') +require('custom.navigation') +require('lazy').setup({ + spec = { { import = 'plugins' } }, + install = { missing = false }, + change_detection = { notify = false }, + rocks = { enabled = false }, +}) diff --git a/lazy-lock.json b/lazy-lock.json new file mode 100644 index 0000000..390743e --- /dev/null +++ b/lazy-lock.json @@ -0,0 +1,12 @@ +{ + "conform.nvim": { "branch": "master", "commit": "fbcb4fa7f34bfea9be702ffff481a8e336ebf6ed" }, + "fzf-lua": { "branch": "main", "commit": "58ebb27333bd12cb497f27b7da07a677116bc0ef" }, + "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": "321bc61580fd066b76861c32de3319c3a6d089e7" }, + "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, + "nvim-ts-autotag": { "branch": "main", "commit": "c4ca798ab95b316a768d51eaaaee48f64a4a46bc" }, + "rose-pine": { "branch": "main", "commit": "72a04c4065345b51b56aed4859ea1d884f734097" } +} diff --git a/lsp/json_ls.lua b/lsp/json_ls.lua new file mode 100644 index 0000000..13479b2 --- /dev/null +++ b/lsp/json_ls.lua @@ -0,0 +1,6 @@ +return { + cmd = { 'vscode-json-language-server', '--stdio' }, + filetypes = { 'json', 'jsonc' }, + init_options = { provideFormatter = true }, + root_markers = { '.git' }, +} diff --git a/lsp/lua_ls.lua b/lsp/lua_ls.lua new file mode 100644 index 0000000..3033225 --- /dev/null +++ b/lsp/lua_ls.lua @@ -0,0 +1,14 @@ +return { + cmd = { "lua-language-server" }, + filetypes = { "lua" }, + root_markers = { + ".luarc.json", + ".luarc.jsonc", + ".luacheckrc", + ".stylua.toml", + "stylua.toml", + "selene.toml", + "selene.yml", + ".git", + }, +} diff --git a/lua/config/autocmds.lua b/lua/config/autocmds.lua new file mode 100644 index 0000000..befeecc --- /dev/null +++ b/lua/config/autocmds.lua @@ -0,0 +1,49 @@ +-- 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, +}) diff --git a/lua/config/clipboard.lua b/lua/config/clipboard.lua new file mode 100644 index 0000000..984aab8 --- /dev/null +++ b/lua/config/clipboard.lua @@ -0,0 +1,25 @@ +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 + +vim.schedule(function() + vim.opt.clipboard = 'unnamedplus' +end) + +-- TEMP: Check if it helps with edge cases +vim.api.nvim_create_user_command('FixClipboard', function() + vim.cmd('lua require("vim.ui.clipboard.osc52")') + vim.schedule(function() + vim.notify('Clipboard provider reloaded (OSC52)') + end) +end, {}) diff --git a/lua/config/keymaps.lua b/lua/config/keymaps.lua new file mode 100644 index 0000000..558464f --- /dev/null +++ b/lua/config/keymaps.lua @@ -0,0 +1,54 @@ +local map = vim.keymap.set + +map('n', 'q', vim.diagnostic.setloclist) + +map({ 'i', 'c' }, 'jk', '') +map('n', '', 'nohlsearch') + +-- Prevent overriding the register +map('n', 'x', '"_x') + +-- Window Navigation +map('n', '', 'h') +map('n', '', 'l') +map('n', '', 'j') +map('n', '', 'k') + +-- Tab management +map('n', 'tn', ':tabnew') +map('n', 'tc', ':tabclose') +map('n', 'tl', ':tabnext') +map('n', 'th', ':tabprevious') +map('n', 'tm.', ':tabmove +1') +map('n', 'tm,', ':tabmove -1') +for i = 1, 9 do + map('n', string.format('%d', i), string.format('%dgt', i)) +end + +-- Buffer Management +-- map('n', 'bl', ':ls') +-- map('n', 'bd', ':bdelete') +-- map('n', ']b', ':bnext') +-- map('n', '[b', ':bprevious') +-- map('n', 'bb', ':b') +-- map('n', 'bo', ':bufdo bd|1bd') + +-- Terminal +map('n', 'tt', ':TermDefault') +map('n', 'tr', ':TermRelative') +map('n', 'ts', ':TermSplit') +map('n', 'tv', ':TermVSplit') + +-- Terminal mode mappings +local tn = '' +map('t', '', tn) +map('t', 'jk', tn) +map('t', '', tn .. '') +map('t', '', 'wincmd h') +map('t', '', 'wincmd j') +map('t', '', 'wincmd k') +map('t', '', 'wincmd l') + +-- File explorer +vim.keymap.set('n', 'e', 'NvimTreeToggle') +vim.keymap.set('n', 'E', 'NvimTreeOpen') diff --git a/lua/config/options.lua b/lua/config/options.lua new file mode 100644 index 0000000..ef64b18 --- /dev/null +++ b/lua/config/options.lua @@ -0,0 +1,89 @@ +-- 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_netrw = 1 -- use nvim-tree instead +vim.g.loaded_netrwPlugin = 1 + +-- UI +vim.g.health = { style = 'float' } +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.winborder = 'rounded' +vim.opt.guicursor = 'n-v-i-c:block' +vim.opt.ruler = false +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 = { 'menu,menuone,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 diff --git a/lua/config/terminal.lua b/lua/config/terminal.lua new file mode 100644 index 0000000..6221433 --- /dev/null +++ b/lua/config/terminal.lua @@ -0,0 +1,62 @@ +local term_group = vim.api.nvim_create_augroup('custom-term-open', { clear = true }) +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, +}) + +-- Close all terminal buffers before quitting +vim.api.nvim_create_autocmd('QuitPre', { + group = vim.api.nvim_create_augroup('shoutoff_terminals', { clear = true }), + 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, +}) + +-- 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, +}) + +local function open_default() + vim.cmd('terminal') +end + +local function open_relative() + 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 + +local function open_split() + vim.cmd('new') + vim.cmd('wincmd J') + vim.api.nvim_win_set_height(0, 12) + vim.wo.winfixheight = true + vim.cmd('term') +end + +local function open_vertical() + vim.cmd('vsplit') + vim.cmd('term') +end + +vim.api.nvim_create_user_command('TermDefault', open_default, {}) +vim.api.nvim_create_user_command('TermRelative', open_relative, {}) +vim.api.nvim_create_user_command('TermSplit', open_split, {}) +vim.api.nvim_create_user_command('TermVSplit', open_vertical, {}) diff --git a/lua/custom/navigation.lua b/lua/custom/navigation.lua new file mode 100644 index 0000000..1c12870 --- /dev/null +++ b/lua/custom/navigation.lua @@ -0,0 +1,323 @@ +-- Minimal fuzzy finder + content search for Neovim 0.11+ +-- Optional: `fdfind` or `fd` for file listing, and `rg` (ripgrep) for text search. + +local Fuzzy = {} + +-------------------------------------------------------------------- +-- 🧩 Helpers +-------------------------------------------------------------------- + +-- Collect all files (try fdfind/fd first, then globpath) +local function get_file_list() + local handle = io.popen('fdfind --type f 2>/dev/null || fd --type f 2>/dev/null') + if handle then + local result = handle:read('*a') + handle:close() + if result and result ~= '' then + return vim.split(result, '\n', { trimempty = true }) + end + end + return vim.fn.globpath('.', '**/*', false, true) +end + +-- Create floating input + result windows +local function open_float(prompt) + local input_buf = vim.api.nvim_create_buf(false, true) + local result_buf = vim.api.nvim_create_buf(false, true) + + -- mark both buffers as scratch/unlisted + for _, b in ipairs({ input_buf, result_buf }) do + vim.bo[b].bufhidden = 'wipe' + vim.bo[b].buflisted = false + vim.bo[b].swapfile = false + end + vim.bo[input_buf].buftype = 'prompt' + vim.bo[result_buf].buftype = 'nofile' + + local width = math.floor(vim.o.columns * 0.7) + local height = 20 + local row = math.floor((vim.o.lines - height) / 2) + local col = math.floor((vim.o.columns - width) / 2) + + local input_win = vim.api.nvim_open_win(input_buf, true, { + relative = 'editor', + row = row, + col = col, + width = width, + height = 1, + style = 'minimal', + border = 'rounded', + }) + vim.fn.prompt_setprompt(input_buf, prompt) + + local result_win = vim.api.nvim_open_win(result_buf, false, { + relative = 'editor', + row = row + 2, + col = col, + width = width, + height = height - 2, + style = 'minimal', + border = 'single', + }) + + return input_buf, result_buf, input_win, result_win +end + +-------------------------------------------------------------------- +-- 🔵 Highlight current selection +-------------------------------------------------------------------- +function Fuzzy:highlight_selection() + if not self.result_buf then + return + end + if not self.ns_id then + self.ns_id = vim.api.nvim_create_namespace('FuzzyHighlight') + end + vim.api.nvim_buf_clear_namespace(self.result_buf, self.ns_id, 0, -1) + if self.matches and self.matches[self.cursor] then + local rel_cursor = self.cursor - (self.scroll or 0) + if rel_cursor >= 1 and rel_cursor <= self.page_size then + vim.api.nvim_buf_set_extmark(self.result_buf, self.ns_id, rel_cursor - 1, 0, { + end_line = rel_cursor, + hl_group = 'Visual', + hl_eol = true, + }) + end + end +end + +-------------------------------------------------------------------- +-- 🔴 Close all floating windows +-------------------------------------------------------------------- +function Fuzzy.close() + local wins = { Fuzzy.input_win, Fuzzy.result_win } + for _, win in ipairs(wins) do + if win and vim.api.nvim_win_is_valid(win) then + vim.api.nvim_win_close(win, true) + end + end + Fuzzy.active = false +end + +-------------------------------------------------------------------- +-- 🟢 File finder +-------------------------------------------------------------------- +function Fuzzy.open() + if Fuzzy.active then + Fuzzy.close() + end + Fuzzy.active = true + + Fuzzy.files = get_file_list() + Fuzzy.matches = Fuzzy.files + Fuzzy.cursor = 1 + Fuzzy.scroll = 0 + Fuzzy.page_size = 50 + + Fuzzy.input_buf, Fuzzy.result_buf, Fuzzy.input_win, Fuzzy.result_win = open_float('Search: ') + + local function render_results() + local total = #Fuzzy.matches + if total == 0 then + vim.api.nvim_buf_set_lines(Fuzzy.result_buf, 0, -1, false, { '-- no matches --' }) + return + end + local start_idx = Fuzzy.scroll + 1 + local end_idx = math.min(start_idx + Fuzzy.page_size - 1, total) + local display = {} + for i = start_idx, end_idx do + display[#display + 1] = Fuzzy.matches[i] + end + vim.api.nvim_buf_set_lines(Fuzzy.result_buf, 0, -1, false, display) + Fuzzy:highlight_selection() + end + + local function update_results(text) + if text == '' then + Fuzzy.matches = Fuzzy.files + else + Fuzzy.matches = vim.fn.matchfuzzy(Fuzzy.files, text) + end + Fuzzy.cursor, Fuzzy.scroll = 1, 0 + render_results() + end + + vim.api.nvim_create_autocmd({ 'TextChangedI', 'TextChangedP' }, { + buffer = Fuzzy.input_buf, + callback = function() + local text = vim.fn.getline('.'):gsub('^Search:%s*', '') + update_results(text) + end, + }) + + vim.keymap.set('i', '', function() + if Fuzzy.cursor < #Fuzzy.matches then + Fuzzy.cursor = Fuzzy.cursor + 1 + if Fuzzy.cursor > Fuzzy.scroll + Fuzzy.page_size then + Fuzzy.scroll = Fuzzy.scroll + 1 + end + render_results() + end + end, { buffer = Fuzzy.input_buf }) + + vim.keymap.set('i', '', function() + if Fuzzy.cursor > 1 then + Fuzzy.cursor = Fuzzy.cursor - 1 + if Fuzzy.cursor <= Fuzzy.scroll then + Fuzzy.scroll = math.max(Fuzzy.scroll - 1, 0) + end + render_results() + end + end, { buffer = Fuzzy.input_buf }) + + vim.keymap.set('i', '', function() + local choice = Fuzzy.matches[Fuzzy.cursor] + if choice then + Fuzzy.close() + vim.cmd.edit(vim.fn.fnameescape(choice)) + end + end, { buffer = Fuzzy.input_buf }) + + vim.keymap.set('i', '', Fuzzy.close, { buffer = Fuzzy.input_buf }) + vim.keymap.set('i', '', Fuzzy.close, { buffer = Fuzzy.input_buf }) + vim.keymap.set('n', '', Fuzzy.close, { buffer = Fuzzy.input_buf }) + vim.keymap.set('n', 'q', Fuzzy.close, { buffer = Fuzzy.input_buf }) + + vim.cmd.startinsert() +end + +-------------------------------------------------------------------- +-- 🟣 Ripgrep-based content search (scrolling + match highlighting) +-------------------------------------------------------------------- +function Fuzzy.open_grep() + if Fuzzy.active then + Fuzzy.close() + end + Fuzzy.active = true + + Fuzzy.input_buf, Fuzzy.result_buf, Fuzzy.input_win, Fuzzy.result_win = open_float('Grep: ') + Fuzzy.matches, Fuzzy.cursor, Fuzzy.scroll = {}, 1, 0 + Fuzzy.page_size = 50 + Fuzzy.ns_id = vim.api.nvim_create_namespace('FuzzyHighlight') + + local function render_results(query) + local total = #Fuzzy.matches + if total == 0 then + vim.api.nvim_buf_set_lines(Fuzzy.result_buf, 0, -1, false, { '-- no matches --' }) + return + end + + local start_idx = Fuzzy.scroll + 1 + local end_idx = math.min(start_idx + Fuzzy.page_size - 1, total) + local display = {} + for i = start_idx, end_idx do + display[#display + 1] = Fuzzy.matches[i] + end + + vim.api.nvim_buf_set_lines(Fuzzy.result_buf, 0, -1, false, display) + vim.api.nvim_buf_clear_namespace(Fuzzy.result_buf, Fuzzy.ns_id, 0, -1) + + -- highlight selection + local rel_cursor = math.min(Fuzzy.cursor - Fuzzy.scroll, #display) + vim.api.nvim_buf_set_extmark(Fuzzy.result_buf, Fuzzy.ns_id, rel_cursor - 1, 0, { + end_line = rel_cursor, + hl_group = 'Visual', + hl_eol = true, + }) + + -- highlight query matches + if query and query ~= '' then + local pattern = vim.pesc(query) + for i, line in ipairs(display) do + for s, e in line:gmatch('()' .. pattern .. '()') do + vim.api.nvim_buf_set_extmark(Fuzzy.result_buf, Fuzzy.ns_id, i - 1, s - 1, { + end_col = e - 1, + hl_group = 'Search', + }) + end + end + end + end + + local function run_grep(query) + if query == '' then + vim.api.nvim_buf_set_lines(Fuzzy.result_buf, 0, -1, false, { '-- type to search --' }) + return + end + local handle = io.popen('rg --vimgrep --hidden --smart-case ' .. vim.fn.shellescape(query)) + if not handle then + return + end + local result = handle:read('*a') + handle:close() + Fuzzy.matches = vim.split(result, '\n', { trimempty = true }) + Fuzzy.cursor, Fuzzy.scroll = 1, 0 + render_results(query) + end + + vim.api.nvim_create_autocmd({ 'TextChangedI', 'TextChangedP' }, { + buffer = Fuzzy.input_buf, + callback = function() + local text = vim.fn.getline('.'):gsub('^Grep:%s*', '') + run_grep(text) + end, + }) + + vim.keymap.set('i', '', function() + if Fuzzy.cursor < #Fuzzy.matches then + Fuzzy.cursor = Fuzzy.cursor + 1 + if Fuzzy.cursor > Fuzzy.scroll + Fuzzy.page_size then + Fuzzy.scroll = Fuzzy.scroll + 1 + end + local query = vim.fn.getline('.'):gsub('^Grep:%s*', '') + render_results(query) + end + end, { buffer = Fuzzy.input_buf }) + + vim.keymap.set('i', '', function() + if Fuzzy.cursor > 1 then + Fuzzy.cursor = Fuzzy.cursor - 1 + if Fuzzy.cursor <= Fuzzy.scroll then + Fuzzy.scroll = math.max(Fuzzy.scroll - 1, 0) + end + local query = vim.fn.getline('.'):gsub('^Grep:%s*', '') + render_results(query) + end + end, { buffer = Fuzzy.input_buf }) + + vim.keymap.set('i', '', function() + local line = Fuzzy.matches[Fuzzy.cursor] + if line then + local parts = vim.split(line, ':') + local file, lnum = parts[1], tonumber(parts[2]) or 1 + Fuzzy.close() + vim.cmd.edit(vim.fn.fnameescape(file)) + vim.api.nvim_win_set_cursor(0, { lnum, 0 }) + end + end, { buffer = Fuzzy.input_buf }) + + vim.keymap.set('i', '', function() + Fuzzy.close() + end, { buffer = Fuzzy.input_buf }) + + vim.cmd.startinsert() +end + +-------------------------------------------------------------------- +-- 🧩 Commands & Keymaps +-------------------------------------------------------------------- +vim.api.nvim_create_user_command('FuzzyLive', function() + Fuzzy.open() +end, {}) +vim.api.nvim_create_user_command('FuzzyGrep', function() + Fuzzy.open_grep() +end, {}) + +vim.keymap.set('n', 'f', function() + vim.cmd.FuzzyLive() +end, { desc = 'Open fuzzy file finder' }) +vim.keymap.set('n', 'g', function() + vim.cmd.FuzzyGrep() +end, { desc = 'Search file contents with ripgrep' }) + +return Fuzzy diff --git a/lua/plugins/colorscheme.lua b/lua/plugins/colorscheme.lua new file mode 100644 index 0000000..6668657 --- /dev/null +++ b/lua/plugins/colorscheme.lua @@ -0,0 +1,26 @@ +return { + 'triimdev/invero.nvim', + lazy = false, + priority = 1000, + dev = true, + 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 { + WinSeparator = { fg = c.outline, bg = c.base }, + StatusLine = { fg = c.outline, bg = c.base }, + StatusLineNC = { fg = c.text, bg = c.base, bold = true }, + } + end, + }) + + vim.o.background = 'light' + vim.cmd.colorscheme('invero') + end, +} diff --git a/lua/plugins/filetree.lua b/lua/plugins/filetree.lua new file mode 100644 index 0000000..7cf75e5 --- /dev/null +++ b/lua/plugins/filetree.lua @@ -0,0 +1,148 @@ +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 { + 'nvim-tree/nvim-tree.lua', + version = '*', + dev = true, + 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/plugins/syntax.lua b/lua/plugins/syntax.lua new file mode 100644 index 0000000..7ed22c6 --- /dev/null +++ b/lua/plugins/syntax.lua @@ -0,0 +1,128 @@ +vim.keymap.set('n', 'd', vim.diagnostic.open_float, { noremap = true, silent = true }) + +vim.diagnostic.config({ + update_in_insert = false, + virtual_text = { + prefix = '', -- remove annoying ▎ etc + format = function(diagnostic) + if diagnostic.source then + return string.format('[%s] %s', diagnostic.source, diagnostic.message) + end + return diagnostic.message + end, + }, + float = { + border = 'rounded', + source = true, -- show source in floating window too + }, + severity_sort = true, +}) + +vim.lsp.enable({ 'lua_ls' }) +vim.lsp.enable({ 'json_ls' }) + +return { + { 'mason-org/mason.nvim', config = true }, + { 'windwp/nvim-ts-autotag', config = true }, + { 'windwp/nvim-autopairs', event = 'InsertEnter', config = true }, + + -- { 'saghen/blink.cmp', version = '1.*' }, + { + 'nvim-treesitter/nvim-treesitter', + build = ':TSUpdate', + main = 'nvim-treesitter.configs', + opts = { + highlight = { enable = true }, + incremental_selection = { enable = true }, + textobjects = { enable = true }, + ensure_installed = { + -- Documentation + 'vim', + 'vimdoc', + 'markdown', + 'markdown_inline', + -- Data + 'gitcommit', + 'gitignore', + 'dockerfile', + 'diff', + 'json', + 'jsonc', + -- Scripting + 'bash', + 'lua', + 'sql', + -- Programming + 'c', + 'cpp', + 'go', + 'rust', + 'python', + -- Web + 'html', + 'css', + 'javascript', + 'typescript', + 'tsx', + }, + }, + }, + { + 'mfussenegger/nvim-lint', + event = { 'BufReadPre', 'BufNewFile' }, + opts = { + linters_by_ft = { + lua = { 'luacheck' }, + python = { 'ruff' }, + javascript = { 'eslint_d' }, + }, + }, + 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 = { + -- Automatically format on save + format_on_save = { + timeout_ms = 500, + lsp_format = 'fallback', -- Use LSP when no formatter is configured + }, + + -- Formatters per filetype + default_format_opts = { stop_after_first = true }, + formatters_by_ft = { + lua = { 'stylua' }, + sh = { 'shfmt' }, + swift = { 'swift_format' }, + python = { 'isort', 'black' }, + json = { 'jq' }, + javascript = { 'prettierd', 'prettier' }, + javascriptreact = { 'prettierd', 'prettier' }, + typescript = { 'prettierd', 'prettier' }, + typescriptreact = { 'prettierd', 'prettier' }, + }, + }, + + config = function(_, opts) + require('conform').setup(opts) + + -- Create a command to format manually + vim.api.nvim_create_user_command('Format', function() + require('conform').format({ + async = true, + lsp_format = 'fallback', + }) + end, { desc = 'Format current buffer' }) + end, + }, +}