on master help sign in
Add `Increment` trait
c9641fcJason Rodney Hansen
helix-core/src/date.rs → helix-core/src/increment/date.rsrenamed+23−14
@@ -9,6 +9,8 @@ use crate::{Range, Tendril};
9
9
10use chrono::{Datelike, Duration, NaiveDate};
10use chrono::{Datelike, Duration, NaiveDate};
11
11
12+use super::Increment;
13+
12fn ndays_in_month(year: i32, month: u32) -> u32 {
14fn ndays_in_month(year: i32, month: u32) -> u32 {
13 // The first day of the next month...
15 // The first day of the next month...
14 let (y, m) = if month == 12 {
16 let (y, m) = if month == 12 {
@@ -29,6 +31,7 @@ fn add_days(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
29fn add_months(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
31fn add_months(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
30 let month = date.month0() as i64 + amount;
32 let month = date.month0() as i64 + amount;
31 let year = date.year() + i32::try_from(month / 12).ok()?;
33 let year = date.year() + i32::try_from(month / 12).ok()?;
34+ let year = if month.is_negative() { year - 1 } else { year };
32
35
33 // Normalize month
36 // Normalize month
34 let month = month % 12;
37 let month = month % 12;
@@ -45,7 +48,6 @@ fn add_months(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
45
48
46fn add_years(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
49fn add_years(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
47 let year = i32::try_from(date.year() as i64 + amount).ok()?;
50 let year = i32::try_from(date.year() as i64 + amount).ok()?;
48−
49 let ndays = ndays_in_month(year, date.month());
51 let ndays = ndays_in_month(year, date.month());
50
52
51 if date.day() > ndays {
53 if date.day() > ndays {
@@ -85,9 +87,8 @@ enum DateField {
85
87
86#[derive(Debug, PartialEq, Eq)]
88#[derive(Debug, PartialEq, Eq)]
87pub struct DateIncrementor {
89pub struct DateIncrementor {
88− pub date: NaiveDate,
90+ date: NaiveDate,
89− pub range: Range,
91+ range: Range,
90−
91 field: DateField,
92 field: DateField,
92 format: Format,
93 format: Format,
93}
94}
@@ -150,8 +151,10 @@ impl DateIncrementor {
150 })
151 })
151 })
152 })
152 }
153 }
154+}
153
155
156+impl Increment for DateIncrementor {
154− pub fn incremented_text(&self, amount: i64) -> Tendril {
157+ fn increment(&self, amount: i64) -> (Range, Tendril) {
155 let date = match self.field {
158 let date = match self.field {
156 DateField::Year => add_years(self.date, amount),
159 DateField::Year => add_years(self.date, amount),
157 DateField::Month => add_months(self.date, amount),
160 DateField::Month => add_months(self.date, amount),
@@ -159,15 +162,18 @@ impl DateIncrementor {
159 }
162 }
160 .unwrap_or(self.date);
163 .unwrap_or(self.date);
161
164
165+ (
166+ self.range,
162− format!(
167+ format!(
163− "{:04}{}{:02}{}{:02}",
168+ "{:04}{}{:02}{}{:02}",
164− date.year(),
169+ date.year(),
165− self.format.separator,
170+ self.format.separator,
166− date.month(),
171+ date.month(),
167− self.format.separator,
172+ self.format.separator,
168− date.day()
173+ date.day()
174+ )
175+ .into(),
169 )
176 )
170− .into()
171 }
177 }
172}
178}
173
179
@@ -437,6 +443,8 @@ mod test {
437 ("2020-02-29", 0, 1, "2021-03-01"),
443 ("2020-02-29", 0, 1, "2021-03-01"),
438 ("2020-01-31", 5, 1, "2020-02-29"),
444 ("2020-01-31", 5, 1, "2020-02-29"),
439 ("2020-01-20", 5, 1, "2020-02-20"),
445 ("2020-01-20", 5, 1, "2020-02-20"),
446+ ("2021-01-01", 5, -1, "2020-12-01"),
447+ ("2021-01-31", 5, -2, "2020-11-30"),
440 ("2020-02-28", 8, 1, "2020-02-29"),
448 ("2020-02-28", 8, 1, "2020-02-29"),
441 ("2021-02-28", 8, 1, "2021-03-01"),
449 ("2021-02-28", 8, 1, "2021-03-01"),
442 ("2021-02-28", 0, -1, "2020-02-28"),
450 ("2021-02-28", 0, -1, "2020-02-28"),
@@ -457,7 +465,8 @@ mod test {
457 assert_eq!(
465 assert_eq!(
458 DateIncrementor::from_range(rope.slice(..), range)
466 DateIncrementor::from_range(rope.slice(..), range)
459 .unwrap()
467 .unwrap()
460− .incremented_text(amount),
468+ .increment(amount)
469+ .1,
461 expected.into()
470 expected.into()
462 );
471 );
463 }
472 }
helix-core/src/increment/mod.rsnew file+8
@@ -0,0 +1,8 @@
1+pub mod date;
2+pub mod number;
3+
4+use crate::{Range, Tendril};
5+
6+pub trait Increment {
7+ fn increment(&self, amount: i64) -> (Range, Tendril);
8+}
helix-core/src/numbers.rs → helix-core/src/increment/number.rsrenamed+19−11
@@ -2,6 +2,8 @@ use std::borrow::Cow;
2
2
3use ropey::RopeSlice;
3use ropey::RopeSlice;
4
4
5+use super::Increment;
6+
5use crate::{
7use crate::{
6 textobject::{textobject_word, TextObject},
8 textobject::{textobject_word, TextObject},
7 Range, Tendril,
9 Range, Tendril,
@@ -9,9 +11,9 @@ use crate::{
9
11
10#[derive(Debug, PartialEq, Eq)]
12#[derive(Debug, PartialEq, Eq)]
11pub struct NumberIncrementor<'a> {
13pub struct NumberIncrementor<'a> {
12− pub range: Range,
13− pub value: i64,
14+ value: i64,
14− pub radix: u32,
15+ radix: u32,
16+ range: Range,
15
17
16 text: RopeSlice<'a>,
18 text: RopeSlice<'a>,
17}
19}
@@ -71,9 +73,10 @@ impl<'a> NumberIncrementor<'a> {
71 text,
73 text,
72 })
74 })
73 }
75 }
76+}
74
77
75− /// Add `amount` to the number and return the formatted text.
78+impl<'a> Increment for NumberIncrementor<'a> {
76− pub fn incremented_text(&self, amount: i64) -> Tendril {
79+ fn increment(&self, amount: i64) -> (Range, Tendril) {
77 let old_text: Cow<str> = self.text.slice(self.range.from()..self.range.to()).into();
80 let old_text: Cow<str> = self.text.slice(self.range.from()..self.range.to()).into();
78 let old_length = old_text.len();
81 let old_length = old_text.len();
79 let new_value = self.value.wrapping_add(amount);
82 let new_value = self.value.wrapping_add(amount);
@@ -144,7 +147,7 @@ impl<'a> NumberIncrementor<'a> {
144 }
147 }
145 }
148 }
146
149
147− new_text.into()
150+ (self.range, new_text.into())
148 }
151 }
149}
152}
150
153
@@ -366,7 +369,8 @@ mod test {
366 assert_eq!(
369 assert_eq!(
367 NumberIncrementor::from_range(rope.slice(..), range)
370 NumberIncrementor::from_range(rope.slice(..), range)
368 .unwrap()
371 .unwrap()
369− .incremented_text(amount),
372+ .increment(amount)
373+ .1,
370 expected.into()
374 expected.into()
371 );
375 );
372 }
376 }
@@ -392,7 +396,8 @@ mod test {
392 assert_eq!(
396 assert_eq!(
393 NumberIncrementor::from_range(rope.slice(..), range)
397 NumberIncrementor::from_range(rope.slice(..), range)
394 .unwrap()
398 .unwrap()
395− .incremented_text(amount),
399+ .increment(amount)
400+ .1,
396 expected.into()
401 expected.into()
397 );
402 );
398 }
403 }
@@ -419,7 +424,8 @@ mod test {
419 assert_eq!(
424 assert_eq!(
420 NumberIncrementor::from_range(rope.slice(..), range)
425 NumberIncrementor::from_range(rope.slice(..), range)
421 .unwrap()
426 .unwrap()
422− .incremented_text(amount),
427+ .increment(amount)
428+ .1,
423 expected.into()
429 expected.into()
424 );
430 );
425 }
431 }
@@ -464,7 +470,8 @@ mod test {
464 assert_eq!(
470 assert_eq!(
465 NumberIncrementor::from_range(rope.slice(..), range)
471 NumberIncrementor::from_range(rope.slice(..), range)
466 .unwrap()
472 .unwrap()
467− .incremented_text(amount),
473+ .increment(amount)
474+ .1,
468 expected.into()
475 expected.into()
469 );
476 );
470 }
477 }
@@ -491,7 +498,8 @@ mod test {
491 assert_eq!(
498 assert_eq!(
492 NumberIncrementor::from_range(rope.slice(..), range)
499 NumberIncrementor::from_range(rope.slice(..), range)
493 .unwrap()
500 .unwrap()
494− .incremented_text(amount),
501+ .increment(amount)
502+ .1,
495 expected.into()
503 expected.into()
496 );
504 );
497 }
505 }
helix-core/src/lib.rs+1−2
@@ -1,17 +1,16 @@
1pub mod auto_pairs;
1pub mod auto_pairs;
2pub mod chars;
2pub mod chars;
3pub mod comment;
3pub mod comment;
4−pub mod date;
5pub mod diagnostic;
4pub mod diagnostic;
6pub mod diff;
5pub mod diff;
7pub mod graphemes;
6pub mod graphemes;
8pub mod history;
7pub mod history;
8+pub mod increment;
9pub mod indent;
9pub mod indent;
10pub mod line_ending;
10pub mod line_ending;
11pub mod macros;
11pub mod macros;
12pub mod match_brackets;
12pub mod match_brackets;
13pub mod movement;
13pub mod movement;
14−pub mod numbers;
15pub mod object;
14pub mod object;
16pub mod path;
15pub mod path;
17mod position;
16mod position;
helix-term/src/commands.rs+12−18
@@ -1,14 +1,13 @@
1use helix_core::{
1use helix_core::{
2− comment, coords_at_pos,
3− date::DateIncrementor,
4− find_first_non_whitespace_char, find_root, graphemes,
2+ comment, coords_at_pos, find_first_non_whitespace_char, find_root, graphemes,
5 history::UndoKind,
3 history::UndoKind,
4+ increment::date::DateIncrementor,
5+ increment::{number::NumberIncrementor, Increment},
6 indent,
6 indent,
7 indent::IndentStyle,
7 indent::IndentStyle,
8 line_ending::{get_line_ending_of_str, line_end_char_index, str_is_line_ending},
8 line_ending::{get_line_ending_of_str, line_end_char_index, str_is_line_ending},
9 match_brackets,
9 match_brackets,
10 movement::{self, Direction},
10 movement::{self, Direction},
11− numbers::NumberIncrementor,
12 object, pos_at_coords,
11 object, pos_at_coords,
13 regex::{self, Regex, RegexBuilder},
12 regex::{self, Regex, RegexBuilder},
14 search, selection, surround, textobject,
13 search, selection, surround, textobject,
@@ -5804,23 +5803,18 @@ fn increment_impl(cx: &mut Context, amount: i64) {
5804 let text = doc.text();
5803 let text = doc.text();
5805
5804
5806 let changes = selection.ranges().iter().filter_map(|range| {
5805 let changes = selection.ranges().iter().filter_map(|range| {
5806+ let incrementor: Option<Box<dyn Increment>> = if let Some(incrementor) =
5807− if let Some(incrementor) = DateIncrementor::from_range(text.slice(..), *range) {
5807+ DateIncrementor::from_range(text.slice(..), *range)
5808− let new_text = incrementor.incremented_text(amount);
5808+ {
5809− Some((
5810− incrementor.range.from(),
5811− incrementor.range.to(),
5809+ Some(Box::new(incrementor))
5812− Some(new_text),
5813− ))
5814 } else if let Some(incrementor) = NumberIncrementor::from_range(text.slice(..), *range) {
5810 } else if let Some(incrementor) = NumberIncrementor::from_range(text.slice(..), *range) {
5815− let new_text = incrementor.incremented_text(amount);
5816− Some((
5817− incrementor.range.from(),
5818− incrementor.range.to(),
5811+ Some(Box::new(incrementor))
5819− Some(new_text),
5820− ))
5821 } else {
5812 } else {
5822 None
5813 None
5823− }
5814+ };
5815+
5816+ let (range, new_text) = incrementor?.increment(amount);
5817+ Some((range.from(), range.to(), Some(new_text)))
5824 });
5818 });
5825
5819
5826 if changes.clone().count() > 0 {
5820 if changes.clone().count() > 0 {