Skip to content

language: Fix auto-indent overwriting manual indentation when replacing a line's contents - #62644

Open
lingyaochu wants to merge 2 commits into
zed-industries:mainfrom
lingyaochu:ime_indent
Open

language: Fix auto-indent overwriting manual indentation when replacing a line's contents#62644
lingyaochu wants to merge 2 commits into
zed-industries:mainfrom
lingyaochu:ime_indent

Conversation

@lingyaochu

Copy link
Copy Markdown
Collaborator

Objective

Closes #62617

Turns out #62617 is just a special trigger point of a more general issue: replacing a line's contents can silently rewrite the line's indentation with the auto-indent suggestion.

Consider the following Rust code, where the line has an extra tab, making its indent 8 spaces instead of the default 4:

fn main() {
        println!("hello world");
}

If we select and replace the line's contents (without the indentation):

fn main() {
        «println!("hello world");»
}

with let a = 8;, the result is:

fn main() {
    let a = 8;
}

The extra indent has been stripped.

Tracing this down to Buffer::edit_internal() in crates/language/src/buffer.rs, the code decides whether the edited line needs an indent update via the first_line_is_new flag, which ends up as the old_row of an AutoindentRequestEntry. One of these checks is:

if !new_text.contains('\n')
&& (old_start.column + (range_len as u32) < old_line_end
|| old_line_end == old_line_start)
{
first_line_is_new = false;
}

When replacing a line's contents, the edit range ends exactly at the end of the line, so old_start.column + (range_len as u32) == old_line_end. Because the check uses <, this case meets none of the these conditions, first_line_is_new stays true, and an indent update is triggered. If the manual indent differs from the suggested indent, it gets overwritten — exactly as in the example above.

For IME input, composition updates replace the previously marked preedit text, which sits at the end of the line — the same geometry as a full line-content replacement. In some environments (observed on KDE Wayland with fcitx), a single keystroke delivers the preedit update twice, so the replacement happens on the very first keystroke, which is what #62617 reports. On other platforms, the replacement may happens once the composition changes, i.e. on the second keystroke, so it takes at least two characters to trigger.

Solution

Simply change the guard from (old_start.column + (range_len as u32) < old_line_end to (old_start.column + (range_len as u32) <= old_line_end.

Testing

Two new tests are added: test_ime_composition_keeps_manual_indent covers the IME input path, and test_replacing_line_content_keeps_manual_indent covers a plain line-content replacement.

Self-Review Checklist:

  • I've reviewed my own diff for quality, security, and reliability
  • Unsafe blocks (if any) have justifying comments
  • The content adheres to Zed's UI standards (UX/UI and icon guidelines)
  • Tests cover the new/changed behavior
  • Performance impact has been considered and is acceptable

Release Notes:

  • Fixed manual indentation being lost when replacing a line's contents or typing with an input method

@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Aug 14, 2026
@zed-community-bot zed-community-bot Bot added community champion Issues filed by our amazing community champions! 🫶 guild Pull requests by someone in Zed Guild. NOTE: the label application is automated via github actions labels Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement community champion Issues filed by our amazing community champions! 🫶 guild Pull requests by someone in Zed Guild. NOTE: the label application is automated via github actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IME input snaps cursor back to the auto-indent position, deleting manually added indentation

1 participant