From 16a790188510d768c92cb9b9f3fea26cad354971 Mon Sep 17 00:00:00 2001 From: Rootiest Date: Sat, 3 Oct 2026 11:50:53 -0400 Subject: [PATCH] feat(keymaps): move by display line through wrapped text Count-less j/k// in normal and visual mode act as gj/gk, so long wrapped lines (mostly Markdown) can be navigated screen line by screen line. Counts still move by real lines to keep relative-number jumps working, and operator-pending mode is untouched (dj/yk stay linewise). Insert-mode / follow display lines too, deferring to the popup menu when it is visible. --- README.md | 1 + lua/keymaps.lua | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/README.md b/README.md index f3c5549..7c73df6 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ Place machine-specific or secret configuration in `~/.config/.user-dots/nvim/loc | :--- | :--- | | `` | Smart Find Files (Snacks) | | `e` | File Explorer (Snacks) | +| `j` / `k` / `↓` / `↑` | Move by wrapped (display) line when no count is given; counts still move by real lines. Arrows do the same in insert mode | | `sr` | Search and Replace (Grug-far) | | `gg` | Open Lazygit | | `qs` | Restore Last Session (Persistence) | diff --git a/lua/keymaps.lua b/lua/keymaps.lua index e9da93d..35839da 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -26,6 +26,26 @@ end ---------------------------------------------------------- +-- Move by display line through wrapped text +-- Count-less j/k// follow the visual (wrapped) line; with a count +-- (e.g. 5j) they move by real lines so relative line numbers still work. +-- Not mapped in operator-pending mode, so dj/yk keep acting on whole lines. +for _, m in ipairs({ { "j", "gj" }, { "", "gj" }, { "k", "gk" }, { "", "gk" } }) do + vim.keymap.set({ "n", "x" }, m[1], function() + return vim.v.count == 0 and m[2] or m[1] + end, { expr = true, silent = true, desc = m[2] == "gj" and "Down (display line)" or "Up (display line)" }) +end + +-- Same for the arrow keys in insert mode. The native popup menu keeps its +-- own /; blink.cmp's buffer-local maps fall back to these. +for _, m in ipairs({ { "", "gj" }, { "", "gk" } }) do + vim.keymap.set("i", m[1], function() + return vim.fn.pumvisible() == 1 and m[1] or "normal! " .. m[2] .. "" + end, { expr = true, silent = true, desc = m[2] == "gj" and "Down (display line)" or "Up (display line)" }) +end + +---------------------------------------------------------- + -- Quit and Save All with :Q -- Replaces safety-checked :xa with a forced write-all-and-quit. vim.api.nvim_create_user_command("Q", "xa!", { desc = "Write all and quit (forced)" }) -- 2.54.0