gdiff: make generated hunk actionability capability-aware #122

Closed
opened 2026-09-21 18:12:22 +00:00 by barrettruth · 1 comment
Owner

Original issue: barrettruth/diffs.nvim#273
Original author: barrettruth
Original date: 2026-05-09T19:21:39Z

Parent tracker: #270

Problem

Generated :Gdiff hunk metadata currently marks more edges actionable than the action layer actually supports.

Examples:

  • rev -> worktree hunks get mutation_target = "worktree" from DiffSpec.mutation_target().
  • hunks.parse() marks any non-nil mutation target as actionable = true.
  • Generated buffers then install both do and dp.
  • But actions.obtain_hunk() refuses index -> worktree with restoring worktree hunks is not supported.
  • actions.put_hunk() refuses read-only or already-index edges where staging does not make sense.

This mismatch is confusing. It advertises operations that are not implemented.

Product decision

Generated diffs:// buffers are plugin-owned UI, so they may install default maps without requiring user configuration.

But maps should be capability-aware:

  • index -> worktree: install dp for stage current hunk/range; do not install do unless restoring worktree becomes supported later.
  • tree -> index: install do for unstage current hunk/range; do not install dp because the hunk is already in the index.
  • tree -> worktree: read-only generated comparison; no do/dp.
  • tree -> tree: read-only generated comparison; no do/dp.
  • generated buffers should still install navigation/source maps where they make sense: ]c, [c, <CR>, o, q.

This should not force users to configure generated-buffer maps. Generated buffers are owned by the plugin.

Relevant existing code

  • lua/diffs/behavior.lua
    • mutation_target() returns "index" whenever right endpoint is index and "worktree" whenever right endpoint is worktree.
    • That is too broad for actual action support.
  • lua/diffs/hunks.lua
    • normalize_spec() sets actionable = target ~= nil.
  • lua/diffs/commands.lua
    • setup_diff_buf() installs do, dp, visual do, and visual dp whenever diffs_hunks exists.
    • Keymap ownership logic already avoids replacing existing buffer-local maps and cleans up plugin-owned maps.
  • lua/diffs/actions.lua
    • actual supported operations are narrower:
      • put_hunk / put_range: supports index -> worktree staging.
      • obtain_hunk / obtain_range: supports tree -> index unstaging.
      • worktree restore and read-only edges are refused.

Implementation guidance

Introduce a capability model instead of overloading mutation_target as "actionable".

Possible shape:

  • helper in diffs.behavior or diffs.hunks returning:
    • can_stage
    • can_unstage
    • can_open_source
    • read_only
  • or keep it local to commands.setup_diff_buf() and hunks.parse() if a broader API would be premature.

Keep the code conservative. Do not add worktree-restore semantics in this issue.

Acceptance criteria

  • Generated hunk metadata no longer says read-only or unsupported edges are actionable.
  • Generated buffers expose only meaningful do/dp maps.
  • Existing user buffer-local maps remain protected.
  • No new worktree restore behavior is implied.
> Original issue: barrettruth/diffs.nvim#273 > Original author: `barrettruth` > Original date: 2026-05-09T19:21:39Z Parent tracker: #270 ## Problem Generated `:Gdiff` hunk metadata currently marks more edges actionable than the action layer actually supports. Examples: - `rev -> worktree` hunks get `mutation_target = "worktree"` from `DiffSpec.mutation_target()`. - `hunks.parse()` marks any non-nil mutation target as `actionable = true`. - Generated buffers then install both `do` and `dp`. - But `actions.obtain_hunk()` refuses `index -> worktree` with `restoring worktree hunks is not supported`. - `actions.put_hunk()` refuses read-only or already-index edges where staging does not make sense. This mismatch is confusing. It advertises operations that are not implemented. ## Product decision Generated `diffs://` buffers are plugin-owned UI, so they may install default maps without requiring user configuration. But maps should be capability-aware: - `index -> worktree`: install `dp` for stage current hunk/range; do not install `do` unless restoring worktree becomes supported later. - `tree -> index`: install `do` for unstage current hunk/range; do not install `dp` because the hunk is already in the index. - `tree -> worktree`: read-only generated comparison; no `do`/`dp`. - `tree -> tree`: read-only generated comparison; no `do`/`dp`. - generated buffers should still install navigation/source maps where they make sense: `]c`, `[c`, `<CR>`, `o`, `q`. This should not force users to configure generated-buffer maps. Generated buffers are owned by the plugin. ## Relevant existing code - `lua/diffs/behavior.lua` - `mutation_target()` returns `"index"` whenever right endpoint is index and `"worktree"` whenever right endpoint is worktree. - That is too broad for actual action support. - `lua/diffs/hunks.lua` - `normalize_spec()` sets `actionable = target ~= nil`. - `lua/diffs/commands.lua` - `setup_diff_buf()` installs `do`, `dp`, visual `do`, and visual `dp` whenever `diffs_hunks` exists. - Keymap ownership logic already avoids replacing existing buffer-local maps and cleans up plugin-owned maps. - `lua/diffs/actions.lua` - actual supported operations are narrower: - `put_hunk` / `put_range`: supports `index -> worktree` staging. - `obtain_hunk` / `obtain_range`: supports `tree -> index` unstaging. - worktree restore and read-only edges are refused. ## Implementation guidance Introduce a capability model instead of overloading `mutation_target` as "actionable". Possible shape: - helper in `diffs.behavior` or `diffs.hunks` returning: - `can_stage` - `can_unstage` - `can_open_source` - `read_only` - or keep it local to `commands.setup_diff_buf()` and `hunks.parse()` if a broader API would be premature. Keep the code conservative. Do not add worktree-restore semantics in this issue. ## Acceptance criteria - Generated hunk metadata no longer says read-only or unsupported edges are actionable. - Generated buffers expose only meaningful `do`/`dp` maps. - Existing user buffer-local maps remain protected. - No new worktree restore behavior is implied.
barrettruth 2026-09-21 18:12:22 +00:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

Original comment: barrettruth/diffs.nvim#273, comment 4413540612
Original author: barrettruth
Original date: 2026-05-09T19:55:30Z

Completed by #268. Current hunk metadata uses can_put/can_obtain, actionability is capability-based, generated buffers install only the meaningful do/dp maps, and the behavior is covered in diffspec, hunks, and UX specs.

> Original comment: barrettruth/diffs.nvim#273, comment 4413540612 > Original author: `barrettruth` > Original date: 2026-05-09T19:55:30Z Completed by #268. Current hunk metadata uses can_put/can_obtain, actionability is capability-based, generated buffers install only the meaningful do/dp maps, and the behavior is covered in diffspec, hunks, and UX specs.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
barrettruth/diffs.nvim#122
No description provided.