brk_string_wizard/magic_string/
movement.rs

1use crate::MagicString;
2
3use super::update::UpdateOptions;
4
5impl MagicString<'_> {
6  pub fn remove(&mut self, start: usize, end: usize) -> Result<&mut Self, String> {
7    self.inner_update_with(
8      start,
9      end,
10      "".into(),
11      UpdateOptions { keep_original: false, overwrite: true },
12      false,
13    )
14  }
15
16  /// Moves the characters from start and end to index. Returns this.
17  // `move` is reserved keyword in Rust, so we use `relocate` instead.
18  pub fn relocate(&mut self, start: usize, end: usize, to: usize) -> Result<&mut Self, String> {
19    if to >= start && to <= end {
20      return Err("Cannot move a selection inside itself".to_string());
21    }
22
23    self.split_at(start)?;
24    self.split_at(end)?;
25    self.split_at(to)?;
26
27    let first_idx = self.chunk_by_start[&start];
28    let last_idx = self.chunk_by_end[&end];
29
30    let old_left_idx = self.chunks[first_idx].prev;
31    let old_right_idx = self.chunks[last_idx].next;
32
33    let new_right_idx = self.chunk_by_start.get(&to).copied();
34
35    // `new_right_idx` is `None` means that the `to` index is at the end of the string.
36    // Moving chunks which contain the last chunk to the end is meaningless.
37    if new_right_idx.is_none() && last_idx == self.last_chunk_idx {
38      return Ok(self);
39    }
40
41    let new_left_idx = new_right_idx
42      .map(|idx| self.chunks[idx].prev)
43      // If the `to` index is at the end of the string, then the `new_right_idx` will be `None`.
44      // In this case, we want to use the last chunk as the left chunk to connect the relocated chunk.
45      .unwrap_or(Some(self.last_chunk_idx));
46
47    // Adjust next/prev pointers, this remove the [start, end] range from the old position
48    if let Some(old_left_idx) = old_left_idx {
49      self.chunks[old_left_idx].next = old_right_idx;
50    }
51    if let Some(old_right_idx) = old_right_idx {
52      self.chunks[old_right_idx].prev = old_left_idx;
53    }
54
55    if let Some(new_left_idx) = new_left_idx {
56      self.chunks[new_left_idx].next = Some(first_idx);
57    }
58    if let Some(new_right_idx) = new_right_idx {
59      self.chunks[new_right_idx].prev = Some(last_idx);
60    }
61
62    if self.chunks[first_idx].prev.is_none() {
63      // If the `first_idx` is the first chunk, then we need to update the `first_chunk_idx`.
64      self.first_chunk_idx = self.chunks[last_idx].next.unwrap();
65    }
66    if self.chunks[last_idx].next.is_none() {
67      // If the `last_idx` is the last chunk, then we need to update the `last_chunk_idx`.
68      self.last_chunk_idx = self.chunks[first_idx].prev.unwrap();
69      self.chunks[last_idx].next = None;
70    }
71
72    if new_left_idx.is_none() {
73      self.first_chunk_idx = first_idx;
74    }
75    if new_right_idx.is_none() {
76      self.last_chunk_idx = last_idx;
77    }
78
79    self.chunks[first_idx].prev = new_left_idx;
80    self.chunks[last_idx].next = new_right_idx;
81
82    Ok(self)
83  }
84
85  /// Returns a clone with content outside the specified range removed.
86  /// This is equivalent to `clone().remove(0, start).remove(end, original.len())`.
87  pub fn snip(&self, start: usize, end: usize) -> Result<Self, String> {
88    let mut clone = self.clone();
89    if start > 0 {
90      clone.remove(0, start)?;
91    }
92    let original_len = self.source.len();
93    if end < original_len {
94      clone.remove(end, original_len)?;
95    }
96    Ok(clone)
97  }
98}