Skip to content

input: Draw a placeholder for a folded line - #3791

Merged
huacnlee merged 4 commits into
longbridge:mainfrom
insd47:fold-placeholder
Nov 5, 2026
Merged

huacnlee merged 4 commits into
longbridge:mainfrom
insd47:fold-placeholder

Conversation

@insd47

@insd47 insd47 commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Description

A folded line in the editor looks like any other line: only the gutter chevron tells that lines are hidden under it. VS Code marks a folded line with a ⋯ right after its text and a background across the line (editor.foldBackground).

This lets an application do the same, through two setters on the editor state:

  • The placeholder sits right after the text of the folded line's last wrapped row, centered on that row. Spacing is the application's: it can give the element margins, as VS Code gives its ⋯ 0.2em on each side.
  • Only the element takes the pointer and the click, so the rest of the row still places the caret. Clicking it unfolds the line, as the chevron does.
  • It is painted with the text, before the gutter, so it scrolls sideways with the text and goes under the gutter. The background is painted under the text across the row, like the active line.
  • Rows below the cursor move down by the inline completion's ghost lines, as the text does.
  • Both are unset by default, so nothing changes until an application sets them, and InputEditorStyle is untouched.

The renderer gets the row, so the application decides what to show. We draw a muted ⋯ with VS Code's spacing.

Not handled. The placeholder is not counted in the line's width: with soft wrap, a last row that already fills the width pushes it past the edge, where it is clipped, and without soft wrap it is not part of the horizontal scroll range.

Public API

gpui-base:

  • gpui_base::input::FoldPlaceholderRenderer = Rc<dyn Fn(usize) -> AnyElement> — draws the placeholder of a folded line, given its buffer row.
  • InputBaseState::set_fold_placeholder_renderer(&mut self, renderer: Option<FoldPlaceholderRenderer>, cx: &mut Context<Self>) — draws that placeholder right after the text of each folded line; clicking it unfolds the line.
  • InputBaseState::set_fold_background(&mut self, color: Option<Hsla>, cx: &mut Context<Self>) — fills each folded line across the editor, as VS Code's editor.foldBackground.

How to Test

input::element::tests::a_folded_line_shows_its_placeholder_right_after_the_text_and_unfolds_on_click folds top { over two lines and checks that:

  • the renderer is called only once the line is folded;
  • a press in the same row, above the 10 px placeholder, leaves the line folded;
  • a press on the placeholder, right after top {, unfolds it. This part fails with the unfold handler removed.

Checks run:

  • cargo clippy --workspace --exclude gpui-shell --exclude webview -- --deny warnings, as CI runs it: clean.
  • cargo test -p gpui-base --features test-support --lib: 1578 passed.
  • cargo test -p gpui-component --lib: 758 passed. cargo check -p gpui-component --no-default-features: clean.
  • cargo fmt --check: clean.

Verified by hand on macOS in an editor with folding and line numbers, with C in Menlo and Markdown in Pretendard: the ⋯ follows the text, the folded row is filled, and clicking the ⋯ unfolds the line.

Not run: the story gallery, Windows and Linux, and centered or right-aligned text.

The change and test were written with Claude Code.

Checklist

  • I have read the CONTRIBUTING document and followed the guidelines.
  • Reviewed the changes in this PR and confirmed AI generated code (If any) is accurate.
  • Passed cargo run for story tests related to the changes.
  • Tested macOS, Windows and Linux platforms performance (if the change is platform-specific)

🤖 Generated with Claude Code

insd47 and others added 3 commits November 4, 2026 16:08
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ckground

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@insd47

insd47 commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Changed direction before review, in 4709444: the placeholder now follows VS Code instead of IntelliJ, and the description is rewritten to match.

with_column, which let a placeholder start over the opening brace, is gone. The covered text stayed in the buffer, so the caret could stop on it and drew inside the placeholder; handling that properly needs folds that start inside a line, which is a different feature. The renderer now returns a plain element drawn right after the text, only that element takes the click, and an editor_fold_background color fills the folded row.

@huacnlee huacnlee left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes folded code visible beyond the gutter chevron by allowing an application-owned trailing placeholder and folded-row background. That is a reasonable editor capability; the names describe their scope, and keeping both opt-in preserves the existing appearance. The remaining concern is Rust API compatibility.

  • P2 — Preserve existing InputEditorStyle consumers (crates/base/src/input/editor/highlighting.rs:123–127). The two added public fields break existing exhaustive struct literals. I verified a consumer listing the previous twelve fields: it compiles against current main 1ded8be6, but fails with E0063 against 47094447 because editor_fold_background and fold_placeholder_renderer are missing. The description correctly acknowledges this, but the repository currently requires existing consumers to remain compatible. Please use state setters backed by private storage, as suggested in the description, or explicitly agree a breaking-release migration before landing. Retrofitting #[non_exhaustive] alone also breaks those existing literals and does not solve this compatibility requirement. Please also list the new items under the required ## Public API section.

Validation on Linux, head 470944475533cce543b8838ec7baf3f58414eb83:

  • cargo fmt --check: passed before adding local review fixtures.
  • cargo test -p gpui-base --features test-support --lib input::: 416 passed, including the new placeholder click regression.
  • cargo clippy -p gpui-base --lib --features test-support -- --deny warnings and cargo clippy -p gpui-component --lib -- --deny warnings: passed.
  • Built a local Base editor fixture and ran actual X11 windows under Xvfb on current main and the PR. The folded row gains its background and trailing dots only on the PR; clicking the dots unfolds it. Saved a side-by-side comparison locally for the owner. No contributor-branch changes were pushed.
  • The broader --all-targets Clippy invocations could not complete: without test-support, existing Base tests reference a gated crate::test_support; enabling it across Base/Component instead exposes Observed<E>.vertical_scrollbar in popup_menu.rs. These are outside this diff; the narrower production-library checks above passed.

Not verified here: light theme, HiDPI, actual wrapped-header geometry, horizontal scrolling, inline-completion ghost rows, centered/right-aligned text, keyboard/accessibility activation of the new placeholder, macOS or Windows. The documented overflow/scroll-range limitation remains present and was not treated as a newly discovered defect.

🤖 Noop (reviewer) · AI agent acting for @huacnlee

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@insd47

insd47 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks. Moved both to setters on the state in ca8806d, so the PR no longer touches InputEditorStyle or crates/component:

state.set_fold_placeholder_renderer(Some(renderer), cx);
state.set_fold_background(Some(color), cx);

They are backed by private fields on InputBaseState, unset by default. A literal listing the previous twelve fields of InputEditorStyle builds again, since the struct is the same as on main. The description now has a ## Public API section with the type and the two setters.

Ran cargo clippy --workspace --exclude gpui-shell --exclude webview -- --deny warnings (clean), cargo test -p gpui-base --features test-support --lib (1578 passed) and cargo test -p gpui-component --lib (758 passed) on macOS.

@huacnlee huacnlee left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds an opt-in trailing placeholder and row background so folded code is recognizable without relying only on the gutter. The application still owns the placeholder presentation, and the new state setters describe their scope appropriately.

Follow-up review of ca8806da3456db04b64364abdf4301425874d18e: the previous compatibility finding is resolved. The storage is private on InputBaseState; InputEditorStyle is unchanged from the base branch, and the description now lists the renderer alias and both setters under Public API. I rebuilt the previous exhaustive twelve-field InputEditorStyle consumer successfully. No new blocking finding in this delta.

Validation on Linux:

  • cargo test -p gpui-base --features test-support --lib input::: 416 passed, including the placeholder click regression.
  • Repository cargo fmt --all --check: passed after removing the local review fixture.
  • cargo clippy -p gpui-base --lib --features test-support -- --deny warnings: passed.
  • Rebuilt and ran the Base editor fixture in an actual X11 window under Xvfb using the new setters. The folded background and trailing dots appear, and clicking the dots unfolds the line. Compared against the main fixture screenshot retained from the preceding review; the rendering implementation itself is unchanged by this follow-up.

The earlier documented coverage limits remain: this fixture covers dark theme at 1x and the simple click flow, not light theme, HiDPI, genuinely wrapped headers, horizontal-scroll extent, ghost completion rows, centered/right alignment, keyboard/accessibility activation, or native macOS/Windows interaction. The documented placeholder overflow/scroll-range limit also remains. Current CI is still pending; this comment is not a merge approval. No contributor branch changes were pushed.

🤖 Noop (reviewer) · AI agent acting for @huacnlee

@huacnlee
huacnlee merged commit e7ee940 into longbridge:main Nov 5, 2026
19 checks passed
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