Add Buffer::language_at, update MultiBuffer to use it
Co-authored-by: Julia Risley <floc@unpromptedtirade.com>
This commit is contained in:
parent
a2e57e8d71
commit
67e188a015
16 changed files with 245 additions and 81 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -1719,6 +1719,8 @@ dependencies = [
|
||||||
"text",
|
"text",
|
||||||
"theme",
|
"theme",
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
|
"tree-sitter-html",
|
||||||
|
"tree-sitter-javascript",
|
||||||
"tree-sitter-rust",
|
"tree-sitter-rust",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
|
@ -6062,6 +6064,16 @@ dependencies = [
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-javascript"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2490fab08630b2c8943c320f7b63473cbf65511c8d83aec551beb9b4375906ed"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"tree-sitter",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tree-sitter-json"
|
name = "tree-sitter-json"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
|
|
|
@ -51,6 +51,8 @@ serde = { version = "1.0", features = ["derive", "rc"] }
|
||||||
smallvec = { version = "1.6", features = ["union"] }
|
smallvec = { version = "1.6", features = ["union"] }
|
||||||
smol = "1.2"
|
smol = "1.2"
|
||||||
tree-sitter-rust = { version = "*", optional = true }
|
tree-sitter-rust = { version = "*", optional = true }
|
||||||
|
tree-sitter-html = { version = "*", optional = true }
|
||||||
|
tree-sitter-javascript = { version = "*", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
text = { path = "../text", features = ["test-support"] }
|
text = { path = "../text", features = ["test-support"] }
|
||||||
|
@ -67,3 +69,5 @@ rand = "0.8"
|
||||||
unindent = "0.1.7"
|
unindent = "0.1.7"
|
||||||
tree-sitter = "0.20"
|
tree-sitter = "0.20"
|
||||||
tree-sitter-rust = "0.20"
|
tree-sitter-rust = "0.20"
|
||||||
|
tree-sitter-html = "0.19"
|
||||||
|
tree-sitter-javascript = "0.20"
|
||||||
|
|
|
@ -1116,7 +1116,7 @@ impl Editor {
|
||||||
&self,
|
&self,
|
||||||
point: T,
|
point: T,
|
||||||
cx: &'a AppContext,
|
cx: &'a AppContext,
|
||||||
) -> Option<&'a Arc<Language>> {
|
) -> Option<Arc<Language>> {
|
||||||
self.buffer.read(cx).language_at(point, cx)
|
self.buffer.read(cx).language_at(point, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4501,9 +4501,9 @@ impl Editor {
|
||||||
// as that portion won't be used for detecting if a line is a comment.
|
// as that portion won't be used for detecting if a line is a comment.
|
||||||
let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
|
let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
|
||||||
.language_at(selection.start, cx)
|
.language_at(selection.start, cx)
|
||||||
.and_then(|l| l.line_comment_prefix())
|
.and_then(|l| l.line_comment_prefix().map(|p| p.into()))
|
||||||
{
|
{
|
||||||
prefix.into()
|
prefix
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -6713,7 +6713,7 @@ mod tests {
|
||||||
platform::{WindowBounds, WindowOptions},
|
platform::{WindowBounds, WindowOptions},
|
||||||
};
|
};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{FakeLspAdapter, LanguageConfig};
|
use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry};
|
||||||
use project::FakeFs;
|
use project::FakeFs;
|
||||||
use settings::EditorSettings;
|
use settings::EditorSettings;
|
||||||
use std::{cell::RefCell, rc::Rc, time::Instant};
|
use std::{cell::RefCell, rc::Rc, time::Instant};
|
||||||
|
@ -9792,6 +9792,92 @@ mod tests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
|
||||||
|
let html_language = Arc::new(
|
||||||
|
Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "HTML".into(),
|
||||||
|
brackets: vec![BracketPair {
|
||||||
|
start: "<".to_string(),
|
||||||
|
end: ">".to_string(),
|
||||||
|
close: true,
|
||||||
|
newline: true,
|
||||||
|
}],
|
||||||
|
autoclose_before: "})]".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Some(tree_sitter_html::language()),
|
||||||
|
)
|
||||||
|
.with_injection_query(
|
||||||
|
r#"
|
||||||
|
(script_element
|
||||||
|
(raw_text) @content
|
||||||
|
(#set! "language" "javascript"))
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let javascript_language = Arc::new(Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "JavaScript".into(),
|
||||||
|
brackets: vec![BracketPair {
|
||||||
|
start: "/*".to_string(),
|
||||||
|
end: "*/".to_string(),
|
||||||
|
close: true,
|
||||||
|
newline: true,
|
||||||
|
}],
|
||||||
|
autoclose_before: "})]".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Some(tree_sitter_javascript::language()),
|
||||||
|
));
|
||||||
|
|
||||||
|
let registry = Arc::new(LanguageRegistry::test());
|
||||||
|
registry.add(html_language.clone());
|
||||||
|
registry.add(javascript_language.clone());
|
||||||
|
|
||||||
|
cx.update_buffer(|buffer, cx| {
|
||||||
|
buffer.set_language_registry(registry);
|
||||||
|
buffer.set_language(Some(html_language), cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.set_state(
|
||||||
|
&r#"
|
||||||
|
<body>ˇ
|
||||||
|
<script>
|
||||||
|
var x = 1;ˇ
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let cursors = cx.update_editor(|editor, cx| editor.selections.ranges::<usize>(cx));
|
||||||
|
cx.update_buffer(|buffer, _| {
|
||||||
|
let snapshot = buffer.snapshot();
|
||||||
|
assert_eq!(
|
||||||
|
snapshot
|
||||||
|
.language_at(cursors[0].start)
|
||||||
|
.unwrap()
|
||||||
|
.name()
|
||||||
|
.as_ref(),
|
||||||
|
"HTML"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
snapshot
|
||||||
|
.language_at(cursors[1].start)
|
||||||
|
.unwrap()
|
||||||
|
.name()
|
||||||
|
.as_ref(),
|
||||||
|
"JavaScript"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
|
async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
|
||||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
|
|
|
@ -1212,9 +1212,9 @@ impl MultiBuffer {
|
||||||
&self,
|
&self,
|
||||||
point: T,
|
point: T,
|
||||||
cx: &'a AppContext,
|
cx: &'a AppContext,
|
||||||
) -> Option<&'a Arc<Language>> {
|
) -> Option<Arc<Language>> {
|
||||||
self.point_to_buffer_offset(point, cx)
|
self.point_to_buffer_offset(point, cx)
|
||||||
.and_then(|(buffer, _)| buffer.read(cx).language())
|
.and_then(|(buffer, offset)| buffer.read(cx).language_at(offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn files<'a>(&'a self, cx: &'a AppContext) -> SmallVec<[&'a dyn File; 2]> {
|
pub fn files<'a>(&'a self, cx: &'a AppContext) -> SmallVec<[&'a dyn File; 2]> {
|
||||||
|
@ -1940,6 +1940,24 @@ impl MultiBufferSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn point_to_buffer_offset<T: ToOffset>(
|
||||||
|
&self,
|
||||||
|
point: T,
|
||||||
|
) -> Option<(&BufferSnapshot, usize)> {
|
||||||
|
let offset = point.to_offset(&self);
|
||||||
|
let mut cursor = self.excerpts.cursor::<usize>();
|
||||||
|
cursor.seek(&offset, Bias::Right, &());
|
||||||
|
if cursor.item().is_none() {
|
||||||
|
cursor.prev(&());
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.item().map(|excerpt| {
|
||||||
|
let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
|
||||||
|
let buffer_point = excerpt_start + offset - *cursor.start();
|
||||||
|
(&excerpt.buffer, buffer_point)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn suggested_indents(
|
pub fn suggested_indents(
|
||||||
&self,
|
&self,
|
||||||
rows: impl IntoIterator<Item = u32>,
|
rows: impl IntoIterator<Item = u32>,
|
||||||
|
@ -2490,6 +2508,11 @@ impl MultiBufferSnapshot {
|
||||||
.and_then(|excerpt| excerpt.buffer.language())
|
.and_then(|excerpt| excerpt.buffer.language())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn language_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc<Language>> {
|
||||||
|
self.point_to_buffer_offset(point)
|
||||||
|
.and_then(|(buffer, offset)| buffer.language_at(offset))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_dirty(&self) -> bool {
|
pub fn is_dirty(&self) -> bool {
|
||||||
self.is_dirty
|
self.is_dirty
|
||||||
}
|
}
|
||||||
|
|
|
@ -641,6 +641,15 @@ impl Buffer {
|
||||||
self.language.as_ref()
|
self.language.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn language_at<D: ToOffset>(&self, position: D) -> Option<Arc<Language>> {
|
||||||
|
let offset = position.to_offset(self);
|
||||||
|
self.syntax_map
|
||||||
|
.lock()
|
||||||
|
.layers_for_range(offset..offset, &self.text)
|
||||||
|
.last()
|
||||||
|
.map(|info| info.language.clone())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_count(&self) -> usize {
|
pub fn parse_count(&self) -> usize {
|
||||||
self.parse_count
|
self.parse_count
|
||||||
}
|
}
|
||||||
|
@ -1826,6 +1835,14 @@ impl BufferSnapshot {
|
||||||
self.language.as_ref()
|
self.language.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn language_at<D: ToOffset>(&self, position: D) -> Option<&Arc<Language>> {
|
||||||
|
let offset = position.to_offset(self);
|
||||||
|
self.syntax
|
||||||
|
.layers_for_range(offset..offset, &self.text)
|
||||||
|
.last()
|
||||||
|
.map(|info| info.language)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn surrounding_word<T: ToOffset>(&self, start: T) -> (Range<usize>, Option<CharKind>) {
|
pub fn surrounding_word<T: ToOffset>(&self, start: T) -> (Range<usize>, Option<CharKind>) {
|
||||||
let mut start = start.to_offset(self);
|
let mut start = start.to_offset(self);
|
||||||
let mut end = start;
|
let mut end = start;
|
||||||
|
@ -1858,8 +1875,8 @@ 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 (_, _, node) in self.syntax.layers_for_range(range.clone(), &self.text) {
|
'outer: for layer in self.syntax.layers_for_range(range.clone(), &self.text) {
|
||||||
let mut cursor = 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,
|
||||||
// and if the range is non-empty, extends beyond the start.
|
// and if the range is non-empty, extends beyond the start.
|
||||||
|
|
|
@ -135,7 +135,7 @@ impl CachedLspAdapter {
|
||||||
pub async fn label_for_completion(
|
pub async fn label_for_completion(
|
||||||
&self,
|
&self,
|
||||||
completion_item: &lsp::CompletionItem,
|
completion_item: &lsp::CompletionItem,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
self.adapter
|
self.adapter
|
||||||
.label_for_completion(completion_item, language)
|
.label_for_completion(completion_item, language)
|
||||||
|
@ -146,7 +146,7 @@ impl CachedLspAdapter {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
kind: lsp::SymbolKind,
|
kind: lsp::SymbolKind,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
self.adapter.label_for_symbol(name, kind, language).await
|
self.adapter.label_for_symbol(name, kind, language).await
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||||
async fn label_for_completion(
|
async fn label_for_completion(
|
||||||
&self,
|
&self,
|
||||||
_: &lsp::CompletionItem,
|
_: &lsp::CompletionItem,
|
||||||
_: &Language,
|
_: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||||
&self,
|
&self,
|
||||||
_: &str,
|
_: &str,
|
||||||
_: lsp::SymbolKind,
|
_: lsp::SymbolKind,
|
||||||
_: &Language,
|
_: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -793,7 +793,7 @@ impl Language {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn label_for_completion(
|
pub async fn label_for_completion(
|
||||||
&self,
|
self: &Arc<Self>,
|
||||||
completion: &lsp::CompletionItem,
|
completion: &lsp::CompletionItem,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
self.adapter
|
self.adapter
|
||||||
|
@ -802,7 +802,11 @@ impl Language {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn label_for_symbol(&self, name: &str, kind: lsp::SymbolKind) -> Option<CodeLabel> {
|
pub async fn label_for_symbol(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
name: &str,
|
||||||
|
kind: lsp::SymbolKind,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
self.adapter
|
self.adapter
|
||||||
.as_ref()?
|
.as_ref()?
|
||||||
.label_for_symbol(name, kind, self)
|
.label_for_symbol(name, kind, self)
|
||||||
|
@ -810,20 +814,17 @@ impl Language {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn highlight_text<'a>(
|
pub fn highlight_text<'a>(
|
||||||
&'a self,
|
self: &'a Arc<Self>,
|
||||||
text: &'a Rope,
|
text: &'a Rope,
|
||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
) -> Vec<(Range<usize>, HighlightId)> {
|
) -> Vec<(Range<usize>, HighlightId)> {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
if let Some(grammar) = &self.grammar {
|
if let Some(grammar) = &self.grammar {
|
||||||
let tree = grammar.parse_text(text, None);
|
let tree = grammar.parse_text(text, None);
|
||||||
let captures = SyntaxSnapshot::single_tree_captures(
|
let captures =
|
||||||
range.clone(),
|
SyntaxSnapshot::single_tree_captures(range.clone(), text, &tree, self, |grammar| {
|
||||||
text,
|
grammar.highlights_query.as_ref()
|
||||||
&tree,
|
});
|
||||||
grammar,
|
|
||||||
|grammar| grammar.highlights_query.as_ref(),
|
|
||||||
);
|
|
||||||
let highlight_maps = vec![grammar.highlight_map()];
|
let highlight_maps = vec![grammar.highlight_map()];
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for chunk in BufferChunks::new(text, range, Some((captures, highlight_maps)), vec![]) {
|
for chunk in BufferChunks::new(text, range, Some((captures, highlight_maps)), vec![]) {
|
||||||
|
|
|
@ -92,6 +92,12 @@ struct SyntaxLayer {
|
||||||
language: Arc<Language>,
|
language: Arc<Language>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SyntaxLayerInfo<'a> {
|
||||||
|
pub depth: usize,
|
||||||
|
pub node: Node<'a>,
|
||||||
|
pub language: &'a Arc<Language>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct SyntaxLayerSummary {
|
struct SyntaxLayerSummary {
|
||||||
min_depth: usize,
|
min_depth: usize,
|
||||||
|
@ -473,13 +479,18 @@ impl SyntaxSnapshot {
|
||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
text: &'a Rope,
|
text: &'a Rope,
|
||||||
tree: &'a Tree,
|
tree: &'a Tree,
|
||||||
grammar: &'a Grammar,
|
language: &'a Arc<Language>,
|
||||||
query: fn(&Grammar) -> Option<&Query>,
|
query: fn(&Grammar) -> Option<&Query>,
|
||||||
) -> SyntaxMapCaptures<'a> {
|
) -> SyntaxMapCaptures<'a> {
|
||||||
SyntaxMapCaptures::new(
|
SyntaxMapCaptures::new(
|
||||||
range.clone(),
|
range.clone(),
|
||||||
text,
|
text,
|
||||||
[(grammar, 0, tree.root_node())].into_iter(),
|
[SyntaxLayerInfo {
|
||||||
|
language,
|
||||||
|
depth: 0,
|
||||||
|
node: tree.root_node(),
|
||||||
|
}]
|
||||||
|
.into_iter(),
|
||||||
query,
|
query,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -513,7 +524,7 @@ impl SyntaxSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn layers(&self, buffer: &BufferSnapshot) -> Vec<(&Grammar, usize, Node)> {
|
pub fn layers(&self, buffer: &BufferSnapshot) -> Vec<SyntaxLayerInfo> {
|
||||||
self.layers_for_range(0..buffer.len(), buffer)
|
self.layers_for_range(0..buffer.len(), buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,7 +532,7 @@ impl SyntaxSnapshot {
|
||||||
&self,
|
&self,
|
||||||
range: Range<T>,
|
range: Range<T>,
|
||||||
buffer: &BufferSnapshot,
|
buffer: &BufferSnapshot,
|
||||||
) -> Vec<(&Grammar, usize, Node)> {
|
) -> Vec<SyntaxLayerInfo> {
|
||||||
let start = buffer.anchor_before(range.start.to_offset(buffer));
|
let start = buffer.anchor_before(range.start.to_offset(buffer));
|
||||||
let end = buffer.anchor_after(range.end.to_offset(buffer));
|
let end = buffer.anchor_after(range.end.to_offset(buffer));
|
||||||
|
|
||||||
|
@ -538,16 +549,14 @@ impl SyntaxSnapshot {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
cursor.next(buffer);
|
cursor.next(buffer);
|
||||||
while let Some(layer) = cursor.item() {
|
while let Some(layer) = cursor.item() {
|
||||||
if let Some(grammar) = &layer.language.grammar {
|
result.push(SyntaxLayerInfo {
|
||||||
result.push((
|
language: &layer.language,
|
||||||
grammar.as_ref(),
|
depth: layer.depth,
|
||||||
layer.depth,
|
node: layer.tree.root_node_with_offset(
|
||||||
layer.tree.root_node_with_offset(
|
layer.range.start.to_offset(buffer),
|
||||||
layer.range.start.to_offset(buffer),
|
layer.range.start.to_point(buffer).to_ts_point(),
|
||||||
layer.range.start.to_point(buffer).to_ts_point(),
|
),
|
||||||
),
|
});
|
||||||
));
|
|
||||||
}
|
|
||||||
cursor.next(buffer)
|
cursor.next(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +568,7 @@ impl<'a> SyntaxMapCaptures<'a> {
|
||||||
fn new(
|
fn new(
|
||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
text: &'a Rope,
|
text: &'a Rope,
|
||||||
layers: impl Iterator<Item = (&'a Grammar, usize, Node<'a>)>,
|
layers: impl Iterator<Item = SyntaxLayerInfo<'a>>,
|
||||||
query: fn(&Grammar) -> Option<&Query>,
|
query: fn(&Grammar) -> Option<&Query>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut result = Self {
|
let mut result = Self {
|
||||||
|
@ -567,11 +576,19 @@ impl<'a> SyntaxMapCaptures<'a> {
|
||||||
grammars: Vec::new(),
|
grammars: Vec::new(),
|
||||||
active_layer_count: 0,
|
active_layer_count: 0,
|
||||||
};
|
};
|
||||||
for (grammar, depth, node) in layers {
|
for SyntaxLayerInfo {
|
||||||
let query = if let Some(query) = query(grammar) {
|
language,
|
||||||
query
|
depth,
|
||||||
} else {
|
node,
|
||||||
continue;
|
} in layers
|
||||||
|
{
|
||||||
|
let grammar = match &language.grammar {
|
||||||
|
Some(grammer) => grammer,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let query = match query(&grammar) {
|
||||||
|
Some(query) => query,
|
||||||
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut query_cursor = QueryCursorHandle::new();
|
let mut query_cursor = QueryCursorHandle::new();
|
||||||
|
@ -678,15 +695,23 @@ impl<'a> SyntaxMapMatches<'a> {
|
||||||
fn new(
|
fn new(
|
||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
text: &'a Rope,
|
text: &'a Rope,
|
||||||
layers: impl Iterator<Item = (&'a Grammar, usize, Node<'a>)>,
|
layers: impl Iterator<Item = SyntaxLayerInfo<'a>>,
|
||||||
query: fn(&Grammar) -> Option<&Query>,
|
query: fn(&Grammar) -> Option<&Query>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut result = Self::default();
|
let mut result = Self::default();
|
||||||
for (grammar, depth, node) in layers {
|
for SyntaxLayerInfo {
|
||||||
let query = if let Some(query) = query(grammar) {
|
language,
|
||||||
query
|
depth,
|
||||||
} else {
|
node,
|
||||||
continue;
|
} in layers
|
||||||
|
{
|
||||||
|
let grammar = match &language.grammar {
|
||||||
|
Some(grammer) => grammer,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let query = match query(&grammar) {
|
||||||
|
Some(query) => query,
|
||||||
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut query_cursor = QueryCursorHandle::new();
|
let mut query_cursor = QueryCursorHandle::new();
|
||||||
|
@ -1624,8 +1649,8 @@ mod tests {
|
||||||
let reference_layers = reference_syntax_map.layers(&buffer);
|
let reference_layers = reference_syntax_map.layers(&buffer);
|
||||||
for (edited_layer, reference_layer) in layers.into_iter().zip(reference_layers.into_iter())
|
for (edited_layer, reference_layer) in layers.into_iter().zip(reference_layers.into_iter())
|
||||||
{
|
{
|
||||||
assert_eq!(edited_layer.2.to_sexp(), reference_layer.2.to_sexp());
|
assert_eq!(edited_layer.node.to_sexp(), reference_layer.node.to_sexp());
|
||||||
assert_eq!(edited_layer.2.range(), reference_layer.2.range());
|
assert_eq!(edited_layer.node.range(), reference_layer.node.range());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1770,13 +1795,13 @@ mod tests {
|
||||||
mutated_layers.into_iter().zip(reference_layers.into_iter())
|
mutated_layers.into_iter().zip(reference_layers.into_iter())
|
||||||
{
|
{
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edited_layer.2.to_sexp(),
|
edited_layer.node.to_sexp(),
|
||||||
reference_layer.2.to_sexp(),
|
reference_layer.node.to_sexp(),
|
||||||
"different layer at step {i}"
|
"different layer at step {i}"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edited_layer.2.range(),
|
edited_layer.node.range(),
|
||||||
reference_layer.2.range(),
|
reference_layer.node.range(),
|
||||||
"different layer at step {i}"
|
"different layer at step {i}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1828,7 +1853,7 @@ mod tests {
|
||||||
expected_layers.len(),
|
expected_layers.len(),
|
||||||
"wrong number of layers"
|
"wrong number of layers"
|
||||||
);
|
);
|
||||||
for (i, ((_, _, node), expected_s_exp)) in
|
for (i, (SyntaxLayerInfo { node, .. }, expected_s_exp)) in
|
||||||
layers.iter().zip(expected_layers.iter()).enumerate()
|
layers.iter().zip(expected_layers.iter()).enumerate()
|
||||||
{
|
{
|
||||||
let actual_s_exp = node.to_sexp();
|
let actual_s_exp = node.to_sexp();
|
||||||
|
|
|
@ -1449,7 +1449,7 @@ fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> Str
|
||||||
buffer.read_with(cx, |buffer, _| {
|
buffer.read_with(cx, |buffer, _| {
|
||||||
let snapshot = buffer.snapshot();
|
let snapshot = buffer.snapshot();
|
||||||
let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
|
let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
|
||||||
layers[0].2.to_sexp()
|
layers[0].node.to_sexp()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ pub async fn init(languages: Arc<LanguageRegistry>, _executor: Arc<Background>)
|
||||||
Some(CachedLspAdapter::new(html::HtmlLspAdapter).await),
|
Some(CachedLspAdapter::new(html::HtmlLspAdapter).await),
|
||||||
),
|
),
|
||||||
] {
|
] {
|
||||||
languages.add(Arc::new(language(name, grammar, lsp_adapter)));
|
languages.add(language(name, grammar, lsp_adapter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ pub(crate) fn language(
|
||||||
name: &str,
|
name: &str,
|
||||||
grammar: tree_sitter::Language,
|
grammar: tree_sitter::Language,
|
||||||
lsp_adapter: Option<Arc<CachedLspAdapter>>,
|
lsp_adapter: Option<Arc<CachedLspAdapter>>,
|
||||||
) -> Language {
|
) -> Arc<Language> {
|
||||||
let config = toml::from_slice(
|
let config = toml::from_slice(
|
||||||
&LanguageDir::get(&format!("{}/config.toml", name))
|
&LanguageDir::get(&format!("{}/config.toml", name))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -153,7 +153,7 @@ pub(crate) fn language(
|
||||||
if let Some(lsp_adapter) = lsp_adapter {
|
if let Some(lsp_adapter) = lsp_adapter {
|
||||||
language = language.with_lsp_adapter(lsp_adapter)
|
language = language.with_lsp_adapter(lsp_adapter)
|
||||||
}
|
}
|
||||||
language
|
Arc::new(language)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_query(name: &str, filename_prefix: &str) -> Option<Cow<'static, str>> {
|
fn load_query(name: &str, filename_prefix: &str) -> Option<Cow<'static, str>> {
|
||||||
|
|
|
@ -112,7 +112,7 @@ impl super::LspAdapter for CLspAdapter {
|
||||||
async fn label_for_completion(
|
async fn label_for_completion(
|
||||||
&self,
|
&self,
|
||||||
completion: &lsp::CompletionItem,
|
completion: &lsp::CompletionItem,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
let label = completion
|
let label = completion
|
||||||
.label
|
.label
|
||||||
|
@ -190,7 +190,7 @@ impl super::LspAdapter for CLspAdapter {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
kind: lsp::SymbolKind,
|
kind: lsp::SymbolKind,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
let (text, filter_range, display_range) = match kind {
|
let (text, filter_range, display_range) = match kind {
|
||||||
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
||||||
|
@ -251,7 +251,6 @@ mod tests {
|
||||||
use gpui::MutableAppContext;
|
use gpui::MutableAppContext;
|
||||||
use language::{AutoindentMode, Buffer};
|
use language::{AutoindentMode, Buffer};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_c_autoindent(cx: &mut MutableAppContext) {
|
fn test_c_autoindent(cx: &mut MutableAppContext) {
|
||||||
|
@ -262,7 +261,7 @@ mod tests {
|
||||||
let language = crate::languages::language("c", tree_sitter_c::language(), None);
|
let language = crate::languages::language("c", tree_sitter_c::language(), None);
|
||||||
|
|
||||||
cx.add_model(|cx| {
|
cx.add_model(|cx| {
|
||||||
let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(language), cx);
|
let mut buffer = Buffer::new(0, "", cx).with_language(language, cx);
|
||||||
|
|
||||||
// empty function
|
// empty function
|
||||||
buffer.edit([(0..0, "int main() {}")], None, cx);
|
buffer.edit([(0..0, "int main() {}")], None, cx);
|
||||||
|
|
|
@ -113,7 +113,7 @@ impl LspAdapter for ElixirLspAdapter {
|
||||||
async fn label_for_completion(
|
async fn label_for_completion(
|
||||||
&self,
|
&self,
|
||||||
completion: &lsp::CompletionItem,
|
completion: &lsp::CompletionItem,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
match completion.kind.zip(completion.detail.as_ref()) {
|
match completion.kind.zip(completion.detail.as_ref()) {
|
||||||
Some((_, detail)) if detail.starts_with("(function)") => {
|
Some((_, detail)) if detail.starts_with("(function)") => {
|
||||||
|
@ -168,7 +168,7 @@ impl LspAdapter for ElixirLspAdapter {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
kind: SymbolKind,
|
kind: SymbolKind,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
let (text, filter_range, display_range) = match kind {
|
let (text, filter_range, display_range) = match kind {
|
||||||
SymbolKind::METHOD | SymbolKind::FUNCTION => {
|
SymbolKind::METHOD | SymbolKind::FUNCTION => {
|
||||||
|
|
|
@ -134,7 +134,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
async fn label_for_completion(
|
async fn label_for_completion(
|
||||||
&self,
|
&self,
|
||||||
completion: &lsp::CompletionItem,
|
completion: &lsp::CompletionItem,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
let label = &completion.label;
|
let label = &completion.label;
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
kind: lsp::SymbolKind,
|
kind: lsp::SymbolKind,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
let (text, filter_range, display_range) = match kind {
|
let (text, filter_range, display_range) = match kind {
|
||||||
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl LspAdapter for PythonLspAdapter {
|
||||||
async fn label_for_completion(
|
async fn label_for_completion(
|
||||||
&self,
|
&self,
|
||||||
item: &lsp::CompletionItem,
|
item: &lsp::CompletionItem,
|
||||||
language: &language::Language,
|
language: &Arc<language::Language>,
|
||||||
) -> Option<language::CodeLabel> {
|
) -> Option<language::CodeLabel> {
|
||||||
let label = &item.label;
|
let label = &item.label;
|
||||||
let grammar = language.grammar()?;
|
let grammar = language.grammar()?;
|
||||||
|
@ -112,7 +112,7 @@ impl LspAdapter for PythonLspAdapter {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
kind: lsp::SymbolKind,
|
kind: lsp::SymbolKind,
|
||||||
language: &language::Language,
|
language: &Arc<language::Language>,
|
||||||
) -> Option<language::CodeLabel> {
|
) -> Option<language::CodeLabel> {
|
||||||
let (text, filter_range, display_range) = match kind {
|
let (text, filter_range, display_range) = match kind {
|
||||||
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
||||||
|
@ -149,7 +149,6 @@ mod tests {
|
||||||
use gpui::{ModelContext, MutableAppContext};
|
use gpui::{ModelContext, MutableAppContext};
|
||||||
use language::{AutoindentMode, Buffer};
|
use language::{AutoindentMode, Buffer};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_python_autoindent(cx: &mut MutableAppContext) {
|
fn test_python_autoindent(cx: &mut MutableAppContext) {
|
||||||
|
@ -160,7 +159,7 @@ mod tests {
|
||||||
cx.set_global(settings);
|
cx.set_global(settings);
|
||||||
|
|
||||||
cx.add_model(|cx| {
|
cx.add_model(|cx| {
|
||||||
let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(language), cx);
|
let mut buffer = Buffer::new(0, "", cx).with_language(language, cx);
|
||||||
let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext<Buffer>| {
|
let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext<Buffer>| {
|
||||||
let ix = buffer.len();
|
let ix = buffer.len();
|
||||||
buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx);
|
buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx);
|
||||||
|
|
|
@ -119,7 +119,7 @@ impl LspAdapter for RustLspAdapter {
|
||||||
async fn label_for_completion(
|
async fn label_for_completion(
|
||||||
&self,
|
&self,
|
||||||
completion: &lsp::CompletionItem,
|
completion: &lsp::CompletionItem,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
match completion.kind {
|
match completion.kind {
|
||||||
Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
|
Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
|
||||||
|
@ -196,7 +196,7 @@ impl LspAdapter for RustLspAdapter {
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
kind: lsp::SymbolKind,
|
kind: lsp::SymbolKind,
|
||||||
language: &Language,
|
language: &Arc<Language>,
|
||||||
) -> Option<CodeLabel> {
|
) -> Option<CodeLabel> {
|
||||||
let (text, filter_range, display_range) = match kind {
|
let (text, filter_range, display_range) = match kind {
|
||||||
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
||||||
|
@ -439,7 +439,7 @@ mod tests {
|
||||||
cx.set_global(settings);
|
cx.set_global(settings);
|
||||||
|
|
||||||
cx.add_model(|cx| {
|
cx.add_model(|cx| {
|
||||||
let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(language), cx);
|
let mut buffer = Buffer::new(0, "", cx).with_language(language, cx);
|
||||||
|
|
||||||
// indent between braces
|
// indent between braces
|
||||||
buffer.set_text("fn a() {}", cx);
|
buffer.set_text("fn a() {}", cx);
|
||||||
|
|
|
@ -115,7 +115,7 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||||
async fn label_for_completion(
|
async fn label_for_completion(
|
||||||
&self,
|
&self,
|
||||||
item: &lsp::CompletionItem,
|
item: &lsp::CompletionItem,
|
||||||
language: &language::Language,
|
language: &Arc<language::Language>,
|
||||||
) -> Option<language::CodeLabel> {
|
) -> Option<language::CodeLabel> {
|
||||||
use lsp::CompletionItemKind as Kind;
|
use lsp::CompletionItemKind as Kind;
|
||||||
let len = item.label.len();
|
let len = item.label.len();
|
||||||
|
@ -144,7 +144,6 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use gpui::MutableAppContext;
|
use gpui::MutableAppContext;
|
||||||
use unindent::Unindent;
|
use unindent::Unindent;
|
||||||
|
@ -172,9 +171,8 @@ mod tests {
|
||||||
"#
|
"#
|
||||||
.unindent();
|
.unindent();
|
||||||
|
|
||||||
let buffer = cx.add_model(|cx| {
|
let buffer =
|
||||||
language::Buffer::new(0, text, cx).with_language(Arc::new(language), cx)
|
cx.add_model(|cx| language::Buffer::new(0, text, cx).with_language(language, cx));
|
||||||
});
|
|
||||||
let outline = buffer.read(cx).snapshot().outline(None).unwrap();
|
let outline = buffer.read(cx).snapshot().outline(None).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
outline
|
outline
|
||||||
|
|
|
@ -1133,7 +1133,7 @@ mod tests {
|
||||||
assert!(!editor.is_dirty(cx));
|
assert!(!editor.is_dirty(cx));
|
||||||
assert_eq!(editor.title(cx), "untitled");
|
assert_eq!(editor.title(cx), "untitled");
|
||||||
assert!(Arc::ptr_eq(
|
assert!(Arc::ptr_eq(
|
||||||
editor.language_at(0, cx).unwrap(),
|
&editor.language_at(0, cx).unwrap(),
|
||||||
&languages::PLAIN_TEXT
|
&languages::PLAIN_TEXT
|
||||||
));
|
));
|
||||||
editor.handle_input("hi", cx);
|
editor.handle_input("hi", cx);
|
||||||
|
@ -1220,7 +1220,7 @@ mod tests {
|
||||||
|
|
||||||
editor.update(cx, |editor, cx| {
|
editor.update(cx, |editor, cx| {
|
||||||
assert!(Arc::ptr_eq(
|
assert!(Arc::ptr_eq(
|
||||||
editor.language_at(0, cx).unwrap(),
|
&editor.language_at(0, cx).unwrap(),
|
||||||
&languages::PLAIN_TEXT
|
&languages::PLAIN_TEXT
|
||||||
));
|
));
|
||||||
editor.handle_input("hi", cx);
|
editor.handle_input("hi", cx);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue