From 9af6e82e65946f8b6224d03c7a6730808f2b10d3 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 1 Dec 2025 14:57:15 +0100 Subject: [PATCH] language: Only block the foreground on buffer reparsing when necessary (#43888) 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 --- 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 +- .../configure_context_server_modal.rs | 4 +- crates/agent_ui/src/buffer_codegen.rs | 16 +- crates/agent_ui/src/text_thread_editor.rs | 2 +- .../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 +- .../edit_prediction_context/src/reference.rs | 5 +- .../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(-) diff --git a/crates/acp_thread/src/diff.rs b/crates/acp_thread/src/diff.rs index f17e9d0fce..eed5e21cb3 100644 --- a/crates/acp_thread/src/diff.rs +++ b/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 }); diff --git a/crates/agent_ui/src/acp/message_editor.rs b/crates/agent_ui/src/acp/message_editor.rs index facb86f3b8..8562fa2a42 100644 --- a/crates/agent_ui/src/acp/message_editor.rs +++ b/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); diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index a9b4127ea9..ec284fece1 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/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); })?; diff --git a/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs b/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs index 85f527ff5a..669ca54a63 100644 --- a/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs +++ b/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 }) diff --git a/crates/agent_ui/src/buffer_codegen.rs b/crates/agent_ui/src/buffer_codegen.rs index 6474377706..f6838d710a 100644 --- a/crates/agent_ui/src/buffer_codegen.rs +++ b/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); diff --git a/crates/agent_ui/src/text_thread_editor.rs b/crates/agent_ui/src/text_thread_editor.rs index 6d5e226b6a..7ea737fb18 100644 --- a/crates/agent_ui/src/text_thread_editor.rs +++ b/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(); diff --git a/crates/assistant_text_thread/src/text_thread.rs b/crates/assistant_text_thread/src/text_thread.rs index 7f24c8f665..971a4f8b46 100644 --- a/crates/assistant_text_thread/src/text_thread.rs +++ b/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); diff --git a/crates/collab_ui/src/channel_view.rs b/crates/collab_ui/src/channel_view.rs index 8959c6ccbe..256bcf5c7f 100644 --- a/crates/collab_ui/src/channel_view.rs +++ b/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); }) })?; diff --git a/crates/debugger_tools/src/dap_log.rs b/crates/debugger_tools/src/dap_log.rs index 8841a3744a..cd4514c04b 100644 --- a/crates/debugger_tools/src/dap_log.rs +++ b/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); }) } }) diff --git a/crates/debugger_ui/src/tests/inline_values.rs b/crates/debugger_ui/src/tests/inline_values.rs index 801e6d4362..6a4d76766c 100644 --- a/crates/debugger_ui/src/tests/inline_values.rs +++ b/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| { diff --git a/crates/edit_prediction_context/src/excerpt.rs b/crates/edit_prediction_context/src/excerpt.rs index 7a4bb73edf..213a05042e 100644 --- a/crates/edit_prediction_context/src/excerpt.rs +++ b/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()) } diff --git a/crates/edit_prediction_context/src/imports.rs b/crates/edit_prediction_context/src/imports.rs index 70f1751593..6c56017445 100644 --- a/crates/edit_prediction_context/src/imports.rs +++ b/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(); diff --git a/crates/edit_prediction_context/src/reference.rs b/crates/edit_prediction_context/src/reference.rs index 699adf1d80..b7b5c80201 100644 --- a/crates/edit_prediction_context/src/reference.rs +++ b/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()) } diff --git a/crates/edit_prediction_context/src/syntax_index.rs b/crates/edit_prediction_context/src/syntax_index.rs index f489a08334..5b8b19721c 100644 --- a/crates/edit_prediction_context/src/syntax_index.rs +++ b/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 })?; diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 7189dd9f20..456ef5e1ef 100644 --- a/crates/editor/src/display_map.rs +++ b/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)); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 0bcfad7b88..2ed9c8eb03 100644 --- a/crates/editor/src/editor_tests.rs +++ b/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, 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, 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! {" ˇ @@ -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(""); @@ -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); }) }); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 8111c837e2..826353b040 100644 --- a/crates/editor/src/items.rs +++ b/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() { diff --git a/crates/editor/src/jsx_tag_auto_close.rs b/crates/editor/src/jsx_tag_auto_close.rs index e22fde313d..fe744d15cf 100644 --- a/crates/editor/src/jsx_tag_auto_close.rs +++ b/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(", + cx: &mut Context, + ) -> 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>, cx: &mut Context) { + 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>, + cx: &mut Context, + ) { + self.set_language_(language, true, cx); + } + + fn set_language_( + &mut self, + language: Option>, + may_block: bool, + cx: &mut Context, + ) { 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) { + pub fn reparse(&mut self, cx: &mut Context, 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); diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index efef0a0812..f734ee520d 100644 --- a/crates/language/src/buffer_tests.rs +++ b/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(); diff --git a/crates/language_tools/src/lsp_log_view.rs b/crates/language_tools/src/lsp_log_view.rs index df24f46949..b307b8131e 100644 --- a/crates/language_tools/src/lsp_log_view.rs +++ b/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); }) } }) diff --git a/crates/languages/src/bash.rs b/crates/languages/src/bash.rs index 9720007d09..569d587d8d 100644 --- a/crates/languages/src/bash.rs +++ b/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, input: &str, expected: &str| { diff --git a/crates/languages/src/c.rs b/crates/languages/src/c.rs index eb33bca022..78ea553c27 100644 --- a/crates/languages/src/c.rs +++ b/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( [( diff --git a/crates/languages/src/cpp.rs b/crates/languages/src/cpp.rs index 85a3fb5045..5918e4a6de 100644 --- a/crates/languages/src/cpp.rs +++ b/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( [( diff --git a/crates/languages/src/css.rs b/crates/languages/src/css.rs index 6a925586a6..af8d1bca50 100644 --- a/crates/languages/src/css.rs +++ b/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 diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index a8699fe9c2..3da3eef5a6 100644 --- a/crates/languages/src/go.rs +++ b/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, _| { diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 56512ee8af..7d481b3658 100644 --- a/crates/languages/src/python.rs +++ b/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| { let ix = buffer.len(); buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx); diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index a8638a4686..25b7bfab7d 100644 --- a/crates/languages/src/rust.rs +++ b/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); diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index eeca5ebb6f..a7f68329b0 100644 --- a/crates/languages/src/typescript.rs +++ b/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 diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index daafa014b2..1a351170fd 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -684,8 +684,9 @@ impl LocalBufferStore { cx: &mut Context, ) -> Task>> { 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, ) -> Entity { 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(); diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index be08ed2744..76f1f2b4ad 100644 --- a/crates/project/src/git_store.rs +++ b/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); })?; } diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index a4d92fcf1a..f1c2c8058b 100644 --- a/crates/project/src/lsp_store.rs +++ b/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(); diff --git a/crates/repl/src/notebook/cell.rs b/crates/repl/src/notebook/cell.rs index 87b8e1d55a..2f1bc0924b 100644 --- a/crates/repl/src/notebook/cell.rs +++ b/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); }); }); diff --git a/crates/repl/src/outputs.rs b/crates/repl/src/outputs.rs index b99562393a..54cbba231e 100644 --- a/crates/repl/src/outputs.rs +++ b/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, ); diff --git a/crates/repl/src/outputs/markdown.rs b/crates/repl/src/outputs/markdown.rs index bd88f4e159..b18028bfb8 100644 --- a/crates/repl/src/outputs/markdown.rs +++ b/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 }); diff --git a/crates/repl/src/outputs/plain.rs b/crates/repl/src/outputs/plain.rs index 54e4983b9f..a27069f1a0 100644 --- a/crates/repl/src/outputs/plain.rs +++ b/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 }); diff --git a/crates/repl/src/repl_editor.rs b/crates/repl/src/repl_editor.rs index 9e52637ab7..6c29ca83ec 100644 --- a/crates/repl/src/repl_editor.rs +++ b/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(); diff --git a/crates/rules_library/src/rules_library.rs b/crates/rules_library/src/rules_library.rs index 09b7e0b539..1814db39e0 100644 --- a/crates/rules_library/src/rules_library.rs +++ b/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 }); diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index d17efa6350..2862e91fef 100644 --- a/crates/search/src/buffer_search.rs +++ b/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); }) } } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index abc36a1667..012be5c23a 100644 --- a/crates/search/src/project_search.rs +++ b/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); }) } } diff --git a/crates/sum_tree/src/cursor.rs b/crates/sum_tree/src/cursor.rs index 0bb60a1300..0ca89d16db 100644 --- a/crates/sum_tree/src/cursor.rs +++ b/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(&mut self, pos: &Target, bias: Bias) -> bool where diff --git a/crates/tasks_ui/src/tasks_ui.rs b/crates/tasks_ui/src/tasks_ui.rs index 35c8a2ee22..9682b21123 100644 --- a/crates/tasks_ui/src/tasks_ui.rs +++ b/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)); diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index 2f5ccac07b..dab9d0e0ad 100644 --- a/crates/vim/src/object.rs +++ b/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); }) }; diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 5932a74094..51179682bc 100644 --- a/crates/vim/src/test.rs +++ b/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! { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index f92c819dd2..dd29207cda 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -832,7 +832,7 @@ fn handle_open_request(request: OpenRequest, app_state: Arc, 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))], diff --git a/crates/zeta/src/assemble_excerpts.rs b/crates/zeta/src/assemble_excerpts.rs index f2a5b5adb1..e3871f0221 100644 --- a/crates/zeta/src/assemble_excerpts.rs +++ b/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| { diff --git a/crates/zeta/src/zeta1/input_excerpt.rs b/crates/zeta/src/zeta1/input_excerpt.rs index 853d74da46..f1681c1ff8 100644 --- a/crates/zeta/src/zeta1/input_excerpt.rs +++ b/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 diff --git a/crates/zeta2_tools/src/zeta2_tools.rs b/crates/zeta2_tools/src/zeta2_tools.rs index 4e650f2405..31e4721eb2 100644 --- a/crates/zeta2_tools/src/zeta2_tools.rs +++ b/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)); diff --git a/crates/zlog/src/zlog.rs b/crates/zlog/src/zlog.rs index 04f58e9102..bcd1321625 100644 --- a/crates/zlog/src/zlog.rs +++ b/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) }; }