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 <marshall@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-08-29 11:23:33 -07:00 committed by GitHub
parent cdaa80fefb
commit d60466212d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 124 additions and 63 deletions

View file

@ -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<Item = SyntaxLayer> + '_ {
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<D: ToOffset>(&self, position: D) -> Option<SyntaxLayer> {
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<Range<usize>> = 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<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
let range = range.start.to_offset(self)..range.end.to_offset(self);
let mut result: Option<Range<usize>> = 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,

View file

@ -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<Buffer>, cx: &mut gpui::TestAppContext) -> String {
buffer.update(cx, |buffer, _| {
let snapshot = buffer.snapshot();

View file

@ -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<String>,
/// 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<str> {
self.language.config.name.clone()
}
pub fn collapsed_placeholder(&self) -> &str {
self.language.config.collapsed_placeholder.as_ref()
}

View file

@ -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<SyntaxLayer> {
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<T>,
buffer: &'a BufferSnapshot,
include_hidden: bool,
) -> impl 'a + Iterator<Item = SyntaxLayer> {
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() {

View file

@ -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::<Vec<_>>();
assert_eq!(
layers.len(),

View file

@ -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