You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Until this feature has been added, is there a way to prevent the split diffs from appearing and only see the suggested changes in the claude code sidebar?
You can try creating a custom output style with something like this: /output-style:new Claude only shows diffs for suggested changes in the Claude Code pane, but doesn't create diffs in the linked IDE. With this command, Claude created for me a custom style in ~/.claude/output-styles/diff-review-only.md and when I activated this output style with /output-style, Claude really reacted by just showing the diff in the Claude Code pane (at least for a simple refactor task). When I re-enabled the default output style, Claude started creating diffs in Nvim again.
Until this feature has been added, is there a way to prevent the split diffs from appearing and only see the suggested changes in the claude code sidebar?
There's a setting in Claude Code for this. Once connected to the Neovim plugin, a "Diff Tool" option will appear under /config. You can then select whether diffs are displayed in Neovim (auto) or within Claude Code (terminal).
Diffs will be shown in Neovim:
Diffs will be shown in Claude Code:
Reacted by Dan Nguyen, Luis A. Rodriguez, Nick Pupko, Daniel Imfeld, Kyle Awayan and Andrew
This triage report is AI-generated using Claude Code
Triage: inline diff
This issue is a feature request. Reading through the thread, two distinct asks are present, and separating them helps:
(A) An inline / unified diff inside Neovim — a single, compact buffer with additions and deletions interleaved, instead of the current side-by-side split. This is the original request.
(B) Suppressing the Neovim diff entirely — raised later in the thread — so proposed changes are shown only inside the Claude Code pane. This one is already solved: selecting /config → Diff Tool → terminal makes Claude render diffs in its own TUI and never call the editor's openDiff at all (as covered earlier in this thread). The custom output-style suggestion mentioned above also works; the built-in setting is the durable answer.
The remainder of this report concerns ask (A).
Is an inline diff feasible and in scope?
Yes on both counts.
The protocol does not mandate a layout. The openDiff MCP tool is defined by four parameters and two text results (FILE_SAVED + final contents on accept, DIFF_REJECTED + tab name on reject) — there is no orientation / side-by-side field anywhere (PROTOCOL.md §2; lua/claudecode/tools/open_diff.lua). Diff presentation is therefore entirely client-side. This is corroborated by an independent client of the identical protocol (manzaltu/claude-code-ide.el), which renders its own way while honoring the same contract.
Relationship to the official VS Code experience. The official extension's default is actually a side-by-side, editable comparison; its inline / unified view is a VS Code editor toggle (diffEditor.renderSideBySide), not a Claude setting. The current side-by-side layout here is therefore faithful to upstream, and an inline view would be an opt-in enhancement rather than a parity fix.
It can be done with zero dependencies. A single-buffer unified diff is achievable with built-ins only — vim.diff() for hunk computation plus extmarks for rendering — on Neovim ≥ 0.9. (Verified against api.txt at the v0.9.0 tag: virt_lines, line_hl_group, sign_text, and sign_hl_group are all present then; vim.diff itself dates to 0.6 and was renamed vim.text.diff in 0.12, with the old name kept as an alias.)
PR #195 already implements this. It adds a self-contained diff_inline.lua (pure vim.diff() + extmark rendering with + / - sign markers), dispatches accept / reject / cleanup on layout == "inline", and ships 23 tests.
The approach was reviewed and exercised during this triage:
Correctness — confirmed. The diff computation and accept-time content extraction reconstruct exactly the proposed file across modify / new-file / pure-deletion cases (the read-only buffer keeps deleted lines for display, and accept strips them via the saved line-type array). A standalone run is in the first collapsible box below.
Version gate — correct. The stated requirement of Neovim ≥ 0.9 is accurate, and even slightly conservative.
Points worth addressing before merge:
The branch is currently out of date and conflicts with main (its base predates roughly 35 commits of diff.lua refactoring — including the reject-on-:q, only-window, new-tab, foreign-diff, and terminal-resize changes), so a rebase is required.
vim.diff() is called directly; binding vim.text.diff or vim.diff keeps it forward-compatible with 0.12+.
The buffer is rendered read-only, which forfeits the ability to edit a proposal before accepting — see the next section.
Design note: editability
The current side-by-side layout (and the official VS Code experience) allows a proposal to be edited before accepting, with the final buffer contents returned in FILE_SAVED. The inline buffer in PR #195 is read-only, so that capability is lost in inline mode. The same trade-off appears in a sibling fork that also moved to an extmark overlay (douglasjordan2/claudecode.nvim).
This trade-off is avoidable. If the proposed lines are rendered as real, editable buffer lines (highlighted, with a + sign) and the deleted lines are shown as read-only virtual lines (virt_lines), the inline view stays compact and editable, and accept simply returns the live buffer contents. A prototype of this pattern is in the second collapsible box; a simulated edit to a proposed line is correctly captured on accept while the virtual deletions are excluded. This mirrors how codecompanion.nvim's built-in inline engine and folke/sidekick.nvim render.
On delegating to an external diff engine (relates to #169)
Delegating the diff view to diffview.nvim or codediff.nvim was also evaluated, since #169 requests it. It is not recommended: diffview.nvim requires a Git root and a heavier (GPL-3) dependency and still leaves the blocking accept / reject + final-content handshake to be built here; codediff.nvim cannot render an in-memory right-hand side at all. Notably, codecompanion.nvimremoved its pluggable diff providers (including mini.diff) in favor of a single built-in engine — a useful precedent. A self-contained, pure-Lua engine is the better fit for this project's zero-dependency design. (A bonus of a single-buffer extmark view: it opens no &diff windows, so it sidesteps the class of foreign-diff-window problems seen in #277 and the stale-diff accumulation in #205.)
Workarounds available today
For ask (B): /config → Diff Tool → terminal (built-in), or a custom output style.
For ask (A): there is no configuration-only workaround — the native diffopt inline options only affect two-buffer diff mode, not a single-buffer unified view — so this needs code (i.e. PR feat(diff): add layout = "inline" for VS Code-style unified diff #195 or equivalent). The closest existing options are diff_opts.layout = "horizontal" (less width) or the terminal Diff Tool above.
Minor documentation / cleanup notes
The README documents only layout = "vertical" | "horizontal"; an inline option would need a docs entry.
diff_opts.auto_close_on_accept and diff_opts.show_diff_stats are validated in config.lua but not implemented or documented anywhere (they appear to date back to an early PR) — candidates for either implementation or removal.
Suggested direction
Adopting PR #195 as the basis — after a rebase, the forward-compatible diff call, and a decision on the editability model (the editable proposed-lines pattern is recommended) — looks like the most sensible path, and #169 can be addressed by the same self-contained engine rather than an external dependency.
The two prototypes used to ground this report are included below for review.
proto_inline.lua — verification of PR #195's diff algorithm (run under headless Neovim 0.12.3)
-- Standalone prototype of PR #195's inline-diff core (pure functions copied verbatim),-- exercised in real headless nvim to verify correctness + probe version gates.localfunctionsplit_lines(text)
iftext=="" thenreturn {} endlocallines=vim.split(text, "\n", { plain=true })
if#lines>0andlines[#lines] =="" thentable.remove(lines) endreturnlinesendlocalfunctioncompute_inline_diff(old_text, new_text)
old_text=old_textor""new_text=new_textor""localhunks=vim.diff(old_text, new_text, { result_type="indices" }) or {}
localold_lines=split_lines(old_text)
localnew_lines=split_lines(new_text)
localresult_lines, result_types= {}, {}
localold_pos, new_pos=1, 1for_, hunkinipairs(hunks) dolocalstart_a, count_a, _, count_b=hunk[1], hunk[2], hunk[3], hunk[4]
localunchanged_countifcount_a>0thenunchanged_count=start_a-old_poselseunchanged_count=start_a-old_pos+1endfor_=1, unchanged_countdoresult_lines[#result_lines+1] =new_lines[new_pos]
result_types[#result_types+1] ="unchanged"old_pos=old_pos+1; new_pos=new_pos+1endfor_=1, count_adoresult_lines[#result_lines+1] =old_lines[old_pos]
result_types[#result_types+1] ="deleted"old_pos=old_pos+1endfor_=1, count_bdoresult_lines[#result_lines+1] =new_lines[new_pos]
result_types[#result_types+1] ="added"new_pos=new_pos+1endendwhilenew_pos<=#new_linesdoresult_lines[#result_lines+1] =new_lines[new_pos]
result_types[#result_types+1] ="unchanged"new_pos=new_pos+1endreturnresult_lines, result_typesendlocalfunctionextract_new_content(lines, line_types)
localout= {}
fori, ltinipairs(line_types) doiflt~="deleted" thenout[#out+1] =lines[i] endendreturntable.concat(out, "\n")
endlocalfunctionP(s) io.write(s.."\n") endP("=== nvim version ===")
P(tostring(vim.version()))
P("vim.diff present: " ..tostring(vim.diff~=nil))
-- Probe: does nvim_buf_set_extmark accept sign_text in THIS version?localprobe_buf=vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(probe_buf, 0, -1, false, { "x" })
localns=vim.api.nvim_create_namespace("proto")
localok_sign=pcall(vim.api.nvim_buf_set_extmark, probe_buf, ns, 0, 0, {
sign_text="+", sign_hl_group="DiffAdd", line_hl_group="DiffAdd",
})
P("nvim_buf_set_extmark{sign_text=...} accepted: " ..tostring(ok_sign))
localfunctionshow_case(title, old_text, new_text)
P("\n=== CASE: " ..title.." ===")
locallines, types=compute_inline_diff(old_text, new_text)
fori=1, #linesdolocalmark= ({ unchanged="", added="+ ", deleted="- " })[types[i]]
P(string.format("%s%s", mark, lines[i]))
endlocalextracted=extract_new_content(lines, types)
-- The accepted content must equal new_text (modulo trailing newline handling)localnorm=function(s) return (s:gsub("\n$", "")) endlocalmatch=norm(extracted) ==norm(new_text)
P(string.format("-> extract_new_content == new_text ? %s", tostring(match)))
ifnotmatchthenP(" !! MISMATCH")
P(" extracted=[" ..extracted.."]")
P(" expected =[" ..norm(new_text) .."]")
endendlocalold1=table.concat({
"local function greet(name)",
" print('hi ' .. name)",
" return true",
"end",
}, "\n")
localnew1=table.concat({
"local function greet(name)",
" -- be polite",
" print('hello, ' .. name .. '!')",
" return true",
"end",
}, "\n")
show_case("modify a function", old1, new1)
show_case("new file (old empty)", "", "line one\nline two\nline three")
show_case("pure deletion", "keep\ndrop me\nkeep2", "keep\nkeep2")
-- Demonstrate the read-only limitation: in inline mode the buffer holds deleted-- lines too, so accept must reconstruct from line_types (NOT from buffer text).-- If a user could edit the buffer, their edits to 'deleted' lines would be discarded,-- and edits to 'added' lines are only preserved if accept re-reads the buffer (it does NOT).P("\n=== editability note ===")
P("PR #195 sets modifiable=false and reconstructs accepted content from the saved")
P("line_types array, so user edits to the proposal are impossible (unlike vertical/horizontal,")
P("where the proposed buffer is modifiable and accept re-reads live buffer lines).")
vim.cmd("qa!")
Output (headless nvim --headless -l):
=== nvim version ===
0.12.3+v0.12.3
vim.diff present: true
nvim_buf_set_extmark{sign_text=...} accepted: true
=== CASE: modify a function ===
local function greet(name)
- print('hi ' .. name)
+ -- be polite
+ print('hello, ' .. name .. '!')
return true
end
-> extract_new_content == new_text ? true
=== CASE: new file (old empty) ===
+ line one
+ line two
+ line three
-> extract_new_content == new_text ? true
=== CASE: pure deletion ===
keep
- drop me
keep2
-> extract_new_content == new_text ? true
=== editability note ===
PR #195 sets modifiable=false and reconstructs accepted content from the saved
line_types array, so user edits to the proposal are impossible (unlike vertical/horizontal,
where the proposed buffer is modifiable and accept re-reads live buffer lines).
proto_editable_inline.lua — prototype of the editability-preserving inline pattern (proposed lines real & editable, deletions as virtual lines)
-- Prototype: editability-PRESERVING inline unified diff (the pattern PR #195 does NOT use).-- Buffer holds the NEW/proposed content as REAL, EDITABLE lines (added lines highlighted +-- "+" sign). Deleted OLD lines are shown as read-only virtual lines (virt_lines) anchored in-- place. On accept, final content = the live buffer text (so user edits are captured), exactly-- like the current vertical/horizontal split and like VS Code.localdiff_fn=vim.textandvim.text.difforvim.diff-- forward-compatible (0.12 renamed it)localfunctionsplit_lines(text)
iftext=="" thenreturn {} endlocall=vim.split(text, "\n", { plain=true })
if#l>0andl[#l] =="" thentable.remove(l) endreturnlendlocalns=vim.api.nvim_create_namespace("proto_editable")
vim.api.nvim_set_hl(0, "PInlAdd", { link="DiffAdd", default=true })
vim.api.nvim_set_hl(0, "PInlDel", { link="DiffDelete", default=true })
--- Render new_text as real editable lines; show deletions as virt_lines. Returns buf.localfunctionrender(old_text, new_text)
localbuf=vim.api.nvim_create_buf(false, true)
localnew_lines=split_lines(new_text)
localold_lines=split_lines(old_text)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, new_lines) -- REAL proposed contentvim.bo[buf].modifiable=true-- editable!localhunks=diff_fn(old_text, new_text, { result_type="indices" }) or {}
localadded, deleted=0, 0for_, hinipairs(hunks) dolocalstart_a, count_a, start_b, count_b=h[1], h[2], h[3], h[4]
-- Added new lines -> real lines, highlight + "+" signforr=start_b, start_b+count_b-1doadded=added+1vim.api.nvim_buf_set_extmark(buf, ns, r-1, 0, {
line_hl_group="PInlAdd", sign_text="+", sign_hl_group="PInlAdd",
})
end-- Deleted old lines -> read-only virtual lines, anchored at the hunklocalvlines= {}
forr=start_a, start_a+count_a-1dodeleted=deleted+1vlines[#vlines+1] = { { "- " .. (old_lines[r] or""), "PInlDel" } }
endif#vlines>0thenifcount_b>0then-- anchor above the first added linevim.api.nvim_buf_set_extmark(buf, ns, start_b-1, 0,
{ virt_lines=vlines, virt_lines_above=true })
else-- pure deletion: anchor below the preceding kept line (start_b is the kept line)localanchor=math.max(start_b-1, 0)
vim.api.nvim_buf_set_extmark(buf, ns, anchor, 0,
{ virt_lines=vlines, virt_lines_above=false })
endendendreturnbuf, added, deletedendlocalfunctionP(s) io.write(s.."\n") endlocalfunctiondump(buf)
locallines=vim.api.nvim_buf_get_lines(buf, 0, -1, false)
localmarks=vim.api.nvim_buf_get_extmarks(buf, ns, 0, -1, { details=true })
-- index extmarks by rowlocaladd_at, del_at= {}, {}
for_, minipairs(marks) dolocalrow, det=m[2], m[4]
ifdet.line_hl_group=="PInlAdd" thenadd_at[row] =trueendifdet.virt_linesthendel_at[row] =det.virt_linesendendfori=0, #lines-1do-- print any deletion virt_lines anchored ABOVE this rowifdel_at[i] thenfor_, vlinipairs(del_at[i]) doP(" ¦ " ..vl[1][1]) endendP((add_at[i] and"+ " or"") ..lines[i+1])
endendlocalold1=table.concat({
"function greet(name)",
" print('hi ' .. name)",
" return true",
"end",
}, "\n")
localnew1=table.concat({
"function greet(name)",
" -- be polite",
" print('hello, ' .. name .. '!')",
" return true",
"end",
}, "\n")
P("=== editable inline render (proposed=real, deleted=virtual) ===")
P("nvim " ..tostring(vim.version()) .." diff_fn=" .. (vim.textandvim.text.diffand"vim.text.diff" or"vim.diff"))
localbuf, added, deleted=render(old1, new1)
dump(buf)
P(string.format("-> %d added (real,editable), %d deleted (virtual,read-only)", added, deleted))
-- Editability proof: user tweaks an added line, then accepts.localcur=vim.api.nvim_buf_get_lines(buf, 0, -1, false)
fori, linipairs(cur) doifl:find("hello, ", 1, true) thencur[i] =" print('Hello there, ' .. name)" endendvim.api.nvim_buf_set_lines(buf, 0, -1, false, cur) -- simulate a user edit in the diff bufferlocalaccepted=table.concat(vim.api.nvim_buf_get_lines(buf, 0, -1, false), "\n")
P("\n=== accept after user edit -> FILE_SAVED content is the LIVE buffer (edits captured) ===")
P(accepted)
P("contains user's edited text? " ..tostring(accepted:find("Hello there", 1, true) ~=nil))
P("virtual deleted lines are NOT in accepted content? " ..tostring(accepted:find("print%('hi '") ==nil))
vim.cmd("qa!")
Output (headless nvim --headless -l):
=== editable inline render (proposed=real, deleted=virtual) ===
nvim 0.12.3+v0.12.3 diff_fn=vim.text.diff
function greet(name)
¦ - print('hi ' .. name)
+ -- be polite
+ print('hello, ' .. name .. '!')
return true
end
-> 2 added (real,editable), 1 deleted (virtual,read-only)
=== accept after user edit -> FILE_SAVED content is the LIVE buffer (edits captured) ===
function greet(name)
-- be polite
print('Hello there, ' .. name)
return true
end
contains user's edited text? true
virtual deleted lines are NOT in accepted content? true
Activity
benbrastmckie commentedon Sep 16, 2025
Until this feature has been added, is there a way to prevent the split diffs from appearing and only see the suggested changes in the claude code sidebar?
jakubbortlik commentedon Oct 13, 2025
You can try creating a custom output style with something like this:
/output-style:new Claude only shows diffs for suggested changes in the Claude Code pane, but doesn't create diffs in the linked IDE.With this command, Claude created for me a custom style in~/.claude/output-styles/diff-review-only.mdand when I activated this output style with/output-style, Claude really reacted by just showing the diff in the Claude Code pane (at least for a simple refactor task). When I re-enabled the default output style, Claude started creating diffs in Nvim again.ThomasK33 commentedon Oct 15, 2025
There's a setting in Claude Code for this. Once connected to the Neovim plugin, a "Diff Tool" option will appear under
/config. You can then select whether diffs are displayed in Neovim (auto) or within Claude Code (terminal).Diffs will be shown in Neovim:
Diffs will be shown in Claude Code:
ThomasK33 commentedon Jun 22, 2026
Note
This triage report is AI-generated using Claude Code
Triage: inline diff
This issue is a feature request. Reading through the thread, two distinct asks are present, and separating them helps:
/config→ Diff Tool →terminalmakes Claude render diffs in its own TUI and never call the editor'sopenDiffat all (as covered earlier in this thread). The custom output-style suggestion mentioned above also works; the built-in setting is the durable answer.The remainder of this report concerns ask (A).
Is an inline diff feasible and in scope?
Yes on both counts.
openDiffMCP tool is defined by four parameters and two text results (FILE_SAVED+ final contents on accept,DIFF_REJECTED+ tab name on reject) — there is no orientation / side-by-side field anywhere (PROTOCOL.md§2;lua/claudecode/tools/open_diff.lua). Diff presentation is therefore entirely client-side. This is corroborated by an independent client of the identical protocol (manzaltu/claude-code-ide.el), which renders its own way while honoring the same contract.diffEditor.renderSideBySide), not a Claude setting. The current side-by-side layout here is therefore faithful to upstream, and an inline view would be an opt-in enhancement rather than a parity fix.vim.diff()for hunk computation plus extmarks for rendering — on Neovim ≥ 0.9. (Verified againstapi.txtat the v0.9.0 tag:virt_lines,line_hl_group,sign_text, andsign_hl_groupare all present then;vim.diffitself dates to 0.6 and was renamedvim.text.diffin 0.12, with the old name kept as an alias.)An implementation already exists: PR #195
PR #195 already implements this. It adds a self-contained
diff_inline.lua(purevim.diff()+ extmark rendering with+/-sign markers), dispatches accept / reject / cleanup onlayout == "inline", and ships 23 tests.The approach was reviewed and exercised during this triage:
main(its base predates roughly 35 commits ofdiff.luarefactoring — including the reject-on-:q, only-window, new-tab, foreign-diff, and terminal-resize changes), so a rebase is required.vim.diff()is called directly; bindingvim.text.diff or vim.diffkeeps it forward-compatible with 0.12+.Design note: editability
The current side-by-side layout (and the official VS Code experience) allows a proposal to be edited before accepting, with the final buffer contents returned in
FILE_SAVED. The inline buffer in PR #195 is read-only, so that capability is lost in inline mode. The same trade-off appears in a sibling fork that also moved to an extmark overlay (douglasjordan2/claudecode.nvim).This trade-off is avoidable. If the proposed lines are rendered as real, editable buffer lines (highlighted, with a
+sign) and the deleted lines are shown as read-only virtual lines (virt_lines), the inline view stays compact and editable, and accept simply returns the live buffer contents. A prototype of this pattern is in the second collapsible box; a simulated edit to a proposed line is correctly captured on accept while the virtual deletions are excluded. This mirrors howcodecompanion.nvim's built-in inline engine andfolke/sidekick.nvimrender.On delegating to an external diff engine (relates to #169)
Delegating the diff view to
diffview.nvimorcodediff.nvimwas also evaluated, since #169 requests it. It is not recommended:diffview.nvimrequires a Git root and a heavier (GPL-3) dependency and still leaves the blocking accept / reject + final-content handshake to be built here;codediff.nvimcannot render an in-memory right-hand side at all. Notably,codecompanion.nvimremoved its pluggable diff providers (includingmini.diff) in favor of a single built-in engine — a useful precedent. A self-contained, pure-Lua engine is the better fit for this project's zero-dependency design. (A bonus of a single-buffer extmark view: it opens no&diffwindows, so it sidesteps the class of foreign-diff-window problems seen in #277 and the stale-diff accumulation in #205.)Workarounds available today
/config→ Diff Tool →terminal(built-in), or a custom output style.diffoptinline options only affect two-buffer diff mode, not a single-buffer unified view — so this needs code (i.e. PR feat(diff): add layout = "inline" for VS Code-style unified diff #195 or equivalent). The closest existing options arediff_opts.layout = "horizontal"(less width) or the terminal Diff Tool above.Minor documentation / cleanup notes
layout = "vertical" | "horizontal"; an inline option would need a docs entry.diff_opts.auto_close_on_acceptanddiff_opts.show_diff_statsare validated inconfig.luabut not implemented or documented anywhere (they appear to date back to an early PR) — candidates for either implementation or removal.Suggested direction
Adopting PR #195 as the basis — after a rebase, the forward-compatible diff call, and a decision on the editability model (the editable proposed-lines pattern is recommended) — looks like the most sensible path, and #169 can be addressed by the same self-contained engine rather than an external dependency.
The two prototypes used to ground this report are included below for review.
proto_inline.lua — verification of PR #195's diff algorithm (run under headless Neovim 0.12.3)
Output (headless
nvim --headless -l):proto_editable_inline.lua — prototype of the editability-preserving inline pattern (proposed lines real & editable, deletions as virtual lines)
Output (headless
nvim --headless -l):feat(diff): add layout = "inline" for VS Code-style unified diff (cod…
refactor(diff): rename diff_opts.layout "inline" to "unified" (coder#295