-- Keybindings (qwerty). -- DESCRIPTION: -- All mappings are defined here. -- Sections: -- ## Base bindings -- -> icons displayed on which-key.nvim -- -> standard operations -- -> clipboard -- -> search highlighting -- -> improved tabulation -- -> improved gg -- -> packages -- -> buffers/tabs [buffers] -- -> ui toggles [ui] -- -> shifted movement keys -- -> cmdline autocompletion -- -> special cases -- ## Plugin bindings -- -> alpha-nvim -- -> git [git] -- -> file browsers -- -> session manager -- -> smart-splits.nvim -- -> aerial.nvim -- -> litee-calltree.nvim -- -> telescope.nvim [find] -- -> toggleterm.nvim -- -> dap.nvim [debugger] -- -> tests [tests] -- -> nvim-ufo -- -> code documentation [docs] -- -> ask chatgpt [neural] -- -> hop.nvim -- -> mason-lspconfig.nvim [lsp] -- -- KEYBINDINGS REFERENCE -- ------------------------------------------------------------------- -- | Mode | Norm | Ins | Cmd | Vis | Sel | Opr | Term | Lang | -- Command +------+-----+-----+-----+-----+-----+------+------+ -- [nore]map | yes | - | - | yes | yes | yes | - | - | -- n[nore]map | yes | - | - | - | - | - | - | - | -- [nore]map! | - | yes | yes | - | - | - | - | - | -- i[nore]map | - | yes | - | - | - | - | - | - | -- c[nore]map | - | - | yes | - | - | - | - | - | -- v[nore]map | - | - | - | yes | yes | - | - | - | -- x[nore]map | - | - | - | yes | - | - | - | - | -- s[nore]map | - | - | - | - | yes | - | - | - | -- o[nore]map | - | - | - | - | - | yes | - | - | -- t[nore]map | - | - | - | - | - | - | yes | - | -- l[nore]map | - | yes | yes | - | - | - | - | yes | -- ------------------------------------------------------------------- local M = {} local utils = require("base.utils") local get_icon = utils.get_icon local is_available = utils.is_available local ui = require("base.utils.ui") local maps = require("base.utils").get_mappings_template() local is_android = vim.fn.isdirectory('/data') == 1 -- true if on android -- ------------------------------------------------------------------------- -- -- ## Base bindings ######################################################## -- -- ------------------------------------------------------------------------- -- icons displayed on which-key.nvim --------------------------------------- local icons = { f = { desc = get_icon("Find", true) .. " Find" }, p = { desc = get_icon("Packages", true) .. " Packages" }, l = { desc = get_icon("LSP", true) .. " LSP" }, u = { desc = get_icon("UI", true) .. " UI" }, b = { desc = get_icon("Buffer", true) .. " Buffers" }, bs = { desc = get_icon("Sort", true) .. " Sort Buffers" }, c = { desc = get_icon("Run", true) .. " Compiler" }, d = { desc = get_icon("Debugger", true) .. " Debugger" }, tt = { desc = get_icon("Test", true) .. " Test" }, dc = { desc = get_icon("Docs", true) .. " Docs" }, g = { desc = get_icon("Git", true) .. " Git" }, S = { desc = get_icon("Session", true) .. " Session" }, t = { desc = get_icon("Terminal", true) .. " Terminal" }, } -- standard Operations ----------------------------------------------------- maps.n["j"] = { "v:count == 0 ? 'gj' : 'j'", expr = true, desc = "Move cursor down" } maps.n["k"] = { "v:count == 0 ? 'gk' : 'k'", expr = true, desc = "Move cursor up" } maps.n["w"] = { "w", desc = "Save" } maps.n["W"] = { function() vim.cmd("SudaWrite") end, desc = "Save as sudo" } maps.n["n"] = { "enew", desc = "New file" } maps.n["/"] = { "gcc", remap = true, desc = "Toggle comment line" } maps.x["/"] = { "gc", remap = true, desc = "Toggle comment" } maps.n["gx"] = { utils.open_with_program, desc = "Open the file under cursor with a program" } maps.n[""] = { "w!", desc = "Force write" } maps.n["|"] = { "vsplit", desc = "Vertical Split" } maps.n["\\"] = { "split", desc = "Horizontal Split" } maps.i[""] = { "", desc = "Enable CTRL+backsace to delete." } maps.n["0"] = { "^", desc = "Go to the fist character of the line (aliases 0 to ^)" } maps.n["q"] = { "confirm q", desc = "Quit" } maps.n["q"] = { function() -- Ask user for confirmation local choice = vim.fn.confirm("Do you really want to exit nvim?", "&Yes\n&No", 2) if choice == 1 then -- If user confirms, but there are still files to be saved: Ask vim.cmd('confirm quit') end end, desc = "Quit", } maps.n[""] = { "", noremap = true, silent = true, expr = false, desc = "FIX: Prevent TAB from behaving like , as they share the same internal code", } -- clipboard --------------------------------------------------------------- -- BUG: We disable these mappings on termux by default because -- is the keycode for scrolling, and remapping it would break it. if not is_android then -- only useful when the option clipboard is commented on ./1-options.lua maps.n[""] = { '"+y', desc = "Copy to cliboard" } maps.x[""] = { '"+y', desc = "Copy to cliboard" } maps.n[""] = { '"+ydd', desc = "Copy to clipboard and delete line" } maps.x[""] = { '"+ydd', desc = "Copy to clipboard and delete line" } maps.n[""] = { '"+p', desc = "Paste from clipboard" } end -- Make 'c' key not copy to clipboard when changing a character. maps.n["c"] = { '"_c', desc = "Change without yanking" } maps.n["C"] = { '"_C', desc = "Change without yanking" } maps.x["c"] = { '"_c', desc = "Change without yanking" } maps.x["C"] = { '"_C', desc = "Change without yanking" } -- Make 'x' key not copy to clipboard when deleting a character. maps.n["x"] = { -- Also let's allow 'x' key to delete blank lines in normal mode. function() if vim.fn.col "." == 1 then local line = vim.fn.getline "." if line:match "^%s*$" then vim.api.nvim_feedkeys('"_dd', "n", false) vim.api.nvim_feedkeys("$", "n", false) else vim.api.nvim_feedkeys('"_x', "n", false) end else vim.api.nvim_feedkeys('"_x', "n", false) end end, desc = "Delete character without yanking it", } maps.x["x"] = { '"_x', desc = "Delete all characters in line" } -- Same for shifted X maps.n["X"] = { -- Also let's allow 'x' key to delete blank lines in normal mode. function() if vim.fn.col "." == 1 then local line = vim.fn.getline "." if line:match "^%s*$" then vim.api.nvim_feedkeys('"_dd', "n", false) vim.api.nvim_feedkeys("$", "n", false) else vim.api.nvim_feedkeys('"_X', "n", false) end else vim.api.nvim_feedkeys('"_X', "n", false) end end, desc = "Delete before character without yanking it", } maps.x["X"] = { '"_X', desc = "Delete all characters in line" } -- Override nvim default behavior so it doesn't auto-yank when pasting on visual mode. maps.x["p"] = { "P", desc = "Paste content you've previourly yanked" } maps.x["P"] = { "p", desc = "Yank what you are going to override, then paste" } -- search highlighting ------------------------------------------------------ -- use ESC to clear hlsearch, while preserving its original functionality. -- -- TIP: If you prefer, use ENTER instead of -- to avoid triggering it by accident. maps.n[""] = { function() if vim.fn.hlexists("Search") then vim.cmd("nohlsearch") else vim.api.nvim_feedkeys( vim.api.nvim_replace_termcodes("", true, true, true), "n", true ) end end, } -- Improved tabulation ------------------------------------------------------ maps.x[""] = { ""] = { ">gv", desc = "indent line" } maps.x["<"] = { ""] = { ">gv", desc = "indent line" } -- improved gg -------------------------------------------------------------- maps.n["gg"] = { function() vim.g.minianimate_disable = true if vim.v.count > 0 then vim.cmd("normal! " .. vim.v.count .. "gg") else vim.cmd("normal! gg0") end vim.g.minianimate_disable = false end, desc = "gg and go to the first position", } maps.n["G"] = { function() vim.g.minianimate_disable = true vim.cmd("normal! G$") vim.g.minianimate_disable = false end, desc = "G and go to the last position", } maps.x["gg"] = { function() vim.g.minianimate_disable = true if vim.v.count > 0 then vim.cmd("normal! " .. vim.v.count .. "gg") else vim.cmd("normal! gg0") end vim.g.minianimate_disable = false end, desc = "gg and go to the first position (visual)", } maps.x["G"] = { function() vim.g.minianimate_disable = true vim.cmd("normal! G$") vim.g.minianimate_disable = false end, desc = "G and go to the last position (visual)", } maps.n[""] = { -- to move to the previous position press ctrl + oo function() vim.g.minianimate_disable = true vim.cmd("normal! gg0vG$") vim.g.minianimate_disable = false end, desc = "Visually select all", } -- packages ----------------------------------------------------------------- -- lazy maps.n["p"] = icons.p maps.n["pu"] = { function() require("lazy").check() end, desc = "Lazy open" } maps.n["pU"] = { function() require("lazy").update() end, desc = "Lazy update" } -- mason if is_available("mason.nvim") then maps.n["pm"] = { "Mason", desc = "Mason open" } maps.n["pM"] = { "MasonUpdateAll", desc = "Mason update" } end -- treesitter if is_available("nvim-treesitter") then maps.n["pT"] = { "TSUpdate", desc = "Treesitter update" } maps.n["pt"] = { "TSInstallInfo", desc = "Treesitter open" } end -- nvim updater maps.n["pD"] = { "DistroUpdate", desc = "Distro update" } maps.n["pv"] = { "DistroReadVersion", desc = "Distro version" } maps.n["pc"] = { "DistroReadChangelog", desc = "Distro changelog" } -- buffers/tabs [buffers ]-------------------------------------------------- maps.n["c"] = { -- Close window and buffer at the same time. function() require("heirline-components.buffer").wipe() end, desc = "Wipe buffer", } maps.n["C"] = { -- Close buffer keeping the window. function() require("heirline-components.buffer").close() end, desc = "Close buffer", } maps.n["bw"] = { -- Closes the window function() vim.cmd("silent! close") -- Be aware you can't close the last window end, desc = "Close window", } -- Close buffer keeping the window → Without confirmation. -- maps.n["X"] = { -- function() require("heirline-components.buffer").close(0, true) end, -- desc = "Force close buffer", -- maps.n["ba"] = { function() vim.cmd("wa") end, desc = "Write all changed buffers", } maps.n["]b"] = { function() require("heirline-components.buffer").nav(vim.v.count > 0 and vim.v.count or 1) end, desc = "Next buffer", } maps.n["[b"] = { function() require("heirline-components.buffer").nav(-(vim.v.count > 0 and vim.v.count or 1)) end, desc = "Previous buffer", } maps.n[">b"] = { function() require("heirline-components.buffer").move(vim.v.count > 0 and vim.v.count or 1) end, desc = "Move buffer tab right", } maps.n[" 0 and vim.v.count or 1)) end, desc = "Move buffer tab left", } maps.n["b"] = icons.b maps.n["bc"] = { function() require("heirline-components.buffer").close_all(true) end, desc = "Close all buffers except current", } maps.n["bC"] = { function() require("heirline-components.buffer").close_all() end, desc = "Close all buffers", } maps.n["bb"] = { function() require("heirline-components.all").heirline.buffer_picker( function(bufnr) vim.api.nvim_win_set_buf(0, bufnr) end ) end, desc = "Select buffer from tabline", } maps.n["bd"] = { function() require("heirline-components.all").heirline.buffer_picker( function(bufnr) require("heirline-components.buffer").close(bufnr) end ) end, desc = "Delete buffer from tabline", } maps.n["bl"] = { function() require("heirline-components.buffer").close_left() end, desc = "Close all buffers to the left", } maps.n["br"] = { function() require("heirline-components.buffer").close_right() end, desc = "Close all buffers to the right", } maps.n["bs"] = icons.bs maps.n["bse"] = { function() require("heirline-components.buffer").sort "extension" end, desc = "Sort by extension (buffers)", } maps.n["bsr"] = { function() require("heirline-components.buffer").sort "unique_path" end, desc = "Sort by relative path (buffers)", } maps.n["bsp"] = { function() require("heirline-components.buffer").sort "full_path" end, desc = "Sort by full path (buffers)", } maps.n["bsi"] = { function() require("heirline-components.buffer").sort "bufnr" end, desc = "Sort by buffer number (buffers)", } maps.n["bsm"] = { function() require("heirline-components.buffer").sort "modified" end, desc = "Sort by modification (buffers)", } maps.n["b\\"] = { function() require("heirline-components.all").heirline.buffer_picker(function(bufnr) vim.cmd.split() vim.api.nvim_win_set_buf(0, bufnr) end) end, desc = "Horizontal split buffer from tabline", } maps.n["b|"] = { function() require("heirline-components.all").heirline.buffer_picker(function(bufnr) vim.cmd.vsplit() vim.api.nvim_win_set_buf(0, bufnr) end) end, desc = "Vertical split buffer from tabline", } -- quick buffer switching maps.n[""] = { function() require("heirline-components.buffer").nav(vim.v.count > 0 and vim.v.count or 1) end, desc = "Next buffer", } maps.n[""] = { function() require("heirline-components.buffer").nav(-(vim.v.count > 0 and vim.v.count or 1)) end, desc = "Previous buffer", } -- tabs maps.n["]t"] = { function() vim.cmd.tabnext() end, desc = "Next tab" } maps.n["[t"] = { function() vim.cmd.tabprevious() end, desc = "Previous tab" } -- zen mode if is_available("zen-mode.nvim") then maps.n["uz"] = { function() ui.toggle_zen_mode() end, desc = "Zen mode" } end -- ui toggles [ui] --------------------------------------------------------- maps.n["u"] = icons.u if is_available("nvim-autopairs") then maps.n["ua"] = { ui.toggle_autopairs, desc = "Autopairs" } end maps.n["ub"] = { ui.toggle_background, desc = "Background" } if is_available("nvim-cmp") then maps.n["uc"] = { ui.toggle_cmp, desc = "Autocompletion" } end if is_available("nvim-highlight-colors") then maps.n["uC"] = { "HighlightColors toggle", desc = "Color highlight" } end maps.n["ud"] = { ui.toggle_diagnostics, desc = "Diagnostics" } maps.n["uD"] = { ui.set_indent, desc = "Change indent setting" } maps.n["ug"] = { ui.toggle_signcolumn, desc = "Signcolumn" } maps.n["ul"] = { ui.toggle_statusline, desc = "Statusline" } maps.n["un"] = { ui.change_number, desc = "Change line numbering" } maps.n["uP"] = { ui.toggle_paste, desc = "Paste mode" } maps.n["us"] = { ui.toggle_spell, desc = "Spellcheck" } maps.n["uS"] = { ui.toggle_conceal, desc = "Conceal" } maps.n["ut"] = { ui.toggle_tabline, desc = "Tabline" } maps.n["uu"] = { ui.toggle_url_effect, desc = "URL highlight" } maps.n["uw"] = { ui.toggle_wrap, desc = "Wrap" } maps.n["uy"] = { ui.toggle_buffer_syntax, desc = "Syntax highlight (buffer)" } maps.n["uh"] = { ui.toggle_foldcolumn, desc = "Foldcolumn" } maps.n["uN"] = { ui.toggle_ui_notifications, desc = "UI notifications" } if is_available("lsp_signature.nvim") then maps.n["up"] = { ui.toggle_lsp_signature, desc = "LSP signature" } end if is_available("mini.animate") then maps.n["uA"] = { ui.toggle_animations, desc = "Animations" } end -- shifted movement keys ---------------------------------------------------- maps.n[""] = { function() vim.api.nvim_feedkeys("7j", "n", true) end, desc = "Fast move down", } maps.n[""] = { function() vim.api.nvim_feedkeys("7k", "n", true) end, desc = "Fast move up", } maps.n[""] = { function() local current_line = vim.fn.line "." local total_lines = vim.fn.line "$" local target_line = current_line + 1 + math.floor(total_lines * 0.20) if target_line > total_lines then target_line = total_lines end vim.api.nvim_win_set_cursor(0, { target_line, 0 }) vim.cmd("normal! zz") end, desc = "Page down exactly a 20% of the total size of the buffer", } maps.n[""] = { function() local current_line = vim.fn.line "." local target_line = current_line - 1 - math.floor(vim.fn.line "$" * 0.20) if target_line < 1 then target_line = 1 end vim.api.nvim_win_set_cursor(0, { target_line, 0 }) vim.cmd("normal! zz") end, desc = "Page up exactly 20% of the total size of the buffer", } -- cmdline autocompletion --------------------------------------------------- maps.c[""] = { function() return vim.fn.wildmenumode() == 1 and "" or "" end, noremap = true, expr = true, desc = "Wildmenu fix for neovim bug #9953", } maps.c[""] = { function() return vim.fn.wildmenumode() == 1 and "" or "" end, noremap = true, expr = true, desc = "Wildmenu fix for neovim bug #9953", } maps.c[""] = { function() return vim.fn.wildmenumode() == 1 and "" or "" end, noremap = true, expr = true, desc = "Wildmenu fix for neovim bug #9953", } maps.c[""] = { function() return vim.fn.wildmenumode() == 1 and "" or "" end, noremap = true, expr = true, desc = "Wildmenu fix for neovim bug #9953", } -- special cases ------------------------------------------------------------ vim.api.nvim_create_autocmd("BufWinEnter", { desc = "Make q close help, man, quickfix, dap floats", callback = function(args) local buftype = vim.api.nvim_get_option_value("buftype", { buf = args.buf }) if vim.tbl_contains({ "help", "nofile", "quickfix" }, buftype) then vim.keymap.set( "n", "q", "close", { buffer = args.buf, silent = true, nowait = true } ) end end, }) vim.api.nvim_create_autocmd("CmdwinEnter", { desc = "Make q close command history (q: and q?)", callback = function(args) vim.keymap.set( "n", "q", "close", { buffer = args.buf, silent = true, nowait = true } ) end, }) -- ------------------------------------------------------------------------- -- -- ## Plugin bindings -- -- ------------------------------------------------------------------------- -- alpha-nvim -------------------------------------------------------------- if is_available("alpha-nvim") then maps.n["h"] = { function() local wins = vim.api.nvim_tabpage_list_wins(0) if #wins > 1 and vim.api.nvim_get_option_value("filetype", {}) == "neo-tree" then vim.fn.win_gotoid(wins[2]) -- go to non-neo-tree window to toggle alpha end require("alpha").start(false, require("alpha").default_config) vim.b.miniindentscope_disable = true end, desc = "Home screen", } end -- [git] ----------------------------------------------------------- -- gitsigns.nvim maps.n["g"] = icons.g if is_available("gitsigns.nvim") then maps.n["g"] = icons.g maps.n["]g"] = { function() require("gitsigns").nav_hunk('next') end, desc = "Next Git hunk" } maps.n["[g"] = { function() require("gitsigns").nav_hunk('prev') end, desc = "Previous Git hunk", } maps.n["gl"] = { function() require("gitsigns").blame_line() end, desc = "View Git blame", } maps.n["gL"] = { function() require("gitsigns").blame_line { full = true } end, desc = "View full Git blame", } maps.n["gp"] = { function() require("gitsigns").preview_hunk() end, desc = "Preview Git hunk", } maps.n["gh"] = { function() require("gitsigns").reset_hunk() end, desc = "Reset Git hunk", } maps.n["gr"] = { function() require("gitsigns").reset_buffer() end, desc = "Reset Git buffer", } maps.n["gs"] = { function() require("gitsigns").stage_hunk() end, desc = "Stage Git hunk", } maps.n["gS"] = { function() require("gitsigns").stage_buffer() end, desc = "Stage Git buffer", } maps.n["gu"] = { function() require("gitsigns").undo_stage_hunk() end, desc = "Unstage Git hunk", } maps.n["gd"] = { function() require("gitsigns").diffthis() end, desc = "View Git diff", } end -- git fugitive if is_available("vim-fugitive") then maps.n["gP"] = { function() vim.cmd(":GBrowse") end, desc = "Open in github ", } end -- git client if vim.fn.executable "lazygit" == 1 then -- if lazygit exists, show it maps.n["gg"] = { function() local git_dir = vim.fn.finddir(".git", vim.fn.getcwd() .. ";") if git_dir ~= "" then vim.cmd("TermExec cmd='lazygit && exit'") else utils.notify("Not a git repository", vim.log.levels.WARN) end end, desc = "ToggleTerm lazygit", } end if vim.fn.executable "gitui" == 1 then -- if gitui exists, show it maps.n["gg"] = { function() local git_dir = vim.fn.finddir(".git", vim.fn.getcwd() .. ";") if git_dir ~= "" then if vim.fn.executable "keychain" == 1 then vim.cmd('TermExec cmd="eval `keychain --eval ~/.ssh/github.key` && gitui && exit"') else vim.cmd("TermExec cmd='gitui && exit'") end else utils.notify("Not a git repository", vim.log.levels.WARN) end end, desc = "ToggleTerm gitui", } end -- file browsers ------------------------------------ -- yazi if is_available("yazi.nvim") and vim.fn.executable("yazi") == 1 then maps.n["r"] = { -- TODO: use 'Yazi toggle' instead once yazi v0.4.0 is released. "Yazi", desc = "File browser", } end -- neotree if is_available("neo-tree.nvim") then maps.n["e"] = { "Neotree toggle", desc = "Neotree" } end -- session manager --------------------------------------------------------- if is_available("neovim-session-manager") then maps.n["S"] = icons.S maps.n["Sl"] = { "SessionManager! load_last_session", desc = "Load last session", } maps.n["Ss"] = { "SessionManager! save_current_session", desc = "Save this session", } maps.n["Sd"] = { "SessionManager! delete_session", desc = "Delete session" } maps.n["Sf"] = { "SessionManager! load_session", desc = "Search sessions" } maps.n["S."] = { "SessionManager! load_current_dir_session", desc = "Load current directory session", } end if is_available("resession.nvim") then maps.n["S"] = icons.S maps.n["Sl"] = { function() require("resession").load "Last Session" end, desc = "Load last session", } maps.n["Ss"] = { function() require("resession").save() end, desc = "Save this session" } maps.n["St"] = { function() require("resession").save_tab() end, desc = "Save this tab's session", } maps.n["Sd"] = { function() require("resession").delete() end, desc = "Delete a session" } maps.n["Sf"] = { function() require("resession").load() end, desc = "Load a session" } maps.n["S."] = { function() require("resession").load(vim.fn.getcwd(), { dir = "dirsession" }) end, desc = "Load current directory session", } end -- smart-splits.nvim if is_available("smart-splits.nvim") then maps.n[""] = { function() require("smart-splits").move_cursor_left() end, desc = "Move to left split", } maps.n[""] = { function() require("smart-splits").move_cursor_down() end, desc = "Move to below split", } maps.n[""] = { function() require("smart-splits").move_cursor_up() end, desc = "Move to above split", } maps.n[""] = { function() require("smart-splits").move_cursor_right() end, desc = "Move to right split", } maps.n[""] = { function() require("smart-splits").resize_up() end, desc = "Resize split up", } maps.n[""] = { function() require("smart-splits").resize_down() end, desc = "Resize split down", } maps.n[""] = { function() require("smart-splits").resize_left() end, desc = "Resize split left", } maps.n[""] = { function() require("smart-splits").resize_right() end, desc = "Resize split right", } else maps.n[""] = { "h", desc = "Move to left split" } maps.n[""] = { "j", desc = "Move to below split" } maps.n[""] = { "k", desc = "Move to above split" } maps.n[""] = { "l", desc = "Move to right split" } maps.n[""] = { "resize -2", desc = "Resize split up" } maps.n[""] = { "resize +2", desc = "Resize split down" } maps.n[""] = { "vertical resize -2", desc = "Resize split left" } maps.n[""] = { "vertical resize +2", desc = "Resize split right" } end -- aerial.nvimm ------------------------------------------------------------ if is_available("aerial.nvim") then maps.n["i"] = { function() require("aerial").toggle() end, desc = "Aerial" } end -- letee-calltree.nvimm ------------------------------------------------------------ if is_available("litee-calltree.nvim") then -- For every buffer, look for the one with filetype "calltree" and focus it. local calltree_delay = 1500 -- first run? wait a bit longer. local function focus_calltree() -- Note: No go to the previous cursor position, press ctrl+i / ctrl+o vim.defer_fn(function() for _, win in ipairs(vim.api.nvim_list_wins()) do local buf = vim.api.nvim_win_get_buf(win) local ft = vim.api.nvim_get_option_value('filetype', { buf = buf }) if ft == "calltree" then vim.api.nvim_set_current_win(win) return true end end end, calltree_delay) calltree_delay = 100 end maps.n["gj"] = { function() vim.lsp.buf.incoming_calls() focus_calltree() end, desc = "Call tree (incoming)" } maps.n["gJ"] = { function() vim.lsp.buf.outgoing_calls() focus_calltree() end, desc = "Call tree (outgoing)" } end -- telescope.nvim [find] ---------------------------------------------------- if is_available("telescope.nvim") then maps.n["f"] = icons.f maps.n["gb"] = { function() require("telescope.builtin").git_branches() end, desc = "Git branches", } maps.n["gc"] = { function() require("telescope.builtin").git_commits() end, desc = "Git commits (repository)" } maps.n["gC"] = { function() require("telescope.builtin").git_bcommits() end, desc = "Git commits (current file)" } maps.n["gt"] = { function() require("telescope.builtin").git_status() end, desc = "Git status", } maps.n["f"] = { function() require("telescope.builtin").resume() end, desc = "Resume previous search", } maps.n["f'"] = { function() require("telescope.builtin").marks() end, desc = "Find marks", } maps.n["fa"] = { function() local cwd = vim.fn.stdpath "config" .. "/.." local search_dirs = { vim.fn.stdpath "config" } if #search_dirs == 1 then cwd = search_dirs[1] end -- if only one directory, focus cwd require("telescope.builtin").find_files { prompt_title = "Config Files", search_dirs = search_dirs, cwd = cwd, follow = true, } -- call telescope end, desc = "Find nvim config files", } maps.n["fB"] = { function() require("telescope.builtin").buffers() end, desc = "Find buffers", } maps.n["fw"] = { function() require("telescope.builtin").grep_string() end, desc = "Find word under cursor in project", } maps.n["fC"] = { function() require("telescope.builtin").commands() end, desc = "Find commands", } -- Let's disable this. It is way too imprecise. Use rnvimr instead. -- maps.n["ff"] = { -- function() -- require("telescope.builtin").find_files { hidden = true, no_ignore = true } -- end, -- desc = "Find all files", -- } -- maps.n["fF"] = { -- function() require("telescope.builtin").find_files() end, -- desc = "Find files (no hidden)", -- } maps.n["fh"] = { function() require("telescope.builtin").help_tags() end, desc = "Find help", } maps.n["fk"] = { function() require("telescope.builtin").keymaps() end, desc = "Find keymaps", } maps.n["fm"] = { function() require("telescope.builtin").man_pages() end, desc = "Find man", } if is_available("nvim-notify") then maps.n["fn"] = { function() require("telescope").extensions.notify.notify() end, desc = "Find notifications", } end maps.n["fo"] = { function() require("telescope.builtin").oldfiles() end, desc = "Find recent", } maps.n["fv"] = { function() require("telescope.builtin").registers() end, desc = "Find vim registers", } maps.n["ft"] = { function() -- load color schemes before listing them pcall(vim.api.nvim_command, "doautocmd User LoadColorSchemes") -- Open telescope pcall(require("telescope.builtin").colorscheme, { enable_preview = true, ignore_builtins = true }) end, desc = "Find themes", } maps.n["ff"] = { function() require("telescope.builtin").live_grep({ additional_args = function(args) args.additional_args = { "--hidden", "--no-ignore" } return args.additional_args end, }) end, desc = "Find words in project", } maps.n["fF"] = { function() require("telescope.builtin").live_grep() end, desc = "Find words in project (no hidden)", } maps.n["f/"] = { function() require("telescope.builtin").current_buffer_fuzzy_find() end, desc = "Find words in current buffer", } -- Some lsp keymappings are here because they depend on telescope maps.n["l"] = icons.l maps.n["ls"] = { function() local aerial_avail, _ = pcall(require, "aerial") if aerial_avail then require("telescope").extensions.aerial.aerial() else require("telescope.builtin").lsp_document_symbols() end end, desc = "Search symbol in buffer", -- Useful to find every time a variable is assigned. } maps.n["gs"] = { function() local aerial_avail, _ = pcall(require, "aerial") if aerial_avail then require("telescope").extensions.aerial.aerial() else require("telescope.builtin").lsp_document_symbols() end end, desc = "Search symbol in buffer", -- Useful to find every time a variable is assigned. } -- extra - project.nvim if is_available("project.nvim") then maps.n["fp"] = { function() vim.cmd("Telescope projects") end, desc = "Find project", } end -- extra - spectre.nvim (search and replace in project) if is_available("nvim-spectre") then maps.n["fr"] = { function() require("spectre").toggle() end, desc = "Find and replace word in project", } maps.n["fb"] = { function() require("spectre").toggle { path = vim.fn.expand "%:t:p" } end, desc = "Find and replace word in buffer", } end -- extra - luasnip if is_available("LuaSnip") and is_available("telescope-luasnip.nvim") then maps.n["fs"] = { function() require("telescope").extensions.luasnip.luasnip {} end, desc = "Find snippets", } end -- extra - nvim-neoclip (neovim internal clipboard) -- Specially useful if you disable the shared clipboard in options. if is_available("nvim-neoclip.lua") then maps.n["fy"] = { function() require("telescope").extensions.neoclip.default() end, desc = "Find yank history", } maps.n["fq"] = { function() require("telescope").extensions.macroscope.default() end, desc = "Find macro history", } end -- extra - undotree if is_available("telescope-undo.nvim") then maps.n["fu"] = { function() require("telescope").extensions.undo.undo() end, desc = "Find in undo tree", } end -- extra - compiler if is_available("compiler.nvim") and is_available("overseer.nvim") then maps.n["m"] = icons.c maps.n["mm"] = { function() vim.cmd("CompilerOpen") end, desc = "Open compiler", } maps.n["mr"] = { function() vim.cmd("CompilerRedo") end, desc = "Compiler redo", } maps.n["mt"] = { function() vim.cmd("CompilerToggleResults") end, desc = "compiler results", } maps.n[""] = { function() vim.cmd("CompilerOpen") end, desc = "Open compiler", } maps.n[""] = { function() vim.cmd("CompilerRedo") end, desc = "Compiler redo", } maps.n[""] = { function() vim.cmd("CompilerToggleResults") end, desc = "compiler resume", } end end -- toggleterm.nvim ---------------------------------------------------------- if is_available("toggleterm.nvim") then maps.n["t"] = icons.t maps.n["tt"] = { "ToggleTerm direction=float", desc = "ToggleTerm float" } maps.n["th"] = { "ToggleTerm size=10 direction=horizontal", desc = "Toggleterm horizontal split", } maps.n["tv"] = { "ToggleTerm size=80 direction=vertical", desc = "Toggleterm vertical split", } maps.n[""] = { "ToggleTerm", desc = "terminal" } maps.t[""] = maps.n[""] maps.n[""] = maps.n[""] -- requires terminal that supports binding maps.t[""] = maps.n[""] -- requires terminal that supports binding end -- extra - improved terminal navigation maps.t[""] = { "wincmd h", desc = "Terminal left window navigation" } maps.t[""] = { "wincmd j", desc = "Terminal down window navigation" } maps.t[""] = { "wincmd k", desc = "Terminal up window navigation" } maps.t[""] = { "wincmd l", desc = "Terminal right window navigation" } -- dap.nvim [debugger] ----------------------------------------------------- -- Depending your terminal some F keys may not work. To fix it: -- modified function keys found with `showkey -a` in the terminal to get key code -- run `nvim -V3log +quit` and search through the "Terminal info" in the `log` file for the correct keyname if is_available("nvim-dap") then maps.n["d"] = icons.d maps.x["d"] = icons.d -- F keys maps.n[""] = { function() require("dap").continue() end, desc = "Debugger: Start" } maps.n[""] = { function() require("dap").terminate() end, desc = "Debugger: Stop" } maps.n[""] = { function() require("dap").restart_frame() end, desc = "Debugger: Restart" } maps.n[""] = { function() require("dap").toggle_breakpoint() end, desc = "Debugger: Toggle Breakpoint" } maps.n[""] = { function() vim.ui.input({ prompt = "Condition: " }, function(condition) if condition then require("dap").set_breakpoint(condition) end end) end, desc = "Debugger: Conditional Breakpoint", } maps.n[""] = { function() require("dap").step_over() end, desc = "Debugger: Step Over" } maps.n[""] = { function() require("dap").step_back() end, desc = "Debugger: Step Back" } maps.n[""] = { function() require("dap").step_into() end, desc = "Debugger: Step Into" } maps.n[""] = { function() require("dap").step_out() end, desc = "Debugger: Step Out" } -- Space + d maps.n["db"] = { function() require("dap").toggle_breakpoint() end, desc = "Breakpoint (F9)", } maps.n["dB"] = { function() require("dap").clear_breakpoints() end, desc = "Clear Breakpoints", } maps.n["dc"] = { function() require("dap").continue() end, desc = "Start/Continue (F5)" } maps.n["dC"] = { function() vim.ui.input({ prompt = "Condition: " }, function(condition) if condition then require("dap").set_breakpoint(condition) end end) end, desc = "Conditional Breakpoint (S-F9)", } maps.n["do"] = { function() require("dap").step_over() end, desc = "Step Over (F10)" } maps.n["do"] = { function() require("dap").step_back() end, desc = "Step Back (S-F10)" } maps.n["db"] = { function() require("dap").step_into() end, desc = "Step Into (F11)" } maps.n["dO"] = { function() require("dap").step_out() end, desc = "Step Out (S-F11)" } maps.n["dq"] = { function() require("dap").close() end, desc = "Close Session" } maps.n["dQ"] = { function() require("dap").terminate() end, desc = "Terminate Session (S-F5)", } maps.n["dp"] = { function() require("dap").pause() end, desc = "Pause" } maps.n["dr"] = { function() require("dap").restart_frame() end, desc = "Restart (C-F5)" } maps.n["dR"] = { function() require("dap").repl.toggle() end, desc = "REPL" } maps.n["ds"] = { function() require("dap").run_to_cursor() end, desc = "Run To Cursor" } if is_available("nvim-dap-ui") then maps.n["dE"] = { function() vim.ui.input({ prompt = "Expression: " }, function(expr) if expr then require("dapui").eval(expr, { enter = true }) end end) end, desc = "Evaluate Input", } maps.x["dE"] = { function() require("dapui").eval() end, desc = "Evaluate Input" } maps.n["du"] = { function() require("dapui").toggle() end, desc = "Debugger UI" } maps.n["dh"] = { function() require("dap.ui.widgets").hover() end, desc = "Debugger Hover", } end end -- testing [tests] ------------------------------------------------- -- neotest maps.n["T"] = icons.tt maps.x["T"] = icons.tt if is_available("neotest") then maps.n["Tu"] = { function() require("neotest").run.run() end, desc = "Unit", } maps.n["Ts"] = { function() require("neotest").run.stop() end, desc = "Stop unit", } maps.n["Tf"] = { function() require("neotest").run.run(vim.fn.expand "%") end, desc = "File", } maps.n["Td"] = { function() require("neotest").run.run { strategy = "dap" } end, desc = "Unit in debugger", } maps.n["Tt"] = { function() require("neotest").summary.toggle() end, desc = "Neotest summary", } maps.n["TT"] = { function() require("neotest").output_panel.toggle() end, desc = "Output panel", } end -- Extra - nvim-coverage -- Your project must generate coverage/lcov.info for this to work. -- -- On jest, make sure your packages.json file has this: -- "test": "jest --coverage" -- -- If you use other framework or language, refer to nvim-coverage docs: -- https://github.com/andythigpen/nvim-coverage/blob/main/doc/nvim-coverage.txt if is_available("nvim-coverage") then maps.n["Tc"] = { function() require("coverage").load(false) require("coverage").summary() end, desc = "Coverage", } maps.n["TC"] = { function() ui.toggle_coverage_signs() end, desc = "Coverage signs (toggle)", } end -- Extra - nodejs testing commands maps.n["Ta"] = { function() vim.cmd("TestNodejs") end, desc = "All", } maps.n["Te"] = { function() vim.cmd("TestNodejsE2e") end, desc = "E2e", } -- nvim-ufo [code folding] -------------------------------------------------- if is_available("nvim-ufo") then maps.n["zR"] = { function() require("ufo").openAllFolds() end, desc = "Open all folds" } maps.n["zM"] = { function() require("ufo").closeAllFolds() end, desc = "Close all folds" } maps.n["zr"] = { function() require("ufo").openFoldsExceptKinds() end, desc = "Fold less", } maps.n["zm"] = { function() require("ufo").closeFoldsWith() end, desc = "Fold more" } maps.n["zp"] = { function() require("ufo").peekFoldedLinesUnderCursor() end, desc = "Peek fold", } maps.n["zn"] = { function() require("ufo").openFoldsExceptKinds({ 'comment' }) end, desc = "Fold comments" } maps.n["zN"] = { function() require("ufo").openFoldsExceptKinds({ 'region' }) end, desc = "Fold region" } end -- code docmentation [docs] ------------------------------------------------- if is_available("markdown-preview.nvim") or is_available("markmap.nvim") or is_available("dooku.nvim") then maps.n["D"] = icons.dc -- Markdown preview if is_available("markdown-preview.nvim") then maps.n["Dp"] = { function() vim.cmd("silent! MarkdownPreview") end, desc = "Markdown preview", } end -- Markdown Mindmap if is_available("markmap.nvim") then maps.n["Dm"] = { function() if is_android then vim.cmd("MarkmapWatch") else vim.cmd("MarkmapOpen") end end, desc = "Markmap", } end if is_available("dooku.nvim") then maps.n["Dd"] = { function() vim.cmd(":DookuGenerate") end, desc = "Open documentation", } end end -- [neural] ----------------------------------------------------------------- if is_available("neural") or is_available("copilot") then maps.n["a"] = { function() require("neural").prompt() end, desc = "Ask chatgpt", } end -- hop.nvim ---------------------------------------------------------------- if is_available("hop.nvim") then -- Note that Even though we are using ENTER for hop, you can still select items -- from special menus like 'quickfix', 'q?' and 'q:' with . maps.n[""] = { -- The terminal undersand C-m and ENTER as the same key. function() require("hop") vim.cmd("silent! HopWord") end, desc = "Hop to word", } maps.x[""] = { -- The terminal undersand C-m and ENTER as the same key. function() require("hop") vim.cmd("silent! HopWord") end, desc = "Hop to word", } end -- mason-lspconfig.nvim [lsp] ------------------------------------------------- -- WARNING: Don't delete this section, or you won't have LSP keymappings. --A function we call from the script to start lsp. --@return table lsp_mappings function M.lsp_mappings(client, bufnr) -- Helper function to check if a lsp client implements a certain method. -- -- Wrapper for `client:supports_method()` to avoid code repetition. -- @param capability string The server capability to check for (example: "documentFormattingProvider"). -- @param filter? vim.lsp.get_clients.filter|nil A valid get_clients filter (see function docs). -- @return boolean # `true` if any of the clients provide the capability. local function supports_method(method, filter) -- default filter: current buffer. if not filter then filter = { bufnr = bufnr } end for _, lsp_client in ipairs(vim.lsp.get_clients(filter)) do if lsp_client:supports_method(method) then return true end end return false end local lsp_mappings = require("base.utils").get_mappings_template() -- Diagnostics lsp_mappings.n["ld"] = { function() vim.diagnostic.open_float() end, desc = "Hover diagnostics" } lsp_mappings.n["[d"] = { function() vim.diagnostic.jump({ count = -1 }) end, desc = "Previous diagnostic", } lsp_mappings.n["]d"] = { function() vim.diagnostic.jump({ count = 1 }) end, desc = "Next diagnostic", } -- Diagnostics lsp_mappings.n["gl"] = { function() vim.diagnostic.open_float() end, desc = "Hover diagnostics" } if is_available("telescope.nvim") then lsp_mappings.n["lD"] = { function() require("telescope.builtin").diagnostics() end, desc = "Diagnostics" } end -- LSP info if is_available("mason-lspconfig.nvim") then lsp_mappings.n["li"] = { "LspInfo", desc = "LSP information" } end if is_available("none-ls.nvim") then lsp_mappings.n["lI"] = { "NullLsInfo", desc = "None-ls information" } end -- Code actions lsp_mappings.n["la"] = { function() vim.lsp.buf.code_action() end, desc = "LSP code action", } lsp_mappings.v["la"] = lsp_mappings.n["la"] -- Codelens utils.add_autocmds_to_buffer("lsp_codelens_refresh", bufnr, { events = { "InsertLeave" }, desc = "Refresh codelens", callback = function(args) if supports_method("textDocument/codeLens") then if vim.g.codelens_enabled then vim.lsp.codelens.refresh({ bufnr = args.buf }) end end end, }) if supports_method("textDocument/codeLens") then -- on LspAttach if vim.g.codelens_enabled then vim.lsp.codelens.refresh({ bufnr = 0 }) end end lsp_mappings.n["ll"] = { function() vim.lsp.codelens.run() vim.lsp.codelens.refresh({ bufnr = 0 }) end, desc = "LSP CodeLens run", } lsp_mappings.n["uL"] = { function() ui.toggle_codelens() end, desc = "CodeLens", } -- Formatting (keymapping) local format_opts = { format_on_save = { enabled = vim.g.autoformat_enabled or false }, disabled = {} -- You can disable formatting for desired lsp clients. } lsp_mappings.n["lf"] = { function() vim.lsp.buf.format(format_opts) vim.cmd("checktime") -- Sync buffer with changes end, desc = "Format buffer", } lsp_mappings.v["lf"] = lsp_mappings.n["lf"] -- Formatting (command) vim.api.nvim_buf_create_user_command( bufnr, "Format", function() vim.lsp.buf.format(format_opts) end, { desc = "Format file with LSP" } ) -- Autoformatting (autocmd) utils.add_autocmds_to_buffer("lsp_auto_format", bufnr, { events = "BufWritePre", -- Trigger before save desc = "Autoformat on save", callback = function() -- guard clause: supports_method if not supports_method("textDocument/formatting", { bufnr = bufnr }) then utils.del_autocmds_from_buffer("lsp_auto_format", bufnr) return end -- Get autoformat setting (buffer or global) local autoformat_enabled = vim.b.autoformat_enabled or vim.g.autoformat_enabled local has_no_filter = not format_opts.filter local passes_filter = format_opts.filter and format_opts.filter(bufnr) -- Use these variables in the if condition if autoformat_enabled and (has_no_filter or passes_filter) then local affected_bufnr = vim.tbl_deep_extend("force", format_opts, { bufnr = bufnr }) vim.lsp.buf.format(affected_bufnr) end end, }) -- Key mappings for toggling autoformat (buffer/global) lsp_mappings.n["uf"] = { function() require("base.utils.ui").toggle_buffer_autoformat() end, desc = "Autoformat (buffer)", } lsp_mappings.n["uF"] = { function() require("base.utils.ui").toggle_autoformat() end, desc = "Autoformat", } -- Highlight references when cursor holds utils.add_autocmds_to_buffer("lsp_document_highlight", bufnr, { { events = { "CursorHold", "CursorHoldI" }, desc = "highlight references when cursor holds", callback = function() if supports_method("textDocument/documentHighlight") then vim.lsp.buf.document_highlight() end end, }, { events = { "CursorMoved", "CursorMovedI", "BufLeave" }, desc = "clear references when cursor moves", callback = function() vim.lsp.buf.clear_references() end, }, }) -- Other LSP mappings lsp_mappings.n["lL"] = { function() vim.api.nvim_command(':LspRestart') end, desc = "LSP refresh", } -- Goto definition / declaration lsp_mappings.n["gd"] = { function() vim.lsp.buf.definition() end, desc = "Goto definition of current symbol", } lsp_mappings.n["gD"] = { function() vim.lsp.buf.declaration() end, desc = "Goto declaration of current symbol", } -- Goto implementation lsp_mappings.n["gI"] = { function() vim.lsp.buf.implementation() end, desc = "Goto implementation of current symbol", } -- Goto type definition lsp_mappings.n["gT"] = { function() vim.lsp.buf.type_definition() end, desc = "Goto definition of current type", } -- Goto references lsp_mappings.n["lR"] = { function() vim.lsp.buf.references() end, desc = "Hover references", } lsp_mappings.n["gr"] = { function() vim.lsp.buf.references() end, desc = "References of current symbol", } -- Goto help local hover_opts = vim.g.lsp_round_borders_enabled and { border = "rounded", silent = true } or {} lsp_mappings.n["gh"] = { function() vim.lsp.buf.hover(hover_opts) end, desc = "Hover help", } lsp_mappings.n["gH"] = { function() vim.lsp.buf.signature_help(hover_opts) end, desc = "Signature help", } lsp_mappings.n["lh"] = { function() vim.lsp.buf.hover(hover_opts) end, desc = "Hover help", } lsp_mappings.n["lH"] = { function() vim.lsp.buf.signature_help(hover_opts) end, desc = "Signature help", } -- Goto man lsp_mappings.n["gm"] = { function() vim.api.nvim_feedkeys("K", "n", false) end, desc = "Hover man", } lsp_mappings.n["lm"] = { function() vim.api.nvim_feedkeys("K", "n", false) end, desc = "Hover man", } -- Rename symbol lsp_mappings.n["lr"] = { function() vim.lsp.buf.rename() end, desc = "Rename current symbol", } -- Toggle inlay hints if vim.b.inlay_hints_enabled == nil then vim.b.inlay_hints_enabled = vim.g.inlay_hints_enabled end if vim.b.inlay_hints_enabled then vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) end lsp_mappings.n["uH"] = { function() require("base.utils.ui").toggle_buffer_inlay_hints(bufnr) end, desc = "LSP inlay hints (buffer)", } -- Toggle semantic tokens if vim.g.semantic_tokens_enabled then vim.b[bufnr].semantic_tokens_enabled = true lsp_mappings.n["uY"] = { function() require("base.utils.ui").toggle_buffer_semantic_tokens(bufnr) end, desc = "LSP semantic highlight (buffer)", } else client.server_capabilities.semanticTokensProvider = nil end -- LSP based search lsp_mappings.n["lS"] = { function() vim.lsp.buf.workspace_symbol() end, desc = "Search symbol in workspace" } lsp_mappings.n["gS"] = { function() vim.lsp.buf.workspace_symbol() end, desc = "Search symbol in workspace" } -- LSP telescope if is_available("telescope.nvim") then -- setup telescope mappings if available if lsp_mappings.n.gd then lsp_mappings.n.gd[1] = function() require("telescope.builtin").lsp_definitions() end end if lsp_mappings.n.gI then lsp_mappings.n.gI[1] = function() require("telescope.builtin").lsp_implementations() end end if lsp_mappings.n.gr then lsp_mappings.n.gr[1] = function() require("telescope.builtin").lsp_references() end end if lsp_mappings.n["lR"] then lsp_mappings.n["lR"][1] = function() require("telescope.builtin").lsp_references() end end if lsp_mappings.n.gy then lsp_mappings.n.gy[1] = function() require("telescope.builtin").lsp_type_definitions() end end if lsp_mappings.n["lS"] then lsp_mappings.n["lS"][1] = function() vim.ui.input({ prompt = "Symbol Query: (leave empty for word under cursor)" }, function(query) if query then -- word under cursor if given query is empty if query == "" then query = vim.fn.expand "" end require("telescope.builtin").lsp_workspace_symbols { query = query, prompt_title = ("Find word (%s)"):format(query), } end end) end end if lsp_mappings.n["gS"] then lsp_mappings.n["gS"][1] = function() vim.ui.input({ prompt = "Symbol Query: (leave empty for word under cursor)" }, function(query) if query then -- word under cursor if given query is empty if query == "" then query = vim.fn.expand "" end require("telescope.builtin").lsp_workspace_symbols { query = query, prompt_title = ("Find word (%s)"):format(query), } end end) end end end return lsp_mappings end utils.set_mappings(maps) return M