mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-25 19:03:30 +04:00
Add API to create a Transaction from potentially overlapping changes
This commit adds new functions to `Transaction` that allow creating edits that might potentially overlap. Any change that overlaps previous changes is ignored. Furthermore, a utility method is added that also drops selections associated with dropped changes (for transactions that are created from a selection). This is needed to avoid crashes when applying multicursor autocompletions, as the edit from a previous cursor may overlap with the next cursor/edit.
This commit is contained in:
parent
cdec933523
commit
2b64a64d7e
@ -1,3 +1,5 @@
|
|||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{Range, Rope, Selection, Tendril};
|
use crate::{Range, Rope, Selection, Tendril};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
@ -466,6 +468,33 @@ pub fn with_selection(mut self, selection: Selection) -> Self {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a transaction from a set of potentially overlapping changes. The `change_ranges`
|
||||||
|
/// iterator yield the range (of removed text) in the old document for each edit. If any change
|
||||||
|
/// overlaps with a range overlaps with a previous range then that range is ignored.
|
||||||
|
///
|
||||||
|
/// The `process_change` callback is called for each edit that is not ignored (in the order
|
||||||
|
/// yielded by `changes`) and should return the new text that the associated range will be
|
||||||
|
/// replaced with.
|
||||||
|
///
|
||||||
|
/// To make this function more flexible the iterator can yield additional data for each change
|
||||||
|
/// that is passed to `process_change`
|
||||||
|
pub fn change_ignore_overlapping<T>(
|
||||||
|
doc: &Rope,
|
||||||
|
change_ranges: impl Iterator<Item = (usize, usize, T)>,
|
||||||
|
mut process_change: impl FnMut(usize, usize, T) -> Option<Tendril>,
|
||||||
|
) -> Self {
|
||||||
|
let mut last = 0;
|
||||||
|
let changes = change_ranges.filter_map(|(from, to, data)| {
|
||||||
|
if from < last {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let tendril = process_change(from, to, data);
|
||||||
|
last = to;
|
||||||
|
Some((from, to, tendril))
|
||||||
|
});
|
||||||
|
Self::change(doc, changes)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate a transaction from a set of changes.
|
/// Generate a transaction from a set of changes.
|
||||||
pub fn change<I>(doc: &Rope, changes: I) -> Self
|
pub fn change<I>(doc: &Rope, changes: I) -> Self
|
||||||
where
|
where
|
||||||
@ -513,6 +542,44 @@ pub fn change_by_selection<F>(doc: &Rope, selection: &Selection, f: F) -> Self
|
|||||||
Self::change(doc, selection.iter().map(f))
|
Self::change(doc, selection.iter().map(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn change_by_selection_ignore_overlapping(
|
||||||
|
doc: &Rope,
|
||||||
|
selection: &Selection,
|
||||||
|
mut change_range: impl FnMut(&Range) -> (usize, usize),
|
||||||
|
mut create_tendril: impl FnMut(usize, usize) -> Option<Tendril>,
|
||||||
|
) -> (Transaction, Selection) {
|
||||||
|
let mut last_selection_idx = None;
|
||||||
|
let mut new_primary_idx = None;
|
||||||
|
let mut ranges: SmallVec<[Range; 1]> = SmallVec::new();
|
||||||
|
let process_change = |change_start, change_end, (idx, range): (usize, &Range)| {
|
||||||
|
// update the primary idx
|
||||||
|
if idx == selection.primary_index() {
|
||||||
|
new_primary_idx = Some(idx);
|
||||||
|
} else if new_primary_idx.is_none() {
|
||||||
|
if idx > selection.primary_index() {
|
||||||
|
new_primary_idx = last_selection_idx;
|
||||||
|
} else {
|
||||||
|
last_selection_idx = Some(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ranges.push(*range);
|
||||||
|
create_tendril(change_start, change_end)
|
||||||
|
};
|
||||||
|
let transaction = Self::change_ignore_overlapping(
|
||||||
|
doc,
|
||||||
|
selection.iter().enumerate().map(|range| {
|
||||||
|
let (change_start, change_end) = change_range(range.1);
|
||||||
|
(change_start, change_end, range)
|
||||||
|
}),
|
||||||
|
process_change,
|
||||||
|
);
|
||||||
|
|
||||||
|
(
|
||||||
|
transaction,
|
||||||
|
Selection::new(ranges, new_primary_idx.unwrap_or(0)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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| {
|
||||||
|
Loading…
Reference in New Issue
Block a user