Skip to content

[audit] ]c/[c class-jump keymaps shadow vimdiff's built-in diff-hunk navigation #116

Description

@stanfish06

What

lua/config/plugin_config.lua lines 222–226 bind ]c and [c to treesitter-textobjects class navigation:

vim.keymap.set({ "n", "x", "o" }, "]c", function()
    move.goto_next_start("@class.outer", "textobjects")
end, { desc = "Next class start" })
vim.keymap.set({ "n", "x", "o" }, "[c", function()
    move.goto_previous_start("@class.outer", "textobjects")
end, { desc = "Prev class start" })

Where

lua/config/plugin_config.lua, lines 222–226.

Why it matters

:help ]c — ]c / [c are Neovim/Vim built-in normal-mode keymaps for jumping to the next/previous diff hunk in a vimdiff or diff buffer. These mappings apply globally in normal mode, so after the textobjects plugin loads, pressing ]c / [c in a diff buffer navigates classes instead of diff hunks — making vimdiff navigation broken.

Important side-effect on the existing ]g workaround: plugin_config.lua lines 191–204 route ]g → normal! ]c when vim.wo.diff is true. That normal! bypasses user maps, so it still hits the built-in correctly. However, anyone pressing ]c directly (muscle-memory from years of vimdiff use) gets class navigation, not diff navigation — with no warning.

Recommended action

Rename the class-jump bindings to keys that don't conflict with diff navigation. Common alternatives:

Current Suggested Meaning
]c / [c ]t / [t type/class
]c / [c ]k / [k arbitrary free keys

Or guard the binding so it only activates outside diff mode:

vim.keymap.set("n", "]c", function()
    if vim.wo.diff then
        vim.cmd("normal! ]c")
    else
        move.goto_next_start("@class.outer", "textobjects")
    end
end, { desc = "Next class start (or diff hunk in diff mode)" })

The guard approach keeps muscle memory for both workflows at the cost of a small runtime check.

Activity

  1. stanfish06 commented on Jun 29, 2026

    @stanfish06
    OwnerAuthor

    Additional detail while re-auditing: the ]c/[c textobjects-based class-jump rebind in lua/config/plugin_config.lua is registered for normal, visual, and operator-pending modes (not just normal mode), so visual-mode and operator-pending diff users also lose the native hunk-navigation binding, not just normal-mode users. Doesn't change the recommended fix, just widens the affected scope slightly.


    Generated by Claude Code

  2. stanfish06 commented on Jul 26, 2026

    @stanfish06
    OwnerAuthor

    Fixed in 3cfb0cf, though not the way this issue proposed.

    The suggested fix — rename the class-jump bindings to ]C/[C — would have collided: ]C/[C are already bound to class end (plugin_config.lua:283-288), same as ]F/[F are function end. Renaming would have silently clobbered them.

    Instead ]c/[c now defer to the native motion when vim.wo.diff is set, mirroring the pattern the ]g/[g handler already uses:

    if vim.wo.diff then
        vim.cmd("normal! " .. vim.v.count1 .. "]c")
        return
    end
    move.goto_next_start("@class.outer", "textobjects")

    normal! bypasses the map, so there is no recursion, and the count is forwarded. Diff-hunk navigation works in diff buffers and class-jump still works everywhere else — no keys relearned. Verified: ]c/[c carry the new desc and ]C/[C remain bound to class end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions