Test more editing patterns of SyntaxMap, fix bugs
This commit is contained in:
parent
e8548e7732
commit
58fda5ac1c
3 changed files with 341 additions and 197 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -5842,7 +5842,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tree-sitter"
|
name = "tree-sitter"
|
||||||
version = "0.20.8"
|
version = "0.20.8"
|
||||||
source = "git+https://github.com/tree-sitter/tree-sitter?rev=1f1b1eb4501ed0a2d195d37f7de15f72aa10acd0#1f1b1eb4501ed0a2d195d37f7de15f72aa10acd0"
|
source = "git+https://github.com/tree-sitter/tree-sitter?rev=477b6677537e89c7bdff14ce84dad6d23a6415bb#477b6677537e89c7bdff14ce84dad6d23a6415bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"regex",
|
"regex",
|
||||||
|
|
|
@ -4,7 +4,7 @@ default-members = ["crates/zed"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1f1b1eb4501ed0a2d195d37f7de15f72aa10acd0" }
|
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "477b6677537e89c7bdff14ce84dad6d23a6415bb" }
|
||||||
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
|
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
|
||||||
|
|
||||||
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457
|
# TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use sum_tree::{Bias, SeekTarget, SumTree};
|
use sum_tree::{Bias, SeekTarget, SumTree};
|
||||||
use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint};
|
use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint};
|
||||||
use tree_sitter::{Parser, Tree};
|
use tree_sitter::{Node, Parser, Tree};
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static PARSER: RefCell<Parser> = RefCell::new(Parser::new());
|
static PARSER: RefCell<Parser> = RefCell::new(Parser::new());
|
||||||
|
@ -15,7 +15,8 @@ thread_local! {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SyntaxMap {
|
pub struct SyntaxMap {
|
||||||
version: clock::Global,
|
parsed_version: clock::Global,
|
||||||
|
interpolated_version: clock::Global,
|
||||||
snapshot: SyntaxSnapshot,
|
snapshot: SyntaxSnapshot,
|
||||||
language_registry: Option<Arc<LanguageRegistry>>,
|
language_registry: Option<Arc<LanguageRegistry>>,
|
||||||
}
|
}
|
||||||
|
@ -40,14 +41,14 @@ struct SyntaxLayerSummary {
|
||||||
last_layer_range: Range<Anchor>,
|
last_layer_range: Range<Anchor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct DepthAndRange(usize, Range<Anchor>);
|
struct DepthAndRange(usize, Range<Anchor>);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct DepthAndMaxPosition(usize, Anchor);
|
struct DepthAndMaxPosition(usize, Anchor);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct DepthAndRangeOrMaxPosition(usize, Range<Anchor>, Anchor);
|
struct DepthAndRangeOrMaxPosition(DepthAndRange, DepthAndMaxPosition);
|
||||||
|
|
||||||
struct ReparseStep {
|
struct ReparseStep {
|
||||||
depth: usize,
|
depth: usize,
|
||||||
|
@ -76,44 +77,29 @@ impl SyntaxMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpolate(&mut self, text: &BufferSnapshot) {
|
pub fn interpolate(&mut self, text: &BufferSnapshot) {
|
||||||
self.snapshot.interpolate(&self.version, text);
|
self.snapshot.interpolate(&self.interpolated_version, text);
|
||||||
self.version = text.version.clone();
|
self.interpolated_version = text.version.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reparse(&mut self, language: Arc<Language>, text: &BufferSnapshot) {
|
pub fn reparse(&mut self, language: Arc<Language>, text: &BufferSnapshot) {
|
||||||
self.version = text.version.clone();
|
if !self.interpolated_version.observed_all(&text.version) {
|
||||||
self.snapshot
|
self.interpolate(text);
|
||||||
.reparse(self.language_registry.clone(), language, text);
|
}
|
||||||
|
|
||||||
|
self.snapshot.reparse(
|
||||||
|
&self.parsed_version,
|
||||||
|
text,
|
||||||
|
self.language_registry.clone(),
|
||||||
|
language,
|
||||||
|
);
|
||||||
|
self.parsed_version = text.version.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assumptions:
|
|
||||||
// * The maximum depth is small (< 5)
|
|
||||||
// * For a given depth, the number of layers that touch a given range
|
|
||||||
// is small (usually only 1)
|
|
||||||
|
|
||||||
// |change|
|
|
||||||
// 0 (............................................................)
|
|
||||||
// 1 (...............................................)
|
|
||||||
// 1 (................)
|
|
||||||
// 1 (.......)
|
|
||||||
// 2 (....)
|
|
||||||
// 2 (....)
|
|
||||||
// 2 (.......)
|
|
||||||
// 2 (...)
|
|
||||||
// 2 (.........)
|
|
||||||
// 2 (...)
|
|
||||||
// 3 (.)
|
|
||||||
// 3 (.)
|
|
||||||
// 3 (..)
|
|
||||||
// 3 (..)
|
|
||||||
// 3 (..)
|
|
||||||
// 3 (.)
|
|
||||||
|
|
||||||
impl SyntaxSnapshot {
|
impl SyntaxSnapshot {
|
||||||
pub fn interpolate(&mut self, current_version: &clock::Global, text: &BufferSnapshot) {
|
pub fn interpolate(&mut self, from_version: &clock::Global, text: &BufferSnapshot) {
|
||||||
let edits = text
|
let edits = text
|
||||||
.edits_since::<(usize, Point)>(¤t_version)
|
.edits_since::<(usize, Point)>(&from_version)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if edits.is_empty() {
|
if edits.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
@ -152,16 +138,9 @@ impl SyntaxSnapshot {
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
if first_edit.new.start.0 > layer_range.end.0 {
|
let target = DepthAndMaxPosition(depth, text.anchor_before(first_edit.new.start.0));
|
||||||
layers.push_tree(
|
if target.cmp(&cursor.start(), text).is_gt() {
|
||||||
cursor.slice(
|
layers.push_tree(cursor.slice(&target, Bias::Left, text), text);
|
||||||
&DepthAndMaxPosition(depth, text.anchor_before(first_edit.new.start.0)),
|
|
||||||
Bias::Left,
|
|
||||||
text,
|
|
||||||
),
|
|
||||||
text,
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preserve any layers at this depth that follow the last edit.
|
// Preserve any layers at this depth that follow the last edit.
|
||||||
|
@ -226,10 +205,17 @@ impl SyntaxSnapshot {
|
||||||
|
|
||||||
pub fn reparse(
|
pub fn reparse(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
from_version: &clock::Global,
|
||||||
|
text: &BufferSnapshot,
|
||||||
registry: Option<Arc<LanguageRegistry>>,
|
registry: Option<Arc<LanguageRegistry>>,
|
||||||
language: Arc<Language>,
|
language: Arc<Language>,
|
||||||
text: &BufferSnapshot,
|
|
||||||
) {
|
) {
|
||||||
|
let edits = text.edits_since::<usize>(from_version).collect::<Vec<_>>();
|
||||||
|
if edits.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_depth = self.layers.summary().max_depth;
|
||||||
let mut cursor = self.layers.cursor::<SyntaxLayerSummary>();
|
let mut cursor = self.layers.cursor::<SyntaxLayerSummary>();
|
||||||
cursor.next(&text);
|
cursor.next(&text);
|
||||||
let mut layers = SumTree::new();
|
let mut layers = SumTree::new();
|
||||||
|
@ -248,44 +234,55 @@ impl SyntaxSnapshot {
|
||||||
let (depth, range) = if let Some(step) = &step {
|
let (depth, range) = if let Some(step) = &step {
|
||||||
(step.depth, step.range.clone())
|
(step.depth, step.range.clone())
|
||||||
} else {
|
} else {
|
||||||
(cursor.start().max_depth, Anchor::MAX..Anchor::MAX)
|
(max_depth + 1, Anchor::MAX..Anchor::MAX)
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = DepthAndRange(depth, range.clone());
|
let target = DepthAndRange(depth, range.clone());
|
||||||
if target.cmp(cursor.start(), &text).is_gt() {
|
let mut done = cursor.item().is_none();
|
||||||
let change_start_anchor = changed_regions
|
while !done && target.cmp(cursor.start(), &text).is_gt() {
|
||||||
.first()
|
let bounded_target = DepthAndRangeOrMaxPosition(
|
||||||
.map_or(Anchor::MAX, |region| region.range.start);
|
target.clone(),
|
||||||
let seek_target =
|
changed_regions
|
||||||
DepthAndRangeOrMaxPosition(depth, range.clone(), change_start_anchor);
|
.first()
|
||||||
let slice = cursor.slice(&seek_target, Bias::Left, text);
|
.map_or(DepthAndMaxPosition(usize::MAX, Anchor::MAX), |region| {
|
||||||
layers.push_tree(slice, &text);
|
DepthAndMaxPosition(region.depth, region.range.start)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if bounded_target.cmp(&cursor.start(), &text).is_gt() {
|
||||||
|
let slice = cursor.slice(&bounded_target, Bias::Left, text);
|
||||||
|
layers.push_tree(slice, &text);
|
||||||
|
}
|
||||||
|
|
||||||
while let Some(layer) = cursor.item() {
|
while target.cmp(&cursor.end(text), text).is_gt() {
|
||||||
if target.cmp(&cursor.end(text), text).is_le() {
|
let layer = if let Some(layer) = cursor.item() {
|
||||||
|
layer
|
||||||
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
};
|
||||||
|
|
||||||
if layer_is_changed(layer, text, &changed_regions) {
|
if layer_is_changed(layer, text, &changed_regions) {
|
||||||
let region = ChangedRegion {
|
ChangedRegion {
|
||||||
depth: depth + 1,
|
depth: depth + 1,
|
||||||
range: layer.range.clone(),
|
range: layer.range.clone(),
|
||||||
};
|
|
||||||
if let Err(i) =
|
|
||||||
changed_regions.binary_search_by(|probe| probe.cmp(®ion, text))
|
|
||||||
{
|
|
||||||
changed_regions.insert(i, region);
|
|
||||||
}
|
}
|
||||||
|
.insert(text, &mut changed_regions);
|
||||||
} else {
|
} else {
|
||||||
layers.push(layer.clone(), text);
|
layers.push(layer.clone(), text);
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.next(text);
|
cursor.next(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done = true;
|
||||||
changed_regions.retain(|region| {
|
changed_regions.retain(|region| {
|
||||||
region.depth > depth
|
if region.depth > depth
|
||||||
|| (region.depth == depth
|
|| (region.depth == depth
|
||||||
&& region.range.end.cmp(&range.start, text).is_gt())
|
&& region.range.end.cmp(&range.start, text).is_gt())
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
done = false;
|
||||||
|
false
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,15 +329,19 @@ impl SyntaxSnapshot {
|
||||||
Some(old_layer.tree.clone()),
|
Some(old_layer.tree.clone()),
|
||||||
ranges,
|
ranges,
|
||||||
);
|
);
|
||||||
|
changed_ranges = join_ranges(
|
||||||
changed_ranges = old_layer
|
edits
|
||||||
.tree
|
.iter()
|
||||||
.changed_ranges(&tree)
|
.map(|e| e.new.clone())
|
||||||
.map(|r| r.start_byte..r.end_byte)
|
.filter(|range| range.start < end_byte && range.end > start_byte),
|
||||||
.collect();
|
old_layer
|
||||||
|
.tree
|
||||||
|
.changed_ranges(&tree)
|
||||||
|
.map(|r| start_byte + r.start_byte..start_byte + r.end_byte),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
tree = parse_text(grammar, text.as_rope(), None, ranges);
|
tree = parse_text(grammar, text.as_rope(), None, ranges);
|
||||||
changed_ranges = vec![0..end_byte - start_byte];
|
changed_ranges = vec![start_byte..end_byte];
|
||||||
}
|
}
|
||||||
|
|
||||||
layers.push(
|
layers.push(
|
||||||
|
@ -358,27 +359,19 @@ impl SyntaxSnapshot {
|
||||||
changed_ranges.is_empty(),
|
changed_ranges.is_empty(),
|
||||||
) {
|
) {
|
||||||
let depth = depth + 1;
|
let depth = depth + 1;
|
||||||
|
|
||||||
for range in &changed_ranges {
|
for range in &changed_ranges {
|
||||||
let region = ChangedRegion {
|
ChangedRegion {
|
||||||
depth,
|
depth,
|
||||||
range: text.anchor_before(range.start)..text.anchor_after(range.end),
|
range: text.anchor_before(range.start)..text.anchor_after(range.end),
|
||||||
};
|
|
||||||
if let Err(i) =
|
|
||||||
changed_regions.binary_search_by(|probe| probe.cmp(®ion, text))
|
|
||||||
{
|
|
||||||
changed_regions.insert(i, region);
|
|
||||||
}
|
}
|
||||||
|
.insert(text, &mut changed_regions);
|
||||||
}
|
}
|
||||||
|
|
||||||
get_injections(
|
get_injections(
|
||||||
config,
|
config,
|
||||||
text,
|
text,
|
||||||
&tree,
|
tree.root_node_with_offset(start_byte, start_point),
|
||||||
registry,
|
registry,
|
||||||
depth,
|
depth,
|
||||||
start_byte,
|
|
||||||
Point::from_ts_point(start_point),
|
|
||||||
&changed_ranges,
|
&changed_ranges,
|
||||||
&mut queue,
|
&mut queue,
|
||||||
);
|
);
|
||||||
|
@ -389,17 +382,16 @@ impl SyntaxSnapshot {
|
||||||
self.layers = layers;
|
self.layers = layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layers(&self, buffer: &BufferSnapshot) -> Vec<(&Grammar, &Tree, (usize, Point))> {
|
pub fn layers(&self, buffer: &BufferSnapshot) -> Vec<(&Grammar, Node)> {
|
||||||
self.layers
|
self.layers
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|layer| {
|
.filter_map(|layer| {
|
||||||
if let Some(grammar) = &layer.language.grammar {
|
if let Some(grammar) = &layer.language.grammar {
|
||||||
Some((
|
Some((
|
||||||
grammar.as_ref(),
|
grammar.as_ref(),
|
||||||
&layer.tree,
|
layer.tree.root_node_with_offset(
|
||||||
(
|
|
||||||
layer.range.start.to_offset(buffer),
|
layer.range.start.to_offset(buffer),
|
||||||
layer.range.start.to_point(buffer),
|
layer.range.start.to_point(buffer).to_ts_point(),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
@ -413,7 +405,7 @@ impl SyntaxSnapshot {
|
||||||
&self,
|
&self,
|
||||||
range: Range<T>,
|
range: Range<T>,
|
||||||
buffer: &BufferSnapshot,
|
buffer: &BufferSnapshot,
|
||||||
) -> Vec<(&Grammar, &Tree, (usize, Point))> {
|
) -> Vec<(&Grammar, Node)> {
|
||||||
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));
|
||||||
|
|
||||||
|
@ -429,10 +421,9 @@ impl SyntaxSnapshot {
|
||||||
if let Some(grammar) = &layer.language.grammar {
|
if let Some(grammar) = &layer.language.grammar {
|
||||||
result.push((
|
result.push((
|
||||||
grammar.as_ref(),
|
grammar.as_ref(),
|
||||||
&layer.tree,
|
layer.tree.root_node_with_offset(
|
||||||
(
|
|
||||||
layer.range.start.to_offset(buffer),
|
layer.range.start.to_offset(buffer),
|
||||||
layer.range.start.to_point(buffer),
|
layer.range.start.to_point(buffer).to_ts_point(),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -443,6 +434,38 @@ impl SyntaxSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn join_ranges(
|
||||||
|
a: impl Iterator<Item = Range<usize>>,
|
||||||
|
b: impl Iterator<Item = Range<usize>>,
|
||||||
|
) -> Vec<Range<usize>> {
|
||||||
|
let mut result = Vec::<Range<usize>>::new();
|
||||||
|
let mut a = a.peekable();
|
||||||
|
let mut b = b.peekable();
|
||||||
|
loop {
|
||||||
|
let range = match (a.peek(), b.peek()) {
|
||||||
|
(Some(range_a), Some(range_b)) => {
|
||||||
|
if range_a.start < range_b.start {
|
||||||
|
a.next().unwrap()
|
||||||
|
} else {
|
||||||
|
b.next().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(None, Some(_)) => b.next().unwrap(),
|
||||||
|
(Some(_), None) => a.next().unwrap(),
|
||||||
|
(None, None) => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(last) = result.last_mut() {
|
||||||
|
if range.start <= last.end {
|
||||||
|
last.end = last.end.max(range.end);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(range);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_text(
|
fn parse_text(
|
||||||
grammar: &Grammar,
|
grammar: &Grammar,
|
||||||
text: &Rope,
|
text: &Rope,
|
||||||
|
@ -485,11 +508,9 @@ fn parse_text(
|
||||||
fn get_injections(
|
fn get_injections(
|
||||||
config: &InjectionConfig,
|
config: &InjectionConfig,
|
||||||
text: &BufferSnapshot,
|
text: &BufferSnapshot,
|
||||||
tree: &Tree,
|
node: Node,
|
||||||
language_registry: &LanguageRegistry,
|
language_registry: &LanguageRegistry,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
start_byte: usize,
|
|
||||||
start_point: Point,
|
|
||||||
query_ranges: &[Range<usize>],
|
query_ranges: &[Range<usize>],
|
||||||
queue: &mut BinaryHeap<ReparseStep>,
|
queue: &mut BinaryHeap<ReparseStep>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -498,21 +519,10 @@ fn get_injections(
|
||||||
let mut prev_match = None;
|
let mut prev_match = None;
|
||||||
for query_range in query_ranges {
|
for query_range in query_ranges {
|
||||||
query_cursor.set_byte_range(query_range.start..query_range.end);
|
query_cursor.set_byte_range(query_range.start..query_range.end);
|
||||||
for mat in query_cursor.matches(
|
for mat in query_cursor.matches(&config.query, node, TextProvider(text.as_rope())) {
|
||||||
&config.query,
|
|
||||||
tree.root_node(),
|
|
||||||
TextProvider(text.as_rope()),
|
|
||||||
) {
|
|
||||||
let content_ranges = mat
|
let content_ranges = mat
|
||||||
.nodes_for_capture_index(config.content_capture_ix)
|
.nodes_for_capture_index(config.content_capture_ix)
|
||||||
.map(|node| tree_sitter::Range {
|
.map(|node| node.range())
|
||||||
start_byte: start_byte + node.start_byte(),
|
|
||||||
end_byte: start_byte + node.end_byte(),
|
|
||||||
start_point: (start_point + Point::from_ts_point(node.start_position()))
|
|
||||||
.to_ts_point(),
|
|
||||||
end_point: (start_point + Point::from_ts_point(node.end_position()))
|
|
||||||
.to_ts_point(),
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if content_ranges.is_empty() {
|
if content_ranges.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
|
@ -534,12 +544,7 @@ fn get_injections(
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let ix = config.language_capture_ix?;
|
let ix = config.language_capture_ix?;
|
||||||
let node = mat.nodes_for_capture_index(ix).next()?;
|
let node = mat.nodes_for_capture_index(ix).next()?;
|
||||||
Some(Cow::Owned(
|
Some(Cow::Owned(text.text_for_range(node.byte_range()).collect()))
|
||||||
text.text_for_range(
|
|
||||||
start_byte + node.start_byte()..start_byte + node.end_byte(),
|
|
||||||
)
|
|
||||||
.collect(),
|
|
||||||
))
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(language_name) = language_name {
|
if let Some(language_name) = language_name {
|
||||||
|
@ -566,9 +571,10 @@ fn layer_is_changed(
|
||||||
changed_regions: &[ChangedRegion],
|
changed_regions: &[ChangedRegion],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
changed_regions.iter().any(|region| {
|
changed_regions.iter().any(|region| {
|
||||||
|
let same_depth = region.depth == layer.depth;
|
||||||
let is_before_layer = region.range.end.cmp(&layer.range.start, text).is_le();
|
let is_before_layer = region.range.end.cmp(&layer.range.start, text).is_le();
|
||||||
let is_after_layer = region.range.start.cmp(&layer.range.end, text).is_ge();
|
let is_after_layer = region.range.start.cmp(&layer.range.end, text).is_ge();
|
||||||
!is_before_layer && !is_after_layer
|
same_depth && !is_before_layer && !is_after_layer
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,6 +619,12 @@ impl ReparseStep {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChangedRegion {
|
impl ChangedRegion {
|
||||||
|
fn insert(self, text: &BufferSnapshot, set: &mut Vec<Self>) {
|
||||||
|
if let Err(ix) = set.binary_search_by(|probe| probe.cmp(&self, text)) {
|
||||||
|
set.insert(ix, self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn cmp(&self, other: &Self, buffer: &BufferSnapshot) -> Ordering {
|
fn cmp(&self, other: &Self, buffer: &BufferSnapshot) -> Ordering {
|
||||||
let range_a = &self.range;
|
let range_a = &self.range;
|
||||||
let range_b = &other.range;
|
let range_b = &other.range;
|
||||||
|
@ -676,25 +688,11 @@ impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary> for DepthAndMaxP
|
||||||
|
|
||||||
impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary> for DepthAndRangeOrMaxPosition {
|
impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary> for DepthAndRangeOrMaxPosition {
|
||||||
fn cmp(&self, cursor_location: &SyntaxLayerSummary, buffer: &BufferSnapshot) -> Ordering {
|
fn cmp(&self, cursor_location: &SyntaxLayerSummary, buffer: &BufferSnapshot) -> Ordering {
|
||||||
let cmp = Ord::cmp(&self.0, &cursor_location.max_depth);
|
if self.1.cmp(cursor_location, buffer).is_le() {
|
||||||
if cmp.is_ne() {
|
return Ordering::Less;
|
||||||
return cmp;
|
} else {
|
||||||
|
self.0.cmp(cursor_location, buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
let cmp = self.2.cmp(&cursor_location.range.end, buffer);
|
|
||||||
if cmp.is_gt() {
|
|
||||||
return Ordering::Greater;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.1
|
|
||||||
.start
|
|
||||||
.cmp(&cursor_location.last_layer_range.start, buffer)
|
|
||||||
.then_with(|| {
|
|
||||||
cursor_location
|
|
||||||
.last_layer_range
|
|
||||||
.end
|
|
||||||
.cmp(&self.1.end, buffer)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -827,37 +825,22 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_syntax_map_edits() {
|
fn test_typing_multiple_new_injections() {
|
||||||
let registry = Arc::new(LanguageRegistry::test());
|
let (buffer, syntax_map) = test_edit_sequence(&[
|
||||||
let language = Arc::new(rust_lang());
|
"fn a() { dbg }",
|
||||||
let mut syntax_map = SyntaxMap::new();
|
"fn a() { dbg«!» }",
|
||||||
syntax_map.set_language_registry(registry.clone());
|
"fn a() { dbg!«()» }",
|
||||||
registry.add(language.clone());
|
"fn a() { dbg!(«b») }",
|
||||||
|
"fn a() { dbg!(b«.») }",
|
||||||
let mut buffer = Buffer::new(0, 0, "".into());
|
"fn a() { dbg!(b.«c») }",
|
||||||
syntax_map.reparse(language.clone(), &buffer);
|
"fn a() { dbg!(b.c«()») }",
|
||||||
|
"fn a() { dbg!(b.c(«vec»)) }",
|
||||||
edit_buffer_n(
|
"fn a() { dbg!(b.c(vec«!»)) }",
|
||||||
&mut buffer,
|
"fn a() { dbg!(b.c(vec!«[]»)) }",
|
||||||
&[
|
"fn a() { dbg!(b.c(vec![«d»])) }",
|
||||||
"«fn a() { dbg }»",
|
"fn a() { dbg!(b.c(vec![d«.»])) }",
|
||||||
"fn a() { dbg«!» }",
|
"fn a() { dbg!(b.c(vec![d.«e»])) }",
|
||||||
"fn a() { dbg!«()» }",
|
]);
|
||||||
"fn a() { dbg!(«b») }",
|
|
||||||
"fn a() { dbg!(b«.») }",
|
|
||||||
"fn a() { dbg!(b.«c») }",
|
|
||||||
"fn a() { dbg!(b.c«()») }",
|
|
||||||
"fn a() { dbg!(b.c(«vec»)) }",
|
|
||||||
"fn a() { dbg!(b.c(vec«!»)) }",
|
|
||||||
"fn a() { dbg!(b.c(vec!«[]»)) }",
|
|
||||||
"fn a() { dbg!(b.c(vec![«d»])) }",
|
|
||||||
"fn a() { dbg!(b.c(vec![d«.»])) }",
|
|
||||||
"fn a() { dbg!(b.c(vec![d.«e»])) }",
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
syntax_map.interpolate(&buffer);
|
|
||||||
syntax_map.reparse(language.clone(), &buffer);
|
|
||||||
|
|
||||||
assert_node_ranges(
|
assert_node_ranges(
|
||||||
&syntax_map,
|
&syntax_map,
|
||||||
|
@ -867,6 +850,163 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_pasting_new_injection_line_between_others() {
|
||||||
|
let (buffer, syntax_map) = test_edit_sequence(&[
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
b!(B {});
|
||||||
|
c!(C {});
|
||||||
|
d!(D {});
|
||||||
|
e!(E {});
|
||||||
|
f!(F {});
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
b!(B {});
|
||||||
|
c!(C {});
|
||||||
|
«g!(G {});
|
||||||
|
»d!(D {});
|
||||||
|
e!(E {});
|
||||||
|
f!(F {});
|
||||||
|
}
|
||||||
|
",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_node_ranges(
|
||||||
|
&syntax_map,
|
||||||
|
&buffer,
|
||||||
|
"(struct_expression) @_",
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
b!(«B {}»);
|
||||||
|
c!(«C {}»);
|
||||||
|
g!(«G {}»);
|
||||||
|
d!(«D {}»);
|
||||||
|
e!(«E {}»);
|
||||||
|
f!(«F {}»);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_joining_injections_with_child_injections() {
|
||||||
|
let (buffer, syntax_map) = test_edit_sequence(&[
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
b!(
|
||||||
|
c![one.two.three],
|
||||||
|
d![four.five.six],
|
||||||
|
);
|
||||||
|
e!(
|
||||||
|
f![seven.eight],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
b!(
|
||||||
|
c![one.two.three],
|
||||||
|
d![four.five.six],
|
||||||
|
ˇ f![seven.eight],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_node_ranges(
|
||||||
|
&syntax_map,
|
||||||
|
&buffer,
|
||||||
|
"(field_identifier) @_",
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
b!(
|
||||||
|
c![one.«two».«three»],
|
||||||
|
d![four.«five».«six»],
|
||||||
|
f![seven.«eight»],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_editing_edges_of_injection() {
|
||||||
|
test_edit_sequence(&[
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
b!(c!())
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
«d»!(c!())
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
«e»d!(c!())
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
ed!«[»c!()«]»
|
||||||
|
}
|
||||||
|
",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_edit_sequence(steps: &[&str]) -> (Buffer, SyntaxMap) {
|
||||||
|
let registry = Arc::new(LanguageRegistry::test());
|
||||||
|
let language = Arc::new(rust_lang());
|
||||||
|
registry.add(language.clone());
|
||||||
|
let mut buffer = Buffer::new(0, 0, Default::default());
|
||||||
|
|
||||||
|
let mut mutated_syntax_map = SyntaxMap::new();
|
||||||
|
mutated_syntax_map.set_language_registry(registry.clone());
|
||||||
|
mutated_syntax_map.reparse(language.clone(), &buffer);
|
||||||
|
|
||||||
|
for (i, marked_string) in steps.into_iter().enumerate() {
|
||||||
|
edit_buffer(&mut buffer, &marked_string.unindent());
|
||||||
|
|
||||||
|
// Reparse the syntax map
|
||||||
|
mutated_syntax_map.interpolate(&buffer);
|
||||||
|
mutated_syntax_map.reparse(language.clone(), &buffer);
|
||||||
|
|
||||||
|
// Create a second syntax map from scratch
|
||||||
|
let mut reference_syntax_map = SyntaxMap::new();
|
||||||
|
reference_syntax_map.set_language_registry(registry.clone());
|
||||||
|
reference_syntax_map.reparse(language.clone(), &buffer);
|
||||||
|
|
||||||
|
// Compare the mutated syntax map to the new syntax map
|
||||||
|
let mutated_layers = mutated_syntax_map.layers(&buffer);
|
||||||
|
let reference_layers = reference_syntax_map.layers(&buffer);
|
||||||
|
assert_eq!(
|
||||||
|
mutated_layers.len(),
|
||||||
|
reference_layers.len(),
|
||||||
|
"wrong number of layers at step {i}"
|
||||||
|
);
|
||||||
|
for (edited_layer, reference_layer) in
|
||||||
|
mutated_layers.into_iter().zip(reference_layers.into_iter())
|
||||||
|
{
|
||||||
|
assert_eq!(
|
||||||
|
edited_layer.1.to_sexp(),
|
||||||
|
reference_layer.1.to_sexp(),
|
||||||
|
"different layer at step {i}"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edited_layer.1.range(),
|
||||||
|
reference_layer.1.range(),
|
||||||
|
"different layer at step {i}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(buffer, mutated_syntax_map)
|
||||||
|
}
|
||||||
|
|
||||||
fn rust_lang() -> Language {
|
fn rust_lang() -> Language {
|
||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
|
@ -903,10 +1043,10 @@ mod tests {
|
||||||
expected_layers.len(),
|
expected_layers.len(),
|
||||||
"wrong number of layers"
|
"wrong number of layers"
|
||||||
);
|
);
|
||||||
for (i, ((_, tree, _), expected_s_exp)) in
|
for (i, ((_, node), expected_s_exp)) in
|
||||||
layers.iter().zip(expected_layers.iter()).enumerate()
|
layers.iter().zip(expected_layers.iter()).enumerate()
|
||||||
{
|
{
|
||||||
let actual_s_exp = tree.root_node().to_sexp();
|
let actual_s_exp = node.to_sexp();
|
||||||
assert!(
|
assert!(
|
||||||
string_contains_sequence(
|
string_contains_sequence(
|
||||||
&actual_s_exp,
|
&actual_s_exp,
|
||||||
|
@ -925,50 +1065,54 @@ mod tests {
|
||||||
) {
|
) {
|
||||||
let mut cursor = QueryCursorHandle::new();
|
let mut cursor = QueryCursorHandle::new();
|
||||||
let mut actual_ranges = Vec::<Range<usize>>::new();
|
let mut actual_ranges = Vec::<Range<usize>>::new();
|
||||||
for (grammar, tree, (start_byte, _)) in syntax_map.layers(buffer) {
|
for (grammar, node) in syntax_map.layers(buffer) {
|
||||||
let query = Query::new(grammar.ts_language, query).unwrap();
|
let query = Query::new(grammar.ts_language, query).unwrap();
|
||||||
for (mat, ix) in
|
for (mat, ix) in cursor.captures(&query, node, TextProvider(buffer.as_rope())) {
|
||||||
cursor.captures(&query, tree.root_node(), TextProvider(buffer.as_rope()))
|
actual_ranges.push(mat.captures[ix].node.byte_range());
|
||||||
{
|
|
||||||
let range = mat.captures[ix].node.byte_range();
|
|
||||||
actual_ranges.push(start_byte + range.start..start_byte + range.end);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (text, expected_ranges) = marked_text_ranges(marked_string, false);
|
let (text, expected_ranges) = marked_text_ranges(&marked_string.unindent(), false);
|
||||||
assert_eq!(text, buffer.text());
|
assert_eq!(text, buffer.text());
|
||||||
assert_eq!(actual_ranges, expected_ranges);
|
assert_eq!(actual_ranges, expected_ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edit_buffer_n(buffer: &mut Buffer, marked_strings: &[&str]) {
|
|
||||||
for marked_string in marked_strings {
|
|
||||||
edit_buffer(buffer, marked_string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn edit_buffer(buffer: &mut Buffer, marked_string: &str) {
|
fn edit_buffer(buffer: &mut Buffer, marked_string: &str) {
|
||||||
let old_text = buffer.text();
|
let old_text = buffer.text();
|
||||||
let (new_text, mut ranges) = marked_text_ranges(marked_string, false);
|
let (new_text, mut ranges) = marked_text_ranges(marked_string, false);
|
||||||
assert_eq!(ranges.len(), 1);
|
if ranges.is_empty() {
|
||||||
|
ranges.push(0..new_text.len());
|
||||||
|
}
|
||||||
|
|
||||||
let inserted_range = ranges.pop().unwrap();
|
let mut delta = 0;
|
||||||
let inserted_text = new_text[inserted_range.clone()].to_string();
|
let mut edits = Vec::new();
|
||||||
let deleted_len = (inserted_range.len() as isize + old_text.len() as isize
|
let mut ranges = ranges.into_iter().peekable();
|
||||||
- new_text.len() as isize) as usize;
|
|
||||||
let deleted_range = inserted_range.start..inserted_range.start + deleted_len;
|
while let Some(inserted_range) = ranges.next() {
|
||||||
|
let old_start = (inserted_range.start as isize - delta) as usize;
|
||||||
|
let following_text = if let Some(next_range) = ranges.peek() {
|
||||||
|
&new_text[inserted_range.end..next_range.start]
|
||||||
|
} else {
|
||||||
|
&new_text[inserted_range.end..]
|
||||||
|
};
|
||||||
|
|
||||||
|
let inserted_len = inserted_range.len();
|
||||||
|
let deleted_len = old_text[old_start..]
|
||||||
|
.find(following_text)
|
||||||
|
.expect("invalid edit");
|
||||||
|
|
||||||
|
let old_range = old_start..old_start + deleted_len;
|
||||||
|
edits.push((old_range, new_text[inserted_range].to_string()));
|
||||||
|
delta += inserted_len as isize - deleted_len as isize;
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
old_text[..deleted_range.start],
|
old_text.len() as isize + delta,
|
||||||
new_text[..inserted_range.start],
|
new_text.len() as isize,
|
||||||
"invalid edit",
|
"invalid edit"
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
old_text[deleted_range.end..],
|
|
||||||
new_text[inserted_range.end..],
|
|
||||||
"invalid edit",
|
|
||||||
);
|
);
|
||||||
|
|
||||||
buffer.edit([(deleted_range, inserted_text)]);
|
buffer.edit(edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string_contains_sequence(text: &str, parts: &[&str]) -> bool {
|
pub fn string_contains_sequence(text: &str, parts: &[&str]) -> bool {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue