normal.rs

   1mod change;
   2mod delete;
   3
   4use crate::{
   5    motion::Motion,
   6    state::{Mode, Operator},
   7    Vim,
   8};
   9use change::init as change_init;
  10use collections::HashSet;
  11use editor::{Bias, DisplayPoint};
  12use gpui::{actions, MutableAppContext, ViewContext};
  13use language::SelectionGoal;
  14use workspace::Workspace;
  15
  16use self::{change::change_over, delete::delete_over};
  17
  18actions!(
  19    vim,
  20    [
  21        InsertAfter,
  22        InsertFirstNonWhitespace,
  23        InsertEndOfLine,
  24        InsertLineAbove,
  25        InsertLineBelow,
  26        DeleteLeft,
  27        DeleteRight,
  28        ChangeToEndOfLine,
  29        DeleteToEndOfLine,
  30    ]
  31);
  32
  33pub fn init(cx: &mut MutableAppContext) {
  34    cx.add_action(insert_after);
  35    cx.add_action(insert_first_non_whitespace);
  36    cx.add_action(insert_end_of_line);
  37    cx.add_action(insert_line_above);
  38    cx.add_action(insert_line_below);
  39    cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
  40        Vim::update(cx, |vim, cx| {
  41            delete_over(vim, Motion::Left, cx);
  42        })
  43    });
  44    cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| {
  45        Vim::update(cx, |vim, cx| {
  46            delete_over(vim, Motion::Right, cx);
  47        })
  48    });
  49    cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| {
  50        Vim::update(cx, |vim, cx| {
  51            change_over(vim, Motion::EndOfLine, cx);
  52        })
  53    });
  54    cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| {
  55        Vim::update(cx, |vim, cx| {
  56            delete_over(vim, Motion::EndOfLine, cx);
  57        })
  58    });
  59
  60    change_init(cx);
  61}
  62
  63pub fn normal_motion(motion: Motion, cx: &mut MutableAppContext) {
  64    Vim::update(cx, |vim, cx| {
  65        match vim.state.operator_stack.pop() {
  66            None => move_cursor(vim, motion, cx),
  67            Some(Operator::Change) => change_over(vim, motion, cx),
  68            Some(Operator::Delete) => delete_over(vim, motion, cx),
  69            Some(Operator::Namespace(_)) => {
  70                // Can't do anything for a namespace operator. Ignoring
  71            }
  72        }
  73        vim.clear_operator(cx);
  74    });
  75}
  76
  77fn move_cursor(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
  78    vim.update_active_editor(cx, |editor, cx| {
  79        editor.move_cursors(cx, |map, cursor, goal| motion.move_point(map, cursor, goal))
  80    });
  81}
  82
  83fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspace>) {
  84    Vim::update(cx, |vim, cx| {
  85        vim.switch_mode(Mode::Insert, cx);
  86        vim.update_active_editor(cx, |editor, cx| {
  87            editor.move_cursors(cx, |map, cursor, goal| {
  88                Motion::Right.move_point(map, cursor, goal)
  89            });
  90        });
  91    });
  92}
  93
  94fn insert_first_non_whitespace(
  95    _: &mut Workspace,
  96    _: &InsertFirstNonWhitespace,
  97    cx: &mut ViewContext<Workspace>,
  98) {
  99    Vim::update(cx, |vim, cx| {
 100        vim.switch_mode(Mode::Insert, cx);
 101        vim.update_active_editor(cx, |editor, cx| {
 102            editor.move_cursors(cx, |map, cursor, goal| {
 103                Motion::FirstNonWhitespace.move_point(map, cursor, goal)
 104            });
 105        });
 106    });
 107}
 108
 109fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewContext<Workspace>) {
 110    Vim::update(cx, |vim, cx| {
 111        vim.switch_mode(Mode::Insert, cx);
 112        vim.update_active_editor(cx, |editor, cx| {
 113            editor.move_cursors(cx, |map, cursor, goal| {
 114                Motion::EndOfLine.move_point(map, cursor, goal)
 115            });
 116        });
 117    });
 118}
 119
 120fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContext<Workspace>) {
 121    Vim::update(cx, |vim, cx| {
 122        vim.switch_mode(Mode::Insert, cx);
 123        vim.update_active_editor(cx, |editor, cx| {
 124            editor.transact(cx, |editor, cx| {
 125                let (map, old_selections) = editor.display_selections(cx);
 126                let selection_start_rows: HashSet<u32> = old_selections
 127                    .into_iter()
 128                    .map(|selection| selection.start.row())
 129                    .collect();
 130                let edits = selection_start_rows.into_iter().map(|row| {
 131                    let (indent, _) = map.line_indent(row);
 132                    let start_of_line = map
 133                        .clip_point(DisplayPoint::new(row, 0), Bias::Left)
 134                        .to_point(&map);
 135                    let mut new_text = " ".repeat(indent as usize);
 136                    new_text.push('\n');
 137                    (start_of_line..start_of_line, new_text)
 138                });
 139                editor.edit_with_autoindent(edits, cx);
 140                editor.move_cursors(cx, |map, mut cursor, _| {
 141                    *cursor.row_mut() -= 1;
 142                    *cursor.column_mut() = map.line_len(cursor.row());
 143                    (map.clip_point(cursor, Bias::Left), SelectionGoal::None)
 144                });
 145            });
 146        });
 147    });
 148}
 149
 150fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContext<Workspace>) {
 151    Vim::update(cx, |vim, cx| {
 152        vim.switch_mode(Mode::Insert, cx);
 153        vim.update_active_editor(cx, |editor, cx| {
 154            editor.transact(cx, |editor, cx| {
 155                let (map, old_selections) = editor.display_selections(cx);
 156                let selection_end_rows: HashSet<u32> = old_selections
 157                    .into_iter()
 158                    .map(|selection| selection.end.row())
 159                    .collect();
 160                let edits = selection_end_rows.into_iter().map(|row| {
 161                    let (indent, _) = map.line_indent(row);
 162                    let end_of_line = map
 163                        .clip_point(DisplayPoint::new(row, map.line_len(row)), Bias::Left)
 164                        .to_point(&map);
 165                    let mut new_text = "\n".to_string();
 166                    new_text.push_str(&" ".repeat(indent as usize));
 167                    (end_of_line..end_of_line, new_text)
 168                });
 169                editor.move_cursors(cx, |map, cursor, goal| {
 170                    Motion::EndOfLine.move_point(map, cursor, goal)
 171                });
 172                editor.edit_with_autoindent(edits, cx);
 173            });
 174        });
 175    });
 176}
 177
 178#[cfg(test)]
 179mod test {
 180    use indoc::indoc;
 181    use language::Selection;
 182    use util::test::marked_text;
 183
 184    use crate::{
 185        state::{
 186            Mode::{self, *},
 187            Namespace, Operator,
 188        },
 189        vim_test_context::VimTestContext,
 190    };
 191
 192    #[gpui::test]
 193    async fn test_h(cx: &mut gpui::TestAppContext) {
 194        let cx = VimTestContext::new(cx, true).await;
 195        let mut cx = cx.binding(["h"]);
 196        cx.assert("The q|uick", "The |quick");
 197        cx.assert("|The quick", "|The quick");
 198        cx.assert(
 199            indoc! {"
 200                The quick
 201                |brown"},
 202            indoc! {"
 203                The quick
 204                |brown"},
 205        );
 206    }
 207
 208    #[gpui::test]
 209    async fn test_backspace(cx: &mut gpui::TestAppContext) {
 210        let cx = VimTestContext::new(cx, true).await;
 211        let mut cx = cx.binding(["backspace"]);
 212        cx.assert("The q|uick", "The |quick");
 213        cx.assert("|The quick", "|The quick");
 214        cx.assert(
 215            indoc! {"
 216                The quick
 217                |brown"},
 218            indoc! {"
 219                The quick
 220                |brown"},
 221        );
 222    }
 223
 224    #[gpui::test]
 225    async fn test_j(cx: &mut gpui::TestAppContext) {
 226        let cx = VimTestContext::new(cx, true).await;
 227        let mut cx = cx.binding(["j"]);
 228        cx.assert(
 229            indoc! {"
 230                The |quick
 231                brown fox"},
 232            indoc! {"
 233                The quick
 234                brow|n fox"},
 235        );
 236        cx.assert(
 237            indoc! {"
 238                The quick
 239                brow|n fox"},
 240            indoc! {"
 241                The quick
 242                brow|n fox"},
 243        );
 244        cx.assert(
 245            indoc! {"
 246                The quic|k
 247                brown"},
 248            indoc! {"
 249                The quick
 250                brow|n"},
 251        );
 252        cx.assert(
 253            indoc! {"
 254                The quick
 255                |brown"},
 256            indoc! {"
 257                The quick
 258                |brown"},
 259        );
 260    }
 261
 262    #[gpui::test]
 263    async fn test_k(cx: &mut gpui::TestAppContext) {
 264        let cx = VimTestContext::new(cx, true).await;
 265        let mut cx = cx.binding(["k"]);
 266        cx.assert(
 267            indoc! {"
 268                The |quick
 269                brown fox"},
 270            indoc! {"
 271                The |quick
 272                brown fox"},
 273        );
 274        cx.assert(
 275            indoc! {"
 276                The quick
 277                brow|n fox"},
 278            indoc! {"
 279                The |quick
 280                brown fox"},
 281        );
 282        cx.assert(
 283            indoc! {"
 284                The
 285                quic|k"},
 286            indoc! {"
 287                Th|e
 288                quick"},
 289        );
 290    }
 291
 292    #[gpui::test]
 293    async fn test_l(cx: &mut gpui::TestAppContext) {
 294        let cx = VimTestContext::new(cx, true).await;
 295        let mut cx = cx.binding(["l"]);
 296        cx.assert("The q|uick", "The qu|ick");
 297        cx.assert("The quic|k", "The quic|k");
 298        cx.assert(
 299            indoc! {"
 300                The quic|k
 301                brown"},
 302            indoc! {"
 303                The quic|k
 304                brown"},
 305        );
 306    }
 307
 308    #[gpui::test]
 309    async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
 310        let cx = VimTestContext::new(cx, true).await;
 311        let mut cx = cx.binding(["shift-$"]);
 312        cx.assert("T|est test", "Test tes|t");
 313        cx.assert("Test tes|t", "Test tes|t");
 314        cx.assert(
 315            indoc! {"
 316                The |quick
 317                brown"},
 318            indoc! {"
 319                The quic|k
 320                brown"},
 321        );
 322        cx.assert(
 323            indoc! {"
 324                The quic|k
 325                brown"},
 326            indoc! {"
 327                The quic|k
 328                brown"},
 329        );
 330
 331        let mut cx = cx.binding(["0"]);
 332        cx.assert("Test |test", "|Test test");
 333        cx.assert("|Test test", "|Test test");
 334        cx.assert(
 335            indoc! {"
 336                The |quick
 337                brown"},
 338            indoc! {"
 339                |The quick
 340                brown"},
 341        );
 342        cx.assert(
 343            indoc! {"
 344                |The quick
 345                brown"},
 346            indoc! {"
 347                |The quick
 348                brown"},
 349        );
 350    }
 351
 352    #[gpui::test]
 353    async fn test_jump_to_end(cx: &mut gpui::TestAppContext) {
 354        let cx = VimTestContext::new(cx, true).await;
 355        let mut cx = cx.binding(["shift-G"]);
 356
 357        cx.assert(
 358            indoc! {"
 359                The |quick
 360                
 361                brown fox jumps
 362                over the lazy dog"},
 363            indoc! {"
 364                The quick
 365                
 366                brown fox jumps
 367                over| the lazy dog"},
 368        );
 369        cx.assert(
 370            indoc! {"
 371                The quick
 372                
 373                brown fox jumps
 374                over| the lazy dog"},
 375            indoc! {"
 376                The quick
 377                
 378                brown fox jumps
 379                over| the lazy dog"},
 380        );
 381        cx.assert(
 382            indoc! {"
 383            The qui|ck
 384            
 385            brown"},
 386            indoc! {"
 387            The quick
 388            
 389            brow|n"},
 390        );
 391        cx.assert(
 392            indoc! {"
 393            The qui|ck
 394            
 395            "},
 396            indoc! {"
 397            The quick
 398            
 399            |"},
 400        );
 401    }
 402
 403    #[gpui::test]
 404    async fn test_w(cx: &mut gpui::TestAppContext) {
 405        let mut cx = VimTestContext::new(cx, true).await;
 406        let (_, cursor_offsets) = marked_text(indoc! {"
 407            The |quick|-|brown
 408            |
 409            |
 410            |fox_jumps |over
 411            |th||e"});
 412        cx.set_state(
 413            indoc! {"
 414            |The quick-brown
 415            
 416            
 417            fox_jumps over
 418            the"},
 419            Mode::Normal,
 420        );
 421
 422        for cursor_offset in cursor_offsets {
 423            cx.simulate_keystroke("w");
 424            cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
 425        }
 426
 427        // Reset and test ignoring punctuation
 428        let (_, cursor_offsets) = marked_text(indoc! {"
 429            The |quick-brown
 430            |
 431            |
 432            |fox_jumps |over
 433            |th||e"});
 434        cx.set_state(
 435            indoc! {"
 436            |The quick-brown
 437            
 438            
 439            fox_jumps over
 440            the"},
 441            Mode::Normal,
 442        );
 443
 444        for cursor_offset in cursor_offsets {
 445            cx.simulate_keystroke("shift-W");
 446            cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
 447        }
 448    }
 449
 450    #[gpui::test]
 451    async fn test_e(cx: &mut gpui::TestAppContext) {
 452        let mut cx = VimTestContext::new(cx, true).await;
 453        let (_, cursor_offsets) = marked_text(indoc! {"
 454            Th|e quic|k|-brow|n
 455            
 456            
 457            fox_jump|s ove|r
 458            th|e"});
 459        cx.set_state(
 460            indoc! {"
 461            |The quick-brown
 462            
 463            
 464            fox_jumps over
 465            the"},
 466            Mode::Normal,
 467        );
 468
 469        for cursor_offset in cursor_offsets {
 470            cx.simulate_keystroke("e");
 471            cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
 472        }
 473
 474        // Reset and test ignoring punctuation
 475        let (_, cursor_offsets) = marked_text(indoc! {"
 476            Th|e quick-brow|n
 477            
 478            
 479            fox_jump|s ove|r
 480            th||e"});
 481        cx.set_state(
 482            indoc! {"
 483            |The quick-brown
 484            
 485            
 486            fox_jumps over
 487            the"},
 488            Mode::Normal,
 489        );
 490        for cursor_offset in cursor_offsets {
 491            cx.simulate_keystroke("shift-E");
 492            cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
 493        }
 494    }
 495
 496    #[gpui::test]
 497    async fn test_b(cx: &mut gpui::TestAppContext) {
 498        let mut cx = VimTestContext::new(cx, true).await;
 499        let (_, cursor_offsets) = marked_text(indoc! {"
 500            ||The |quick|-|brown
 501            |
 502            |
 503            |fox_jumps |over
 504            |the"});
 505        cx.set_state(
 506            indoc! {"
 507            The quick-brown
 508            
 509            
 510            fox_jumps over
 511            th|e"},
 512            Mode::Normal,
 513        );
 514
 515        for cursor_offset in cursor_offsets.into_iter().rev() {
 516            cx.simulate_keystroke("b");
 517            cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
 518        }
 519
 520        // Reset and test ignoring punctuation
 521        let (_, cursor_offsets) = marked_text(indoc! {"
 522            ||The |quick-brown
 523            |
 524            |
 525            |fox_jumps |over
 526            |the"});
 527        cx.set_state(
 528            indoc! {"
 529            The quick-brown
 530            
 531            
 532            fox_jumps over
 533            th|e"},
 534            Mode::Normal,
 535        );
 536        for cursor_offset in cursor_offsets.into_iter().rev() {
 537            cx.simulate_keystroke("shift-B");
 538            cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
 539        }
 540    }
 541
 542    #[gpui::test]
 543    async fn test_g_prefix_and_abort(cx: &mut gpui::TestAppContext) {
 544        let mut cx = VimTestContext::new(cx, true).await;
 545
 546        // Can abort with escape to get back to normal mode
 547        cx.simulate_keystroke("g");
 548        assert_eq!(cx.mode(), Normal);
 549        assert_eq!(
 550            cx.active_operator(),
 551            Some(Operator::Namespace(Namespace::G))
 552        );
 553        cx.simulate_keystroke("escape");
 554        assert_eq!(cx.mode(), Normal);
 555        assert_eq!(cx.active_operator(), None);
 556    }
 557
 558    #[gpui::test]
 559    async fn test_gg(cx: &mut gpui::TestAppContext) {
 560        let cx = VimTestContext::new(cx, true).await;
 561        let mut cx = cx.binding(["g", "g"]);
 562        cx.assert(
 563            indoc! {"
 564                The quick
 565            
 566                brown fox jumps
 567                over |the lazy dog"},
 568            indoc! {"
 569                The q|uick
 570            
 571                brown fox jumps
 572                over the lazy dog"},
 573        );
 574        cx.assert(
 575            indoc! {"
 576                The q|uick
 577            
 578                brown fox jumps
 579                over the lazy dog"},
 580            indoc! {"
 581                The q|uick
 582            
 583                brown fox jumps
 584                over the lazy dog"},
 585        );
 586        cx.assert(
 587            indoc! {"
 588                The quick
 589            
 590                brown fox jumps
 591                over the la|zy dog"},
 592            indoc! {"
 593                The quic|k
 594            
 595                brown fox jumps
 596                over the lazy dog"},
 597        );
 598        cx.assert(
 599            indoc! {"
 600                
 601            
 602                brown fox jumps
 603                over the la|zy dog"},
 604            indoc! {"
 605                |
 606            
 607                brown fox jumps
 608                over the lazy dog"},
 609        );
 610    }
 611
 612    #[gpui::test]
 613    async fn test_a(cx: &mut gpui::TestAppContext) {
 614        let cx = VimTestContext::new(cx, true).await;
 615        let mut cx = cx.binding(["a"]).mode_after(Mode::Insert);
 616
 617        cx.assert("The q|uick", "The qu|ick");
 618        cx.assert("The quic|k", "The quick|");
 619    }
 620
 621    #[gpui::test]
 622    async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) {
 623        let cx = VimTestContext::new(cx, true).await;
 624        let mut cx = cx.binding(["shift-A"]).mode_after(Mode::Insert);
 625        cx.assert("The q|uick", "The quick|");
 626        cx.assert("The q|uick ", "The quick |");
 627        cx.assert("|", "|");
 628        cx.assert(
 629            indoc! {"
 630                The q|uick
 631                brown fox"},
 632            indoc! {"
 633                The quick|
 634                brown fox"},
 635        );
 636        cx.assert(
 637            indoc! {"
 638                |
 639                The quick"},
 640            indoc! {"
 641                |
 642                The quick"},
 643        );
 644    }
 645
 646    #[gpui::test]
 647    async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) {
 648        let cx = VimTestContext::new(cx, true).await;
 649        let mut cx = cx.binding(["shift-^"]);
 650        cx.assert("The q|uick", "|The quick");
 651        cx.assert(" The q|uick", " |The quick");
 652        cx.assert("|", "|");
 653        cx.assert(
 654            indoc! {"
 655                The q|uick
 656                brown fox"},
 657            indoc! {"
 658                |The quick
 659                brown fox"},
 660        );
 661        cx.assert(
 662            indoc! {"
 663                |
 664                The quick"},
 665            indoc! {"
 666                |
 667                The quick"},
 668        );
 669        cx.assert(
 670            indoc! {"
 671                    |
 672                The quick"},
 673            indoc! {"
 674                    |
 675                The quick"},
 676        );
 677    }
 678
 679    #[gpui::test]
 680    async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
 681        let cx = VimTestContext::new(cx, true).await;
 682        let mut cx = cx.binding(["shift-I"]).mode_after(Mode::Insert);
 683        cx.assert("The q|uick", "|The quick");
 684        cx.assert(" The q|uick", " |The quick");
 685        cx.assert("|", "|");
 686        cx.assert(
 687            indoc! {"
 688                The q|uick
 689                brown fox"},
 690            indoc! {"
 691                |The quick
 692                brown fox"},
 693        );
 694        cx.assert(
 695            indoc! {"
 696                |
 697                The quick"},
 698            indoc! {"
 699                |
 700                The quick"},
 701        );
 702    }
 703
 704    #[gpui::test]
 705    async fn test_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
 706        let cx = VimTestContext::new(cx, true).await;
 707        let mut cx = cx.binding(["shift-D"]);
 708        cx.assert(
 709            indoc! {"
 710                The q|uick
 711                brown fox"},
 712            indoc! {"
 713                The |q
 714                brown fox"},
 715        );
 716        cx.assert(
 717            indoc! {"
 718                The quick
 719                |
 720                brown fox"},
 721            indoc! {"
 722                The quick
 723                |
 724                brown fox"},
 725        );
 726    }
 727
 728    #[gpui::test]
 729    async fn test_x(cx: &mut gpui::TestAppContext) {
 730        let cx = VimTestContext::new(cx, true).await;
 731        let mut cx = cx.binding(["x"]);
 732        cx.assert("|Test", "|est");
 733        cx.assert("Te|st", "Te|t");
 734        cx.assert("Tes|t", "Te|s");
 735        cx.assert(
 736            indoc! {"
 737                Tes|t
 738                test"},
 739            indoc! {"
 740                Te|s
 741                test"},
 742        );
 743    }
 744
 745    #[gpui::test]
 746    async fn test_delete_left(cx: &mut gpui::TestAppContext) {
 747        let cx = VimTestContext::new(cx, true).await;
 748        let mut cx = cx.binding(["shift-X"]);
 749        cx.assert("Te|st", "T|st");
 750        cx.assert("T|est", "|est");
 751        cx.assert("|Test", "|Test");
 752        cx.assert(
 753            indoc! {"
 754                Test
 755                |test"},
 756            indoc! {"
 757                Test
 758                |test"},
 759        );
 760    }
 761
 762    #[gpui::test]
 763    async fn test_o(cx: &mut gpui::TestAppContext) {
 764        let cx = VimTestContext::new(cx, true).await;
 765        let mut cx = cx.binding(["o"]).mode_after(Mode::Insert);
 766
 767        cx.assert(
 768            "|",
 769            indoc! {"
 770                
 771                |"},
 772        );
 773        cx.assert(
 774            "The |quick",
 775            indoc! {"
 776                The quick
 777                |"},
 778        );
 779        cx.assert(
 780            indoc! {"
 781                The quick
 782                brown |fox
 783                jumps over"},
 784            indoc! {"
 785                The quick
 786                brown fox
 787                |
 788                jumps over"},
 789        );
 790        cx.assert(
 791            indoc! {"
 792                The quick
 793                brown fox
 794                jumps |over"},
 795            indoc! {"
 796                The quick
 797                brown fox
 798                jumps over
 799                |"},
 800        );
 801        cx.assert(
 802            indoc! {"
 803                The q|uick
 804                brown fox
 805                jumps over"},
 806            indoc! {"
 807                The quick
 808                |
 809                brown fox
 810                jumps over"},
 811        );
 812        cx.assert(
 813            indoc! {"
 814                The quick
 815                |
 816                brown fox"},
 817            indoc! {"
 818                The quick
 819                
 820                |
 821                brown fox"},
 822        );
 823        cx.assert(
 824            indoc! {"
 825                fn test()
 826                    println!(|);"},
 827            indoc! {"
 828                fn test()
 829                    println!();
 830                    |"},
 831        );
 832        cx.assert(
 833            indoc! {"
 834                fn test(|)
 835                    println!();"},
 836            indoc! {"
 837                fn test()
 838                |
 839                    println!();"},
 840        );
 841    }
 842
 843    #[gpui::test]
 844    async fn test_insert_line_above(cx: &mut gpui::TestAppContext) {
 845        let cx = VimTestContext::new(cx, true).await;
 846        let mut cx = cx.binding(["shift-O"]).mode_after(Mode::Insert);
 847
 848        cx.assert(
 849            "|",
 850            indoc! {"
 851                |
 852                "},
 853        );
 854        cx.assert(
 855            "The |quick",
 856            indoc! {"
 857                |
 858                The quick"},
 859        );
 860        cx.assert(
 861            indoc! {"
 862                The quick
 863                brown |fox
 864                jumps over"},
 865            indoc! {"
 866                The quick
 867                |
 868                brown fox
 869                jumps over"},
 870        );
 871        cx.assert(
 872            indoc! {"
 873                The quick
 874                brown fox
 875                jumps |over"},
 876            indoc! {"
 877                The quick
 878                brown fox
 879                |
 880                jumps over"},
 881        );
 882        cx.assert(
 883            indoc! {"
 884                The q|uick
 885                brown fox
 886                jumps over"},
 887            indoc! {"
 888                |
 889                The quick
 890                brown fox
 891                jumps over"},
 892        );
 893        cx.assert(
 894            indoc! {"
 895                The quick
 896                |
 897                brown fox"},
 898            indoc! {"
 899                The quick
 900                |
 901                
 902                brown fox"},
 903        );
 904        cx.assert(
 905            indoc! {"
 906                fn test()
 907                    println!(|);"},
 908            indoc! {"
 909                fn test()
 910                    |
 911                    println!();"},
 912        );
 913        cx.assert(
 914            indoc! {"
 915                fn test(|)
 916                    println!();"},
 917            indoc! {"
 918                |
 919                fn test()
 920                    println!();"},
 921        );
 922    }
 923
 924    #[gpui::test]
 925    async fn test_dd(cx: &mut gpui::TestAppContext) {
 926        let cx = VimTestContext::new(cx, true).await;
 927        let mut cx = cx.binding(["d", "d"]);
 928
 929        cx.assert("|", "|");
 930        cx.assert("The |quick", "|");
 931        cx.assert(
 932            indoc! {"
 933                The quick
 934                brown |fox
 935                jumps over"},
 936            indoc! {"
 937                The quick
 938                jumps |over"},
 939        );
 940        cx.assert(
 941            indoc! {"
 942                The quick
 943                brown fox
 944                jumps |over"},
 945            indoc! {"
 946                The quick
 947                brown |fox"},
 948        );
 949        cx.assert(
 950            indoc! {"
 951                The q|uick
 952                brown fox
 953                jumps over"},
 954            indoc! {"
 955                brown| fox
 956                jumps over"},
 957        );
 958        cx.assert(
 959            indoc! {"
 960                The quick
 961                |
 962                brown fox"},
 963            indoc! {"
 964                The quick
 965                |brown fox"},
 966        );
 967    }
 968
 969    #[gpui::test]
 970    async fn test_cc(cx: &mut gpui::TestAppContext) {
 971        let cx = VimTestContext::new(cx, true).await;
 972        let mut cx = cx.binding(["c", "c"]).mode_after(Mode::Insert);
 973
 974        cx.assert("|", "|");
 975        cx.assert("The |quick", "|");
 976        cx.assert(
 977            indoc! {"
 978                The quick
 979                brown |fox
 980                jumps over"},
 981            indoc! {"
 982                The quick
 983                |
 984                jumps over"},
 985        );
 986        cx.assert(
 987            indoc! {"
 988                The quick
 989                brown fox
 990                jumps |over"},
 991            indoc! {"
 992                The quick
 993                brown fox
 994                |"},
 995        );
 996        cx.assert(
 997            indoc! {"
 998                The q|uick
 999                brown fox
1000                jumps over"},
1001            indoc! {"
1002                |
1003                brown fox
1004                jumps over"},
1005        );
1006        cx.assert(
1007            indoc! {"
1008                The quick
1009                |
1010                brown fox"},
1011            indoc! {"
1012                The quick
1013                |
1014                brown fox"},
1015        );
1016    }
1017}