mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 10:40:54 +00:00
Improve calculation of which lines are new when auto-indenting
This commit is contained in:
parent
6b9c1e78c1
commit
09ed149184
2 changed files with 121 additions and 44 deletions
|
@ -232,7 +232,7 @@ struct SyntaxTree {
|
|||
struct AutoindentRequest {
|
||||
before_edit: BufferSnapshot,
|
||||
edited: Vec<Anchor>,
|
||||
inserted: Option<Vec<Range<Anchor>>>,
|
||||
inserted: Vec<Range<Anchor>>,
|
||||
indent_size: IndentSize,
|
||||
}
|
||||
|
||||
|
@ -874,9 +874,10 @@ impl Buffer {
|
|||
yield_now().await;
|
||||
}
|
||||
|
||||
if let Some(inserted) = request.inserted.as_ref() {
|
||||
if !request.inserted.is_empty() {
|
||||
let inserted_row_ranges = contiguous_ranges(
|
||||
inserted
|
||||
request
|
||||
.inserted
|
||||
.iter()
|
||||
.map(|range| range.to_point(&snapshot))
|
||||
.flat_map(|range| range.start.row..range.end.row + 1),
|
||||
|
@ -1203,52 +1204,61 @@ impl Buffer {
|
|||
|
||||
self.start_transaction();
|
||||
self.pending_autoindent.take();
|
||||
let autoindent_request =
|
||||
self.language
|
||||
.as_ref()
|
||||
.and_then(|_| autoindent_size)
|
||||
.map(|autoindent_size| {
|
||||
let before_edit = self.snapshot();
|
||||
let edited = edits
|
||||
.iter()
|
||||
.filter_map(|(range, new_text)| {
|
||||
let start = range.start.to_point(self);
|
||||
if new_text.starts_with('\n')
|
||||
&& start.column == self.line_len(start.row)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(self.anchor_before(range.start))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
(before_edit, edited, autoindent_size)
|
||||
});
|
||||
let autoindent_request = self
|
||||
.language
|
||||
.as_ref()
|
||||
.and_then(|_| autoindent_size)
|
||||
.map(|autoindent_size| (self.snapshot(), autoindent_size));
|
||||
|
||||
let edit_operation = self.text.edit(edits.iter().cloned());
|
||||
let edit_id = edit_operation.local_timestamp();
|
||||
|
||||
if let Some((before_edit, edited, size)) = autoindent_request {
|
||||
let mut delta = 0isize;
|
||||
if let Some((before_edit, size)) = autoindent_request {
|
||||
let mut inserted = Vec::new();
|
||||
let mut edited = Vec::new();
|
||||
|
||||
let inserted_ranges = edits
|
||||
let mut delta = 0isize;
|
||||
for ((range, _), new_text) in edits
|
||||
.into_iter()
|
||||
.zip(&edit_operation.as_edit().unwrap().new_text)
|
||||
.filter_map(|((range, _), new_text)| {
|
||||
let first_newline_ix = new_text.find('\n')?;
|
||||
let new_text_len = new_text.len();
|
||||
let start = (delta + range.start as isize) as usize + first_newline_ix + 1;
|
||||
let end = (delta + range.start as isize) as usize + new_text_len;
|
||||
delta += new_text_len as isize - (range.end as isize - range.start as isize);
|
||||
Some(self.anchor_before(start)..self.anchor_after(end))
|
||||
})
|
||||
.collect::<Vec<Range<Anchor>>>();
|
||||
{
|
||||
let new_text_len = new_text.len();
|
||||
let first_newline_ix = new_text.find('\n');
|
||||
let old_start = range.start.to_point(&before_edit);
|
||||
|
||||
let inserted = if inserted_ranges.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(inserted_ranges)
|
||||
};
|
||||
let start = (delta + range.start as isize) as usize;
|
||||
delta += new_text_len as isize - (range.end as isize - range.start as isize);
|
||||
|
||||
// When inserting multiple lines of text at the beginning of a line,
|
||||
// treat all of the affected lines as newly-inserted.
|
||||
if first_newline_ix.is_some()
|
||||
&& old_start.column < before_edit.indent_size_for_line(old_start.row).len
|
||||
{
|
||||
inserted
|
||||
.push(self.anchor_before(start)..self.anchor_after(start + new_text_len));
|
||||
continue;
|
||||
}
|
||||
|
||||
// When inserting a newline at the end of an existing line, treat the following
|
||||
// line as newly-inserted.
|
||||
if first_newline_ix == Some(0)
|
||||
&& old_start.column == before_edit.line_len(old_start.row)
|
||||
{
|
||||
inserted.push(
|
||||
self.anchor_before(start + 1)..self.anchor_after(start + new_text_len),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, mark the start of the edit as edited, and any subsequent
|
||||
// lines as newly inserted.
|
||||
edited.push(before_edit.anchor_before(range.start));
|
||||
if let Some(ix) = first_newline_ix {
|
||||
inserted.push(
|
||||
self.anchor_before(start + ix + 1)..self.anchor_after(start + new_text_len),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.autoindent_requests.push(Arc::new(AutoindentRequest {
|
||||
before_edit,
|
||||
|
|
|
@ -756,8 +756,18 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
|||
});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let text = "fn a() {\n {\n b()?\n }\n\n Ok(())\n}";
|
||||
let text = "
|
||||
fn a() {
|
||||
{
|
||||
b()?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
"
|
||||
.unindent();
|
||||
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
||||
|
||||
// Delete a closing curly brace changes the suggested indent for the line.
|
||||
buffer.edit_with_autoindent(
|
||||
[(Point::new(3, 4)..Point::new(3, 5), "")],
|
||||
IndentSize::spaces(4),
|
||||
|
@ -765,9 +775,19 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
|||
);
|
||||
assert_eq!(
|
||||
buffer.text(),
|
||||
"fn a() {\n {\n b()?\n \n\n Ok(())\n}"
|
||||
"
|
||||
fn a() {
|
||||
{
|
||||
b()?
|
||||
|
|
||||
Ok(())
|
||||
}
|
||||
"
|
||||
.replace("|", "") // included in the string to preserve trailing whites
|
||||
.unindent()
|
||||
);
|
||||
|
||||
// Manually editing the leading whitespace
|
||||
buffer.edit_with_autoindent(
|
||||
[(Point::new(3, 0)..Point::new(3, 12), "")],
|
||||
IndentSize::spaces(4),
|
||||
|
@ -775,7 +795,15 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta
|
|||
);
|
||||
assert_eq!(
|
||||
buffer.text(),
|
||||
"fn a() {\n {\n b()?\n\n\n Ok(())\n}"
|
||||
"
|
||||
fn a() {
|
||||
{
|
||||
b()?
|
||||
|
||||
Ok(())
|
||||
}
|
||||
"
|
||||
.unindent()
|
||||
);
|
||||
buffer
|
||||
});
|
||||
|
@ -832,6 +860,45 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_multi_line_insertion(cx: &mut MutableAppContext) {
|
||||
cx.add_model(|cx| {
|
||||
let text = "
|
||||
const a: usize = 1;
|
||||
fn b() {
|
||||
if c {
|
||||
let d = 2;
|
||||
}
|
||||
}
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
||||
buffer.edit_with_autoindent(
|
||||
[(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
|
||||
IndentSize::spaces(4),
|
||||
cx,
|
||||
);
|
||||
assert_eq!(
|
||||
buffer.text(),
|
||||
"
|
||||
const a: usize = 1;
|
||||
fn b() {
|
||||
if c {
|
||||
e(
|
||||
f()
|
||||
);
|
||||
let d = 2;
|
||||
}
|
||||
}
|
||||
"
|
||||
.unindent()
|
||||
);
|
||||
|
||||
buffer
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_disabled(cx: &mut MutableAppContext) {
|
||||
cx.add_model(|cx| {
|
||||
|
|
Loading…
Reference in a new issue