Neovim Keymap Inventory and Troubleshooting
This document expands on a personal note from 2023—a shortcut table I wrote so I would not forget it.
The original note contained only five mappings and the one-line legend L: Leader Key, without
distinguishing whether each mapping was a Neovim default or registered by a plugin. This document
preserves the intent of that note while clearly identifying the owner of each mapping and providing
both a procedure for verifying the mappings actually applied to the current session and a diagnostic
sequence for mappings that do not work.
Scope and assumptions
- The shortcut table below is a snapshot of an old local configuration. The same key may be bound
to a completely different action depending on the Neovim distribution (LazyVim, NvChad, AstroNvim,
and so on) or personal
init.lua, so do not treat this table as fact for your own environment. - Neovim adds default mappings as versions advance. For example, the current stable release provides
a default
<C-W>dmapping that displays the diagnostic under the cursor in a floating window. This means that some actions in the table may now be available as built-in features without local mappings. - The verification commands assume Neovim 0.8 or later, where the Lua API has stabilized. If the output
differs from this document, trust the
:verboseoutput rather than the document.
Key notation
The original note represented the leader key as L, which caused two problems. First, L conflicts
with a built-in Neovim Normal mode command (move the cursor to the last line on the screen, :h L).
Second, the actual key for <Leader> defaults to backslash (\) and can be changed with
g:mapleader, so there was no way to tell whether the letter L meant the literal input key or the
leader. This document uses standard Vim/Neovim notation.
<Leader>— leader key (:h mapleader, default\)<C-x>— Ctrl combination,<S-x>— Shift combination,<C-Space>— Ctrl+Space<CR>— Enter,<Cmd>— notation for running an Ex command without opening the command line
Local configuration snapshot
The table below rewrites the mappings from the 2023 note in standard notation. Every entry is a
local configuration snapshot, and you must use :verbose to identify which configuration file or
plugin registered it. None is asserted to be a built-in Neovim feature.
| Mode | Key | Intended action | Suspected owner | Classification |
|---|---|---|---|---|
| Normal | <Leader>f | Display the diagnostic under the cursor in a floating window | Mapping around vim.diagnostic.open_float() in the local LSP configuration | Local snapshot — owner must be verified |
| Normal | <Leader><Leader>t | Toggle the file tree | NvimTree/neo-tree family plugin | Local snapshot — owner must be verified |
| Normal | <Leader><Leader>s | Leap forward search (character-wise jump) | leap.nvim | Local snapshot — owner must be verified |
| Normal | <Leader>S | Leap backward search | leap.nvim | Local snapshot — owner must be verified |
| Insert | <C-Space> | Manually invoke the completion popup | nvim-cmp complete() binding | Local snapshot — owner must be verified |
The original note wrote the backward Leap mapping as L S, making it ambiguous whether the mapping
was <Leader>S or <Leader><Leader>S. The table interprets it as <Leader>S, but verify the actual
configuration with :verbose nmap <Leader>S.
Knowing the built-in alternatives keeps your workflow intact even in an environment where the local mappings have disappeared.
- Diagnostic float — current stable default mapping
<C-W>d(:h CTRL-W_d-default) - File navigation — built-in Netrw:
:Explore,:Lexplore - Completion — built-in insert completion:
<C-N>/<C-P>(keywords),<C-X><C-O>(omni) s/S— built-ins in the substitute family (siscl, andSiscc). leap.nvim works by overriding these keys, so they revert to their original actions in an environment where Leap is not loaded.
Official procedure for inspecting active mappings
"Mappings written in the configuration file" can differ from "mappings applied to the current session." A plugin may overwrite the same key later, or a buffer-local mapping may shadow a global mapping. The following commands are primary sources for the current session state.
| Command | Output |
|---|---|
:nmap | All Normal mode mappings for the current buffer (global + buffer-local) |
:imap | All Insert mode mappings |
:verbose nmap <lhs> | The mapping for that lhs and Last set from file:line — identifies its owner |
:verbose imap <lhs> | The same inspection for Insert mode |
:nmap <buffer> | Only buffer-local Normal mappings |
:lua =vim.api.nvim_get_keymap('n') | Returns global Normal mappings as a Lua table (for dumping/comparison) |
:lua =vim.api.nvim_buf_get_keymap(0, 'n') | Returns buffer-local mappings for the current buffer (0) as a Lua table |
The standard sequence for identifying the owner starts by running :verbose nmap <lhs> in the buffer
where the problem occurs. Last set from in the output points to the owning file and line. Empty output
means there is no mapping in that mode, so the action is either a built-in command rather than a mapping,
or a mapping in another mode. nvim_get_keymap() returns only global mappings, while
nvim_buf_get_keymap() returns only buffer-local mappings; comparing them lets a script detect a
buffer-local override.
Diagnosing mapping conflicts (shadowing)
When the same lhs is mapped twice, the mapping executed later wins. Lazy loading by a plugin manager
often creates a situation where "my configuration definitely loads first, but the action is different."
If your mapping is registered during initialization and then a plugin registers the same lhs when it
loads, the plugin becomes the final owner. A buffer-local mapping shadows a global mapping inside that
buffer. In a setup where an on_attach callback installs buffer-local mappings when an LSP attaches,
the behavior may differ only in buffers with an attached LSP.
Diagnostic sequence:
- In the affected buffer, run
:verbose nmap <lhs>— inspect the current owner andLast set from - Run
:nmap <buffer>— check whether a buffer-local mapping exists - If
Last set frompoints to your configuration, it is a load-order problem. If it points to a plugin directory, disable that plugin's default mapping option or defer your mapping until after the plugin loads unique = trueonvim.keymap.setturns a conflict with an lhs that already exists at registration time into an error. It does not prevent a lazy-loaded plugin from overwriting the mapping later, so handle that conflict by disabling the plugin's default mapping or checking the owner again after its load hook
When <C-Space> does not work in the terminal
In traditional terminals, <C-Space> is often sent as the NUL (0x00) byte and cannot be distinguished
from <C-@>. The terminal emulator may not send this byte at all, or the operating system may intercept
it first. On macOS, for example, Ctrl+Space is assigned to "Select the previous input source" by default;
unless that keyboard shortcut is disabled in System Settings, the key never reaches Neovim. Modifier
combinations may also be lost inside tmux when extended keys are not configured.
The fastest way to inspect the transmission path is to examine the byte directly outside Neovim.
- Run
cat -vin the shell and press Ctrl+Space. If^@appears, NUL is being transmitted; if nothing appears, the terminal or OS is swallowing it. - Inside Neovim, press
<C-V>in Insert mode and then<C-Space>to inspect the character actually inserted. - tmux users should repeat the same test outside tmux to isolate whether tmux is a variable in the path.
There is no reason to insist on that lhs in an environment where it cannot be transmitted. Replace
completion with the built-in <C-N>/<C-P> and <C-X><C-O>, or choose another lhs whose transmission
is guaranteed. See :h tui for terminal key input behavior in general.
Registering mappings with Lua
For new mappings, vim.keymap.set is recommended over the :map family of Ex commands. Unlike :map,
it defaults to noremap = true, preventing accidental recursive mappings, and stores a documentation
string through desc. The desc appears directly in :map output and discovery UIs such as which-key.
-- Example: registration code that reproduces the intent of the local snapshot table
local map = vim.keymap.set
-- Normal: show the diagnostic under the cursor in a floating window
map('n', '<Leader>f', function()
vim.diagnostic.open_float({ border = 'rounded' })
end, { desc = 'Open diagnostic float', silent = true })
-- Insert: manually invoke the completion popup (requires nvim-cmp)
map('i', '<C-Space>', function()
require('cmp').complete()
end, { desc = 'Open completion popup' })
-- Normal: toggle the file tree — detect conflicts at registration time with unique
map('n', '<Leader><Leader>t', '<Cmd>NvimTreeToggle<CR>', {
desc = 'Toggle file tree',
unique = true,
})
Keeping the "Intended action" column in the snapshot table aligned with the actual desc lets :map
output immediately reveal whether the document and configuration have diverged. The nvim-cmp example
above is valid only in environments where that plugin is installed; if you use only built-in completion,
the <C-Space> mapping itself is unnecessary.
Maintenance checklist
- Always add
descto a new mapping, and run:verbose nmap <lhs>before adding it to check for conflicts - When possible, use
unique = trueto turn registration-time duplicates into errors, and check the owner again after lazy loading - Re-verify every lhs in the snapshot table with
:verboseafter upgrading Neovim or a plugin (new default mappings may be added, or plugin defaults may change) - Remove a mapping from this table at the same time it is removed from the configuration
- After changing terminal emulators, use
cat -vto recheck transmission of control keys such as<C-Space> - Remove local mappings that can be replaced by built-in defaults (for example, diagnostic float with
<C-W>d)
Related documents
- Tools:: index — other tool notes in this section
- When Korean Text Is Unreadable in Vim — troubleshooting in the same editor family
- iTerm2 shell integration — macOS terminal environments and the background of key transmission issues
Official references
- map.txt —
:h map— the:mapcommand family and definitions of<Leader>,<unique>, and<buffer> :h mapleader— the leader key's default and how to change itvim.keymap.set()— Lua mapping APInvim_get_keymap()/nvim_buf_get_keymap()— APIs for querying global and buffer-local mappings- diagnostic.txt —
:h vim.diagnostic—open_float()and the default<C-W>dmapping (CTRL-W_d-default) - TUI —
:h tui— terminal key input and modifier transmission behavior