Merge pull request #2059 from zed-industries/no-indent-adjustment-on-error
Avoid adjusting indentation of lines inside of newly-created errors
This commit is contained in:
commit
e1a58e9381
7 changed files with 263 additions and 113 deletions
|
@ -282,6 +282,7 @@ struct AutoindentRequestEntry {
|
||||||
struct IndentSuggestion {
|
struct IndentSuggestion {
|
||||||
basis_row: u32,
|
basis_row: u32,
|
||||||
delta: Ordering,
|
delta: Ordering,
|
||||||
|
within_error: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BufferChunkHighlights<'a> {
|
struct BufferChunkHighlights<'a> {
|
||||||
|
@ -937,7 +938,7 @@ impl Buffer {
|
||||||
// Build a map containing the suggested indentation for each of the edited lines
|
// Build a map containing the suggested indentation for each of the edited lines
|
||||||
// with respect to the state of the buffer before these edits. This map is keyed
|
// with respect to the state of the buffer before these edits. This map is keyed
|
||||||
// by the rows for these lines in the current state of the buffer.
|
// by the rows for these lines in the current state of the buffer.
|
||||||
let mut old_suggestions = BTreeMap::<u32, IndentSize>::default();
|
let mut old_suggestions = BTreeMap::<u32, (IndentSize, bool)>::default();
|
||||||
let old_edited_ranges =
|
let old_edited_ranges =
|
||||||
contiguous_ranges(old_to_new_rows.keys().copied(), max_rows_between_yields);
|
contiguous_ranges(old_to_new_rows.keys().copied(), max_rows_between_yields);
|
||||||
let mut language_indent_sizes = language_indent_sizes_by_new_row.iter().peekable();
|
let mut language_indent_sizes = language_indent_sizes_by_new_row.iter().peekable();
|
||||||
|
@ -963,14 +964,17 @@ impl Buffer {
|
||||||
|
|
||||||
let suggested_indent = old_to_new_rows
|
let suggested_indent = old_to_new_rows
|
||||||
.get(&suggestion.basis_row)
|
.get(&suggestion.basis_row)
|
||||||
.and_then(|from_row| old_suggestions.get(from_row).copied())
|
.and_then(|from_row| {
|
||||||
|
Some(old_suggestions.get(from_row).copied()?.0)
|
||||||
|
})
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
request
|
request
|
||||||
.before_edit
|
.before_edit
|
||||||
.indent_size_for_line(suggestion.basis_row)
|
.indent_size_for_line(suggestion.basis_row)
|
||||||
})
|
})
|
||||||
.with_delta(suggestion.delta, language_indent_size);
|
.with_delta(suggestion.delta, language_indent_size);
|
||||||
old_suggestions.insert(new_row, suggested_indent);
|
old_suggestions
|
||||||
|
.insert(new_row, (suggested_indent, suggestion.within_error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
|
@ -1016,12 +1020,13 @@ impl Buffer {
|
||||||
snapshot.indent_size_for_line(suggestion.basis_row)
|
snapshot.indent_size_for_line(suggestion.basis_row)
|
||||||
})
|
})
|
||||||
.with_delta(suggestion.delta, language_indent_size);
|
.with_delta(suggestion.delta, language_indent_size);
|
||||||
if old_suggestions
|
if old_suggestions.get(&new_row).map_or(
|
||||||
.get(&new_row)
|
true,
|
||||||
.map_or(true, |old_indentation| {
|
|(old_indentation, was_within_error)| {
|
||||||
suggested_indent != *old_indentation
|
suggested_indent != *old_indentation
|
||||||
})
|
&& (!suggestion.within_error || *was_within_error)
|
||||||
{
|
},
|
||||||
|
) {
|
||||||
indent_sizes.insert(new_row, suggested_indent);
|
indent_sizes.insert(new_row, suggested_indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1661,6 +1666,16 @@ impl Buffer {
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
impl Buffer {
|
impl Buffer {
|
||||||
|
pub fn edit_via_marked_text(
|
||||||
|
&mut self,
|
||||||
|
marked_string: &str,
|
||||||
|
autoindent_mode: Option<AutoindentMode>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) {
|
||||||
|
let edits = self.edits_for_marked_text(marked_string);
|
||||||
|
self.edit(edits, autoindent_mode, cx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_group_interval(&mut self, group_interval: Duration) {
|
pub fn set_group_interval(&mut self, group_interval: Duration) {
|
||||||
self.text.set_group_interval(group_interval);
|
self.text.set_group_interval(group_interval);
|
||||||
}
|
}
|
||||||
|
@ -1779,7 +1794,7 @@ impl BufferSnapshot {
|
||||||
let start = Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0);
|
let start = Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0);
|
||||||
let end = Point::new(row_range.end, 0);
|
let end = Point::new(row_range.end, 0);
|
||||||
let range = (start..end).to_offset(&self.text);
|
let range = (start..end).to_offset(&self.text);
|
||||||
let mut matches = self.syntax.matches(range, &self.text, |grammar| {
|
let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
|
||||||
Some(&grammar.indents_config.as_ref()?.query)
|
Some(&grammar.indents_config.as_ref()?.query)
|
||||||
});
|
});
|
||||||
let indent_configs = matches
|
let indent_configs = matches
|
||||||
|
@ -1825,6 +1840,30 @@ impl BufferSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut error_ranges = Vec::<Range<Point>>::new();
|
||||||
|
let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
|
||||||
|
Some(&grammar.error_query)
|
||||||
|
});
|
||||||
|
while let Some(mat) = matches.peek() {
|
||||||
|
let node = mat.captures[0].node;
|
||||||
|
let start = Point::from_ts_point(node.start_position());
|
||||||
|
let end = Point::from_ts_point(node.end_position());
|
||||||
|
let range = start..end;
|
||||||
|
let ix = match error_ranges.binary_search_by_key(&range.start, |r| r.start) {
|
||||||
|
Ok(ix) | Err(ix) => ix,
|
||||||
|
};
|
||||||
|
let mut end_ix = ix;
|
||||||
|
while let Some(existing_range) = error_ranges.get(end_ix) {
|
||||||
|
if existing_range.end < end {
|
||||||
|
end_ix += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error_ranges.splice(ix..end_ix, [range]);
|
||||||
|
matches.advance();
|
||||||
|
}
|
||||||
|
|
||||||
outdent_positions.sort();
|
outdent_positions.sort();
|
||||||
for outdent_position in outdent_positions {
|
for outdent_position in outdent_positions {
|
||||||
// find the innermost indent range containing this outdent_position
|
// find the innermost indent range containing this outdent_position
|
||||||
|
@ -1902,33 +1941,42 @@ impl BufferSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let within_error = error_ranges
|
||||||
|
.iter()
|
||||||
|
.any(|e| e.start.row < row && e.end > row_start);
|
||||||
|
|
||||||
let suggestion = if outdent_to_row == prev_row
|
let suggestion = if outdent_to_row == prev_row
|
||||||
|| (outdent_from_prev_row && indent_from_prev_row)
|
|| (outdent_from_prev_row && indent_from_prev_row)
|
||||||
{
|
{
|
||||||
Some(IndentSuggestion {
|
Some(IndentSuggestion {
|
||||||
basis_row: prev_row,
|
basis_row: prev_row,
|
||||||
delta: Ordering::Equal,
|
delta: Ordering::Equal,
|
||||||
|
within_error,
|
||||||
})
|
})
|
||||||
} else if indent_from_prev_row {
|
} else if indent_from_prev_row {
|
||||||
Some(IndentSuggestion {
|
Some(IndentSuggestion {
|
||||||
basis_row: prev_row,
|
basis_row: prev_row,
|
||||||
delta: Ordering::Greater,
|
delta: Ordering::Greater,
|
||||||
|
within_error,
|
||||||
})
|
})
|
||||||
} else if outdent_to_row < prev_row {
|
} else if outdent_to_row < prev_row {
|
||||||
Some(IndentSuggestion {
|
Some(IndentSuggestion {
|
||||||
basis_row: outdent_to_row,
|
basis_row: outdent_to_row,
|
||||||
delta: Ordering::Equal,
|
delta: Ordering::Equal,
|
||||||
|
within_error,
|
||||||
})
|
})
|
||||||
} else if outdent_from_prev_row {
|
} else if outdent_from_prev_row {
|
||||||
Some(IndentSuggestion {
|
Some(IndentSuggestion {
|
||||||
basis_row: prev_row,
|
basis_row: prev_row,
|
||||||
delta: Ordering::Less,
|
delta: Ordering::Less,
|
||||||
|
within_error,
|
||||||
})
|
})
|
||||||
} else if config.auto_indent_using_last_non_empty_line || !self.is_line_blank(prev_row)
|
} else if config.auto_indent_using_last_non_empty_line || !self.is_line_blank(prev_row)
|
||||||
{
|
{
|
||||||
Some(IndentSuggestion {
|
Some(IndentSuggestion {
|
||||||
basis_row: prev_row,
|
basis_row: prev_row,
|
||||||
delta: Ordering::Equal,
|
delta: Ordering::Equal,
|
||||||
|
within_error,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -800,23 +800,29 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
||||||
cx.set_global(settings);
|
cx.set_global(settings);
|
||||||
|
|
||||||
cx.add_model(|cx| {
|
cx.add_model(|cx| {
|
||||||
let text = "
|
let mut buffer = Buffer::new(
|
||||||
|
0,
|
||||||
|
"
|
||||||
fn a() {
|
fn a() {
|
||||||
c;
|
c;
|
||||||
d;
|
d;
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
.unindent();
|
.unindent(),
|
||||||
|
cx,
|
||||||
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
)
|
||||||
|
.with_language(Arc::new(rust_lang()), cx);
|
||||||
|
|
||||||
// Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
|
// Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
|
||||||
// their indentation is not adjusted.
|
// their indentation is not adjusted.
|
||||||
buffer.edit(
|
buffer.edit_via_marked_text(
|
||||||
[
|
&"
|
||||||
(empty(Point::new(1, 1)), "()"),
|
fn a() {
|
||||||
(empty(Point::new(2, 1)), "()"),
|
c«()»;
|
||||||
],
|
d«()»;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
Some(AutoindentMode::EachLine),
|
Some(AutoindentMode::EachLine),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -833,14 +839,22 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
||||||
|
|
||||||
// When appending new content after these lines, the indentation is based on the
|
// When appending new content after these lines, the indentation is based on the
|
||||||
// preceding lines' actual indentation.
|
// preceding lines' actual indentation.
|
||||||
buffer.edit(
|
buffer.edit_via_marked_text(
|
||||||
[
|
&"
|
||||||
(empty(Point::new(1, 1)), "\n.f\n.g"),
|
fn a() {
|
||||||
(empty(Point::new(2, 1)), "\n.f\n.g"),
|
c«
|
||||||
],
|
.f
|
||||||
|
.g()»;
|
||||||
|
d«
|
||||||
|
.f
|
||||||
|
.g()»;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
Some(AutoindentMode::EachLine),
|
Some(AutoindentMode::EachLine),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer.text(),
|
buffer.text(),
|
||||||
"
|
"
|
||||||
|
@ -859,20 +873,27 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.add_model(|cx| {
|
cx.add_model(|cx| {
|
||||||
let text = "
|
let mut buffer = Buffer::new(
|
||||||
|
0,
|
||||||
|
"
|
||||||
fn a() {
|
fn a() {
|
||||||
{
|
b();
|
||||||
b()?
|
|
|
||||||
}
|
"
|
||||||
Ok(())
|
.replace("|", "") // marker to preserve trailing whitespace
|
||||||
}
|
.unindent(),
|
||||||
"
|
cx,
|
||||||
.unindent();
|
)
|
||||||
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
.with_language(Arc::new(rust_lang()), cx);
|
||||||
|
|
||||||
// Delete a closing curly brace changes the suggested indent for the line.
|
// Insert a closing brace. It is outdented.
|
||||||
buffer.edit(
|
buffer.edit_via_marked_text(
|
||||||
[(Point::new(3, 4)..Point::new(3, 5), "")],
|
&"
|
||||||
|
fn a() {
|
||||||
|
b();
|
||||||
|
«}»
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
Some(AutoindentMode::EachLine),
|
Some(AutoindentMode::EachLine),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -880,19 +901,20 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
||||||
buffer.text(),
|
buffer.text(),
|
||||||
"
|
"
|
||||||
fn a() {
|
fn a() {
|
||||||
{
|
b();
|
||||||
b()?
|
|
||||||
|
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
.replace('|', "") // included in the string to preserve trailing whites
|
|
||||||
.unindent()
|
.unindent()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Manually editing the leading whitespace
|
// Manually edit the leading whitespace. The edit is preserved.
|
||||||
buffer.edit(
|
buffer.edit_via_marked_text(
|
||||||
[(Point::new(3, 0)..Point::new(3, 12), "")],
|
&"
|
||||||
|
fn a() {
|
||||||
|
b();
|
||||||
|
« »}
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
Some(AutoindentMode::EachLine),
|
Some(AutoindentMode::EachLine),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -900,11 +922,8 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
||||||
buffer.text(),
|
buffer.text(),
|
||||||
"
|
"
|
||||||
fn a() {
|
fn a() {
|
||||||
{
|
b();
|
||||||
b()?
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
"
|
"
|
||||||
.unindent()
|
.unindent()
|
||||||
);
|
);
|
||||||
|
@ -913,30 +932,108 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
|
fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut MutableAppContext) {
|
||||||
cx.set_global(Settings::test(cx));
|
let settings = Settings::test(cx);
|
||||||
|
cx.set_global(settings);
|
||||||
|
|
||||||
cx.add_model(|cx| {
|
cx.add_model(|cx| {
|
||||||
let text = "
|
let mut buffer = Buffer::new(
|
||||||
fn a() {}
|
0,
|
||||||
"
|
"
|
||||||
.unindent();
|
fn a() {
|
||||||
|
i
|
||||||
|
}
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.with_language(Arc::new(rust_lang()), cx);
|
||||||
|
|
||||||
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
// Regression test: line does not get outdented due to syntax error
|
||||||
|
buffer.edit_via_marked_text(
|
||||||
buffer.edit([(5..5, "\nb")], Some(AutoindentMode::EachLine), cx);
|
&"
|
||||||
|
fn a() {
|
||||||
|
i«f let Some(x) = y»
|
||||||
|
}
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
|
Some(AutoindentMode::EachLine),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer.text(),
|
buffer.text(),
|
||||||
"
|
"
|
||||||
fn a(
|
fn a() {
|
||||||
b) {}
|
if let Some(x) = y
|
||||||
|
}
|
||||||
|
"
|
||||||
|
.unindent()
|
||||||
|
);
|
||||||
|
|
||||||
|
buffer.edit_via_marked_text(
|
||||||
|
&"
|
||||||
|
fn a() {
|
||||||
|
if let Some(x) = y« {»
|
||||||
|
}
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
|
Some(AutoindentMode::EachLine),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
buffer.text(),
|
||||||
|
"
|
||||||
|
fn a() {
|
||||||
|
if let Some(x) = y {
|
||||||
|
}
|
||||||
|
"
|
||||||
|
.unindent()
|
||||||
|
);
|
||||||
|
|
||||||
|
buffer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
|
cx.add_model(|cx| {
|
||||||
|
let mut buffer = Buffer::new(
|
||||||
|
0,
|
||||||
|
"
|
||||||
|
fn a() {}
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.with_language(Arc::new(rust_lang()), cx);
|
||||||
|
|
||||||
|
buffer.edit_via_marked_text(
|
||||||
|
&"
|
||||||
|
fn a(«
|
||||||
|
b») {}
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
|
Some(AutoindentMode::EachLine),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
buffer.text(),
|
||||||
|
"
|
||||||
|
fn a(
|
||||||
|
b) {}
|
||||||
"
|
"
|
||||||
.unindent()
|
.unindent()
|
||||||
);
|
);
|
||||||
|
|
||||||
// The indentation suggestion changed because `@end` node (a close paren)
|
// The indentation suggestion changed because `@end` node (a close paren)
|
||||||
// is now at the beginning of the line.
|
// is now at the beginning of the line.
|
||||||
buffer.edit(
|
buffer.edit_via_marked_text(
|
||||||
[(Point::new(1, 4)..Point::new(1, 5), "")],
|
&"
|
||||||
|
fn a(
|
||||||
|
ˇ) {}
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
Some(AutoindentMode::EachLine),
|
Some(AutoindentMode::EachLine),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -1894,7 +1991,3 @@ fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> Str
|
||||||
layers[0].node.to_sexp()
|
layers[0].node.to_sexp()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty(point: Point) -> Range<Point> {
|
|
||||||
point..point
|
|
||||||
}
|
|
||||||
|
|
|
@ -348,6 +348,7 @@ pub struct Language {
|
||||||
pub struct Grammar {
|
pub struct Grammar {
|
||||||
id: usize,
|
id: usize,
|
||||||
pub(crate) ts_language: tree_sitter::Language,
|
pub(crate) ts_language: tree_sitter::Language,
|
||||||
|
pub(crate) error_query: Query,
|
||||||
pub(crate) highlights_query: Option<Query>,
|
pub(crate) highlights_query: Option<Query>,
|
||||||
pub(crate) brackets_config: Option<BracketConfig>,
|
pub(crate) brackets_config: Option<BracketConfig>,
|
||||||
pub(crate) indents_config: Option<IndentConfig>,
|
pub(crate) indents_config: Option<IndentConfig>,
|
||||||
|
@ -684,6 +685,7 @@ impl Language {
|
||||||
indents_config: None,
|
indents_config: None,
|
||||||
injection_config: None,
|
injection_config: None,
|
||||||
override_config: None,
|
override_config: None,
|
||||||
|
error_query: Query::new(ts_language, "(ERROR) @error").unwrap(),
|
||||||
ts_language,
|
ts_language,
|
||||||
highlight_map: Default::default(),
|
highlight_map: Default::default(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -2262,7 +2262,7 @@ mod tests {
|
||||||
mutated_syntax_map.reparse(language.clone(), &buffer);
|
mutated_syntax_map.reparse(language.clone(), &buffer);
|
||||||
|
|
||||||
for (i, marked_string) in steps.into_iter().enumerate() {
|
for (i, marked_string) in steps.into_iter().enumerate() {
|
||||||
edit_buffer(&mut buffer, &marked_string.unindent());
|
buffer.edit_via_marked_text(&marked_string.unindent());
|
||||||
|
|
||||||
// Reparse the syntax map
|
// Reparse the syntax map
|
||||||
mutated_syntax_map.interpolate(&buffer);
|
mutated_syntax_map.interpolate(&buffer);
|
||||||
|
@ -2452,52 +2452,6 @@ mod tests {
|
||||||
assert_eq!(actual_ranges, expected_ranges);
|
assert_eq!(actual_ranges, expected_ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edit_buffer(buffer: &mut Buffer, marked_string: &str) {
|
|
||||||
let old_text = buffer.text();
|
|
||||||
let (new_text, mut ranges) = marked_text_ranges(marked_string, false);
|
|
||||||
if ranges.is_empty() {
|
|
||||||
ranges.push(0..new_text.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
old_text[..ranges[0].start],
|
|
||||||
new_text[..ranges[0].start],
|
|
||||||
"invalid edit"
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut delta = 0;
|
|
||||||
let mut edits = Vec::new();
|
|
||||||
let mut ranges = ranges.into_iter().peekable();
|
|
||||||
|
|
||||||
while let Some(inserted_range) = ranges.next() {
|
|
||||||
let new_start = inserted_range.start;
|
|
||||||
let old_start = (new_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!(
|
|
||||||
old_text.len() as isize + delta,
|
|
||||||
new_text.len() as isize,
|
|
||||||
"invalid edit"
|
|
||||||
);
|
|
||||||
|
|
||||||
buffer.edit(edits);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn string_contains_sequence(text: &str, parts: &[&str]) -> bool {
|
pub fn string_contains_sequence(text: &str, parts: &[&str]) -> bool {
|
||||||
let mut last_part_end = 0;
|
let mut last_part_end = 0;
|
||||||
for part in parts {
|
for part in parts {
|
||||||
|
|
|
@ -1372,6 +1372,57 @@ impl Buffer {
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
impl Buffer {
|
impl Buffer {
|
||||||
|
pub fn edit_via_marked_text(&mut self, marked_string: &str) {
|
||||||
|
let edits = self.edits_for_marked_text(marked_string);
|
||||||
|
self.edit(edits);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn edits_for_marked_text(&self, marked_string: &str) -> Vec<(Range<usize>, String)> {
|
||||||
|
let old_text = self.text();
|
||||||
|
let (new_text, mut ranges) = util::test::marked_text_ranges(marked_string, false);
|
||||||
|
if ranges.is_empty() {
|
||||||
|
ranges.push(0..new_text.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
old_text[..ranges[0].start],
|
||||||
|
new_text[..ranges[0].start],
|
||||||
|
"invalid edit"
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut delta = 0;
|
||||||
|
let mut edits = Vec::new();
|
||||||
|
let mut ranges = ranges.into_iter().peekable();
|
||||||
|
|
||||||
|
while let Some(inserted_range) = ranges.next() {
|
||||||
|
let new_start = inserted_range.start;
|
||||||
|
let old_start = (new_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!(
|
||||||
|
old_text.len() as isize + delta,
|
||||||
|
new_text.len() as isize,
|
||||||
|
"invalid edit"
|
||||||
|
);
|
||||||
|
|
||||||
|
edits
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_invariants(&self) {
|
pub fn check_invariants(&self) {
|
||||||
// Ensure every fragment is ordered by locator in the fragment tree and corresponds
|
// Ensure every fragment is ordered by locator in the fragment tree and corresponds
|
||||||
// to an insertion fragment in the insertions tree.
|
// to an insertion fragment in the insertions tree.
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
[
|
[
|
||||||
"as"
|
"as"
|
||||||
"async"
|
"async"
|
||||||
|
"await"
|
||||||
"break"
|
"break"
|
||||||
"const"
|
"const"
|
||||||
"continue"
|
"continue"
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
(assignment_expression)
|
(assignment_expression)
|
||||||
(let_declaration)
|
(let_declaration)
|
||||||
(let_chain)
|
(let_chain)
|
||||||
|
(await_expression)
|
||||||
] @indent
|
] @indent
|
||||||
|
|
||||||
(_ "[" "]" @end) @indent
|
(_ "[" "]" @end) @indent
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue