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);
|
let offset = position.to_offset(self);
|
||||||
self.syntax_map
|
self.syntax_map
|
||||||
.lock()
|
.lock()
|
||||||
.layers_for_range(offset..offset, &self.text)
|
.layers_for_range(offset..offset, &self.text, false)
|
||||||
.last()
|
.last()
|
||||||
.map(|info| info.language.clone())
|
.map(|info| info.language.clone())
|
||||||
.or_else(|| self.language.clone())
|
.or_else(|| self.language.clone())
|
||||||
|
@ -2625,13 +2625,14 @@ impl BufferSnapshot {
|
||||||
|
|
||||||
/// Iterates over every [`SyntaxLayer`] in the buffer.
|
/// Iterates over every [`SyntaxLayer`] in the buffer.
|
||||||
pub fn syntax_layers(&self) -> impl Iterator<Item = SyntaxLayer> + '_ {
|
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> {
|
pub fn syntax_layer_at<D: ToOffset>(&self, position: D) -> Option<SyntaxLayer> {
|
||||||
let offset = position.to_offset(self);
|
let offset = position.to_offset(self);
|
||||||
self.syntax
|
self.syntax
|
||||||
.layers_for_range(offset..offset, &self.text)
|
.layers_for_range(offset..offset, &self.text, false)
|
||||||
.filter(|l| l.node().end_byte() > offset)
|
.filter(|l| l.node().end_byte() > offset)
|
||||||
.last()
|
.last()
|
||||||
}
|
}
|
||||||
|
@ -2664,7 +2665,10 @@ impl BufferSnapshot {
|
||||||
let mut smallest_range: Option<Range<usize>> = None;
|
let mut smallest_range: Option<Range<usize>> = None;
|
||||||
|
|
||||||
// Use the layer that has the smallest node intersecting the given point.
|
// 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 cursor = layer.node().walk();
|
||||||
|
|
||||||
let mut range = None;
|
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>> {
|
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 range = range.start.to_offset(self)..range.end.to_offset(self);
|
||||||
let mut result: Option<Range<usize>> = None;
|
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();
|
let mut cursor = layer.node().walk();
|
||||||
|
|
||||||
// Descend to the first leaf that touches the start of the range,
|
// 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]
|
#[gpui::test]
|
||||||
fn test_serialization(cx: &mut gpui::AppContext) {
|
fn test_serialization(cx: &mut gpui::AppContext) {
|
||||||
let mut now = Instant::now();
|
let mut now = Instant::now();
|
||||||
|
@ -2868,6 +2907,45 @@ fn javascript_lang() -> Language {
|
||||||
.unwrap()
|
.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 {
|
fn get_tree_sexp(buffer: &Model<Buffer>, cx: &mut gpui::TestAppContext) -> String {
|
||||||
buffer.update(cx, |buffer, _| {
|
buffer.update(cx, |buffer, _| {
|
||||||
let snapshot = buffer.snapshot();
|
let snapshot = buffer.snapshot();
|
||||||
|
|
|
@ -17,7 +17,7 @@ mod syntax_map;
|
||||||
mod task_context;
|
mod task_context;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod buffer_tests;
|
pub mod buffer_tests;
|
||||||
pub mod markdown;
|
pub mod markdown;
|
||||||
|
|
||||||
use crate::language_settings::SoftWrap;
|
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.
|
/// If there's a parser name in the language settings, that will be used instead.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub prettier_parser_name: Option<String>,
|
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)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
|
||||||
|
@ -713,6 +717,7 @@ impl Default for LanguageConfig {
|
||||||
tab_size: None,
|
tab_size: None,
|
||||||
soft_wrap: None,
|
soft_wrap: None,
|
||||||
prettier_parser_name: None,
|
prettier_parser_name: None,
|
||||||
|
hidden: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1414,6 +1419,10 @@ impl Language {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageScope {
|
impl LanguageScope {
|
||||||
|
pub fn language_name(&self) -> Arc<str> {
|
||||||
|
self.language.config.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn collapsed_placeholder(&self) -> &str {
|
pub fn collapsed_placeholder(&self) -> &str {
|
||||||
self.language.config.collapsed_placeholder.as_ref()
|
self.language.config.collapsed_placeholder.as_ref()
|
||||||
}
|
}
|
||||||
|
|
|
@ -782,7 +782,7 @@ impl SyntaxSnapshot {
|
||||||
SyntaxMapCaptures::new(
|
SyntaxMapCaptures::new(
|
||||||
range.clone(),
|
range.clone(),
|
||||||
buffer.as_rope(),
|
buffer.as_rope(),
|
||||||
self.layers_for_range(range, buffer),
|
self.layers_for_range(range, buffer, true),
|
||||||
query,
|
query,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -796,20 +796,22 @@ impl SyntaxSnapshot {
|
||||||
SyntaxMapMatches::new(
|
SyntaxMapMatches::new(
|
||||||
range.clone(),
|
range.clone(),
|
||||||
buffer.as_rope(),
|
buffer.as_rope(),
|
||||||
self.layers_for_range(range, buffer),
|
self.layers_for_range(range, buffer, true),
|
||||||
query,
|
query,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn layers<'a>(&'a self, buffer: &'a BufferSnapshot) -> Vec<SyntaxLayer> {
|
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>(
|
pub fn layers_for_range<'a, T: ToOffset>(
|
||||||
&'a self,
|
&'a self,
|
||||||
range: Range<T>,
|
range: Range<T>,
|
||||||
buffer: &'a BufferSnapshot,
|
buffer: &'a BufferSnapshot,
|
||||||
|
include_hidden: bool,
|
||||||
) -> impl 'a + Iterator<Item = SyntaxLayer> {
|
) -> impl 'a + Iterator<Item = SyntaxLayer> {
|
||||||
let start_offset = range.start.to_offset(buffer);
|
let start_offset = range.start.to_offset(buffer);
|
||||||
let end_offset = range.end.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 {
|
if let SyntaxLayerContent::Parsed { tree, language } = &layer.content {
|
||||||
let layer_start_offset = layer.range.start.to_offset(buffer);
|
let layer_start_offset = layer.range.start.to_offset(buffer);
|
||||||
let layer_start_point = layer.range.start.to_point(buffer).to_ts_point();
|
let layer_start_point = layer.range.start.to_point(buffer).to_ts_point();
|
||||||
|
if include_hidden || !language.config.hidden {
|
||||||
info = Some(SyntaxLayer {
|
info = Some(SyntaxLayer {
|
||||||
tree,
|
tree,
|
||||||
language,
|
language,
|
||||||
depth: layer.depth,
|
depth: layer.depth,
|
||||||
offset: (layer_start_offset, layer_start_point),
|
offset: (layer_start_offset, layer_start_point),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cursor.next(buffer);
|
cursor.next(buffer);
|
||||||
if info.is_some() {
|
if info.is_some() {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{LanguageConfig, LanguageMatcher};
|
use crate::{
|
||||||
|
buffer_tests::{markdown_inline_lang, markdown_lang},
|
||||||
|
LanguageConfig, LanguageMatcher,
|
||||||
|
};
|
||||||
use gpui::AppContext;
|
use gpui::AppContext;
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
use std::{env, ops::Range, sync::Arc};
|
use std::{env, ops::Range, sync::Arc};
|
||||||
|
@ -779,8 +782,13 @@ fn test_empty_combined_injections_inside_injections(cx: &mut AppContext) {
|
||||||
&buffer,
|
&buffer,
|
||||||
Point::new(0, 0)..Point::new(5, 0),
|
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))))",
|
"(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...",
|
"(template...",
|
||||||
|
// Markdown inline content
|
||||||
|
"(inline)",
|
||||||
|
// HTML within the ERB
|
||||||
"(document (text))",
|
"(document (text))",
|
||||||
// The ruby syntax tree should be empty, since there are
|
// The ruby syntax tree should be empty, since there are
|
||||||
// no interpolations in the ERB template.
|
// no interpolations in the ERB template.
|
||||||
|
@ -1229,39 +1237,6 @@ fn rust_lang() -> Language {
|
||||||
.unwrap()
|
.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 {
|
fn elixir_lang() -> Language {
|
||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
|
@ -1327,7 +1302,7 @@ fn assert_layers_for_range(
|
||||||
expected_layers: &[&str],
|
expected_layers: &[&str],
|
||||||
) {
|
) {
|
||||||
let layers = syntax_map
|
let layers = syntax_map
|
||||||
.layers_for_range(range, &buffer)
|
.layers_for_range(range, &buffer, true)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
layers.len(),
|
layers.len(),
|
||||||
|
|
|
@ -1,14 +1,3 @@
|
||||||
name = "Markdown-Inline"
|
name = "Markdown-Inline"
|
||||||
grammar = "markdown-inline"
|
grammar = "markdown-inline"
|
||||||
path_suffixes = []
|
hidden = true
|
||||||
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
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue