Skip to content

Separate jump behavior from increment/decrement #4123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,45 @@ use crate::{
};

#[derive(Debug, PartialEq, Eq)]
pub struct NumberIncrementor<'a> {
pub struct IntegerIncrementor<'a> {
value: i64,
radix: u32,
range: Range,

text: RopeSlice<'a>,
}

impl<'a> NumberIncrementor<'a> {
/// Return information about number under rang if there is one.
pub fn from_range(text: RopeSlice, range: Range) -> Option<NumberIncrementor> {
// If the cursor is on the minus sign of a number we want to get the word textobject to the
// right of it.
impl<'a> IntegerIncrementor<'a> {
/// Return information about an integer under range if there is one.
/// Integer includes possible negative sign, digits in bases 2, 8, 10, or 16 and underscores.
/// Integer does not include decimal point or trailing underscores
/// Examples:
/// -1, 99, 0xABCD, 0x1010, 0o1234567, 1_000_000
pub fn from_range(text: RopeSlice, range: Range) -> Option<IntegerIncrementor> {
// If the cursor is on the minus sign of an integer we want to get the word textobject
// to the right of it.
let range = if range.to() < text.len_chars()
&& range.to() - range.from() <= 1
&& range.len() <= 1
&& text.char(range.from()) == '-'
{
Range::new(range.from() + 1, range.to() + 1)
} else {
range
};

let range = textobject_word(text, range, TextObject::Inside, 1, false);
let mut range = textobject_word(text, range, TextObject::Inside, 1, false);

// If there is a minus sign to the left of the word object, we want to include it in the range.
let range = if range.from() > 0 && text.char(range.from() - 1) == '-' {
range.extend(range.from() - 1, range.from())
} else {
range
if range.from() > 0 && text.char(range.from() - 1) == '-' {
range = range.extend(range.from() - 1, range.from())
};

// If there are trailing underscores, remove them from the range.
// There is a `- 1` here because range ends are exclusive.
while !range.is_empty() && text.char(range.to() - 1) == '_' {
range = range.shorten()
}

let word: String = text
.slice(range.from()..range.to())
.chars()
Expand All @@ -66,7 +74,7 @@ impl<'a> NumberIncrementor<'a> {
}

let value = value as i64;
Some(NumberIncrementor {
Some(IntegerIncrementor {
range,
value,
radix,
Expand All @@ -75,7 +83,7 @@ impl<'a> NumberIncrementor<'a> {
}
}

impl<'a> Increment for NumberIncrementor<'a> {
impl<'a> Increment for IntegerIncrementor<'a> {
fn increment(&self, amount: i64) -> (Range, Tendril) {
let old_text: Cow<str> = self.text.slice(self.range.from()..self.range.to()).into();
let old_length = old_text.len();
Expand Down Expand Up @@ -161,8 +169,8 @@ mod test {
let rope = Rope::from_str("Test text 12345 more text.");
let range = Range::point(12);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(10, 15),
value: 12345,
radix: 10,
Expand All @@ -176,8 +184,8 @@ mod test {
let rope = Rope::from_str("Test text 0x123ABCDEF more text.");
let range = Range::point(12);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(10, 21),
value: 0x123ABCDEF,
radix: 16,
Expand All @@ -191,8 +199,8 @@ mod test {
let rope = Rope::from_str("Test text 0xfa3b4e more text.");
let range = Range::point(12);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(10, 18),
value: 0xfa3b4e,
radix: 16,
Expand All @@ -206,8 +214,8 @@ mod test {
let rope = Rope::from_str("Test text 0o1074312 more text.");
let range = Range::point(12);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(10, 19),
value: 0o1074312,
radix: 8,
Expand All @@ -221,8 +229,8 @@ mod test {
let rope = Rope::from_str("Test text 0b10111010010101 more text.");
let range = Range::point(12);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(10, 26),
value: 0b10111010010101,
radix: 2,
Expand All @@ -236,8 +244,8 @@ mod test {
let rope = Rope::from_str("Test text -54321 more text.");
let range = Range::point(12);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(10, 16),
value: -54321,
radix: 10,
Expand All @@ -251,8 +259,8 @@ mod test {
let rope = Rope::from_str("Test text 000045326 more text.");
let range = Range::point(12);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(10, 19),
value: 45326,
radix: 10,
Expand All @@ -266,8 +274,8 @@ mod test {
let rope = Rope::from_str("Test text -54321 more text.");
let range = Range::point(10);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(10, 16),
value: -54321,
radix: 10,
Expand All @@ -281,8 +289,8 @@ mod test {
let rope = Rope::from_str("100");
let range = Range::point(0);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(0, 3),
value: 100,
radix: 10,
Expand All @@ -296,8 +304,8 @@ mod test {
let rope = Rope::from_str("100");
let range = Range::point(2);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(0, 3),
value: 100,
radix: 10,
Expand All @@ -311,8 +319,8 @@ mod test {
let rope = Rope::from_str(",100;");
let range = Range::point(1);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range),
Some(NumberIncrementor {
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(1, 4),
value: 100,
radix: 10,
Expand All @@ -325,28 +333,28 @@ mod test {
fn test_not_a_number_point() {
let rope = Rope::from_str("Test text 45326 more text.");
let range = Range::point(6);
assert_eq!(NumberIncrementor::from_range(rope.slice(..), range), None);
assert_eq!(IntegerIncrementor::from_range(rope.slice(..), range), None);
}

#[test]
fn test_number_too_large_at_point() {
let rope = Rope::from_str("Test text 0xFFFFFFFFFFFFFFFFF more text.");
let range = Range::point(12);
assert_eq!(NumberIncrementor::from_range(rope.slice(..), range), None);
assert_eq!(IntegerIncrementor::from_range(rope.slice(..), range), None);
}

#[test]
fn test_number_cursor_one_right_of_number() {
let rope = Rope::from_str("100 ");
let range = Range::point(3);
assert_eq!(NumberIncrementor::from_range(rope.slice(..), range), None);
assert_eq!(IntegerIncrementor::from_range(rope.slice(..), range), None);
}

#[test]
fn test_number_cursor_one_left_of_number() {
let rope = Rope::from_str(" 100");
let range = Range::point(0);
assert_eq!(NumberIncrementor::from_range(rope.slice(..), range), None);
assert_eq!(IntegerIncrementor::from_range(rope.slice(..), range), None);
}

#[test]
Expand All @@ -367,7 +375,7 @@ mod test {
let rope = Rope::from_str(original);
let range = Range::point(0);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range)
IntegerIncrementor::from_range(rope.slice(..), range)
.unwrap()
.increment(amount)
.1,
Expand All @@ -394,7 +402,7 @@ mod test {
let rope = Rope::from_str(original);
let range = Range::point(0);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range)
IntegerIncrementor::from_range(rope.slice(..), range)
.unwrap()
.increment(amount)
.1,
Expand Down Expand Up @@ -422,7 +430,7 @@ mod test {
let rope = Rope::from_str(original);
let range = Range::point(0);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range)
IntegerIncrementor::from_range(rope.slice(..), range)
.unwrap()
.increment(amount)
.1,
Expand Down Expand Up @@ -468,7 +476,7 @@ mod test {
let rope = Rope::from_str(original);
let range = Range::point(0);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range)
IntegerIncrementor::from_range(rope.slice(..), range)
.unwrap()
.increment(amount)
.1,
Expand Down Expand Up @@ -496,12 +504,27 @@ mod test {
let rope = Rope::from_str(original);
let range = Range::point(0);
assert_eq!(
NumberIncrementor::from_range(rope.slice(..), range)
IntegerIncrementor::from_range(rope.slice(..), range)
.unwrap()
.increment(amount)
.1,
Tendril::from(expected)
);
}
}

#[test]
fn test_number_trailing_separator_ignored() {
let rope = Rope::from_str("1_000_000_");
let range = Range::point(0);
assert_eq!(
IntegerIncrementor::from_range(rope.slice(..), range),
Some(IntegerIncrementor {
range: Range::new(0, 9),
value: 1000000,
radix: 10,
text: rope.slice(..),
})
);
}
}
2 changes: 1 addition & 1 deletion helix-core/src/increment/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod date_time;
pub mod number;
pub mod integer;

use crate::{Range, Tendril};

Expand Down
18 changes: 18 additions & 0 deletions helix-core/src/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,24 @@ impl Range {
}
}

/// Shorten the range by 1 character.
#[must_use]
pub fn shorten(&self) -> Self {
match self.head.cmp(&self.anchor) {
std::cmp::Ordering::Greater => Self {
anchor: self.anchor,
head: self.head - 1,
horiz: None,
},
std::cmp::Ordering::Less => Self {
anchor: self.anchor - 1,
head: self.head,
horiz: None,
},
std::cmp::Ordering::Equal => *self,
}
}

/// Returns a range that encompasses both input ranges.
///
/// This is like `extend()`, but tries to negotiate the
Expand Down
Loading