add delete_by_and_with_selection

pull/7269/head
Skyler Hawthorne 2 years ago
parent f0d8662973
commit b1d17e7e61

@ -110,17 +110,22 @@ impl Default for AutoPairs {
// middle of triple quotes, and more exotic pairs like Jinja's {% %} // middle of triple quotes, and more exotic pairs like Jinja's {% %}
#[must_use] #[must_use]
pub fn hook(doc: &Rope, range: &Range, ch: char, pairs: &AutoPairs) -> Option<(Change, Range)> { pub fn hook_insert(
doc: &Rope,
range: &Range,
ch: char,
pairs: &AutoPairs,
) -> Option<(Change, Range)> {
log::trace!("autopairs hook range: {:#?}", range); log::trace!("autopairs hook range: {:#?}", range);
if let Some(pair) = pairs.get(ch) { if let Some(pair) = pairs.get(ch) {
if pair.same() { if pair.same() {
return handle_same(doc, range, pair); return handle_insert_same(doc, range, pair);
} else if pair.open == ch { } else if pair.open == ch {
return handle_open(doc, range, pair); return handle_insert_open(doc, range, pair);
} else if pair.close == ch { } else if pair.close == ch {
// && char_at pos == close // && char_at pos == close
return handle_close(doc, range, pair); return handle_insert_close(doc, range, pair);
} }
} }
@ -243,7 +248,7 @@ fn get_next_range(doc: &Rope, start_range: &Range, len_inserted: usize) -> Range
Range::new(end_anchor, end_head) Range::new(end_anchor, end_head)
} }
fn handle_open(doc: &Rope, range: &Range, pair: &Pair) -> Option<(Change, Range)> { fn handle_insert_open(doc: &Rope, range: &Range, pair: &Pair) -> Option<(Change, Range)> {
let cursor = range.cursor(doc.slice(..)); let cursor = range.cursor(doc.slice(..));
let next_char = doc.get_char(cursor); let next_char = doc.get_char(cursor);
let len_inserted; let len_inserted;
@ -271,7 +276,7 @@ fn handle_open(doc: &Rope, range: &Range, pair: &Pair) -> Option<(Change, Range)
Some(result) Some(result)
} }
fn handle_close(doc: &Rope, range: &Range, pair: &Pair) -> Option<(Change, Range)> { fn handle_insert_close(doc: &Rope, range: &Range, pair: &Pair) -> Option<(Change, Range)> {
let cursor = range.cursor(doc.slice(..)); let cursor = range.cursor(doc.slice(..));
let next_char = doc.get_char(cursor); let next_char = doc.get_char(cursor);
@ -291,7 +296,7 @@ fn handle_close(doc: &Rope, range: &Range, pair: &Pair) -> Option<(Change, Range
} }
/// handle cases where open and close is the same, or in triples ("""docstring""") /// handle cases where open and close is the same, or in triples ("""docstring""")
fn handle_same(doc: &Rope, range: &Range, pair: &Pair) -> Option<(Change, Range)> { fn handle_insert_same(doc: &Rope, range: &Range, pair: &Pair) -> Option<(Change, Range)> {
let cursor = range.cursor(doc.slice(..)); let cursor = range.cursor(doc.slice(..));
let mut len_inserted = 0; let mut len_inserted = 0;
let next_char = doc.get_char(cursor); let next_char = doc.get_char(cursor);

@ -738,16 +738,6 @@ impl Transaction {
) )
} }
/// Generate a transaction with a deletion per selection range.
/// Compared to using `change_by_selection` directly these ranges may overlap.
/// In that case they are merged
pub fn delete_by_selection<F>(doc: &Rope, selection: &Selection, f: F) -> Self
where
F: FnMut(&Range) -> Deletion,
{
Self::delete(doc, selection.iter().map(f))
}
/// Generate a transaction with a change per selection range, which /// Generate a transaction with a change per selection range, which
/// generates a new selection as well. Each range is operated upon by /// generates a new selection as well. Each range is operated upon by
/// the given function and can optionally produce a new range. If none /// the given function and can optionally produce a new range. If none
@ -801,6 +791,60 @@ impl Transaction {
transaction.with_selection(Selection::new(end_ranges, selection.primary_index())) transaction.with_selection(Selection::new(end_ranges, selection.primary_index()))
} }
/// Generate a transaction with a deletion per selection range.
/// Compared to using `change_by_selection` directly these ranges may overlap.
/// In that case they are merged.
pub fn delete_by_selection<F>(doc: &Rope, selection: &Selection, f: F) -> Self
where
F: FnMut(&Range) -> Deletion,
{
Self::delete(doc, selection.iter().map(f))
}
/// Generate a transaction with a delete per selection range, which
/// generates a new selection as well. Each range is operated upon by
/// the given function and can optionally produce a new range. If none
/// is returned by the function, that range is mapped through the change
/// as usual.
///
/// Compared to using `change_by_and_with_selection` directly these ranges
/// may overlap. In that case they are merged.
pub fn delete_by_and_with_selection<F>(doc: &Rope, selection: &Selection, mut f: F) -> Self
where
F: FnMut(&Range) -> (Deletion, Option<Range>),
{
let mut end_ranges = SmallVec::with_capacity(selection.len());
let mut offset = 0;
let transaction = Transaction::delete_by_selection(doc, selection, |start_range| {
let ((from, to), end_range) = f(start_range);
let change_size = to - from;
if let Some(end_range) = end_range {
let offset_range = Range::new(
end_range.anchor.saturating_sub(offset),
end_range.head.saturating_sub(offset),
);
log::trace!("end range {:?} offset to: {:?}", end_range, offset_range);
end_ranges.push(offset_range);
} else {
let changeset = ChangeSet::from_change(doc, (from, to, None));
let end_range = start_range.map(&changeset);
end_ranges.push(end_range);
}
offset += change_size;
log::trace!("delete from: {}, to: {}, offset: {}", from, to, offset);
(from, to)
});
transaction.with_selection(Selection::new(end_ranges, selection.primary_index()))
}
/// Insert text at each selection head. /// Insert text at each selection head.
pub fn insert(doc: &Rope, selection: &Selection, text: Tendril) -> Self { pub fn insert(doc: &Rope, selection: &Selection, text: Tendril) -> Self {
Self::change_by_selection(doc, selection, |range| { Self::change_by_selection(doc, selection, |range| {

@ -3868,7 +3868,7 @@ pub mod insert {
auto_pairs auto_pairs
.as_ref() .as_ref()
.and_then(|ap| { .and_then(|ap| {
auto_pairs::hook(text, range, c, ap) auto_pairs::hook_insert(text, range, c, ap)
.map(|(change, range)| (change, Some(range))) .map(|(change, range)| (change, Some(range)))
.or(Some(insert_char(*range, c))) .or(Some(insert_char(*range, c)))
}) })
@ -4055,11 +4055,13 @@ pub mod insert {
let indent_width = doc.indent_width(); let indent_width = doc.indent_width();
let auto_pairs = doc.auto_pairs(cx.editor); let auto_pairs = doc.auto_pairs(cx.editor);
let transaction = let transaction = Transaction::delete_by_and_with_selection(
Transaction::delete_by_selection(doc.text(), doc.selection(view.id), |range| { doc.text(),
doc.selection(view.id),
|range| {
let pos = range.cursor(text); let pos = range.cursor(text);
if pos == 0 { if pos == 0 {
return (pos, pos); return ((pos, pos), None);
} }
let line_start_pos = text.line_to_char(range.cursor_line(text)); let line_start_pos = text.line_to_char(range.cursor_line(text));
// consider to delete by indent level if all characters before `pos` are indent units. // consider to delete by indent level if all characters before `pos` are indent units.
@ -4067,7 +4069,10 @@ pub mod insert {
if !fragment.is_empty() && fragment.chars().all(|ch| ch == ' ' || ch == '\t') { if !fragment.is_empty() && fragment.chars().all(|ch| ch == ' ' || ch == '\t') {
if text.get_char(pos.saturating_sub(1)) == Some('\t') { if text.get_char(pos.saturating_sub(1)) == Some('\t') {
// fast path, delete one char // fast path, delete one char
(graphemes::nth_prev_grapheme_boundary(text, pos, 1), pos) (
(graphemes::nth_prev_grapheme_boundary(text, pos, 1), pos),
None,
)
} else { } else {
let width: usize = fragment let width: usize = fragment
.chars() .chars()
@ -4094,7 +4099,7 @@ pub mod insert {
_ => break, _ => break,
} }
} }
(start, pos) // delete! ((start, pos), None) // delete!
} }
} else { } else {
match ( match (
@ -4110,18 +4115,26 @@ pub mod insert {
// delete both autopaired characters // delete both autopaired characters
{ {
( (
graphemes::nth_prev_grapheme_boundary(text, pos, count), (
graphemes::nth_next_grapheme_boundary(text, pos, count), graphemes::nth_prev_grapheme_boundary(text, pos, count),
graphemes::nth_next_grapheme_boundary(text, pos, count),
),
None,
) )
} }
_ => _ =>
// delete 1 char // delete 1 char
{ {
(graphemes::nth_prev_grapheme_boundary(text, pos, count), pos) (
(graphemes::nth_prev_grapheme_boundary(text, pos, count), pos),
None,
)
} }
} }
} }
}); },
);
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
} }

Loading…
Cancel
Save