Banner of The Toolbelt: Keymaps, User Commands, and File‑Operation Pipelines

The Toolbelt: Keymaps, User Commands, and File‑Operation Pipelines


Category: vim

📅 May 15, 2026   |   👁️ Views: 1

Author:   mosaid

Turning Functions Into a Control Surface

Custom functions (covered in Article #3) are the engine; keymaps and user commands are the steering wheel and dashboard. A well‑designed Neovim configuration makes the most frequent operations available without a thought – and the less frequent ones discoverable through muscle‑memory shortcuts or short colon commands.

This article opens lua/config/keymaps.lua and lua/config/commands.lua and shows how they form a unified toolbelt. Every mapping is intentionally chosen; every command mirrors a function but adds a discoverability layer. We’ll also examine the ranger file‑chooser integration and the buffer‑wrapping utilities that bridge the editor to external workflows.

The Keymaps Layer

The file lua/config/keymaps.lua is loaded in the scheduled callback of init.lua, after plugins and functions are available. It defines global keybindings that never depend on a specific filetype – filetype‑specific mappings live in ftplugin/ (Article #2). This separation keeps the global mapset clean and predictable.

The complete keymaps file:


local opts = { noremap = true, silent = true }
local functions = require("config.functions")

-- Copy whole file to system clipboard
vim.keymap.set("n", "cc", 'gg"+yG', opts)
vim.keymap.set("v", "", '"+y', opts)
vim.keymap.set("v", "7", '"+y', opts)
vim.keymap.set("i", "", '+', opts)

-- Toggle line numbers
vim.keymap.set("n", "l", ":set number! relativenumber!", opts)

-- redo
vim.keymap.set("n", "r", '', opts)

-- Buffer navigation
vim.keymap.set("n", "", ":bn", opts)
vim.keymap.set("n", "", ":bp", opts)

-- Window movements
vim.keymap.set("n", "", "", opts)
vim.keymap.set("n", "", "", opts)

-- Python selection execution (Visual mode)
vim.keymap.set("v", "c", functions.run_python_selection, { noremap = true })
vim.keymap.set("v", "r", functions.run_python_and_replace, { noremap = true })

-- Run scripts on files/selection
vim.keymap.set("n", "rf", functions.run_scripts_on_files, opts)
vim.keymap.set("v", "rs", functions.run_scripts_on_select, { noremap = true })

-- Basic file commands
vim.keymap.set("n", "w", ":w", opts)
vim.keymap.set("n", "q", ":q", opts)
vim.keymap.set("n", "h", ":nohlsearch", opts)

-- Window navigation using Ctrl + hjkl
vim.keymap.set("n", "", "h", opts)
vim.keymap.set("n", "", "j", opts)
vim.keymap.set("n", "", "k", opts)
vim.keymap.set("n", "", "l", opts)

-- Selection and utility commands
vim.keymap.set("n", "v", functions.select_inside_any_pair, opts)
vim.keymap.set("n", "wc", functions.word_count, opts)
vim.keymap.set("n", "mm", functions.output_old_files, opts)
vim.keymap.set("n", "cc", functions.command_history_explorer, opts)
vim.keymap.set("n", "rr", functions.output_regs, opts)
vim.keymap.set("n", "hh", functions.wrap_code_in_buffer, opts)
vim.keymap.set("n", "ranger", functions.ranger_chooser, opts)


vim.keymap.set("n", "ì", "^", opts)
vim.keymap.set("o", "ì", "^", opts)

local ls = require("luasnip")


-- Forward jump to <++> (now using Ctrl+L)
vim.cmd([[
  inoremap  /<++>"_c4l
  nnoremap  /<++>"_c4l
]])

-- Backward jump to <++> (now using Ctrl+H)
vim.cmd([[
  inoremap  ?<++>"_c4l
  nnoremap  ?<++>"_c4l
]])

-- LuaSnip forward/backward jumps with Ctrl+J / Ctrl+K
vim.keymap.set("i", "", function() require("luasnip").jump(1) end, { silent = true })
vim.keymap.set("i", "", function() require("luasnip").jump(-1) end, { silent = true })

    

Several design patterns stand out:

  • Mnemonic leader prefixes: <leader>wc for word count, <leader>cc for command history, <leader>rr for registers. The leader key is , so these are fast two‑stroke combos.
  • Buffer and window movement: Tab/Shift‑Tab for buffer switching, Ctrl‑hjkl for window navigation. No arrow‑key dependency, minimal finger travel.
  • Placeholder jumping: Ctrl‑L and Ctrl‑H jump to the next/previous <++> placeholder – a convention used throughout snippets and insert‑mode expansions. This is separate from LuaSnip’s Ctrl‑J/Ctrl‑K for snippet node jumps.
  • Clipboard shortcuts: cc in normal mode copies the entire file. Ctrl‑c in visual mode copies selection. Ctrl‑v in insert mode pastes.

User Commands: The Discoverable Layer

Keymaps are fast but invisible. User commands (the : interface) serve as the discoverable, self‑documenting counterpart. The file lua/config/commands.lua creates one command per major function:


local functions = require("config.functions")

-- Create user commands
vim.api.nvim_create_user_command("WC", functions.word_count, {})
vim.api.nvim_create_user_command("Mm", functions.output_old_files, {})
vim.api.nvim_create_user_command("MM", functions.output_old_files, {})
vim.api.nvim_create_user_command("Cc", functions.command_history_explorer, {})
vim.api.nvim_create_user_command("CC", functions.command_history_explorer, {})
vim.api.nvim_create_user_command("Ss", functions.search_command_history, {})
vim.api.nvim_create_user_command("SS", functions.search_command_history, {})
vim.api.nvim_create_user_command("Rr", functions.output_regs, {})
vim.api.nvim_create_user_command("RR", functions.output_regs, {})
vim.api.nvim_create_user_command("HH", functions.wrap_code_in_buffer, {})
vim.api.nvim_create_user_command("RangerChooser", functions.ranger_chooser, {})
vim.api.nvim_create_user_command("SaveVimInfo", functions.save_vim_bindings_and_functions, {})
vim.api.nvim_create_user_command("EngType", functions.eng_type, {})
vim.api.nvim_create_user_command("FrType", functions.fr_type, {})

    

Commands are kept short (often two or three uppercase letters) to minimise typing. A few have dual‑case variants (e.g., :Mm and :MM) – the uppercase version is a deliberate redundancy, ensuring the command still works if Caps Lock is on.

Every command mirrors a function from functions.lua but requires no modifier key. This makes the toolbelt accessible from both muscle‑memory (keymap) and conscious recall (command palette / command history).

The Ranger Chooser: External Tool Integration

The ranger_chooser() function (and its :RangerChooser command) launches the ranger terminal file manager and opens all selected files as Neovim arguments. It detects whether Neovim is running in a GUI or terminal and adapts:


function M.ranger_chooser()
  local temp = vim.fn.tempname()
  if vim.fn.has('gui_running') == 1 then
    vim.fn.system('xterm -e ranger --choosefiles=' .. vim.fn.shellescape(temp))
  else
    vim.fn.system('ranger --choosefiles=' .. vim.fn.shellescape(temp))
  end

  if vim.fn.filereadable(temp) == 0 then
    vim.cmd('redraw!')
    return
  end

  local names = vim.fn.readfile(temp)
  if #names == 0 then
    vim.cmd('redraw!')
    return
  end

  vim.cmd('edit ' .. vim.fn.fnameescape(names[1]))
  for i = 2, #names do
    vim.cmd('argadd ' .. vim.fn.fnameescape(names[i]))
  end
  vim.cmd('redraw!')
end

    

This is the Unix philosophy in action: let ranger do what it does best (file navigation) and let Neovim do what it does best (editing). The integration is a thin glue layer – a temporary file is used to pass the selected paths back, and Neovim’s argument list (:args) is populated so you can navigate between the chosen files with :next and :prev.

The HH Wrapper: Format Conversion on Demand

The wrap_code_in_buffer() function (bound to <leader>hh and :HH) takes the current buffer’s content and wraps it in <pre><code> tags, opening the result in a new buffer with HTML filetype. This is used constantly when writing articles that embed code blocks.


function M.wrap_code_in_buffer()
  local save_cursor = vim.fn.getpos('.')
  local buffer_text = vim.api.nvim_buf_get_lines(0, 0, -1, false)

  vim.cmd('enew')
  vim.bo.buftype = 'nofile'
  vim.bo.bufhidden = 'wipe'
  vim.bo.swapfile = false
  vim.wo.wrap = false
  vim.bo.filetype = 'html'

  local content = '
\n' ..
                  table.concat(buffer_text, "\n") ..
                  '\n
' vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(content, "\n")) vim.fn.setpos('.', save_cursor) end

A similar wrapper exists in the LaTeX ftplugin (Article #2), but this global version works on any buffer. Together they form a small but powerful content pipeline: write in your preferred language, hit a key, get a properly escaped HTML code block ready for the blog.

Composability and Consistency

The keymaps and commands layer follows a strict rule: every keybinding has a corresponding user command, and every command delegates to a single function. There are no inline Vimscript hacks, no copy‑pasted logic. This makes the system:

  • Auditable: you can trace any action from keypress to function in seconds.
  • Remappable: changing a keybinding never breaks the command‑line interface, and vice versa.
  • Documentable: the user commands themselves serve as living documentation.

The toolbelt doesn’t try to do everything – it focuses on operations that are too frequent to type out manually but too specific for a general‑purpose plugin. Combined with the filetype‑specific maps (Article #2) and the snippet system (Article #4), the editor becomes a personalised engineering console.

In the final article, we’ll survey the plugin ecosystem – Treesitter, completion, LSP, and bufferline – and how they complete the developer dashboard.


← The Visual Stack: Deterministic Themes, Transparency, and Custom Highlights Snippets as Code: LuaSnip, Dynamic Expansion, and File‑Based Snippet Injection →