documents/proposal/
slices.rs1use super::{Slice, SliceKind};
18
19pub const ALIGN_CELLS_MAX: usize = 1 << 22;
23
24#[derive(Debug, Clone, PartialEq, Eq, Default)]
27pub(super) struct Piece {
28 lead: String,
29 text: String,
30 trail: String,
31}
32
33impl Piece {
34 fn push(&mut self, character: char) {
35 if !character.is_whitespace() {
36 self.text.push_str(&self.trail);
37 self.trail.clear();
38 self.text.push(character);
39 } else if self.text.is_empty() {
40 self.lead.push(character);
41 } else {
42 self.trail.push(character);
43 }
44 }
45
46 fn is_blank(&self) -> bool {
47 self.text.is_empty()
48 }
49
50 fn into_slice(self, kind: SliceKind) -> Slice {
51 Slice {
52 kind,
53 text: self.text,
54 lead: self.lead,
55 trail: self.trail,
56 }
57 }
58}
59
60pub(super) fn pieces(text: &str) -> Vec<Piece> {
67 let mut found = Vec::new();
68 let mut building = Piece::default();
69 let mut characters = text.chars().peekable();
70 while let Some(character) = characters.next() {
71 building.push(character);
72 if is_terminator(character) {
73 while let Some(&next) = characters.peek() {
74 if !is_terminator(next) && !is_closer(next) {
75 break;
76 }
77 building.push(next);
78 characters.next();
79 }
80 found.push(std::mem::take(&mut building));
81 }
82 }
83 if building != Piece::default() {
84 match found.last_mut() {
85 Some(last) if building.is_blank() => last.trail.push_str(&building.lead),
86 Some(_) | None => found.push(building),
87 }
88 }
89 found
90}
91
92fn is_terminator(character: char) -> bool {
93 matches!(character, '。' | '!' | '?' | '…' | '!' | '?' | '.')
94}
95
96fn is_closer(character: char) -> bool {
97 matches!(
98 character,
99 '"' | '\'' | '\u{201d}' | '\u{2019}' | ')' | ')' | '」' | '』'
100 )
101}
102
103pub(super) fn align(before: Vec<Piece>, after: Vec<Piece>) -> Vec<Slice> {
106 let head = before
107 .iter()
108 .zip(after.iter())
109 .take_while(|(one, other)| one == other)
110 .count();
111 let tail = before
112 .iter()
113 .skip(head)
114 .rev()
115 .zip(after.iter().skip(head).rev())
116 .take_while(|(one, other)| one == other)
117 .count();
118 let middle = |pieces: &[Piece]| -> Vec<Piece> {
119 pieces
120 .iter()
121 .skip(head)
122 .take(pieces.len().saturating_sub(head).saturating_sub(tail))
123 .cloned()
124 .collect()
125 };
126 let (gone, come) = (middle(&before), middle(&after));
127 let mut slices: Vec<Slice> = before
128 .iter()
129 .take(head)
130 .cloned()
131 .map(|piece| piece.into_slice(SliceKind::Same))
132 .collect();
133 slices.extend(middle_slices(gone, come));
134 slices.extend(
135 before
136 .into_iter()
137 .skip(head)
138 .rev()
139 .take(tail)
140 .rev()
141 .map(|piece| piece.into_slice(SliceKind::Same)),
142 );
143 slices
144}
145
146fn middle_slices(gone: Vec<Piece>, come: Vec<Piece>) -> Vec<Slice> {
149 let Some(table) = Table::of(&gone, &come) else {
150 return gone
151 .into_iter()
152 .map(|piece| piece.into_slice(SliceKind::Delete))
153 .chain(
154 come.into_iter()
155 .map(|piece| piece.into_slice(SliceKind::Insert)),
156 )
157 .collect();
158 };
159 let mut slices = Vec::with_capacity(gone.len().saturating_add(come.len()));
160 let mut gone = gone.into_iter().enumerate().peekable();
161 let mut come = come.into_iter().enumerate().peekable();
162 while let (Some((left, one)), Some((right, other))) = (gone.peek(), come.peek()) {
163 if one == other {
164 slices.push(one.clone().into_slice(SliceKind::Same));
165 gone.next();
166 come.next();
167 } else if table.at(left.saturating_add(1), *right)
168 >= table.at(*left, right.saturating_add(1))
169 {
170 slices.push(one.clone().into_slice(SliceKind::Delete));
171 gone.next();
172 } else {
173 slices.push(other.clone().into_slice(SliceKind::Insert));
174 come.next();
175 }
176 }
177 slices.extend(gone.map(|(_, piece)| piece.into_slice(SliceKind::Delete)));
178 slices.extend(come.map(|(_, piece)| piece.into_slice(SliceKind::Insert)));
179 slices
180}
181
182struct Table {
185 cells: Vec<u32>,
186 width: usize,
187}
188
189impl Table {
190 fn of(gone: &[Piece], come: &[Piece]) -> Option<Table> {
193 let width = come.len();
194 let size = gone.len().checked_mul(width)?;
195 if size > ALIGN_CELLS_MAX {
196 return None;
197 }
198 let mut table = Table {
199 cells: vec![0; size],
200 width,
201 };
202 for (left, one) in gone.iter().enumerate().rev() {
203 for (right, other) in come.iter().enumerate().rev() {
204 let value = if one == other {
205 table
206 .at(left.saturating_add(1), right.saturating_add(1))
207 .saturating_add(1)
208 } else {
209 table
210 .at(left.saturating_add(1), right)
211 .max(table.at(left, right.saturating_add(1)))
212 };
213 if let Some(cell) = table
214 .cell(left, right)
215 .and_then(|at| table.cells.get_mut(at))
216 {
217 *cell = value;
218 }
219 }
220 }
221 Some(table)
222 }
223
224 fn at(&self, left: usize, right: usize) -> u32 {
228 self.cell(left, right)
229 .and_then(|at| self.cells.get(at))
230 .copied()
231 .unwrap_or(0)
232 }
233
234 fn cell(&self, left: usize, right: usize) -> Option<usize> {
235 (right < self.width)
236 .then(|| left.checked_mul(self.width)?.checked_add(right))
237 .flatten()
238 }
239}