From d60466212d6951ba489f553560de4e38e531481c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 29 Aug 2024 11:23:33 -0700 Subject: [PATCH] Hide Markdown-Inline language from users with a new 'hidden' flag on language configs (#17104) /cc @mrnugget Release Notes: - Fixed an issue where toggling inline completions in a markdown file did not work correctly --------- Co-authored-by: Marshall --- crates/language/src/buffer.rs | 17 ++-- crates/language/src/buffer_tests.rs | 78 +++++++++++++++++++ crates/language/src/language.rs | 11 ++- crates/language/src/syntax_map.rs | 23 +++--- .../src/syntax_map/syntax_map_tests.rs | 45 +++-------- .../languages/src/markdown-inline/config.toml | 13 +--- 6 files changed, 124 insertions(+), 63 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 9d11633c10a7f9f4115d0e7d8ff3f0c08250bed6..6e0050e643fe56fe5d1528bd9b5fcf6171d2bcaa 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1018,7 +1018,7 @@ impl Buffer { let offset = position.to_offset(self); self.syntax_map .lock() - .layers_for_range(offset..offset, &self.text) + .layers_for_range(offset..offset, &self.text, false) .last() .map(|info| info.language.clone()) .or_else(|| self.language.clone()) @@ -2625,13 +2625,14 @@ impl BufferSnapshot { /// Iterates over every [`SyntaxLayer`] in the buffer. pub fn syntax_layers(&self) -> impl Iterator + '_ { - self.syntax.layers_for_range(0..self.len(), &self.text) + self.syntax + .layers_for_range(0..self.len(), &self.text, true) } pub fn syntax_layer_at(&self, position: D) -> Option { let offset = position.to_offset(self); self.syntax - .layers_for_range(offset..offset, &self.text) + .layers_for_range(offset..offset, &self.text, false) .filter(|l| l.node().end_byte() > offset) .last() } @@ -2664,7 +2665,10 @@ impl BufferSnapshot { let mut smallest_range: Option> = None; // Use the layer that has the smallest node intersecting the given point. - for layer in self.syntax.layers_for_range(offset..offset, &self.text) { + for layer in self + .syntax + .layers_for_range(offset..offset, &self.text, false) + { let mut cursor = layer.node().walk(); let mut range = None; @@ -2740,7 +2744,10 @@ impl BufferSnapshot { pub fn range_for_syntax_ancestor(&self, range: Range) -> Option> { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut result: Option> = None; - 'outer: for layer in self.syntax.layers_for_range(range.clone(), &self.text) { + 'outer: for layer in self + .syntax + .layers_for_range(range.clone(), &self.text, true) + { let mut cursor = layer.node().walk(); // Descend to the first leaf that touches the start of the range, diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 096d26fbbc8a767c274981969ec8b875ac40ae2f..8584eee4c7d493093ccafd8e70e434bc30c2a693 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -2216,6 +2216,45 @@ fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) { }); } +#[gpui::test] +fn test_language_at_with_hidden_languages(cx: &mut AppContext) { + init_settings(cx, |_| {}); + + cx.new_model(|cx| { + let text = r#" + this is an *emphasized* word. + "# + .unindent(); + + let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone())); + language_registry.add(Arc::new(markdown_lang())); + language_registry.add(Arc::new(markdown_inline_lang())); + + let mut buffer = Buffer::local(text, cx); + buffer.set_language_registry(language_registry.clone()); + buffer.set_language( + language_registry + .language_for_name("Markdown") + .now_or_never() + .unwrap() + .ok(), + cx, + ); + + let snapshot = buffer.snapshot(); + + for point in [Point::new(0, 4), Point::new(0, 16)] { + let config = snapshot.language_scope_at(point).unwrap(); + assert_eq!(config.language_name().as_ref(), "Markdown"); + + let language = snapshot.language_at(point).unwrap(); + assert_eq!(language.name().as_ref(), "Markdown"); + } + + buffer + }); +} + #[gpui::test] fn test_serialization(cx: &mut gpui::AppContext) { let mut now = Instant::now(); @@ -2868,6 +2907,45 @@ fn javascript_lang() -> Language { .unwrap() } +pub fn markdown_lang() -> Language { + Language::new( + LanguageConfig { + name: "Markdown".into(), + matcher: LanguageMatcher { + path_suffixes: vec!["md".into()], + ..Default::default() + }, + ..Default::default() + }, + Some(tree_sitter_md::language()), + ) + .with_injection_query( + r#" + (fenced_code_block + (info_string + (language) @language) + (code_fence_content) @content) + + ((inline) @content + (#set! "language" "markdown-inline")) + "#, + ) + .unwrap() +} + +pub fn markdown_inline_lang() -> Language { + Language::new( + LanguageConfig { + name: "Markdown-Inline".into(), + hidden: true, + ..LanguageConfig::default() + }, + Some(tree_sitter_md::inline_language()), + ) + .with_highlights_query("(emphasis) @emphasis") + .unwrap() +} + fn get_tree_sexp(buffer: &Model, cx: &mut gpui::TestAppContext) -> String { buffer.update(cx, |buffer, _| { let snapshot = buffer.snapshot(); diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index e8c283e9c581dfc6bd9e97b78a2c39da760cb23a..b883e3fb451ee34bd3ba0cf6eeb412da35306c99 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -17,7 +17,7 @@ mod syntax_map; mod task_context; #[cfg(test)] -mod buffer_tests; +pub mod buffer_tests; pub mod markdown; use crate::language_settings::SoftWrap; @@ -627,6 +627,10 @@ pub struct LanguageConfig { /// If there's a parser name in the language settings, that will be used instead. #[serde(default)] pub prettier_parser_name: Option, + /// If true, this language is only for syntax highlighting via an injection into other + /// languages, but should not appear to the user as a distinct language. + #[serde(default)] + pub hidden: bool, } #[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)] @@ -713,6 +717,7 @@ impl Default for LanguageConfig { tab_size: None, soft_wrap: None, prettier_parser_name: None, + hidden: false, } } } @@ -1414,6 +1419,10 @@ impl Language { } impl LanguageScope { + pub fn language_name(&self) -> Arc { + self.language.config.name.clone() + } + pub fn collapsed_placeholder(&self) -> &str { self.language.config.collapsed_placeholder.as_ref() } diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 40fd73bbd9e6ef27ef701bb715f77c348ab953b4..cc8b0f823de1bc684f856beb7def1cb7eb750b4c 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -782,7 +782,7 @@ impl SyntaxSnapshot { SyntaxMapCaptures::new( range.clone(), buffer.as_rope(), - self.layers_for_range(range, buffer), + self.layers_for_range(range, buffer, true), query, ) } @@ -796,20 +796,22 @@ impl SyntaxSnapshot { SyntaxMapMatches::new( range.clone(), buffer.as_rope(), - self.layers_for_range(range, buffer), + self.layers_for_range(range, buffer, true), query, ) } #[cfg(test)] pub fn layers<'a>(&'a self, buffer: &'a BufferSnapshot) -> Vec { - self.layers_for_range(0..buffer.len(), buffer).collect() + self.layers_for_range(0..buffer.len(), buffer, true) + .collect() } pub fn layers_for_range<'a, T: ToOffset>( &'a self, range: Range, buffer: &'a BufferSnapshot, + include_hidden: bool, ) -> impl 'a + Iterator { let start_offset = range.start.to_offset(buffer); let end_offset = range.end.to_offset(buffer); @@ -833,13 +835,14 @@ impl SyntaxSnapshot { if let SyntaxLayerContent::Parsed { tree, language } = &layer.content { let layer_start_offset = layer.range.start.to_offset(buffer); let layer_start_point = layer.range.start.to_point(buffer).to_ts_point(); - - info = Some(SyntaxLayer { - tree, - language, - depth: layer.depth, - offset: (layer_start_offset, layer_start_point), - }); + if include_hidden || !language.config.hidden { + info = Some(SyntaxLayer { + tree, + language, + depth: layer.depth, + offset: (layer_start_offset, layer_start_point), + }); + } } cursor.next(buffer); if info.is_some() { diff --git a/crates/language/src/syntax_map/syntax_map_tests.rs b/crates/language/src/syntax_map/syntax_map_tests.rs index 88d168855edf22fe8d7a45051233b9fcfbee86cc..0612ad35b47511551aad46f46646f9c4ca8681aa 100644 --- a/crates/language/src/syntax_map/syntax_map_tests.rs +++ b/crates/language/src/syntax_map/syntax_map_tests.rs @@ -1,5 +1,8 @@ use super::*; -use crate::{LanguageConfig, LanguageMatcher}; +use crate::{ + buffer_tests::{markdown_inline_lang, markdown_lang}, + LanguageConfig, LanguageMatcher, +}; use gpui::AppContext; use rand::rngs::StdRng; use std::{env, ops::Range, sync::Arc}; @@ -779,8 +782,13 @@ fn test_empty_combined_injections_inside_injections(cx: &mut AppContext) { &buffer, Point::new(0, 0)..Point::new(5, 0), &[ + // Markdown document "(document (section (fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (block_continuation) (code_fence_content (block_continuation)) (fenced_code_block_delimiter)) (paragraph (inline))))", + // ERB template in the code block "(template...", + // Markdown inline content + "(inline)", + // HTML within the ERB "(document (text))", // The ruby syntax tree should be empty, since there are // no interpolations in the ERB template. @@ -1229,39 +1237,6 @@ fn rust_lang() -> Language { .unwrap() } -fn markdown_lang() -> Language { - Language::new( - LanguageConfig { - name: "Markdown".into(), - matcher: LanguageMatcher { - path_suffixes: vec!["md".into()], - ..Default::default() - }, - ..Default::default() - }, - Some(tree_sitter_md::language()), - ) - .with_injection_query( - r#" - (fenced_code_block - (info_string - (language) @language) - (code_fence_content) @content) - "#, - ) - .unwrap() -} - -fn markdown_inline_lang() -> Language { - Language::new( - LanguageConfig { - name: "Markdown-Inline".into(), - ..LanguageConfig::default() - }, - Some(tree_sitter_md::inline_language()), - ) -} - fn elixir_lang() -> Language { Language::new( LanguageConfig { @@ -1327,7 +1302,7 @@ fn assert_layers_for_range( expected_layers: &[&str], ) { let layers = syntax_map - .layers_for_range(range, &buffer) + .layers_for_range(range, &buffer, true) .collect::>(); assert_eq!( layers.len(), diff --git a/crates/languages/src/markdown-inline/config.toml b/crates/languages/src/markdown-inline/config.toml index 8f1ab65d6c307200fb4c387402ee055f09e7a024..e0b41c72c28fe5e9c3940a662e1facb548c8b5b5 100644 --- a/crates/languages/src/markdown-inline/config.toml +++ b/crates/languages/src/markdown-inline/config.toml @@ -1,14 +1,3 @@ name = "Markdown-Inline" grammar = "markdown-inline" -path_suffixes = [] -brackets = [ - { start = "{", end = "}", close = true, newline = true }, - { start = "[", end = "]", close = true, newline = true }, - { start = "(", end = ")", close = true, newline = true }, - { start = "<", end = ">", close = true, newline = true }, - { start = "\"", end = "\"", close = false, newline = false }, - { start = "'", end = "'", close = false, newline = false }, - { start = "`", end = "`", close = false, newline = false }, -] - -tab_size = 2 +hidden = true