language: Only block the foreground on buffer reparsing when necessary (#43888)

Lukas Wirth created

Gist is we only need to block the foreground thread for reparsing if
immediate language changes are useful to the user. That is usually only
the case when they edit the buffer

Release Notes:

- Improved performance of large project searches and project diffs

Co-authored by: David Kleingeld <david@zed.dev>

Change summary

crates/acp_thread/src/diff.rs                                             |   6 
crates/agent_ui/src/acp/message_editor.rs                                 |   3 
crates/agent_ui/src/acp/thread_view.rs                                    |   2 
crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs |   4 
crates/agent_ui/src/buffer_codegen.rs                                     |  16 
crates/agent_ui/src/text_thread_editor.rs                                 |   2 
crates/assistant_text_thread/src/text_thread.rs                           |   5 
crates/collab_ui/src/channel_view.rs                                      |   2 
crates/debugger_tools/src/dap_log.rs                                      |   4 
crates/debugger_ui/src/tests/inline_values.rs                             |   6 
crates/edit_prediction_context/src/excerpt.rs                             |   3 
crates/edit_prediction_context/src/imports.rs                             |   2 
crates/edit_prediction_context/src/reference.rs                           |   5 
crates/edit_prediction_context/src/syntax_index.rs                        |   2 
crates/editor/src/display_map.rs                                          |   8 
crates/editor/src/editor_tests.rs                                         | 137 
crates/editor/src/items.rs                                                |   2 
crates/editor/src/jsx_tag_auto_close.rs                                   |   6 
crates/editor/src/rust_analyzer_ext.rs                                    |   2 
crates/editor/src/test/editor_test_context.rs                             |   2 
crates/git_ui/src/git_panel.rs                                            |   2 
crates/git_ui/src/text_diff_view.rs                                       |   2 
crates/keymap_editor/src/keymap_editor.rs                                 |   4 
crates/language/src/buffer.rs                                             | 138 
crates/language/src/buffer_tests.rs                                       |  91 
crates/language_tools/src/lsp_log_view.rs                                 |   2 
crates/languages/src/bash.rs                                              |   2 
crates/languages/src/c.rs                                                 |   4 
crates/languages/src/cpp.rs                                               |   4 
crates/languages/src/css.rs                                               |   3 
crates/languages/src/go.rs                                                |  36 
crates/languages/src/python.rs                                            |   2 
crates/languages/src/rust.rs                                              |   2 
crates/languages/src/typescript.rs                                        |  15 
crates/project/src/buffer_store.rs                                        |  11 
crates/project/src/git_store.rs                                           |   4 
crates/project/src/lsp_store.rs                                           |   2 
crates/repl/src/notebook/cell.rs                                          |   2 
crates/repl/src/outputs.rs                                                |   2 
crates/repl/src/outputs/markdown.rs                                       |   2 
crates/repl/src/outputs/plain.rs                                          |   4 
crates/repl/src/repl_editor.rs                                            |  10 
crates/rules_library/src/rules_library.rs                                 |   2 
crates/search/src/buffer_search.rs                                        |   4 
crates/search/src/project_search.rs                                       |   7 
crates/sum_tree/src/cursor.rs                                             |   4 
crates/tasks_ui/src/tasks_ui.rs                                           |   6 
crates/vim/src/object.rs                                                  |   2 
crates/vim/src/test.rs                                                    |   4 
crates/zed/src/main.rs                                                    |   2 
crates/zeta/src/assemble_excerpts.rs                                      |   5 
crates/zeta/src/zeta1/input_excerpt.rs                                    |   3 
crates/zeta2_tools/src/zeta2_tools.rs                                     |   9 
crates/zlog/src/zlog.rs                                                   |   2 
54 files changed, 363 insertions(+), 250 deletions(-)

Detailed changes

crates/acp_thread/src/diff.rs 🔗

@@ -35,7 +35,9 @@ impl Diff {
                     .await
                     .log_err();
 
-                buffer.update(cx, |buffer, cx| buffer.set_language(language.clone(), cx))?;
+                buffer.update(cx, |buffer, cx| {
+                    buffer.set_language_immediate(language.clone(), cx)
+                })?;
 
                 let diff = build_buffer_diff(
                     old_text.unwrap_or("".into()).into(),
@@ -261,7 +263,7 @@ impl PendingDiff {
                 self.new_buffer.read(cx).as_rope().clone(),
             );
             let mut buffer = Buffer::build(buffer, None, Capability::ReadWrite);
-            buffer.set_language(language, cx);
+            buffer.set_language_immediate(language, cx);
             buffer
         });
 

crates/agent_ui/src/acp/message_editor.rs 🔗

@@ -118,7 +118,8 @@ impl MessageEditor {
         );
 
         let editor = cx.new(|cx| {
-            let buffer = cx.new(|cx| Buffer::local("", cx).with_language(Arc::new(language), cx));
+            let buffer =
+                cx.new(|cx| Buffer::local("", cx).with_language_immediate(Arc::new(language), cx));
             let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 
             let mut editor = Editor::new(mode, buffer, None, window, cx);

crates/agent_ui/src/acp/thread_view.rs 🔗

@@ -4839,7 +4839,7 @@ impl AcpThreadView {
 
             buffer.update(cx, |buffer, cx| {
                 buffer.set_text(markdown, cx);
-                buffer.set_language(Some(markdown_language), cx);
+                buffer.set_language_immediate(Some(markdown_language), cx);
                 buffer.set_capability(language::Capability::ReadWrite, cx);
             })?;
 

crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs 🔗

@@ -97,7 +97,9 @@ impl ConfigurationSource {
                 editor.set_show_gutter(false, cx);
                 editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx);
                 if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
-                    buffer.update(cx, |buffer, cx| buffer.set_language(jsonc_language, cx))
+                    buffer.update(cx, |buffer, cx| {
+                        buffer.set_language_immediate(jsonc_language, cx)
+                    })
                 }
                 editor
             })

crates/agent_ui/src/buffer_codegen.rs 🔗

@@ -269,9 +269,9 @@ impl CodegenAlternative {
                 .language_registry();
 
             let mut buffer = Buffer::local_normalized(text, line_ending, cx);
-            buffer.set_language(language, cx);
+            buffer.set_language_immediate(language, cx);
             if let Some(language_registry) = language_registry {
-                buffer.set_language_registry(language_registry)
+                buffer.set_language_registry(language_registry);
             }
             buffer
         });
@@ -1077,7 +1077,8 @@ mod tests {
                 }
             }
         "};
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer =
+            cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
         let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let range = buffer.read_with(cx, |buffer, cx| {
             let snapshot = buffer.snapshot(cx);
@@ -1139,7 +1140,8 @@ mod tests {
                 le
             }
         "};
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer =
+            cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
         let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let range = buffer.read_with(cx, |buffer, cx| {
             let snapshot = buffer.snapshot(cx);
@@ -1203,7 +1205,8 @@ mod tests {
             "  \n",
             "}\n" //
         );
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer =
+            cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
         let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let range = buffer.read_with(cx, |buffer, cx| {
             let snapshot = buffer.snapshot(cx);
@@ -1319,7 +1322,8 @@ mod tests {
                 let x = 0;
             }
         "};
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer =
+            cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
         let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let range = buffer.read_with(cx, |buffer, cx| {
             let snapshot = buffer.snapshot(cx);

crates/agent_ui/src/text_thread_editor.rs 🔗

@@ -3095,7 +3095,7 @@ mod tests {
             "#
             .unindent();
             let mut buffer = Buffer::local(text, cx);
-            buffer.set_language(Some(markdown.clone()), cx);
+            buffer.set_language_immediate(Some(markdown.clone()), cx);
             buffer
         });
         let snapshot = buffer.read(cx).snapshot();

crates/assistant_text_thread/src/text_thread.rs 🔗

@@ -1223,8 +1223,9 @@ impl TextThread {
         cx.spawn(async move |this, cx| {
             let markdown = markdown.await?;
             this.update(cx, |this, cx| {
-                this.buffer
-                    .update(cx, |buffer, cx| buffer.set_language(Some(markdown), cx));
+                this.buffer.update(cx, |buffer, cx| {
+                    buffer.set_language_immediate(Some(markdown), cx)
+                });
             })
         })
         .detach_and_log_err(cx);

crates/collab_ui/src/channel_view.rs 🔗

@@ -171,7 +171,7 @@ impl ChannelView {
                     let Some(markdown) = markdown else {
                         return;
                     };
-                    buffer.set_language(Some(markdown), cx);
+                    buffer.set_language_immediate(Some(markdown), cx);
                 })
             })?;
 

crates/debugger_tools/src/dap_log.rs 🔗

@@ -830,7 +830,7 @@ impl DapLogView {
                         async move |buffer, cx| {
                             let language = language.await.ok();
                             buffer.update(cx, |buffer, cx| {
-                                buffer.set_language(language, cx);
+                                buffer.set_language_immediate(language, cx);
                             })
                         }
                     })
@@ -901,7 +901,7 @@ impl DapLogView {
                         async move |_, cx| {
                             let language = language.await.ok();
                             buffer.update(cx, |buffer, cx| {
-                                buffer.set_language(language, cx);
+                                buffer.set_language_immediate(language, cx);
                             })
                         }
                     })

crates/debugger_ui/src/tests/inline_values.rs 🔗

@@ -224,7 +224,7 @@ fn main() {
         .unwrap();
 
     buffer.update(cx, |buffer, cx| {
-        buffer.set_language(Some(Arc::new(rust_lang())), cx);
+        buffer.set_language_immediate(Some(Arc::new(rust_lang())), cx);
     });
 
     let (editor, cx) = cx.add_window_view(|window, cx| {
@@ -1593,7 +1593,7 @@ def process_data(untyped_param, typed_param: int, another_typed: str):
         .unwrap();
 
     buffer.update(cx, |buffer, cx| {
-        buffer.set_language(Some(Arc::new(python_lang())), cx);
+        buffer.set_language_immediate(Some(Arc::new(python_lang())), cx);
     });
 
     let (editor, cx) = cx.add_window_view(|window, cx| {
@@ -2091,7 +2091,7 @@ async fn test_inline_values_util(
         .unwrap();
 
     buffer.update(cx, |buffer, cx| {
-        buffer.set_language(Some(Arc::new(language)), cx);
+        buffer.set_language_immediate(Some(Arc::new(language)), cx);
     });
 
     let (editor, cx) = cx.add_window_view(|window, cx| {

crates/edit_prediction_context/src/excerpt.rs 🔗

@@ -475,7 +475,8 @@ mod tests {
     use util::test::{generate_marked_text, marked_text_offsets_by};
 
     fn create_buffer(text: &str, cx: &mut TestAppContext) -> BufferSnapshot {
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang().into(), cx));
+        let buffer =
+            cx.new(|cx| Buffer::local(text, cx).with_language_immediate(rust_lang().into(), cx));
         buffer.read_with(cx, |buffer, _| buffer.snapshot())
     }
 

crates/edit_prediction_context/src/imports.rs 🔗

@@ -1100,7 +1100,7 @@ mod test {
     ) {
         let buffer = cx.new(|cx| {
             let mut buffer = Buffer::local(source, cx);
-            buffer.set_language(Some(language.clone()), cx);
+            buffer.set_language_immediate(Some(language.clone()), cx);
             buffer
         });
         cx.run_until_parked();

crates/edit_prediction_context/src/reference.rs 🔗

@@ -148,8 +148,9 @@ mod test {
     }
 
     fn create_buffer(text: &str, cx: &mut TestAppContext) -> BufferSnapshot {
-        let buffer =
-            cx.new(|cx| language::Buffer::local(text, cx).with_language(rust_lang().into(), cx));
+        let buffer = cx.new(|cx| {
+            language::Buffer::local(text, cx).with_language_immediate(rust_lang().into(), cx)
+        });
         buffer.read_with(cx, |buffer, _| buffer.snapshot())
     }
 

crates/edit_prediction_context/src/syntax_index.rs 🔗

@@ -531,7 +531,7 @@ impl SyntaxIndex {
 
                 let buffer = cx.new(|cx| {
                     let mut buffer = Buffer::local(loaded_file.text, cx);
-                    buffer.set_language(Some(language.clone()), cx);
+                    buffer.set_language_immediate(Some(language.clone()), cx);
                     buffer
                 })?;
 

crates/editor/src/display_map.rs 🔗

@@ -2210,7 +2210,7 @@ pub mod tests {
             })
         });
 
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
         cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
         let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 
@@ -2312,7 +2312,7 @@ pub mod tests {
 
         cx.update(|cx| init_test(cx, |_| {}));
 
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
         cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
         let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
         let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
@@ -2647,7 +2647,7 @@ pub mod tests {
 
         cx.update(|cx| init_test(cx, |_| {}));
 
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
         cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
         let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 
@@ -2734,7 +2734,7 @@ pub mod tests {
 
         let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»«:» B = "c «d»""#, false);
 
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
         cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
 
         let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));

crates/editor/src/editor_tests.rs 🔗

@@ -2878,7 +2878,7 @@ async fn test_delete_to_bracket(cx: &mut TestAppContext) {
     );
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     cx.set_state(r#"macro!("// ˇCOMMENT");"#);
     cx.update_editor(|editor, window, cx| {
@@ -3109,7 +3109,7 @@ async fn test_newline_yaml(cx: &mut TestAppContext) {
 
     let mut cx = EditorTestContext::new(cx).await;
     let yaml_language = languages::language("yaml", tree_sitter_yaml::LANGUAGE.into());
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(yaml_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(yaml_language), cx));
 
     // Object (between 2 fields)
     cx.set_state(indoc! {"
@@ -3272,7 +3272,7 @@ async fn test_newline_above(cx: &mut TestAppContext) {
     );
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
     cx.set_state(indoc! {"
         const a: ˇA = (
             (ˇ
@@ -3320,7 +3320,7 @@ async fn test_newline_below(cx: &mut TestAppContext) {
     );
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
     cx.set_state(indoc! {"
         const a: ˇA = (
             (ˇ
@@ -3367,7 +3367,7 @@ async fn test_newline_comments(cx: &mut TestAppContext) {
     ));
     {
         let mut cx = EditorTestContext::new(cx).await;
-        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+        cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
         cx.set_state(indoc! {"
         // Fooˇ
     "});
@@ -3439,7 +3439,7 @@ async fn test_newline_comments_with_multiple_delimiters(cx: &mut TestAppContext)
     ));
     {
         let mut cx = EditorTestContext::new(cx).await;
-        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+        cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
         cx.set_state(indoc! {"
         //ˇ
     "});
@@ -3486,7 +3486,7 @@ async fn test_newline_documentation_comments(cx: &mut TestAppContext) {
 
     {
         let mut cx = EditorTestContext::new(cx).await;
-        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+        cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
         cx.set_state(indoc! {"
         /**ˇ
     "});
@@ -3695,7 +3695,7 @@ async fn test_newline_comments_with_block_comment(cx: &mut TestAppContext) {
     ));
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(lua_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(lua_language), cx));
 
     // Line with line comment should extend
     cx.set_state(indoc! {"
@@ -3816,7 +3816,7 @@ async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppConte
         .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
         .unwrap(),
     );
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     // test when all cursors are not at suggested indent
     // then simply move to their suggested indent location
@@ -4040,7 +4040,7 @@ async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
     );
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
     cx.set_state(indoc! {"
         fn a() {
             if b {
@@ -4139,7 +4139,7 @@ async fn test_indent_yaml_comments_with_multiple_cursors(cx: &mut TestAppContext
 
     let mut cx = EditorTestContext::new(cx).await;
     let yaml_language = languages::language("yaml", tree_sitter_yaml::LANGUAGE.into());
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(yaml_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(yaml_language), cx));
 
     cx.set_state(
         r#"ˇ#     ingress:
@@ -4174,7 +4174,7 @@ async fn test_indent_yaml_non_comments_with_multiple_cursors(cx: &mut TestAppCon
 
     let mut cx = EditorTestContext::new(cx).await;
     let yaml_language = languages::language("yaml", tree_sitter_yaml::LANGUAGE.into());
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(yaml_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(yaml_language), cx));
 
     cx.set_state(
         r#"ˇingress:
@@ -4329,9 +4329,10 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
     ));
 
     let toml_buffer =
-        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
-    let rust_buffer =
-        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
+        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language_immediate(toml_language, cx));
+    let rust_buffer = cx.new(|cx| {
+        Buffer::local("const c: usize = 3;\n", cx).with_language_immediate(rust_language, cx)
+    });
     let multibuffer = cx.new(|cx| {
         let mut multibuffer = MultiBuffer::new(ReadWrite);
         multibuffer.push_excerpts(
@@ -5030,7 +5031,7 @@ async fn test_wrap_in_tag_single_selection(cx: &mut TestAppContext) {
         None,
     ));
 
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(js_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(js_language), cx));
 
     cx.set_state(indoc! {"
         «testˇ»
@@ -5079,7 +5080,7 @@ async fn test_wrap_in_tag_multi_selection(cx: &mut TestAppContext) {
         None,
     ));
 
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(js_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(js_language), cx));
 
     cx.set_state(indoc! {"
         «testˇ»
@@ -5122,7 +5123,7 @@ async fn test_wrap_in_tag_does_nothing_in_unsupported_languages(cx: &mut TestApp
         None,
     ));
 
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(plaintext_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(plaintext_language), cx));
 
     cx.set_state(indoc! {"
         «testˇ»
@@ -6568,7 +6569,7 @@ async fn test_rewrap(cx: &mut TestAppContext) {
         language: Arc<Language>,
         cx: &mut EditorTestContext,
     ) {
-        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+        cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
         cx.set_state(unwrapped_text);
         cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
         cx.assert_editor_state(wrapped_text);
@@ -6973,7 +6974,7 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) {
         language: Arc<Language>,
         cx: &mut EditorTestContext,
     ) {
-        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+        cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
         cx.set_state(unwrapped_text);
         cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
         cx.assert_editor_state(wrapped_text);
@@ -6985,7 +6986,7 @@ async fn test_hard_wrap(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
     let mut cx = EditorTestContext::new(cx).await;
 
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(git_commit_lang()), cx));
     cx.update_editor(|editor, _, cx| {
         editor.set_hard_wrap(Some(14), cx);
     });
@@ -7424,7 +7425,7 @@ async fn test_paste_multiline(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(rust_lang()), cx));
 
     // Cut an indented block, without the leading whitespace.
     cx.set_state(indoc! {"
@@ -7566,7 +7567,7 @@ async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
     ));
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(rust_lang()), cx));
 
     cx.set_state(indoc! {"
         fn a() {
@@ -8970,7 +8971,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 
@@ -9155,7 +9156,7 @@ async fn test_select_larger_syntax_node_for_cursor_at_end(cx: &mut TestAppContex
 
     let text = "let a = 2;";
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 
@@ -9225,7 +9226,7 @@ async fn test_select_larger_syntax_node_for_cursor_at_symbol(cx: &mut TestAppCon
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 
@@ -9402,7 +9403,7 @@ async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppConte
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 
@@ -9576,7 +9577,7 @@ async fn test_unwrap_syntax_nodes(cx: &mut gpui::TestAppContext) {
     ));
 
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(language), cx);
+        buffer.set_language_immediate(Some(language), cx);
     });
 
     cx.set_state(indoc! { r#"use mod1::{mod2::{«mod3ˇ», mod4}, mod5::{mod6, «mod7ˇ»}};"# });
@@ -9789,7 +9790,7 @@ async fn test_autoindent(cx: &mut TestAppContext) {
 
     let text = "fn a() {}";
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
     editor
@@ -9858,7 +9859,7 @@ async fn test_autoindent_disabled(cx: &mut TestAppContext) {
 
     let text = "fn a() {}";
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
     editor
@@ -9997,7 +9998,7 @@ async fn test_autoindent_disabled_with_nested_language(cx: &mut TestAppContext)
     cx.language_registry().add(language.clone());
 
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(language), cx);
+        buffer.set_language_immediate(Some(language), cx);
     });
 
     cx.set_state(r#"struct A {ˇ}"#);
@@ -10084,7 +10085,9 @@ async fn test_autoindent_selections(cx: &mut TestAppContext) {
             let buffer = editor.buffer().update(cx, |buffer, _| {
                 buffer.all_buffers().iter().next().unwrap().clone()
             });
-            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
+            buffer.update(cx, |buffer, cx| {
+                buffer.set_language_immediate(Some(rust_lang()), cx)
+            });
             buffer
         });
 
@@ -10177,7 +10180,7 @@ async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 
     cx.language_registry().add(language.clone());
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(language), cx);
+        buffer.set_language_immediate(Some(language), cx);
     });
 
     cx.set_state(
@@ -10366,7 +10369,7 @@ async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppCont
 
     cx.language_registry().add(language.clone());
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(language), cx);
+        buffer.set_language_immediate(Some(language), cx);
     });
 
     cx.set_state(
@@ -10509,7 +10512,7 @@ async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
     cx.executor().run_until_parked();
 
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(html_language), cx);
+        buffer.set_language_immediate(Some(html_language), cx);
     });
 
     cx.set_state(
@@ -10687,7 +10690,7 @@ async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 
     cx.language_registry().add(rust_language.clone());
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(rust_language), cx);
+        buffer.set_language_immediate(Some(rust_language), cx);
     });
 
     cx.set_state(
@@ -10794,7 +10797,7 @@ async fn test_surround_with_pair(cx: &mut TestAppContext) {
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
     editor
@@ -10944,7 +10947,7 @@ async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
     editor
@@ -11072,7 +11075,7 @@ async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext
 
     cx.language_registry().add(language.clone());
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(language), cx);
+        buffer.set_language_immediate(Some(language), cx);
     });
 
     cx.set_state(
@@ -11140,7 +11143,7 @@ async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
         Some(tree_sitter_rust::LANGUAGE.into()),
     ));
 
-    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local("", cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
     editor
@@ -13177,7 +13180,7 @@ async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 
     cx.language_registry().add(language.clone());
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(language), cx);
+        buffer.set_language_immediate(Some(language), cx);
     });
 
     cx.set_state(
@@ -13318,7 +13321,7 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestA
 
     cx.language_registry().add(language.clone());
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(language), cx);
+        buffer.set_language_immediate(Some(language), cx);
     });
 
     // Ensure that signature_help is not called when no signature help is enabled.
@@ -15900,7 +15903,7 @@ async fn test_toggle_comment(cx: &mut TestAppContext) {
         },
         Some(tree_sitter_rust::LANGUAGE.into()),
     ));
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     // If multiple selections intersect a line, the line is only toggled once.
     cx.set_state(indoc! {"
@@ -16021,7 +16024,7 @@ async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
         },
         Some(tree_sitter_rust::LANGUAGE.into()),
     ));
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     let toggle_comments = &ToggleComments {
         advance_downwards: false,
@@ -16151,7 +16154,7 @@ async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
 
     cx.language_registry().add(language.clone());
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(language), cx);
+        buffer.set_language_immediate(Some(language), cx);
     });
 
     let toggle_comments = &ToggleComments {
@@ -16309,7 +16312,7 @@ async fn test_toggle_block_comment(cx: &mut TestAppContext) {
     cx.language_registry().add(html_language.clone());
     cx.language_registry().add(javascript_language);
     cx.update_buffer(|buffer, cx| {
-        buffer.set_language(Some(html_language), cx);
+        buffer.set_language_immediate(Some(html_language), cx);
     });
 
     // Toggle comments for empty selections
@@ -16744,7 +16747,7 @@ async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
         "{{} }\n",     //
     );
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
     editor
@@ -17639,7 +17642,7 @@ async fn test_move_to_enclosing_bracket_in_markdown_code_block(cx: &mut TestAppC
             cx,
         );
         buffer.set_language_registry(language_registry.clone());
-        buffer.set_language(Some(markdown_lang()), cx);
+        buffer.set_language_immediate(Some(markdown_lang()), cx);
         buffer
     });
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
@@ -22451,7 +22454,7 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
     let project = Project::test(fs, ["/a".as_ref()], cx).await;
     let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
     let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
 
     let editor = cx.new_window_entity(|window, cx| {
@@ -23940,7 +23943,7 @@ async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
         )
         .unwrap(),
     );
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     cx.set_state(indoc! {"
         <span>ˇ</span>
@@ -24825,7 +24828,7 @@ async fn test_linked_edits_on_typing_punctuation(cx: &mut TestAppContext) {
         },
         Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
     ));
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     // Test typing > does not extend linked pair
     cx.set_state("<divˇ<div></div>");
@@ -25025,7 +25028,7 @@ async fn test_tab_in_leading_whitespace_auto_indents_for_python(cx: &mut TestApp
 
     let mut cx = EditorTestContext::new(cx).await;
     let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     // test cursor move to start of each line on tab
     // for `if`, `elif`, `else`, `while`, `with` and `for`
@@ -25135,7 +25138,7 @@ async fn test_outdent_after_input_for_python(cx: &mut TestAppContext) {
 
     let mut cx = EditorTestContext::new(cx).await;
     let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     // test `else` auto outdents when typed inside `if` block
     cx.set_state(indoc! {"
@@ -25367,7 +25370,7 @@ async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
     });
     let mut cx = EditorTestContext::new(cx).await;
     let language = languages::language("python", tree_sitter_python::LANGUAGE.into());
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     // test correct indent after newline on comment
     cx.set_state(indoc! {"
@@ -25428,7 +25431,7 @@ async fn test_tab_in_leading_whitespace_auto_indents_for_bash(cx: &mut TestAppCo
 
     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_immediate(Some(language), cx));
 
     // test cursor move to start of each line on tab
     // for `if`, `elif`, `else`, `while`, `for`, `case` and `function`
@@ -25522,7 +25525,7 @@ 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_immediate(Some(language), cx));
 
     // test indents on comment insert
     cx.set_state(indoc! {"
@@ -25564,7 +25567,7 @@ async fn test_outdent_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_immediate(Some(language), cx));
 
     // test `else` auto outdents when typed inside `if` block
     cx.set_state(indoc! {"
@@ -25717,7 +25720,7 @@ async fn test_indent_on_newline_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_immediate(Some(language), cx));
 
     // test correct indent after newline on comment
     cx.set_state(indoc! {"
@@ -27016,7 +27019,7 @@ async fn test_select_next_prev_syntax_node(cx: &mut TestAppContext) {
         }
     "#;
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
+    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language_immediate(language, cx));
     let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
     let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 
@@ -27208,7 +27211,7 @@ async fn test_paste_url_from_other_app_creates_markdown_link_over_selected_text(
     ));
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(markdown_language), cx));
     cx.set_state("Hello, «editorˇ».\nZed is «ˇgreat» (see this link: ˇ)");
 
     cx.update_editor(|editor, window, cx| {
@@ -27238,7 +27241,7 @@ async fn test_paste_url_from_zed_copy_creates_markdown_link_over_selected_text(
     ));
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(markdown_language), cx));
     cx.set_state(&format!(
         "Hello, editor.\nZed is great (see this link: )\n«{url}ˇ»"
     ));
@@ -27277,7 +27280,7 @@ async fn test_paste_url_from_other_app_replaces_existing_url_without_creating_ma
     ));
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(markdown_language), cx));
     cx.set_state("Please visit zed's homepage: «https://www.apple.comˇ»");
 
     cx.update_editor(|editor, window, cx| {
@@ -27305,7 +27308,7 @@ async fn test_paste_plain_text_from_other_app_replaces_selection_without_creatin
     ));
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(markdown_language), cx));
     cx.set_state("Hello, «editorˇ».\nZed is «ˇgreat»");
 
     cx.update_editor(|editor, window, cx| {
@@ -27333,7 +27336,7 @@ async fn test_paste_url_from_other_app_without_creating_markdown_link_in_non_mar
     ));
 
     let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(markdown_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(markdown_language), cx));
     cx.set_state("// Hello, «editorˇ».\n// Zed is «ˇgreat» (see this link: ˇ)");
 
     cx.update_editor(|editor, window, cx| {
@@ -27385,7 +27388,7 @@ async fn test_paste_url_from_other_app_creates_markdown_link_selectively_in_mult
             .unwrap();
         let first_buffer = multi_buffer.read(cx).buffer(first_buffer_id).unwrap();
         first_buffer.update(cx, |buffer, cx| {
-            buffer.set_language(Some(markdown_language.clone()), cx);
+            buffer.set_language_immediate(Some(markdown_language.clone()), cx);
         });
 
         editor
@@ -27688,7 +27691,7 @@ async fn test_sticky_scroll(cx: &mut TestAppContext) {
             .as_singleton()
             .unwrap()
             .update(cx, |buffer, cx| {
-                buffer.set_language(Some(rust_lang()), cx);
+                buffer.set_language_immediate(Some(rust_lang()), cx);
             })
     });
 
@@ -27778,7 +27781,7 @@ async fn test_scroll_by_clicking_sticky_header(cx: &mut TestAppContext) {
             .as_singleton()
             .unwrap()
             .update(cx, |buffer, cx| {
-                buffer.set_language(Some(rust_lang()), cx);
+                buffer.set_language_immediate(Some(rust_lang()), cx);
             })
     });
 

crates/editor/src/items.rs 🔗

@@ -1139,7 +1139,7 @@ impl SerializableItem for Editor {
                     buffer.update(cx, |buffer, cx| {
                         buffer.set_language_registry(language_registry);
                         if let Some(language) = language {
-                            buffer.set_language(Some(language), cx);
+                            buffer.set_language_immediate(Some(language), cx);
                         }
                         buffer.set_text(contents, cx);
                         if let Some(entry) = buffer.peek_undo_stack() {

crates/editor/src/jsx_tag_auto_close.rs 🔗

@@ -638,7 +638,7 @@ mod jsx_tag_autoclose_tests {
         cx.update_buffer(|buffer, cx| {
             let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
 
-            buffer.set_language(Some(language), cx)
+            buffer.set_language_immediate(Some(language), cx)
         });
 
         cx
@@ -802,7 +802,7 @@ mod jsx_tag_autoclose_tests {
 
         let buffer_a = cx.new(|cx| {
             let mut buf = language::Buffer::local("<div", cx);
-            buf.set_language(
+            buf.set_language_immediate(
                 Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
                 cx,
             );
@@ -810,7 +810,7 @@ mod jsx_tag_autoclose_tests {
         });
         let buffer_b = cx.new(|cx| {
             let mut buf = language::Buffer::local("<pre", cx);
-            buf.set_language(
+            buf.set_language_immediate(
                 Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
                 cx,
             );

crates/editor/src/rust_analyzer_ext.rs 🔗

@@ -205,7 +205,7 @@ pub fn expand_macro_recursively(
         workspace.update_in(cx, |workspace, window, cx| {
             buffer.update(cx, |buffer, cx| {
                 buffer.set_text(macro_expansion.expansion, cx);
-                buffer.set_language(Some(rust_language), cx);
+                buffer.set_language_immediate(Some(rust_language), cx);
                 buffer.set_capability(Capability::ReadOnly, cx);
             });
             let multibuffer =

crates/editor/src/test/editor_test_context.rs 🔗

@@ -67,7 +67,7 @@ impl EditorTestContext {
             .await
             .unwrap();
         buffer.update(cx, |buffer, cx| {
-            buffer.set_language(Some(language), cx);
+            buffer.set_language_immediate(Some(language), cx);
         });
 
         let editor = cx.add_window(|window, cx| {

crates/git_ui/src/git_panel.rs 🔗

@@ -1628,7 +1628,7 @@ impl GitPanel {
         }
         let buffer = cx.new(|cx| {
             let mut buffer = Buffer::local(message, cx);
-            buffer.set_language(git_commit_language, cx);
+            buffer.set_language_immediate(git_commit_language, cx);
             buffer
         });
         let editor = cx.new(|cx| Editor::for_buffer(buffer, None, window, cx));

crates/git_ui/src/text_diff_view.rs 🔗

@@ -240,7 +240,7 @@ fn build_clipboard_buffer(
     cx.new(|cx| {
         let mut buffer = language::Buffer::local(source_buffer_snapshot.text(), cx);
         let language = source_buffer.read(cx).language().cloned();
-        buffer.set_language(language, cx);
+        buffer.set_language_immediate(language, cx);
 
         let range_start = source_buffer_snapshot.point_to_offset(replacement_range.start);
         let range_end = source_buffer_snapshot.point_to_offset(replacement_range.end);

crates/keymap_editor/src/keymap_editor.rs 🔗

@@ -2225,7 +2225,7 @@ impl KeybindingEditorModal {
                     .update(cx, |editor, cx| {
                         if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
                             buffer.update(cx, |buffer, cx| {
-                                buffer.set_language(Some(language), cx);
+                                buffer.set_language_immediate(Some(language), cx);
                             });
                         }
                         editor.set_completion_provider(Some(std::rc::Rc::new(
@@ -2815,7 +2815,7 @@ impl ActionArgumentsEditor {
                     this.editor.update(cx, |editor, cx| {
                         if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
                             buffer.update(cx, |buffer, cx| {
-                                buffer.set_language(Some(json_language.clone()), cx)
+                                buffer.set_language_immediate(Some(json_language.clone()), cx)
                             });
                         }
                     })

crates/language/src/buffer.rs 🔗

@@ -1036,6 +1036,16 @@ impl Buffer {
         self
     }
 
+    /// Assign a language to the buffer, blocking for up to 1ms to reparse the buffer, returning the buffer.
+    pub fn with_language_immediate(
+        mut self,
+        language: Arc<Language>,
+        cx: &mut Context<Self>,
+    ) -> Self {
+        self.set_language_immediate(Some(language), cx);
+        self
+    }
+
     /// Returns the [`Capability`] of this buffer.
     pub fn capability(&self) -> Capability {
         self.capability
@@ -1210,7 +1220,7 @@ impl Buffer {
             }
 
             // Reparse the branch buffer so that we get syntax highlighting immediately.
-            branch.reparse(cx);
+            branch.reparse(cx, true);
 
             branch
         })
@@ -1363,11 +1373,29 @@ impl Buffer {
 
     /// Assign a language to the buffer.
     pub fn set_language(&mut self, language: Option<Arc<Language>>, cx: &mut Context<Self>) {
+        self.set_language_(language, cfg!(any(test, feature = "test-support")), cx);
+    }
+
+    /// Assign a language to the buffer, blocking for up to 1ms to reparse the buffer.
+    pub fn set_language_immediate(
+        &mut self,
+        language: Option<Arc<Language>>,
+        cx: &mut Context<Self>,
+    ) {
+        self.set_language_(language, true, cx);
+    }
+
+    fn set_language_(
+        &mut self,
+        language: Option<Arc<Language>>,
+        may_block: bool,
+        cx: &mut Context<Self>,
+    ) {
         self.non_text_state_update_count += 1;
         self.syntax_map.lock().clear(&self.text);
         self.language = language;
         self.was_changed();
-        self.reparse(cx);
+        self.reparse(cx, may_block);
         cx.emit(BufferEvent::LanguageChanged);
     }
 
@@ -1610,9 +1638,9 @@ impl Buffer {
     /// The snapshot with the interpolated edits is sent to a background thread,
     /// where we ask Tree-sitter to perform an incremental parse.
     ///
-    /// Meanwhile, in the foreground, we block the main thread for up to 1ms
-    /// waiting on the parse to complete. As soon as it completes, we proceed
-    /// synchronously, unless a 1ms timeout elapses.
+    /// Meanwhile, in the foreground if `may_block` is true, we block the main
+    /// thread for up to 1ms waiting on the parse to complete. As soon as it
+    /// completes, we proceed synchronously, unless a 1ms timeout elapses.
     ///
     /// If we time out waiting on the parse, we spawn a second task waiting
     /// until the parse does complete and return with the interpolated tree still
@@ -1623,7 +1651,7 @@ impl Buffer {
     /// initiate an additional reparse recursively. To avoid concurrent parses
     /// for the same buffer, we only initiate a new parse if we are not already
     /// parsing in the background.
-    pub fn reparse(&mut self, cx: &mut Context<Self>) {
+    pub fn reparse(&mut self, cx: &mut Context<Self>, may_block: bool) {
         if self.reparse.is_some() {
             return;
         }
@@ -1652,42 +1680,70 @@ impl Buffer {
         });
 
         self.parse_status.0.send(ParseStatus::Parsing).unwrap();
-        match cx
-            .background_executor()
-            .block_with_timeout(self.sync_parse_timeout, parse_task)
-        {
-            Ok(new_syntax_snapshot) => {
-                self.did_finish_parsing(new_syntax_snapshot, cx);
-                self.reparse = None;
-            }
-            Err(parse_task) => {
-                // todo(lw): hot foreground spawn
-                self.reparse = Some(cx.spawn(async move |this, cx| {
-                    let new_syntax_map = cx.background_spawn(parse_task).await;
-                    this.update(cx, move |this, cx| {
-                        let grammar_changed = || {
-                            this.language.as_ref().is_none_or(|current_language| {
-                                !Arc::ptr_eq(&language, current_language)
-                            })
-                        };
-                        let language_registry_changed = || {
-                            new_syntax_map.contains_unknown_injections()
-                                && language_registry.is_some_and(|registry| {
-                                    registry.version() != new_syntax_map.language_registry_version()
+        if may_block {
+            match cx
+                .background_executor()
+                .block_with_timeout(self.sync_parse_timeout, parse_task)
+            {
+                Ok(new_syntax_snapshot) => {
+                    self.did_finish_parsing(new_syntax_snapshot, cx);
+                    self.reparse = None;
+                }
+                Err(parse_task) => {
+                    self.reparse = Some(cx.spawn(async move |this, cx| {
+                        let new_syntax_map = cx.background_spawn(parse_task).await;
+                        this.update(cx, move |this, cx| {
+                            let grammar_changed = || {
+                                this.language.as_ref().is_none_or(|current_language| {
+                                    !Arc::ptr_eq(&language, current_language)
                                 })
-                        };
-                        let parse_again = this.version.changed_since(&parsed_version)
-                            || language_registry_changed()
-                            || grammar_changed();
-                        this.did_finish_parsing(new_syntax_map, cx);
-                        this.reparse = None;
-                        if parse_again {
-                            this.reparse(cx);
-                        }
-                    })
-                    .ok();
-                }));
+                            };
+                            let language_registry_changed = || {
+                                new_syntax_map.contains_unknown_injections()
+                                    && language_registry.is_some_and(|registry| {
+                                        registry.version()
+                                            != new_syntax_map.language_registry_version()
+                                    })
+                            };
+                            let parse_again = this.version.changed_since(&parsed_version)
+                                || language_registry_changed()
+                                || grammar_changed();
+                            this.did_finish_parsing(new_syntax_map, cx);
+                            this.reparse = None;
+                            if parse_again {
+                                this.reparse(cx, false);
+                            }
+                        })
+                        .ok();
+                    }));
+                }
             }
+        } else {
+            self.reparse = Some(cx.spawn(async move |this, cx| {
+                let new_syntax_map = cx.background_spawn(parse_task).await;
+                this.update(cx, move |this, cx| {
+                    let grammar_changed = || {
+                        this.language.as_ref().is_none_or(|current_language| {
+                            !Arc::ptr_eq(&language, current_language)
+                        })
+                    };
+                    let language_registry_changed = || {
+                        new_syntax_map.contains_unknown_injections()
+                            && language_registry.is_some_and(|registry| {
+                                registry.version() != new_syntax_map.language_registry_version()
+                            })
+                    };
+                    let parse_again = this.version.changed_since(&parsed_version)
+                        || language_registry_changed()
+                        || grammar_changed();
+                    this.did_finish_parsing(new_syntax_map, cx);
+                    this.reparse = None;
+                    if parse_again {
+                        this.reparse(cx, false);
+                    }
+                })
+                .ok();
+            }));
         }
     }
 
@@ -2588,7 +2644,7 @@ impl Buffer {
             return;
         }
 
-        self.reparse(cx);
+        self.reparse(cx, true);
         cx.emit(BufferEvent::Edited);
         if was_dirty != self.is_dirty() {
             cx.emit(BufferEvent::DirtyChanged);

crates/language/src/buffer_tests.rs 🔗

@@ -46,8 +46,8 @@ fn test_line_endings(cx: &mut gpui::App) {
     init_settings(cx, |_| {});
 
     cx.new(|cx| {
-        let mut buffer =
-            Buffer::local("one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local("one\r\ntwo\rthree", cx)
+            .with_language_immediate(Arc::new(rust_lang()), cx);
         assert_eq!(buffer.text(), "one\ntwo\nthree");
         assert_eq!(buffer.line_ending(), LineEnding::Windows);
 
@@ -608,7 +608,8 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
 #[gpui::test]
 async fn test_reparse(cx: &mut gpui::TestAppContext) {
     let text = "fn a() {}";
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer =
+        cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
 
     // Wait for the initial text to parse
     cx.executor().run_until_parked();
@@ -735,7 +736,7 @@ async fn test_reparse(cx: &mut gpui::TestAppContext) {
 #[gpui::test]
 async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
     let buffer = cx.new(|cx| {
-        let mut buffer = Buffer::local("{}", cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local("{}", cx).with_language_immediate(Arc::new(rust_lang()), cx);
         buffer.set_sync_parse_timeout(Duration::ZERO);
         buffer
     });
@@ -748,7 +749,7 @@ async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
     );
 
     buffer.update(cx, |buffer, cx| {
-        buffer.set_language(Some(Arc::new(json_lang())), cx)
+        buffer.set_language_immediate(Some(Arc::new(json_lang())), cx)
     });
     cx.executor().run_until_parked();
     assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
@@ -783,7 +784,8 @@ async fn test_outline(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer =
+        cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
     let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None));
 
     assert_eq!(
@@ -865,7 +867,8 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer =
+        cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
     let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None));
 
     assert_eq!(
@@ -899,7 +902,8 @@ async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(language), cx));
+    let buffer =
+        cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(language), cx));
     let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 
     // extra context nodes are included in the outline.
@@ -945,7 +949,8 @@ fn test_outline_annotations(cx: &mut App) {
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer =
+        cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
     let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None));
 
     assert_eq!(
@@ -993,7 +998,8 @@ async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
     "#
     .unindent();
 
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer =
+        cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
     let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 
     // point is at the start of an item
@@ -1068,7 +1074,8 @@ async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
     "
         .unindent(),
     );
-    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer =
+        cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
     let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 
     // note, it would be nice to actually return the method test in this
@@ -1087,8 +1094,9 @@ fn test_text_objects(cx: &mut App) {
         false,
     );
 
-    let buffer =
-        cx.new(|cx| Buffer::local(text.clone(), cx).with_language(Arc::new(rust_lang()), cx));
+    let buffer = cx.new(|cx| {
+        Buffer::local(text.clone(), cx).with_language_immediate(Arc::new(rust_lang()), cx)
+    });
     let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 
     let matches = snapshot
@@ -1268,7 +1276,7 @@ fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &
 fn test_range_for_syntax_ancestor(cx: &mut App) {
     cx.new(|cx| {
         let text = "fn a() { b(|c| {}) }";
-        let buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
         let snapshot = buffer.snapshot();
 
         assert_eq!(
@@ -1320,7 +1328,7 @@ fn test_autoindent_with_soft_tabs(cx: &mut App) {
 
     cx.new(|cx| {
         let text = "fn a() {}";
-        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
 
         buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
         assert_eq!(buffer.text(), "fn a() {\n    \n}");
@@ -1362,7 +1370,7 @@ fn test_autoindent_with_hard_tabs(cx: &mut App) {
 
     cx.new(|cx| {
         let text = "fn a() {}";
-        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
 
         buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
         assert_eq!(buffer.text(), "fn a() {\n\t\n}");
@@ -1411,7 +1419,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut App)
             .unindent(),
             cx,
         )
-        .with_language(Arc::new(rust_lang()), cx);
+        .with_language_immediate(Arc::new(rust_lang()), cx);
 
         // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
         // their indentation is not adjusted.
@@ -1552,7 +1560,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut App)
             .unindent(),
             cx,
         )
-        .with_language(Arc::new(rust_lang()), cx);
+        .with_language_immediate(Arc::new(rust_lang()), cx);
 
         // Insert a closing brace. It is outdented.
         buffer.edit_via_marked_text(
@@ -1615,7 +1623,7 @@ fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut Ap
             .unindent(),
             cx,
         )
-        .with_language(Arc::new(rust_lang()), cx);
+        .with_language_immediate(Arc::new(rust_lang()), cx);
 
         // Regression test: line does not get outdented due to syntax error
         buffer.edit_via_marked_text(
@@ -1674,7 +1682,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut App) {
             .unindent(),
             cx,
         )
-        .with_language(Arc::new(rust_lang()), cx);
+        .with_language_immediate(Arc::new(rust_lang()), cx);
 
         buffer.edit_via_marked_text(
             &"
@@ -1724,7 +1732,7 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut App) {
 
     cx.new(|cx| {
         let text = "a\nb";
-        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
         buffer.edit(
             [(0..1, "\n"), (2..3, "\n")],
             Some(AutoindentMode::EachLine),
@@ -1750,7 +1758,7 @@ fn test_autoindent_multi_line_insertion(cx: &mut App) {
         "
         .unindent();
 
-        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
         buffer.edit(
             [(Point::new(3, 0)..Point::new(3, 0), "e(\n    f()\n);\n")],
             Some(AutoindentMode::EachLine),
@@ -1787,7 +1795,7 @@ fn test_autoindent_block_mode(cx: &mut App) {
             }
         "#
         .unindent();
-        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
 
         // When this text was copied, both of the quotation marks were at the same
         // indent level, but the indentation of the first line was not included in
@@ -1870,7 +1878,7 @@ fn test_autoindent_block_mode_with_newline(cx: &mut App) {
             }
         "#
         .unindent();
-        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
 
         // First line contains just '\n', it's indentation is stored in "original_indent_columns"
         let original_indent_columns = vec![Some(4)];
@@ -1922,7 +1930,7 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut App) {
             }
         "#
         .unindent();
-        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
 
         // The original indent columns are not known, so this text is
         // auto-indented in a block as if the first line was copied in
@@ -2013,7 +2021,7 @@ fn test_autoindent_block_mode_multiple_adjacent_ranges(cx: &mut App) {
             false,
         );
 
-        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
 
         buffer.edit(
             [
@@ -2064,7 +2072,7 @@ fn test_autoindent_language_without_indents_query(cx: &mut App) {
         "
         .unindent();
 
-        let mut buffer = Buffer::local(text, cx).with_language(
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(
             Arc::new(Language::new(
                 LanguageConfig {
                     name: "Markdown".into(),
@@ -2142,7 +2150,7 @@ fn test_autoindent_with_injected_languages(cx: &mut App) {
 
         let mut buffer = Buffer::local(text, cx);
         buffer.set_language_registry(language_registry);
-        buffer.set_language(Some(html_language), cx);
+        buffer.set_language_immediate(Some(html_language), cx);
         buffer.edit(
             ranges.into_iter().map(|range| (range, "\na")),
             Some(AutoindentMode::EachLine),
@@ -2176,7 +2184,7 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut App) {
     });
 
     cx.new(|cx| {
-        let mut buffer = Buffer::local("", cx).with_language(Arc::new(ruby_lang()), cx);
+        let mut buffer = Buffer::local("", cx).with_language_immediate(Arc::new(ruby_lang()), cx);
 
         let text = r#"
             class C
@@ -2221,7 +2229,7 @@ async fn test_async_autoindents_preserve_preview(cx: &mut TestAppContext) {
     // Then we request that a preview tab be preserved for the new version, even though it's edited.
     let buffer = cx.new(|cx| {
         let text = "fn a() {}";
-        let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
+        let mut buffer = Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx);
 
         // This causes autoindent to be async.
         buffer.set_sync_parse_timeout(Duration::ZERO);
@@ -2434,7 +2442,7 @@ fn test_language_scope_at_with_javascript(cx: &mut App) {
         "#
         .unindent();
 
-        let buffer = Buffer::local(&text, cx).with_language(Arc::new(language), cx);
+        let buffer = Buffer::local(&text, cx).with_language_immediate(Arc::new(language), cx);
         let snapshot = buffer.snapshot();
 
         let config = snapshot.language_scope_at(0).unwrap();
@@ -2592,7 +2600,8 @@ fn test_language_scope_at_with_rust(cx: &mut App) {
         "#
         .unindent();
 
-        let buffer = Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx);
+        let buffer =
+            Buffer::local(text.clone(), cx).with_language_immediate(Arc::new(language), cx);
         let snapshot = buffer.snapshot();
 
         // By default, all brackets are enabled
@@ -2638,7 +2647,7 @@ fn test_language_scope_at_with_combined_injections(cx: &mut App) {
 
         let mut buffer = Buffer::local(text, cx);
         buffer.set_language_registry(language_registry.clone());
-        buffer.set_language(
+        buffer.set_language_immediate(
             language_registry
                 .language_for_name("HTML+ERB")
                 .now_or_never()
@@ -2684,7 +2693,7 @@ fn test_language_at_with_hidden_languages(cx: &mut App) {
 
         let mut buffer = Buffer::local(text, cx);
         buffer.set_language_registry(language_registry.clone());
-        buffer.set_language(
+        buffer.set_language_immediate(
             language_registry
                 .language_for_name("Markdown")
                 .now_or_never()
@@ -2727,7 +2736,7 @@ fn test_language_at_for_markdown_code_block(cx: &mut App) {
 
         let mut buffer = Buffer::local(text, cx);
         buffer.set_language_registry(language_registry.clone());
-        buffer.set_language(
+        buffer.set_language_immediate(
             language_registry
                 .language_for_name("Markdown")
                 .now_or_never()
@@ -2780,7 +2789,7 @@ fn test_syntax_layer_at_for_injected_languages(cx: &mut App) {
 
         let mut buffer = Buffer::local(text, cx);
         buffer.set_language_registry(language_registry.clone());
-        buffer.set_language(
+        buffer.set_language_immediate(
             language_registry
                 .language_for_name("HTML+ERB")
                 .now_or_never()
@@ -3120,7 +3129,8 @@ async fn test_preview_edits(cx: &mut TestAppContext) {
         cx: &mut TestAppContext,
         assert_fn: impl Fn(HighlightedText),
     ) {
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer =
+            cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
         let edits = buffer.read_with(cx, |buffer, _| {
             edits
                 .into_iter()
@@ -3531,7 +3541,7 @@ let word=öäpple.bar你 Öäpple word2-öÄpPlE-Pizza-word ÖÄPPLE word
     "#;
 
     let buffer = cx.new(|cx| {
-        let buffer = Buffer::local(contents, cx).with_language(Arc::new(rust_lang()), cx);
+        let buffer = Buffer::local(contents, cx).with_language_immediate(Arc::new(rust_lang()), cx);
         assert_eq!(buffer.text(), contents);
         buffer.check_invariants();
         buffer
@@ -3921,8 +3931,9 @@ fn assert_bracket_pairs(
     cx: &mut App,
 ) {
     let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
-    let buffer =
-        cx.new(|cx| Buffer::local(expected_text.clone(), cx).with_language(Arc::new(language), cx));
+    let buffer = cx.new(|cx| {
+        Buffer::local(expected_text.clone(), cx).with_language_immediate(Arc::new(language), cx)
+    });
     let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
 
     let selection_range = selection_ranges[0].clone();

crates/language_tools/src/lsp_log_view.rs 🔗

@@ -560,7 +560,7 @@ impl LspLogView {
                         async move |_, cx| {
                             let language = language.await.ok();
                             buffer.update(cx, |buffer, cx| {
-                                buffer.set_language(language, cx);
+                                buffer.set_language_immediate(language, cx);
                             })
                         }
                     })

crates/languages/src/bash.rs 🔗

@@ -40,7 +40,7 @@ mod tests {
         });
 
         cx.new(|cx| {
-            let mut buffer = Buffer::local("", cx).with_language(language, cx);
+            let mut buffer = Buffer::local("", cx).with_language_immediate(language, cx);
 
             let expect_indents_to =
                 |buffer: &mut Buffer, cx: &mut Context<Buffer>, input: &str, expected: &str| {

crates/languages/src/c.rs 🔗

@@ -388,7 +388,7 @@ mod tests {
         let language = crate::language("c", tree_sitter_c::LANGUAGE.into());
 
         cx.new(|cx| {
-            let mut buffer = Buffer::local("", cx).with_language(language, cx);
+            let mut buffer = Buffer::local("", cx).with_language_immediate(language, cx);
 
             buffer.edit([(0..0, "int main() {}")], None, cx);
 
@@ -418,7 +418,7 @@ mod tests {
         let language = crate::language("c", tree_sitter_c::LANGUAGE.into());
 
         cx.new(|cx| {
-            let mut buffer = Buffer::local("", cx).with_language(language, cx);
+            let mut buffer = Buffer::local("", cx).with_language_immediate(language, cx);
 
             buffer.edit(
                 [(

crates/languages/src/cpp.rs 🔗

@@ -20,7 +20,7 @@ mod tests {
         let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
 
         cx.new(|cx| {
-            let mut buffer = Buffer::local("", cx).with_language(language, cx);
+            let mut buffer = Buffer::local("", cx).with_language_immediate(language, cx);
 
             buffer.edit([(0..0, "int main() {}")], None, cx);
 
@@ -50,7 +50,7 @@ mod tests {
         let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
 
         cx.new(|cx| {
-            let mut buffer = Buffer::local("", cx).with_language(language, cx);
+            let mut buffer = Buffer::local("", cx).with_language_immediate(language, cx);
 
             buffer.edit(
                 [(

crates/languages/src/css.rs 🔗

@@ -224,7 +224,8 @@ mod tests {
         "#
         .unindent();
 
-        let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
+        let buffer =
+            cx.new(|cx| language::Buffer::local(text, cx).with_language_immediate(language, cx));
         let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
         assert_eq!(
             outline

crates/languages/src/go.rs 🔗

@@ -868,8 +868,9 @@ mod tests {
         }
         "#;
 
-        let buffer = cx
-            .new(|cx| crate::Buffer::local(testify_suite, cx).with_language(language.clone(), cx));
+        let buffer = cx.new(|cx| {
+            crate::Buffer::local(testify_suite, cx).with_language_immediate(language.clone(), cx)
+        });
         cx.executor().run_until_parked();
 
         let runnables: Vec<_> = buffer.update(cx, |buffer, _| {
@@ -926,7 +927,8 @@ mod tests {
         "#;
 
         let buffer = cx.new(|cx| {
-            crate::Buffer::local(interpreted_string_subtest, cx).with_language(language.clone(), cx)
+            crate::Buffer::local(interpreted_string_subtest, cx)
+                .with_language_immediate(language.clone(), cx)
         });
         cx.executor().run_until_parked();
 
@@ -955,7 +957,8 @@ mod tests {
         );
 
         let buffer = cx.new(|cx| {
-            crate::Buffer::local(raw_string_subtest, cx).with_language(language.clone(), cx)
+            crate::Buffer::local(raw_string_subtest, cx)
+                .with_language_immediate(language.clone(), cx)
         });
         cx.executor().run_until_parked();
 
@@ -999,8 +1002,9 @@ mod tests {
         }
         "#;
 
-        let buffer =
-            cx.new(|cx| crate::Buffer::local(example_test, cx).with_language(language.clone(), cx));
+        let buffer = cx.new(|cx| {
+            crate::Buffer::local(example_test, cx).with_language_immediate(language.clone(), cx)
+        });
         cx.executor().run_until_parked();
 
         let runnables: Vec<_> = buffer.update(cx, |buffer, _| {
@@ -1070,8 +1074,9 @@ mod tests {
         }
         "#;
 
-        let buffer =
-            cx.new(|cx| crate::Buffer::local(table_test, cx).with_language(language.clone(), cx));
+        let buffer = cx.new(|cx| {
+            crate::Buffer::local(table_test, cx).with_language_immediate(language.clone(), cx)
+        });
         cx.executor().run_until_parked();
 
         let runnables: Vec<_> = buffer.update(cx, |buffer, _| {
@@ -1138,8 +1143,9 @@ mod tests {
         }
         "#;
 
-        let buffer =
-            cx.new(|cx| crate::Buffer::local(table_test, cx).with_language(language.clone(), cx));
+        let buffer = cx.new(|cx| {
+            crate::Buffer::local(table_test, cx).with_language_immediate(language.clone(), cx)
+        });
         cx.executor().run_until_parked();
 
         let runnables: Vec<_> = buffer.update(cx, |buffer, _| {
@@ -1210,8 +1216,9 @@ mod tests {
         }
         "#;
 
-        let buffer =
-            cx.new(|cx| crate::Buffer::local(table_test, cx).with_language(language.clone(), cx));
+        let buffer = cx.new(|cx| {
+            crate::Buffer::local(table_test, cx).with_language_immediate(language.clone(), cx)
+        });
         cx.executor().run_until_parked();
 
         let runnables: Vec<_> = buffer.update(cx, |buffer, _| {
@@ -1277,8 +1284,9 @@ mod tests {
         }
         "#;
 
-        let buffer =
-            cx.new(|cx| crate::Buffer::local(table_test, cx).with_language(language.clone(), cx));
+        let buffer = cx.new(|cx| {
+            crate::Buffer::local(table_test, cx).with_language_immediate(language.clone(), cx)
+        });
         cx.executor().run_until_parked();
 
         let runnables: Vec<_> = buffer.update(cx, |buffer, _| {

crates/languages/src/python.rs 🔗

@@ -2326,7 +2326,7 @@ mod tests {
         });
 
         cx.new(|cx| {
-            let mut buffer = Buffer::local("", cx).with_language(language, cx);
+            let mut buffer = Buffer::local("", cx).with_language_immediate(language, cx);
             let append = |buffer: &mut Buffer, text: &str, cx: &mut Context<Buffer>| {
                 let ix = buffer.len();
                 buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx);

crates/languages/src/rust.rs 🔗

@@ -1430,7 +1430,7 @@ mod tests {
         let language = crate::language("rust", tree_sitter_rust::LANGUAGE.into());
 
         cx.new(|cx| {
-            let mut buffer = Buffer::local("", cx).with_language(language, cx);
+            let mut buffer = Buffer::local("", cx).with_language_immediate(language, cx);
 
             // indent between braces
             buffer.set_text("fn a() {}", cx);

crates/languages/src/typescript.rs 🔗

@@ -895,7 +895,8 @@ mod tests {
         "#
             .unindent();
 
-            let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
+            let buffer = cx
+                .new(|cx| language::Buffer::local(text, cx).with_language_immediate(language, cx));
             let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
             assert_eq!(
                 outline
@@ -954,7 +955,8 @@ mod tests {
         "#
             .unindent();
 
-            let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
+            let buffer = cx
+                .new(|cx| language::Buffer::local(text, cx).with_language_immediate(language, cx));
             let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
             assert_eq!(
                 outline
@@ -1026,7 +1028,8 @@ mod tests {
         "#
             .unindent();
 
-            let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
+            let buffer = cx
+                .new(|cx| language::Buffer::local(text, cx).with_language_immediate(language, cx));
             let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
             assert_eq!(
                 outline
@@ -1106,7 +1109,8 @@ mod tests {
         "#
             .unindent();
 
-            let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
+            let buffer = cx
+                .new(|cx| language::Buffer::local(text, cx).with_language_immediate(language, cx));
             let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
             assert_eq!(
                 outline
@@ -1177,7 +1181,8 @@ mod tests {
         "#
         .unindent();
 
-        let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
+        let buffer =
+            cx.new(|cx| language::Buffer::local(text, cx).with_language_immediate(language, cx));
         let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
         assert_eq!(
             outline

crates/project/src/buffer_store.rs 🔗

@@ -684,8 +684,9 @@ impl LocalBufferStore {
         cx: &mut Context<BufferStore>,
     ) -> Task<Result<Entity<Buffer>>> {
         cx.spawn(async move |buffer_store, cx| {
-            let buffer =
-                cx.new(|cx| Buffer::local("", cx).with_language(language::PLAIN_TEXT.clone(), cx))?;
+            let buffer = cx.new(|cx| {
+                Buffer::local("", cx).with_language_immediate(language::PLAIN_TEXT.clone(), cx)
+            })?;
             buffer_store.update(cx, |buffer_store, cx| {
                 buffer_store.add_buffer(buffer.clone(), cx).log_err();
                 if !project_searchable {
@@ -1591,8 +1592,10 @@ impl BufferStore {
         cx: &mut Context<Self>,
     ) -> Entity<Buffer> {
         let buffer = cx.new(|cx| {
-            Buffer::local(text, cx)
-                .with_language(language.unwrap_or_else(|| language::PLAIN_TEXT.clone()), cx)
+            Buffer::local(text, cx).with_language_immediate(
+                language.unwrap_or_else(|| language::PLAIN_TEXT.clone()),
+                cx,
+            )
         });
 
         self.add_buffer(buffer.clone(), cx).log_err();

crates/project/src/git_store.rs 🔗

@@ -3885,7 +3885,7 @@ impl Repository {
                         let git_commit_language =
                             language_registry.language_for_name("Git Commit").await?;
                         buffer.update(&mut cx, |buffer, cx| {
-                            buffer.set_language(Some(git_commit_language), cx);
+                            buffer.set_language_immediate(Some(git_commit_language), cx);
                         })?;
                     }
                     this.update(&mut cx, |this, _| {
@@ -3912,7 +3912,7 @@ impl Repository {
             if let Some(language_registry) = language_registry {
                 let git_commit_language = language_registry.language_for_name("Git Commit").await?;
                 buffer.update(cx, |buffer, cx| {
-                    buffer.set_language(Some(git_commit_language), cx);
+                    buffer.set_language_immediate(Some(git_commit_language), cx);
                 })?;
             }
 

crates/project/src/lsp_store.rs 🔗

@@ -4353,7 +4353,7 @@ impl LspStore {
                         }
 
                         for buffer in buffers_with_unknown_injections {
-                            buffer.update(cx, |buffer, cx| buffer.reparse(cx));
+                            buffer.update(cx, |buffer, cx| buffer.reparse(cx, false));
                         }
                     })
                     .ok();

crates/repl/src/notebook/cell.rs 🔗

@@ -210,7 +210,7 @@ impl Cell {
                     let language = notebook_language.await;
 
                     buffer.update(cx, |buffer, cx| {
-                        buffer.set_language(language.clone(), cx);
+                        buffer.set_language_immediate(language.clone(), cx);
                     });
                 });
 

crates/repl/src/outputs.rs 🔗

@@ -282,7 +282,7 @@ impl Output {
                                             );
                                             let buffer = cx.new(|cx| {
                                                 let mut buffer = Buffer::local(full_error, cx)
-                                                    .with_language(
+                                                    .with_language_immediate(
                                                         language::PLAIN_TEXT.clone(),
                                                         cx,
                                                     );

crates/repl/src/outputs/markdown.rs 🔗

@@ -60,7 +60,7 @@ impl OutputContent for MarkdownView {
         let buffer = cx.new(|cx| {
             // TODO: Bring in the language registry so we can set the language to markdown
             let mut buffer = Buffer::local(self.raw_text.clone(), cx)
-                .with_language(language::PLAIN_TEXT.clone(), cx);
+                .with_language_immediate(language::PLAIN_TEXT.clone(), cx);
             buffer.set_capability(language::Capability::ReadOnly, cx);
             buffer
         });

crates/repl/src/outputs/plain.rs 🔗

@@ -339,8 +339,8 @@ impl OutputContent for TerminalOutput {
         }
 
         let buffer = cx.new(|cx| {
-            let mut buffer =
-                Buffer::local(self.full_text(), cx).with_language(language::PLAIN_TEXT.clone(), cx);
+            let mut buffer = Buffer::local(self.full_text(), cx)
+                .with_language_immediate(language::PLAIN_TEXT.clone(), cx);
             buffer.set_capability(language::Capability::ReadOnly, cx);
             buffer
         });

crates/repl/src/repl_editor.rs 🔗

@@ -521,7 +521,7 @@ mod tests {
                 "# },
                 cx,
             )
-            .with_language(test_language, cx)
+            .with_language_immediate(test_language, cx)
         });
         let snapshot = buffer.read(cx).snapshot();
 
@@ -595,7 +595,7 @@ mod tests {
                 "# },
                 cx,
             )
-            .with_language(test_language, cx)
+            .with_language_immediate(test_language, cx)
         });
         let snapshot = buffer.read(cx).snapshot();
 
@@ -716,7 +716,7 @@ mod tests {
                 cx,
             );
             buffer.set_language_registry(language_registry.clone());
-            buffer.set_language(Some(markdown.clone()), cx);
+            buffer.set_language_immediate(Some(markdown.clone()), cx);
             buffer
         });
         let snapshot = buffer.read(cx).snapshot();
@@ -761,7 +761,7 @@ mod tests {
                 cx,
             );
             buffer.set_language_registry(language_registry.clone());
-            buffer.set_language(Some(markdown.clone()), cx);
+            buffer.set_language_immediate(Some(markdown.clone()), cx);
             buffer
         });
         let snapshot = buffer.read(cx).snapshot();
@@ -800,7 +800,7 @@ mod tests {
                 cx,
             );
             buffer.set_language_registry(language_registry.clone());
-            buffer.set_language(Some(markdown.clone()), cx);
+            buffer.set_language_immediate(Some(markdown.clone()), cx);
             buffer
         });
         let snapshot = buffer.read(cx).snapshot();

crates/rules_library/src/rules_library.rs 🔗

@@ -715,7 +715,7 @@ impl RulesLibrary {
                         let body_editor = cx.new(|cx| {
                             let buffer = cx.new(|cx| {
                                 let mut buffer = Buffer::local(rule, cx);
-                                buffer.set_language(markdown.log_err(), cx);
+                                buffer.set_language_immediate(markdown.log_err(), cx);
                                 buffer.set_language_registry(language_registry);
                                 buffer
                             });

crates/search/src/buffer_search.rs 🔗

@@ -1510,12 +1510,12 @@ impl BufferSearchBar {
         if enable {
             if let Some(regex_language) = self.regex_language.clone() {
                 query_buffer.update(cx, |query_buffer, cx| {
-                    query_buffer.set_language(Some(regex_language), cx);
+                    query_buffer.set_language_immediate(Some(regex_language), cx);
                 })
             }
         } else {
             query_buffer.update(cx, |query_buffer, cx| {
-                query_buffer.set_language(None, cx);
+                query_buffer.set_language_immediate(None, cx);
             })
         }
     }

crates/search/src/project_search.rs 🔗

@@ -378,6 +378,9 @@ impl ProjectSearch {
                     })
                     .ok()?;
                 while let Some(new_ranges) = new_ranges.next().await {
+                    // `new_ranges.next().await` likely never gets hit while still pending so `async_task`
+                    // will not reschedule, starving other front end tasks, insert a yield point for that here
+                    smol::future::yield_now().await;
                     project_search
                         .update(cx, |project_search, cx| {
                             project_search.match_ranges.extend(new_ranges);
@@ -1651,12 +1654,12 @@ impl ProjectSearchView {
         if enable {
             if let Some(regex_language) = self.regex_language.clone() {
                 query_buffer.update(cx, |query_buffer, cx| {
-                    query_buffer.set_language(Some(regex_language), cx);
+                    query_buffer.set_language_immediate(Some(regex_language), cx);
                 })
             }
         } else {
             query_buffer.update(cx, |query_buffer, cx| {
-                query_buffer.set_language(None, cx);
+                query_buffer.set_language_immediate(None, cx);
             })
         }
     }

crates/sum_tree/src/cursor.rs 🔗

@@ -403,6 +403,10 @@ where
     }
 
     /// Returns whether we found the item you were seeking for.
+    ///
+    /// # Panics
+    ///
+    /// If we did not seek before, use seek instead in that case.
     #[track_caller]
     pub fn seek_forward<Target>(&mut self, pos: &Target, bias: Bias) -> bool
     where

crates/tasks_ui/src/tasks_ui.rs 🔗

@@ -486,7 +486,7 @@ mod tests {
             .await
             .unwrap();
         buffer1.update(cx, |this, cx| {
-            this.set_language(Some(typescript_language), cx)
+            this.set_language_immediate(Some(typescript_language), cx)
         });
         let editor1 = cx.new_window_entity(|window, cx| {
             Editor::for_buffer(buffer1, Some(project.clone()), window, cx)
@@ -499,7 +499,9 @@ mod tests {
             })
             .await
             .unwrap();
-        buffer2.update(cx, |this, cx| this.set_language(Some(rust_language), cx));
+        buffer2.update(cx, |this, cx| {
+            this.set_language_immediate(Some(rust_language), cx)
+        });
         let editor2 = cx
             .new_window_entity(|window, cx| Editor::for_buffer(buffer2, Some(project), window, cx));
 

crates/vim/src/object.rs 🔗

@@ -3244,7 +3244,7 @@ mod test {
             let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
             if let Some(buffer) = multi_buffer.read(cx).buffer(buffer_ids[1]) {
                 buffer.update(cx, |buffer, cx| {
-                    buffer.set_language(Some(language::rust_lang()), cx);
+                    buffer.set_language_immediate(Some(language::rust_lang()), cx);
                 })
             };
 

crates/vim/src/test.rs 🔗

@@ -1478,7 +1478,7 @@ async fn test_toggle_comments(cx: &mut gpui::TestAppContext) {
         },
         Some(language::tree_sitter_rust::LANGUAGE.into()),
     ));
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(language), cx));
 
     // works in normal model
     cx.set_state(
@@ -2363,7 +2363,7 @@ async fn test_wrap_selections_in_tag_line_mode(cx: &mut gpui::TestAppContext) {
         None,
     ));
 
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(js_language), cx));
+    cx.update_buffer(|buffer, cx| buffer.set_language_immediate(Some(js_language), cx));
 
     cx.set_state(
         indoc! {

crates/zed/src/main.rs 🔗

@@ -832,7 +832,7 @@ fn handle_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut
 
                             workspace.update_in(cx, |workspace, window, cx| {
                                 buffer.update(cx, |buffer, cx| {
-                                    buffer.set_language(json, cx);
+                                    buffer.set_language_immediate(json, cx);
                                     buffer.edit([(0..0, json_schema_content)], None, cx);
                                     buffer.edit(
                                         [(0..0, format!("// {} JSON Schema\n", schema_path))],

crates/zeta/src/assemble_excerpts.rs 🔗

@@ -119,8 +119,9 @@ mod tests {
             let input_without_caret = input.replace('ˇ', "");
             let cursor_offset = input_without_ranges.find('ˇ');
             let (input, ranges) = marked_text_ranges(&input_without_caret, false);
-            let buffer =
-                cx.new(|cx| Buffer::local(input, cx).with_language(Arc::new(rust_lang()), cx));
+            let buffer = cx.new(|cx| {
+                Buffer::local(input, cx).with_language_immediate(Arc::new(rust_lang()), cx)
+            });
             buffer.read_with(cx, |buffer, _cx| {
                 let insertions = cursor_offset
                     .map(|offset| {

crates/zeta/src/zeta1/input_excerpt.rs 🔗

@@ -156,7 +156,8 @@ mod tests {
                 numbers
             }
         "#};
-        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
+        let buffer =
+            cx.new(|cx| Buffer::local(text, cx).with_language_immediate(Arc::new(rust_lang()), cx));
         let snapshot = buffer.read(cx).snapshot();
 
         // Ensure we try to fit the largest possible syntax scope, resorting to line-based expansion

crates/zeta2_tools/src/zeta2_tools.rs 🔗

@@ -394,7 +394,7 @@ impl Zeta2Inspector {
                                             let feedback_editor = cx.new(|cx| {
                                                 let buffer = cx.new(|cx| {
                                                     let mut buffer = Buffer::local("", cx);
-                                                    buffer.set_language(
+                                                    buffer.set_language_immediate(
                                                         markdown_language.clone(),
                                                         cx,
                                                     );
@@ -460,7 +460,10 @@ impl Zeta2Inspector {
                                                                 .unwrap_or_default(),
                                                             cx,
                                                         );
-                                                        buffer.set_language(json_language, cx);
+                                                        buffer.set_language_immediate(
+                                                            json_language,
+                                                            cx,
+                                                        );
                                                         buffer
                                                     });
                                                     let buffer = cx.new(|cx| {
@@ -505,7 +508,7 @@ impl Zeta2Inspector {
                             let buffer = cx.new(|cx| {
                                 let mut buffer =
                                     Buffer::local(local_prompt.unwrap_or_else(|err| err), cx);
-                                buffer.set_language(markdown_language.clone(), cx);
+                                buffer.set_language_immediate(markdown_language.clone(), cx);
                                 buffer
                             });
                             let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));

crates/zlog/src/zlog.rs 🔗

@@ -183,7 +183,7 @@ macro_rules! time {
         $crate::Timer::new($logger, $name)
     };
     ($name:expr) => {
-        time!($crate::default_logger!() => $name)
+        $crate::time!($crate::default_logger!() => $name)
     };
 }