@@ -4773,6 +4773,8 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
47734773 let mut ranges = SmallVec::with_capacity(selection.len());
47744774 let text = doc.text().slice(..);
47754775
4776+ let mut offset = 0isize;
4777+
47764778 for range in selection.ranges() {
47774779 let fragment = range.slice(text);
47784780 let (output, success) = match shell_impl(shell, cmd, pipe.then(|| fragment.into())) {
@@ -4788,13 +4790,23 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
47884790 return;
47894791 }
47904792
4791− let (from, to) = match behavior {
4792− ShellBehavior::Replace => (range.from(), range.to()),
4793− ShellBehavior::Insert => (range.from(), range.from()),
4794− ShellBehavior::Append => (range.to(), range.to()),
4795− _ => (range.from(), range.from()),
4793+ let output_len = output.chars().count();
4794+
4795+ let (from, to, deleted_len) = match behavior {
4796+ ShellBehavior::Replace => (range.from(), range.to(), range.len()),
4797+ ShellBehavior::Insert => (range.from(), range.from(), 0),
4798+ ShellBehavior::Append => (range.to(), range.to(), 0),
4799+ _ => (range.from(), range.from(), 0),
47964800 };
4797− ranges.push(Range::new(to, to + output.chars().count()));
4801+
4802+ // These `usize`s cannot underflow because selection ranges cannot overlap.
4803+ // Once the MSRV is 1.66.0 (mixed_integer_ops is stabilized), we can use checked
4804+ // arithmetic to assert this.
4805+ let anchor = (to as isize + offset - deleted_len as isize) as usize;
4806+ let new_range = Range::new(anchor, anchor + output_len).with_direction(range.direction());
4807+ ranges.push(new_range);
4808+ offset = offset + output_len as isize - deleted_len as isize;
4809+
47984810 changes.push((from, to, Some(output)));
47994811 }
48004812