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:
parent
cdaa80fefb
commit
d60466212d
6 changed files with 124 additions and 63 deletions
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue