Skip to content

diff: Add a Git history example and complete review APIs - #3388

Merged
huacnlee merged 3 commits into
mainfrom
diff-review-example
Oct 7, 2026
Merged

huacnlee merged 3 commits into
mainfrom
diff-review-example

Conversation

@huacnlee

@huacnlee huacnlee commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Description

Add a desktop Git history browser inspired by tig as a complete Diff example. Keep commits and changed files in a resizable left sidebar, with source as the main work area. Default to Dark appearance and Split layout; retain syntax highlighting, wrapped source, added/deleted backgrounds and inline emphasis.

Refine the command surfaces and containment: commit-body disclosure sits immediately after the subject, copying sits beside the hash, Refresh belongs to history, and compact change-navigation/settings buttons share the metadata row. The settings icon opens a top-right anchored menu containing layout, appearance and display choices. Commit details, sidebar headings and the source frame have theme-token borders. Changed-file rows have more vertical padding, file icons and aligned status/statistics. Commit bodies scroll independently and reset on commit changes.

The sidebar toggle lives at the left of the status bar (Cmd/Ctrl+B). Status reports actual commit/file position and selection; shortcut hints use Kbd resolved from Actions. There are no function-key bindings. Startup --commit, --file and --line arguments reproduce a real source change without changing repository contents or limiting subsequent navigation.

Fix source spacing when line numbers are disabled: rendered row insets now match the width budget, annotation anchors include the inset, and an empty Split cell uses the source row height. Wrapped rows remain aligned and selectable when line numbers change.

Fix Diff wheel handling so vertical wheel input scrolls rows instead of being remapped to horizontal scrolling. Reuse the existing horizontal scroll mask. Clearing gutter selection now emits SelectionChanged(None) once without interrupting Shift extension.

Complete the additive review API: expose parsed conflict coordinates/labels, add runtime presentation setters, and provide renderer names matching GPUI Kit conventions. Existing renderer names remain supported with unchanged signatures. This builds on #3378; it does not repeat the dependency update from #3380. English and Chinese Diff documentation are synchronized.

Screenshot

image

Reproduce with:

cargo run -p example-tig -- --commit 4890b1c2 --file crates/component/src/speech/waveform.rs --line 70 .

Earlier screenshots are retained: initial preview and intermediate preview.

Public API

gpui-component (gpui_kit::component::diff)

  • DiffConflict (Clone, Debug) — parser-owned conflict snapshot with private fields.
  • DiffConflict::lines(&self) -> Range<usize> — one-based, half-open source range including markers.
  • DiffConflict::current_lines(&self) -> Range<usize> — current-side source range, possibly empty.
  • DiffConflict::base_lines(&self) -> Option<Range<usize>> — optional diff3 base source range.
  • DiffConflict::incoming_lines(&self) -> Range<usize> — incoming-side source range, possibly empty.
  • DiffConflict::current_label(&self) -> &SharedString — opening marker label.
  • DiffConflict::base_label(&self) -> Option<&SharedString> — optional base marker label.
  • DiffConflict::incoming_label(&self) -> &SharedString — closing marker label.
  • DiffFile::conflicts(&self) -> &[DiffConflict] — source-ordered snapshots; slice indices address conflict resolution.
  • DiffState::set_expansion_lines(&mut self, lines: usize, cx: &mut Context<Self>) — update incremental context expansion size.
  • DiffState::set_min_collapsed_lines(&mut self, lines: usize, cx: &mut Context<Self>) — update the context folding threshold while retaining the viewport anchor.
  • DiffState::set_inline_unit(&mut self, unit: Option<DiffInlineUnit>, cx: &mut Context<Self>) — switch word/grapheme emphasis or disable it.
  • DiffState::set_inline_max_line_length(&mut self, length: usize, cx: &mut Context<Self>) — update the inline preparation limit and invalidate earlier preparation.
  • DiffState::set_syntax_max_line_length(&mut self, length: usize, cx: &mut Context<Self>) — update the syntax preparation limit and invalidate earlier preparation.
  • Diff::render_annotation<F, E>(self, render: F) -> Self — render application-owned annotation content.
  • Diff::render_header<F, E>(self, render: F) -> Self — replace header content while retaining its shell and collapse control.
  • Diff::render_header_prefix<F, E>(self, render: F) -> Self — insert content before the path.
  • Diff::render_header_title_suffix<F, E>(self, render: F) -> Self — insert content directly after the path.
  • Diff::render_header_suffix<F, E>(self, render: F) -> Self — insert trailing content after statistics.

For annotation renderers, F: Fn(&DiffAnnotation, &mut Window, &mut App) -> E + 'static; header renderers use &DiffFile instead. All require E: IntoElement. Existing annotation_content, header, header_prefix, header_title_suffix, and header_suffix keep their signatures as compatibility aliases.

How to Test

cargo run -p example-tig -- .

Select commits with Up/Down or j/k, use [ / ] to change files, Enter to focus source, and Escape to return to history. Use n/Shift+N to navigate changes and the settings menu to disable wrapping before checking vertical versus horizontal wheel gestures.

Validated locally on Linux:

  • cargo test -p example-tig --locked — five tests, including production-view navigation, startup file selection, default Dark appearance, independent message scrolling and stale-result rejection.
  • cargo test -p gpui-kit --features test-support --test diff --locked — all 16 Diff UI tests, including wheel axes, gutter event ordering and wrapped-source selection with hidden/toggled line numbers.
  • Clippy with warnings denied for Component, Kit, Story and the example, including test targets.
  • Standard and gpui-fast example builds/checks; Component wasm check.
  • Format and whitespace checks; repository smoke checks for history, an empty bare repository and an invalid path.
  • Native Dark Split capture of a real SpeechWaveform change showing wrapped Rust, inline emphasis and added/deleted backgrounds; earlier standard/minimum-size inspections are retained.

No full test suite was run. macOS and Windows runtime behavior was not tested locally. The typos executable is not installed locally.

AI-assisted implementation and review.

@huacnlee
huacnlee enabled auto-merge (squash) October 7, 2026 06:13
@huacnlee
huacnlee merged commit dc00da1 into main Oct 7, 2026
30 of 31 checks passed
@huacnlee
huacnlee deleted the diff-review-example branch October 7, 2026 06:37
linruohan pushed a commit to linruohan/gpui-component that referenced this pull request Oct 9, 2026
…3388)

## Description

Add a desktop Git history browser inspired by tig as a complete Diff
example. Keep commits and changed files in a resizable left sidebar,
with source as the main work area. Default to Dark appearance and Split
layout; retain syntax highlighting, wrapped source, added/deleted
backgrounds and inline emphasis.

Refine the command surfaces and containment: commit-body disclosure sits
immediately after the subject, copying sits beside the hash, Refresh
belongs to history, and compact change-navigation/settings buttons share
the metadata row. The settings icon opens a top-right anchored menu
containing layout, appearance and display choices. Commit details,
sidebar headings and the source frame have theme-token borders.
Changed-file rows have more vertical padding, file icons and aligned
status/statistics. Commit bodies scroll independently and reset on
commit changes.

The sidebar toggle lives at the left of the status bar (Cmd/Ctrl+B).
Status reports actual commit/file position and selection; shortcut hints
use Kbd resolved from Actions. There are no function-key bindings.
Startup `--commit`, `--file` and `--line` arguments reproduce a real
source change without changing repository contents or limiting
subsequent navigation.

Fix source spacing when line numbers are disabled: rendered row insets
now match the width budget, annotation anchors include the inset, and an
empty Split cell uses the source row height. Wrapped rows remain aligned
and selectable when line numbers change.

Fix Diff wheel handling so vertical wheel input scrolls rows instead of
being remapped to horizontal scrolling. Reuse the existing horizontal
scroll mask. Clearing gutter selection now emits
`SelectionChanged(None)` once without interrupting Shift extension.

Complete the additive review API: expose parsed conflict
coordinates/labels, add runtime presentation setters, and provide
renderer names matching GPUI Kit conventions. Existing renderer names
remain supported with unchanged signatures. This builds on longbridge#3378; it
does not repeat the dependency update from longbridge#3380. English and Chinese
Diff documentation are synchronized.

## Screenshot

<img width="2100" height="1290" alt="image"
src="https://github.com/user-attachments/assets/ecf65e9d-0efb-4ce7-8906-7c3ab00ec319"
/>


Reproduce with:

```sh
cargo run -p example-tig -- --commit 4890b1c --file crates/component/src/speech/waveform.rs --line 70 .
```

Earlier screenshots are retained: [initial
preview](https://raw.githubusercontent.com/longbridge/gpui-kit/diff-review-example/examples/tig/screenshots/before.png)
and [intermediate
preview](https://raw.githubusercontent.com/longbridge/gpui-kit/diff-review-example/examples/tig/screenshots/unified.png).

## Public API

### gpui-component (`gpui_kit::component::diff`)

- `DiffConflict` (`Clone`, `Debug`) — parser-owned conflict snapshot
with private fields.
- `DiffConflict::lines(&self) -> Range<usize>` — one-based, half-open
source range including markers.
- `DiffConflict::current_lines(&self) -> Range<usize>` — current-side
source range, possibly empty.
- `DiffConflict::base_lines(&self) -> Option<Range<usize>>` — optional
diff3 base source range.
- `DiffConflict::incoming_lines(&self) -> Range<usize>` — incoming-side
source range, possibly empty.
- `DiffConflict::current_label(&self) -> &SharedString` — opening marker
label.
- `DiffConflict::base_label(&self) -> Option<&SharedString>` — optional
base marker label.
- `DiffConflict::incoming_label(&self) -> &SharedString` — closing
marker label.
- `DiffFile::conflicts(&self) -> &[DiffConflict]` — source-ordered
snapshots; slice indices address conflict resolution.
- `DiffState::set_expansion_lines(&mut self, lines: usize, cx: &mut
Context<Self>)` — update incremental context expansion size.
- `DiffState::set_min_collapsed_lines(&mut self, lines: usize, cx: &mut
Context<Self>)` — update the context folding threshold while retaining
the viewport anchor.
- `DiffState::set_inline_unit(&mut self, unit: Option<DiffInlineUnit>,
cx: &mut Context<Self>)` — switch word/grapheme emphasis or disable it.
- `DiffState::set_inline_max_line_length(&mut self, length: usize, cx:
&mut Context<Self>)` — update the inline preparation limit and
invalidate earlier preparation.
- `DiffState::set_syntax_max_line_length(&mut self, length: usize, cx:
&mut Context<Self>)` — update the syntax preparation limit and
invalidate earlier preparation.
- `Diff::render_annotation<F, E>(self, render: F) -> Self` — render
application-owned annotation content.
- `Diff::render_header<F, E>(self, render: F) -> Self` — replace header
content while retaining its shell and collapse control.
- `Diff::render_header_prefix<F, E>(self, render: F) -> Self` — insert
content before the path.
- `Diff::render_header_title_suffix<F, E>(self, render: F) -> Self` —
insert content directly after the path.
- `Diff::render_header_suffix<F, E>(self, render: F) -> Self` — insert
trailing content after statistics.

For annotation renderers, `F: Fn(&DiffAnnotation, &mut Window, &mut App)
-> E + 'static`; header renderers use `&DiffFile` instead. All require
`E: IntoElement`. Existing `annotation_content`, `header`,
`header_prefix`, `header_title_suffix`, and `header_suffix` keep their
signatures as compatibility aliases.

## How to Test

```sh
cargo run -p example-tig -- .
```

Select commits with Up/Down or j/k, use [ / ] to change files, Enter to
focus source, and Escape to return to history. Use n/Shift+N to navigate
changes and the settings menu to disable wrapping before checking
vertical versus horizontal wheel gestures.

Validated locally on Linux:

- `cargo test -p example-tig --locked` — five tests, including
production-view navigation, startup file selection, default Dark
appearance, independent message scrolling and stale-result rejection.
- `cargo test -p gpui-kit --features test-support --test diff --locked`
— all 16 Diff UI tests, including wheel axes, gutter event ordering and
wrapped-source selection with hidden/toggled line numbers.
- Clippy with warnings denied for Component, Kit, Story and the example,
including test targets.
- Standard and `gpui-fast` example builds/checks; Component wasm check.
- Format and whitespace checks; repository smoke checks for history, an
empty bare repository and an invalid path.
- Native Dark Split capture of a real SpeechWaveform change showing
wrapped Rust, inline emphasis and added/deleted backgrounds; earlier
standard/minimum-size inspections are retained.

No full test suite was run. macOS and Windows runtime behavior was not
tested locally. The `typos` executable is not installed locally.

AI-assisted implementation and review.
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.

1 participant