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