├── .envrc ├── .ripgreprc ├── .gitignore ├── queries └── gotmpl │ └── injections.scm ├── .editorconfig ├── shell.nix ├── lua ├── plugins │ ├── hop.lua │ ├── dap_persistent_breakpoints.lua │ ├── noice.lua │ ├── minty.lua │ ├── overseer.lua │ ├── neocodeium.lua │ ├── aw-watcher.lua │ ├── colorizer.lua │ ├── dap_virtual_text.lua │ ├── notify.lua │ ├── toggleterm.lua │ ├── kulala.lua │ ├── nvim-lint.lua │ ├── render-markdown.lua │ ├── rustacean.lua │ ├── language-servers.lua │ ├── copilot_chat.lua │ ├── lualine.lua │ ├── telescope.lua │ ├── trouble.lua │ ├── image.lua │ ├── no_config.lua │ ├── tiny_code_action.lua │ ├── colorful-menu.lua │ ├── barbar.lua │ ├── opencode.lua │ ├── dap_ui.lua │ ├── treesitter.lua │ ├── neotree.lua │ ├── dap.lua │ ├── which_key.lua │ ├── neotest.lua │ ├── conform.lua │ └── cmp.lua ├── init.lua ├── config │ └── lazy.lua └── lsp │ ├── lua_ls.lua │ ├── init.lua │ └── cspellls.lua ├── flake.nix ├── maps.vim ├── helpers ├── autosave.vim ├── close_initial_empty_tab.vim ├── functions.vim ├── buffers.vim └── rust_prettifier_for_lldb.py ├── coc-settings.json ├── gui.vim ├── LICENSE.txt ├── session_management.vim ├── AGENTS.md ├── README.md ├── flake.lock ├── .vsvimrc ├── .ideavimrc ├── theme.vim ├── plugins_config.vim ├── plugins.vim ├── nerdtree.vim ├── init.vim ├── lazy-lock.json ├── coc_nvim.vim └── manage_plugins.vim /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.ripgreprc: -------------------------------------------------------------------------------- 1 | --hidden 2 | 3 | --iglob=!.git 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .netrwhist 2 | autoload/plug.vim 3 | plugged 4 | .session.vim 5 | .session.nvim 6 | *.old 7 | .direnv 8 | -------------------------------------------------------------------------------- /queries/gotmpl/injections.scm: -------------------------------------------------------------------------------- 1 | ;; extends 2 | 3 | ((text) @injection.content 4 | (#set! injection.language "html") 5 | (#set! injection.combined)) 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | max_line_length = 120 10 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { }, ... }: 2 | 3 | pkgs.mkShell { 4 | name = "vimfiles"; 5 | nativeBuildInputs = with pkgs; [ 6 | ]; 7 | shellHook = '' 8 | echo "Let's (Neo)Vim!" 9 | ''; 10 | } 11 | -------------------------------------------------------------------------------- /lua/plugins/hop.lua: -------------------------------------------------------------------------------- 1 | -- Neovim motions on speed! (easymotion like motions) 2 | -- https://github.com/smoka7/hop.nvim 3 | return { 4 | "smoka7/hop.nvim", 5 | version = "*", 6 | lazy = true, 7 | opts = {}, 8 | keys = { 9 | { "", "HopWord", desc = "Hop to word" }, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /lua/plugins/dap_persistent_breakpoints.lua: -------------------------------------------------------------------------------- 1 | -- Neovim plugin for persistent breakpoints. 2 | -- https://github.com/Weissle/persistent-breakpoints.nvim 3 | return { 4 | "Weissle/persistent-breakpoints.nvim", 5 | event = "VeryLazy", 6 | opts = { 7 | load_breakpoints_event = { "BufReadPost" }, 8 | always_reload = true, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /lua/plugins/noice.lua: -------------------------------------------------------------------------------- 1 | -- Highly experimental plugin that completely replaces the UI for messages, cmdline and the popupmenu. 2 | -- https://github.com/folke/noice.nvim 3 | return { 4 | "folke/noice.nvim", 5 | event = "VeryLazy", 6 | opts = {}, 7 | dependencies = { 8 | "MunifTanjim/nui.nvim", 9 | "rcarriga/nvim-notify", 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /lua/plugins/minty.lua: -------------------------------------------------------------------------------- 1 | -- Most Beautifully crafted color tools for Neovim 2 | -- https://github.com/nvzone/minty 3 | return { 4 | "nvzone/minty", 5 | cmd = { "Shades", "Huefy" }, 6 | lazy = true, 7 | dependencies = { 8 | { "nvzone/volt", lazy = true }, -- Create blazing fast & beautiful reactive UI in Neovim https://github.com/nvzone/volt 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /lua/plugins/overseer.lua: -------------------------------------------------------------------------------- 1 | -- A task runner and job management plugin for Neovim 2 | -- https://github.com/stevearc/overseer.nvim 3 | return { 4 | "stevearc/overseer.nvim", 5 | config = function() 6 | require("overseer").setup({}) 7 | end, 8 | opts = {}, 9 | keys = { 10 | { "", "OverseerRun BUILD", desc = "Run BUILD task" }, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /lua/plugins/neocodeium.lua: -------------------------------------------------------------------------------- 1 | -- https://github.com/monkoose/neocodeium 2 | -- free AI completion plugin for neovim that uses Windsurf 3 | return { 4 | "monkoose/neocodeium", 5 | event = "VeryLazy", 6 | config = function() 7 | local neocodeium = require("neocodeium") 8 | neocodeium.setup() 9 | vim.keymap.set("i", "", neocodeium.accept) 10 | end, 11 | } 12 | -------------------------------------------------------------------------------- /lua/plugins/aw-watcher.lua: -------------------------------------------------------------------------------- 1 | -- A neovim watcher for ActivityWatch time tracker. 2 | -- https://github.com/lowitea/aw-watcher.nvim 3 | return { 4 | "lowitea/aw-watcher.nvim", 5 | lazy = true, 6 | event = "VeryLazy", 7 | config = function() 8 | require("aw_watcher").setup({ 9 | aw_server = { 10 | pulsetime = 5, 11 | }, 12 | }) 13 | end, 14 | } 15 | -------------------------------------------------------------------------------- /lua/plugins/colorizer.lua: -------------------------------------------------------------------------------- 1 | -- The fastest Neovim colorizer 2 | -- https://github.com/catgoose/nvim-colorizer.lua 3 | return { 4 | "catgoose/nvim-colorizer.lua", 5 | event = "BufReadPre", 6 | opts = { 7 | lazy_load = true, 8 | user_default_options = { 9 | mode = "virtualtext", 10 | virtualtext_inline = "before", 11 | xterm = true, 12 | }, 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /lua/plugins/dap_virtual_text.lua: -------------------------------------------------------------------------------- 1 | -- This plugin adds virtual text support to nvim-dap. 2 | -- https://github.com/theHamsta/nvim-dap-virtual-text 3 | return { 4 | "theHamsta/nvim-dap-virtual-text", 5 | config = function() 6 | require("nvim-dap-virtual-text").setup({}) 7 | end, 8 | requires = { 9 | "nvim-treesitter/nvim-treesitter", 10 | "mfussenegger/nvim-dap", 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /lua/plugins/notify.lua: -------------------------------------------------------------------------------- 1 | -- A fancy, configurable, notification manager for NeoVim 2 | -- https://github.com/rcarriga/nvim-notify 3 | return { 4 | "rcarriga/nvim-notify", 5 | lazy = true, 6 | event = "VeryLazy", 7 | config = function() 8 | local notify = require("notify") 9 | notify.setup({ 10 | background_colour = "#000000", 11 | }) 12 | vim.notify = notify 13 | end, 14 | } 15 | -------------------------------------------------------------------------------- /lua/plugins/toggleterm.lua: -------------------------------------------------------------------------------- 1 | -- A neovim lua plugin to help easily manage multiple terminal windows 2 | -- https://github.com/akinsho/toggleterm.nvim 3 | return { 4 | "akinsho/toggleterm.nvim", 5 | version = "*", 6 | opts = { 7 | open_mapping = [[]], 8 | insert_mappings = true, 9 | terminal_mappings = true, 10 | direction = "float", 11 | float_opts = { 12 | border = "curved", 13 | }, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "vimfiles"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | }; 8 | 9 | outputs = { self, nixpkgs, flake-utils }: 10 | flake-utils.lib.eachDefaultSystem (system: 11 | let 12 | pkgs = import nixpkgs { 13 | inherit system; 14 | }; 15 | in 16 | { 17 | devShells.default = import ./shell.nix { inherit pkgs; }; 18 | } 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /maps.vim: -------------------------------------------------------------------------------- 1 | inoremap jj 2 | nnoremap oo ok 3 | nnoremap OO Oj 4 | 5 | " remap split navigation 6 | nnoremap 7 | nnoremap 8 | nnoremap 9 | nnoremap 10 | 11 | if !has('nvim') 12 | " reformat (visual and normal mode) 13 | function! s:Reformat() 14 | mark a 15 | normal! ggVGgq 16 | 'a 17 | delmarks a 18 | endfunction 19 | nnoremap j :call Reformat() 20 | xnoremap j gq 21 | endif 22 | -------------------------------------------------------------------------------- /helpers/autosave.vim: -------------------------------------------------------------------------------- 1 | if has('timers') 2 | let g:autosave_enabled = 1 3 | let g:autosave_timer = 0 4 | 5 | function! RunAutosaveTimer() abort 6 | if !g:autosave_enabled 7 | return 8 | endif 9 | if g:autosave_timer > 0 10 | call timer_stop(g:autosave_timer) 11 | endif 12 | let g:autosave_timer = timer_start(3000, 'WriteAllFiles') 13 | endfunction 14 | 15 | function! WriteAllFiles(_) 16 | if mode() != 'i' 17 | execute 'wall' 18 | endif 19 | endfunction 20 | 21 | augroup AutoSaveDebounce 22 | autocmd! 23 | autocmd TextChanged,InsertLeave * call RunAutosaveTimer() 24 | augroup END 25 | endif 26 | -------------------------------------------------------------------------------- /lua/plugins/kulala.lua: -------------------------------------------------------------------------------- 1 | -- A fully-featured HTTP-client interface for Neovim 2 | -- https://github.com/mistweaverco/kulala.nvim 3 | return { 4 | { 5 | "mistweaverco/kulala.nvim", 6 | -- todo: go back to main when treesitter/main stabilizes, see: https://github.com/mistweaverco/kulala.nvim/issues/591 7 | commit = "698307bbe630a5cce93addd942fb721cf4ff32bf", 8 | init = function() 9 | vim.filetype.add({ 10 | extension = { 11 | ["http"] = "http", 12 | }, 13 | }) 14 | end, 15 | keys = { 16 | { "Rs", desc = "Send request" }, 17 | { "Ra", desc = "Send all requests" }, 18 | { "Rb", desc = "Open scratchpad" }, 19 | }, 20 | ft = { "http", "rest" }, 21 | opts = { 22 | global_keymaps = true, 23 | global_keymaps_prefix = "R", 24 | kulala_keymaps_prefix = "", 25 | }, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /lua/plugins/nvim-lint.lua: -------------------------------------------------------------------------------- 1 | -- An asynchronous linter plugin for Neovim complementary to the built-in Language Server Protocol support. 2 | -- https://github.com/mfussenegger/nvim-lint 3 | return { 4 | "mfussenegger/nvim-lint", 5 | opts = {}, 6 | lazy = true, 7 | event = "VeryLazy", 8 | init = function() 9 | vim.api.nvim_create_augroup("LintOnOpenAndSave", { clear = true }) 10 | vim.api.nvim_create_autocmd({ "BufReadPost", "BufWritePost" }, { 11 | group = "LintOnOpenAndSave", 12 | callback = function() 13 | local path = vim.fn.expand("%:p") 14 | if vim.bo.buftype == "" and vim.loop.fs_stat(path) then 15 | require("lint").try_lint() 16 | end 17 | end, 18 | }) 19 | end, 20 | config = function() 21 | require("lint").linters_by_ft = { 22 | markdown = { "markdownlint-cli2" }, 23 | dockerfile = { "hadolint" }, 24 | } 25 | end, 26 | } 27 | -------------------------------------------------------------------------------- /coc-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.procMacro.enable": true, 3 | "rust-analyzer.check.command": "clippy", 4 | "languageserver": { 5 | "nixd": { 6 | "command": "nixd", 7 | "rootPatterns": [".nixd.json"], 8 | "filetypes": ["nix"] 9 | }, 10 | "dockerfile": { 11 | "command": "docker-langserver", 12 | "filetypes": ["dockerfile"], 13 | "args": ["--stdio"] 14 | }, 15 | "dockercompose": { 16 | "command": "docker-compose-langserver", 17 | "args": ["--stdio"], 18 | "filetypes": ["dockercompose"], 19 | "rootPatterns": [".git", ".env", "docker-compose.yml", "compose.yml"] 20 | }, 21 | "systemd-language-server": { 22 | "command": "systemd-language-server", 23 | "filetypes": ["systemd"] 24 | }, 25 | "csharp-ls": { 26 | "command": "csharp-ls", 27 | "filetypes": ["cs"], 28 | "rootPatterns": ["*.sln", ".git/"] 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /gui.vim: -------------------------------------------------------------------------------- 1 | if has("gui_running") 2 | if has('autocmd') 3 | augroup SetVisualBell 4 | autocmd! 5 | autocmd GUIEnter * set visualbell t_vb= 6 | augroup END 7 | endif 8 | set lines=999 columns=999 9 | if has("gui_gtk2") 10 | set guifont=Inconsolata\ 12 11 | elseif has("gui_macvim") 12 | set guifont=Menlo\ Regular:h14 13 | elseif has("gui_win32") 14 | " au GUIEnter * simalt ~x " todo: maximize the window on Windows only, maybe not necessary, verify, if it works with set lines above, remove 15 | set guifont=CaskaydiaCove\ NF:h11:cANSI,DejaVu_Sans_Mono_for_Powerline:h11:cANSI,Consolas:h11:cANSI,Courier:h12:cANSI 16 | endif 17 | if ! has('nvim') 18 | :set guioptions+=m "add menu bar 19 | :set guioptions+=R "remove right-hand scroll bar 20 | :set guioptions-=T "remove toolbar 21 | :set guioptions-=r "remove right-hand scroll bar 22 | :set guioptions-=l "remove left-hand scroll bar 23 | :set guioptions-=L "remove left-hand scroll bar 24 | endif 25 | endif 26 | 27 | -------------------------------------------------------------------------------- /lua/init.lua: -------------------------------------------------------------------------------- 1 | vim.loader.enable() 2 | require("lsp") 3 | 4 | local nix_profiles = os.getenv("NIX_PROFILES") 5 | if nix_profiles then 6 | if package.path:sub(-1) ~= ";" then 7 | package.path = package.path .. ";" 8 | end 9 | if package.cpath:sub(-1) ~= ";" then 10 | package.cpath = package.cpath .. ";" 11 | end 12 | for _, profile in ipairs(vim.split(nix_profiles, " ")) do 13 | if profile ~= "" then 14 | if profile:sub(-1) ~= "/" then 15 | profile = profile .. "/" 16 | end 17 | local share_lua_path = profile .. "share/lua/5.1" 18 | if vim.fn.isdirectory(share_lua_path) == 1 then 19 | package.path = package.path 20 | .. share_lua_path 21 | .. "/?.lua;" 22 | .. share_lua_path 23 | .. "/?/init.lua;" 24 | end 25 | local lib_lua_path = profile .. "lib/lua/5.1" 26 | if vim.fn.isdirectory(lib_lua_path) == 1 then 27 | package.cpath = package.cpath .. lib_lua_path .. "/?.so;" 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lua/plugins/render-markdown.lua: -------------------------------------------------------------------------------- 1 | -- Plugin to improve viewing Markdown files in Neovim 2 | -- https://github.com/MeanderingProgrammer/render-markdown.nvim 3 | return { 4 | "MeanderingProgrammer/render-markdown.nvim", 5 | dependencies = { "nvim-treesitter/nvim-treesitter" }, 6 | ---@module 'render-markdown' 7 | ---@type render.md.UserConfig 8 | opts = { 9 | code = { 10 | enabled = true, 11 | -- language_info = false, 12 | language_name = false, 13 | }, 14 | anti_conceal = { 15 | enabled = true, 16 | disabled_modes = false, 17 | above = 1, 18 | below = 1, 19 | ignore = { 20 | code_background = true, 21 | }, 22 | }, 23 | }, 24 | config = function() 25 | vim.api.nvim_create_autocmd({ "ColorScheme" }, { 26 | group = vim.api.nvim_create_augroup("RenderMarkdownColorScheme", { clear = true }), 27 | callback = function() 28 | vim.cmd("highlight RenderMarkdownCode guibg=#1D1D1D ctermbg=black") 29 | end, 30 | }) 31 | end, 32 | } 33 | -------------------------------------------------------------------------------- /lua/plugins/rustacean.lua: -------------------------------------------------------------------------------- 1 | -- 🦀 Supercharge your Rust experience in Neovim! 2 | -- https://github.com/mrcjkb/rustaceanvim 3 | return { 4 | "mrcjkb/rustaceanvim", 5 | version = "^6", 6 | lazy = false, -- This plugin is already lazy 7 | opt = { 8 | tools = { 9 | enable_nextest = false, 10 | }, 11 | }, 12 | config = function() 13 | vim.g.rustaceanvim = { 14 | -- Plugin configuration 15 | tools = { 16 | -- enable_nextest = false, 17 | }, 18 | -- LSP configuration 19 | server = { 20 | on_attach = function(client, bufnr) 21 | -- you can also put keymaps in here 22 | end, 23 | default_settings = { 24 | ["rust-analyzer"] = { 25 | diagnostics = { 26 | disabled = { "inactive-code" }, 27 | }, 28 | cfg = { 29 | setTest = true, 30 | }, 31 | }, 32 | }, 33 | }, 34 | -- DAP configuration 35 | -- dap = { 36 | -- }, 37 | } 38 | end, 39 | } 40 | -------------------------------------------------------------------------------- /lua/plugins/language-servers.lua: -------------------------------------------------------------------------------- 1 | return { 2 | -- JSON schemas for Neovim 3 | -- https://github.com/b0o/SchemaStore.nvim 4 | "b0o/schemastore.nvim", 5 | { 6 | -- A neovim plugin that helps managing crates.io dependencies 7 | -- https://github.com/saecki/crates.nvim 8 | "saecki/crates.nvim", 9 | tag = "stable", 10 | config = function() 11 | require("crates").setup({ 12 | lsp = { 13 | enabled = true, 14 | -- on_attach = function(client, bufnr) 15 | -- -- the same on_attach function as for your other language servers 16 | -- -- can be ommited if you're using the `LspAttach` autocmd 17 | -- end, 18 | actions = true, 19 | completion = true, 20 | hover = true, 21 | }, 22 | completion = { 23 | crates = { 24 | enabled = true, 25 | max_results = 8, 26 | min_chars = 3, 27 | }, 28 | cmp = { 29 | enabled = true, 30 | }, 31 | }, 32 | }) 33 | end, 34 | }, 35 | } 36 | -------------------------------------------------------------------------------- /lua/plugins/copilot_chat.lua: -------------------------------------------------------------------------------- 1 | -- https://github.com/CopilotC-Nvim/CopilotChat.nvim 2 | -- Chat with GitHub Copilot in Neovim 3 | return {} 4 | -- return { 5 | -- { 6 | -- "CopilotC-Nvim/CopilotChat.nvim", 7 | -- event = "VeryLazy", 8 | -- enabled = vim.fn.has("unix") == 1, -- not working on Windows 9 | -- dependencies = { 10 | -- { "github/copilot.vim" }, -- or zbirenbaum/copilot.lua 11 | -- { "nvim-lua/plenary.nvim", branch = "master" }, -- for curl, log and async functions 12 | -- }, 13 | -- build = "make tiktoken", -- Only on MacOS or Linux 14 | -- opts = { 15 | -- { 16 | -- model = "gpt-4.1", -- AI model to use 17 | -- temperature = 0.1, -- Lower = focused, higher = creative 18 | -- window = { 19 | -- layout = "vertical", -- 'vertical', 'horizontal', 'float' 20 | -- width = 0.3, -- 50% of screen width 21 | -- }, 22 | -- auto_insert_mode = true, -- Enter insert mode when opening 23 | -- }, 24 | -- }, 25 | -- -- See Commands section for default commands if you want to lazy load on them 26 | -- }, 27 | -- } 28 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Giovanni Bassi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lua/plugins/lualine.lua: -------------------------------------------------------------------------------- 1 | -- https://github.com/nvim-lualine/lualine.nvim 2 | -- A blazing fast and easy to configure neovim statusline plugin written in pure lua. 3 | return { 4 | "nvim-lualine/lualine.nvim", 5 | dependencies = { "nvim-tree/nvim-web-devicons" }, 6 | config = function() 7 | require("lualine").setup({ 8 | options = { 9 | theme = "ayu_dark", 10 | }, 11 | extensions = { 12 | "neo-tree", 13 | "lazy", 14 | "fugitive", 15 | "nvim-dap-ui", 16 | "man", 17 | "overseer", 18 | "quickfix", 19 | "trouble", 20 | { sections = { lualine_a = { "filename" } }, filetypes = { "neotest-summary" } }, 21 | }, 22 | sections = { 23 | lualine_c = { 24 | { 25 | "filename", 26 | path = 1, 27 | shorting_target = 50, 28 | }, 29 | }, 30 | lualine_x = { 31 | -- 'lsp_status', 32 | "encoding", 33 | "fileformat", 34 | { 35 | "filetype", 36 | icon_only = true, 37 | }, 38 | }, 39 | }, 40 | }) 41 | end, 42 | } 43 | -------------------------------------------------------------------------------- /session_management.vim: -------------------------------------------------------------------------------- 1 | function! s:SessionAutoStart() 2 | let g:session_autoloading=0 3 | if exists("g:session_disable_autoload") && g:session_disable_autoload == 1 4 | return 5 | endif 6 | if argc() > 0 7 | let s:session_autoload=0 8 | return 9 | endif 10 | let s:session_autoload=1 11 | let g:session_autoloading=1 12 | if filereadable(".session.vim") 13 | source .session.vim 14 | endif 15 | let g:session_autoloading=0 16 | endfunction 17 | 18 | function! SaveSession() 19 | call VerboseEchomsg("Saving session...") 20 | if s:session_autoload == 0 21 | return 22 | endif 23 | if exists("g:session_disable_autoload") && g:session_disable_autoload == 1 24 | return 25 | endif 26 | silent! mksession! .session.vim 27 | call VerboseEchomsg("Session saved.") 28 | endfunction 29 | 30 | set sessionoptions-=options 31 | set sessionoptions-=blank 32 | set sessionoptions-=help 33 | set sessionoptions-=terminal 34 | let s:session_autoload=0 35 | 36 | if has("autocmd") 37 | augroup SessionAuto 38 | autocmd! 39 | autocmd VimLeavePre * call SaveSession() 40 | autocmd VimEnter * ++nested call s:SessionAutoStart() 41 | augroup END 42 | endif 43 | -------------------------------------------------------------------------------- /lua/plugins/telescope.lua: -------------------------------------------------------------------------------- 1 | -- Find, Filter, Preview, Pick. All lua, all the time. 2 | -- https://github.com/nvim-telescope/telescope.nvim 3 | return { 4 | "nvim-telescope/telescope.nvim", 5 | -- todo: switch back to branch or tag when treesitter integration is released: branch = '0.1.x', 6 | commit = "b4da76be54691e854d3e0e02c36b0245f945c2c7", 7 | dependencies = { 8 | "nvim-lua/plenary.nvim", 9 | { "nvim-telescope/telescope-fzf-native.nvim", build = "make" }, 10 | "nvim-telescope/telescope-ui-select.nvim", 11 | "nvim-telescope/telescope-dap.nvim", 12 | "nvim-telescope/telescope-github.nvim", 13 | }, 14 | lazy = true, 15 | event = "VeryLazy", 16 | config = function() 17 | require("telescope").setup({ 18 | extensions = { 19 | ["ui-select"] = { 20 | require("telescope.themes").get_dropdown({}), 21 | }, 22 | }, 23 | }) 24 | require("telescope").load_extension("ui-select") 25 | require("telescope").load_extension("fzf") 26 | require("telescope").load_extension("dap") 27 | end, 28 | keys = { 29 | { "", "Telescope find_files" }, 30 | { "\\f", "Telescope live_grep" }, 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /lua/plugins/trouble.lua: -------------------------------------------------------------------------------- 1 | -- A pretty diagnostics, references, telescope results, quickfix and location list to help you solve all the trouble your code is causing. 2 | -- https://github.com/folke/trouble.nvim 3 | return { 4 | "folke/trouble.nvim", 5 | opts = {}, 6 | cmd = "Trouble", 7 | keys = { 8 | { 9 | "xx", 10 | "Trouble diagnostics toggle", 11 | desc = "Diagnostics (Trouble)", 12 | }, 13 | { 14 | "xX", 15 | "Trouble diagnostics toggle filter.buf=0", 16 | desc = "Buffer Diagnostics (Trouble)", 17 | }, 18 | { 19 | "cs", 20 | "Trouble symbols toggle focus=false", 21 | desc = "Symbols (Trouble)", 22 | }, 23 | { 24 | "cl", 25 | "Trouble lsp toggle focus=false win.position=right", 26 | desc = "LSP Definitions / references / ... (Trouble)", 27 | }, 28 | { 29 | "xL", 30 | "Trouble loclist toggle", 31 | desc = "Location List (Trouble)", 32 | }, 33 | { 34 | "xQ", 35 | "Trouble qflist toggle", 36 | desc = "Quickfix List (Trouble)", 37 | }, 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /helpers/close_initial_empty_tab.vim: -------------------------------------------------------------------------------- 1 | function! s:CheckIfInitialTabIsEmpty() 2 | if g:session_autoloading == 1 3 | return 4 | endif 5 | let g:ace_prev_buf = bufnr('%') 6 | let g:ace_prev_tab = tabpagenr() 7 | " no filename, not modified, exactly 1 liner, that line is empty 8 | let g:ace_prev_empty = empty(bufname(g:ace_prev_buf)) 9 | \ && !getbufvar(g:ace_prev_buf, '&modified') 10 | \ && line("$", bufwinid(g:ace_prev_buf)) == 1 11 | \ && getbufline(g:ace_prev_buf, 1)[0] ==# '' 12 | endfunction 13 | function! s:CloseInitialEmptyTab() 14 | if g:session_autoloading == 1 15 | return 16 | endif 17 | if tabpagenr('$') == 1 " do not close the only tab 18 | unlet g:ace_prev_empty g:ace_prev_buf g:ace_prev_tab 19 | return 20 | endif 21 | if exists('g:ace_prev_empty') && g:ace_prev_empty && bufloaded(g:ace_prev_buf) 22 | execute 'tabclose' g:ace_prev_tab 23 | execute 'silent! bdelete' g:ace_prev_buf 24 | unlet g:ace_prev_empty g:ace_prev_buf g:ace_prev_tab 25 | endif 26 | endfunction 27 | augroup AutoCloseEmptyTab 28 | autocmd! 29 | autocmd TabLeave * call s:CheckIfInitialTabIsEmpty() 30 | autocmd TabEnter * call s:CloseInitialEmptyTab() 31 | augroup END 32 | 33 | 34 | -------------------------------------------------------------------------------- /lua/plugins/image.lua: -------------------------------------------------------------------------------- 1 | -- 🖼️ Bringing images to Neovim. 2 | -- https://github.com/3rd/image.nvim 3 | return { 4 | "3rd/image.nvim", 5 | -- todo: remove commit when https://github.com/3rd/image.nvim/issues/292 is fixed 6 | -- this affects only neo-tree 7 | commit = "21909e3eb03bc738cce497f45602bf157b396672", 8 | lazy = false, 9 | enabled = vim.fn.has("unix") == 1, -- no Windows Support, see https://github.com/3rd/image.nvim/issues/115 10 | config = function() 11 | require("image").setup({ 12 | backend = "kitty", 13 | processor = "magick_cli", 14 | integrations = { 15 | markdown = { 16 | enabled = true, 17 | clear_in_insert_mode = true, 18 | download_remote_images = true, 19 | only_render_image_at_cursor = false, 20 | only_render_image_at_cursor_mode = "popup", 21 | floating_windows = false, -- if true, images will be rendered in floating markdown windows 22 | filetypes = { "markdown" }, -- markdown extensions (ie. quarto) can go here 23 | }, 24 | html = { 25 | enabled = true, 26 | }, 27 | css = { 28 | enabled = true, 29 | }, 30 | }, 31 | }) 32 | end, 33 | } 34 | -------------------------------------------------------------------------------- /lua/plugins/no_config.lua: -------------------------------------------------------------------------------- 1 | return { 2 | -- Quickstart configs for Nvim LSP 3 | -- https://github.com/neovim/nvim-lspconfig 4 | "neovim/nvim-lspconfig", -- Quickstart configs for Nvim LSP https://github.com/neovim/nvim-lspconfig 5 | -- Neovim treesitter plugin for setting the commentstring based on the cursor location in a file. 6 | -- https://github.com/JoosepAlviste/nvim-ts-context-commentstring 7 | "JoosepAlviste/nvim-ts-context-commentstring", 8 | -- Provides Nerd Font icons (glyphs) for use by neovim plugins 9 | -- https://github.com/nvim-tree/nvim-web-devicons 10 | "nvim-tree/nvim-web-devicons", 11 | { 12 | -- Smart and powerful comment plugin for neovim. 13 | -- https://github.com/numToStr/Comment.nvim 14 | "numToStr/Comment.nvim", 15 | config = function() 16 | require("Comment").setup() 17 | end, 18 | }, 19 | { 20 | -- A json5 parser for luajit 21 | -- https://github.com/Joakker/lua-json5 22 | "Joakker/lua-json5", 23 | build = vim.fn.has("unix") and "./install.sh" or "powershell ./install.ps1", 24 | event = "VeryLazy", 25 | }, 26 | { 27 | -- Git integration for buffers 28 | -- https://github.com/lewis6991/gitsigns.nvim 29 | "lewis6991/gitsigns.nvim", 30 | lazy = true, 31 | opts = { sign_priority = 8196 }, 32 | }, 33 | } 34 | -------------------------------------------------------------------------------- /lua/plugins/tiny_code_action.lua: -------------------------------------------------------------------------------- 1 | -- A Neovim plugin that provides a simple way to run and visualize code actions with Telescope. 2 | -- https://github.com/rachartier/tiny-code-action.nvim 3 | return { 4 | "rachartier/tiny-code-action.nvim", 5 | dependencies = { 6 | { "nvim-telescope/telescope.nvim" }, 7 | }, 8 | event = "LspAttach", 9 | config = function() 10 | require("tiny-code-action").setup({ 11 | backend = "delta", 12 | -- picker = "telescope", 13 | picker = { 14 | "buffer", 15 | opts = { 16 | hotkeys = true, 17 | hotkeys_mode = "sequential", 18 | auto_preview = true, 19 | position = "center", 20 | keymaps = { 21 | preview = "K", 22 | close = { "q", "" }, 23 | select = "", 24 | }, 25 | }, 26 | }, 27 | backend_opts = { 28 | delta = { 29 | header_lines_to_remove = 4, 30 | args = { 31 | "--line-numbers", 32 | }, 33 | }, 34 | }, 35 | }) 36 | end, 37 | keys = { 38 | { 39 | ".", 40 | function() 41 | require("tiny-code-action").code_action() 42 | end, 43 | mode = { "n", "x" }, 44 | desc = "Code Action", 45 | noremap = true, 46 | silent = true, 47 | }, 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /lua/plugins/colorful-menu.lua: -------------------------------------------------------------------------------- 1 | -- Bring enjoyment to your auto completion. 2 | -- https://github.com/xzbdmw/colorful-menu.nvim 3 | return { 4 | "xzbdmw/colorful-menu.nvim", 5 | config = function() 6 | require("colorful-menu").setup({ 7 | ls = { 8 | lua_ls = { 9 | arguments_hl = "@comment", -- Maybe you want to dim arguments a bit. 10 | }, 11 | ts_ls = { -- for lsp_config or typescript-tools 12 | extra_info_hl = "@comment", -- false means do not include any extra info, see https://github.com/xzbdmw/colorful-menu.nvim/issues/42 13 | }, 14 | ["rust-analyzer"] = { 15 | extra_info_hl = "@comment", -- Such as (as Iterator), (use std::io). 16 | align_type_to_right = true, -- Similar to the same setting of gopls. 17 | preserve_type_when_truncate = false, -- See https://github.com/xzbdmw/colorful-menu.nvim/pull/36 18 | }, 19 | roslyn = { 20 | extra_info_hl = "@comment", 21 | }, 22 | fallback = true, -- If true, try to highlight "not supported" languages. 23 | fallback_extra_info_hl = "@comment", -- this will be applied to label description for unsupport languages 24 | }, 25 | fallback_highlight = "@variable", -- If the built-in logic fails to find a suitable highlight group for a label, this highlight is applied to the label. 26 | max_width = 60, 27 | }) 28 | end, 29 | } 30 | -------------------------------------------------------------------------------- /AGENTS.md: -------------------------------------------------------------------------------- 1 | # Agent Guidelines for Neovim Configuration 2 | 3 | This is a personal system wide Vim/Neovim configuration, written in Lua and Vimscript. 4 | Neovim has precedence over Vim configuration, Vim is maintained for compatibility, but many features are still written 5 | in Vimscript. 6 | Check for oficial documentation when in doubt about Neovim or Vim features, including for plugins. 7 | 8 | ## Project Structure 9 | 10 | - Plugins setup is managed via `lazy.nvim` in `lua/plugins/`, and lazy itself is configured in `lua/config/lazy.lua`. 11 | - LSP configurations are in `lua/lsp/` 12 | - Helper functions are in `lua/helpers/` 13 | - There aren't automated tests; this is a personal Neovim config. 14 | 15 | ## Formatting & Linting 16 | 17 | - Format on save enabled via conform.nvim (only when no errors present) 18 | - Formatters: stylua (Lua) 19 | - Linters: markdownlint-cli2 (Markdown) 20 | 21 | ## Code Style 22 | 23 | - **Indentation**: 2 spaces (tabs expanded), configured in `.editorconfig` 24 | - **Lua**: Use `require()` at top, follow plugin structure in `lua/plugins/`, return table with lazy.nvim spec 25 | - **VimScript**: Use `runtime` for sourcing, functions in `helpers/`, autocmds in augroups 26 | - **LSP**: Enable via `vim.lsp.enable()` in `lua/lsp/init.lua` 27 | - **Naming**: kebab-case for files, snake_case for Lua vars/functions, PascalCase for augroups 28 | - **Types**: Use LSP for type checking, inlay hints enabled 29 | - **Error Handling**: For Vimscript Use `g:CatchError()` for startup errors, store in `g:StartupErrors`, for Lua follow 30 | Lua conventions and best practices. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Giovanni Bassi's vim and nvim files 2 | 3 | These are my personal vim and nvim files. These are personal, if you want to use 4 | it, do it with care. Read the whole thing! 5 | 6 | I am using Vim both on Ubuntu and Windows. Right now there are some nice 7 | bundles, like NERDTree. I use Vim mostly for script editing in general. 8 | 9 | ## Installation instructions 10 | 11 | ### Linux 12 | 13 | Vim: 14 | 15 | ```bash 16 | git clone --recursive https://github.com/giggio/vimfiles.git ~/.vim 17 | echo "source ~/.vim/init.vim" > ~/.vimrc 18 | ``` 19 | 20 | Neovim: 21 | 22 | ```bash 23 | # either clone directory to nvim config dir 24 | git clone --recursive https://github.com/giggio/vimfiles.git ~/.config/nvim/ 25 | # or symlink this directory to ~/.config/nvim 26 | ln -s /path/to/your/clone ~/.config/nvim/ 27 | # sharing with the vim directory, it would be: 28 | ln -s $HOME/.vim ~/.config/nvim/ 29 | ``` 30 | 31 | ### Windows (PowerShell Core) 32 | 33 | Vim: 34 | 35 | ```powershell 36 | git clone --recursive https://github.com/giggio/vimfiles.git ~/.vim 37 | # or git clone --recursive git@github.com:giggio/vimfiles.git ~/.vim 38 | # I'm using scoop to install Python, adapt at your will: 39 | Set-Content -NoNewline -Path ~/_vimrc -Value "let `$PYTHONHOME = '$env:USERPROFILE\scoop\apps\python\current\'`nsource $($($env:USERPROFILE).Replace('\', '/'))/.vim/init.vim`n" 40 | ``` 41 | 42 | Neovim: 43 | 44 | ```powershell 45 | # TBD 46 | ``` 47 | 48 | Notes on Windows' version: The normal Vim home (`runtimepath`) would be at 49 | `~/vimfiles`, but this is changed to `~/.vim` so that Linux and Windows work the 50 | same way. 51 | -------------------------------------------------------------------------------- /lua/config/lazy.lua: -------------------------------------------------------------------------------- 1 | -- Bootstrap lazy.nvim 2 | local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" 3 | if not (vim.uv or vim.loop).fs_stat(lazypath) then 4 | local lazyrepo = "https://github.com/folke/lazy.nvim.git" 5 | local out = 6 | vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath }) 7 | if vim.v.shell_error ~= 0 then 8 | vim.api.nvim_echo({ 9 | { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, 10 | { out, "WarningMsg" }, 11 | { "\nPress any key to exit..." }, 12 | }, true, {}) 13 | vim.fn.getchar() 14 | os.exit(1) 15 | end 16 | end 17 | vim.opt.rtp:prepend(lazypath) 18 | 19 | -- Make sure to setup `mapleader` and `maplocalleader` before 20 | -- loading lazy.nvim so that mappings are correct. 21 | -- This is also a good place to setup other settings (vim.opt) 22 | vim.g.mapleader = "\\" 23 | vim.g.maplocalleader = "\\" 24 | 25 | require("lazy").setup({ 26 | root = vim.g.nvimPluginInstallPath, 27 | performance = { 28 | reset_packpath = false, 29 | }, 30 | change_detection = { 31 | -- automatically check for config file changes and reload the ui 32 | enabled = true, 33 | notify = false, -- get a notification when changes are found 34 | }, 35 | spec = { 36 | { import = "plugins" }, 37 | LazyPlugSpecs, -- bringing in the plugin in list from vim 38 | }, 39 | checker = { enabled = false }, -- automatically check for plugin updates 40 | rocks = { 41 | hererocks = false, 42 | }, 43 | -- dev = { 44 | -- path = "~/p/local_neovim_plugins", 45 | -- patterns = { "giggio" }, 46 | -- fallback = false, 47 | -- }, 48 | }) 49 | -------------------------------------------------------------------------------- /lua/plugins/barbar.lua: -------------------------------------------------------------------------------- 1 | -- https://github.com/romgrk/barbar.nvim 2 | -- The neovim tabline plugin. 3 | return { 4 | "romgrk/barbar.nvim", 5 | lazy = true, 6 | event = "VeryLazy", 7 | enabled = true, 8 | dependencies = { 9 | "nvim-tree/nvim-web-devicons", 10 | "lewis6991/gitsigns.nvim", 11 | }, 12 | init = function() 13 | vim.g.barbar_auto_setup = false 14 | end, 15 | opts = { 16 | icons = { 17 | buffer_index = true, 18 | filetype = { 19 | custom_colors = false, 20 | enabled = true, 21 | }, 22 | diagnostics = { 23 | [vim.diagnostic.severity.ERROR] = { enabled = true, icon = " " }, 24 | [vim.diagnostic.severity.WARN] = { enabled = true, icon = " " }, 25 | [vim.diagnostic.severity.INFO] = { enabled = false }, 26 | [vim.diagnostic.severity.HINT] = { enabled = false }, 27 | }, 28 | preset = "slanted", 29 | }, 30 | }, 31 | version = "^1.0.0", 32 | keys = { 33 | { "1", "BufferGoto 1" }, 34 | { "2", "BufferGoto 2" }, 35 | { "3", "BufferGoto 3" }, 36 | { "4", "BufferGoto 4" }, 37 | { "5", "BufferGoto 5" }, 38 | { "6", "BufferGoto 6" }, 39 | { "7", "BufferGoto 7" }, 40 | { "8", "BufferGoto 8" }, 41 | { "9", "BufferGoto 9" }, 42 | { "0", "BufferLast" }, 43 | { "", "BufferMovePrevious" }, 44 | { "", "BufferMoveNext" }, 45 | { "[b", "BufferPrevious" }, 46 | { "]b", "BufferNext" }, 47 | }, 48 | } 49 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1748370509, 24 | "narHash": "sha256-QlL8slIgc16W5UaI3w7xHQEP+Qmv/6vSNTpoZrrSlbk=", 25 | "owner": "nixos", 26 | "repo": "nixpkgs", 27 | "rev": "4faa5f5321320e49a78ae7848582f684d64783e9", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "nixos", 32 | "ref": "nixos-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /lua/plugins/opencode.lua: -------------------------------------------------------------------------------- 1 | -- Integrate the opencode AI assistant with Neovim 2 | -- https://github.com/NickvanDyke/opencode.nvim 3 | return { 4 | "NickvanDyke/opencode.nvim", 5 | dependencies = { 6 | { "folke/snacks.nvim", opts = { input = {}, picker = {}, terminal = {} } }, 7 | }, 8 | config = function() 9 | ---@type opencode.Opts 10 | vim.g.opencode_opts = {} 11 | 12 | vim.o.autoread = true 13 | end, 14 | keys = { 15 | { 16 | "o", 17 | mode = { "n", "x" }, 18 | desc = "Opencode", 19 | }, 20 | { 21 | "oa", 22 | function() 23 | require("opencode").ask("@this: ", { submit = true }) 24 | end, 25 | mode = { "n", "x" }, 26 | desc = "Ask opencode", 27 | }, 28 | { 29 | "ox", 30 | function() 31 | require("opencode").select() 32 | end, 33 | mode = { "n", "x" }, 34 | desc = "Execute opencode action", 35 | }, 36 | { 37 | "og", 38 | function() 39 | require("opencode").prompt("@this") 40 | end, 41 | mode = { "n", "x" }, 42 | desc = "Add to opencode", 43 | }, 44 | { 45 | "o.", 46 | function() 47 | require("opencode").toggle() 48 | end, 49 | mode = "n", 50 | desc = "Toggle opencode", 51 | }, 52 | { 53 | "ou", 54 | function() 55 | require("opencode").command("messages_half_page_up") 56 | end, 57 | mode = "n", 58 | desc = "opencode half page up", 59 | }, 60 | { 61 | "od", 62 | function() 63 | require("opencode").command("messages_half_page_down") 64 | end, 65 | mode = "n", 66 | desc = "opencode half page down", 67 | }, 68 | }, 69 | } 70 | -------------------------------------------------------------------------------- /helpers/functions.vim: -------------------------------------------------------------------------------- 1 | function! g:CatchError(expr) 2 | try 3 | execute a:expr 4 | catch /^Vim\%((\a\+)\)\=:E\w\+/ 5 | call add(g:StartupErrors, {'exception': v:exception, 'stacktrace': v:stacktrace}) 6 | endtry 7 | endfunction 8 | 9 | function! g:AddError(err) 10 | call add(g:StartupErrors, {'txt': err}) 11 | endfunction 12 | 13 | function! g:ShowStartupErrors() 14 | if !empty(g:StartupErrors) | 15 | tabnew 16 | " make it a no-file buffer so Vim won't nag us to save it 17 | setlocal buftype=nofile bufhidden=wipe nobuflisted 18 | file [Startup Errors] " name in the tabline 19 | call append(0, ['=== Startup Errors ===']) 20 | for ex in g:StartupErrors 21 | if has_key(ex, 'txt') 22 | call append(line('$'), ex.txt) 23 | endif 24 | if has_key(ex, 'exception') 25 | call append(line('$'), ex.exception) 26 | endif 27 | if has_key(ex, 'stacktrace') 28 | for stack_item in ex.stacktrace 29 | call append(line('$'), 'at line ' . g:Pad(stack_item.lnum, 5) . "\tin file" . stack_item.filepath) 30 | if has_key(stack_item, 'funcref') 31 | call append(line('$'), 'in function ' . get(stack_item.funcref, 'name')) 32 | endif 33 | if has_key(stack_item, 'event') 34 | call append(line('$'), 'event: ' . stack_item.event) 35 | endif 36 | endfor 37 | endif 38 | call append(line('$'), '') 39 | endfor 40 | normal! gg 41 | endif 42 | endfunction 43 | 44 | function! g:Pad(number, number_of_zeroes) 45 | let char = '0' 46 | return repeat('0', a:number_of_zeroes - len(string(a:number))) . a:number 47 | endfunction 48 | 49 | function! g:VerboseEchomsg(msg) 50 | if &verbose > 0 51 | echomsg a:msg 52 | endif 53 | endfunction 54 | -------------------------------------------------------------------------------- /lua/lsp/lua_ls.lua: -------------------------------------------------------------------------------- 1 | vim.lsp.config("lua_ls", { 2 | -- from: https://github.com/neovim/nvim-lspconfig/blob/master/doc/configs.md#lua_ls 3 | on_init = function(client) 4 | if client.workspace_folders then 5 | local path = client.workspace_folders[1].name 6 | if 7 | path ~= vim.fn.stdpath("config") 8 | and (vim.uv.fs_stat(path .. "/.luarc.json") or vim.uv.fs_stat(path .. "/.luarc.jsonc")) 9 | then 10 | return 11 | end 12 | end 13 | client.config.settings.Lua = vim.tbl_deep_extend("force", client.config.settings.Lua, { 14 | runtime = { 15 | -- Tell the language server which version of Lua you're using (most 16 | -- likely LuaJIT in the case of Neovim) 17 | version = "LuaJIT", 18 | -- Tell the language server how to find Lua modules same way as Neovim 19 | -- (see `:h lua-module-load`) 20 | path = { 21 | "lua/?.lua", 22 | "lua/?/init.lua", 23 | }, 24 | }, 25 | -- Make the server aware of Neovim runtime files 26 | workspace = { 27 | checkThirdParty = false, 28 | library = { 29 | vim.env.VIMRUNTIME, 30 | -- Depending on the usage, you might want to add additional paths 31 | -- here. 32 | "${3rd}/luv/library", 33 | -- '${3rd}/busted/library' 34 | }, 35 | -- Or pull in all of 'runtimepath'. 36 | -- NOTE: this is a lot slower and will cause issues when working on 37 | -- your own configuration. 38 | -- See https://github.com/neovim/nvim-lspconfig/issues/3189 39 | -- library = { 40 | -- vim.api.nvim_get_runtime_file('', true), 41 | -- } 42 | }, 43 | }) 44 | end, 45 | settings = { 46 | Lua = {}, 47 | }, 48 | }) 49 | 50 | vim.lsp.enable("lua_ls") 51 | -------------------------------------------------------------------------------- /.vsvimrc: -------------------------------------------------------------------------------- 1 | " display incomplete commands 2 | set showcmd 3 | " Whitespace 4 | set nowrap " don't wrap lines 5 | set tabstop=2 shiftwidth=2 " a tab is two spaces (or set this to 4) 6 | set expandtab " use spaces, not tabs (optional) 7 | set backspace=indent,eol,start " backspace through everything in insert mode 8 | 9 | " Searching 10 | set hlsearch " highlight matches 11 | set incsearch " incremental searching 12 | set ignorecase " searches are case insensitive... 13 | set smartcase " ... unless they contain at least one capital letter 14 | 15 | " My customizations 16 | set ls=2 " always show status bar 17 | set number " show line numbers 18 | set cursorline " display a marker on current line 19 | imap jj 20 | nmap oo ok 21 | nmap OO Oj 22 | au GUIEnter * simalt ~x 23 | nmap :mksession! " Quick write session with F9 24 | nmap :source Session.vim " And load session with F10 25 | set wrapscan 26 | 27 | " remap split navigation 28 | nnoremap 29 | nnoremap 30 | nnoremap 31 | nnoremap 32 | 33 | " highlight trailing white spaces: 34 | highlight ExtraWhitespace ctermbg=red guibg=red 35 | match ExtraWhitespace /\s\+$/ 36 | autocmd BufWinEnter * match ExtraWhitespace /\s\+$/ 37 | autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@ 22 | nmap oo ok 23 | nmap OO Oj 24 | au GUIEnter * simalt ~x 25 | nmap :mksession! " Quick write session with F9 26 | nmap :source Session.vim " And load session with F10 27 | set wrapscan 28 | 29 | " remap split navigation 30 | nnoremap 31 | nnoremap 32 | nnoremap 33 | nnoremap 34 | 35 | " highlight trailing white spaces: 36 | highlight ExtraWhitespace ctermbg=red guibg=red 37 | match ExtraWhitespace /\s\+$/ 38 | autocmd BufWinEnter * match ExtraWhitespace /\s\+$/ 39 | autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@ 30 | let g:airline#extensions#coc#enabled = 1 31 | " change error symbol: > 32 | let g:airline#extensions#coc#error_symbol = 'E:' 33 | " change warning symbol: > 34 | let g:airline#extensions#coc#warning_symbol = 'W:' 35 | " enable/disable coc status display > 36 | let g:airline#extensions#coc#show_coc_status = 1 37 | " change the error format (%C - error count, %L - line number): > 38 | let g:airline#extensions#coc#stl_format_err = '%C(L%L)' 39 | " change the warning format (%C - error count, %L - line number): > 40 | let g:airline#extensions#coc#stl_format_warn = '%C(L%L)' 41 | let g:airline_theme = 'material' 42 | endif 43 | 44 | set background=dark 45 | let g:material_theme_style = 'darker' " 'default' | 'palenight' | 'ocean' | 'lighter' | 'darker' | 'default-community' | 'palenight-community' | 'ocean-community' | 'lighter-community' | 'darker-community' 46 | let g:material_terminal_italics = 1 47 | " Enable true colors 48 | if (has("termguicolors")) 49 | set termguicolors 50 | endif 51 | -------------------------------------------------------------------------------- /plugins_config.vim: -------------------------------------------------------------------------------- 1 | " Easymotion config: 2 | let g:EasyMotion_smartcase = 1 " Turn on case insensitive feature 3 | 4 | if !has('nvim') 5 | " CtrlP config: 6 | let g:ctrlp_max_files=0 7 | let g:ctrlp_custom_ignore = { 8 | \ 'dir': '\v[\/]\.(git|hg|svn|yarn)|node_modules$', 9 | \ 'file': '\v\.(exe|so|dll)$' 10 | \ } 11 | let g:ctrlp_user_command = { 12 | \ 'types': { 13 | \ 1: ['.git', 'cd %s && git ls-files -co --exclude-standard'], 14 | \ 2: ['.hg', 'hg --cwd %s locate -I .'], 15 | \ }, 16 | \ } 17 | let g:ctrlp_switch_buffer = 'ET' " switch to buffer in any tab or window, if file is already open 18 | let g:ctrlp_open_new_file = 'r' " open new file in current window 19 | let g:ctrlp_open_multiple_files = 'v' " open multiple files in vertical splits 20 | 21 | " FZF config: 22 | nmap f :Rg 23 | 24 | " vim-airline key maps: 25 | let g:airline#extensions#tabline#buffer_idx_mode = 1 26 | nmap 1 AirlineSelectTab1 27 | nmap 2 AirlineSelectTab2 28 | nmap 3 AirlineSelectTab3 29 | nmap 4 AirlineSelectTab4 30 | nmap 5 AirlineSelectTab5 31 | nmap 6 AirlineSelectTab6 32 | nmap 7 AirlineSelectTab7 33 | nmap 8 AirlineSelectTab8 34 | nmap 9 AirlineSelectTab9 35 | nmap 0 AirlineSelectTab0 36 | nmap ]b :wincmd tAirlineSelectNextTab 37 | nmap [b :wincmd tAirlineSelectPrevTab 38 | endif 39 | 40 | " EditorConfig config: 41 | let g:EditorConfig_exclude_patterns = ['fugitive://.*'] " exclude fugitive from editorconfig 42 | 43 | " Used by ripgrep to find the config file hosted at ~/.vim/.ripgreprc 44 | " See more at: https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md#configuration-file 45 | " ripgrep is used by fzf and by telescope 46 | let $RIPGREP_CONFIG_PATH=expand(vimHome . "/.ripgreprc") 47 | 48 | " Ths if for the futitive command, which uses Git, with the first letter capitalized 49 | cnoreabbrev git ((getcmdtype() == ':' && getcmdline() ==# 'git') ? 'Git' : 'git') 50 | 51 | " Better whitespace options 52 | let g:better_whitespace_operator='_s' 53 | let g:better_whitespace_enabled=1 54 | let g:strip_whitespace_confirm=0 55 | let g:better_whitespace_filetypes_blacklist=['diff', 'git', 'gitcommit', 'unite', 'qf', 'help', 'fugitive'] " removing markdown from this list 56 | 57 | " fugitive 58 | augroup FugitiveSetup 59 | autocmd! 60 | " Open the fugitive index on the right side 61 | autocmd BufWinEnter * if &filetype == 'fugitive' && b:fugitive_type == 'index' | wincmd L | endif 62 | augroup END 63 | -------------------------------------------------------------------------------- /helpers/buffers.vim: -------------------------------------------------------------------------------- 1 | function s:BufferReopen() 2 | if len(s:LastBuffer) == 0 3 | echo "Last buffer is not set, cannot reopen" 4 | else 5 | let fname = s:LastBuffer[-1] 6 | call VerboseEchomsg("Reopening last buffer: " . fname . ". Buffer stack: " . string(s:LastBuffer)) 7 | let s:LastBuffer = s:LastBuffer[:-2] 8 | execute 'e ' .. fnameescape(fname) 9 | endif 10 | endfunction 11 | 12 | function s:BufferDelete() 13 | let current_buffer = bufnr('%') 14 | let s:LastBuffer = s:LastBuffer + [bufname('%')] 15 | call VerboseEchomsg("Buffer to delete: " . current_buffer) 16 | if buflisted(bufnr('#')) 17 | call VerboseEchomsg("Switching to alternate buffer") 18 | execute 'b#' 19 | else 20 | call VerboseEchomsg("The file in the alternate buffer is not loaded, switching to previous buffer") 21 | execute 'normal [b' 22 | endif 23 | let all_buffers = s:AllBuffersOpenedInAllTabs() 24 | if index(all_buffers, current_buffer) == -1 25 | call VerboseEchomsg("Buffer " . current_buffer . " is not hidden and loaded after replacing the opened buffer in current window.") 26 | else 27 | execute "bd " . current_buffer 28 | endif 29 | endfunction 30 | 31 | function s:BufferDeleteOrQuit() 32 | call VerboseEchomsg("Starting BufferDeleteOrQuit()") 33 | if &buftype != '' || &filetype == 'git' || &previewwindow 34 | call VerboseEchomsg("Quitting not normal buffer") 35 | quit 36 | return 37 | endif 38 | let listed_buffers = s:AllBuffersOpenedInAllTabs() 39 | call VerboseEchomsg("Listed buffers: " . string(listed_buffers)) 40 | if len(listed_buffers) == 1 41 | call VerboseEchomsg("Quitting with last buffer") 42 | quitall 43 | return 44 | endif 45 | if tabpagenr('$') > 1 46 | call VerboseEchomsg("Quitting with multiple tabs") 47 | quit 48 | return 49 | endif 50 | let current_buffer = bufnr('%') 51 | call VerboseEchomsg("Current buffer: " . current_buffer) 52 | let hidden_buffers = filter(listed_buffers, 'v:val != bufnr("%")') 53 | call VerboseEchomsg("These are hidden buffers: " . string(hidden_buffers)) 54 | call s:BufferDelete() 55 | call VerboseEchomsg("Finished BufferDeleteOrQuit()") 56 | endfunction 57 | 58 | function s:AllBuffersOpenedInAllTabs () 59 | return map(getbufinfo({'bufloaded': 0, 'buflisted': 1}), 'v:val.bufnr') 60 | endfunction 61 | 62 | let s:LastBuffer = [] 63 | " cnoreabbrev q ((getcmdtype() == ':' && getcmdline() ==# 'q') ? 'call BufferDeleteOrQuit()' : 'q') 64 | command -nargs=0 BufferReopen call BufferReopen() 65 | command -nargs=0 BufferDeleteOrQuit call BufferDeleteOrQuit () 66 | nmap br :BufferReopen 67 | nmap q :BufferDeleteOrQuit 68 | -------------------------------------------------------------------------------- /lua/plugins/treesitter.lua: -------------------------------------------------------------------------------- 1 | -- Nvim Treesitter configurations and abstraction layer 2 | -- https://github.com/nvim-treesitter/nvim-treesitter 3 | return { 4 | "nvim-treesitter/nvim-treesitter", 5 | branch = "main", 6 | lazy = false, 7 | build = ":TSUpdate", 8 | dependencies = { 9 | "HiPhish/rainbow-delimiters.nvim", 10 | "nvim-treesitter/nvim-treesitter-context", 11 | }, 12 | config = function() 13 | local languages = { -- also remeber to add to the autocmd for FileType below 14 | "awk", 15 | "bash", 16 | "c", 17 | "cpp", 18 | "css", 19 | "csv", 20 | "c_sharp", 21 | "desktop", 22 | "dockerfile", 23 | "editorconfig", 24 | "diff", 25 | "fsharp", 26 | "javascript", 27 | "git_config", 28 | "git_rebase", 29 | "gitattributes", 30 | "gitcommit", 31 | "gitignore", 32 | "go", 33 | "gotmpl", 34 | "html", 35 | "http", 36 | "ini", 37 | "json", 38 | "json5", 39 | "jsonc", 40 | "json", 41 | "just", 42 | "lua", 43 | "make", 44 | "markdown", 45 | "markdown_inline", 46 | "nix", 47 | "nu", 48 | "powershell", 49 | "python", 50 | "razor", 51 | "regex", 52 | "ruby", 53 | "rust", 54 | "scss", 55 | "sql", 56 | "ssh_config", 57 | "terraform", 58 | "toml", 59 | "typescript", 60 | "vim", 61 | "xml", 62 | "yaml", 63 | } 64 | local installed = vim.api.nvim_get_runtime_file("parser/*.so", true) 65 | local missing = {} 66 | for _, lang in ipairs(languages) do 67 | local found = false 68 | for _, file in ipairs(installed) do 69 | if file:match(lang .. "%.so$") then 70 | found = true 71 | break 72 | end 73 | end 74 | if not found then 75 | table.insert(missing, lang) 76 | end 77 | end 78 | if #missing > 0 then 79 | print("Missing parsers: " .. table.concat(missing, ", ")) 80 | require("nvim-treesitter").install(missing) 81 | end 82 | 83 | vim.api.nvim_create_autocmd("FileType", { 84 | pattern = languages, 85 | callback = function() 86 | vim.treesitter.start() 87 | vim.opt.foldexpr = "v:lua.vim.treesitter.foldexpr()" 88 | vim.opt.foldmethod = "expr" 89 | vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()" 90 | end, 91 | }) 92 | 93 | require("treesitter-context").setup({ 94 | enable = true, -- Enable this plugin (Can be enabled/disabled later via commands) 95 | multiwindow = true, -- Enable multiwindow support. 96 | max_lines = 0, -- How many lines the window should span. Values <= 0 mean no limit. 97 | min_window_height = 0, -- Minimum editor window height to enable context. Values <= 0 mean no limit. 98 | line_numbers = true, 99 | multiline_threshold = 20, -- Maximum number of lines to show for a single context 100 | trim_scope = "outer", -- Which context lines to discard if `max_lines` is exceeded. Choices: 'inner', 'outer' 101 | mode = "cursor", -- Line used to calculate context. Choices: 'cursor', 'topline' 102 | zindex = 20, -- The Z-index of the context window 103 | }) 104 | end, 105 | } 106 | -------------------------------------------------------------------------------- /lua/plugins/neotree.lua: -------------------------------------------------------------------------------- 1 | -- Neovim plugin to manage the file system and other tree like structures. 2 | -- (a replacement for NERDTree) 3 | -- https://github.com/nvim-neo-tree/neo-tree.nvim 4 | return { 5 | "nvim-neo-tree/neo-tree.nvim", 6 | branch = "v3.x", 7 | dependencies = { 8 | "nvim-lua/plenary.nvim", 9 | "MunifTanjim/nui.nvim", 10 | "3rd/image.nvim", 11 | { 12 | "s1n7ax/nvim-window-picker", 13 | version = "2.*", 14 | config = function() 15 | require("window-picker").setup({ 16 | filter_rules = { 17 | include_current_win = false, 18 | autoselect_one = true, 19 | bo = { 20 | -- if the file type is one of following, the window will be ignored 21 | filetype = { "neo-tree", "neo-tree-popup", "notify" }, 22 | -- if the buffer type is one of following, the window will be ignored 23 | buftype = { "terminal", "quickfix" }, 24 | }, 25 | }, 26 | }) 27 | end, 28 | }, 29 | }, 30 | lazy = false, 31 | config = function() 32 | -- vim.diagnostic.config({ 33 | -- signs = { 34 | -- text = { 35 | -- [vim.diagnostic.severity.ERROR] = '', 36 | -- [vim.diagnostic.severity.WARN] = '', 37 | -- [vim.diagnostic.severity.INFO] = '', 38 | -- [vim.diagnostic.severity.HINT] = '󰌵', 39 | -- }, 40 | -- } 41 | -- }) 42 | vim.keymap.set("n", "", "Neotree toggle") 43 | require("neo-tree").setup({ 44 | sources = { 45 | "filesystem", 46 | "buffers", 47 | "git_status", 48 | "document_symbols", 49 | }, 50 | close_if_last_window = true, -- Close Neo-tree if it is the last window left in the tab 51 | enable_git_status = true, 52 | enable_diagnostics = true, 53 | open_files_do_not_replace_types = { "terminal", "trouble", "qf" }, -- when opening files, do not use windows containing these filetypes or buftypes 54 | sort_case_insensitive = true, 55 | filesystem = { 56 | filtered_items = { 57 | visible = false, -- when true, they will just be displayed differently than normal items 58 | hide_dotfiles = false, 59 | hide_gitignored = false, 60 | hide_hidden = false, -- only works on Windows for hidden files/directories 61 | hide_by_name = { 62 | ".git", 63 | ".direnv", 64 | "node_modules", 65 | "target", -- rust build output 66 | "dist", -- common build output 67 | "plugged", -- vim/nvim installed plugins 68 | }, 69 | hide_by_pattern = {}, 70 | always_show = {}, 71 | always_show_by_pattern = {}, 72 | never_show = { 73 | ".session.vim", 74 | }, 75 | never_show_by_pattern = {}, 76 | }, 77 | follow_current_file = { 78 | enabled = true, 79 | leave_dirs_open = true, -- `false` closes auto expanded dirs, such as with `:Neotree reveal` 80 | }, 81 | group_empty_dirs = true, 82 | use_libuv_file_watcher = true, 83 | }, 84 | window = { 85 | position = "right", 86 | mapping_options = { 87 | noremap = true, 88 | nowait = true, 89 | }, 90 | mappings = { 91 | ["Z"] = "expand_all_nodes", 92 | ["P"] = { "toggle_preview", config = { use_float = true, use_image_nvim = true } }, 93 | }, 94 | }, 95 | }) 96 | vim.api.nvim_create_autocmd({ "VimEnter" }, { 97 | group = vim.api.nvim_create_augroup("NeoTreeCmds", { clear = true }), 98 | callback = function() 99 | vim.cmd("Neotree reveal") 100 | vim.cmd("wincmd t") 101 | end, 102 | }) 103 | end, 104 | } 105 | -------------------------------------------------------------------------------- /lua/plugins/dap.lua: -------------------------------------------------------------------------------- 1 | -- https://github.com/mfussenegger/nvim-dap 2 | -- Debug Adapter Protocol client implementation for Neovim 3 | return { 4 | "mfussenegger/nvim-dap", 5 | event = "VeryLazy", 6 | dependencies = { 7 | "rcarriga/nvim-dap-ui", 8 | "nvim-neotest/nvim-nio", 9 | "theHamsta/nvim-dap-virtual-text", 10 | "Joakker/lua-json5", 11 | }, 12 | config = function() 13 | local dap = require("dap") 14 | dap.adapters = { 15 | lldb_dap = { -- lldb_dap is the name we use in the configuration.type 16 | type = "executable", 17 | command = "lldb-dap", 18 | name = "lldb-dap", 19 | }, 20 | lldb = { -- use lldb for codelldb as it works the same in vscode 21 | -- https://codeberg.org/mfussenegger/nvim-dap/wiki/C-C---Rust-(via--codelldb) 22 | -- https://codeberg.org/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#c-c-rust-via-codelldb-https-github-com-vadimcn-vscode-lldb 23 | type = "executable", 24 | command = "codelldb", 25 | name = "codelldb", 26 | -- On windows you may have to uncomment this: 27 | -- detached = false, 28 | }, 29 | node = { 30 | type = "server", 31 | host = "localhost", 32 | port = "${port}", 33 | executable = { 34 | command = "js-debug", 35 | args = { "${port}" }, 36 | }, 37 | enrich_config = function(conf, on_config) -- necessary so type 'node' also works 38 | if not vim.startswith(conf.type, "pwa-") then 39 | local config = vim.deepcopy(conf) 40 | config.type = "pwa-" .. config.type 41 | on_config(config) 42 | else 43 | on_config(conf) 44 | end 45 | end, 46 | }, 47 | } 48 | dap.adapters["pwa-node"] = { 49 | type = "server", 50 | host = "localhost", 51 | port = "${port}", 52 | executable = { 53 | command = "js-debug", 54 | args = { "${port}" }, 55 | }, 56 | } 57 | dap.configurations = { 58 | rust = { 59 | { 60 | name = "Debug Rust (nvim - codelldb)", 61 | type = "lldb", 62 | request = "launch", 63 | program = function() 64 | return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file") 65 | end, 66 | cwd = "${workspaceFolder}", 67 | stopOnEntry = false, 68 | preRunCommands = { 69 | "command script import " .. vim.g.vimHome .. "/helpers/rust_prettifier_for_lldb.py", 70 | }, 71 | }, 72 | { 73 | name = "Debug Rust (nvim - lldb-dap)", 74 | type = "lldb_dap", 75 | request = "launch", 76 | program = function() 77 | return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file") 78 | end, 79 | cwd = "${workspaceFolder}", 80 | stopOnEntry = false, 81 | args = {}, 82 | }, 83 | }, 84 | javascript = { 85 | { 86 | name = "Node - Launch file (nvim - js-debug)", 87 | type = "node", 88 | request = "launch", 89 | program = "${file}", 90 | skipFiles = { 91 | "/**", 92 | }, 93 | cwd = "${workspaceFolder}", 94 | }, 95 | { 96 | name = "Node - npm run debug (nvim - js-debug)", 97 | type = "node", 98 | request = "launch", 99 | runtimeArgs = { 100 | "run-script", 101 | "debug", 102 | }, 103 | runtimeExecutable = "npm", 104 | skipFiles = { 105 | "/**", 106 | }, 107 | cwd = "${workspaceFolder}", 108 | }, 109 | }, 110 | } 111 | local ext_vscode = require("dap.ext.vscode") 112 | ext_vscode.json_decode = require("json5").parse 113 | end, 114 | } 115 | -------------------------------------------------------------------------------- /plugins.vim: -------------------------------------------------------------------------------- 1 | if !has('nvim') 2 | if empty(glob(g:vimHome . '/autoload/plug.vim')) 3 | if has('unix') 4 | silent !curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim 5 | elseif has('win32') 6 | execute 'silent !powershell -noprofile -c "Invoke-WebRequest -UseBasicParsing https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim | New-Item $env:USERPROFILE/.vim/autoload/plug.vim -Force"' 7 | endif 8 | endif 9 | 10 | if has("autocmd") 11 | augroup InstallPlugins 12 | autocmd! 13 | " Run PlugInstall if there are missing plugins 14 | autocmd VimEnter * if len(filter(values(g:plugs), '!isdirectory(v:val.dir)')) 15 | \| PlugInstall --sync | source $MYVIMRC 16 | \| endif 17 | augroup END 18 | endif 19 | endif 20 | 21 | let g:pluginInstallPath = g:vimHome . '/plugged' 22 | let g:vimPluginInstallPath = g:pluginInstallPath . '/vim' 23 | let g:nvimPluginInstallPath = g:pluginInstallPath . '/nvim' 24 | runtime manage_plugins.vim 25 | 26 | if has('nvim') " editorconfig is on by default on nvim 27 | if filereadable(".editorconfig") 28 | let g:editorconfig_is_enabled=1 29 | else 30 | let g:editorconfig_is_enabled=0 31 | endif 32 | else 33 | let g:editorconfig_is_enabled=0 34 | if &verbose == 0 " todo: Error when loading with verbose, remove when https://github.com/editorconfig/editorconfig-vim/issues/221 is fixed 35 | packadd! editorconfig 36 | if filereadable(".editorconfig") 37 | let g:editorconfig_is_enabled=1 38 | endif 39 | endif 40 | endif 41 | 42 | " Using 'Plugin' instead of 'Plug' because of the adapter from manage_plugins.vim 43 | if !has('nvim') 44 | call plug#begin(g:vimPluginInstallPath) 45 | " LSP support for Vim. Neovim has built-in LSP support. 46 | Plugin 'neoclide/coc.nvim', {'branch': 'release'} 47 | let g:vim_nerdtree_plug_args = { } 48 | if has('nvim') 49 | let g:vim_nerdtree_plug_args['lazy'] = 'false' 50 | endif 51 | " file explorer 52 | Plugin 'scrooloose/nerdtree', g:vim_nerdtree_plug_args 53 | Plugin 'ryanoasis/vim-devicons', {'dependencies': ['scrooloose/nerdtree']} 54 | Plugin 'ctrlpvim/ctrlp.vim' 55 | Plugin 'mattn/emmet-vim' 56 | " snippets: 57 | " (using snippes in coc-vim, so that is why only the snipped sources are installed here) 58 | " nvim is using its own snippet sources and providers 59 | Plugin 'honza/vim-snippets' 60 | Plugin 'vim-airline/vim-airline' 61 | Plugin 'vim-airline/vim-airline-themes' 62 | Plugin 'easymotion/vim-easymotion' 63 | endif 64 | 65 | Plugin 'Shougo/vimproc.vim', {'do' : 'make'} 66 | Plugin 'tpope/vim-fugitive' 67 | Plugin 'tpope/vim-unimpaired' 68 | Plugin 'vim-scripts/ReplaceWithRegister' 69 | Plugin 'kaicataldo/material.vim', { 'branch': 'main' } 70 | Plugin 'tiagofumo/vim-nerdtree-syntax-highlight' 71 | Plugin 'AndrewRadev/bufferize.vim' 72 | " we don't need to strip whitespace on save if editorconfig is enabled 73 | " as it has its own configuration for this 74 | " this config is for ntpeters/vim-better-whitespace 75 | let g:strip_whitespace_on_save=!g:editorconfig_is_enabled 76 | Plugin 'ntpeters/vim-better-whitespace' 77 | if !has('nvim') 78 | " nvim already has a built-in comment system 79 | Plugin 'tpope/vim-commentary' 80 | " using telescope instead of fzf in nvim 81 | Plugin 'junegunn/fzf', { 'lazy': 'true', 'do': { -> fzf#install() } } 82 | " loading in the end as fzf has issues with Buffers (specially NERDTree) 83 | Plugin 'junegunn/fzf.vim', { 'for': 'nerdtree', 'lazy': 'true', 'event': 'VeryLazy', 'dependencies': ['junegunn/fzf'] } 84 | endif 85 | " if version >= 900 || has('nvim') 86 | " let g:copilot_lsp_settings = { 87 | " \ 'telemetry': { 88 | " \ 'telemetryLevel': 'off', 89 | " \ }, 90 | " \ } 91 | " Plugin 'github/copilot.vim' 92 | " endif 93 | 94 | if has('nvim') 95 | lua require("config.lazy") 96 | else 97 | call plug#end() 98 | endif 99 | 100 | -------------------------------------------------------------------------------- /lua/plugins/which_key.lua: -------------------------------------------------------------------------------- 1 | -- Create key bindings that stick. WhichKey helps you remember your Neovim keymaps, by showing available keybindings in a popup as you type. 2 | -- https://github.com/folke/which-key.nvim 3 | return { 4 | "folke/which-key.nvim", 5 | event = "VeryLazy", 6 | dependencies = { 7 | "nvim-mini/mini.icons", -- https://github.com/nvim-mini/mini.icons/ 8 | "ryanoasis/vim-devicons", -- https://github.com/nvim-tree/nvim-web-devicons 9 | }, 10 | opts = { 11 | icons = { 12 | rules = { 13 | -- view rules examples at https://github.com/folke/which-key.nvim/blob/main/lua/which-key/icons.lua 14 | -- view nerdfont glyphs at https://www.nerdfonts.com/cheat-sheet 15 | { plugin = "barbar.nvim", icon = " ", color = "orange" }, 16 | }, 17 | }, 18 | spec = { 19 | -- Debugger 20 | { 21 | "\\d", 22 | group = "Debugger", 23 | nowait = true, 24 | }, 25 | { 26 | "", 27 | function() 28 | require("persistent-breakpoints.api").toggle_breakpoint() 29 | end, 30 | desc = "Toggle Breakpoint", 31 | nowait = true, 32 | }, 33 | { 34 | "", 35 | function() 36 | require("dap").continue() 37 | end, 38 | desc = "Continue", 39 | nowait = true, 40 | }, 41 | { 42 | "", 43 | function() 44 | require("dap").step_into() 45 | end, 46 | desc = "Step Into", 47 | nowait = true, 48 | }, 49 | { 50 | "", 51 | function() 52 | require("dap").step_over() 53 | end, 54 | desc = "Step Over", 55 | nowait = true, 56 | }, 57 | { 58 | "", -- "", 59 | function() 60 | require("dap").step_out() 61 | end, 62 | desc = "Step Out", 63 | nowait = true, 64 | }, 65 | { 66 | "", --"", 67 | function() 68 | require("dap").terminate() 69 | require("dapui").close() 70 | require("nvim-dap-virtual-text").toggle() 71 | end, 72 | desc = "Terminate", 73 | nowait = true, 74 | }, 75 | { 76 | "C-", 77 | function() 78 | require("dap").run_to_cursor() 79 | end, 80 | desc = "Run to cursor", 81 | nowait = true, 82 | }, 83 | { 84 | "", -- "S-F10", 85 | function() 86 | require("dap").goto_() 87 | end, 88 | desc = "Set Next Statement", 89 | nowait = true, 90 | }, 91 | { 92 | "\\dr", 93 | function() 94 | require("dap").repl.open() 95 | end, 96 | desc = "Open REPL", 97 | nowait = true, 98 | }, 99 | { 100 | "\\dl", 101 | function() 102 | require("dap").run_last() 103 | end, 104 | desc = "Run Last", 105 | nowait = true, 106 | }, 107 | { 108 | "\\db", 109 | function() 110 | require("dap").list_breakpoints() 111 | end, 112 | desc = "List Breakpoints", 113 | nowait = true, 114 | }, 115 | { 116 | "\\de", 117 | function() 118 | require("dap").set_exception_breakpoints({ "all" }) 119 | end, 120 | desc = "Set Exception Breakpoints", 121 | nowait = true, 122 | }, 123 | { 124 | "", -- comming from Kitty, as "map menu send_text all \x1b[5777;5u" 125 | "popup PopUp", 126 | mode = { "n", "x", "v" }, 127 | desc = "Open PopUp window", 128 | nowait = true, 129 | }, 130 | }, 131 | }, 132 | keys = { 133 | { 134 | "\\?", 135 | function() 136 | require("which-key").show({ global = false }) 137 | end, 138 | desc = "Buffer Local Keymaps (which-key)", 139 | }, 140 | }, 141 | } 142 | -------------------------------------------------------------------------------- /lua/plugins/neotest.lua: -------------------------------------------------------------------------------- 1 | -- An extensible framework for interacting with tests within NeoVim. 2 | -- https://github.com/nvim-neotest/neotest 3 | local function open_neotest_summary() 4 | local win_found = false 5 | for _, win in ipairs(vim.api.nvim_tabpage_list_wins(vim.api.nvim_get_current_tabpage())) do 6 | local buf = vim.api.nvim_win_get_buf(win) 7 | if vim.api.nvim_get_option_value("filetype", { buf = buf }) == "neotest-summary" then 8 | win_found = true 9 | break 10 | end 11 | end 12 | require("neotest").watch.toggle({ suite = true }) 13 | if not win_found then 14 | require("neotest").summary.open() 15 | end 16 | if not win_found then 17 | require("neotest").summary.open() 18 | end 19 | end 20 | return { 21 | "nvim-neotest/neotest", 22 | dependencies = { 23 | "nvim-neotest/nvim-nio", 24 | "nvim-lua/plenary.nvim", 25 | "antoinemadec/FixCursorHold.nvim", 26 | "nvim-treesitter/nvim-treesitter", 27 | { 28 | "giggio/neo-neotest-rust", -- Neotest adapter for Rust, using cargo-nextest. https://github.com/giggio/neo-neotest-rust forked from https://github.com/rouge8/neotest-rust 29 | -- dir = "~/p/local_neovim_plugins/neo-neotest-rust", 30 | }, 31 | "MisanthropicBit/neotest-busted", -- Neotest adapter for running busted tests using neovim as a lua interpreter https://github.com/MisanthropicBit/neotest-busted 32 | }, 33 | config = function() 34 | require("neotest").setup({ 35 | adapters = { 36 | -- require('rustaceanvim.neotest') -- todo: verify again when this issue is fixed in nightly: https://github.com/mrcjkb/rustaceanvim/issues/864 37 | require("neo-neotest-rust").setup({ -- this is my fork, evaluate rustaceanvim.neotest, see above message 38 | args = { "--no-capture" }, 39 | dap_adapter = "lldb", 40 | dap_extra_options = { -- fork was because of this section 41 | preRunCommands = { 42 | "command script import " .. vim.g.vimHome .. "/helpers/rust_prettifier_for_lldb.py", 43 | }, 44 | }, 45 | }), 46 | require("neotest-busted")({}), 47 | }, 48 | }) 49 | end, 50 | keys = { 51 | { 52 | "t", 53 | desc = "Neotest commands", 54 | }, 55 | { 56 | "tr", 57 | function() 58 | open_neotest_summary() 59 | require("neotest").run.run() 60 | end, 61 | mode = { "n", "x" }, 62 | desc = "Run test", 63 | noremap = true, 64 | silent = true, 65 | }, 66 | { 67 | "ta", 68 | function() 69 | open_neotest_summary() 70 | require("neotest").run.run({ suite = true }) 71 | end, 72 | mode = { "n", "x" }, 73 | desc = "Run all tests", 74 | noremap = true, 75 | silent = true, 76 | }, 77 | { 78 | "tw", 79 | function() 80 | open_neotest_summary() 81 | require("neotest").run.run() 82 | end, 83 | mode = { "n", "x" }, 84 | desc = "Watch test", 85 | noremap = true, 86 | silent = true, 87 | }, 88 | { 89 | "twa", 90 | function() 91 | open_neotest_summary() 92 | require("neotest").watch.toggle({ suite = true }) 93 | end, 94 | mode = { "n", "x" }, 95 | desc = "Watch all tests", 96 | noremap = true, 97 | silent = true, 98 | }, 99 | { 100 | "te", 101 | function() 102 | require("neotest").summary.toggle() 103 | for _, win in ipairs(vim.api.nvim_tabpage_list_wins(vim.api.nvim_get_current_tabpage())) do 104 | local buf = vim.api.nvim_win_get_buf(win) 105 | if vim.api.nvim_get_option_value("filetype", { buf = buf }) == "neotest-summary" then 106 | vim.api.nvim_set_current_win(win) 107 | return 108 | end 109 | end 110 | end, 111 | mode = { "n", "x" }, 112 | desc = "View test explorer (summary)", 113 | noremap = true, 114 | silent = true, 115 | }, 116 | { 117 | "tl", 118 | function() 119 | open_neotest_summary() 120 | require("neotest").run.run_last() 121 | end, 122 | mode = { "n", "x" }, 123 | desc = "Run last test", 124 | noremap = true, 125 | silent = true, 126 | }, 127 | { 128 | "tdt", 129 | function() 130 | open_neotest_summary() 131 | require("neotest").run.run({ strategy = "dap" }) 132 | end, 133 | mode = { "n", "x" }, 134 | desc = "Debug test", 135 | noremap = true, 136 | silent = true, 137 | }, 138 | { 139 | "tda", 140 | function() 141 | open_neotest_summary() 142 | require("neotest").run.run({ suite = true, strategy = "dap" }) 143 | end, 144 | mode = { "n", "x" }, 145 | desc = "Debug all tests", 146 | noremap = true, 147 | silent = true, 148 | }, 149 | }, 150 | } 151 | -------------------------------------------------------------------------------- /lua/plugins/conform.lua: -------------------------------------------------------------------------------- 1 | -- Lightweight yet powerful formatter plugin for Neovim 2 | -- https://github.com/stevearc/conform.nvim 3 | return { 4 | "stevearc/conform.nvim", 5 | opts = {}, 6 | lazy = true, 7 | event = "VeryLazy", 8 | config = function() 9 | local undo_redo_detected = false 10 | require("conform").setup({ 11 | log_level = vim.log.levels.DEBUG, 12 | default_format_opts = { 13 | timeout_ms = 2000, 14 | lsp_format = "fallback", 15 | }, 16 | format_on_save = function(bufnr) 17 | local errors = vim.diagnostic.get(bufnr, { severity = { min = vim.diagnostic.severity.ERROR } }) 18 | if #errors > 0 then 19 | return { dry_run = true } 20 | end 21 | if undo_redo_detected then 22 | -- vim.fn.VerboseEchomsg("Skipping format ON SAVE due to undo/redo") 23 | -- print("Skipping format ON SAVE due to undo/redo") 24 | return { dry_run = true } 25 | end 26 | -- print("Formatting ON SAVE due to diagnostics change") 27 | return { 28 | timeout_ms = 500, 29 | lsp_fallback = true, 30 | } 31 | end, 32 | formatters_by_ft = { 33 | css = { "prettierd" }, 34 | html = { "prettierd" }, 35 | javascript = { "prettierd" }, 36 | json = { "prettierd" }, 37 | lua = { "stylua" }, 38 | markdown = { "markdownlint-cli2" }, 39 | nix = { "nixfmt" }, 40 | rust = { "rustfmt" }, 41 | scss = { "prettierd" }, 42 | sh = { "shfmt" }, 43 | typescript = { "prettierd" }, 44 | yaml = { "yamlfmt" }, 45 | }, 46 | }) 47 | vim.api.nvim_create_user_command("Format", function(args) 48 | local range = nil 49 | if args.count ~= -1 then 50 | local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1] 51 | range = { 52 | start = { args.line1, 0 }, 53 | ["end"] = { args.line2, end_line:len() }, 54 | } 55 | end 56 | require("conform").format({ async = true, range = range }) 57 | end, { range = true }) 58 | 59 | local group_format_when_diags_change = vim.api.nvim_create_augroup("FormatWhenDiagnosticsChange", { clear = true }) 60 | 61 | vim.api.nvim_create_autocmd("DiagnosticChanged", { 62 | group = group_format_when_diags_change, 63 | callback = function(args) 64 | local mode = vim.api.nvim_get_mode().mode 65 | if mode ~= "n" and not mode:match("v") then 66 | return 67 | end 68 | local errors = vim.diagnostic.get(args.buf, { severity = { min = vim.diagnostic.severity.ERROR } }) 69 | if #errors > 0 then 70 | return 71 | end 72 | if vim.api.nvim_get_option_value("readonly", { buf = args.buf }) ~= "noreadonly" then 73 | return 74 | end 75 | if undo_redo_detected then 76 | -- print("Skipping format due to undo/redo") 77 | return 78 | end 79 | -- print("Formatting due to diagnostics change") 80 | require("conform").format({ bufnr = args.buf }) 81 | end, 82 | }) 83 | 84 | local prev = vim.fn.undotree() 85 | vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, { 86 | group = group_format_when_diags_change, 87 | callback = function() 88 | local u = vim.fn.undotree() 89 | if u.seq_cur < prev.seq_cur then 90 | -- print("Undo detected") 91 | undo_redo_detected = true 92 | elseif u.seq_cur > prev.seq_cur then 93 | if u.seq_last == prev.seq_last then 94 | -- print("Redo detected") 95 | undo_redo_detected = true 96 | else 97 | undo_redo_detected = false 98 | -- print("Edit detected") 99 | end 100 | end 101 | prev = u 102 | end, 103 | }) 104 | 105 | local state_buffer_had_errors = {} 106 | 107 | vim.api.nvim_create_autocmd("InsertEnter", { 108 | group = group_format_when_diags_change, 109 | callback = function(args) 110 | local errors = vim.diagnostic.get(args.buf, { severity = { min = vim.diagnostic.severity.ERROR } }) 111 | state_buffer_had_errors[args.buf] = #errors > 0 112 | end, 113 | }) 114 | 115 | vim.api.nvim_create_autocmd("InsertLeave", { 116 | group = group_format_when_diags_change, 117 | callback = function(args) 118 | if state_buffer_had_errors[args.buf] then 119 | local errors = vim.diagnostic.get(args.buf, { severity = { min = vim.diagnostic.severity.ERROR } }) 120 | if #errors == 0 then 121 | require("conform").format({ bufnr = args.buf }) 122 | end 123 | end 124 | end, 125 | }) 126 | end, 127 | keys = { 128 | { 129 | "j", 130 | "Format", 131 | mode = { "n", "x", "v" }, 132 | desc = "Reformat", 133 | noremap = true, 134 | silent = true, 135 | }, 136 | }, 137 | } 138 | -------------------------------------------------------------------------------- /nerdtree.vim: -------------------------------------------------------------------------------- 1 | if has('nvim') " Neovim is using a different tree plugin 2 | finish 3 | endif 4 | 5 | function s:OpenNERDTree(bootstrap) 6 | " if the current tab is the first one, open a new NERDTree, 7 | " otherwise mirror the existing NERDTree. 8 | if &filetype =~# 'dap' || g:dap_debugger_running || exists('t:NERDTreeBufName') 9 | return 10 | endif 11 | 12 | if g:NERDTreeShouldBeOpen == 1 13 | if &buftype == '' && getcmdwintype() == '' 14 | if a:bootstrap 15 | silent NERDTree 16 | else 17 | if g:NERDTree.ExistsForTab() 18 | silent NERDTreeToggle 19 | else 20 | silent NERDTree 21 | endif 22 | endif 23 | endif 24 | if &filetype ==# 'nerdtree' | wincmd t | endif " move the cursor to code window 25 | endif 26 | endfunction 27 | 28 | function s:ToggleNERDTreeOnTabEnter() 29 | if &filetype =~# 'dap' || g:dap_debugger_running 30 | return 31 | endif 32 | 33 | if g:NERDTreeShouldBeOpen == 1 34 | if g:NERDTree.ExistsForTab() 35 | if !g:NERDTree.IsOpen() 36 | silent NERDTreeToggle 37 | endif 38 | else 39 | if &buftype == '' && getcmdwintype() == '' 40 | silent NERDTree 41 | endif 42 | endif 43 | if &filetype ==# 'nerdtree' | wincmd t | endif " move the cursor to code window 44 | else 45 | if g:NERDTree.ExistsForTab() 46 | if g:NERDTree.IsOpen() 47 | silent NERDTreeToggle 48 | endif 49 | endif 50 | endif 51 | endfunction 52 | 53 | function s:SelectFileOnNERDTree() 54 | if &filetype =~# 'dap' || g:dap_debugger_running || &filetype == 'gitcommit' || &filetype == 'nerdtree' 55 | return 56 | endif 57 | if g:NERDTreeShouldBeOpen == 0 58 | return 59 | endif 60 | if &filetype ==# 'nerdtree' | wincmd t | endif " move the cursor to code window 61 | if &buftype == '' && getcmdwintype() == '' 62 | silent NERDTreeFind 63 | endif 64 | if &filetype ==# 'nerdtree' | wincmd t | endif " move the cursor to code window 65 | endfunction 66 | 67 | function s:OpenInitialNERDTreeWindows() 68 | let s:number_of_tabs = tabpagenr('$') 69 | if (argc() == 0 || s:number_of_tabs > 1) && !exists('s:std_in') 70 | let s:current_tab = tabpagenr() 71 | for i in range(1, s:number_of_tabs) 72 | exec 'tabnext ' . i 73 | call s:OpenNERDTree(i) 74 | endfor 75 | for i in range(1, s:number_of_tabs) 76 | exec 'tabnext ' . i 77 | call s:SelectFileOnNERDTree() 78 | endfor 79 | exec 'tabnext ' . s:current_tab 80 | if &filetype ==# 'nerdtree' | wincmd t | endif " move the cursor to code window 81 | endif 82 | endfunction 83 | 84 | function! g:ToggleNERDTreeOnKeyPress() 85 | if g:NERDTree.IsOpen() 86 | let g:NERDTreeShouldBeOpen=0 87 | else 88 | let g:NERDTreeShouldBeOpen=1 89 | endif 90 | if g:NERDTree.ExistsForTab() 91 | silent NERDTreeToggle 92 | else 93 | silent NERDTree 94 | endif 95 | if &filetype ==# 'nerdtree' | wincmd t | endif " move the cursor to code window 96 | endfunction 97 | 98 | function s:ConfigureNERDTree() 99 | if exists("g:NERDTree") 100 | " from the box NERDTree settings 101 | let g:NERDTreeShowHidden=1 102 | let g:NERDTreeAutoDeleteBuffer=1 103 | let g:NERDTreeExtensionHighlightColor = {} 104 | let g:NERDTreeExtensionHighlightColor['nix'] = "689FB6" 105 | let g:NERDTreeWinPos = "right" 106 | let g:NERDTreeCustomOpenArgs = {'file': {'reuse': 'all', 'where': 'p', 'keepopen': 1, 'stay': 0}, 'dir': {}} " always open new files in new tabs, and reuse existing tabs if they are already open 107 | " my custom NERDTree settings 108 | augroup MyNERDTreeConfig 109 | autocmd! 110 | autocmd BufWinEnter * call s:OpenNERDTree(0) 111 | autocmd BufWinEnter * call s:SelectFileOnNERDTree() 112 | " Close the tab if NERDTree is the only window remaining in it. 113 | autocmd BufEnter * if winnr('$') == 1 && exists('b:NERDTree') && b:NERDTree.isTabTree() | quit | endif 114 | " always select the code window when a tab changes 115 | autocmd TabEnter * call s:ToggleNERDTreeOnTabEnter() 116 | autocmd TabEnter * if &filetype ==# 'nerdtree' | wincmd t | endif 117 | " If another buffer tries to replace NERDTree, put it in the other window, and bring back NERDTree. 118 | autocmd BufEnter * if winnr() == winnr('h') && bufname('#') =~ 'NERD_tree_\d\+' && bufname('%') !~ 'NERD_tree_\d\+' && winnr('$') > 1 | 119 | \ let buf=bufnr() | buffer# | execute "normal! \w" | execute 'buffer'.buf | endif 120 | augroup END 121 | map :NERDTreeFind 122 | noremap :call g:ToggleNERDTreeOnKeyPress() 123 | endif 124 | endfunction 125 | 126 | if !exists("g:dap_debugger_running") 127 | let g:dap_debugger_running=0 128 | endif 129 | let g:NERDTreeShouldBeOpen=1 130 | augroup NERDTreeSetup 131 | autocmd! 132 | autocmd StdinReadPre * let s:std_in=1 133 | autocmd VimEnter * call s:ConfigureNERDTree() 134 | autocmd VimEnter * call s:OpenInitialNERDTreeWindows() 135 | augroup END 136 | -------------------------------------------------------------------------------- /init.vim: -------------------------------------------------------------------------------- 1 | " init.vim - Neovim entrypoint configuration file 2 | " In my configuration, ~/.vimrc is also loading this file 3 | 4 | " vimHome is a variable used for compatibility with both Vim and Neovim. 5 | if has('win32unix') 6 | let g:vimHome = $HOME . '/.vim' 7 | elseif has('win32') 8 | let g:vimHome = $USERPROFILE . '/.vim' 9 | else 10 | let g:vimHome = '~/.vim' 11 | endif 12 | " reset runtime path to be the same for all platforms 13 | if has('nvim') 14 | set runtimepath=$HOME/.vim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,$HOME/.vim/after 15 | else 16 | let &runtimepath = g:vimHome . ',$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,' . g:vimHome . '/after' 17 | endif 18 | 19 | runtime helpers/functions.vim 20 | 21 | " startup errors will accumulate in this list 22 | let g:StartupErrors = [] 23 | 24 | " Disable beep 25 | set noerrorbells visualbell t_vb= 26 | " Based on @mislav post http://mislav.uniqpath.com/2011/12/vim-revisited/ 27 | set nocompatible " choose no compatibility with legacy vi 28 | syntax enable 29 | set encoding=utf-8 30 | set showcmd " display incomplete commands 31 | filetype plugin indent on " load file type plugins + indentation 32 | if has("autocmd") 33 | augroup FileTypes 34 | autocmd! 35 | autocmd BufRead,BufNewFile launch.json set filetype=json5 36 | autocmd BufRead,BufNewFile settings.json set filetype=json5 37 | augroup END 38 | endif 39 | 40 | "" Whitespace 41 | set nowrap " don't wrap lines 42 | set tabstop=2 shiftwidth=2 " a tab is two spaces (or set this to 4) 43 | set expandtab " use spaces, not tabs (optional) 44 | set backspace=indent,eol,start " backspace through everything in insert mode 45 | 46 | "" Searching 47 | set hlsearch " highlight matches 48 | set incsearch " incremental searching 49 | set ignorecase " searches are case insensitive... 50 | set smartcase " ... unless they contain at least one capital letter 51 | 52 | " My customizations 53 | set ls=2 " always show status bar 54 | set number " show line numbers 55 | if has("nvim") " Show sign column when needed (for glyphs etc) 56 | set signcolumn=auto:2-4 57 | elseif has("signs") 58 | set signcolumn=auto " Show sign column when needed (for glyphs etc) 59 | endif 60 | set cursorline " display a marker on current line 61 | 62 | set wildmenu 63 | set wildmode=list:longest,full " autocomplete for paths and files 64 | set wildignore+=.git " ignore these extensions on autocomplete 65 | 66 | set hidden " change buffers without warnings even when there are unsaved changes 67 | 68 | set backupdir=/tmp " directory used to save backup files 69 | set directory=/tmp " directory used to save swap files 70 | if has("win32") 71 | set backupdir=$TEMP 72 | set directory=$TEMP 73 | endif 74 | set nobackup 75 | set nowritebackup 76 | 77 | let &t_SI = "\e[1 q" " Insert mode, blinking block 78 | let &t_SR = "\e[4 q" " Replace mode, solid underscore 79 | let &t_EI = "\e[2 q" " Normal mode, solid block 80 | 81 | let vimlocal = expand("%:p:h") . "/.vimrc.local" 82 | if filereadable(vimlocal) 83 | execute 'source '.vimlocal 84 | endif 85 | set switchbuf+=usetab,newtab 86 | set wrapscan 87 | 88 | set mouse=a 89 | 90 | " open splits in a more natural way: 91 | set splitbelow 92 | set splitright 93 | 94 | set number relativenumber 95 | set diffopt=filler,vertical 96 | 97 | if has("autocmd") 98 | augroup ContinueOnTheSameLineNumber 99 | autocmd! 100 | autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g`\"" | endif 101 | augroup END 102 | augroup SetBufferNotListed 103 | autocmd! 104 | autocmd BufEnter * if &buftype != '' | setlocal nobuflisted | endif 105 | augroup END 106 | endif 107 | 108 | set completeopt=longest,menuone,preview 109 | " this setting controls how long to wait (in ms) before fetching type / symbol information. 110 | set updatetime=300 111 | " Remove 'Press Enter to continue' message when type information is longer than one line. 112 | set cmdheight=2 113 | " start with all unfolded. 114 | set foldlevelstart=99 115 | 116 | runtime maps.vim 117 | 118 | runtime session_management.vim 119 | 120 | runtime plugins_config.vim 121 | 122 | runtime gui.vim 123 | 124 | runtime nerdtree.vim 125 | 126 | runtime helpers/buffers.vim 127 | 128 | runtime helpers/autosave.vim 129 | 130 | runtime helpers/close_initial_empty_tab.vim 131 | 132 | runtime theme.vim 133 | 134 | if !has('nvim') 135 | runtime coc_nvim.vim 136 | endif 137 | 138 | runtime plugins.vim 139 | 140 | call g:CatchError('colorscheme material') 141 | " remove background from vim: 142 | hi Normal guibg=NONE ctermbg=NONE 143 | 144 | if has("autocmd") 145 | augroup ShowStartupErrorsGroup 146 | autocmd! 147 | autocmd VimEnter * call g:ShowStartupErrors() 148 | augroup END 149 | endif 150 | 151 | if has('nvim') 152 | " disable popup menu "Disable mouse" message 153 | aunmenu PopUp.How-to\ disable\ mouse 154 | aunmenu PopUp.-2- 155 | lua require('init') 156 | endif 157 | -------------------------------------------------------------------------------- /lua/plugins/cmp.lua: -------------------------------------------------------------------------------- 1 | -- A completion plugin for neovim coded in Lua 2 | -- https://github.com/hrsh7th/nvim-cmp 3 | return { 4 | { 5 | "hrsh7th/nvim-cmp", 6 | event = { "InsertEnter", "CmdlineEnter" }, 7 | dependencies = { 8 | "nvim-web-devicons", 9 | "hrsh7th/cmp-buffer", 10 | "hrsh7th/cmp-path", 11 | "hrsh7th/cmp-cmdline", 12 | "hrsh7th/cmp-nvim-lsp", 13 | "onsails/lspkind.nvim", 14 | "saadparwaiz1/cmp_luasnip", 15 | { 16 | "L3MON4D3/LuaSnip", 17 | version = "v2.*", 18 | lazy = true, 19 | build = "make install_jsregexp", 20 | dependencies = { 21 | "rafamadriz/friendly-snippets", 22 | "honza/vim-snippets", 23 | }, 24 | config = function() 25 | require("luasnip.loaders.from_vscode").lazy_load() 26 | require("luasnip.loaders.from_snipmate").lazy_load() 27 | end, 28 | }, 29 | { 30 | "petertriho/cmp-git", 31 | dependencies = { 32 | "nvim-lua/plenary.nvim", 33 | }, 34 | }, 35 | }, 36 | config = function() 37 | local luasnip = require("luasnip") 38 | local cmp = require("cmp") 39 | cmp.setup({ 40 | experimental = { 41 | ghost_text = false, -- this feature conflict with copilot.vim's preview. 42 | }, 43 | snippet = { 44 | expand = function(args) 45 | vim.snippet.expand(args.body) -- native neovim snippets 46 | end, 47 | }, 48 | window = { 49 | completion = cmp.config.window.bordered(), 50 | documentation = cmp.config.window.bordered(), 51 | }, 52 | formatting = { 53 | format = function(entry, vim_item) 54 | local highlights_info = require("colorful-menu").cmp_highlights(entry) 55 | -- highlight_info is nil means we are missing the ts parser, it's 56 | -- better to fallback to use default `vim_item.abbr`. What this plugin 57 | -- offers is two fields: `vim_item.abbr_hl_group` and `vim_item.abbr`. 58 | if highlights_info ~= nil then 59 | vim_item.abbr_hl_group = highlights_info.highlights 60 | vim_item.abbr = highlights_info.text 61 | end 62 | if vim.tbl_contains({ "path" }, entry.source.name) then 63 | local icon, hl_group = require("nvim-web-devicons").get_icon(entry:get_completion_item().label) 64 | if icon then 65 | vim_item.kind = icon 66 | vim_item.kind_hl_group = hl_group 67 | return vim_item 68 | end 69 | end 70 | return require("lspkind").cmp_format({ with_text = false })(entry, vim_item) 71 | end, 72 | }, 73 | mapping = { 74 | [""] = cmp.mapping(function(fallback) 75 | if cmp.visible() then 76 | cmp.select_next_item() 77 | else 78 | fallback() 79 | end 80 | end, { "i", "s" }), 81 | [""] = cmp.mapping(function(fallback) 82 | if cmp.visible() then 83 | cmp.select_prev_item() 84 | else 85 | fallback() 86 | end 87 | end, { "i", "s" }), 88 | [""] = cmp.mapping(function(fallback) 89 | if luasnip.locally_jumpable(1) then 90 | luasnip.jump(1) 91 | else 92 | fallback() 93 | end 94 | end, { "i", "s" }), 95 | [""] = cmp.mapping(function(fallback) 96 | if luasnip.locally_jumpable(-1) then 97 | luasnip.jump(-1) 98 | else 99 | fallback() 100 | end 101 | end, { "i", "s" }), 102 | [""] = cmp.mapping(function(fallback) 103 | if cmp.visible() then 104 | if luasnip.expandable() then 105 | luasnip.expand() 106 | else 107 | cmp.confirm({ select = true }) 108 | end 109 | else 110 | fallback() 111 | end 112 | end), 113 | [""] = cmp.mapping.scroll_docs(-4), 114 | [""] = cmp.mapping.scroll_docs(4), 115 | [""] = cmp.mapping.complete(), 116 | }, 117 | sources = cmp.config.sources({ 118 | { name = "nvim_lsp" }, 119 | { name = "luasnip" }, 120 | { name = "crates" }, 121 | }, { 122 | { 123 | name = "buffer", 124 | option = { 125 | get_bufnrs = function() 126 | return vim.api.nvim_list_bufs() 127 | end, 128 | }, 129 | }, 130 | }), 131 | }) 132 | 133 | -- To use git you need to install the plugin petertriho/cmp-git and uncomment lines below 134 | -- Set configuration for specific filetype. 135 | cmp.setup.filetype("gitcommit", { 136 | sources = cmp.config.sources({ 137 | { name = "git" }, 138 | }, { 139 | { name = "buffer" }, 140 | }), 141 | }) 142 | require("cmp_git").setup() 143 | 144 | -- Use buffer source for `/` and `?` (if you enabled `native_menu`, this won't work anymore). 145 | cmp.setup.cmdline({ "/", "?" }, { 146 | mapping = cmp.mapping.preset.cmdline(), 147 | sources = { 148 | { name = "buffer" }, 149 | }, 150 | }) 151 | 152 | -- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore). 153 | cmp.setup.cmdline(":", { 154 | mapping = cmp.mapping.preset.cmdline(), 155 | sources = cmp.config.sources({ 156 | { name = "path" }, 157 | }, { 158 | { name = "cmdline" }, 159 | }), 160 | matching = { disallow_symbol_nonprefix_matching = false }, 161 | }) 162 | end, 163 | }, 164 | } 165 | -------------------------------------------------------------------------------- /lazy-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" }, 3 | "FixCursorHold.nvim": { "branch": "master", "commit": "1900f89dc17c603eec29960f57c00bd9ae696495" }, 4 | "LuaSnip": { "branch": "master", "commit": "5a1e39223db9a0498024a77b8441169d260c8c25" }, 5 | "ReplaceWithRegister": { "branch": "master", "commit": "832efc23111d19591d495dc72286de2fb0b09345" }, 6 | "aw-watcher.nvim": { "branch": "master", "commit": "c9cdc2285d384f4e2717bd3734435dfc63ff6f48" }, 7 | "barbar.nvim": { "branch": "master", "commit": "53b5a2f34b68875898f0531032fbf090e3952ad7" }, 8 | "bufferize.vim": { "branch": "main", "commit": "ec7c4445a97f19e5784a6fb6ad3c3d4a8ff505ac" }, 9 | "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" }, 10 | "cmp-cmdline": { "branch": "main", "commit": "d126061b624e0af6c3a556428712dd4d4194ec6d" }, 11 | "cmp-git": { "branch": "main", "commit": "b24309c386c9666c549a1abaedd4956541676d06" }, 12 | "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" }, 13 | "cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" }, 14 | "cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" }, 15 | "colorful-menu.nvim": { "branch": "master", "commit": "b51a659459df8d078201aefc995db8175ed55e84" }, 16 | "conform.nvim": { "branch": "master", "commit": "4993e07fac6679d0a5005aa7499e0bad2bd39f19" }, 17 | "crates.nvim": { "branch": "main", "commit": "afcd1cc3eeceb5783676fc8464389b9216a29d05" }, 18 | "friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" }, 19 | "gitsigns.nvim": { "branch": "main", "commit": "cdafc320f03f2572c40ab93a4eecb733d4016d07" }, 20 | "hop.nvim": { "branch": "master", "commit": "08ddca799089ab96a6d1763db0b8adc5320bf050" }, 21 | "image.nvim": { "branch": "master", "commit": "21909e3eb03bc738cce497f45602bf157b396672" }, 22 | "kulala.nvim": { "branch": "main", "commit": "698307bbe630a5cce93addd942fb721cf4ff32bf" }, 23 | "lspkind.nvim": { "branch": "master", "commit": "3ddd1b4edefa425fda5a9f95a4f25578727c0bb3" }, 24 | "lua-json5": { "branch": "master", "commit": "8ffccf71abf6d9b082045fdab1d318a5357193ea" }, 25 | "lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" }, 26 | "material.vim": { "branch": "main", "commit": "32f423c825ee89a37d66e3d8f00e777c7d8a41ab" }, 27 | "mini.icons": { "branch": "main", "commit": "ff2e4f1d29f659cc2bad0f9256f2f6195c6b2428" }, 28 | "minty": { "branch": "main", "commit": "aafc9e8e0afe6bf57580858a2849578d8d8db9e0" }, 29 | "neo-neotest-rust": { "branch": "main", "commit": "35ba959d1f2876c1726c12fc2367df56f5d87b69" }, 30 | "neo-tree.nvim": { "branch": "v3.x", "commit": "f3df514fff2bdd4318127c40470984137f87b62e" }, 31 | "neocodeium": { "branch": "main", "commit": "bfe790d78e66adaa95cb50a8c75c64a752336e9c" }, 32 | "neotest": { "branch": "master", "commit": "deadfb1af5ce458742671ad3a013acb9a6b41178" }, 33 | "neotest-busted": { "branch": "master", "commit": "a8bc63bdf00cf555a42041ff1a7a38fd6e2a9bdf" }, 34 | "noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" }, 35 | "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, 36 | "nvim-cmp": { "branch": "main", "commit": "d97d85e01339f01b842e6ec1502f639b080cb0fc" }, 37 | "nvim-colorizer.lua": { "branch": "master", "commit": "81e676d3203c9eb6e4c0ccf1eba1679296ef923f" }, 38 | "nvim-dap": { "branch": "master", "commit": "5860c7c501eb428d3137ee22c522828d20cca0b3" }, 39 | "nvim-dap-ui": { "branch": "master", "commit": "cf91d5e2d07c72903d052f5207511bf7ecdb7122" }, 40 | "nvim-dap-virtual-text": { "branch": "master", "commit": "fbdb48c2ed45f4a8293d0d483f7730d24467ccb6" }, 41 | "nvim-lint": { "branch": "master", "commit": "d1118791070d090777398792a73032a0ca5c79ff" }, 42 | "nvim-lspconfig": { "branch": "master", "commit": "30a2b191bccf541ce1797946324c9329e90ec448" }, 43 | "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" }, 44 | "nvim-notify": { "branch": "master", "commit": "8701bece920b38ea289b457f902e2ad184131a5d" }, 45 | "nvim-treesitter": { "branch": "main", "commit": "c5871d9d870c866fea9f271f1a3b3f29049a4793" }, 46 | "nvim-treesitter-context": { "branch": "master", "commit": "660861b1849256398f70450afdf93908d28dc945" }, 47 | "nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" }, 48 | "nvim-web-devicons": { "branch": "master", "commit": "8dcb311b0c92d460fac00eac706abd43d94d68af" }, 49 | "nvim-window-picker": { "branch": "main", "commit": "6382540b2ae5de6c793d4aa2e3fe6dbb518505ec" }, 50 | "opencode.nvim": { "branch": "main", "commit": "fda7eedb597155257817a9f4c9ec17308164657d" }, 51 | "overseer.nvim": { "branch": "master", "commit": "36298eab791cca87ac3a228933413d094423400e" }, 52 | "persistent-breakpoints.nvim": { "branch": "main", "commit": "d1656221836207787b8a7969cc2dc72668c4742a" }, 53 | "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, 54 | "rainbow-delimiters.nvim": { "branch": "master", "commit": "8aafe2cbd89cd4090f573a98cab6b20366576fde" }, 55 | "render-markdown.nvim": { "branch": "main", "commit": "6e0e8902dac70fecbdd8ce557d142062a621ec38" }, 56 | "rustaceanvim": { "branch": "master", "commit": "88575b98bb9937fb9983ddec5e532b67e75ce677" }, 57 | "schemastore.nvim": { "branch": "main", "commit": "aa25399c48236b77af71d4b64cdf157d2ba4e990" }, 58 | "snacks.nvim": { "branch": "main", "commit": "fe7cfe9800a182274d0f868a74b7263b8c0c020b" }, 59 | "telescope-dap.nvim": { "branch": "master", "commit": "783366bd6c1e7fa0a5c59c07db37f49c805a28df" }, 60 | "telescope-fzf-native.nvim": { "branch": "main", "commit": "6fea601bd2b694c6f2ae08a6c6fab14930c60e2c" }, 61 | "telescope-github.nvim": { "branch": "master", "commit": "ee95c509901c3357679e9f2f9eaac3561c811736" }, 62 | "telescope-ui-select.nvim": { "branch": "master", "commit": "6e51d7da30bd139a6950adf2a47fda6df9fa06d2" }, 63 | "telescope.nvim": { "branch": "master", "commit": "b4da76be54691e854d3e0e02c36b0245f945c2c7" }, 64 | "tiny-code-action.nvim": { "branch": "main", "commit": "b6c9ee6444196c1ca616bf4ff381124b140db21f" }, 65 | "toggleterm.nvim": { "branch": "main", "commit": "50ea089fc548917cc3cc16b46a8211833b9e3c7c" }, 66 | "trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" }, 67 | "vim-better-whitespace": { "branch": "master", "commit": "de99b55a6fe8c96a69f9376f16b1d5d627a56e81" }, 68 | "vim-devicons": { "branch": "master", "commit": "71f239af28b7214eebb60d4ea5bd040291fb7e33" }, 69 | "vim-fugitive": { "branch": "master", "commit": "61b51c09b7c9ce04e821f6cf76ea4f6f903e3cf4" }, 70 | "vim-nerdtree-syntax-highlight": { "branch": "master", "commit": "35e70334a2ff6e89b82a145d1ac889e82d1ddb4e" }, 71 | "vim-snippets": { "branch": "master", "commit": "ededcf7581962ee616cadab360d5966f3307f11a" }, 72 | "vim-unimpaired": { "branch": "master", "commit": "db65482581a28e4ccf355be297f1864a4e66985c" }, 73 | "vimproc.vim": { "branch": "master", "commit": "63a4ce0768c7af434ac53d37bdc1e7ff7fd2bece" }, 74 | "volt": { "branch": "main", "commit": "620de1321f275ec9d80028c68d1b88b409c0c8b1" }, 75 | "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" } 76 | } 77 | -------------------------------------------------------------------------------- /coc_nvim.vim: -------------------------------------------------------------------------------- 1 | 2 | function! SetupCocCustomizations() 3 | if !exists("g:coc_service_initialized") 4 | return 5 | endif 6 | let g:coc_global_extensions = [ 7 | \ 'coc-angular', 8 | \ 'coc-css', 9 | \ 'coc-html', 10 | \ 'coc-json', 11 | \ 'coc-markdownlint', 12 | \ 'coc-rust-analyzer', 13 | \ 'coc-sh', 14 | \ 'coc-snippets', 15 | \ 'coc-tsserver', 16 | \ 'coc-vimlsp', 17 | \ '@yaegassy/coc-marksman', 18 | \] 19 | let g:coc_snippet_next="" 20 | let g:coc_snippet_prev="" 21 | 22 | if has("autocmd") 23 | augroup SetupCocFileTypes 24 | autocmd! 25 | autocmd FileType yaml if bufname("%") =~# "docker-compose.yml" | set ft=yaml.docker-compose | endif 26 | autocmd FileType yaml if bufname("%") =~# "compose.yml" | set ft=yaml.docker-compose | endif 27 | augroup END 28 | endif 29 | 30 | let g:coc_filetype_map = { 31 | \ 'yaml.docker-compose': 'dockercompose', 32 | \ } 33 | 34 | " this is what the ctrl+. from vscode, now \. 35 | nmap . (coc-codeaction-line) 36 | " this is refactoring, now \, 37 | nmap , (coc-codeaction-refactor) 38 | " im not sure 39 | 40 | " Bellow is from example config: 41 | " https://github.com/neoclide/coc.nvim/blob/master/doc/coc-example-config.vim 42 | 43 | " May need for Vim (not Neovim) since coc.nvim calculates byte offset by count 44 | " utf-8 byte sequence 45 | set encoding=utf-8 46 | " Some servers have issues with backup files, see #649 47 | set nobackup 48 | set nowritebackup 49 | 50 | " Having longer updatetime (default is 4000 ms = 4s) leads to noticeable 51 | " delays and poor user experience 52 | set updatetime=300 53 | 54 | " Always show the signcolumn, otherwise it would shift the text each time 55 | " diagnostics appear/become resolved 56 | set signcolumn=yes 57 | 58 | " Make to accept selected completion item or notify coc.nvim to format 59 | " u breaks current undo, please make your own choice 60 | inoremap coc#pum#visible() ? coc#pum#confirm() 61 | \: "\u\\=coc#on_enter()\" 62 | 63 | function! CheckBackspace() abort 64 | let col = col('.') - 1 65 | return !col || getline('.')[col - 1] =~# '\s' 66 | endfunction 67 | 68 | " Use to trigger completion 69 | if has('nvim') 70 | inoremap coc#refresh() 71 | else 72 | inoremap coc#refresh() 73 | endif 74 | 75 | " Use `[g` and `]g` to navigate diagnostics 76 | " Use `:CocDiagnostics` to get all diagnostics of current buffer in location list 77 | nmap [g (coc-diagnostic-prev) 78 | nmap ]g (coc-diagnostic-next) 79 | 80 | " GoTo code navigation 81 | nmap gd (coc-definition) 82 | nmap gy (coc-type-definition) 83 | nmap gi (coc-implementation) 84 | nmap gr (coc-references) 85 | 86 | " Use K to show documentation in preview window 87 | nnoremap K :call ShowDocumentation() 88 | 89 | function! ShowDocumentation() 90 | if CocAction('hasProvider', 'hover') 91 | call CocActionAsync('doHover') 92 | else 93 | call feedkeys('K', 'in') 94 | endif 95 | endfunction 96 | 97 | " Highlight the symbol and its references when holding the cursor 98 | if has("autocmd") 99 | augroup SetupCocCursorHold 100 | autocmd! 101 | autocmd CursorHold * silent call CocActionAsync('highlight') 102 | augroup END 103 | endif 104 | 105 | " Symbol renaming 106 | nmap rn (coc-rename) 107 | 108 | " Formatting selected code 109 | xmap F (coc-format-selected) 110 | nmap F (coc-format-selected) 111 | 112 | if has("autocmd") 113 | augroup SetupCocFormatExpr 114 | autocmd! 115 | autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected') 116 | augroup end 117 | endif 118 | 119 | " Applying code actions to the selected code block 120 | " Example: `aap` for current paragraph 121 | xmap a (coc-codeaction-selected) 122 | nmap a (coc-codeaction-selected) 123 | 124 | " Remap keys for applying code actions at the cursor position 125 | nmap ac (coc-codeaction-cursor) 126 | " Remap keys for apply code actions affect whole buffer 127 | nmap as (coc-codeaction-source) 128 | " Apply the most preferred quickfix action to fix diagnostic on the current line 129 | nmap qf (coc-fix-current) 130 | 131 | " Remap keys for applying refactor code actions 132 | nmap re (coc-codeaction-refactor) 133 | xmap r (coc-codeaction-refactor-selected) 134 | nmap r (coc-codeaction-refactor-selected) 135 | 136 | " Run the Code Lens action on the current line 137 | nmap cl (coc-codelens-action) 138 | 139 | " Map function and class text objects 140 | " NOTE: Requires 'textDocument.documentSymbol' support from the language server 141 | xmap if (coc-funcobj-i) 142 | omap if (coc-funcobj-i) 143 | xmap af (coc-funcobj-a) 144 | omap af (coc-funcobj-a) 145 | xmap ic (coc-classobj-i) 146 | omap ic (coc-classobj-i) 147 | xmap ac (coc-classobj-a) 148 | omap ac (coc-classobj-a) 149 | 150 | " Remap and to scroll float windows/popups 151 | if has('nvim-0.4.0') || has('patch-8.2.0750') 152 | nnoremap coc#float#has_scroll() ? coc#float#scroll(1) : "\" 153 | nnoremap coc#float#has_scroll() ? coc#float#scroll(0) : "\" 154 | inoremap coc#float#has_scroll() ? "\=coc#float#scroll(1)\" : "\" 155 | inoremap coc#float#has_scroll() ? "\=coc#float#scroll(0)\" : "\" 156 | vnoremap coc#float#has_scroll() ? coc#float#scroll(1) : "\" 157 | vnoremap coc#float#has_scroll() ? coc#float#scroll(0) : "\" 158 | endif 159 | 160 | " Use CTRL-S for selections ranges 161 | " Requires 'textDocument/selectionRange' support of language server 162 | nmap (coc-range-select) 163 | xmap (coc-range-select) 164 | 165 | " Add `:Format` command to format current buffer 166 | command! -nargs=0 Format :call CocActionAsync('format') 167 | 168 | " Add `:Fold` command to fold current buffer 169 | command! -nargs=? Fold :call CocAction('fold', ) 170 | 171 | " Add `:OR` command for organize imports of the current buffer 172 | command! -nargs=0 OR :call CocActionAsync('runCommand', 'editor.action.organizeImport') 173 | 174 | " Add (Neo)Vim's native statusline support 175 | " NOTE: Please see `:h coc-status` for integrations with external plugins that 176 | " provide custom statusline: lightline.vim, vim-airline 177 | set statusline^="%{coc#status()}%{get(b:,'coc_current_function','')}" 178 | 179 | " Mappings for CoCList 180 | " Show all diagnostics 181 | nnoremap a :CocList diagnostics 182 | " Manage extensions 183 | nnoremap e :CocList extensions 184 | " Show commands 185 | nnoremap c :CocList commands 186 | " Find symbol of current document 187 | nnoremap o :CocList outline 188 | " Search workspace symbols 189 | nnoremap s :CocList -I symbols 190 | " Do default action for next item 191 | nnoremap j :CocNext 192 | " Do default action for previous item 193 | nnoremap k :CocPrev 194 | " Resume latest coc list 195 | nnoremap p :CocListResume 196 | endfunction 197 | 198 | if !has('nvim') 199 | " this remap has to be loaded before copilot.vim, or will be incorrectly mapped 200 | " Use tab for trigger completion with characters ahead and navigate 201 | inoremap 202 | \ coc#pum#visible() ? coc#pum#next(1) : 203 | \ CheckBackspace() ? "\" : 204 | \ coc#refresh() 205 | inoremap coc#pum#visible() ? coc#pum#prev(1) : "\" 206 | endif 207 | 208 | if has("autocmd") && !has('nvim') 209 | augroup SetupCoc 210 | autocmd! 211 | autocmd VimEnter * call SetupCocCustomizations() 212 | augroup END 213 | endif 214 | -------------------------------------------------------------------------------- /lua/lsp/init.lua: -------------------------------------------------------------------------------- 1 | local capabilities = require("cmp_nvim_lsp").default_capabilities() 2 | vim.lsp.config("*", { 3 | capabilities = capabilities, 4 | }) 5 | 6 | -- todo: can't use Cspell LS until it supports .yaml and more than one config file 7 | -- see: https://github.com/vlabo/cspell-lsp/issues/13 8 | -- if cspell.yaml exists, use it as the config filename 9 | -- if vim.fn.filereadable('./cspell.yaml') == 1 then 10 | -- vim.lsp.config('cspell_ls', { 11 | -- cmd = { 'cspell-lsp', '--stdio', '--config', 'cspell.yaml' }, 12 | -- }) 13 | -- end 14 | -- vim.lsp.enable('cspell_ls') 15 | 16 | vim.lsp.config("cSpell", require("lsp.cspellls")) 17 | vim.lsp.enable("cSpell") 18 | 19 | vim.lsp.config("powershell_es", { 20 | -- using powershell-editor-services from nix, it comes already bundled 21 | cmd = function(dispatchers) 22 | local temp_path = vim.fn.stdpath("cache") 23 | local command_fmt = 24 | [[ -LogPath '%s/powershell_es.log' -SessionDetailsPath '%s/powershell_es.session.json' -FeatureFlags @() -AdditionalModules @() -HostName nvim -HostProfileId 0 -HostVersion 1.0.0 -Stdio -LogLevel Normal]] 25 | local command = command_fmt:format(temp_path, temp_path) 26 | local cmd = { "powershell-editor-services", command } 27 | return vim.lsp.rpc.start(cmd, dispatchers) 28 | end, 29 | }) 30 | 31 | vim.lsp.config("jsonls", { 32 | settings = { 33 | json = { 34 | schemas = require("schemastore").json.schemas(), 35 | validate = { enable = true }, 36 | }, 37 | }, 38 | }) 39 | 40 | require("lsp.lua_ls") 41 | 42 | -- vim.lsp.enable('bacon_ls') -- rust enabled using rustacean.lua 43 | vim.lsp.enable("basedpyright") 44 | vim.lsp.enable("bashls") 45 | vim.lsp.enable("clangd") 46 | vim.lsp.enable("csharp_ls") 47 | vim.lsp.enable("cssls") 48 | vim.lsp.enable("dockerls") 49 | vim.lsp.enable("emmet_language_server") 50 | vim.lsp.enable("eslint") 51 | vim.lsp.enable("fsautocomplete") 52 | vim.lsp.enable("gopls") 53 | vim.lsp.enable("html") 54 | vim.lsp.enable("jsonls") 55 | vim.lsp.enable("marksman") 56 | vim.lsp.enable("nushell") 57 | vim.lsp.enable("powershell_es") 58 | vim.lsp.enable("ruby_lsp") 59 | -- vim.lsp.enable('rust_analyzer') -- rust enabled using rustacean.lua 60 | vim.lsp.enable("sqls") 61 | vim.lsp.enable("systemd_ls") 62 | vim.lsp.enable("ts_ls") 63 | vim.lsp.enable("vimls") 64 | vim.lsp.enable("yamlls") 65 | 66 | vim.api.nvim_create_autocmd("CursorHold", { 67 | pattern = "*", 68 | group = vim.api.nvim_create_augroup("LspDiagnosticsHold", { clear = true }), 69 | callback = function() 70 | -- Only open if a diagnostic exists under the cursor 71 | local line, col = unpack(vim.api.nvim_win_get_cursor(0)) 72 | local diags = vim.diagnostic.get(0, { lnum = line - 1 }) 73 | for _, d in ipairs(diags) do 74 | if col >= d.col and col < d.end_col then 75 | vim.diagnostic.open_float(nil, { 76 | focusable = false, 77 | close_events = { 78 | "BufHidden", 79 | "BufLeave", 80 | "CursorMoved", 81 | "InsertEnter", 82 | "WinLeave", 83 | }, 84 | border = "rounded", 85 | source = "always", 86 | prefix = " ", 87 | scope = "cursor", 88 | }) 89 | break 90 | end 91 | end 92 | end, 93 | }) 94 | 95 | -- allow gotmpl files to be recognized as HTML files when hugo config files are present 96 | if 97 | vim.fn.filereadable("./hugo.yaml") == 1 98 | or vim.fn.filereadable("./hugo.toml") == 1 99 | or vim.fn.filereadable("./hugo.json") == 1 100 | or vim.fn.glob("./config/**/hugo.yaml") ~= "" 101 | or vim.fn.glob("./config/**/hugo.toml") ~= "" 102 | or vim.fn.glob("./config/**/hugo.json") ~= "" 103 | then 104 | vim.api.nvim_create_autocmd("BufEnter", { 105 | pattern = { "*.html" }, 106 | command = "set filetype=gotmpl", 107 | }) 108 | end 109 | 110 | -- using tiny_code_action.nvim for code actions: 111 | -- vim.keymap.set("n", ".", vim.lsp.buf.code_action, { noremap = true, silent = true, desc = "LSP: code action" }) 112 | vim.keymap.set( 113 | "n", 114 | "rn", 115 | vim.lsp.buf.rename, 116 | { noremap = true, silent = true, desc = "LSP: rename" } 117 | ) 118 | vim.keymap.set( 119 | "n", 120 | "gd", 121 | vim.lsp.buf.definition, 122 | { noremap = true, silent = true, desc = "LSP: go to definition" } 123 | ) 124 | vim.keymap.set( 125 | "n", 126 | "", 127 | vim.lsp.buf.definition, 128 | { noremap = true, silent = true, desc = "LSP: go to definition" } 129 | ) 130 | vim.keymap.set( 131 | "n", 132 | "gy", 133 | vim.lsp.buf.type_definition, 134 | { noremap = true, silent = true, desc = "LSP: go to type definition" } 135 | ) 136 | -- vim.keymap.set( "n", "gr", vim.lsp.buf.references, { noremap = true, silent = true, desc = "LSP: go to references" }) 137 | vim.keymap.set( 138 | "n", 139 | "gi", 140 | vim.lsp.buf.implementation, 141 | { noremap = true, silent = true, desc = "LSP: go to implementation" } 142 | ) 143 | 144 | -- vim.api.nvim_set_keymap('n', 'do', 'lua vim.diagnostic.open_float()', { noremap = true, silent = true }) 145 | vim.keymap.set("n", "do", function() 146 | vim.diagnostic.open_float({ scope = "buffer" }) 147 | end, { noremap = true, silent = true }) 148 | vim.api.nvim_set_keymap( 149 | "n", 150 | "d[", 151 | "lua vim.diagnostic.goto_prev()", 152 | { noremap = true, silent = true } 153 | ) 154 | vim.api.nvim_set_keymap( 155 | "n", 156 | "d]", 157 | "lua vim.diagnostic.goto_next()", 158 | { noremap = true, silent = true } 159 | ) 160 | -- The following command requires plug-ins "nvim-telescope/telescope.nvim", "nvim-lua/plenary.nvim", and optionally "kyazdani42/nvim-web-devicons" for icon support 161 | vim.api.nvim_set_keymap( 162 | "n", 163 | "dd", 164 | "Telescope diagnostics", 165 | { noremap = true, silent = true } 166 | ) 167 | -- If you don't want to use the telescope plug-in but still want to see all the errors/warnings, comment out the telescope line and uncomment this: 168 | -- vim.api.nvim_set_keymap('n', 'dd', 'lua vim.diagnostic.setloclist()', { noremap = true, silent = true }) 169 | 170 | vim.lsp.inlay_hint.enable(true) 171 | 172 | -- vim.api.nvim_create_autocmd('BufRead', { 173 | -- -- configuration of update_in_insert as per https://github.com/crisidev/bacon-ls#neovim---manual 174 | -- group = vim.api.nvim_create_augroup('filetype_rust', { clear = true }), 175 | -- desc = 'Set LSP diagnostics for Rust', 176 | -- pattern = { '*.rs' }, 177 | -- callback = function() 178 | -- vim.diagnostic.config({ 179 | -- update_in_insert = true, 180 | -- }) 181 | -- end, 182 | -- }) 183 | -- 184 | 185 | -- Open PopUp window with Menu key 186 | vim.diagnostic.config({ 187 | update_in_insert = true, 188 | signs = { 189 | text = { 190 | [vim.diagnostic.severity.ERROR] = "", 191 | [vim.diagnostic.severity.WARN] = "", 192 | [vim.diagnostic.severity.INFO] = "", 193 | [vim.diagnostic.severity.HINT] = "󰌵", 194 | }, 195 | }, 196 | }) 197 | 198 | -- keep original handler 199 | local default_publish_diagnostics = vim.lsp.handlers["textDocument/publishDiagnostics"] 200 | 201 | -- hide rustc errors from LSP diagnostics 202 | -- vim.lsp.handlers["textDocument/publishDiagnostics"] = function(err, result, ctx, config) 203 | -- if not result or not result.diagnostics then 204 | -- return default_publish_diagnostics(err, result, ctx, config) 205 | -- end 206 | -- if vim.api.nvim_get_option_value("filetype", { buf = ctx.bufnr }) ~= "rust" then 207 | -- return default_publish_diagnostics(err, result, ctx, config) 208 | -- end 209 | -- -- filter: drop diagnostics that come from "rustc" and have severity = Error 210 | -- result.diagnostics = vim.tbl_filter(function(d) 211 | -- if not d.source then 212 | -- return true 213 | -- end 214 | -- if d.source == "rustc" then 215 | -- local sev = d.severity or 1 216 | -- -- LSP severities: Error=1, Warning=2, Information=3, Hint=4 217 | -- if sev == vim.diagnostic.severity.ERROR or sev == 1 then 218 | -- return false -- drop rustc errors 219 | -- end 220 | -- end 221 | -- return true 222 | -- end, result.diagnostics) 223 | -- return default_publish_diagnostics(err, result, ctx, config) 224 | -- end 225 | 226 | -- consolidate LSP signs to show only the highest severity per line 227 | local ns = vim.api.nvim_create_namespace("consolidated_signs") 228 | local orig_signs_handler = vim.diagnostic.handlers.signs 229 | vim.diagnostic.handlers.signs = { 230 | show = function(_, bufnr, _, opts) 231 | local diagnostics = vim.diagnostic.get(bufnr) 232 | local max_severity_per_line = {} 233 | for _, d in pairs(diagnostics) do 234 | local m = max_severity_per_line[d.lnum] 235 | if not m or d.severity < m.severity then 236 | max_severity_per_line[d.lnum] = d 237 | end 238 | end 239 | orig_signs_handler.show(ns, bufnr, vim.tbl_values(max_severity_per_line), opts) 240 | end, 241 | hide = function(_, bufnr) 242 | orig_signs_handler.hide(ns, bufnr) 243 | end, 244 | } 245 | 246 | vim.keymap.set({ "n", "v" }, "", "popup PopUp") 247 | vim.keymap.set("i", "", "popup PopUp") 248 | -------------------------------------------------------------------------------- /manage_plugins.vim: -------------------------------------------------------------------------------- 1 | " Script adapted from: 2 | " https://github.com/BlueDrink9/env/blob/master/editors/vim/manage_plugins.vim 3 | " See gist: 4 | " https://gist.github.com/BlueDrink9/474b150c44d41b80934990c0acfb00be 5 | 6 | " This command is your main interface to the plugin: 7 | " args: anything vim-plug supports in its extra options, plus: 8 | " * afterLoad: the name of a vimscript function to run after the plugin 9 | " lazy-loads. 10 | " * event: list of autocmd events that the plugin should lazy-load on 11 | " * Any other lazy.nvim spec config options that don't have a Plug alternative 12 | " (Note, for `keys`, modes are only supported via the special MakeLazyKeys 13 | " function.) 14 | command! -bang -nargs=+ Plugin call PluginAdapter() 15 | 16 | function! GetPluginName(pluginUrl) 17 | " Get plugin name out of a plugin spec name/url. Fairly simplistic, so 18 | " don't include .git at the end of your urls. 19 | return split(a:pluginUrl, '/')[-1] 20 | endfunction 21 | 22 | " Define function to check if a plugin is added to the manager. 23 | " Accepts only the last part of the plugin name (See GetPluginName()). 24 | " Usage: IsPluginUsed('nvim-treesitter') 25 | if has('nvim') 26 | lua IsPluginUsed = function(name) local s = require'lazy.core.config'.spec return s ~= nil and s.plugins[name] ~= nil end 27 | endif 28 | 29 | function! IsPluginUsed(name) 30 | if has('nvim') 31 | return has_key(s:plugs, a:name) 32 | else 33 | return has_key(g:plugs, a:name) 34 | endif 35 | endfunction 36 | 37 | " To remove a Plugged repo using UnPlug 'pluginName' 38 | function! s:deregister(name) 39 | " name: See GetPluginName() 40 | try 41 | if has('nvim') 42 | call remove(s:plugs, a:name) 43 | return 44 | else 45 | call remove(g:plugs, a:name) 46 | call remove(g:plugs_order, index(g:plugs_order, a:name)) 47 | endif 48 | " strip anything after period because not legitimate variable. 49 | let l:varname = substitute(a:name, '\..*', '', '') 50 | let l:varname = substitute(l:varname, 'vim-', '', '') 51 | exec 'let g:loaded_' . l:varname . ' = 1' 52 | catch /^Vim\%((\a\+)\)\=:E716:/ 53 | echom 'Unplug failed for ' . a:name 54 | endtry 55 | endfunction 56 | command! -nargs=1 -bar UnPlug call s:deregister() 57 | 58 | if has('nvim') 59 | lua << EOF 60 | -- Allow making a keys table with modes for Lazy.vim in vimscript 61 | -- Expects a dictionary where keys are a string of modes and values are 62 | -- a list of keys. 63 | -- usage: add plugin opt: 64 | -- Plugin 'abc/def', {'keys': MakeLazyKeys({ 65 | -- " \ 'n': ['[', ']'], 66 | -- " \ 'ov': ['i,', 'a', 'I', 'A'], 67 | -- " \ })} 68 | -- Returns a lua function for setting up a lazy keys spec. Need to return a 69 | -- function because can't return a mixed list/dict table in vimscript. 70 | MakeLazyKeys = function(args) 71 | return function() 72 | local ret = {} 73 | for modes, keys in pairs(args) do 74 | for _, key in ipairs(keys) do 75 | modesT = {} 76 | for i = 1, #modes do 77 | modesT[i] = modes:sub(i, i) 78 | end 79 | table.insert(ret, { key, mode = modesT}) 80 | end 81 | end 82 | return ret 83 | end 84 | end 85 | EOF 86 | function! MakeLazyKeys(args) 87 | return luaeval('MakeLazyKeys(_A[1])', [a:args]) 88 | endfunction 89 | else 90 | function! MakeLazyKeys(args) 91 | return [] 92 | endfunction 93 | endif 94 | 95 | " Passes plugin to a lua function that creates a lazy.nvim spec, or to a vimscript 96 | " function to adapt the args for vim-plug. 97 | function! s:PluginAdapter(...) 98 | let l:plugin = a:1 99 | let l:args = {} 100 | if a:0 == 2 101 | let l:args = a:2 102 | endif 103 | if has('nvim') 104 | " Has to be global so lua sees it 105 | let g:__plugin_args = l:args 106 | exec 'lua PlugToLazy("' . l:plugin . '", vim.g.__plugin_args)' 107 | let s:plugs[GetPluginName(l:plugin)] = 1 108 | else 109 | call PlugPlusLazyArgs(l:plugin, l:args) 110 | endif 111 | endfunction 112 | 113 | if has('nvim') 114 | let s:plugs = {} 115 | lua << EOF 116 | LazyPlugSpecs = {} 117 | -- Compatibility function to convert vim-plug's Plug command to lazy.nvim spec 118 | function PlugToLazy(plugin, opts) 119 | -- Build lazy plugin spec, converting any vim-plug options. 120 | local lazySpec = {} 121 | if opts then 122 | lazySpec = opts 123 | lazySpec.ft = opts["for"] 124 | -- lazySpec.for = nil 125 | lazySpec.name = opts["as"] 126 | lazySpec.as = nil 127 | lazySpec.cmd = opts["on"] 128 | lazySpec.on = nil 129 | lazySpec.version = opts["tag"] 130 | if opts['afterLoad'] then 131 | lazySpec['config'] = function() 132 | -- Either call the default afterLoad function... 133 | if opts['afterLoad'] == true then 134 | vim.fn[vim.fn.PluginNameToFunc( 135 | vim.fn.GetPluginName(plugin) 136 | )]() 137 | else 138 | -- ...or call the specified name. 139 | vim.fn[opts['afterLoad']]() 140 | end 141 | end 142 | end 143 | if lazySpec.cmd then 144 | -- Ensure it is a list/table 145 | if type(lazySpec.cmd) == "string" then 146 | lazySpec.cmd = {lazySpec.cmd} 147 | end 148 | -- mappings are commands ('on') for Plug, but keys for Lazy 149 | for k, cmd in pairs(lazySpec.cmd) do 150 | if string.find(string.lower(cmd), "", 1, 6) then 151 | lazySpec.keys = lazySpec.keys or {} 152 | -- Convert plug mappings for all modes, not just default of 'n' 153 | table.insert(lazySpec.keys, {cmd, mode={'n','v','o','l'}}) 154 | lazySpec.cmd[k] = nil 155 | end 156 | end 157 | -- Remove any empty cmd table to prevent lazyload (may be empty if 158 | -- used to force a lazy load in Plug) 159 | if not lazySpec.cmd then lazySpec.cmd = nil end 160 | end 161 | end 162 | lazySpec[1] = plugin 163 | table.insert(LazyPlugSpecs, lazySpec) 164 | end 165 | EOF 166 | endif 167 | 168 | function! PluginNameToFunc(name) 169 | " Convert a plugins name to default function name to use for afterLoad 170 | " functions. 171 | " Has to be a global function so lua can access it. 172 | return 'Plug_after_' . substitute(a:name, '[\\.-]', '_', 'g') 173 | endfunction 174 | 175 | " Rest is entirely plug-specific 176 | if has('nvim') 177 | finish 178 | endif 179 | 180 | function! PlugPlusLazyArgs(plugin, args) 181 | let l:plugin_name = GetPluginName(a:plugin) 182 | let l:args = a:args 183 | " convert lazy args we want to keep when using plug 184 | for dep in get(l:args, 'dependencies', []) 185 | Plug dep 186 | endfor 187 | " Handle hook for after load 188 | let l:func = get(l:args, 'afterLoad', v:false) 189 | " If 'afterLoad' is v:true, call function based off a default name 190 | " convention (the plugin name, with _ replacing . and -). Otherwise 191 | " call the function name passed in. For example, to configure for a 192 | " plugin named 'my-plugin.vim', define a function called 193 | " Plug_after_my_plugin_vim(). 194 | " Only will be called for lazy-loaded plugins, so don't use without 195 | " an 'on' or 'for' mapping. ('keys' gets ignored). 196 | if l:func == v:true 197 | exec 'au User ' . l:plugin_name . ' call ' . PluginNameToFunc(l:plugin_name) . '()' 198 | elseif l:func != v:false 199 | exec 'au User ' . l:plugin_name . ' call ' . l:func . '()' 200 | endif 201 | 202 | for event in get(l:args, 'event', []) 203 | " Removes unsupported events, e.g. VeryLazy. 204 | if !exists('##' . event) 205 | continue 206 | endif 207 | call s:loadPluginOnEvent(l:plugin_name, event) 208 | " Add empty 'on' argument to enable lazyloading. 209 | if !has_key(l:args, 'on') 210 | let l:args['on'] = [] 211 | endif 212 | endfor 213 | 214 | call s:removeUnsupportedArgs(l:args) 215 | Plug a:plugin, l:args 216 | endfunction 217 | 218 | function! s:removeUnsupportedArgs(args) 219 | " Remove args unsupported by Plug 220 | let l:PlugOpts = [ 221 | \ 'branch', 222 | \ 'tag', 223 | \ 'commit', 224 | \ 'rtp', 225 | \ 'dir', 226 | \ 'as', 227 | \ 'do', 228 | \ 'on', 229 | \ 'for', 230 | \ 'frozen', 231 | \ ] 232 | for opt in keys(a:args) 233 | if index(l:PlugOpts, opt) < 0 " If item not in the list. 234 | silent! call remove(a:args, opt) 235 | endif 236 | endfor 237 | endfunction 238 | 239 | " Add support for loading Plug plugins on specific event. 240 | function! s:loadPluginOnEvent(name, event) 241 | " Plug-loads function on autocmd event. 242 | " Plug options should include 'on': [] to prevent load before event. 243 | " name: the last part of the plugin url (See GetPluginName()). 244 | " event: name of autocmd event 245 | " Example: 246 | " Plug 'ycm-core/YouCompleteMe', {'on': []} 247 | " call LoadPluginOnEvent('YouCompleteMe', 'InsertEnter') 248 | let l:plugLoad = 'autocmd ' . a:event . ' * call plug#load("' 249 | let l:plugLoadEnd = '")' 250 | let l:augroupName = a:name . '_' . a:event 251 | let l:undoAutocmd = 'autocmd! ' . l:augroupName 252 | exec 'augroup ' . l:augroupName 253 | autocmd! 254 | exec l:plugLoad . a:name . l:plugLoadEnd . ' | ' . l:undoAutocmd 255 | exec 'augroup END' 256 | endfunction 257 | -------------------------------------------------------------------------------- /lua/lsp/cspellls.lua: -------------------------------------------------------------------------------- 1 | -- originally from: 2 | -- https://github.com/quolpr/nvim-config/blob/0ffedf1a75c26508a3e94673c11def63e8488676/lua/cspell-lsp/init.lua 3 | -- see also gist from same user: https://gist.github.com/quolpr/2d9560c0ad5e77796a068061c8ea439c 4 | 5 | -- Function to decode a URI to a file path 6 | local function decode_uri(uri) 7 | return string.gsub(uri, "file://", "") 8 | end 9 | 10 | -- JSON Formatter implementation 11 | local JsonFormatter = {} 12 | 13 | function JsonFormatter:escape_chars(str) 14 | return str:gsub('[\\"\a\b\f\n\r\t\v]', { 15 | ["\\"] = "\\\\", 16 | ['"'] = '\\"', 17 | ["\a"] = "\\a", 18 | ["\b"] = "\\b", 19 | ["\f"] = "\\f", 20 | ["\n"] = "\\n", 21 | ["\r"] = "\\r", 22 | ["\t"] = "\\t", 23 | ["\v"] = "\\v", 24 | }) 25 | end 26 | 27 | function JsonFormatter:format_string(value) 28 | local result = self.escape_special_chars and self:escape_chars(value) or value 29 | self:emit(([["%s"]]):format(result), true) 30 | end 31 | 32 | function JsonFormatter:format_table(value, add_indent) 33 | local tbl_count = vim.tbl_count(value) 34 | self:emit("{\n", add_indent) 35 | self.indent = self.indent + 2 36 | local prev_indent = self.indent 37 | local i = 1 38 | for k, v in self.pairs_by_keys(value, self.compare[self.indent / 2] or self.default_compare) do 39 | self:emit(('"%s": '):format(self.escape_special_chars and self:escape_chars(k) or k), true) 40 | if type(v) == "string" then 41 | self.indent = 0 42 | end 43 | self:format_value(v) 44 | self.indent = prev_indent 45 | if i == tbl_count then 46 | self:emit("\n") 47 | else 48 | self:emit(",\n") 49 | end 50 | i = i + 1 51 | end 52 | self.indent = self.indent - 2 53 | self:emit("}", true) 54 | end 55 | 56 | function JsonFormatter:format_array(value) 57 | local array_count = #value 58 | self:emit("[\n") 59 | self.indent = self.indent + 2 60 | for i, item in ipairs(value) do 61 | self:format_value(item, true) 62 | if i == array_count then 63 | self:emit("\n") 64 | else 65 | self:emit(",\n") 66 | end 67 | end 68 | self.indent = self.indent - 2 69 | self:emit("]", true) 70 | end 71 | 72 | function JsonFormatter:emit(value, add_indent) 73 | if add_indent then 74 | self.out[#self.out + 1] = (" "):rep(self.indent) 75 | end 76 | self.out[#self.out + 1] = value 77 | end 78 | 79 | function JsonFormatter:format_value(value, add_indent) 80 | if value == nil then 81 | self:emit("null") 82 | end 83 | local _type = type(value) 84 | if _type == "string" then 85 | self:format_string(value) 86 | elseif _type == "number" then 87 | self:emit(tostring(value), add_indent) 88 | elseif _type == "boolean" then 89 | self:emit(value == true and "true" or "false", add_indent) 90 | elseif _type == "table" then 91 | local count = vim.tbl_count(value) 92 | if count == 0 then 93 | self:emit("{}") 94 | elseif #value > 0 then 95 | self:format_array(value) 96 | else 97 | self:format_table(value, add_indent) 98 | end 99 | end 100 | end 101 | 102 | function JsonFormatter:pretty_print(data, keys_orders, escape_special_chars) 103 | self.compare = {} 104 | if keys_orders then 105 | for indentation_level, keys_order in pairs(keys_orders) do 106 | local order = {} 107 | for i, key in ipairs(keys_order) do 108 | order[key] = i 109 | end 110 | local max_pos = #keys_order + 1 111 | self.compare[indentation_level] = function(a, b) 112 | return (order[a] or max_pos) - (order[b] or max_pos) < 0 113 | end 114 | end 115 | end 116 | self.default_compare = function(a, b) 117 | return a:lower() < b:lower() 118 | end 119 | self.escape_special_chars = escape_special_chars 120 | self.indent = 0 121 | self.out = {} 122 | self:format_value(data, false) 123 | return table.concat(self.out) 124 | end 125 | 126 | -- Helper for sorting pairs by keys 127 | JsonFormatter.pairs_by_keys = function(tbl, compare) 128 | local keys = {} 129 | for key, _ in pairs(tbl) do 130 | table.insert(keys, key) 131 | end 132 | compare = compare or function(a, b) 133 | return a:lower() < b:lower() 134 | end 135 | table.sort(keys, compare) 136 | local i = 0 137 | return function() 138 | i = i + 1 139 | if keys[i] then 140 | return keys[i], tbl[keys[i]] 141 | end 142 | end 143 | end 144 | 145 | -- Function to read and parse JSON from a file 146 | local function read_json_file(path) 147 | local file = io.open(path, "r") 148 | if not file then 149 | error("Failed to open file: " .. path) 150 | end 151 | local data = file:read("*a") 152 | file:close() 153 | 154 | local decoded = vim.json.decode(data) 155 | return decoded 156 | end 157 | 158 | -- Function to write formatted JSON data to a file 159 | local function write_json_file(path, table) 160 | local formatted = JsonFormatter:pretty_print(table, nil, true) 161 | 162 | local file = io.open(path, "w") 163 | if not file then 164 | error("Failed to open file for writing: " .. path) 165 | end 166 | file:write(formatted) 167 | file:close() 168 | end 169 | 170 | local function line_byte_from_position(lines, lnum, col, offset_encoding) 171 | if not lines or offset_encoding == "utf-8" then 172 | return col 173 | end 174 | 175 | local line = lines[lnum + 1] 176 | local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == "utf-16") 177 | if ok then 178 | return result --- @type integer 179 | end 180 | 181 | return col 182 | end 183 | 184 | ---@param bufnr integer 185 | ---@return string[]? 186 | local function get_buf_lines(bufnr) 187 | if vim.api.nvim_buf_is_loaded(bufnr) then 188 | return vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) 189 | end 190 | 191 | local filename = vim.api.nvim_buf_get_name(bufnr) 192 | local f = io.open(filename) 193 | if not f then 194 | return 195 | end 196 | 197 | local content = f:read("*a") 198 | if not content then 199 | -- Some LSP servers report diagnostics at a directory level, in which case 200 | -- io.read() returns nil 201 | f:close() 202 | return 203 | end 204 | 205 | local lines = vim.split(content, "\n") 206 | f:close() 207 | return lines 208 | end 209 | 210 | -- Find the correct buffer for cSpell text edits 211 | -- When using code action pickers, the scope.bufnr may refer to the picker buffer 212 | -- This function finds the actual file buffer that contains the text to be edited 213 | ---@param scope_bufnr integer The buffer number from the command scope 214 | ---@param range table The LSP range containing start and end positions 215 | ---@return integer|nil target_bufnr The buffer number to edit, or nil if not found 216 | local function find_target_buffer(scope_bufnr, range) 217 | -- If scope.bufnr is already a valid file buffer, use it 218 | if vim.bo[scope_bufnr].buftype == "" then 219 | return scope_bufnr 220 | end 221 | 222 | local candidates = {} 223 | 224 | -- First, try to get the buffer from the previous window (before picker opened) 225 | local prev_win = vim.fn.win_getid(vim.fn.winnr("#")) 226 | if prev_win and prev_win ~= 0 then 227 | local prev_bufnr = vim.api.nvim_win_get_buf(prev_win) 228 | if vim.api.nvim_buf_is_loaded(prev_bufnr) and vim.bo[prev_bufnr].buftype == "" then 229 | local clients = vim.lsp.get_clients({ bufnr = prev_bufnr, name = "cSpell" }) 230 | if #clients > 0 then 231 | -- Prioritize the previous window's buffer 232 | table.insert(candidates, 1, prev_bufnr) 233 | end 234 | end 235 | end 236 | 237 | -- Collect all other buffers with cSpell attached 238 | for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do 239 | if vim.api.nvim_buf_is_loaded(bufnr) and vim.bo[bufnr].buftype == "" then 240 | local clients = vim.lsp.get_clients({ bufnr = bufnr, name = "cSpell" }) 241 | if #clients > 0 and bufnr ~= candidates[1] then 242 | table.insert(candidates, bufnr) 243 | end 244 | end 245 | end 246 | 247 | -- Verify which candidate has the matching text at the range 248 | for _, bufnr in ipairs(candidates) do 249 | local buf_lines = get_buf_lines(bufnr) 250 | if buf_lines and buf_lines[range.start.line + 1] then 251 | local start_ch = line_byte_from_position(buf_lines, range.start.line, range.start.character, "utf-16") 252 | local end_ch = line_byte_from_position(buf_lines, range["end"].line, range["end"].character, "utf-16") 253 | local line = buf_lines[range.start.line + 1] 254 | local text_at_range = line:sub(start_ch + 1, end_ch) 255 | 256 | -- Check if this text exists at the specified position 257 | if text_at_range and #text_at_range > 0 then 258 | return bufnr 259 | end 260 | end 261 | end 262 | 263 | -- No suitable buffer found 264 | if #candidates == 0 then 265 | vim.notify("cSpell: Could not find target buffer with cSpell LSP client", vim.log.levels.ERROR) 266 | else 267 | vim.notify("cSpell: Could not match text at range in any candidate buffer", vim.log.levels.ERROR) 268 | end 269 | return nil 270 | end 271 | 272 | return { 273 | capabilities = vim.lsp.protocol.make_client_capabilities(), 274 | cmd = { 275 | "cspellls", 276 | "--stdio", 277 | }, 278 | filetypes = { "markdown", "html", "gitcommit" }, 279 | root_markers = { 280 | ".git", 281 | ".cSpell.json", 282 | ".cspell.json", 283 | "cSpell.json", 284 | "cspell.config.cjs", 285 | "cspell.config.js", 286 | "cspell.config.json", 287 | "cspell.config.yaml", 288 | "cspell.config.yml", 289 | "cspell.json", 290 | "cspell.json", 291 | "cspell.yaml", 292 | "cspell.yml", 293 | }, 294 | single_file_support = true, 295 | settings = { 296 | cSpell = { -- see more here: https://streetsidesoftware.com/vscode-spell-checker/docs/configuration 297 | enabled = true, 298 | language = { "pt_BR", "en" }, 299 | trustedWorkspace = true, -- Enable loading JavaScript CSpell configuration files. https://github.com/streetsidesoftware/vscode-spell-checker/blob/main/website/docs/configuration/auto_advanced.md#cspelltrustedworkspace 300 | checkOnlyEnabledFileTypes = false, -- https://github.com/streetsidesoftware/vscode-spell-checker/blob/main/website/docs/configuration/auto_files-folders-and-workspaces.md#cspellcheckonlyenabledfiletypes 301 | doNotUseCustomDecorationForScheme = true, -- Use VS Code to Render Spelling Issues. https://github.com/streetsidesoftware/vscode-spell-checker/blob/main/website/docs/configuration/auto_appearance.md#cspelldonotusecustomdecorationforscheme 302 | useCustomDecorations = false, -- Draw custom decorations on Spelling Issues. https://github.com/streetsidesoftware/vscode-spell-checker/blob/main/website/docs/configuration/auto_appearance.md#cspellusecustomdecorations 303 | }, 304 | }, 305 | handlers = { 306 | ["_onDiagnostics"] = function(err, result, ctx, config) 307 | local results = result[1] 308 | for aResult in results do 309 | vim.lsp.handlers["textDocument/publishDiagnostics"](err, aResult, ctx, config) 310 | vim.lsp.diagnostic.on_publish_diagnostics(err, aResult, ctx) 311 | end 312 | end, 313 | ["_onWorkspaceConfigForDocumentRequest"] = function() 314 | return { 315 | ["uri"] = nil, 316 | ["workspaceFile"] = nil, 317 | ["workspaceFolder"] = nil, 318 | ["words"] = {}, 319 | ["ignoreWords"] = {}, 320 | } 321 | end, 322 | }, 323 | on_init = function() 324 | vim.lsp.commands["cSpell.editText"] = function(command, scope) 325 | local range = command.arguments[3][1].range 326 | local new_text = command.arguments[3][1].newText 327 | 328 | -- Find the correct buffer to edit 329 | local target_bufnr = find_target_buffer(scope.bufnr, range) 330 | if not target_bufnr then 331 | return 332 | end 333 | 334 | local buf_lines = get_buf_lines(target_bufnr) 335 | if not buf_lines then 336 | vim.notify("cSpell: Could not read buffer lines for buffer " .. target_bufnr, vim.log.levels.ERROR) 337 | return 338 | end 339 | 340 | local start_line = range.start.line 341 | local start_ch = 342 | line_byte_from_position(buf_lines, range.start.line, range.start.character, "utf-16") 343 | local end_line = range["end"].line 344 | local end_ch = 345 | line_byte_from_position(buf_lines, range["end"].line, range["end"].character, "utf-16") 346 | 347 | local lines = vim.api.nvim_buf_get_lines(target_bufnr, start_line, end_line + 1, false) 348 | 349 | local start_line_content = lines[1] 350 | local end_line_content = lines[#lines] 351 | 352 | local before_range = start_line_content:sub(1, start_ch) 353 | local after_range = end_line_content:sub(end_ch + 1) 354 | 355 | lines[1] = before_range .. new_text .. after_range 356 | 357 | if #lines > 1 then 358 | for i = 2, #lines do 359 | lines[i] = nil 360 | end 361 | end 362 | 363 | vim.api.nvim_buf_set_lines(target_bufnr, start_line, start_line + 1, false, lines) 364 | end 365 | 366 | vim.lsp.commands["cSpell.addWordsToConfigFileFromServer"] = function(command) 367 | local words = command.arguments[1] 368 | local config_file_uri = command.arguments[3].uri 369 | local config_file_path = decode_uri(config_file_uri) 370 | local ext = vim.fn.fnamemodify(config_file_path, ":e") 371 | if ext == "yaml" or ext == "yml" then 372 | if not vim.fn.executable("yq") then 373 | vim.notify("yq is not installed or not in the PATH, cannot update " .. config_file_path) 374 | return 375 | end 376 | -- example of command: 377 | -- yq '(.words += ["Foo"]) | .words |= sort_by(. | ascii_downcase)' --yaml-roundtrip --in-place cspell.yaml 378 | local command = [[yq '(.words += ["]] 379 | .. table.concat(words, '", "') 380 | .. [["]) | .words |= sort_by(. | downcase)' --inplace ]] 381 | .. config_file_path 382 | local result = os.execute(command) 383 | if result ~= 0 then 384 | vim.notify( 385 | "Failed to update YAML file: " .. config_file_path .. " with command:\n" .. command 386 | ) 387 | end 388 | elseif ext == "json" then 389 | local json_data = read_json_file(config_file_path) 390 | vim.list_extend(json_data.words, words) 391 | write_json_file(config_file_path, json_data) 392 | elseif ext == "cjs" or ext == "js" then 393 | vim.notify( 394 | "JavaScript files not supported for updating configuration (file " 395 | .. config_file_path 396 | .. ")" 397 | ) 398 | else 399 | vim.notify("Unsupported file type: " .. ext .. " (file " .. config_file_path .. ")") 400 | end 401 | end 402 | 403 | vim.lsp.commands["cSpell.addWordsToDictionaryFileFromServer"] = function() 404 | vim.notify("Not supported") 405 | end 406 | 407 | vim.lsp.commands["cSpell.addWordsToVSCodeSettingsFromServer"] = function() 408 | vim.notify("Not supported") 409 | end 410 | end, 411 | } 412 | -------------------------------------------------------------------------------- /helpers/rust_prettifier_for_lldb.py: -------------------------------------------------------------------------------- 1 | # rust-prettifier-for-lldb, Christian Schwarz, 2024 2 | 3 | # This file is based on by vadimcn/codelldb by Vadim Chugunov 4 | # https://github.com/vadimcn/codelldb/blob/05502bf75e4e7878a99b0bf0a7a81bba2922cbe3/formatters/rust.py 5 | # The original version was used and adapted under the terms of the MIT License: 6 | # 7 | # The MIT License (MIT) 8 | # 9 | # Copyright (c) 2016 Vadim Chugunov 10 | # 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documentation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furnished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in all 19 | # copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | # SOFTWARE. 28 | 29 | 30 | from __future__ import print_function, division 31 | import sys 32 | import lldb # type: ignore 33 | import weakref 34 | import re 35 | 36 | module = sys.modules[__name__] 37 | rust_category = None 38 | lldb_major_version = None 39 | 40 | MAX_STRING_SUMMARY_LENGTH = 1024 41 | MAX_SEQUENCE_SUMMARY_LENGTH = 10 42 | 43 | TARGET_ADDR_SIZE = 8 44 | 45 | 46 | def initialize_category(debugger, internal_dict): 47 | global rust_category, MAX_STRING_SUMMARY_LENGTH, TARGET_ADDR_SIZE, lldb_major_version 48 | 49 | version_string_match = re.match( 50 | r"lldb version (\d+)\.\d+", 51 | lldb.SBDebugger.GetVersionString(), 52 | re.IGNORECASE 53 | ) 54 | if version_string_match is not None: 55 | lldb_major_version = version_string_match.groups(1) 56 | 57 | # remove previous conflicting prettifiers potentially added e.g. by CodeLLDB 58 | rust_category = debugger.DeleteCategory('Rust') 59 | rust_category = debugger.CreateCategory('Rust') 60 | # rust_category.AddLanguage(lldb.eLanguageTypeRust) 61 | rust_category.SetEnabled(True) 62 | 63 | attach_synthetic_to_type(TupleSynthProvider, r'^\(.*\)$', True) 64 | # *-windows-msvc uses this name since 1.47 65 | attach_synthetic_to_type(MsvcTupleSynthProvider, r'^tuple\$?<.+>$', True) 66 | 67 | attach_synthetic_to_type(CharSynthProvider, 'char32_t') 68 | 69 | # there is no 1 byte char type in rust, so these have to be u8/i8 70 | attach_synthetic_to_type(U8SynthProvider, 'unsigned char') 71 | attach_synthetic_to_type(I8SynthProvider, 'char') 72 | 73 | attach_synthetic_to_type(StrSliceSynthProvider, '&str') 74 | attach_synthetic_to_type(StrSliceSynthProvider, 'str*') 75 | # *-windows-msvc uses this name since 1.5? 76 | attach_synthetic_to_type(StrSliceSynthProvider, 'str') 77 | attach_synthetic_to_type(StrSliceSynthProvider, 'ref$') 78 | attach_synthetic_to_type(StrSliceSynthProvider, 'ref_mut$') 79 | 80 | attach_synthetic_to_type(StdStringSynthProvider, 81 | '^(collections|alloc)::string::String$', True) 82 | attach_synthetic_to_type(StdVectorSynthProvider, 83 | r'^(collections|alloc)::vec::Vec<.+>$', True) 84 | attach_synthetic_to_type(StdVecDequeSynthProvider, 85 | r'^(collections|alloc::collections)::vec_deque::VecDeque<.+>$', True) 86 | 87 | attach_synthetic_to_type(MsvcEnumSynthProvider, r'^enum\$<.+>$', True) 88 | attach_synthetic_to_type(MsvcEnum2SynthProvider, r'^enum2\$<.+>$', True) 89 | 90 | attach_synthetic_to_type(SliceSynthProvider, r'^&(mut *)?\[.*\]$', True) 91 | attach_synthetic_to_type(MsvcSliceSynthProvider, 92 | r'^(mut *)?slice\$?<.+>.*$', True) 93 | attach_synthetic_to_type(MsvcSliceSynthProvider, 94 | r'^ref(_mut)?\$.*>$', True) 95 | 96 | attach_synthetic_to_type(StdCStringSynthProvider, 97 | '^(std|alloc)::ffi::c_str::CString$', True) 98 | attach_synthetic_to_type(StdCStrSynthProvider, 99 | '^&?(std|core)::ffi::c_str::CStr$', True) 100 | attach_synthetic_to_type(StdCStrSynthProvider, 101 | 'ref$') 102 | attach_synthetic_to_type(StdCStrSynthProvider, 103 | 'ref_mut$') 104 | 105 | attach_synthetic_to_type(StdOsStringSynthProvider, 106 | 'std::ffi::os_str::OsString') 107 | attach_synthetic_to_type(StdOsStrSynthProvider, 108 | '^&?std::ffi::os_str::OsStr', True) 109 | attach_synthetic_to_type(StdOsStrSynthProvider, 110 | 'ref$') 111 | attach_synthetic_to_type(StdOsStrSynthProvider, 112 | 'ref_mut$') 113 | 114 | attach_synthetic_to_type(StdPathBufSynthProvider, 'std::path::PathBuf') 115 | attach_synthetic_to_type(StdPathSynthProvider, '^&?std::path::Path', True) 116 | attach_synthetic_to_type(StdPathSynthProvider, 'ref$') 117 | attach_synthetic_to_type(StdPathSynthProvider, 'ref_mut$') 118 | 119 | attach_synthetic_to_type(StdRcSynthProvider, r'^alloc::rc::Rc<.+>$', True) 120 | attach_synthetic_to_type( 121 | StdRcSynthProvider, r'^alloc::rc::Weak<.+>$', True) 122 | attach_synthetic_to_type( 123 | StdArcSynthProvider, r'^alloc::(sync|arc)::Arc<.+>$', True) 124 | attach_synthetic_to_type( 125 | StdArcSynthProvider, r'^alloc::(sync|arc)::Weak<.+>$', True) 126 | attach_synthetic_to_type(StdMutexSynthProvider, 127 | r'^std::sync::mutex::Mutex<.+>$', True) 128 | 129 | attach_synthetic_to_type(StdCellSynthProvider, 130 | r'^core::cell::Cell<.+>$', True) 131 | attach_synthetic_to_type(StdUnsafeCellSynthProvider, 132 | r'^core::cell::UnsafeCell<.+>$', True) 133 | attach_synthetic_to_type(StdRefCellSynthProvider, 134 | r'^core::cell::RefCell<.+>$', True) 135 | attach_synthetic_to_type( 136 | StdRefCellBorrowSynthProvider, r'^core::cell::Ref<.+>$', True) 137 | attach_synthetic_to_type( 138 | StdRefCellBorrowSynthProvider, r'^core::cell::RefMut<.+>$', True) 139 | 140 | attach_synthetic_to_type( 141 | StdHashMapSynthProvider, r'^std::collections::hash::map::HashMap<.+>$', True) 142 | attach_synthetic_to_type( 143 | StdHashSetSynthProvider, r'^std::collections::hash::set::HashSet<.+>$', True) 144 | 145 | attach_synthetic_to_type(OptionSynthProvider, 146 | r'^core::option::Option<.+>$', True) 147 | attach_synthetic_to_type(ResultSynthProvider, 148 | r'^core::result::Result<.+>$', True) 149 | attach_synthetic_to_type(CowSynthProvider, 150 | r'^alloc::borrow::Cow<.+>$', True) 151 | 152 | attach_synthetic_to_type(CrossbeamAtomicCellSynthProvider, 153 | r'^crossbeam_utils::atomic::atomic_cell::AtomicCell<.+>$', True) 154 | 155 | debugger.HandleCommand( 156 | "type summary add" 157 | + f" --python-function {__name__}.enum_summary_provider" 158 | + f" --recognizer-function {__name__}.enum_recognizer_function", 159 | ) 160 | debugger.HandleCommand( 161 | "type synthetic add" 162 | + f" --python-class {__name__}.GenericEnumSynthProvider" 163 | + f" --recognizer-function {__name__}.enum_recognizer_function", 164 | ) 165 | 166 | if 'rust' in internal_dict.get('source_languages', []): 167 | lldb.SBDebugger.SetInternalVariable('target.process.thread.step-avoid-regexp', 168 | '^ 231 | def read_unique_ptr(valobj): 232 | pointer = valobj.GetChildMemberWithName('pointer') 233 | if pointer.TypeIsPointerType(): # Between 1.33 and 1.63 pointer was just *const T 234 | return pointer 235 | return pointer.GetChildAtIndex(0) 236 | 237 | 238 | def string_from_addr(process, addr, length): 239 | if length <= 0: 240 | return u'' 241 | error = lldb.SBError() 242 | data = process.ReadMemory(addr, length, error) 243 | if error.Success(): 244 | return data.decode('utf8', 'replace') 245 | else: 246 | raise Exception('ReadMemory error: %s', error.GetCString()) 247 | 248 | 249 | def string_from_ptr(pointer, length): 250 | return string_from_addr(pointer.GetProcess(), pointer.GetValueAsUnsigned(), length) 251 | 252 | 253 | # turns foo::Bar::Baz::Quux into Quux 254 | def unscope_typename(type_name): 255 | start = 0 256 | level = 0 257 | prev_was_quote = False 258 | for i, c in enumerate(type_name): 259 | if c == '<': 260 | level += 1 261 | continue 262 | if c == '>': 263 | level -= 1 264 | continue 265 | if c == ':': 266 | if prev_was_quote: 267 | if level == 0: 268 | start = i + 1 269 | prev_was_quote = False 270 | continue 271 | prev_was_quote = True 272 | continue 273 | prev_was_quote = False 274 | 275 | return type_name[start::] 276 | 277 | # turns Cow::Borrowed::("asdf") into Cow::Borrowed("asdf") 278 | def drop_template_args(type_name): 279 | res = "" 280 | level = 0 281 | start = 0 282 | for i, c in enumerate(type_name): 283 | if c == '<': 284 | if level == 0: 285 | res += type_name[start:i] 286 | level += 1 287 | elif c == '>': 288 | level -= 1 289 | if level == 0: 290 | start = i + 1 291 | res += type_name[start:] 292 | return res 293 | 294 | 295 | def get_template_params(type_name): 296 | params = [] 297 | level = 0 298 | start = 0 299 | for i, c in enumerate(type_name): 300 | if c == '<': 301 | level += 1 302 | if level == 1: 303 | start = i + 1 304 | elif c == '>': 305 | level -= 1 306 | if level == 0: 307 | params.append(type_name[start:i].strip()) 308 | elif c == ',' and level == 1: 309 | params.append(type_name[start:i].strip()) 310 | start = i + 1 311 | return params 312 | 313 | 314 | def obj_summary(valobj, obj_typename=None, unavailable='{...}', parenthesize_single_value=False, max_len=32): 315 | summary = valobj.GetSummary() 316 | if summary is not None: 317 | if parenthesize_single_value: 318 | return f"({summary})" 319 | return summary 320 | child_count = valobj.GetNumChildren() 321 | if child_count != 0: 322 | if valobj.GetChildAtIndex(0).GetName() in ['0', '__0'] and obj_typename is None: 323 | return tuple_summary(valobj) 324 | 325 | if obj_typename is None: 326 | summary = "{" 327 | else: 328 | summary = f"{obj_typename}{{" 329 | 330 | for i in range(child_count): 331 | name = valobj.GetType().GetFieldAtIndex(i).GetName() 332 | value = valobj.GetChildAtIndex(i) 333 | member_summary = f"{name}: {obj_summary(value)}" 334 | if i != 0: 335 | summary += ", " 336 | if len(summary) + 1 + len(member_summary) > max_len: 337 | summary += ".." 338 | break 339 | summary += member_summary 340 | summary += "}" 341 | if obj_typename is not None and parenthesize_single_value: 342 | return f"({summary})" 343 | return summary 344 | 345 | summary = valobj.GetValue() 346 | if summary is not None: 347 | if obj_typename is None: 348 | res = summary 349 | else: 350 | res = f"{obj_typename}({summary})" 351 | if parenthesize_single_value: 352 | return f"({res})" 353 | return res 354 | 355 | return unavailable 356 | 357 | 358 | def sequence_summary(childern, max_len=MAX_STRING_SUMMARY_LENGTH, max_elem_count= MAX_SEQUENCE_SUMMARY_LENGTH): 359 | s = '' 360 | for (i, child) in enumerate(childern): 361 | if len(s) > 0: 362 | s += ', ' 363 | summary = obj_summary(child) 364 | if len(s + summary) > max_len or i >= max_elem_count: 365 | s += '...' 366 | break 367 | s += summary 368 | return s 369 | 370 | 371 | def tuple_summary(obj, skip_first=0, max_len=32, include_parens=True): 372 | if include_parens: 373 | s = "(" 374 | else: 375 | s = "" 376 | for i in range(skip_first, obj.GetNumChildren()): 377 | if i > 0: 378 | s += ', ' 379 | os = obj_summary(obj.GetChildAtIndex(i), max_len=max_len-len(s)-1) 380 | if len(s) + len(os) + int(include_parens) > max_len: 381 | s += '..' 382 | break 383 | s += os 384 | if include_parens: 385 | s += ')' 386 | return s 387 | 388 | 389 | class RustSynthProvider(object): 390 | synth_by_id = weakref.WeakValueDictionary() 391 | next_id = 0 392 | obj_id = 0 393 | valobj = None 394 | summary = None 395 | 396 | def __init__(self, valobj, dict={}): 397 | self.valobj = valobj 398 | self.obj_id = RustSynthProvider.next_id 399 | RustSynthProvider.synth_by_id[self.obj_id] = self 400 | RustSynthProvider.next_id += 1 401 | 402 | def update(self): 403 | return True 404 | 405 | def has_children(self): 406 | return False 407 | 408 | def num_children(self): 409 | return 0 410 | 411 | def get_child_at_index(self, index): 412 | return None 413 | 414 | def get_child_index(self, name): 415 | if name == '$$object-id$$': 416 | return self.obj_id 417 | 418 | return self.get_index_of_child(name) 419 | 420 | def get_summary(self): 421 | return self.summary 422 | 423 | 424 | class CharSynthProvider(RustSynthProvider): 425 | def update(self): 426 | value = self.valobj.GetValueAsUnsigned() 427 | c = chr(value) 428 | if c.isprintable(): 429 | self.summary = f"'{c}'" 430 | else: 431 | self.summary = f"U+0x{value:08X}" 432 | 433 | 434 | class U8SynthProvider(RustSynthProvider): 435 | def update(self): 436 | value = self.valobj.GetValueAsUnsigned() 437 | self.summary = f"{int(value)}" 438 | 439 | 440 | class I8SynthProvider(RustSynthProvider): 441 | def update(self): 442 | value = self.valobj.GetValueAsSigned() 443 | self.summary = f"{int(value)}" 444 | 445 | 446 | class TupleSynthProvider(RustSynthProvider): 447 | def update(self): 448 | self.summary = tuple_summary(self.valobj) 449 | 450 | def has_children(self): 451 | return True 452 | 453 | def num_children(self): 454 | return self.valobj.GetNumChildren() 455 | 456 | def get_index_of_child(self, name): 457 | return int(name.lstrip('_[').rstrip(']')) 458 | 459 | def get_child_at_index(self, index): 460 | value = self.valobj.GetChildAtIndex(index) 461 | value = self.valobj.CreateValueFromData(str(index), value.GetData(), value.GetType()) 462 | return value 463 | 464 | 465 | class ArrayLikeSynthProvider(RustSynthProvider): 466 | '''Base class for providers that represent array-like objects''' 467 | 468 | def update(self): 469 | self.ptr, self.len = self.ptr_and_len(self.valobj) # type: ignore 470 | self.item_type = self.ptr.GetType().GetPointeeType() 471 | self.item_size = self.item_type.GetByteSize() 472 | 473 | def ptr_and_len(self, obj): 474 | pass # abstract 475 | 476 | def num_children(self): 477 | return self.len 478 | 479 | def has_children(self): 480 | return True 481 | 482 | def get_child_at_index(self, index): 483 | if not 0 <= index < self.len: 484 | return None 485 | offset = index * self.item_size 486 | return self.ptr.CreateChildAtOffset('[%s]' % index, offset, self.item_type) 487 | 488 | def get_index_of_child(self, name): 489 | return int(name.lstrip('[').rstrip(']')) 490 | 491 | def get_summary(self): 492 | return '(%d)' % (self.len,) 493 | 494 | 495 | class StdVectorSynthProvider(ArrayLikeSynthProvider): 496 | def ptr_and_len(self, vec): 497 | element_type = self.valobj.GetType().GetTemplateArgumentType(0) 498 | ptr = read_unique_ptr(gcm(vec, 'buf', 'inner', 'ptr', 'pointer')) 499 | ptr = ptr.Cast(element_type.GetPointerType()) 500 | len = gcm(vec, 'len').GetValueAsUnsigned() 501 | return (ptr, len) 502 | 503 | def get_summary(self): 504 | return '(%d) vec![%s]' % (self.len, sequence_summary((self.get_child_at_index(i) for i in range(self.len)))) 505 | 506 | 507 | class StdVecDequeSynthProvider(RustSynthProvider): 508 | def update(self): 509 | element_type = self.valobj.GetType().GetTemplateArgumentType(0) 510 | ptr = read_unique_ptr( 511 | gcm(self.valobj, 'buf', 'inner', 'ptr', 'pointer')) 512 | self.ptr = ptr.Cast(element_type.GetPointerType()) 513 | self.cap = ( 514 | gcm(self.valobj, 'buf', 'inner', 'cap') 515 | .GetChildAtIndex(0) 516 | .GetValueAsUnsigned() 517 | ) 518 | 519 | head = gcm(self.valobj, 'head').GetValueAsUnsigned() 520 | 521 | # rust 1.67 changed from a head, tail implementation to a head, length impl 522 | # https://github.com/rust-lang/rust/pull/102991 523 | vd_len = gcm(self.valobj, 'len') 524 | if vd_len.IsValid(): 525 | self.len = vd_len.GetValueAsUnsigned() 526 | self.startptr = head 527 | else: 528 | tail = gcm(self.valobj, 'tail').GetValueAsUnsigned() 529 | self.len = head - tail 530 | self.startptr = tail 531 | 532 | self.item_type = self.ptr.GetType().GetPointeeType() 533 | self.item_size = self.item_type.GetByteSize() 534 | 535 | def num_children(self): 536 | return self.len 537 | 538 | def has_children(self): 539 | return True 540 | 541 | def get_child_at_index(self, index): 542 | if not 0 <= index < self.num_children(): 543 | return None 544 | offset = ((self.startptr + index) % self.cap) * self.item_size 545 | return self.ptr.CreateChildAtOffset('[%s]' % index, offset, self.item_type) 546 | 547 | def get_index_of_child(self, name): 548 | return int(name.lstrip('[').rstrip(']')) 549 | 550 | def get_summary(self): 551 | return '(%d) VecDeque[%s]' % ( 552 | self.num_children(), 553 | sequence_summary((self.get_child_at_index(i) 554 | for i in range(self.num_children()))) 555 | ) 556 | 557 | 558 | class SliceSynthProvider(ArrayLikeSynthProvider): 559 | def ptr_and_len(self, vec): 560 | return ( 561 | gcm(vec, 'data_ptr'), 562 | gcm(vec, 'length').GetValueAsUnsigned() 563 | ) 564 | 565 | def get_summary(self): 566 | return '(%d) &[%s]' % (self.len, sequence_summary((self.get_child_at_index(i) for i in range(self.len)))) 567 | 568 | 569 | class MsvcSliceSynthProvider(SliceSynthProvider): 570 | def get_type_name(self): 571 | tparams = get_template_params(self.valobj.GetTypeName()) 572 | return '&[' + tparams[0] + ']' 573 | 574 | 575 | # Base class for *String providers 576 | class StringLikeSynthProvider(ArrayLikeSynthProvider): 577 | def update(self): 578 | super().update() 579 | self.strval = string_from_ptr( 580 | self.ptr, 581 | min(self.len, MAX_STRING_SUMMARY_LENGTH) 582 | ) 583 | if self.len > MAX_STRING_SUMMARY_LENGTH: 584 | self.strval += u'...' 585 | 586 | def get_child_at_index(self, index): 587 | ch = ArrayLikeSynthProvider.get_child_at_index(self, index) 588 | ch.SetFormat(lldb.eFormatChar) 589 | return ch 590 | 591 | def get_summary(self): 592 | return u'"%s"' % self.strval 593 | 594 | 595 | class StrSliceSynthProvider(StringLikeSynthProvider): 596 | def ptr_and_len(self, valobj): 597 | return ( 598 | gcm(valobj, 'data_ptr'), 599 | gcm(valobj, 'length').GetValueAsUnsigned() 600 | ) 601 | 602 | def get_type_name(self): 603 | return '&str' 604 | 605 | 606 | class StdStringSynthProvider(StringLikeSynthProvider): 607 | def ptr_and_len(self, valobj): 608 | vec = gcm(valobj, 'vec') 609 | return ( 610 | read_unique_ptr(gcm(vec, 'buf', 'inner', 'ptr', 'pointer')), 611 | gcm(vec, 'len').GetValueAsUnsigned() 612 | ) 613 | 614 | 615 | class StdCStringSynthProvider(StringLikeSynthProvider): 616 | def ptr_and_len(self, valobj): 617 | vec = gcm(valobj, 'inner') 618 | return ( 619 | gcm(vec, 'data_ptr'), 620 | gcm(vec, 'length').GetValueAsUnsigned() - 1 621 | ) 622 | 623 | 624 | class StdOsStringSynthProvider(StringLikeSynthProvider): 625 | def ptr_and_len(self, valobj): 626 | vec = gcm(valobj, 'inner', 'inner') 627 | tmp = gcm(vec, 'bytes') # Windows OSString has an extra layer 628 | if tmp.IsValid(): 629 | vec = tmp 630 | return ( 631 | read_unique_ptr(gcm(vec, 'buf', 'ptr')), 632 | gcm(vec, 'len').GetValueAsUnsigned() 633 | ) 634 | 635 | 636 | class FFISliceSynthProvider(StringLikeSynthProvider): 637 | def ptr_and_len(self, valobj): 638 | process = valobj.GetProcess() 639 | slice_ptr = valobj.GetLoadAddress() 640 | data_ptr_type = valobj.GetTarget().GetBasicType( 641 | lldb.eBasicTypeChar).GetPointerType() 642 | # Unsized slice objects have incomplete debug info, so here we just assume standard slice 643 | # reference layout: [, ] 644 | error = lldb.SBError() 645 | pointer = valobj.CreateValueFromAddress( 646 | 'data', slice_ptr, data_ptr_type) 647 | length = process.ReadPointerFromMemory( 648 | slice_ptr + process.GetAddressByteSize(), error) 649 | return pointer, length 650 | 651 | 652 | class StdCStrSynthProvider(FFISliceSynthProvider): 653 | def ptr_and_len(self, valobj): 654 | ptr, len = FFISliceSynthProvider.ptr_and_len(self, valobj) 655 | return (ptr, len-1) # drop terminaing '\0' 656 | 657 | 658 | class StdOsStrSynthProvider(FFISliceSynthProvider): 659 | pass 660 | 661 | 662 | class StdPathBufSynthProvider(StdOsStringSynthProvider): 663 | def ptr_and_len(self, valobj): 664 | return StdOsStringSynthProvider.ptr_and_len(self, gcm(valobj, 'inner')) 665 | 666 | 667 | class StdPathSynthProvider(FFISliceSynthProvider): 668 | pass 669 | 670 | 671 | class DerefSynthProvider(RustSynthProvider): 672 | deref = lldb.SBValue() 673 | 674 | def has_children(self): 675 | return self.deref.MightHaveChildren() 676 | 677 | def num_children(self): 678 | return self.deref.GetNumChildren() 679 | 680 | def get_child_at_index(self, index): 681 | return self.deref.GetChildAtIndex(index) 682 | 683 | def get_index_of_child(self, name): 684 | return self.deref.GetIndexOfChildWithName(name) 685 | 686 | def get_summary(self): 687 | return obj_summary(self.deref) 688 | 689 | 690 | # Base for Rc and Arc 691 | class StdRefCountedSynthProvider(RustSynthProvider): 692 | weak = 0 693 | strong = 0 694 | slice_len = None 695 | value = None 696 | 697 | def has_children(self): 698 | return True 699 | 700 | def num_children(self): 701 | return self.value.GetNumChildren() if self.slice_len is None else self.slice_len 702 | 703 | def get_child_at_index(self, i): 704 | if self.slice_len: 705 | elem_size = self.value.GetByteSize() 706 | return self.value.CreateChildAtOffset( 707 | f'[{i}]', i * elem_size, self.value.GetType() 708 | ) 709 | 710 | return self.value.GetChildAtIndex(i) 711 | 712 | 713 | def get_index_of_child(self, name): 714 | if self.slice_len is not None: 715 | return int(name.lstrip('[').rstrip(']')) 716 | 717 | return self.value.GetIndexOfChildWithName(name) 718 | 719 | def get_summary(self): 720 | if self.weak != 0: 721 | s = '(strong:%d, weak:%d) ' % (self.strong, self.weak) 722 | else: 723 | s = '(strong:%d) ' % self.strong 724 | if self.strong > 0: 725 | if self.slice_len is not None: 726 | if self.value.GetType().GetName() == "unsigned char": 727 | str_data = string_from_addr( 728 | self.value.GetProcess(), 729 | self.value.GetLoadAddress(), 730 | min(MAX_STRING_SUMMARY_LENGTH, self.slice_len) 731 | ) 732 | s += f"\"{str_data}\"" 733 | else: 734 | s += "[%s]" % sequence_summary(( 735 | self.get_child_at_index(i) for i in range(self.slice_len) 736 | )) 737 | else: 738 | s += obj_summary(self.value) 739 | else: 740 | s += '' 741 | return s 742 | 743 | 744 | class StdRcSynthProvider(StdRefCountedSynthProvider): 745 | def update(self): 746 | inner = read_unique_ptr(gcm(self.valobj, 'ptr')) 747 | self.strong = gcm(inner, 'strong', 'value', 'value').GetValueAsUnsigned() 748 | self.weak = gcm(inner, 'weak', 'value', 'value').GetValueAsUnsigned() 749 | if self.strong > 0: 750 | self.value = gcm(inner, 'value') 751 | self.weak -= 1 # There's an implicit weak reference communally owned by all the strong pointers 752 | if self.valobj.GetType().size == 2 * TARGET_ADDR_SIZE: 753 | self.slice_len = gcm(self.valobj, "ptr", "pointer", "length").GetValueAsUnsigned() 754 | else: 755 | self.value = lldb.SBValue() 756 | self.value.SetPreferSyntheticValue(True) 757 | 758 | 759 | class StdArcSynthProvider(StdRefCountedSynthProvider): 760 | def update(self): 761 | inner = read_unique_ptr(gcm(self.valobj, 'ptr')) 762 | self.strong = gcm(inner, 'strong', 'v', 'value').GetValueAsUnsigned() 763 | self.weak = gcm(inner, 'weak', 'v', 'value').GetValueAsUnsigned() 764 | if self.strong > 0: 765 | self.value = gcm(inner, 'data') 766 | if self.valobj.GetType().size == 2 * TARGET_ADDR_SIZE: 767 | self.slice_len = gcm(self.valobj, "ptr", "pointer", "length").GetValueAsUnsigned() 768 | self.weak -= 1 # There's an implicit weak reference communally owned by all the strong pointers 769 | else: 770 | self.value = lldb.SBValue() 771 | self.value.SetPreferSyntheticValue(True) 772 | 773 | 774 | class StdMutexSynthProvider(DerefSynthProvider): 775 | def update(self): 776 | self.deref = gcm(self.valobj, 'data', 'value') 777 | self.deref.SetPreferSyntheticValue(True) 778 | 779 | 780 | class StdCellSynthProvider(DerefSynthProvider): 781 | def update(self): 782 | self.deref = gcm(self.valobj, 'value', 'value') 783 | self.deref.SetPreferSyntheticValue(True) 784 | 785 | 786 | class StdUnsafeCellSynthProvider(DerefSynthProvider): 787 | def update(self): 788 | self.deref = gcm(self.valobj, 'value') 789 | self.deref.SetPreferSyntheticValue(True) 790 | 791 | class StdRefCellSynthProvider(DerefSynthProvider): 792 | def update(self): 793 | self.deref = gcm(self.valobj, 'value', 'value') 794 | self.deref.SetPreferSyntheticValue(True) 795 | 796 | def get_summary(self): 797 | borrow = gcm(self.valobj, 'borrow', 'value', 798 | 'value').GetValueAsSigned() 799 | s = '' 800 | if borrow < 0: 801 | s = '(borrowed:mut) ' 802 | elif borrow > 0: 803 | s = '(borrowed:%d) ' % borrow 804 | return s + obj_summary(self.deref) 805 | 806 | 807 | class StdRefCellBorrowSynthProvider(DerefSynthProvider): 808 | def update(self): 809 | self.deref = gcm(self.valobj, 'value', 'pointer').Dereference() 810 | self.deref.SetPreferSyntheticValue(True) 811 | 812 | 813 | class EnumSynthProvider(RustSynthProvider): 814 | variant = lldb.SBValue() 815 | typename_summary = "" 816 | variant_name = "" 817 | variant_summary = "" 818 | skip_first = 0 819 | 820 | def has_children(self): 821 | return self.variant.MightHaveChildren() 822 | 823 | def num_children(self): 824 | return self.variant.GetNumChildren() - self.skip_first 825 | 826 | def get_child_at_index(self, index): 827 | return self.variant.GetChildAtIndex(index + self.skip_first) 828 | 829 | def get_index_of_child(self, name): 830 | return self.variant.GetIndexOfChildWithName(name) - self.skip_first 831 | 832 | def get_summary(self): 833 | value_summary = self.variant_name + self.variant_summary 834 | 835 | if self.typename_summary != "": 836 | return self.typename_summary + "::" + value_summary 837 | else: 838 | return value_summary 839 | 840 | 841 | def get_enum_discriminator_value(union, index): 842 | obj = union.GetChildAtIndex(index) 843 | if not obj or obj.GetNumChildren() < 1: 844 | return None 845 | 846 | discr = obj.GetChildAtIndex(0) 847 | if not discr or discr.GetName() != "$discr$": 848 | return None 849 | 850 | return discr.GetValueAsUnsigned() 851 | 852 | def enum_summary_provider(valobj, dict): 853 | return get_synth_summary(GenericEnumSynthProvider, valobj, dict) 854 | 855 | def enum_recognizer_function(sbtype, _internal_dict): 856 | if sbtype.GetNumberOfFields() != 1: 857 | return False 858 | 859 | if sbtype.GetFieldAtIndex(0).GetName() != "$variants$": 860 | return False 861 | 862 | name = sbtype.GetName() 863 | special_cases = ["core::option::Option", "core::result::Result", "alloc::borrow::Cow"] 864 | for case in special_cases: 865 | if name.startswith(case): 866 | return False 867 | 868 | return True 869 | 870 | 871 | class GenericEnumSynthProvider(EnumSynthProvider): 872 | def update(self): 873 | self.summary = '' 874 | self.variant = self.valobj 875 | 876 | self.valobj.SetPreferSyntheticValue(False) 877 | union = self.valobj.GetChildAtIndex(0) 878 | union.SetPreferSyntheticValue(False) 879 | 880 | # at this point we assume this is a rust enum, 881 | # so if we fail further down the line we report an error 882 | self.variant_name = '' 883 | 884 | enum_type = self.valobj.GetType() 885 | if enum_type.IsPointerType(): 886 | enum_name = "&" + \ 887 | unscope_typename(enum_type.GetPointeeType().GetName()) 888 | else: 889 | enum_name = unscope_typename(enum_type.GetName()) 890 | self.typename_summary = enum_name 891 | 892 | variant_count = union.GetNumChildren() 893 | 894 | discriminator = None 895 | first_variant_without_discriminator = None 896 | 897 | for i in range(variant_count): 898 | dc = get_enum_discriminator_value(union, i) 899 | if dc is None: 900 | if first_variant_without_discriminator is None: 901 | first_variant_without_discriminator = i 902 | else: 903 | return # multiple variants without discriminator 904 | else: 905 | if discriminator is not None: 906 | if dc != discriminator: 907 | return # conflicting discriminator values 908 | else: 909 | discriminator = dc 910 | 911 | selected_variant = discriminator 912 | 913 | if first_variant_without_discriminator is not None: 914 | # probably a pointer based niche 915 | # all of this is just based on trial and error 916 | high_bit = 1 << (8 * TARGET_ADDR_SIZE - 1) 917 | 918 | if variant_count == 1: 919 | selected_variant = 0 920 | elif discriminator >= high_bit: 921 | selected_variant = discriminator - high_bit 922 | if selected_variant >= first_variant_without_discriminator: 923 | if selected_variant + 1 < variant_count: 924 | selected_variant += 1 925 | elif discriminator == 0 or discriminator >= variant_count : 926 | selected_variant = first_variant_without_discriminator 927 | 928 | if selected_variant >= variant_count: 929 | return 930 | 931 | union.SetPreferSyntheticValue(True) 932 | variant_outer = union.GetChildAtIndex(selected_variant) 933 | 934 | if variant_outer.GetNumChildren() == 1 and selected_variant == first_variant_without_discriminator: 935 | variant_outer_subindex = 0 936 | elif variant_outer.GetNumChildren() != 2: 937 | return 938 | else: 939 | variant_outer_subindex = 1 940 | 941 | variant_outer.SetPreferSyntheticValue(True) 942 | variant = variant_outer.GetChildAtIndex(variant_outer_subindex) 943 | 944 | # GetTypeName() gives weird results, e.g. `Foo::A:8`. Don't ask me why. 945 | variant_typename = drop_template_args(unscope_typename(variant.GetType().GetName())) 946 | self.variant_name = variant_typename 947 | 948 | variant_deref = False 949 | 950 | variant_child_count = variant.GetNumChildren() 951 | if variant_child_count == 1 and variant.GetChildAtIndex(0).GetName() in ['0', '__0']: 952 | variant = variant.GetChildAtIndex(0) 953 | variant_child_count = variant.GetNumChildren() 954 | variant_deref = True 955 | 956 | if variant_child_count == 0 and variant.GetValue() is None: 957 | summary = variant.GetSummary() 958 | if summary is not None: 959 | self.variant = variant 960 | self.variant_summary = f"({summary})" 961 | return 962 | 963 | objname = None 964 | if variant_deref and variant_child_count != 0: 965 | objname = variant_typename 966 | 967 | self.variant_summary = obj_summary( 968 | variant, 969 | obj_typename=objname, 970 | parenthesize_single_value=True 971 | ) 972 | 973 | self.variant = variant 974 | 975 | 976 | class OptionSynthProvider(GenericEnumSynthProvider): 977 | def update(self): 978 | super().update() 979 | self.typename_summary = "" 980 | # turn `Some(..)` into `Some(..)` 981 | if self.variant_name.startswith("Some"): 982 | self.variant_name = "Some" 983 | elif self.variant_name.startswith("None"): 984 | self.variant_name = "None" 985 | 986 | 987 | class ResultSynthProvider(GenericEnumSynthProvider): 988 | def update(self): 989 | super().update() 990 | self.typename_summary = "" 991 | # turn `Ok(..)` into `Ok(..)` 992 | if self.variant_name.startswith("Ok"): 993 | self.variant_name = "Ok" 994 | elif self.variant_name.startswith("Err"): 995 | self.variant_name = "Err" 996 | 997 | 998 | class CowSynthProvider(GenericEnumSynthProvider): 999 | def update(self): 1000 | super().update() 1001 | self.typename_summary = "" 1002 | # turn `Borrowed(..)` into `Borrowed(..)` 1003 | if self.variant_name.startswith("Borrowed"): 1004 | self.variant_name = "Borrowed" 1005 | elif self.variant_name.startswith("Owned"): 1006 | self.variant_name = "Owned" 1007 | 1008 | 1009 | class MsvcTupleSynthProvider(RustSynthProvider): 1010 | def update(self): 1011 | tparams = get_template_params(self.valobj.GetTypeName()) 1012 | self.type_name = '(' + ', '.join(tparams) + ')' 1013 | 1014 | def has_children(self): 1015 | return self.valobj.MightHaveChildren() 1016 | 1017 | def num_children(self): 1018 | return self.valobj.GetNumChildren() 1019 | 1020 | def get_child_at_index(self, index): 1021 | child = self.valobj.GetChildAtIndex(index) 1022 | return child.CreateChildAtOffset(str(index), 0, child.GetType()) 1023 | 1024 | def get_index_of_child(self, name): 1025 | return str(name) 1026 | 1027 | def get_summary(self): 1028 | return tuple_summary(self.valobj) 1029 | 1030 | def get_type_name(self): 1031 | return self.type_name 1032 | 1033 | 1034 | class MsvcEnumSynthProvider(EnumSynthProvider): 1035 | is_tuple_variant = False 1036 | 1037 | def update(self): 1038 | tparams = get_template_params(self.valobj.GetTypeName()) 1039 | if len(tparams) == 1: # Regular enum 1040 | discr = gcm(self.valobj, 'discriminant') 1041 | self.variant = gcm(self.valobj, 'variant' + 1042 | str(discr.GetValueAsUnsigned())) 1043 | variant_name = discr.GetValue() 1044 | else: # Niche enum 1045 | dataful_min = int(tparams[1]) 1046 | dataful_max = int(tparams[2]) 1047 | dataful_var = tparams[3] 1048 | discr = gcm(self.valobj, 'discriminant') 1049 | if dataful_min <= discr.GetValueAsUnsigned() <= dataful_max: 1050 | self.variant = gcm(self.valobj, 'dataful_variant') 1051 | variant_name = dataful_var 1052 | else: 1053 | variant_name = discr.GetValue() 1054 | 1055 | self.type_name = tparams[0] 1056 | 1057 | if self.variant.IsValid() and self.variant.GetNumChildren() > self.skip_first: 1058 | if self.variant.GetChildAtIndex(self.skip_first).GetName() == '__0': 1059 | self.is_tuple_variant = True 1060 | self.summary = variant_name + \ 1061 | tuple_summary(self.variant, skip_first=self.skip_first) 1062 | else: 1063 | self.summary = variant_name + '{...}' 1064 | else: 1065 | self.summary = variant_name 1066 | 1067 | def get_child_at_index(self, index): 1068 | child = self.variant.GetChildAtIndex(index + self.skip_first) 1069 | if self.is_tuple_variant: 1070 | return child.CreateChildAtOffset(str(index), 0, child.GetType()) 1071 | else: 1072 | return child 1073 | 1074 | def get_index_of_child(self, name): 1075 | if self.is_tuple_variant: 1076 | return int(name) 1077 | else: 1078 | return self.variant.GetIndexOfChildWithName(name) - self.skip_first 1079 | 1080 | def get_type_name(self): 1081 | return self.type_name 1082 | 1083 | 1084 | class MsvcEnum2SynthProvider(EnumSynthProvider): 1085 | is_tuple_variant = False 1086 | 1087 | def update(self): 1088 | tparams = get_template_params(self.valobj.GetTypeName()) 1089 | 1090 | if len(tparams) == 1: # Regular enum 1091 | discr = gcm(self.valobj, 'tag') 1092 | self.variant = gcm(self.valobj, 'variant' + 1093 | str(discr.GetValueAsUnsigned())).GetChildAtIndex(0) 1094 | else: # Niche enum 1095 | dataful_min = int(tparams[1]) 1096 | dataful_max = int(tparams[2]) 1097 | discr = gcm(self.valobj, 'tag') 1098 | if dataful_min <= discr.GetValueAsUnsigned() <= dataful_max: 1099 | self.variant = gcm(self.valobj, 'dataful_variant') 1100 | 1101 | names = re.split("::", self.variant.GetTypeName()) 1102 | variant_name = names[-1] 1103 | self.type_name = tparams[0] 1104 | 1105 | if self.variant.IsValid() and self.variant.GetNumChildren() > self.skip_first: 1106 | if self.variant.GetChildAtIndex(self.skip_first).GetName() == '__0': 1107 | self.is_tuple_variant = True 1108 | self.summary = variant_name + \ 1109 | tuple_summary(self.variant, skip_first=self.skip_first) 1110 | else: 1111 | self.summary = variant_name + " " + obj_summary(self.variant) 1112 | else: 1113 | self.summary = variant_name 1114 | 1115 | def get_summary(self): 1116 | return self.summary 1117 | 1118 | 1119 | class StdHashMapSynthProvider(RustSynthProvider): 1120 | def update(self): 1121 | self.initialize_table(gcm(self.valobj, 'base', 'table')) 1122 | 1123 | def initialize_table(self, table): 1124 | assert table.IsValid() 1125 | 1126 | if table.type.GetNumberOfTemplateArguments() > 0: 1127 | item_ty = table.type.GetTemplateArgumentType(0) 1128 | else: # we must be on windows-msvc - try to look up item type by name 1129 | table_ty_name = table.GetType().GetName() # "hashbrown::raw::RawTable" 1130 | item_ty_name = get_template_params(table_ty_name)[0] 1131 | item_ty = table.GetTarget().FindTypes(item_ty_name).GetTypeAtIndex(0) 1132 | 1133 | if item_ty.IsTypedefType(): 1134 | item_ty = item_ty.GetTypedefedType() 1135 | 1136 | inner_table = table.GetChildMemberWithName('table') 1137 | if inner_table.IsValid(): 1138 | self.initialize_hashbrown_v2( 1139 | inner_table, item_ty) # 1.52 <= std_version 1140 | else: 1141 | if not table.GetChildMemberWithName('data'): 1142 | self.initialize_hashbrown_v2( 1143 | table, item_ty) # ? <= std_version < 1.52 1144 | else: 1145 | self.initialize_hashbrown_v1( 1146 | table, item_ty) # 1.36 <= std_version < ? 1147 | 1148 | def initialize_hashbrown_v2(self, table, item_ty): 1149 | self.num_buckets = gcm(table, 'bucket_mask').GetValueAsUnsigned() + 1 1150 | ctrl_ptr = gcm(table, 'ctrl', 'pointer') 1151 | ctrl = ctrl_ptr.GetPointeeData(0, self.num_buckets) 1152 | # Buckets are located above `ctrl`, in reverse order. 1153 | start_addr = ctrl_ptr.GetValueAsUnsigned() - item_ty.GetByteSize() * \ 1154 | self.num_buckets 1155 | buckets_ty = item_ty.GetArrayType(self.num_buckets) 1156 | self.buckets = self.valobj.CreateValueFromAddress( 1157 | 'data', start_addr, buckets_ty) 1158 | error = lldb.SBError() 1159 | self.valid_indices = [] 1160 | for i in range(self.num_buckets): 1161 | if ctrl.GetUnsignedInt8(error, i) & 0x80 == 0: 1162 | self.valid_indices.append(self.num_buckets - 1 - i) 1163 | 1164 | def initialize_hashbrown_v1(self, table, item_ty): 1165 | self.num_buckets = gcm(table, 'bucket_mask').GetValueAsUnsigned() + 1 1166 | ctrl_ptr = gcm(table, 'ctrl', 'pointer') 1167 | ctrl = ctrl_ptr.GetPointeeData(0, self.num_buckets) 1168 | buckets_ty = item_ty.GetArrayType(self.num_buckets) 1169 | self.buckets = gcm( 1170 | table, 'data', 'pointer').Dereference().Cast(buckets_ty) 1171 | error = lldb.SBError() 1172 | self.valid_indices = [] 1173 | for i in range(self.num_buckets): 1174 | if ctrl.GetUnsignedInt8(error, i) & 0x80 == 0: 1175 | self.valid_indices.append(i) 1176 | 1177 | def has_children(self): 1178 | return True 1179 | 1180 | def num_children(self): 1181 | return len(self.valid_indices) 1182 | 1183 | def get_child_at_index(self, index): 1184 | bucket_idx = self.valid_indices[index] 1185 | item = self.buckets.GetChildAtIndex(bucket_idx) 1186 | item.SetPreferSyntheticValue(True) 1187 | v = item.CreateChildAtOffset('[%d]' % index, 0, item.GetType()) 1188 | v.SetPreferSyntheticValue(True) 1189 | return v 1190 | 1191 | def get_index_of_child(self, name): 1192 | return int(name.lstrip('[').rstrip(']')) 1193 | 1194 | def get_summary(self): 1195 | return 'size=%d, capacity=%d' % (self.num_children(), self.num_buckets) 1196 | 1197 | 1198 | class StdHashSetSynthProvider(StdHashMapSynthProvider): 1199 | def update(self): 1200 | table = gcm(self.valobj, 'base', 'map', 'table') # std_version >= 1.48 1201 | if not table.IsValid(): 1202 | table = gcm(self.valobj, 'map', 'base', 1203 | 'table') # std_version < 1.48 1204 | self.initialize_table(table) 1205 | 1206 | def get_child_at_index(self, index): 1207 | bucket_idx = self.valid_indices[index] 1208 | item = self.buckets.GetChildAtIndex(bucket_idx).GetChildAtIndex(0) 1209 | return item.CreateChildAtOffset('[%d]' % index, 0, item.GetType()) 1210 | 1211 | class CrossbeamAtomicCellSynthProvider(DerefSynthProvider): 1212 | def update(self): 1213 | self.deref = gcm(self.valobj, 'value', 'value', 'value', 'value') 1214 | self.deref.SetPreferSyntheticValue(True) 1215 | 1216 | 1217 | def __lldb_init_module(debugger_obj, internal_dict): # pyright: ignore 1218 | initialize_category(debugger_obj, internal_dict) 1219 | print(f"loaded rust-prettifier-for-lldb from {__file__}") 1220 | --------------------------------------------------------------------------------