editor/language: hoist out non-generic parts of edit functions. (#3130)

This reduces LLVM IR size of editor (that's one of the heaviest crates
to build) by almost 5%.

LLVM IR size of `editor` before this PR: 3280386
LLVM IR size with `editor::edit` changed: 3227092
LLVM IR size with `editor::edit` and `language::edit` changed: 3146807

Release Notes:
- N/A
This commit is contained in:
Piotr Osiewicz 2023-10-16 13:17:44 +02:00 committed by GitHub
parent 6f4008ebab
commit cc335db9e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 158 additions and 131 deletions

View file

@ -498,77 +498,91 @@ impl MultiBuffer {
}
}
for (buffer_id, mut edits) in buffer_edits {
edits.sort_unstable_by_key(|edit| edit.range.start);
self.buffers.borrow()[&buffer_id]
.buffer
.update(cx, |buffer, cx| {
let mut edits = edits.into_iter().peekable();
let mut insertions = Vec::new();
let mut original_indent_columns = Vec::new();
let mut deletions = Vec::new();
let empty_str: Arc<str> = "".into();
while let Some(BufferEdit {
mut range,
new_text,
mut is_insertion,
original_indent_column,
}) = edits.next()
{
drop(cursor);
drop(snapshot);
// Non-generic part of edit, hoisted out to avoid blowing up LLVM IR.
fn tail(
this: &mut MultiBuffer,
buffer_edits: HashMap<u64, Vec<BufferEdit>>,
autoindent_mode: Option<AutoindentMode>,
edited_excerpt_ids: Vec<ExcerptId>,
cx: &mut ModelContext<MultiBuffer>,
) {
for (buffer_id, mut edits) in buffer_edits {
edits.sort_unstable_by_key(|edit| edit.range.start);
this.buffers.borrow()[&buffer_id]
.buffer
.update(cx, |buffer, cx| {
let mut edits = edits.into_iter().peekable();
let mut insertions = Vec::new();
let mut original_indent_columns = Vec::new();
let mut deletions = Vec::new();
let empty_str: Arc<str> = "".into();
while let Some(BufferEdit {
range: next_range,
is_insertion: next_is_insertion,
..
}) = edits.peek()
mut range,
new_text,
mut is_insertion,
original_indent_column,
}) = edits.next()
{
if range.end >= next_range.start {
range.end = cmp::max(next_range.end, range.end);
is_insertion |= *next_is_insertion;
edits.next();
} else {
break;
while let Some(BufferEdit {
range: next_range,
is_insertion: next_is_insertion,
..
}) = edits.peek()
{
if range.end >= next_range.start {
range.end = cmp::max(next_range.end, range.end);
is_insertion |= *next_is_insertion;
edits.next();
} else {
break;
}
}
if is_insertion {
original_indent_columns.push(original_indent_column);
insertions.push((
buffer.anchor_before(range.start)
..buffer.anchor_before(range.end),
new_text.clone(),
));
} else if !range.is_empty() {
deletions.push((
buffer.anchor_before(range.start)
..buffer.anchor_before(range.end),
empty_str.clone(),
));
}
}
if is_insertion {
original_indent_columns.push(original_indent_column);
insertions.push((
buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
new_text.clone(),
));
} else if !range.is_empty() {
deletions.push((
buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
empty_str.clone(),
));
}
}
let deletion_autoindent_mode =
if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
Some(AutoindentMode::Block {
original_indent_columns: Default::default(),
})
} else {
None
};
let insertion_autoindent_mode =
if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
Some(AutoindentMode::Block {
original_indent_columns,
})
} else {
None
};
let deletion_autoindent_mode =
if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
Some(AutoindentMode::Block {
original_indent_columns: Default::default(),
})
} else {
None
};
let insertion_autoindent_mode =
if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
Some(AutoindentMode::Block {
original_indent_columns,
})
} else {
None
};
buffer.edit(deletions, deletion_autoindent_mode, cx);
buffer.edit(insertions, insertion_autoindent_mode, cx);
})
}
buffer.edit(deletions, deletion_autoindent_mode, cx);
buffer.edit(insertions, insertion_autoindent_mode, cx);
})
cx.emit(Event::ExcerptsEdited {
ids: edited_excerpt_ids,
});
}
cx.emit(Event::ExcerptsEdited {
ids: edited_excerpt_ids,
});
tail(self, buffer_edits, autoindent_mode, edited_excerpt_ids, cx);
}
pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {

View file

@ -1448,82 +1448,95 @@ impl Buffer {
return None;
}
self.start_transaction();
self.pending_autoindent.take();
let autoindent_request = autoindent_mode
.and_then(|mode| self.language.as_ref().map(|_| (self.snapshot(), mode)));
// Non-generic part hoisted out to reduce LLVM IR size.
fn tail(
this: &mut Buffer,
edits: Vec<(Range<usize>, Arc<str>)>,
autoindent_mode: Option<AutoindentMode>,
cx: &mut ModelContext<Buffer>,
) -> Option<clock::Lamport> {
this.start_transaction();
this.pending_autoindent.take();
let autoindent_request = autoindent_mode
.and_then(|mode| this.language.as_ref().map(|_| (this.snapshot(), mode)));
let edit_operation = self.text.edit(edits.iter().cloned());
let edit_id = edit_operation.timestamp();
let edit_operation = this.text.edit(edits.iter().cloned());
let edit_id = edit_operation.timestamp();
if let Some((before_edit, mode)) = autoindent_request {
let mut delta = 0isize;
let entries = edits
.into_iter()
.enumerate()
.zip(&edit_operation.as_edit().unwrap().new_text)
.map(|((ix, (range, _)), new_text)| {
let new_text_length = new_text.len();
let old_start = range.start.to_point(&before_edit);
let new_start = (delta + range.start as isize) as usize;
delta += new_text_length as isize - (range.end as isize - range.start as isize);
if let Some((before_edit, mode)) = autoindent_request {
let mut delta = 0isize;
let entries = edits
.into_iter()
.enumerate()
.zip(&edit_operation.as_edit().unwrap().new_text)
.map(|((ix, (range, _)), new_text)| {
let new_text_length = new_text.len();
let old_start = range.start.to_point(&before_edit);
let new_start = (delta + range.start as isize) as usize;
delta +=
new_text_length as isize - (range.end as isize - range.start as isize);
let mut range_of_insertion_to_indent = 0..new_text_length;
let mut first_line_is_new = false;
let mut original_indent_column = None;
let mut range_of_insertion_to_indent = 0..new_text_length;
let mut first_line_is_new = false;
let mut original_indent_column = None;
// When inserting an entire line at the beginning of an existing line,
// treat the insertion as new.
if new_text.contains('\n')
&& old_start.column <= before_edit.indent_size_for_line(old_start.row).len
{
first_line_is_new = true;
}
// When inserting text starting with a newline, avoid auto-indenting the
// previous line.
if new_text.starts_with('\n') {
range_of_insertion_to_indent.start += 1;
first_line_is_new = true;
}
// Avoid auto-indenting after the insertion.
if let AutoindentMode::Block {
original_indent_columns,
} = &mode
{
original_indent_column =
Some(original_indent_columns.get(ix).copied().unwrap_or_else(|| {
indent_size_for_text(
new_text[range_of_insertion_to_indent.clone()].chars(),
)
.len
}));
if new_text[range_of_insertion_to_indent.clone()].ends_with('\n') {
range_of_insertion_to_indent.end -= 1;
// When inserting an entire line at the beginning of an existing line,
// treat the insertion as new.
if new_text.contains('\n')
&& old_start.column
<= before_edit.indent_size_for_line(old_start.row).len
{
first_line_is_new = true;
}
}
AutoindentRequestEntry {
first_line_is_new,
original_indent_column,
indent_size: before_edit.language_indent_size_at(range.start, cx),
range: self.anchor_before(new_start + range_of_insertion_to_indent.start)
..self.anchor_after(new_start + range_of_insertion_to_indent.end),
}
})
.collect();
// When inserting text starting with a newline, avoid auto-indenting the
// previous line.
if new_text.starts_with('\n') {
range_of_insertion_to_indent.start += 1;
first_line_is_new = true;
}
self.autoindent_requests.push(Arc::new(AutoindentRequest {
before_edit,
entries,
is_block_mode: matches!(mode, AutoindentMode::Block { .. }),
}));
// Avoid auto-indenting after the insertion.
if let AutoindentMode::Block {
original_indent_columns,
} = &mode
{
original_indent_column = Some(
original_indent_columns.get(ix).copied().unwrap_or_else(|| {
indent_size_for_text(
new_text[range_of_insertion_to_indent.clone()].chars(),
)
.len
}),
);
if new_text[range_of_insertion_to_indent.clone()].ends_with('\n') {
range_of_insertion_to_indent.end -= 1;
}
}
AutoindentRequestEntry {
first_line_is_new,
original_indent_column,
indent_size: before_edit.language_indent_size_at(range.start, cx),
range: this
.anchor_before(new_start + range_of_insertion_to_indent.start)
..this.anchor_after(new_start + range_of_insertion_to_indent.end),
}
})
.collect();
this.autoindent_requests.push(Arc::new(AutoindentRequest {
before_edit,
entries,
is_block_mode: matches!(mode, AutoindentMode::Block { .. }),
}));
}
this.end_transaction(cx);
this.send_operation(Operation::Buffer(edit_operation), cx);
Some(edit_id)
}
self.end_transaction(cx);
self.send_operation(Operation::Buffer(edit_operation), cx);
Some(edit_id)
tail(self, edits, autoindent_mode, cx)
}
fn did_edit(