[GH-ISSUE #771] Diff split view: syntax highlighting fails on side with few lines (tree-sitter gets fragmented code) #981

Open
opened 2026-03-14 09:11:47 +03:00 by kerem · 0 comments
Owner

Originally created by @rsbmk on GitHub (Mar 3, 2026).
Original GitHub issue: https://github.com/anomalyco/opentui/issues/771

Problem

In split diff view, syntax highlighting often fails on the side that has fewer lines (typically the "old" / removed side), while the other side with more lines highlights correctly.

Root Cause

The DiffRenderable.buildSplitView() method reconstructs two separate content strings for the left and right CodeRenderable instances:

const preLeftContent = leftLogicalLines.map((l) => l.content).join('\n')
const preRightContent = rightLogicalLines.map((l) => l.content).join('\n')

Each CodeRenderable runs tree-sitter independently on its fragment. When one side has very few lines (e.g., 3-4 removed lines), tree-sitter receives incomplete/invalid code like:

</box>

<KeybindingBar />
<GlobalKeyHandler />

This isn't valid TSX — there's a closing tag without an opening tag, standalone components without a return statement or function wrapper. Tree-sitter can't parse it correctly, so no syntax tokens are emitted and the text renders unstyled.

Meanwhile, the other side might have 30+ lines of complete JSX that tree-sitter can successfully parse.

Expected Behavior

Both sides of a split diff should have correct syntax highlighting, regardless of how many lines each side has.

Suggested Fix

Instead of passing only the diff-visible lines to tree-sitter, the DiffRenderable could:

  1. Reconstruct the full old/new file content from the diff (context lines + removed/added lines can reconstruct both versions)
  2. Run tree-sitter on the complete file content for each side
  3. Map the highlighting results back to only the diff-visible lines

This is how editors like VS Code handle diff syntax highlighting — they highlight the full file and then render only the relevant lines in the diff view.

An alternative (simpler but less complete) approach would be to pad the tree-sitter input with additional context beyond what's shown in the diff, to give the parser more syntactic structure to work with.

Environment

  • OpenTUI core: installed via npm
  • Using <diff> component in Solid.js with view="split", filetype="tsx", and a SyntaxStyle
  • Default git diff context of 3 lines (-U3)

Reproduction

Any diff where one side has significantly fewer lines than the other, especially when the removed/added code is a small fragment (like closing tags, short component declarations) without enough surrounding structure for tree-sitter to identify the language.

Originally created by @rsbmk on GitHub (Mar 3, 2026). Original GitHub issue: https://github.com/anomalyco/opentui/issues/771 ## Problem In **split diff view**, syntax highlighting often fails on the side that has fewer lines (typically the "old" / removed side), while the other side with more lines highlights correctly. ## Root Cause The `DiffRenderable.buildSplitView()` method reconstructs two separate content strings for the left and right `CodeRenderable` instances: ```js const preLeftContent = leftLogicalLines.map((l) => l.content).join('\n') const preRightContent = rightLogicalLines.map((l) => l.content).join('\n') ``` Each `CodeRenderable` runs tree-sitter **independently** on its fragment. When one side has very few lines (e.g., 3-4 removed lines), tree-sitter receives incomplete/invalid code like: ```tsx </box> <KeybindingBar /> <GlobalKeyHandler /> ``` This isn't valid TSX — there's a closing tag without an opening tag, standalone components without a `return` statement or function wrapper. Tree-sitter can't parse it correctly, so no syntax tokens are emitted and the text renders unstyled. Meanwhile, the other side might have 30+ lines of complete JSX that tree-sitter can successfully parse. ## Expected Behavior Both sides of a split diff should have correct syntax highlighting, regardless of how many lines each side has. ## Suggested Fix Instead of passing only the diff-visible lines to tree-sitter, the `DiffRenderable` could: 1. **Reconstruct the full old/new file content** from the diff (context lines + removed/added lines can reconstruct both versions) 2. **Run tree-sitter on the complete file content** for each side 3. **Map the highlighting results back** to only the diff-visible lines This is how editors like VS Code handle diff syntax highlighting — they highlight the full file and then render only the relevant lines in the diff view. An alternative (simpler but less complete) approach would be to **pad the tree-sitter input with additional context** beyond what's shown in the diff, to give the parser more syntactic structure to work with. ## Environment - OpenTUI core: installed via npm - Using `<diff>` component in Solid.js with `view="split"`, `filetype="tsx"`, and a `SyntaxStyle` - Default git diff context of 3 lines (`-U3`) ## Reproduction Any diff where one side has significantly fewer lines than the other, especially when the removed/added code is a small fragment (like closing tags, short component declarations) without enough surrounding structure for tree-sitter to identify the language.
Sign in to join this conversation.
No milestone
No project
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
starred/opentui#981
No description provided.