Expand diagnostic excerpts using heuristics on syntactic information from TreeSitter (#21942)
This is quite experimental and untested in languages other than Rust. It's written to attempt to do something sensible in many languages. Due to its experimental nature, just releasing to staff, and so not including it in release notes. Future release note might be "Improved diagnostic excerpts by using syntactic info to determine the context lines to show." Release Notes: - N/A
This commit is contained in:
parent
ca9cee85e1
commit
9b2bc458e3
11 changed files with 284 additions and 43 deletions
|
@ -68,7 +68,7 @@ pub use text::{
|
|||
use theme::SyntaxTheme;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use util::RandomCharIter;
|
||||
use util::{debug_panic, RangeExt};
|
||||
use util::{debug_panic, maybe, RangeExt};
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub use {tree_sitter_rust, tree_sitter_typescript};
|
||||
|
@ -2923,10 +2923,13 @@ impl BufferSnapshot {
|
|||
(start..end, word_kind)
|
||||
}
|
||||
|
||||
/// Returns the range for the closes syntax node enclosing the given range.
|
||||
pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
|
||||
/// Returns the closest syntax node enclosing the given range.
|
||||
pub fn syntax_ancestor<'a, T: ToOffset>(
|
||||
&'a self,
|
||||
range: Range<T>,
|
||||
) -> Option<tree_sitter::Node<'a>> {
|
||||
let range = range.start.to_offset(self)..range.end.to_offset(self);
|
||||
let mut result: Option<Range<usize>> = None;
|
||||
let mut result: Option<tree_sitter::Node<'a>> = None;
|
||||
'outer: for layer in self
|
||||
.syntax
|
||||
.layers_for_range(range.clone(), &self.text, true)
|
||||
|
@ -2956,7 +2959,7 @@ impl BufferSnapshot {
|
|||
}
|
||||
|
||||
let left_node = cursor.node();
|
||||
let mut layer_result = left_node.byte_range();
|
||||
let mut layer_result = left_node;
|
||||
|
||||
// For an empty range, try to find another node immediately to the right of the range.
|
||||
if left_node.end_byte() == range.start {
|
||||
|
@ -2979,13 +2982,13 @@ impl BufferSnapshot {
|
|||
// If both nodes are the same in that regard, favor the right one.
|
||||
if let Some(right_node) = right_node {
|
||||
if right_node.is_named() || !left_node.is_named() {
|
||||
layer_result = right_node.byte_range();
|
||||
layer_result = right_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(previous_result) = &result {
|
||||
if previous_result.len() < layer_result.len() {
|
||||
if previous_result.byte_range().len() < layer_result.byte_range().len() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -3028,6 +3031,48 @@ impl BufferSnapshot {
|
|||
Some(items)
|
||||
}
|
||||
|
||||
pub fn outline_range_containing<T: ToOffset>(&self, range: Range<T>) -> Option<Range<Point>> {
|
||||
let range = range.to_offset(self);
|
||||
let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
|
||||
grammar.outline_config.as_ref().map(|c| &c.query)
|
||||
});
|
||||
let configs = matches
|
||||
.grammars()
|
||||
.iter()
|
||||
.map(|g| g.outline_config.as_ref().unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
while let Some(mat) = matches.peek() {
|
||||
let config = &configs[mat.grammar_index];
|
||||
let containing_item_node = maybe!({
|
||||
let item_node = mat.captures.iter().find_map(|cap| {
|
||||
if cap.index == config.item_capture_ix {
|
||||
Some(cap.node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
|
||||
let item_byte_range = item_node.byte_range();
|
||||
if item_byte_range.end < range.start || item_byte_range.start > range.end {
|
||||
None
|
||||
} else {
|
||||
Some(item_node)
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(item_node) = containing_item_node {
|
||||
return Some(
|
||||
Point::from_ts_point(item_node.start_position())
|
||||
..Point::from_ts_point(item_node.end_position()),
|
||||
);
|
||||
}
|
||||
|
||||
matches.advance();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn outline_items_containing<T: ToOffset>(
|
||||
&self,
|
||||
range: Range<T>,
|
||||
|
|
|
@ -1104,20 +1104,32 @@ fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
|
|||
let snapshot = buffer.snapshot();
|
||||
|
||||
assert_eq!(
|
||||
snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
|
||||
Some(range_of(text, "|"))
|
||||
snapshot
|
||||
.syntax_ancestor(empty_range_at(text, "|"))
|
||||
.unwrap()
|
||||
.byte_range(),
|
||||
range_of(text, "|")
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.range_for_syntax_ancestor(range_of(text, "|")),
|
||||
Some(range_of(text, "|c|"))
|
||||
snapshot
|
||||
.syntax_ancestor(range_of(text, "|"))
|
||||
.unwrap()
|
||||
.byte_range(),
|
||||
range_of(text, "|c|")
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
|
||||
Some(range_of(text, "|c| {}"))
|
||||
snapshot
|
||||
.syntax_ancestor(range_of(text, "|c|"))
|
||||
.unwrap()
|
||||
.byte_range(),
|
||||
range_of(text, "|c| {}")
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
|
||||
Some(range_of(text, "(|c| {})"))
|
||||
snapshot
|
||||
.syntax_ancestor(range_of(text, "|c| {}"))
|
||||
.unwrap()
|
||||
.byte_range(),
|
||||
range_of(text, "(|c| {})")
|
||||
);
|
||||
|
||||
buffer
|
||||
|
|
|
@ -78,7 +78,7 @@ pub use language_registry::{
|
|||
};
|
||||
pub use lsp::LanguageServerId;
|
||||
pub use outline::*;
|
||||
pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer, TreeSitterOptions};
|
||||
pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer, ToTreeSitterPoint, TreeSitterOptions};
|
||||
pub use text::{AnchorRangeExt, LineEnding};
|
||||
pub use tree_sitter::{Node, Parser, Tree, TreeCursor};
|
||||
|
||||
|
|
|
@ -1845,7 +1845,7 @@ impl Drop for QueryCursorHandle {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) trait ToTreeSitterPoint {
|
||||
pub trait ToTreeSitterPoint {
|
||||
fn to_ts_point(self) -> tree_sitter::Point;
|
||||
fn from_ts_point(point: tree_sitter::Point) -> Self;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue