From f086fa3f21e56fb2a3f66f2c2bfd017811191c22 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 20 Jan 2023 10:38:14 +0100 Subject: [PATCH 01/10] Add syntax injections for Markdown fenced code blocks --- crates/zed/src/languages/markdown/injections.scm | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 crates/zed/src/languages/markdown/injections.scm diff --git a/crates/zed/src/languages/markdown/injections.scm b/crates/zed/src/languages/markdown/injections.scm new file mode 100644 index 0000000000000000000000000000000000000000..577054b4040d174954e365371842c459e1dfc1ba --- /dev/null +++ b/crates/zed/src/languages/markdown/injections.scm @@ -0,0 +1,4 @@ +(fenced_code_block + (info_string + (language) @language) + (code_fence_content) @content) From c49dc8d6e573d70730248ee9d7ebc2e8ec8d7a83 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 20 Jan 2023 10:39:06 +0100 Subject: [PATCH 02/10] Rename `LanguageRegistry::get_language` to `language_for_name` --- crates/editor/src/hover_popover.rs | 2 +- crates/language/src/language.rs | 2 +- crates/language/src/syntax_map.rs | 7 ++++--- crates/zed/src/zed.rs | 10 +++++++--- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 6d003cae5dbaf42d54a85b56ea941d0851816538..f92b07da1dd49ef4de90e7bb9fc9a00ee4993fd4 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -331,7 +331,7 @@ impl InfoPopover { if let Some(language) = content .language .clone() - .and_then(|language| project.languages().get_language(&language)) + .and_then(|language| project.languages().language_for_name(&language)) { let runs = language .highlight_text(&content.text.as_str().into(), 0..content.text.len()); diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 045e8dcd6f510772c2050a55cbbbb228823f40f3..06891d780d285ab9a13a65f91c7396d3f6cdbfc6 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -466,7 +466,7 @@ impl LanguageRegistry { self.language_server_download_dir = Some(path.into()); } - pub fn get_language(&self, name: &str) -> Option> { + pub fn language_for_name(&self, name: &str) -> Option> { self.languages .read() .iter() diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 8d6673085494263970baf3fdbb3a0f6c21a939d0..9ef4d82fd1ff13ef0747f96322a280101f95d519 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -976,7 +976,7 @@ fn get_injections( combined_injection_ranges.clear(); for pattern in &config.patterns { if let (Some(language_name), true) = (pattern.language.as_ref(), pattern.combined) { - if let Some(language) = language_registry.get_language(language_name) { + if let Some(language) = language_registry.language_for_name(language_name) { combined_injection_ranges.insert(language, Vec::new()); } } @@ -1015,7 +1015,8 @@ fn get_injections( }); if let Some(language_name) = language_name { - if let Some(language) = language_registry.get_language(language_name.as_ref()) { + if let Some(language) = language_registry.language_for_name(language_name.as_ref()) + { result = true; let range = text.anchor_before(content_range.start) ..text.anchor_after(content_range.end); @@ -2254,7 +2255,7 @@ mod tests { registry.add(Arc::new(ruby_lang())); registry.add(Arc::new(html_lang())); registry.add(Arc::new(erb_lang())); - let language = registry.get_language(language_name).unwrap(); + let language = registry.language_for_name(language_name).unwrap(); let mut buffer = Buffer::new(0, 0, Default::default()); let mut mutated_syntax_map = SyntaxMap::new(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index b6b00e7869992af0d38e9d8395741b58098952f2..19c6bae6ebad06fcaaefbaf8a0095201e4e8e8eb 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -226,7 +226,11 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext| { let content = to_string_pretty(&cx.debug_elements()).unwrap(); let project = workspace.project().clone(); - let json_language = project.read(cx).languages().get_language("JSON").unwrap(); + let json_language = project + .read(cx) + .languages() + .language_for_name("JSON") + .unwrap(); if project.read(cx).is_remote() { cx.propagate_action(); } else if let Some(buffer) = project @@ -624,7 +628,7 @@ fn open_telemetry_log_file( .update(cx, |project, cx| project.create_buffer("", None, cx)) .expect("creating buffers on a local workspace always succeeds"); buffer.update(cx, |buffer, cx| { - buffer.set_language(app_state.languages.get_language("JSON"), cx); + buffer.set_language(app_state.languages.language_for_name("JSON"), cx); buffer.edit( [( 0..0, @@ -670,7 +674,7 @@ fn open_bundled_config_file( let text = Assets::get(asset_path).unwrap().data; let text = str::from_utf8(text.as_ref()).unwrap(); project - .create_buffer(text, project.languages().get_language("JSON"), cx) + .create_buffer(text, project.languages().language_for_name("JSON"), cx) .expect("creating buffers on a local workspace always succeeds") }); let buffer = From 36e4dcef16f060f4d4fd063d982909bbe4160f19 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 20 Jan 2023 10:41:40 +0100 Subject: [PATCH 03/10] Avoid allocating a string to compare language names --- Cargo.lock | 1 + crates/language/Cargo.toml | 1 + crates/language/src/language.rs | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 06222d02a6bd44b824e9e33124d0d800e8fb9561..174965952fde76c8d260bbf9f363922609f38b9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3157,6 +3157,7 @@ dependencies = [ "tree-sitter-ruby", "tree-sitter-rust", "tree-sitter-typescript", + "unicase", "unindent", "util", ] diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 6c074a2d75e0ef59523bd52dbfaf53bb563025d2..62de0c4e44f5836489e75c83fbbdde1f7efe1fec 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -53,6 +53,7 @@ smol = "1.2" tree-sitter = "0.20" tree-sitter-rust = { version = "*", optional = true } tree-sitter-typescript = { version = "*", optional = true } +unicase = "2.6" [dev-dependencies] client = { path = "../client", features = ["test-support"] } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 06891d780d285ab9a13a65f91c7396d3f6cdbfc6..046076a48ec51244a5fc310f5cf0a7e9ea8e98cc 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -41,6 +41,7 @@ use std::{ use syntax_map::SyntaxSnapshot; use theme::{SyntaxTheme, Theme}; use tree_sitter::{self, Query}; +use unicase::UniCase; use util::ResultExt; #[cfg(any(test, feature = "test-support"))] @@ -467,10 +468,11 @@ impl LanguageRegistry { } pub fn language_for_name(&self, name: &str) -> Option> { + let name = UniCase::new(name); self.languages .read() .iter() - .find(|language| language.name().to_lowercase() == name.to_lowercase()) + .find(|language| UniCase::new(language.name()) == name) .cloned() } From cb610f37f2dd0f6d449b1aa076e83a8fae808828 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 20 Jan 2023 10:56:20 +0100 Subject: [PATCH 04/10] WIP: Search language injections also by file extension There are still a few things left: 1. Add test to verify we can successfully locate a language by its extension 2. Add test to reproduce bug where changing the fenced code block language won't reparse the block with the new language 3. Reparse injections for which we couldn't find a language when the language registry changes. 4. Check why the markdown grammar considers the trailing triple backtick as `(code_block_content)`, as opposed to being part of the outer markdown. --- Cargo.lock | 1 + crates/language/Cargo.toml | 3 ++- crates/language/src/language.rs | 15 +++++++++++++++ crates/language/src/syntax_map.rs | 27 +++++++++++++++++++++++++-- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 174965952fde76c8d260bbf9f363922609f38b9c..abdf8b8a552cfd594bed8579c6afb6b44ef98e1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3153,6 +3153,7 @@ dependencies = [ "tree-sitter-html", "tree-sitter-javascript", "tree-sitter-json 0.19.0", + "tree-sitter-markdown", "tree-sitter-python", "tree-sitter-ruby", "tree-sitter-rust", diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 62de0c4e44f5836489e75c83fbbdde1f7efe1fec..64db58c8470722fd365dc25f7656f2b152832e0c 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -66,12 +66,13 @@ util = { path = "../util", features = ["test-support"] } ctor = "0.1" env_logger = "0.9" rand = "0.8.3" +tree-sitter-embedded-template = "*" tree-sitter-html = "*" tree-sitter-javascript = "*" tree-sitter-json = "*" +tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" } tree-sitter-rust = "*" tree-sitter-python = "*" tree-sitter-typescript = "*" tree-sitter-ruby = "*" -tree-sitter-embedded-template = "*" unindent = "0.1.7" diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 046076a48ec51244a5fc310f5cf0a7e9ea8e98cc..1ddd3e3939956dac4bbecf8362b24648171f42ef 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -476,6 +476,21 @@ impl LanguageRegistry { .cloned() } + pub fn language_for_extension(&self, extension: &str) -> Option> { + let extension = UniCase::new(extension); + self.languages + .read() + .iter() + .find(|language| { + language + .config + .path_suffixes + .iter() + .any(|suffix| UniCase::new(suffix) == extension) + }) + .cloned() + } + pub fn to_vec(&self) -> Vec> { self.languages.read().iter().cloned().collect() } diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 9ef4d82fd1ff13ef0747f96322a280101f95d519..9707cf5471ea94fe56b18174603f290c0a1119d6 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1015,8 +1015,10 @@ fn get_injections( }); if let Some(language_name) = language_name { - if let Some(language) = language_registry.language_for_name(language_name.as_ref()) - { + let language = language_registry + .language_for_name(&language_name) + .or_else(|| language_registry.language_for_extension(&language_name)); + if let Some(language) = language { result = true; let range = text.anchor_before(content_range.start) ..text.anchor_after(content_range.end); @@ -2255,6 +2257,7 @@ mod tests { registry.add(Arc::new(ruby_lang())); registry.add(Arc::new(html_lang())); registry.add(Arc::new(erb_lang())); + registry.add(Arc::new(markdown_lang())); let language = registry.language_for_name(language_name).unwrap(); let mut buffer = Buffer::new(0, 0, Default::default()); @@ -2393,6 +2396,26 @@ mod tests { .unwrap() } + fn markdown_lang() -> Language { + Language::new( + LanguageConfig { + name: "Markdown".into(), + path_suffixes: vec!["md".into()], + ..Default::default() + }, + Some(tree_sitter_markdown::language()), + ) + .with_injection_query( + r#" + (fenced_code_block + (info_string + (language) @language) + (code_fence_content) @content) + "#, + ) + .unwrap() + } + fn range_for_text(buffer: &Buffer, text: &str) -> Range { let start = buffer.as_rope().to_string().find(text).unwrap(); start..start + text.len() From 79cf6fb8b6b633f4c5a504ee050d93ff2ba0e6cd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 23 Jan 2023 09:45:36 +0100 Subject: [PATCH 05/10] WIP: Add test for dynamic language injection --- crates/language/src/syntax_map.rs | 56 +++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 9707cf5471ea94fe56b18174603f290c0a1119d6..c0887809c235a6fc0dcdce8da0dd9b3d485088ba 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1596,6 +1596,56 @@ mod tests { ); } + #[gpui::test] + fn test_dynamic_language_injection() { + let registry = Arc::new(LanguageRegistry::test()); + let markdown = Arc::new(markdown_lang()); + registry.add(markdown.clone()); + registry.add(Arc::new(rust_lang())); + registry.add(Arc::new(ruby_lang())); + + let mut buffer = Buffer::new( + 0, + 0, + r#" + This is a code block: + + ```rs + fn foo() {} + ``` + "# + .unindent(), + ); + + let mut syntax_map = SyntaxMap::new(); + syntax_map.set_language_registry(registry.clone()); + syntax_map.reparse(markdown.clone(), &buffer); + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(3, 0)..Point::new(3, 0), + &[ + "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter...", + "...(function_item name: (identifier) parameters: (parameters) body: (block)...", + ], + ); + + // Replace Rust with Ruby in code block. + let macro_name_range = range_for_text(&buffer, "rs"); + buffer.edit([(macro_name_range, "ruby")]); + syntax_map.interpolate(&buffer); + syntax_map.reparse(markdown.clone(), &buffer); + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(3, 0)..Point::new(3, 0), + &[ + "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter...", + "...(call method: (identifier) arguments: (argument_list (call method: (identifier) arguments: (argument_list) block: (block)...", + ], + ); + } + #[gpui::test] fn test_typing_multiple_new_injections() { let (buffer, syntax_map) = test_edit_sequence( @@ -2408,9 +2458,9 @@ mod tests { .with_injection_query( r#" (fenced_code_block - (info_string - (language) @language) - (code_fence_content) @content) + (info_string + (language) @language) + (code_fence_content) @content) "#, ) .unwrap() From 8dabdd1baad7c2226d6ff85fd7a6da9ce2f3d087 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 23 Jan 2023 19:02:06 +0100 Subject: [PATCH 06/10] Ensure injection layer is recomputed when language changes Co-Authored-By: Max Brunsfeld --- crates/language/src/syntax_map.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index c0887809c235a6fc0dcdce8da0dd9b3d485088ba..275d05ba7f7f8a0d7e7237751e5bd545e9d4ef1f 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -5,7 +5,7 @@ use parking_lot::Mutex; use std::{ borrow::Cow, cell::RefCell, - cmp::{Ordering, Reverse}, + cmp::{self, Ordering, Reverse}, collections::BinaryHeap, ops::{Deref, DerefMut, Range}, sync::Arc, @@ -1004,15 +1004,21 @@ fn get_injections( prev_match = Some((mat.pattern_index, content_range.clone())); let combined = config.patterns[mat.pattern_index].combined; - let language_name = config.patterns[mat.pattern_index] - .language - .as_ref() - .map(|s| Cow::Borrowed(s.as_ref())) - .or_else(|| { - let ix = config.language_capture_ix?; - let node = mat.nodes_for_capture_index(ix).next()?; - Some(Cow::Owned(text.text_for_range(node.byte_range()).collect())) - }); + + let mut language_name = None; + let mut step_range = content_range.clone(); + if let Some(name) = config.patterns[mat.pattern_index].language.as_ref() { + language_name = Some(Cow::Borrowed(name.as_ref())) + } else if let Some(language_node) = config + .language_capture_ix + .and_then(|ix| mat.nodes_for_capture_index(ix).next()) + { + step_range.start = cmp::min(content_range.start, language_node.start_byte()); + step_range.end = cmp::max(content_range.end, language_node.end_byte()); + language_name = Some(Cow::Owned( + text.text_for_range(language_node.byte_range()).collect(), + )) + }; if let Some(language_name) = language_name { let language = language_registry @@ -1020,8 +1026,8 @@ fn get_injections( .or_else(|| language_registry.language_for_extension(&language_name)); if let Some(language) = language { result = true; - let range = text.anchor_before(content_range.start) - ..text.anchor_after(content_range.end); + let range = + text.anchor_before(step_range.start)..text.anchor_after(step_range.end); if combined { combined_injection_ranges .get_mut(&language.clone()) From 14c72cac5849d729ff8aea67daaf859efedfe4b0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 24 Jan 2023 12:25:12 +0100 Subject: [PATCH 07/10] Store syntax layers even if a language for the injection can't be found --- crates/language/src/syntax_map.rs | 349 ++++++++++++++++++------------ 1 file changed, 206 insertions(+), 143 deletions(-) diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 275d05ba7f7f8a0d7e7237751e5bd545e9d4ef1f..7564d234e568b125343960d99deefec9753ce331 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -89,8 +89,34 @@ struct SyntaxMapMatchesLayer<'a> { struct SyntaxLayer { depth: usize, range: Range, - tree: tree_sitter::Tree, - language: Arc, + content: SyntaxLayerContent, +} + +#[derive(Clone)] +enum SyntaxLayerContent { + Parsed { + tree: tree_sitter::Tree, + language: Arc, + }, + Pending { + language_name: Arc, + }, +} + +impl SyntaxLayerContent { + fn language_id(&self) -> Option { + match self { + SyntaxLayerContent::Parsed { language, .. } => language.id(), + SyntaxLayerContent::Pending { .. } => None, + } + } + + fn tree(&self) -> Option<&Tree> { + match self { + SyntaxLayerContent::Parsed { tree, .. } => Some(tree), + SyntaxLayerContent::Pending { .. } => None, + } + } } #[derive(Debug)] @@ -130,12 +156,26 @@ struct SyntaxLayerPositionBeforeChange { struct ParseStep { depth: usize, - language: Arc, + language: ParseStepLanguage, range: Range, included_ranges: Vec, mode: ParseMode, } +enum ParseStepLanguage { + Loaded { language: Arc }, + Pending { name: Arc }, +} + +impl ParseStepLanguage { + fn id(&self) -> Option { + match self { + ParseStepLanguage::Loaded { language } => language.id(), + ParseStepLanguage::Pending { .. } => None, + } + } +} + enum ParseMode { Single, Combined { @@ -276,46 +316,48 @@ impl SyntaxSnapshot { } let mut layer = layer.clone(); - for (edit, edit_range) in &edits[first_edit_ix_for_depth..] { - // Ignore any edits that follow this layer. - if edit_range.start.cmp(&layer.range.end, text).is_ge() { - break; - } - - // Apply any edits that intersect this layer to the layer's syntax tree. - let tree_edit = if edit_range.start.cmp(&layer.range.start, text).is_ge() { - tree_sitter::InputEdit { - start_byte: edit.new.start.0 - start_byte, - old_end_byte: edit.new.start.0 - start_byte - + (edit.old.end.0 - edit.old.start.0), - new_end_byte: edit.new.end.0 - start_byte, - start_position: (edit.new.start.1 - start_point).to_ts_point(), - old_end_position: (edit.new.start.1 - start_point - + (edit.old.end.1 - edit.old.start.1)) - .to_ts_point(), - new_end_position: (edit.new.end.1 - start_point).to_ts_point(), + if let SyntaxLayerContent::Parsed { tree, .. } = &mut layer.content { + for (edit, edit_range) in &edits[first_edit_ix_for_depth..] { + // Ignore any edits that follow this layer. + if edit_range.start.cmp(&layer.range.end, text).is_ge() { + break; } - } else { - let node = layer.tree.root_node(); - tree_sitter::InputEdit { - start_byte: 0, - old_end_byte: node.end_byte(), - new_end_byte: 0, - start_position: Default::default(), - old_end_position: node.end_position(), - new_end_position: Default::default(), - } - }; - layer.tree.edit(&tree_edit); - } + // Apply any edits that intersect this layer to the layer's syntax tree. + let tree_edit = if edit_range.start.cmp(&layer.range.start, text).is_ge() { + tree_sitter::InputEdit { + start_byte: edit.new.start.0 - start_byte, + old_end_byte: edit.new.start.0 - start_byte + + (edit.old.end.0 - edit.old.start.0), + new_end_byte: edit.new.end.0 - start_byte, + start_position: (edit.new.start.1 - start_point).to_ts_point(), + old_end_position: (edit.new.start.1 - start_point + + (edit.old.end.1 - edit.old.start.1)) + .to_ts_point(), + new_end_position: (edit.new.end.1 - start_point).to_ts_point(), + } + } else { + let node = tree.root_node(); + tree_sitter::InputEdit { + start_byte: 0, + old_end_byte: node.end_byte(), + new_end_byte: 0, + start_position: Default::default(), + old_end_position: node.end_position(), + new_end_position: Default::default(), + } + }; - debug_assert!( - layer.tree.root_node().end_byte() <= text.len(), - "tree's size {}, is larger than text size {}", - layer.tree.root_node().end_byte(), - text.len(), - ); + tree.edit(&tree_edit); + } + + debug_assert!( + tree.root_node().end_byte() <= text.len(), + "tree's size {}, is larger than text size {}", + tree.root_node().end_byte(), + text.len(), + ); + } layers.push(layer, text); cursor.next(text); @@ -344,7 +386,9 @@ impl SyntaxSnapshot { let mut combined_injection_ranges = HashMap::default(); queue.push(ParseStep { depth: 0, - language: root_language.clone(), + language: ParseStepLanguage::Loaded { + language: root_language, + }, included_ranges: vec![tree_sitter::Range { start_byte: 0, end_byte: text.len(), @@ -415,12 +459,11 @@ impl SyntaxSnapshot { let (step_start_byte, step_start_point) = step.range.start.summary::<(usize, Point)>(text); let step_end_byte = step.range.end.to_offset(text); - let Some(grammar) = step.language.grammar.as_deref() else { continue }; let mut old_layer = cursor.item(); if let Some(layer) = old_layer { if layer.range.to_offset(text) == (step_start_byte..step_end_byte) - && layer.language.id() == step.language.id() + && layer.content.language_id() == step.language.id() { cursor.next(&text); } else { @@ -428,85 +471,99 @@ impl SyntaxSnapshot { } } - let tree; - let changed_ranges; - let mut included_ranges = step.included_ranges; - if let Some(old_layer) = old_layer { - if let ParseMode::Combined { - parent_layer_changed_ranges, - .. - } = step.mode - { - included_ranges = splice_included_ranges( - old_layer.tree.included_ranges(), - &parent_layer_changed_ranges, - &included_ranges, - ); - } + let content = match step.language { + ParseStepLanguage::Loaded { language } => { + let Some(grammar) = language.grammar() else { continue }; + let tree; + let changed_ranges; + let mut included_ranges = step.included_ranges; + if let Some(SyntaxLayerContent::Parsed { tree: old_tree, .. }) = + old_layer.map(|layer| &layer.content) + { + if let ParseMode::Combined { + parent_layer_changed_ranges, + .. + } = step.mode + { + included_ranges = splice_included_ranges( + old_tree.included_ranges(), + &parent_layer_changed_ranges, + &included_ranges, + ); + } - tree = parse_text( - grammar, - text.as_rope(), - step_start_byte, - step_start_point, - included_ranges, - Some(old_layer.tree.clone()), - ); - changed_ranges = join_ranges( - edits.iter().map(|e| e.new.clone()).filter(|range| { - range.start <= step_end_byte && range.end >= step_start_byte - }), - old_layer - .tree - .changed_ranges(&tree) - .map(|r| step_start_byte + r.start_byte..step_start_byte + r.end_byte), - ); - } else { - tree = parse_text( - grammar, - text.as_rope(), - step_start_byte, - step_start_point, - included_ranges, - None, - ); - changed_ranges = vec![step_start_byte..step_end_byte]; - } + tree = parse_text( + grammar, + text.as_rope(), + step_start_byte, + step_start_point, + included_ranges, + Some(old_tree.clone()), + ); + changed_ranges = join_ranges( + edits.iter().map(|e| e.new.clone()).filter(|range| { + range.start <= step_end_byte && range.end >= step_start_byte + }), + old_tree.changed_ranges(&tree).map(|r| { + step_start_byte + r.start_byte..step_start_byte + r.end_byte + }), + ); + } else { + tree = parse_text( + grammar, + text.as_rope(), + step_start_byte, + step_start_point, + included_ranges, + None, + ); + changed_ranges = vec![step_start_byte..step_end_byte]; + } + + if let (Some((config, registry)), false) = ( + grammar.injection_config.as_ref().zip(registry.as_ref()), + changed_ranges.is_empty(), + ) { + for range in &changed_ranges { + changed_regions.insert( + ChangedRegion { + depth: step.depth + 1, + range: text.anchor_before(range.start) + ..text.anchor_after(range.end), + }, + text, + ); + } + get_injections( + config, + text, + tree.root_node_with_offset( + step_start_byte, + step_start_point.to_ts_point(), + ), + registry, + step.depth + 1, + &changed_ranges, + &mut combined_injection_ranges, + &mut queue, + ); + } + + SyntaxLayerContent::Parsed { tree, language } + } + ParseStepLanguage::Pending { name } => SyntaxLayerContent::Pending { + language_name: name, + }, + }; layers.push( SyntaxLayer { depth: step.depth, range: step.range, - tree: tree.clone(), - language: step.language.clone(), + content, }, &text, ); - - if let (Some((config, registry)), false) = ( - grammar.injection_config.as_ref().zip(registry.as_ref()), - changed_ranges.is_empty(), - ) { - for range in &changed_ranges { - changed_regions.insert( - ChangedRegion { - depth: step.depth + 1, - range: text.anchor_before(range.start)..text.anchor_after(range.end), - }, - text, - ); - } - get_injections( - config, - text, - tree.root_node_with_offset(step_start_byte, step_start_point.to_ts_point()), - registry, - step.depth + 1, - &changed_ranges, - &mut combined_injection_ranges, - &mut queue, - ); - } } drop(cursor); @@ -586,20 +643,23 @@ impl SyntaxSnapshot { cursor.next(buffer); std::iter::from_fn(move || { - if let Some(layer) = cursor.item() { - let info = SyntaxLayerInfo { - language: &layer.language, - depth: layer.depth, - node: layer.tree.root_node_with_offset( - layer.range.start.to_offset(buffer), - layer.range.start.to_point(buffer).to_ts_point(), - ), - }; - cursor.next(buffer); - Some(info) - } else { - None + while let Some(layer) = cursor.item() { + if let SyntaxLayerContent::Parsed { tree, language } = &layer.content { + let info = SyntaxLayerInfo { + language, + depth: layer.depth, + node: tree.root_node_with_offset( + layer.range.start.to_offset(buffer), + layer.range.start.to_point(buffer).to_ts_point(), + ), + }; + cursor.next(buffer); + return Some(info); + } else { + cursor.next(buffer); + } } + None }) } } @@ -968,8 +1028,7 @@ fn get_injections( changed_ranges: &[Range], combined_injection_ranges: &mut HashMap, Vec>, queue: &mut BinaryHeap, -) -> bool { - let mut result = false; +) { let mut query_cursor = QueryCursorHandle::new(); let mut prev_match = None; @@ -1024,10 +1083,8 @@ fn get_injections( let language = language_registry .language_for_name(&language_name) .or_else(|| language_registry.language_for_extension(&language_name)); + let range = text.anchor_before(step_range.start)..text.anchor_after(step_range.end); if let Some(language) = language { - result = true; - let range = - text.anchor_before(step_range.start)..text.anchor_after(step_range.end); if combined { combined_injection_ranges .get_mut(&language.clone()) @@ -1036,12 +1093,22 @@ fn get_injections( } else { queue.push(ParseStep { depth, - language, + language: ParseStepLanguage::Loaded { language }, included_ranges: content_ranges, range, mode: ParseMode::Single, }); } + } else { + queue.push(ParseStep { + depth, + language: ParseStepLanguage::Pending { + name: language_name.into(), + }, + included_ranges: content_ranges, + range, + mode: ParseMode::Single, + }); } } } @@ -1052,7 +1119,7 @@ fn get_injections( let range = text.anchor_before(node.start_byte())..text.anchor_after(node.end_byte()); queue.push(ParseStep { depth, - language, + language: ParseStepLanguage::Loaded { language }, range, included_ranges, mode: ParseMode::Combined { @@ -1061,8 +1128,6 @@ fn get_injections( }, }) } - - result } fn splice_included_ranges( @@ -1361,7 +1426,7 @@ impl sum_tree::Item for SyntaxLayer { max_depth: self.depth, range: self.range.clone(), last_layer_range: self.range.clone(), - last_layer_language: self.language.id(), + last_layer_language: self.content.language_id(), } } } @@ -1371,7 +1436,7 @@ impl std::fmt::Debug for SyntaxLayer { f.debug_struct("SyntaxLayer") .field("depth", &self.depth) .field("range", &self.range) - .field("tree", &self.tree) + .field("tree", &self.content.tree()) .finish() } } @@ -2216,16 +2281,14 @@ mod tests { .zip(new_syntax_map.layers.iter()) { assert_eq!(old_layer.range, new_layer.range); + let Some(old_tree) = old_layer.content.tree() else { continue }; + let Some(new_tree) = new_layer.content.tree() else { continue }; let old_start_byte = old_layer.range.start.to_offset(old_buffer); let new_start_byte = new_layer.range.start.to_offset(new_buffer); let old_start_point = old_layer.range.start.to_point(old_buffer).to_ts_point(); let new_start_point = new_layer.range.start.to_point(new_buffer).to_ts_point(); - let old_node = old_layer - .tree - .root_node_with_offset(old_start_byte, old_start_point); - let new_node = new_layer - .tree - .root_node_with_offset(new_start_byte, new_start_point); + let old_node = old_tree.root_node_with_offset(old_start_byte, old_start_point); + let new_node = new_tree.root_node_with_offset(new_start_byte, new_start_point); check_node_edits( old_layer.depth, &old_layer.range, From f3509824e89b413713af9b60e3b5b7212abe4325 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 24 Jan 2023 12:55:49 +0100 Subject: [PATCH 08/10] WIP: Start on `SyntaxMapSnapshot::unknown_injection_languages` --- crates/language/src/syntax_map.rs | 50 ++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 7564d234e568b125343960d99deefec9753ce331..aa402bccd8293f5091c7ef52772c859de7ae7988 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -7,6 +7,7 @@ use std::{ cell::RefCell, cmp::{self, Ordering, Reverse}, collections::BinaryHeap, + iter, ops::{Deref, DerefMut, Range}, sync::Arc, }; @@ -133,6 +134,7 @@ struct SyntaxLayerSummary { range: Range, last_layer_range: Range, last_layer_language: Option, + contains_pending_layer: bool, } #[derive(Clone, Debug)] @@ -642,7 +644,7 @@ impl SyntaxSnapshot { }); cursor.next(buffer); - std::iter::from_fn(move || { + iter::from_fn(move || { while let Some(layer) = cursor.item() { if let SyntaxLayerContent::Parsed { tree, language } = &layer.content { let info = SyntaxLayerInfo { @@ -662,6 +664,27 @@ impl SyntaxSnapshot { None }) } + + pub fn unknown_injection_languages<'a>( + &'a self, + buffer: &'a BufferSnapshot, + ) -> impl 'a + Iterator> { + let mut cursor = self + .layers + .filter::<_, ()>(|summary| summary.contains_pending_layer); + cursor.next(buffer); + iter::from_fn(move || { + while let Some(layer) = cursor.item() { + if let SyntaxLayerContent::Pending { language_name } = &layer.content { + cursor.next(buffer); + return Some(language_name); + } else { + cursor.next(buffer); + } + } + None + }) + } } impl<'a> SyntaxMapCaptures<'a> { @@ -1356,6 +1379,7 @@ impl Default for SyntaxLayerSummary { range: Anchor::MAX..Anchor::MIN, last_layer_range: Anchor::MIN..Anchor::MAX, last_layer_language: None, + contains_pending_layer: false, } } } @@ -1377,6 +1401,7 @@ impl sum_tree::Summary for SyntaxLayerSummary { } self.last_layer_range = other.last_layer_range.clone(); self.last_layer_language = other.last_layer_language; + self.contains_pending_layer |= other.contains_pending_layer; } } @@ -1427,6 +1452,7 @@ impl sum_tree::Item for SyntaxLayer { range: self.range.clone(), last_layer_range: self.range.clone(), last_layer_language: self.content.language_id(), + contains_pending_layer: matches!(self.content, SyntaxLayerContent::Pending { .. }), } } } @@ -1715,6 +1741,28 @@ mod tests { "...(call method: (identifier) arguments: (argument_list (call method: (identifier) arguments: (argument_list) block: (block)...", ], ); + + // Replace Ruby with a language that hasn't been loaded yet. + let macro_name_range = range_for_text(&buffer, "ruby"); + buffer.edit([(macro_name_range, "erb")]); + syntax_map.interpolate(&buffer); + syntax_map.reparse(markdown.clone(), &buffer); + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(3, 0)..Point::new(3, 0), + &[ + "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter..." + ], + ); + assert_eq!( + syntax_map + .unknown_injection_languages(&buffer) + .collect::>(), + vec![&Arc::from("erb")] + ); + + registry.add(Arc::new(erb_lang())); } #[gpui::test] From c48e3f3d05a9bb19b8133de5ec7445ea623df560 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 24 Jan 2023 15:29:59 +0100 Subject: [PATCH 09/10] Reparse unknown injection ranges in buffer when adding a new language --- crates/language/src/buffer.rs | 33 ++++--- crates/language/src/language.rs | 7 ++ crates/language/src/syntax_map.rs | 151 ++++++++++++++++++------------ crates/project/src/project.rs | 14 ++- 4 files changed, 129 insertions(+), 76 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 110e10564c35ecf6b04892c4d06dd6a53ee5e679..78858e7d72cc6186e6098dbf67154b40b799f884 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -797,12 +797,16 @@ impl Buffer { self.parsing_in_background } + pub fn contains_unknown_injections(&self) -> bool { + self.syntax_map.lock().contains_unknown_injections() + } + #[cfg(test)] pub fn set_sync_parse_timeout(&mut self, timeout: Duration) { self.sync_parse_timeout = timeout; } - fn reparse(&mut self, cx: &mut ModelContext) { + pub fn reparse(&mut self, cx: &mut ModelContext) { if self.parsing_in_background { return; } @@ -819,13 +823,13 @@ impl Buffer { syntax_map.interpolate(&text); let language_registry = syntax_map.language_registry(); let mut syntax_snapshot = syntax_map.snapshot(); - let syntax_map_version = syntax_map.parsed_version(); drop(syntax_map); let parse_task = cx.background().spawn({ let language = language.clone(); + let language_registry = language_registry.clone(); async move { - syntax_snapshot.reparse(&syntax_map_version, &text, language_registry, language); + syntax_snapshot.reparse(&text, language_registry, language); syntax_snapshot } }); @@ -835,7 +839,7 @@ impl Buffer { .block_with_timeout(self.sync_parse_timeout, parse_task) { Ok(new_syntax_snapshot) => { - self.did_finish_parsing(new_syntax_snapshot, parsed_version, cx); + self.did_finish_parsing(new_syntax_snapshot, cx); return; } Err(parse_task) => { @@ -847,9 +851,15 @@ impl Buffer { this.language.as_ref().map_or(true, |current_language| { !Arc::ptr_eq(&language, current_language) }); - let parse_again = - this.version.changed_since(&parsed_version) || grammar_changed; - this.did_finish_parsing(new_syntax_map, parsed_version, cx); + let language_registry_changed = new_syntax_map + .contains_unknown_injections() + && language_registry.map_or(false, |registry| { + registry.version() != new_syntax_map.language_registry_version() + }); + let parse_again = language_registry_changed + || grammar_changed + || this.version.changed_since(&parsed_version); + this.did_finish_parsing(new_syntax_map, cx); this.parsing_in_background = false; if parse_again { this.reparse(cx); @@ -861,14 +871,9 @@ impl Buffer { } } - fn did_finish_parsing( - &mut self, - syntax_snapshot: SyntaxSnapshot, - version: clock::Global, - cx: &mut ModelContext, - ) { + fn did_finish_parsing(&mut self, syntax_snapshot: SyntaxSnapshot, cx: &mut ModelContext) { self.parse_count += 1; - self.syntax_map.lock().did_parse(syntax_snapshot, version); + self.syntax_map.lock().did_parse(syntax_snapshot); self.request_autoindent(cx); cx.emit(Event::Reparsed); cx.notify(); diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 1ddd3e3939956dac4bbecf8362b24648171f42ef..6e1a120c819d830a713183d1666c13a34a6dcf2a 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -422,6 +422,7 @@ pub struct LanguageRegistry { >, subscription: RwLock<(watch::Sender<()>, watch::Receiver<()>)>, theme: RwLock>>, + version: AtomicUsize, } impl LanguageRegistry { @@ -436,6 +437,7 @@ impl LanguageRegistry { lsp_binary_paths: Default::default(), subscription: RwLock::new(watch::channel()), theme: Default::default(), + version: Default::default(), } } @@ -449,6 +451,7 @@ impl LanguageRegistry { language.set_theme(&theme.editor.syntax); } self.languages.write().push(language); + self.version.fetch_add(1, SeqCst); *self.subscription.write().0.borrow_mut() = (); } @@ -456,6 +459,10 @@ impl LanguageRegistry { self.subscription.read().1.clone() } + pub fn version(&self) -> usize { + self.version.load(SeqCst) + } + pub fn set_theme(&self, theme: Arc) { *self.theme.write() = Some(theme.clone()); for language in self.languages.read().iter() { diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index aa402bccd8293f5091c7ef52772c859de7ae7988..ada981ec264fabe41e7aa123fc160519ed256ac9 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -27,8 +27,6 @@ lazy_static! { #[derive(Default)] pub struct SyntaxMap { - parsed_version: clock::Global, - interpolated_version: clock::Global, snapshot: SyntaxSnapshot, language_registry: Option>, } @@ -36,6 +34,9 @@ pub struct SyntaxMap { #[derive(Clone, Default)] pub struct SyntaxSnapshot { layers: SumTree, + parsed_version: clock::Global, + interpolated_version: clock::Global, + language_registry_version: usize, } #[derive(Default)] @@ -134,7 +135,7 @@ struct SyntaxLayerSummary { range: Range, last_layer_range: Range, last_layer_language: Option, - contains_pending_layer: bool, + contains_unknown_injections: bool, } #[derive(Clone, Debug)] @@ -218,30 +219,17 @@ impl SyntaxMap { self.language_registry.clone() } - pub fn parsed_version(&self) -> clock::Global { - self.parsed_version.clone() - } - pub fn interpolate(&mut self, text: &BufferSnapshot) { - self.snapshot.interpolate(&self.interpolated_version, text); - self.interpolated_version = text.version.clone(); + self.snapshot.interpolate(text); } #[cfg(test)] pub fn reparse(&mut self, language: Arc, text: &BufferSnapshot) { - self.snapshot.reparse( - &self.parsed_version, - text, - self.language_registry.clone(), - language, - ); - self.parsed_version = text.version.clone(); - self.interpolated_version = text.version.clone(); + self.snapshot + .reparse(text, self.language_registry.clone(), language); } - pub fn did_parse(&mut self, snapshot: SyntaxSnapshot, version: clock::Global) { - self.interpolated_version = version.clone(); - self.parsed_version = version; + pub fn did_parse(&mut self, snapshot: SyntaxSnapshot) { self.snapshot = snapshot; } @@ -255,10 +243,12 @@ impl SyntaxSnapshot { self.layers.is_empty() } - pub fn interpolate(&mut self, from_version: &clock::Global, text: &BufferSnapshot) { + fn interpolate(&mut self, text: &BufferSnapshot) { let edits = text - .anchored_edits_since::<(usize, Point)>(&from_version) + .anchored_edits_since::<(usize, Point)>(&self.interpolated_version) .collect::>(); + self.interpolated_version = text.version().clone(); + if edits.is_empty() { return; } @@ -372,12 +362,53 @@ impl SyntaxSnapshot { pub fn reparse( &mut self, - from_version: &clock::Global, text: &BufferSnapshot, registry: Option>, root_language: Arc, ) { - let edits = text.edits_since::(from_version).collect::>(); + let edit_ranges = text + .edits_since::(&self.parsed_version) + .map(|edit| edit.new) + .collect::>(); + self.reparse_with_ranges(text, root_language.clone(), edit_ranges, registry.as_ref()); + + if let Some(registry) = registry { + if registry.version() != self.language_registry_version { + let mut resolved_injection_ranges = Vec::new(); + let mut cursor = self + .layers + .filter::<_, ()>(|summary| summary.contains_unknown_injections); + cursor.next(text); + while let Some(layer) = cursor.item() { + let SyntaxLayerContent::Pending { language_name } = &layer.content else { unreachable!() }; + if language_for_injection(language_name, ®istry).is_some() { + resolved_injection_ranges.push(layer.range.to_offset(text)); + } + + cursor.next(text); + } + drop(cursor); + + if !resolved_injection_ranges.is_empty() { + self.reparse_with_ranges( + text, + root_language, + resolved_injection_ranges, + Some(®istry), + ); + } + self.language_registry_version = registry.version(); + } + } + } + + fn reparse_with_ranges( + &mut self, + text: &BufferSnapshot, + root_language: Arc, + invalidated_ranges: Vec>, + registry: Option<&Arc>, + ) { let max_depth = self.layers.summary().max_depth; let mut cursor = self.layers.cursor::(); cursor.next(&text); @@ -503,7 +534,7 @@ impl SyntaxSnapshot { Some(old_tree.clone()), ); changed_ranges = join_ranges( - edits.iter().map(|e| e.new.clone()).filter(|range| { + invalidated_ranges.iter().cloned().filter(|range| { range.start <= step_end_byte && range.end >= step_start_byte }), old_tree.changed_ranges(&tree).map(|r| { @@ -570,6 +601,8 @@ impl SyntaxSnapshot { drop(cursor); self.layers = layers; + self.interpolated_version = text.version.clone(); + self.parsed_version = text.version.clone(); } pub fn single_tree_captures<'a>( @@ -665,25 +698,12 @@ impl SyntaxSnapshot { }) } - pub fn unknown_injection_languages<'a>( - &'a self, - buffer: &'a BufferSnapshot, - ) -> impl 'a + Iterator> { - let mut cursor = self - .layers - .filter::<_, ()>(|summary| summary.contains_pending_layer); - cursor.next(buffer); - iter::from_fn(move || { - while let Some(layer) = cursor.item() { - if let SyntaxLayerContent::Pending { language_name } = &layer.content { - cursor.next(buffer); - return Some(language_name); - } else { - cursor.next(buffer); - } - } - None - }) + pub fn contains_unknown_injections(&self) -> bool { + self.layers.summary().contains_unknown_injections + } + + pub fn language_registry_version(&self) -> usize { + self.language_registry_version } } @@ -1058,7 +1078,7 @@ fn get_injections( combined_injection_ranges.clear(); for pattern in &config.patterns { if let (Some(language_name), true) = (pattern.language.as_ref(), pattern.combined) { - if let Some(language) = language_registry.language_for_name(language_name) { + if let Some(language) = language_for_injection(language_name, language_registry) { combined_injection_ranges.insert(language, Vec::new()); } } @@ -1103,9 +1123,7 @@ fn get_injections( }; if let Some(language_name) = language_name { - let language = language_registry - .language_for_name(&language_name) - .or_else(|| language_registry.language_for_extension(&language_name)); + let language = language_for_injection(&language_name, language_registry); let range = text.anchor_before(step_range.start)..text.anchor_after(step_range.end); if let Some(language) = language { if combined { @@ -1153,6 +1171,15 @@ fn get_injections( } } +fn language_for_injection( + language_name: &str, + language_registry: &LanguageRegistry, +) -> Option> { + language_registry + .language_for_name(language_name) + .or_else(|| language_registry.language_for_extension(language_name)) +} + fn splice_included_ranges( mut ranges: Vec, changed_ranges: &[Range], @@ -1379,7 +1406,7 @@ impl Default for SyntaxLayerSummary { range: Anchor::MAX..Anchor::MIN, last_layer_range: Anchor::MIN..Anchor::MAX, last_layer_language: None, - contains_pending_layer: false, + contains_unknown_injections: false, } } } @@ -1401,7 +1428,7 @@ impl sum_tree::Summary for SyntaxLayerSummary { } self.last_layer_range = other.last_layer_range.clone(); self.last_layer_language = other.last_layer_language; - self.contains_pending_layer |= other.contains_pending_layer; + self.contains_unknown_injections |= other.contains_unknown_injections; } } @@ -1452,7 +1479,7 @@ impl sum_tree::Item for SyntaxLayer { range: self.range.clone(), last_layer_range: self.range.clone(), last_layer_language: self.content.language_id(), - contains_pending_layer: matches!(self.content, SyntaxLayerContent::Pending { .. }), + contains_unknown_injections: matches!(self.content, SyntaxLayerContent::Pending { .. }), } } } @@ -1744,7 +1771,7 @@ mod tests { // Replace Ruby with a language that hasn't been loaded yet. let macro_name_range = range_for_text(&buffer, "ruby"); - buffer.edit([(macro_name_range, "erb")]); + buffer.edit([(macro_name_range, "html")]); syntax_map.interpolate(&buffer); syntax_map.reparse(markdown.clone(), &buffer); assert_layers_for_range( @@ -1755,14 +1782,20 @@ mod tests { "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter..." ], ); - assert_eq!( - syntax_map - .unknown_injection_languages(&buffer) - .collect::>(), - vec![&Arc::from("erb")] - ); + assert!(syntax_map.contains_unknown_injections()); - registry.add(Arc::new(erb_lang())); + registry.add(Arc::new(html_lang())); + syntax_map.reparse(markdown.clone(), &buffer); + assert_layers_for_range( + &syntax_map, + &buffer, + Point::new(3, 0)..Point::new(3, 0), + &[ + "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter...", + "(fragment (text))", + ], + ); + assert!(!syntax_map.contains_unknown_injections()); } #[gpui::test] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 995a6514c5d3d8472b295f8d30769989177ef76e..f324865b5cd291aac25b330940475893266c7b26 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1765,10 +1765,14 @@ impl Project { if let Some(project) = project.upgrade(&cx) { project.update(&mut cx, |project, cx| { let mut buffers_without_language = Vec::new(); + let mut buffers_with_unknown_injections = Vec::new(); for buffer in project.opened_buffers.values() { - if let Some(buffer) = buffer.upgrade(cx) { - if buffer.read(cx).language().is_none() { - buffers_without_language.push(buffer); + if let Some(handle) = buffer.upgrade(cx) { + let buffer = &handle.read(cx); + if buffer.language().is_none() { + buffers_without_language.push(handle); + } else if buffer.contains_unknown_injections() { + buffers_with_unknown_injections.push(handle); } } } @@ -1777,6 +1781,10 @@ impl Project { project.assign_language_to_buffer(&buffer, cx); project.register_buffer_with_language_server(&buffer, cx); } + + for buffer in buffers_with_unknown_injections { + buffer.update(cx, |buffer, cx| buffer.reparse(cx)); + } }); } } From 51984f0d39438b91865b07e7ff4400b3b702a593 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 24 Jan 2023 14:09:24 -0800 Subject: [PATCH 10/10] Fix feedback editor compile error due to LanguageRegistry API change --- crates/feedback/src/feedback_editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index 8185fbad9ac61244b1acad73baa9d2323222e974..ce0da1cf3c12bb6921608d900084e673c3b8aa97 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -123,7 +123,7 @@ impl FeedbackEditor { } fn new(project: ModelHandle, cx: &mut ViewContext) -> Self { - let markdown_language = project.read(cx).languages().get_language("Markdown"); + let markdown_language = project.read(cx).languages().language_for_name("Markdown"); let buffer = project .update(cx, |project, cx| {