1use std::collections::HashMap;
15use std::ops::Range;
16
17use serde::{Deserialize, Serialize};
18
19use crate::changeset::{Bias, ChangeSet, Edit};
20use crate::history::EditKind;
21use crate::position::{ByteOffset, VisualCol, byte_to_visual_col, visual_col_to_byte};
22use crate::resolve::Resolved;
23use crate::selection::{Selection, SelectionSet};
24use crate::text::Document;
25
26#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
28pub enum ColumnInsert {
29 Text(String),
31 Numbers(NumberSequence),
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(default)]
39pub struct NumberSequence {
40 pub initial: i64,
41 pub increase: i64,
42 pub repeat: usize,
44 pub leading: Leading,
45 pub format: NumberFormat,
46}
47
48impl Default for NumberSequence {
49 fn default() -> Self {
50 Self {
51 initial: 1,
52 increase: 1,
53 repeat: 1,
54 leading: Leading::None,
55 format: NumberFormat::Dec,
56 }
57 }
58}
59
60#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
62pub enum Leading {
63 #[default]
64 None,
65 Zeros,
67 Spaces,
69}
70
71#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
72pub enum NumberFormat {
73 #[default]
74 Dec,
75 Hex,
77 Oct,
78 Bin,
79}
80
81impl NumberSequence {
82 pub fn render(&self, count: usize) -> Vec<String> {
85 let repeat = self.repeat.max(1);
86 let parts: Vec<(bool, String)> = (0..count)
87 .map(|i| {
88 let steps = i64::try_from(i / repeat).unwrap_or(i64::MAX);
89 let value = self
90 .initial
91 .saturating_add(steps.saturating_mul(self.increase));
92 (value < 0, digits(value.unsigned_abs(), self.format))
93 })
94 .collect();
95 let digits_width = parts.iter().map(|(_, d)| d.len()).max().unwrap_or(0);
96 let full_width = parts
97 .iter()
98 .map(|(neg, d)| d.len() + usize::from(*neg))
99 .max()
100 .unwrap_or(0);
101 parts
102 .into_iter()
103 .map(|(neg, d)| {
104 let sign = if neg { "-" } else { "" };
105 match self.leading {
106 Leading::None => format!("{sign}{d}"),
107 Leading::Zeros => format!("{sign}{d:0>digits_width$}"),
108 Leading::Spaces => format!("{:>full_width$}", format!("{sign}{d}")),
109 }
110 })
111 .collect()
112 }
113}
114
115fn digits(n: u64, format: NumberFormat) -> String {
116 match format {
117 NumberFormat::Dec => n.to_string(),
118 NumberFormat::Hex => format!("{n:X}"),
119 NumberFormat::Oct => format!("{n:o}"),
120 NumberFormat::Bin => format!("{n:b}"),
121 }
122}
123
124impl ColumnInsert {
125 pub fn items(&self, count: usize) -> Vec<String> {
127 match self {
128 ColumnInsert::Text(text) => vec![text.clone(); count],
129 ColumnInsert::Numbers(seq) => seq.render(count),
130 }
131 }
132}
133
134const MAX_PADDING: usize = 16 << 20;
137
138struct Target {
141 range: Range<ByteOffset>,
142 pad: usize,
143}
144
145fn at_column(doc: &Document, line: usize, col: VisualCol, tab_width: usize) -> Target {
148 let end = doc.line_end(line);
149 let width = byte_to_visual_col(doc, end, tab_width).0;
150 if width < col.0 {
151 Target {
152 range: end..end,
153 pad: col.0 - width,
154 }
155 } else {
156 let at = visual_col_to_byte(doc, line, col, tab_width);
157 Target {
158 range: at..at,
159 pad: 0,
160 }
161 }
162}
163
164pub(crate) fn insert(
166 doc: &Document,
167 sels: &SelectionSet,
168 what: &ColumnInsert,
169 tab_width: usize,
170) -> Resolved {
171 if matches!(what, ColumnInsert::Text(t) if t.is_empty()) {
172 return Resolved::Selections(sels.clone());
173 }
174 let many = sels.rect().is_some() || sels.len() > 1;
175 let targets: Vec<Target> = if let Some(r) = sels.rect() {
176 let on_line: HashMap<usize, &Selection> = sels
179 .ranges()
180 .iter()
181 .map(|s| (doc.line_of(s.start()), s))
182 .collect();
183 (r.top()..=r.bottom().min(doc.line_count() - 1))
184 .map(|line| match on_line.get(&line) {
185 Some(s) if byte_to_visual_col(doc, s.start(), tab_width) >= r.left() => Target {
186 range: s.range(),
187 pad: 0,
188 },
189 _ => at_column(doc, line, r.left(), tab_width),
190 })
191 .collect()
192 } else if sels.len() > 1 {
193 sels.ranges()
194 .iter()
195 .map(|s| Target {
196 range: s.range(),
197 pad: 0,
198 })
199 .collect()
200 } else {
201 let s = sels.primary();
202 let first = doc.line_of(s.start());
203 let last = if s.is_empty() {
204 let last = doc.line_count() - 1;
207 if last > first && doc.line_start(last) == doc.line_end(last) {
208 last - 1
209 } else {
210 last
211 }
212 } else {
213 let last = doc.line_of(s.end());
214 if last > first && s.end() == doc.line_start(last) {
215 last - 1
216 } else {
217 last
218 }
219 };
220 let col = byte_to_visual_col(doc, s.start(), tab_width);
221 (first..=last)
222 .map(|line| at_column(doc, line, col, tab_width))
223 .collect()
224 };
225 if targets.iter().map(|t| t.pad).sum::<usize>() > MAX_PADDING {
228 return Resolved::Selections(sels.clone());
229 }
230 let items = what.items(targets.len());
231 let edits: Vec<Edit> = targets
232 .iter()
233 .zip(&items)
234 .map(|(t, item)| {
235 Edit::new(
236 t.range.start,
237 t.range.end,
238 format!("{}{item}", " ".repeat(t.pad)),
239 )
240 })
241 .collect();
242 let changes = ChangeSet::new(doc.len(), edits);
243 if changes.is_empty() {
244 return Resolved::Selections(sels.clone());
245 }
246 let after_item = |t: &Target| Selection::caret(changes.map_position(t.range.end, Bias::After));
249 let after = if many {
250 SelectionSet::new(
251 targets.iter().map(after_item).collect(),
252 sels.primary_index(),
253 )
254 } else {
255 SelectionSet::single(after_item(&targets[0]))
256 };
257 Resolved::Edit {
258 changes,
259 after,
260 kind: EditKind::Other,
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 fn seq(f: impl FnOnce(&mut NumberSequence)) -> NumberSequence {
269 let mut s = NumberSequence::default();
270 f(&mut s);
271 s
272 }
273
274 #[test]
275 fn numbers_count_repeat_and_pad() {
276 assert_eq!(seq(|_| {}).render(3), ["1", "2", "3"]);
277 assert_eq!(
278 seq(|s| {
279 s.initial = 0;
280 s.increase = 5;
281 s.repeat = 2;
282 })
283 .render(5),
284 ["0", "0", "5", "5", "10"]
285 );
286 assert_eq!(
287 seq(|s| {
288 s.initial = 8;
289 s.leading = Leading::Zeros;
290 })
291 .render(3),
292 ["08", "09", "10"]
293 );
294 assert_eq!(
295 seq(|s| {
296 s.initial = 9;
297 s.leading = Leading::Spaces;
298 })
299 .render(2),
300 [" 9", "10"]
301 );
302 }
303
304 #[test]
305 fn number_formats_and_negative_numbers() {
306 let hex = seq(|s| {
307 s.initial = 9;
308 s.format = NumberFormat::Hex;
309 });
310 assert_eq!(hex.render(3), ["9", "A", "B"]);
311 let bin = seq(|s| {
312 s.initial = 1;
313 s.format = NumberFormat::Bin;
314 s.leading = Leading::Zeros;
315 });
316 assert_eq!(bin.render(3), ["01", "10", "11"]);
317 let oct = seq(|s| {
318 s.initial = 7;
319 s.format = NumberFormat::Oct;
320 });
321 assert_eq!(oct.render(2), ["7", "10"]);
322 let down = seq(|s| {
323 s.initial = 1;
324 s.increase = -1;
325 s.leading = Leading::Zeros;
326 });
327 assert_eq!(down.render(3), ["1", "0", "-1"]);
328 let spaced = seq(|s| {
329 s.initial = 1;
330 s.increase = -10;
331 s.leading = Leading::Spaces;
332 });
333 assert_eq!(spaced.render(2), [" 1", "-9"]);
334 let edge = seq(|s| {
336 s.initial = i64::MAX - 1;
337 s.repeat = 0;
338 });
339 assert_eq!(
340 edge.render(3),
341 [
342 (i64::MAX - 1).to_string(),
343 i64::MAX.to_string(),
344 i64::MAX.to_string()
345 ]
346 );
347 }
348}