vim: Fix increment/decrement for negative numbers without gaps (#58327)
Finally continuing from PR #35200: I adapted the fix to the changed find_target() logic and added more tests for version strings like `0.82.46`. This is a different but similar fix compared to #47356 for helix mode - not sure if this can and should be unified? Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) (should not affect UI at all) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #35193 Release Notes: - Fixed vim's increment (`ctrl-a`) and decrement (`ctrl-x`) commands skipping the number under the cursor in dotted strings like version numbers (e.g. `0.81.46`) and hyphened date strings (e.g. `2015-02-01`) --------- Co-authored-by: Stefan Bethge <kjyv@users.noreply.github.com> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: dino <dinojoaocosta@gmail.com>
This commit is contained in:
3 files changed
+53
-8
No files matched your search
@@ -227,6 +227,15 @@ fn find_target(
|
||||
break;
|
||||
}
|
||||
|
||||
// vim's ctrl-a/ctrl-x operate on the number at or after the cursor and
|
||||
// do not require it to be whitespace-separated. Stop the backward scan
|
||||
// at a '-' so we keep the number the cursor is on (e.g. `05` in
|
||||
// `2025-05-10`) instead of scanning past the '-' to an earlier number on
|
||||
// the line. vim folds a leading '-' into the number, making it negative.
|
||||
if ch == '-' {
|
||||
break;
|
||||
}
|
||||
|
||||
// Avoid the influence of hexadecimal letters
|
||||
if first_char_is_num
|
||||
&& !ch.is_ascii_hexdigit()
|
||||
@@ -284,17 +293,17 @@ fn find_target(
|
||||
begin = None;
|
||||
target = String::new();
|
||||
} else if ch == '.' {
|
||||
// When the cursor is on a number followed by a dot and a non-digit
|
||||
// (`ˇ1. item`), terminate the match so the number is incrementable.
|
||||
// Without this, the dot unconditionally resets the scan and the
|
||||
// number is skipped. We only do this when the cursor is on the
|
||||
// number, when it's past (`111.ˇ.2`), we still reset so the forward
|
||||
// scan can find the number after the dots.
|
||||
let next_is_non_digit = chars.peek().map_or(true, |char| !char.is_digit(radix));
|
||||
// vim treats '.' as a separator, not a decimal point: ctrl-a/ctrl-x
|
||||
// act on the whole digit run, not a float. So when the cursor is on
|
||||
// the current number, terminate the match at the dot regardless of
|
||||
// what follows it, so `ˇ1. item` and version strings like `0.8ˇ1.46`
|
||||
// (-> `0.82.46`) both increment the number under the cursor. When the
|
||||
// cursor is past the number (`111.ˇ.2`), `on_number` is false and we
|
||||
// still reset so the forward scan finds the number after the dots.
|
||||
let on_number =
|
||||
is_num && begin.is_some_and(|begin| begin >= start_offset || start_offset < offset);
|
||||
|
||||
if on_number && next_is_non_digit {
|
||||
if on_number {
|
||||
end = Some(offset);
|
||||
break;
|
||||
}
|
||||
@@ -475,6 +484,11 @@ mod test {
|
||||
cx.shared_state().await.assert_eq(indoc! {"
|
||||
1.ˇ2
|
||||
"});
|
||||
|
||||
// '.' is a separator, not a decimal point, so the number the cursor is
|
||||
// on is incremented even without surrounding whitespace.
|
||||
cx.simulate("ctrl-a", "0.8ˇ1.46").await.assert_matches();
|
||||
cx.simulate("ctrl-x", "0.8ˇ1.46").await.assert_matches();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@@ -778,6 +792,22 @@ mod test {
|
||||
30"});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_increment_negative_numbers(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
|
||||
// vim folds a leading '-' into the number, so ctrl-a on the `05` here
|
||||
// operates on `-05` and decrements the visible digits to `04`.
|
||||
cx.simulate("ctrl-a", "2025-0ˇ5-10").await.assert_matches();
|
||||
|
||||
// Cursor on or just before a trailing '-' (with or without a following
|
||||
// number) must not scan past the '-' into the earlier number.
|
||||
cx.simulate("ctrl-a", "2025-05ˇ-").await.assert_matches();
|
||||
cx.simulate("ctrl-a", "2025-05ˇ- 345")
|
||||
.await
|
||||
.assert_matches();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_increment_toggle(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{"Put":{"state":"2025-0ˇ5-10"}}
|
||||
{"Key":"ctrl-a"}
|
||||
{"Get":{"state":"2025-0ˇ4-10","mode":"Normal"}}
|
||||
{"Put":{"state":"2025-05ˇ-"}}
|
||||
{"Key":"ctrl-a"}
|
||||
{"Get":{"state":"2025-05ˇ-","mode":"Normal"}}
|
||||
{"Put":{"state":"2025-05ˇ- 345"}}
|
||||
{"Key":"ctrl-a"}
|
||||
{"Get":{"state":"2025-05- 34ˇ6","mode":"Normal"}}
|
||||
@@ -3,3 +3,9 @@
|
||||
{"Get":{"state":"1.ˇ3\n","mode":"Normal"}}
|
||||
{"Key":"ctrl-x"}
|
||||
{"Get":{"state":"1.ˇ2\n","mode":"Normal"}}
|
||||
{"Put":{"state":"0.8ˇ1.46"}}
|
||||
{"Key":"ctrl-a"}
|
||||
{"Get":{"state":"0.8ˇ2.46","mode":"Normal"}}
|
||||
{"Put":{"state":"0.8ˇ1.46"}}
|
||||
{"Key":"ctrl-x"}
|
||||
{"Get":{"state":"0.8ˇ0.46","mode":"Normal"}}
|
||||
Reference in new issue
Block a user