Skip to content

feat: add treesitter syntax highlighting and word-level diff to split view - #22

Open
KEY60228 wants to merge 2 commits into
mainfrom
feat/treesitter-word-diff
Open

feat: add treesitter syntax highlighting and word-level diff to split view#22
KEY60228 wants to merge 2 commits into
mainfrom
feat/treesitter-word-diff

Conversation

@KEY60228

@KEY60228 KEY60228 commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • Strip the +/-/ prefix from rendered diff lines so the split buffers contain pure code — the add/remove state is already conveyed by the line highlights and the colored inline line numbers
  • Enable treesitter-based language syntax highlighting in the diff panes: the language is detected from the file path (vim.filetype.match + vim.treesitter.language.get_lang) and the highlighter is attached with pcall(vim.treesitter.start, ...), falling back silently to no highlighting when no parser is installed (keeps the plugin zero-dependency); the highlighter is stopped before re-rendering a different file to avoid stale parsers
  • Add word-level (intra-line) diff highlighting: for each positionally paired remove/add line, the common prefix and suffix are scanned byte-wise (backing off to UTF-8 character boundaries) and the differing middle span is highlighted on both sides via new ReviewThemWordAdd / ReviewThemWordDelete groups (linked to DiffText, theme-friendly)
  • Add a word_diff = true config option to disable word-level highlighting; documented in README and :help reviewthem-configuration

Implementation notes

  • Word diff spans are computed in build_split_content() and carried through the line map (entry.word_diff = {start_col, end_col}), then applied as hl_group extmarks with end_col in apply_split_decorations() (priority 110, above treesitter's 100)
  • Pure adds/removes with no counterpart and identical pairs get no word diff mark
  • Diff backgrounds still show through treesitter highlighting since line_hl_group only contributes the background
  • No code relied on the old 1-column prefix offset (cursor context and keymaps are row-based), so stripping it required no other adjustments

Test plan

  • Headless load check: nvim --headless -c "set rtp+=." -c "lua require('reviewthem').setup()" -c "q"
  • Headless functional check (scratch script via nvim --headless -l): fed a fake DiffFile through render_file and asserted prefix-free buffer lines, empty padding lines, expected word diff spans (including a héllohállo case verifying spans don't split multibyte chars), no span on unpaired removes, active treesitter highlighter on both buffers, working cursor context, and that word_diff = false removes the marks
  • Open a review session on a Lua/TS file with a parser installed — verify code is syntax highlighted and changed spans within modified lines stand out
  • Open a file with no parser installed — verify the diff renders normally with no errors

🤖 Generated with Claude Code

KEY60228 and others added 2 commits July 5, 2026 01:22
… view

- Strip the "+"/"-"/" " prefix from rendered diff lines so buffers
  contain pure code; add/remove state is already conveyed by line
  highlights and colored inline line numbers
- Attach a treesitter highlighter per file via vim.filetype.match +
  vim.treesitter.start, guarded with pcall so it degrades silently
  when no parser is installed (zero-dependency), and stopped before
  re-rendering a different file
- Highlight the changed span within positionally paired remove/add
  lines (common prefix/suffix scan, UTF-8 boundary safe) using new
  ReviewThemWordAdd / ReviewThemWordDelete groups linked to DiffText
- Add word_diff config option (default true) to disable word-level
  highlighting; document it in README and :help

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The word diff extmark used priority 110 to sit above treesitter
highlights (100), but the add/remove line highlight it competes with is
a `line_hl_group` extmark. Neovim ignores `priority` for
`line_hl_group` and lets it hide any `hl_group` extmark on the same
line, so the changed span was never visible: every line it targets is
an add or remove line.

- Paint the add/remove line background as a character range spanning
  the EOL with `hl_eol` instead of `line_hl_group`, at priority 90.
  Priorities now order as intended: line background (90) below
  treesitter (100) below the word diff span (200), so syntax
  foreground colors and the changed span are both visible.
- Skip word-level highlighting when the changed span covers more than
  60% of both sides. Remove/add lines are paired by position within a
  hunk, so an unrelated pair would otherwise be highlighted almost
  end to end, which is noise on top of the line highlight.
- Stop the outgoing treesitter highlighter before replacing buffer
  content rather than after, so it does not parse lines it is about
  to lose.
- Initialize config options from the defaults so they are populated
  when setup() was never called.
- Note in README and :help that the diff buffer holds hunks rather
  than whole files, so treesitter highlighting can be imperfect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwFzEL9QFsBdzj97JD8HRz
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants