@@ -326,7 +326,7 @@ impl ChangeSet {
326 self.changes.is_empty() || self.changes == [Operation::Retain(self.len)]
326 self.changes.is_empty() || self.changes == [Operation::Retain(self.len)]
329− /// Map a *sorted* list of positions through the changes.
329+ /// Map a (mostly) *sorted* list of positions through the changes.
331 /// This is equivalent to updating each position with `map_pos`:
331 /// This is equivalent to updating each position with `map_pos`:
@@ -335,27 +335,63 @@ impl ChangeSet {
335 /// *pos = changes.map_pos(*pos, assoc);
335 /// *pos = changes.map_pos(*pos, assoc);
338− /// However this function is significantly faster running in `O(N+M)` instead of `O(NM)`
338+ /// However this function is significantly faster for sorted lists running
339+ /// in `O(N+M)` instead of `O(NM)`. This function also handles unsorted/
340+ /// partially sorted lists. However, in that case worst case complexity is
341+ /// again `O(MN)`. For lists that are often/mostly sorted (like the end of diagnostic ranges)
342+ /// performance is usally close to `O(N + M)`
339 pub fn update_positions<'a>(&self, positions: impl Iterator<Item = (&'a mut usize, Assoc)>) {
343 pub fn update_positions<'a>(&self, positions: impl Iterator<Item = (&'a mut usize, Assoc)>) {
340 use Operation::*;
344 use Operation::*;
342 let mut positions = positions.peekable();
346 let mut positions = positions.peekable();
346− let Some((pos, assoc)) = positions.peek_mut() else { return; };
347− let Some(new_pos) = $map(**pos, *assoc) else { break; };
354 let mut old_pos = 0;
348 let mut old_pos = 0;
355 let mut new_pos = 0;
349 let mut new_pos = 0;
356− let mut iter = self.changes.iter().peekable();
350+ let mut iter = self.changes.iter().enumerate().peekable();
354+ ($map: expr, $i: expr) => {
356+ let Some((pos, assoc)) = positions.peek_mut() else { return; };
357+ if **pos < old_pos {
358+ // Positions are not sorted, revert to the last Operation that
359+ // contains this position and continue iterating from there.
360+ // We can unwrap here since `pos` can not be negative
361+ // (unsigned integer) and iterating backwards to the start
362+ // should always move us back to the start
363+ for (i, change) in self.changes[..$i].iter().enumerate().rev() {
373+ new_pos -= ins.chars().count();
376+ if old_pos <= **pos {
377+ iter = self.changes[i..].iter().enumerate().peekable();
380+ debug_assert!(old_pos <= **pos, "Reverse Iter across changeset works");
383+ let Some(new_pos) = $map(**pos, *assoc) else { break; };
390+ let Some((i, change)) = iter.next() else {
391+ map!(|pos, _| (old_pos == pos).then_some(new_pos), self.changes.len());
358− while let Some(change) = iter.next() {
359 let len = match change {
395 let len = match change {
360 Delete(i) | Retain(i) => *i,
396 Delete(i) | Retain(i) => *i,
361 Insert(_) => 0,
397 Insert(_) => 0,
@@ -364,42 +400,51 @@ impl ChangeSet {
365 match change {
401 match change {
366 Retain(_) => {
402 Retain(_) => {
367− map!(|pos, _| (old_end > pos).then_some(new_pos + (pos - old_pos)));
404+ |pos, _| (old_end > pos).then_some(new_pos + (pos - old_pos)),
368 new_pos += len;
407 new_pos += len;
370 Delete(_) => {
409 Delete(_) => {
371 // in range
410 // in range
372− map!(|pos, _| (old_end > pos).then_some(new_pos));
411+ map!(|pos, _| (old_end > pos).then_some(new_pos), i);
374 Insert(s) => {
413 Insert(s) => {
375 let ins = s.chars().count();
414 let ins = s.chars().count();
377 // a subsequent delete means a replace, consume it
416 // a subsequent delete means a replace, consume it
378− if let Some(Delete(len)) = iter.peek() {
417+ if let Some((_, Delete(len))) = iter.peek() {
379 iter.next();
418 iter.next();
381 old_end = old_pos + len;
420 old_end = old_pos + len;
382 // in range of replaced text
421 // in range of replaced text
383− map!(|pos, assoc| (old_end > pos).then(|| {
423+ |pos, assoc| (old_end > pos).then(|| {
384− // at point or tracking before
424+ // at point or tracking before
385− if pos == old_pos || assoc == Assoc::Before {
425+ if pos == old_pos || assoc == Assoc::Before {
387− } else {
427+ } else {
388− // place to end of insert
428+ // place to end of insert
389− new_pos + ins
429+ new_pos + ins
393 // at insert point
435 // at insert point
394− map!(|pos, assoc| (old_pos == pos).then(|| {
437+ |pos, assoc| (old_pos == pos).then(|| {
395− // return position before inserted text
438+ // return position before inserted text
396− if assoc == Assoc::Before {
439+ if assoc == Assoc::Before {
398− } else {
441+ } else {
399− // after text
442+ // after text
400− new_pos + ins
443+ new_pos + ins
405 new_pos += ins;
450 new_pos += ins;
@@ -407,7 +452,6 @@ impl ChangeSet {
408 old_pos = old_end;
453 old_pos = old_end;
410− map!(|pos, _| (old_pos == pos).then_some(new_pos));
411 let out_of_bounds: Vec<_> = positions.collect();
455 let out_of_bounds: Vec<_> = positions.collect();
413 panic!("Positions {out_of_bounds:?} are out of range for changeset len {old_pos}!",)
457 panic!("Positions {out_of_bounds:?} are out of range for changeset len {old_pos}!",)
@@ -822,6 +866,20 @@ mod test {
823 assert_eq!(cs.map_pos(2, Assoc::Before), 2);
867 assert_eq!(cs.map_pos(2, Assoc::Before), 2);
824 assert_eq!(cs.map_pos(2, Assoc::After), 2);
868 assert_eq!(cs.map_pos(2, Assoc::After), 2);
869+ // unsorted selection
870+ let cs = ChangeSet {
872+ Insert("ab".into()),
874+ Insert("cd".into()),
880+ let mut positions = [4, 2];
881+ cs.update_positions(positions.iter_mut().map(|pos| (pos, Assoc::After)));
882+ assert_eq!(positions, [4, 2]);