language: Defer auto-indent until parsing catches up (#62024)

# Objective

Prevent auto-indent from using stale syntax when text changes during a
background parse.

## Solution

Keep auto-indent pending and parsing active until the current parse
completes.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Editor: Fixed auto-indent occasionally using stale syntax after rapid
edits.
This commit is contained in:
Ben Kunkle authored and GitHub committed 2026-08-05 16:55:10 +00:00
1 parent 2c60a17676
commit 3917caa9a0
3 files changed
+31 -7

No files matched your search

+4 -1
View File
@@ -35072,7 +35072,10 @@ async fn test_indent_after_input_for_bash(cx: &mut TestAppContext) {
let mut cx = EditorTestContext::new(cx).await;
let language = languages::language("bash", tree_sitter_bash::LANGUAGE.into());
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
cx.update_buffer(|buffer, cx| {
buffer.set_language(Some(language), cx);
buffer.set_sync_parse_timeout(None);
});
// test indents on comment insert
cx.set_state(indoc! {"
+18 -5
View File
@@ -1874,7 +1874,12 @@ impl Buffer {
language.clone(),
sync_parse_timeout,
) {
self.did_finish_parsing(syntax_snapshot, Some(Duration::from_millis(300)), cx);
self.did_finish_parsing(
syntax_snapshot,
Some(Duration::from_millis(300)),
false,
cx,
);
self.reparse = None;
return;
}
@@ -1906,7 +1911,7 @@ impl Buffer {
let parse_again = this.version.changed_since(&parsed_version)
|| language_registry_changed()
|| grammar_changed();
this.did_finish_parsing(new_syntax_map, None, cx);
this.did_finish_parsing(new_syntax_map, None, parse_again, cx);
this.reparse = None;
if parse_again {
this.reparse(cx, false);
@@ -1920,13 +1925,21 @@ impl Buffer {
&mut self,
syntax_snapshot: SyntaxSnapshot,
block_budget: Option<Duration>,
parse_again: bool,
cx: &mut Context<Self>,
) {
self.non_text_state_update_count += 1;
self.syntax_map.lock().did_parse(syntax_snapshot);
self.was_changed();
self.request_autoindent(cx, block_budget);
self.parse_status.0.send(ParseStatus::Idle).unwrap();
let parsing_complete = !parse_again || self.language.is_none();
if self.language.is_none() {
self.syntax_map.lock().clear(&self.text);
}
if parsing_complete {
self.request_autoindent(cx, block_budget);
self.parse_status.0.send(ParseStatus::Idle).unwrap();
}
Self::invalidate_tree_sitter_data(&mut self.tree_sitter_data, &self.text.snapshot());
cx.emit(BufferEvent::Reparsed);
cx.notify();
@@ -3473,7 +3486,7 @@ impl Buffer {
self.text.fast_forward(edited.text);
if edited.snapshot.language == self.language {
self.reparse = None;
self.did_finish_parsing(edited.snapshot.syntax, None, cx);
self.did_finish_parsing(edited.snapshot.syntax, None, false, cx);
if did_edit {
cx.emit(BufferEvent::Edited {
source: BufferEditSource::User,
+9 -1
View File
@@ -232,7 +232,15 @@ mod tests {
"#
.unindent();
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
let buffer = cx.new(|cx| {
let mut buffer = language::Buffer::local(text, cx);
buffer.set_sync_parse_timeout(None);
buffer.set_language(Some(language), cx);
buffer
});
buffer
.read_with(cx, |buffer, _| buffer.parsing_idle())
.await;
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
assert_eq!(
outline