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