Remove 2 suffix for vim, diagnostics, go_to_line, theme_selector, command_palette, file_finder

Max Brunsfeld and Mikayla created

Co-authored-by: Mikayla <mikayla@zed.dev>

Change summary

Cargo.lock                                                               |  134 
Cargo.toml                                                               |    3 
crates/command_palette/Cargo.toml                                        |   33 
crates/command_palette/src/command_palette.rs                            |  451 
crates/command_palette2/Cargo.toml                                       |   36 
crates/command_palette2/src/command_palette.rs                           |  510 
crates/diagnostics/Cargo.toml                                            |   31 
crates/diagnostics/src/diagnostics.rs                                    |  572 
crates/diagnostics/src/items.rs                                          |  249 
crates/diagnostics/src/project_diagnostics_settings.rs                   |    4 
crates/diagnostics/src/toolbar_controls.rs                               |   98 
crates/diagnostics2/Cargo.toml                                           |   43 
crates/diagnostics2/src/diagnostics.rs                                   | 1612 
crates/diagnostics2/src/items.rs                                         |  187 
crates/diagnostics2/src/project_diagnostics_settings.rs                  |   28 
crates/diagnostics2/src/toolbar_controls.rs                              |   65 
crates/file_finder/Cargo.toml                                            |   32 
crates/file_finder/src/file_finder.rs                                    |  685 
crates/file_finder2/Cargo.toml                                           |   37 
crates/file_finder2/src/file_finder.rs                                   | 1956 
crates/go_to_line/Cargo.toml                                             |   18 
crates/go_to_line/src/go_to_line.rs                                      |  270 
crates/go_to_line2/Cargo.toml                                            |   25 
crates/go_to_line2/src/go_to_line.rs                                     |  188 
crates/vim/Cargo.toml                                                    |   34 
crates/vim/src/command.rs                                                |   20 
crates/vim/src/editor_events.rs                                          |  114 
crates/vim/src/insert.rs                                                 |   44 
crates/vim/src/mode_indicator.rs                                         |   87 
crates/vim/src/motion.rs                                                 |   97 
crates/vim/src/normal.rs                                                 |   50 
crates/vim/src/normal/increment.rs                                       |    8 
crates/vim/src/normal/paste.rs                                           |    6 
crates/vim/src/normal/repeat.rs                                          |  134 
crates/vim/src/normal/scroll.rs                                          |   59 
crates/vim/src/normal/search.rs                                          |   72 
crates/vim/src/normal/substitute.rs                                      |    8 
crates/vim/src/object.rs                                                 |   39 
crates/vim/src/state.rs                                                  |   18 
crates/vim/src/test.rs                                                   |   23 
crates/vim/src/test/neovim_backed_test_context.rs                        |   34 
crates/vim/src/test/neovim_connection.rs                                 |   26 
crates/vim/src/test/vim_test_context.rs                                  |   63 
crates/vim/src/vim.rs                                                    |  171 
crates/vim/src/visual.rs                                                 |   53 
crates/vim2/Cargo.toml                                                   |   53 
crates/vim2/src/command.rs                                               |  434 
crates/vim2/src/editor_events.rs                                         |  104 
crates/vim2/src/insert.rs                                                |  125 
crates/vim2/src/mode_indicator.rs                                        |   74 
crates/vim2/src/motion.rs                                                | 1107 
crates/vim2/src/normal.rs                                                |  910 
crates/vim2/src/normal/case.rs                                           |  116 
crates/vim2/src/normal/change.rs                                         |  502 
crates/vim2/src/normal/delete.rs                                         |  475 
crates/vim2/src/normal/increment.rs                                      |  278 
crates/vim2/src/normal/paste.rs                                          |  476 
crates/vim2/src/normal/repeat.rs                                         |  496 
crates/vim2/src/normal/scroll.rs                                         |  247 
crates/vim2/src/normal/search.rs                                         |  477 
crates/vim2/src/normal/substitute.rs                                     |  276 
crates/vim2/src/normal/yank.rs                                           |   50 
crates/vim2/src/object.rs                                                | 1025 
crates/vim2/src/state.rs                                                 |  234 
crates/vim2/src/test.rs                                                  |  752 
crates/vim2/src/test/neovim_backed_binding_test_context.rs               |   95 
crates/vim2/src/test/neovim_backed_test_context.rs                       |  439 
crates/vim2/src/test/neovim_connection.rs                                |  599 
crates/vim2/src/test/vim_test_context.rs                                 |  177 
crates/vim2/src/utils.rs                                                 |   50 
crates/vim2/src/vim.rs                                                   |  607 
crates/vim2/src/visual.rs                                                | 1034 
crates/vim2/test_data/neovim_backed_test_context_works.json              |    3 
crates/vim2/test_data/test_a.json                                        |    6 
crates/vim2/test_data/test_b.json                                        |   54 
crates/vim2/test_data/test_backspace.json                                |    9 
crates/vim2/test_data/test_capital_f_and_capital_t.json                  |  570 
crates/vim2/test_data/test_cc.json                                       |   24 
crates/vim2/test_data/test_change_0.json                                 |    8 
crates/vim2/test_data/test_change_b.json                                 |   24 
crates/vim2/test_data/test_change_backspace.json                         |   16 
crates/vim2/test_data/test_change_case.json                              |   23 
crates/vim2/test_data/test_change_e.json                                 |   24 
crates/vim2/test_data/test_change_end_of_document.json                   |   16 
crates/vim2/test_data/test_change_end_of_line.json                       |    8 
crates/vim2/test_data/test_change_gg.json                                |   20 
crates/vim2/test_data/test_change_h.json                                 |   16 
crates/vim2/test_data/test_change_j.json                                 |   16 
crates/vim2/test_data/test_change_k.json                                 |   16 
crates/vim2/test_data/test_change_l.json                                 |    8 
crates/vim2/test_data/test_change_sentence_object.json                   |  270 
crates/vim2/test_data/test_change_surrounding_character_objects.json     | 2380 
crates/vim2/test_data/test_change_w.json                                 |   28 
crates/vim2/test_data/test_change_word_object.json                       |  460 
crates/vim2/test_data/test_clear_counts.json                             |    7 
crates/vim2/test_data/test_comma_semicolon.json                          |   17 
crates/vim2/test_data/test_command_basics.json                           |    6 
crates/vim2/test_data/test_command_goto.json                             |    5 
crates/vim2/test_data/test_command_replace.json                          |   22 
crates/vim2/test_data/test_command_search.json                           |   11 
crates/vim2/test_data/test_ctrl_d_u.json                                 |   22 
crates/vim2/test_data/test_dd.json                                       |   24 
crates/vim2/test_data/test_delete_0.json                                 |    8 
crates/vim2/test_data/test_delete_b.json                                 |   24 
crates/vim2/test_data/test_delete_end_of_document.json                   |   16 
crates/vim2/test_data/test_delete_end_of_line.json                       |    8 
crates/vim2/test_data/test_delete_gg.json                                |   20 
crates/vim2/test_data/test_delete_h.json                                 |   16 
crates/vim2/test_data/test_delete_j.json                                 |   16 
crates/vim2/test_data/test_delete_k.json                                 |   16 
crates/vim2/test_data/test_delete_l.json                                 |   16 
crates/vim2/test_data/test_delete_left.json                              |   15 
crates/vim2/test_data/test_delete_next_word_end.json                     |   12 
crates/vim2/test_data/test_delete_sentence_object.json                   |  270 
crates/vim2/test_data/test_delete_surrounding_character_objects.json     | 2372 
crates/vim2/test_data/test_delete_to_end_of_line.json                    |    6 
crates/vim2/test_data/test_delete_w.json                                 |   28 
crates/vim2/test_data/test_delete_with_counts.json                       |   16 
crates/vim2/test_data/test_delete_word_object.json                       |  460 
crates/vim2/test_data/test_dot_repeat.json                               |   38 
crates/vim2/test_data/test_end_of_document.json                          |   15 
crates/vim2/test_data/test_end_of_word.json                              |   32 
crates/vim2/test_data/test_enter.json                                    |   11 
crates/vim2/test_data/test_enter_visual_line_mode.json                   |   15 
crates/vim2/test_data/test_enter_visual_mode.json                        |   20 
crates/vim2/test_data/test_f_and_t.json                                  |  557 
crates/vim2/test_data/test_folds.json                                    |   23 
crates/vim2/test_data/test_folds_panic.json                              |   13 
crates/vim2/test_data/test_gg.json                                       |   21 
crates/vim2/test_data/test_h.json                                        |    9 
crates/vim2/test_data/test_h_through_unicode.json                        |   12 
crates/vim2/test_data/test_increment.json                                |   16 
crates/vim2/test_data/test_increment_radix.json                          |   18 
crates/vim2/test_data/test_increment_steps.json                          |   15 
crates/vim2/test_data/test_insert_end_of_line.json                       |    9 
crates/vim2/test_data/test_insert_first_non_whitespace.json              |   15 
crates/vim2/test_data/test_insert_line_above.json                        |   18 
crates/vim2/test_data/test_insert_with_counts.json                       |   36 
crates/vim2/test_data/test_insert_with_repeat.json                       |   23 
crates/vim2/test_data/test_j.json                                        |   15 
crates/vim2/test_data/test_join_lines.json                               |   13 
crates/vim2/test_data/test_jump_to_end.json                              |   14 
crates/vim2/test_data/test_jump_to_first_non_whitespace.json             |   18 
crates/vim2/test_data/test_jump_to_line_boundaries.json                  |   28 
crates/vim2/test_data/test_k.json                                        |   15 
crates/vim2/test_data/test_l.json                                        |   15 
crates/vim2/test_data/test_matching.json                                 |   17 
crates/vim2/test_data/test_multiline_surrounding_character_objects.json  |   15 
crates/vim2/test_data/test_neovim.json                                   |   16 
crates/vim2/test_data/test_next_line_start.json                          |    3 
crates/vim2/test_data/test_o.json                                        |   18 
crates/vim2/test_data/test_paragraphs_dont_wrap.json                     |    8 
crates/vim2/test_data/test_paste.json                                    |   31 
crates/vim2/test_data/test_paste_visual.json                             |   42 
crates/vim2/test_data/test_paste_visual_block.json                       |   31 
crates/vim2/test_data/test_percent.json                                  |   58 
crates/vim2/test_data/test_repeat_motion_counts.json                     |   13 
crates/vim2/test_data/test_repeat_visual.json                            |   51 
crates/vim2/test_data/test_repeated_cb.json                              |  275 
crates/vim2/test_data/test_repeated_ce.json                              |  275 
crates/vim2/test_data/test_repeated_cj.json                              |  275 
crates/vim2/test_data/test_repeated_cl.json                              |  275 
crates/vim2/test_data/test_repeated_word.json                            |  214 
crates/vim2/test_data/test_selection_goal.json                           |    8 
crates/vim2/test_data/test_singleline_surrounding_character_objects.json |   27 
crates/vim2/test_data/test_start_end_of_paragraph.json                   |   13 
crates/vim2/test_data/test_substitute_line.json                          |   29 
crates/vim2/test_data/test_visual_block_insert.json                      |   18 
crates/vim2/test_data/test_visual_block_issue_2123.json                  |    5 
crates/vim2/test_data/test_visual_block_mode.json                        |   38 
crates/vim2/test_data/test_visual_change.json                            |   47 
crates/vim2/test_data/test_visual_delete.json                            |   48 
crates/vim2/test_data/test_visual_line_change.json                       |   35 
crates/vim2/test_data/test_visual_line_delete.json                       |   23 
crates/vim2/test_data/test_visual_object.json                            |   19 
crates/vim2/test_data/test_visual_sentence_object.json                   |    0 
crates/vim2/test_data/test_visual_word_object.json                       |  236 
crates/vim2/test_data/test_visual_yank.json                              |   35 
crates/vim2/test_data/test_w.json                                        |   40 
crates/vim2/test_data/test_wrapped_lines.json                            |   61 
crates/vim2/test_data/test_wrapped_motions.json                          |   15 
crates/vim2/test_data/test_x.json                                        |   12 
crates/vim2/test_data/test_zero.json                                     |    7 
crates/welcome/Cargo.toml                                                |    2 
crates/zed/Cargo.toml                                                    |   10 
185 files changed, 1,730 insertions(+), 28,720 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1906,27 +1906,6 @@ dependencies = [
 [[package]]
 name = "command_palette"
 version = "0.1.0"
-dependencies = [
- "collections",
- "ctor",
- "editor",
- "env_logger",
- "fuzzy",
- "gpui",
- "language",
- "picker",
- "project",
- "serde_json",
- "settings",
- "theme",
- "util",
- "workspace",
- "zed-actions",
-]
-
-[[package]]
-name = "command_palette2"
-version = "0.1.0"
 dependencies = [
  "anyhow",
  "collections",
@@ -1934,7 +1913,7 @@ dependencies = [
  "editor2",
  "env_logger",
  "fuzzy2",
- "go_to_line2",
+ "go_to_line",
  "gpui2",
  "language2",
  "menu2",
@@ -2623,33 +2602,6 @@ dependencies = [
 [[package]]
 name = "diagnostics"
 version = "0.1.0"
-dependencies = [
- "anyhow",
- "client",
- "collections",
- "editor",
- "futures 0.3.28",
- "gpui",
- "language",
- "log",
- "lsp",
- "postage",
- "project",
- "schemars",
- "serde",
- "serde_derive",
- "serde_json",
- "settings",
- "smallvec",
- "theme",
- "unindent",
- "util",
- "workspace",
-]
-
-[[package]]
-name = "diagnostics2"
-version = "0.1.0"
 dependencies = [
  "anyhow",
  "client2",
@@ -3166,29 +3118,6 @@ dependencies = [
 [[package]]
 name = "file_finder"
 version = "0.1.0"
-dependencies = [
- "collections",
- "ctor",
- "editor",
- "env_logger",
- "fuzzy",
- "gpui",
- "language",
- "menu",
- "picker",
- "postage",
- "project",
- "serde_json",
- "settings",
- "text",
- "theme",
- "util",
- "workspace",
-]
-
-[[package]]
-name = "file_finder2"
-version = "0.1.0"
 dependencies = [
  "collections",
  "ctor",
@@ -3745,21 +3674,6 @@ dependencies = [
 [[package]]
 name = "go_to_line"
 version = "0.1.0"
-dependencies = [
- "editor",
- "gpui",
- "menu",
- "postage",
- "settings",
- "text",
- "theme",
- "util",
- "workspace",
-]
-
-[[package]]
-name = "go_to_line2"
-version = "0.1.0"
 dependencies = [
  "editor2",
  "gpui2",
@@ -10540,40 +10454,6 @@ dependencies = [
  "collections",
  "command_palette",
  "diagnostics",
- "editor",
- "futures 0.3.28",
- "gpui",
- "indoc",
- "itertools 0.10.5",
- "language",
- "language_selector",
- "log",
- "lsp",
- "nvim-rs",
- "parking_lot 0.11.2",
- "project",
- "search",
- "serde",
- "serde_derive",
- "serde_json",
- "settings",
- "theme",
- "tokio",
- "util",
- "workspace",
- "zed-actions",
-]
-
-[[package]]
-name = "vim2"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "async-compat",
- "async-trait",
- "collections",
- "command_palette2",
- "diagnostics2",
  "editor2",
  "futures 0.3.28",
  "gpui2",
@@ -11007,7 +10887,7 @@ dependencies = [
  "theme_selector",
  "ui2",
  "util",
- "vim2",
+ "vim",
  "workspace2",
 ]
 
@@ -11429,21 +11309,21 @@ dependencies = [
  "client2",
  "collab_ui",
  "collections",
- "command_palette2",
+ "command_palette",
  "copilot2",
  "copilot_button2",
  "ctor",
  "db2",
- "diagnostics2",
+ "diagnostics",
  "editor2",
  "env_logger",
  "feature_flags2",
  "feedback2",
- "file_finder2",
+ "file_finder",
  "fs2",
  "fsevent",
  "futures 0.3.28",
- "go_to_line2",
+ "go_to_line",
  "gpui2",
  "ignore",
  "image",
@@ -11530,7 +11410,7 @@ dependencies = [
  "urlencoding",
  "util",
  "uuid 1.4.1",
- "vim2",
+ "vim",
  "welcome",
  "workspace2",
  "zed_actions2",

Cargo.toml 🔗

@@ -24,7 +24,6 @@ members = [
     "crates/collab_ui",
     "crates/collections",
     "crates/command_palette",
-    "crates/command_palette2",
     "crates/component_test",
     "crates/context_menu",
     "crates/copilot",
@@ -35,7 +34,6 @@ members = [
     "crates/refineable",
     "crates/refineable/derive_refineable",
     "crates/diagnostics",
-    "crates/diagnostics2",
     "crates/drag_and_drop",
     "crates/editor",
     "crates/feature_flags",
@@ -49,7 +47,6 @@ members = [
     "crates/fuzzy2",
     "crates/git",
     "crates/go_to_line",
-    "crates/go_to_line2",
     "crates/gpui",
     "crates/gpui_macros",
     "crates/gpui2",

crates/command_palette/Cargo.toml 🔗

@@ -10,23 +10,28 @@ doctest = false
 
 [dependencies]
 collections = { path = "../collections" }
-editor = { path = "../editor" }
-fuzzy = { path = "../fuzzy" }
-gpui = { path = "../gpui" }
-picker = { path = "../picker" }
-project = { path = "../project" }
-settings = { path = "../settings" }
+editor = { package = "editor2", path = "../editor2" }
+fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+picker = { package = "picker2", path = "../picker2" }
+project = { package = "project2", path = "../project2" }
+settings = { package = "settings2", path = "../settings2" }
+ui = { package = "ui2", path = "../ui2" }
 util = { path = "../util" }
-theme = { path = "../theme" }
-workspace = { path = "../workspace" }
-zed-actions = { path = "../zed-actions" }
+theme = { package = "theme2", path = "../theme2" }
+workspace = { package="workspace2", path = "../workspace2" }
+zed_actions = { package = "zed_actions2", path = "../zed_actions2" }
+anyhow.workspace = true
+serde.workspace = true
 
 [dev-dependencies]
-gpui = { path = "../gpui", features = ["test-support"] }
-editor = { path = "../editor", features = ["test-support"] }
-language = { path = "../language", features = ["test-support"] }
-project = { path = "../project", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+language = { package="language2", path = "../language2", features = ["test-support"] }
+project = { package="project2", path = "../project2", features = ["test-support"] }
+menu = { package = "menu2", path = "../menu2" }
+go_to_line = { path = "../go_to_line" }
 serde_json.workspace = true
-workspace = { path = "../workspace", features = ["test-support"] }
+workspace = { package="workspace2", path = "../workspace2", features = ["test-support"] }
 ctor.workspace = true
 env_logger.workspace = true

crates/command_palette/src/command_palette.rs 🔗

@@ -1,26 +1,92 @@
+use std::{
+    cmp::{self, Reverse},
+    sync::Arc,
+};
+
 use collections::{CommandPaletteFilter, HashMap};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, anyhow::anyhow, elements::*, keymap_matcher::Keystroke, Action, AnyWindowHandle,
-    AppContext, Element, MouseState, ViewContext,
+    actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
+    ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
 };
-use picker::{Picker, PickerDelegate, PickerEvent};
-use std::cmp::{self, Reverse};
+use picker::{Picker, PickerDelegate};
+
+use ui::{h_stack, prelude::*, v_stack, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing};
 use util::{
     channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
     ResultExt,
 };
-use workspace::Workspace;
+use workspace::{ModalView, Workspace};
 use zed_actions::OpenZedURL;
 
+actions!(command_palette, [Toggle]);
+
 pub fn init(cx: &mut AppContext) {
-    cx.add_action(toggle_command_palette);
-    CommandPalette::init(cx);
+    cx.set_global(HitCounts::default());
+    cx.set_global(CommandPaletteFilter::default());
+    cx.observe_new_views(CommandPalette::register).detach();
 }
 
-actions!(command_palette, [Toggle]);
+impl ModalView for CommandPalette {}
+
+pub struct CommandPalette {
+    picker: View<Picker<CommandPaletteDelegate>>,
+}
+
+impl CommandPalette {
+    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+        workspace.register_action(|workspace, _: &Toggle, cx| {
+            let Some(previous_focus_handle) = cx.focused() else {
+                return;
+            };
+            workspace.toggle_modal(cx, move |cx| CommandPalette::new(previous_focus_handle, cx));
+        });
+    }
+
+    fn new(previous_focus_handle: FocusHandle, cx: &mut ViewContext<Self>) -> Self {
+        let filter = cx.try_global::<CommandPaletteFilter>();
+
+        let commands = cx
+            .available_actions()
+            .into_iter()
+            .filter_map(|action| {
+                let name = action.name();
+                let namespace = name.split("::").next().unwrap_or("malformed action name");
+                if filter.is_some_and(|f| {
+                    f.hidden_namespaces.contains(namespace)
+                        || f.hidden_action_types.contains(&action.type_id())
+                }) {
+                    return None;
+                }
+
+                Some(Command {
+                    name: humanize_action_name(&name),
+                    action,
+                })
+            })
+            .collect();
+
+        let delegate =
+            CommandPaletteDelegate::new(cx.view().downgrade(), commands, previous_focus_handle);
+
+        let picker = cx.new_view(|cx| Picker::new(delegate, cx));
+        Self { picker }
+    }
+}
+
+impl EventEmitter<DismissEvent> for CommandPalette {}
 
-pub type CommandPalette = Picker<CommandPaletteDelegate>;
+impl FocusableView for CommandPalette {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.picker.focus_handle(cx)
+    }
+}
+
+impl Render for CommandPalette {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+        v_stack().w(rems(34.)).child(self.picker.clone())
+    }
+}
 
 pub type CommandPaletteInterceptor =
     Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>;
@@ -32,24 +98,26 @@ pub struct CommandInterceptResult {
 }
 
 pub struct CommandPaletteDelegate {
-    actions: Vec<Command>,
+    command_palette: WeakView<CommandPalette>,
+    all_commands: Vec<Command>,
+    commands: Vec<Command>,
     matches: Vec<StringMatch>,
     selected_ix: usize,
-    focused_view_id: usize,
+    previous_focus_handle: FocusHandle,
 }
 
-pub enum Event {
-    Dismissed,
-    Confirmed {
-        window: AnyWindowHandle,
-        focused_view_id: usize,
-        action: Box<dyn Action>,
-    },
-}
 struct Command {
     name: String,
     action: Box<dyn Action>,
-    keystrokes: Vec<Keystroke>,
+}
+
+impl Clone for Command {
+    fn clone(&self) -> Self {
+        Self {
+            name: self.name.clone(),
+            action: self.action.boxed_clone(),
+        }
+    }
 }
 
 /// Hit count for each command in the palette.
@@ -58,26 +126,27 @@ struct Command {
 #[derive(Default)]
 struct HitCounts(HashMap<String, usize>);
 
-fn toggle_command_palette(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
-    let focused_view_id = cx.focused_view_id().unwrap_or_else(|| cx.view_id());
-    workspace.toggle_modal(cx, |_, cx| {
-        cx.add_view(|cx| Picker::new(CommandPaletteDelegate::new(focused_view_id), cx))
-    });
-}
-
 impl CommandPaletteDelegate {
-    pub fn new(focused_view_id: usize) -> Self {
+    fn new(
+        command_palette: WeakView<CommandPalette>,
+        commands: Vec<Command>,
+        previous_focus_handle: FocusHandle,
+    ) -> Self {
         Self {
-            actions: Default::default(),
+            command_palette,
+            all_commands: commands.clone(),
             matches: vec![],
+            commands,
             selected_ix: 0,
-            focused_view_id,
+            previous_focus_handle,
         }
     }
 }
 
 impl PickerDelegate for CommandPaletteDelegate {
-    fn placeholder_text(&self) -> std::sync::Arc<str> {
+    type ListItem = ListItem;
+
+    fn placeholder_text(&self) -> Arc<str> {
         "Execute a command...".into()
     }
 
@@ -98,49 +167,20 @@ impl PickerDelegate for CommandPaletteDelegate {
         query: String,
         cx: &mut ViewContext<Picker<Self>>,
     ) -> gpui::Task<()> {
-        let view_id = self.focused_view_id;
-        let window = cx.window();
+        let mut commands = self.all_commands.clone();
+
         cx.spawn(move |picker, mut cx| async move {
-            let mut actions = window
-                .available_actions(view_id, &cx)
-                .into_iter()
-                .flatten()
-                .filter_map(|(name, action, bindings)| {
-                    let filtered = cx.read(|cx| {
-                        if cx.has_global::<CommandPaletteFilter>() {
-                            let filter = cx.global::<CommandPaletteFilter>();
-                            filter.hidden_namespaces.contains(action.namespace())
-                        } else {
-                            false
-                        }
-                    });
-
-                    if filtered {
-                        None
-                    } else {
-                        Some(Command {
-                            name: humanize_action_name(name),
-                            action,
-                            keystrokes: bindings
-                                .iter()
-                                .map(|binding| binding.keystrokes())
-                                .last()
-                                .map_or(Vec::new(), |keystrokes| keystrokes.to_vec()),
-                        })
-                    }
-                })
-                .collect::<Vec<_>>();
-            let mut actions = cx.read(move |cx| {
-                let hit_counts = cx.optional_global::<HitCounts>();
-                actions.sort_by_key(|action| {
+            cx.read_global::<HitCounts, _>(|hit_counts, _| {
+                commands.sort_by_key(|action| {
                     (
-                        Reverse(hit_counts.and_then(|map| map.0.get(&action.name)).cloned()),
+                        Reverse(hit_counts.0.get(&action.name).cloned()),
                         action.name.clone(),
                     )
                 });
-                actions
-            });
-            let candidates = actions
+            })
+            .ok();
+
+            let candidates = commands
                 .iter()
                 .enumerate()
                 .map(|(ix, command)| StringMatchCandidate {
@@ -167,17 +207,17 @@ impl PickerDelegate for CommandPaletteDelegate {
                     true,
                     10000,
                     &Default::default(),
-                    cx.background(),
+                    cx.background_executor().clone(),
                 )
                 .await
             };
-            let mut intercept_result = cx.read(|cx| {
-                if cx.has_global::<CommandPaletteInterceptor>() {
-                    cx.global::<CommandPaletteInterceptor>()(&query, cx)
-                } else {
-                    None
-                }
-            });
+
+            let mut intercept_result = cx
+                .try_read_global(|interceptor: &CommandPaletteInterceptor, cx| {
+                    (interceptor)(&query, cx)
+                })
+                .flatten();
+
             if *RELEASE_CHANNEL == ReleaseChannel::Dev {
                 if parse_zed_link(&query).is_some() {
                     intercept_result = Some(CommandInterceptResult {
@@ -187,6 +227,7 @@ impl PickerDelegate for CommandPaletteDelegate {
                     })
                 }
             }
+
             if let Some(CommandInterceptResult {
                 action,
                 string,
@@ -195,29 +236,29 @@ impl PickerDelegate for CommandPaletteDelegate {
             {
                 if let Some(idx) = matches
                     .iter()
-                    .position(|m| actions[m.candidate_id].action.id() == action.id())
+                    .position(|m| commands[m.candidate_id].action.type_id() == action.type_id())
                 {
                     matches.remove(idx);
                 }
-                actions.push(Command {
+                commands.push(Command {
                     name: string.clone(),
                     action,
-                    keystrokes: vec![],
                 });
                 matches.insert(
                     0,
                     StringMatch {
-                        candidate_id: actions.len() - 1,
+                        candidate_id: commands.len() - 1,
                         string,
                         positions,
                         score: 0.0,
                     },
                 )
             }
+
             picker
                 .update(&mut cx, |picker, _| {
-                    let delegate = picker.delegate_mut();
-                    delegate.actions = actions;
+                    let delegate = &mut picker.delegate;
+                    delegate.commands = commands;
                     delegate.matches = matches;
                     if delegate.matches.is_empty() {
                         delegate.selected_ix = 0;
@@ -230,83 +271,60 @@ impl PickerDelegate for CommandPaletteDelegate {
         })
     }
 
-    fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
+    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+        self.command_palette
+            .update(cx, |_, cx| cx.emit(DismissEvent))
+            .log_err();
+    }
 
     fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
-        if !self.matches.is_empty() {
-            let window = cx.window();
-            let focused_view_id = self.focused_view_id;
-            let action_ix = self.matches[self.selected_ix].candidate_id;
-            let command = self.actions.remove(action_ix);
-            cx.update_default_global(|hit_counts: &mut HitCounts, _| {
-                *hit_counts.0.entry(command.name).or_default() += 1;
-            });
-            let action = command.action;
-
-            cx.app_context()
-                .spawn(move |mut cx| async move {
-                    window
-                        .dispatch_action(focused_view_id, action.as_ref(), &mut cx)
-                        .ok_or_else(|| anyhow!("window was closed"))
-                })
-                .detach_and_log_err(cx);
+        if self.matches.is_empty() {
+            self.dismissed(cx);
+            return;
         }
-        cx.emit(PickerEvent::Dismiss);
+        let action_ix = self.matches[self.selected_ix].candidate_id;
+        let command = self.commands.swap_remove(action_ix);
+        self.matches.clear();
+        self.commands.clear();
+        cx.update_global(|hit_counts: &mut HitCounts, _| {
+            *hit_counts.0.entry(command.name).or_default() += 1;
+        });
+        let action = command.action;
+        cx.focus(&self.previous_focus_handle);
+        cx.window_context()
+            .spawn(move |mut cx| async move { cx.update(|_, cx| cx.dispatch_action(action)) })
+            .detach_and_log_err(cx);
+        self.dismissed(cx);
     }
 
     fn render_match(
         &self,
         ix: usize,
-        mouse_state: &mut MouseState,
         selected: bool,
-        cx: &gpui::AppContext,
-    ) -> AnyElement<Picker<Self>> {
-        let mat = &self.matches[ix];
-        let command = &self.actions[mat.candidate_id];
-        let theme = theme::current(cx);
-        let style = theme.picker.item.in_state(selected).style_for(mouse_state);
-        let key_style = &theme.command_palette.key.in_state(selected);
-        let keystroke_spacing = theme.command_palette.keystroke_spacing;
-
-        Flex::row()
-            .with_child(
-                Label::new(mat.string.clone(), style.label.clone())
-                    .with_highlights(mat.positions.clone()),
-            )
-            .with_children(command.keystrokes.iter().map(|keystroke| {
-                Flex::row()
-                    .with_children(
-                        [
-                            (keystroke.ctrl, "^"),
-                            (keystroke.alt, "⌥"),
-                            (keystroke.cmd, "⌘"),
-                            (keystroke.shift, "⇧"),
-                        ]
-                        .into_iter()
-                        .filter_map(|(modifier, label)| {
-                            if modifier {
-                                Some(
-                                    Label::new(label, key_style.label.clone())
-                                        .contained()
-                                        .with_style(key_style.container),
-                                )
-                            } else {
-                                None
-                            }
-                        }),
-                    )
-                    .with_child(
-                        Label::new(keystroke.key.clone(), key_style.label.clone())
-                            .contained()
-                            .with_style(key_style.container),
-                    )
-                    .contained()
-                    .with_margin_left(keystroke_spacing)
-                    .flex_float()
-            }))
-            .contained()
-            .with_style(style.container)
-            .into_any()
+        cx: &mut ViewContext<Picker<Self>>,
+    ) -> Option<Self::ListItem> {
+        let r#match = self.matches.get(ix)?;
+        let command = self.commands.get(r#match.candidate_id)?;
+        Some(
+            ListItem::new(ix)
+                .inset(true)
+                .spacing(ListItemSpacing::Sparse)
+                .selected(selected)
+                .child(
+                    h_stack()
+                        .w_full()
+                        .justify_between()
+                        .child(HighlightedLabel::new(
+                            command.name.clone(),
+                            r#match.positions.clone(),
+                        ))
+                        .children(KeyBinding::for_action_in(
+                            &*command.action,
+                            &self.previous_focus_handle,
+                            cx,
+                        )),
+                ),
+        )
     }
 }
 
@@ -338,8 +356,7 @@ impl std::fmt::Debug for Command {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("Command")
             .field("name", &self.name)
-            .field("keystrokes", &self.keystrokes)
-            .finish()
+            .finish_non_exhaustive()
     }
 }
 
@@ -349,7 +366,9 @@ mod tests {
 
     use super::*;
     use editor::Editor;
-    use gpui::{executor::Deterministic, TestAppContext};
+    use go_to_line::GoToLine;
+    use gpui::TestAppContext;
+    use language::Point;
     use project::Project;
     use workspace::{AppState, Workspace};
 
@@ -370,101 +389,121 @@ mod tests {
     }
 
     #[gpui::test]
-    async fn test_command_palette(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
+    async fn test_command_palette(cx: &mut TestAppContext) {
         let app_state = init_test(cx);
-
         let project = Project::test(app_state.fs.clone(), [], cx).await;
-        let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-        let workspace = window.root(cx);
-        let editor = window.add_view(cx, |cx| {
-            let mut editor = Editor::single_line(None, cx);
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+
+        let editor = cx.new_view(|cx| {
+            let mut editor = Editor::single_line(cx);
             editor.set_text("abc", cx);
             editor
         });
 
         workspace.update(cx, |workspace, cx| {
-            cx.focus(&editor);
-            workspace.add_item(Box::new(editor.clone()), cx)
+            workspace.add_item(Box::new(editor.clone()), cx);
+            editor.update(cx, |editor, cx| editor.focus(cx))
         });
 
-        workspace.update(cx, |workspace, cx| {
-            toggle_command_palette(workspace, &Toggle, cx);
-        });
+        cx.simulate_keystrokes("cmd-shift-p");
 
-        let palette = workspace.read_with(cx, |workspace, _| {
-            workspace.modal::<CommandPalette>().unwrap()
+        let palette = workspace.update(cx, |workspace, cx| {
+            workspace
+                .active_modal::<CommandPalette>(cx)
+                .unwrap()
+                .read(cx)
+                .picker
+                .clone()
         });
 
-        palette
-            .update(cx, |palette, cx| {
-                // Fill up palette's command list by running an empty query;
-                // we only need it to subsequently assert that the palette is initially
-                // sorted by command's name.
-                palette.delegate_mut().update_matches("".to_string(), cx)
-            })
-            .await;
-
         palette.update(cx, |palette, _| {
+            assert!(palette.delegate.commands.len() > 5);
             let is_sorted =
                 |actions: &[Command]| actions.windows(2).all(|pair| pair[0].name <= pair[1].name);
-            assert!(is_sorted(&palette.delegate().actions));
+            assert!(is_sorted(&palette.delegate.commands));
         });
 
-        palette
-            .update(cx, |palette, cx| {
-                palette
-                    .delegate_mut()
-                    .update_matches("bcksp".to_string(), cx)
-            })
-            .await;
+        cx.simulate_input("bcksp");
 
-        palette.update(cx, |palette, cx| {
-            assert_eq!(palette.delegate().matches[0].string, "editor: backspace");
-            palette.confirm(&Default::default(), cx);
+        palette.update(cx, |palette, _| {
+            assert_eq!(palette.delegate.matches[0].string, "editor: backspace");
         });
-        deterministic.run_until_parked();
-        editor.read_with(cx, |editor, cx| {
-            assert_eq!(editor.text(cx), "ab");
+
+        cx.simulate_keystrokes("enter");
+
+        workspace.update(cx, |workspace, cx| {
+            assert!(workspace.active_modal::<CommandPalette>(cx).is_none());
+            assert_eq!(editor.read(cx).text(cx), "ab")
         });
 
         // Add namespace filter, and redeploy the palette
         cx.update(|cx| {
-            cx.update_default_global::<CommandPaletteFilter, _, _>(|filter, _| {
+            cx.set_global(CommandPaletteFilter::default());
+            cx.update_global::<CommandPaletteFilter, _>(|filter, _| {
                 filter.hidden_namespaces.insert("editor");
             })
         });
 
-        workspace.update(cx, |workspace, cx| {
-            toggle_command_palette(workspace, &Toggle, cx);
+        cx.simulate_keystrokes("cmd-shift-p");
+        cx.simulate_input("bcksp");
+
+        let palette = workspace.update(cx, |workspace, cx| {
+            workspace
+                .active_modal::<CommandPalette>(cx)
+                .unwrap()
+                .read(cx)
+                .picker
+                .clone()
+        });
+        palette.update(cx, |palette, _| {
+            assert!(palette.delegate.matches.is_empty())
+        });
+    }
+
+    #[gpui::test]
+    async fn test_go_to_line(cx: &mut TestAppContext) {
+        let app_state = init_test(cx);
+        let project = Project::test(app_state.fs.clone(), [], cx).await;
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
+
+        cx.simulate_keystrokes("cmd-n");
+
+        let editor = workspace.update(cx, |workspace, cx| {
+            workspace.active_item_as::<Editor>(cx).unwrap()
         });
+        editor.update(cx, |editor, cx| editor.set_text("1\n2\n3\n4\n5\n6\n", cx));
+
+        cx.simulate_keystrokes("cmd-shift-p");
+        cx.simulate_input("go to line: Toggle");
+        cx.simulate_keystrokes("enter");
 
-        // Assert editor command not present
-        let palette = workspace.read_with(cx, |workspace, _| {
-            workspace.modal::<CommandPalette>().unwrap()
+        workspace.update(cx, |workspace, cx| {
+            assert!(workspace.active_modal::<GoToLine>(cx).is_some())
         });
 
-        palette
-            .update(cx, |palette, cx| {
-                palette
-                    .delegate_mut()
-                    .update_matches("bcksp".to_string(), cx)
-            })
-            .await;
+        cx.simulate_keystrokes("3 enter");
 
-        palette.update(cx, |palette, _| {
-            assert!(palette.delegate().matches.is_empty())
+        editor.update(cx, |editor, cx| {
+            assert!(editor.focus_handle(cx).is_focused(cx));
+            assert_eq!(
+                editor.selections.last::<Point>(cx).range().start,
+                Point::new(2, 0)
+            );
         });
     }
 
     fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
         cx.update(|cx| {
             let app_state = AppState::test(cx);
-            theme::init((), cx);
+            theme::init(theme::LoadThemes::JustBase, cx);
             language::init(cx);
             editor::init(cx);
+            menu::init();
+            go_to_line::init(cx);
             workspace::init(app_state.clone(), cx);
             init(cx);
             Project::init_settings(cx);
+            settings::load_default_keymap(cx);
             app_state
         })
     }

crates/command_palette2/Cargo.toml 🔗

@@ -1,36 +0,0 @@
-[package]
-name = "command_palette2"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-path = "src/command_palette.rs"
-doctest = false
-
-[dependencies]
-collections = { path = "../collections" }
-editor = { package = "editor2", path = "../editor2" }
-fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
-gpui = { package = "gpui2", path = "../gpui2" }
-picker = { package = "picker2", path = "../picker2" }
-project = { package = "project2", path = "../project2" }
-settings = { package = "settings2", path = "../settings2" }
-ui = { package = "ui2", path = "../ui2" }
-util = { path = "../util" }
-theme = { package = "theme2", path = "../theme2" }
-workspace = { package="workspace2", path = "../workspace2" }
-zed_actions = { package = "zed_actions2", path = "../zed_actions2" }
-anyhow.workspace = true
-serde.workspace = true
-[dev-dependencies]
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
-language = { package="language2", path = "../language2", features = ["test-support"] }
-project = { package="project2", path = "../project2", features = ["test-support"] }
-menu = { package = "menu2", path = "../menu2" }
-go_to_line = { package = "go_to_line2", path = "../go_to_line2" }
-serde_json.workspace = true
-workspace = { package="workspace2", path = "../workspace2", features = ["test-support"] }
-ctor.workspace = true
-env_logger.workspace = true

crates/command_palette2/src/command_palette.rs 🔗

@@ -1,510 +0,0 @@
-use std::{
-    cmp::{self, Reverse},
-    sync::Arc,
-};
-
-use collections::{CommandPaletteFilter, HashMap};
-use fuzzy::{StringMatch, StringMatchCandidate};
-use gpui::{
-    actions, Action, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
-    ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
-};
-use picker::{Picker, PickerDelegate};
-
-use ui::{h_stack, prelude::*, v_stack, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing};
-use util::{
-    channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
-    ResultExt,
-};
-use workspace::{ModalView, Workspace};
-use zed_actions::OpenZedURL;
-
-actions!(command_palette, [Toggle]);
-
-pub fn init(cx: &mut AppContext) {
-    cx.set_global(HitCounts::default());
-    cx.set_global(CommandPaletteFilter::default());
-    cx.observe_new_views(CommandPalette::register).detach();
-}
-
-impl ModalView for CommandPalette {}
-
-pub struct CommandPalette {
-    picker: View<Picker<CommandPaletteDelegate>>,
-}
-
-impl CommandPalette {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.register_action(|workspace, _: &Toggle, cx| {
-            let Some(previous_focus_handle) = cx.focused() else {
-                return;
-            };
-            workspace.toggle_modal(cx, move |cx| CommandPalette::new(previous_focus_handle, cx));
-        });
-    }
-
-    fn new(previous_focus_handle: FocusHandle, cx: &mut ViewContext<Self>) -> Self {
-        let filter = cx.try_global::<CommandPaletteFilter>();
-
-        let commands = cx
-            .available_actions()
-            .into_iter()
-            .filter_map(|action| {
-                let name = action.name();
-                let namespace = name.split("::").next().unwrap_or("malformed action name");
-                if filter.is_some_and(|f| {
-                    f.hidden_namespaces.contains(namespace)
-                        || f.hidden_action_types.contains(&action.type_id())
-                }) {
-                    return None;
-                }
-
-                Some(Command {
-                    name: humanize_action_name(&name),
-                    action,
-                })
-            })
-            .collect();
-
-        let delegate =
-            CommandPaletteDelegate::new(cx.view().downgrade(), commands, previous_focus_handle);
-
-        let picker = cx.new_view(|cx| Picker::new(delegate, cx));
-        Self { picker }
-    }
-}
-
-impl EventEmitter<DismissEvent> for CommandPalette {}
-
-impl FocusableView for CommandPalette {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
-        self.picker.focus_handle(cx)
-    }
-}
-
-impl Render for CommandPalette {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
-        v_stack().w(rems(34.)).child(self.picker.clone())
-    }
-}
-
-pub type CommandPaletteInterceptor =
-    Box<dyn Fn(&str, &AppContext) -> Option<CommandInterceptResult>>;
-
-pub struct CommandInterceptResult {
-    pub action: Box<dyn Action>,
-    pub string: String,
-    pub positions: Vec<usize>,
-}
-
-pub struct CommandPaletteDelegate {
-    command_palette: WeakView<CommandPalette>,
-    all_commands: Vec<Command>,
-    commands: Vec<Command>,
-    matches: Vec<StringMatch>,
-    selected_ix: usize,
-    previous_focus_handle: FocusHandle,
-}
-
-struct Command {
-    name: String,
-    action: Box<dyn Action>,
-}
-
-impl Clone for Command {
-    fn clone(&self) -> Self {
-        Self {
-            name: self.name.clone(),
-            action: self.action.boxed_clone(),
-        }
-    }
-}
-
-/// Hit count for each command in the palette.
-/// We only account for commands triggered directly via command palette and not by e.g. keystrokes because
-/// if an user already knows a keystroke for a command, they are unlikely to use a command palette to look for it.
-#[derive(Default)]
-struct HitCounts(HashMap<String, usize>);
-
-impl CommandPaletteDelegate {
-    fn new(
-        command_palette: WeakView<CommandPalette>,
-        commands: Vec<Command>,
-        previous_focus_handle: FocusHandle,
-    ) -> Self {
-        Self {
-            command_palette,
-            all_commands: commands.clone(),
-            matches: vec![],
-            commands,
-            selected_ix: 0,
-            previous_focus_handle,
-        }
-    }
-}
-
-impl PickerDelegate for CommandPaletteDelegate {
-    type ListItem = ListItem;
-
-    fn placeholder_text(&self) -> Arc<str> {
-        "Execute a command...".into()
-    }
-
-    fn match_count(&self) -> usize {
-        self.matches.len()
-    }
-
-    fn selected_index(&self) -> usize {
-        self.selected_ix
-    }
-
-    fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
-        self.selected_ix = ix;
-    }
-
-    fn update_matches(
-        &mut self,
-        query: String,
-        cx: &mut ViewContext<Picker<Self>>,
-    ) -> gpui::Task<()> {
-        let mut commands = self.all_commands.clone();
-
-        cx.spawn(move |picker, mut cx| async move {
-            cx.read_global::<HitCounts, _>(|hit_counts, _| {
-                commands.sort_by_key(|action| {
-                    (
-                        Reverse(hit_counts.0.get(&action.name).cloned()),
-                        action.name.clone(),
-                    )
-                });
-            })
-            .ok();
-
-            let candidates = commands
-                .iter()
-                .enumerate()
-                .map(|(ix, command)| StringMatchCandidate {
-                    id: ix,
-                    string: command.name.to_string(),
-                    char_bag: command.name.chars().collect(),
-                })
-                .collect::<Vec<_>>();
-            let mut matches = if query.is_empty() {
-                candidates
-                    .into_iter()
-                    .enumerate()
-                    .map(|(index, candidate)| StringMatch {
-                        candidate_id: index,
-                        string: candidate.string,
-                        positions: Vec::new(),
-                        score: 0.0,
-                    })
-                    .collect()
-            } else {
-                fuzzy::match_strings(
-                    &candidates,
-                    &query,
-                    true,
-                    10000,
-                    &Default::default(),
-                    cx.background_executor().clone(),
-                )
-                .await
-            };
-
-            let mut intercept_result = cx
-                .try_read_global(|interceptor: &CommandPaletteInterceptor, cx| {
-                    (interceptor)(&query, cx)
-                })
-                .flatten();
-
-            if *RELEASE_CHANNEL == ReleaseChannel::Dev {
-                if parse_zed_link(&query).is_some() {
-                    intercept_result = Some(CommandInterceptResult {
-                        action: OpenZedURL { url: query.clone() }.boxed_clone(),
-                        string: query.clone(),
-                        positions: vec![],
-                    })
-                }
-            }
-
-            if let Some(CommandInterceptResult {
-                action,
-                string,
-                positions,
-            }) = intercept_result
-            {
-                if let Some(idx) = matches
-                    .iter()
-                    .position(|m| commands[m.candidate_id].action.type_id() == action.type_id())
-                {
-                    matches.remove(idx);
-                }
-                commands.push(Command {
-                    name: string.clone(),
-                    action,
-                });
-                matches.insert(
-                    0,
-                    StringMatch {
-                        candidate_id: commands.len() - 1,
-                        string,
-                        positions,
-                        score: 0.0,
-                    },
-                )
-            }
-
-            picker
-                .update(&mut cx, |picker, _| {
-                    let delegate = &mut picker.delegate;
-                    delegate.commands = commands;
-                    delegate.matches = matches;
-                    if delegate.matches.is_empty() {
-                        delegate.selected_ix = 0;
-                    } else {
-                        delegate.selected_ix =
-                            cmp::min(delegate.selected_ix, delegate.matches.len() - 1);
-                    }
-                })
-                .log_err();
-        })
-    }
-
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
-        self.command_palette
-            .update(cx, |_, cx| cx.emit(DismissEvent))
-            .log_err();
-    }
-
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
-        if self.matches.is_empty() {
-            self.dismissed(cx);
-            return;
-        }
-        let action_ix = self.matches[self.selected_ix].candidate_id;
-        let command = self.commands.swap_remove(action_ix);
-        self.matches.clear();
-        self.commands.clear();
-        cx.update_global(|hit_counts: &mut HitCounts, _| {
-            *hit_counts.0.entry(command.name).or_default() += 1;
-        });
-        let action = command.action;
-        cx.focus(&self.previous_focus_handle);
-        cx.window_context()
-            .spawn(move |mut cx| async move { cx.update(|_, cx| cx.dispatch_action(action)) })
-            .detach_and_log_err(cx);
-        self.dismissed(cx);
-    }
-
-    fn render_match(
-        &self,
-        ix: usize,
-        selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
-    ) -> Option<Self::ListItem> {
-        let r#match = self.matches.get(ix)?;
-        let command = self.commands.get(r#match.candidate_id)?;
-        Some(
-            ListItem::new(ix)
-                .inset(true)
-                .spacing(ListItemSpacing::Sparse)
-                .selected(selected)
-                .child(
-                    h_stack()
-                        .w_full()
-                        .justify_between()
-                        .child(HighlightedLabel::new(
-                            command.name.clone(),
-                            r#match.positions.clone(),
-                        ))
-                        .children(KeyBinding::for_action_in(
-                            &*command.action,
-                            &self.previous_focus_handle,
-                            cx,
-                        )),
-                ),
-        )
-    }
-}
-
-fn humanize_action_name(name: &str) -> String {
-    let capacity = name.len() + name.chars().filter(|c| c.is_uppercase()).count();
-    let mut result = String::with_capacity(capacity);
-    for char in name.chars() {
-        if char == ':' {
-            if result.ends_with(':') {
-                result.push(' ');
-            } else {
-                result.push(':');
-            }
-        } else if char == '_' {
-            result.push(' ');
-        } else if char.is_uppercase() {
-            if !result.ends_with(' ') {
-                result.push(' ');
-            }
-            result.extend(char.to_lowercase());
-        } else {
-            result.push(char);
-        }
-    }
-    result
-}
-
-impl std::fmt::Debug for Command {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("Command")
-            .field("name", &self.name)
-            .finish_non_exhaustive()
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use std::sync::Arc;
-
-    use super::*;
-    use editor::Editor;
-    use go_to_line::GoToLine;
-    use gpui::TestAppContext;
-    use language::Point;
-    use project::Project;
-    use workspace::{AppState, Workspace};
-
-    #[test]
-    fn test_humanize_action_name() {
-        assert_eq!(
-            humanize_action_name("editor::GoToDefinition"),
-            "editor: go to definition"
-        );
-        assert_eq!(
-            humanize_action_name("editor::Backspace"),
-            "editor: backspace"
-        );
-        assert_eq!(
-            humanize_action_name("go_to_line::Deploy"),
-            "go to line: deploy"
-        );
-    }
-
-    #[gpui::test]
-    async fn test_command_palette(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-        let project = Project::test(app_state.fs.clone(), [], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
-
-        let editor = cx.new_view(|cx| {
-            let mut editor = Editor::single_line(cx);
-            editor.set_text("abc", cx);
-            editor
-        });
-
-        workspace.update(cx, |workspace, cx| {
-            workspace.add_item(Box::new(editor.clone()), cx);
-            editor.update(cx, |editor, cx| editor.focus(cx))
-        });
-
-        cx.simulate_keystrokes("cmd-shift-p");
-
-        let palette = workspace.update(cx, |workspace, cx| {
-            workspace
-                .active_modal::<CommandPalette>(cx)
-                .unwrap()
-                .read(cx)
-                .picker
-                .clone()
-        });
-
-        palette.update(cx, |palette, _| {
-            assert!(palette.delegate.commands.len() > 5);
-            let is_sorted =
-                |actions: &[Command]| actions.windows(2).all(|pair| pair[0].name <= pair[1].name);
-            assert!(is_sorted(&palette.delegate.commands));
-        });
-
-        cx.simulate_input("bcksp");
-
-        palette.update(cx, |palette, _| {
-            assert_eq!(palette.delegate.matches[0].string, "editor: backspace");
-        });
-
-        cx.simulate_keystrokes("enter");
-
-        workspace.update(cx, |workspace, cx| {
-            assert!(workspace.active_modal::<CommandPalette>(cx).is_none());
-            assert_eq!(editor.read(cx).text(cx), "ab")
-        });
-
-        // Add namespace filter, and redeploy the palette
-        cx.update(|cx| {
-            cx.set_global(CommandPaletteFilter::default());
-            cx.update_global::<CommandPaletteFilter, _>(|filter, _| {
-                filter.hidden_namespaces.insert("editor");
-            })
-        });
-
-        cx.simulate_keystrokes("cmd-shift-p");
-        cx.simulate_input("bcksp");
-
-        let palette = workspace.update(cx, |workspace, cx| {
-            workspace
-                .active_modal::<CommandPalette>(cx)
-                .unwrap()
-                .read(cx)
-                .picker
-                .clone()
-        });
-        palette.update(cx, |palette, _| {
-            assert!(palette.delegate.matches.is_empty())
-        });
-    }
-
-    #[gpui::test]
-    async fn test_go_to_line(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-        let project = Project::test(app_state.fs.clone(), [], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
-
-        cx.simulate_keystrokes("cmd-n");
-
-        let editor = workspace.update(cx, |workspace, cx| {
-            workspace.active_item_as::<Editor>(cx).unwrap()
-        });
-        editor.update(cx, |editor, cx| editor.set_text("1\n2\n3\n4\n5\n6\n", cx));
-
-        cx.simulate_keystrokes("cmd-shift-p");
-        cx.simulate_input("go to line: Toggle");
-        cx.simulate_keystrokes("enter");
-
-        workspace.update(cx, |workspace, cx| {
-            assert!(workspace.active_modal::<GoToLine>(cx).is_some())
-        });
-
-        cx.simulate_keystrokes("3 enter");
-
-        editor.update(cx, |editor, cx| {
-            assert!(editor.focus_handle(cx).is_focused(cx));
-            assert_eq!(
-                editor.selections.last::<Point>(cx).range().start,
-                Point::new(2, 0)
-            );
-        });
-    }
-
-    fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
-        cx.update(|cx| {
-            let app_state = AppState::test(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
-            language::init(cx);
-            editor::init(cx);
-            menu::init();
-            go_to_line::init(cx);
-            workspace::init(app_state.clone(), cx);
-            init(cx);
-            Project::init_settings(cx);
-            settings::load_default_keymap(cx);
-            app_state
-        })
-    }
-}

crates/diagnostics/Cargo.toml 🔗

@@ -10,15 +10,16 @@ doctest = false
 
 [dependencies]
 collections = { path = "../collections" }
-editor = { path = "../editor" }
-gpui = { path = "../gpui" }
-language = { path = "../language" }
-lsp = { path = "../lsp" }
-project = { path = "../project" }
-settings = { path = "../settings" }
-theme = { path = "../theme" }
+editor = { package = "editor2", path = "../editor2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+ui = { package = "ui2", path = "../ui2" }
+language = { package = "language2", path = "../language2" }
+lsp = { package = "lsp2", path = "../lsp2" }
+project = { package = "project2", path = "../project2" }
+settings = { package = "settings2", path = "../settings2" }
+theme = { package = "theme2", path = "../theme2" }
 util = { path = "../util" }
-workspace = { path = "../workspace" }
+workspace = { package = "workspace2", path = "../workspace2" }
 
 log.workspace = true
 anyhow.workspace = true
@@ -30,13 +31,13 @@ smallvec.workspace = true
 postage.workspace = true
 
 [dev-dependencies]
-client = { path = "../client", features = ["test-support"] }
-editor = { path = "../editor", features = ["test-support"] }
-language = { path = "../language", features = ["test-support"] }
-lsp = { path = "../lsp", features = ["test-support"] }
-gpui = { path = "../gpui", features = ["test-support"] }
-workspace = { path = "../workspace", features = ["test-support"] }
-theme = { path = "../theme", features = ["test-support"] }
+client = { package = "client2", path = "../client2", features = ["test-support"] }
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
+lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
+theme = { package = "theme2", path = "../theme2", features = ["test-support"] }
 
 serde_json.workspace = true
 unindent.workspace = true

crates/diagnostics/src/diagnostics.rs 🔗

@@ -2,19 +2,21 @@ pub mod items;
 mod project_diagnostics_settings;
 mod toolbar_controls;
 
-use anyhow::{Context, Result};
+use anyhow::{Context as _, Result};
 use collections::{HashMap, HashSet};
 use editor::{
     diagnostic_block_renderer,
     display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock},
     highlight_diagnostic_message,
     scroll::autoscroll::Autoscroll,
-    Editor, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
+    Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
 };
 use futures::future::try_join_all;
 use gpui::{
-    actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity,
-    ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
+    actions, div, svg, AnyElement, AnyView, AppContext, Context, EventEmitter, FocusHandle,
+    FocusableView, HighlightStyle, InteractiveElement, IntoElement, Model, ParentElement, Render,
+    SharedString, Styled, StyledText, Subscription, Task, View, ViewContext, VisualContext,
+    WeakView, WindowContext,
 };
 use language::{
     Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection,
@@ -23,23 +25,22 @@ use language::{
 use lsp::LanguageServerId;
 use project::{DiagnosticSummary, Project, ProjectPath};
 use project_diagnostics_settings::ProjectDiagnosticsSettings;
-use serde_json::json;
-use smallvec::SmallVec;
+use settings::Settings;
 use std::{
     any::{Any, TypeId},
-    borrow::Cow,
     cmp::Ordering,
     mem,
     ops::Range,
     path::PathBuf,
     sync::Arc,
 };
-use theme::ThemeSettings;
+use theme::ActiveTheme;
 pub use toolbar_controls::ToolbarControls;
+use ui::{h_stack, prelude::*, Icon, IconElement, Label};
 use util::TryFutureExt;
 use workspace::{
     item::{BreadcrumbText, Item, ItemEvent, ItemHandle},
-    ItemNavHistory, Pane, PaneBackdrop, ToolbarItemLocation, Workspace,
+    ItemNavHistory, Pane, ToolbarItemLocation, Workspace,
 };
 
 actions!(diagnostics, [Deploy, ToggleWarnings]);
@@ -47,20 +48,18 @@ actions!(diagnostics, [Deploy, ToggleWarnings]);
 const CONTEXT_LINE_COUNT: u32 = 1;
 
 pub fn init(cx: &mut AppContext) {
-    settings::register::<ProjectDiagnosticsSettings>(cx);
-    cx.add_action(ProjectDiagnosticsEditor::deploy);
-    cx.add_action(ProjectDiagnosticsEditor::toggle_warnings);
-    items::init(cx);
+    ProjectDiagnosticsSettings::register(cx);
+    cx.observe_new_views(ProjectDiagnosticsEditor::register)
+        .detach();
 }
 
-type Event = editor::Event;
-
 struct ProjectDiagnosticsEditor {
-    project: ModelHandle<Project>,
-    workspace: WeakViewHandle<Workspace>,
-    editor: ViewHandle<Editor>,
+    project: Model<Project>,
+    workspace: WeakView<Workspace>,
+    focus_handle: FocusHandle,
+    editor: View<Editor>,
     summary: DiagnosticSummary,
-    excerpts: ModelHandle<MultiBuffer>,
+    excerpts: Model<MultiBuffer>,
     path_states: Vec<PathState>,
     paths_to_update: HashMap<LanguageServerId, HashSet<ProjectPath>>,
     current_diagnostics: HashMap<LanguageServerId, HashSet<ProjectPath>>,
@@ -89,71 +88,38 @@ struct DiagnosticGroupState {
     block_count: usize,
 }
 
-impl Entity for ProjectDiagnosticsEditor {
-    type Event = Event;
-}
-
-impl View for ProjectDiagnosticsEditor {
-    fn ui_name() -> &'static str {
-        "ProjectDiagnosticsEditor"
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-        if self.path_states.is_empty() {
-            let theme = &theme::current(cx).project_diagnostics;
-            PaneBackdrop::new(
-                cx.view_id(),
-                Label::new("No problems in workspace", theme.empty_message.clone())
-                    .aligned()
-                    .contained()
-                    .with_style(theme.container)
-                    .into_any(),
-            )
-            .into_any()
+impl EventEmitter<EditorEvent> for ProjectDiagnosticsEditor {}
+
+impl Render for ProjectDiagnosticsEditor {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+        let child = if self.path_states.is_empty() {
+            div()
+                .bg(cx.theme().colors().editor_background)
+                .flex()
+                .items_center()
+                .justify_center()
+                .size_full()
+                .child(Label::new("No problems in workspace"))
         } else {
-            ChildView::new(&self.editor, cx).into_any()
-        }
-    }
-
-    fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
-        if cx.is_self_focused() && !self.path_states.is_empty() {
-            cx.focus(&self.editor);
-        }
-    }
+            div().size_full().child(self.editor.clone())
+        };
 
-    fn debug_json(&self, cx: &AppContext) -> serde_json::Value {
-        let project = self.project.read(cx);
-        json!({
-            "project": json!({
-                "language_servers": project.language_server_statuses().collect::<Vec<_>>(),
-                "summary": project.diagnostic_summary(false, cx),
-            }),
-            "summary": self.summary,
-            "paths_to_update": self.paths_to_update.iter().map(|(server_id, paths)|
-                (server_id.0, paths.into_iter().map(|path| path.path.to_string_lossy()).collect::<Vec<_>>())
-            ).collect::<HashMap<_, _>>(),
-            "current_diagnostics": self.current_diagnostics.iter().map(|(server_id, paths)|
-                (server_id.0, paths.into_iter().map(|path| path.path.to_string_lossy()).collect::<Vec<_>>())
-            ).collect::<HashMap<_, _>>(),
-            "paths_states": self.path_states.iter().map(|state|
-                json!({
-                    "path": state.path.path.to_string_lossy(),
-                    "groups": state.diagnostic_groups.iter().map(|group|
-                        json!({
-                            "block_count": group.blocks.len(),
-                            "excerpt_count": group.excerpts.len(),
-                        })
-                    ).collect::<Vec<_>>(),
-                })
-            ).collect::<Vec<_>>(),
-        })
+        div()
+            .track_focus(&self.focus_handle)
+            .size_full()
+            .on_action(cx.listener(Self::toggle_warnings))
+            .child(child)
     }
 }
 
 impl ProjectDiagnosticsEditor {
+    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+        workspace.register_action(Self::deploy);
+    }
+
     fn new(
-        project_handle: ModelHandle<Project>,
-        workspace: WeakViewHandle<Workspace>,
+        project_handle: Model<Project>,
+        workspace: WeakView<Workspace>,
         cx: &mut ViewContext<Self>,
     ) -> Self {
         let project_event_subscription =
@@ -180,19 +146,25 @@ impl ProjectDiagnosticsEditor {
                 _ => {}
             });
 
-        let excerpts = cx.add_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
-        let editor = cx.add_view(|cx| {
+        let focus_handle = cx.focus_handle();
+
+        let focus_in_subscription =
+            cx.on_focus_in(&focus_handle, |diagnostics, cx| diagnostics.focus_in(cx));
+
+        let excerpts = cx.new_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
+        let editor = cx.new_view(|cx| {
             let mut editor =
                 Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), cx);
             editor.set_vertical_scroll_margin(5, cx);
             editor
         });
-        let editor_event_subscription = cx.subscribe(&editor, |this, _, event, cx| {
-            cx.emit(event.clone());
-            if event == &editor::Event::Focused && this.path_states.is_empty() {
-                cx.focus_self()
-            }
-        });
+        let editor_event_subscription =
+            cx.subscribe(&editor, |this, _editor, event: &EditorEvent, cx| {
+                cx.emit(event.clone());
+                if event == &EditorEvent::Focused && this.path_states.is_empty() {
+                    cx.focus(&this.focus_handle);
+                }
+            });
 
         let project = project_handle.read(cx);
         let summary = project.diagnostic_summary(false, cx);
@@ -201,12 +173,17 @@ impl ProjectDiagnosticsEditor {
             summary,
             workspace,
             excerpts,
+            focus_handle,
             editor,
             path_states: Default::default(),
             paths_to_update: HashMap::default(),
-            include_warnings: settings::get::<ProjectDiagnosticsSettings>(cx).include_warnings,
+            include_warnings: ProjectDiagnosticsSettings::get_global(cx).include_warnings,
             current_diagnostics: HashMap::default(),
-            _subscriptions: vec![project_event_subscription, editor_event_subscription],
+            _subscriptions: vec![
+                project_event_subscription,
+                editor_event_subscription,
+                focus_in_subscription,
+            ],
         };
         this.update_excerpts(None, cx);
         this
@@ -216,8 +193,8 @@ impl ProjectDiagnosticsEditor {
         if let Some(existing) = workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
             workspace.activate_item(&existing, cx);
         } else {
-            let workspace_handle = cx.weak_handle();
-            let diagnostics = cx.add_view(|cx| {
+            let workspace_handle = cx.view().downgrade();
+            let diagnostics = cx.new_view(|cx| {
                 ProjectDiagnosticsEditor::new(workspace.project().clone(), workspace_handle, cx)
             });
             workspace.add_item(Box::new(diagnostics), cx);
@@ -231,6 +208,12 @@ impl ProjectDiagnosticsEditor {
         cx.notify();
     }
 
+    fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
+        if self.focus_handle.is_focused(cx) && !self.path_states.is_empty() {
+            self.editor.focus_handle(cx).focus(cx)
+        }
+    }
+
     fn update_excerpts(
         &mut self,
         language_server_id: Option<LanguageServerId>,
@@ -304,9 +287,10 @@ impl ProjectDiagnosticsEditor {
                 let _: Vec<()> = try_join_all(paths_to_recheck.into_iter().map(|path| {
                     let mut cx = cx.clone();
                     let project = project.clone();
+                    let this = this.clone();
                     async move {
                         let buffer = project
-                            .update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx))
+                            .update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx))?
                             .await
                             .with_context(|| format!("opening buffer for path {path:?}"))?;
                         this.update(&mut cx, |this, cx| {
@@ -321,7 +305,7 @@ impl ProjectDiagnosticsEditor {
 
                 this.update(&mut cx, |this, cx| {
                     this.summary = this.project.read(cx).diagnostic_summary(false, cx);
-                    cx.emit(Event::TitleChanged);
+                    cx.emit(EditorEvent::TitleChanged);
                 })?;
                 anyhow::Ok(())
             }
@@ -334,7 +318,7 @@ impl ProjectDiagnosticsEditor {
         &mut self,
         path: ProjectPath,
         language_server_id: Option<LanguageServerId>,
-        buffer: ModelHandle<Buffer>,
+        buffer: Model<Buffer>,
         cx: &mut ViewContext<Self>,
     ) {
         let was_empty = self.path_states.is_empty();
@@ -618,41 +602,32 @@ impl ProjectDiagnosticsEditor {
         });
 
         if self.path_states.is_empty() {
-            if self.editor.is_focused(cx) {
-                cx.focus_self();
+            if self.editor.focus_handle(cx).is_focused(cx) {
+                cx.focus(&self.focus_handle);
             }
-        } else if cx.handle().is_focused(cx) {
-            cx.focus(&self.editor);
+        } else if self.focus_handle.is_focused(cx) {
+            let focus_handle = self.editor.focus_handle(cx);
+            cx.focus(&focus_handle);
         }
         cx.notify();
     }
 }
 
-impl Item for ProjectDiagnosticsEditor {
-    fn tab_content<T: 'static>(
-        &self,
-        _detail: Option<usize>,
-        style: &theme::Tab,
-        cx: &AppContext,
-    ) -> AnyElement<T> {
-        render_summary(
-            &self.summary,
-            &style.label.text,
-            &theme::current(cx).project_diagnostics,
-        )
+impl FocusableView for ProjectDiagnosticsEditor {
+    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+        self.focus_handle.clone()
     }
+}
 
-    fn for_each_project_item(&self, cx: &AppContext, f: &mut dyn FnMut(usize, &dyn project::Item)) {
-        self.editor.for_each_project_item(cx, f)
-    }
+impl Item for ProjectDiagnosticsEditor {
+    type Event = EditorEvent;
 
-    fn is_singleton(&self, _: &AppContext) -> bool {
-        false
+    fn to_item_events(event: &EditorEvent, f: impl FnMut(ItemEvent)) {
+        Editor::to_item_events(event, f)
     }
 
-    fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
-        self.editor
-            .update(cx, |editor, cx| editor.added_to_workspace(workspace, cx));
+    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
+        self.editor.update(cx, |editor, cx| editor.deactivated(cx));
     }
 
     fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
@@ -660,10 +635,82 @@ impl Item for ProjectDiagnosticsEditor {
             .update(cx, |editor, cx| editor.navigate(data, cx))
     }
 
-    fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
+    fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
         Some("Project Diagnostics".into())
     }
 
+    fn tab_content(&self, _detail: Option<usize>, selected: bool, _: &WindowContext) -> AnyElement {
+        if self.summary.error_count == 0 && self.summary.warning_count == 0 {
+            let label = Label::new("No problems");
+            label.into_any_element()
+        } else {
+            h_stack()
+                .gap_1()
+                .when(self.summary.error_count > 0, |then| {
+                    then.child(
+                        h_stack()
+                            .gap_1()
+                            .child(IconElement::new(Icon::XCircle).color(Color::Error))
+                            .child(Label::new(self.summary.error_count.to_string()).color(
+                                if selected {
+                                    Color::Default
+                                } else {
+                                    Color::Muted
+                                },
+                            )),
+                    )
+                })
+                .when(self.summary.warning_count > 0, |then| {
+                    then.child(
+                        h_stack()
+                            .gap_1()
+                            .child(
+                                IconElement::new(Icon::ExclamationTriangle).color(Color::Warning),
+                            )
+                            .child(Label::new(self.summary.warning_count.to_string()).color(
+                                if selected {
+                                    Color::Default
+                                } else {
+                                    Color::Muted
+                                },
+                            )),
+                    )
+                })
+                .into_any_element()
+        }
+    }
+
+    fn for_each_project_item(
+        &self,
+        cx: &AppContext,
+        f: &mut dyn FnMut(gpui::EntityId, &dyn project::Item),
+    ) {
+        self.editor.for_each_project_item(cx, f)
+    }
+
+    fn is_singleton(&self, _: &AppContext) -> bool {
+        false
+    }
+
+    fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
+        self.editor.update(cx, |editor, _| {
+            editor.set_nav_history(Some(nav_history));
+        });
+    }
+
+    fn clone_on_split(
+        &self,
+        _workspace_id: workspace::WorkspaceId,
+        cx: &mut ViewContext<Self>,
+    ) -> Option<View<Self>>
+    where
+        Self: Sized,
+    {
+        Some(cx.new_view(|cx| {
+            ProjectDiagnosticsEditor::new(self.project.clone(), self.workspace.clone(), cx)
+        }))
+    }
+
     fn is_dirty(&self, cx: &AppContext) -> bool {
         self.excerpts.read(cx).is_dirty(cx)
     }
@@ -676,209 +723,133 @@ impl Item for ProjectDiagnosticsEditor {
         true
     }
 
-    fn save(
-        &mut self,
-        project: ModelHandle<Project>,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
+    fn save(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
         self.editor.save(project, cx)
     }
 
-    fn reload(
-        &mut self,
-        project: ModelHandle<Project>,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
-        self.editor.reload(project, cx)
-    }
-
     fn save_as(
         &mut self,
-        _: ModelHandle<Project>,
+        _: Model<Project>,
         _: PathBuf,
         _: &mut ViewContext<Self>,
     ) -> Task<Result<()>> {
         unreachable!()
     }
 
-    fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
-        Editor::to_item_events(event)
-    }
-
-    fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
-        self.editor.update(cx, |editor, _| {
-            editor.set_nav_history(Some(nav_history));
-        });
-    }
-
-    fn clone_on_split(
-        &self,
-        _workspace_id: workspace::WorkspaceId,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<Self>
-    where
-        Self: Sized,
-    {
-        Some(ProjectDiagnosticsEditor::new(
-            self.project.clone(),
-            self.workspace.clone(),
-            cx,
-        ))
+    fn reload(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
+        self.editor.reload(project, cx)
     }
 
     fn act_as_type<'a>(
         &'a self,
         type_id: TypeId,
-        self_handle: &'a ViewHandle<Self>,
+        self_handle: &'a View<Self>,
         _: &'a AppContext,
-    ) -> Option<&AnyViewHandle> {
+    ) -> Option<AnyView> {
         if type_id == TypeId::of::<Self>() {
-            Some(self_handle)
+            Some(self_handle.to_any())
         } else if type_id == TypeId::of::<Editor>() {
-            Some(&self.editor)
+            Some(self.editor.to_any())
         } else {
             None
         }
     }
 
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
-        self.editor.update(cx, |editor, cx| editor.deactivated(cx));
-    }
-
-    fn serialized_item_kind() -> Option<&'static str> {
-        Some("diagnostics")
+    fn breadcrumb_location(&self) -> ToolbarItemLocation {
+        ToolbarItemLocation::PrimaryLeft
     }
 
     fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
         self.editor.breadcrumbs(theme, cx)
     }
 
-    fn breadcrumb_location(&self) -> ToolbarItemLocation {
-        ToolbarItemLocation::PrimaryLeft { flex: None }
+    fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
+        self.editor
+            .update(cx, |editor, cx| editor.added_to_workspace(workspace, cx));
+    }
+
+    fn serialized_item_kind() -> Option<&'static str> {
+        Some("diagnostics")
     }
 
     fn deserialize(
-        project: ModelHandle<Project>,
-        workspace: WeakViewHandle<Workspace>,
+        project: Model<Project>,
+        workspace: WeakView<Workspace>,
         _workspace_id: workspace::WorkspaceId,
         _item_id: workspace::ItemId,
         cx: &mut ViewContext<Pane>,
-    ) -> Task<Result<ViewHandle<Self>>> {
-        Task::ready(Ok(cx.add_view(|cx| Self::new(project, workspace, cx))))
+    ) -> Task<Result<View<Self>>> {
+        Task::ready(Ok(cx.new_view(|cx| Self::new(project, workspace, cx))))
     }
 }
 
 fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
-    let (message, highlights) = highlight_diagnostic_message(Vec::new(), &diagnostic.message);
+    let (message, code_ranges) = highlight_diagnostic_message(&diagnostic);
+    let message: SharedString = message.into();
     Arc::new(move |cx| {
-        let settings = settings::get::<ThemeSettings>(cx);
-        let theme = &settings.theme.editor;
-        let style = theme.diagnostic_header.clone();
-        let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
-        let icon_width = cx.em_width * style.icon_width_factor;
-        let icon = if diagnostic.severity == DiagnosticSeverity::ERROR {
-            Svg::new("icons/error.svg").with_color(theme.error_diagnostic.message.text.color)
-        } else {
-            Svg::new("icons/warning.svg").with_color(theme.warning_diagnostic.message.text.color)
-        };
-
-        Flex::row()
-            .with_child(
-                icon.constrained()
-                    .with_width(icon_width)
-                    .aligned()
-                    .contained()
-                    .with_margin_right(cx.gutter_padding),
+        let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
+        h_stack()
+            .id("diagnostic header")
+            .py_2()
+            .pl_10()
+            .pr_5()
+            .w_full()
+            .justify_between()
+            .gap_2()
+            .child(
+                h_stack()
+                    .gap_3()
+                    .map(|stack| {
+                        stack.child(
+                            svg()
+                                .size(cx.text_style().font_size)
+                                .flex_none()
+                                .map(|icon| {
+                                    if diagnostic.severity == DiagnosticSeverity::ERROR {
+                                        icon.path(Icon::XCircle.path())
+                                            .text_color(Color::Error.color(cx))
+                                    } else {
+                                        icon.path(Icon::ExclamationTriangle.path())
+                                            .text_color(Color::Warning.color(cx))
+                                    }
+                                }),
+                        )
+                    })
+                    .child(
+                        h_stack()
+                            .gap_1()
+                            .child(
+                                StyledText::new(message.clone()).with_highlights(
+                                    &cx.text_style(),
+                                    code_ranges
+                                        .iter()
+                                        .map(|range| (range.clone(), highlight_style)),
+                                ),
+                            )
+                            .when_some(diagnostic.code.as_ref(), |stack, code| {
+                                stack.child(
+                                    div()
+                                        .child(SharedString::from(format!("({code})")))
+                                        .text_color(cx.theme().colors().text_muted),
+                                )
+                            }),
+                    ),
             )
-            .with_children(diagnostic.source.as_ref().map(|source| {
-                Label::new(
-                    format!("{source}: "),
-                    style.source.label.clone().with_font_size(font_size),
-                )
-                .contained()
-                .with_style(style.message.container)
-                .aligned()
-            }))
-            .with_child(
-                Label::new(
-                    message.clone(),
-                    style.message.label.clone().with_font_size(font_size),
-                )
-                .with_highlights(highlights.clone())
-                .contained()
-                .with_style(style.message.container)
-                .aligned(),
+            .child(
+                h_stack()
+                    .gap_1()
+                    .when_some(diagnostic.source.as_ref(), |stack, source| {
+                        stack.child(
+                            div()
+                                .child(SharedString::from(source.clone()))
+                                .text_color(cx.theme().colors().text_muted),
+                        )
+                    }),
             )
-            .with_children(diagnostic.code.clone().map(|code| {
-                Label::new(code, style.code.text.clone().with_font_size(font_size))
-                    .contained()
-                    .with_style(style.code.container)
-                    .aligned()
-            }))
-            .contained()
-            .with_style(style.container)
-            .with_padding_left(cx.gutter_padding)
-            .with_padding_right(cx.gutter_padding)
-            .expanded()
-            .into_any_named("diagnostic header")
+            .into_any_element()
     })
 }
 
-pub(crate) fn render_summary<T: 'static>(
-    summary: &DiagnosticSummary,
-    text_style: &TextStyle,
-    theme: &theme::ProjectDiagnostics,
-) -> AnyElement<T> {
-    if summary.error_count == 0 && summary.warning_count == 0 {
-        Label::new("No problems", text_style.clone()).into_any()
-    } else {
-        let icon_width = theme.tab_icon_width;
-        let icon_spacing = theme.tab_icon_spacing;
-        let summary_spacing = theme.tab_summary_spacing;
-        Flex::row()
-            .with_child(
-                Svg::new("icons/error.svg")
-                    .with_color(text_style.color)
-                    .constrained()
-                    .with_width(icon_width)
-                    .aligned()
-                    .contained()
-                    .with_margin_right(icon_spacing),
-            )
-            .with_child(
-                Label::new(
-                    summary.error_count.to_string(),
-                    LabelStyle {
-                        text: text_style.clone(),
-                        highlight_text: None,
-                    },
-                )
-                .aligned(),
-            )
-            .with_child(
-                Svg::new("icons/warning.svg")
-                    .with_color(text_style.color)
-                    .constrained()
-                    .with_width(icon_width)
-                    .aligned()
-                    .contained()
-                    .with_margin_left(summary_spacing)
-                    .with_margin_right(icon_spacing),
-            )
-            .with_child(
-                Label::new(
-                    summary.warning_count.to_string(),
-                    LabelStyle {
-                        text: text_style.clone(),
-                        highlight_text: None,
-                    },
-                )
-                .aligned(),
-            )
-            .into_any()
-    }
-}
-
 fn compare_diagnostics<L: language::ToOffset, R: language::ToOffset>(
     lhs: &DiagnosticEntry<L>,
     rhs: &DiagnosticEntry<R>,
@@ -904,7 +875,7 @@ mod tests {
         display_map::{BlockContext, TransformBlock},
         DisplayPoint,
     };
-    use gpui::{TestAppContext, WindowContext};
+    use gpui::{px, TestAppContext, VisualTestContext, WindowContext};
     use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped};
     use project::FakeFs;
     use serde_json::json;
@@ -915,7 +886,7 @@ mod tests {
     async fn test_diagnostics(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let fs = FakeFs::new(cx.background());
+        let fs = FakeFs::new(cx.executor());
         fs.insert_tree(
             "/test",
             json!({
@@ -945,7 +916,8 @@ mod tests {
         let language_server_id = LanguageServerId(0);
         let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-        let workspace = window.root(cx);
+        let cx = &mut VisualTestContext::from_window(*window, cx);
+        let workspace = window.root(cx).unwrap();
 
         // Create some diagnostics
         project.update(cx, |project, cx| {
@@ -1032,7 +1004,7 @@ mod tests {
         });
 
         // Open the project diagnostics view while there are already diagnostics.
-        let view = window.add_view(cx, |cx| {
+        let view = window.build_view(cx, |cx| {
             ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
         });
 
@@ -1320,7 +1292,7 @@ mod tests {
     async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let fs = FakeFs::new(cx.background());
+        let fs = FakeFs::new(cx.executor());
         fs.insert_tree(
             "/test",
             json!({
@@ -1339,9 +1311,10 @@ mod tests {
         let server_id_2 = LanguageServerId(101);
         let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
         let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-        let workspace = window.root(cx);
+        let cx = &mut VisualTestContext::from_window(*window, cx);
+        let workspace = window.root(cx).unwrap();
 
-        let view = window.add_view(cx, |cx| {
+        let view = window.build_view(cx, |cx| {
             ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
         });
 
@@ -1376,7 +1349,7 @@ mod tests {
         });
 
         // Only the first language server's diagnostics are shown.
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
         view.update(cx, |view, cx| {
             assert_eq!(
                 editor_blocks(&view.editor, cx),
@@ -1424,7 +1397,7 @@ mod tests {
         });
 
         // Both language server's diagnostics are shown.
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
         view.update(cx, |view, cx| {
             assert_eq!(
                 editor_blocks(&view.editor, cx),
@@ -1492,7 +1465,7 @@ mod tests {
         });
 
         // Only the first language server's diagnostics are updated.
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
         view.update(cx, |view, cx| {
             assert_eq!(
                 editor_blocks(&view.editor, cx),
@@ -1550,7 +1523,7 @@ mod tests {
         });
 
         // Both language servers' diagnostics are updated.
-        cx.foreground().run_until_parked();
+        cx.executor().run_until_parked();
         view.update(cx, |view, cx| {
             assert_eq!(
                 editor_blocks(&view.editor, cx),
@@ -1586,8 +1559,9 @@ mod tests {
 
     fn init_test(cx: &mut TestAppContext) {
         cx.update(|cx| {
-            cx.set_global(SettingsStore::test(cx));
-            theme::init((), cx);
+            let settings = SettingsStore::test(cx);
+            cx.set_global(settings);
+            theme::init(theme::LoadThemes::JustBase, cx);
             language::init(cx);
             client::init_settings(cx);
             workspace::init_settings(cx);
@@ -1596,7 +1570,7 @@ mod tests {
         });
     }
 
-    fn editor_blocks(editor: &ViewHandle<Editor>, cx: &mut WindowContext) -> Vec<(u32, String)> {
+    fn editor_blocks(editor: &View<Editor>, cx: &mut WindowContext) -> Vec<(u32, SharedString)> {
         editor.update(cx, |editor, cx| {
             let snapshot = editor.snapshot(cx);
             snapshot
@@ -1607,23 +1581,25 @@ mod tests {
                         TransformBlock::Custom(block) => block
                             .render(&mut BlockContext {
                                 view_context: cx,
-                                anchor_x: 0.,
-                                scroll_x: 0.,
-                                gutter_padding: 0.,
-                                gutter_width: 0.,
-                                line_height: 0.,
-                                em_width: 0.,
+                                anchor_x: px(0.),
+                                gutter_padding: px(0.),
+                                gutter_width: px(0.),
+                                line_height: px(0.),
+                                em_width: px(0.),
                                 block_id: ix,
+                                editor_style: &editor::EditorStyle::default(),
                             })
-                            .name()?
-                            .to_string(),
+                            .inner_id()?
+                            .try_into()
+                            .ok()?,
+
                         TransformBlock::ExcerptHeader {
                             starts_new_buffer, ..
                         } => {
                             if *starts_new_buffer {
-                                "path header block".to_string()
+                                "path header block".into()
                             } else {
-                                "collapsed context".to_string()
+                                "collapsed context".into()
                             }
                         }
                     };

crates/diagnostics/src/items.rs 🔗

@@ -1,27 +1,105 @@
 use collections::HashSet;
-use editor::{Editor, GoToDiagnostic};
+use editor::Editor;
 use gpui::{
-    elements::*,
-    platform::{CursorStyle, MouseButton},
-    serde_json, AppContext, Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
+    rems, EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, View,
+    ViewContext, WeakView,
 };
 use language::Diagnostic;
 use lsp::LanguageServerId;
-use workspace::{item::ItemHandle, StatusItemView, Workspace};
+use ui::{h_stack, prelude::*, Button, ButtonLike, Color, Icon, IconElement, Label, Tooltip};
+use workspace::{item::ItemHandle, StatusItemView, ToolbarItemEvent, Workspace};
 
-use crate::ProjectDiagnosticsEditor;
+use crate::{Deploy, ProjectDiagnosticsEditor};
 
 pub struct DiagnosticIndicator {
     summary: project::DiagnosticSummary,
-    active_editor: Option<WeakViewHandle<Editor>>,
-    workspace: WeakViewHandle<Workspace>,
+    active_editor: Option<WeakView<Editor>>,
+    workspace: WeakView<Workspace>,
     current_diagnostic: Option<Diagnostic>,
     in_progress_checks: HashSet<LanguageServerId>,
     _observe_active_editor: Option<Subscription>,
 }
 
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(DiagnosticIndicator::go_to_next_diagnostic);
+impl Render for DiagnosticIndicator {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) {
+            (0, 0) => h_stack().child(
+                IconElement::new(Icon::Check)
+                    .size(IconSize::Small)
+                    .color(Color::Success),
+            ),
+            (0, warning_count) => h_stack()
+                .gap_1()
+                .child(
+                    IconElement::new(Icon::ExclamationTriangle)
+                        .size(IconSize::Small)
+                        .color(Color::Warning),
+                )
+                .child(Label::new(warning_count.to_string()).size(LabelSize::Small)),
+            (error_count, 0) => h_stack()
+                .gap_1()
+                .child(
+                    IconElement::new(Icon::XCircle)
+                        .size(IconSize::Small)
+                        .color(Color::Error),
+                )
+                .child(Label::new(error_count.to_string()).size(LabelSize::Small)),
+            (error_count, warning_count) => h_stack()
+                .gap_1()
+                .child(
+                    IconElement::new(Icon::XCircle)
+                        .size(IconSize::Small)
+                        .color(Color::Error),
+                )
+                .child(Label::new(error_count.to_string()).size(LabelSize::Small))
+                .child(
+                    IconElement::new(Icon::ExclamationTriangle)
+                        .size(IconSize::Small)
+                        .color(Color::Warning),
+                )
+                .child(Label::new(warning_count.to_string()).size(LabelSize::Small)),
+        };
+
+        let status = if !self.in_progress_checks.is_empty() {
+            Some(
+                Label::new("Checking…")
+                    .size(LabelSize::Small)
+                    .into_any_element(),
+            )
+        } else if let Some(diagnostic) = &self.current_diagnostic {
+            let message = diagnostic.message.split('\n').next().unwrap().to_string();
+            Some(
+                Button::new("diagnostic_message", message)
+                    .label_size(LabelSize::Small)
+                    .tooltip(|cx| {
+                        Tooltip::for_action("Next Diagnostic", &editor::GoToDiagnostic, cx)
+                    })
+                    .on_click(cx.listener(|this, _, cx| {
+                        this.go_to_next_diagnostic(cx);
+                    }))
+                    .into_any_element(),
+            )
+        } else {
+            None
+        };
+
+        h_stack()
+            .h(rems(1.375))
+            .gap_2()
+            .child(
+                ButtonLike::new("diagnostic-indicator")
+                    .child(diagnostic_indicator)
+                    .tooltip(|cx| Tooltip::for_action("Project Diagnostics", &Deploy, cx))
+                    .on_click(cx.listener(|this, _, cx| {
+                        if let Some(workspace) = this.workspace.upgrade() {
+                            workspace.update(cx, |workspace, cx| {
+                                ProjectDiagnosticsEditor::deploy(workspace, &Default::default(), cx)
+                            })
+                        }
+                    })),
+            )
+            .children(status)
+    }
 }
 
 impl DiagnosticIndicator {
@@ -32,19 +110,23 @@ impl DiagnosticIndicator {
                 this.in_progress_checks.insert(*language_server_id);
                 cx.notify();
             }
+
             project::Event::DiskBasedDiagnosticsFinished { language_server_id }
             | project::Event::LanguageServerRemoved(language_server_id) => {
                 this.summary = project.read(cx).diagnostic_summary(false, cx);
                 this.in_progress_checks.remove(language_server_id);
                 cx.notify();
             }
+
             project::Event::DiagnosticsUpdated { .. } => {
                 this.summary = project.read(cx).diagnostic_summary(false, cx);
                 cx.notify();
             }
+
             _ => {}
         })
         .detach();
+
         Self {
             summary: project.read(cx).diagnostic_summary(false, cx),
             in_progress_checks: project
@@ -58,15 +140,15 @@ impl DiagnosticIndicator {
         }
     }
 
-    fn go_to_next_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
-        if let Some(editor) = self.active_editor.as_ref().and_then(|e| e.upgrade(cx)) {
+    fn go_to_next_diagnostic(&mut self, cx: &mut ViewContext<Self>) {
+        if let Some(editor) = self.active_editor.as_ref().and_then(|e| e.upgrade()) {
             editor.update(cx, |editor, cx| {
                 editor.go_to_diagnostic_impl(editor::Direction::Next, cx);
             })
         }
     }
 
-    fn update(&mut self, editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) {
+    fn update(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
         let editor = editor.read(cx);
         let buffer = editor.buffer().read(cx);
         let cursor_position = editor.selections.newest::<usize>(cx).head();
@@ -83,146 +165,7 @@ impl DiagnosticIndicator {
     }
 }
 
-impl Entity for DiagnosticIndicator {
-    type Event = ();
-}
-
-impl View for DiagnosticIndicator {
-    fn ui_name() -> &'static str {
-        "DiagnosticIndicator"
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-        enum Summary {}
-        enum Message {}
-
-        let tooltip_style = theme::current(cx).tooltip.clone();
-        let in_progress = !self.in_progress_checks.is_empty();
-        let mut element = Flex::row().with_child(
-            MouseEventHandler::new::<Summary, _>(0, cx, |state, cx| {
-                let theme = theme::current(cx);
-                let style = theme
-                    .workspace
-                    .status_bar
-                    .diagnostic_summary
-                    .style_for(state);
-
-                let mut summary_row = Flex::row();
-                if self.summary.error_count > 0 {
-                    summary_row.add_child(
-                        Svg::new("icons/error.svg")
-                            .with_color(style.icon_color_error)
-                            .constrained()
-                            .with_width(style.icon_width)
-                            .aligned()
-                            .contained()
-                            .with_margin_right(style.icon_spacing),
-                    );
-                    summary_row.add_child(
-                        Label::new(self.summary.error_count.to_string(), style.text.clone())
-                            .aligned(),
-                    );
-                }
-
-                if self.summary.warning_count > 0 {
-                    summary_row.add_child(
-                        Svg::new("icons/warning.svg")
-                            .with_color(style.icon_color_warning)
-                            .constrained()
-                            .with_width(style.icon_width)
-                            .aligned()
-                            .contained()
-                            .with_margin_right(style.icon_spacing)
-                            .with_margin_left(if self.summary.error_count > 0 {
-                                style.summary_spacing
-                            } else {
-                                0.
-                            }),
-                    );
-                    summary_row.add_child(
-                        Label::new(self.summary.warning_count.to_string(), style.text.clone())
-                            .aligned(),
-                    );
-                }
-
-                if self.summary.error_count == 0 && self.summary.warning_count == 0 {
-                    summary_row.add_child(
-                        Svg::new("icons/check_circle.svg")
-                            .with_color(style.icon_color_ok)
-                            .constrained()
-                            .with_width(style.icon_width)
-                            .aligned()
-                            .into_any_named("ok-icon"),
-                    );
-                }
-
-                summary_row
-                    .constrained()
-                    .with_height(style.height)
-                    .contained()
-                    .with_style(if self.summary.error_count > 0 {
-                        style.container_error
-                    } else if self.summary.warning_count > 0 {
-                        style.container_warning
-                    } else {
-                        style.container_ok
-                    })
-            })
-            .with_cursor_style(CursorStyle::PointingHand)
-            .on_click(MouseButton::Left, |_, this, cx| {
-                if let Some(workspace) = this.workspace.upgrade(cx) {
-                    workspace.update(cx, |workspace, cx| {
-                        ProjectDiagnosticsEditor::deploy(workspace, &Default::default(), cx)
-                    })
-                }
-            })
-            .with_tooltip::<Summary>(
-                0,
-                "Project Diagnostics",
-                Some(Box::new(crate::Deploy)),
-                tooltip_style,
-                cx,
-            )
-            .aligned()
-            .into_any(),
-        );
-
-        let style = &theme::current(cx).workspace.status_bar;
-        let item_spacing = style.item_spacing;
-
-        if in_progress {
-            element.add_child(
-                Label::new("Checking…", style.diagnostic_message.default.text.clone())
-                    .aligned()
-                    .contained()
-                    .with_margin_left(item_spacing),
-            );
-        } else if let Some(diagnostic) = &self.current_diagnostic {
-            let message_style = style.diagnostic_message.clone();
-            element.add_child(
-                MouseEventHandler::new::<Message, _>(1, cx, |state, _| {
-                    Label::new(
-                        diagnostic.message.split('\n').next().unwrap().to_string(),
-                        message_style.style_for(state).text.clone(),
-                    )
-                    .aligned()
-                    .contained()
-                    .with_margin_left(item_spacing)
-                })
-                .with_cursor_style(CursorStyle::PointingHand)
-                .on_click(MouseButton::Left, |_, this, cx| {
-                    this.go_to_next_diagnostic(&Default::default(), cx)
-                }),
-            );
-        }
-
-        element.into_any_named("diagnostic indicator")
-    }
-
-    fn debug_json(&self, _: &gpui::AppContext) -> serde_json::Value {
-        serde_json::json!({ "summary": self.summary })
-    }
-}
+impl EventEmitter<ToolbarItemEvent> for DiagnosticIndicator {}
 
 impl StatusItemView for DiagnosticIndicator {
     fn set_active_pane_item(

crates/diagnostics/src/project_diagnostics_settings.rs 🔗

@@ -11,14 +11,14 @@ pub struct ProjectDiagnosticsSettingsContent {
     include_warnings: Option<bool>,
 }
 
-impl settings::Setting for ProjectDiagnosticsSettings {
+impl settings::Settings for ProjectDiagnosticsSettings {
     const KEY: Option<&'static str> = Some("diagnostics");
     type FileContent = ProjectDiagnosticsSettingsContent;
 
     fn load(
         default_value: &Self::FileContent,
         user_values: &[&Self::FileContent],
-        _cx: &gpui::AppContext,
+        _cx: &mut gpui::AppContext,
     ) -> anyhow::Result<Self>
     where
         Self: Sized,

crates/diagnostics/src/toolbar_controls.rs 🔗

@@ -1,55 +1,44 @@
-use crate::{ProjectDiagnosticsEditor, ToggleWarnings};
-use gpui::{
-    elements::*,
-    platform::{CursorStyle, MouseButton},
-    Action, Entity, EventContext, View, ViewContext, WeakViewHandle,
-};
-use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView};
+use crate::ProjectDiagnosticsEditor;
+use gpui::{div, EventEmitter, ParentElement, Render, ViewContext, WeakView};
+use ui::prelude::*;
+use ui::{Icon, IconButton, Tooltip};
+use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
 
 pub struct ToolbarControls {
-    editor: Option<WeakViewHandle<ProjectDiagnosticsEditor>>,
+    editor: Option<WeakView<ProjectDiagnosticsEditor>>,
 }
 
-impl Entity for ToolbarControls {
-    type Event = ();
-}
-
-impl View for ToolbarControls {
-    fn ui_name() -> &'static str {
-        "ToolbarControls"
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+impl Render for ToolbarControls {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let include_warnings = self
             .editor
             .as_ref()
-            .and_then(|editor| editor.upgrade(cx))
+            .and_then(|editor| editor.upgrade())
             .map(|editor| editor.read(cx).include_warnings)
             .unwrap_or(false);
+
         let tooltip = if include_warnings {
-            "Exclude Warnings".into()
+            "Exclude Warnings"
         } else {
-            "Include Warnings".into()
+            "Include Warnings"
         };
-        Flex::row()
-            .with_child(render_toggle_button(
-                0,
-                "icons/warning.svg",
-                include_warnings,
-                (tooltip, Some(Box::new(ToggleWarnings))),
-                cx,
-                move |this, cx| {
-                    if let Some(editor) = this.editor.and_then(|editor| editor.upgrade(cx)) {
+
+        div().child(
+            IconButton::new("toggle-warnings", Icon::ExclamationTriangle)
+                .tooltip(move |cx| Tooltip::text(tooltip, cx))
+                .on_click(cx.listener(|this, _, cx| {
+                    if let Some(editor) = this.editor.as_ref().and_then(|editor| editor.upgrade()) {
                         editor.update(cx, |editor, cx| {
-                            editor.toggle_warnings(&Default::default(), cx)
+                            editor.toggle_warnings(&Default::default(), cx);
                         });
                     }
-                },
-            ))
-            .into_any()
+                })),
+        )
     }
 }
 
+impl EventEmitter<ToolbarItemEvent> for ToolbarControls {}
+
 impl ToolbarItemView for ToolbarControls {
     fn set_active_pane_item(
         &mut self,
@@ -59,7 +48,7 @@ impl ToolbarItemView for ToolbarControls {
         if let Some(pane_item) = active_pane_item.as_ref() {
             if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
                 self.editor = Some(editor.downgrade());
-                ToolbarItemLocation::PrimaryRight { flex: None }
+                ToolbarItemLocation::PrimaryRight
             } else {
                 ToolbarItemLocation::Hidden
             }
@@ -74,42 +63,3 @@ impl ToolbarControls {
         ToolbarControls { editor: None }
     }
 }
-
-fn render_toggle_button<
-    F: 'static + Fn(&mut ToolbarControls, &mut EventContext<ToolbarControls>),
->(
-    index: usize,
-    icon: &'static str,
-    toggled: bool,
-    tooltip: (String, Option<Box<dyn Action>>),
-    cx: &mut ViewContext<ToolbarControls>,
-    on_click: F,
-) -> AnyElement<ToolbarControls> {
-    enum Button {}
-
-    let theme = theme::current(cx);
-    let (tooltip_text, action) = tooltip;
-
-    MouseEventHandler::new::<Button, _>(index, cx, |mouse_state, _| {
-        let style = theme
-            .workspace
-            .toolbar
-            .toggleable_tool
-            .in_state(toggled)
-            .style_for(mouse_state);
-        Svg::new(icon)
-            .with_color(style.color)
-            .constrained()
-            .with_width(style.icon_width)
-            .aligned()
-            .constrained()
-            .with_width(style.button_width)
-            .with_height(style.button_width)
-            .contained()
-            .with_style(style.container)
-    })
-    .with_cursor_style(CursorStyle::PointingHand)
-    .on_click(MouseButton::Left, move |_, view, cx| on_click(view, cx))
-    .with_tooltip::<Button>(index, tooltip_text, action, theme.tooltip.clone(), cx)
-    .into_any_named("quick action bar button")
-}

crates/diagnostics2/Cargo.toml 🔗

@@ -1,43 +0,0 @@
-[package]
-name = "diagnostics2"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-path = "src/diagnostics.rs"
-doctest = false
-
-[dependencies]
-collections = { path = "../collections" }
-editor = { package = "editor2", path = "../editor2" }
-gpui = { package = "gpui2", path = "../gpui2" }
-ui = { package = "ui2", path = "../ui2" }
-language = { package = "language2", path = "../language2" }
-lsp = { package = "lsp2", path = "../lsp2" }
-project = { package = "project2", path = "../project2" }
-settings = { package = "settings2", path = "../settings2" }
-theme = { package = "theme2", path = "../theme2" }
-util = { path = "../util" }
-workspace = { package = "workspace2", path = "../workspace2" }
-
-log.workspace = true
-anyhow.workspace = true
-futures.workspace = true
-schemars.workspace = true
-serde.workspace = true
-serde_derive.workspace = true
-smallvec.workspace = true
-postage.workspace = true
-
-[dev-dependencies]
-client = { package = "client2", path = "../client2", features = ["test-support"] }
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
-language = { package = "language2", path = "../language2", features = ["test-support"] }
-lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
-workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
-theme = { package = "theme2", path = "../theme2", features = ["test-support"] }
-
-serde_json.workspace = true
-unindent.workspace = true

crates/diagnostics2/src/diagnostics.rs 🔗

@@ -1,1612 +0,0 @@
-pub mod items;
-mod project_diagnostics_settings;
-mod toolbar_controls;
-
-use anyhow::{Context as _, Result};
-use collections::{HashMap, HashSet};
-use editor::{
-    diagnostic_block_renderer,
-    display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock},
-    highlight_diagnostic_message,
-    scroll::autoscroll::Autoscroll,
-    Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
-};
-use futures::future::try_join_all;
-use gpui::{
-    actions, div, svg, AnyElement, AnyView, AppContext, Context, EventEmitter, FocusHandle,
-    FocusableView, HighlightStyle, InteractiveElement, IntoElement, Model, ParentElement, Render,
-    SharedString, Styled, StyledText, Subscription, Task, View, ViewContext, VisualContext,
-    WeakView, WindowContext,
-};
-use language::{
-    Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection,
-    SelectionGoal,
-};
-use lsp::LanguageServerId;
-use project::{DiagnosticSummary, Project, ProjectPath};
-use project_diagnostics_settings::ProjectDiagnosticsSettings;
-use settings::Settings;
-use std::{
-    any::{Any, TypeId},
-    cmp::Ordering,
-    mem,
-    ops::Range,
-    path::PathBuf,
-    sync::Arc,
-};
-use theme::ActiveTheme;
-pub use toolbar_controls::ToolbarControls;
-use ui::{h_stack, prelude::*, Icon, IconElement, Label};
-use util::TryFutureExt;
-use workspace::{
-    item::{BreadcrumbText, Item, ItemEvent, ItemHandle},
-    ItemNavHistory, Pane, ToolbarItemLocation, Workspace,
-};
-
-actions!(diagnostics, [Deploy, ToggleWarnings]);
-
-const CONTEXT_LINE_COUNT: u32 = 1;
-
-pub fn init(cx: &mut AppContext) {
-    ProjectDiagnosticsSettings::register(cx);
-    cx.observe_new_views(ProjectDiagnosticsEditor::register)
-        .detach();
-}
-
-struct ProjectDiagnosticsEditor {
-    project: Model<Project>,
-    workspace: WeakView<Workspace>,
-    focus_handle: FocusHandle,
-    editor: View<Editor>,
-    summary: DiagnosticSummary,
-    excerpts: Model<MultiBuffer>,
-    path_states: Vec<PathState>,
-    paths_to_update: HashMap<LanguageServerId, HashSet<ProjectPath>>,
-    current_diagnostics: HashMap<LanguageServerId, HashSet<ProjectPath>>,
-    include_warnings: bool,
-    _subscriptions: Vec<Subscription>,
-}
-
-struct PathState {
-    path: ProjectPath,
-    diagnostic_groups: Vec<DiagnosticGroupState>,
-}
-
-#[derive(Clone, Debug, PartialEq)]
-struct Jump {
-    path: ProjectPath,
-    position: Point,
-    anchor: Anchor,
-}
-
-struct DiagnosticGroupState {
-    language_server_id: LanguageServerId,
-    primary_diagnostic: DiagnosticEntry<language::Anchor>,
-    primary_excerpt_ix: usize,
-    excerpts: Vec<ExcerptId>,
-    blocks: HashSet<BlockId>,
-    block_count: usize,
-}
-
-impl EventEmitter<EditorEvent> for ProjectDiagnosticsEditor {}
-
-impl Render for ProjectDiagnosticsEditor {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
-        let child = if self.path_states.is_empty() {
-            div()
-                .bg(cx.theme().colors().editor_background)
-                .flex()
-                .items_center()
-                .justify_center()
-                .size_full()
-                .child(Label::new("No problems in workspace"))
-        } else {
-            div().size_full().child(self.editor.clone())
-        };
-
-        div()
-            .track_focus(&self.focus_handle)
-            .size_full()
-            .on_action(cx.listener(Self::toggle_warnings))
-            .child(child)
-    }
-}
-
-impl ProjectDiagnosticsEditor {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.register_action(Self::deploy);
-    }
-
-    fn new(
-        project_handle: Model<Project>,
-        workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<Self>,
-    ) -> Self {
-        let project_event_subscription =
-            cx.subscribe(&project_handle, |this, _, event, cx| match event {
-                project::Event::DiskBasedDiagnosticsFinished { language_server_id } => {
-                    log::debug!("Disk based diagnostics finished for server {language_server_id}");
-                    this.update_excerpts(Some(*language_server_id), cx);
-                }
-                project::Event::DiagnosticsUpdated {
-                    language_server_id,
-                    path,
-                } => {
-                    log::debug!("Adding path {path:?} to update for server {language_server_id}");
-                    this.paths_to_update
-                        .entry(*language_server_id)
-                        .or_default()
-                        .insert(path.clone());
-                    if this.editor.read(cx).selections.all::<usize>(cx).is_empty()
-                        && !this.is_dirty(cx)
-                    {
-                        this.update_excerpts(Some(*language_server_id), cx);
-                    }
-                }
-                _ => {}
-            });
-
-        let focus_handle = cx.focus_handle();
-
-        let focus_in_subscription =
-            cx.on_focus_in(&focus_handle, |diagnostics, cx| diagnostics.focus_in(cx));
-
-        let excerpts = cx.new_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
-        let editor = cx.new_view(|cx| {
-            let mut editor =
-                Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), cx);
-            editor.set_vertical_scroll_margin(5, cx);
-            editor
-        });
-        let editor_event_subscription =
-            cx.subscribe(&editor, |this, _editor, event: &EditorEvent, cx| {
-                cx.emit(event.clone());
-                if event == &EditorEvent::Focused && this.path_states.is_empty() {
-                    cx.focus(&this.focus_handle);
-                }
-            });
-
-        let project = project_handle.read(cx);
-        let summary = project.diagnostic_summary(false, cx);
-        let mut this = Self {
-            project: project_handle,
-            summary,
-            workspace,
-            excerpts,
-            focus_handle,
-            editor,
-            path_states: Default::default(),
-            paths_to_update: HashMap::default(),
-            include_warnings: ProjectDiagnosticsSettings::get_global(cx).include_warnings,
-            current_diagnostics: HashMap::default(),
-            _subscriptions: vec![
-                project_event_subscription,
-                editor_event_subscription,
-                focus_in_subscription,
-            ],
-        };
-        this.update_excerpts(None, cx);
-        this
-    }
-
-    fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
-        if let Some(existing) = workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
-            workspace.activate_item(&existing, cx);
-        } else {
-            let workspace_handle = cx.view().downgrade();
-            let diagnostics = cx.new_view(|cx| {
-                ProjectDiagnosticsEditor::new(workspace.project().clone(), workspace_handle, cx)
-            });
-            workspace.add_item(Box::new(diagnostics), cx);
-        }
-    }
-
-    fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext<Self>) {
-        self.include_warnings = !self.include_warnings;
-        self.paths_to_update = self.current_diagnostics.clone();
-        self.update_excerpts(None, cx);
-        cx.notify();
-    }
-
-    fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
-        if self.focus_handle.is_focused(cx) && !self.path_states.is_empty() {
-            self.editor.focus_handle(cx).focus(cx)
-        }
-    }
-
-    fn update_excerpts(
-        &mut self,
-        language_server_id: Option<LanguageServerId>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        log::debug!("Updating excerpts for server {language_server_id:?}");
-        let mut paths_to_recheck = HashSet::default();
-        let mut new_summaries: HashMap<LanguageServerId, HashSet<ProjectPath>> = self
-            .project
-            .read(cx)
-            .diagnostic_summaries(false, cx)
-            .fold(HashMap::default(), |mut summaries, (path, server_id, _)| {
-                summaries.entry(server_id).or_default().insert(path);
-                summaries
-            });
-        let mut old_diagnostics = if let Some(language_server_id) = language_server_id {
-            new_summaries.retain(|server_id, _| server_id == &language_server_id);
-            self.paths_to_update.retain(|server_id, paths| {
-                if server_id == &language_server_id {
-                    paths_to_recheck.extend(paths.drain());
-                    false
-                } else {
-                    true
-                }
-            });
-            let mut old_diagnostics = HashMap::default();
-            if let Some(new_paths) = new_summaries.get(&language_server_id) {
-                if let Some(old_paths) = self
-                    .current_diagnostics
-                    .insert(language_server_id, new_paths.clone())
-                {
-                    old_diagnostics.insert(language_server_id, old_paths);
-                }
-            } else {
-                if let Some(old_paths) = self.current_diagnostics.remove(&language_server_id) {
-                    old_diagnostics.insert(language_server_id, old_paths);
-                }
-            }
-            old_diagnostics
-        } else {
-            paths_to_recheck.extend(self.paths_to_update.drain().flat_map(|(_, paths)| paths));
-            mem::replace(&mut self.current_diagnostics, new_summaries.clone())
-        };
-        for (server_id, new_paths) in new_summaries {
-            match old_diagnostics.remove(&server_id) {
-                Some(mut old_paths) => {
-                    paths_to_recheck.extend(
-                        new_paths
-                            .into_iter()
-                            .filter(|new_path| !old_paths.remove(new_path)),
-                    );
-                    paths_to_recheck.extend(old_paths);
-                }
-                None => paths_to_recheck.extend(new_paths),
-            }
-        }
-        paths_to_recheck.extend(old_diagnostics.into_iter().flat_map(|(_, paths)| paths));
-
-        if paths_to_recheck.is_empty() {
-            log::debug!("No paths to recheck for language server {language_server_id:?}");
-            return;
-        }
-        log::debug!(
-            "Rechecking {} paths for language server {:?}",
-            paths_to_recheck.len(),
-            language_server_id
-        );
-        let project = self.project.clone();
-        cx.spawn(|this, mut cx| {
-            async move {
-                let _: Vec<()> = try_join_all(paths_to_recheck.into_iter().map(|path| {
-                    let mut cx = cx.clone();
-                    let project = project.clone();
-                    let this = this.clone();
-                    async move {
-                        let buffer = project
-                            .update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx))?
-                            .await
-                            .with_context(|| format!("opening buffer for path {path:?}"))?;
-                        this.update(&mut cx, |this, cx| {
-                            this.populate_excerpts(path, language_server_id, buffer, cx);
-                        })
-                        .context("missing project")?;
-                        anyhow::Ok(())
-                    }
-                }))
-                .await
-                .context("rechecking diagnostics for paths")?;
-
-                this.update(&mut cx, |this, cx| {
-                    this.summary = this.project.read(cx).diagnostic_summary(false, cx);
-                    cx.emit(EditorEvent::TitleChanged);
-                })?;
-                anyhow::Ok(())
-            }
-            .log_err()
-        })
-        .detach();
-    }
-
-    fn populate_excerpts(
-        &mut self,
-        path: ProjectPath,
-        language_server_id: Option<LanguageServerId>,
-        buffer: Model<Buffer>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let was_empty = self.path_states.is_empty();
-        let snapshot = buffer.read(cx).snapshot();
-        let path_ix = match self.path_states.binary_search_by_key(&&path, |e| &e.path) {
-            Ok(ix) => ix,
-            Err(ix) => {
-                self.path_states.insert(
-                    ix,
-                    PathState {
-                        path: path.clone(),
-                        diagnostic_groups: Default::default(),
-                    },
-                );
-                ix
-            }
-        };
-
-        let mut prev_excerpt_id = if path_ix > 0 {
-            let prev_path_last_group = &self.path_states[path_ix - 1]
-                .diagnostic_groups
-                .last()
-                .unwrap();
-            prev_path_last_group.excerpts.last().unwrap().clone()
-        } else {
-            ExcerptId::min()
-        };
-
-        let path_state = &mut self.path_states[path_ix];
-        let mut groups_to_add = Vec::new();
-        let mut group_ixs_to_remove = Vec::new();
-        let mut blocks_to_add = Vec::new();
-        let mut blocks_to_remove = HashSet::default();
-        let mut first_excerpt_id = None;
-        let max_severity = if self.include_warnings {
-            DiagnosticSeverity::WARNING
-        } else {
-            DiagnosticSeverity::ERROR
-        };
-        let excerpts_snapshot = self.excerpts.update(cx, |excerpts, excerpts_cx| {
-            let mut old_groups = path_state.diagnostic_groups.iter().enumerate().peekable();
-            let mut new_groups = snapshot
-                .diagnostic_groups(language_server_id)
-                .into_iter()
-                .filter(|(_, group)| {
-                    group.entries[group.primary_ix].diagnostic.severity <= max_severity
-                })
-                .peekable();
-            loop {
-                let mut to_insert = None;
-                let mut to_remove = None;
-                let mut to_keep = None;
-                match (old_groups.peek(), new_groups.peek()) {
-                    (None, None) => break,
-                    (None, Some(_)) => to_insert = new_groups.next(),
-                    (Some((_, old_group)), None) => {
-                        if language_server_id.map_or(true, |id| id == old_group.language_server_id)
-                        {
-                            to_remove = old_groups.next();
-                        } else {
-                            to_keep = old_groups.next();
-                        }
-                    }
-                    (Some((_, old_group)), Some((_, new_group))) => {
-                        let old_primary = &old_group.primary_diagnostic;
-                        let new_primary = &new_group.entries[new_group.primary_ix];
-                        match compare_diagnostics(old_primary, new_primary, &snapshot) {
-                            Ordering::Less => {
-                                if language_server_id
-                                    .map_or(true, |id| id == old_group.language_server_id)
-                                {
-                                    to_remove = old_groups.next();
-                                } else {
-                                    to_keep = old_groups.next();
-                                }
-                            }
-                            Ordering::Equal => {
-                                to_keep = old_groups.next();
-                                new_groups.next();
-                            }
-                            Ordering::Greater => to_insert = new_groups.next(),
-                        }
-                    }
-                }
-
-                if let Some((language_server_id, group)) = to_insert {
-                    let mut group_state = DiagnosticGroupState {
-                        language_server_id,
-                        primary_diagnostic: group.entries[group.primary_ix].clone(),
-                        primary_excerpt_ix: 0,
-                        excerpts: Default::default(),
-                        blocks: Default::default(),
-                        block_count: 0,
-                    };
-                    let mut pending_range: Option<(Range<Point>, usize)> = None;
-                    let mut is_first_excerpt_for_group = true;
-                    for (ix, entry) in group.entries.iter().map(Some).chain([None]).enumerate() {
-                        let resolved_entry = entry.map(|e| e.resolve::<Point>(&snapshot));
-                        if let Some((range, start_ix)) = &mut pending_range {
-                            if let Some(entry) = resolved_entry.as_ref() {
-                                if entry.range.start.row
-                                    <= range.end.row + 1 + CONTEXT_LINE_COUNT * 2
-                                {
-                                    range.end = range.end.max(entry.range.end);
-                                    continue;
-                                }
-                            }
-
-                            let excerpt_start =
-                                Point::new(range.start.row.saturating_sub(CONTEXT_LINE_COUNT), 0);
-                            let excerpt_end = snapshot.clip_point(
-                                Point::new(range.end.row + CONTEXT_LINE_COUNT, u32::MAX),
-                                Bias::Left,
-                            );
-                            let excerpt_id = excerpts
-                                .insert_excerpts_after(
-                                    prev_excerpt_id,
-                                    buffer.clone(),
-                                    [ExcerptRange {
-                                        context: excerpt_start..excerpt_end,
-                                        primary: Some(range.clone()),
-                                    }],
-                                    excerpts_cx,
-                                )
-                                .pop()
-                                .unwrap();
-
-                            prev_excerpt_id = excerpt_id.clone();
-                            first_excerpt_id.get_or_insert_with(|| prev_excerpt_id.clone());
-                            group_state.excerpts.push(excerpt_id.clone());
-                            let header_position = (excerpt_id.clone(), language::Anchor::MIN);
-
-                            if is_first_excerpt_for_group {
-                                is_first_excerpt_for_group = false;
-                                let mut primary =
-                                    group.entries[group.primary_ix].diagnostic.clone();
-                                primary.message =
-                                    primary.message.split('\n').next().unwrap().to_string();
-                                group_state.block_count += 1;
-                                blocks_to_add.push(BlockProperties {
-                                    position: header_position,
-                                    height: 2,
-                                    style: BlockStyle::Sticky,
-                                    render: diagnostic_header_renderer(primary),
-                                    disposition: BlockDisposition::Above,
-                                });
-                            }
-
-                            for entry in &group.entries[*start_ix..ix] {
-                                let mut diagnostic = entry.diagnostic.clone();
-                                if diagnostic.is_primary {
-                                    group_state.primary_excerpt_ix = group_state.excerpts.len() - 1;
-                                    diagnostic.message =
-                                        entry.diagnostic.message.split('\n').skip(1).collect();
-                                }
-
-                                if !diagnostic.message.is_empty() {
-                                    group_state.block_count += 1;
-                                    blocks_to_add.push(BlockProperties {
-                                        position: (excerpt_id.clone(), entry.range.start),
-                                        height: diagnostic.message.matches('\n').count() as u8 + 1,
-                                        style: BlockStyle::Fixed,
-                                        render: diagnostic_block_renderer(diagnostic, true),
-                                        disposition: BlockDisposition::Below,
-                                    });
-                                }
-                            }
-
-                            pending_range.take();
-                        }
-
-                        if let Some(entry) = resolved_entry {
-                            pending_range = Some((entry.range.clone(), ix));
-                        }
-                    }
-
-                    groups_to_add.push(group_state);
-                } else if let Some((group_ix, group_state)) = to_remove {
-                    excerpts.remove_excerpts(group_state.excerpts.iter().copied(), excerpts_cx);
-                    group_ixs_to_remove.push(group_ix);
-                    blocks_to_remove.extend(group_state.blocks.iter().copied());
-                } else if let Some((_, group)) = to_keep {
-                    prev_excerpt_id = group.excerpts.last().unwrap().clone();
-                    first_excerpt_id.get_or_insert_with(|| prev_excerpt_id.clone());
-                }
-            }
-
-            excerpts.snapshot(excerpts_cx)
-        });
-
-        self.editor.update(cx, |editor, cx| {
-            editor.remove_blocks(blocks_to_remove, None, cx);
-            let block_ids = editor.insert_blocks(
-                blocks_to_add.into_iter().map(|block| {
-                    let (excerpt_id, text_anchor) = block.position;
-                    BlockProperties {
-                        position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor),
-                        height: block.height,
-                        style: block.style,
-                        render: block.render,
-                        disposition: block.disposition,
-                    }
-                }),
-                Some(Autoscroll::fit()),
-                cx,
-            );
-
-            let mut block_ids = block_ids.into_iter();
-            for group_state in &mut groups_to_add {
-                group_state.blocks = block_ids.by_ref().take(group_state.block_count).collect();
-            }
-        });
-
-        for ix in group_ixs_to_remove.into_iter().rev() {
-            path_state.diagnostic_groups.remove(ix);
-        }
-        path_state.diagnostic_groups.extend(groups_to_add);
-        path_state.diagnostic_groups.sort_unstable_by(|a, b| {
-            let range_a = &a.primary_diagnostic.range;
-            let range_b = &b.primary_diagnostic.range;
-            range_a
-                .start
-                .cmp(&range_b.start, &snapshot)
-                .then_with(|| range_a.end.cmp(&range_b.end, &snapshot))
-        });
-
-        if path_state.diagnostic_groups.is_empty() {
-            self.path_states.remove(path_ix);
-        }
-
-        self.editor.update(cx, |editor, cx| {
-            let groups;
-            let mut selections;
-            let new_excerpt_ids_by_selection_id;
-            if was_empty {
-                groups = self.path_states.first()?.diagnostic_groups.as_slice();
-                new_excerpt_ids_by_selection_id = [(0, ExcerptId::min())].into_iter().collect();
-                selections = vec![Selection {
-                    id: 0,
-                    start: 0,
-                    end: 0,
-                    reversed: false,
-                    goal: SelectionGoal::None,
-                }];
-            } else {
-                groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice();
-                new_excerpt_ids_by_selection_id =
-                    editor.change_selections(Some(Autoscroll::fit()), cx, |s| s.refresh());
-                selections = editor.selections.all::<usize>(cx);
-            }
-
-            // If any selection has lost its position, move it to start of the next primary diagnostic.
-            let snapshot = editor.snapshot(cx);
-            for selection in &mut selections {
-                if let Some(new_excerpt_id) = new_excerpt_ids_by_selection_id.get(&selection.id) {
-                    let group_ix = match groups.binary_search_by(|probe| {
-                        probe
-                            .excerpts
-                            .last()
-                            .unwrap()
-                            .cmp(new_excerpt_id, &snapshot.buffer_snapshot)
-                    }) {
-                        Ok(ix) | Err(ix) => ix,
-                    };
-                    if let Some(group) = groups.get(group_ix) {
-                        let offset = excerpts_snapshot
-                            .anchor_in_excerpt(
-                                group.excerpts[group.primary_excerpt_ix].clone(),
-                                group.primary_diagnostic.range.start,
-                            )
-                            .to_offset(&excerpts_snapshot);
-                        selection.start = offset;
-                        selection.end = offset;
-                    }
-                }
-            }
-            editor.change_selections(None, cx, |s| {
-                s.select(selections);
-            });
-            Some(())
-        });
-
-        if self.path_states.is_empty() {
-            if self.editor.focus_handle(cx).is_focused(cx) {
-                cx.focus(&self.focus_handle);
-            }
-        } else if self.focus_handle.is_focused(cx) {
-            let focus_handle = self.editor.focus_handle(cx);
-            cx.focus(&focus_handle);
-        }
-        cx.notify();
-    }
-}
-
-impl FocusableView for ProjectDiagnosticsEditor {
-    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
-        self.focus_handle.clone()
-    }
-}
-
-impl Item for ProjectDiagnosticsEditor {
-    type Event = EditorEvent;
-
-    fn to_item_events(event: &EditorEvent, f: impl FnMut(ItemEvent)) {
-        Editor::to_item_events(event, f)
-    }
-
-    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
-        self.editor.update(cx, |editor, cx| editor.deactivated(cx));
-    }
-
-    fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
-        self.editor
-            .update(cx, |editor, cx| editor.navigate(data, cx))
-    }
-
-    fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
-        Some("Project Diagnostics".into())
-    }
-
-    fn tab_content(&self, _detail: Option<usize>, selected: bool, _: &WindowContext) -> AnyElement {
-        if self.summary.error_count == 0 && self.summary.warning_count == 0 {
-            let label = Label::new("No problems");
-            label.into_any_element()
-        } else {
-            h_stack()
-                .gap_1()
-                .when(self.summary.error_count > 0, |then| {
-                    then.child(
-                        h_stack()
-                            .gap_1()
-                            .child(IconElement::new(Icon::XCircle).color(Color::Error))
-                            .child(Label::new(self.summary.error_count.to_string()).color(
-                                if selected {
-                                    Color::Default
-                                } else {
-                                    Color::Muted
-                                },
-                            )),
-                    )
-                })
-                .when(self.summary.warning_count > 0, |then| {
-                    then.child(
-                        h_stack()
-                            .gap_1()
-                            .child(
-                                IconElement::new(Icon::ExclamationTriangle).color(Color::Warning),
-                            )
-                            .child(Label::new(self.summary.warning_count.to_string()).color(
-                                if selected {
-                                    Color::Default
-                                } else {
-                                    Color::Muted
-                                },
-                            )),
-                    )
-                })
-                .into_any_element()
-        }
-    }
-
-    fn for_each_project_item(
-        &self,
-        cx: &AppContext,
-        f: &mut dyn FnMut(gpui::EntityId, &dyn project::Item),
-    ) {
-        self.editor.for_each_project_item(cx, f)
-    }
-
-    fn is_singleton(&self, _: &AppContext) -> bool {
-        false
-    }
-
-    fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
-        self.editor.update(cx, |editor, _| {
-            editor.set_nav_history(Some(nav_history));
-        });
-    }
-
-    fn clone_on_split(
-        &self,
-        _workspace_id: workspace::WorkspaceId,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<View<Self>>
-    where
-        Self: Sized,
-    {
-        Some(cx.new_view(|cx| {
-            ProjectDiagnosticsEditor::new(self.project.clone(), self.workspace.clone(), cx)
-        }))
-    }
-
-    fn is_dirty(&self, cx: &AppContext) -> bool {
-        self.excerpts.read(cx).is_dirty(cx)
-    }
-
-    fn has_conflict(&self, cx: &AppContext) -> bool {
-        self.excerpts.read(cx).has_conflict(cx)
-    }
-
-    fn can_save(&self, _: &AppContext) -> bool {
-        true
-    }
-
-    fn save(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
-        self.editor.save(project, cx)
-    }
-
-    fn save_as(
-        &mut self,
-        _: Model<Project>,
-        _: PathBuf,
-        _: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
-        unreachable!()
-    }
-
-    fn reload(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
-        self.editor.reload(project, cx)
-    }
-
-    fn act_as_type<'a>(
-        &'a self,
-        type_id: TypeId,
-        self_handle: &'a View<Self>,
-        _: &'a AppContext,
-    ) -> Option<AnyView> {
-        if type_id == TypeId::of::<Self>() {
-            Some(self_handle.to_any())
-        } else if type_id == TypeId::of::<Editor>() {
-            Some(self.editor.to_any())
-        } else {
-            None
-        }
-    }
-
-    fn breadcrumb_location(&self) -> ToolbarItemLocation {
-        ToolbarItemLocation::PrimaryLeft
-    }
-
-    fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
-        self.editor.breadcrumbs(theme, cx)
-    }
-
-    fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
-        self.editor
-            .update(cx, |editor, cx| editor.added_to_workspace(workspace, cx));
-    }
-
-    fn serialized_item_kind() -> Option<&'static str> {
-        Some("diagnostics")
-    }
-
-    fn deserialize(
-        project: Model<Project>,
-        workspace: WeakView<Workspace>,
-        _workspace_id: workspace::WorkspaceId,
-        _item_id: workspace::ItemId,
-        cx: &mut ViewContext<Pane>,
-    ) -> Task<Result<View<Self>>> {
-        Task::ready(Ok(cx.new_view(|cx| Self::new(project, workspace, cx))))
-    }
-}
-
-fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
-    let (message, code_ranges) = highlight_diagnostic_message(&diagnostic);
-    let message: SharedString = message.into();
-    Arc::new(move |cx| {
-        let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
-        h_stack()
-            .id("diagnostic header")
-            .py_2()
-            .pl_10()
-            .pr_5()
-            .w_full()
-            .justify_between()
-            .gap_2()
-            .child(
-                h_stack()
-                    .gap_3()
-                    .map(|stack| {
-                        stack.child(
-                            svg()
-                                .size(cx.text_style().font_size)
-                                .flex_none()
-                                .map(|icon| {
-                                    if diagnostic.severity == DiagnosticSeverity::ERROR {
-                                        icon.path(Icon::XCircle.path())
-                                            .text_color(Color::Error.color(cx))
-                                    } else {
-                                        icon.path(Icon::ExclamationTriangle.path())
-                                            .text_color(Color::Warning.color(cx))
-                                    }
-                                }),
-                        )
-                    })
-                    .child(
-                        h_stack()
-                            .gap_1()
-                            .child(
-                                StyledText::new(message.clone()).with_highlights(
-                                    &cx.text_style(),
-                                    code_ranges
-                                        .iter()
-                                        .map(|range| (range.clone(), highlight_style)),
-                                ),
-                            )
-                            .when_some(diagnostic.code.as_ref(), |stack, code| {
-                                stack.child(
-                                    div()
-                                        .child(SharedString::from(format!("({code})")))
-                                        .text_color(cx.theme().colors().text_muted),
-                                )
-                            }),
-                    ),
-            )
-            .child(
-                h_stack()
-                    .gap_1()
-                    .when_some(diagnostic.source.as_ref(), |stack, source| {
-                        stack.child(
-                            div()
-                                .child(SharedString::from(source.clone()))
-                                .text_color(cx.theme().colors().text_muted),
-                        )
-                    }),
-            )
-            .into_any_element()
-    })
-}
-
-fn compare_diagnostics<L: language::ToOffset, R: language::ToOffset>(
-    lhs: &DiagnosticEntry<L>,
-    rhs: &DiagnosticEntry<R>,
-    snapshot: &language::BufferSnapshot,
-) -> Ordering {
-    lhs.range
-        .start
-        .to_offset(snapshot)
-        .cmp(&rhs.range.start.to_offset(snapshot))
-        .then_with(|| {
-            lhs.range
-                .end
-                .to_offset(snapshot)
-                .cmp(&rhs.range.end.to_offset(snapshot))
-        })
-        .then_with(|| lhs.diagnostic.message.cmp(&rhs.diagnostic.message))
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use editor::{
-        display_map::{BlockContext, TransformBlock},
-        DisplayPoint,
-    };
-    use gpui::{px, TestAppContext, VisualTestContext, WindowContext};
-    use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped};
-    use project::FakeFs;
-    use serde_json::json;
-    use settings::SettingsStore;
-    use unindent::Unindent as _;
-
-    #[gpui::test]
-    async fn test_diagnostics(cx: &mut TestAppContext) {
-        init_test(cx);
-
-        let fs = FakeFs::new(cx.executor());
-        fs.insert_tree(
-            "/test",
-            json!({
-                "consts.rs": "
-                    const a: i32 = 'a';
-                    const b: i32 = c;
-                "
-                .unindent(),
-
-                "main.rs": "
-                    fn main() {
-                        let x = vec![];
-                        let y = vec![];
-                        a(x);
-                        b(y);
-                        // comment 1
-                        // comment 2
-                        c(y);
-                        d(x);
-                    }
-                "
-                .unindent(),
-            }),
-        )
-        .await;
-
-        let language_server_id = LanguageServerId(0);
-        let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
-        let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-        let cx = &mut VisualTestContext::from_window(*window, cx);
-        let workspace = window.root(cx).unwrap();
-
-        // Create some diagnostics
-        project.update(cx, |project, cx| {
-            project
-                .update_diagnostic_entries(
-                    language_server_id,
-                    PathBuf::from("/test/main.rs"),
-                    None,
-                    vec![
-                        DiagnosticEntry {
-                            range: Unclipped(PointUtf16::new(1, 8))..Unclipped(PointUtf16::new(1, 9)),
-                            diagnostic: Diagnostic {
-                                message:
-                                    "move occurs because `x` has type `Vec<char>`, which does not implement the `Copy` trait"
-                                        .to_string(),
-                                severity: DiagnosticSeverity::INFORMATION,
-                                is_primary: false,
-                                is_disk_based: true,
-                                group_id: 1,
-                                ..Default::default()
-                            },
-                        },
-                        DiagnosticEntry {
-                            range: Unclipped(PointUtf16::new(2, 8))..Unclipped(PointUtf16::new(2, 9)),
-                            diagnostic: Diagnostic {
-                                message:
-                                    "move occurs because `y` has type `Vec<char>`, which does not implement the `Copy` trait"
-                                        .to_string(),
-                                severity: DiagnosticSeverity::INFORMATION,
-                                is_primary: false,
-                                is_disk_based: true,
-                                group_id: 0,
-                                ..Default::default()
-                            },
-                        },
-                        DiagnosticEntry {
-                            range: Unclipped(PointUtf16::new(3, 6))..Unclipped(PointUtf16::new(3, 7)),
-                            diagnostic: Diagnostic {
-                                message: "value moved here".to_string(),
-                                severity: DiagnosticSeverity::INFORMATION,
-                                is_primary: false,
-                                is_disk_based: true,
-                                group_id: 1,
-                                ..Default::default()
-                            },
-                        },
-                        DiagnosticEntry {
-                            range: Unclipped(PointUtf16::new(4, 6))..Unclipped(PointUtf16::new(4, 7)),
-                            diagnostic: Diagnostic {
-                                message: "value moved here".to_string(),
-                                severity: DiagnosticSeverity::INFORMATION,
-                                is_primary: false,
-                                is_disk_based: true,
-                                group_id: 0,
-                                ..Default::default()
-                            },
-                        },
-                        DiagnosticEntry {
-                            range: Unclipped(PointUtf16::new(7, 6))..Unclipped(PointUtf16::new(7, 7)),
-                            diagnostic: Diagnostic {
-                                message: "use of moved value\nvalue used here after move".to_string(),
-                                severity: DiagnosticSeverity::ERROR,
-                                is_primary: true,
-                                is_disk_based: true,
-                                group_id: 0,
-                                ..Default::default()
-                            },
-                        },
-                        DiagnosticEntry {
-                            range: Unclipped(PointUtf16::new(8, 6))..Unclipped(PointUtf16::new(8, 7)),
-                            diagnostic: Diagnostic {
-                                message: "use of moved value\nvalue used here after move".to_string(),
-                                severity: DiagnosticSeverity::ERROR,
-                                is_primary: true,
-                                is_disk_based: true,
-                                group_id: 1,
-                                ..Default::default()
-                            },
-                        },
-                    ],
-                    cx,
-                )
-                .unwrap();
-        });
-
-        // Open the project diagnostics view while there are already diagnostics.
-        let view = window.build_view(cx, |cx| {
-            ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
-        });
-
-        view.next_notification(cx).await;
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (15, "collapsed context".into()),
-                    (16, "diagnostic header".into()),
-                    (25, "collapsed context".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    //
-                    // main.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "    let x = vec![];\n",
-                    "    let y = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    a(x);\n",
-                    "    b(y);\n",
-                    "\n", // supporting diagnostic
-                    "    // comment 1\n",
-                    "    // comment 2\n",
-                    "    c(y);\n",
-                    "\n", // supporting diagnostic
-                    "    d(x);\n",
-                    "\n", // context ellipsis
-                    // diagnostic group 2
-                    "\n", // primary message
-                    "\n", // padding
-                    "fn main() {\n",
-                    "    let x = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    let y = vec![];\n",
-                    "    a(x);\n",
-                    "\n", // supporting diagnostic
-                    "    b(y);\n",
-                    "\n", // context ellipsis
-                    "    c(y);\n",
-                    "    d(x);\n",
-                    "\n", // supporting diagnostic
-                    "}"
-                )
-            );
-
-            // Cursor is at the first diagnostic
-            view.editor.update(cx, |editor, cx| {
-                assert_eq!(
-                    editor.selections.display_ranges(cx),
-                    [DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)]
-                );
-            });
-        });
-
-        // Diagnostics are added for another earlier path.
-        project.update(cx, |project, cx| {
-            project.disk_based_diagnostics_started(language_server_id, cx);
-            project
-                .update_diagnostic_entries(
-                    language_server_id,
-                    PathBuf::from("/test/consts.rs"),
-                    None,
-                    vec![DiagnosticEntry {
-                        range: Unclipped(PointUtf16::new(0, 15))..Unclipped(PointUtf16::new(0, 15)),
-                        diagnostic: Diagnostic {
-                            message: "mismatched types\nexpected `usize`, found `char`".to_string(),
-                            severity: DiagnosticSeverity::ERROR,
-                            is_primary: true,
-                            is_disk_based: true,
-                            group_id: 0,
-                            ..Default::default()
-                        },
-                    }],
-                    cx,
-                )
-                .unwrap();
-            project.disk_based_diagnostics_finished(language_server_id, cx);
-        });
-
-        view.next_notification(cx).await;
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (7, "path header block".into()),
-                    (9, "diagnostic header".into()),
-                    (22, "collapsed context".into()),
-                    (23, "diagnostic header".into()),
-                    (32, "collapsed context".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    //
-                    // consts.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "const a: i32 = 'a';\n",
-                    "\n", // supporting diagnostic
-                    "const b: i32 = c;\n",
-                    //
-                    // main.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "    let x = vec![];\n",
-                    "    let y = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    a(x);\n",
-                    "    b(y);\n",
-                    "\n", // supporting diagnostic
-                    "    // comment 1\n",
-                    "    // comment 2\n",
-                    "    c(y);\n",
-                    "\n", // supporting diagnostic
-                    "    d(x);\n",
-                    "\n", // collapsed context
-                    // diagnostic group 2
-                    "\n", // primary message
-                    "\n", // filename
-                    "fn main() {\n",
-                    "    let x = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    let y = vec![];\n",
-                    "    a(x);\n",
-                    "\n", // supporting diagnostic
-                    "    b(y);\n",
-                    "\n", // context ellipsis
-                    "    c(y);\n",
-                    "    d(x);\n",
-                    "\n", // supporting diagnostic
-                    "}"
-                )
-            );
-
-            // Cursor keeps its position.
-            view.editor.update(cx, |editor, cx| {
-                assert_eq!(
-                    editor.selections.display_ranges(cx),
-                    [DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)]
-                );
-            });
-        });
-
-        // Diagnostics are added to the first path
-        project.update(cx, |project, cx| {
-            project.disk_based_diagnostics_started(language_server_id, cx);
-            project
-                .update_diagnostic_entries(
-                    language_server_id,
-                    PathBuf::from("/test/consts.rs"),
-                    None,
-                    vec![
-                        DiagnosticEntry {
-                            range: Unclipped(PointUtf16::new(0, 15))
-                                ..Unclipped(PointUtf16::new(0, 15)),
-                            diagnostic: Diagnostic {
-                                message: "mismatched types\nexpected `usize`, found `char`"
-                                    .to_string(),
-                                severity: DiagnosticSeverity::ERROR,
-                                is_primary: true,
-                                is_disk_based: true,
-                                group_id: 0,
-                                ..Default::default()
-                            },
-                        },
-                        DiagnosticEntry {
-                            range: Unclipped(PointUtf16::new(1, 15))
-                                ..Unclipped(PointUtf16::new(1, 15)),
-                            diagnostic: Diagnostic {
-                                message: "unresolved name `c`".to_string(),
-                                severity: DiagnosticSeverity::ERROR,
-                                is_primary: true,
-                                is_disk_based: true,
-                                group_id: 1,
-                                ..Default::default()
-                            },
-                        },
-                    ],
-                    cx,
-                )
-                .unwrap();
-            project.disk_based_diagnostics_finished(language_server_id, cx);
-        });
-
-        view.next_notification(cx).await;
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (7, "collapsed context".into()),
-                    (8, "diagnostic header".into()),
-                    (13, "path header block".into()),
-                    (15, "diagnostic header".into()),
-                    (28, "collapsed context".into()),
-                    (29, "diagnostic header".into()),
-                    (38, "collapsed context".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    //
-                    // consts.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "const a: i32 = 'a';\n",
-                    "\n", // supporting diagnostic
-                    "const b: i32 = c;\n",
-                    "\n", // context ellipsis
-                    // diagnostic group 2
-                    "\n", // primary message
-                    "\n", // padding
-                    "const a: i32 = 'a';\n",
-                    "const b: i32 = c;\n",
-                    "\n", // supporting diagnostic
-                    //
-                    // main.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "    let x = vec![];\n",
-                    "    let y = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    a(x);\n",
-                    "    b(y);\n",
-                    "\n", // supporting diagnostic
-                    "    // comment 1\n",
-                    "    // comment 2\n",
-                    "    c(y);\n",
-                    "\n", // supporting diagnostic
-                    "    d(x);\n",
-                    "\n", // context ellipsis
-                    // diagnostic group 2
-                    "\n", // primary message
-                    "\n", // filename
-                    "fn main() {\n",
-                    "    let x = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    let y = vec![];\n",
-                    "    a(x);\n",
-                    "\n", // supporting diagnostic
-                    "    b(y);\n",
-                    "\n", // context ellipsis
-                    "    c(y);\n",
-                    "    d(x);\n",
-                    "\n", // supporting diagnostic
-                    "}"
-                )
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
-        init_test(cx);
-
-        let fs = FakeFs::new(cx.executor());
-        fs.insert_tree(
-            "/test",
-            json!({
-                "main.js": "
-                    a();
-                    b();
-                    c();
-                    d();
-                    e();
-                ".unindent()
-            }),
-        )
-        .await;
-
-        let server_id_1 = LanguageServerId(100);
-        let server_id_2 = LanguageServerId(101);
-        let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
-        let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-        let cx = &mut VisualTestContext::from_window(*window, cx);
-        let workspace = window.root(cx).unwrap();
-
-        let view = window.build_view(cx, |cx| {
-            ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
-        });
-
-        // Two language servers start updating diagnostics
-        project.update(cx, |project, cx| {
-            project.disk_based_diagnostics_started(server_id_1, cx);
-            project.disk_based_diagnostics_started(server_id_2, cx);
-            project
-                .update_diagnostic_entries(
-                    server_id_1,
-                    PathBuf::from("/test/main.js"),
-                    None,
-                    vec![DiagnosticEntry {
-                        range: Unclipped(PointUtf16::new(0, 0))..Unclipped(PointUtf16::new(0, 1)),
-                        diagnostic: Diagnostic {
-                            message: "error 1".to_string(),
-                            severity: DiagnosticSeverity::WARNING,
-                            is_primary: true,
-                            is_disk_based: true,
-                            group_id: 1,
-                            ..Default::default()
-                        },
-                    }],
-                    cx,
-                )
-                .unwrap();
-        });
-
-        // The first language server finishes
-        project.update(cx, |project, cx| {
-            project.disk_based_diagnostics_finished(server_id_1, cx);
-        });
-
-        // Only the first language server's diagnostics are shown.
-        cx.executor().run_until_parked();
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "a();\n", //
-                    "b();",
-                )
-            );
-        });
-
-        // The second language server finishes
-        project.update(cx, |project, cx| {
-            project
-                .update_diagnostic_entries(
-                    server_id_2,
-                    PathBuf::from("/test/main.js"),
-                    None,
-                    vec![DiagnosticEntry {
-                        range: Unclipped(PointUtf16::new(1, 0))..Unclipped(PointUtf16::new(1, 1)),
-                        diagnostic: Diagnostic {
-                            message: "warning 1".to_string(),
-                            severity: DiagnosticSeverity::ERROR,
-                            is_primary: true,
-                            is_disk_based: true,
-                            group_id: 2,
-                            ..Default::default()
-                        },
-                    }],
-                    cx,
-                )
-                .unwrap();
-            project.disk_based_diagnostics_finished(server_id_2, cx);
-        });
-
-        // Both language server's diagnostics are shown.
-        cx.executor().run_until_parked();
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (6, "collapsed context".into()),
-                    (7, "diagnostic header".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "a();\n", // location
-                    "b();\n", //
-                    "\n",     // collapsed context
-                    // diagnostic group 2
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "a();\n", // context
-                    "b();\n", //
-                    "c();",   // context
-                )
-            );
-        });
-
-        // Both language servers start updating diagnostics, and the first server finishes.
-        project.update(cx, |project, cx| {
-            project.disk_based_diagnostics_started(server_id_1, cx);
-            project.disk_based_diagnostics_started(server_id_2, cx);
-            project
-                .update_diagnostic_entries(
-                    server_id_1,
-                    PathBuf::from("/test/main.js"),
-                    None,
-                    vec![DiagnosticEntry {
-                        range: Unclipped(PointUtf16::new(2, 0))..Unclipped(PointUtf16::new(2, 1)),
-                        diagnostic: Diagnostic {
-                            message: "warning 2".to_string(),
-                            severity: DiagnosticSeverity::WARNING,
-                            is_primary: true,
-                            is_disk_based: true,
-                            group_id: 1,
-                            ..Default::default()
-                        },
-                    }],
-                    cx,
-                )
-                .unwrap();
-            project
-                .update_diagnostic_entries(
-                    server_id_2,
-                    PathBuf::from("/test/main.rs"),
-                    None,
-                    vec![],
-                    cx,
-                )
-                .unwrap();
-            project.disk_based_diagnostics_finished(server_id_1, cx);
-        });
-
-        // Only the first language server's diagnostics are updated.
-        cx.executor().run_until_parked();
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (7, "collapsed context".into()),
-                    (8, "diagnostic header".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "a();\n", // location
-                    "b();\n", //
-                    "c();\n", // context
-                    "\n",     // collapsed context
-                    // diagnostic group 2
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "b();\n", // context
-                    "c();\n", //
-                    "d();",   // context
-                )
-            );
-        });
-
-        // The second language server finishes.
-        project.update(cx, |project, cx| {
-            project
-                .update_diagnostic_entries(
-                    server_id_2,
-                    PathBuf::from("/test/main.js"),
-                    None,
-                    vec![DiagnosticEntry {
-                        range: Unclipped(PointUtf16::new(3, 0))..Unclipped(PointUtf16::new(3, 1)),
-                        diagnostic: Diagnostic {
-                            message: "warning 2".to_string(),
-                            severity: DiagnosticSeverity::WARNING,
-                            is_primary: true,
-                            is_disk_based: true,
-                            group_id: 1,
-                            ..Default::default()
-                        },
-                    }],
-                    cx,
-                )
-                .unwrap();
-            project.disk_based_diagnostics_finished(server_id_2, cx);
-        });
-
-        // Both language servers' diagnostics are updated.
-        cx.executor().run_until_parked();
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (7, "collapsed context".into()),
-                    (8, "diagnostic header".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "b();\n", // location
-                    "c();\n", //
-                    "d();\n", // context
-                    "\n",     // collapsed context
-                    // diagnostic group 2
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "c();\n", // context
-                    "d();\n", //
-                    "e();",   // context
-                )
-            );
-        });
-    }
-
-    fn init_test(cx: &mut TestAppContext) {
-        cx.update(|cx| {
-            let settings = SettingsStore::test(cx);
-            cx.set_global(settings);
-            theme::init(theme::LoadThemes::JustBase, cx);
-            language::init(cx);
-            client::init_settings(cx);
-            workspace::init_settings(cx);
-            Project::init_settings(cx);
-            crate::init(cx);
-        });
-    }
-
-    fn editor_blocks(editor: &View<Editor>, cx: &mut WindowContext) -> Vec<(u32, SharedString)> {
-        editor.update(cx, |editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            snapshot
-                .blocks_in_range(0..snapshot.max_point().row())
-                .enumerate()
-                .filter_map(|(ix, (row, block))| {
-                    let name = match block {
-                        TransformBlock::Custom(block) => block
-                            .render(&mut BlockContext {
-                                view_context: cx,
-                                anchor_x: px(0.),
-                                gutter_padding: px(0.),
-                                gutter_width: px(0.),
-                                line_height: px(0.),
-                                em_width: px(0.),
-                                block_id: ix,
-                                editor_style: &editor::EditorStyle::default(),
-                            })
-                            .inner_id()?
-                            .try_into()
-                            .ok()?,
-
-                        TransformBlock::ExcerptHeader {
-                            starts_new_buffer, ..
-                        } => {
-                            if *starts_new_buffer {
-                                "path header block".into()
-                            } else {
-                                "collapsed context".into()
-                            }
-                        }
-                    };
-
-                    Some((row, name))
-                })
-                .collect()
-        })
-    }
-}

crates/diagnostics2/src/items.rs 🔗

@@ -1,187 +0,0 @@
-use collections::HashSet;
-use editor::Editor;
-use gpui::{
-    rems, EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, View,
-    ViewContext, WeakView,
-};
-use language::Diagnostic;
-use lsp::LanguageServerId;
-use ui::{h_stack, prelude::*, Button, ButtonLike, Color, Icon, IconElement, Label, Tooltip};
-use workspace::{item::ItemHandle, StatusItemView, ToolbarItemEvent, Workspace};
-
-use crate::{Deploy, ProjectDiagnosticsEditor};
-
-pub struct DiagnosticIndicator {
-    summary: project::DiagnosticSummary,
-    active_editor: Option<WeakView<Editor>>,
-    workspace: WeakView<Workspace>,
-    current_diagnostic: Option<Diagnostic>,
-    in_progress_checks: HashSet<LanguageServerId>,
-    _observe_active_editor: Option<Subscription>,
-}
-
-impl Render for DiagnosticIndicator {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) {
-            (0, 0) => h_stack().child(
-                IconElement::new(Icon::Check)
-                    .size(IconSize::Small)
-                    .color(Color::Success),
-            ),
-            (0, warning_count) => h_stack()
-                .gap_1()
-                .child(
-                    IconElement::new(Icon::ExclamationTriangle)
-                        .size(IconSize::Small)
-                        .color(Color::Warning),
-                )
-                .child(Label::new(warning_count.to_string()).size(LabelSize::Small)),
-            (error_count, 0) => h_stack()
-                .gap_1()
-                .child(
-                    IconElement::new(Icon::XCircle)
-                        .size(IconSize::Small)
-                        .color(Color::Error),
-                )
-                .child(Label::new(error_count.to_string()).size(LabelSize::Small)),
-            (error_count, warning_count) => h_stack()
-                .gap_1()
-                .child(
-                    IconElement::new(Icon::XCircle)
-                        .size(IconSize::Small)
-                        .color(Color::Error),
-                )
-                .child(Label::new(error_count.to_string()).size(LabelSize::Small))
-                .child(
-                    IconElement::new(Icon::ExclamationTriangle)
-                        .size(IconSize::Small)
-                        .color(Color::Warning),
-                )
-                .child(Label::new(warning_count.to_string()).size(LabelSize::Small)),
-        };
-
-        let status = if !self.in_progress_checks.is_empty() {
-            Some(
-                Label::new("Checking…")
-                    .size(LabelSize::Small)
-                    .into_any_element(),
-            )
-        } else if let Some(diagnostic) = &self.current_diagnostic {
-            let message = diagnostic.message.split('\n').next().unwrap().to_string();
-            Some(
-                Button::new("diagnostic_message", message)
-                    .label_size(LabelSize::Small)
-                    .tooltip(|cx| {
-                        Tooltip::for_action("Next Diagnostic", &editor::GoToDiagnostic, cx)
-                    })
-                    .on_click(cx.listener(|this, _, cx| {
-                        this.go_to_next_diagnostic(cx);
-                    }))
-                    .into_any_element(),
-            )
-        } else {
-            None
-        };
-
-        h_stack()
-            .h(rems(1.375))
-            .gap_2()
-            .child(
-                ButtonLike::new("diagnostic-indicator")
-                    .child(diagnostic_indicator)
-                    .tooltip(|cx| Tooltip::for_action("Project Diagnostics", &Deploy, cx))
-                    .on_click(cx.listener(|this, _, cx| {
-                        if let Some(workspace) = this.workspace.upgrade() {
-                            workspace.update(cx, |workspace, cx| {
-                                ProjectDiagnosticsEditor::deploy(workspace, &Default::default(), cx)
-                            })
-                        }
-                    })),
-            )
-            .children(status)
-    }
-}
-
-impl DiagnosticIndicator {
-    pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
-        let project = workspace.project();
-        cx.subscribe(project, |this, project, event, cx| match event {
-            project::Event::DiskBasedDiagnosticsStarted { language_server_id } => {
-                this.in_progress_checks.insert(*language_server_id);
-                cx.notify();
-            }
-
-            project::Event::DiskBasedDiagnosticsFinished { language_server_id }
-            | project::Event::LanguageServerRemoved(language_server_id) => {
-                this.summary = project.read(cx).diagnostic_summary(false, cx);
-                this.in_progress_checks.remove(language_server_id);
-                cx.notify();
-            }
-
-            project::Event::DiagnosticsUpdated { .. } => {
-                this.summary = project.read(cx).diagnostic_summary(false, cx);
-                cx.notify();
-            }
-
-            _ => {}
-        })
-        .detach();
-
-        Self {
-            summary: project.read(cx).diagnostic_summary(false, cx),
-            in_progress_checks: project
-                .read(cx)
-                .language_servers_running_disk_based_diagnostics()
-                .collect(),
-            active_editor: None,
-            workspace: workspace.weak_handle(),
-            current_diagnostic: None,
-            _observe_active_editor: None,
-        }
-    }
-
-    fn go_to_next_diagnostic(&mut self, cx: &mut ViewContext<Self>) {
-        if let Some(editor) = self.active_editor.as_ref().and_then(|e| e.upgrade()) {
-            editor.update(cx, |editor, cx| {
-                editor.go_to_diagnostic_impl(editor::Direction::Next, cx);
-            })
-        }
-    }
-
-    fn update(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
-        let editor = editor.read(cx);
-        let buffer = editor.buffer().read(cx);
-        let cursor_position = editor.selections.newest::<usize>(cx).head();
-        let new_diagnostic = buffer
-            .snapshot(cx)
-            .diagnostics_in_range::<_, usize>(cursor_position..cursor_position, false)
-            .filter(|entry| !entry.range.is_empty())
-            .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))
-            .map(|entry| entry.diagnostic);
-        if new_diagnostic != self.current_diagnostic {
-            self.current_diagnostic = new_diagnostic;
-            cx.notify();
-        }
-    }
-}
-
-impl EventEmitter<ToolbarItemEvent> for DiagnosticIndicator {}
-
-impl StatusItemView for DiagnosticIndicator {
-    fn set_active_pane_item(
-        &mut self,
-        active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
-            self.active_editor = Some(editor.downgrade());
-            self._observe_active_editor = Some(cx.observe(&editor, Self::update));
-            self.update(editor, cx);
-        } else {
-            self.active_editor = None;
-            self.current_diagnostic = None;
-            self._observe_active_editor = None;
-        }
-        cx.notify();
-    }
-}

crates/diagnostics2/src/project_diagnostics_settings.rs 🔗

@@ -1,28 +0,0 @@
-use schemars::JsonSchema;
-use serde::{Deserialize, Serialize};
-
-#[derive(Deserialize, Debug)]
-pub struct ProjectDiagnosticsSettings {
-    pub include_warnings: bool,
-}
-
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
-pub struct ProjectDiagnosticsSettingsContent {
-    include_warnings: Option<bool>,
-}
-
-impl settings::Settings for ProjectDiagnosticsSettings {
-    const KEY: Option<&'static str> = Some("diagnostics");
-    type FileContent = ProjectDiagnosticsSettingsContent;
-
-    fn load(
-        default_value: &Self::FileContent,
-        user_values: &[&Self::FileContent],
-        _cx: &mut gpui::AppContext,
-    ) -> anyhow::Result<Self>
-    where
-        Self: Sized,
-    {
-        Self::load_via_json_merge(default_value, user_values)
-    }
-}

crates/diagnostics2/src/toolbar_controls.rs 🔗

@@ -1,65 +0,0 @@
-use crate::ProjectDiagnosticsEditor;
-use gpui::{div, EventEmitter, ParentElement, Render, ViewContext, WeakView};
-use ui::prelude::*;
-use ui::{Icon, IconButton, Tooltip};
-use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
-
-pub struct ToolbarControls {
-    editor: Option<WeakView<ProjectDiagnosticsEditor>>,
-}
-
-impl Render for ToolbarControls {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let include_warnings = self
-            .editor
-            .as_ref()
-            .and_then(|editor| editor.upgrade())
-            .map(|editor| editor.read(cx).include_warnings)
-            .unwrap_or(false);
-
-        let tooltip = if include_warnings {
-            "Exclude Warnings"
-        } else {
-            "Include Warnings"
-        };
-
-        div().child(
-            IconButton::new("toggle-warnings", Icon::ExclamationTriangle)
-                .tooltip(move |cx| Tooltip::text(tooltip, cx))
-                .on_click(cx.listener(|this, _, cx| {
-                    if let Some(editor) = this.editor.as_ref().and_then(|editor| editor.upgrade()) {
-                        editor.update(cx, |editor, cx| {
-                            editor.toggle_warnings(&Default::default(), cx);
-                        });
-                    }
-                })),
-        )
-    }
-}
-
-impl EventEmitter<ToolbarItemEvent> for ToolbarControls {}
-
-impl ToolbarItemView for ToolbarControls {
-    fn set_active_pane_item(
-        &mut self,
-        active_pane_item: Option<&dyn ItemHandle>,
-        _: &mut ViewContext<Self>,
-    ) -> ToolbarItemLocation {
-        if let Some(pane_item) = active_pane_item.as_ref() {
-            if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
-                self.editor = Some(editor.downgrade());
-                ToolbarItemLocation::PrimaryRight
-            } else {
-                ToolbarItemLocation::Hidden
-            }
-        } else {
-            ToolbarItemLocation::Hidden
-        }
-    }
-}
-
-impl ToolbarControls {
-    pub fn new() -> Self {
-        ToolbarControls { editor: None }
-    }
-}

crates/file_finder/Cargo.toml 🔗

@@ -9,26 +9,28 @@ path = "src/file_finder.rs"
 doctest = false
 
 [dependencies]
-editor = { path = "../editor" }
+editor = { package = "editor2", path = "../editor2" }
 collections = { path = "../collections" }
-fuzzy = { path = "../fuzzy" }
-gpui = { path = "../gpui" }
-menu = { path = "../menu" }
-picker = { path = "../picker" }
-project = { path = "../project" }
-settings = { path = "../settings" }
-text = { path = "../text" }
+fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+menu = { package = "menu2", path = "../menu2" }
+picker = { package = "picker2", path = "../picker2" }
+project = { package = "project2", path = "../project2" }
+settings = { package = "settings2", path = "../settings2" }
+text = { package = "text2", path = "../text2" }
 util = { path = "../util" }
-theme = { path = "../theme" }
-workspace = { path = "../workspace" }
+theme = { package = "theme2", path = "../theme2" }
+ui = { package = "ui2", path = "../ui2" }
+workspace = { package = "workspace2", path = "../workspace2" }
 postage.workspace = true
+serde.workspace = true
 
 [dev-dependencies]
-editor = { path = "../editor", features = ["test-support"] }
-gpui = { path = "../gpui", features = ["test-support"] }
-language = { path = "../language", features = ["test-support"] }
-workspace = { path = "../workspace", features = ["test-support"] }
-theme = { path = "../theme", features = ["test-support"] }
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
+workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
+theme = { package = "theme2", path = "../theme2", features = ["test-support"] }
 
 serde_json.workspace = true
 ctor.workspace = true

crates/file_finder/src/file_finder.rs 🔗

@@ -2,7 +2,8 @@ use collections::HashMap;
 use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
 use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
 use gpui::{
-    actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
+    actions, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
+    ParentElement, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
 };
 use picker::{Picker, PickerDelegate};
 use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
@@ -14,14 +15,118 @@ use std::{
     },
 };
 use text::Point;
+use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
 use util::{paths::PathLikeWithPosition, post_inc, ResultExt};
-use workspace::Workspace;
+use workspace::{ModalView, Workspace};
 
-pub type FileFinder = Picker<FileFinderDelegate>;
+actions!(file_finder, [Toggle]);
+
+impl ModalView for FileFinder {}
+
+pub struct FileFinder {
+    picker: View<Picker<FileFinderDelegate>>,
+}
+
+pub fn init(cx: &mut AppContext) {
+    cx.observe_new_views(FileFinder::register).detach();
+}
+
+impl FileFinder {
+    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+        workspace.register_action(|workspace, _: &Toggle, cx| {
+            let Some(file_finder) = workspace.active_modal::<Self>(cx) else {
+                Self::open(workspace, cx);
+                return;
+            };
+
+            file_finder.update(cx, |file_finder, cx| {
+                file_finder
+                    .picker
+                    .update(cx, |picker, cx| picker.cycle_selection(cx))
+            });
+        });
+    }
+
+    fn open(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+        let project = workspace.project().read(cx);
+
+        let currently_opened_path = workspace
+            .active_item(cx)
+            .and_then(|item| item.project_path(cx))
+            .map(|project_path| {
+                let abs_path = project
+                    .worktree_for_id(project_path.worktree_id, cx)
+                    .map(|worktree| worktree.read(cx).abs_path().join(&project_path.path));
+                FoundPath::new(project_path, abs_path)
+            });
+
+        // if exists, bubble the currently opened path to the top
+        let history_items = currently_opened_path
+            .clone()
+            .into_iter()
+            .chain(
+                workspace
+                    .recent_navigation_history(Some(MAX_RECENT_SELECTIONS), cx)
+                    .into_iter()
+                    .filter(|(history_path, _)| {
+                        Some(history_path)
+                            != currently_opened_path
+                                .as_ref()
+                                .map(|found_path| &found_path.project)
+                    })
+                    .filter(|(_, history_abs_path)| {
+                        history_abs_path.as_ref()
+                            != currently_opened_path
+                                .as_ref()
+                                .and_then(|found_path| found_path.absolute.as_ref())
+                    })
+                    .filter(|(_, history_abs_path)| match history_abs_path {
+                        Some(abs_path) => history_file_exists(abs_path),
+                        None => true,
+                    })
+                    .map(|(history_path, abs_path)| FoundPath::new(history_path, abs_path)),
+            )
+            .collect();
+
+        let project = workspace.project().clone();
+        let weak_workspace = cx.view().downgrade();
+        workspace.toggle_modal(cx, |cx| {
+            let delegate = FileFinderDelegate::new(
+                cx.view().downgrade(),
+                weak_workspace,
+                project,
+                currently_opened_path,
+                history_items,
+                cx,
+            );
+
+            FileFinder::new(delegate, cx)
+        });
+    }
+
+    fn new(delegate: FileFinderDelegate, cx: &mut ViewContext<Self>) -> Self {
+        Self {
+            picker: cx.new_view(|cx| Picker::new(delegate, cx)),
+        }
+    }
+}
+
+impl EventEmitter<DismissEvent> for FileFinder {}
+impl FocusableView for FileFinder {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.picker.focus_handle(cx)
+    }
+}
+impl Render for FileFinder {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+        v_stack().w(rems(34.)).child(self.picker.clone())
+    }
+}
 
 pub struct FileFinderDelegate {
-    workspace: WeakViewHandle<Workspace>,
-    project: ModelHandle<Project>,
+    file_finder: WeakView<FileFinder>,
+    workspace: WeakView<Workspace>,
+    project: Model<Project>,
     search_count: usize,
     latest_search_id: usize,
     latest_search_did_cancel: bool,
@@ -165,91 +270,8 @@ impl FoundPath {
     }
 }
 
-actions!(file_finder, [Toggle]);
-
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(toggle_or_cycle_file_finder);
-    FileFinder::init(cx);
-}
-
 const MAX_RECENT_SELECTIONS: usize = 20;
 
-fn toggle_or_cycle_file_finder(
-    workspace: &mut Workspace,
-    _: &Toggle,
-    cx: &mut ViewContext<Workspace>,
-) {
-    match workspace.modal::<FileFinder>() {
-        Some(file_finder) => file_finder.update(cx, |file_finder, cx| {
-            let current_index = file_finder.delegate().selected_index();
-            file_finder.select_next(&menu::SelectNext, cx);
-            let new_index = file_finder.delegate().selected_index();
-            if current_index == new_index {
-                file_finder.select_first(&menu::SelectFirst, cx);
-            }
-        }),
-        None => {
-            workspace.toggle_modal(cx, |workspace, cx| {
-                let project = workspace.project().read(cx);
-
-                let currently_opened_path = workspace
-                    .active_item(cx)
-                    .and_then(|item| item.project_path(cx))
-                    .map(|project_path| {
-                        let abs_path = project
-                            .worktree_for_id(project_path.worktree_id, cx)
-                            .map(|worktree| worktree.read(cx).abs_path().join(&project_path.path));
-                        FoundPath::new(project_path, abs_path)
-                    });
-
-                // if exists, bubble the currently opened path to the top
-                let history_items = currently_opened_path
-                    .clone()
-                    .into_iter()
-                    .chain(
-                        workspace
-                            .recent_navigation_history(Some(MAX_RECENT_SELECTIONS), cx)
-                            .into_iter()
-                            .filter(|(history_path, _)| {
-                                Some(history_path)
-                                    != currently_opened_path
-                                        .as_ref()
-                                        .map(|found_path| &found_path.project)
-                            })
-                            .filter(|(_, history_abs_path)| {
-                                history_abs_path.as_ref()
-                                    != currently_opened_path
-                                        .as_ref()
-                                        .and_then(|found_path| found_path.absolute.as_ref())
-                            })
-                            .filter(|(_, history_abs_path)| match history_abs_path {
-                                Some(abs_path) => history_file_exists(abs_path),
-                                None => true,
-                            })
-                            .map(|(history_path, abs_path)| FoundPath::new(history_path, abs_path)),
-                    )
-                    .collect();
-
-                let project = workspace.project().clone();
-                let workspace = cx.handle().downgrade();
-                let finder = cx.add_view(|cx| {
-                    Picker::new(
-                        FileFinderDelegate::new(
-                            workspace,
-                            project,
-                            currently_opened_path,
-                            history_items,
-                            cx,
-                        ),
-                        cx,
-                    )
-                });
-                finder
-            });
-        }
-    }
-}
-
 #[cfg(not(test))]
 fn history_file_exists(abs_path: &PathBuf) -> bool {
     abs_path.exists()
@@ -282,17 +304,23 @@ impl FileSearchQuery {
 
 impl FileFinderDelegate {
     fn new(
-        workspace: WeakViewHandle<Workspace>,
-        project: ModelHandle<Project>,
+        file_finder: WeakView<FileFinder>,
+        workspace: WeakView<Workspace>,
+        project: Model<Project>,
         currently_opened_path: Option<FoundPath>,
         history_items: Vec<FoundPath>,
         cx: &mut ViewContext<FileFinder>,
     ) -> Self {
-        cx.observe(&project, |picker, _, cx| {
-            picker.update_matches(picker.query(cx), cx);
+        cx.observe(&project, |file_finder, _, cx| {
+            //todo!() We should probably not re-render on every project anything
+            file_finder
+                .picker
+                .update(cx, |picker, cx| picker.refresh(cx))
         })
         .detach();
+
         Self {
+            file_finder,
             workspace,
             project,
             search_count: 0,
@@ -310,7 +338,7 @@ impl FileFinderDelegate {
     fn spawn_search(
         &mut self,
         query: PathLikeWithPosition<FileSearchQuery>,
-        cx: &mut ViewContext<FileFinder>,
+        cx: &mut ViewContext<Picker<Self>>,
     ) -> Task<()> {
         let relative_to = self
             .currently_opened_path
@@ -343,14 +371,14 @@ impl FileFinderDelegate {
                 false,
                 100,
                 &cancel_flag,
-                cx.background(),
+                cx.background_executor().clone(),
             )
             .await;
             let did_cancel = cancel_flag.load(atomic::Ordering::Relaxed);
             picker
                 .update(&mut cx, |picker, cx| {
                     picker
-                        .delegate_mut()
+                        .delegate
                         .set_search_matches(search_id, did_cancel, query, matches, cx)
                 })
                 .log_err();
@@ -363,7 +391,7 @@ impl FileFinderDelegate {
         did_cancel: bool,
         query: PathLikeWithPosition<FileSearchQuery>,
         matches: Vec<PathMatch>,
-        cx: &mut ViewContext<FileFinder>,
+        cx: &mut ViewContext<Picker<Self>>,
     ) {
         if search_id >= self.latest_search_id {
             self.latest_search_id = search_id;
@@ -495,6 +523,8 @@ impl FileFinderDelegate {
 }
 
 impl PickerDelegate for FileFinderDelegate {
+    type ListItem = ListItem;
+
     fn placeholder_text(&self) -> Arc<str> {
         "Search project files...".into()
     }
@@ -507,12 +537,25 @@ impl PickerDelegate for FileFinderDelegate {
         self.selected_index.unwrap_or(0)
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<FileFinder>) {
+    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
         self.selected_index = Some(ix);
         cx.notify();
     }
 
-    fn update_matches(&mut self, raw_query: String, cx: &mut ViewContext<FileFinder>) -> Task<()> {
+    fn separators_after_indices(&self) -> Vec<usize> {
+        let history_items = self.matches.history.len();
+        if history_items == 0 || self.matches.search.is_empty() {
+            Vec::new()
+        } else {
+            vec![history_items - 1]
+        }
+    }
+
+    fn update_matches(
+        &mut self,
+        raw_query: String,
+        cx: &mut ViewContext<Picker<Self>>,
+    ) -> Task<()> {
         let raw_query = raw_query.trim();
         if raw_query.is_empty() {
             let project = self.project.read(cx);
@@ -550,9 +593,9 @@ impl PickerDelegate for FileFinderDelegate {
         }
     }
 
-    fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<FileFinder>) {
+    fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
         if let Some(m) = self.matches.get(self.selected_index()) {
-            if let Some(workspace) = self.workspace.upgrade(cx) {
+            if let Some(workspace) = self.workspace.upgrade() {
                 let open_task = workspace.update(cx, move |workspace, cx| {
                     let split_or_open = |workspace: &mut Workspace, project_path, cx| {
                         if secondary {
@@ -628,6 +671,8 @@ impl PickerDelegate for FileFinderDelegate {
                     .and_then(|query| query.column)
                     .unwrap_or(0)
                     .saturating_sub(1);
+                let finder = self.file_finder.clone();
+
                 cx.spawn(|_, mut cx| async move {
                     let item = open_task.await.log_err()?;
                     if let Some(row) = row {
@@ -646,10 +691,7 @@ impl PickerDelegate for FileFinderDelegate {
                                 .log_err();
                         }
                     }
-                    workspace
-                        .downgrade()
-                        .update(&mut cx, |workspace, cx| workspace.dismiss_modal(cx))
-                        .log_err();
+                    finder.update(&mut cx, |_, cx| cx.emit(DismissEvent)).ok()?;
 
                     Some(())
                 })
@@ -658,44 +700,47 @@ impl PickerDelegate for FileFinderDelegate {
         }
     }
 
-    fn dismissed(&mut self, _: &mut ViewContext<FileFinder>) {}
+    fn dismissed(&mut self, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
+        self.file_finder
+            .update(cx, |_, cx| cx.emit(DismissEvent))
+            .log_err();
+    }
 
     fn render_match(
         &self,
         ix: usize,
-        mouse_state: &mut MouseState,
         selected: bool,
-        cx: &AppContext,
-    ) -> AnyElement<Picker<Self>> {
+        cx: &mut ViewContext<Picker<Self>>,
+    ) -> Option<Self::ListItem> {
         let path_match = self
             .matches
             .get(ix)
             .expect("Invalid matches state: no element for index {ix}");
-        let theme = theme::current(cx);
-        let style = theme.picker.item.in_state(selected).style_for(mouse_state);
+
         let (file_name, file_name_positions, full_path, full_path_positions) =
             self.labels_for_match(path_match, cx, ix);
-        Flex::column()
-            .with_child(
-                Label::new(file_name, style.label.clone()).with_highlights(file_name_positions),
-            )
-            .with_child(
-                Label::new(full_path, style.label.clone()).with_highlights(full_path_positions),
-            )
-            .flex(1., false)
-            .contained()
-            .with_style(style.container)
-            .into_any_named("match")
+
+        Some(
+            ListItem::new(ix)
+                .spacing(ListItemSpacing::Sparse)
+                .inset(true)
+                .selected(selected)
+                .child(
+                    v_stack()
+                        .child(HighlightedLabel::new(file_name, file_name_positions))
+                        .child(HighlightedLabel::new(full_path, full_path_positions)),
+                ),
+        )
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use std::{assert_eq, collections::HashMap, path::Path, time::Duration};
+    use std::{assert_eq, path::Path, time::Duration};
 
     use super::*;
     use editor::Editor;
-    use gpui::{TestAppContext, ViewHandle};
+    use gpui::{Entity, TestAppContext, VisualTestContext};
     use menu::{Confirm, SelectNext};
     use serde_json::json;
     use workspace::{AppState, Workspace};
@@ -725,37 +770,18 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let window = cx.add_window(|cx| Workspace::test_new(project, cx));
-        let workspace = window.root(cx);
-        cx.dispatch_action(window.into(), Toggle);
 
-        let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
+        let (picker, workspace, cx) = build_find_picker(project, cx);
 
-        finder
-            .update(cx, |finder, cx| {
-                finder.delegate_mut().update_matches("bna".to_string(), cx)
-            })
-            .await;
-        finder.read_with(cx, |finder, _| {
-            assert_eq!(finder.delegate().matches.len(), 2);
+        cx.simulate_input("bna");
+        picker.update(cx, |picker, _| {
+            assert_eq!(picker.delegate.matches.len(), 2);
         });
-        let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
-        cx.dispatch_action(window.into(), SelectNext);
-        cx.dispatch_action(window.into(), Confirm);
-        active_pane
-            .condition(cx, |pane, _| pane.active_item().is_some())
-            .await;
+        cx.dispatch_action(SelectNext);
+        cx.dispatch_action(Confirm);
         cx.read(|cx| {
-            let active_item = active_pane.read(cx).active_item().unwrap();
-            assert_eq!(
-                active_item
-                    .as_any()
-                    .downcast_ref::<Editor>()
-                    .unwrap()
-                    .read(cx)
-                    .title(cx),
-                "bandana"
-            );
+            let active_editor = workspace.read(cx).active_item_as::<Editor>(cx).unwrap();
+            assert_eq!(active_editor.read(cx).title(cx), "bandana");
         });
 
         for bandana_query in [
@@ -766,35 +792,26 @@ mod tests {
             " ndan ",
             " band ",
         ] {
-            finder
-                .update(cx, |finder, cx| {
-                    finder
-                        .delegate_mut()
+            picker
+                .update(cx, |picker, cx| {
+                    picker
+                        .delegate
                         .update_matches(bandana_query.to_string(), cx)
                 })
                 .await;
-            finder.read_with(cx, |finder, _| {
+            picker.update(cx, |picker, _| {
                 assert_eq!(
-                    finder.delegate().matches.len(),
+                    picker.delegate.matches.len(),
                     1,
                     "Wrong number of matches for bandana query '{bandana_query}'"
                 );
             });
-            let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
-            cx.dispatch_action(window.into(), SelectNext);
-            cx.dispatch_action(window.into(), Confirm);
-            active_pane
-                .condition(cx, |pane, _| pane.active_item().is_some())
-                .await;
+            cx.dispatch_action(SelectNext);
+            cx.dispatch_action(Confirm);
             cx.read(|cx| {
-                let active_item = active_pane.read(cx).active_item().unwrap();
+                let active_editor = workspace.read(cx).active_item_as::<Editor>(cx).unwrap();
                 assert_eq!(
-                    active_item
-                        .as_any()
-                        .downcast_ref::<Editor>()
-                        .unwrap()
-                        .read(cx)
-                        .title(cx),
+                    active_editor.read(cx).title(cx),
                     "bandana",
                     "Wrong match for bandana query '{bandana_query}'"
                 );
@@ -823,25 +840,23 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let window = cx.add_window(|cx| Workspace::test_new(project, cx));
-        let workspace = window.root(cx);
-        cx.dispatch_action(window.into(), Toggle);
-        let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
+
+        let (picker, workspace, cx) = build_find_picker(project, cx);
 
         let file_query = &first_file_name[..3];
         let file_row = 1;
         let file_column = 3;
         assert!(file_column <= first_file_contents.len());
         let query_inside_file = format!("{file_query}:{file_row}:{file_column}");
-        finder
+        picker
             .update(cx, |finder, cx| {
                 finder
-                    .delegate_mut()
+                    .delegate
                     .update_matches(query_inside_file.to_string(), cx)
             })
             .await;
-        finder.read_with(cx, |finder, _| {
-            let finder = finder.delegate();
+        picker.update(cx, |finder, _| {
+            let finder = &finder.delegate;
             assert_eq!(finder.matches.len(), 1);
             let latest_search_query = finder
                 .latest_search_query
@@ -856,34 +871,27 @@ mod tests {
             assert_eq!(latest_search_query.column, Some(file_column as u32));
         });
 
-        let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
-        cx.dispatch_action(window.into(), SelectNext);
-        cx.dispatch_action(window.into(), Confirm);
-        active_pane
-            .condition(cx, |pane, _| pane.active_item().is_some())
-            .await;
-        let editor = cx.update(|cx| {
-            let active_item = active_pane.read(cx).active_item().unwrap();
-            active_item.downcast::<Editor>().unwrap()
-        });
-        cx.foreground().advance_clock(Duration::from_secs(2));
-        cx.foreground().start_waiting();
-        cx.foreground().finish_waiting();
+        cx.dispatch_action(SelectNext);
+        cx.dispatch_action(Confirm);
+
+        let editor = cx.update(|cx| workspace.read(cx).active_item_as::<Editor>(cx).unwrap());
+        cx.executor().advance_clock(Duration::from_secs(2));
+
         editor.update(cx, |editor, cx| {
-            let all_selections = editor.selections.all_adjusted(cx);
-            assert_eq!(
-                all_selections.len(),
-                1,
-                "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}"
-            );
-            let caret_selection = all_selections.into_iter().next().unwrap();
-            assert_eq!(caret_selection.start, caret_selection.end,
-                "Caret selection should have its start and end at the same position");
-            assert_eq!(file_row, caret_selection.start.row + 1,
-                "Query inside file should get caret with the same focus row");
-            assert_eq!(file_column, caret_selection.start.column as usize + 1,
-                "Query inside file should get caret with the same focus column");
-        });
+                let all_selections = editor.selections.all_adjusted(cx);
+                assert_eq!(
+                    all_selections.len(),
+                    1,
+                    "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}"
+                );
+                let caret_selection = all_selections.into_iter().next().unwrap();
+                assert_eq!(caret_selection.start, caret_selection.end,
+                    "Caret selection should have its start and end at the same position");
+                assert_eq!(file_row, caret_selection.start.row + 1,
+                    "Query inside file should get caret with the same focus row");
+                assert_eq!(file_column, caret_selection.start.column as usize + 1,
+                    "Query inside file should get caret with the same focus column");
+            });
     }
 
     #[gpui::test]
@@ -907,27 +915,25 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let window = cx.add_window(|cx| Workspace::test_new(project, cx));
-        let workspace = window.root(cx);
-        cx.dispatch_action(window.into(), Toggle);
-        let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
+
+        let (picker, workspace, cx) = build_find_picker(project, cx);
 
         let file_query = &first_file_name[..3];
         let file_row = 200;
         let file_column = 300;
         assert!(file_column > first_file_contents.len());
         let query_outside_file = format!("{file_query}:{file_row}:{file_column}");
-        finder
-            .update(cx, |finder, cx| {
-                finder
-                    .delegate_mut()
+        picker
+            .update(cx, |picker, cx| {
+                picker
+                    .delegate
                     .update_matches(query_outside_file.to_string(), cx)
             })
             .await;
-        finder.read_with(cx, |finder, _| {
-            let finder = finder.delegate();
-            assert_eq!(finder.matches.len(), 1);
-            let latest_search_query = finder
+        picker.update(cx, |finder, _| {
+            let delegate = &finder.delegate;
+            assert_eq!(delegate.matches.len(), 1);
+            let latest_search_query = delegate
                 .latest_search_query
                 .as_ref()
                 .expect("Finder should have a query after the update_matches call");
@@ -940,34 +946,27 @@ mod tests {
             assert_eq!(latest_search_query.column, Some(file_column as u32));
         });
 
-        let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
-        cx.dispatch_action(window.into(), SelectNext);
-        cx.dispatch_action(window.into(), Confirm);
-        active_pane
-            .condition(cx, |pane, _| pane.active_item().is_some())
-            .await;
-        let editor = cx.update(|cx| {
-            let active_item = active_pane.read(cx).active_item().unwrap();
-            active_item.downcast::<Editor>().unwrap()
-        });
-        cx.foreground().advance_clock(Duration::from_secs(2));
-        cx.foreground().start_waiting();
-        cx.foreground().finish_waiting();
+        cx.dispatch_action(SelectNext);
+        cx.dispatch_action(Confirm);
+
+        let editor = cx.update(|cx| workspace.read(cx).active_item_as::<Editor>(cx).unwrap());
+        cx.executor().advance_clock(Duration::from_secs(2));
+
         editor.update(cx, |editor, cx| {
-            let all_selections = editor.selections.all_adjusted(cx);
-            assert_eq!(
-                all_selections.len(),
-                1,
-                "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}"
-            );
-            let caret_selection = all_selections.into_iter().next().unwrap();
-            assert_eq!(caret_selection.start, caret_selection.end,
-                "Caret selection should have its start and end at the same position");
-            assert_eq!(0, caret_selection.start.row,
-                "Excessive rows (as in query outside file borders) should get trimmed to last file row");
-            assert_eq!(first_file_contents.len(), caret_selection.start.column as usize,
-                "Excessive columns (as in query outside file borders) should get trimmed to selected row's last column");
-        });
+                let all_selections = editor.selections.all_adjusted(cx);
+                assert_eq!(
+                    all_selections.len(),
+                    1,
+                    "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}"
+                );
+                let caret_selection = all_selections.into_iter().next().unwrap();
+                assert_eq!(caret_selection.start, caret_selection.end,
+                    "Caret selection should have its start and end at the same position");
+                assert_eq!(0, caret_selection.start.row,
+                    "Excessive rows (as in query outside file borders) should get trimmed to last file row");
+                assert_eq!(first_file_contents.len(), caret_selection.start.column as usize,
+                    "Excessive columns (as in query outside file borders) should get trimmed to selected row's last column");
+            });
     }
 
     #[gpui::test]
@@ -991,32 +990,22 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project, cx))
-            .root(cx);
-        let finder = cx
-            .add_window(|cx| {
-                Picker::new(
-                    FileFinderDelegate::new(
-                        workspace.downgrade(),
-                        workspace.read(cx).project().clone(),
-                        None,
-                        Vec::new(),
-                        cx,
-                    ),
-                    cx,
-                )
-            })
-            .root(cx);
+
+        let (picker, _, cx) = build_find_picker(project, cx);
 
         let query = test_path_like("hi");
-        finder
-            .update(cx, |f, cx| f.delegate_mut().spawn_search(query.clone(), cx))
+        picker
+            .update(cx, |picker, cx| {
+                picker.delegate.spawn_search(query.clone(), cx)
+            })
             .await;
-        finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 5));
 
-        finder.update(cx, |finder, cx| {
-            let delegate = finder.delegate_mut();
+        picker.update(cx, |picker, _cx| {
+            assert_eq!(picker.delegate.matches.len(), 5)
+        });
+
+        picker.update(cx, |picker, cx| {
+            let delegate = &mut picker.delegate;
             assert!(
                 delegate.matches.history.is_empty(),
                 "Search matches expected"
@@ -1088,31 +1077,17 @@ mod tests {
             cx,
         )
         .await;
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project, cx))
-            .root(cx);
-        let finder = cx
-            .add_window(|cx| {
-                Picker::new(
-                    FileFinderDelegate::new(
-                        workspace.downgrade(),
-                        workspace.read(cx).project().clone(),
-                        None,
-                        Vec::new(),
-                        cx,
-                    ),
-                    cx,
-                )
-            })
-            .root(cx);
-        finder
-            .update(cx, |f, cx| {
-                f.delegate_mut().spawn_search(test_path_like("hi"), cx)
+
+        let (picker, _, cx) = build_find_picker(project, cx);
+
+        picker
+            .update(cx, |picker, cx| {
+                picker.delegate.spawn_search(test_path_like("hi"), cx)
             })
             .await;
-        finder.update(cx, |f, _| {
+        picker.update(cx, |picker, _| {
             assert_eq!(
-                collect_search_results(f),
+                collect_search_results(picker),
                 vec![
                     PathBuf::from("ignored-root/happiness"),
                     PathBuf::from("ignored-root/height"),
@@ -1157,20 +1132,13 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let window = cx.add_window(|cx| Workspace::test_new(project, cx));
-        let workspace = window.root(cx);
-        cx.dispatch_action(window.into(), Toggle);
 
-        let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
+        let (picker, workspace, cx) = build_find_picker(project, cx);
 
-        finder
-            .update(cx, |finder, cx| {
-                finder.delegate_mut().update_matches("env".to_string(), cx)
-            })
-            .await;
-        finder.update(cx, |f, _| {
+        cx.simulate_input("env");
+        picker.update(cx, |picker, _| {
             assert_eq!(
-                collect_search_results(f),
+                collect_search_results(picker),
                 vec![
                     PathBuf::from(".env"),
                     PathBuf::from("a/banana_env"),
@@ -1190,15 +1158,11 @@ mod tests {
             })
             .await
             .unwrap();
-        cx.foreground().run_until_parked();
-        finder
-            .update(cx, |finder, cx| {
-                finder.delegate_mut().update_matches("env".to_string(), cx)
-            })
-            .await;
-        finder.update(cx, |f, _| {
+        cx.run_until_parked();
+        cx.simulate_input("env");
+        picker.update(cx, |picker, _| {
             assert_eq!(
-                collect_search_results(f),
+                collect_search_results(picker),
                 vec![
                     PathBuf::from(".env"),
                     PathBuf::from("a/banana_env"),
@@ -1226,34 +1190,19 @@ mod tests {
             cx,
         )
         .await;
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project, cx))
-            .root(cx);
-        let finder = cx
-            .add_window(|cx| {
-                Picker::new(
-                    FileFinderDelegate::new(
-                        workspace.downgrade(),
-                        workspace.read(cx).project().clone(),
-                        None,
-                        Vec::new(),
-                        cx,
-                    ),
-                    cx,
-                )
-            })
-            .root(cx);
+
+        let (picker, _, cx) = build_find_picker(project, cx);
 
         // Even though there is only one worktree, that worktree's filename
         // is included in the matching, because the worktree is a single file.
-        finder
-            .update(cx, |f, cx| {
-                f.delegate_mut().spawn_search(test_path_like("thf"), cx)
+        picker
+            .update(cx, |picker, cx| {
+                picker.delegate.spawn_search(test_path_like("thf"), cx)
             })
             .await;
         cx.read(|cx| {
-            let finder = finder.read(cx);
-            let delegate = finder.delegate();
+            let picker = picker.read(cx);
+            let delegate = &picker.delegate;
             assert!(
                 delegate.matches.history.is_empty(),
                 "Search matches expected"
@@ -1271,12 +1220,12 @@ mod tests {
 
         // Since the worktree root is a file, searching for its name followed by a slash does
         // not match anything.
-        finder
+        picker
             .update(cx, |f, cx| {
-                f.delegate_mut().spawn_search(test_path_like("thf/"), cx)
+                f.delegate.spawn_search(test_path_like("thf/"), cx)
             })
             .await;
-        finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 0));
+        picker.update(cx, |f, _| assert_eq!(f.delegate.matches.len(), 0));
     }
 
     #[gpui::test]
@@ -1298,45 +1247,36 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project, cx))
-            .root(cx);
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
+
         let worktree_id = cx.read(|cx| {
             let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
             assert_eq!(worktrees.len(), 1);
-            WorktreeId::from_usize(worktrees[0].id())
+            WorktreeId::from_usize(worktrees[0].entity_id().as_u64() as usize)
         });
 
         // When workspace has an active item, sort items which are closer to that item
         // first when they have the same name. In this case, b.txt is closer to dir2's a.txt
         // so that one should be sorted earlier
-        let b_path = Some(dummy_found_path(ProjectPath {
+        let b_path = ProjectPath {
             worktree_id,
             path: Arc::from(Path::new("/root/dir2/b.txt")),
-        }));
-        let finder = cx
-            .add_window(|cx| {
-                Picker::new(
-                    FileFinderDelegate::new(
-                        workspace.downgrade(),
-                        workspace.read(cx).project().clone(),
-                        b_path,
-                        Vec::new(),
-                        cx,
-                    ),
-                    cx,
-                )
+        };
+        workspace
+            .update(cx, |workspace, cx| {
+                workspace.open_path(b_path, None, true, cx)
             })
-            .root(cx);
-
+            .await
+            .unwrap();
+        let finder = open_file_picker(&workspace, cx);
         finder
             .update(cx, |f, cx| {
-                f.delegate_mut().spawn_search(test_path_like("a.txt"), cx)
+                f.delegate.spawn_search(test_path_like("a.txt"), cx)
             })
             .await;
 
-        finder.read_with(cx, |f, _| {
-            let delegate = f.delegate();
+        finder.update(cx, |f, _| {
+            let delegate = &f.delegate;
             assert!(
                 delegate.matches.history.is_empty(),
                 "Search matches expected"
@@ -1365,39 +1305,21 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project, cx))
-            .root(cx);
-        let finder = cx
-            .add_window(|cx| {
-                Picker::new(
-                    FileFinderDelegate::new(
-                        workspace.downgrade(),
-                        workspace.read(cx).project().clone(),
-                        None,
-                        Vec::new(),
-                        cx,
-                    ),
-                    cx,
-                )
-            })
-            .root(cx);
-        finder
+        let (picker, _workspace, cx) = build_find_picker(project, cx);
+
+        picker
             .update(cx, |f, cx| {
-                f.delegate_mut().spawn_search(test_path_like("dir"), cx)
+                f.delegate.spawn_search(test_path_like("dir"), cx)
             })
             .await;
         cx.read(|cx| {
-            let finder = finder.read(cx);
-            assert_eq!(finder.delegate().matches.len(), 0);
+            let finder = picker.read(cx);
+            assert_eq!(finder.delegate.matches.len(), 0);
         });
     }
 
     #[gpui::test]
-    async fn test_query_history(
-        deterministic: Arc<gpui::executor::Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
+    async fn test_query_history(cx: &mut gpui::TestAppContext) {
         let app_state = init_test(cx);
 
         app_state
@@ -1416,12 +1338,11 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let window = cx.add_window(|cx| Workspace::test_new(project, cx));
-        let workspace = window.root(cx);
+        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
         let worktree_id = cx.read(|cx| {
             let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
             assert_eq!(worktrees.len(), 1);
-            WorktreeId::from_usize(worktrees[0].id())
+            WorktreeId::from_usize(worktrees[0].entity_id().as_u64() as usize)
         });
 
         // Open and close panels, getting their history items afterwards.

crates/file_finder2/Cargo.toml 🔗

@@ -1,37 +0,0 @@
-[package]
-name = "file_finder2"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-path = "src/file_finder.rs"
-doctest = false
-
-[dependencies]
-editor = { package = "editor2", path = "../editor2" }
-collections = { path = "../collections" }
-fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
-gpui = { package = "gpui2", path = "../gpui2" }
-menu = { package = "menu2", path = "../menu2" }
-picker = { package = "picker2", path = "../picker2" }
-project = { package = "project2", path = "../project2" }
-settings = { package = "settings2", path = "../settings2" }
-text = { package = "text2", path = "../text2" }
-util = { path = "../util" }
-theme = { package = "theme2", path = "../theme2" }
-ui = { package = "ui2", path = "../ui2" }
-workspace = { package = "workspace2", path = "../workspace2" }
-postage.workspace = true
-serde.workspace = true
-
-[dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
-language = { package = "language2", path = "../language2", features = ["test-support"] }
-workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
-theme = { package = "theme2", path = "../theme2", features = ["test-support"] }
-
-serde_json.workspace = true
-ctor.workspace = true
-env_logger.workspace = true

crates/file_finder2/src/file_finder.rs 🔗

@@ -1,1956 +0,0 @@
-use collections::HashMap;
-use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
-use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
-use gpui::{
-    actions, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Model,
-    ParentElement, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
-};
-use picker::{Picker, PickerDelegate};
-use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
-use std::{
-    path::{Path, PathBuf},
-    sync::{
-        atomic::{self, AtomicBool},
-        Arc,
-    },
-};
-use text::Point;
-use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
-use util::{paths::PathLikeWithPosition, post_inc, ResultExt};
-use workspace::{ModalView, Workspace};
-
-actions!(file_finder, [Toggle]);
-
-impl ModalView for FileFinder {}
-
-pub struct FileFinder {
-    picker: View<Picker<FileFinderDelegate>>,
-}
-
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(FileFinder::register).detach();
-}
-
-impl FileFinder {
-    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-        workspace.register_action(|workspace, _: &Toggle, cx| {
-            let Some(file_finder) = workspace.active_modal::<Self>(cx) else {
-                Self::open(workspace, cx);
-                return;
-            };
-
-            file_finder.update(cx, |file_finder, cx| {
-                file_finder
-                    .picker
-                    .update(cx, |picker, cx| picker.cycle_selection(cx))
-            });
-        });
-    }
-
-    fn open(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
-        let project = workspace.project().read(cx);
-
-        let currently_opened_path = workspace
-            .active_item(cx)
-            .and_then(|item| item.project_path(cx))
-            .map(|project_path| {
-                let abs_path = project
-                    .worktree_for_id(project_path.worktree_id, cx)
-                    .map(|worktree| worktree.read(cx).abs_path().join(&project_path.path));
-                FoundPath::new(project_path, abs_path)
-            });
-
-        // if exists, bubble the currently opened path to the top
-        let history_items = currently_opened_path
-            .clone()
-            .into_iter()
-            .chain(
-                workspace
-                    .recent_navigation_history(Some(MAX_RECENT_SELECTIONS), cx)
-                    .into_iter()
-                    .filter(|(history_path, _)| {
-                        Some(history_path)
-                            != currently_opened_path
-                                .as_ref()
-                                .map(|found_path| &found_path.project)
-                    })
-                    .filter(|(_, history_abs_path)| {
-                        history_abs_path.as_ref()
-                            != currently_opened_path
-                                .as_ref()
-                                .and_then(|found_path| found_path.absolute.as_ref())
-                    })
-                    .filter(|(_, history_abs_path)| match history_abs_path {
-                        Some(abs_path) => history_file_exists(abs_path),
-                        None => true,
-                    })
-                    .map(|(history_path, abs_path)| FoundPath::new(history_path, abs_path)),
-            )
-            .collect();
-
-        let project = workspace.project().clone();
-        let weak_workspace = cx.view().downgrade();
-        workspace.toggle_modal(cx, |cx| {
-            let delegate = FileFinderDelegate::new(
-                cx.view().downgrade(),
-                weak_workspace,
-                project,
-                currently_opened_path,
-                history_items,
-                cx,
-            );
-
-            FileFinder::new(delegate, cx)
-        });
-    }
-
-    fn new(delegate: FileFinderDelegate, cx: &mut ViewContext<Self>) -> Self {
-        Self {
-            picker: cx.new_view(|cx| Picker::new(delegate, cx)),
-        }
-    }
-}
-
-impl EventEmitter<DismissEvent> for FileFinder {}
-impl FocusableView for FileFinder {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
-        self.picker.focus_handle(cx)
-    }
-}
-impl Render for FileFinder {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
-        v_stack().w(rems(34.)).child(self.picker.clone())
-    }
-}
-
-pub struct FileFinderDelegate {
-    file_finder: WeakView<FileFinder>,
-    workspace: WeakView<Workspace>,
-    project: Model<Project>,
-    search_count: usize,
-    latest_search_id: usize,
-    latest_search_did_cancel: bool,
-    latest_search_query: Option<PathLikeWithPosition<FileSearchQuery>>,
-    currently_opened_path: Option<FoundPath>,
-    matches: Matches,
-    selected_index: Option<usize>,
-    cancel_flag: Arc<AtomicBool>,
-    history_items: Vec<FoundPath>,
-}
-
-#[derive(Debug, Default)]
-struct Matches {
-    history: Vec<(FoundPath, Option<PathMatch>)>,
-    search: Vec<PathMatch>,
-}
-
-#[derive(Debug)]
-enum Match<'a> {
-    History(&'a FoundPath, Option<&'a PathMatch>),
-    Search(&'a PathMatch),
-}
-
-impl Matches {
-    fn len(&self) -> usize {
-        self.history.len() + self.search.len()
-    }
-
-    fn get(&self, index: usize) -> Option<Match<'_>> {
-        if index < self.history.len() {
-            self.history
-                .get(index)
-                .map(|(path, path_match)| Match::History(path, path_match.as_ref()))
-        } else {
-            self.search
-                .get(index - self.history.len())
-                .map(Match::Search)
-        }
-    }
-
-    fn push_new_matches(
-        &mut self,
-        history_items: &Vec<FoundPath>,
-        query: &PathLikeWithPosition<FileSearchQuery>,
-        mut new_search_matches: Vec<PathMatch>,
-        extend_old_matches: bool,
-    ) {
-        let matching_history_paths = matching_history_item_paths(history_items, query);
-        new_search_matches
-            .retain(|path_match| !matching_history_paths.contains_key(&path_match.path));
-        let history_items_to_show = history_items
-            .iter()
-            .filter_map(|history_item| {
-                Some((
-                    history_item.clone(),
-                    Some(
-                        matching_history_paths
-                            .get(&history_item.project.path)?
-                            .clone(),
-                    ),
-                ))
-            })
-            .collect::<Vec<_>>();
-        self.history = history_items_to_show;
-        if extend_old_matches {
-            self.search
-                .retain(|path_match| !matching_history_paths.contains_key(&path_match.path));
-            util::extend_sorted(
-                &mut self.search,
-                new_search_matches.into_iter(),
-                100,
-                |a, b| b.cmp(a),
-            )
-        } else {
-            self.search = new_search_matches;
-        }
-    }
-}
-
-fn matching_history_item_paths(
-    history_items: &Vec<FoundPath>,
-    query: &PathLikeWithPosition<FileSearchQuery>,
-) -> HashMap<Arc<Path>, PathMatch> {
-    let history_items_by_worktrees = history_items
-        .iter()
-        .filter_map(|found_path| {
-            let candidate = PathMatchCandidate {
-                path: &found_path.project.path,
-                // Only match history items names, otherwise their paths may match too many queries, producing false positives.
-                // E.g. `foo` would match both `something/foo/bar.rs` and `something/foo/foo.rs` and if the former is a history item,
-                // it would be shown first always, despite the latter being a better match.
-                char_bag: CharBag::from_iter(
-                    found_path
-                        .project
-                        .path
-                        .file_name()?
-                        .to_string_lossy()
-                        .to_lowercase()
-                        .chars(),
-                ),
-            };
-            Some((found_path.project.worktree_id, candidate))
-        })
-        .fold(
-            HashMap::default(),
-            |mut candidates, (worktree_id, new_candidate)| {
-                candidates
-                    .entry(worktree_id)
-                    .or_insert_with(Vec::new)
-                    .push(new_candidate);
-                candidates
-            },
-        );
-    let mut matching_history_paths = HashMap::default();
-    for (worktree, candidates) in history_items_by_worktrees {
-        let max_results = candidates.len() + 1;
-        matching_history_paths.extend(
-            fuzzy::match_fixed_path_set(
-                candidates,
-                worktree.to_usize(),
-                query.path_like.path_query(),
-                false,
-                max_results,
-            )
-            .into_iter()
-            .map(|path_match| (Arc::clone(&path_match.path), path_match)),
-        );
-    }
-    matching_history_paths
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-struct FoundPath {
-    project: ProjectPath,
-    absolute: Option<PathBuf>,
-}
-
-impl FoundPath {
-    fn new(project: ProjectPath, absolute: Option<PathBuf>) -> Self {
-        Self { project, absolute }
-    }
-}
-
-const MAX_RECENT_SELECTIONS: usize = 20;
-
-#[cfg(not(test))]
-fn history_file_exists(abs_path: &PathBuf) -> bool {
-    abs_path.exists()
-}
-
-#[cfg(test)]
-fn history_file_exists(abs_path: &PathBuf) -> bool {
-    !abs_path.ends_with("nonexistent.rs")
-}
-
-pub enum Event {
-    Selected(ProjectPath),
-    Dismissed,
-}
-
-#[derive(Debug, Clone)]
-struct FileSearchQuery {
-    raw_query: String,
-    file_query_end: Option<usize>,
-}
-
-impl FileSearchQuery {
-    fn path_query(&self) -> &str {
-        match self.file_query_end {
-            Some(file_path_end) => &self.raw_query[..file_path_end],
-            None => &self.raw_query,
-        }
-    }
-}
-
-impl FileFinderDelegate {
-    fn new(
-        file_finder: WeakView<FileFinder>,
-        workspace: WeakView<Workspace>,
-        project: Model<Project>,
-        currently_opened_path: Option<FoundPath>,
-        history_items: Vec<FoundPath>,
-        cx: &mut ViewContext<FileFinder>,
-    ) -> Self {
-        cx.observe(&project, |file_finder, _, cx| {
-            //todo!() We should probably not re-render on every project anything
-            file_finder
-                .picker
-                .update(cx, |picker, cx| picker.refresh(cx))
-        })
-        .detach();
-
-        Self {
-            file_finder,
-            workspace,
-            project,
-            search_count: 0,
-            latest_search_id: 0,
-            latest_search_did_cancel: false,
-            latest_search_query: None,
-            currently_opened_path,
-            matches: Matches::default(),
-            selected_index: None,
-            cancel_flag: Arc::new(AtomicBool::new(false)),
-            history_items,
-        }
-    }
-
-    fn spawn_search(
-        &mut self,
-        query: PathLikeWithPosition<FileSearchQuery>,
-        cx: &mut ViewContext<Picker<Self>>,
-    ) -> Task<()> {
-        let relative_to = self
-            .currently_opened_path
-            .as_ref()
-            .map(|found_path| Arc::clone(&found_path.project.path));
-        let worktrees = self
-            .project
-            .read(cx)
-            .visible_worktrees(cx)
-            .collect::<Vec<_>>();
-        let include_root_name = worktrees.len() > 1;
-        let candidate_sets = worktrees
-            .into_iter()
-            .map(|worktree| PathMatchCandidateSet {
-                snapshot: worktree.read(cx).snapshot(),
-                include_ignored: true,
-                include_root_name,
-            })
-            .collect::<Vec<_>>();
-
-        let search_id = util::post_inc(&mut self.search_count);
-        self.cancel_flag.store(true, atomic::Ordering::Relaxed);
-        self.cancel_flag = Arc::new(AtomicBool::new(false));
-        let cancel_flag = self.cancel_flag.clone();
-        cx.spawn(|picker, mut cx| async move {
-            let matches = fuzzy::match_path_sets(
-                candidate_sets.as_slice(),
-                query.path_like.path_query(),
-                relative_to,
-                false,
-                100,
-                &cancel_flag,
-                cx.background_executor().clone(),
-            )
-            .await;
-            let did_cancel = cancel_flag.load(atomic::Ordering::Relaxed);
-            picker
-                .update(&mut cx, |picker, cx| {
-                    picker
-                        .delegate
-                        .set_search_matches(search_id, did_cancel, query, matches, cx)
-                })
-                .log_err();
-        })
-    }
-
-    fn set_search_matches(
-        &mut self,
-        search_id: usize,
-        did_cancel: bool,
-        query: PathLikeWithPosition<FileSearchQuery>,
-        matches: Vec<PathMatch>,
-        cx: &mut ViewContext<Picker<Self>>,
-    ) {
-        if search_id >= self.latest_search_id {
-            self.latest_search_id = search_id;
-            let extend_old_matches = self.latest_search_did_cancel
-                && Some(query.path_like.path_query())
-                    == self
-                        .latest_search_query
-                        .as_ref()
-                        .map(|query| query.path_like.path_query());
-            self.matches
-                .push_new_matches(&self.history_items, &query, matches, extend_old_matches);
-            self.latest_search_query = Some(query);
-            self.latest_search_did_cancel = did_cancel;
-            cx.notify();
-        }
-    }
-
-    fn labels_for_match(
-        &self,
-        path_match: Match,
-        cx: &AppContext,
-        ix: usize,
-    ) -> (String, Vec<usize>, String, Vec<usize>) {
-        let (file_name, file_name_positions, full_path, full_path_positions) = match path_match {
-            Match::History(found_path, found_path_match) => {
-                let worktree_id = found_path.project.worktree_id;
-                let project_relative_path = &found_path.project.path;
-                let has_worktree = self
-                    .project
-                    .read(cx)
-                    .worktree_for_id(worktree_id, cx)
-                    .is_some();
-
-                if !has_worktree {
-                    if let Some(absolute_path) = &found_path.absolute {
-                        return (
-                            absolute_path
-                                .file_name()
-                                .map_or_else(
-                                    || project_relative_path.to_string_lossy(),
-                                    |file_name| file_name.to_string_lossy(),
-                                )
-                                .to_string(),
-                            Vec::new(),
-                            absolute_path.to_string_lossy().to_string(),
-                            Vec::new(),
-                        );
-                    }
-                }
-
-                let mut path = Arc::clone(project_relative_path);
-                if project_relative_path.as_ref() == Path::new("") {
-                    if let Some(absolute_path) = &found_path.absolute {
-                        path = Arc::from(absolute_path.as_path());
-                    }
-                }
-
-                let mut path_match = PathMatch {
-                    score: ix as f64,
-                    positions: Vec::new(),
-                    worktree_id: worktree_id.to_usize(),
-                    path,
-                    path_prefix: "".into(),
-                    distance_to_relative_ancestor: usize::MAX,
-                };
-                if let Some(found_path_match) = found_path_match {
-                    path_match
-                        .positions
-                        .extend(found_path_match.positions.iter())
-                }
-
-                self.labels_for_path_match(&path_match)
-            }
-            Match::Search(path_match) => self.labels_for_path_match(path_match),
-        };
-
-        if file_name_positions.is_empty() {
-            if let Some(user_home_path) = std::env::var("HOME").ok() {
-                let user_home_path = user_home_path.trim();
-                if !user_home_path.is_empty() {
-                    if (&full_path).starts_with(user_home_path) {
-                        return (
-                            file_name,
-                            file_name_positions,
-                            full_path.replace(user_home_path, "~"),
-                            full_path_positions,
-                        );
-                    }
-                }
-            }
-        }
-
-        (
-            file_name,
-            file_name_positions,
-            full_path,
-            full_path_positions,
-        )
-    }
-
-    fn labels_for_path_match(
-        &self,
-        path_match: &PathMatch,
-    ) -> (String, Vec<usize>, String, Vec<usize>) {
-        let path = &path_match.path;
-        let path_string = path.to_string_lossy();
-        let full_path = [path_match.path_prefix.as_ref(), path_string.as_ref()].join("");
-        let path_positions = path_match.positions.clone();
-
-        let file_name = path.file_name().map_or_else(
-            || path_match.path_prefix.to_string(),
-            |file_name| file_name.to_string_lossy().to_string(),
-        );
-        let file_name_start = path_match.path_prefix.chars().count() + path_string.chars().count()
-            - file_name.chars().count();
-        let file_name_positions = path_positions
-            .iter()
-            .filter_map(|pos| {
-                if pos >= &file_name_start {
-                    Some(pos - file_name_start)
-                } else {
-                    None
-                }
-            })
-            .collect();
-
-        (file_name, file_name_positions, full_path, path_positions)
-    }
-}
-
-impl PickerDelegate for FileFinderDelegate {
-    type ListItem = ListItem;
-
-    fn placeholder_text(&self) -> Arc<str> {
-        "Search project files...".into()
-    }
-
-    fn match_count(&self) -> usize {
-        self.matches.len()
-    }
-
-    fn selected_index(&self) -> usize {
-        self.selected_index.unwrap_or(0)
-    }
-
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
-        self.selected_index = Some(ix);
-        cx.notify();
-    }
-
-    fn separators_after_indices(&self) -> Vec<usize> {
-        let history_items = self.matches.history.len();
-        if history_items == 0 || self.matches.search.is_empty() {
-            Vec::new()
-        } else {
-            vec![history_items - 1]
-        }
-    }
-
-    fn update_matches(
-        &mut self,
-        raw_query: String,
-        cx: &mut ViewContext<Picker<Self>>,
-    ) -> Task<()> {
-        let raw_query = raw_query.trim();
-        if raw_query.is_empty() {
-            let project = self.project.read(cx);
-            self.latest_search_id = post_inc(&mut self.search_count);
-            self.matches = Matches {
-                history: self
-                    .history_items
-                    .iter()
-                    .filter(|history_item| {
-                        project
-                            .worktree_for_id(history_item.project.worktree_id, cx)
-                            .is_some()
-                            || (project.is_local() && history_item.absolute.is_some())
-                    })
-                    .cloned()
-                    .map(|p| (p, None))
-                    .collect(),
-                search: Vec::new(),
-            };
-            cx.notify();
-            Task::ready(())
-        } else {
-            let query = PathLikeWithPosition::parse_str(raw_query, |path_like_str| {
-                Ok::<_, std::convert::Infallible>(FileSearchQuery {
-                    raw_query: raw_query.to_owned(),
-                    file_query_end: if path_like_str == raw_query {
-                        None
-                    } else {
-                        Some(path_like_str.len())
-                    },
-                })
-            })
-            .expect("infallible");
-            self.spawn_search(query, cx)
-        }
-    }
-
-    fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
-        if let Some(m) = self.matches.get(self.selected_index()) {
-            if let Some(workspace) = self.workspace.upgrade() {
-                let open_task = workspace.update(cx, move |workspace, cx| {
-                    let split_or_open = |workspace: &mut Workspace, project_path, cx| {
-                        if secondary {
-                            workspace.split_path(project_path, cx)
-                        } else {
-                            workspace.open_path(project_path, None, true, cx)
-                        }
-                    };
-                    match m {
-                        Match::History(history_match, _) => {
-                            let worktree_id = history_match.project.worktree_id;
-                            if workspace
-                                .project()
-                                .read(cx)
-                                .worktree_for_id(worktree_id, cx)
-                                .is_some()
-                            {
-                                split_or_open(
-                                    workspace,
-                                    ProjectPath {
-                                        worktree_id,
-                                        path: Arc::clone(&history_match.project.path),
-                                    },
-                                    cx,
-                                )
-                            } else {
-                                match history_match.absolute.as_ref() {
-                                    Some(abs_path) => {
-                                        if secondary {
-                                            workspace.split_abs_path(
-                                                abs_path.to_path_buf(),
-                                                false,
-                                                cx,
-                                            )
-                                        } else {
-                                            workspace.open_abs_path(
-                                                abs_path.to_path_buf(),
-                                                false,
-                                                cx,
-                                            )
-                                        }
-                                    }
-                                    None => split_or_open(
-                                        workspace,
-                                        ProjectPath {
-                                            worktree_id,
-                                            path: Arc::clone(&history_match.project.path),
-                                        },
-                                        cx,
-                                    ),
-                                }
-                            }
-                        }
-                        Match::Search(m) => split_or_open(
-                            workspace,
-                            ProjectPath {
-                                worktree_id: WorktreeId::from_usize(m.worktree_id),
-                                path: m.path.clone(),
-                            },
-                            cx,
-                        ),
-                    }
-                });
-
-                let row = self
-                    .latest_search_query
-                    .as_ref()
-                    .and_then(|query| query.row)
-                    .map(|row| row.saturating_sub(1));
-                let col = self
-                    .latest_search_query
-                    .as_ref()
-                    .and_then(|query| query.column)
-                    .unwrap_or(0)
-                    .saturating_sub(1);
-                let finder = self.file_finder.clone();
-
-                cx.spawn(|_, mut cx| async move {
-                    let item = open_task.await.log_err()?;
-                    if let Some(row) = row {
-                        if let Some(active_editor) = item.downcast::<Editor>() {
-                            active_editor
-                                .downgrade()
-                                .update(&mut cx, |editor, cx| {
-                                    let snapshot = editor.snapshot(cx).display_snapshot;
-                                    let point = snapshot
-                                        .buffer_snapshot
-                                        .clip_point(Point::new(row, col), Bias::Left);
-                                    editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-                                        s.select_ranges([point..point])
-                                    });
-                                })
-                                .log_err();
-                        }
-                    }
-                    finder.update(&mut cx, |_, cx| cx.emit(DismissEvent)).ok()?;
-
-                    Some(())
-                })
-                .detach();
-            }
-        }
-    }
-
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
-        self.file_finder
-            .update(cx, |_, cx| cx.emit(DismissEvent))
-            .log_err();
-    }
-
-    fn render_match(
-        &self,
-        ix: usize,
-        selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
-    ) -> Option<Self::ListItem> {
-        let path_match = self
-            .matches
-            .get(ix)
-            .expect("Invalid matches state: no element for index {ix}");
-
-        let (file_name, file_name_positions, full_path, full_path_positions) =
-            self.labels_for_match(path_match, cx, ix);
-
-        Some(
-            ListItem::new(ix)
-                .spacing(ListItemSpacing::Sparse)
-                .inset(true)
-                .selected(selected)
-                .child(
-                    v_stack()
-                        .child(HighlightedLabel::new(file_name, file_name_positions))
-                        .child(HighlightedLabel::new(full_path, full_path_positions)),
-                ),
-        )
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use std::{assert_eq, path::Path, time::Duration};
-
-    use super::*;
-    use editor::Editor;
-    use gpui::{Entity, TestAppContext, VisualTestContext};
-    use menu::{Confirm, SelectNext};
-    use serde_json::json;
-    use workspace::{AppState, Workspace};
-
-    #[ctor::ctor]
-    fn init_logger() {
-        if std::env::var("RUST_LOG").is_ok() {
-            env_logger::init();
-        }
-    }
-
-    #[gpui::test]
-    async fn test_matching_paths(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/root",
-                json!({
-                    "a": {
-                        "banana": "",
-                        "bandana": "",
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-
-        let (picker, workspace, cx) = build_find_picker(project, cx);
-
-        cx.simulate_input("bna");
-        picker.update(cx, |picker, _| {
-            assert_eq!(picker.delegate.matches.len(), 2);
-        });
-        cx.dispatch_action(SelectNext);
-        cx.dispatch_action(Confirm);
-        cx.read(|cx| {
-            let active_editor = workspace.read(cx).active_item_as::<Editor>(cx).unwrap();
-            assert_eq!(active_editor.read(cx).title(cx), "bandana");
-        });
-
-        for bandana_query in [
-            "bandana",
-            " bandana",
-            "bandana ",
-            " bandana ",
-            " ndan ",
-            " band ",
-        ] {
-            picker
-                .update(cx, |picker, cx| {
-                    picker
-                        .delegate
-                        .update_matches(bandana_query.to_string(), cx)
-                })
-                .await;
-            picker.update(cx, |picker, _| {
-                assert_eq!(
-                    picker.delegate.matches.len(),
-                    1,
-                    "Wrong number of matches for bandana query '{bandana_query}'"
-                );
-            });
-            cx.dispatch_action(SelectNext);
-            cx.dispatch_action(Confirm);
-            cx.read(|cx| {
-                let active_editor = workspace.read(cx).active_item_as::<Editor>(cx).unwrap();
-                assert_eq!(
-                    active_editor.read(cx).title(cx),
-                    "bandana",
-                    "Wrong match for bandana query '{bandana_query}'"
-                );
-            });
-        }
-    }
-
-    #[gpui::test]
-    async fn test_row_column_numbers_query_inside_file(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-
-        let first_file_name = "first.rs";
-        let first_file_contents = "// First Rust file";
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/src",
-                json!({
-                    "test": {
-                        first_file_name: first_file_contents,
-                        "second.rs": "// Second Rust file",
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-
-        let (picker, workspace, cx) = build_find_picker(project, cx);
-
-        let file_query = &first_file_name[..3];
-        let file_row = 1;
-        let file_column = 3;
-        assert!(file_column <= first_file_contents.len());
-        let query_inside_file = format!("{file_query}:{file_row}:{file_column}");
-        picker
-            .update(cx, |finder, cx| {
-                finder
-                    .delegate
-                    .update_matches(query_inside_file.to_string(), cx)
-            })
-            .await;
-        picker.update(cx, |finder, _| {
-            let finder = &finder.delegate;
-            assert_eq!(finder.matches.len(), 1);
-            let latest_search_query = finder
-                .latest_search_query
-                .as_ref()
-                .expect("Finder should have a query after the update_matches call");
-            assert_eq!(latest_search_query.path_like.raw_query, query_inside_file);
-            assert_eq!(
-                latest_search_query.path_like.file_query_end,
-                Some(file_query.len())
-            );
-            assert_eq!(latest_search_query.row, Some(file_row));
-            assert_eq!(latest_search_query.column, Some(file_column as u32));
-        });
-
-        cx.dispatch_action(SelectNext);
-        cx.dispatch_action(Confirm);
-
-        let editor = cx.update(|cx| workspace.read(cx).active_item_as::<Editor>(cx).unwrap());
-        cx.executor().advance_clock(Duration::from_secs(2));
-
-        editor.update(cx, |editor, cx| {
-                let all_selections = editor.selections.all_adjusted(cx);
-                assert_eq!(
-                    all_selections.len(),
-                    1,
-                    "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}"
-                );
-                let caret_selection = all_selections.into_iter().next().unwrap();
-                assert_eq!(caret_selection.start, caret_selection.end,
-                    "Caret selection should have its start and end at the same position");
-                assert_eq!(file_row, caret_selection.start.row + 1,
-                    "Query inside file should get caret with the same focus row");
-                assert_eq!(file_column, caret_selection.start.column as usize + 1,
-                    "Query inside file should get caret with the same focus column");
-            });
-    }
-
-    #[gpui::test]
-    async fn test_row_column_numbers_query_outside_file(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-
-        let first_file_name = "first.rs";
-        let first_file_contents = "// First Rust file";
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/src",
-                json!({
-                    "test": {
-                        first_file_name: first_file_contents,
-                        "second.rs": "// Second Rust file",
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-
-        let (picker, workspace, cx) = build_find_picker(project, cx);
-
-        let file_query = &first_file_name[..3];
-        let file_row = 200;
-        let file_column = 300;
-        assert!(file_column > first_file_contents.len());
-        let query_outside_file = format!("{file_query}:{file_row}:{file_column}");
-        picker
-            .update(cx, |picker, cx| {
-                picker
-                    .delegate
-                    .update_matches(query_outside_file.to_string(), cx)
-            })
-            .await;
-        picker.update(cx, |finder, _| {
-            let delegate = &finder.delegate;
-            assert_eq!(delegate.matches.len(), 1);
-            let latest_search_query = delegate
-                .latest_search_query
-                .as_ref()
-                .expect("Finder should have a query after the update_matches call");
-            assert_eq!(latest_search_query.path_like.raw_query, query_outside_file);
-            assert_eq!(
-                latest_search_query.path_like.file_query_end,
-                Some(file_query.len())
-            );
-            assert_eq!(latest_search_query.row, Some(file_row));
-            assert_eq!(latest_search_query.column, Some(file_column as u32));
-        });
-
-        cx.dispatch_action(SelectNext);
-        cx.dispatch_action(Confirm);
-
-        let editor = cx.update(|cx| workspace.read(cx).active_item_as::<Editor>(cx).unwrap());
-        cx.executor().advance_clock(Duration::from_secs(2));
-
-        editor.update(cx, |editor, cx| {
-                let all_selections = editor.selections.all_adjusted(cx);
-                assert_eq!(
-                    all_selections.len(),
-                    1,
-                    "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}"
-                );
-                let caret_selection = all_selections.into_iter().next().unwrap();
-                assert_eq!(caret_selection.start, caret_selection.end,
-                    "Caret selection should have its start and end at the same position");
-                assert_eq!(0, caret_selection.start.row,
-                    "Excessive rows (as in query outside file borders) should get trimmed to last file row");
-                assert_eq!(first_file_contents.len(), caret_selection.start.column as usize,
-                    "Excessive columns (as in query outside file borders) should get trimmed to selected row's last column");
-            });
-    }
-
-    #[gpui::test]
-    async fn test_matching_cancellation(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/dir",
-                json!({
-                    "hello": "",
-                    "goodbye": "",
-                    "halogen-light": "",
-                    "happiness": "",
-                    "height": "",
-                    "hi": "",
-                    "hiccup": "",
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
-
-        let (picker, _, cx) = build_find_picker(project, cx);
-
-        let query = test_path_like("hi");
-        picker
-            .update(cx, |picker, cx| {
-                picker.delegate.spawn_search(query.clone(), cx)
-            })
-            .await;
-
-        picker.update(cx, |picker, _cx| {
-            assert_eq!(picker.delegate.matches.len(), 5)
-        });
-
-        picker.update(cx, |picker, cx| {
-            let delegate = &mut picker.delegate;
-            assert!(
-                delegate.matches.history.is_empty(),
-                "Search matches expected"
-            );
-            let matches = delegate.matches.search.clone();
-
-            // Simulate a search being cancelled after the time limit,
-            // returning only a subset of the matches that would have been found.
-            drop(delegate.spawn_search(query.clone(), cx));
-            delegate.set_search_matches(
-                delegate.latest_search_id,
-                true, // did-cancel
-                query.clone(),
-                vec![matches[1].clone(), matches[3].clone()],
-                cx,
-            );
-
-            // Simulate another cancellation.
-            drop(delegate.spawn_search(query.clone(), cx));
-            delegate.set_search_matches(
-                delegate.latest_search_id,
-                true, // did-cancel
-                query.clone(),
-                vec![matches[0].clone(), matches[2].clone(), matches[3].clone()],
-                cx,
-            );
-
-            assert!(
-                delegate.matches.history.is_empty(),
-                "Search matches expected"
-            );
-            assert_eq!(delegate.matches.search.as_slice(), &matches[0..4]);
-        });
-    }
-
-    #[gpui::test]
-    async fn test_ignored_root(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/ancestor",
-                json!({
-                    ".gitignore": "ignored-root",
-                    "ignored-root": {
-                        "happiness": "",
-                        "height": "",
-                        "hi": "",
-                        "hiccup": "",
-                    },
-                    "tracked-root": {
-                        ".gitignore": "height",
-                        "happiness": "",
-                        "height": "",
-                        "hi": "",
-                        "hiccup": "",
-                    },
-                }),
-            )
-            .await;
-
-        let project = Project::test(
-            app_state.fs.clone(),
-            [
-                "/ancestor/tracked-root".as_ref(),
-                "/ancestor/ignored-root".as_ref(),
-            ],
-            cx,
-        )
-        .await;
-
-        let (picker, _, cx) = build_find_picker(project, cx);
-
-        picker
-            .update(cx, |picker, cx| {
-                picker.delegate.spawn_search(test_path_like("hi"), cx)
-            })
-            .await;
-        picker.update(cx, |picker, _| {
-            assert_eq!(
-                collect_search_results(picker),
-                vec![
-                    PathBuf::from("ignored-root/happiness"),
-                    PathBuf::from("ignored-root/height"),
-                    PathBuf::from("ignored-root/hi"),
-                    PathBuf::from("ignored-root/hiccup"),
-                    PathBuf::from("tracked-root/happiness"),
-                    PathBuf::from("tracked-root/height"),
-                    PathBuf::from("tracked-root/hi"),
-                    PathBuf::from("tracked-root/hiccup"),
-                ],
-                "All files in all roots (including gitignored) should be searched"
-            )
-        });
-    }
-
-    #[gpui::test]
-    async fn test_ignored_files(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/root",
-                json!({
-                    ".git": {},
-                    ".gitignore": "ignored_a\n.env\n",
-                    "a": {
-                        "banana_env": "11",
-                        "bandana_env": "12",
-                    },
-                    "ignored_a": {
-                        "ignored_banana_env": "21",
-                        "ignored_bandana_env": "22",
-                        "ignored_nested": {
-                            "ignored_nested_banana_env": "31",
-                            "ignored_nested_bandana_env": "32",
-                        },
-                    },
-                    ".env": "something",
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-
-        let (picker, workspace, cx) = build_find_picker(project, cx);
-
-        cx.simulate_input("env");
-        picker.update(cx, |picker, _| {
-            assert_eq!(
-                collect_search_results(picker),
-                vec![
-                    PathBuf::from(".env"),
-                    PathBuf::from("a/banana_env"),
-                    PathBuf::from("a/bandana_env"),
-                ],
-                "Root gitignored files and all non-gitignored files should be searched"
-            )
-        });
-
-        let _ = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_abs_path(
-                    PathBuf::from("/root/ignored_a/ignored_banana_env"),
-                    true,
-                    cx,
-                )
-            })
-            .await
-            .unwrap();
-        cx.run_until_parked();
-        cx.simulate_input("env");
-        picker.update(cx, |picker, _| {
-            assert_eq!(
-                collect_search_results(picker),
-                vec![
-                    PathBuf::from(".env"),
-                    PathBuf::from("a/banana_env"),
-                    PathBuf::from("a/bandana_env"),
-                    PathBuf::from("ignored_a/ignored_banana_env"),
-                    PathBuf::from("ignored_a/ignored_bandana_env"),
-                ],
-                "Root gitignored dir got listed and its entries got into worktree, but all gitignored dirs below it were not listed. Old entries + new listed gitignored entries should be searched"
-            )
-        });
-    }
-
-    #[gpui::test]
-    async fn test_single_file_worktrees(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree("/root", json!({ "the-parent-dir": { "the-file": "" } }))
-            .await;
-
-        let project = Project::test(
-            app_state.fs.clone(),
-            ["/root/the-parent-dir/the-file".as_ref()],
-            cx,
-        )
-        .await;
-
-        let (picker, _, cx) = build_find_picker(project, cx);
-
-        // Even though there is only one worktree, that worktree's filename
-        // is included in the matching, because the worktree is a single file.
-        picker
-            .update(cx, |picker, cx| {
-                picker.delegate.spawn_search(test_path_like("thf"), cx)
-            })
-            .await;
-        cx.read(|cx| {
-            let picker = picker.read(cx);
-            let delegate = &picker.delegate;
-            assert!(
-                delegate.matches.history.is_empty(),
-                "Search matches expected"
-            );
-            let matches = delegate.matches.search.clone();
-            assert_eq!(matches.len(), 1);
-
-            let (file_name, file_name_positions, full_path, full_path_positions) =
-                delegate.labels_for_path_match(&matches[0]);
-            assert_eq!(file_name, "the-file");
-            assert_eq!(file_name_positions, &[0, 1, 4]);
-            assert_eq!(full_path, "the-file");
-            assert_eq!(full_path_positions, &[0, 1, 4]);
-        });
-
-        // Since the worktree root is a file, searching for its name followed by a slash does
-        // not match anything.
-        picker
-            .update(cx, |f, cx| {
-                f.delegate.spawn_search(test_path_like("thf/"), cx)
-            })
-            .await;
-        picker.update(cx, |f, _| assert_eq!(f.delegate.matches.len(), 0));
-    }
-
-    #[gpui::test]
-    async fn test_path_distance_ordering(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/root",
-                json!({
-                    "dir1": { "a.txt": "" },
-                    "dir2": {
-                        "a.txt": "",
-                        "b.txt": ""
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-
-        let worktree_id = cx.read(|cx| {
-            let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
-            assert_eq!(worktrees.len(), 1);
-            WorktreeId::from_usize(worktrees[0].entity_id().as_u64() as usize)
-        });
-
-        // When workspace has an active item, sort items which are closer to that item
-        // first when they have the same name. In this case, b.txt is closer to dir2's a.txt
-        // so that one should be sorted earlier
-        let b_path = ProjectPath {
-            worktree_id,
-            path: Arc::from(Path::new("/root/dir2/b.txt")),
-        };
-        workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path(b_path, None, true, cx)
-            })
-            .await
-            .unwrap();
-        let finder = open_file_picker(&workspace, cx);
-        finder
-            .update(cx, |f, cx| {
-                f.delegate.spawn_search(test_path_like("a.txt"), cx)
-            })
-            .await;
-
-        finder.update(cx, |f, _| {
-            let delegate = &f.delegate;
-            assert!(
-                delegate.matches.history.is_empty(),
-                "Search matches expected"
-            );
-            let matches = delegate.matches.search.clone();
-            assert_eq!(matches[0].path.as_ref(), Path::new("dir2/a.txt"));
-            assert_eq!(matches[1].path.as_ref(), Path::new("dir1/a.txt"));
-        });
-    }
-
-    #[gpui::test]
-    async fn test_search_worktree_without_files(cx: &mut TestAppContext) {
-        let app_state = init_test(cx);
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/root",
-                json!({
-                    "dir1": {},
-                    "dir2": {
-                        "dir3": {}
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (picker, _workspace, cx) = build_find_picker(project, cx);
-
-        picker
-            .update(cx, |f, cx| {
-                f.delegate.spawn_search(test_path_like("dir"), cx)
-            })
-            .await;
-        cx.read(|cx| {
-            let finder = picker.read(cx);
-            assert_eq!(finder.delegate.matches.len(), 0);
-        });
-    }
-
-    #[gpui::test]
-    async fn test_query_history(cx: &mut gpui::TestAppContext) {
-        let app_state = init_test(cx);
-
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/src",
-                json!({
-                    "test": {
-                        "first.rs": "// First Rust file",
-                        "second.rs": "// Second Rust file",
-                        "third.rs": "// Third Rust file",
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let worktree_id = cx.read(|cx| {
-            let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
-            assert_eq!(worktrees.len(), 1);
-            WorktreeId::from_usize(worktrees[0].entity_id().as_u64() as usize)
-        });
-
-        // Open and close panels, getting their history items afterwards.
-        // Ensure history items get populated with opened items, and items are kept in a certain order.
-        // The history lags one opened buffer behind, since it's updated in the search panel only on its reopen.
-        //
-        // TODO: without closing, the opened items do not propagate their history changes for some reason
-        // it does work in real app though, only tests do not propagate.
-        workspace.update(cx, |_, cx| cx.focused());
-
-        let initial_history = open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
-        assert!(
-            initial_history.is_empty(),
-            "Should have no history before opening any files"
-        );
-
-        let history_after_first =
-            open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
-        assert_eq!(
-            history_after_first,
-            vec![FoundPath::new(
-                ProjectPath {
-                    worktree_id,
-                    path: Arc::from(Path::new("test/first.rs")),
-                },
-                Some(PathBuf::from("/src/test/first.rs"))
-            )],
-            "Should show 1st opened item in the history when opening the 2nd item"
-        );
-
-        let history_after_second =
-            open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
-        assert_eq!(
-            history_after_second,
-            vec![
-                FoundPath::new(
-                    ProjectPath {
-                        worktree_id,
-                        path: Arc::from(Path::new("test/second.rs")),
-                    },
-                    Some(PathBuf::from("/src/test/second.rs"))
-                ),
-                FoundPath::new(
-                    ProjectPath {
-                        worktree_id,
-                        path: Arc::from(Path::new("test/first.rs")),
-                    },
-                    Some(PathBuf::from("/src/test/first.rs"))
-                ),
-            ],
-            "Should show 1st and 2nd opened items in the history when opening the 3rd item. \
-    2nd item should be the first in the history, as the last opened."
-        );
-
-        let history_after_third =
-            open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
-        assert_eq!(
-                history_after_third,
-                vec![
-                    FoundPath::new(
-                        ProjectPath {
-                            worktree_id,
-                            path: Arc::from(Path::new("test/third.rs")),
-                        },
-                        Some(PathBuf::from("/src/test/third.rs"))
-                    ),
-                    FoundPath::new(
-                        ProjectPath {
-                            worktree_id,
-                            path: Arc::from(Path::new("test/second.rs")),
-                        },
-                        Some(PathBuf::from("/src/test/second.rs"))
-                    ),
-                    FoundPath::new(
-                        ProjectPath {
-                            worktree_id,
-                            path: Arc::from(Path::new("test/first.rs")),
-                        },
-                        Some(PathBuf::from("/src/test/first.rs"))
-                    ),
-                ],
-                "Should show 1st, 2nd and 3rd opened items in the history when opening the 2nd item again. \
-    3rd item should be the first in the history, as the last opened."
-            );
-
-        let history_after_second_again =
-            open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
-        assert_eq!(
-                history_after_second_again,
-                vec![
-                    FoundPath::new(
-                        ProjectPath {
-                            worktree_id,
-                            path: Arc::from(Path::new("test/second.rs")),
-                        },
-                        Some(PathBuf::from("/src/test/second.rs"))
-                    ),
-                    FoundPath::new(
-                        ProjectPath {
-                            worktree_id,
-                            path: Arc::from(Path::new("test/third.rs")),
-                        },
-                        Some(PathBuf::from("/src/test/third.rs"))
-                    ),
-                    FoundPath::new(
-                        ProjectPath {
-                            worktree_id,
-                            path: Arc::from(Path::new("test/first.rs")),
-                        },
-                        Some(PathBuf::from("/src/test/first.rs"))
-                    ),
-                ],
-                "Should show 1st, 2nd and 3rd opened items in the history when opening the 3rd item again. \
-    2nd item, as the last opened, 3rd item should go next as it was opened right before."
-            );
-    }
-
-    #[gpui::test]
-    async fn test_external_files_history(cx: &mut gpui::TestAppContext) {
-        let app_state = init_test(cx);
-
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/src",
-                json!({
-                    "test": {
-                        "first.rs": "// First Rust file",
-                        "second.rs": "// Second Rust file",
-                    }
-                }),
-            )
-            .await;
-
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/external-src",
-                json!({
-                    "test": {
-                        "third.rs": "// Third Rust file",
-                        "fourth.rs": "// Fourth Rust file",
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        cx.update(|cx| {
-            project.update(cx, |project, cx| {
-                project.find_or_create_local_worktree("/external-src", false, cx)
-            })
-        })
-        .detach();
-        cx.background_executor.run_until_parked();
-
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let worktree_id = cx.read(|cx| {
-            let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
-            assert_eq!(worktrees.len(), 1,);
-
-            WorktreeId::from_usize(worktrees[0].entity_id().as_u64() as usize)
-        });
-        workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_abs_path(PathBuf::from("/external-src/test/third.rs"), false, cx)
-            })
-            .detach();
-        cx.background_executor.run_until_parked();
-        let external_worktree_id = cx.read(|cx| {
-            let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
-            assert_eq!(
-                worktrees.len(),
-                2,
-                "External file should get opened in a new worktree"
-            );
-
-            WorktreeId::from_usize(
-                worktrees
-                    .into_iter()
-                    .find(|worktree| {
-                        worktree.entity_id().as_u64() as usize != worktree_id.to_usize()
-                    })
-                    .expect("New worktree should have a different id")
-                    .entity_id()
-                    .as_u64() as usize,
-            )
-        });
-        cx.dispatch_action(workspace::CloseActiveItem { save_intent: None });
-
-        let initial_history_items =
-            open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
-        assert_eq!(
-            initial_history_items,
-            vec![FoundPath::new(
-                ProjectPath {
-                    worktree_id: external_worktree_id,
-                    path: Arc::from(Path::new("")),
-                },
-                Some(PathBuf::from("/external-src/test/third.rs"))
-            )],
-            "Should show external file with its full path in the history after it was open"
-        );
-
-        let updated_history_items =
-            open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
-        assert_eq!(
-            updated_history_items,
-            vec![
-                FoundPath::new(
-                    ProjectPath {
-                        worktree_id,
-                        path: Arc::from(Path::new("test/second.rs")),
-                    },
-                    Some(PathBuf::from("/src/test/second.rs"))
-                ),
-                FoundPath::new(
-                    ProjectPath {
-                        worktree_id: external_worktree_id,
-                        path: Arc::from(Path::new("")),
-                    },
-                    Some(PathBuf::from("/external-src/test/third.rs"))
-                ),
-            ],
-            "Should keep external file with history updates",
-        );
-    }
-
-    #[gpui::test]
-    async fn test_toggle_panel_new_selections(cx: &mut gpui::TestAppContext) {
-        let app_state = init_test(cx);
-
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/src",
-                json!({
-                    "test": {
-                        "first.rs": "// First Rust file",
-                        "second.rs": "// Second Rust file",
-                        "third.rs": "// Third Rust file",
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-
-        // generate some history to select from
-        open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
-        cx.executor().run_until_parked();
-        open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
-        open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
-        let current_history =
-            open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
-
-        for expected_selected_index in 0..current_history.len() {
-            cx.dispatch_action(Toggle);
-            let picker = active_file_picker(&workspace, cx);
-            let selected_index = picker.update(cx, |picker, _| picker.delegate.selected_index());
-            assert_eq!(
-                selected_index, expected_selected_index,
-                "Should select the next item in the history"
-            );
-        }
-
-        cx.dispatch_action(Toggle);
-        let selected_index = workspace.update(cx, |workspace, cx| {
-            workspace
-                .active_modal::<FileFinder>(cx)
-                .unwrap()
-                .read(cx)
-                .picker
-                .read(cx)
-                .delegate
-                .selected_index()
-        });
-        assert_eq!(
-            selected_index, 0,
-            "Should wrap around the history and start all over"
-        );
-    }
-
-    #[gpui::test]
-    async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) {
-        let app_state = init_test(cx);
-
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/src",
-                json!({
-                    "test": {
-                        "first.rs": "// First Rust file",
-                        "second.rs": "// Second Rust file",
-                        "third.rs": "// Third Rust file",
-                        "fourth.rs": "// Fourth Rust file",
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let worktree_id = cx.read(|cx| {
-            let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
-            assert_eq!(worktrees.len(), 1,);
-
-            WorktreeId::from_usize(worktrees[0].entity_id().as_u64() as usize)
-        });
-
-        // generate some history to select from
-        open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
-        open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
-        open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
-        open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
-
-        let finder = open_file_picker(&workspace, cx);
-        let first_query = "f";
-        finder
-            .update(cx, |finder, cx| {
-                finder.delegate.update_matches(first_query.to_string(), cx)
-            })
-            .await;
-        finder.update(cx, |finder, _| {
-            let delegate = &finder.delegate;
-            assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query}, it should be present and others should be filtered out");
-            let history_match = delegate.matches.history.first().unwrap();
-            assert!(history_match.1.is_some(), "Should have path matches for history items after querying");
-            assert_eq!(history_match.0, FoundPath::new(
-                ProjectPath {
-                    worktree_id,
-                    path: Arc::from(Path::new("test/first.rs")),
-                },
-                Some(PathBuf::from("/src/test/first.rs"))
-            ));
-            assert_eq!(delegate.matches.search.len(), 1, "Only one non-history item contains {first_query}, it should be present");
-            assert_eq!(delegate.matches.search.first().unwrap().path.as_ref(), Path::new("test/fourth.rs"));
-        });
-
-        let second_query = "fsdasdsa";
-        let finder = active_file_picker(&workspace, cx);
-        finder
-            .update(cx, |finder, cx| {
-                finder.delegate.update_matches(second_query.to_string(), cx)
-            })
-            .await;
-        finder.update(cx, |finder, _| {
-            let delegate = &finder.delegate;
-            assert!(
-                delegate.matches.history.is_empty(),
-                "No history entries should match {second_query}"
-            );
-            assert!(
-                delegate.matches.search.is_empty(),
-                "No search entries should match {second_query}"
-            );
-        });
-
-        let first_query_again = first_query;
-
-        let finder = active_file_picker(&workspace, cx);
-        finder
-            .update(cx, |finder, cx| {
-                finder
-                    .delegate
-                    .update_matches(first_query_again.to_string(), cx)
-            })
-            .await;
-        finder.update(cx, |finder, _| {
-            let delegate = &finder.delegate;
-            assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query_again}, it should be present and others should be filtered out, even after non-matching query");
-            let history_match = delegate.matches.history.first().unwrap();
-            assert!(history_match.1.is_some(), "Should have path matches for history items after querying");
-            assert_eq!(history_match.0, FoundPath::new(
-                ProjectPath {
-                    worktree_id,
-                    path: Arc::from(Path::new("test/first.rs")),
-                },
-                Some(PathBuf::from("/src/test/first.rs"))
-            ));
-            assert_eq!(delegate.matches.search.len(), 1, "Only one non-history item contains {first_query_again}, it should be present, even after non-matching query");
-            assert_eq!(delegate.matches.search.first().unwrap().path.as_ref(), Path::new("test/fourth.rs"));
-        });
-    }
-
-    #[gpui::test]
-    async fn test_history_items_vs_very_good_external_match(cx: &mut gpui::TestAppContext) {
-        let app_state = init_test(cx);
-
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/src",
-                json!({
-                    "collab_ui": {
-                        "first.rs": "// First Rust file",
-                        "second.rs": "// Second Rust file",
-                        "third.rs": "// Third Rust file",
-                        "collab_ui.rs": "// Fourth Rust file",
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        // generate some history to select from
-        open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
-        open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
-        open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
-        open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await;
-
-        let finder = open_file_picker(&workspace, cx);
-        let query = "collab_ui";
-        cx.simulate_input(query);
-        finder.update(cx, |finder, _| {
-            let delegate = &finder.delegate;
-            assert!(
-                delegate.matches.history.is_empty(),
-                "History items should not math query {query}, they should be matched by name only"
-            );
-
-            let search_entries = delegate
-                .matches
-                .search
-                .iter()
-                .map(|path_match| path_match.path.to_path_buf())
-                .collect::<Vec<_>>();
-            assert_eq!(
-                search_entries,
-                vec![
-                    PathBuf::from("collab_ui/collab_ui.rs"),
-                    PathBuf::from("collab_ui/third.rs"),
-                    PathBuf::from("collab_ui/first.rs"),
-                    PathBuf::from("collab_ui/second.rs"),
-                ],
-                "Despite all search results having the same directory name, the most matching one should be on top"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_nonexistent_history_items_not_shown(cx: &mut gpui::TestAppContext) {
-        let app_state = init_test(cx);
-
-        app_state
-            .fs
-            .as_fake()
-            .insert_tree(
-                "/src",
-                json!({
-                    "test": {
-                        "first.rs": "// First Rust file",
-                        "nonexistent.rs": "// Second Rust file",
-                        "third.rs": "// Third Rust file",
-                    }
-                }),
-            )
-            .await;
-
-        let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); // generate some history to select from
-        open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
-        open_close_queried_buffer("non", 1, "nonexistent.rs", &workspace, cx).await;
-        open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
-        open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
-
-        let picker = open_file_picker(&workspace, cx);
-        cx.simulate_input("rs");
-
-        picker.update(cx, |finder, _| {
-            let history_entries = finder.delegate
-                .matches
-                .history
-                .iter()
-                .map(|(_, path_match)| path_match.as_ref().expect("should have a path match").path.to_path_buf())
-                .collect::<Vec<_>>();
-            assert_eq!(
-                history_entries,
-                vec![
-                    PathBuf::from("test/first.rs"),
-                    PathBuf::from("test/third.rs"),
-                ],
-                "Should have all opened files in the history, except the ones that do not exist on disk"
-            );
-        });
-    }
-
-    async fn open_close_queried_buffer(
-        input: &str,
-        expected_matches: usize,
-        expected_editor_title: &str,
-        workspace: &View<Workspace>,
-        cx: &mut gpui::VisualTestContext<'_>,
-    ) -> Vec<FoundPath> {
-        let picker = open_file_picker(&workspace, cx);
-        cx.simulate_input(input);
-
-        let history_items = picker.update(cx, |finder, _| {
-            assert_eq!(
-                finder.delegate.matches.len(),
-                expected_matches,
-                "Unexpected number of matches found for query {input}"
-            );
-            finder.delegate.history_items.clone()
-        });
-
-        cx.dispatch_action(SelectNext);
-        cx.dispatch_action(Confirm);
-
-        cx.read(|cx| {
-            let active_editor = workspace.read(cx).active_item_as::<Editor>(cx).unwrap();
-            let active_editor_title = active_editor.read(cx).title(cx);
-            assert_eq!(
-                expected_editor_title, active_editor_title,
-                "Unexpected editor title for query {input}"
-            );
-        });
-
-        cx.dispatch_action(workspace::CloseActiveItem { save_intent: None });
-
-        history_items
-    }
-
-    fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
-        cx.update(|cx| {
-            let state = AppState::test(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
-            language::init(cx);
-            super::init(cx);
-            editor::init(cx);
-            workspace::init_settings(cx);
-            Project::init_settings(cx);
-            state
-        })
-    }
-
-    fn test_path_like(test_str: &str) -> PathLikeWithPosition<FileSearchQuery> {
-        PathLikeWithPosition::parse_str(test_str, |path_like_str| {
-            Ok::<_, std::convert::Infallible>(FileSearchQuery {
-                raw_query: test_str.to_owned(),
-                file_query_end: if path_like_str == test_str {
-                    None
-                } else {
-                    Some(path_like_str.len())
-                },
-            })
-        })
-        .unwrap()
-    }
-
-    fn build_find_picker(
-        project: Model<Project>,
-        cx: &mut TestAppContext,
-    ) -> (
-        View<Picker<FileFinderDelegate>>,
-        View<Workspace>,
-        &mut VisualTestContext,
-    ) {
-        let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx));
-        let picker = open_file_picker(&workspace, cx);
-        (picker, workspace, cx)
-    }
-
-    #[track_caller]
-    fn open_file_picker(
-        workspace: &View<Workspace>,
-        cx: &mut VisualTestContext,
-    ) -> View<Picker<FileFinderDelegate>> {
-        cx.dispatch_action(Toggle);
-        active_file_picker(workspace, cx)
-    }
-
-    #[track_caller]
-    fn active_file_picker(
-        workspace: &View<Workspace>,
-        cx: &mut VisualTestContext,
-    ) -> View<Picker<FileFinderDelegate>> {
-        workspace.update(cx, |workspace, cx| {
-            workspace
-                .active_modal::<FileFinder>(cx)
-                .unwrap()
-                .read(cx)
-                .picker
-                .clone()
-        })
-    }
-
-    fn collect_search_results(picker: &Picker<FileFinderDelegate>) -> Vec<PathBuf> {
-        let matches = &picker.delegate.matches;
-        assert!(
-            matches.history.is_empty(),
-            "Should have no history matches, but got: {:?}",
-            matches.history
-        );
-        let mut results = matches
-            .search
-            .iter()
-            .map(|path_match| Path::new(path_match.path_prefix.as_ref()).join(&path_match.path))
-            .collect::<Vec<_>>();
-        results.sort();
-        results
-    }
-}

crates/go_to_line/Cargo.toml 🔗

@@ -9,15 +9,17 @@ path = "src/go_to_line.rs"
 doctest = false
 
 [dependencies]
-editor = { path = "../editor" }
-gpui = { path = "../gpui" }
-menu = { path = "../menu" }
-settings = { path = "../settings" }
-text = { path = "../text" }
-workspace = { path = "../workspace" }
+editor = { package = "editor2", path = "../editor2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+menu = { package = "menu2", path = "../menu2" }
+serde.workspace = true
+settings = { package = "settings2", path = "../settings2" }
+text = { package = "text2", path = "../text2" }
+workspace = { package = "workspace2", path = "../workspace2" }
 postage.workspace = true
-theme = { path = "../theme" }
+theme = { package = "theme2", path = "../theme2" }
+ui = { package = "ui2", path = "../ui2" }
 util = { path = "../util" }
 
 [dev-dependencies]
-editor = { path = "../editor", features = ["test-support"] }
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }

crates/go_to_line/src/go_to_line.rs 🔗

@@ -1,117 +1,117 @@
-use std::sync::Arc;
-
 use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
 use gpui::{
-    actions, elements::*, geometry::vector::Vector2F, AnyViewHandle, AppContext, Axis, Entity,
-    View, ViewContext, ViewHandle,
+    actions, div, prelude::*, AnyWindowHandle, AppContext, DismissEvent, EventEmitter, FocusHandle,
+    FocusableView, Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext,
 };
-use menu::{Cancel, Confirm};
 use text::{Bias, Point};
+use theme::ActiveTheme;
+use ui::{h_stack, prelude::*, v_stack, Label};
 use util::paths::FILE_ROW_COLUMN_DELIMITER;
-use workspace::{Modal, Workspace};
+use workspace::ModalView;
 
 actions!(go_to_line, [Toggle]);
 
 pub fn init(cx: &mut AppContext) {
-    cx.add_action(GoToLine::toggle);
-    cx.add_action(GoToLine::confirm);
-    cx.add_action(GoToLine::cancel);
+    cx.observe_new_views(GoToLine::register).detach();
 }
 
 pub struct GoToLine {
-    line_editor: ViewHandle<Editor>,
-    active_editor: ViewHandle<Editor>,
-    prev_scroll_position: Option<Vector2F>,
-    cursor_point: Point,
-    max_point: Point,
-    has_focus: bool,
+    line_editor: View<Editor>,
+    active_editor: View<Editor>,
+    current_text: SharedString,
+    prev_scroll_position: Option<gpui::Point<f32>>,
+    _subscriptions: Vec<Subscription>,
 }
 
-pub enum Event {
-    Dismissed,
+impl ModalView for GoToLine {}
+
+impl FocusableView for GoToLine {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.line_editor.focus_handle(cx)
+    }
 }
+impl EventEmitter<DismissEvent> for GoToLine {}
 
 impl GoToLine {
-    pub fn new(active_editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) -> Self {
-        let line_editor = cx.add_view(|cx| {
-            Editor::single_line(
-                Some(Arc::new(|theme| theme.picker.input_editor.clone())),
-                cx,
-            )
-        });
-        cx.subscribe(&line_editor, Self::on_line_editor_event)
-            .detach();
-
-        let (scroll_position, cursor_point, max_point) = active_editor.update(cx, |editor, cx| {
-            let scroll_position = editor.scroll_position(cx);
-            let buffer = editor.buffer().read(cx).snapshot(cx);
-            (
-                Some(scroll_position),
-                editor.selections.newest(cx).head(),
-                buffer.max_point(),
-            )
+    fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
+        let handle = cx.view().downgrade();
+        editor.register_action(move |_: &Toggle, cx| {
+            let Some(editor) = handle.upgrade() else {
+                return;
+            };
+            let Some(workspace) = editor.read(cx).workspace() else {
+                return;
+            };
+            workspace.update(cx, |workspace, cx| {
+                workspace.toggle_modal(cx, move |cx| GoToLine::new(editor, cx));
+            })
         });
+    }
+
+    pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
+        let line_editor = cx.new_view(|cx| Editor::single_line(cx));
+        let line_editor_change = cx.subscribe(&line_editor, Self::on_line_editor_event);
+
+        let editor = active_editor.read(cx);
+        let cursor = editor.selections.last::<Point>(cx).head();
+        let last_line = editor.buffer().read(cx).snapshot(cx).max_point().row;
+        let scroll_position = active_editor.update(cx, |editor, cx| editor.scroll_position(cx));
+
+        let current_text = format!(
+            "line {} of {} (column {})",
+            cursor.row + 1,
+            last_line + 1,
+            cursor.column + 1,
+        );
 
         Self {
             line_editor,
             active_editor,
-            prev_scroll_position: scroll_position,
-            cursor_point,
-            max_point,
-            has_focus: false,
+            current_text: current_text.into(),
+            prev_scroll_position: Some(scroll_position),
+            _subscriptions: vec![line_editor_change, cx.on_release(Self::release)],
         }
     }
 
-    fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
-        if let Some(editor) = workspace
-            .active_item(cx)
-            .and_then(|active_item| active_item.downcast::<Editor>())
-        {
-            workspace.toggle_modal(cx, |_, cx| cx.add_view(|cx| GoToLine::new(editor, cx)));
-        }
+    fn release(&mut self, window: AnyWindowHandle, cx: &mut AppContext) {
+        window
+            .update(cx, |_, cx| {
+                let scroll_position = self.prev_scroll_position.take();
+                self.active_editor.update(cx, |editor, cx| {
+                    editor.highlight_rows(None);
+                    if let Some(scroll_position) = scroll_position {
+                        editor.set_scroll_position(scroll_position, cx);
+                    }
+                    cx.notify();
+                })
+            })
+            .ok();
     }
 
-    fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
-        cx.emit(Event::Dismissed);
+    fn on_line_editor_event(
+        &mut self,
+        _: View<Editor>,
+        event: &editor::EditorEvent,
+        cx: &mut ViewContext<Self>,
+    ) {
+        match event {
+            editor::EditorEvent::Blurred => cx.emit(DismissEvent),
+            editor::EditorEvent::BufferEdited { .. } => self.highlight_current_line(cx),
+            _ => {}
+        }
     }
 
-    fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
-        self.prev_scroll_position.take();
+    fn highlight_current_line(&mut self, cx: &mut ViewContext<Self>) {
         if let Some(point) = self.point_from_query(cx) {
             self.active_editor.update(cx, |active_editor, cx| {
                 let snapshot = active_editor.snapshot(cx).display_snapshot;
                 let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
-                active_editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-                    s.select_ranges([point..point])
-                });
+                let display_point = point.to_display_point(&snapshot);
+                let row = display_point.row();
+                active_editor.highlight_rows(Some(row..row + 1));
+                active_editor.request_autoscroll(Autoscroll::center(), cx);
             });
-        }
-
-        cx.emit(Event::Dismissed);
-    }
-
-    fn on_line_editor_event(
-        &mut self,
-        _: ViewHandle<Editor>,
-        event: &editor::Event,
-        cx: &mut ViewContext<Self>,
-    ) {
-        match event {
-            editor::Event::Blurred => cx.emit(Event::Dismissed),
-            editor::Event::BufferEdited { .. } => {
-                if let Some(point) = self.point_from_query(cx) {
-                    self.active_editor.update(cx, |active_editor, cx| {
-                        let snapshot = active_editor.snapshot(cx).display_snapshot;
-                        let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
-                        let display_point = point.to_display_point(&snapshot);
-                        let row = display_point.row();
-                        active_editor.highlight_rows(Some(row..row + 1));
-                        active_editor.request_autoscroll(Autoscroll::center(), cx);
-                    });
-                    cx.notify();
-                }
-            }
-            _ => {}
+            cx.notify();
         }
     }
 
@@ -128,73 +128,61 @@ impl GoToLine {
             column.unwrap_or(0).saturating_sub(1),
         ))
     }
-}
-
-impl Entity for GoToLine {
-    type Event = Event;
-
-    fn release(&mut self, cx: &mut AppContext) {
-        let scroll_position = self.prev_scroll_position.take();
-        self.active_editor.window().update(cx, |cx| {
-            self.active_editor.update(cx, |editor, cx| {
-                editor.highlight_rows(None);
-                if let Some(scroll_position) = scroll_position {
-                    editor.set_scroll_position(scroll_position, cx);
-                }
-            })
-        });
-    }
-}
-
-impl View for GoToLine {
-    fn ui_name() -> &'static str {
-        "GoToLine"
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-        let theme = &theme::current(cx).picker;
 
-        let label = format!(
-            "{}{FILE_ROW_COLUMN_DELIMITER}{} of {} lines",
-            self.cursor_point.row + 1,
-            self.cursor_point.column + 1,
-            self.max_point.row + 1
-        );
-
-        Flex::new(Axis::Vertical)
-            .with_child(
-                ChildView::new(&self.line_editor, cx)
-                    .contained()
-                    .with_style(theme.input_editor.container),
-            )
-            .with_child(
-                Label::new(label, theme.no_matches.label.clone())
-                    .contained()
-                    .with_style(theme.no_matches.container),
-            )
-            .contained()
-            .with_style(theme.container)
-            .constrained()
-            .with_max_width(500.0)
-            .into_any_named("go to line")
+    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+        cx.emit(DismissEvent);
     }
 
-    fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
-        self.has_focus = true;
-        cx.focus(&self.line_editor);
-    }
+    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+        if let Some(point) = self.point_from_query(cx) {
+            self.active_editor.update(cx, |editor, cx| {
+                let snapshot = editor.snapshot(cx).display_snapshot;
+                let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
+                editor.change_selections(Some(Autoscroll::center()), cx, |s| {
+                    s.select_ranges([point..point])
+                });
+                editor.focus(cx);
+                cx.notify();
+            });
+            self.prev_scroll_position.take();
+        }
 
-    fn focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {
-        self.has_focus = false;
+        cx.emit(DismissEvent);
     }
 }
 
-impl Modal for GoToLine {
-    fn has_focus(&self) -> bool {
-        self.has_focus
-    }
-
-    fn dismiss_on_event(event: &Self::Event) -> bool {
-        matches!(event, Event::Dismissed)
+impl Render for GoToLine {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        div()
+            .elevation_2(cx)
+            .key_context("GoToLine")
+            .on_action(cx.listener(Self::cancel))
+            .on_action(cx.listener(Self::confirm))
+            .w_96()
+            .child(
+                v_stack()
+                    .px_1()
+                    .pt_0p5()
+                    .gap_px()
+                    .child(
+                        v_stack()
+                            .py_0p5()
+                            .px_1()
+                            .child(div().px_1().py_0p5().child(self.line_editor.clone())),
+                    )
+                    .child(
+                        div()
+                            .h_px()
+                            .w_full()
+                            .bg(cx.theme().colors().element_background),
+                    )
+                    .child(
+                        h_stack()
+                            .justify_between()
+                            .px_2()
+                            .py_1()
+                            .child(Label::new(self.current_text.clone()).color(Color::Muted)),
+                    ),
+            )
     }
 }

crates/go_to_line2/Cargo.toml 🔗

@@ -1,25 +0,0 @@
-[package]
-name = "go_to_line2"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-path = "src/go_to_line.rs"
-doctest = false
-
-[dependencies]
-editor = { package = "editor2", path = "../editor2" }
-gpui = { package = "gpui2", path = "../gpui2" }
-menu = { package = "menu2", path = "../menu2" }
-serde.workspace = true
-settings = { package = "settings2", path = "../settings2" }
-text = { package = "text2", path = "../text2" }
-workspace = { package = "workspace2", path = "../workspace2" }
-postage.workspace = true
-theme = { package = "theme2", path = "../theme2" }
-ui = { package = "ui2", path = "../ui2" }
-util = { path = "../util" }
-
-[dev-dependencies]
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }

crates/go_to_line2/src/go_to_line.rs 🔗

@@ -1,188 +0,0 @@
-use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
-use gpui::{
-    actions, div, prelude::*, AnyWindowHandle, AppContext, DismissEvent, EventEmitter, FocusHandle,
-    FocusableView, Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext,
-};
-use text::{Bias, Point};
-use theme::ActiveTheme;
-use ui::{h_stack, prelude::*, v_stack, Label};
-use util::paths::FILE_ROW_COLUMN_DELIMITER;
-use workspace::ModalView;
-
-actions!(go_to_line, [Toggle]);
-
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(GoToLine::register).detach();
-}
-
-pub struct GoToLine {
-    line_editor: View<Editor>,
-    active_editor: View<Editor>,
-    current_text: SharedString,
-    prev_scroll_position: Option<gpui::Point<f32>>,
-    _subscriptions: Vec<Subscription>,
-}
-
-impl ModalView for GoToLine {}
-
-impl FocusableView for GoToLine {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
-        self.line_editor.focus_handle(cx)
-    }
-}
-impl EventEmitter<DismissEvent> for GoToLine {}
-
-impl GoToLine {
-    fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
-        let handle = cx.view().downgrade();
-        editor.register_action(move |_: &Toggle, cx| {
-            let Some(editor) = handle.upgrade() else {
-                return;
-            };
-            let Some(workspace) = editor.read(cx).workspace() else {
-                return;
-            };
-            workspace.update(cx, |workspace, cx| {
-                workspace.toggle_modal(cx, move |cx| GoToLine::new(editor, cx));
-            })
-        });
-    }
-
-    pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
-        let line_editor = cx.new_view(|cx| Editor::single_line(cx));
-        let line_editor_change = cx.subscribe(&line_editor, Self::on_line_editor_event);
-
-        let editor = active_editor.read(cx);
-        let cursor = editor.selections.last::<Point>(cx).head();
-        let last_line = editor.buffer().read(cx).snapshot(cx).max_point().row;
-        let scroll_position = active_editor.update(cx, |editor, cx| editor.scroll_position(cx));
-
-        let current_text = format!(
-            "line {} of {} (column {})",
-            cursor.row + 1,
-            last_line + 1,
-            cursor.column + 1,
-        );
-
-        Self {
-            line_editor,
-            active_editor,
-            current_text: current_text.into(),
-            prev_scroll_position: Some(scroll_position),
-            _subscriptions: vec![line_editor_change, cx.on_release(Self::release)],
-        }
-    }
-
-    fn release(&mut self, window: AnyWindowHandle, cx: &mut AppContext) {
-        window
-            .update(cx, |_, cx| {
-                let scroll_position = self.prev_scroll_position.take();
-                self.active_editor.update(cx, |editor, cx| {
-                    editor.highlight_rows(None);
-                    if let Some(scroll_position) = scroll_position {
-                        editor.set_scroll_position(scroll_position, cx);
-                    }
-                    cx.notify();
-                })
-            })
-            .ok();
-    }
-
-    fn on_line_editor_event(
-        &mut self,
-        _: View<Editor>,
-        event: &editor::EditorEvent,
-        cx: &mut ViewContext<Self>,
-    ) {
-        match event {
-            editor::EditorEvent::Blurred => cx.emit(DismissEvent),
-            editor::EditorEvent::BufferEdited { .. } => self.highlight_current_line(cx),
-            _ => {}
-        }
-    }
-
-    fn highlight_current_line(&mut self, cx: &mut ViewContext<Self>) {
-        if let Some(point) = self.point_from_query(cx) {
-            self.active_editor.update(cx, |active_editor, cx| {
-                let snapshot = active_editor.snapshot(cx).display_snapshot;
-                let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
-                let display_point = point.to_display_point(&snapshot);
-                let row = display_point.row();
-                active_editor.highlight_rows(Some(row..row + 1));
-                active_editor.request_autoscroll(Autoscroll::center(), cx);
-            });
-            cx.notify();
-        }
-    }
-
-    fn point_from_query(&self, cx: &ViewContext<Self>) -> Option<Point> {
-        let line_editor = self.line_editor.read(cx).text(cx);
-        let mut components = line_editor
-            .splitn(2, FILE_ROW_COLUMN_DELIMITER)
-            .map(str::trim)
-            .fuse();
-        let row = components.next().and_then(|row| row.parse::<u32>().ok())?;
-        let column = components.next().and_then(|col| col.parse::<u32>().ok());
-        Some(Point::new(
-            row.saturating_sub(1),
-            column.unwrap_or(0).saturating_sub(1),
-        ))
-    }
-
-    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
-        cx.emit(DismissEvent);
-    }
-
-    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
-        if let Some(point) = self.point_from_query(cx) {
-            self.active_editor.update(cx, |editor, cx| {
-                let snapshot = editor.snapshot(cx).display_snapshot;
-                let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
-                editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-                    s.select_ranges([point..point])
-                });
-                editor.focus(cx);
-                cx.notify();
-            });
-            self.prev_scroll_position.take();
-        }
-
-        cx.emit(DismissEvent);
-    }
-}
-
-impl Render for GoToLine {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        div()
-            .elevation_2(cx)
-            .key_context("GoToLine")
-            .on_action(cx.listener(Self::cancel))
-            .on_action(cx.listener(Self::confirm))
-            .w_96()
-            .child(
-                v_stack()
-                    .px_1()
-                    .pt_0p5()
-                    .gap_px()
-                    .child(
-                        v_stack()
-                            .py_0p5()
-                            .px_1()
-                            .child(div().px_1().py_0p5().child(self.line_editor.clone())),
-                    )
-                    .child(
-                        div()
-                            .h_px()
-                            .w_full()
-                            .bg(cx.theme().colors().element_background),
-                    )
-                    .child(
-                        h_stack()
-                            .justify_between()
-                            .px_2()
-                            .py_1()
-                            .child(Label::new(self.current_text.clone()).color(Color::Muted)),
-                    ),
-            )
-    }
-}

crates/vim/Cargo.toml 🔗

@@ -26,28 +26,28 @@ serde_json.workspace = true
 
 collections = { path = "../collections" }
 command_palette = { path = "../command_palette" }
-editor = { path = "../editor" }
-gpui = { path = "../gpui" }
-language = { path = "../language" }
-search = { path = "../search" }
-settings = { path = "../settings" }
-workspace = { path = "../workspace" }
-theme = { path = "../theme" }
-language_selector = { path = "../language_selector"}
+editor = { package = "editor2", path = "../editor2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+language = { package = "language2", path = "../language2" }
+search = { package = "search2", path = "../search2" }
+settings = { package = "settings2", path = "../settings2" }
+workspace = { package = "workspace2", path = "../workspace2" }
+theme = { package = "theme2", path = "../theme2" }
+ui = { package = "ui2", path = "../ui2"}
 diagnostics = { path = "../diagnostics" }
-zed-actions = { path = "../zed-actions" }
+zed_actions = { package = "zed_actions2", path = "../zed_actions2" }
 
 [dev-dependencies]
 indoc.workspace = true
 parking_lot.workspace = true
 futures.workspace = true
 
-editor = { path = "../editor", features = ["test-support"] }
-gpui = { path = "../gpui", features = ["test-support"] }
-language = { path = "../language", features = ["test-support"] }
-project = { path = "../project", features = ["test-support"] }
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
+project = { package = "project2", path = "../project2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
-settings = { path = "../settings" }
-workspace = { path = "../workspace", features = ["test-support"] }
-theme = { path = "../theme", features = ["test-support"] }
-lsp = { path = "../lsp", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2" }
+workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
+theme = { package = "theme2", path = "../theme2", features = ["test-support"] }
+lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }

crates/vim/src/command.rs 🔗

@@ -1,6 +1,6 @@
 use command_palette::CommandInterceptResult;
 use editor::{SortLinesCaseInsensitive, SortLinesCaseSensitive};
-use gpui::{impl_actions, Action, AppContext};
+use gpui::{impl_actions, Action, AppContext, ViewContext};
 use serde_derive::Deserialize;
 use workspace::{SaveIntent, Workspace};
 
@@ -22,8 +22,8 @@ pub struct GoToLine {
 
 impl_actions!(vim, [GoToLine]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(|_: &mut Workspace, action: &GoToLine, cx| {
+pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(|_: &mut Workspace, action: &GoToLine, cx| {
         Vim::update(cx, |vim, cx| {
             vim.switch_mode(Mode::Normal, false, cx);
             move_cursor(vim, Motion::StartOfDocument, Some(action.line as usize), cx);
@@ -293,14 +293,11 @@ mod test {
     use std::path::Path;
 
     use crate::test::{NeovimBackedTestContext, VimTestContext};
-    use gpui::{executor::Foreground, TestAppContext};
+    use gpui::TestAppContext;
     use indoc::indoc;
 
     #[gpui::test]
     async fn test_command_basics(cx: &mut TestAppContext) {
-        if let Foreground::Deterministic { cx_id: _, executor } = cx.foreground().as_ref() {
-            executor.run_until_parked();
-        }
         let mut cx = NeovimBackedTestContext::new(cx).await;
 
         cx.set_shared_state(indoc! {"
@@ -410,15 +407,14 @@ mod test {
         // conflict!
         cx.simulate_keystrokes(["i", "@", "escape"]);
         cx.simulate_keystrokes([":", "w", "enter"]);
-        let window = cx.window;
-        assert!(window.has_pending_prompt(cx.cx));
+        assert!(cx.has_pending_prompt());
         // "Cancel"
-        window.simulate_prompt_answer(0, cx.cx);
+        cx.simulate_prompt_answer(0);
         assert_eq!(fs.load(&path).await.unwrap(), "oops\n");
-        assert!(!window.has_pending_prompt(cx.cx));
+        assert!(!cx.has_pending_prompt());
         // force overwrite
         cx.simulate_keystrokes([":", "w", "!", "enter"]);
-        assert!(!window.has_pending_prompt(cx.cx));
+        assert!(!cx.has_pending_prompt());
         assert_eq!(fs.load(&path).await.unwrap(), "@@\n");
     }
 

crates/vim/src/editor_events.rs 🔗

@@ -1,65 +1,67 @@
-use crate::{Vim, VimEvent};
-use editor::{EditorBlurred, EditorFocused, EditorReleased};
-use gpui::AppContext;
+use crate::Vim;
+use editor::{Editor, EditorEvent};
+use gpui::{AppContext, Entity, EntityId, View, ViewContext, WindowContext};
 
 pub fn init(cx: &mut AppContext) {
-    cx.subscribe_global(focused).detach();
-    cx.subscribe_global(blurred).detach();
-    cx.subscribe_global(released).detach();
+    cx.observe_new_views(|_, cx: &mut ViewContext<Editor>| {
+        let editor = cx.view().clone();
+        cx.subscribe(&editor, |_, editor, event: &EditorEvent, cx| match event {
+            EditorEvent::Focused => cx.window_context().defer(|cx| focused(editor, cx)),
+            EditorEvent::Blurred => cx.window_context().defer(|cx| blurred(editor, cx)),
+            _ => {}
+        })
+        .detach();
+
+        let id = cx.view().entity_id();
+        cx.on_release(move |_, _, cx| released(id, cx)).detach();
+    })
+    .detach();
 }
 
-fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) {
-    if let Some(previously_active_editor) = Vim::read(cx).active_editor.clone() {
-        previously_active_editor.window().update(cx, |cx| {
-            Vim::update(cx, |vim, cx| {
-                vim.update_active_editor(cx, |previously_active_editor, cx| {
-                    vim.unhook_vim_settings(previously_active_editor, cx)
-                });
+fn focused(editor: View<Editor>, cx: &mut WindowContext) {
+    if Vim::read(cx).active_editor.clone().is_some() {
+        Vim::update(cx, |vim, cx| {
+            vim.update_active_editor(cx, |previously_active_editor, cx| {
+                vim.unhook_vim_settings(previously_active_editor, cx)
             });
         });
     }
 
-    editor.window().update(cx, |cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.set_active_editor(editor.clone(), cx);
-            if vim.enabled {
-                cx.emit_global(VimEvent::ModeChanged {
-                    mode: vim.state().mode,
-                });
-            }
-        });
+    Vim::update(cx, |vim, cx| {
+        vim.set_active_editor(editor.clone(), cx);
     });
 }
 
-fn blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut AppContext) {
-    editor.window().update(cx, |cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.workspace_state.recording = false;
-            vim.workspace_state.recorded_actions.clear();
-            if let Some(previous_editor) = vim.active_editor.clone() {
-                if previous_editor == editor.clone() {
-                    vim.clear_operator(cx);
-                    vim.active_editor = None;
-                    vim.editor_subscription = None;
-                }
+fn blurred(editor: View<Editor>, cx: &mut WindowContext) {
+    Vim::update(cx, |vim, cx| {
+        vim.workspace_state.recording = false;
+        vim.workspace_state.recorded_actions.clear();
+        if let Some(previous_editor) = vim.active_editor.clone() {
+            if previous_editor
+                .upgrade()
+                .is_some_and(|previous| previous == editor.clone())
+            {
+                vim.clear_operator(cx);
+                vim.active_editor = None;
+                vim.editor_subscription = None;
             }
+        }
 
-            editor.update(cx, |editor, cx| vim.unhook_vim_settings(editor, cx))
-        });
+        editor.update(cx, |editor, cx| vim.unhook_vim_settings(editor, cx))
     });
 }
 
-fn released(EditorReleased(editor): &EditorReleased, cx: &mut AppContext) {
-    editor.window().update(cx, |cx| {
-        Vim::update(cx, |vim, _| {
-            if let Some(previous_editor) = vim.active_editor.clone() {
-                if previous_editor == editor.clone() {
-                    vim.active_editor = None;
-                    vim.editor_subscription = None;
-                }
-            }
-            vim.editor_states.remove(&editor.id())
-        });
+fn released(entity_id: EntityId, cx: &mut AppContext) {
+    cx.update_global(|vim: &mut Vim, _| {
+        if vim
+            .active_editor
+            .as_ref()
+            .is_some_and(|previous| previous.entity_id() == entity_id)
+        {
+            vim.active_editor = None;
+            vim.editor_subscription = None;
+        }
+        vim.editor_states.remove(&entity_id)
     });
 }
 
@@ -67,7 +69,7 @@ fn released(EditorReleased(editor): &EditorReleased, cx: &mut AppContext) {
 mod test {
     use crate::{test::VimTestContext, Vim};
     use editor::Editor;
-    use gpui::View;
+    use gpui::{Context, Entity};
     use language::Buffer;
 
     // regression test for blur called with a different active editor
@@ -75,18 +77,28 @@ mod test {
     async fn test_blur_focus(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
 
-        let buffer = cx.add_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
+        let buffer = cx.new_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
         let window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx));
-        let editor2 = cx.read(|cx| window2.root(cx)).unwrap();
+        let editor2 = cx
+            .update(|cx| {
+                window2.update(cx, |_, cx| {
+                    cx.focus_self();
+                    cx.view().clone()
+                })
+            })
+            .unwrap();
 
         cx.update(|cx| {
             let vim = Vim::read(cx);
-            assert_eq!(vim.active_editor.unwrap().id(), editor2.id())
+            assert_eq!(
+                vim.active_editor.as_ref().unwrap().entity_id(),
+                editor2.entity_id(),
+            )
         });
 
         // no panic when blurring an editor in a different window.
         cx.update_editor(|editor1, cx| {
-            editor1.focus_out(cx.handle().into_any(), cx);
+            editor1.handle_blur(cx);
         });
     }
 }

crates/vim/src/insert.rs 🔗

@@ -1,13 +1,13 @@
 use crate::{normal::repeat, state::Mode, Vim};
 use editor::{scroll::autoscroll::Autoscroll, Bias};
-use gpui::{actions, Action, AppContext, ViewContext};
+use gpui::{actions, Action, ViewContext};
 use language::SelectionGoal;
 use workspace::Workspace;
 
 actions!(vim, [NormalBefore]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(normal_before);
+pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(normal_before);
 }
 
 fn normal_before(_: &mut Workspace, action: &NormalBefore, cx: &mut ViewContext<Workspace>) {
@@ -38,10 +38,6 @@ fn normal_before(_: &mut Workspace, action: &NormalBefore, cx: &mut ViewContext<
 
 #[cfg(test)]
 mod test {
-    use std::sync::Arc;
-
-    use gpui::executor::Deterministic;
-
     use crate::{
         state::Mode,
         test::{NeovimBackedTestContext, VimTestContext},
@@ -60,76 +56,70 @@ mod test {
     }
 
     #[gpui::test]
-    async fn test_insert_with_counts(
-        deterministic: Arc<Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
+    async fn test_insert_with_counts(cx: &mut gpui::TestAppContext) {
         let mut cx = NeovimBackedTestContext::new(cx).await;
 
         cx.set_shared_state("ˇhello\n").await;
         cx.simulate_shared_keystrokes(["5", "i", "-", "escape"])
             .await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("----ˇ-hello\n").await;
 
         cx.set_shared_state("ˇhello\n").await;
         cx.simulate_shared_keystrokes(["5", "a", "-", "escape"])
             .await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("h----ˇ-ello\n").await;
 
         cx.simulate_shared_keystrokes(["4", "shift-i", "-", "escape"])
             .await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("---ˇ-h-----ello\n").await;
 
         cx.simulate_shared_keystrokes(["3", "shift-a", "-", "escape"])
             .await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("----h-----ello--ˇ-\n").await;
 
         cx.set_shared_state("ˇhello\n").await;
         cx.simulate_shared_keystrokes(["3", "o", "o", "i", "escape"])
             .await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("hello\noi\noi\noˇi\n").await;
 
         cx.set_shared_state("ˇhello\n").await;
         cx.simulate_shared_keystrokes(["3", "shift-o", "o", "i", "escape"])
             .await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("oi\noi\noˇi\nhello\n").await;
     }
 
     #[gpui::test]
-    async fn test_insert_with_repeat(
-        deterministic: Arc<Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
+    async fn test_insert_with_repeat(cx: &mut gpui::TestAppContext) {
         let mut cx = NeovimBackedTestContext::new(cx).await;
 
         cx.set_shared_state("ˇhello\n").await;
         cx.simulate_shared_keystrokes(["3", "i", "-", "escape"])
             .await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("--ˇ-hello\n").await;
         cx.simulate_shared_keystrokes(["."]).await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("----ˇ--hello\n").await;
         cx.simulate_shared_keystrokes(["2", "."]).await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("-----ˇ---hello\n").await;
 
         cx.set_shared_state("ˇhello\n").await;
         cx.simulate_shared_keystrokes(["2", "o", "k", "k", "escape"])
             .await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("hello\nkk\nkˇk\n").await;
         cx.simulate_shared_keystrokes(["."]).await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("hello\nkk\nkk\nkk\nkˇk\n").await;
         cx.simulate_shared_keystrokes(["1", "."]).await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_shared_state("hello\nkk\nkk\nkk\nkk\nkˇk\n").await;
     }
 }

crates/vim/src/mode_indicator.rs 🔗

@@ -1,58 +1,40 @@
-use gpui::{
-    elements::{Empty, Label},
-    AnyElement, Element, Entity, Subscription, View, ViewContext,
-};
+use gpui::{div, Element, Render, Subscription, ViewContext};
 use settings::SettingsStore;
-use workspace::{item::ItemHandle, StatusItemView};
+use workspace::{item::ItemHandle, ui::prelude::*, StatusItemView};
 
-use crate::{state::Mode, Vim, VimEvent, VimModeSetting};
+use crate::{state::Mode, Vim};
 
 pub struct ModeIndicator {
     pub mode: Option<Mode>,
-    _subscription: Subscription,
+    _subscriptions: Vec<Subscription>,
 }
 
 impl ModeIndicator {
     pub fn new(cx: &mut ViewContext<Self>) -> Self {
-        let handle = cx.handle().downgrade();
+        let _subscriptions = vec![
+            cx.observe_global::<Vim>(|this, cx| this.update_mode(cx)),
+            cx.observe_global::<SettingsStore>(|this, cx| this.update_mode(cx)),
+        ];
 
-        let _subscription = cx.subscribe_global::<VimEvent, _>(move |&event, cx| {
-            if let Some(mode_indicator) = handle.upgrade(cx) {
-                match event {
-                    VimEvent::ModeChanged { mode } => {
-                        mode_indicator.window().update(cx, |cx| {
-                            mode_indicator.update(cx, move |mode_indicator, cx| {
-                                mode_indicator.set_mode(mode, cx);
-                            })
-                        });
-                    }
-                }
-            }
-        });
-
-        cx.observe_global::<SettingsStore, _>(move |mode_indicator, cx| {
-            if settings::get::<VimModeSetting>(cx).0 {
-                mode_indicator.mode = cx
-                    .has_global::<Vim>()
-                    .then(|| cx.global::<Vim>().state().mode);
-            } else {
-                mode_indicator.mode.take();
-            }
-        })
-        .detach();
+        let mut this = Self {
+            mode: None,
+            _subscriptions,
+        };
+        this.update_mode(cx);
+        this
+    }
 
+    fn update_mode(&mut self, cx: &mut ViewContext<Self>) {
         // Vim doesn't exist in some tests
-        let mode = cx
-            .has_global::<Vim>()
-            .then(|| {
-                let vim = cx.global::<Vim>();
-                vim.enabled.then(|| vim.state().mode)
-            })
-            .flatten();
+        if !cx.has_global::<Vim>() {
+            return;
+        }
 
-        Self {
-            mode,
-            _subscription,
+        let vim = Vim::read(cx);
+        if vim.enabled {
+            self.mode = Some(vim.state().mode);
+        } else {
+            self.mode = None;
         }
     }
 
@@ -64,22 +46,12 @@ impl ModeIndicator {
     }
 }
 
-impl Entity for ModeIndicator {
-    type Event = ();
-}
-
-impl View for ModeIndicator {
-    fn ui_name() -> &'static str {
-        "ModeIndicatorView"
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+impl Render for ModeIndicator {
+    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
         let Some(mode) = self.mode.as_ref() else {
-            return Empty::new().into_any();
+            return div().into_any();
         };
 
-        let theme = &theme::current(cx).workspace.status_bar;
-
         let text = match mode {
             Mode::Normal => "-- NORMAL --",
             Mode::Insert => "-- INSERT --",
@@ -87,10 +59,7 @@ impl View for ModeIndicator {
             Mode::VisualLine => "-- VISUAL LINE --",
             Mode::VisualBlock => "-- VISUAL BLOCK --",
         };
-        Label::new(text, theme.vim_mode_indicator.text.clone())
-            .contained()
-            .with_style(theme.vim_mode_indicator.container)
-            .into_any()
+        Label::new(text).size(LabelSize::Small).into_any_element()
     }
 }
 

crates/vim/src/motion.rs 🔗

@@ -4,7 +4,7 @@ use editor::{
     movement::{self, find_boundary, find_preceding_boundary, FindRange, TextLayoutDetails},
     Bias, CharKind, DisplayPoint, ToOffset,
 };
-use gpui::{actions, impl_actions, AppContext, WindowContext};
+use gpui::{actions, impl_actions, px, ViewContext, WindowContext};
 use language::{Point, Selection, SelectionGoal};
 use serde::Deserialize;
 use workspace::Workspace;
@@ -105,6 +105,21 @@ struct RepeatFind {
     backwards: bool,
 }
 
+impl_actions!(
+    vim,
+    [
+        RepeatFind,
+        StartOfLine,
+        EndOfLine,
+        FirstNonWhitespace,
+        Down,
+        Up,
+        PreviousWordStart,
+        NextWordEnd,
+        NextWordStart
+    ]
+);
+
 actions!(
     vim,
     [
@@ -123,25 +138,12 @@ actions!(
         GoToColumn,
     ]
 );
-impl_actions!(
-    vim,
-    [
-        NextWordStart,
-        NextWordEnd,
-        PreviousWordStart,
-        RepeatFind,
-        Up,
-        Down,
-        FirstNonWhitespace,
-        EndOfLine,
-        StartOfLine,
-    ]
-);
 
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(|_: &mut Workspace, _: &Left, cx: _| motion(Motion::Left, cx));
-    cx.add_action(|_: &mut Workspace, _: &Backspace, cx: _| motion(Motion::Backspace, cx));
-    cx.add_action(|_: &mut Workspace, action: &Down, cx: _| {
+pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(|_: &mut Workspace, _: &Left, cx: _| motion(Motion::Left, cx));
+    workspace
+        .register_action(|_: &mut Workspace, _: &Backspace, cx: _| motion(Motion::Backspace, cx));
+    workspace.register_action(|_: &mut Workspace, action: &Down, cx: _| {
         motion(
             Motion::Down {
                 display_lines: action.display_lines,
@@ -149,7 +151,7 @@ pub fn init(cx: &mut AppContext) {
             cx,
         )
     });
-    cx.add_action(|_: &mut Workspace, action: &Up, cx: _| {
+    workspace.register_action(|_: &mut Workspace, action: &Up, cx: _| {
         motion(
             Motion::Up {
                 display_lines: action.display_lines,
@@ -157,8 +159,8 @@ pub fn init(cx: &mut AppContext) {
             cx,
         )
     });
-    cx.add_action(|_: &mut Workspace, _: &Right, cx: _| motion(Motion::Right, cx));
-    cx.add_action(|_: &mut Workspace, action: &FirstNonWhitespace, cx: _| {
+    workspace.register_action(|_: &mut Workspace, _: &Right, cx: _| motion(Motion::Right, cx));
+    workspace.register_action(|_: &mut Workspace, action: &FirstNonWhitespace, cx: _| {
         motion(
             Motion::FirstNonWhitespace {
                 display_lines: action.display_lines,
@@ -166,7 +168,7 @@ pub fn init(cx: &mut AppContext) {
             cx,
         )
     });
-    cx.add_action(|_: &mut Workspace, action: &StartOfLine, cx: _| {
+    workspace.register_action(|_: &mut Workspace, action: &StartOfLine, cx: _| {
         motion(
             Motion::StartOfLine {
                 display_lines: action.display_lines,
@@ -174,7 +176,7 @@ pub fn init(cx: &mut AppContext) {
             cx,
         )
     });
-    cx.add_action(|_: &mut Workspace, action: &EndOfLine, cx: _| {
+    workspace.register_action(|_: &mut Workspace, action: &EndOfLine, cx: _| {
         motion(
             Motion::EndOfLine {
                 display_lines: action.display_lines,
@@ -182,45 +184,53 @@ pub fn init(cx: &mut AppContext) {
             cx,
         )
     });
-    cx.add_action(|_: &mut Workspace, _: &CurrentLine, cx: _| motion(Motion::CurrentLine, cx));
-    cx.add_action(|_: &mut Workspace, _: &StartOfParagraph, cx: _| {
+    workspace.register_action(|_: &mut Workspace, _: &CurrentLine, cx: _| {
+        motion(Motion::CurrentLine, cx)
+    });
+    workspace.register_action(|_: &mut Workspace, _: &StartOfParagraph, cx: _| {
         motion(Motion::StartOfParagraph, cx)
     });
-    cx.add_action(|_: &mut Workspace, _: &EndOfParagraph, cx: _| {
+    workspace.register_action(|_: &mut Workspace, _: &EndOfParagraph, cx: _| {
         motion(Motion::EndOfParagraph, cx)
     });
-    cx.add_action(|_: &mut Workspace, _: &StartOfDocument, cx: _| {
+    workspace.register_action(|_: &mut Workspace, _: &StartOfDocument, cx: _| {
         motion(Motion::StartOfDocument, cx)
     });
-    cx.add_action(|_: &mut Workspace, _: &EndOfDocument, cx: _| motion(Motion::EndOfDocument, cx));
-    cx.add_action(|_: &mut Workspace, _: &Matching, cx: _| motion(Motion::Matching, cx));
+    workspace.register_action(|_: &mut Workspace, _: &EndOfDocument, cx: _| {
+        motion(Motion::EndOfDocument, cx)
+    });
+    workspace
+        .register_action(|_: &mut Workspace, _: &Matching, cx: _| motion(Motion::Matching, cx));
 
-    cx.add_action(
+    workspace.register_action(
         |_: &mut Workspace, &NextWordStart { ignore_punctuation }: &NextWordStart, cx: _| {
             motion(Motion::NextWordStart { ignore_punctuation }, cx)
         },
     );
-    cx.add_action(
+    workspace.register_action(
         |_: &mut Workspace, &NextWordEnd { ignore_punctuation }: &NextWordEnd, cx: _| {
             motion(Motion::NextWordEnd { ignore_punctuation }, cx)
         },
     );
-    cx.add_action(
+    workspace.register_action(
         |_: &mut Workspace,
          &PreviousWordStart { ignore_punctuation }: &PreviousWordStart,
          cx: _| { motion(Motion::PreviousWordStart { ignore_punctuation }, cx) },
     );
-    cx.add_action(|_: &mut Workspace, &NextLineStart, cx: _| motion(Motion::NextLineStart, cx));
-    cx.add_action(|_: &mut Workspace, &StartOfLineDownward, cx: _| {
+    workspace.register_action(|_: &mut Workspace, &NextLineStart, cx: _| {
+        motion(Motion::NextLineStart, cx)
+    });
+    workspace.register_action(|_: &mut Workspace, &StartOfLineDownward, cx: _| {
         motion(Motion::StartOfLineDownward, cx)
     });
-    cx.add_action(|_: &mut Workspace, &EndOfLineDownward, cx: _| {
+    workspace.register_action(|_: &mut Workspace, &EndOfLineDownward, cx: _| {
         motion(Motion::EndOfLineDownward, cx)
     });
-    cx.add_action(|_: &mut Workspace, &GoToColumn, cx: _| motion(Motion::GoToColumn, cx));
-    cx.add_action(|_: &mut Workspace, action: &RepeatFind, cx: _| {
+    workspace
+        .register_action(|_: &mut Workspace, &GoToColumn, cx: _| motion(Motion::GoToColumn, cx));
+    workspace.register_action(|_: &mut Workspace, action: &RepeatFind, cx: _| {
         repeat_motion(action.backwards, cx)
-    })
+    });
 }
 
 pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) {
@@ -578,9 +588,9 @@ fn up_down_buffer_rows(
         SelectionGoal::HorizontalRange { end, .. } => (select_nth_wrapped_row, end),
         SelectionGoal::HorizontalPosition(x) => (select_nth_wrapped_row, x),
         _ => {
-            let x = map.x_for_point(point, text_layout_details);
-            goal = SelectionGoal::WrappedHorizontalPosition((select_nth_wrapped_row, x));
-            (select_nth_wrapped_row, x)
+            let x = map.x_for_display_point(point, text_layout_details);
+            goal = SelectionGoal::WrappedHorizontalPosition((select_nth_wrapped_row, x.0));
+            (select_nth_wrapped_row, x.0)
         }
     };
 
@@ -608,7 +618,7 @@ fn up_down_buffer_rows(
     }
 
     let new_col = if i == goal_wrap {
-        map.column_for_x(begin_folded_line.row(), goal_x, text_layout_details)
+        map.display_column_for_x(begin_folded_line.row(), px(goal_x), text_layout_details)
     } else {
         map.line_len(begin_folded_line.row())
     };
@@ -943,7 +953,6 @@ pub(crate) fn next_line_end(
 }
 
 #[cfg(test)]
-
 mod test {
 
     use crate::test::NeovimBackedTestContext;

crates/vim/src/normal.rs 🔗

@@ -20,7 +20,7 @@ use crate::{
 use collections::HashSet;
 use editor::scroll::autoscroll::Autoscroll;
 use editor::{Bias, DisplayPoint};
-use gpui::{actions, AppContext, ViewContext, WindowContext};
+use gpui::{actions, ViewContext, WindowContext};
 use language::SelectionGoal;
 use log::error;
 use workspace::Workspace;
@@ -52,38 +52,31 @@ actions!(
     ]
 );
 
-pub fn init(cx: &mut AppContext) {
-    paste::init(cx);
-    repeat::init(cx);
-    scroll::init(cx);
-    search::init(cx);
-    substitute::init(cx);
-    increment::init(cx);
-
-    cx.add_action(insert_after);
-    cx.add_action(insert_before);
-    cx.add_action(insert_first_non_whitespace);
-    cx.add_action(insert_end_of_line);
-    cx.add_action(insert_line_above);
-    cx.add_action(insert_line_below);
-    cx.add_action(change_case);
-    cx.add_action(yank_line);
-
-    cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
+pub(crate) fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+    workspace.register_action(insert_after);
+    workspace.register_action(insert_before);
+    workspace.register_action(insert_first_non_whitespace);
+    workspace.register_action(insert_end_of_line);
+    workspace.register_action(insert_line_above);
+    workspace.register_action(insert_line_below);
+    workspace.register_action(change_case);
+    workspace.register_action(yank_line);
+
+    workspace.register_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
         Vim::update(cx, |vim, cx| {
             vim.record_current_action(cx);
             let times = vim.take_count(cx);
             delete_motion(vim, Motion::Left, times, cx);
         })
     });
-    cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &DeleteRight, cx| {
         Vim::update(cx, |vim, cx| {
             vim.record_current_action(cx);
             let times = vim.take_count(cx);
             delete_motion(vim, Motion::Right, times, cx);
         })
     });
-    cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| {
         Vim::update(cx, |vim, cx| {
             vim.start_recording(cx);
             let times = vim.take_count(cx);
@@ -97,7 +90,7 @@ pub fn init(cx: &mut AppContext) {
             );
         })
     });
-    cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| {
         Vim::update(cx, |vim, cx| {
             vim.record_current_action(cx);
             let times = vim.take_count(cx);
@@ -111,7 +104,7 @@ pub fn init(cx: &mut AppContext) {
             );
         })
     });
-    cx.add_action(|_: &mut Workspace, _: &JoinLines, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &JoinLines, cx| {
         Vim::update(cx, |vim, cx| {
             vim.record_current_action(cx);
             let mut times = vim.take_count(cx).unwrap_or(1);
@@ -129,8 +122,15 @@ pub fn init(cx: &mut AppContext) {
                     }
                 })
             })
-        })
-    })
+        });
+    });
+
+    paste::register(workspace, cx);
+    repeat::register(workspace, cx);
+    scroll::register(workspace, cx);
+    search::register(workspace, cx);
+    substitute::register(workspace, cx);
+    increment::register(workspace, cx);
 }
 
 pub fn normal_motion(

crates/vim/src/normal/increment.rs 🔗

@@ -1,7 +1,7 @@
 use std::ops::Range;
 
 use editor::{scroll::autoscroll::Autoscroll, MultiBufferSnapshot, ToOffset, ToPoint};
-use gpui::{impl_actions, AppContext, WindowContext};
+use gpui::{impl_actions, ViewContext, WindowContext};
 use language::{Bias, Point};
 use serde::Deserialize;
 use workspace::Workspace;
@@ -24,8 +24,8 @@ struct Decrement {
 
 impl_actions!(vim, [Increment, Decrement]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(|_: &mut Workspace, action: &Increment, cx| {
+pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(|_: &mut Workspace, action: &Increment, cx| {
         Vim::update(cx, |vim, cx| {
             vim.record_current_action(cx);
             let count = vim.take_count(cx).unwrap_or(1);
@@ -33,7 +33,7 @@ pub fn init(cx: &mut AppContext) {
             increment(vim, count as i32, step, cx)
         })
     });
-    cx.add_action(|_: &mut Workspace, action: &Decrement, cx| {
+    workspace.register_action(|_: &mut Workspace, action: &Decrement, cx| {
         Vim::update(cx, |vim, cx| {
             vim.record_current_action(cx);
             let count = vim.take_count(cx).unwrap_or(1);

crates/vim/src/normal/paste.rs 🔗

@@ -4,7 +4,7 @@ use editor::{
     display_map::ToDisplayPoint, movement, scroll::autoscroll::Autoscroll, ClipboardSelection,
     DisplayPoint,
 };
-use gpui::{impl_actions, AppContext, ViewContext};
+use gpui::{impl_actions, ViewContext};
 use language::{Bias, SelectionGoal};
 use serde::Deserialize;
 use workspace::Workspace;
@@ -22,8 +22,8 @@ struct Paste {
 
 impl_actions!(vim, [Paste]);
 
-pub(crate) fn init(cx: &mut AppContext) {
-    cx.add_action(paste);
+pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(paste);
 }
 
 fn paste(_: &mut Workspace, action: &Paste, cx: &mut ViewContext<Workspace>) {

crates/vim/src/normal/repeat.rs 🔗

@@ -5,14 +5,14 @@ use crate::{
     visual::visual_motion,
     Vim,
 };
-use gpui::{actions, Action, AppContext, WindowContext};
+use gpui::{actions, Action, ViewContext, WindowContext};
 use workspace::Workspace;
 
-actions!(vim, [Repeat, EndRepeat,]);
+actions!(vim, [Repeat, EndRepeat]);
 
 fn should_replay(action: &Box<dyn Action>) -> bool {
     // skip so that we don't leave the character palette open
-    if editor::ShowCharacterPalette.id() == action.id() {
+    if editor::ShowCharacterPalette.partial_eq(&**action) {
         return false;
     }
     true
@@ -21,14 +21,14 @@ fn should_replay(action: &Box<dyn Action>) -> bool {
 fn repeatable_insert(action: &ReplayableAction) -> Option<Box<dyn Action>> {
     match action {
         ReplayableAction::Action(action) => {
-            if super::InsertBefore.id() == action.id()
-                || super::InsertAfter.id() == action.id()
-                || super::InsertFirstNonWhitespace.id() == action.id()
-                || super::InsertEndOfLine.id() == action.id()
+            if super::InsertBefore.partial_eq(&**action)
+                || super::InsertAfter.partial_eq(&**action)
+                || super::InsertFirstNonWhitespace.partial_eq(&**action)
+                || super::InsertEndOfLine.partial_eq(&**action)
             {
                 Some(super::InsertBefore.boxed_clone())
-            } else if super::InsertLineAbove.id() == action.id()
-                || super::InsertLineBelow.id() == action.id()
+            } else if super::InsertLineAbove.partial_eq(&**action)
+                || super::InsertLineBelow.partial_eq(&**action)
             {
                 Some(super::InsertLineBelow.boxed_clone())
             } else {
@@ -39,15 +39,15 @@ fn repeatable_insert(action: &ReplayableAction) -> Option<Box<dyn Action>> {
     }
 }
 
-pub(crate) fn init(cx: &mut AppContext) {
-    cx.add_action(|_: &mut Workspace, _: &EndRepeat, cx| {
+pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(|_: &mut Workspace, _: &EndRepeat, cx| {
         Vim::update(cx, |vim, cx| {
             vim.workspace_state.replaying = false;
             vim.switch_mode(Mode::Normal, false, cx)
         });
     });
 
-    cx.add_action(|_: &mut Workspace, _: &Repeat, cx| repeat(cx, false));
+    workspace.register_action(|_: &mut Workspace, _: &Repeat, cx| repeat(cx, false));
 }
 
 pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) {
@@ -142,7 +142,7 @@ pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) {
     // 3 times, instead it inserts the content thrice at the insert position.
     if let Some(to_repeat) = repeatable_insert(&actions[0]) {
         if let Some(ReplayableAction::Action(action)) = actions.last() {
-            if action.id() == NormalBefore.id() {
+            if NormalBefore.partial_eq(&**action) {
                 actions.pop();
             }
         }
@@ -166,50 +166,43 @@ pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) {
     }
 
     Vim::update(cx, |vim, _| vim.workspace_state.replaying = true);
-    let window = cx.window();
-    cx.app_context()
-        .spawn(move |mut cx| async move {
-            editor.update(&mut cx, |editor, _| {
-                editor.show_local_selections = false;
-            })?;
-            for action in actions {
-                match action {
-                    ReplayableAction::Action(action) => {
-                        if should_replay(&action) {
-                            window
-                                .dispatch_action(editor.id(), action.as_ref(), &mut cx)
-                                .ok_or_else(|| anyhow::anyhow!("window was closed"))
-                        } else {
-                            Ok(())
-                        }
+    let window = cx.window_handle();
+    cx.spawn(move |mut cx| async move {
+        editor.update(&mut cx, |editor, _| {
+            editor.show_local_selections = false;
+        })?;
+        for action in actions {
+            match action {
+                ReplayableAction::Action(action) => {
+                    if should_replay(&action) {
+                        window.update(&mut cx, |_, cx| cx.dispatch_action(action))
+                    } else {
+                        Ok(())
                     }
-                    ReplayableAction::Insertion {
-                        text,
-                        utf16_range_to_replace,
-                    } => editor.update(&mut cx, |editor, cx| {
-                        editor.replay_insert_event(&text, utf16_range_to_replace.clone(), cx)
-                    }),
-                }?
-            }
-            editor.update(&mut cx, |editor, _| {
-                editor.show_local_selections = true;
-            })?;
-            window
-                .dispatch_action(editor.id(), &EndRepeat, &mut cx)
-                .ok_or_else(|| anyhow::anyhow!("window was closed"))
-        })
-        .detach_and_log_err(cx);
+                }
+                ReplayableAction::Insertion {
+                    text,
+                    utf16_range_to_replace,
+                } => editor.update(&mut cx, |editor, cx| {
+                    editor.replay_insert_event(&text, utf16_range_to_replace.clone(), cx)
+                }),
+            }?
+        }
+        editor.update(&mut cx, |editor, _| {
+            editor.show_local_selections = true;
+        })?;
+        window.update(&mut cx, |_, cx| cx.dispatch_action(EndRepeat.boxed_clone()))
+    })
+    .detach_and_log_err(cx);
 }
 
 #[cfg(test)]
 mod test {
-    use std::sync::Arc;
-
     use editor::test::editor_lsp_test_context::EditorLspTestContext;
     use futures::StreamExt;
     use indoc::indoc;
 
-    use gpui::{executor::Deterministic, View};
+    use gpui::InputHandler;
 
     use crate::{
         state::Mode,
@@ -217,7 +210,7 @@ mod test {
     };
 
     #[gpui::test]
-    async fn test_dot_repeat(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
+    async fn test_dot_repeat(cx: &mut gpui::TestAppContext) {
         let mut cx = NeovimBackedTestContext::new(cx).await;
 
         // "o"
@@ -226,38 +219,32 @@ mod test {
             .await;
         cx.assert_shared_state("hello\nworlˇd").await;
         cx.simulate_shared_keystrokes(["."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state("hello\nworld\nworlˇd").await;
 
         // "d"
         cx.simulate_shared_keystrokes(["^", "d", "f", "o"]).await;
         cx.simulate_shared_keystrokes(["g", "g", "."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state("ˇ\nworld\nrld").await;
 
         // "p" (note that it pastes the current clipboard)
         cx.simulate_shared_keystrokes(["j", "y", "y", "p"]).await;
         cx.simulate_shared_keystrokes(["shift-g", "y", "y", "."])
             .await;
-        deterministic.run_until_parked();
         cx.assert_shared_state("\nworld\nworld\nrld\nˇrld").await;
 
         // "~" (note that counts apply to the action taken, not . itself)
         cx.set_shared_state("ˇthe quick brown fox").await;
         cx.simulate_shared_keystrokes(["2", "~", "."]).await;
-        deterministic.run_until_parked();
         cx.set_shared_state("THE ˇquick brown fox").await;
         cx.simulate_shared_keystrokes(["3", "."]).await;
-        deterministic.run_until_parked();
         cx.set_shared_state("THE QUIˇck brown fox").await;
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.simulate_shared_keystrokes(["."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state("THE QUICK ˇbrown fox").await;
     }
 
     #[gpui::test]
-    async fn test_repeat_ime(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
+    async fn test_repeat_ime(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
 
         cx.set_state("hˇllo", Mode::Normal);
@@ -271,15 +258,12 @@ mod test {
         cx.simulate_keystrokes(["escape"]);
         cx.assert_state("hˇällo", Mode::Normal);
         cx.simulate_keystrokes(["."]);
-        deterministic.run_until_parked();
         cx.assert_state("hˇäällo", Mode::Normal);
     }
 
     #[gpui::test]
-    async fn test_repeat_completion(
-        deterministic: Arc<Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
+    async fn test_repeat_completion(cx: &mut gpui::TestAppContext) {
+        VimTestContext::init(cx);
         let cx = EditorLspTestContext::new_rust(
             lsp::ServerCapabilities {
                 completion_provider: Some(lsp::CompletionOptions {
@@ -340,7 +324,6 @@ mod test {
             Mode::Normal,
         );
         cx.simulate_keystrokes(["j", "."]);
-        deterministic.run_until_parked();
         cx.assert_state(
             indoc! {"
                 one.second!
@@ -352,7 +335,7 @@ mod test {
     }
 
     #[gpui::test]
-    async fn test_repeat_visual(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
+    async fn test_repeat_visual(cx: &mut gpui::TestAppContext) {
         let mut cx = NeovimBackedTestContext::new(cx).await;
 
         // single-line (3 columns)
@@ -371,7 +354,6 @@ mod test {
         })
         .await;
         cx.simulate_shared_keystrokes(["j", "w", "."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state(indoc! {
             "o quick brown
             fox ˇops over
@@ -379,7 +361,6 @@ mod test {
         })
         .await;
         cx.simulate_shared_keystrokes(["f", "r", "."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state(indoc! {
             "o quick brown
             fox ops oveˇothe lazy dog"
@@ -404,7 +385,6 @@ mod test {
         })
         .await;
         cx.simulate_shared_keystrokes(["."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state(indoc! {
             "the ˇumps over
             fox jumps over
@@ -412,14 +392,12 @@ mod test {
         })
         .await;
         cx.simulate_shared_keystrokes(["w", "."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state(indoc! {
             "the umps ˇumps over
             the lazy dog"
         })
         .await;
         cx.simulate_shared_keystrokes(["j", "."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state(indoc! {
             "the umps umps over
             the ˇog"
@@ -442,7 +420,6 @@ mod test {
         })
         .await;
         cx.simulate_shared_keystrokes(["j", "4", "l", "."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state(indoc! {
             "othe quick brown
             ofoxˇo jumps over
@@ -466,7 +443,6 @@ mod test {
         })
         .await;
         cx.simulate_shared_keystrokes(["j", "."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state(indoc! {
             "o
             ˇo
@@ -476,10 +452,7 @@ mod test {
     }
 
     #[gpui::test]
-    async fn test_repeat_motion_counts(
-        deterministic: Arc<Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
+    async fn test_repeat_motion_counts(cx: &mut gpui::TestAppContext) {
         let mut cx = NeovimBackedTestContext::new(cx).await;
 
         cx.set_shared_state(indoc! {
@@ -496,7 +469,6 @@ mod test {
         })
         .await;
         cx.simulate_shared_keystrokes(["j", "."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state(indoc! {
             " brown
             ˇ over
@@ -504,7 +476,6 @@ mod test {
         })
         .await;
         cx.simulate_shared_keystrokes(["j", "2", "."]).await;
-        deterministic.run_until_parked();
         cx.assert_shared_state(indoc! {
             " brown
              over
@@ -514,15 +485,12 @@ mod test {
     }
 
     #[gpui::test]
-    async fn test_record_interrupted(
-        deterministic: Arc<Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
+    async fn test_record_interrupted(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
 
         cx.set_state("ˇhello\n", Mode::Normal);
-        cx.simulate_keystrokes(["4", "i", "j", "cmd-shift-p", "escape", "escape"]);
-        deterministic.run_until_parked();
+        cx.simulate_keystrokes(["4", "i", "j", "cmd-shift-p", "escape"]);
+        cx.simulate_keystrokes(["escape"]);
         cx.assert_state("ˇjhello\n", Mode::Normal);
     }
 }

crates/vim/src/normal/scroll.rs 🔗

@@ -4,29 +4,29 @@ use editor::{
     scroll::{scroll_amount::ScrollAmount, VERTICAL_SCROLL_MARGIN},
     DisplayPoint, Editor,
 };
-use gpui::{actions, AppContext, ViewContext};
+use gpui::{actions, ViewContext};
 use language::Bias;
 use workspace::Workspace;
 
 actions!(
     vim,
-    [LineUp, LineDown, ScrollUp, ScrollDown, PageUp, PageDown,]
+    [LineUp, LineDown, ScrollUp, ScrollDown, PageUp, PageDown]
 );
 
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(|_: &mut Workspace, _: &LineDown, cx| {
+pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(|_: &mut Workspace, _: &LineDown, cx| {
         scroll(cx, false, |c| ScrollAmount::Line(c.unwrap_or(1.)))
     });
-    cx.add_action(|_: &mut Workspace, _: &LineUp, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &LineUp, cx| {
         scroll(cx, false, |c| ScrollAmount::Line(-c.unwrap_or(1.)))
     });
-    cx.add_action(|_: &mut Workspace, _: &PageDown, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &PageDown, cx| {
         scroll(cx, false, |c| ScrollAmount::Page(c.unwrap_or(1.)))
     });
-    cx.add_action(|_: &mut Workspace, _: &PageUp, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &PageUp, cx| {
         scroll(cx, false, |c| ScrollAmount::Page(-c.unwrap_or(1.)))
     });
-    cx.add_action(|_: &mut Workspace, _: &ScrollDown, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &ScrollDown, cx| {
         scroll(cx, true, |c| {
             if let Some(c) = c {
                 ScrollAmount::Line(c)
@@ -35,7 +35,7 @@ pub fn init(cx: &mut AppContext) {
             }
         })
     });
-    cx.add_action(|_: &mut Workspace, _: &ScrollUp, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &ScrollUp, cx| {
         scroll(cx, true, |c| {
             if let Some(c) = c {
                 ScrollAmount::Line(-c)
@@ -114,7 +114,7 @@ mod test {
         state::Mode,
         test::{NeovimBackedTestContext, VimTestContext},
     };
-    use gpui::geometry::vector::vec2f;
+    use gpui::{point, px, size, Context};
     use indoc::indoc;
     use language::Point;
 
@@ -122,10 +122,27 @@ mod test {
     async fn test_scroll(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
 
+        let (line_height, visible_line_count) = cx.editor(|editor, cx| {
+            (
+                editor
+                    .style()
+                    .unwrap()
+                    .text
+                    .line_height_in_pixels(cx.rem_size()),
+                editor.visible_line_count().unwrap(),
+            )
+        });
+
         let window = cx.window;
-        let line_height =
-            cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
-        window.simulate_resize(vec2f(1000., 8.0 * line_height - 1.0), &mut cx);
+        let margin = cx
+            .update_window(window, |_, cx| {
+                cx.viewport_size().height - line_height * visible_line_count
+            })
+            .unwrap();
+        cx.simulate_window_resize(
+            cx.window,
+            size(px(1000.), margin + 8. * line_height - px(1.0)),
+        );
 
         cx.set_state(
             indoc!(
@@ -147,29 +164,29 @@ mod test {
         );
 
         cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.))
+            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
         });
         cx.simulate_keystrokes(["ctrl-e"]);
         cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.))
+            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 1.))
         });
         cx.simulate_keystrokes(["2", "ctrl-e"]);
         cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.))
+            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.))
         });
         cx.simulate_keystrokes(["ctrl-y"]);
         cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 2.))
+            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 2.))
         });
 
         // does not select in normal mode
         cx.simulate_keystrokes(["g", "g"]);
         cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.))
+            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
         });
         cx.simulate_keystrokes(["ctrl-d"]);
         cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0));
+            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0));
             assert_eq!(
                 editor.selections.newest(cx).range(),
                 Point::new(6, 0)..Point::new(6, 0)
@@ -179,11 +196,11 @@ mod test {
         // does select in visual mode
         cx.simulate_keystrokes(["g", "g"]);
         cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.))
+            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
         });
         cx.simulate_keystrokes(["v", "ctrl-d"]);
         cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0));
+            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0));
             assert_eq!(
                 editor.selections.newest(cx).range(),
                 Point::new(0, 0)..Point::new(6, 1)

crates/vim/src/normal/search.rs 🔗

@@ -1,7 +1,7 @@
-use gpui::{actions, impl_actions, AppContext, ViewContext};
+use gpui::{actions, impl_actions, ViewContext};
 use search::{buffer_search, BufferSearchBar, SearchMode, SearchOptions};
 use serde_derive::Deserialize;
-use workspace::{searchable::Direction, Pane, Workspace};
+use workspace::{searchable::Direction, Workspace};
 
 use crate::{motion::Motion, normal::move_cursor, state::SearchState, Vim};
 
@@ -44,21 +44,21 @@ struct Replacement {
     is_case_sensitive: bool,
 }
 
+actions!(vim, [SearchSubmit]);
 impl_actions!(
     vim,
-    [MoveToNext, MoveToPrev, Search, FindCommand, ReplaceCommand]
+    [FindCommand, ReplaceCommand, Search, MoveToPrev, MoveToNext]
 );
-actions!(vim, [SearchSubmit]);
 
-pub(crate) fn init(cx: &mut AppContext) {
-    cx.add_action(move_to_next);
-    cx.add_action(move_to_prev);
-    cx.add_action(search);
-    cx.add_action(search_submit);
-    cx.add_action(search_deploy);
+pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(move_to_next);
+    workspace.register_action(move_to_prev);
+    workspace.register_action(search);
+    workspace.register_action(search_submit);
+    workspace.register_action(search_deploy);
 
-    cx.add_action(find_command);
-    cx.add_action(replace_command);
+    workspace.register_action(find_command);
+    workspace.register_action(replace_command);
 }
 
 fn move_to_next(workspace: &mut Workspace, action: &MoveToNext, cx: &mut ViewContext<Workspace>) {
@@ -106,9 +106,9 @@ fn search(workspace: &mut Workspace, action: &Search, cx: &mut ViewContext<Works
 }
 
 // hook into the existing to clear out any vim search state on cmd+f or edit -> find.
-fn search_deploy(_: &mut Pane, _: &buffer_search::Deploy, cx: &mut ViewContext<Pane>) {
+fn search_deploy(_: &mut Workspace, _: &buffer_search::Deploy, cx: &mut ViewContext<Workspace>) {
     Vim::update(cx, |vim, _| vim.workspace_state.search = Default::default());
-    cx.propagate_action();
+    cx.propagate();
 }
 
 fn search_submit(workspace: &mut Workspace, _: &SearchSubmit, cx: &mut ViewContext<Workspace>) {
@@ -347,58 +347,50 @@ fn parse_replace_all(query: &str) -> Replacement {
 
 #[cfg(test)]
 mod test {
-    use std::sync::Arc;
-
     use editor::DisplayPoint;
     use search::BufferSearchBar;
 
     use crate::{state::Mode, test::VimTestContext};
 
     #[gpui::test]
-    async fn test_move_to_next(
-        cx: &mut gpui::TestAppContext,
-        deterministic: Arc<gpui::executor::Deterministic>,
-    ) {
+    async fn test_move_to_next(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
         cx.set_state("ˇhi\nhigh\nhi\n", Mode::Normal);
 
         cx.simulate_keystrokes(["*"]);
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
 
         cx.simulate_keystrokes(["*"]);
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
 
         cx.simulate_keystrokes(["#"]);
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
 
         cx.simulate_keystrokes(["#"]);
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
 
         cx.simulate_keystrokes(["2", "*"]);
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
 
         cx.simulate_keystrokes(["g", "*"]);
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
 
         cx.simulate_keystrokes(["n"]);
         cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
 
         cx.simulate_keystrokes(["g", "#"]);
-        deterministic.run_until_parked();
+        cx.run_until_parked();
         cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
     }
 
     #[gpui::test]
-    async fn test_search(
-        cx: &mut gpui::TestAppContext,
-        deterministic: Arc<gpui::executor::Deterministic>,
-    ) {
+    async fn test_search(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, true).await;
 
         cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
@@ -414,11 +406,11 @@ mod test {
                 .expect("Buffer search bar should be deployed")
         });
 
-        search_bar.read_with(cx.cx, |bar, cx| {
+        cx.update_view(search_bar, |bar, cx| {
             assert_eq!(bar.query(cx), "cc");
         });
 
-        deterministic.run_until_parked();
+        cx.run_until_parked();
 
         cx.update_editor(|editor, cx| {
             let highlights = editor.all_text_background_highlights(cx);
@@ -440,51 +432,41 @@ mod test {
 
         // ?<enter> to go to previous
         cx.simulate_keystrokes(["?", "enter"]);
-        deterministic.run_until_parked();
         cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
         cx.simulate_keystrokes(["?", "enter"]);
-        deterministic.run_until_parked();
         cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
 
         // /<enter> to go to next
         cx.simulate_keystrokes(["/", "enter"]);
-        deterministic.run_until_parked();
         cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
 
         // ?{search}<enter> to search backwards
         cx.simulate_keystrokes(["?", "b", "enter"]);
-        deterministic.run_until_parked();
         cx.assert_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
 
         // works with counts
         cx.simulate_keystrokes(["4", "/", "c"]);
-        deterministic.run_until_parked();
         cx.simulate_keystrokes(["enter"]);
         cx.assert_state("aa\nbb\ncc\ncˇc\ncc\n", Mode::Normal);
 
         // check that searching resumes from cursor, not previous match
         cx.set_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
         cx.simulate_keystrokes(["/", "d"]);
-        deterministic.run_until_parked();
         cx.simulate_keystrokes(["enter"]);
         cx.assert_state("aa\nbb\nˇdd\ncc\nbb\n", Mode::Normal);
         cx.update_editor(|editor, cx| editor.move_to_beginning(&Default::default(), cx));
         cx.assert_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
         cx.simulate_keystrokes(["/", "b"]);
-        deterministic.run_until_parked();
         cx.simulate_keystrokes(["enter"]);
         cx.assert_state("aa\nˇbb\ndd\ncc\nbb\n", Mode::Normal);
     }
 
     #[gpui::test]
-    async fn test_non_vim_search(
-        cx: &mut gpui::TestAppContext,
-        deterministic: Arc<gpui::executor::Deterministic>,
-    ) {
+    async fn test_non_vim_search(cx: &mut gpui::TestAppContext) {
         let mut cx = VimTestContext::new(cx, false).await;
         cx.set_state("ˇone one one one", Mode::Normal);
         cx.simulate_keystrokes(["cmd-f"]);
-        deterministic.run_until_parked();
+        cx.run_until_parked();
 
         cx.assert_editor_state("«oneˇ» one one one");
         cx.simulate_keystrokes(["enter"]);

crates/vim/src/normal/substitute.rs 🔗

@@ -1,5 +1,5 @@
 use editor::movement;
-use gpui::{actions, AppContext, WindowContext};
+use gpui::{actions, ViewContext, WindowContext};
 use language::Point;
 use workspace::Workspace;
 
@@ -7,8 +7,8 @@ use crate::{motion::Motion, utils::copy_selections_content, Mode, Vim};
 
 actions!(vim, [Substitute, SubstituteLine]);
 
-pub(crate) fn init(cx: &mut AppContext) {
-    cx.add_action(|_: &mut Workspace, _: &Substitute, cx| {
+pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(|_: &mut Workspace, _: &Substitute, cx| {
         Vim::update(cx, |vim, cx| {
             vim.start_recording(cx);
             let count = vim.take_count(cx);
@@ -16,7 +16,7 @@ pub(crate) fn init(cx: &mut AppContext) {
         })
     });
 
-    cx.add_action(|_: &mut Workspace, _: &SubstituteLine, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &SubstituteLine, cx| {
         Vim::update(cx, |vim, cx| {
             vim.start_recording(cx);
             if matches!(vim.state().mode, Mode::VisualBlock | Mode::Visual) {

crates/vim/src/object.rs 🔗

@@ -6,7 +6,7 @@ use editor::{
     movement::{self, FindRange},
     Bias, CharKind, DisplayPoint,
 };
-use gpui::{actions, impl_actions, AppContext, WindowContext};
+use gpui::{actions, impl_actions, ViewContext, WindowContext};
 use language::Selection;
 use serde::Deserialize;
 use workspace::Workspace;
@@ -34,6 +34,8 @@ struct Word {
     ignore_punctuation: bool,
 }
 
+impl_actions!(vim, [Word]);
+
 actions!(
     vim,
     [
@@ -48,25 +50,36 @@ actions!(
         AngleBrackets
     ]
 );
-impl_actions!(vim, [Word]);
 
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(
+pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(
         |_: &mut Workspace, &Word { ignore_punctuation }: &Word, cx: _| {
             object(Object::Word { ignore_punctuation }, cx)
         },
     );
-    cx.add_action(|_: &mut Workspace, _: &Sentence, cx: _| object(Object::Sentence, cx));
-    cx.add_action(|_: &mut Workspace, _: &Quotes, cx: _| object(Object::Quotes, cx));
-    cx.add_action(|_: &mut Workspace, _: &BackQuotes, cx: _| object(Object::BackQuotes, cx));
-    cx.add_action(|_: &mut Workspace, _: &DoubleQuotes, cx: _| object(Object::DoubleQuotes, cx));
-    cx.add_action(|_: &mut Workspace, _: &Parentheses, cx: _| object(Object::Parentheses, cx));
-    cx.add_action(|_: &mut Workspace, _: &SquareBrackets, cx: _| {
+    workspace
+        .register_action(|_: &mut Workspace, _: &Sentence, cx: _| object(Object::Sentence, cx));
+    workspace.register_action(|_: &mut Workspace, _: &Quotes, cx: _| object(Object::Quotes, cx));
+    workspace
+        .register_action(|_: &mut Workspace, _: &BackQuotes, cx: _| object(Object::BackQuotes, cx));
+    workspace.register_action(|_: &mut Workspace, _: &DoubleQuotes, cx: _| {
+        object(Object::DoubleQuotes, cx)
+    });
+    workspace.register_action(|_: &mut Workspace, _: &Parentheses, cx: _| {
+        object(Object::Parentheses, cx)
+    });
+    workspace.register_action(|_: &mut Workspace, _: &SquareBrackets, cx: _| {
         object(Object::SquareBrackets, cx)
     });
-    cx.add_action(|_: &mut Workspace, _: &CurlyBrackets, cx: _| object(Object::CurlyBrackets, cx));
-    cx.add_action(|_: &mut Workspace, _: &AngleBrackets, cx: _| object(Object::AngleBrackets, cx));
-    cx.add_action(|_: &mut Workspace, _: &VerticalBars, cx: _| object(Object::VerticalBars, cx));
+    workspace.register_action(|_: &mut Workspace, _: &CurlyBrackets, cx: _| {
+        object(Object::CurlyBrackets, cx)
+    });
+    workspace.register_action(|_: &mut Workspace, _: &AngleBrackets, cx: _| {
+        object(Object::AngleBrackets, cx)
+    });
+    workspace.register_action(|_: &mut Workspace, _: &VerticalBars, cx: _| {
+        object(Object::VerticalBars, cx)
+    });
 }
 
 fn object(object: Object, cx: &mut WindowContext) {

crates/vim/src/state.rs 🔗

@@ -1,6 +1,6 @@
 use std::{ops::Range, sync::Arc};
 
-use gpui::{keymap_matcher::KeymapContext, Action};
+use gpui::{Action, KeyContext};
 use language::CursorShape;
 use serde::{Deserialize, Serialize};
 use workspace::searchable::Direction;
@@ -167,10 +167,10 @@ impl EditorState {
         self.operator_stack.last().copied()
     }
 
-    pub fn keymap_context_layer(&self) -> KeymapContext {
-        let mut context = KeymapContext::default();
-        context.add_identifier("VimEnabled");
-        context.add_key(
+    pub fn keymap_context_layer(&self) -> KeyContext {
+        let mut context = KeyContext::default();
+        context.add("VimEnabled");
+        context.set(
             "vim_mode",
             match self.mode {
                 Mode::Normal => "normal",
@@ -180,24 +180,24 @@ impl EditorState {
         );
 
         if self.vim_controlled() {
-            context.add_identifier("VimControl");
+            context.add("VimControl");
         }
 
         if self.active_operator().is_none() && self.pre_count.is_some()
             || self.active_operator().is_some() && self.post_count.is_some()
         {
-            context.add_identifier("VimCount");
+            context.add("VimCount");
         }
 
         let active_operator = self.active_operator();
 
         if let Some(active_operator) = active_operator {
             for context_flag in active_operator.context_flags().into_iter() {
-                context.add_identifier(*context_flag);
+                context.add(*context_flag);
             }
         }
 
-        context.add_key(
+        context.set(
             "vim_operator",
             active_operator.map(|op| op.id()).unwrap_or_else(|| "none"),
         );

crates/vim/src/test.rs 🔗

@@ -3,8 +3,6 @@ mod neovim_backed_test_context;
 mod neovim_connection;
 mod vim_test_context;
 
-use std::sync::Arc;
-
 use command_palette::CommandPalette;
 use editor::DisplayPoint;
 pub use neovim_backed_binding_test_context::*;
@@ -96,7 +94,7 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
             .expect("Buffer search bar should be deployed")
     });
 
-    search_bar.read_with(cx.cx, |bar, cx| {
+    cx.update_view(search_bar, |bar, cx| {
         assert_eq!(bar.query(cx), "");
     })
 }
@@ -149,9 +147,10 @@ async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
     cx.set_state("aˇbc\n", Mode::Normal);
     cx.simulate_keystrokes(["i", "cmd-shift-p"]);
 
-    assert!(cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
+    assert!(cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
     cx.simulate_keystroke("escape");
-    assert!(!cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
+    cx.run_until_parked();
+    assert!(!cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
     cx.assert_state("aˇbc\n", Mode::Insert);
 }
 
@@ -182,7 +181,7 @@ async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
             .expect("Buffer search bar should be deployed")
     });
 
-    search_bar.read_with(cx.cx, |bar, cx| {
+    cx.update_view(search_bar, |bar, cx| {
         assert_eq!(bar.query(cx), "cc");
     });
 
@@ -204,12 +203,8 @@ async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-async fn test_status_indicator(
-    cx: &mut gpui::TestAppContext,
-    deterministic: Arc<gpui::executor::Deterministic>,
-) {
+async fn test_status_indicator(cx: &mut gpui::TestAppContext) {
     let mut cx = VimTestContext::new(cx, true).await;
-    deterministic.run_until_parked();
 
     let mode_indicator = cx.workspace(|workspace, cx| {
         let status_bar = workspace.status_bar().read(cx);
@@ -225,7 +220,6 @@ async fn test_status_indicator(
 
     // shows the correct mode
     cx.simulate_keystrokes(["i"]);
-    deterministic.run_until_parked();
     assert_eq!(
         cx.workspace(|_, cx| mode_indicator.read(cx).mode),
         Some(Mode::Insert)
@@ -233,7 +227,6 @@ async fn test_status_indicator(
 
     // shows even in search
     cx.simulate_keystrokes(["escape", "v", "/"]);
-    deterministic.run_until_parked();
     assert_eq!(
         cx.workspace(|_, cx| mode_indicator.read(cx).mode),
         Some(Mode::Visual)
@@ -241,7 +234,7 @@ async fn test_status_indicator(
 
     // hides if vim mode is disabled
     cx.disable_vim();
-    deterministic.run_until_parked();
+    cx.run_until_parked();
     cx.workspace(|workspace, cx| {
         let status_bar = workspace.status_bar().read(cx);
         let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
@@ -249,7 +242,7 @@ async fn test_status_indicator(
     });
 
     cx.enable_vim();
-    deterministic.run_until_parked();
+    cx.run_until_parked();
     cx.workspace(|workspace, cx| {
         let status_bar = workspace.status_bar().read(cx);
         let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();

crates/vim/src/test/neovim_backed_test_context.rs 🔗

@@ -1,4 +1,5 @@
-use editor::scroll::VERTICAL_SCROLL_MARGIN;
+use editor::{scroll::VERTICAL_SCROLL_MARGIN, test::editor_test_context::ContextHandle};
+use gpui::{px, size, Context};
 use indoc::indoc;
 use settings::SettingsStore;
 use std::{
@@ -7,7 +8,6 @@ use std::{
 };
 
 use collections::{HashMap, HashSet};
-use gpui::{geometry::vector::vec2f, ContextHandle};
 use language::language_settings::{AllLanguageSettings, SoftWrap};
 use util::test::marked_text_offsets;
 
@@ -158,11 +158,28 @@ impl<'a> NeovimBackedTestContext<'a> {
             .await;
         // +2 to account for the vim command UI at the bottom.
         self.neovim.set_option(&format!("lines={}", rows + 2)).await;
+        let (line_height, visible_line_count) = self.editor(|editor, cx| {
+            (
+                editor
+                    .style()
+                    .unwrap()
+                    .text
+                    .line_height_in_pixels(cx.rem_size()),
+                editor.visible_line_count().unwrap(),
+            )
+        });
+
         let window = self.window;
-        let line_height =
-            self.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
+        let margin = self
+            .update_window(window, |_, cx| {
+                cx.viewport_size().height - line_height * visible_line_count
+            })
+            .unwrap();
 
-        window.simulate_resize(vec2f(1000., (rows as f32) * line_height), &mut self.cx);
+        self.simulate_window_resize(
+            self.window,
+            size(px(1000.), margin + (rows as f32) * line_height),
+        );
     }
 
     pub async fn set_neovim_option(&mut self, option: &str) {
@@ -211,12 +228,7 @@ impl<'a> NeovimBackedTestContext<'a> {
 
     pub async fn assert_shared_clipboard(&mut self, text: &str) {
         let neovim = self.neovim.read_register('"').await;
-        let editor = self
-            .platform()
-            .read_from_clipboard()
-            .unwrap()
-            .text()
-            .clone();
+        let editor = self.read_from_clipboard().unwrap().text().clone();
 
         if text == neovim && text == editor {
             return;

crates/vim/src/test/neovim_connection.rs 🔗

@@ -10,7 +10,7 @@ use async_compat::Compat;
 #[cfg(feature = "neovim")]
 use async_trait::async_trait;
 #[cfg(feature = "neovim")]
-use gpui::keymap_matcher::Keystroke;
+use gpui::Keystroke;
 
 #[cfg(feature = "neovim")]
 use language::Point;
@@ -116,16 +116,24 @@ impl NeovimConnection {
             keystroke.key = "lt".to_string()
         }
 
-        let special = keystroke.shift
-            || keystroke.ctrl
-            || keystroke.alt
-            || keystroke.cmd
+        let special = keystroke.modifiers.shift
+            || keystroke.modifiers.control
+            || keystroke.modifiers.alt
+            || keystroke.modifiers.command
             || keystroke.key.len() > 1;
         let start = if special { "<" } else { "" };
-        let shift = if keystroke.shift { "S-" } else { "" };
-        let ctrl = if keystroke.ctrl { "C-" } else { "" };
-        let alt = if keystroke.alt { "M-" } else { "" };
-        let cmd = if keystroke.cmd { "D-" } else { "" };
+        let shift = if keystroke.modifiers.shift { "S-" } else { "" };
+        let ctrl = if keystroke.modifiers.control {
+            "C-"
+        } else {
+            ""
+        };
+        let alt = if keystroke.modifiers.alt { "M-" } else { "" };
+        let cmd = if keystroke.modifiers.command {
+            "D-"
+        } else {
+            ""
+        };
         let end = if special { ">" } else { "" };
 
         let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key);

crates/vim/src/test/vim_test_context.rs 🔗

@@ -4,9 +4,9 @@ use editor::test::{
     editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
 };
 use futures::Future;
-use gpui::ContextHandle;
+use gpui::{Context, View, VisualContext};
 use lsp::request;
-use search::{BufferSearchBar, ProjectSearchBar};
+use search::BufferSearchBar;
 
 use crate::{state::Operator, *};
 
@@ -15,12 +15,28 @@ pub struct VimTestContext<'a> {
 }
 
 impl<'a> VimTestContext<'a> {
+    pub fn init(cx: &mut gpui::TestAppContext) {
+        if cx.has_global::<Vim>() {
+            dbg!("OOPS");
+            return;
+        }
+        cx.update(|cx| {
+            search::init(cx);
+            let settings = SettingsStore::test(cx);
+            cx.set_global(settings);
+            command_palette::init(cx);
+            crate::init(cx);
+        });
+    }
+
     pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> {
+        Self::init(cx);
         let lsp = EditorLspTestContext::new_rust(Default::default(), cx).await;
         Self::new_with_lsp(lsp, enabled)
     }
 
     pub async fn new_typescript(cx: &'a mut gpui::TestAppContext) -> VimTestContext<'a> {
+        Self::init(cx);
         Self::new_with_lsp(
             EditorLspTestContext::new_typescript(Default::default(), cx).await,
             true,
@@ -28,12 +44,6 @@ impl<'a> VimTestContext<'a> {
     }
 
     pub fn new_with_lsp(mut cx: EditorLspTestContext<'a>, enabled: bool) -> VimTestContext<'a> {
-        cx.update(|cx| {
-            search::init(cx);
-            crate::init(cx);
-            command_palette::init(cx);
-        });
-
         cx.update(|cx| {
             cx.update_global(|store: &mut SettingsStore, cx| {
                 store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
@@ -47,14 +57,15 @@ impl<'a> VimTestContext<'a> {
             observe_keystrokes(cx);
             workspace.active_pane().update(cx, |pane, cx| {
                 pane.toolbar().update(cx, |toolbar, cx| {
-                    let buffer_search_bar = cx.add_view(BufferSearchBar::new);
+                    let buffer_search_bar = cx.new_view(BufferSearchBar::new);
                     toolbar.add_item(buffer_search_bar, cx);
-                    let project_search_bar = cx.add_view(|_| ProjectSearchBar::new());
-                    toolbar.add_item(project_search_bar, cx);
+                    // todo!();
+                    // let project_search_bar = cx.add_view(|_| ProjectSearchBar::new());
+                    // toolbar.add_item(project_search_bar, cx);
                 })
             });
             workspace.status_bar().update(cx, |status_bar, cx| {
-                let vim_mode_indicator = cx.add_view(ModeIndicator::new);
+                let vim_mode_indicator = cx.new_view(ModeIndicator::new);
                 status_bar.add_right_item(vim_mode_indicator, cx);
             });
         });
@@ -62,11 +73,21 @@ impl<'a> VimTestContext<'a> {
         Self { cx }
     }
 
-    pub fn workspace<F, T>(&mut self, read: F) -> T
+    pub fn update_view<F, T, R>(&mut self, view: View<T>, update: F) -> R
     where
-        F: FnOnce(&Workspace, &ViewContext<Workspace>) -> T,
+        T: 'static,
+        F: FnOnce(&mut T, &mut ViewContext<T>) -> R + 'static,
     {
-        self.cx.workspace.read_with(self.cx.cx.cx, read)
+        let window = self.window.clone();
+        self.update_window(window, move |_, cx| view.update(cx, update))
+            .unwrap()
+    }
+
+    pub fn workspace<F, T>(&mut self, update: F) -> T
+    where
+        F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
+    {
+        self.cx.update_workspace(update)
     }
 
     pub fn enable_vim(&mut self) {
@@ -94,16 +115,16 @@ impl<'a> VimTestContext<'a> {
             .read(|cx| cx.global::<Vim>().state().operator_stack.last().copied())
     }
 
-    pub fn set_state(&mut self, text: &str, mode: Mode) -> ContextHandle {
+    pub fn set_state(&mut self, text: &str, mode: Mode) {
         let window = self.window;
-        let context_handle = self.cx.set_state(text);
-        window.update(self.cx.cx.cx, |cx| {
+        self.cx.set_state(text);
+        self.update_window(window, |_, cx| {
             Vim::update(cx, |vim, cx| {
                 vim.switch_mode(mode, true, cx);
             })
-        });
-        self.cx.foreground().run_until_parked();
-        context_handle
+        })
+        .unwrap();
+        self.cx.cx.cx.run_until_parked();
     }
 
     #[track_caller]

crates/vim/src/vim.rs 🔗

@@ -1,5 +1,3 @@
-#![allow(unused)]
-
 #[cfg(test)]
 mod test;
 
@@ -17,17 +15,17 @@ mod visual;
 use anyhow::Result;
 use collections::{CommandPaletteFilter, HashMap};
 use command_palette::CommandPaletteInterceptor;
-use editor::{movement, Editor, EditorMode, Event};
+use editor::{movement, Editor, EditorEvent, EditorMode};
 use gpui::{
-    actions, impl_actions, keymap_matcher::KeymapContext, keymap_matcher::MatchResult, Action,
-    AppContext, Subscription, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
+    actions, impl_actions, Action, AppContext, EntityId, KeyContext, Subscription, View,
+    ViewContext, WeakView, WindowContext,
 };
 use language::{CursorShape, Point, Selection, SelectionGoal};
 pub use mode_indicator::ModeIndicator;
 use motion::Motion;
 use normal::normal_replace;
 use serde::Deserialize;
-use settings::{update_settings_file, Setting, SettingsStore};
+use settings::{update_settings_file, Settings, SettingsStore};
 use state::{EditorState, Mode, Operator, RecordedSelection, WorkspaceState};
 use std::{ops::Range, sync::Arc};
 use visual::{visual_block_motion, visual_replace};
@@ -50,83 +48,86 @@ actions!(
     vim,
     [Tab, Enter, Object, InnerObject, FindForward, FindBackward]
 );
+// in the workspace namespace so it's not filtered out when vim is disabled.
 actions!(workspace, [ToggleVimMode]);
-impl_actions!(vim, [Number, SwitchMode, PushOperator]);
 
-#[derive(Copy, Clone, Debug)]
-enum VimEvent {
-    ModeChanged { mode: Mode },
-}
+impl_actions!(vim, [SwitchMode, PushOperator, Number]);
 
 pub fn init(cx: &mut AppContext) {
     cx.set_global(Vim::default());
-    settings::register::<VimModeSetting>(cx);
+    VimModeSetting::register(cx);
 
     editor_events::init(cx);
-    normal::init(cx);
-    visual::init(cx);
-    insert::init(cx);
-    object::init(cx);
-    motion::init(cx);
-    command::init(cx);
-
-    // Vim Actions
-    cx.add_action(|_: &mut Workspace, &SwitchMode(mode): &SwitchMode, cx| {
+
+    cx.observe_new_views(|workspace: &mut Workspace, cx| register(workspace, cx))
+        .detach();
+
+    // Any time settings change, update vim mode to match. The Vim struct
+    // will be initialized as disabled by default, so we filter its commands
+    // out when starting up.
+    cx.update_global::<CommandPaletteFilter, _>(|filter, _| {
+        filter.hidden_namespaces.insert("vim");
+    });
+    cx.update_global(|vim: &mut Vim, cx: &mut AppContext| {
+        vim.set_enabled(VimModeSetting::get_global(cx).0, cx)
+    });
+    cx.observe_global::<SettingsStore>(|cx| {
+        cx.update_global(|vim: &mut Vim, cx: &mut AppContext| {
+            vim.set_enabled(VimModeSetting::get_global(cx).0, cx)
+        });
+    })
+    .detach();
+}
+
+fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
+    workspace.register_action(|_: &mut Workspace, &SwitchMode(mode): &SwitchMode, cx| {
         Vim::update(cx, |vim, cx| vim.switch_mode(mode, false, cx))
     });
-    cx.add_action(
+    workspace.register_action(
         |_: &mut Workspace, &PushOperator(operator): &PushOperator, cx| {
             Vim::update(cx, |vim, cx| vim.push_operator(operator, cx))
         },
     );
-    cx.add_action(|_: &mut Workspace, n: &Number, cx: _| {
+    workspace.register_action(|_: &mut Workspace, n: &Number, cx: _| {
         Vim::update(cx, |vim, cx| vim.push_count_digit(n.0, cx));
     });
 
-    cx.add_action(|_: &mut Workspace, _: &Tab, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &Tab, cx| {
         Vim::active_editor_input_ignored(" ".into(), cx)
     });
 
-    cx.add_action(|_: &mut Workspace, _: &Enter, cx| {
+    workspace.register_action(|_: &mut Workspace, _: &Enter, cx| {
         Vim::active_editor_input_ignored("\n".into(), cx)
     });
 
-    cx.add_action(|workspace: &mut Workspace, _: &ToggleVimMode, cx| {
+    workspace.register_action(|workspace: &mut Workspace, _: &ToggleVimMode, cx| {
         let fs = workspace.app_state().fs.clone();
-        let currently_enabled = settings::get::<VimModeSetting>(cx).0;
+        let currently_enabled = VimModeSetting::get_global(cx).0;
         update_settings_file::<VimModeSetting>(fs, cx, move |setting| {
             *setting = Some(!currently_enabled)
         })
     });
 
-    // Any time settings change, update vim mode to match. The Vim struct
-    // will be initialized as disabled by default, so we filter its commands
-    // out when starting up.
-    cx.update_default_global::<CommandPaletteFilter, _, _>(|filter, _| {
-        filter.hidden_namespaces.insert("vim");
-    });
-    cx.update_global(|vim: &mut Vim, cx: &mut AppContext| {
-        vim.set_enabled(settings::get::<VimModeSetting>(cx).0, cx)
-    });
-    cx.observe_global::<SettingsStore, _>(|cx| {
-        cx.update_global(|vim: &mut Vim, cx: &mut AppContext| {
-            vim.set_enabled(settings::get::<VimModeSetting>(cx).0, cx)
-        });
-    })
-    .detach();
+    normal::register(workspace, cx);
+    insert::register(workspace, cx);
+    motion::register(workspace, cx);
+    command::register(workspace, cx);
+    object::register(workspace, cx);
+    visual::register(workspace, cx);
 }
 
 pub fn observe_keystrokes(cx: &mut WindowContext) {
-    cx.observe_keystrokes(|_keystroke, result, handled_by, cx| {
-        if result == &MatchResult::Pending {
-            return true;
-        }
-        if let Some(handled_by) = handled_by {
+    cx.observe_keystrokes(|keystroke_event, cx| {
+        if let Some(action) = keystroke_event
+            .action
+            .as_ref()
+            .map(|action| action.boxed_clone())
+        {
             Vim::update(cx, |vim, _| {
                 if vim.workspace_state.recording {
                     vim.workspace_state
                         .recorded_actions
-                        .push(ReplayableAction::Action(handled_by.boxed_clone()));
+                        .push(ReplayableAction::Action(action.boxed_clone()));
 
                     if vim.workspace_state.stop_recording_after_next_action {
                         vim.workspace_state.recording = false;
@@ -136,9 +137,11 @@ pub fn observe_keystrokes(cx: &mut WindowContext) {
             });
 
             // Keystroke is handled by the vim system, so continue forward
-            if handled_by.namespace() == "vim" {
-                return true;
+            if action.name().starts_with("vim::") {
+                return;
             }
+        } else if cx.has_pending_keystrokes() {
+            return;
         }
 
         Vim::update(cx, |vim, cx| match vim.active_operator() {
@@ -150,24 +153,23 @@ pub fn observe_keystrokes(cx: &mut WindowContext) {
             }
             _ => {}
         });
-        true
     })
     .detach()
 }
 
 #[derive(Default)]
 pub struct Vim {
-    active_editor: Option<WeakViewHandle<Editor>>,
+    active_editor: Option<WeakView<Editor>>,
     editor_subscription: Option<Subscription>,
     enabled: bool,
-    editor_states: HashMap<usize, EditorState>,
+    editor_states: HashMap<EntityId, EditorState>,
     workspace_state: WorkspaceState,
     default_state: EditorState,
 }
 
 impl Vim {
     fn read(cx: &mut AppContext) -> &Self {
-        cx.default_global()
+        cx.global::<Self>()
     }
 
     fn update<F, S>(cx: &mut WindowContext, update: F) -> S
@@ -177,21 +179,21 @@ impl Vim {
         cx.update_global(update)
     }
 
-    fn set_active_editor(&mut self, editor: ViewHandle<Editor>, cx: &mut WindowContext) {
+    fn set_active_editor(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
         self.active_editor = Some(editor.clone().downgrade());
         self.editor_subscription = Some(cx.subscribe(&editor, |editor, event, cx| match event {
-            Event::SelectionsChanged { local: true } => {
+            EditorEvent::SelectionsChanged { local: true } => {
                 let editor = editor.read(cx);
                 if editor.leader_peer_id().is_none() {
                     let newest = editor.selections.newest::<usize>(cx);
                     local_selections_changed(newest, cx);
                 }
             }
-            Event::InputIgnored { text } => {
+            EditorEvent::InputIgnored { text } => {
                 Vim::active_editor_input_ignored(text.clone(), cx);
                 Vim::record_insertion(text, None, cx)
             }
-            Event::InputHandled {
+            EditorEvent::InputHandled {
                 text,
                 utf16_range_to_replace: range_to_replace,
             } => Vim::record_insertion(text, range_to_replace.clone(), cx),
@@ -242,7 +244,7 @@ impl Vim {
         cx: &mut WindowContext,
         update: impl FnOnce(&mut Editor, &mut ViewContext<Editor>) -> S,
     ) -> Option<S> {
-        let editor = self.active_editor.clone()?.upgrade(cx)?;
+        let editor = self.active_editor.clone()?.upgrade()?;
         Some(editor.update(cx, update))
     }
 
@@ -254,7 +256,8 @@ impl Vim {
 
             let selections = self
                 .active_editor
-                .and_then(|editor| editor.upgrade(cx))
+                .as_ref()
+                .and_then(|editor| editor.upgrade())
                 .map(|editor| {
                     let editor = editor.read(cx);
                     (
@@ -323,8 +326,6 @@ impl Vim {
             self.take_count(cx);
         }
 
-        cx.emit_global(VimEvent::ModeChanged { mode });
-
         // Sync editor settings like clip mode
         self.sync_vim_settings(cx);
 
@@ -477,7 +478,7 @@ impl Vim {
         if self.enabled != enabled {
             self.enabled = enabled;
 
-            cx.update_default_global::<CommandPaletteFilter, _, _>(|filter, _| {
+            cx.update_global::<CommandPaletteFilter, _>(|filter, _| {
                 if self.enabled {
                     filter.hidden_namespaces.remove("vim");
                 } else {
@@ -491,26 +492,30 @@ impl Vim {
                 let _ = cx.remove_global::<CommandPaletteInterceptor>();
             }
 
-            cx.update_active_window(|cx| {
-                if self.enabled {
-                    let active_editor = cx
-                        .root_view()
-                        .downcast_ref::<Workspace>()
-                        .and_then(|workspace| workspace.read(cx).active_item(cx))
-                        .and_then(|item| item.downcast::<Editor>());
-                    if let Some(active_editor) = active_editor {
-                        self.set_active_editor(active_editor, cx);
-                    }
-                    self.switch_mode(Mode::Normal, false, cx);
-                }
-                self.sync_vim_settings(cx);
-            });
+            if let Some(active_window) = cx.active_window() {
+                active_window
+                    .update(cx, |root_view, cx| {
+                        if self.enabled {
+                            let active_editor = root_view
+                                .downcast::<Workspace>()
+                                .ok()
+                                .and_then(|workspace| workspace.read(cx).active_item(cx))
+                                .and_then(|item| item.downcast::<Editor>());
+                            if let Some(active_editor) = active_editor {
+                                self.set_active_editor(active_editor, cx);
+                            }
+                            self.switch_mode(Mode::Normal, false, cx);
+                        }
+                        self.sync_vim_settings(cx);
+                    })
+                    .ok();
+            }
         }
     }
 
     pub fn state(&self) -> &EditorState {
         if let Some(active_editor) = self.active_editor.as_ref() {
-            if let Some(state) = self.editor_states.get(&active_editor.id()) {
+            if let Some(state) = self.editor_states.get(&active_editor.entity_id()) {
                 return state;
             }
         }
@@ -523,7 +528,7 @@ impl Vim {
         let ret = func(&mut state);
 
         if let Some(active_editor) = self.active_editor.as_ref() {
-            self.editor_states.insert(active_editor.id(), state);
+            self.editor_states.insert(active_editor.entity_id(), state);
         }
 
         ret
@@ -564,8 +569,8 @@ impl Vim {
         // This is a bit of a hack, but currently the search crate does not depend on vim,
         // and it seems nice to keep it that way.
         if self.enabled {
-            let mut context = KeymapContext::default();
-            context.add_identifier("VimEnabled");
+            let mut context = KeyContext::default();
+            context.add("VimEnabled");
             editor.set_keymap_context_layer::<Self>(context, cx)
         } else {
             editor.remove_keymap_context_layer::<Self>(cx);
@@ -573,7 +578,7 @@ impl Vim {
     }
 }
 
-impl Setting for VimModeSetting {
+impl Settings for VimModeSetting {
     const KEY: Option<&'static str> = Some("vim_mode");
 
     type FileContent = Option<bool>;
@@ -581,7 +586,7 @@ impl Setting for VimModeSetting {
     fn load(
         default_value: &Self::FileContent,
         user_values: &[&Self::FileContent],
-        _: &AppContext,
+        _: &mut AppContext,
     ) -> Result<Self> {
         Ok(Self(user_values.iter().rev().find_map(|v| **v).unwrap_or(
             default_value.ok_or_else(Self::missing_default)?,

crates/vim/src/visual.rs 🔗

@@ -8,7 +8,7 @@ use editor::{
     scroll::autoscroll::Autoscroll,
     Bias, DisplayPoint, Editor,
 };
-use gpui::{actions, AppContext, ViewContext, WindowContext};
+use gpui::{actions, ViewContext, WindowContext};
 use language::{Selection, SelectionGoal};
 use workspace::Workspace;
 
@@ -34,24 +34,28 @@ actions!(
     ]
 );
 
-pub fn init(cx: &mut AppContext) {
-    cx.add_action(|_, _: &ToggleVisual, cx: &mut ViewContext<Workspace>| {
+pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+    workspace.register_action(|_, _: &ToggleVisual, cx: &mut ViewContext<Workspace>| {
         toggle_mode(Mode::Visual, cx)
     });
-    cx.add_action(|_, _: &ToggleVisualLine, cx: &mut ViewContext<Workspace>| {
+    workspace.register_action(|_, _: &ToggleVisualLine, cx: &mut ViewContext<Workspace>| {
         toggle_mode(Mode::VisualLine, cx)
     });
-    cx.add_action(
+    workspace.register_action(
         |_, _: &ToggleVisualBlock, cx: &mut ViewContext<Workspace>| {
             toggle_mode(Mode::VisualBlock, cx)
         },
     );
-    cx.add_action(other_end);
-    cx.add_action(delete);
-    cx.add_action(yank);
+    workspace.register_action(other_end);
+    workspace.register_action(delete);
+    workspace.register_action(yank);
 
-    cx.add_action(select_next);
-    cx.add_action(select_previous);
+    workspace.register_action(|workspace, action, cx| {
+        select_next(workspace, action, cx).ok();
+    });
+    workspace.register_action(|workspace, action, cx| {
+        select_previous(workspace, action, cx).ok();
+    });
 }
 
 pub fn visual_motion(motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
@@ -146,13 +150,13 @@ pub fn visual_block_motion(
         let mut head = s.newest_anchor().head().to_display_point(map);
         let mut tail = s.oldest_anchor().tail().to_display_point(map);
 
-        let mut head_x = map.x_for_point(head, &text_layout_details);
-        let mut tail_x = map.x_for_point(tail, &text_layout_details);
+        let mut head_x = map.x_for_display_point(head, &text_layout_details);
+        let mut tail_x = map.x_for_display_point(tail, &text_layout_details);
 
         let (start, end) = match s.newest_anchor().goal {
             SelectionGoal::HorizontalRange { start, end } if preserve_goal => (start, end),
             SelectionGoal::HorizontalPosition(start) if preserve_goal => (start, start),
-            _ => (tail_x, head_x),
+            _ => (tail_x.0, head_x.0),
         };
         let mut goal = SelectionGoal::HorizontalRange { start, end };
 
@@ -165,19 +169,19 @@ pub fn visual_block_motion(
             return;
         };
         head = new_head;
-        head_x = map.x_for_point(head, &text_layout_details);
+        head_x = map.x_for_display_point(head, &text_layout_details);
 
         let is_reversed = tail_x > head_x;
         if was_reversed && !is_reversed {
             tail = movement::saturating_left(map, tail);
-            tail_x = map.x_for_point(tail, &text_layout_details);
+            tail_x = map.x_for_display_point(tail, &text_layout_details);
         } else if !was_reversed && is_reversed {
             tail = movement::saturating_right(map, tail);
-            tail_x = map.x_for_point(tail, &text_layout_details);
+            tail_x = map.x_for_display_point(tail, &text_layout_details);
         }
         if !is_reversed && !preserve_goal {
             head = movement::saturating_right(map, head);
-            head_x = map.x_for_point(head, &text_layout_details);
+            head_x = map.x_for_display_point(head, &text_layout_details);
         }
 
         let positions = if is_reversed {
@@ -188,8 +192,8 @@ pub fn visual_block_motion(
 
         if !preserve_goal {
             goal = SelectionGoal::HorizontalRange {
-                start: positions.start,
-                end: positions.end,
+                start: positions.start.0,
+                end: positions.end.0,
             };
         }
 
@@ -197,7 +201,7 @@ pub fn visual_block_motion(
         let mut row = tail.row();
 
         loop {
-            let layed_out_line = map.lay_out_line_for_row(row, &text_layout_details);
+            let layed_out_line = map.layout_row(row, &text_layout_details);
             let start = DisplayPoint::new(
                 row,
                 layed_out_line.closest_index_for_x(positions.start) as u32,
@@ -214,7 +218,7 @@ pub fn visual_block_motion(
                 }
             }
 
-            if positions.start <= layed_out_line.width() {
+            if positions.start <= layed_out_line.width {
                 let selection = Selection {
                     id: s.new_selection_id(),
                     start: start.to_point(map),
@@ -749,7 +753,12 @@ mod test {
                     fox jumps over
                     the lazy dog"})
             .await;
-        cx.assert_clipboard_content(Some("The q"));
+        assert_eq!(
+            cx.read_from_clipboard()
+                .map(|item| item.text().clone())
+                .unwrap(),
+            "The q"
+        );
 
         cx.set_shared_state(indoc! {"
                     The quick brown

crates/vim2/Cargo.toml 🔗

@@ -1,53 +0,0 @@
-[package]
-name = "vim2"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-path = "src/vim.rs"
-doctest = false
-
-[features]
-neovim = ["nvim-rs", "async-compat", "async-trait", "tokio"]
-
-[dependencies]
-anyhow.workspace = true
-serde.workspace = true
-serde_derive.workspace = true
-itertools = "0.10"
-log.workspace = true
-
-async-compat = { version = "0.2.1", "optional" = true }
-async-trait = { workspace = true, "optional" = true }
-nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", features = ["use_tokio"], optional = true }
-tokio = { version = "1.15", "optional" = true }
-serde_json.workspace = true
-
-collections = { path = "../collections" }
-command_palette = { package = "command_palette2", path = "../command_palette2" }
-editor = { package = "editor2", path = "../editor2" }
-gpui = { package = "gpui2", path = "../gpui2" }
-language = { package = "language2", path = "../language2" }
-search = { package = "search2", path = "../search2" }
-settings = { package = "settings2", path = "../settings2" }
-workspace = { package = "workspace2", path = "../workspace2" }
-theme = { package = "theme2", path = "../theme2" }
-ui = { package = "ui2", path = "../ui2"}
-diagnostics = { package = "diagnostics2", path = "../diagnostics2" }
-zed_actions = { package = "zed_actions2", path = "../zed_actions2" }
-
-[dev-dependencies]
-indoc.workspace = true
-parking_lot.workspace = true
-futures.workspace = true
-
-editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
-language = { package = "language2", path = "../language2", features = ["test-support"] }
-project = { package = "project2", path = "../project2", features = ["test-support"] }
-util = { path = "../util", features = ["test-support"] }
-settings = { package = "settings2", path = "../settings2" }
-workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
-theme = { package = "theme2", path = "../theme2", features = ["test-support"] }
-lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }

crates/vim2/src/command.rs 🔗

@@ -1,434 +0,0 @@
-use command_palette::CommandInterceptResult;
-use editor::{SortLinesCaseInsensitive, SortLinesCaseSensitive};
-use gpui::{impl_actions, Action, AppContext, ViewContext};
-use serde_derive::Deserialize;
-use workspace::{SaveIntent, Workspace};
-
-use crate::{
-    motion::{EndOfDocument, Motion},
-    normal::{
-        move_cursor,
-        search::{FindCommand, ReplaceCommand},
-        JoinLines,
-    },
-    state::Mode,
-    Vim,
-};
-
-#[derive(Debug, Clone, PartialEq, Deserialize)]
-pub struct GoToLine {
-    pub line: u32,
-}
-
-impl_actions!(vim, [GoToLine]);
-
-pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(|_: &mut Workspace, action: &GoToLine, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.switch_mode(Mode::Normal, false, cx);
-            move_cursor(vim, Motion::StartOfDocument, Some(action.line as usize), cx);
-        });
-    });
-}
-
-pub fn command_interceptor(mut query: &str, _: &AppContext) -> Option<CommandInterceptResult> {
-    // Note: this is a very poor simulation of vim's command palette.
-    // In the future we should adjust it to handle parsing range syntax,
-    // and then calling the appropriate commands with/without ranges.
-    //
-    // We also need to support passing arguments to commands like :w
-    // (ideally with filename autocompletion).
-    //
-    // For now, you can only do a replace on the % range, and you can
-    // only use a specific line number range to "go to line"
-    while query.starts_with(":") {
-        query = &query[1..];
-    }
-
-    let (name, action) = match query {
-        // save and quit
-        "w" | "wr" | "wri" | "writ" | "write" => (
-            "write",
-            workspace::Save {
-                save_intent: Some(SaveIntent::Save),
-            }
-            .boxed_clone(),
-        ),
-        "w!" | "wr!" | "wri!" | "writ!" | "write!" => (
-            "write!",
-            workspace::Save {
-                save_intent: Some(SaveIntent::Overwrite),
-            }
-            .boxed_clone(),
-        ),
-        "q" | "qu" | "qui" | "quit" => (
-            "quit",
-            workspace::CloseActiveItem {
-                save_intent: Some(SaveIntent::Close),
-            }
-            .boxed_clone(),
-        ),
-        "q!" | "qu!" | "qui!" | "quit!" => (
-            "quit!",
-            workspace::CloseActiveItem {
-                save_intent: Some(SaveIntent::Skip),
-            }
-            .boxed_clone(),
-        ),
-        "wq" => (
-            "wq",
-            workspace::CloseActiveItem {
-                save_intent: Some(SaveIntent::Save),
-            }
-            .boxed_clone(),
-        ),
-        "wq!" => (
-            "wq!",
-            workspace::CloseActiveItem {
-                save_intent: Some(SaveIntent::Overwrite),
-            }
-            .boxed_clone(),
-        ),
-        "x" | "xi" | "xit" | "exi" | "exit" => (
-            "exit",
-            workspace::CloseActiveItem {
-                save_intent: Some(SaveIntent::SaveAll),
-            }
-            .boxed_clone(),
-        ),
-        "x!" | "xi!" | "xit!" | "exi!" | "exit!" => (
-            "exit!",
-            workspace::CloseActiveItem {
-                save_intent: Some(SaveIntent::Overwrite),
-            }
-            .boxed_clone(),
-        ),
-        "up" | "upd" | "upda" | "updat" | "update" => (
-            "update",
-            workspace::Save {
-                save_intent: Some(SaveIntent::SaveAll),
-            }
-            .boxed_clone(),
-        ),
-        "wa" | "wal" | "wall" => (
-            "wall",
-            workspace::SaveAll {
-                save_intent: Some(SaveIntent::SaveAll),
-            }
-            .boxed_clone(),
-        ),
-        "wa!" | "wal!" | "wall!" => (
-            "wall!",
-            workspace::SaveAll {
-                save_intent: Some(SaveIntent::Overwrite),
-            }
-            .boxed_clone(),
-        ),
-        "qa" | "qal" | "qall" | "quita" | "quital" | "quitall" => (
-            "quitall",
-            workspace::CloseAllItemsAndPanes {
-                save_intent: Some(SaveIntent::Close),
-            }
-            .boxed_clone(),
-        ),
-        "qa!" | "qal!" | "qall!" | "quita!" | "quital!" | "quitall!" => (
-            "quitall!",
-            workspace::CloseAllItemsAndPanes {
-                save_intent: Some(SaveIntent::Skip),
-            }
-            .boxed_clone(),
-        ),
-        "xa" | "xal" | "xall" => (
-            "xall",
-            workspace::CloseAllItemsAndPanes {
-                save_intent: Some(SaveIntent::SaveAll),
-            }
-            .boxed_clone(),
-        ),
-        "xa!" | "xal!" | "xall!" => (
-            "xall!",
-            workspace::CloseAllItemsAndPanes {
-                save_intent: Some(SaveIntent::Overwrite),
-            }
-            .boxed_clone(),
-        ),
-        "wqa" | "wqal" | "wqall" => (
-            "wqall",
-            workspace::CloseAllItemsAndPanes {
-                save_intent: Some(SaveIntent::SaveAll),
-            }
-            .boxed_clone(),
-        ),
-        "wqa!" | "wqal!" | "wqall!" => (
-            "wqall!",
-            workspace::CloseAllItemsAndPanes {
-                save_intent: Some(SaveIntent::Overwrite),
-            }
-            .boxed_clone(),
-        ),
-        "cq" | "cqu" | "cqui" | "cquit" | "cq!" | "cqu!" | "cqui!" | "cquit!" => {
-            ("cquit!", zed_actions::Quit.boxed_clone())
-        }
-
-        // pane management
-        "sp" | "spl" | "spli" | "split" => ("split", workspace::SplitUp.boxed_clone()),
-        "vs" | "vsp" | "vspl" | "vspli" | "vsplit" => {
-            ("vsplit", workspace::SplitLeft.boxed_clone())
-        }
-        "new" => (
-            "new",
-            workspace::NewFileInDirection(workspace::SplitDirection::Up).boxed_clone(),
-        ),
-        "vne" | "vnew" => (
-            "vnew",
-            workspace::NewFileInDirection(workspace::SplitDirection::Left).boxed_clone(),
-        ),
-        "tabe" | "tabed" | "tabedi" | "tabedit" => ("tabedit", workspace::NewFile.boxed_clone()),
-        "tabnew" => ("tabnew", workspace::NewFile.boxed_clone()),
-
-        "tabn" | "tabne" | "tabnex" | "tabnext" => {
-            ("tabnext", workspace::ActivateNextItem.boxed_clone())
-        }
-        "tabp" | "tabpr" | "tabpre" | "tabprev" | "tabprevi" | "tabprevio" | "tabpreviou"
-        | "tabprevious" => ("tabprevious", workspace::ActivatePrevItem.boxed_clone()),
-        "tabN" | "tabNe" | "tabNex" | "tabNext" => {
-            ("tabNext", workspace::ActivatePrevItem.boxed_clone())
-        }
-        "tabc" | "tabcl" | "tabclo" | "tabclos" | "tabclose" => (
-            "tabclose",
-            workspace::CloseActiveItem {
-                save_intent: Some(SaveIntent::Close),
-            }
-            .boxed_clone(),
-        ),
-
-        // quickfix / loclist (merged together for now)
-        "cl" | "cli" | "clis" | "clist" => ("clist", diagnostics::Deploy.boxed_clone()),
-        "cc" => ("cc", editor::Hover.boxed_clone()),
-        "ll" => ("ll", editor::Hover.boxed_clone()),
-        "cn" | "cne" | "cnex" | "cnext" => ("cnext", editor::GoToDiagnostic.boxed_clone()),
-        "lne" | "lnex" | "lnext" => ("cnext", editor::GoToDiagnostic.boxed_clone()),
-
-        "cpr" | "cpre" | "cprev" | "cprevi" | "cprevio" | "cpreviou" | "cprevious" => {
-            ("cprevious", editor::GoToPrevDiagnostic.boxed_clone())
-        }
-        "cN" | "cNe" | "cNex" | "cNext" => ("cNext", editor::GoToPrevDiagnostic.boxed_clone()),
-        "lp" | "lpr" | "lpre" | "lprev" | "lprevi" | "lprevio" | "lpreviou" | "lprevious" => {
-            ("lprevious", editor::GoToPrevDiagnostic.boxed_clone())
-        }
-        "lN" | "lNe" | "lNex" | "lNext" => ("lNext", editor::GoToPrevDiagnostic.boxed_clone()),
-
-        // modify the buffer (should accept [range])
-        "j" | "jo" | "joi" | "join" => ("join", JoinLines.boxed_clone()),
-        "d" | "de" | "del" | "dele" | "delet" | "delete" | "dl" | "dell" | "delel" | "deletl"
-        | "deletel" | "dp" | "dep" | "delp" | "delep" | "deletp" | "deletep" => {
-            ("delete", editor::DeleteLine.boxed_clone())
-        }
-        "sor" | "sor " | "sort" | "sort " => ("sort", SortLinesCaseSensitive.boxed_clone()),
-        "sor i" | "sort i" => ("sort i", SortLinesCaseInsensitive.boxed_clone()),
-
-        // goto (other ranges handled under _ => )
-        "$" => ("$", EndOfDocument.boxed_clone()),
-
-        _ => {
-            if query.starts_with("/") || query.starts_with("?") {
-                (
-                    query,
-                    FindCommand {
-                        query: query[1..].to_string(),
-                        backwards: query.starts_with("?"),
-                    }
-                    .boxed_clone(),
-                )
-            } else if query.starts_with("%") {
-                (
-                    query,
-                    ReplaceCommand {
-                        query: query.to_string(),
-                    }
-                    .boxed_clone(),
-                )
-            } else if let Ok(line) = query.parse::<u32>() {
-                (query, GoToLine { line }.boxed_clone())
-            } else {
-                return None;
-            }
-        }
-    };
-
-    let string = ":".to_owned() + name;
-    let positions = generate_positions(&string, query);
-
-    Some(CommandInterceptResult {
-        action,
-        string,
-        positions,
-    })
-}
-
-fn generate_positions(string: &str, query: &str) -> Vec<usize> {
-    let mut positions = Vec::new();
-    let mut chars = query.chars().into_iter();
-
-    let Some(mut current) = chars.next() else {
-        return positions;
-    };
-
-    for (i, c) in string.chars().enumerate() {
-        if c == current {
-            positions.push(i);
-            if let Some(c) = chars.next() {
-                current = c;
-            } else {
-                break;
-            }
-        }
-    }
-
-    positions
-}
-
-#[cfg(test)]
-mod test {
-    use std::path::Path;
-
-    use crate::test::{NeovimBackedTestContext, VimTestContext};
-    use gpui::TestAppContext;
-    use indoc::indoc;
-
-    #[gpui::test]
-    async fn test_command_basics(cx: &mut TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {"
-            ˇa
-            b
-            c"})
-            .await;
-
-        cx.simulate_shared_keystrokes([":", "j", "enter"]).await;
-
-        // hack: our cursor positionining after a join command is wrong
-        cx.simulate_shared_keystrokes(["^"]).await;
-        cx.assert_shared_state(indoc! {
-            "ˇa b
-            c"
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_command_goto(cx: &mut TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {"
-            ˇa
-            b
-            c"})
-            .await;
-        cx.simulate_shared_keystrokes([":", "3", "enter"]).await;
-        cx.assert_shared_state(indoc! {"
-            a
-            b
-            ˇc"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_command_replace(cx: &mut TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {"
-            ˇa
-            b
-            c"})
-            .await;
-        cx.simulate_shared_keystrokes([":", "%", "s", "/", "b", "/", "d", "enter"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            a
-            ˇd
-            c"})
-            .await;
-        cx.simulate_shared_keystrokes([
-            ":", "%", "s", ":", ".", ":", "\\", "0", "\\", "0", "enter",
-        ])
-        .await;
-        cx.assert_shared_state(indoc! {"
-            aa
-            dd
-            ˇcc"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_command_search(cx: &mut TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {"
-                ˇa
-                b
-                a
-                c"})
-            .await;
-        cx.simulate_shared_keystrokes([":", "/", "b", "enter"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-                a
-                ˇb
-                a
-                c"})
-            .await;
-        cx.simulate_shared_keystrokes([":", "?", "a", "enter"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-                ˇa
-                b
-                a
-                c"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_command_write(cx: &mut TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-        let path = Path::new("/root/dir/file.rs");
-        let fs = cx.workspace(|workspace, cx| workspace.project().read(cx).fs().clone());
-
-        cx.simulate_keystrokes(["i", "@", "escape"]);
-        cx.simulate_keystrokes([":", "w", "enter"]);
-
-        assert_eq!(fs.load(&path).await.unwrap(), "@\n");
-
-        fs.as_fake()
-            .write_file_internal(path, "oops\n".to_string())
-            .unwrap();
-
-        // conflict!
-        cx.simulate_keystrokes(["i", "@", "escape"]);
-        cx.simulate_keystrokes([":", "w", "enter"]);
-        assert!(cx.has_pending_prompt());
-        // "Cancel"
-        cx.simulate_prompt_answer(0);
-        assert_eq!(fs.load(&path).await.unwrap(), "oops\n");
-        assert!(!cx.has_pending_prompt());
-        // force overwrite
-        cx.simulate_keystrokes([":", "w", "!", "enter"]);
-        assert!(!cx.has_pending_prompt());
-        assert_eq!(fs.load(&path).await.unwrap(), "@@\n");
-    }
-
-    #[gpui::test]
-    async fn test_command_quit(cx: &mut TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-
-        cx.simulate_keystrokes([":", "n", "e", "w", "enter"]);
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2));
-        cx.simulate_keystrokes([":", "q", "enter"]);
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 1));
-        cx.simulate_keystrokes([":", "n", "e", "w", "enter"]);
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2));
-        cx.simulate_keystrokes([":", "q", "a", "enter"]);
-        cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 0));
-    }
-}

crates/vim2/src/editor_events.rs 🔗

@@ -1,104 +0,0 @@
-use crate::Vim;
-use editor::{Editor, EditorEvent};
-use gpui::{AppContext, Entity, EntityId, View, ViewContext, WindowContext};
-
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(|_, cx: &mut ViewContext<Editor>| {
-        let editor = cx.view().clone();
-        cx.subscribe(&editor, |_, editor, event: &EditorEvent, cx| match event {
-            EditorEvent::Focused => cx.window_context().defer(|cx| focused(editor, cx)),
-            EditorEvent::Blurred => cx.window_context().defer(|cx| blurred(editor, cx)),
-            _ => {}
-        })
-        .detach();
-
-        let id = cx.view().entity_id();
-        cx.on_release(move |_, _, cx| released(id, cx)).detach();
-    })
-    .detach();
-}
-
-fn focused(editor: View<Editor>, cx: &mut WindowContext) {
-    if Vim::read(cx).active_editor.clone().is_some() {
-        Vim::update(cx, |vim, cx| {
-            vim.update_active_editor(cx, |previously_active_editor, cx| {
-                vim.unhook_vim_settings(previously_active_editor, cx)
-            });
-        });
-    }
-
-    Vim::update(cx, |vim, cx| {
-        vim.set_active_editor(editor.clone(), cx);
-    });
-}
-
-fn blurred(editor: View<Editor>, cx: &mut WindowContext) {
-    Vim::update(cx, |vim, cx| {
-        vim.workspace_state.recording = false;
-        vim.workspace_state.recorded_actions.clear();
-        if let Some(previous_editor) = vim.active_editor.clone() {
-            if previous_editor
-                .upgrade()
-                .is_some_and(|previous| previous == editor.clone())
-            {
-                vim.clear_operator(cx);
-                vim.active_editor = None;
-                vim.editor_subscription = None;
-            }
-        }
-
-        editor.update(cx, |editor, cx| vim.unhook_vim_settings(editor, cx))
-    });
-}
-
-fn released(entity_id: EntityId, cx: &mut AppContext) {
-    cx.update_global(|vim: &mut Vim, _| {
-        if vim
-            .active_editor
-            .as_ref()
-            .is_some_and(|previous| previous.entity_id() == entity_id)
-        {
-            vim.active_editor = None;
-            vim.editor_subscription = None;
-        }
-        vim.editor_states.remove(&entity_id)
-    });
-}
-
-#[cfg(test)]
-mod test {
-    use crate::{test::VimTestContext, Vim};
-    use editor::Editor;
-    use gpui::{Context, Entity};
-    use language::Buffer;
-
-    // regression test for blur called with a different active editor
-    #[gpui::test]
-    async fn test_blur_focus(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-
-        let buffer = cx.new_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
-        let window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx));
-        let editor2 = cx
-            .update(|cx| {
-                window2.update(cx, |_, cx| {
-                    cx.focus_self();
-                    cx.view().clone()
-                })
-            })
-            .unwrap();
-
-        cx.update(|cx| {
-            let vim = Vim::read(cx);
-            assert_eq!(
-                vim.active_editor.as_ref().unwrap().entity_id(),
-                editor2.entity_id(),
-            )
-        });
-
-        // no panic when blurring an editor in a different window.
-        cx.update_editor(|editor1, cx| {
-            editor1.handle_blur(cx);
-        });
-    }
-}

crates/vim2/src/insert.rs 🔗

@@ -1,125 +0,0 @@
-use crate::{normal::repeat, state::Mode, Vim};
-use editor::{scroll::autoscroll::Autoscroll, Bias};
-use gpui::{actions, Action, ViewContext};
-use language::SelectionGoal;
-use workspace::Workspace;
-
-actions!(vim, [NormalBefore]);
-
-pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(normal_before);
-}
-
-fn normal_before(_: &mut Workspace, action: &NormalBefore, cx: &mut ViewContext<Workspace>) {
-    let should_repeat = Vim::update(cx, |vim, cx| {
-        let count = vim.take_count(cx).unwrap_or(1);
-        vim.stop_recording_immediately(action.boxed_clone());
-        if count <= 1 || vim.workspace_state.replaying {
-            vim.update_active_editor(cx, |editor, cx| {
-                editor.cancel(&Default::default(), cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.move_cursors_with(|map, mut cursor, _| {
-                        *cursor.column_mut() = cursor.column().saturating_sub(1);
-                        (map.clip_point(cursor, Bias::Left), SelectionGoal::None)
-                    });
-                });
-            });
-            vim.switch_mode(Mode::Normal, false, cx);
-            false
-        } else {
-            true
-        }
-    });
-
-    if should_repeat {
-        repeat::repeat(cx, true)
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use crate::{
-        state::Mode,
-        test::{NeovimBackedTestContext, VimTestContext},
-    };
-
-    #[gpui::test]
-    async fn test_enter_and_exit_insert_mode(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-        cx.simulate_keystroke("i");
-        assert_eq!(cx.mode(), Mode::Insert);
-        cx.simulate_keystrokes(["T", "e", "s", "t"]);
-        cx.assert_editor_state("Testˇ");
-        cx.simulate_keystroke("escape");
-        assert_eq!(cx.mode(), Mode::Normal);
-        cx.assert_editor_state("Tesˇt");
-    }
-
-    #[gpui::test]
-    async fn test_insert_with_counts(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state("ˇhello\n").await;
-        cx.simulate_shared_keystrokes(["5", "i", "-", "escape"])
-            .await;
-        cx.run_until_parked();
-        cx.assert_shared_state("----ˇ-hello\n").await;
-
-        cx.set_shared_state("ˇhello\n").await;
-        cx.simulate_shared_keystrokes(["5", "a", "-", "escape"])
-            .await;
-        cx.run_until_parked();
-        cx.assert_shared_state("h----ˇ-ello\n").await;
-
-        cx.simulate_shared_keystrokes(["4", "shift-i", "-", "escape"])
-            .await;
-        cx.run_until_parked();
-        cx.assert_shared_state("---ˇ-h-----ello\n").await;
-
-        cx.simulate_shared_keystrokes(["3", "shift-a", "-", "escape"])
-            .await;
-        cx.run_until_parked();
-        cx.assert_shared_state("----h-----ello--ˇ-\n").await;
-
-        cx.set_shared_state("ˇhello\n").await;
-        cx.simulate_shared_keystrokes(["3", "o", "o", "i", "escape"])
-            .await;
-        cx.run_until_parked();
-        cx.assert_shared_state("hello\noi\noi\noˇi\n").await;
-
-        cx.set_shared_state("ˇhello\n").await;
-        cx.simulate_shared_keystrokes(["3", "shift-o", "o", "i", "escape"])
-            .await;
-        cx.run_until_parked();
-        cx.assert_shared_state("oi\noi\noˇi\nhello\n").await;
-    }
-
-    #[gpui::test]
-    async fn test_insert_with_repeat(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state("ˇhello\n").await;
-        cx.simulate_shared_keystrokes(["3", "i", "-", "escape"])
-            .await;
-        cx.run_until_parked();
-        cx.assert_shared_state("--ˇ-hello\n").await;
-        cx.simulate_shared_keystrokes(["."]).await;
-        cx.run_until_parked();
-        cx.assert_shared_state("----ˇ--hello\n").await;
-        cx.simulate_shared_keystrokes(["2", "."]).await;
-        cx.run_until_parked();
-        cx.assert_shared_state("-----ˇ---hello\n").await;
-
-        cx.set_shared_state("ˇhello\n").await;
-        cx.simulate_shared_keystrokes(["2", "o", "k", "k", "escape"])
-            .await;
-        cx.run_until_parked();
-        cx.assert_shared_state("hello\nkk\nkˇk\n").await;
-        cx.simulate_shared_keystrokes(["."]).await;
-        cx.run_until_parked();
-        cx.assert_shared_state("hello\nkk\nkk\nkk\nkˇk\n").await;
-        cx.simulate_shared_keystrokes(["1", "."]).await;
-        cx.run_until_parked();
-        cx.assert_shared_state("hello\nkk\nkk\nkk\nkk\nkˇk\n").await;
-    }
-}

crates/vim2/src/mode_indicator.rs 🔗

@@ -1,74 +0,0 @@
-use gpui::{div, Element, Render, Subscription, ViewContext};
-use settings::SettingsStore;
-use workspace::{item::ItemHandle, ui::prelude::*, StatusItemView};
-
-use crate::{state::Mode, Vim};
-
-pub struct ModeIndicator {
-    pub mode: Option<Mode>,
-    _subscriptions: Vec<Subscription>,
-}
-
-impl ModeIndicator {
-    pub fn new(cx: &mut ViewContext<Self>) -> Self {
-        let _subscriptions = vec![
-            cx.observe_global::<Vim>(|this, cx| this.update_mode(cx)),
-            cx.observe_global::<SettingsStore>(|this, cx| this.update_mode(cx)),
-        ];
-
-        let mut this = Self {
-            mode: None,
-            _subscriptions,
-        };
-        this.update_mode(cx);
-        this
-    }
-
-    fn update_mode(&mut self, cx: &mut ViewContext<Self>) {
-        // Vim doesn't exist in some tests
-        if !cx.has_global::<Vim>() {
-            return;
-        }
-
-        let vim = Vim::read(cx);
-        if vim.enabled {
-            self.mode = Some(vim.state().mode);
-        } else {
-            self.mode = None;
-        }
-    }
-
-    pub fn set_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
-        if self.mode != Some(mode) {
-            self.mode = Some(mode);
-            cx.notify();
-        }
-    }
-}
-
-impl Render for ModeIndicator {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
-        let Some(mode) = self.mode.as_ref() else {
-            return div().into_any();
-        };
-
-        let text = match mode {
-            Mode::Normal => "-- NORMAL --",
-            Mode::Insert => "-- INSERT --",
-            Mode::Visual => "-- VISUAL --",
-            Mode::VisualLine => "-- VISUAL LINE --",
-            Mode::VisualBlock => "-- VISUAL BLOCK --",
-        };
-        Label::new(text).size(LabelSize::Small).into_any_element()
-    }
-}
-
-impl StatusItemView for ModeIndicator {
-    fn set_active_pane_item(
-        &mut self,
-        _active_pane_item: Option<&dyn ItemHandle>,
-        _cx: &mut ViewContext<Self>,
-    ) {
-        // nothing to do.
-    }
-}

crates/vim2/src/motion.rs 🔗

@@ -1,1107 +0,0 @@
-use editor::{
-    char_kind,
-    display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint},
-    movement::{self, find_boundary, find_preceding_boundary, FindRange, TextLayoutDetails},
-    Bias, CharKind, DisplayPoint, ToOffset,
-};
-use gpui::{actions, impl_actions, px, ViewContext, WindowContext};
-use language::{Point, Selection, SelectionGoal};
-use serde::Deserialize;
-use workspace::Workspace;
-
-use crate::{
-    normal::normal_motion,
-    state::{Mode, Operator},
-    visual::visual_motion,
-    Vim,
-};
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum Motion {
-    Left,
-    Backspace,
-    Down { display_lines: bool },
-    Up { display_lines: bool },
-    Right,
-    NextWordStart { ignore_punctuation: bool },
-    NextWordEnd { ignore_punctuation: bool },
-    PreviousWordStart { ignore_punctuation: bool },
-    FirstNonWhitespace { display_lines: bool },
-    CurrentLine,
-    StartOfLine { display_lines: bool },
-    EndOfLine { display_lines: bool },
-    StartOfParagraph,
-    EndOfParagraph,
-    StartOfDocument,
-    EndOfDocument,
-    Matching,
-    FindForward { before: bool, char: char },
-    FindBackward { after: bool, char: char },
-    NextLineStart,
-    StartOfLineDownward,
-    EndOfLineDownward,
-    GoToColumn,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct NextWordStart {
-    #[serde(default)]
-    ignore_punctuation: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct NextWordEnd {
-    #[serde(default)]
-    ignore_punctuation: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct PreviousWordStart {
-    #[serde(default)]
-    ignore_punctuation: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub(crate) struct Up {
-    #[serde(default)]
-    pub(crate) display_lines: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct Down {
-    #[serde(default)]
-    display_lines: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct FirstNonWhitespace {
-    #[serde(default)]
-    display_lines: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct EndOfLine {
-    #[serde(default)]
-    display_lines: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub struct StartOfLine {
-    #[serde(default)]
-    pub(crate) display_lines: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-struct RepeatFind {
-    #[serde(default)]
-    backwards: bool,
-}
-
-impl_actions!(
-    vim,
-    [
-        RepeatFind,
-        StartOfLine,
-        EndOfLine,
-        FirstNonWhitespace,
-        Down,
-        Up,
-        PreviousWordStart,
-        NextWordEnd,
-        NextWordStart
-    ]
-);
-
-actions!(
-    vim,
-    [
-        Left,
-        Backspace,
-        Right,
-        CurrentLine,
-        StartOfParagraph,
-        EndOfParagraph,
-        StartOfDocument,
-        EndOfDocument,
-        Matching,
-        NextLineStart,
-        StartOfLineDownward,
-        EndOfLineDownward,
-        GoToColumn,
-    ]
-);
-
-pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(|_: &mut Workspace, _: &Left, cx: _| motion(Motion::Left, cx));
-    workspace
-        .register_action(|_: &mut Workspace, _: &Backspace, cx: _| motion(Motion::Backspace, cx));
-    workspace.register_action(|_: &mut Workspace, action: &Down, cx: _| {
-        motion(
-            Motion::Down {
-                display_lines: action.display_lines,
-            },
-            cx,
-        )
-    });
-    workspace.register_action(|_: &mut Workspace, action: &Up, cx: _| {
-        motion(
-            Motion::Up {
-                display_lines: action.display_lines,
-            },
-            cx,
-        )
-    });
-    workspace.register_action(|_: &mut Workspace, _: &Right, cx: _| motion(Motion::Right, cx));
-    workspace.register_action(|_: &mut Workspace, action: &FirstNonWhitespace, cx: _| {
-        motion(
-            Motion::FirstNonWhitespace {
-                display_lines: action.display_lines,
-            },
-            cx,
-        )
-    });
-    workspace.register_action(|_: &mut Workspace, action: &StartOfLine, cx: _| {
-        motion(
-            Motion::StartOfLine {
-                display_lines: action.display_lines,
-            },
-            cx,
-        )
-    });
-    workspace.register_action(|_: &mut Workspace, action: &EndOfLine, cx: _| {
-        motion(
-            Motion::EndOfLine {
-                display_lines: action.display_lines,
-            },
-            cx,
-        )
-    });
-    workspace.register_action(|_: &mut Workspace, _: &CurrentLine, cx: _| {
-        motion(Motion::CurrentLine, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, _: &StartOfParagraph, cx: _| {
-        motion(Motion::StartOfParagraph, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, _: &EndOfParagraph, cx: _| {
-        motion(Motion::EndOfParagraph, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, _: &StartOfDocument, cx: _| {
-        motion(Motion::StartOfDocument, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, _: &EndOfDocument, cx: _| {
-        motion(Motion::EndOfDocument, cx)
-    });
-    workspace
-        .register_action(|_: &mut Workspace, _: &Matching, cx: _| motion(Motion::Matching, cx));
-
-    workspace.register_action(
-        |_: &mut Workspace, &NextWordStart { ignore_punctuation }: &NextWordStart, cx: _| {
-            motion(Motion::NextWordStart { ignore_punctuation }, cx)
-        },
-    );
-    workspace.register_action(
-        |_: &mut Workspace, &NextWordEnd { ignore_punctuation }: &NextWordEnd, cx: _| {
-            motion(Motion::NextWordEnd { ignore_punctuation }, cx)
-        },
-    );
-    workspace.register_action(
-        |_: &mut Workspace,
-         &PreviousWordStart { ignore_punctuation }: &PreviousWordStart,
-         cx: _| { motion(Motion::PreviousWordStart { ignore_punctuation }, cx) },
-    );
-    workspace.register_action(|_: &mut Workspace, &NextLineStart, cx: _| {
-        motion(Motion::NextLineStart, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, &StartOfLineDownward, cx: _| {
-        motion(Motion::StartOfLineDownward, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, &EndOfLineDownward, cx: _| {
-        motion(Motion::EndOfLineDownward, cx)
-    });
-    workspace
-        .register_action(|_: &mut Workspace, &GoToColumn, cx: _| motion(Motion::GoToColumn, cx));
-    workspace.register_action(|_: &mut Workspace, action: &RepeatFind, cx: _| {
-        repeat_motion(action.backwards, cx)
-    });
-}
-
-pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) {
-    if let Some(Operator::FindForward { .. }) | Some(Operator::FindBackward { .. }) =
-        Vim::read(cx).active_operator()
-    {
-        Vim::update(cx, |vim, cx| vim.pop_operator(cx));
-    }
-
-    let count = Vim::update(cx, |vim, cx| vim.take_count(cx));
-    let operator = Vim::read(cx).active_operator();
-    match Vim::read(cx).state().mode {
-        Mode::Normal => normal_motion(motion, operator, count, cx),
-        Mode::Visual | Mode::VisualLine | Mode::VisualBlock => visual_motion(motion, count, cx),
-        Mode::Insert => {
-            // Shouldn't execute a motion in insert mode. Ignoring
-        }
-    }
-    Vim::update(cx, |vim, cx| vim.clear_operator(cx));
-}
-
-fn repeat_motion(backwards: bool, cx: &mut WindowContext) {
-    let find = match Vim::read(cx).workspace_state.last_find.clone() {
-        Some(Motion::FindForward { before, char }) => {
-            if backwards {
-                Motion::FindBackward {
-                    after: before,
-                    char,
-                }
-            } else {
-                Motion::FindForward { before, char }
-            }
-        }
-
-        Some(Motion::FindBackward { after, char }) => {
-            if backwards {
-                Motion::FindForward {
-                    before: after,
-                    char,
-                }
-            } else {
-                Motion::FindBackward { after, char }
-            }
-        }
-        _ => return,
-    };
-
-    motion(find, cx)
-}
-
-// Motion handling is specified here:
-// https://github.com/vim/vim/blob/master/runtime/doc/motion.txt
-impl Motion {
-    pub fn linewise(&self) -> bool {
-        use Motion::*;
-        match self {
-            Down { .. }
-            | Up { .. }
-            | StartOfDocument
-            | EndOfDocument
-            | CurrentLine
-            | NextLineStart
-            | StartOfLineDownward
-            | StartOfParagraph
-            | EndOfParagraph => true,
-            EndOfLine { .. }
-            | NextWordEnd { .. }
-            | Matching
-            | FindForward { .. }
-            | Left
-            | Backspace
-            | Right
-            | StartOfLine { .. }
-            | EndOfLineDownward
-            | GoToColumn
-            | NextWordStart { .. }
-            | PreviousWordStart { .. }
-            | FirstNonWhitespace { .. }
-            | FindBackward { .. } => false,
-        }
-    }
-
-    pub fn infallible(&self) -> bool {
-        use Motion::*;
-        match self {
-            StartOfDocument | EndOfDocument | CurrentLine => true,
-            Down { .. }
-            | Up { .. }
-            | EndOfLine { .. }
-            | NextWordEnd { .. }
-            | Matching
-            | FindForward { .. }
-            | Left
-            | Backspace
-            | Right
-            | StartOfLine { .. }
-            | StartOfParagraph
-            | EndOfParagraph
-            | StartOfLineDownward
-            | EndOfLineDownward
-            | GoToColumn
-            | NextWordStart { .. }
-            | PreviousWordStart { .. }
-            | FirstNonWhitespace { .. }
-            | FindBackward { .. }
-            | NextLineStart => false,
-        }
-    }
-
-    pub fn inclusive(&self) -> bool {
-        use Motion::*;
-        match self {
-            Down { .. }
-            | Up { .. }
-            | StartOfDocument
-            | EndOfDocument
-            | CurrentLine
-            | EndOfLine { .. }
-            | EndOfLineDownward
-            | NextWordEnd { .. }
-            | Matching
-            | FindForward { .. }
-            | NextLineStart => true,
-            Left
-            | Backspace
-            | Right
-            | StartOfLine { .. }
-            | StartOfLineDownward
-            | StartOfParagraph
-            | EndOfParagraph
-            | GoToColumn
-            | NextWordStart { .. }
-            | PreviousWordStart { .. }
-            | FirstNonWhitespace { .. }
-            | FindBackward { .. } => false,
-        }
-    }
-
-    pub fn move_point(
-        &self,
-        map: &DisplaySnapshot,
-        point: DisplayPoint,
-        goal: SelectionGoal,
-        maybe_times: Option<usize>,
-        text_layout_details: &TextLayoutDetails,
-    ) -> Option<(DisplayPoint, SelectionGoal)> {
-        let times = maybe_times.unwrap_or(1);
-        use Motion::*;
-        let infallible = self.infallible();
-        let (new_point, goal) = match self {
-            Left => (left(map, point, times), SelectionGoal::None),
-            Backspace => (backspace(map, point, times), SelectionGoal::None),
-            Down {
-                display_lines: false,
-            } => up_down_buffer_rows(map, point, goal, times as isize, &text_layout_details),
-            Down {
-                display_lines: true,
-            } => down_display(map, point, goal, times, &text_layout_details),
-            Up {
-                display_lines: false,
-            } => up_down_buffer_rows(map, point, goal, 0 - times as isize, &text_layout_details),
-            Up {
-                display_lines: true,
-            } => up_display(map, point, goal, times, &text_layout_details),
-            Right => (right(map, point, times), SelectionGoal::None),
-            NextWordStart { ignore_punctuation } => (
-                next_word_start(map, point, *ignore_punctuation, times),
-                SelectionGoal::None,
-            ),
-            NextWordEnd { ignore_punctuation } => (
-                next_word_end(map, point, *ignore_punctuation, times),
-                SelectionGoal::None,
-            ),
-            PreviousWordStart { ignore_punctuation } => (
-                previous_word_start(map, point, *ignore_punctuation, times),
-                SelectionGoal::None,
-            ),
-            FirstNonWhitespace { display_lines } => (
-                first_non_whitespace(map, *display_lines, point),
-                SelectionGoal::None,
-            ),
-            StartOfLine { display_lines } => (
-                start_of_line(map, *display_lines, point),
-                SelectionGoal::None,
-            ),
-            EndOfLine { display_lines } => {
-                (end_of_line(map, *display_lines, point), SelectionGoal::None)
-            }
-            StartOfParagraph => (
-                movement::start_of_paragraph(map, point, times),
-                SelectionGoal::None,
-            ),
-            EndOfParagraph => (
-                map.clip_at_line_end(movement::end_of_paragraph(map, point, times)),
-                SelectionGoal::None,
-            ),
-            CurrentLine => (next_line_end(map, point, times), SelectionGoal::None),
-            StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None),
-            EndOfDocument => (
-                end_of_document(map, point, maybe_times),
-                SelectionGoal::None,
-            ),
-            Matching => (matching(map, point), SelectionGoal::None),
-            FindForward { before, char } => (
-                find_forward(map, point, *before, *char, times),
-                SelectionGoal::None,
-            ),
-            FindBackward { after, char } => (
-                find_backward(map, point, *after, *char, times),
-                SelectionGoal::None,
-            ),
-            NextLineStart => (next_line_start(map, point, times), SelectionGoal::None),
-            StartOfLineDownward => (next_line_start(map, point, times - 1), SelectionGoal::None),
-            EndOfLineDownward => (next_line_end(map, point, times), SelectionGoal::None),
-            GoToColumn => (go_to_column(map, point, times), SelectionGoal::None),
-        };
-
-        (new_point != point || infallible).then_some((new_point, goal))
-    }
-
-    // Expands a selection using self motion for an operator
-    pub fn expand_selection(
-        &self,
-        map: &DisplaySnapshot,
-        selection: &mut Selection<DisplayPoint>,
-        times: Option<usize>,
-        expand_to_surrounding_newline: bool,
-        text_layout_details: &TextLayoutDetails,
-    ) -> bool {
-        if let Some((new_head, goal)) = self.move_point(
-            map,
-            selection.head(),
-            selection.goal,
-            times,
-            &text_layout_details,
-        ) {
-            selection.set_head(new_head, goal);
-
-            if self.linewise() {
-                selection.start = map.prev_line_boundary(selection.start.to_point(map)).1;
-
-                if expand_to_surrounding_newline {
-                    if selection.end.row() < map.max_point().row() {
-                        *selection.end.row_mut() += 1;
-                        *selection.end.column_mut() = 0;
-                        selection.end = map.clip_point(selection.end, Bias::Right);
-                        // Don't reset the end here
-                        return true;
-                    } else if selection.start.row() > 0 {
-                        *selection.start.row_mut() -= 1;
-                        *selection.start.column_mut() = map.line_len(selection.start.row());
-                        selection.start = map.clip_point(selection.start, Bias::Left);
-                    }
-                }
-
-                (_, selection.end) = map.next_line_boundary(selection.end.to_point(map));
-            } else {
-                // Another special case: When using the "w" motion in combination with an
-                // operator and the last word moved over is at the end of a line, the end of
-                // that word becomes the end of the operated text, not the first word in the
-                // next line.
-                if let Motion::NextWordStart {
-                    ignore_punctuation: _,
-                } = self
-                {
-                    let start_row = selection.start.to_point(&map).row;
-                    if selection.end.to_point(&map).row > start_row {
-                        selection.end =
-                            Point::new(start_row, map.buffer_snapshot.line_len(start_row))
-                                .to_display_point(&map)
-                    }
-                }
-
-                // If the motion is exclusive and the end of the motion is in column 1, the
-                // end of the motion is moved to the end of the previous line and the motion
-                // becomes inclusive. Example: "}" moves to the first line after a paragraph,
-                // but "d}" will not include that line.
-                let mut inclusive = self.inclusive();
-                if !inclusive
-                    && self != &Motion::Backspace
-                    && selection.end.row() > selection.start.row()
-                    && selection.end.column() == 0
-                {
-                    inclusive = true;
-                    *selection.end.row_mut() -= 1;
-                    *selection.end.column_mut() = 0;
-                    selection.end = map.clip_point(
-                        map.next_line_boundary(selection.end.to_point(map)).1,
-                        Bias::Left,
-                    );
-                }
-
-                if inclusive && selection.end.column() < map.line_len(selection.end.row()) {
-                    *selection.end.column_mut() += 1;
-                }
-            }
-            true
-        } else {
-            false
-        }
-    }
-}
-
-fn left(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> DisplayPoint {
-    for _ in 0..times {
-        point = movement::saturating_left(map, point);
-        if point.column() == 0 {
-            break;
-        }
-    }
-    point
-}
-
-fn backspace(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> DisplayPoint {
-    for _ in 0..times {
-        point = movement::left(map, point);
-    }
-    point
-}
-
-pub(crate) fn start_of_relative_buffer_row(
-    map: &DisplaySnapshot,
-    point: DisplayPoint,
-    times: isize,
-) -> DisplayPoint {
-    let start = map.display_point_to_fold_point(point, Bias::Left);
-    let target = start.row() as isize + times;
-    let new_row = (target.max(0) as u32).min(map.fold_snapshot.max_point().row());
-
-    map.clip_point(
-        map.fold_point_to_display_point(
-            map.fold_snapshot
-                .clip_point(FoldPoint::new(new_row, 0), Bias::Right),
-        ),
-        Bias::Right,
-    )
-}
-
-fn up_down_buffer_rows(
-    map: &DisplaySnapshot,
-    point: DisplayPoint,
-    mut goal: SelectionGoal,
-    times: isize,
-    text_layout_details: &TextLayoutDetails,
-) -> (DisplayPoint, SelectionGoal) {
-    let start = map.display_point_to_fold_point(point, Bias::Left);
-    let begin_folded_line = map.fold_point_to_display_point(
-        map.fold_snapshot
-            .clip_point(FoldPoint::new(start.row(), 0), Bias::Left),
-    );
-    let select_nth_wrapped_row = point.row() - begin_folded_line.row();
-
-    let (goal_wrap, goal_x) = match goal {
-        SelectionGoal::WrappedHorizontalPosition((row, x)) => (row, x),
-        SelectionGoal::HorizontalRange { end, .. } => (select_nth_wrapped_row, end),
-        SelectionGoal::HorizontalPosition(x) => (select_nth_wrapped_row, x),
-        _ => {
-            let x = map.x_for_display_point(point, text_layout_details);
-            goal = SelectionGoal::WrappedHorizontalPosition((select_nth_wrapped_row, x.0));
-            (select_nth_wrapped_row, x.0)
-        }
-    };
-
-    let target = start.row() as isize + times;
-    let new_row = (target.max(0) as u32).min(map.fold_snapshot.max_point().row());
-
-    let mut begin_folded_line = map.fold_point_to_display_point(
-        map.fold_snapshot
-            .clip_point(FoldPoint::new(new_row, 0), Bias::Left),
-    );
-
-    let mut i = 0;
-    while i < goal_wrap && begin_folded_line.row() < map.max_point().row() {
-        let next_folded_line = DisplayPoint::new(begin_folded_line.row() + 1, 0);
-        if map
-            .display_point_to_fold_point(next_folded_line, Bias::Right)
-            .row()
-            == new_row
-        {
-            i += 1;
-            begin_folded_line = next_folded_line;
-        } else {
-            break;
-        }
-    }
-
-    let new_col = if i == goal_wrap {
-        map.display_column_for_x(begin_folded_line.row(), px(goal_x), text_layout_details)
-    } else {
-        map.line_len(begin_folded_line.row())
-    };
-
-    (
-        map.clip_point(
-            DisplayPoint::new(begin_folded_line.row(), new_col),
-            Bias::Left,
-        ),
-        goal,
-    )
-}
-
-fn down_display(
-    map: &DisplaySnapshot,
-    mut point: DisplayPoint,
-    mut goal: SelectionGoal,
-    times: usize,
-    text_layout_details: &TextLayoutDetails,
-) -> (DisplayPoint, SelectionGoal) {
-    for _ in 0..times {
-        (point, goal) = movement::down(map, point, goal, true, text_layout_details);
-    }
-
-    (point, goal)
-}
-
-fn up_display(
-    map: &DisplaySnapshot,
-    mut point: DisplayPoint,
-    mut goal: SelectionGoal,
-    times: usize,
-    text_layout_details: &TextLayoutDetails,
-) -> (DisplayPoint, SelectionGoal) {
-    for _ in 0..times {
-        (point, goal) = movement::up(map, point, goal, true, &text_layout_details);
-    }
-
-    (point, goal)
-}
-
-pub(crate) fn right(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> DisplayPoint {
-    for _ in 0..times {
-        let new_point = movement::saturating_right(map, point);
-        if point == new_point {
-            break;
-        }
-        point = new_point;
-    }
-    point
-}
-
-pub(crate) fn next_word_start(
-    map: &DisplaySnapshot,
-    mut point: DisplayPoint,
-    ignore_punctuation: bool,
-    times: usize,
-) -> DisplayPoint {
-    let scope = map.buffer_snapshot.language_scope_at(point.to_point(map));
-    for _ in 0..times {
-        let mut crossed_newline = false;
-        point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
-            let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
-            let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
-            let at_newline = right == '\n';
-
-            let found = (left_kind != right_kind && right_kind != CharKind::Whitespace)
-                || at_newline && crossed_newline
-                || at_newline && left == '\n'; // Prevents skipping repeated empty lines
-
-            crossed_newline |= at_newline;
-            found
-        })
-    }
-    point
-}
-
-fn next_word_end(
-    map: &DisplaySnapshot,
-    mut point: DisplayPoint,
-    ignore_punctuation: bool,
-    times: usize,
-) -> DisplayPoint {
-    let scope = map.buffer_snapshot.language_scope_at(point.to_point(map));
-    for _ in 0..times {
-        if point.column() < map.line_len(point.row()) {
-            *point.column_mut() += 1;
-        } else if point.row() < map.max_buffer_row() {
-            *point.row_mut() += 1;
-            *point.column_mut() = 0;
-        }
-        point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
-            let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
-            let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
-
-            left_kind != right_kind && left_kind != CharKind::Whitespace
-        });
-
-        // find_boundary clips, so if the character after the next character is a newline or at the end of the document, we know
-        // we have backtracked already
-        if !map
-            .chars_at(point)
-            .nth(1)
-            .map(|(c, _)| c == '\n')
-            .unwrap_or(true)
-        {
-            *point.column_mut() = point.column().saturating_sub(1);
-        }
-        point = map.clip_point(point, Bias::Left);
-    }
-    point
-}
-
-fn previous_word_start(
-    map: &DisplaySnapshot,
-    mut point: DisplayPoint,
-    ignore_punctuation: bool,
-    times: usize,
-) -> DisplayPoint {
-    let scope = map.buffer_snapshot.language_scope_at(point.to_point(map));
-    for _ in 0..times {
-        // This works even though find_preceding_boundary is called for every character in the line containing
-        // cursor because the newline is checked only once.
-        point =
-            movement::find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| {
-                let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
-                let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
-
-                (left_kind != right_kind && !right.is_whitespace()) || left == '\n'
-            });
-    }
-    point
-}
-
-pub(crate) fn first_non_whitespace(
-    map: &DisplaySnapshot,
-    display_lines: bool,
-    from: DisplayPoint,
-) -> DisplayPoint {
-    let mut last_point = start_of_line(map, display_lines, from);
-    let scope = map.buffer_snapshot.language_scope_at(from.to_point(map));
-    for (ch, point) in map.chars_at(last_point) {
-        if ch == '\n' {
-            return from;
-        }
-
-        last_point = point;
-
-        if char_kind(&scope, ch) != CharKind::Whitespace {
-            break;
-        }
-    }
-
-    map.clip_point(last_point, Bias::Left)
-}
-
-pub(crate) fn start_of_line(
-    map: &DisplaySnapshot,
-    display_lines: bool,
-    point: DisplayPoint,
-) -> DisplayPoint {
-    if display_lines {
-        map.clip_point(DisplayPoint::new(point.row(), 0), Bias::Right)
-    } else {
-        map.prev_line_boundary(point.to_point(map)).1
-    }
-}
-
-pub(crate) fn end_of_line(
-    map: &DisplaySnapshot,
-    display_lines: bool,
-    point: DisplayPoint,
-) -> DisplayPoint {
-    if display_lines {
-        map.clip_point(
-            DisplayPoint::new(point.row(), map.line_len(point.row())),
-            Bias::Left,
-        )
-    } else {
-        map.clip_point(map.next_line_boundary(point.to_point(map)).1, Bias::Left)
-    }
-}
-
-fn start_of_document(map: &DisplaySnapshot, point: DisplayPoint, line: usize) -> DisplayPoint {
-    let mut new_point = Point::new((line - 1) as u32, 0).to_display_point(map);
-    *new_point.column_mut() = point.column();
-    map.clip_point(new_point, Bias::Left)
-}
-
-fn end_of_document(
-    map: &DisplaySnapshot,
-    point: DisplayPoint,
-    line: Option<usize>,
-) -> DisplayPoint {
-    let new_row = if let Some(line) = line {
-        (line - 1) as u32
-    } else {
-        map.max_buffer_row()
-    };
-
-    let new_point = Point::new(new_row, point.column());
-    map.clip_point(new_point.to_display_point(map), Bias::Left)
-}
-
-fn matching(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
-    // https://github.com/vim/vim/blob/1d87e11a1ef201b26ed87585fba70182ad0c468a/runtime/doc/motion.txt#L1200
-    let point = display_point.to_point(map);
-    let offset = point.to_offset(&map.buffer_snapshot);
-
-    // Ensure the range is contained by the current line.
-    let mut line_end = map.next_line_boundary(point).0;
-    if line_end == point {
-        line_end = map.max_point().to_point(map);
-    }
-
-    let line_range = map.prev_line_boundary(point).0..line_end;
-    let visible_line_range =
-        line_range.start..Point::new(line_range.end.row, line_range.end.column.saturating_sub(1));
-    let ranges = map
-        .buffer_snapshot
-        .bracket_ranges(visible_line_range.clone());
-    if let Some(ranges) = ranges {
-        let line_range = line_range.start.to_offset(&map.buffer_snapshot)
-            ..line_range.end.to_offset(&map.buffer_snapshot);
-        let mut closest_pair_destination = None;
-        let mut closest_distance = usize::MAX;
-
-        for (open_range, close_range) in ranges {
-            if open_range.start >= offset && line_range.contains(&open_range.start) {
-                let distance = open_range.start - offset;
-                if distance < closest_distance {
-                    closest_pair_destination = Some(close_range.start);
-                    closest_distance = distance;
-                    continue;
-                }
-            }
-
-            if close_range.start >= offset && line_range.contains(&close_range.start) {
-                let distance = close_range.start - offset;
-                if distance < closest_distance {
-                    closest_pair_destination = Some(open_range.start);
-                    closest_distance = distance;
-                    continue;
-                }
-            }
-
-            continue;
-        }
-
-        closest_pair_destination
-            .map(|destination| destination.to_display_point(map))
-            .unwrap_or(display_point)
-    } else {
-        display_point
-    }
-}
-
-fn find_forward(
-    map: &DisplaySnapshot,
-    from: DisplayPoint,
-    before: bool,
-    target: char,
-    times: usize,
-) -> DisplayPoint {
-    let mut to = from;
-    let mut found = false;
-
-    for _ in 0..times {
-        found = false;
-        to = find_boundary(map, to, FindRange::SingleLine, |_, right| {
-            found = right == target;
-            found
-        });
-    }
-
-    if found {
-        if before && to.column() > 0 {
-            *to.column_mut() -= 1;
-            map.clip_point(to, Bias::Left)
-        } else {
-            to
-        }
-    } else {
-        from
-    }
-}
-
-fn find_backward(
-    map: &DisplaySnapshot,
-    from: DisplayPoint,
-    after: bool,
-    target: char,
-    times: usize,
-) -> DisplayPoint {
-    let mut to = from;
-
-    for _ in 0..times {
-        to = find_preceding_boundary(map, to, FindRange::SingleLine, |_, right| right == target);
-    }
-
-    if map.buffer_snapshot.chars_at(to.to_point(map)).next() == Some(target) {
-        if after {
-            *to.column_mut() += 1;
-            map.clip_point(to, Bias::Right)
-        } else {
-            to
-        }
-    } else {
-        from
-    }
-}
-
-fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
-    let correct_line = start_of_relative_buffer_row(map, point, times as isize);
-    first_non_whitespace(map, false, correct_line)
-}
-
-fn go_to_column(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
-    let correct_line = start_of_relative_buffer_row(map, point, 0);
-    right(map, correct_line, times.saturating_sub(1))
-}
-
-pub(crate) fn next_line_end(
-    map: &DisplaySnapshot,
-    mut point: DisplayPoint,
-    times: usize,
-) -> DisplayPoint {
-    if times > 1 {
-        point = start_of_relative_buffer_row(map, point, times as isize - 1);
-    }
-    end_of_line(map, false, point)
-}
-
-#[cfg(test)]
-mod test {
-
-    use crate::test::NeovimBackedTestContext;
-    use indoc::indoc;
-
-    #[gpui::test]
-    async fn test_start_end_of_paragraph(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        let initial_state = indoc! {r"ˇabc
-            def
-
-            paragraph
-            the second
-
-
-
-            third and
-            final"};
-
-        // goes down once
-        cx.set_shared_state(initial_state).await;
-        cx.simulate_shared_keystrokes(["}"]).await;
-        cx.assert_shared_state(indoc! {r"abc
-            def
-            ˇ
-            paragraph
-            the second
-
-
-
-            third and
-            final"})
-            .await;
-
-        // goes up once
-        cx.simulate_shared_keystrokes(["{"]).await;
-        cx.assert_shared_state(initial_state).await;
-
-        // goes down twice
-        cx.simulate_shared_keystrokes(["2", "}"]).await;
-        cx.assert_shared_state(indoc! {r"abc
-            def
-
-            paragraph
-            the second
-            ˇ
-
-
-            third and
-            final"})
-            .await;
-
-        // goes down over multiple blanks
-        cx.simulate_shared_keystrokes(["}"]).await;
-        cx.assert_shared_state(indoc! {r"abc
-                def
-
-                paragraph
-                the second
-
-
-
-                third and
-                finaˇl"})
-            .await;
-
-        // goes up twice
-        cx.simulate_shared_keystrokes(["2", "{"]).await;
-        cx.assert_shared_state(indoc! {r"abc
-                def
-                ˇ
-                paragraph
-                the second
-
-
-
-                third and
-                final"})
-            .await
-    }
-
-    #[gpui::test]
-    async fn test_matching(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {r"func ˇ(a string) {
-                do(something(with<Types>.and_arrays[0, 2]))
-            }"})
-            .await;
-        cx.simulate_shared_keystrokes(["%"]).await;
-        cx.assert_shared_state(indoc! {r"func (a stringˇ) {
-                do(something(with<Types>.and_arrays[0, 2]))
-            }"})
-            .await;
-
-        // test it works on the last character of the line
-        cx.set_shared_state(indoc! {r"func (a string) ˇ{
-            do(something(with<Types>.and_arrays[0, 2]))
-            }"})
-            .await;
-        cx.simulate_shared_keystrokes(["%"]).await;
-        cx.assert_shared_state(indoc! {r"func (a string) {
-            do(something(with<Types>.and_arrays[0, 2]))
-            ˇ}"})
-            .await;
-
-        // test it works on immediate nesting
-        cx.set_shared_state("ˇ{()}").await;
-        cx.simulate_shared_keystrokes(["%"]).await;
-        cx.assert_shared_state("{()ˇ}").await;
-        cx.simulate_shared_keystrokes(["%"]).await;
-        cx.assert_shared_state("ˇ{()}").await;
-
-        // test it works on immediate nesting inside braces
-        cx.set_shared_state("{\n    ˇ{()}\n}").await;
-        cx.simulate_shared_keystrokes(["%"]).await;
-        cx.assert_shared_state("{\n    {()ˇ}\n}").await;
-
-        // test it jumps to the next paren on a line
-        cx.set_shared_state("func ˇboop() {\n}").await;
-        cx.simulate_shared_keystrokes(["%"]).await;
-        cx.assert_shared_state("func boop(ˇ) {\n}").await;
-    }
-
-    #[gpui::test]
-    async fn test_comma_semicolon(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state("ˇone two three four").await;
-        cx.simulate_shared_keystrokes(["f", "o"]).await;
-        cx.assert_shared_state("one twˇo three four").await;
-        cx.simulate_shared_keystrokes([","]).await;
-        cx.assert_shared_state("ˇone two three four").await;
-        cx.simulate_shared_keystrokes(["2", ";"]).await;
-        cx.assert_shared_state("one two three fˇour").await;
-        cx.simulate_shared_keystrokes(["shift-t", "e"]).await;
-        cx.assert_shared_state("one two threeˇ four").await;
-        cx.simulate_shared_keystrokes(["3", ";"]).await;
-        cx.assert_shared_state("oneˇ two three four").await;
-        cx.simulate_shared_keystrokes([","]).await;
-        cx.assert_shared_state("one two thˇree four").await;
-    }
-
-    #[gpui::test]
-    async fn test_next_line_start(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.set_shared_state("ˇone\n  two\nthree").await;
-        cx.simulate_shared_keystrokes(["enter"]).await;
-        cx.assert_shared_state("one\n  ˇtwo\nthree").await;
-    }
-}

crates/vim2/src/normal.rs 🔗

@@ -1,910 +0,0 @@
-mod case;
-mod change;
-mod delete;
-mod increment;
-mod paste;
-pub(crate) mod repeat;
-mod scroll;
-pub(crate) mod search;
-pub mod substitute;
-mod yank;
-
-use std::sync::Arc;
-
-use crate::{
-    motion::{self, first_non_whitespace, next_line_end, right, Motion},
-    object::Object,
-    state::{Mode, Operator},
-    Vim,
-};
-use collections::HashSet;
-use editor::scroll::autoscroll::Autoscroll;
-use editor::{Bias, DisplayPoint};
-use gpui::{actions, ViewContext, WindowContext};
-use language::SelectionGoal;
-use log::error;
-use workspace::Workspace;
-
-use self::{
-    case::change_case,
-    change::{change_motion, change_object},
-    delete::{delete_motion, delete_object},
-    yank::{yank_motion, yank_object},
-};
-
-actions!(
-    vim,
-    [
-        InsertAfter,
-        InsertBefore,
-        InsertFirstNonWhitespace,
-        InsertEndOfLine,
-        InsertLineAbove,
-        InsertLineBelow,
-        DeleteLeft,
-        DeleteRight,
-        ChangeToEndOfLine,
-        DeleteToEndOfLine,
-        Yank,
-        YankLine,
-        ChangeCase,
-        JoinLines,
-    ]
-);
-
-pub(crate) fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
-    workspace.register_action(insert_after);
-    workspace.register_action(insert_before);
-    workspace.register_action(insert_first_non_whitespace);
-    workspace.register_action(insert_end_of_line);
-    workspace.register_action(insert_line_above);
-    workspace.register_action(insert_line_below);
-    workspace.register_action(change_case);
-    workspace.register_action(yank_line);
-
-    workspace.register_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.record_current_action(cx);
-            let times = vim.take_count(cx);
-            delete_motion(vim, Motion::Left, times, cx);
-        })
-    });
-    workspace.register_action(|_: &mut Workspace, _: &DeleteRight, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.record_current_action(cx);
-            let times = vim.take_count(cx);
-            delete_motion(vim, Motion::Right, times, cx);
-        })
-    });
-    workspace.register_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.start_recording(cx);
-            let times = vim.take_count(cx);
-            change_motion(
-                vim,
-                Motion::EndOfLine {
-                    display_lines: false,
-                },
-                times,
-                cx,
-            );
-        })
-    });
-    workspace.register_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.record_current_action(cx);
-            let times = vim.take_count(cx);
-            delete_motion(
-                vim,
-                Motion::EndOfLine {
-                    display_lines: false,
-                },
-                times,
-                cx,
-            );
-        })
-    });
-    workspace.register_action(|_: &mut Workspace, _: &JoinLines, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.record_current_action(cx);
-            let mut times = vim.take_count(cx).unwrap_or(1);
-            if vim.state().mode.is_visual() {
-                times = 1;
-            } else if times > 1 {
-                // 2J joins two lines together (same as J or 1J)
-                times -= 1;
-            }
-
-            vim.update_active_editor(cx, |editor, cx| {
-                editor.transact(cx, |editor, cx| {
-                    for _ in 0..times {
-                        editor.join_lines(&Default::default(), cx)
-                    }
-                })
-            })
-        });
-    });
-
-    paste::register(workspace, cx);
-    repeat::register(workspace, cx);
-    scroll::register(workspace, cx);
-    search::register(workspace, cx);
-    substitute::register(workspace, cx);
-    increment::register(workspace, cx);
-}
-
-pub fn normal_motion(
-    motion: Motion,
-    operator: Option<Operator>,
-    times: Option<usize>,
-    cx: &mut WindowContext,
-) {
-    Vim::update(cx, |vim, cx| {
-        match operator {
-            None => move_cursor(vim, motion, times, cx),
-            Some(Operator::Change) => change_motion(vim, motion, times, cx),
-            Some(Operator::Delete) => delete_motion(vim, motion, times, cx),
-            Some(Operator::Yank) => yank_motion(vim, motion, times, cx),
-            Some(operator) => {
-                // Can't do anything for text objects, Ignoring
-                error!("Unexpected normal mode motion operator: {:?}", operator)
-            }
-        }
-    });
-}
-
-pub fn normal_object(object: Object, cx: &mut WindowContext) {
-    Vim::update(cx, |vim, cx| {
-        match vim.maybe_pop_operator() {
-            Some(Operator::Object { around }) => match vim.maybe_pop_operator() {
-                Some(Operator::Change) => change_object(vim, object, around, cx),
-                Some(Operator::Delete) => delete_object(vim, object, around, cx),
-                Some(Operator::Yank) => yank_object(vim, object, around, cx),
-                _ => {
-                    // Can't do anything for namespace operators. Ignoring
-                }
-            },
-            _ => {
-                // Can't do anything with change/delete/yank and text objects. Ignoring
-            }
-        }
-        vim.clear_operator(cx);
-    })
-}
-
-pub(crate) fn move_cursor(
-    vim: &mut Vim,
-    motion: Motion,
-    times: Option<usize>,
-    cx: &mut WindowContext,
-) {
-    vim.update_active_editor(cx, |editor, cx| {
-        let text_layout_details = editor.text_layout_details(cx);
-        editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-            s.move_cursors_with(|map, cursor, goal| {
-                motion
-                    .move_point(map, cursor, goal, times, &text_layout_details)
-                    .unwrap_or((cursor, goal))
-            })
-        })
-    });
-}
-
-fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.start_recording(cx);
-        vim.switch_mode(Mode::Insert, false, cx);
-        vim.update_active_editor(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_cursors_with(|map, cursor, _| (right(map, cursor, 1), SelectionGoal::None));
-            });
-        });
-    });
-}
-
-fn insert_before(_: &mut Workspace, _: &InsertBefore, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.start_recording(cx);
-        vim.switch_mode(Mode::Insert, false, cx);
-    });
-}
-
-fn insert_first_non_whitespace(
-    _: &mut Workspace,
-    _: &InsertFirstNonWhitespace,
-    cx: &mut ViewContext<Workspace>,
-) {
-    Vim::update(cx, |vim, cx| {
-        vim.start_recording(cx);
-        vim.switch_mode(Mode::Insert, false, cx);
-        vim.update_active_editor(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_cursors_with(|map, cursor, _| {
-                    (
-                        first_non_whitespace(map, false, cursor),
-                        SelectionGoal::None,
-                    )
-                });
-            });
-        });
-    });
-}
-
-fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.start_recording(cx);
-        vim.switch_mode(Mode::Insert, false, cx);
-        vim.update_active_editor(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_cursors_with(|map, cursor, _| {
-                    (next_line_end(map, cursor, 1), SelectionGoal::None)
-                });
-            });
-        });
-    });
-}
-
-fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.start_recording(cx);
-        vim.switch_mode(Mode::Insert, false, cx);
-        vim.update_active_editor(cx, |editor, cx| {
-            editor.transact(cx, |editor, cx| {
-                let (map, old_selections) = editor.selections.all_display(cx);
-                let selection_start_rows: HashSet<u32> = old_selections
-                    .into_iter()
-                    .map(|selection| selection.start.row())
-                    .collect();
-                let edits = selection_start_rows.into_iter().map(|row| {
-                    let (indent, _) = map.line_indent(row);
-                    let start_of_line =
-                        motion::start_of_line(&map, false, DisplayPoint::new(row, 0))
-                            .to_point(&map);
-                    let mut new_text = " ".repeat(indent as usize);
-                    new_text.push('\n');
-                    (start_of_line..start_of_line, new_text)
-                });
-                editor.edit_with_autoindent(edits, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.move_cursors_with(|map, cursor, _| {
-                        let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1);
-                        let insert_point = motion::end_of_line(map, false, previous_line);
-                        (insert_point, SelectionGoal::None)
-                    });
-                });
-            });
-        });
-    });
-}
-
-fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.start_recording(cx);
-        vim.switch_mode(Mode::Insert, false, cx);
-        vim.update_active_editor(cx, |editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
-                let (map, old_selections) = editor.selections.all_display(cx);
-
-                let selection_end_rows: HashSet<u32> = old_selections
-                    .into_iter()
-                    .map(|selection| selection.end.row())
-                    .collect();
-                let edits = selection_end_rows.into_iter().map(|row| {
-                    let (indent, _) = map.line_indent(row);
-                    let end_of_line =
-                        motion::end_of_line(&map, false, DisplayPoint::new(row, 0)).to_point(&map);
-
-                    let mut new_text = "\n".to_string();
-                    new_text.push_str(&" ".repeat(indent as usize));
-                    (end_of_line..end_of_line, new_text)
-                });
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.maybe_move_cursors_with(|map, cursor, goal| {
-                        Motion::CurrentLine.move_point(
-                            map,
-                            cursor,
-                            goal,
-                            None,
-                            &text_layout_details,
-                        )
-                    });
-                });
-                editor.edit_with_autoindent(edits, cx);
-            });
-        });
-    });
-}
-
-fn yank_line(_: &mut Workspace, _: &YankLine, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        let count = vim.take_count(cx);
-        yank_motion(vim, motion::Motion::CurrentLine, count, cx)
-    })
-}
-
-pub(crate) fn normal_replace(text: Arc<str>, cx: &mut WindowContext) {
-    Vim::update(cx, |vim, cx| {
-        vim.stop_recording();
-        vim.update_active_editor(cx, |editor, cx| {
-            editor.transact(cx, |editor, cx| {
-                editor.set_clip_at_line_ends(false, cx);
-                let (map, display_selections) = editor.selections.all_display(cx);
-                // Selections are biased right at the start. So we need to store
-                // anchors that are biased left so that we can restore the selections
-                // after the change
-                let stable_anchors = editor
-                    .selections
-                    .disjoint_anchors()
-                    .into_iter()
-                    .map(|selection| {
-                        let start = selection.start.bias_left(&map.buffer_snapshot);
-                        start..start
-                    })
-                    .collect::<Vec<_>>();
-
-                let edits = display_selections
-                    .into_iter()
-                    .map(|selection| {
-                        let mut range = selection.range();
-                        *range.end.column_mut() += 1;
-                        range.end = map.clip_point(range.end, Bias::Right);
-
-                        (
-                            range.start.to_offset(&map, Bias::Left)
-                                ..range.end.to_offset(&map, Bias::Left),
-                            text.clone(),
-                        )
-                    })
-                    .collect::<Vec<_>>();
-
-                editor.buffer().update(cx, |buffer, cx| {
-                    buffer.edit(edits, None, cx);
-                });
-                editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(None, cx, |s| {
-                    s.select_anchor_ranges(stable_anchors);
-                });
-            });
-        });
-        vim.pop_operator(cx)
-    });
-}
-
-#[cfg(test)]
-mod test {
-    use gpui::TestAppContext;
-    use indoc::indoc;
-
-    use crate::{
-        state::Mode::{self},
-        test::NeovimBackedTestContext,
-    };
-
-    #[gpui::test]
-    async fn test_h(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["h"]);
-        cx.assert_all(indoc! {"
-            ˇThe qˇuick
-            ˇbrown"
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_backspace(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx)
-            .await
-            .binding(["backspace"]);
-        cx.assert_all(indoc! {"
-            ˇThe qˇuick
-            ˇbrown"
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_j(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {"
-                    aaˇaa
-                    😃😃"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["j"]).await;
-        cx.assert_shared_state(indoc! {"
-                    aaaa
-                    😃ˇ😃"
-        })
-        .await;
-
-        for marked_position in cx.each_marked_position(indoc! {"
-                    ˇThe qˇuick broˇwn
-                    ˇfox jumps"
-        }) {
-            cx.assert_neovim_compatible(&marked_position, ["j"]).await;
-        }
-    }
-
-    #[gpui::test]
-    async fn test_enter(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["enter"]);
-        cx.assert_all(indoc! {"
-            ˇThe qˇuick broˇwn
-            ˇfox jumps"
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_k(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["k"]);
-        cx.assert_all(indoc! {"
-            ˇThe qˇuick
-            ˇbrown fˇox jumˇps"
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_l(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["l"]);
-        cx.assert_all(indoc! {"
-            ˇThe qˇuicˇk
-            ˇbrowˇn"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_binding_matches_all(
-            ["$"],
-            indoc! {"
-            ˇThe qˇuicˇk
-            ˇbrowˇn"},
-        )
-        .await;
-        cx.assert_binding_matches_all(
-            ["0"],
-            indoc! {"
-                ˇThe qˇuicˇk
-                ˇbrowˇn"},
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_jump_to_end(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-g"]);
-
-        cx.assert_all(indoc! {"
-                The ˇquick
-
-                brown fox jumps
-                overˇ the lazy doˇg"})
-            .await;
-        cx.assert(indoc! {"
-            The quiˇck
-
-            brown"})
-            .await;
-        cx.assert(indoc! {"
-            The quiˇck
-
-            "})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_w(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["w"]);
-        cx.assert_all(indoc! {"
-            The ˇquickˇ-ˇbrown
-            ˇ
-            ˇ
-            ˇfox_jumps ˇover
-            ˇthˇe"})
-            .await;
-        let mut cx = cx.binding(["shift-w"]);
-        cx.assert_all(indoc! {"
-            The ˇquickˇ-ˇbrown
-            ˇ
-            ˇ
-            ˇfox_jumps ˇover
-            ˇthˇe"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_end_of_word(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["e"]);
-        cx.assert_all(indoc! {"
-            Thˇe quicˇkˇ-browˇn
-
-
-            fox_jumpˇs oveˇr
-            thˇe"})
-            .await;
-        let mut cx = cx.binding(["shift-e"]);
-        cx.assert_all(indoc! {"
-            Thˇe quicˇkˇ-browˇn
-
-
-            fox_jumpˇs oveˇr
-            thˇe"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_b(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["b"]);
-        cx.assert_all(indoc! {"
-            ˇThe ˇquickˇ-ˇbrown
-            ˇ
-            ˇ
-            ˇfox_jumps ˇover
-            ˇthe"})
-            .await;
-        let mut cx = cx.binding(["shift-b"]);
-        cx.assert_all(indoc! {"
-            ˇThe ˇquickˇ-ˇbrown
-            ˇ
-            ˇ
-            ˇfox_jumps ˇover
-            ˇthe"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_gg(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_binding_matches_all(
-            ["g", "g"],
-            indoc! {"
-                The qˇuick
-
-                brown fox jumps
-                over ˇthe laˇzy dog"},
-        )
-        .await;
-        cx.assert_binding_matches(
-            ["g", "g"],
-            indoc! {"
-
-
-                brown fox jumps
-                over the laˇzy dog"},
-        )
-        .await;
-        cx.assert_binding_matches(
-            ["2", "g", "g"],
-            indoc! {"
-                ˇ
-
-                brown fox jumps
-                over the lazydog"},
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_end_of_document(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_binding_matches_all(
-            ["shift-g"],
-            indoc! {"
-                The qˇuick
-
-                brown fox jumps
-                over ˇthe laˇzy dog"},
-        )
-        .await;
-        cx.assert_binding_matches(
-            ["shift-g"],
-            indoc! {"
-
-
-                brown fox jumps
-                over the laˇzy dog"},
-        )
-        .await;
-        cx.assert_binding_matches(
-            ["2", "shift-g"],
-            indoc! {"
-                ˇ
-
-                brown fox jumps
-                over the lazydog"},
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_a(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["a"]);
-        cx.assert_all("The qˇuicˇk").await;
-    }
-
-    #[gpui::test]
-    async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-a"]);
-        cx.assert_all(indoc! {"
-            ˇ
-            The qˇuick
-            brown ˇfox "})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["^"]);
-        cx.assert("The qˇuick").await;
-        cx.assert(" The qˇuick").await;
-        cx.assert("ˇ").await;
-        cx.assert(indoc! {"
-                The qˇuick
-                brown fox"})
-            .await;
-        cx.assert(indoc! {"
-                ˇ
-                The quick"})
-            .await;
-        // Indoc disallows trailing whitespace.
-        cx.assert("   ˇ \nThe quick").await;
-    }
-
-    #[gpui::test]
-    async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-i"]);
-        cx.assert("The qˇuick").await;
-        cx.assert(" The qˇuick").await;
-        cx.assert("ˇ").await;
-        cx.assert(indoc! {"
-                The qˇuick
-                brown fox"})
-            .await;
-        cx.assert(indoc! {"
-                ˇ
-                The quick"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-d"]);
-        cx.assert(indoc! {"
-                The qˇuick
-                brown fox"})
-            .await;
-        cx.assert(indoc! {"
-                The quick
-                ˇ
-                brown fox"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_x(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["x"]);
-        cx.assert_all("ˇTeˇsˇt").await;
-        cx.assert(indoc! {"
-                Tesˇt
-                test"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_left(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-x"]);
-        cx.assert_all("ˇTˇeˇsˇt").await;
-        cx.assert(indoc! {"
-                Test
-                ˇtest"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_o(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["o"]);
-        cx.assert("ˇ").await;
-        cx.assert("The ˇquick").await;
-        cx.assert_all(indoc! {"
-                The qˇuick
-                brown ˇfox
-                jumps ˇover"})
-            .await;
-        cx.assert(indoc! {"
-                The quick
-                ˇ
-                brown fox"})
-            .await;
-
-        cx.assert_manual(
-            indoc! {"
-                fn test() {
-                    println!(ˇ);
-                }"},
-            Mode::Normal,
-            indoc! {"
-                fn test() {
-                    println!();
-                    ˇ
-                }"},
-            Mode::Insert,
-        );
-
-        cx.assert_manual(
-            indoc! {"
-                fn test(ˇ) {
-                    println!();
-                }"},
-            Mode::Normal,
-            indoc! {"
-                fn test() {
-                    ˇ
-                    println!();
-                }"},
-            Mode::Insert,
-        );
-    }
-
-    #[gpui::test]
-    async fn test_insert_line_above(cx: &mut gpui::TestAppContext) {
-        let cx = NeovimBackedTestContext::new(cx).await;
-        let mut cx = cx.binding(["shift-o"]);
-        cx.assert("ˇ").await;
-        cx.assert("The ˇquick").await;
-        cx.assert_all(indoc! {"
-            The qˇuick
-            brown ˇfox
-            jumps ˇover"})
-            .await;
-        cx.assert(indoc! {"
-            The quick
-            ˇ
-            brown fox"})
-            .await;
-
-        // Our indentation is smarter than vims. So we don't match here
-        cx.assert_manual(
-            indoc! {"
-                fn test() {
-                    println!(ˇ);
-                }"},
-            Mode::Normal,
-            indoc! {"
-                fn test() {
-                    ˇ
-                    println!();
-                }"},
-            Mode::Insert,
-        );
-        cx.assert_manual(
-            indoc! {"
-                fn test(ˇ) {
-                    println!();
-                }"},
-            Mode::Normal,
-            indoc! {"
-                ˇ
-                fn test() {
-                    println!();
-                }"},
-            Mode::Insert,
-        );
-    }
-
-    #[gpui::test]
-    async fn test_dd(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_neovim_compatible("ˇ", ["d", "d"]).await;
-        cx.assert_neovim_compatible("The ˇquick", ["d", "d"]).await;
-        for marked_text in cx.each_marked_position(indoc! {"
-            The qˇuick
-            brown ˇfox
-            jumps ˇover"})
-        {
-            cx.assert_neovim_compatible(&marked_text, ["d", "d"]).await;
-        }
-        cx.assert_neovim_compatible(
-            indoc! {"
-                The quick
-                ˇ
-                brown fox"},
-            ["d", "d"],
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_cc(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "c"]);
-        cx.assert("ˇ").await;
-        cx.assert("The ˇquick").await;
-        cx.assert_all(indoc! {"
-                The quˇick
-                brown ˇfox
-                jumps ˇover"})
-            .await;
-        cx.assert(indoc! {"
-                The quick
-                ˇ
-                brown fox"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_repeated_word(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        for count in 1..=5 {
-            cx.assert_binding_matches_all(
-                [&count.to_string(), "w"],
-                indoc! {"
-                    ˇThe quˇickˇ browˇn
-                    ˇ
-                    ˇfox ˇjumpsˇ-ˇoˇver
-                    ˇthe lazy dog
-                "},
-            )
-            .await;
-        }
-    }
-
-    #[gpui::test]
-    async fn test_h_through_unicode(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["h"]);
-        cx.assert_all("Testˇ├ˇ──ˇ┐ˇTest").await;
-    }
-
-    #[gpui::test]
-    async fn test_f_and_t(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        for count in 1..=3 {
-            let test_case = indoc! {"
-                ˇaaaˇbˇ ˇbˇ   ˇbˇbˇ aˇaaˇbaaa
-                ˇ    ˇbˇaaˇa ˇbˇbˇb
-                ˇ
-                ˇb
-            "};
-
-            cx.assert_binding_matches_all([&count.to_string(), "f", "b"], test_case)
-                .await;
-
-            cx.assert_binding_matches_all([&count.to_string(), "t", "b"], test_case)
-                .await;
-        }
-    }
-
-    #[gpui::test]
-    async fn test_capital_f_and_capital_t(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        let test_case = indoc! {"
-            ˇaaaˇbˇ ˇbˇ   ˇbˇbˇ aˇaaˇbaaa
-            ˇ    ˇbˇaaˇa ˇbˇbˇb
-            ˇ•••
-            ˇb
-            "
-        };
-
-        for count in 1..=3 {
-            cx.assert_binding_matches_all([&count.to_string(), "shift-f", "b"], test_case)
-                .await;
-
-            cx.assert_binding_matches_all([&count.to_string(), "shift-t", "b"], test_case)
-                .await;
-        }
-    }
-
-    #[gpui::test]
-    async fn test_percent(cx: &mut TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["%"]);
-        cx.assert_all("ˇconsole.logˇ(ˇvaˇrˇ)ˇ;").await;
-        cx.assert_all("ˇconsole.logˇ(ˇ'var', ˇ[ˇ1, ˇ2, 3ˇ]ˇ)ˇ;")
-            .await;
-        cx.assert_all("let result = curried_funˇ(ˇ)ˇ(ˇ)ˇ;").await;
-    }
-}

crates/vim2/src/normal/case.rs 🔗

@@ -1,116 +0,0 @@
-use editor::scroll::autoscroll::Autoscroll;
-use gpui::ViewContext;
-use language::{Bias, Point};
-use workspace::Workspace;
-
-use crate::{normal::ChangeCase, state::Mode, Vim};
-
-pub fn change_case(_: &mut Workspace, _: &ChangeCase, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.record_current_action(cx);
-        let count = vim.take_count(cx).unwrap_or(1) as u32;
-        vim.update_active_editor(cx, |editor, cx| {
-            let mut ranges = Vec::new();
-            let mut cursor_positions = Vec::new();
-            let snapshot = editor.buffer().read(cx).snapshot(cx);
-            for selection in editor.selections.all::<Point>(cx) {
-                match vim.state().mode {
-                    Mode::VisualLine => {
-                        let start = Point::new(selection.start.row, 0);
-                        let end =
-                            Point::new(selection.end.row, snapshot.line_len(selection.end.row));
-                        ranges.push(start..end);
-                        cursor_positions.push(start..start);
-                    }
-                    Mode::Visual => {
-                        ranges.push(selection.start..selection.end);
-                        cursor_positions.push(selection.start..selection.start);
-                    }
-                    Mode::VisualBlock => {
-                        ranges.push(selection.start..selection.end);
-                        if cursor_positions.len() == 0 {
-                            cursor_positions.push(selection.start..selection.start);
-                        }
-                    }
-                    Mode::Insert | Mode::Normal => {
-                        let start = selection.start;
-                        let mut end = start;
-                        for _ in 0..count {
-                            end = snapshot.clip_point(end + Point::new(0, 1), Bias::Right);
-                        }
-                        ranges.push(start..end);
-
-                        if end.column == snapshot.line_len(end.row) {
-                            end = snapshot.clip_point(end - Point::new(0, 1), Bias::Left);
-                        }
-                        cursor_positions.push(end..end)
-                    }
-                }
-            }
-            editor.transact(cx, |editor, cx| {
-                for range in ranges.into_iter().rev() {
-                    let snapshot = editor.buffer().read(cx).snapshot(cx);
-                    editor.buffer().update(cx, |buffer, cx| {
-                        let text = snapshot
-                            .text_for_range(range.start..range.end)
-                            .flat_map(|s| s.chars())
-                            .flat_map(|c| {
-                                if c.is_lowercase() {
-                                    c.to_uppercase().collect::<Vec<char>>()
-                                } else {
-                                    c.to_lowercase().collect::<Vec<char>>()
-                                }
-                            })
-                            .collect::<String>();
-
-                        buffer.edit([(range, text)], None, cx)
-                    })
-                }
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.select_ranges(cursor_positions)
-                })
-            });
-        });
-        vim.switch_mode(Mode::Normal, true, cx)
-    })
-}
-#[cfg(test)]
-mod test {
-    use crate::{state::Mode, test::NeovimBackedTestContext};
-
-    #[gpui::test]
-    async fn test_change_case(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.set_shared_state("ˇabC\n").await;
-        cx.simulate_shared_keystrokes(["~"]).await;
-        cx.assert_shared_state("AˇbC\n").await;
-        cx.simulate_shared_keystrokes(["2", "~"]).await;
-        cx.assert_shared_state("ABˇc\n").await;
-
-        // works in visual mode
-        cx.set_shared_state("a😀C«dÉ1*fˇ»\n").await;
-        cx.simulate_shared_keystrokes(["~"]).await;
-        cx.assert_shared_state("a😀CˇDé1*F\n").await;
-
-        // works with multibyte characters
-        cx.simulate_shared_keystrokes(["~"]).await;
-        cx.set_shared_state("aˇC😀é1*F\n").await;
-        cx.simulate_shared_keystrokes(["4", "~"]).await;
-        cx.assert_shared_state("ac😀É1ˇ*F\n").await;
-
-        // works with line selections
-        cx.set_shared_state("abˇC\n").await;
-        cx.simulate_shared_keystrokes(["shift-v", "~"]).await;
-        cx.assert_shared_state("ˇABc\n").await;
-
-        // works in visual block mode
-        cx.set_shared_state("ˇaa\nbb\ncc").await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "j", "~"]).await;
-        cx.assert_shared_state("ˇAa\nBb\ncc").await;
-
-        // works with multiple cursors (zed only)
-        cx.set_state("aˇßcdˇe\n", Mode::Normal);
-        cx.simulate_keystroke("~");
-        cx.assert_state("aSSˇcdˇE\n", Mode::Normal);
-    }
-}

crates/vim2/src/normal/change.rs 🔗

@@ -1,502 +0,0 @@
-use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
-use editor::{
-    char_kind,
-    display_map::DisplaySnapshot,
-    movement::{self, FindRange, TextLayoutDetails},
-    scroll::autoscroll::Autoscroll,
-    CharKind, DisplayPoint,
-};
-use gpui::WindowContext;
-use language::Selection;
-
-pub fn change_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
-    // Some motions ignore failure when switching to normal mode
-    let mut motion_succeeded = matches!(
-        motion,
-        Motion::Left
-            | Motion::Right
-            | Motion::EndOfLine { .. }
-            | Motion::Backspace
-            | Motion::StartOfLine { .. }
-    );
-    vim.update_active_editor(cx, |editor, cx| {
-        let text_layout_details = editor.text_layout_details(cx);
-        editor.transact(cx, |editor, cx| {
-            // We are swapping to insert mode anyway. Just set the line end clipping behavior now
-            editor.set_clip_at_line_ends(false, cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_with(|map, selection| {
-                    motion_succeeded |= if let Motion::NextWordStart { ignore_punctuation } = motion
-                    {
-                        expand_changed_word_selection(
-                            map,
-                            selection,
-                            times,
-                            ignore_punctuation,
-                            &text_layout_details,
-                        )
-                    } else {
-                        motion.expand_selection(map, selection, times, false, &text_layout_details)
-                    };
-                });
-            });
-            copy_selections_content(editor, motion.linewise(), cx);
-            editor.insert("", cx);
-        });
-    });
-
-    if motion_succeeded {
-        vim.switch_mode(Mode::Insert, false, cx)
-    } else {
-        vim.switch_mode(Mode::Normal, false, cx)
-    }
-}
-
-pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut WindowContext) {
-    let mut objects_found = false;
-    vim.update_active_editor(cx, |editor, cx| {
-        // We are swapping to insert mode anyway. Just set the line end clipping behavior now
-        editor.set_clip_at_line_ends(false, cx);
-        editor.transact(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_with(|map, selection| {
-                    objects_found |= object.expand_selection(map, selection, around);
-                });
-            });
-            if objects_found {
-                copy_selections_content(editor, false, cx);
-                editor.insert("", cx);
-            }
-        });
-    });
-
-    if objects_found {
-        vim.switch_mode(Mode::Insert, false, cx);
-    } else {
-        vim.switch_mode(Mode::Normal, false, cx);
-    }
-}
-
-// From the docs https://vimdoc.sourceforge.net/htmldoc/motion.html
-// Special case: "cw" and "cW" are treated like "ce" and "cE" if the cursor is
-// on a non-blank.  This is because "cw" is interpreted as change-word, and a
-// word does not include the following white space.  {Vi: "cw" when on a blank
-//     followed by other blanks changes only the first blank; this is probably a
-//     bug, because "dw" deletes all the blanks}
-fn expand_changed_word_selection(
-    map: &DisplaySnapshot,
-    selection: &mut Selection<DisplayPoint>,
-    times: Option<usize>,
-    ignore_punctuation: bool,
-    text_layout_details: &TextLayoutDetails,
-) -> bool {
-    if times.is_none() || times.unwrap() == 1 {
-        let scope = map
-            .buffer_snapshot
-            .language_scope_at(selection.start.to_point(map));
-        let in_word = map
-            .chars_at(selection.head())
-            .next()
-            .map(|(c, _)| char_kind(&scope, c) != CharKind::Whitespace)
-            .unwrap_or_default();
-
-        if in_word {
-            selection.end =
-                movement::find_boundary(map, selection.end, FindRange::MultiLine, |left, right| {
-                    let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
-                    let right_kind =
-                        char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
-
-                    left_kind != right_kind && left_kind != CharKind::Whitespace
-                });
-            true
-        } else {
-            Motion::NextWordStart { ignore_punctuation }.expand_selection(
-                map,
-                selection,
-                None,
-                false,
-                &text_layout_details,
-            )
-        }
-    } else {
-        Motion::NextWordStart { ignore_punctuation }.expand_selection(
-            map,
-            selection,
-            times,
-            false,
-            &text_layout_details,
-        )
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use indoc::indoc;
-
-    use crate::test::NeovimBackedTestContext;
-
-    #[gpui::test]
-    async fn test_change_h(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "h"]);
-        cx.assert("Teˇst").await;
-        cx.assert("Tˇest").await;
-        cx.assert("ˇTest").await;
-        cx.assert(indoc! {"
-            Test
-            ˇtest"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_change_backspace(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx)
-            .await
-            .binding(["c", "backspace"]);
-        cx.assert("Teˇst").await;
-        cx.assert("Tˇest").await;
-        cx.assert("ˇTest").await;
-        cx.assert(indoc! {"
-            Test
-            ˇtest"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_change_l(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "l"]);
-        cx.assert("Teˇst").await;
-        cx.assert("Tesˇt").await;
-    }
-
-    #[gpui::test]
-    async fn test_change_w(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "w"]);
-        cx.assert("Teˇst").await;
-        cx.assert("Tˇest test").await;
-        cx.assert("Testˇ  test").await;
-        cx.assert(indoc! {"
-                Test teˇst
-                test"})
-            .await;
-        cx.assert(indoc! {"
-                Test tesˇt
-                test"})
-            .await;
-        cx.assert(indoc! {"
-                Test test
-                ˇ
-                test"})
-            .await;
-
-        let mut cx = cx.binding(["c", "shift-w"]);
-        cx.assert("Test teˇst-test test").await;
-    }
-
-    #[gpui::test]
-    async fn test_change_e(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "e"]);
-        cx.assert("Teˇst Test").await;
-        cx.assert("Tˇest test").await;
-        cx.assert(indoc! {"
-                Test teˇst
-                test"})
-            .await;
-        cx.assert(indoc! {"
-                Test tesˇt
-                test"})
-            .await;
-        cx.assert(indoc! {"
-                Test test
-                ˇ
-                test"})
-            .await;
-
-        let mut cx = cx.binding(["c", "shift-e"]);
-        cx.assert("Test teˇst-test test").await;
-    }
-
-    #[gpui::test]
-    async fn test_change_b(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "b"]);
-        cx.assert("Teˇst Test").await;
-        cx.assert("Test ˇtest").await;
-        cx.assert("Test1 test2 ˇtest3").await;
-        cx.assert(indoc! {"
-                Test test
-                ˇtest"})
-            .await;
-        cx.assert(indoc! {"
-                Test test
-                ˇ
-                test"})
-            .await;
-
-        let mut cx = cx.binding(["c", "shift-b"]);
-        cx.assert("Test test-test ˇtest").await;
-    }
-
-    #[gpui::test]
-    async fn test_change_end_of_line(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "$"]);
-        cx.assert(indoc! {"
-            The qˇuick
-            brown fox"})
-            .await;
-        cx.assert(indoc! {"
-            The quick
-            ˇ
-            brown fox"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_change_0(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The qˇuick
-            brown fox"},
-            ["c", "0"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            ˇ
-            brown fox"},
-            ["c", "0"],
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_change_k(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown ˇfox
-            jumps over"},
-            ["c", "k"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown fox
-            jumps ˇover"},
-            ["c", "k"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The qˇuick
-            brown fox
-            jumps over"},
-            ["c", "k"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            ˇ
-            brown fox
-            jumps over"},
-            ["c", "k"],
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_change_j(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown ˇfox
-            jumps over"},
-            ["c", "j"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown fox
-            jumps ˇover"},
-            ["c", "j"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The qˇuick
-            brown fox
-            jumps over"},
-            ["c", "j"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown fox
-            ˇ"},
-            ["c", "j"],
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_change_end_of_document(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brownˇ fox
-            jumps over
-            the lazy"},
-            ["c", "shift-g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brownˇ fox
-            jumps over
-            the lazy"},
-            ["c", "shift-g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown fox
-            jumps over
-            the lˇazy"},
-            ["c", "shift-g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown fox
-            jumps over
-            ˇ"},
-            ["c", "shift-g"],
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_change_gg(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brownˇ fox
-            jumps over
-            the lazy"},
-            ["c", "g", "g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown fox
-            jumps over
-            the lˇazy"},
-            ["c", "g", "g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The qˇuick
-            brown fox
-            jumps over
-            the lazy"},
-            ["c", "g", "g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            ˇ
-            brown fox
-            jumps over
-            the lazy"},
-            ["c", "g", "g"],
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_repeated_cj(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        for count in 1..=5 {
-            cx.assert_binding_matches_all(
-                ["c", &count.to_string(), "j"],
-                indoc! {"
-                    ˇThe quˇickˇ browˇn
-                    ˇ
-                    ˇfox ˇjumpsˇ-ˇoˇver
-                    ˇthe lazy dog
-                    "},
-            )
-            .await;
-        }
-    }
-
-    #[gpui::test]
-    async fn test_repeated_cl(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        for count in 1..=5 {
-            cx.assert_binding_matches_all(
-                ["c", &count.to_string(), "l"],
-                indoc! {"
-                    ˇThe quˇickˇ browˇn
-                    ˇ
-                    ˇfox ˇjumpsˇ-ˇoˇver
-                    ˇthe lazy dog
-                    "},
-            )
-            .await;
-        }
-    }
-
-    #[gpui::test]
-    async fn test_repeated_cb(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        for count in 1..=5 {
-            for marked_text in cx.each_marked_position(indoc! {"
-                ˇThe quˇickˇ browˇn
-                ˇ
-                ˇfox ˇjumpsˇ-ˇoˇver
-                ˇthe lazy dog
-                "})
-            {
-                cx.assert_neovim_compatible(&marked_text, ["c", &count.to_string(), "b"])
-                    .await;
-            }
-        }
-    }
-
-    #[gpui::test]
-    async fn test_repeated_ce(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        for count in 1..=5 {
-            cx.assert_binding_matches_all(
-                ["c", &count.to_string(), "e"],
-                indoc! {"
-                    ˇThe quˇickˇ browˇn
-                    ˇ
-                    ˇfox ˇjumpsˇ-ˇoˇver
-                    ˇthe lazy dog
-                    "},
-            )
-            .await;
-        }
-    }
-}

crates/vim2/src/normal/delete.rs 🔗

@@ -1,475 +0,0 @@
-use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim};
-use collections::{HashMap, HashSet};
-use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Bias};
-use gpui::WindowContext;
-use language::Point;
-
-pub fn delete_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
-    vim.stop_recording();
-    vim.update_active_editor(cx, |editor, cx| {
-        let text_layout_details = editor.text_layout_details(cx);
-        editor.transact(cx, |editor, cx| {
-            editor.set_clip_at_line_ends(false, cx);
-            let mut original_columns: HashMap<_, _> = Default::default();
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_with(|map, selection| {
-                    let original_head = selection.head();
-                    original_columns.insert(selection.id, original_head.column());
-                    motion.expand_selection(map, selection, times, true, &text_layout_details);
-
-                    // Motion::NextWordStart on an empty line should delete it.
-                    if let Motion::NextWordStart {
-                        ignore_punctuation: _,
-                    } = motion
-                    {
-                        if selection.is_empty()
-                            && map
-                                .buffer_snapshot
-                                .line_len(selection.start.to_point(&map).row)
-                                == 0
-                        {
-                            selection.end = map
-                                .buffer_snapshot
-                                .clip_point(
-                                    Point::new(selection.start.to_point(&map).row + 1, 0),
-                                    Bias::Left,
-                                )
-                                .to_display_point(map)
-                        }
-                    }
-                });
-            });
-            copy_selections_content(editor, motion.linewise(), cx);
-            editor.insert("", cx);
-
-            // Fixup cursor position after the deletion
-            editor.set_clip_at_line_ends(true, cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_with(|map, selection| {
-                    let mut cursor = selection.head();
-                    if motion.linewise() {
-                        if let Some(column) = original_columns.get(&selection.id) {
-                            *cursor.column_mut() = *column
-                        }
-                    }
-                    cursor = map.clip_point(cursor, Bias::Left);
-                    selection.collapse_to(cursor, selection.goal)
-                });
-            });
-        });
-    });
-}
-
-pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut WindowContext) {
-    vim.stop_recording();
-    vim.update_active_editor(cx, |editor, cx| {
-        editor.transact(cx, |editor, cx| {
-            editor.set_clip_at_line_ends(false, cx);
-            // Emulates behavior in vim where if we expanded backwards to include a newline
-            // the cursor gets set back to the start of the line
-            let mut should_move_to_start: HashSet<_> = Default::default();
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_with(|map, selection| {
-                    object.expand_selection(map, selection, around);
-                    let offset_range = selection.map(|p| p.to_offset(map, Bias::Left)).range();
-                    let contains_only_newlines = map
-                        .chars_at(selection.start)
-                        .take_while(|(_, p)| p < &selection.end)
-                        .all(|(char, _)| char == '\n')
-                        && !offset_range.is_empty();
-                    let end_at_newline = map
-                        .chars_at(selection.end)
-                        .next()
-                        .map(|(c, _)| c == '\n')
-                        .unwrap_or(false);
-
-                    // If expanded range contains only newlines and
-                    // the object is around or sentence, expand to include a newline
-                    // at the end or start
-                    if (around || object == Object::Sentence) && contains_only_newlines {
-                        if end_at_newline {
-                            selection.end =
-                                (offset_range.end + '\n'.len_utf8()).to_display_point(map);
-                        } else if selection.start.row() > 0 {
-                            should_move_to_start.insert(selection.id);
-                            selection.start =
-                                (offset_range.start - '\n'.len_utf8()).to_display_point(map);
-                        }
-                    }
-                });
-            });
-            copy_selections_content(editor, false, cx);
-            editor.insert("", cx);
-
-            // Fixup cursor position after the deletion
-            editor.set_clip_at_line_ends(true, cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.move_with(|map, selection| {
-                    let mut cursor = selection.head();
-                    if should_move_to_start.contains(&selection.id) {
-                        *cursor.column_mut() = 0;
-                    }
-                    cursor = map.clip_point(cursor, Bias::Left);
-                    selection.collapse_to(cursor, selection.goal)
-                });
-            });
-        });
-    });
-}
-
-#[cfg(test)]
-mod test {
-    use indoc::indoc;
-
-    use crate::{
-        state::Mode,
-        test::{ExemptionFeatures, NeovimBackedTestContext, VimTestContext},
-    };
-
-    #[gpui::test]
-    async fn test_delete_h(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "h"]);
-        cx.assert("Teˇst").await;
-        cx.assert("Tˇest").await;
-        cx.assert("ˇTest").await;
-        cx.assert(indoc! {"
-            Test
-            ˇtest"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_l(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "l"]);
-        cx.assert("ˇTest").await;
-        cx.assert("Teˇst").await;
-        cx.assert("Tesˇt").await;
-        cx.assert(indoc! {"
-                Tesˇt
-                test"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_w(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            Test tesˇt
-                test"},
-            ["d", "w"],
-        )
-        .await;
-
-        cx.assert_neovim_compatible("Teˇst", ["d", "w"]).await;
-        cx.assert_neovim_compatible("Tˇest test", ["d", "w"]).await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            Test teˇst
-            test"},
-            ["d", "w"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            Test tesˇt
-            test"},
-            ["d", "w"],
-        )
-        .await;
-
-        cx.assert_neovim_compatible(
-            indoc! {"
-            Test test
-            ˇ
-            test"},
-            ["d", "w"],
-        )
-        .await;
-
-        let mut cx = cx.binding(["d", "shift-w"]);
-        cx.assert_neovim_compatible("Test teˇst-test test", ["d", "shift-w"])
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_next_word_end(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "e"]);
-        // cx.assert("Teˇst Test").await;
-        // cx.assert("Tˇest test").await;
-        cx.assert(indoc! {"
-            Test teˇst
-            test"})
-            .await;
-        cx.assert(indoc! {"
-            Test tesˇt
-            test"})
-            .await;
-        cx.assert_exempted(
-            indoc! {"
-            Test test
-            ˇ
-            test"},
-            ExemptionFeatures::OperatorLastNewlineRemains,
-        )
-        .await;
-
-        let mut cx = cx.binding(["d", "shift-e"]);
-        cx.assert("Test teˇst-test test").await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_b(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "b"]);
-        cx.assert("Teˇst Test").await;
-        cx.assert("Test ˇtest").await;
-        cx.assert("Test1 test2 ˇtest3").await;
-        cx.assert(indoc! {"
-            Test test
-            ˇtest"})
-            .await;
-        cx.assert(indoc! {"
-            Test test
-            ˇ
-            test"})
-            .await;
-
-        let mut cx = cx.binding(["d", "shift-b"]);
-        cx.assert("Test test-test ˇtest").await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_end_of_line(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "$"]);
-        cx.assert(indoc! {"
-            The qˇuick
-            brown fox"})
-            .await;
-        cx.assert(indoc! {"
-            The quick
-            ˇ
-            brown fox"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_0(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "0"]);
-        cx.assert(indoc! {"
-            The qˇuick
-            brown fox"})
-            .await;
-        cx.assert(indoc! {"
-            The quick
-            ˇ
-            brown fox"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_k(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "k"]);
-        cx.assert(indoc! {"
-            The quick
-            brown ˇfox
-            jumps over"})
-            .await;
-        cx.assert(indoc! {"
-            The quick
-            brown fox
-            jumps ˇover"})
-            .await;
-        cx.assert(indoc! {"
-            The qˇuick
-            brown fox
-            jumps over"})
-            .await;
-        cx.assert(indoc! {"
-            ˇbrown fox
-            jumps over"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_j(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "j"]);
-        cx.assert(indoc! {"
-            The quick
-            brown ˇfox
-            jumps over"})
-            .await;
-        cx.assert(indoc! {"
-            The quick
-            brown fox
-            jumps ˇover"})
-            .await;
-        cx.assert(indoc! {"
-            The qˇuick
-            brown fox
-            jumps over"})
-            .await;
-        cx.assert(indoc! {"
-            The quick
-            brown fox
-            ˇ"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_end_of_document(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brownˇ fox
-            jumps over
-            the lazy"},
-            ["d", "shift-g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brownˇ fox
-            jumps over
-            the lazy"},
-            ["d", "shift-g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown fox
-            jumps over
-            the lˇazy"},
-            ["d", "shift-g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown fox
-            jumps over
-            ˇ"},
-            ["d", "shift-g"],
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_gg(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx)
-            .await
-            .binding(["d", "g", "g"]);
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brownˇ fox
-            jumps over
-            the lazy"},
-            ["d", "g", "g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The quick
-            brown fox
-            jumps over
-            the lˇazy"},
-            ["d", "g", "g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            The qˇuick
-            brown fox
-            jumps over
-            the lazy"},
-            ["d", "g", "g"],
-        )
-        .await;
-        cx.assert_neovim_compatible(
-            indoc! {"
-            ˇ
-            brown fox
-            jumps over
-            the lazy"},
-            ["d", "g", "g"],
-        )
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_cancel_delete_operator(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-        cx.set_state(
-            indoc! {"
-                The quick brown
-                fox juˇmps over
-                the lazy dog"},
-            Mode::Normal,
-        );
-
-        // Canceling operator twice reverts to normal mode with no active operator
-        cx.simulate_keystrokes(["d", "escape", "k"]);
-        assert_eq!(cx.active_operator(), None);
-        assert_eq!(cx.mode(), Mode::Normal);
-        cx.assert_editor_state(indoc! {"
-            The quˇick brown
-            fox jumps over
-            the lazy dog"});
-    }
-
-    #[gpui::test]
-    async fn test_unbound_command_cancels_pending_operator(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-        cx.set_state(
-            indoc! {"
-                The quick brown
-                fox juˇmps over
-                the lazy dog"},
-            Mode::Normal,
-        );
-
-        // Canceling operator twice reverts to normal mode with no active operator
-        cx.simulate_keystrokes(["d", "y"]);
-        assert_eq!(cx.active_operator(), None);
-        assert_eq!(cx.mode(), Mode::Normal);
-    }
-
-    #[gpui::test]
-    async fn test_delete_with_counts(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.set_shared_state(indoc! {"
-                The ˇquick brown
-                fox jumps over
-                the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["d", "2", "d"]).await;
-        cx.assert_shared_state(indoc! {"
-        the ˇlazy dog"})
-            .await;
-
-        cx.set_shared_state(indoc! {"
-                The ˇquick brown
-                fox jumps over
-                the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["2", "d", "d"]).await;
-        cx.assert_shared_state(indoc! {"
-        the ˇlazy dog"})
-            .await;
-
-        cx.set_shared_state(indoc! {"
-                The ˇquick brown
-                fox jumps over
-                the moon,
-                a star, and
-                the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["2", "d", "2", "d"]).await;
-        cx.assert_shared_state(indoc! {"
-        the ˇlazy dog"})
-            .await;
-    }
-}

crates/vim2/src/normal/increment.rs 🔗

@@ -1,278 +0,0 @@
-use std::ops::Range;
-
-use editor::{scroll::autoscroll::Autoscroll, MultiBufferSnapshot, ToOffset, ToPoint};
-use gpui::{impl_actions, ViewContext, WindowContext};
-use language::{Bias, Point};
-use serde::Deserialize;
-use workspace::Workspace;
-
-use crate::{state::Mode, Vim};
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct Increment {
-    #[serde(default)]
-    step: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct Decrement {
-    #[serde(default)]
-    step: bool,
-}
-
-impl_actions!(vim, [Increment, Decrement]);
-
-pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(|_: &mut Workspace, action: &Increment, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.record_current_action(cx);
-            let count = vim.take_count(cx).unwrap_or(1);
-            let step = if action.step { 1 } else { 0 };
-            increment(vim, count as i32, step, cx)
-        })
-    });
-    workspace.register_action(|_: &mut Workspace, action: &Decrement, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.record_current_action(cx);
-            let count = vim.take_count(cx).unwrap_or(1);
-            let step = if action.step { -1 } else { 0 };
-            increment(vim, count as i32 * -1, step, cx)
-        })
-    });
-}
-
-fn increment(vim: &mut Vim, mut delta: i32, step: i32, cx: &mut WindowContext) {
-    vim.update_active_editor(cx, |editor, cx| {
-        let mut edits = Vec::new();
-        let mut new_anchors = Vec::new();
-
-        let snapshot = editor.buffer().read(cx).snapshot(cx);
-        for selection in editor.selections.all_adjusted(cx) {
-            if !selection.is_empty() {
-                if vim.state().mode != Mode::VisualBlock || new_anchors.is_empty() {
-                    new_anchors.push((true, snapshot.anchor_before(selection.start)))
-                }
-            }
-            for row in selection.start.row..=selection.end.row {
-                let start = if row == selection.start.row {
-                    selection.start
-                } else {
-                    Point::new(row, 0)
-                };
-
-                if let Some((range, num, radix)) = find_number(&snapshot, start) {
-                    if let Ok(val) = i32::from_str_radix(&num, radix) {
-                        let result = val + delta;
-                        delta += step;
-                        let replace = match radix {
-                            10 => format!("{}", result),
-                            16 => {
-                                if num.to_ascii_lowercase() == num {
-                                    format!("{:x}", result)
-                                } else {
-                                    format!("{:X}", result)
-                                }
-                            }
-                            2 => format!("{:b}", result),
-                            _ => unreachable!(),
-                        };
-                        edits.push((range.clone(), replace));
-                    }
-                    if selection.is_empty() {
-                        new_anchors.push((false, snapshot.anchor_after(range.end)))
-                    }
-                } else {
-                    if selection.is_empty() {
-                        new_anchors.push((true, snapshot.anchor_after(start)))
-                    }
-                }
-            }
-        }
-        editor.transact(cx, |editor, cx| {
-            editor.edit(edits, cx);
-
-            let snapshot = editor.buffer().read(cx).snapshot(cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                let mut new_ranges = Vec::new();
-                for (visual, anchor) in new_anchors.iter() {
-                    let mut point = anchor.to_point(&snapshot);
-                    if !*visual && point.column > 0 {
-                        point.column -= 1;
-                        point = snapshot.clip_point(point, Bias::Left)
-                    }
-                    new_ranges.push(point..point);
-                }
-                s.select_ranges(new_ranges)
-            })
-        });
-    });
-    vim.switch_mode(Mode::Normal, true, cx)
-}
-
-fn find_number(
-    snapshot: &MultiBufferSnapshot,
-    start: Point,
-) -> Option<(Range<Point>, String, u32)> {
-    let mut offset = start.to_offset(snapshot);
-
-    // go backwards to the start of any number the selection is within
-    for ch in snapshot.reversed_chars_at(offset) {
-        if ch.is_ascii_digit() || ch == '-' || ch == 'b' || ch == 'x' {
-            offset -= ch.len_utf8();
-            continue;
-        }
-        break;
-    }
-
-    let mut begin = None;
-    let mut end = None;
-    let mut num = String::new();
-    let mut radix = 10;
-
-    let mut chars = snapshot.chars_at(offset).peekable();
-    // find the next number on the line (may start after the original cursor position)
-    while let Some(ch) = chars.next() {
-        if num == "0" && ch == 'b' && chars.peek().is_some() && chars.peek().unwrap().is_digit(2) {
-            radix = 2;
-            begin = None;
-            num = String::new();
-        }
-        if num == "0" && ch == 'x' && chars.peek().is_some() && chars.peek().unwrap().is_digit(16) {
-            radix = 16;
-            begin = None;
-            num = String::new();
-        }
-
-        if ch.is_digit(radix)
-            || (begin.is_none()
-                && ch == '-'
-                && chars.peek().is_some()
-                && chars.peek().unwrap().is_digit(radix))
-        {
-            if begin.is_none() {
-                begin = Some(offset);
-            }
-            num.push(ch);
-        } else {
-            if begin.is_some() {
-                end = Some(offset);
-                break;
-            } else if ch == '\n' {
-                break;
-            }
-        }
-        offset += ch.len_utf8();
-    }
-    if let Some(begin) = begin {
-        let end = end.unwrap_or(offset);
-        Some((begin.to_point(snapshot)..end.to_point(snapshot), num, radix))
-    } else {
-        None
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use indoc::indoc;
-
-    use crate::test::NeovimBackedTestContext;
-
-    #[gpui::test]
-    async fn test_increment(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {"
-            1ˇ2
-            "})
-            .await;
-
-        cx.simulate_shared_keystrokes(["ctrl-a"]).await;
-        cx.assert_shared_state(indoc! {"
-            1ˇ3
-            "})
-            .await;
-        cx.simulate_shared_keystrokes(["ctrl-x"]).await;
-        cx.assert_shared_state(indoc! {"
-            1ˇ2
-            "})
-            .await;
-
-        cx.simulate_shared_keystrokes(["9", "9", "ctrl-a"]).await;
-        cx.assert_shared_state(indoc! {"
-            11ˇ1
-            "})
-            .await;
-        cx.simulate_shared_keystrokes(["1", "1", "1", "ctrl-x"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            ˇ0
-            "})
-            .await;
-        cx.simulate_shared_keystrokes(["."]).await;
-        cx.assert_shared_state(indoc! {"
-            -11ˇ1
-            "})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_increment_radix(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.assert_matches_neovim("ˇ total: 0xff", ["ctrl-a"], " total: 0x10ˇ0")
-            .await;
-        cx.assert_matches_neovim("ˇ total: 0xff", ["ctrl-x"], " total: 0xfˇe")
-            .await;
-        cx.assert_matches_neovim("ˇ total: 0xFF", ["ctrl-x"], " total: 0xFˇE")
-            .await;
-        cx.assert_matches_neovim("(ˇ0b10f)", ["ctrl-a"], "(0b1ˇ1f)")
-            .await;
-        cx.assert_matches_neovim("ˇ-1", ["ctrl-a"], "ˇ0").await;
-        cx.assert_matches_neovim("banˇana", ["ctrl-a"], "banˇana")
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_increment_steps(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {"
-            ˇ1
-            1
-            1  2
-            1
-            1"})
-            .await;
-
-        cx.simulate_shared_keystrokes(["j", "v", "shift-g", "g", "ctrl-a"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            1
-            ˇ2
-            3  2
-            4
-            5"})
-            .await;
-
-        cx.simulate_shared_keystrokes(["shift-g", "ctrl-v", "g", "g"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            «1ˇ»
-            «2ˇ»
-            «3ˇ»  2
-            «4ˇ»
-            «5ˇ»"})
-            .await;
-
-        cx.simulate_shared_keystrokes(["g", "ctrl-x"]).await;
-        cx.assert_shared_state(indoc! {"
-            ˇ0
-            0
-            0  2
-            0
-            0"})
-            .await;
-    }
-}

crates/vim2/src/normal/paste.rs 🔗

@@ -1,476 +0,0 @@
-use std::{borrow::Cow, cmp};
-
-use editor::{
-    display_map::ToDisplayPoint, movement, scroll::autoscroll::Autoscroll, ClipboardSelection,
-    DisplayPoint,
-};
-use gpui::{impl_actions, ViewContext};
-use language::{Bias, SelectionGoal};
-use serde::Deserialize;
-use workspace::Workspace;
-
-use crate::{state::Mode, utils::copy_selections_content, Vim};
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct Paste {
-    #[serde(default)]
-    before: bool,
-    #[serde(default)]
-    preserve_clipboard: bool,
-}
-
-impl_actions!(vim, [Paste]);
-
-pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(paste);
-}
-
-fn paste(_: &mut Workspace, action: &Paste, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.record_current_action(cx);
-        vim.update_active_editor(cx, |editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.transact(cx, |editor, cx| {
-                editor.set_clip_at_line_ends(false, cx);
-
-                let Some(item) = cx.read_from_clipboard() else {
-                    return;
-                };
-                let clipboard_text = Cow::Borrowed(item.text());
-                if clipboard_text.is_empty() {
-                    return;
-                }
-
-                if !action.preserve_clipboard && vim.state().mode.is_visual() {
-                    copy_selections_content(editor, vim.state().mode == Mode::VisualLine, cx);
-                }
-
-                // if we are copying from multi-cursor (of visual block mode), we want
-                // to
-                let clipboard_selections =
-                    item.metadata::<Vec<ClipboardSelection>>()
-                        .filter(|clipboard_selections| {
-                            clipboard_selections.len() > 1 && vim.state().mode != Mode::VisualLine
-                        });
-
-                let (display_map, current_selections) = editor.selections.all_adjusted_display(cx);
-
-                // unlike zed, if you have a multi-cursor selection from vim block mode,
-                // pasting it will paste it on subsequent lines, even if you don't yet
-                // have a cursor there.
-                let mut selections_to_process = Vec::new();
-                let mut i = 0;
-                while i < current_selections.len() {
-                    selections_to_process
-                        .push((current_selections[i].start..current_selections[i].end, true));
-                    i += 1;
-                }
-                if let Some(clipboard_selections) = clipboard_selections.as_ref() {
-                    let left = current_selections
-                        .iter()
-                        .map(|selection| cmp::min(selection.start.column(), selection.end.column()))
-                        .min()
-                        .unwrap();
-                    let mut row = current_selections.last().unwrap().end.row() + 1;
-                    while i < clipboard_selections.len() {
-                        let cursor =
-                            display_map.clip_point(DisplayPoint::new(row, left), Bias::Left);
-                        selections_to_process.push((cursor..cursor, false));
-                        i += 1;
-                        row += 1;
-                    }
-                }
-
-                let first_selection_indent_column =
-                    clipboard_selections.as_ref().and_then(|zed_selections| {
-                        zed_selections
-                            .first()
-                            .map(|selection| selection.first_line_indent)
-                    });
-                let before = action.before || vim.state().mode == Mode::VisualLine;
-
-                let mut edits = Vec::new();
-                let mut new_selections = Vec::new();
-                let mut original_indent_columns = Vec::new();
-                let mut start_offset = 0;
-
-                for (ix, (selection, preserve)) in selections_to_process.iter().enumerate() {
-                    let (mut to_insert, original_indent_column) =
-                        if let Some(clipboard_selections) = &clipboard_selections {
-                            if let Some(clipboard_selection) = clipboard_selections.get(ix) {
-                                let end_offset = start_offset + clipboard_selection.len;
-                                let text = clipboard_text[start_offset..end_offset].to_string();
-                                start_offset = end_offset + 1;
-                                (text, Some(clipboard_selection.first_line_indent))
-                            } else {
-                                ("".to_string(), first_selection_indent_column)
-                            }
-                        } else {
-                            (clipboard_text.to_string(), first_selection_indent_column)
-                        };
-                    let line_mode = to_insert.ends_with("\n");
-                    let is_multiline = to_insert.contains("\n");
-
-                    if line_mode && !before {
-                        if selection.is_empty() {
-                            to_insert =
-                                "\n".to_owned() + &to_insert[..to_insert.len() - "\n".len()];
-                        } else {
-                            to_insert = "\n".to_owned() + &to_insert;
-                        }
-                    } else if !line_mode && vim.state().mode == Mode::VisualLine {
-                        to_insert = to_insert + "\n";
-                    }
-
-                    let display_range = if !selection.is_empty() {
-                        selection.start..selection.end
-                    } else if line_mode {
-                        let point = if before {
-                            movement::line_beginning(&display_map, selection.start, false)
-                        } else {
-                            movement::line_end(&display_map, selection.start, false)
-                        };
-                        point..point
-                    } else {
-                        let point = if before {
-                            selection.start
-                        } else {
-                            movement::saturating_right(&display_map, selection.start)
-                        };
-                        point..point
-                    };
-
-                    let point_range = display_range.start.to_point(&display_map)
-                        ..display_range.end.to_point(&display_map);
-                    let anchor = if is_multiline || vim.state().mode == Mode::VisualLine {
-                        display_map.buffer_snapshot.anchor_before(point_range.start)
-                    } else {
-                        display_map.buffer_snapshot.anchor_after(point_range.end)
-                    };
-
-                    if *preserve {
-                        new_selections.push((anchor, line_mode, is_multiline));
-                    }
-                    edits.push((point_range, to_insert));
-                    original_indent_columns.extend(original_indent_column);
-                }
-
-                editor.edit_with_block_indent(edits, original_indent_columns, cx);
-
-                // in line_mode vim will insert the new text on the next (or previous if before) line
-                // and put the cursor on the first non-blank character of the first inserted line (or at the end if the first line is blank).
-                // otherwise vim will insert the next text at (or before) the current cursor position,
-                // the cursor will go to the last (or first, if is_multiline) inserted character.
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.replace_cursors_with(|map| {
-                        let mut cursors = Vec::new();
-                        for (anchor, line_mode, is_multiline) in &new_selections {
-                            let mut cursor = anchor.to_display_point(map);
-                            if *line_mode {
-                                if !before {
-                                    cursor = movement::down(
-                                        map,
-                                        cursor,
-                                        SelectionGoal::None,
-                                        false,
-                                        &text_layout_details,
-                                    )
-                                    .0;
-                                }
-                                cursor = movement::indented_line_beginning(map, cursor, true);
-                            } else if !is_multiline {
-                                cursor = movement::saturating_left(map, cursor)
-                            }
-                            cursors.push(cursor);
-                            if vim.state().mode == Mode::VisualBlock {
-                                break;
-                            }
-                        }
-
-                        cursors
-                    });
-                })
-            });
-        });
-        vim.switch_mode(Mode::Normal, true, cx);
-    });
-}
-
-#[cfg(test)]
-mod test {
-    use crate::{
-        state::Mode,
-        test::{NeovimBackedTestContext, VimTestContext},
-    };
-    use indoc::indoc;
-
-    #[gpui::test]
-    async fn test_paste(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        // single line
-        cx.set_shared_state(indoc! {"
-            The quick brown
-            fox ˇjumps over
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "w", "y"]).await;
-        cx.assert_shared_clipboard("jumps o").await;
-        cx.set_shared_state(indoc! {"
-            The quick brown
-            fox jumps oveˇr
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystroke("p").await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            fox jumps overjumps ˇo
-            the lazy dog"})
-            .await;
-
-        cx.set_shared_state(indoc! {"
-            The quick brown
-            fox jumps oveˇr
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystroke("shift-p").await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            fox jumps ovejumps ˇor
-            the lazy dog"})
-            .await;
-
-        // line mode
-        cx.set_shared_state(indoc! {"
-            The quick brown
-            fox juˇmps over
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["d", "d"]).await;
-        cx.assert_shared_clipboard("fox jumps over\n").await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            the laˇzy dog"})
-            .await;
-        cx.simulate_shared_keystroke("p").await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            the lazy dog
-            ˇfox jumps over"})
-            .await;
-        cx.simulate_shared_keystrokes(["k", "shift-p"]).await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            ˇfox jumps over
-            the lazy dog
-            fox jumps over"})
-            .await;
-
-        // multiline, cursor to first character of pasted text.
-        cx.set_shared_state(indoc! {"
-            The quick brown
-            fox jumps ˇover
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "j", "y"]).await;
-        cx.assert_shared_clipboard("over\nthe lazy do").await;
-
-        cx.simulate_shared_keystroke("p").await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            fox jumps oˇover
-            the lazy dover
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["u", "shift-p"]).await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            fox jumps ˇover
-            the lazy doover
-            the lazy dog"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_paste_visual(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        // copy in visual mode
-        cx.set_shared_state(indoc! {"
-                The quick brown
-                fox jˇumps over
-                the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "i", "w", "y"]).await;
-        cx.assert_shared_state(indoc! {"
-                The quick brown
-                fox ˇjumps over
-                the lazy dog"})
-            .await;
-        // paste in visual mode
-        cx.simulate_shared_keystrokes(["w", "v", "i", "w", "p"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-                The quick brown
-                fox jumps jumpˇs
-                the lazy dog"})
-            .await;
-        cx.assert_shared_clipboard("over").await;
-        // paste in visual line mode
-        cx.simulate_shared_keystrokes(["up", "shift-v", "shift-p"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            ˇover
-            fox jumps jumps
-            the lazy dog"})
-            .await;
-        cx.assert_shared_clipboard("over").await;
-        // paste in visual block mode
-        cx.simulate_shared_keystrokes(["ctrl-v", "down", "down", "p"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            oveˇrver
-            overox jumps jumps
-            overhe lazy dog"})
-            .await;
-
-        // copy in visual line mode
-        cx.set_shared_state(indoc! {"
-                The quick brown
-                fox juˇmps over
-                the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["shift-v", "d"]).await;
-        cx.assert_shared_state(indoc! {"
-                The quick brown
-                the laˇzy dog"})
-            .await;
-        // paste in visual mode
-        cx.simulate_shared_keystrokes(["v", "i", "w", "p"]).await;
-        cx.assert_shared_state(
-            &indoc! {"
-                The quick brown
-                the_
-                ˇfox jumps over
-                _dog"}
-            .replace("_", " "), // Hack for trailing whitespace
-        )
-        .await;
-        cx.assert_shared_clipboard("lazy").await;
-        cx.set_shared_state(indoc! {"
-            The quick brown
-            fox juˇmps over
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["shift-v", "d"]).await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            the laˇzy dog"})
-            .await;
-        // paste in visual line mode
-        cx.simulate_shared_keystrokes(["k", "shift-v", "p"]).await;
-        cx.assert_shared_state(indoc! {"
-            ˇfox jumps over
-            the lazy dog"})
-            .await;
-        cx.assert_shared_clipboard("The quick brown\n").await;
-    }
-
-    #[gpui::test]
-    async fn test_paste_visual_block(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        // copy in visual block mode
-        cx.set_shared_state(indoc! {"
-            The ˇquick brown
-            fox jumps over
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "2", "j", "y"])
-            .await;
-        cx.assert_shared_clipboard("q\nj\nl").await;
-        cx.simulate_shared_keystrokes(["p"]).await;
-        cx.assert_shared_state(indoc! {"
-            The qˇquick brown
-            fox jjumps over
-            the llazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "i", "w", "shift-p"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            The ˇq brown
-            fox jjjumps over
-            the lllazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "i", "w", "shift-p"])
-            .await;
-
-        cx.set_shared_state(indoc! {"
-            The ˇquick brown
-            fox jumps over
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "j", "y"]).await;
-        cx.assert_shared_clipboard("q\nj").await;
-        cx.simulate_shared_keystrokes(["l", "ctrl-v", "2", "j", "shift-p"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            The qˇqick brown
-            fox jjmps over
-            the lzy dog"})
-            .await;
-
-        cx.simulate_shared_keystrokes(["shift-v", "p"]).await;
-        cx.assert_shared_state(indoc! {"
-            ˇq
-            j
-            fox jjmps over
-            the lzy dog"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_paste_indent(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new_typescript(cx).await;
-
-        cx.set_state(
-            indoc! {"
-            class A {ˇ
-            }
-        "},
-            Mode::Normal,
-        );
-        cx.simulate_keystrokes(["o", "a", "(", ")", "{", "escape"]);
-        cx.assert_state(
-            indoc! {"
-            class A {
-                a()ˇ{}
-            }
-            "},
-            Mode::Normal,
-        );
-        // cursor goes to the first non-blank character in the line;
-        cx.simulate_keystrokes(["y", "y", "p"]);
-        cx.assert_state(
-            indoc! {"
-            class A {
-                a(){}
-                ˇa(){}
-            }
-            "},
-            Mode::Normal,
-        );
-        // indentation is preserved when pasting
-        cx.simulate_keystrokes(["u", "shift-v", "up", "y", "shift-p"]);
-        cx.assert_state(
-            indoc! {"
-                ˇclass A {
-                    a(){}
-                class A {
-                    a(){}
-                }
-                "},
-            Mode::Normal,
-        );
-    }
-}

crates/vim2/src/normal/repeat.rs 🔗

@@ -1,496 +0,0 @@
-use crate::{
-    insert::NormalBefore,
-    motion::Motion,
-    state::{Mode, RecordedSelection, ReplayableAction},
-    visual::visual_motion,
-    Vim,
-};
-use gpui::{actions, Action, ViewContext, WindowContext};
-use workspace::Workspace;
-
-actions!(vim, [Repeat, EndRepeat]);
-
-fn should_replay(action: &Box<dyn Action>) -> bool {
-    // skip so that we don't leave the character palette open
-    if editor::ShowCharacterPalette.partial_eq(&**action) {
-        return false;
-    }
-    true
-}
-
-fn repeatable_insert(action: &ReplayableAction) -> Option<Box<dyn Action>> {
-    match action {
-        ReplayableAction::Action(action) => {
-            if super::InsertBefore.partial_eq(&**action)
-                || super::InsertAfter.partial_eq(&**action)
-                || super::InsertFirstNonWhitespace.partial_eq(&**action)
-                || super::InsertEndOfLine.partial_eq(&**action)
-            {
-                Some(super::InsertBefore.boxed_clone())
-            } else if super::InsertLineAbove.partial_eq(&**action)
-                || super::InsertLineBelow.partial_eq(&**action)
-            {
-                Some(super::InsertLineBelow.boxed_clone())
-            } else {
-                None
-            }
-        }
-        ReplayableAction::Insertion { .. } => None,
-    }
-}
-
-pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(|_: &mut Workspace, _: &EndRepeat, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.workspace_state.replaying = false;
-            vim.switch_mode(Mode::Normal, false, cx)
-        });
-    });
-
-    workspace.register_action(|_: &mut Workspace, _: &Repeat, cx| repeat(cx, false));
-}
-
-pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) {
-    let Some((mut actions, editor, selection)) = Vim::update(cx, |vim, cx| {
-        let actions = vim.workspace_state.recorded_actions.clone();
-        if actions.is_empty() {
-            return None;
-        }
-
-        let Some(editor) = vim.active_editor.clone() else {
-            return None;
-        };
-        let count = vim.take_count(cx);
-
-        let selection = vim.workspace_state.recorded_selection.clone();
-        match selection {
-            RecordedSelection::SingleLine { .. } | RecordedSelection::Visual { .. } => {
-                vim.workspace_state.recorded_count = None;
-                vim.switch_mode(Mode::Visual, false, cx)
-            }
-            RecordedSelection::VisualLine { .. } => {
-                vim.workspace_state.recorded_count = None;
-                vim.switch_mode(Mode::VisualLine, false, cx)
-            }
-            RecordedSelection::VisualBlock { .. } => {
-                vim.workspace_state.recorded_count = None;
-                vim.switch_mode(Mode::VisualBlock, false, cx)
-            }
-            RecordedSelection::None => {
-                if let Some(count) = count {
-                    vim.workspace_state.recorded_count = Some(count);
-                }
-            }
-        }
-
-        Some((actions, editor, selection))
-    }) else {
-        return;
-    };
-
-    match selection {
-        RecordedSelection::SingleLine { cols } => {
-            if cols > 1 {
-                visual_motion(Motion::Right, Some(cols as usize - 1), cx)
-            }
-        }
-        RecordedSelection::Visual { rows, cols } => {
-            visual_motion(
-                Motion::Down {
-                    display_lines: false,
-                },
-                Some(rows as usize),
-                cx,
-            );
-            visual_motion(
-                Motion::StartOfLine {
-                    display_lines: false,
-                },
-                None,
-                cx,
-            );
-            if cols > 1 {
-                visual_motion(Motion::Right, Some(cols as usize - 1), cx)
-            }
-        }
-        RecordedSelection::VisualBlock { rows, cols } => {
-            visual_motion(
-                Motion::Down {
-                    display_lines: false,
-                },
-                Some(rows as usize),
-                cx,
-            );
-            if cols > 1 {
-                visual_motion(Motion::Right, Some(cols as usize - 1), cx);
-            }
-        }
-        RecordedSelection::VisualLine { rows } => {
-            visual_motion(
-                Motion::Down {
-                    display_lines: false,
-                },
-                Some(rows as usize),
-                cx,
-            );
-        }
-        RecordedSelection::None => {}
-    }
-
-    // insert internally uses repeat to handle counts
-    // vim doesn't treat 3a1 as though you literally repeated a1
-    // 3 times, instead it inserts the content thrice at the insert position.
-    if let Some(to_repeat) = repeatable_insert(&actions[0]) {
-        if let Some(ReplayableAction::Action(action)) = actions.last() {
-            if NormalBefore.partial_eq(&**action) {
-                actions.pop();
-            }
-        }
-
-        let mut new_actions = actions.clone();
-        actions[0] = ReplayableAction::Action(to_repeat.boxed_clone());
-
-        let mut count = Vim::read(cx).workspace_state.recorded_count.unwrap_or(1);
-
-        // if we came from insert mode we're just doing repititions 2 onwards.
-        if from_insert_mode {
-            count -= 1;
-            new_actions[0] = actions[0].clone();
-        }
-
-        for _ in 1..count {
-            new_actions.append(actions.clone().as_mut());
-        }
-        new_actions.push(ReplayableAction::Action(NormalBefore.boxed_clone()));
-        actions = new_actions;
-    }
-
-    Vim::update(cx, |vim, _| vim.workspace_state.replaying = true);
-    let window = cx.window_handle();
-    cx.spawn(move |mut cx| async move {
-        editor.update(&mut cx, |editor, _| {
-            editor.show_local_selections = false;
-        })?;
-        for action in actions {
-            match action {
-                ReplayableAction::Action(action) => {
-                    if should_replay(&action) {
-                        window.update(&mut cx, |_, cx| cx.dispatch_action(action))
-                    } else {
-                        Ok(())
-                    }
-                }
-                ReplayableAction::Insertion {
-                    text,
-                    utf16_range_to_replace,
-                } => editor.update(&mut cx, |editor, cx| {
-                    editor.replay_insert_event(&text, utf16_range_to_replace.clone(), cx)
-                }),
-            }?
-        }
-        editor.update(&mut cx, |editor, _| {
-            editor.show_local_selections = true;
-        })?;
-        window.update(&mut cx, |_, cx| cx.dispatch_action(EndRepeat.boxed_clone()))
-    })
-    .detach_and_log_err(cx);
-}
-
-#[cfg(test)]
-mod test {
-    use editor::test::editor_lsp_test_context::EditorLspTestContext;
-    use futures::StreamExt;
-    use indoc::indoc;
-
-    use gpui::InputHandler;
-
-    use crate::{
-        state::Mode,
-        test::{NeovimBackedTestContext, VimTestContext},
-    };
-
-    #[gpui::test]
-    async fn test_dot_repeat(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        // "o"
-        cx.set_shared_state("ˇhello").await;
-        cx.simulate_shared_keystrokes(["o", "w", "o", "r", "l", "d", "escape"])
-            .await;
-        cx.assert_shared_state("hello\nworlˇd").await;
-        cx.simulate_shared_keystrokes(["."]).await;
-        cx.assert_shared_state("hello\nworld\nworlˇd").await;
-
-        // "d"
-        cx.simulate_shared_keystrokes(["^", "d", "f", "o"]).await;
-        cx.simulate_shared_keystrokes(["g", "g", "."]).await;
-        cx.assert_shared_state("ˇ\nworld\nrld").await;
-
-        // "p" (note that it pastes the current clipboard)
-        cx.simulate_shared_keystrokes(["j", "y", "y", "p"]).await;
-        cx.simulate_shared_keystrokes(["shift-g", "y", "y", "."])
-            .await;
-        cx.assert_shared_state("\nworld\nworld\nrld\nˇrld").await;
-
-        // "~" (note that counts apply to the action taken, not . itself)
-        cx.set_shared_state("ˇthe quick brown fox").await;
-        cx.simulate_shared_keystrokes(["2", "~", "."]).await;
-        cx.set_shared_state("THE ˇquick brown fox").await;
-        cx.simulate_shared_keystrokes(["3", "."]).await;
-        cx.set_shared_state("THE QUIˇck brown fox").await;
-        cx.run_until_parked();
-        cx.simulate_shared_keystrokes(["."]).await;
-        cx.assert_shared_state("THE QUICK ˇbrown fox").await;
-    }
-
-    #[gpui::test]
-    async fn test_repeat_ime(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-
-        cx.set_state("hˇllo", Mode::Normal);
-        cx.simulate_keystrokes(["i"]);
-
-        // simulate brazilian input for ä.
-        cx.update_editor(|editor, cx| {
-            editor.replace_and_mark_text_in_range(None, "\"", Some(1..1), cx);
-            editor.replace_text_in_range(None, "ä", cx);
-        });
-        cx.simulate_keystrokes(["escape"]);
-        cx.assert_state("hˇällo", Mode::Normal);
-        cx.simulate_keystrokes(["."]);
-        cx.assert_state("hˇäällo", Mode::Normal);
-    }
-
-    #[gpui::test]
-    async fn test_repeat_completion(cx: &mut gpui::TestAppContext) {
-        VimTestContext::init(cx);
-        let cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                completion_provider: Some(lsp::CompletionOptions {
-                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
-                    resolve_provider: Some(true),
-                    ..Default::default()
-                }),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-        let mut cx = VimTestContext::new_with_lsp(cx, true);
-
-        cx.set_state(
-            indoc! {"
-            onˇe
-            two
-            three
-        "},
-            Mode::Normal,
-        );
-
-        let mut request =
-            cx.handle_request::<lsp::request::Completion, _, _>(move |_, params, _| async move {
-                let position = params.text_document_position.position;
-                Ok(Some(lsp::CompletionResponse::Array(vec![
-                    lsp::CompletionItem {
-                        label: "first".to_string(),
-                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-                            range: lsp::Range::new(position.clone(), position.clone()),
-                            new_text: "first".to_string(),
-                        })),
-                        ..Default::default()
-                    },
-                    lsp::CompletionItem {
-                        label: "second".to_string(),
-                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-                            range: lsp::Range::new(position.clone(), position.clone()),
-                            new_text: "second".to_string(),
-                        })),
-                        ..Default::default()
-                    },
-                ])))
-            });
-        cx.simulate_keystrokes(["a", "."]);
-        request.next().await;
-        cx.condition(|editor, _| editor.context_menu_visible())
-            .await;
-        cx.simulate_keystrokes(["down", "enter", "!", "escape"]);
-
-        cx.assert_state(
-            indoc! {"
-                one.secondˇ!
-                two
-                three
-            "},
-            Mode::Normal,
-        );
-        cx.simulate_keystrokes(["j", "."]);
-        cx.assert_state(
-            indoc! {"
-                one.second!
-                two.secondˇ!
-                three
-            "},
-            Mode::Normal,
-        );
-    }
-
-    #[gpui::test]
-    async fn test_repeat_visual(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        // single-line (3 columns)
-        cx.set_shared_state(indoc! {
-            "ˇthe quick brown
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "i", "w", "s", "o", "escape"])
-            .await;
-        cx.assert_shared_state(indoc! {
-            "ˇo quick brown
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["j", "w", "."]).await;
-        cx.assert_shared_state(indoc! {
-            "o quick brown
-            fox ˇops over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["f", "r", "."]).await;
-        cx.assert_shared_state(indoc! {
-            "o quick brown
-            fox ops oveˇothe lazy dog"
-        })
-        .await;
-
-        // visual
-        cx.set_shared_state(indoc! {
-            "the ˇquick brown
-            fox jumps over
-            fox jumps over
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "j", "x"]).await;
-        cx.assert_shared_state(indoc! {
-            "the ˇumps over
-            fox jumps over
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["."]).await;
-        cx.assert_shared_state(indoc! {
-            "the ˇumps over
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["w", "."]).await;
-        cx.assert_shared_state(indoc! {
-            "the umps ˇumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["j", "."]).await;
-        cx.assert_shared_state(indoc! {
-            "the umps umps over
-            the ˇog"
-        })
-        .await;
-
-        // block mode (3 rows)
-        cx.set_shared_state(indoc! {
-            "ˇthe quick brown
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "j", "j", "shift-i", "o", "escape"])
-            .await;
-        cx.assert_shared_state(indoc! {
-            "ˇothe quick brown
-            ofox jumps over
-            othe lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["j", "4", "l", "."]).await;
-        cx.assert_shared_state(indoc! {
-            "othe quick brown
-            ofoxˇo jumps over
-            otheo lazy dog"
-        })
-        .await;
-
-        // line mode
-        cx.set_shared_state(indoc! {
-            "ˇthe quick brown
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["shift-v", "shift-r", "o", "escape"])
-            .await;
-        cx.assert_shared_state(indoc! {
-            "ˇo
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["j", "."]).await;
-        cx.assert_shared_state(indoc! {
-            "o
-            ˇo
-            the lazy dog"
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_repeat_motion_counts(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {
-            "ˇthe quick brown
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["3", "d", "3", "l"]).await;
-        cx.assert_shared_state(indoc! {
-            "ˇ brown
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["j", "."]).await;
-        cx.assert_shared_state(indoc! {
-            " brown
-            ˇ over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["j", "2", "."]).await;
-        cx.assert_shared_state(indoc! {
-            " brown
-             over
-            ˇe lazy dog"
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_record_interrupted(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-
-        cx.set_state("ˇhello\n", Mode::Normal);
-        cx.simulate_keystrokes(["4", "i", "j", "cmd-shift-p", "escape"]);
-        cx.simulate_keystrokes(["escape"]);
-        cx.assert_state("ˇjhello\n", Mode::Normal);
-    }
-}

crates/vim2/src/normal/scroll.rs 🔗

@@ -1,247 +0,0 @@
-use crate::Vim;
-use editor::{
-    display_map::ToDisplayPoint,
-    scroll::{scroll_amount::ScrollAmount, VERTICAL_SCROLL_MARGIN},
-    DisplayPoint, Editor,
-};
-use gpui::{actions, ViewContext};
-use language::Bias;
-use workspace::Workspace;
-
-actions!(
-    vim,
-    [LineUp, LineDown, ScrollUp, ScrollDown, PageUp, PageDown]
-);
-
-pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(|_: &mut Workspace, _: &LineDown, cx| {
-        scroll(cx, false, |c| ScrollAmount::Line(c.unwrap_or(1.)))
-    });
-    workspace.register_action(|_: &mut Workspace, _: &LineUp, cx| {
-        scroll(cx, false, |c| ScrollAmount::Line(-c.unwrap_or(1.)))
-    });
-    workspace.register_action(|_: &mut Workspace, _: &PageDown, cx| {
-        scroll(cx, false, |c| ScrollAmount::Page(c.unwrap_or(1.)))
-    });
-    workspace.register_action(|_: &mut Workspace, _: &PageUp, cx| {
-        scroll(cx, false, |c| ScrollAmount::Page(-c.unwrap_or(1.)))
-    });
-    workspace.register_action(|_: &mut Workspace, _: &ScrollDown, cx| {
-        scroll(cx, true, |c| {
-            if let Some(c) = c {
-                ScrollAmount::Line(c)
-            } else {
-                ScrollAmount::Page(0.5)
-            }
-        })
-    });
-    workspace.register_action(|_: &mut Workspace, _: &ScrollUp, cx| {
-        scroll(cx, true, |c| {
-            if let Some(c) = c {
-                ScrollAmount::Line(-c)
-            } else {
-                ScrollAmount::Page(-0.5)
-            }
-        })
-    });
-}
-
-fn scroll(
-    cx: &mut ViewContext<Workspace>,
-    move_cursor: bool,
-    by: fn(c: Option<f32>) -> ScrollAmount,
-) {
-    Vim::update(cx, |vim, cx| {
-        let amount = by(vim.take_count(cx).map(|c| c as f32));
-        vim.update_active_editor(cx, |editor, cx| {
-            scroll_editor(editor, move_cursor, &amount, cx)
-        });
-    })
-}
-
-fn scroll_editor(
-    editor: &mut Editor,
-    preserve_cursor_position: bool,
-    amount: &ScrollAmount,
-    cx: &mut ViewContext<Editor>,
-) {
-    let should_move_cursor = editor.newest_selection_on_screen(cx).is_eq();
-    let old_top_anchor = editor.scroll_manager.anchor().anchor;
-
-    editor.scroll_screen(amount, cx);
-    if should_move_cursor {
-        let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
-            visible_rows as u32
-        } else {
-            return;
-        };
-
-        let top_anchor = editor.scroll_manager.anchor().anchor;
-
-        editor.change_selections(None, cx, |s| {
-            s.move_with(|map, selection| {
-                let mut head = selection.head();
-                let top = top_anchor.to_display_point(map);
-
-                if preserve_cursor_position {
-                    let old_top = old_top_anchor.to_display_point(map);
-                    let new_row = top.row() + selection.head().row() - old_top.row();
-                    head = map.clip_point(DisplayPoint::new(new_row, head.column()), Bias::Left)
-                }
-                let min_row = top.row() + VERTICAL_SCROLL_MARGIN as u32;
-                let max_row = top.row() + visible_rows - VERTICAL_SCROLL_MARGIN as u32 - 1;
-
-                let new_head = if head.row() < min_row {
-                    map.clip_point(DisplayPoint::new(min_row, head.column()), Bias::Left)
-                } else if head.row() > max_row {
-                    map.clip_point(DisplayPoint::new(max_row, head.column()), Bias::Left)
-                } else {
-                    head
-                };
-                if selection.is_empty() {
-                    selection.collapse_to(new_head, selection.goal)
-                } else {
-                    selection.set_head(new_head, selection.goal)
-                };
-            })
-        });
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use crate::{
-        state::Mode,
-        test::{NeovimBackedTestContext, VimTestContext},
-    };
-    use gpui::{point, px, size, Context};
-    use indoc::indoc;
-    use language::Point;
-
-    #[gpui::test]
-    async fn test_scroll(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-
-        let (line_height, visible_line_count) = cx.editor(|editor, cx| {
-            (
-                editor
-                    .style()
-                    .unwrap()
-                    .text
-                    .line_height_in_pixels(cx.rem_size()),
-                editor.visible_line_count().unwrap(),
-            )
-        });
-
-        let window = cx.window;
-        let margin = cx
-            .update_window(window, |_, cx| {
-                cx.viewport_size().height - line_height * visible_line_count
-            })
-            .unwrap();
-        cx.simulate_window_resize(
-            cx.window,
-            size(px(1000.), margin + 8. * line_height - px(1.0)),
-        );
-
-        cx.set_state(
-            indoc!(
-                "ˇone
-                two
-                three
-                four
-                five
-                six
-                seven
-                eight
-                nine
-                ten
-                eleven
-                twelve
-            "
-            ),
-            Mode::Normal,
-        );
-
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
-        });
-        cx.simulate_keystrokes(["ctrl-e"]);
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 1.))
-        });
-        cx.simulate_keystrokes(["2", "ctrl-e"]);
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.))
-        });
-        cx.simulate_keystrokes(["ctrl-y"]);
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 2.))
-        });
-
-        // does not select in normal mode
-        cx.simulate_keystrokes(["g", "g"]);
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
-        });
-        cx.simulate_keystrokes(["ctrl-d"]);
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0));
-            assert_eq!(
-                editor.selections.newest(cx).range(),
-                Point::new(6, 0)..Point::new(6, 0)
-            )
-        });
-
-        // does select in visual mode
-        cx.simulate_keystrokes(["g", "g"]);
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
-        });
-        cx.simulate_keystrokes(["v", "ctrl-d"]);
-        cx.update_editor(|editor, cx| {
-            assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0));
-            assert_eq!(
-                editor.selections.newest(cx).range(),
-                Point::new(0, 0)..Point::new(6, 1)
-            )
-        });
-    }
-    #[gpui::test]
-    async fn test_ctrl_d_u(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_scroll_height(10).await;
-
-        pub fn sample_text(rows: usize, cols: usize, start_char: char) -> String {
-            let mut text = String::new();
-            for row in 0..rows {
-                let c: char = (start_char as u32 + row as u32) as u8 as char;
-                let mut line = c.to_string().repeat(cols);
-                if row < rows - 1 {
-                    line.push('\n');
-                }
-                text += &line;
-            }
-            text
-        }
-        let content = "ˇ".to_owned() + &sample_text(26, 2, 'a');
-        cx.set_shared_state(&content).await;
-
-        // skip over the scrolloff at the top
-        // test ctrl-d
-        cx.simulate_shared_keystrokes(["4", "j", "ctrl-d"]).await;
-        cx.assert_state_matches().await;
-        cx.simulate_shared_keystrokes(["ctrl-d"]).await;
-        cx.assert_state_matches().await;
-        cx.simulate_shared_keystrokes(["g", "g", "ctrl-d"]).await;
-        cx.assert_state_matches().await;
-
-        // test ctrl-u
-        cx.simulate_shared_keystrokes(["ctrl-u"]).await;
-        cx.assert_state_matches().await;
-        cx.simulate_shared_keystrokes(["ctrl-d", "ctrl-d", "4", "j", "ctrl-u", "ctrl-u"])
-            .await;
-        cx.assert_state_matches().await;
-    }
-}

crates/vim2/src/normal/search.rs 🔗

@@ -1,477 +0,0 @@
-use gpui::{actions, impl_actions, ViewContext};
-use search::{buffer_search, BufferSearchBar, SearchMode, SearchOptions};
-use serde_derive::Deserialize;
-use workspace::{searchable::Direction, Workspace};
-
-use crate::{motion::Motion, normal::move_cursor, state::SearchState, Vim};
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub(crate) struct MoveToNext {
-    #[serde(default)]
-    partial_word: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub(crate) struct MoveToPrev {
-    #[serde(default)]
-    partial_word: bool,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-pub(crate) struct Search {
-    #[serde(default)]
-    backwards: bool,
-}
-
-#[derive(Debug, Clone, PartialEq, Deserialize)]
-pub struct FindCommand {
-    pub query: String,
-    pub backwards: bool,
-}
-
-#[derive(Debug, Clone, PartialEq, Deserialize)]
-pub struct ReplaceCommand {
-    pub query: String,
-}
-
-#[derive(Debug, Default)]
-struct Replacement {
-    search: String,
-    replacement: String,
-    should_replace_all: bool,
-    is_case_sensitive: bool,
-}
-
-actions!(vim, [SearchSubmit]);
-impl_actions!(
-    vim,
-    [FindCommand, ReplaceCommand, Search, MoveToPrev, MoveToNext]
-);
-
-pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(move_to_next);
-    workspace.register_action(move_to_prev);
-    workspace.register_action(search);
-    workspace.register_action(search_submit);
-    workspace.register_action(search_deploy);
-
-    workspace.register_action(find_command);
-    workspace.register_action(replace_command);
-}
-
-fn move_to_next(workspace: &mut Workspace, action: &MoveToNext, cx: &mut ViewContext<Workspace>) {
-    move_to_internal(workspace, Direction::Next, !action.partial_word, cx)
-}
-
-fn move_to_prev(workspace: &mut Workspace, action: &MoveToPrev, cx: &mut ViewContext<Workspace>) {
-    move_to_internal(workspace, Direction::Prev, !action.partial_word, cx)
-}
-
-fn search(workspace: &mut Workspace, action: &Search, cx: &mut ViewContext<Workspace>) {
-    let pane = workspace.active_pane().clone();
-    let direction = if action.backwards {
-        Direction::Prev
-    } else {
-        Direction::Next
-    };
-    Vim::update(cx, |vim, cx| {
-        let count = vim.take_count(cx).unwrap_or(1);
-        pane.update(cx, |pane, cx| {
-            if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
-                search_bar.update(cx, |search_bar, cx| {
-                    if !search_bar.show(cx) {
-                        return;
-                    }
-                    let query = search_bar.query(cx);
-
-                    search_bar.select_query(cx);
-                    cx.focus_self();
-
-                    if query.is_empty() {
-                        search_bar.set_replacement(None, cx);
-                        search_bar.set_search_options(SearchOptions::CASE_SENSITIVE, cx);
-                        search_bar.activate_search_mode(SearchMode::Regex, cx);
-                    }
-                    vim.workspace_state.search = SearchState {
-                        direction,
-                        count,
-                        initial_query: query.clone(),
-                    };
-                });
-            }
-        })
-    })
-}
-
-// hook into the existing to clear out any vim search state on cmd+f or edit -> find.
-fn search_deploy(_: &mut Workspace, _: &buffer_search::Deploy, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, _| vim.workspace_state.search = Default::default());
-    cx.propagate();
-}
-
-fn search_submit(workspace: &mut Workspace, _: &SearchSubmit, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        let pane = workspace.active_pane().clone();
-        pane.update(cx, |pane, cx| {
-            if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
-                search_bar.update(cx, |search_bar, cx| {
-                    let state = &mut vim.workspace_state.search;
-                    let mut count = state.count;
-                    let direction = state.direction;
-
-                    // in the case that the query has changed, the search bar
-                    // will have selected the next match already.
-                    if (search_bar.query(cx) != state.initial_query)
-                        && state.direction == Direction::Next
-                    {
-                        count = count.saturating_sub(1)
-                    }
-                    state.count = 1;
-                    search_bar.select_match(direction, count, cx);
-                    search_bar.focus_editor(&Default::default(), cx);
-                });
-            }
-        });
-    })
-}
-
-pub fn move_to_internal(
-    workspace: &mut Workspace,
-    direction: Direction,
-    whole_word: bool,
-    cx: &mut ViewContext<Workspace>,
-) {
-    Vim::update(cx, |vim, cx| {
-        let pane = workspace.active_pane().clone();
-        let count = vim.take_count(cx).unwrap_or(1);
-        pane.update(cx, |pane, cx| {
-            if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
-                let search = search_bar.update(cx, |search_bar, cx| {
-                    let mut options = SearchOptions::CASE_SENSITIVE;
-                    options.set(SearchOptions::WHOLE_WORD, whole_word);
-                    if search_bar.show(cx) {
-                        search_bar
-                            .query_suggestion(cx)
-                            .map(|query| search_bar.search(&query, Some(options), cx))
-                    } else {
-                        None
-                    }
-                });
-
-                if let Some(search) = search {
-                    let search_bar = search_bar.downgrade();
-                    cx.spawn(|_, mut cx| async move {
-                        search.await?;
-                        search_bar.update(&mut cx, |search_bar, cx| {
-                            search_bar.select_match(direction, count, cx)
-                        })?;
-                        anyhow::Ok(())
-                    })
-                    .detach_and_log_err(cx);
-                }
-            }
-        });
-        vim.clear_operator(cx);
-    });
-}
-
-fn find_command(workspace: &mut Workspace, action: &FindCommand, cx: &mut ViewContext<Workspace>) {
-    let pane = workspace.active_pane().clone();
-    pane.update(cx, |pane, cx| {
-        if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
-            let search = search_bar.update(cx, |search_bar, cx| {
-                if !search_bar.show(cx) {
-                    return None;
-                }
-                let mut query = action.query.clone();
-                if query == "" {
-                    query = search_bar.query(cx);
-                };
-
-                search_bar.activate_search_mode(SearchMode::Regex, cx);
-                Some(search_bar.search(&query, Some(SearchOptions::CASE_SENSITIVE), cx))
-            });
-            let Some(search) = search else { return };
-            let search_bar = search_bar.downgrade();
-            let direction = if action.backwards {
-                Direction::Prev
-            } else {
-                Direction::Next
-            };
-            cx.spawn(|_, mut cx| async move {
-                search.await?;
-                search_bar.update(&mut cx, |search_bar, cx| {
-                    search_bar.select_match(direction, 1, cx)
-                })?;
-                anyhow::Ok(())
-            })
-            .detach_and_log_err(cx);
-        }
-    })
-}
-
-fn replace_command(
-    workspace: &mut Workspace,
-    action: &ReplaceCommand,
-    cx: &mut ViewContext<Workspace>,
-) {
-    let replacement = parse_replace_all(&action.query);
-    let pane = workspace.active_pane().clone();
-    pane.update(cx, |pane, cx| {
-        let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() else {
-            return;
-        };
-        let search = search_bar.update(cx, |search_bar, cx| {
-            if !search_bar.show(cx) {
-                return None;
-            }
-
-            let mut options = SearchOptions::default();
-            if replacement.is_case_sensitive {
-                options.set(SearchOptions::CASE_SENSITIVE, true)
-            }
-            let search = if replacement.search == "" {
-                search_bar.query(cx)
-            } else {
-                replacement.search
-            };
-
-            search_bar.set_replacement(Some(&replacement.replacement), cx);
-            search_bar.activate_search_mode(SearchMode::Regex, cx);
-            Some(search_bar.search(&search, Some(options), cx))
-        });
-        let Some(search) = search else { return };
-        let search_bar = search_bar.downgrade();
-        cx.spawn(|_, mut cx| async move {
-            search.await?;
-            search_bar.update(&mut cx, |search_bar, cx| {
-                if replacement.should_replace_all {
-                    search_bar.select_last_match(cx);
-                    search_bar.replace_all(&Default::default(), cx);
-                    Vim::update(cx, |vim, cx| {
-                        move_cursor(
-                            vim,
-                            Motion::StartOfLine {
-                                display_lines: false,
-                            },
-                            None,
-                            cx,
-                        )
-                    })
-                }
-            })?;
-            anyhow::Ok(())
-        })
-        .detach_and_log_err(cx);
-    })
-}
-
-// convert a vim query into something more usable by zed.
-// we don't attempt to fully convert between the two regex syntaxes,
-// but we do flip \( and \) to ( and ) (and vice-versa) in the pattern,
-// and convert \0..\9 to $0..$9 in the replacement so that common idioms work.
-fn parse_replace_all(query: &str) -> Replacement {
-    let mut chars = query.chars();
-    if Some('%') != chars.next() || Some('s') != chars.next() {
-        return Replacement::default();
-    }
-
-    let Some(delimeter) = chars.next() else {
-        return Replacement::default();
-    };
-
-    let mut search = String::new();
-    let mut replacement = String::new();
-    let mut flags = String::new();
-
-    let mut buffer = &mut search;
-
-    let mut escaped = false;
-    // 0 - parsing search
-    // 1 - parsing replacement
-    // 2 - parsing flags
-    let mut phase = 0;
-
-    for c in chars {
-        if escaped {
-            escaped = false;
-            if phase == 1 && c.is_digit(10) {
-                buffer.push('$')
-            // unescape escaped parens
-            } else if phase == 0 && c == '(' || c == ')' {
-            } else if c != delimeter {
-                buffer.push('\\')
-            }
-            buffer.push(c)
-        } else if c == '\\' {
-            escaped = true;
-        } else if c == delimeter {
-            if phase == 0 {
-                buffer = &mut replacement;
-                phase = 1;
-            } else if phase == 1 {
-                buffer = &mut flags;
-                phase = 2;
-            } else {
-                break;
-            }
-        } else {
-            // escape unescaped parens
-            if phase == 0 && c == '(' || c == ')' {
-                buffer.push('\\')
-            }
-            buffer.push(c)
-        }
-    }
-
-    let mut replacement = Replacement {
-        search,
-        replacement,
-        should_replace_all: true,
-        is_case_sensitive: true,
-    };
-
-    for c in flags.chars() {
-        match c {
-            'g' | 'I' => {}
-            'c' | 'n' => replacement.should_replace_all = false,
-            'i' => replacement.is_case_sensitive = false,
-            _ => {}
-        }
-    }
-
-    replacement
-}
-
-#[cfg(test)]
-mod test {
-    use editor::DisplayPoint;
-    use search::BufferSearchBar;
-
-    use crate::{state::Mode, test::VimTestContext};
-
-    #[gpui::test]
-    async fn test_move_to_next(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-        cx.set_state("ˇhi\nhigh\nhi\n", Mode::Normal);
-
-        cx.simulate_keystrokes(["*"]);
-        cx.run_until_parked();
-        cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
-
-        cx.simulate_keystrokes(["*"]);
-        cx.run_until_parked();
-        cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
-
-        cx.simulate_keystrokes(["#"]);
-        cx.run_until_parked();
-        cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
-
-        cx.simulate_keystrokes(["#"]);
-        cx.run_until_parked();
-        cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
-
-        cx.simulate_keystrokes(["2", "*"]);
-        cx.run_until_parked();
-        cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
-
-        cx.simulate_keystrokes(["g", "*"]);
-        cx.run_until_parked();
-        cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
-
-        cx.simulate_keystrokes(["n"]);
-        cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
-
-        cx.simulate_keystrokes(["g", "#"]);
-        cx.run_until_parked();
-        cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
-    }
-
-    #[gpui::test]
-    async fn test_search(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-
-        cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
-        cx.simulate_keystrokes(["/", "c", "c"]);
-
-        let search_bar = cx.workspace(|workspace, cx| {
-            workspace
-                .active_pane()
-                .read(cx)
-                .toolbar()
-                .read(cx)
-                .item_of_type::<BufferSearchBar>()
-                .expect("Buffer search bar should be deployed")
-        });
-
-        cx.update_view(search_bar, |bar, cx| {
-            assert_eq!(bar.query(cx), "cc");
-        });
-
-        cx.run_until_parked();
-
-        cx.update_editor(|editor, cx| {
-            let highlights = editor.all_text_background_highlights(cx);
-            assert_eq!(3, highlights.len());
-            assert_eq!(
-                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
-                highlights[0].0
-            )
-        });
-
-        cx.simulate_keystrokes(["enter"]);
-        cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
-
-        // n to go to next/N to go to previous
-        cx.simulate_keystrokes(["n"]);
-        cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
-        cx.simulate_keystrokes(["shift-n"]);
-        cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
-
-        // ?<enter> to go to previous
-        cx.simulate_keystrokes(["?", "enter"]);
-        cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
-        cx.simulate_keystrokes(["?", "enter"]);
-        cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
-
-        // /<enter> to go to next
-        cx.simulate_keystrokes(["/", "enter"]);
-        cx.assert_state("aa\nbb\ncc\ncc\nˇcc\n", Mode::Normal);
-
-        // ?{search}<enter> to search backwards
-        cx.simulate_keystrokes(["?", "b", "enter"]);
-        cx.assert_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
-
-        // works with counts
-        cx.simulate_keystrokes(["4", "/", "c"]);
-        cx.simulate_keystrokes(["enter"]);
-        cx.assert_state("aa\nbb\ncc\ncˇc\ncc\n", Mode::Normal);
-
-        // check that searching resumes from cursor, not previous match
-        cx.set_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
-        cx.simulate_keystrokes(["/", "d"]);
-        cx.simulate_keystrokes(["enter"]);
-        cx.assert_state("aa\nbb\nˇdd\ncc\nbb\n", Mode::Normal);
-        cx.update_editor(|editor, cx| editor.move_to_beginning(&Default::default(), cx));
-        cx.assert_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
-        cx.simulate_keystrokes(["/", "b"]);
-        cx.simulate_keystrokes(["enter"]);
-        cx.assert_state("aa\nˇbb\ndd\ncc\nbb\n", Mode::Normal);
-    }
-
-    #[gpui::test]
-    async fn test_non_vim_search(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, false).await;
-        cx.set_state("ˇone one one one", Mode::Normal);
-        cx.simulate_keystrokes(["cmd-f"]);
-        cx.run_until_parked();
-
-        cx.assert_editor_state("«oneˇ» one one one");
-        cx.simulate_keystrokes(["enter"]);
-        cx.assert_editor_state("one «oneˇ» one one");
-        cx.simulate_keystrokes(["shift-enter"]);
-        cx.assert_editor_state("«oneˇ» one one one");
-    }
-}

crates/vim2/src/normal/substitute.rs 🔗

@@ -1,276 +0,0 @@
-use editor::movement;
-use gpui::{actions, ViewContext, WindowContext};
-use language::Point;
-use workspace::Workspace;
-
-use crate::{motion::Motion, utils::copy_selections_content, Mode, Vim};
-
-actions!(vim, [Substitute, SubstituteLine]);
-
-pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(|_: &mut Workspace, _: &Substitute, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.start_recording(cx);
-            let count = vim.take_count(cx);
-            substitute(vim, count, vim.state().mode == Mode::VisualLine, cx);
-        })
-    });
-
-    workspace.register_action(|_: &mut Workspace, _: &SubstituteLine, cx| {
-        Vim::update(cx, |vim, cx| {
-            vim.start_recording(cx);
-            if matches!(vim.state().mode, Mode::VisualBlock | Mode::Visual) {
-                vim.switch_mode(Mode::VisualLine, false, cx)
-            }
-            let count = vim.take_count(cx);
-            substitute(vim, count, true, cx)
-        })
-    });
-}
-
-pub fn substitute(vim: &mut Vim, count: Option<usize>, line_mode: bool, cx: &mut WindowContext) {
-    vim.update_active_editor(cx, |editor, cx| {
-        editor.set_clip_at_line_ends(false, cx);
-        editor.transact(cx, |editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            editor.change_selections(None, cx, |s| {
-                s.move_with(|map, selection| {
-                    if selection.start == selection.end {
-                        Motion::Right.expand_selection(
-                            map,
-                            selection,
-                            count,
-                            true,
-                            &text_layout_details,
-                        );
-                    }
-                    if line_mode {
-                        // in Visual mode when the selection contains the newline at the end
-                        // of the line, we should exclude it.
-                        if !selection.is_empty() && selection.end.column() == 0 {
-                            selection.end = movement::left(map, selection.end);
-                        }
-                        Motion::CurrentLine.expand_selection(
-                            map,
-                            selection,
-                            None,
-                            false,
-                            &text_layout_details,
-                        );
-                        if let Some((point, _)) = (Motion::FirstNonWhitespace {
-                            display_lines: false,
-                        })
-                        .move_point(
-                            map,
-                            selection.start,
-                            selection.goal,
-                            None,
-                            &text_layout_details,
-                        ) {
-                            selection.start = point;
-                        }
-                    }
-                })
-            });
-            copy_selections_content(editor, line_mode, cx);
-            let selections = editor.selections.all::<Point>(cx).into_iter();
-            let edits = selections.map(|selection| (selection.start..selection.end, ""));
-            editor.edit(edits, cx);
-        });
-    });
-    vim.switch_mode(Mode::Insert, true, cx);
-}
-
-#[cfg(test)]
-mod test {
-    use crate::{
-        state::Mode,
-        test::{NeovimBackedTestContext, VimTestContext},
-    };
-    use indoc::indoc;
-
-    #[gpui::test]
-    async fn test_substitute(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-
-        // supports a single cursor
-        cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
-        cx.simulate_keystrokes(["s", "x"]);
-        cx.assert_editor_state("xˇbc\n");
-
-        // supports a selection
-        cx.set_state(indoc! {"a«bcˇ»\n"}, Mode::Visual);
-        cx.assert_editor_state("a«bcˇ»\n");
-        cx.simulate_keystrokes(["s", "x"]);
-        cx.assert_editor_state("axˇ\n");
-
-        // supports counts
-        cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
-        cx.simulate_keystrokes(["2", "s", "x"]);
-        cx.assert_editor_state("xˇc\n");
-
-        // supports multiple cursors
-        cx.set_state(indoc! {"a«bcˇ»deˇffg\n"}, Mode::Normal);
-        cx.simulate_keystrokes(["2", "s", "x"]);
-        cx.assert_editor_state("axˇdexˇg\n");
-
-        // does not read beyond end of line
-        cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
-        cx.simulate_keystrokes(["5", "s", "x"]);
-        cx.assert_editor_state("xˇ\n");
-
-        // it handles multibyte characters
-        cx.set_state(indoc! {"ˇcàfé\n"}, Mode::Normal);
-        cx.simulate_keystrokes(["4", "s"]);
-        cx.assert_editor_state("ˇ\n");
-
-        // should transactionally undo selection changes
-        cx.simulate_keystrokes(["escape", "u"]);
-        cx.assert_editor_state("ˇcàfé\n");
-
-        // it handles visual line mode
-        cx.set_state(
-            indoc! {"
-            alpha
-              beˇta
-            gamma"},
-            Mode::Normal,
-        );
-        cx.simulate_keystrokes(["shift-v", "s"]);
-        cx.assert_editor_state(indoc! {"
-            alpha
-              ˇ
-            gamma"});
-    }
-
-    #[gpui::test]
-    async fn test_visual_change(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state("The quick ˇbrown").await;
-        cx.simulate_shared_keystrokes(["v", "w", "c"]).await;
-        cx.assert_shared_state("The quick ˇ").await;
-
-        cx.set_shared_state(indoc! {"
-            The ˇquick brown
-            fox jumps over
-            the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "w", "j", "c"]).await;
-        cx.assert_shared_state(indoc! {"
-            The ˇver
-            the lazy dog"})
-            .await;
-
-        let cases = cx.each_marked_position(indoc! {"
-            The ˇquick brown
-            fox jumps ˇover
-            the ˇlazy dog"});
-        for initial_state in cases {
-            cx.assert_neovim_compatible(&initial_state, ["v", "w", "j", "c"])
-                .await;
-            cx.assert_neovim_compatible(&initial_state, ["v", "w", "k", "c"])
-                .await;
-        }
-    }
-
-    #[gpui::test]
-    async fn test_visual_line_change(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx)
-            .await
-            .binding(["shift-v", "c"]);
-        cx.assert(indoc! {"
-            The quˇick brown
-            fox jumps over
-            the lazy dog"})
-            .await;
-        // Test pasting code copied on change
-        cx.simulate_shared_keystrokes(["escape", "j", "p"]).await;
-        cx.assert_state_matches().await;
-
-        cx.assert_all(indoc! {"
-            The quick brown
-            fox juˇmps over
-            the laˇzy dog"})
-            .await;
-        let mut cx = cx.binding(["shift-v", "j", "c"]);
-        cx.assert(indoc! {"
-            The quˇick brown
-            fox jumps over
-            the lazy dog"})
-            .await;
-        // Test pasting code copied on delete
-        cx.simulate_shared_keystrokes(["escape", "j", "p"]).await;
-        cx.assert_state_matches().await;
-
-        cx.assert_all(indoc! {"
-            The quick brown
-            fox juˇmps over
-            the laˇzy dog"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_substitute_line(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        let initial_state = indoc! {"
-                    The quick brown
-                    fox juˇmps over
-                    the lazy dog
-                    "};
-
-        // normal mode
-        cx.set_shared_state(initial_state).await;
-        cx.simulate_shared_keystrokes(["shift-s", "o"]).await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            oˇ
-            the lazy dog
-            "})
-            .await;
-
-        // visual mode
-        cx.set_shared_state(initial_state).await;
-        cx.simulate_shared_keystrokes(["v", "k", "shift-s", "o"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            oˇ
-            the lazy dog
-            "})
-            .await;
-
-        // visual block mode
-        cx.set_shared_state(initial_state).await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "j", "shift-s", "o"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            oˇ
-            "})
-            .await;
-
-        // visual mode including newline
-        cx.set_shared_state(initial_state).await;
-        cx.simulate_shared_keystrokes(["v", "$", "shift-s", "o"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-            oˇ
-            the lazy dog
-            "})
-            .await;
-
-        // indentation
-        cx.set_neovim_option("shiftwidth=4").await;
-        cx.set_shared_state(initial_state).await;
-        cx.simulate_shared_keystrokes([">", ">", "shift-s", "o"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-            The quick brown
-                oˇ
-            the lazy dog
-            "})
-            .await;
-    }
-}

crates/vim2/src/normal/yank.rs 🔗

@@ -1,50 +0,0 @@
-use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim};
-use collections::HashMap;
-use gpui::WindowContext;
-
-pub fn yank_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
-    vim.update_active_editor(cx, |editor, cx| {
-        let text_layout_details = editor.text_layout_details(cx);
-        editor.transact(cx, |editor, cx| {
-            editor.set_clip_at_line_ends(false, cx);
-            let mut original_positions: HashMap<_, _> = Default::default();
-            editor.change_selections(None, cx, |s| {
-                s.move_with(|map, selection| {
-                    let original_position = (selection.head(), selection.goal);
-                    original_positions.insert(selection.id, original_position);
-                    motion.expand_selection(map, selection, times, true, &text_layout_details);
-                });
-            });
-            copy_selections_content(editor, motion.linewise(), cx);
-            editor.change_selections(None, cx, |s| {
-                s.move_with(|_, selection| {
-                    let (head, goal) = original_positions.remove(&selection.id).unwrap();
-                    selection.collapse_to(head, goal);
-                });
-            });
-        });
-    });
-}
-
-pub fn yank_object(vim: &mut Vim, object: Object, around: bool, cx: &mut WindowContext) {
-    vim.update_active_editor(cx, |editor, cx| {
-        editor.transact(cx, |editor, cx| {
-            editor.set_clip_at_line_ends(false, cx);
-            let mut original_positions: HashMap<_, _> = Default::default();
-            editor.change_selections(None, cx, |s| {
-                s.move_with(|map, selection| {
-                    let original_position = (selection.head(), selection.goal);
-                    object.expand_selection(map, selection, around);
-                    original_positions.insert(selection.id, original_position);
-                });
-            });
-            copy_selections_content(editor, false, cx);
-            editor.change_selections(None, cx, |s| {
-                s.move_with(|_, selection| {
-                    let (head, goal) = original_positions.remove(&selection.id).unwrap();
-                    selection.collapse_to(head, goal);
-                });
-            });
-        });
-    });
-}

crates/vim2/src/object.rs 🔗

@@ -1,1025 +0,0 @@
-use std::ops::Range;
-
-use editor::{
-    char_kind,
-    display_map::{DisplaySnapshot, ToDisplayPoint},
-    movement::{self, FindRange},
-    Bias, CharKind, DisplayPoint,
-};
-use gpui::{actions, impl_actions, ViewContext, WindowContext};
-use language::Selection;
-use serde::Deserialize;
-use workspace::Workspace;
-
-use crate::{motion::right, normal::normal_object, state::Mode, visual::visual_object, Vim};
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Object {
-    Word { ignore_punctuation: bool },
-    Sentence,
-    Quotes,
-    BackQuotes,
-    DoubleQuotes,
-    VerticalBars,
-    Parentheses,
-    SquareBrackets,
-    CurlyBrackets,
-    AngleBrackets,
-}
-
-#[derive(Clone, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-struct Word {
-    #[serde(default)]
-    ignore_punctuation: bool,
-}
-
-impl_actions!(vim, [Word]);
-
-actions!(
-    vim,
-    [
-        Sentence,
-        Quotes,
-        BackQuotes,
-        DoubleQuotes,
-        VerticalBars,
-        Parentheses,
-        SquareBrackets,
-        CurlyBrackets,
-        AngleBrackets
-    ]
-);
-
-pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(
-        |_: &mut Workspace, &Word { ignore_punctuation }: &Word, cx: _| {
-            object(Object::Word { ignore_punctuation }, cx)
-        },
-    );
-    workspace
-        .register_action(|_: &mut Workspace, _: &Sentence, cx: _| object(Object::Sentence, cx));
-    workspace.register_action(|_: &mut Workspace, _: &Quotes, cx: _| object(Object::Quotes, cx));
-    workspace
-        .register_action(|_: &mut Workspace, _: &BackQuotes, cx: _| object(Object::BackQuotes, cx));
-    workspace.register_action(|_: &mut Workspace, _: &DoubleQuotes, cx: _| {
-        object(Object::DoubleQuotes, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, _: &Parentheses, cx: _| {
-        object(Object::Parentheses, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, _: &SquareBrackets, cx: _| {
-        object(Object::SquareBrackets, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, _: &CurlyBrackets, cx: _| {
-        object(Object::CurlyBrackets, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, _: &AngleBrackets, cx: _| {
-        object(Object::AngleBrackets, cx)
-    });
-    workspace.register_action(|_: &mut Workspace, _: &VerticalBars, cx: _| {
-        object(Object::VerticalBars, cx)
-    });
-}
-
-fn object(object: Object, cx: &mut WindowContext) {
-    match Vim::read(cx).state().mode {
-        Mode::Normal => normal_object(object, cx),
-        Mode::Visual | Mode::VisualLine | Mode::VisualBlock => visual_object(object, cx),
-        Mode::Insert => {
-            // Shouldn't execute a text object in insert mode. Ignoring
-        }
-    }
-}
-
-impl Object {
-    pub fn is_multiline(self) -> bool {
-        match self {
-            Object::Word { .. }
-            | Object::Quotes
-            | Object::BackQuotes
-            | Object::VerticalBars
-            | Object::DoubleQuotes => false,
-            Object::Sentence
-            | Object::Parentheses
-            | Object::AngleBrackets
-            | Object::CurlyBrackets
-            | Object::SquareBrackets => true,
-        }
-    }
-
-    pub fn always_expands_both_ways(self) -> bool {
-        match self {
-            Object::Word { .. } | Object::Sentence => false,
-            Object::Quotes
-            | Object::BackQuotes
-            | Object::DoubleQuotes
-            | Object::VerticalBars
-            | Object::Parentheses
-            | Object::SquareBrackets
-            | Object::CurlyBrackets
-            | Object::AngleBrackets => true,
-        }
-    }
-
-    pub fn target_visual_mode(self, current_mode: Mode) -> Mode {
-        match self {
-            Object::Word { .. } if current_mode == Mode::VisualLine => Mode::Visual,
-            Object::Word { .. } => current_mode,
-            Object::Sentence
-            | Object::Quotes
-            | Object::BackQuotes
-            | Object::DoubleQuotes
-            | Object::VerticalBars
-            | Object::Parentheses
-            | Object::SquareBrackets
-            | Object::CurlyBrackets
-            | Object::AngleBrackets => Mode::Visual,
-        }
-    }
-
-    pub fn range(
-        self,
-        map: &DisplaySnapshot,
-        relative_to: DisplayPoint,
-        around: bool,
-    ) -> Option<Range<DisplayPoint>> {
-        match self {
-            Object::Word { ignore_punctuation } => {
-                if around {
-                    around_word(map, relative_to, ignore_punctuation)
-                } else {
-                    in_word(map, relative_to, ignore_punctuation)
-                }
-            }
-            Object::Sentence => sentence(map, relative_to, around),
-            Object::Quotes => {
-                surrounding_markers(map, relative_to, around, self.is_multiline(), '\'', '\'')
-            }
-            Object::BackQuotes => {
-                surrounding_markers(map, relative_to, around, self.is_multiline(), '`', '`')
-            }
-            Object::DoubleQuotes => {
-                surrounding_markers(map, relative_to, around, self.is_multiline(), '"', '"')
-            }
-            Object::VerticalBars => {
-                surrounding_markers(map, relative_to, around, self.is_multiline(), '|', '|')
-            }
-            Object::Parentheses => {
-                surrounding_markers(map, relative_to, around, self.is_multiline(), '(', ')')
-            }
-            Object::SquareBrackets => {
-                surrounding_markers(map, relative_to, around, self.is_multiline(), '[', ']')
-            }
-            Object::CurlyBrackets => {
-                surrounding_markers(map, relative_to, around, self.is_multiline(), '{', '}')
-            }
-            Object::AngleBrackets => {
-                surrounding_markers(map, relative_to, around, self.is_multiline(), '<', '>')
-            }
-        }
-    }
-
-    pub fn expand_selection(
-        self,
-        map: &DisplaySnapshot,
-        selection: &mut Selection<DisplayPoint>,
-        around: bool,
-    ) -> bool {
-        if let Some(range) = self.range(map, selection.head(), around) {
-            selection.start = range.start;
-            selection.end = range.end;
-            true
-        } else {
-            false
-        }
-    }
-}
-
-/// Return a range that surrounds the word relative_to is in
-/// If relative_to is at the start of a word, return the word.
-/// If relative_to is between words, return the space between
-fn in_word(
-    map: &DisplaySnapshot,
-    relative_to: DisplayPoint,
-    ignore_punctuation: bool,
-) -> Option<Range<DisplayPoint>> {
-    // Use motion::right so that we consider the character under the cursor when looking for the start
-    let scope = map
-        .buffer_snapshot
-        .language_scope_at(relative_to.to_point(map));
-    let start = movement::find_preceding_boundary(
-        map,
-        right(map, relative_to, 1),
-        movement::FindRange::SingleLine,
-        |left, right| {
-            char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
-                != char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
-        },
-    );
-
-    let end = movement::find_boundary(map, relative_to, FindRange::SingleLine, |left, right| {
-        char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
-            != char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
-    });
-
-    Some(start..end)
-}
-
-/// Return a range that surrounds the word and following whitespace
-/// relative_to is in.
-/// If relative_to is at the start of a word, return the word and following whitespace.
-/// If relative_to is between words, return the whitespace back and the following word
-
-/// if in word
-///   delete that word
-///   if there is whitespace following the word, delete that as well
-///   otherwise, delete any preceding whitespace
-/// otherwise
-///   delete whitespace around cursor
-///   delete word following the cursor
-fn around_word(
-    map: &DisplaySnapshot,
-    relative_to: DisplayPoint,
-    ignore_punctuation: bool,
-) -> Option<Range<DisplayPoint>> {
-    let scope = map
-        .buffer_snapshot
-        .language_scope_at(relative_to.to_point(map));
-    let in_word = map
-        .chars_at(relative_to)
-        .next()
-        .map(|(c, _)| char_kind(&scope, c) != CharKind::Whitespace)
-        .unwrap_or(false);
-
-    if in_word {
-        around_containing_word(map, relative_to, ignore_punctuation)
-    } else {
-        around_next_word(map, relative_to, ignore_punctuation)
-    }
-}
-
-fn around_containing_word(
-    map: &DisplaySnapshot,
-    relative_to: DisplayPoint,
-    ignore_punctuation: bool,
-) -> Option<Range<DisplayPoint>> {
-    in_word(map, relative_to, ignore_punctuation)
-        .map(|range| expand_to_include_whitespace(map, range, true))
-}
-
-fn around_next_word(
-    map: &DisplaySnapshot,
-    relative_to: DisplayPoint,
-    ignore_punctuation: bool,
-) -> Option<Range<DisplayPoint>> {
-    let scope = map
-        .buffer_snapshot
-        .language_scope_at(relative_to.to_point(map));
-    // Get the start of the word
-    let start = movement::find_preceding_boundary(
-        map,
-        right(map, relative_to, 1),
-        FindRange::SingleLine,
-        |left, right| {
-            char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
-                != char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
-        },
-    );
-
-    let mut word_found = false;
-    let end = movement::find_boundary(map, relative_to, FindRange::MultiLine, |left, right| {
-        let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
-        let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
-
-        let found = (word_found && left_kind != right_kind) || right == '\n' && left == '\n';
-
-        if right_kind != CharKind::Whitespace {
-            word_found = true;
-        }
-
-        found
-    });
-
-    Some(start..end)
-}
-
-fn sentence(
-    map: &DisplaySnapshot,
-    relative_to: DisplayPoint,
-    around: bool,
-) -> Option<Range<DisplayPoint>> {
-    let mut start = None;
-    let mut previous_end = relative_to;
-
-    let mut chars = map.chars_at(relative_to).peekable();
-
-    // Search backwards for the previous sentence end or current sentence start. Include the character under relative_to
-    for (char, point) in chars
-        .peek()
-        .cloned()
-        .into_iter()
-        .chain(map.reverse_chars_at(relative_to))
-    {
-        if is_sentence_end(map, point) {
-            break;
-        }
-
-        if is_possible_sentence_start(char) {
-            start = Some(point);
-        }
-
-        previous_end = point;
-    }
-
-    // Search forward for the end of the current sentence or if we are between sentences, the start of the next one
-    let mut end = relative_to;
-    for (char, point) in chars {
-        if start.is_none() && is_possible_sentence_start(char) {
-            if around {
-                start = Some(point);
-                continue;
-            } else {
-                end = point;
-                break;
-            }
-        }
-
-        end = point;
-        *end.column_mut() += char.len_utf8() as u32;
-        end = map.clip_point(end, Bias::Left);
-
-        if is_sentence_end(map, end) {
-            break;
-        }
-    }
-
-    let mut range = start.unwrap_or(previous_end)..end;
-    if around {
-        range = expand_to_include_whitespace(map, range, false);
-    }
-
-    Some(range)
-}
-
-fn is_possible_sentence_start(character: char) -> bool {
-    !character.is_whitespace() && character != '.'
-}
-
-const SENTENCE_END_PUNCTUATION: &[char] = &['.', '!', '?'];
-const SENTENCE_END_FILLERS: &[char] = &[')', ']', '"', '\''];
-const SENTENCE_END_WHITESPACE: &[char] = &[' ', '\t', '\n'];
-fn is_sentence_end(map: &DisplaySnapshot, point: DisplayPoint) -> bool {
-    let mut next_chars = map.chars_at(point).peekable();
-    if let Some((char, _)) = next_chars.next() {
-        // We are at a double newline. This position is a sentence end.
-        if char == '\n' && next_chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) {
-            return true;
-        }
-
-        // The next text is not a valid whitespace. This is not a sentence end
-        if !SENTENCE_END_WHITESPACE.contains(&char) {
-            return false;
-        }
-    }
-
-    for (char, _) in map.reverse_chars_at(point) {
-        if SENTENCE_END_PUNCTUATION.contains(&char) {
-            return true;
-        }
-
-        if !SENTENCE_END_FILLERS.contains(&char) {
-            return false;
-        }
-    }
-
-    return false;
-}
-
-/// Expands the passed range to include whitespace on one side or the other in a line. Attempts to add the
-/// whitespace to the end first and falls back to the start if there was none.
-fn expand_to_include_whitespace(
-    map: &DisplaySnapshot,
-    mut range: Range<DisplayPoint>,
-    stop_at_newline: bool,
-) -> Range<DisplayPoint> {
-    let mut whitespace_included = false;
-
-    let mut chars = map.chars_at(range.end).peekable();
-    while let Some((char, point)) = chars.next() {
-        if char == '\n' && stop_at_newline {
-            break;
-        }
-
-        if char.is_whitespace() {
-            // Set end to the next display_point or the character position after the current display_point
-            range.end = chars.peek().map(|(_, point)| *point).unwrap_or_else(|| {
-                let mut end = point;
-                *end.column_mut() += char.len_utf8() as u32;
-                map.clip_point(end, Bias::Left)
-            });
-
-            if char != '\n' {
-                whitespace_included = true;
-            }
-        } else {
-            // Found non whitespace. Quit out.
-            break;
-        }
-    }
-
-    if !whitespace_included {
-        for (char, point) in map.reverse_chars_at(range.start) {
-            if char == '\n' && stop_at_newline {
-                break;
-            }
-
-            if !char.is_whitespace() {
-                break;
-            }
-
-            range.start = point;
-        }
-    }
-
-    range
-}
-
-fn surrounding_markers(
-    map: &DisplaySnapshot,
-    relative_to: DisplayPoint,
-    around: bool,
-    search_across_lines: bool,
-    open_marker: char,
-    close_marker: char,
-) -> Option<Range<DisplayPoint>> {
-    let point = relative_to.to_offset(map, Bias::Left);
-
-    let mut matched_closes = 0;
-    let mut opening = None;
-
-    if let Some((ch, range)) = movement::chars_after(map, point).next() {
-        if ch == open_marker {
-            if open_marker == close_marker {
-                let mut total = 0;
-                for (ch, _) in movement::chars_before(map, point) {
-                    if ch == '\n' {
-                        break;
-                    }
-                    if ch == open_marker {
-                        total += 1;
-                    }
-                }
-                if total % 2 == 0 {
-                    opening = Some(range)
-                }
-            } else {
-                opening = Some(range)
-            }
-        }
-    }
-
-    if opening.is_none() {
-        for (ch, range) in movement::chars_before(map, point) {
-            if ch == '\n' && !search_across_lines {
-                break;
-            }
-
-            if ch == open_marker {
-                if matched_closes == 0 {
-                    opening = Some(range);
-                    break;
-                }
-                matched_closes -= 1;
-            } else if ch == close_marker {
-                matched_closes += 1
-            }
-        }
-    }
-
-    if opening.is_none() {
-        for (ch, range) in movement::chars_after(map, point) {
-            if ch == open_marker {
-                opening = Some(range);
-                break;
-            } else if ch == close_marker {
-                break;
-            }
-        }
-    }
-
-    let Some(mut opening) = opening else {
-        return None;
-    };
-
-    let mut matched_opens = 0;
-    let mut closing = None;
-
-    for (ch, range) in movement::chars_after(map, opening.end) {
-        if ch == '\n' && !search_across_lines {
-            break;
-        }
-
-        if ch == close_marker {
-            if matched_opens == 0 {
-                closing = Some(range);
-                break;
-            }
-            matched_opens -= 1;
-        } else if ch == open_marker {
-            matched_opens += 1;
-        }
-    }
-
-    let Some(mut closing) = closing else {
-        return None;
-    };
-
-    if around && !search_across_lines {
-        let mut found = false;
-
-        for (ch, range) in movement::chars_after(map, closing.end) {
-            if ch.is_whitespace() && ch != '\n' {
-                found = true;
-                closing.end = range.end;
-            } else {
-                break;
-            }
-        }
-
-        if !found {
-            for (ch, range) in movement::chars_before(map, opening.start) {
-                if ch.is_whitespace() && ch != '\n' {
-                    opening.start = range.start
-                } else {
-                    break;
-                }
-            }
-        }
-    }
-
-    if !around && search_across_lines {
-        if let Some((ch, range)) = movement::chars_after(map, opening.end).next() {
-            if ch == '\n' {
-                opening.end = range.end
-            }
-        }
-
-        for (ch, range) in movement::chars_before(map, closing.start) {
-            if !ch.is_whitespace() {
-                break;
-            }
-            if ch != '\n' {
-                closing.start = range.start
-            }
-        }
-    }
-
-    let result = if around {
-        opening.start..closing.end
-    } else {
-        opening.end..closing.start
-    };
-
-    Some(
-        map.clip_point(result.start.to_display_point(map), Bias::Left)
-            ..map.clip_point(result.end.to_display_point(map), Bias::Right),
-    )
-}
-
-#[cfg(test)]
-mod test {
-    use indoc::indoc;
-
-    use crate::{
-        state::Mode,
-        test::{ExemptionFeatures, NeovimBackedTestContext, VimTestContext},
-    };
-
-    const WORD_LOCATIONS: &'static str = indoc! {"
-        The quick ˇbrowˇnˇ•••
-        fox ˇjuˇmpsˇ over
-        the lazy dogˇ••
-        ˇ
-        ˇ
-        ˇ
-        Thˇeˇ-ˇquˇickˇ ˇbrownˇ•
-        ˇ••
-        ˇ••
-        ˇ  fox-jumpˇs over
-        the lazy dogˇ•
-        ˇ
-        "
-    };
-
-    #[gpui::test]
-    async fn test_change_word_object(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.assert_binding_matches_all(["c", "i", "w"], WORD_LOCATIONS)
-            .await;
-        cx.assert_binding_matches_all(["c", "i", "shift-w"], WORD_LOCATIONS)
-            .await;
-        cx.assert_binding_matches_all(["c", "a", "w"], WORD_LOCATIONS)
-            .await;
-        cx.assert_binding_matches_all(["c", "a", "shift-w"], WORD_LOCATIONS)
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_delete_word_object(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.assert_binding_matches_all(["d", "i", "w"], WORD_LOCATIONS)
-            .await;
-        cx.assert_binding_matches_all(["d", "i", "shift-w"], WORD_LOCATIONS)
-            .await;
-        cx.assert_binding_matches_all(["d", "a", "w"], WORD_LOCATIONS)
-            .await;
-        cx.assert_binding_matches_all(["d", "a", "shift-w"], WORD_LOCATIONS)
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_visual_word_object(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        /*
-                cx.set_shared_state("The quick ˇbrown\nfox").await;
-                cx.simulate_shared_keystrokes(["v"]).await;
-                cx.assert_shared_state("The quick «bˇ»rown\nfox").await;
-                cx.simulate_shared_keystrokes(["i", "w"]).await;
-                cx.assert_shared_state("The quick «brownˇ»\nfox").await;
-        */
-        cx.set_shared_state("The quick brown\nˇ\nfox").await;
-        cx.simulate_shared_keystrokes(["v"]).await;
-        cx.assert_shared_state("The quick brown\n«\nˇ»fox").await;
-        cx.simulate_shared_keystrokes(["i", "w"]).await;
-        cx.assert_shared_state("The quick brown\n«\nˇ»fox").await;
-
-        cx.assert_binding_matches_all(["v", "i", "w"], WORD_LOCATIONS)
-            .await;
-        cx.assert_binding_matches_all_exempted(
-            ["v", "h", "i", "w"],
-            WORD_LOCATIONS,
-            ExemptionFeatures::NonEmptyVisualTextObjects,
-        )
-        .await;
-        cx.assert_binding_matches_all_exempted(
-            ["v", "l", "i", "w"],
-            WORD_LOCATIONS,
-            ExemptionFeatures::NonEmptyVisualTextObjects,
-        )
-        .await;
-        cx.assert_binding_matches_all(["v", "i", "shift-w"], WORD_LOCATIONS)
-            .await;
-
-        cx.assert_binding_matches_all_exempted(
-            ["v", "i", "h", "shift-w"],
-            WORD_LOCATIONS,
-            ExemptionFeatures::NonEmptyVisualTextObjects,
-        )
-        .await;
-        cx.assert_binding_matches_all_exempted(
-            ["v", "i", "l", "shift-w"],
-            WORD_LOCATIONS,
-            ExemptionFeatures::NonEmptyVisualTextObjects,
-        )
-        .await;
-
-        cx.assert_binding_matches_all_exempted(
-            ["v", "a", "w"],
-            WORD_LOCATIONS,
-            ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine,
-        )
-        .await;
-        cx.assert_binding_matches_all_exempted(
-            ["v", "a", "shift-w"],
-            WORD_LOCATIONS,
-            ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine,
-        )
-        .await;
-    }
-
-    const SENTENCE_EXAMPLES: &[&'static str] = &[
-        "ˇThe quick ˇbrownˇ?ˇ ˇFox Jˇumpsˇ!ˇ Ovˇer theˇ lazyˇ.",
-        indoc! {"
-            ˇThe quick ˇbrownˇ
-            fox jumps over
-            the lazy doˇgˇ.ˇ ˇThe quick ˇ
-            brown fox jumps over
-        "},
-        indoc! {"
-            The quick brown fox jumps.
-            Over the lazy dog
-            ˇ
-            ˇ
-            ˇ  fox-jumpˇs over
-            the lazy dog.ˇ
-            ˇ
-        "},
-        r#"ˇThe ˇquick brownˇ.)ˇ]ˇ'ˇ" Brown ˇfox jumpsˇ.ˇ "#,
-    ];
-
-    #[gpui::test]
-    async fn test_change_sentence_object(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx)
-            .await
-            .binding(["c", "i", "s"]);
-        cx.add_initial_state_exemptions(
-            "The quick brown fox jumps.\nOver the lazy dog\nˇ\nˇ\n  fox-jumps over\nthe lazy dog.\n\n",
-            ExemptionFeatures::SentenceOnEmptyLines);
-        cx.add_initial_state_exemptions(
-            "The quick brown fox jumps.\nOver the lazy dog\n\n\nˇ  foxˇ-ˇjumpˇs over\nthe lazy dog.\n\n",
-            ExemptionFeatures::SentenceAtStartOfLineWithWhitespace);
-        cx.add_initial_state_exemptions(
-            "The quick brown fox jumps.\nOver the lazy dog\n\n\n  fox-jumps over\nthe lazy dog.ˇ\nˇ\n",
-            ExemptionFeatures::SentenceAfterPunctuationAtEndOfFile);
-        for sentence_example in SENTENCE_EXAMPLES {
-            cx.assert_all(sentence_example).await;
-        }
-
-        let mut cx = cx.binding(["c", "a", "s"]);
-        cx.add_initial_state_exemptions(
-            "The quick brown?ˇ Fox Jumps! Over the lazy.",
-            ExemptionFeatures::IncorrectLandingPosition,
-        );
-        cx.add_initial_state_exemptions(
-            "The quick brown.)]\'\" Brown fox jumps.ˇ ",
-            ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine,
-        );
-
-        for sentence_example in SENTENCE_EXAMPLES {
-            cx.assert_all(sentence_example).await;
-        }
-    }
-
-    #[gpui::test]
-    async fn test_delete_sentence_object(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx)
-            .await
-            .binding(["d", "i", "s"]);
-        cx.add_initial_state_exemptions(
-            "The quick brown fox jumps.\nOver the lazy dog\nˇ\nˇ\n  fox-jumps over\nthe lazy dog.\n\n",
-            ExemptionFeatures::SentenceOnEmptyLines);
-        cx.add_initial_state_exemptions(
-            "The quick brown fox jumps.\nOver the lazy dog\n\n\nˇ  foxˇ-ˇjumpˇs over\nthe lazy dog.\n\n",
-            ExemptionFeatures::SentenceAtStartOfLineWithWhitespace);
-        cx.add_initial_state_exemptions(
-            "The quick brown fox jumps.\nOver the lazy dog\n\n\n  fox-jumps over\nthe lazy dog.ˇ\nˇ\n",
-            ExemptionFeatures::SentenceAfterPunctuationAtEndOfFile);
-
-        for sentence_example in SENTENCE_EXAMPLES {
-            cx.assert_all(sentence_example).await;
-        }
-
-        let mut cx = cx.binding(["d", "a", "s"]);
-        cx.add_initial_state_exemptions(
-            "The quick brown?ˇ Fox Jumps! Over the lazy.",
-            ExemptionFeatures::IncorrectLandingPosition,
-        );
-        cx.add_initial_state_exemptions(
-            "The quick brown.)]\'\" Brown fox jumps.ˇ ",
-            ExemptionFeatures::AroundObjectLeavesWhitespaceAtEndOfLine,
-        );
-
-        for sentence_example in SENTENCE_EXAMPLES {
-            cx.assert_all(sentence_example).await;
-        }
-    }
-
-    #[gpui::test]
-    async fn test_visual_sentence_object(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx)
-            .await
-            .binding(["v", "i", "s"]);
-        for sentence_example in SENTENCE_EXAMPLES {
-            cx.assert_all_exempted(sentence_example, ExemptionFeatures::SentenceOnEmptyLines)
-                .await;
-        }
-
-        let mut cx = cx.binding(["v", "a", "s"]);
-        for sentence_example in SENTENCE_EXAMPLES {
-            cx.assert_all_exempted(
-                sentence_example,
-                ExemptionFeatures::AroundSentenceStartingBetweenIncludesWrongWhitespace,
-            )
-            .await;
-        }
-    }
-
-    // Test string with "`" for opening surrounders and "'" for closing surrounders
-    const SURROUNDING_MARKER_STRING: &str = indoc! {"
-        ˇTh'ˇe ˇ`ˇ'ˇquˇi`ˇck broˇ'wn`
-        'ˇfox juˇmps ovˇ`ˇer
-        the ˇlazy dˇ'ˇoˇ`ˇg"};
-
-    const SURROUNDING_OBJECTS: &[(char, char)] = &[
-        ('\'', '\''), // Quote
-        ('`', '`'),   // Back Quote
-        ('"', '"'),   // Double Quote
-        ('(', ')'),   // Parentheses
-        ('[', ']'),   // SquareBrackets
-        ('{', '}'),   // CurlyBrackets
-        ('<', '>'),   // AngleBrackets
-    ];
-
-    #[gpui::test]
-    async fn test_change_surrounding_character_objects(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        for (start, end) in SURROUNDING_OBJECTS {
-            let marked_string = SURROUNDING_MARKER_STRING
-                .replace('`', &start.to_string())
-                .replace('\'', &end.to_string());
-
-            cx.assert_binding_matches_all(["c", "i", &start.to_string()], &marked_string)
-                .await;
-            cx.assert_binding_matches_all(["c", "i", &end.to_string()], &marked_string)
-                .await;
-            cx.assert_binding_matches_all(["c", "a", &start.to_string()], &marked_string)
-                .await;
-            cx.assert_binding_matches_all(["c", "a", &end.to_string()], &marked_string)
-                .await;
-        }
-    }
-    #[gpui::test]
-    async fn test_singleline_surrounding_character_objects(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.set_shared_wrap(12).await;
-
-        cx.set_shared_state(indoc! {
-            "helˇlo \"world\"!"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "i", "\""]).await;
-        cx.assert_shared_state(indoc! {
-            "hello \"«worldˇ»\"!"
-        })
-        .await;
-
-        cx.set_shared_state(indoc! {
-            "hello \"wˇorld\"!"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "i", "\""]).await;
-        cx.assert_shared_state(indoc! {
-            "hello \"«worldˇ»\"!"
-        })
-        .await;
-
-        cx.set_shared_state(indoc! {
-            "hello \"wˇorld\"!"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "a", "\""]).await;
-        cx.assert_shared_state(indoc! {
-            "hello« \"world\"ˇ»!"
-        })
-        .await;
-
-        cx.set_shared_state(indoc! {
-            "hello \"wˇorld\" !"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "a", "\""]).await;
-        cx.assert_shared_state(indoc! {
-            "hello «\"world\" ˇ»!"
-        })
-        .await;
-
-        cx.set_shared_state(indoc! {
-            "hello \"wˇorld\"•
-            goodbye"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "a", "\""]).await;
-        cx.assert_shared_state(indoc! {
-            "hello «\"world\" ˇ»
-            goodbye"
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_multiline_surrounding_character_objects(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {
-            "func empty(a string) bool {
-               if a == \"\" {
-                  return true
-               }
-               ˇreturn false
-            }"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "i", "{"]).await;
-        cx.assert_shared_state(indoc! {"
-            func empty(a string) bool {
-            «   if a == \"\" {
-                  return true
-               }
-               return false
-            ˇ»}"})
-            .await;
-        cx.set_shared_state(indoc! {
-            "func empty(a string) bool {
-                 if a == \"\" {
-                     ˇreturn true
-                 }
-                 return false
-            }"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "i", "{"]).await;
-        cx.assert_shared_state(indoc! {"
-            func empty(a string) bool {
-                 if a == \"\" {
-            «         return true
-            ˇ»     }
-                 return false
-            }"})
-            .await;
-
-        cx.set_shared_state(indoc! {
-            "func empty(a string) bool {
-                 if a == \"\" ˇ{
-                     return true
-                 }
-                 return false
-            }"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["v", "i", "{"]).await;
-        cx.assert_shared_state(indoc! {"
-            func empty(a string) bool {
-                 if a == \"\" {
-            «         return true
-            ˇ»     }
-                 return false
-            }"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_vertical_bars(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-        cx.set_state(
-            indoc! {"
-            fn boop() {
-                baz(ˇ|a, b| { bar(|j, k| { })})
-            }"
-            },
-            Mode::Normal,
-        );
-        cx.simulate_keystrokes(["c", "i", "|"]);
-        cx.assert_state(
-            indoc! {"
-            fn boop() {
-                baz(|ˇ| { bar(|j, k| { })})
-            }"
-            },
-            Mode::Insert,
-        );
-        cx.simulate_keystrokes(["escape", "1", "8", "|"]);
-        cx.assert_state(
-            indoc! {"
-            fn boop() {
-                baz(|| { bar(ˇ|j, k| { })})
-            }"
-            },
-            Mode::Normal,
-        );
-
-        cx.simulate_keystrokes(["v", "a", "|"]);
-        cx.assert_state(
-            indoc! {"
-            fn boop() {
-                baz(|| { bar(«|j, k| ˇ»{ })})
-            }"
-            },
-            Mode::Visual,
-        );
-    }
-
-    #[gpui::test]
-    async fn test_delete_surrounding_character_objects(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        for (start, end) in SURROUNDING_OBJECTS {
-            let marked_string = SURROUNDING_MARKER_STRING
-                .replace('`', &start.to_string())
-                .replace('\'', &end.to_string());
-
-            cx.assert_binding_matches_all(["d", "i", &start.to_string()], &marked_string)
-                .await;
-            cx.assert_binding_matches_all(["d", "i", &end.to_string()], &marked_string)
-                .await;
-            cx.assert_binding_matches_all(["d", "a", &start.to_string()], &marked_string)
-                .await;
-            cx.assert_binding_matches_all(["d", "a", &end.to_string()], &marked_string)
-                .await;
-        }
-    }
-}

crates/vim2/src/state.rs 🔗

@@ -1,234 +0,0 @@
-use std::{ops::Range, sync::Arc};
-
-use gpui::{Action, KeyContext};
-use language::CursorShape;
-use serde::{Deserialize, Serialize};
-use workspace::searchable::Direction;
-
-use crate::motion::Motion;
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
-pub enum Mode {
-    Normal,
-    Insert,
-    Visual,
-    VisualLine,
-    VisualBlock,
-}
-
-impl Mode {
-    pub fn is_visual(&self) -> bool {
-        match self {
-            Mode::Normal | Mode::Insert => false,
-            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => true,
-        }
-    }
-}
-
-impl Default for Mode {
-    fn default() -> Self {
-        Self::Normal
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
-pub enum Operator {
-    Change,
-    Delete,
-    Yank,
-    Replace,
-    Object { around: bool },
-    FindForward { before: bool },
-    FindBackward { after: bool },
-}
-
-#[derive(Default, Clone)]
-pub struct EditorState {
-    pub mode: Mode,
-    pub last_mode: Mode,
-
-    /// pre_count is the number before an operator is specified (3 in 3d2d)
-    pub pre_count: Option<usize>,
-    /// post_count is the number after an operator is specified (2 in 3d2d)
-    pub post_count: Option<usize>,
-
-    pub operator_stack: Vec<Operator>,
-}
-
-#[derive(Default, Clone, Debug)]
-pub enum RecordedSelection {
-    #[default]
-    None,
-    Visual {
-        rows: u32,
-        cols: u32,
-    },
-    SingleLine {
-        cols: u32,
-    },
-    VisualBlock {
-        rows: u32,
-        cols: u32,
-    },
-    VisualLine {
-        rows: u32,
-    },
-}
-
-#[derive(Default, Clone)]
-pub struct WorkspaceState {
-    pub search: SearchState,
-    pub last_find: Option<Motion>,
-
-    pub recording: bool,
-    pub stop_recording_after_next_action: bool,
-    pub replaying: bool,
-    pub recorded_count: Option<usize>,
-    pub recorded_actions: Vec<ReplayableAction>,
-    pub recorded_selection: RecordedSelection,
-}
-
-#[derive(Debug)]
-pub enum ReplayableAction {
-    Action(Box<dyn Action>),
-    Insertion {
-        text: Arc<str>,
-        utf16_range_to_replace: Option<Range<isize>>,
-    },
-}
-
-impl Clone for ReplayableAction {
-    fn clone(&self) -> Self {
-        match self {
-            Self::Action(action) => Self::Action(action.boxed_clone()),
-            Self::Insertion {
-                text,
-                utf16_range_to_replace,
-            } => Self::Insertion {
-                text: text.clone(),
-                utf16_range_to_replace: utf16_range_to_replace.clone(),
-            },
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct SearchState {
-    pub direction: Direction,
-    pub count: usize,
-    pub initial_query: String,
-}
-
-impl Default for SearchState {
-    fn default() -> Self {
-        Self {
-            direction: Direction::Next,
-            count: 1,
-            initial_query: "".to_string(),
-        }
-    }
-}
-
-impl EditorState {
-    pub fn cursor_shape(&self) -> CursorShape {
-        match self.mode {
-            Mode::Normal => {
-                if self.operator_stack.is_empty() {
-                    CursorShape::Block
-                } else {
-                    CursorShape::Underscore
-                }
-            }
-            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => CursorShape::Block,
-            Mode::Insert => CursorShape::Bar,
-        }
-    }
-
-    pub fn vim_controlled(&self) -> bool {
-        !matches!(self.mode, Mode::Insert)
-            || matches!(
-                self.operator_stack.last(),
-                Some(Operator::FindForward { .. }) | Some(Operator::FindBackward { .. })
-            )
-    }
-
-    pub fn should_autoindent(&self) -> bool {
-        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
-    }
-
-    pub fn clip_at_line_ends(&self) -> bool {
-        match self.mode {
-            Mode::Insert | Mode::Visual | Mode::VisualLine | Mode::VisualBlock => false,
-            Mode::Normal => true,
-        }
-    }
-
-    pub fn active_operator(&self) -> Option<Operator> {
-        self.operator_stack.last().copied()
-    }
-
-    pub fn keymap_context_layer(&self) -> KeyContext {
-        let mut context = KeyContext::default();
-        context.add("VimEnabled");
-        context.set(
-            "vim_mode",
-            match self.mode {
-                Mode::Normal => "normal",
-                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
-                Mode::Insert => "insert",
-            },
-        );
-
-        if self.vim_controlled() {
-            context.add("VimControl");
-        }
-
-        if self.active_operator().is_none() && self.pre_count.is_some()
-            || self.active_operator().is_some() && self.post_count.is_some()
-        {
-            context.add("VimCount");
-        }
-
-        let active_operator = self.active_operator();
-
-        if let Some(active_operator) = active_operator {
-            for context_flag in active_operator.context_flags().into_iter() {
-                context.add(*context_flag);
-            }
-        }
-
-        context.set(
-            "vim_operator",
-            active_operator.map(|op| op.id()).unwrap_or_else(|| "none"),
-        );
-
-        context
-    }
-}
-
-impl Operator {
-    pub fn id(&self) -> &'static str {
-        match self {
-            Operator::Object { around: false } => "i",
-            Operator::Object { around: true } => "a",
-            Operator::Change => "c",
-            Operator::Delete => "d",
-            Operator::Yank => "y",
-            Operator::Replace => "r",
-            Operator::FindForward { before: false } => "f",
-            Operator::FindForward { before: true } => "t",
-            Operator::FindBackward { after: false } => "F",
-            Operator::FindBackward { after: true } => "T",
-        }
-    }
-
-    pub fn context_flags(&self) -> &'static [&'static str] {
-        match self {
-            Operator::Object { .. } => &["VimObject"],
-            Operator::FindForward { .. } | Operator::FindBackward { .. } | Operator::Replace => {
-                &["VimWaiting"]
-            }
-            _ => &[],
-        }
-    }
-}

crates/vim2/src/test.rs 🔗

@@ -1,752 +0,0 @@
-mod neovim_backed_binding_test_context;
-mod neovim_backed_test_context;
-mod neovim_connection;
-mod vim_test_context;
-
-use command_palette::CommandPalette;
-use editor::DisplayPoint;
-pub use neovim_backed_binding_test_context::*;
-pub use neovim_backed_test_context::*;
-pub use vim_test_context::*;
-
-use indoc::indoc;
-use search::BufferSearchBar;
-
-use crate::{state::Mode, ModeIndicator};
-
-#[gpui::test]
-async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, false).await;
-    cx.simulate_keystrokes(["h", "j", "k", "l"]);
-    cx.assert_editor_state("hjklˇ");
-}
-
-#[gpui::test]
-async fn test_neovim(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-
-    cx.simulate_shared_keystroke("i").await;
-    cx.assert_state_matches().await;
-    cx.simulate_shared_keystrokes([
-        "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w",
-    ])
-    .await;
-    cx.assert_state_matches().await;
-    cx.assert_editor_state("ˇtest");
-}
-
-#[gpui::test]
-async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    cx.simulate_keystroke("i");
-    assert_eq!(cx.mode(), Mode::Insert);
-
-    // Editor acts as though vim is disabled
-    cx.disable_vim();
-    cx.simulate_keystrokes(["h", "j", "k", "l"]);
-    cx.assert_editor_state("hjklˇ");
-
-    // Selections aren't changed if editor is blurred but vim-mode is still disabled.
-    cx.set_state("«hjklˇ»", Mode::Normal);
-    cx.assert_editor_state("«hjklˇ»");
-    cx.update_editor(|_, cx| cx.blur());
-    cx.assert_editor_state("«hjklˇ»");
-    cx.update_editor(|_, cx| cx.focus_self());
-    cx.assert_editor_state("«hjklˇ»");
-
-    // Enabling dynamically sets vim mode again and restores normal mode
-    cx.enable_vim();
-    assert_eq!(cx.mode(), Mode::Normal);
-    cx.simulate_keystrokes(["h", "h", "h", "l"]);
-    assert_eq!(cx.buffer_text(), "hjkl".to_owned());
-    cx.assert_editor_state("hˇjkl");
-    cx.simulate_keystrokes(["i", "T", "e", "s", "t"]);
-    cx.assert_editor_state("hTestˇjkl");
-
-    // Disabling and enabling resets to normal mode
-    assert_eq!(cx.mode(), Mode::Insert);
-    cx.disable_vim();
-    cx.enable_vim();
-    assert_eq!(cx.mode(), Mode::Normal);
-}
-
-#[gpui::test]
-async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    cx.set_state(
-        indoc! {"
-            The quick brown
-            fox juˇmps over
-            the lazy dog"},
-        Mode::Normal,
-    );
-    cx.simulate_keystroke("/");
-
-    let search_bar = cx.workspace(|workspace, cx| {
-        workspace
-            .active_pane()
-            .read(cx)
-            .toolbar()
-            .read(cx)
-            .item_of_type::<BufferSearchBar>()
-            .expect("Buffer search bar should be deployed")
-    });
-
-    cx.update_view(search_bar, |bar, cx| {
-        assert_eq!(bar.query(cx), "");
-    })
-}
-
-#[gpui::test]
-async fn test_count_down(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
-    cx.simulate_keystrokes(["2", "down"]);
-    cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
-    cx.simulate_keystrokes(["9", "down"]);
-    cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
-}
-
-#[gpui::test]
-async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    // goes to end by default
-    cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
-    cx.simulate_keystrokes(["shift-g"]);
-    cx.assert_editor_state("aa\nbb\ncˇc");
-
-    // can go to line 1 (https://github.com/zed-industries/community/issues/710)
-    cx.simulate_keystrokes(["1", "shift-g"]);
-    cx.assert_editor_state("aˇa\nbb\ncc");
-}
-
-#[gpui::test]
-async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    // works in normal mode
-    cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
-    cx.simulate_keystrokes([">", ">"]);
-    cx.assert_editor_state("aa\n    bˇb\ncc");
-    cx.simulate_keystrokes(["<", "<"]);
-    cx.assert_editor_state("aa\nbˇb\ncc");
-
-    // works in visuial mode
-    cx.simulate_keystrokes(["shift-v", "down", ">"]);
-    cx.assert_editor_state("aa\n    b«b\n    ccˇ»");
-}
-
-#[gpui::test]
-async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    cx.set_state("aˇbc\n", Mode::Normal);
-    cx.simulate_keystrokes(["i", "cmd-shift-p"]);
-
-    assert!(cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
-    cx.simulate_keystroke("escape");
-    cx.run_until_parked();
-    assert!(!cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
-    cx.assert_state("aˇbc\n", Mode::Insert);
-}
-
-#[gpui::test]
-async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    cx.set_state("aˇbˇc", Mode::Normal);
-    cx.simulate_keystrokes(["escape"]);
-
-    cx.assert_state("aˇbc", Mode::Normal);
-}
-
-#[gpui::test]
-async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
-    cx.simulate_keystrokes(["/", "c", "c"]);
-
-    let search_bar = cx.workspace(|workspace, cx| {
-        workspace
-            .active_pane()
-            .read(cx)
-            .toolbar()
-            .read(cx)
-            .item_of_type::<BufferSearchBar>()
-            .expect("Buffer search bar should be deployed")
-    });
-
-    cx.update_view(search_bar, |bar, cx| {
-        assert_eq!(bar.query(cx), "cc");
-    });
-
-    cx.update_editor(|editor, cx| {
-        let highlights = editor.all_text_background_highlights(cx);
-        assert_eq!(3, highlights.len());
-        assert_eq!(
-            DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
-            highlights[0].0
-        )
-    });
-    cx.simulate_keystrokes(["enter"]);
-
-    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
-    cx.simulate_keystrokes(["n"]);
-    cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
-    cx.simulate_keystrokes(["shift-n"]);
-    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
-}
-
-#[gpui::test]
-async fn test_status_indicator(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    let mode_indicator = cx.workspace(|workspace, cx| {
-        let status_bar = workspace.status_bar().read(cx);
-        let mode_indicator = status_bar.item_of_type::<ModeIndicator>();
-        assert!(mode_indicator.is_some());
-        mode_indicator.unwrap()
-    });
-
-    assert_eq!(
-        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
-        Some(Mode::Normal)
-    );
-
-    // shows the correct mode
-    cx.simulate_keystrokes(["i"]);
-    assert_eq!(
-        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
-        Some(Mode::Insert)
-    );
-
-    // shows even in search
-    cx.simulate_keystrokes(["escape", "v", "/"]);
-    assert_eq!(
-        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
-        Some(Mode::Visual)
-    );
-
-    // hides if vim mode is disabled
-    cx.disable_vim();
-    cx.run_until_parked();
-    cx.workspace(|workspace, cx| {
-        let status_bar = workspace.status_bar().read(cx);
-        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
-        assert!(mode_indicator.read(cx).mode.is_none());
-    });
-
-    cx.enable_vim();
-    cx.run_until_parked();
-    cx.workspace(|workspace, cx| {
-        let status_bar = workspace.status_bar().read(cx);
-        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
-        assert!(mode_indicator.read(cx).mode.is_some());
-    });
-}
-
-#[gpui::test]
-async fn test_word_characters(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new_typescript(cx).await;
-    cx.set_state(
-        indoc! { "
-        class A {
-            #ˇgoop = 99;
-            $ˇgoop () { return this.#gˇoop };
-        };
-        console.log(new A().$gooˇp())
-    "},
-        Mode::Normal,
-    );
-    cx.simulate_keystrokes(["v", "i", "w"]);
-    cx.assert_state(
-        indoc! {"
-        class A {
-            «#goopˇ» = 99;
-            «$goopˇ» () { return this.«#goopˇ» };
-        };
-        console.log(new A().«$goopˇ»())
-    "},
-        Mode::Visual,
-    )
-}
-
-#[gpui::test]
-async fn test_join_lines(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-
-    cx.set_shared_state(indoc! {"
-      ˇone
-      two
-      three
-      four
-      five
-      six
-      "})
-        .await;
-    cx.simulate_shared_keystrokes(["shift-j"]).await;
-    cx.assert_shared_state(indoc! {"
-          oneˇ two
-          three
-          four
-          five
-          six
-          "})
-        .await;
-    cx.simulate_shared_keystrokes(["3", "shift-j"]).await;
-    cx.assert_shared_state(indoc! {"
-          one two threeˇ four
-          five
-          six
-          "})
-        .await;
-
-    cx.set_shared_state(indoc! {"
-      ˇone
-      two
-      three
-      four
-      five
-      six
-      "})
-        .await;
-    cx.simulate_shared_keystrokes(["j", "v", "3", "j", "shift-j"])
-        .await;
-    cx.assert_shared_state(indoc! {"
-      one
-      two three fourˇ five
-      six
-      "})
-        .await;
-}
-
-#[gpui::test]
-async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-
-    cx.set_shared_wrap(12).await;
-    // tests line wrap as follows:
-    //  1: twelve char
-    //     twelve char
-    //  2: twelve char
-    cx.set_shared_state(indoc! { "
-        tˇwelve char twelve char
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["j"]).await;
-    cx.assert_shared_state(indoc! { "
-        twelve char twelve char
-        tˇwelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["k"]).await;
-    cx.assert_shared_state(indoc! { "
-        tˇwelve char twelve char
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["g", "j"]).await;
-    cx.assert_shared_state(indoc! { "
-        twelve char tˇwelve char
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["g", "j"]).await;
-    cx.assert_shared_state(indoc! { "
-        twelve char twelve char
-        tˇwelve char
-    "})
-        .await;
-
-    cx.simulate_shared_keystrokes(["g", "k"]).await;
-    cx.assert_shared_state(indoc! { "
-        twelve char tˇwelve char
-        twelve char
-    "})
-        .await;
-
-    cx.simulate_shared_keystrokes(["g", "^"]).await;
-    cx.assert_shared_state(indoc! { "
-        twelve char ˇtwelve char
-        twelve char
-    "})
-        .await;
-
-    cx.simulate_shared_keystrokes(["^"]).await;
-    cx.assert_shared_state(indoc! { "
-        ˇtwelve char twelve char
-        twelve char
-    "})
-        .await;
-
-    cx.simulate_shared_keystrokes(["g", "$"]).await;
-    cx.assert_shared_state(indoc! { "
-        twelve charˇ twelve char
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["$"]).await;
-    cx.assert_shared_state(indoc! { "
-        twelve char twelve chaˇr
-        twelve char
-    "})
-        .await;
-
-    cx.set_shared_state(indoc! { "
-        tˇwelve char twelve char
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["enter"]).await;
-    cx.assert_shared_state(indoc! { "
-            twelve char twelve char
-            ˇtwelve char
-        "})
-        .await;
-
-    cx.set_shared_state(indoc! { "
-        twelve char
-        tˇwelve char twelve char
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["o", "o", "escape"]).await;
-    cx.assert_shared_state(indoc! { "
-        twelve char
-        twelve char twelve char
-        ˇo
-        twelve char
-    "})
-        .await;
-
-    cx.set_shared_state(indoc! { "
-        twelve char
-        tˇwelve char twelve char
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["shift-a", "a", "escape"])
-        .await;
-    cx.assert_shared_state(indoc! { "
-        twelve char
-        twelve char twelve charˇa
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["shift-i", "i", "escape"])
-        .await;
-    cx.assert_shared_state(indoc! { "
-        twelve char
-        ˇitwelve char twelve chara
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["shift-d"]).await;
-    cx.assert_shared_state(indoc! { "
-        twelve char
-        ˇ
-        twelve char
-    "})
-        .await;
-
-    cx.set_shared_state(indoc! { "
-        twelve char
-        twelve char tˇwelve char
-        twelve char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["shift-o", "o", "escape"])
-        .await;
-    cx.assert_shared_state(indoc! { "
-        twelve char
-        ˇo
-        twelve char twelve char
-        twelve char
-    "})
-        .await;
-
-    // line wraps as:
-    // fourteen ch
-    // ar
-    // fourteen ch
-    // ar
-    cx.set_shared_state(indoc! { "
-        fourteen chaˇr
-        fourteen char
-    "})
-        .await;
-
-    cx.simulate_shared_keystrokes(["d", "i", "w"]).await;
-    cx.assert_shared_state(indoc! {"
-        fourteenˇ•
-        fourteen char
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["j", "shift-f", "e", "f", "r"])
-        .await;
-    cx.assert_shared_state(indoc! {"
-        fourteen•
-        fourteen chaˇr
-    "})
-        .await;
-}
-
-#[gpui::test]
-async fn test_folds(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-    cx.set_neovim_option("foldmethod=manual").await;
-
-    cx.set_shared_state(indoc! { "
-        fn boop() {
-          ˇbarp()
-          bazp()
-        }
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
-        .await;
-
-    // visual display is now:
-    // fn boop () {
-    //  [FOLDED]
-    // }
-
-    // TODO: this should not be needed but currently zf does not
-    // return to normal mode.
-    cx.simulate_shared_keystrokes(["escape"]).await;
-
-    // skip over fold downward
-    cx.simulate_shared_keystrokes(["g", "g"]).await;
-    cx.assert_shared_state(indoc! { "
-        ˇfn boop() {
-          barp()
-          bazp()
-        }
-    "})
-        .await;
-
-    cx.simulate_shared_keystrokes(["j", "j"]).await;
-    cx.assert_shared_state(indoc! { "
-        fn boop() {
-          barp()
-          bazp()
-        ˇ}
-    "})
-        .await;
-
-    // skip over fold upward
-    cx.simulate_shared_keystrokes(["2", "k"]).await;
-    cx.assert_shared_state(indoc! { "
-        ˇfn boop() {
-          barp()
-          bazp()
-        }
-    "})
-        .await;
-
-    // yank the fold
-    cx.simulate_shared_keystrokes(["down", "y", "y"]).await;
-    cx.assert_shared_clipboard("  barp()\n  bazp()\n").await;
-
-    // re-open
-    cx.simulate_shared_keystrokes(["z", "o"]).await;
-    cx.assert_shared_state(indoc! { "
-        fn boop() {
-        ˇ  barp()
-          bazp()
-        }
-    "})
-        .await;
-}
-
-#[gpui::test]
-async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-    cx.set_neovim_option("foldmethod=manual").await;
-
-    cx.set_shared_state(indoc! { "
-        fn boop() {
-          ˇbarp()
-          bazp()
-        }
-    "})
-        .await;
-    cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
-        .await;
-    cx.simulate_shared_keystrokes(["escape"]).await;
-    cx.simulate_shared_keystrokes(["g", "g"]).await;
-    cx.simulate_shared_keystrokes(["5", "d", "j"]).await;
-    cx.assert_shared_state(indoc! { "ˇ"}).await;
-}
-
-#[gpui::test]
-async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-
-    cx.set_shared_state(indoc! {"
-        The quick brown
-        fox juˇmps over
-        the lazy dog"})
-        .await;
-
-    cx.simulate_shared_keystrokes(["4", "escape", "3", "d", "l"])
-        .await;
-    cx.assert_shared_state(indoc! {"
-        The quick brown
-        fox juˇ over
-        the lazy dog"})
-        .await;
-}
-
-#[gpui::test]
-async fn test_zero(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-
-    cx.set_shared_state(indoc! {"
-        The quˇick brown
-        fox jumps over
-        the lazy dog"})
-        .await;
-
-    cx.simulate_shared_keystrokes(["0"]).await;
-    cx.assert_shared_state(indoc! {"
-        ˇThe quick brown
-        fox jumps over
-        the lazy dog"})
-        .await;
-
-    cx.simulate_shared_keystrokes(["1", "0", "l"]).await;
-    cx.assert_shared_state(indoc! {"
-        The quick ˇbrown
-        fox jumps over
-        the lazy dog"})
-        .await;
-}
-
-#[gpui::test]
-async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-
-    cx.set_shared_state(indoc! {"
-        ;;ˇ;
-        Lorem Ipsum"})
-        .await;
-
-    cx.simulate_shared_keystrokes(["a", "down", "up", ";", "down", "up"])
-        .await;
-    cx.assert_shared_state(indoc! {"
-        ;;;;ˇ
-        Lorem Ipsum"})
-        .await;
-}
-
-#[gpui::test]
-async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-
-    cx.set_shared_wrap(12).await;
-
-    cx.set_shared_state(indoc! {"
-                aaˇaa
-                😃😃"
-    })
-    .await;
-    cx.simulate_shared_keystrokes(["j"]).await;
-    cx.assert_shared_state(indoc! {"
-                aaaa
-                😃ˇ😃"
-    })
-    .await;
-
-    cx.set_shared_state(indoc! {"
-                123456789012aaˇaa
-                123456789012😃😃"
-    })
-    .await;
-    cx.simulate_shared_keystrokes(["j"]).await;
-    cx.assert_shared_state(indoc! {"
-        123456789012aaaa
-        123456789012😃ˇ😃"
-    })
-    .await;
-
-    cx.set_shared_state(indoc! {"
-                123456789012aaˇaa
-                123456789012😃😃"
-    })
-    .await;
-    cx.simulate_shared_keystrokes(["j"]).await;
-    cx.assert_shared_state(indoc! {"
-        123456789012aaaa
-        123456789012😃ˇ😃"
-    })
-    .await;
-
-    cx.set_shared_state(indoc! {"
-        123456789012aaaaˇaaaaaaaa123456789012
-        wow
-        123456789012😃😃😃😃😃😃123456789012"
-    })
-    .await;
-    cx.simulate_shared_keystrokes(["j", "j"]).await;
-    cx.assert_shared_state(indoc! {"
-        123456789012aaaaaaaaaaaa123456789012
-        wow
-        123456789012😃😃ˇ😃😃😃😃123456789012"
-    })
-    .await;
-}
-
-#[gpui::test]
-async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
-    let mut cx = NeovimBackedTestContext::new(cx).await;
-
-    cx.set_shared_state(indoc! {"
-        one
-        ˇ
-        two"})
-        .await;
-
-    cx.simulate_shared_keystrokes(["}", "}"]).await;
-    cx.assert_shared_state(indoc! {"
-        one
-
-        twˇo"})
-        .await;
-
-    cx.simulate_shared_keystrokes(["{", "{", "{"]).await;
-    cx.assert_shared_state(indoc! {"
-        ˇone
-
-        two"})
-        .await;
-}
-
-#[gpui::test]
-async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
-    let mut cx = VimTestContext::new(cx, true).await;
-
-    cx.set_state(
-        indoc! {"
-        defmodule Test do
-            def test(a, ˇ[_, _] = b), do: IO.puts('hi')
-        end
-    "},
-        Mode::Normal,
-    );
-    cx.simulate_keystrokes(["g", "a"]);
-    cx.assert_state(
-        indoc! {"
-        defmodule Test do
-            def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
-        end
-    "},
-        Mode::Visual,
-    );
-}

crates/vim2/src/test/neovim_backed_binding_test_context.rs 🔗

@@ -1,95 +0,0 @@
-use std::ops::{Deref, DerefMut};
-
-use crate::state::Mode;
-
-use super::{ExemptionFeatures, NeovimBackedTestContext, SUPPORTED_FEATURES};
-
-pub struct NeovimBackedBindingTestContext<'a, const COUNT: usize> {
-    cx: NeovimBackedTestContext<'a>,
-    keystrokes_under_test: [&'static str; COUNT],
-}
-
-impl<'a, const COUNT: usize> NeovimBackedBindingTestContext<'a, COUNT> {
-    pub fn new(
-        keystrokes_under_test: [&'static str; COUNT],
-        cx: NeovimBackedTestContext<'a>,
-    ) -> Self {
-        Self {
-            cx,
-            keystrokes_under_test,
-        }
-    }
-
-    pub fn consume(self) -> NeovimBackedTestContext<'a> {
-        self.cx
-    }
-
-    pub fn binding<const NEW_COUNT: usize>(
-        self,
-        keystrokes: [&'static str; NEW_COUNT],
-    ) -> NeovimBackedBindingTestContext<'a, NEW_COUNT> {
-        self.consume().binding(keystrokes)
-    }
-
-    pub async fn assert(&mut self, marked_positions: &str) {
-        self.cx
-            .assert_binding_matches(self.keystrokes_under_test, marked_positions)
-            .await;
-    }
-
-    pub async fn assert_exempted(&mut self, marked_positions: &str, feature: ExemptionFeatures) {
-        if SUPPORTED_FEATURES.contains(&feature) {
-            self.cx
-                .assert_binding_matches(self.keystrokes_under_test, marked_positions)
-                .await
-        }
-    }
-
-    pub fn assert_manual(
-        &mut self,
-        initial_state: &str,
-        mode_before: Mode,
-        state_after: &str,
-        mode_after: Mode,
-    ) {
-        self.cx.assert_binding(
-            self.keystrokes_under_test,
-            initial_state,
-            mode_before,
-            state_after,
-            mode_after,
-        );
-    }
-
-    pub async fn assert_all(&mut self, marked_positions: &str) {
-        self.cx
-            .assert_binding_matches_all(self.keystrokes_under_test, marked_positions)
-            .await
-    }
-
-    pub async fn assert_all_exempted(
-        &mut self,
-        marked_positions: &str,
-        feature: ExemptionFeatures,
-    ) {
-        if SUPPORTED_FEATURES.contains(&feature) {
-            self.cx
-                .assert_binding_matches_all(self.keystrokes_under_test, marked_positions)
-                .await
-        }
-    }
-}
-
-impl<'a, const COUNT: usize> Deref for NeovimBackedBindingTestContext<'a, COUNT> {
-    type Target = NeovimBackedTestContext<'a>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.cx
-    }
-}
-
-impl<'a, const COUNT: usize> DerefMut for NeovimBackedBindingTestContext<'a, COUNT> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.cx
-    }
-}

crates/vim2/src/test/neovim_backed_test_context.rs 🔗

@@ -1,439 +0,0 @@
-use editor::{scroll::VERTICAL_SCROLL_MARGIN, test::editor_test_context::ContextHandle};
-use gpui::{px, size, Context};
-use indoc::indoc;
-use settings::SettingsStore;
-use std::{
-    ops::{Deref, DerefMut},
-    panic, thread,
-};
-
-use collections::{HashMap, HashSet};
-use language::language_settings::{AllLanguageSettings, SoftWrap};
-use util::test::marked_text_offsets;
-
-use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext};
-use crate::state::Mode;
-
-pub const SUPPORTED_FEATURES: &[ExemptionFeatures] = &[];
-
-/// Enum representing features we have tests for but which don't work, yet. Used
-/// to add exemptions and automatically
-#[derive(PartialEq, Eq)]
-pub enum ExemptionFeatures {
-    // MOTIONS
-    // When an operator completes at the end of the file, an extra newline is left
-    OperatorLastNewlineRemains,
-
-    // OBJECTS
-    // Resulting position after the operation is slightly incorrect for unintuitive reasons.
-    IncorrectLandingPosition,
-    // Operator around the text object at the end of the line doesn't remove whitespace.
-    AroundObjectLeavesWhitespaceAtEndOfLine,
-    // Sentence object on empty lines
-    SentenceOnEmptyLines,
-    // Whitespace isn't included with text objects at the start of the line
-    SentenceAtStartOfLineWithWhitespace,
-    // Whitespace around sentences is slightly incorrect when starting between sentences
-    AroundSentenceStartingBetweenIncludesWrongWhitespace,
-    // Non empty selection with text objects in visual mode
-    NonEmptyVisualTextObjects,
-    // Sentence Doesn't backtrack when its at the end of the file
-    SentenceAfterPunctuationAtEndOfFile,
-}
-
-impl ExemptionFeatures {
-    pub fn supported(&self) -> bool {
-        SUPPORTED_FEATURES.contains(self)
-    }
-}
-
-pub struct NeovimBackedTestContext<'a> {
-    cx: VimTestContext<'a>,
-    // Lookup for exempted assertions. Keyed by the insertion text, and with a value indicating which
-    // bindings are exempted. If None, all bindings are ignored for that insertion text.
-    exemptions: HashMap<String, Option<HashSet<String>>>,
-    neovim: NeovimConnection,
-
-    last_set_state: Option<String>,
-    recent_keystrokes: Vec<String>,
-
-    is_dirty: bool,
-}
-
-impl<'a> NeovimBackedTestContext<'a> {
-    pub async fn new(cx: &'a mut gpui::TestAppContext) -> NeovimBackedTestContext<'a> {
-        // rust stores the name of the test on the current thread.
-        // We use this to automatically name a file that will store
-        // the neovim connection's requests/responses so that we can
-        // run without neovim on CI.
-        let thread = thread::current();
-        let test_name = thread
-            .name()
-            .expect("thread is not named")
-            .split(":")
-            .last()
-            .unwrap()
-            .to_string();
-        Self {
-            cx: VimTestContext::new(cx, true).await,
-            exemptions: Default::default(),
-            neovim: NeovimConnection::new(test_name).await,
-
-            last_set_state: None,
-            recent_keystrokes: Default::default(),
-            is_dirty: false,
-        }
-    }
-
-    pub fn add_initial_state_exemptions(
-        &mut self,
-        marked_positions: &str,
-        missing_feature: ExemptionFeatures, // Feature required to support this exempted test case
-    ) {
-        if !missing_feature.supported() {
-            let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions);
-
-            for cursor_offset in cursor_offsets.iter() {
-                let mut marked_text = unmarked_text.clone();
-                marked_text.insert(*cursor_offset, 'ˇ');
-
-                // None represents all key bindings being exempted for that initial state
-                self.exemptions.insert(marked_text, None);
-            }
-        }
-    }
-
-    pub async fn simulate_shared_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
-        self.neovim.send_keystroke(keystroke_text).await;
-        self.simulate_keystroke(keystroke_text)
-    }
-
-    pub async fn simulate_shared_keystrokes<const COUNT: usize>(
-        &mut self,
-        keystroke_texts: [&str; COUNT],
-    ) {
-        for keystroke_text in keystroke_texts.into_iter() {
-            self.recent_keystrokes.push(keystroke_text.to_string());
-            self.neovim.send_keystroke(keystroke_text).await;
-        }
-        self.simulate_keystrokes(keystroke_texts);
-    }
-
-    pub async fn set_shared_state(&mut self, marked_text: &str) {
-        let mode = if marked_text.contains("»") {
-            Mode::Visual
-        } else {
-            Mode::Normal
-        };
-        self.set_state(marked_text, mode);
-        self.last_set_state = Some(marked_text.to_string());
-        self.recent_keystrokes = Vec::new();
-        self.neovim.set_state(marked_text).await;
-        self.is_dirty = true;
-    }
-
-    pub async fn set_shared_wrap(&mut self, columns: u32) {
-        if columns < 12 {
-            panic!("nvim doesn't support columns < 12")
-        }
-        self.neovim.set_option("wrap").await;
-        self.neovim
-            .set_option(&format!("columns={}", columns))
-            .await;
-
-        self.update(|cx| {
-            cx.update_global(|settings: &mut SettingsStore, cx| {
-                settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-                    settings.defaults.soft_wrap = Some(SoftWrap::PreferredLineLength);
-                    settings.defaults.preferred_line_length = Some(columns);
-                });
-            })
-        })
-    }
-
-    pub async fn set_scroll_height(&mut self, rows: u32) {
-        // match Zed's scrolling behavior
-        self.neovim
-            .set_option(&format!("scrolloff={}", VERTICAL_SCROLL_MARGIN))
-            .await;
-        // +2 to account for the vim command UI at the bottom.
-        self.neovim.set_option(&format!("lines={}", rows + 2)).await;
-        let (line_height, visible_line_count) = self.editor(|editor, cx| {
-            (
-                editor
-                    .style()
-                    .unwrap()
-                    .text
-                    .line_height_in_pixels(cx.rem_size()),
-                editor.visible_line_count().unwrap(),
-            )
-        });
-
-        let window = self.window;
-        let margin = self
-            .update_window(window, |_, cx| {
-                cx.viewport_size().height - line_height * visible_line_count
-            })
-            .unwrap();
-
-        self.simulate_window_resize(
-            self.window,
-            size(px(1000.), margin + (rows as f32) * line_height),
-        );
-    }
-
-    pub async fn set_neovim_option(&mut self, option: &str) {
-        self.neovim.set_option(option).await;
-    }
-
-    pub async fn assert_shared_state(&mut self, marked_text: &str) {
-        self.is_dirty = false;
-        let marked_text = marked_text.replace("•", " ");
-        let neovim = self.neovim_state().await;
-        let editor = self.editor_state();
-        if neovim == marked_text && neovim == editor {
-            return;
-        }
-        let initial_state = self
-            .last_set_state
-            .as_ref()
-            .unwrap_or(&"N/A".to_string())
-            .clone();
-
-        let message = if neovim != marked_text {
-            "Test is incorrect (currently expected != neovim_state)"
-        } else {
-            "Editor does not match nvim behaviour"
-        };
-        panic!(
-            indoc! {"{}
-                # initial state:
-                {}
-                # keystrokes:
-                {}
-                # currently expected:
-                {}
-                # neovim state:
-                {}
-                # zed state:
-                {}"},
-            message,
-            initial_state,
-            self.recent_keystrokes.join(" "),
-            marked_text.replace(" \n", "•\n"),
-            neovim.replace(" \n", "•\n"),
-            editor.replace(" \n", "•\n")
-        )
-    }
-
-    pub async fn assert_shared_clipboard(&mut self, text: &str) {
-        let neovim = self.neovim.read_register('"').await;
-        let editor = self.read_from_clipboard().unwrap().text().clone();
-
-        if text == neovim && text == editor {
-            return;
-        }
-
-        let message = if neovim != text {
-            "Test is incorrect (currently expected != neovim)"
-        } else {
-            "Editor does not match nvim behaviour"
-        };
-
-        let initial_state = self
-            .last_set_state
-            .as_ref()
-            .unwrap_or(&"N/A".to_string())
-            .clone();
-
-        panic!(
-            indoc! {"{}
-                # initial state:
-                {}
-                # keystrokes:
-                {}
-                # currently expected:
-                {}
-                # neovim clipboard:
-                {}
-                # zed clipboard:
-                {}"},
-            message,
-            initial_state,
-            self.recent_keystrokes.join(" "),
-            text,
-            neovim,
-            editor
-        )
-    }
-
-    pub async fn neovim_state(&mut self) -> String {
-        self.neovim.marked_text().await
-    }
-
-    pub async fn neovim_mode(&mut self) -> Mode {
-        self.neovim.mode().await.unwrap()
-    }
-
-    pub async fn assert_state_matches(&mut self) {
-        self.is_dirty = false;
-        let neovim = self.neovim_state().await;
-        let editor = self.editor_state();
-        let initial_state = self
-            .last_set_state
-            .as_ref()
-            .unwrap_or(&"N/A".to_string())
-            .clone();
-
-        if neovim != editor {
-            panic!(
-                indoc! {"Test failed (zed does not match nvim behaviour)
-                    # initial state:
-                    {}
-                    # keystrokes:
-                    {}
-                    # neovim state:
-                    {}
-                    # zed state:
-                    {}"},
-                initial_state,
-                self.recent_keystrokes.join(" "),
-                neovim,
-                editor,
-            )
-        }
-    }
-
-    pub async fn assert_binding_matches<const COUNT: usize>(
-        &mut self,
-        keystrokes: [&str; COUNT],
-        initial_state: &str,
-    ) {
-        if let Some(possible_exempted_keystrokes) = self.exemptions.get(initial_state) {
-            match possible_exempted_keystrokes {
-                Some(exempted_keystrokes) => {
-                    if exempted_keystrokes.contains(&format!("{keystrokes:?}")) {
-                        // This keystroke was exempted for this insertion text
-                        return;
-                    }
-                }
-                None => {
-                    // All keystrokes for this insertion text are exempted
-                    return;
-                }
-            }
-        }
-
-        let _state_context = self.set_shared_state(initial_state).await;
-        let _keystroke_context = self.simulate_shared_keystrokes(keystrokes).await;
-        self.assert_state_matches().await;
-    }
-
-    pub async fn assert_binding_matches_all<const COUNT: usize>(
-        &mut self,
-        keystrokes: [&str; COUNT],
-        marked_positions: &str,
-    ) {
-        let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions);
-
-        for cursor_offset in cursor_offsets.iter() {
-            let mut marked_text = unmarked_text.clone();
-            marked_text.insert(*cursor_offset, 'ˇ');
-
-            self.assert_binding_matches(keystrokes, &marked_text).await;
-        }
-    }
-
-    pub fn each_marked_position(&self, marked_positions: &str) -> Vec<String> {
-        let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions);
-        let mut ret = Vec::with_capacity(cursor_offsets.len());
-
-        for cursor_offset in cursor_offsets.iter() {
-            let mut marked_text = unmarked_text.clone();
-            marked_text.insert(*cursor_offset, 'ˇ');
-            ret.push(marked_text)
-        }
-
-        ret
-    }
-
-    pub async fn assert_neovim_compatible<const COUNT: usize>(
-        &mut self,
-        marked_positions: &str,
-        keystrokes: [&str; COUNT],
-    ) {
-        self.set_shared_state(&marked_positions).await;
-        self.simulate_shared_keystrokes(keystrokes).await;
-        self.assert_state_matches().await;
-    }
-
-    pub async fn assert_matches_neovim<const COUNT: usize>(
-        &mut self,
-        marked_positions: &str,
-        keystrokes: [&str; COUNT],
-        result: &str,
-    ) {
-        self.set_shared_state(marked_positions).await;
-        self.simulate_shared_keystrokes(keystrokes).await;
-        self.assert_shared_state(result).await;
-    }
-
-    pub async fn assert_binding_matches_all_exempted<const COUNT: usize>(
-        &mut self,
-        keystrokes: [&str; COUNT],
-        marked_positions: &str,
-        feature: ExemptionFeatures,
-    ) {
-        if SUPPORTED_FEATURES.contains(&feature) {
-            self.assert_binding_matches_all(keystrokes, marked_positions)
-                .await
-        }
-    }
-
-    pub fn binding<const COUNT: usize>(
-        self,
-        keystrokes: [&'static str; COUNT],
-    ) -> NeovimBackedBindingTestContext<'a, COUNT> {
-        NeovimBackedBindingTestContext::new(keystrokes, self)
-    }
-}
-
-impl<'a> Deref for NeovimBackedTestContext<'a> {
-    type Target = VimTestContext<'a>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.cx
-    }
-}
-
-impl<'a> DerefMut for NeovimBackedTestContext<'a> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.cx
-    }
-}
-
-// a common mistake in tests is to call set_shared_state when
-// you mean asswert_shared_state. This notices that and lets
-// you know.
-impl<'a> Drop for NeovimBackedTestContext<'a> {
-    fn drop(&mut self) {
-        if self.is_dirty {
-            panic!("Test context was dropped after set_shared_state before assert_shared_state")
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use gpui::TestAppContext;
-
-    use crate::test::NeovimBackedTestContext;
-
-    #[gpui::test]
-    async fn neovim_backed_test_context_works(cx: &mut TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-        cx.assert_state_matches().await;
-        cx.set_shared_state("This is a tesˇt").await;
-        cx.assert_state_matches().await;
-    }
-}

crates/vim2/src/test/neovim_connection.rs 🔗

@@ -1,599 +0,0 @@
-use std::path::PathBuf;
-#[cfg(feature = "neovim")]
-use std::{
-    cmp,
-    ops::{Deref, DerefMut, Range},
-};
-
-#[cfg(feature = "neovim")]
-use async_compat::Compat;
-#[cfg(feature = "neovim")]
-use async_trait::async_trait;
-#[cfg(feature = "neovim")]
-use gpui::Keystroke;
-
-#[cfg(feature = "neovim")]
-use language::Point;
-
-#[cfg(feature = "neovim")]
-use nvim_rs::{
-    create::tokio::new_child_cmd, error::LoopError, Handler, Neovim, UiAttachOptions, Value,
-};
-#[cfg(feature = "neovim")]
-use parking_lot::ReentrantMutex;
-use serde::{Deserialize, Serialize};
-#[cfg(feature = "neovim")]
-use tokio::{
-    process::{Child, ChildStdin, Command},
-    task::JoinHandle,
-};
-
-use crate::state::Mode;
-use collections::VecDeque;
-
-// Neovim doesn't like to be started simultaneously from multiple threads. We use this lock
-// to ensure we are only constructing one neovim connection at a time.
-#[cfg(feature = "neovim")]
-static NEOVIM_LOCK: ReentrantMutex<()> = ReentrantMutex::new(());
-
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub enum NeovimData {
-    Put { state: String },
-    Key(String),
-    Get { state: String, mode: Option<Mode> },
-    ReadRegister { name: char, value: String },
-    SetOption { value: String },
-}
-
-pub struct NeovimConnection {
-    data: VecDeque<NeovimData>,
-    #[cfg(feature = "neovim")]
-    test_case_id: String,
-    #[cfg(feature = "neovim")]
-    nvim: Neovim<nvim_rs::compat::tokio::Compat<ChildStdin>>,
-    #[cfg(feature = "neovim")]
-    _join_handle: JoinHandle<Result<(), Box<LoopError>>>,
-    #[cfg(feature = "neovim")]
-    _child: Child,
-}
-
-impl NeovimConnection {
-    pub async fn new(test_case_id: String) -> Self {
-        #[cfg(feature = "neovim")]
-        let handler = NvimHandler {};
-        #[cfg(feature = "neovim")]
-        let (nvim, join_handle, child) = Compat::new(async {
-            // Ensure we don't create neovim connections in parallel
-            let _lock = NEOVIM_LOCK.lock();
-            let (nvim, join_handle, child) = new_child_cmd(
-                &mut Command::new("nvim")
-                    .arg("--embed")
-                    .arg("--clean")
-                    // disable swap (otherwise after about 1000 test runs you run out of swap file names)
-                    .arg("-n")
-                    // disable writing files (just in case)
-                    .arg("-m"),
-                handler,
-            )
-            .await
-            .expect("Could not connect to neovim process");
-
-            nvim.ui_attach(100, 100, &UiAttachOptions::default())
-                .await
-                .expect("Could not attach to ui");
-
-            // Makes system act a little more like zed in terms of indentation
-            nvim.set_option("smartindent", nvim_rs::Value::Boolean(true))
-                .await
-                .expect("Could not set smartindent on startup");
-
-            (nvim, join_handle, child)
-        })
-        .await;
-
-        Self {
-            #[cfg(feature = "neovim")]
-            data: Default::default(),
-            #[cfg(not(feature = "neovim"))]
-            data: Self::read_test_data(&test_case_id),
-            #[cfg(feature = "neovim")]
-            test_case_id,
-            #[cfg(feature = "neovim")]
-            nvim,
-            #[cfg(feature = "neovim")]
-            _join_handle: join_handle,
-            #[cfg(feature = "neovim")]
-            _child: child,
-        }
-    }
-
-    // Sends a keystroke to the neovim process.
-    #[cfg(feature = "neovim")]
-    pub async fn send_keystroke(&mut self, keystroke_text: &str) {
-        let mut keystroke = Keystroke::parse(keystroke_text).unwrap();
-
-        if keystroke.key == "<" {
-            keystroke.key = "lt".to_string()
-        }
-
-        let special = keystroke.modifiers.shift
-            || keystroke.modifiers.control
-            || keystroke.modifiers.alt
-            || keystroke.modifiers.command
-            || keystroke.key.len() > 1;
-        let start = if special { "<" } else { "" };
-        let shift = if keystroke.modifiers.shift { "S-" } else { "" };
-        let ctrl = if keystroke.modifiers.control {
-            "C-"
-        } else {
-            ""
-        };
-        let alt = if keystroke.modifiers.alt { "M-" } else { "" };
-        let cmd = if keystroke.modifiers.command {
-            "D-"
-        } else {
-            ""
-        };
-        let end = if special { ">" } else { "" };
-
-        let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key);
-
-        self.data
-            .push_back(NeovimData::Key(keystroke_text.to_string()));
-        self.nvim
-            .input(&key)
-            .await
-            .expect("Could not input keystroke");
-    }
-
-    #[cfg(not(feature = "neovim"))]
-    pub async fn send_keystroke(&mut self, keystroke_text: &str) {
-        if matches!(self.data.front(), Some(NeovimData::Get { .. })) {
-            self.data.pop_front();
-        }
-        assert_eq!(
-            self.data.pop_front(),
-            Some(NeovimData::Key(keystroke_text.to_string())),
-            "operation does not match recorded script. re-record with --features=neovim"
-        );
-    }
-
-    #[cfg(feature = "neovim")]
-    pub async fn set_state(&mut self, marked_text: &str) {
-        let (text, selections) = parse_state(&marked_text);
-
-        let nvim_buffer = self
-            .nvim
-            .get_current_buf()
-            .await
-            .expect("Could not get neovim buffer");
-        let lines = text
-            .split('\n')
-            .map(|line| line.to_string())
-            .collect::<Vec<_>>();
-
-        nvim_buffer
-            .set_lines(0, -1, false, lines)
-            .await
-            .expect("Could not set nvim buffer text");
-
-        self.nvim
-            .input("<escape>")
-            .await
-            .expect("Could not send escape to nvim");
-        self.nvim
-            .input("<escape>")
-            .await
-            .expect("Could not send escape to nvim");
-
-        let nvim_window = self
-            .nvim
-            .get_current_win()
-            .await
-            .expect("Could not get neovim window");
-
-        if selections.len() != 1 {
-            panic!("must have one selection");
-        }
-        let selection = &selections[0];
-
-        let cursor = selection.start;
-        nvim_window
-            .set_cursor((cursor.row as i64 + 1, cursor.column as i64))
-            .await
-            .expect("Could not set nvim cursor position");
-
-        if !selection.is_empty() {
-            self.nvim
-                .input("v")
-                .await
-                .expect("could not enter visual mode");
-
-            let cursor = selection.end;
-            nvim_window
-                .set_cursor((cursor.row as i64 + 1, cursor.column as i64))
-                .await
-                .expect("Could not set nvim cursor position");
-        }
-
-        if let Some(NeovimData::Get { mode, state }) = self.data.back() {
-            if *mode == Some(Mode::Normal) && *state == marked_text {
-                return;
-            }
-        }
-        self.data.push_back(NeovimData::Put {
-            state: marked_text.to_string(),
-        })
-    }
-
-    #[cfg(not(feature = "neovim"))]
-    pub async fn set_state(&mut self, marked_text: &str) {
-        if let Some(NeovimData::Get { mode, state: text }) = self.data.front() {
-            if *mode == Some(Mode::Normal) && *text == marked_text {
-                return;
-            }
-            self.data.pop_front();
-        }
-        assert_eq!(
-            self.data.pop_front(),
-            Some(NeovimData::Put {
-                state: marked_text.to_string()
-            }),
-            "operation does not match recorded script. re-record with --features=neovim"
-        );
-    }
-
-    #[cfg(feature = "neovim")]
-    pub async fn set_option(&mut self, value: &str) {
-        self.nvim
-            .command_output(format!("set {}", value).as_str())
-            .await
-            .unwrap();
-
-        self.data.push_back(NeovimData::SetOption {
-            value: value.to_string(),
-        })
-    }
-
-    #[cfg(not(feature = "neovim"))]
-    pub async fn set_option(&mut self, value: &str) {
-        if let Some(NeovimData::Get { .. }) = self.data.front() {
-            self.data.pop_front();
-        };
-        assert_eq!(
-            self.data.pop_front(),
-            Some(NeovimData::SetOption {
-                value: value.to_string(),
-            }),
-            "operation does not match recorded script. re-record with --features=neovim"
-        );
-    }
-
-    #[cfg(not(feature = "neovim"))]
-    pub async fn read_register(&mut self, register: char) -> String {
-        if let Some(NeovimData::Get { .. }) = self.data.front() {
-            self.data.pop_front();
-        };
-        if let Some(NeovimData::ReadRegister { name, value }) = self.data.pop_front() {
-            if name == register {
-                return value;
-            }
-        }
-
-        panic!("operation does not match recorded script. re-record with --features=neovim")
-    }
-
-    #[cfg(feature = "neovim")]
-    pub async fn read_register(&mut self, name: char) -> String {
-        let value = self
-            .nvim
-            .command_output(format!("echo getreg('{}')", name).as_str())
-            .await
-            .unwrap();
-
-        self.data.push_back(NeovimData::ReadRegister {
-            name,
-            value: value.clone(),
-        });
-
-        value
-    }
-
-    #[cfg(feature = "neovim")]
-    async fn read_position(&mut self, cmd: &str) -> u32 {
-        self.nvim
-            .command_output(cmd)
-            .await
-            .unwrap()
-            .parse::<u32>()
-            .unwrap()
-    }
-
-    #[cfg(feature = "neovim")]
-    pub async fn state(&mut self) -> (Option<Mode>, String) {
-        let nvim_buffer = self
-            .nvim
-            .get_current_buf()
-            .await
-            .expect("Could not get neovim buffer");
-        let text = nvim_buffer
-            .get_lines(0, -1, false)
-            .await
-            .expect("Could not get buffer text")
-            .join("\n");
-
-        // nvim columns are 1-based, so -1.
-        let mut cursor_row = self.read_position("echo line('.')").await - 1;
-        let mut cursor_col = self.read_position("echo col('.')").await - 1;
-        let mut selection_row = self.read_position("echo line('v')").await - 1;
-        let mut selection_col = self.read_position("echo col('v')").await - 1;
-        let total_rows = self.read_position("echo line('$')").await - 1;
-
-        let nvim_mode_text = self
-            .nvim
-            .get_mode()
-            .await
-            .expect("Could not get mode")
-            .into_iter()
-            .find_map(|(key, value)| {
-                if key.as_str() == Some("mode") {
-                    Some(value.as_str().unwrap().to_owned())
-                } else {
-                    None
-                }
-            })
-            .expect("Could not find mode value");
-
-        let mode = match nvim_mode_text.as_ref() {
-            "i" => Some(Mode::Insert),
-            "n" => Some(Mode::Normal),
-            "v" => Some(Mode::Visual),
-            "V" => Some(Mode::VisualLine),
-            "\x16" => Some(Mode::VisualBlock),
-            _ => None,
-        };
-
-        let mut selections = Vec::new();
-        // Vim uses the index of the first and last character in the selection
-        // Zed uses the index of the positions between the characters, so we need
-        // to add one to the end in visual mode.
-        match mode {
-            Some(Mode::VisualBlock) if selection_row != cursor_row => {
-                // in zed we fake a block selecrtion by using multiple cursors (one per line)
-                // this code emulates that.
-                // to deal with casees where the selection is not perfectly rectangular we extract
-                // the content of the selection via the "a register to get the shape correctly.
-                self.nvim.input("\"aygv").await.unwrap();
-                let content = self.nvim.command_output("echo getreg('a')").await.unwrap();
-                let lines = content.split("\n").collect::<Vec<_>>();
-                let top = cmp::min(selection_row, cursor_row);
-                let left = cmp::min(selection_col, cursor_col);
-                for row in top..=cmp::max(selection_row, cursor_row) {
-                    let content = if row - top >= lines.len() as u32 {
-                        ""
-                    } else {
-                        lines[(row - top) as usize]
-                    };
-                    let line_len = self
-                        .read_position(format!("echo strlen(getline({}))", row + 1).as_str())
-                        .await;
-
-                    if left > line_len {
-                        continue;
-                    }
-
-                    let start = Point::new(row, left);
-                    let end = Point::new(row, left + content.len() as u32);
-                    if cursor_col >= selection_col {
-                        selections.push(start..end)
-                    } else {
-                        selections.push(end..start)
-                    }
-                }
-            }
-            Some(Mode::Visual) | Some(Mode::VisualLine) | Some(Mode::VisualBlock) => {
-                if selection_col > cursor_col {
-                    let selection_line_length =
-                        self.read_position("echo strlen(getline(line('v')))").await;
-                    if selection_line_length > selection_col {
-                        selection_col += 1;
-                    } else if selection_row < total_rows {
-                        selection_col = 0;
-                        selection_row += 1;
-                    }
-                } else {
-                    let cursor_line_length =
-                        self.read_position("echo strlen(getline(line('.')))").await;
-                    if cursor_line_length > cursor_col {
-                        cursor_col += 1;
-                    } else if cursor_row < total_rows {
-                        cursor_col = 0;
-                        cursor_row += 1;
-                    }
-                }
-                selections.push(
-                    Point::new(selection_row, selection_col)..Point::new(cursor_row, cursor_col),
-                )
-            }
-            Some(Mode::Insert) | Some(Mode::Normal) | None => selections
-                .push(Point::new(selection_row, selection_col)..Point::new(cursor_row, cursor_col)),
-        }
-
-        let ranges = encode_ranges(&text, &selections);
-        let state = NeovimData::Get {
-            mode,
-            state: ranges.clone(),
-        };
-
-        if self.data.back() != Some(&state) {
-            self.data.push_back(state.clone());
-        }
-
-        (mode, ranges)
-    }
-
-    #[cfg(not(feature = "neovim"))]
-    pub async fn state(&mut self) -> (Option<Mode>, String) {
-        if let Some(NeovimData::Get { state: raw, mode }) = self.data.front() {
-            (*mode, raw.to_string())
-        } else {
-            panic!("operation does not match recorded script. re-record with --features=neovim");
-        }
-    }
-
-    pub async fn mode(&mut self) -> Option<Mode> {
-        self.state().await.0
-    }
-
-    pub async fn marked_text(&mut self) -> String {
-        self.state().await.1
-    }
-
-    fn test_data_path(test_case_id: &str) -> PathBuf {
-        let mut data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
-        data_path.push("test_data");
-        data_path.push(format!("{}.json", test_case_id));
-        data_path
-    }
-
-    #[cfg(not(feature = "neovim"))]
-    fn read_test_data(test_case_id: &str) -> VecDeque<NeovimData> {
-        let path = Self::test_data_path(test_case_id);
-        let json = std::fs::read_to_string(path).expect(
-            "Could not read test data. Is it generated? Try running test with '--features neovim'",
-        );
-
-        let mut result = VecDeque::new();
-        for line in json.lines() {
-            result.push_back(
-                serde_json::from_str(line)
-                    .expect("invalid test data. regenerate it with '--features neovim'"),
-            );
-        }
-        result
-    }
-
-    #[cfg(feature = "neovim")]
-    fn write_test_data(test_case_id: &str, data: &VecDeque<NeovimData>) {
-        let path = Self::test_data_path(test_case_id);
-        let mut json = Vec::new();
-        for entry in data {
-            serde_json::to_writer(&mut json, entry).unwrap();
-            json.push(b'\n');
-        }
-        std::fs::create_dir_all(path.parent().unwrap())
-            .expect("could not create test data directory");
-        std::fs::write(path, json).expect("could not write out test data");
-    }
-}
-
-#[cfg(feature = "neovim")]
-impl Deref for NeovimConnection {
-    type Target = Neovim<nvim_rs::compat::tokio::Compat<ChildStdin>>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.nvim
-    }
-}
-
-#[cfg(feature = "neovim")]
-impl DerefMut for NeovimConnection {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.nvim
-    }
-}
-
-#[cfg(feature = "neovim")]
-impl Drop for NeovimConnection {
-    fn drop(&mut self) {
-        Self::write_test_data(&self.test_case_id, &self.data);
-    }
-}
-
-#[cfg(feature = "neovim")]
-#[derive(Clone)]
-struct NvimHandler {}
-
-#[cfg(feature = "neovim")]
-#[async_trait]
-impl Handler for NvimHandler {
-    type Writer = nvim_rs::compat::tokio::Compat<ChildStdin>;
-
-    async fn handle_request(
-        &self,
-        _event_name: String,
-        _arguments: Vec<Value>,
-        _neovim: Neovim<Self::Writer>,
-    ) -> Result<Value, Value> {
-        unimplemented!();
-    }
-
-    async fn handle_notify(
-        &self,
-        _event_name: String,
-        _arguments: Vec<Value>,
-        _neovim: Neovim<Self::Writer>,
-    ) {
-    }
-}
-
-#[cfg(feature = "neovim")]
-fn parse_state(marked_text: &str) -> (String, Vec<Range<Point>>) {
-    let (text, ranges) = util::test::marked_text_ranges(marked_text, true);
-    let point_ranges = ranges
-        .into_iter()
-        .map(|byte_range| {
-            let mut point_range = Point::zero()..Point::zero();
-            let mut ix = 0;
-            let mut position = Point::zero();
-            for c in text.chars().chain(['\0']) {
-                if ix == byte_range.start {
-                    point_range.start = position;
-                }
-                if ix == byte_range.end {
-                    point_range.end = position;
-                }
-                let len_utf8 = c.len_utf8();
-                ix += len_utf8;
-                if c == '\n' {
-                    position.row += 1;
-                    position.column = 0;
-                } else {
-                    position.column += len_utf8 as u32;
-                }
-            }
-            point_range
-        })
-        .collect::<Vec<_>>();
-    (text, point_ranges)
-}
-
-#[cfg(feature = "neovim")]
-fn encode_ranges(text: &str, point_ranges: &Vec<Range<Point>>) -> String {
-    let byte_ranges = point_ranges
-        .into_iter()
-        .map(|range| {
-            let mut byte_range = 0..0;
-            let mut ix = 0;
-            let mut position = Point::zero();
-            for c in text.chars().chain(['\0']) {
-                if position == range.start {
-                    byte_range.start = ix;
-                }
-                if position == range.end {
-                    byte_range.end = ix;
-                }
-                let len_utf8 = c.len_utf8();
-                ix += len_utf8;
-                if c == '\n' {
-                    position.row += 1;
-                    position.column = 0;
-                } else {
-                    position.column += len_utf8 as u32;
-                }
-            }
-            byte_range
-        })
-        .collect::<Vec<_>>();
-    util::test::generate_marked_text(text, &byte_ranges[..], true)
-}

crates/vim2/src/test/vim_test_context.rs 🔗

@@ -1,177 +0,0 @@
-use std::ops::{Deref, DerefMut};
-
-use editor::test::{
-    editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
-};
-use futures::Future;
-use gpui::{Context, View, VisualContext};
-use lsp::request;
-use search::BufferSearchBar;
-
-use crate::{state::Operator, *};
-
-pub struct VimTestContext<'a> {
-    cx: EditorLspTestContext<'a>,
-}
-
-impl<'a> VimTestContext<'a> {
-    pub fn init(cx: &mut gpui::TestAppContext) {
-        if cx.has_global::<Vim>() {
-            dbg!("OOPS");
-            return;
-        }
-        cx.update(|cx| {
-            search::init(cx);
-            let settings = SettingsStore::test(cx);
-            cx.set_global(settings);
-            command_palette::init(cx);
-            crate::init(cx);
-        });
-    }
-
-    pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> {
-        Self::init(cx);
-        let lsp = EditorLspTestContext::new_rust(Default::default(), cx).await;
-        Self::new_with_lsp(lsp, enabled)
-    }
-
-    pub async fn new_typescript(cx: &'a mut gpui::TestAppContext) -> VimTestContext<'a> {
-        Self::init(cx);
-        Self::new_with_lsp(
-            EditorLspTestContext::new_typescript(Default::default(), cx).await,
-            true,
-        )
-    }
-
-    pub fn new_with_lsp(mut cx: EditorLspTestContext<'a>, enabled: bool) -> VimTestContext<'a> {
-        cx.update(|cx| {
-            cx.update_global(|store: &mut SettingsStore, cx| {
-                store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
-            });
-            settings::KeymapFile::load_asset("keymaps/default.json", cx).unwrap();
-            settings::KeymapFile::load_asset("keymaps/vim.json", cx).unwrap();
-        });
-
-        // Setup search toolbars and keypress hook
-        cx.update_workspace(|workspace, cx| {
-            observe_keystrokes(cx);
-            workspace.active_pane().update(cx, |pane, cx| {
-                pane.toolbar().update(cx, |toolbar, cx| {
-                    let buffer_search_bar = cx.new_view(BufferSearchBar::new);
-                    toolbar.add_item(buffer_search_bar, cx);
-                    // todo!();
-                    // let project_search_bar = cx.add_view(|_| ProjectSearchBar::new());
-                    // toolbar.add_item(project_search_bar, cx);
-                })
-            });
-            workspace.status_bar().update(cx, |status_bar, cx| {
-                let vim_mode_indicator = cx.new_view(ModeIndicator::new);
-                status_bar.add_right_item(vim_mode_indicator, cx);
-            });
-        });
-
-        Self { cx }
-    }
-
-    pub fn update_view<F, T, R>(&mut self, view: View<T>, update: F) -> R
-    where
-        T: 'static,
-        F: FnOnce(&mut T, &mut ViewContext<T>) -> R + 'static,
-    {
-        let window = self.window.clone();
-        self.update_window(window, move |_, cx| view.update(cx, update))
-            .unwrap()
-    }
-
-    pub fn workspace<F, T>(&mut self, update: F) -> T
-    where
-        F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
-    {
-        self.cx.update_workspace(update)
-    }
-
-    pub fn enable_vim(&mut self) {
-        self.cx.update(|cx| {
-            cx.update_global(|store: &mut SettingsStore, cx| {
-                store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(true));
-            });
-        })
-    }
-
-    pub fn disable_vim(&mut self) {
-        self.cx.update(|cx| {
-            cx.update_global(|store: &mut SettingsStore, cx| {
-                store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(false));
-            });
-        })
-    }
-
-    pub fn mode(&mut self) -> Mode {
-        self.cx.read(|cx| cx.global::<Vim>().state().mode)
-    }
-
-    pub fn active_operator(&mut self) -> Option<Operator> {
-        self.cx
-            .read(|cx| cx.global::<Vim>().state().operator_stack.last().copied())
-    }
-
-    pub fn set_state(&mut self, text: &str, mode: Mode) {
-        let window = self.window;
-        self.cx.set_state(text);
-        self.update_window(window, |_, cx| {
-            Vim::update(cx, |vim, cx| {
-                vim.switch_mode(mode, true, cx);
-            })
-        })
-        .unwrap();
-        self.cx.cx.cx.run_until_parked();
-    }
-
-    #[track_caller]
-    pub fn assert_state(&mut self, text: &str, mode: Mode) {
-        self.assert_editor_state(text);
-        assert_eq!(self.mode(), mode, "{}", self.assertion_context());
-    }
-
-    pub fn assert_binding<const COUNT: usize>(
-        &mut self,
-        keystrokes: [&str; COUNT],
-        initial_state: &str,
-        initial_mode: Mode,
-        state_after: &str,
-        mode_after: Mode,
-    ) {
-        self.set_state(initial_state, initial_mode);
-        self.cx.simulate_keystrokes(keystrokes);
-        self.cx.assert_editor_state(state_after);
-        assert_eq!(self.mode(), mode_after, "{}", self.assertion_context());
-        assert_eq!(self.active_operator(), None, "{}", self.assertion_context());
-    }
-
-    pub fn handle_request<T, F, Fut>(
-        &self,
-        handler: F,
-    ) -> futures::channel::mpsc::UnboundedReceiver<()>
-    where
-        T: 'static + request::Request,
-        T::Params: 'static + Send,
-        F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
-        Fut: 'static + Send + Future<Output = Result<T::Result>>,
-    {
-        self.cx.handle_request::<T, F, Fut>(handler)
-    }
-}
-
-impl<'a> Deref for VimTestContext<'a> {
-    type Target = EditorTestContext<'a>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.cx
-    }
-}
-
-impl<'a> DerefMut for VimTestContext<'a> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.cx
-    }
-}

crates/vim2/src/utils.rs 🔗

@@ -1,50 +0,0 @@
-use editor::{ClipboardSelection, Editor};
-use gpui::{AppContext, ClipboardItem};
-use language::Point;
-
-pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut AppContext) {
-    let selections = editor.selections.all_adjusted(cx);
-    let buffer = editor.buffer().read(cx).snapshot(cx);
-    let mut text = String::new();
-    let mut clipboard_selections = Vec::with_capacity(selections.len());
-    {
-        let mut is_first = true;
-        for selection in selections.iter() {
-            let mut start = selection.start;
-            let end = selection.end;
-            if is_first {
-                is_first = false;
-            } else {
-                text.push_str("\n");
-            }
-            let initial_len = text.len();
-
-            // if the file does not end with \n, and our line-mode selection ends on
-            // that line, we will have expanded the start of the selection to ensure it
-            // contains a newline (so that delete works as expected). We undo that change
-            // here.
-            let is_last_line = linewise
-                && end.row == buffer.max_buffer_row()
-                && buffer.max_point().column > 0
-                && start.row < buffer.max_buffer_row()
-                && start == Point::new(start.row, buffer.line_len(start.row));
-
-            if is_last_line {
-                start = Point::new(start.row + 1, 0);
-            }
-            for chunk in buffer.text_for_range(start..end) {
-                text.push_str(chunk);
-            }
-            if is_last_line {
-                text.push_str("\n");
-            }
-            clipboard_selections.push(ClipboardSelection {
-                len: text.len() - initial_len,
-                is_entire_line: linewise,
-                first_line_indent: buffer.indent_size_for_line(start.row).len,
-            });
-        }
-    }
-
-    cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
-}

crates/vim2/src/vim.rs 🔗

@@ -1,607 +0,0 @@
-#[cfg(test)]
-mod test;
-
-mod command;
-mod editor_events;
-mod insert;
-mod mode_indicator;
-mod motion;
-mod normal;
-mod object;
-mod state;
-mod utils;
-mod visual;
-
-use anyhow::Result;
-use collections::{CommandPaletteFilter, HashMap};
-use command_palette::CommandPaletteInterceptor;
-use editor::{movement, Editor, EditorEvent, EditorMode};
-use gpui::{
-    actions, impl_actions, Action, AppContext, EntityId, KeyContext, Subscription, View,
-    ViewContext, WeakView, WindowContext,
-};
-use language::{CursorShape, Point, Selection, SelectionGoal};
-pub use mode_indicator::ModeIndicator;
-use motion::Motion;
-use normal::normal_replace;
-use serde::Deserialize;
-use settings::{update_settings_file, Settings, SettingsStore};
-use state::{EditorState, Mode, Operator, RecordedSelection, WorkspaceState};
-use std::{ops::Range, sync::Arc};
-use visual::{visual_block_motion, visual_replace};
-use workspace::{self, Workspace};
-
-use crate::state::ReplayableAction;
-
-pub struct VimModeSetting(pub bool);
-
-#[derive(Clone, Deserialize, PartialEq)]
-pub struct SwitchMode(pub Mode);
-
-#[derive(Clone, Deserialize, PartialEq)]
-pub struct PushOperator(pub Operator);
-
-#[derive(Clone, Deserialize, PartialEq)]
-struct Number(usize);
-
-actions!(
-    vim,
-    [Tab, Enter, Object, InnerObject, FindForward, FindBackward]
-);
-// in the workspace namespace so it's not filtered out when vim is disabled.
-actions!(workspace, [ToggleVimMode]);
-
-impl_actions!(vim, [SwitchMode, PushOperator, Number]);
-
-pub fn init(cx: &mut AppContext) {
-    cx.set_global(Vim::default());
-    VimModeSetting::register(cx);
-
-    editor_events::init(cx);
-
-    cx.observe_new_views(|workspace: &mut Workspace, cx| register(workspace, cx))
-        .detach();
-
-    // Any time settings change, update vim mode to match. The Vim struct
-    // will be initialized as disabled by default, so we filter its commands
-    // out when starting up.
-    cx.update_global::<CommandPaletteFilter, _>(|filter, _| {
-        filter.hidden_namespaces.insert("vim");
-    });
-    cx.update_global(|vim: &mut Vim, cx: &mut AppContext| {
-        vim.set_enabled(VimModeSetting::get_global(cx).0, cx)
-    });
-    cx.observe_global::<SettingsStore>(|cx| {
-        cx.update_global(|vim: &mut Vim, cx: &mut AppContext| {
-            vim.set_enabled(VimModeSetting::get_global(cx).0, cx)
-        });
-    })
-    .detach();
-}
-
-fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
-    workspace.register_action(|_: &mut Workspace, &SwitchMode(mode): &SwitchMode, cx| {
-        Vim::update(cx, |vim, cx| vim.switch_mode(mode, false, cx))
-    });
-    workspace.register_action(
-        |_: &mut Workspace, &PushOperator(operator): &PushOperator, cx| {
-            Vim::update(cx, |vim, cx| vim.push_operator(operator, cx))
-        },
-    );
-    workspace.register_action(|_: &mut Workspace, n: &Number, cx: _| {
-        Vim::update(cx, |vim, cx| vim.push_count_digit(n.0, cx));
-    });
-
-    workspace.register_action(|_: &mut Workspace, _: &Tab, cx| {
-        Vim::active_editor_input_ignored(" ".into(), cx)
-    });
-
-    workspace.register_action(|_: &mut Workspace, _: &Enter, cx| {
-        Vim::active_editor_input_ignored("\n".into(), cx)
-    });
-
-    workspace.register_action(|workspace: &mut Workspace, _: &ToggleVimMode, cx| {
-        let fs = workspace.app_state().fs.clone();
-        let currently_enabled = VimModeSetting::get_global(cx).0;
-        update_settings_file::<VimModeSetting>(fs, cx, move |setting| {
-            *setting = Some(!currently_enabled)
-        })
-    });
-
-    normal::register(workspace, cx);
-    insert::register(workspace, cx);
-    motion::register(workspace, cx);
-    command::register(workspace, cx);
-    object::register(workspace, cx);
-    visual::register(workspace, cx);
-}
-
-pub fn observe_keystrokes(cx: &mut WindowContext) {
-    cx.observe_keystrokes(|keystroke_event, cx| {
-        if let Some(action) = keystroke_event
-            .action
-            .as_ref()
-            .map(|action| action.boxed_clone())
-        {
-            Vim::update(cx, |vim, _| {
-                if vim.workspace_state.recording {
-                    vim.workspace_state
-                        .recorded_actions
-                        .push(ReplayableAction::Action(action.boxed_clone()));
-
-                    if vim.workspace_state.stop_recording_after_next_action {
-                        vim.workspace_state.recording = false;
-                        vim.workspace_state.stop_recording_after_next_action = false;
-                    }
-                }
-            });
-
-            // Keystroke is handled by the vim system, so continue forward
-            if action.name().starts_with("vim::") {
-                return;
-            }
-        } else if cx.has_pending_keystrokes() {
-            return;
-        }
-
-        Vim::update(cx, |vim, cx| match vim.active_operator() {
-            Some(
-                Operator::FindForward { .. } | Operator::FindBackward { .. } | Operator::Replace,
-            ) => {}
-            Some(_) => {
-                vim.clear_operator(cx);
-            }
-            _ => {}
-        });
-    })
-    .detach()
-}
-
-#[derive(Default)]
-pub struct Vim {
-    active_editor: Option<WeakView<Editor>>,
-    editor_subscription: Option<Subscription>,
-    enabled: bool,
-    editor_states: HashMap<EntityId, EditorState>,
-    workspace_state: WorkspaceState,
-    default_state: EditorState,
-}
-
-impl Vim {
-    fn read(cx: &mut AppContext) -> &Self {
-        cx.global::<Self>()
-    }
-
-    fn update<F, S>(cx: &mut WindowContext, update: F) -> S
-    where
-        F: FnOnce(&mut Self, &mut WindowContext) -> S,
-    {
-        cx.update_global(update)
-    }
-
-    fn set_active_editor(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
-        self.active_editor = Some(editor.clone().downgrade());
-        self.editor_subscription = Some(cx.subscribe(&editor, |editor, event, cx| match event {
-            EditorEvent::SelectionsChanged { local: true } => {
-                let editor = editor.read(cx);
-                if editor.leader_peer_id().is_none() {
-                    let newest = editor.selections.newest::<usize>(cx);
-                    local_selections_changed(newest, cx);
-                }
-            }
-            EditorEvent::InputIgnored { text } => {
-                Vim::active_editor_input_ignored(text.clone(), cx);
-                Vim::record_insertion(text, None, cx)
-            }
-            EditorEvent::InputHandled {
-                text,
-                utf16_range_to_replace: range_to_replace,
-            } => Vim::record_insertion(text, range_to_replace.clone(), cx),
-            _ => {}
-        }));
-
-        if self.enabled {
-            let editor = editor.read(cx);
-            let editor_mode = editor.mode();
-            let newest_selection_empty = editor.selections.newest::<usize>(cx).is_empty();
-
-            if editor_mode == EditorMode::Full
-                && !newest_selection_empty
-                && self.state().mode == Mode::Normal
-                // When following someone, don't switch vim mode.
-                && editor.leader_peer_id().is_none()
-            {
-                self.switch_mode(Mode::Visual, true, cx);
-            }
-        }
-
-        self.sync_vim_settings(cx);
-    }
-
-    fn record_insertion(
-        text: &Arc<str>,
-        range_to_replace: Option<Range<isize>>,
-        cx: &mut WindowContext,
-    ) {
-        Vim::update(cx, |vim, _| {
-            if vim.workspace_state.recording {
-                vim.workspace_state
-                    .recorded_actions
-                    .push(ReplayableAction::Insertion {
-                        text: text.clone(),
-                        utf16_range_to_replace: range_to_replace,
-                    });
-                if vim.workspace_state.stop_recording_after_next_action {
-                    vim.workspace_state.recording = false;
-                    vim.workspace_state.stop_recording_after_next_action = false;
-                }
-            }
-        });
-    }
-
-    fn update_active_editor<S>(
-        &self,
-        cx: &mut WindowContext,
-        update: impl FnOnce(&mut Editor, &mut ViewContext<Editor>) -> S,
-    ) -> Option<S> {
-        let editor = self.active_editor.clone()?.upgrade()?;
-        Some(editor.update(cx, update))
-    }
-
-    pub fn start_recording(&mut self, cx: &mut WindowContext) {
-        if !self.workspace_state.replaying {
-            self.workspace_state.recording = true;
-            self.workspace_state.recorded_actions = Default::default();
-            self.workspace_state.recorded_count = None;
-
-            let selections = self
-                .active_editor
-                .as_ref()
-                .and_then(|editor| editor.upgrade())
-                .map(|editor| {
-                    let editor = editor.read(cx);
-                    (
-                        editor.selections.oldest::<Point>(cx),
-                        editor.selections.newest::<Point>(cx),
-                    )
-                });
-
-            if let Some((oldest, newest)) = selections {
-                self.workspace_state.recorded_selection = match self.state().mode {
-                    Mode::Visual if newest.end.row == newest.start.row => {
-                        RecordedSelection::SingleLine {
-                            cols: newest.end.column - newest.start.column,
-                        }
-                    }
-                    Mode::Visual => RecordedSelection::Visual {
-                        rows: newest.end.row - newest.start.row,
-                        cols: newest.end.column,
-                    },
-                    Mode::VisualLine => RecordedSelection::VisualLine {
-                        rows: newest.end.row - newest.start.row,
-                    },
-                    Mode::VisualBlock => RecordedSelection::VisualBlock {
-                        rows: newest.end.row.abs_diff(oldest.start.row),
-                        cols: newest.end.column.abs_diff(oldest.start.column),
-                    },
-                    _ => RecordedSelection::None,
-                }
-            } else {
-                self.workspace_state.recorded_selection = RecordedSelection::None;
-            }
-        }
-    }
-
-    pub fn stop_recording(&mut self) {
-        if self.workspace_state.recording {
-            self.workspace_state.stop_recording_after_next_action = true;
-        }
-    }
-
-    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>) {
-        if self.workspace_state.recording {
-            self.workspace_state
-                .recorded_actions
-                .push(ReplayableAction::Action(action.boxed_clone()));
-            self.workspace_state.recording = false;
-            self.workspace_state.stop_recording_after_next_action = false;
-        }
-    }
-
-    pub fn record_current_action(&mut self, cx: &mut WindowContext) {
-        self.start_recording(cx);
-        self.stop_recording();
-    }
-
-    fn switch_mode(&mut self, mode: Mode, leave_selections: bool, cx: &mut WindowContext) {
-        let state = self.state();
-        let last_mode = state.mode;
-        let prior_mode = state.last_mode;
-        self.update_state(|state| {
-            state.last_mode = last_mode;
-            state.mode = mode;
-            state.operator_stack.clear();
-        });
-        if mode != Mode::Insert {
-            self.take_count(cx);
-        }
-
-        // Sync editor settings like clip mode
-        self.sync_vim_settings(cx);
-
-        if leave_selections {
-            return;
-        }
-
-        // Adjust selections
-        self.update_active_editor(cx, |editor, cx| {
-            if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
-            {
-                visual_block_motion(true, editor, cx, |_, point, goal| Some((point, goal)))
-            }
-
-            editor.change_selections(None, cx, |s| {
-                // we cheat with visual block mode and use multiple cursors.
-                // the cost of this cheat is we need to convert back to a single
-                // cursor whenever vim would.
-                if last_mode == Mode::VisualBlock
-                    && (mode != Mode::VisualBlock && mode != Mode::Insert)
-                {
-                    let tail = s.oldest_anchor().tail();
-                    let head = s.newest_anchor().head();
-                    s.select_anchor_ranges(vec![tail..head]);
-                } else if last_mode == Mode::Insert
-                    && prior_mode == Mode::VisualBlock
-                    && mode != Mode::VisualBlock
-                {
-                    let pos = s.first_anchor().head();
-                    s.select_anchor_ranges(vec![pos..pos])
-                }
-
-                s.move_with(|map, selection| {
-                    if last_mode.is_visual() && !mode.is_visual() {
-                        let mut point = selection.head();
-                        if !selection.reversed && !selection.is_empty() {
-                            point = movement::left(map, selection.head());
-                        }
-                        selection.collapse_to(point, selection.goal)
-                    } else if !last_mode.is_visual() && mode.is_visual() {
-                        if selection.is_empty() {
-                            selection.end = movement::right(map, selection.start);
-                        }
-                    }
-                });
-            })
-        });
-    }
-
-    fn push_count_digit(&mut self, number: usize, cx: &mut WindowContext) {
-        if self.active_operator().is_some() {
-            self.update_state(|state| {
-                state.post_count = Some(state.post_count.unwrap_or(0) * 10 + number)
-            })
-        } else {
-            self.update_state(|state| {
-                state.pre_count = Some(state.pre_count.unwrap_or(0) * 10 + number)
-            })
-        }
-        // update the keymap so that 0 works
-        self.sync_vim_settings(cx)
-    }
-
-    fn take_count(&mut self, cx: &mut WindowContext) -> Option<usize> {
-        if self.workspace_state.replaying {
-            return self.workspace_state.recorded_count;
-        }
-
-        let count = if self.state().post_count == None && self.state().pre_count == None {
-            return None;
-        } else {
-            Some(self.update_state(|state| {
-                state.post_count.take().unwrap_or(1) * state.pre_count.take().unwrap_or(1)
-            }))
-        };
-        if self.workspace_state.recording {
-            self.workspace_state.recorded_count = count;
-        }
-        self.sync_vim_settings(cx);
-        count
-    }
-
-    fn push_operator(&mut self, operator: Operator, cx: &mut WindowContext) {
-        if matches!(
-            operator,
-            Operator::Change | Operator::Delete | Operator::Replace
-        ) {
-            self.start_recording(cx)
-        };
-        self.update_state(|state| state.operator_stack.push(operator));
-        self.sync_vim_settings(cx);
-    }
-
-    fn maybe_pop_operator(&mut self) -> Option<Operator> {
-        self.update_state(|state| state.operator_stack.pop())
-    }
-
-    fn pop_operator(&mut self, cx: &mut WindowContext) -> Operator {
-        let popped_operator = self.update_state( |state| state.operator_stack.pop()
-        )            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
-        self.sync_vim_settings(cx);
-        popped_operator
-    }
-    fn clear_operator(&mut self, cx: &mut WindowContext) {
-        self.take_count(cx);
-        self.update_state(|state| state.operator_stack.clear());
-        self.sync_vim_settings(cx);
-    }
-
-    fn active_operator(&self) -> Option<Operator> {
-        self.state().operator_stack.last().copied()
-    }
-
-    fn active_editor_input_ignored(text: Arc<str>, cx: &mut WindowContext) {
-        if text.is_empty() {
-            return;
-        }
-
-        match Vim::read(cx).active_operator() {
-            Some(Operator::FindForward { before }) => {
-                let find = Motion::FindForward {
-                    before,
-                    char: text.chars().next().unwrap(),
-                };
-                Vim::update(cx, |vim, _| {
-                    vim.workspace_state.last_find = Some(find.clone())
-                });
-                motion::motion(find, cx)
-            }
-            Some(Operator::FindBackward { after }) => {
-                let find = Motion::FindBackward {
-                    after,
-                    char: text.chars().next().unwrap(),
-                };
-                Vim::update(cx, |vim, _| {
-                    vim.workspace_state.last_find = Some(find.clone())
-                });
-                motion::motion(find, cx)
-            }
-            Some(Operator::Replace) => match Vim::read(cx).state().mode {
-                Mode::Normal => normal_replace(text, cx),
-                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => visual_replace(text, cx),
-                _ => Vim::update(cx, |vim, cx| vim.clear_operator(cx)),
-            },
-            _ => {}
-        }
-    }
-
-    fn set_enabled(&mut self, enabled: bool, cx: &mut AppContext) {
-        if self.enabled != enabled {
-            self.enabled = enabled;
-
-            cx.update_global::<CommandPaletteFilter, _>(|filter, _| {
-                if self.enabled {
-                    filter.hidden_namespaces.remove("vim");
-                } else {
-                    filter.hidden_namespaces.insert("vim");
-                }
-            });
-
-            if self.enabled {
-                cx.set_global::<CommandPaletteInterceptor>(Box::new(command::command_interceptor));
-            } else if cx.has_global::<CommandPaletteInterceptor>() {
-                let _ = cx.remove_global::<CommandPaletteInterceptor>();
-            }
-
-            if let Some(active_window) = cx.active_window() {
-                active_window
-                    .update(cx, |root_view, cx| {
-                        if self.enabled {
-                            let active_editor = root_view
-                                .downcast::<Workspace>()
-                                .ok()
-                                .and_then(|workspace| workspace.read(cx).active_item(cx))
-                                .and_then(|item| item.downcast::<Editor>());
-                            if let Some(active_editor) = active_editor {
-                                self.set_active_editor(active_editor, cx);
-                            }
-                            self.switch_mode(Mode::Normal, false, cx);
-                        }
-                        self.sync_vim_settings(cx);
-                    })
-                    .ok();
-            }
-        }
-    }
-
-    pub fn state(&self) -> &EditorState {
-        if let Some(active_editor) = self.active_editor.as_ref() {
-            if let Some(state) = self.editor_states.get(&active_editor.entity_id()) {
-                return state;
-            }
-        }
-
-        &self.default_state
-    }
-
-    pub fn update_state<T>(&mut self, func: impl FnOnce(&mut EditorState) -> T) -> T {
-        let mut state = self.state().clone();
-        let ret = func(&mut state);
-
-        if let Some(active_editor) = self.active_editor.as_ref() {
-            self.editor_states.insert(active_editor.entity_id(), state);
-        }
-
-        ret
-    }
-
-    fn sync_vim_settings(&self, cx: &mut WindowContext) {
-        let state = self.state();
-        let cursor_shape = state.cursor_shape();
-
-        self.update_active_editor(cx, |editor, cx| {
-            if self.enabled && editor.mode() == EditorMode::Full {
-                editor.set_cursor_shape(cursor_shape, cx);
-                editor.set_clip_at_line_ends(state.clip_at_line_ends(), cx);
-                editor.set_collapse_matches(true);
-                editor.set_input_enabled(!state.vim_controlled());
-                editor.set_autoindent(state.should_autoindent());
-                editor.selections.line_mode = matches!(state.mode, Mode::VisualLine);
-                let context_layer = state.keymap_context_layer();
-                editor.set_keymap_context_layer::<Self>(context_layer, cx);
-            } else {
-                // Note: set_collapse_matches is not in unhook_vim_settings, as that method is called on blur,
-                // but we need collapse_matches to persist when the search bar is focused.
-                editor.set_collapse_matches(false);
-                self.unhook_vim_settings(editor, cx);
-            }
-        });
-    }
-
-    fn unhook_vim_settings(&self, editor: &mut Editor, cx: &mut ViewContext<Editor>) {
-        editor.set_cursor_shape(CursorShape::Bar, cx);
-        editor.set_clip_at_line_ends(false, cx);
-        editor.set_input_enabled(true);
-        editor.set_autoindent(true);
-        editor.selections.line_mode = false;
-
-        // we set the VimEnabled context on all editors so that we
-        // can distinguish between vim mode and non-vim mode in the BufferSearchBar.
-        // This is a bit of a hack, but currently the search crate does not depend on vim,
-        // and it seems nice to keep it that way.
-        if self.enabled {
-            let mut context = KeyContext::default();
-            context.add("VimEnabled");
-            editor.set_keymap_context_layer::<Self>(context, cx)
-        } else {
-            editor.remove_keymap_context_layer::<Self>(cx);
-        }
-    }
-}
-
-impl Settings for VimModeSetting {
-    const KEY: Option<&'static str> = Some("vim_mode");
-
-    type FileContent = Option<bool>;
-
-    fn load(
-        default_value: &Self::FileContent,
-        user_values: &[&Self::FileContent],
-        _: &mut AppContext,
-    ) -> Result<Self> {
-        Ok(Self(user_values.iter().rev().find_map(|v| **v).unwrap_or(
-            default_value.ok_or_else(Self::missing_default)?,
-        )))
-    }
-}
-
-fn local_selections_changed(newest: Selection<usize>, cx: &mut WindowContext) {
-    Vim::update(cx, |vim, cx| {
-        if vim.enabled && vim.state().mode == Mode::Normal && !newest.is_empty() {
-            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
-                vim.switch_mode(Mode::VisualBlock, false, cx);
-            } else {
-                vim.switch_mode(Mode::Visual, false, cx)
-            }
-        }
-    })
-}

crates/vim2/src/visual.rs 🔗

@@ -1,1034 +0,0 @@
-use anyhow::Result;
-use std::sync::Arc;
-
-use collections::HashMap;
-use editor::{
-    display_map::{DisplaySnapshot, ToDisplayPoint},
-    movement,
-    scroll::autoscroll::Autoscroll,
-    Bias, DisplayPoint, Editor,
-};
-use gpui::{actions, ViewContext, WindowContext};
-use language::{Selection, SelectionGoal};
-use workspace::Workspace;
-
-use crate::{
-    motion::{start_of_line, Motion},
-    object::Object,
-    state::{Mode, Operator},
-    utils::copy_selections_content,
-    Vim,
-};
-
-actions!(
-    vim,
-    [
-        ToggleVisual,
-        ToggleVisualLine,
-        ToggleVisualBlock,
-        VisualDelete,
-        VisualYank,
-        OtherEnd,
-        SelectNext,
-        SelectPrevious,
-    ]
-);
-
-pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
-    workspace.register_action(|_, _: &ToggleVisual, cx: &mut ViewContext<Workspace>| {
-        toggle_mode(Mode::Visual, cx)
-    });
-    workspace.register_action(|_, _: &ToggleVisualLine, cx: &mut ViewContext<Workspace>| {
-        toggle_mode(Mode::VisualLine, cx)
-    });
-    workspace.register_action(
-        |_, _: &ToggleVisualBlock, cx: &mut ViewContext<Workspace>| {
-            toggle_mode(Mode::VisualBlock, cx)
-        },
-    );
-    workspace.register_action(other_end);
-    workspace.register_action(delete);
-    workspace.register_action(yank);
-
-    workspace.register_action(|workspace, action, cx| {
-        select_next(workspace, action, cx).ok();
-    });
-    workspace.register_action(|workspace, action, cx| {
-        select_previous(workspace, action, cx).ok();
-    });
-}
-
-pub fn visual_motion(motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
-    Vim::update(cx, |vim, cx| {
-        vim.update_active_editor(cx, |editor, cx| {
-            let text_layout_details = editor.text_layout_details(cx);
-            if vim.state().mode == Mode::VisualBlock
-                && !matches!(
-                    motion,
-                    Motion::EndOfLine {
-                        display_lines: false
-                    }
-                )
-            {
-                let is_up_or_down = matches!(motion, Motion::Up { .. } | Motion::Down { .. });
-                visual_block_motion(is_up_or_down, editor, cx, |map, point, goal| {
-                    motion.move_point(map, point, goal, times, &text_layout_details)
-                })
-            } else {
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.move_with(|map, selection| {
-                        let was_reversed = selection.reversed;
-                        let mut current_head = selection.head();
-
-                        // our motions assume the current character is after the cursor,
-                        // but in (forward) visual mode the current character is just
-                        // before the end of the selection.
-
-                        // If the file ends with a newline (which is common) we don't do this.
-                        // so that if you go to the end of such a file you can use "up" to go
-                        // to the previous line and have it work somewhat as expected.
-                        if !selection.reversed
-                            && !selection.is_empty()
-                            && !(selection.end.column() == 0 && selection.end == map.max_point())
-                        {
-                            current_head = movement::left(map, selection.end)
-                        }
-
-                        let Some((new_head, goal)) = motion.move_point(
-                            map,
-                            current_head,
-                            selection.goal,
-                            times,
-                            &text_layout_details,
-                        ) else {
-                            return;
-                        };
-
-                        selection.set_head(new_head, goal);
-
-                        // ensure the current character is included in the selection.
-                        if !selection.reversed {
-                            let next_point = if vim.state().mode == Mode::VisualBlock {
-                                movement::saturating_right(map, selection.end)
-                            } else {
-                                movement::right(map, selection.end)
-                            };
-
-                            if !(next_point.column() == 0 && next_point == map.max_point()) {
-                                selection.end = next_point;
-                            }
-                        }
-
-                        // vim always ensures the anchor character stays selected.
-                        // if our selection has reversed, we need to move the opposite end
-                        // to ensure the anchor is still selected.
-                        if was_reversed && !selection.reversed {
-                            selection.start = movement::left(map, selection.start);
-                        } else if !was_reversed && selection.reversed {
-                            selection.end = movement::right(map, selection.end);
-                        }
-                    })
-                });
-            }
-        });
-    });
-}
-
-pub fn visual_block_motion(
-    preserve_goal: bool,
-    editor: &mut Editor,
-    cx: &mut ViewContext<Editor>,
-    mut move_selection: impl FnMut(
-        &DisplaySnapshot,
-        DisplayPoint,
-        SelectionGoal,
-    ) -> Option<(DisplayPoint, SelectionGoal)>,
-) {
-    let text_layout_details = editor.text_layout_details(cx);
-    editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-        let map = &s.display_map();
-        let mut head = s.newest_anchor().head().to_display_point(map);
-        let mut tail = s.oldest_anchor().tail().to_display_point(map);
-
-        let mut head_x = map.x_for_display_point(head, &text_layout_details);
-        let mut tail_x = map.x_for_display_point(tail, &text_layout_details);
-
-        let (start, end) = match s.newest_anchor().goal {
-            SelectionGoal::HorizontalRange { start, end } if preserve_goal => (start, end),
-            SelectionGoal::HorizontalPosition(start) if preserve_goal => (start, start),
-            _ => (tail_x.0, head_x.0),
-        };
-        let mut goal = SelectionGoal::HorizontalRange { start, end };
-
-        let was_reversed = tail_x > head_x;
-        if !was_reversed && !preserve_goal {
-            head = movement::saturating_left(map, head);
-        }
-
-        let Some((new_head, _)) = move_selection(&map, head, goal) else {
-            return;
-        };
-        head = new_head;
-        head_x = map.x_for_display_point(head, &text_layout_details);
-
-        let is_reversed = tail_x > head_x;
-        if was_reversed && !is_reversed {
-            tail = movement::saturating_left(map, tail);
-            tail_x = map.x_for_display_point(tail, &text_layout_details);
-        } else if !was_reversed && is_reversed {
-            tail = movement::saturating_right(map, tail);
-            tail_x = map.x_for_display_point(tail, &text_layout_details);
-        }
-        if !is_reversed && !preserve_goal {
-            head = movement::saturating_right(map, head);
-            head_x = map.x_for_display_point(head, &text_layout_details);
-        }
-
-        let positions = if is_reversed {
-            head_x..tail_x
-        } else {
-            tail_x..head_x
-        };
-
-        if !preserve_goal {
-            goal = SelectionGoal::HorizontalRange {
-                start: positions.start.0,
-                end: positions.end.0,
-            };
-        }
-
-        let mut selections = Vec::new();
-        let mut row = tail.row();
-
-        loop {
-            let layed_out_line = map.layout_row(row, &text_layout_details);
-            let start = DisplayPoint::new(
-                row,
-                layed_out_line.closest_index_for_x(positions.start) as u32,
-            );
-            let mut end = DisplayPoint::new(
-                row,
-                layed_out_line.closest_index_for_x(positions.end) as u32,
-            );
-            if end <= start {
-                if start.column() == map.line_len(start.row()) {
-                    end = start;
-                } else {
-                    end = movement::saturating_right(map, start);
-                }
-            }
-
-            if positions.start <= layed_out_line.width {
-                let selection = Selection {
-                    id: s.new_selection_id(),
-                    start: start.to_point(map),
-                    end: end.to_point(map),
-                    reversed: is_reversed,
-                    goal: goal.clone(),
-                };
-
-                selections.push(selection);
-            }
-            if row == head.row() {
-                break;
-            }
-            if tail.row() > head.row() {
-                row -= 1
-            } else {
-                row += 1
-            }
-        }
-
-        s.select(selections);
-    })
-}
-
-pub fn visual_object(object: Object, cx: &mut WindowContext) {
-    Vim::update(cx, |vim, cx| {
-        if let Some(Operator::Object { around }) = vim.active_operator() {
-            vim.pop_operator(cx);
-            let current_mode = vim.state().mode;
-            let target_mode = object.target_visual_mode(current_mode);
-            if target_mode != current_mode {
-                vim.switch_mode(target_mode, true, cx);
-            }
-
-            vim.update_active_editor(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.move_with(|map, selection| {
-                        let mut head = selection.head();
-
-                        // all our motions assume that the current character is
-                        // after the cursor; however in the case of a visual selection
-                        // the current character is before the cursor.
-                        if !selection.reversed {
-                            head = movement::left(map, head);
-                        }
-
-                        if let Some(range) = object.range(map, head, around) {
-                            if !range.is_empty() {
-                                let expand_both_ways = object.always_expands_both_ways()
-                                    || selection.is_empty()
-                                    || movement::right(map, selection.start) == selection.end;
-
-                                if expand_both_ways {
-                                    selection.start = range.start;
-                                    selection.end = range.end;
-                                } else if selection.reversed {
-                                    selection.start = range.start;
-                                } else {
-                                    selection.end = range.end;
-                                }
-                            }
-                        }
-                    });
-                });
-            });
-        }
-    });
-}
-
-fn toggle_mode(mode: Mode, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        if vim.state().mode == mode {
-            vim.switch_mode(Mode::Normal, false, cx);
-        } else {
-            vim.switch_mode(mode, false, cx);
-        }
-    })
-}
-
-pub fn other_end(_: &mut Workspace, _: &OtherEnd, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.update_active_editor(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| {
-                s.move_with(|_, selection| {
-                    selection.reversed = !selection.reversed;
-                })
-            })
-        })
-    });
-}
-
-pub fn delete(_: &mut Workspace, _: &VisualDelete, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.record_current_action(cx);
-        vim.update_active_editor(cx, |editor, cx| {
-            let mut original_columns: HashMap<_, _> = Default::default();
-            let line_mode = editor.selections.line_mode;
-
-            editor.transact(cx, |editor, cx| {
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.move_with(|map, selection| {
-                        if line_mode {
-                            let mut position = selection.head();
-                            if !selection.reversed {
-                                position = movement::left(map, position);
-                            }
-                            original_columns.insert(selection.id, position.to_point(map).column);
-                        }
-                        selection.goal = SelectionGoal::None;
-                    });
-                });
-                copy_selections_content(editor, line_mode, cx);
-                editor.insert("", cx);
-
-                // Fixup cursor position after the deletion
-                editor.set_clip_at_line_ends(true, cx);
-                editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                    s.move_with(|map, selection| {
-                        let mut cursor = selection.head().to_point(map);
-
-                        if let Some(column) = original_columns.get(&selection.id) {
-                            cursor.column = *column
-                        }
-                        let cursor = map.clip_point(cursor.to_display_point(map), Bias::Left);
-                        selection.collapse_to(cursor, selection.goal)
-                    });
-                    if vim.state().mode == Mode::VisualBlock {
-                        s.select_anchors(vec![s.first_anchor()])
-                    }
-                });
-            })
-        });
-        vim.switch_mode(Mode::Normal, true, cx);
-    });
-}
-
-pub fn yank(_: &mut Workspace, _: &VisualYank, cx: &mut ViewContext<Workspace>) {
-    Vim::update(cx, |vim, cx| {
-        vim.update_active_editor(cx, |editor, cx| {
-            let line_mode = editor.selections.line_mode;
-            copy_selections_content(editor, line_mode, cx);
-            editor.change_selections(None, cx, |s| {
-                s.move_with(|map, selection| {
-                    if line_mode {
-                        selection.start = start_of_line(map, false, selection.start);
-                    };
-                    selection.collapse_to(selection.start, SelectionGoal::None)
-                });
-                if vim.state().mode == Mode::VisualBlock {
-                    s.select_anchors(vec![s.first_anchor()])
-                }
-            });
-        });
-        vim.switch_mode(Mode::Normal, true, cx);
-    });
-}
-
-pub(crate) fn visual_replace(text: Arc<str>, cx: &mut WindowContext) {
-    Vim::update(cx, |vim, cx| {
-        vim.stop_recording();
-        vim.update_active_editor(cx, |editor, cx| {
-            editor.transact(cx, |editor, cx| {
-                let (display_map, selections) = editor.selections.all_adjusted_display(cx);
-
-                // Selections are biased right at the start. So we need to store
-                // anchors that are biased left so that we can restore the selections
-                // after the change
-                let stable_anchors = editor
-                    .selections
-                    .disjoint_anchors()
-                    .into_iter()
-                    .map(|selection| {
-                        let start = selection.start.bias_left(&display_map.buffer_snapshot);
-                        start..start
-                    })
-                    .collect::<Vec<_>>();
-
-                let mut edits = Vec::new();
-                for selection in selections.iter() {
-                    let selection = selection.clone();
-                    for row_range in
-                        movement::split_display_range_by_lines(&display_map, selection.range())
-                    {
-                        let range = row_range.start.to_offset(&display_map, Bias::Right)
-                            ..row_range.end.to_offset(&display_map, Bias::Right);
-                        let text = text.repeat(range.len());
-                        edits.push((range, text));
-                    }
-                }
-
-                editor.buffer().update(cx, |buffer, cx| {
-                    buffer.edit(edits, None, cx);
-                });
-                editor.change_selections(None, cx, |s| s.select_ranges(stable_anchors));
-            });
-        });
-        vim.switch_mode(Mode::Normal, false, cx);
-    });
-}
-
-pub fn select_next(
-    _: &mut Workspace,
-    _: &SelectNext,
-    cx: &mut ViewContext<Workspace>,
-) -> Result<()> {
-    Vim::update(cx, |vim, cx| {
-        let count =
-            vim.take_count(cx)
-                .unwrap_or_else(|| if vim.state().mode.is_visual() { 1 } else { 2 });
-        vim.update_active_editor(cx, |editor, cx| {
-            for _ in 0..count {
-                match editor.select_next(&Default::default(), cx) {
-                    Err(a) => return Err(a),
-                    _ => {}
-                }
-            }
-            Ok(())
-        })
-    })
-    .unwrap_or(Ok(()))
-}
-
-pub fn select_previous(
-    _: &mut Workspace,
-    _: &SelectPrevious,
-    cx: &mut ViewContext<Workspace>,
-) -> Result<()> {
-    Vim::update(cx, |vim, cx| {
-        let count =
-            vim.take_count(cx)
-                .unwrap_or_else(|| if vim.state().mode.is_visual() { 1 } else { 2 });
-        vim.update_active_editor(cx, |editor, cx| {
-            for _ in 0..count {
-                match editor.select_previous(&Default::default(), cx) {
-                    Err(a) => return Err(a),
-                    _ => {}
-                }
-            }
-            Ok(())
-        })
-    })
-    .unwrap_or(Ok(()))
-}
-
-#[cfg(test)]
-mod test {
-    use indoc::indoc;
-    use workspace::item::Item;
-
-    use crate::{
-        state::Mode,
-        test::{NeovimBackedTestContext, VimTestContext},
-    };
-
-    #[gpui::test]
-    async fn test_enter_visual_mode(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {
-            "The ˇquick brown
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx));
-
-        // entering visual mode should select the character
-        // under cursor
-        cx.simulate_shared_keystrokes(["v"]).await;
-        cx.assert_shared_state(indoc! { "The «qˇ»uick brown
-            fox jumps over
-            the lazy dog"})
-            .await;
-        cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
-
-        // forwards motions should extend the selection
-        cx.simulate_shared_keystrokes(["w", "j"]).await;
-        cx.assert_shared_state(indoc! { "The «quick brown
-            fox jumps oˇ»ver
-            the lazy dog"})
-            .await;
-
-        cx.simulate_shared_keystrokes(["escape"]).await;
-        assert_eq!(Mode::Normal, cx.neovim_mode().await);
-        cx.assert_shared_state(indoc! { "The quick brown
-            fox jumps ˇover
-            the lazy dog"})
-            .await;
-
-        // motions work backwards
-        cx.simulate_shared_keystrokes(["v", "k", "b"]).await;
-        cx.assert_shared_state(indoc! { "The «ˇquick brown
-            fox jumps o»ver
-            the lazy dog"})
-            .await;
-
-        // works on empty lines
-        cx.set_shared_state(indoc! {"
-            a
-            ˇ
-            b
-            "})
-            .await;
-        let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx));
-        cx.simulate_shared_keystrokes(["v"]).await;
-        cx.assert_shared_state(indoc! {"
-            a
-            «
-            ˇ»b
-        "})
-            .await;
-        cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
-
-        // toggles off again
-        cx.simulate_shared_keystrokes(["v"]).await;
-        cx.assert_shared_state(indoc! {"
-            a
-            ˇ
-            b
-            "})
-            .await;
-
-        // works at the end of a document
-        cx.set_shared_state(indoc! {"
-            a
-            b
-            ˇ"})
-            .await;
-
-        cx.simulate_shared_keystrokes(["v"]).await;
-        cx.assert_shared_state(indoc! {"
-            a
-            b
-            ˇ"})
-            .await;
-        assert_eq!(cx.mode(), cx.neovim_mode().await);
-    }
-
-    #[gpui::test]
-    async fn test_enter_visual_line_mode(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {
-            "The ˇquick brown
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["shift-v"]).await;
-        cx.assert_shared_state(indoc! { "The «qˇ»uick brown
-            fox jumps over
-            the lazy dog"})
-            .await;
-        assert_eq!(cx.mode(), cx.neovim_mode().await);
-        cx.simulate_shared_keystrokes(["x"]).await;
-        cx.assert_shared_state(indoc! { "fox ˇjumps over
-        the lazy dog"})
-            .await;
-
-        // it should work on empty lines
-        cx.set_shared_state(indoc! {"
-            a
-            ˇ
-            b"})
-            .await;
-        cx.simulate_shared_keystrokes(["shift-v"]).await;
-        cx.assert_shared_state(indoc! { "
-            a
-            «
-            ˇ»b"})
-            .await;
-        cx.simulate_shared_keystrokes(["x"]).await;
-        cx.assert_shared_state(indoc! { "
-            a
-            ˇb"})
-            .await;
-
-        // it should work at the end of the document
-        cx.set_shared_state(indoc! {"
-            a
-            b
-            ˇ"})
-            .await;
-        let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx));
-        cx.simulate_shared_keystrokes(["shift-v"]).await;
-        cx.assert_shared_state(indoc! {"
-            a
-            b
-            ˇ"})
-            .await;
-        assert_eq!(cx.mode(), cx.neovim_mode().await);
-        cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
-        cx.simulate_shared_keystrokes(["x"]).await;
-        cx.assert_shared_state(indoc! {"
-            a
-            ˇb"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_visual_delete(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.assert_binding_matches(["v", "w"], "The quick ˇbrown")
-            .await;
-
-        cx.assert_binding_matches(["v", "w", "x"], "The quick ˇbrown")
-            .await;
-        cx.assert_binding_matches(
-            ["v", "w", "j", "x"],
-            indoc! {"
-                The ˇquick brown
-                fox jumps over
-                the lazy dog"},
-        )
-        .await;
-        // Test pasting code copied on delete
-        cx.simulate_shared_keystrokes(["j", "p"]).await;
-        cx.assert_state_matches().await;
-
-        let mut cx = cx.binding(["v", "w", "j", "x"]);
-        cx.assert_all(indoc! {"
-                The ˇquick brown
-                fox jumps over
-                the ˇlazy dog"})
-            .await;
-        let mut cx = cx.binding(["v", "b", "k", "x"]);
-        cx.assert_all(indoc! {"
-                The ˇquick brown
-                fox jumps ˇover
-                the ˇlazy dog"})
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_visual_line_delete(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {"
-                The quˇick brown
-                fox jumps over
-                the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["shift-v", "x"]).await;
-        cx.assert_state_matches().await;
-
-        // Test pasting code copied on delete
-        cx.simulate_shared_keystroke("p").await;
-        cx.assert_state_matches().await;
-
-        cx.set_shared_state(indoc! {"
-                The quick brown
-                fox jumps over
-                the laˇzy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["shift-v", "x"]).await;
-        cx.assert_state_matches().await;
-        cx.assert_shared_clipboard("the lazy dog\n").await;
-
-        for marked_text in cx.each_marked_position(indoc! {"
-                        The quˇick brown
-                        fox jumps over
-                        the lazy dog"})
-        {
-            cx.set_shared_state(&marked_text).await;
-            cx.simulate_shared_keystrokes(["shift-v", "j", "x"]).await;
-            cx.assert_state_matches().await;
-            // Test pasting code copied on delete
-            cx.simulate_shared_keystroke("p").await;
-            cx.assert_state_matches().await;
-        }
-
-        cx.set_shared_state(indoc! {"
-            The ˇlong line
-            should not
-            crash
-            "})
-            .await;
-        cx.simulate_shared_keystrokes(["shift-v", "$", "x"]).await;
-        cx.assert_state_matches().await;
-    }
-
-    #[gpui::test]
-    async fn test_visual_yank(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state("The quick ˇbrown").await;
-        cx.simulate_shared_keystrokes(["v", "w", "y"]).await;
-        cx.assert_shared_state("The quick ˇbrown").await;
-        cx.assert_shared_clipboard("brown").await;
-
-        cx.set_shared_state(indoc! {"
-                The ˇquick brown
-                fox jumps over
-                the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "w", "j", "y"]).await;
-        cx.assert_shared_state(indoc! {"
-                    The ˇquick brown
-                    fox jumps over
-                    the lazy dog"})
-            .await;
-        cx.assert_shared_clipboard(indoc! {"
-                quick brown
-                fox jumps o"})
-            .await;
-
-        cx.set_shared_state(indoc! {"
-                    The quick brown
-                    fox jumps over
-                    the ˇlazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "w", "j", "y"]).await;
-        cx.assert_shared_state(indoc! {"
-                    The quick brown
-                    fox jumps over
-                    the ˇlazy dog"})
-            .await;
-        cx.assert_shared_clipboard("lazy d").await;
-        cx.simulate_shared_keystrokes(["shift-v", "y"]).await;
-        cx.assert_shared_clipboard("the lazy dog\n").await;
-
-        let mut cx = cx.binding(["v", "b", "k", "y"]);
-        cx.set_shared_state(indoc! {"
-                    The ˇquick brown
-                    fox jumps over
-                    the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["v", "b", "k", "y"]).await;
-        cx.assert_shared_state(indoc! {"
-                    ˇThe quick brown
-                    fox jumps over
-                    the lazy dog"})
-            .await;
-        assert_eq!(
-            cx.read_from_clipboard()
-                .map(|item| item.text().clone())
-                .unwrap(),
-            "The q"
-        );
-
-        cx.set_shared_state(indoc! {"
-                    The quick brown
-                    fox ˇjumps over
-                    the lazy dog"})
-            .await;
-        cx.simulate_shared_keystrokes(["shift-v", "shift-g", "shift-y"])
-            .await;
-        cx.assert_shared_state(indoc! {"
-                    The quick brown
-                    ˇfox jumps over
-                    the lazy dog"})
-            .await;
-        cx.assert_shared_clipboard("fox jumps over\nthe lazy dog\n")
-            .await;
-    }
-
-    #[gpui::test]
-    async fn test_visual_block_mode(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {
-            "The ˇquick brown
-             fox jumps over
-             the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["ctrl-v"]).await;
-        cx.assert_shared_state(indoc! {
-            "The «qˇ»uick brown
-            fox jumps over
-            the lazy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["2", "down"]).await;
-        cx.assert_shared_state(indoc! {
-            "The «qˇ»uick brown
-            fox «jˇ»umps over
-            the «lˇ»azy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["e"]).await;
-        cx.assert_shared_state(indoc! {
-            "The «quicˇ»k brown
-            fox «jumpˇ»s over
-            the «lazyˇ» dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["^"]).await;
-        cx.assert_shared_state(indoc! {
-            "«ˇThe q»uick brown
-            «ˇfox j»umps over
-            «ˇthe l»azy dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["$"]).await;
-        cx.assert_shared_state(indoc! {
-            "The «quick brownˇ»
-            fox «jumps overˇ»
-            the «lazy dogˇ»"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["shift-f", " "]).await;
-        cx.assert_shared_state(indoc! {
-            "The «quickˇ» brown
-            fox «jumpsˇ» over
-            the «lazy ˇ»dog"
-        })
-        .await;
-
-        // toggling through visual mode works as expected
-        cx.simulate_shared_keystrokes(["v"]).await;
-        cx.assert_shared_state(indoc! {
-            "The «quick brown
-            fox jumps over
-            the lazy ˇ»dog"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["ctrl-v"]).await;
-        cx.assert_shared_state(indoc! {
-            "The «quickˇ» brown
-            fox «jumpsˇ» over
-            the «lazy ˇ»dog"
-        })
-        .await;
-
-        cx.set_shared_state(indoc! {
-            "The ˇquick
-             brown
-             fox
-             jumps over the
-
-             lazy dog
-            "
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "down", "down"])
-            .await;
-        cx.assert_shared_state(indoc! {
-            "The«ˇ q»uick
-            bro«ˇwn»
-            foxˇ
-            jumps over the
-
-            lazy dog
-            "
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["down"]).await;
-        cx.assert_shared_state(indoc! {
-            "The «qˇ»uick
-            brow«nˇ»
-            fox
-            jump«sˇ» over the
-
-            lazy dog
-            "
-        })
-        .await;
-        cx.simulate_shared_keystroke("left").await;
-        cx.assert_shared_state(indoc! {
-            "The«ˇ q»uick
-            bro«ˇwn»
-            foxˇ
-            jum«ˇps» over the
-
-            lazy dog
-            "
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["s", "o", "escape"]).await;
-        cx.assert_shared_state(indoc! {
-            "Theˇouick
-            broo
-            foxo
-            jumo over the
-
-            lazy dog
-            "
-        })
-        .await;
-
-        //https://github.com/zed-industries/community/issues/1950
-        cx.set_shared_state(indoc! {
-            "Theˇ quick brown
-
-            fox jumps over
-            the lazy dog
-            "
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["l", "ctrl-v", "j", "j"])
-            .await;
-        cx.assert_shared_state(indoc! {
-            "The «qˇ»uick brown
-
-            fox «jˇ»umps over
-            the lazy dog
-            "
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_visual_block_issue_2123(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {
-            "The ˇquick brown
-            fox jumps over
-            the lazy dog
-            "
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "right", "down"])
-            .await;
-        cx.assert_shared_state(indoc! {
-            "The «quˇ»ick brown
-            fox «juˇ»mps over
-            the lazy dog
-            "
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_visual_block_insert(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state(indoc! {
-            "ˇThe quick brown
-            fox jumps over
-            the lazy dog
-            "
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "9", "down"]).await;
-        cx.assert_shared_state(indoc! {
-            "«Tˇ»he quick brown
-            «fˇ»ox jumps over
-            «tˇ»he lazy dog
-            ˇ"
-        })
-        .await;
-
-        cx.simulate_shared_keystrokes(["shift-i", "k", "escape"])
-            .await;
-        cx.assert_shared_state(indoc! {
-            "ˇkThe quick brown
-            kfox jumps over
-            kthe lazy dog
-            k"
-        })
-        .await;
-
-        cx.set_shared_state(indoc! {
-            "ˇThe quick brown
-            fox jumps over
-            the lazy dog
-            "
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "9", "down"]).await;
-        cx.assert_shared_state(indoc! {
-            "«Tˇ»he quick brown
-            «fˇ»ox jumps over
-            «tˇ»he lazy dog
-            ˇ"
-        })
-        .await;
-        cx.simulate_shared_keystrokes(["c", "k", "escape"]).await;
-        cx.assert_shared_state(indoc! {
-            "ˇkhe quick brown
-            kox jumps over
-            khe lazy dog
-            k"
-        })
-        .await;
-    }
-
-    #[gpui::test]
-    async fn test_visual_object(cx: &mut gpui::TestAppContext) {
-        let mut cx = NeovimBackedTestContext::new(cx).await;
-
-        cx.set_shared_state("hello (in [parˇens] o)").await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "l"]).await;
-        cx.simulate_shared_keystrokes(["a", "]"]).await;
-        cx.assert_shared_state("hello (in «[parens]ˇ» o)").await;
-        assert_eq!(cx.mode(), Mode::Visual);
-        cx.simulate_shared_keystrokes(["i", "("]).await;
-        cx.assert_shared_state("hello («in [parens] oˇ»)").await;
-
-        cx.set_shared_state("hello in a wˇord again.").await;
-        cx.simulate_shared_keystrokes(["ctrl-v", "l", "i", "w"])
-            .await;
-        cx.assert_shared_state("hello in a w«ordˇ» again.").await;
-        assert_eq!(cx.mode(), Mode::VisualBlock);
-        cx.simulate_shared_keystrokes(["o", "a", "s"]).await;
-        cx.assert_shared_state("«ˇhello in a word» again.").await;
-        assert_eq!(cx.mode(), Mode::Visual);
-    }
-
-    #[gpui::test]
-    async fn test_mode_across_command(cx: &mut gpui::TestAppContext) {
-        let mut cx = VimTestContext::new(cx, true).await;
-
-        cx.set_state("aˇbc", Mode::Normal);
-        cx.simulate_keystrokes(["ctrl-v"]);
-        assert_eq!(cx.mode(), Mode::VisualBlock);
-        cx.simulate_keystrokes(["cmd-shift-p", "escape"]);
-        assert_eq!(cx.mode(), Mode::VisualBlock);
-    }
-}

crates/vim2/test_data/test_a.json 🔗

@@ -1,6 +0,0 @@
-{"Put":{"state":"The qˇuick"}}
-{"Key":"a"}
-{"Get":{"state":"The quˇick","mode":"Insert"}}
-{"Put":{"state":"The quicˇk"}}
-{"Key":"a"}
-{"Get":{"state":"The quickˇ","mode":"Insert"}}

crates/vim2/test_data/test_b.json 🔗

@@ -1,54 +0,0 @@
-{"Put":{"state":"ˇThe quick-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"b"}
-{"Get":{"state":"ˇThe quick-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The ˇquick-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"b"}
-{"Get":{"state":"ˇThe quick-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quickˇ-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"b"}
-{"Get":{"state":"The ˇquick-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-ˇbrown\n\n\nfox_jumps over\nthe"}}
-{"Key":"b"}
-{"Get":{"state":"The quickˇ-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\nˇ\n\nfox_jumps over\nthe"}}
-{"Key":"b"}
-{"Get":{"state":"The quick-ˇbrown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\n\nˇ\nfox_jumps over\nthe"}}
-{"Key":"b"}
-{"Get":{"state":"The quick-brown\nˇ\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\n\n\nˇfox_jumps over\nthe"}}
-{"Key":"b"}
-{"Get":{"state":"The quick-brown\n\nˇ\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\n\n\nfox_jumps ˇover\nthe"}}
-{"Key":"b"}
-{"Get":{"state":"The quick-brown\n\n\nˇfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\n\n\nfox_jumps over\nˇthe"}}
-{"Key":"b"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps ˇover\nthe","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-b"}
-{"Get":{"state":"ˇThe quick-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The ˇquick-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-b"}
-{"Get":{"state":"ˇThe quick-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quickˇ-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-b"}
-{"Get":{"state":"The ˇquick-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-ˇbrown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-b"}
-{"Get":{"state":"The ˇquick-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\nˇ\n\nfox_jumps over\nthe"}}
-{"Key":"shift-b"}
-{"Get":{"state":"The ˇquick-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\n\nˇ\nfox_jumps over\nthe"}}
-{"Key":"shift-b"}
-{"Get":{"state":"The quick-brown\nˇ\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\n\n\nˇfox_jumps over\nthe"}}
-{"Key":"shift-b"}
-{"Get":{"state":"The quick-brown\n\nˇ\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\n\n\nfox_jumps ˇover\nthe"}}
-{"Key":"shift-b"}
-{"Get":{"state":"The quick-brown\n\n\nˇfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-brown\n\n\nfox_jumps over\nˇthe"}}
-{"Key":"shift-b"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps ˇover\nthe","mode":"Normal"}}

crates/vim2/test_data/test_backspace.json 🔗

@@ -1,9 +0,0 @@
-{"Put":{"state":"ˇThe quick\nbrown"}}
-{"Key":"backspace"}
-{"Get":{"state":"ˇThe quick\nbrown","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown"}}
-{"Key":"backspace"}
-{"Get":{"state":"The ˇquick\nbrown","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇbrown"}}
-{"Key":"backspace"}
-{"Get":{"state":"The quicˇk\nbrown","mode":"Normal"}}

crates/vim2/test_data/test_capital_f_and_capital_t.json 🔗

@@ -1,570 +0,0 @@
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n"}}
-{"Key":"1"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n"}}
-{"Key":"1"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n"}}
-{"Key":"2"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n"}}
-{"Key":"2"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n"}}
-{"Key":"3"}
-{"Key":"shift-f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ   \nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n"}}
-{"Key":"3"}
-{"Key":"shift-t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n   \nˇb\n","mode":"Normal"}}

crates/vim2/test_data/test_cc.json 🔗

@@ -1,24 +0,0 @@
-{"Put":{"state":"ˇ"}}
-{"Key":"c"}
-{"Key":"c"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The ˇquick"}}
-{"Key":"c"}
-{"Key":"c"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The quˇick\nbrown fox\njumps over"}}
-{"Key":"c"}
-{"Key":"c"}
-{"Get":{"state":"ˇ\nbrown fox\njumps over","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown ˇfox\njumps over"}}
-{"Key":"c"}
-{"Key":"c"}
-{"Get":{"state":"The quick\nˇ\njumps over","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown fox\njumps ˇover"}}
-{"Key":"c"}
-{"Key":"c"}
-{"Get":{"state":"The quick\nbrown fox\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick\nˇ\nbrown fox"}}
-{"Key":"c"}
-{"Key":"c"}
-{"Get":{"state":"The quick\nˇ\nbrown fox","mode":"Insert"}}

crates/vim2/test_data/test_change_0.json 🔗

@@ -1,8 +0,0 @@
-{"Put":{"state":"The qˇuick\nbrown fox"}}
-{"Key":"c"}
-{"Key":"0"}
-{"Get":{"state":"ˇuick\nbrown fox","mode":"Insert"}}
-{"Put":{"state":"The quick\nˇ\nbrown fox"}}
-{"Key":"c"}
-{"Key":"0"}
-{"Get":{"state":"The quick\nˇ\nbrown fox","mode":"Insert"}}

crates/vim2/test_data/test_change_b.json 🔗

@@ -1,24 +0,0 @@
-{"Put":{"state":"Teˇst Test"}}
-{"Key":"c"}
-{"Key":"b"}
-{"Get":{"state":"ˇst Test","mode":"Insert"}}
-{"Put":{"state":"Test ˇtest"}}
-{"Key":"c"}
-{"Key":"b"}
-{"Get":{"state":"ˇtest","mode":"Insert"}}
-{"Put":{"state":"Test1 test2 ˇtest3"}}
-{"Key":"c"}
-{"Key":"b"}
-{"Get":{"state":"Test1 ˇtest3","mode":"Insert"}}
-{"Put":{"state":"Test test\nˇtest"}}
-{"Key":"c"}
-{"Key":"b"}
-{"Get":{"state":"Test ˇ\ntest","mode":"Insert"}}
-{"Put":{"state":"Test test\nˇ\ntest"}}
-{"Key":"c"}
-{"Key":"b"}
-{"Get":{"state":"Test ˇ\n\ntest","mode":"Insert"}}
-{"Put":{"state":"Test test-test ˇtest"}}
-{"Key":"c"}
-{"Key":"shift-b"}
-{"Get":{"state":"Test ˇtest","mode":"Insert"}}

crates/vim2/test_data/test_change_backspace.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"Teˇst"}}
-{"Key":"c"}
-{"Key":"backspace"}
-{"Get":{"state":"Tˇst","mode":"Insert"}}
-{"Put":{"state":"Tˇest"}}
-{"Key":"c"}
-{"Key":"backspace"}
-{"Get":{"state":"ˇest","mode":"Insert"}}
-{"Put":{"state":"ˇTest"}}
-{"Key":"c"}
-{"Key":"backspace"}
-{"Get":{"state":"ˇTest","mode":"Insert"}}
-{"Put":{"state":"Test\nˇtest"}}
-{"Key":"c"}
-{"Key":"backspace"}
-{"Get":{"state":"Testˇtest","mode":"Insert"}}

crates/vim2/test_data/test_change_case.json 🔗

@@ -1,23 +0,0 @@
-{"Put":{"state":"ˇabC\n"}}
-{"Key":"~"}
-{"Get":{"state":"AˇbC\n","mode":"Normal"}}
-{"Key":"2"}
-{"Key":"~"}
-{"Get":{"state":"ABˇc\n","mode":"Normal"}}
-{"Put":{"state":"a😀C«dÉ1*fˇ»\n"}}
-{"Key":"~"}
-{"Get":{"state":"a😀CˇDé1*F\n","mode":"Normal"}}
-{"Key":"~"}
-{"Put":{"state":"aˇC😀é1*F\n"}}
-{"Key":"4"}
-{"Key":"~"}
-{"Get":{"state":"ac😀É1ˇ*F\n","mode":"Normal"}}
-{"Put":{"state":"abˇC\n"}}
-{"Key":"shift-v"}
-{"Key":"~"}
-{"Get":{"state":"ˇABc\n","mode":"Normal"}}
-{"Put":{"state":"ˇaa\nbb\ncc"}}
-{"Key":"ctrl-v"}
-{"Key":"j"}
-{"Key":"~"}
-{"Get":{"state":"ˇAa\nBb\ncc","mode":"Normal"}}

crates/vim2/test_data/test_change_e.json 🔗

@@ -1,24 +0,0 @@
-{"Put":{"state":"Teˇst Test"}}
-{"Key":"c"}
-{"Key":"e"}
-{"Get":{"state":"Teˇ Test","mode":"Insert"}}
-{"Put":{"state":"Tˇest test"}}
-{"Key":"c"}
-{"Key":"e"}
-{"Get":{"state":"Tˇ test","mode":"Insert"}}
-{"Put":{"state":"Test teˇst\ntest"}}
-{"Key":"c"}
-{"Key":"e"}
-{"Get":{"state":"Test teˇ\ntest","mode":"Insert"}}
-{"Put":{"state":"Test tesˇt\ntest"}}
-{"Key":"c"}
-{"Key":"e"}
-{"Get":{"state":"Test tesˇ","mode":"Insert"}}
-{"Put":{"state":"Test test\nˇ\ntest"}}
-{"Key":"c"}
-{"Key":"e"}
-{"Get":{"state":"Test test\nˇ","mode":"Insert"}}
-{"Put":{"state":"Test teˇst-test test"}}
-{"Key":"c"}
-{"Key":"shift-e"}
-{"Get":{"state":"Test teˇ test","mode":"Insert"}}

crates/vim2/test_data/test_change_end_of_document.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"The quick\nbrownˇ fox\njumps over\nthe lazy"}}
-{"Key":"c"}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrownˇ fox\njumps over\nthe lazy"}}
-{"Key":"c"}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown fox\njumps over\nthe lˇazy"}}
-{"Key":"c"}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\nbrown fox\njumps over\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown fox\njumps over\nˇ"}}
-{"Key":"c"}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\nbrown fox\njumps over\nˇ","mode":"Insert"}}

crates/vim2/test_data/test_change_end_of_line.json 🔗

@@ -1,8 +0,0 @@
-{"Put":{"state":"The qˇuick\nbrown fox"}}
-{"Key":"c"}
-{"Key":"$"}
-{"Get":{"state":"The qˇ\nbrown fox","mode":"Insert"}}
-{"Put":{"state":"The quick\nˇ\nbrown fox"}}
-{"Key":"c"}
-{"Key":"$"}
-{"Get":{"state":"The quick\nˇ\nbrown fox","mode":"Insert"}}

crates/vim2/test_data/test_change_gg.json 🔗

@@ -1,20 +0,0 @@
-{"Put":{"state":"The quick\nbrownˇ fox\njumps over\nthe lazy"}}
-{"Key":"c"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"ˇ\njumps over\nthe lazy","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown fox\njumps over\nthe lˇazy"}}
-{"Key":"c"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The qˇuick\nbrown fox\njumps over\nthe lazy"}}
-{"Key":"c"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"ˇ\nbrown fox\njumps over\nthe lazy","mode":"Insert"}}
-{"Put":{"state":"ˇ\nbrown fox\njumps over\nthe lazy"}}
-{"Key":"c"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"ˇ\nbrown fox\njumps over\nthe lazy","mode":"Insert"}}

crates/vim2/test_data/test_change_h.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"Teˇst"}}
-{"Key":"c"}
-{"Key":"h"}
-{"Get":{"state":"Tˇst","mode":"Insert"}}
-{"Put":{"state":"Tˇest"}}
-{"Key":"c"}
-{"Key":"h"}
-{"Get":{"state":"ˇest","mode":"Insert"}}
-{"Put":{"state":"ˇTest"}}
-{"Key":"c"}
-{"Key":"h"}
-{"Get":{"state":"ˇTest","mode":"Insert"}}
-{"Put":{"state":"Test\nˇtest"}}
-{"Key":"c"}
-{"Key":"h"}
-{"Get":{"state":"Test\nˇtest","mode":"Insert"}}

crates/vim2/test_data/test_change_j.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"The quick\nbrown ˇfox\njumps over"}}
-{"Key":"c"}
-{"Key":"j"}
-{"Get":{"state":"The quick\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown fox\njumps ˇover"}}
-{"Key":"c"}
-{"Key":"j"}
-{"Get":{"state":"The quick\nbrown fox\njumps ˇover","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown fox\njumps over"}}
-{"Key":"c"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\njumps over","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown fox\nˇ"}}
-{"Key":"c"}
-{"Key":"j"}
-{"Get":{"state":"The quick\nbrown fox\nˇ","mode":"Normal"}}

crates/vim2/test_data/test_change_k.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"The quick\nbrown ˇfox\njumps over"}}
-{"Key":"c"}
-{"Key":"k"}
-{"Get":{"state":"ˇ\njumps over","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown fox\njumps ˇover"}}
-{"Key":"c"}
-{"Key":"k"}
-{"Get":{"state":"The quick\nˇ","mode":"Insert"}}
-{"Put":{"state":"The qˇuick\nbrown fox\njumps over"}}
-{"Key":"c"}
-{"Key":"k"}
-{"Get":{"state":"The qˇuick\nbrown fox\njumps over","mode":"Normal"}}
-{"Put":{"state":"ˇ\nbrown fox\njumps over"}}
-{"Key":"c"}
-{"Key":"k"}
-{"Get":{"state":"ˇ\nbrown fox\njumps over","mode":"Normal"}}

crates/vim2/test_data/test_change_l.json 🔗

@@ -1,8 +0,0 @@
-{"Put":{"state":"Teˇst"}}
-{"Key":"c"}
-{"Key":"l"}
-{"Get":{"state":"Teˇt","mode":"Insert"}}
-{"Put":{"state":"Tesˇt"}}
-{"Key":"c"}
-{"Key":"l"}
-{"Get":{"state":"Tesˇ","mode":"Insert"}}

crates/vim2/test_data/test_change_sentence_object.json 🔗

@@ -1,270 +0,0 @@
-{"Put":{"state":"ˇThe quick brown? Fox Jumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Fox Jumps! Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick ˇbrown? Fox Jumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Fox Jumps! Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ? Fox Jumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Fox Jumps! Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown?ˇ Fox Jumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown?ˇFox Jumps! Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown? ˇFox Jumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇ Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jˇumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇ Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumpsˇ! Over the lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇ Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumps!ˇ Over the lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps!ˇOver the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Ovˇer the lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps! ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Over theˇ lazy."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps! ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Over the lazyˇ."}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps! ˇ","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick ˇbrown\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy doˇg. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dogˇ. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇThe quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog. ˇThe quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog. ˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog. The quick ˇ\nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog. ˇ\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown.)]'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The ˇquick brown.)]'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ.)]'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)ˇ]'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)]ˇ'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)]'ˇ\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)]'\" Brown ˇfox jumps. "}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\" ˇ ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)]'\" Brown fox jumpsˇ. "}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\" ˇ ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)]'\" Brown fox jumps.ˇ "}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\" Brown fox jumps.ˇ","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown? Fox Jumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇFox Jumps! Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick ˇbrown? Fox Jumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇFox Jumps! Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ? Fox Jumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇFox Jumps! Over the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown? ˇFox Jumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇOver the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jˇumps! Over the lazy."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇOver the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumpsˇ! Over the lazy."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇOver the lazy.","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumps!ˇ Over the lazy."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps!ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Ovˇer the lazy."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps!ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Over theˇ lazy."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps!ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Over the lazyˇ."}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps!ˇ","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick ˇbrown\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy doˇg. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dogˇ. The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ The quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog. ˇThe quick \nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog. The quick ˇ\nbrown fox jumps over\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown.)]'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The ˇquick brown.)]'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ.)]'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)ˇ]'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)]ˇ'\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)]'ˇ\" Brown fox jumps. "}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)]'\" Brown ˇfox jumps. "}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\" ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown.)]'\" Brown fox jumpsˇ. "}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\" ˇ","mode":"Insert"}}

crates/vim2/test_data/test_change_surrounding_character_objects.json 🔗

@@ -1,2380 +0,0 @@
-{"Put":{"state":"ˇTh'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'ˇe ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e 'ˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''ˇqui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''quˇi'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck broˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'ˇ'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'ˇfox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox juˇmps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ovˇ'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe ˇlazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇ'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇo'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'oˇ'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'ˇe ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e 'ˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''ˇqui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''quˇi'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck broˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'ˇ'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'ˇfox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox juˇmps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ovˇ'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe ˇlazy d'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇ'o'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇo'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'oˇ'g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'ˇe ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e 'ˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''ˇqui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''quˇi'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''quiˇwn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck broˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck broˇ\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'ˇfox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox juˇmps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ovˇ'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe ˇlazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇ'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇo'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'oˇ'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'ˇe ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e 'ˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''ˇqui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''quˇi'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''quiˇwn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck broˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck broˇ\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'ˇfox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox juˇmps ov'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ovˇ'er\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe ˇlazy d'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇ'o'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇo'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'oˇ'g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`ˇe ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e `ˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``ˇqui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``quˇi`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck broˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`ˇ`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`ˇfox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox juˇmps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ovˇ`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe ˇlazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇ`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇo`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`oˇ`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`ˇe ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e `ˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``ˇqui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``quˇi`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck broˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`ˇ`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`ˇfox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox juˇmps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ovˇ`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe ˇlazy d`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇ`o`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇo`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`oˇ`g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`ˇe ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e `ˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``ˇqui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``quˇi`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``quiˇwn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck broˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck broˇ\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`ˇfox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox juˇmps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ovˇ`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe ˇlazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇ`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇo`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`oˇ`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`ˇe ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e `ˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``ˇqui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``quˇi`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``quiˇwn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck broˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck broˇ\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`ˇfox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox juˇmps ov`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ovˇ`er\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe ˇlazy d`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇ`o`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇo`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`oˇ`g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"ˇe \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"ˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"ˇqui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"quˇi\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck broˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"ˇ\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇfox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox juˇmps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ovˇ\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe ˇlazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇ\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇo\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"oˇ\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"ˇe \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"ˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"ˇqui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"quˇi\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck broˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"ˇ\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇfox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox juˇmps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ovˇ\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe ˇlazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇ\"o\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇo\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"oˇ\"g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"ˇe \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"ˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"ˇqui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"quˇi\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"quiˇwn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck broˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck broˇ\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇfox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox juˇmps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ovˇ\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe ˇlazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇ\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇo\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"oˇ\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"ˇe \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"ˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"ˇqui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"quˇi\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"quiˇwn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck broˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck broˇ\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇfox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox juˇmps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ovˇ\"er\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe ˇlazy d\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇ\"o\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇo\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"oˇ\"g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Insert"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)ˇe ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ˇ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()quˇi(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ˇck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck broˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)ˇfox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox juˇmps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇ(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇer\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe ˇlazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy dˇ)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)ˇe ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ˇ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()quˇi(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ˇck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck broˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)ˇfox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox juˇmps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇ(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇer\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe ˇlazy d)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy dˇ)o(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)ˇe ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ˇ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()quˇi(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ˇck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck broˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)ˇfox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox juˇmps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇ(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇer\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe ˇlazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy dˇ)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)ˇe ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ˇ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()quˇi(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ˇck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck broˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)ˇfox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox juˇmps ov(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇ(er\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇer\nthe lazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe ˇlazy d)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy dˇ)o(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Insert"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]ˇe []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e ˇ[]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []quˇi[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ˇck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck broˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]ˇfox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox juˇmps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇ[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇer\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe ˇlazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy dˇ]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]ˇe []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e ˇ[]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []quˇi[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ˇck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck broˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]ˇfox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox juˇmps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇ[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇer\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe ˇlazy d]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy dˇ]o[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]ˇe []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e ˇ[]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []quˇi[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ˇck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck broˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]ˇfox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox juˇmps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇ[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇer\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe ˇlazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy dˇ]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]ˇe []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e ˇ[]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []quˇi[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ˇck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck broˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]ˇfox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox juˇmps ov[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇ[er\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇer\nthe lazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe ˇlazy d]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy dˇ]o[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Insert"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}ˇe {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e ˇ{}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}quˇi{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ˇck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck broˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}ˇfox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox juˇmps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇ{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇer\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe ˇlazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy dˇ}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}ˇe {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e ˇ{}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}quˇi{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ˇck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck broˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}ˇfox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox juˇmps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇ{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇer\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe ˇlazy d}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy dˇ}o{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}ˇe {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e ˇ{}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}quˇi{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ˇck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck broˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}ˇfox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox juˇmps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇ{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇer\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe ˇlazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy dˇ}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}ˇe {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e ˇ{}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}quˇi{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ˇck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck broˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}ˇfox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox juˇmps ov{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇ{er\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇer\nthe lazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe ˇlazy d}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy dˇ}o{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Insert"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>ˇe <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e ˇ<>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>quˇi<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ˇck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck broˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>ˇfox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox juˇmps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇ<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇer\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe ˇlazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy dˇ>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>ˇe <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e ˇ<>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>quˇi<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ˇck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck broˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>ˇfox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox juˇmps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇ<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇer\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe ˇlazy d>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy dˇ>o<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>ˇe <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e ˇ<>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>quˇi<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ˇck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck broˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>ˇfox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox juˇmps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇ<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇer\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe ˇlazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy dˇ>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>ˇe <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e ˇ<>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>quˇi<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ˇck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck broˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>ˇfox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox juˇmps ov<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇ<er\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇer\nthe lazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe ˇlazy d>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy dˇ>o<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Insert"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg","mode":"Normal"}}

crates/vim2/test_data/test_change_w.json 🔗

@@ -1,28 +0,0 @@
-{"Put":{"state":"Teˇst"}}
-{"Key":"c"}
-{"Key":"w"}
-{"Get":{"state":"Teˇ","mode":"Insert"}}
-{"Put":{"state":"Tˇest test"}}
-{"Key":"c"}
-{"Key":"w"}
-{"Get":{"state":"Tˇ test","mode":"Insert"}}
-{"Put":{"state":"Testˇ  test"}}
-{"Key":"c"}
-{"Key":"w"}
-{"Get":{"state":"Testˇtest","mode":"Insert"}}
-{"Put":{"state":"Test teˇst\ntest"}}
-{"Key":"c"}
-{"Key":"w"}
-{"Get":{"state":"Test teˇ\ntest","mode":"Insert"}}
-{"Put":{"state":"Test tesˇt\ntest"}}
-{"Key":"c"}
-{"Key":"w"}
-{"Get":{"state":"Test tesˇ\ntest","mode":"Insert"}}
-{"Put":{"state":"Test test\nˇ\ntest"}}
-{"Key":"c"}
-{"Key":"w"}
-{"Get":{"state":"Test test\nˇ\ntest","mode":"Insert"}}
-{"Put":{"state":"Test teˇst-test test"}}
-{"Key":"c"}
-{"Key":"shift-w"}
-{"Get":{"state":"Test teˇ test","mode":"Insert"}}

crates/vim2/test_data/test_change_word_object.json 🔗

@@ -1,460 +0,0 @@
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick ˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick ˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox ˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox ˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumpsˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ\n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ\n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ\n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ\n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇfox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-ˇ over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ\n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick ˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick ˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox ˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox ˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumpsˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ\n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ\n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ\n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ\n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇfox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  ˇ over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ\n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"c"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick ˇ\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick ˇ\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brownˇ jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox ˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox ˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumpsˇ\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇ\n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-ˇover\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick ˇ\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick ˇ\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brownˇ jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox ˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox ˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumpsˇ\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇ\n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ over\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  ˇover\nthe lazy dog \n\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"c"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ","mode":"Insert"}}

crates/vim2/test_data/test_clear_counts.json 🔗

@@ -1,7 +0,0 @@
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
-{"Key":"4"}
-{"Key":"escape"}
-{"Key":"3"}
-{"Key":"d"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\nfox juˇ over\nthe lazy dog","mode":"Normal"}}

crates/vim2/test_data/test_comma_semicolon.json 🔗

@@ -1,17 +0,0 @@
-{"Put":{"state":"ˇone two three four"}}
-{"Key":"f"}
-{"Key":"o"}
-{"Get":{"state":"one twˇo three four","mode":"Normal"}}
-{"Key":","}
-{"Get":{"state":"ˇone two three four","mode":"Normal"}}
-{"Key":"2"}
-{"Key":";"}
-{"Get":{"state":"one two three fˇour","mode":"Normal"}}
-{"Key":"shift-t"}
-{"Key":"e"}
-{"Get":{"state":"one two threeˇ four","mode":"Normal"}}
-{"Key":"3"}
-{"Key":";"}
-{"Get":{"state":"oneˇ two three four","mode":"Normal"}}
-{"Key":","}
-{"Get":{"state":"one two thˇree four","mode":"Normal"}}

crates/vim2/test_data/test_command_replace.json 🔗

@@ -1,22 +0,0 @@
-{"Put":{"state":"ˇa\nb\nc"}}
-{"Key":":"}
-{"Key":"%"}
-{"Key":"s"}
-{"Key":"/"}
-{"Key":"b"}
-{"Key":"/"}
-{"Key":"d"}
-{"Key":"enter"}
-{"Get":{"state":"a\nˇd\nc","mode":"Normal"}}
-{"Key":":"}
-{"Key":"%"}
-{"Key":"s"}
-{"Key":":"}
-{"Key":"."}
-{"Key":":"}
-{"Key":"\\"}
-{"Key":"0"}
-{"Key":"\\"}
-{"Key":"0"}
-{"Key":"enter"}
-{"Get":{"state":"aa\ndd\nˇcc","mode":"Normal"}}

crates/vim2/test_data/test_command_search.json 🔗

@@ -1,11 +0,0 @@
-{"Put":{"state":"ˇa\nb\na\nc"}}
-{"Key":":"}
-{"Key":"/"}
-{"Key":"b"}
-{"Key":"enter"}
-{"Get":{"state":"a\nˇb\na\nc","mode":"Normal"}}
-{"Key":":"}
-{"Key":"?"}
-{"Key":"a"}
-{"Key":"enter"}
-{"Get":{"state":"ˇa\nb\na\nc","mode":"Normal"}}

crates/vim2/test_data/test_ctrl_d_u.json 🔗

@@ -1,22 +0,0 @@
-{"SetOption":{"value":"scrolloff=3"}}
-{"SetOption":{"value":"lines=12"}}
-{"Put":{"state":"ˇaa\nbb\ncc\ndd\nee\nff\ngg\nhh\nii\njj\nkk\nll\nmm\nnn\noo\npp\nqq\nrr\nss\ntt\nuu\nvv\nww\nxx\nyy\nzz"}}
-{"Key":"4"}
-{"Key":"j"}
-{"Key":"ctrl-d"}
-{"Get":{"state":"aa\nbb\ncc\ndd\nee\nff\ngg\nhh\nii\nˇjj\nkk\nll\nmm\nnn\noo\npp\nqq\nrr\nss\ntt\nuu\nvv\nww\nxx\nyy\nzz","mode":"Normal"}}
-{"Key":"ctrl-d"}
-{"Get":{"state":"aa\nbb\ncc\ndd\nee\nff\ngg\nhh\nii\njj\nkk\nll\nmm\nnn\nˇoo\npp\nqq\nrr\nss\ntt\nuu\nvv\nww\nxx\nyy\nzz","mode":"Normal"}}
-{"Key":"g"}
-{"Key":"g"}
-{"Key":"ctrl-d"}
-{"Get":{"state":"aa\nbb\ncc\ndd\nee\nff\ngg\nhh\nˇii\njj\nkk\nll\nmm\nnn\noo\npp\nqq\nrr\nss\ntt\nuu\nvv\nww\nxx\nyy\nzz","mode":"Normal"}}
-{"Key":"ctrl-u"}
-{"Get":{"state":"aa\nbb\ncc\nˇdd\nee\nff\ngg\nhh\nii\njj\nkk\nll\nmm\nnn\noo\npp\nqq\nrr\nss\ntt\nuu\nvv\nww\nxx\nyy\nzz","mode":"Normal"}}
-{"Key":"ctrl-d"}
-{"Key":"ctrl-d"}
-{"Key":"4"}
-{"Key":"j"}
-{"Key":"ctrl-u"}
-{"Key":"ctrl-u"}
-{"Get":{"state":"aa\nbb\ncc\ndd\nee\nff\ngg\nˇhh\nii\njj\nkk\nll\nmm\nnn\noo\npp\nqq\nrr\nss\ntt\nuu\nvv\nww\nxx\nyy\nzz","mode":"Normal"}}

crates/vim2/test_data/test_dd.json 🔗

@@ -1,24 +0,0 @@
-{"Put":{"state":"ˇ"}}
-{"Key":"d"}
-{"Key":"d"}
-{"Get":{"state":"ˇ","mode":"Normal"}}
-{"Put":{"state":"The ˇquick"}}
-{"Key":"d"}
-{"Key":"d"}
-{"Get":{"state":"ˇ","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown fox\njumps over"}}
-{"Key":"d"}
-{"Key":"d"}
-{"Get":{"state":"brownˇ fox\njumps over","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown ˇfox\njumps over"}}
-{"Key":"d"}
-{"Key":"d"}
-{"Get":{"state":"The quick\njumps ˇover","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown fox\njumps ˇover"}}
-{"Key":"d"}
-{"Key":"d"}
-{"Get":{"state":"The quick\nbrown ˇfox","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇ\nbrown fox"}}
-{"Key":"d"}
-{"Key":"d"}
-{"Get":{"state":"The quick\nˇbrown fox","mode":"Normal"}}

crates/vim2/test_data/test_delete_0.json 🔗

@@ -1,8 +0,0 @@
-{"Put":{"state":"The qˇuick\nbrown fox"}}
-{"Key":"d"}
-{"Key":"0"}
-{"Get":{"state":"ˇuick\nbrown fox","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇ\nbrown fox"}}
-{"Key":"d"}
-{"Key":"0"}
-{"Get":{"state":"The quick\nˇ\nbrown fox","mode":"Normal"}}

crates/vim2/test_data/test_delete_b.json 🔗

@@ -1,24 +0,0 @@
-{"Put":{"state":"Teˇst Test"}}
-{"Key":"d"}
-{"Key":"b"}
-{"Get":{"state":"ˇst Test","mode":"Normal"}}
-{"Put":{"state":"Test ˇtest"}}
-{"Key":"d"}
-{"Key":"b"}
-{"Get":{"state":"ˇtest","mode":"Normal"}}
-{"Put":{"state":"Test1 test2 ˇtest3"}}
-{"Key":"d"}
-{"Key":"b"}
-{"Get":{"state":"Test1 ˇtest3","mode":"Normal"}}
-{"Put":{"state":"Test test\nˇtest"}}
-{"Key":"d"}
-{"Key":"b"}
-{"Get":{"state":"Testˇ \ntest","mode":"Normal"}}
-{"Put":{"state":"Test test\nˇ\ntest"}}
-{"Key":"d"}
-{"Key":"b"}
-{"Get":{"state":"Testˇ \n\ntest","mode":"Normal"}}
-{"Put":{"state":"Test test-test ˇtest"}}
-{"Key":"d"}
-{"Key":"shift-b"}
-{"Get":{"state":"Test ˇtest","mode":"Normal"}}

crates/vim2/test_data/test_delete_end_of_document.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"The quick\nbrownˇ fox\njumps over\nthe lazy"}}
-{"Key":"d"}
-{"Key":"shift-g"}
-{"Get":{"state":"The qˇuick","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrownˇ fox\njumps over\nthe lazy"}}
-{"Key":"d"}
-{"Key":"shift-g"}
-{"Get":{"state":"The qˇuick","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown fox\njumps over\nthe lˇazy"}}
-{"Key":"d"}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\nbrown fox\njumpsˇ over","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown fox\njumps over\nˇ"}}
-{"Key":"d"}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\nbrown fox\nˇjumps over","mode":"Normal"}}

crates/vim2/test_data/test_delete_end_of_line.json 🔗

@@ -1,8 +0,0 @@
-{"Put":{"state":"The qˇuick\nbrown fox"}}
-{"Key":"d"}
-{"Key":"$"}
-{"Get":{"state":"The ˇq\nbrown fox","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇ\nbrown fox"}}
-{"Key":"d"}
-{"Key":"$"}
-{"Get":{"state":"The quick\nˇ\nbrown fox","mode":"Normal"}}

crates/vim2/test_data/test_delete_gg.json 🔗

@@ -1,20 +0,0 @@
-{"Put":{"state":"The quick\nbrownˇ fox\njumps over\nthe lazy"}}
-{"Key":"d"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"jumpsˇ over\nthe lazy","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown fox\njumps over\nthe lˇazy"}}
-{"Key":"d"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"ˇ","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown fox\njumps over\nthe lazy"}}
-{"Key":"d"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"brownˇ fox\njumps over\nthe lazy","mode":"Normal"}}
-{"Put":{"state":"ˇ\nbrown fox\njumps over\nthe lazy"}}
-{"Key":"d"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"ˇbrown fox\njumps over\nthe lazy","mode":"Normal"}}

crates/vim2/test_data/test_delete_h.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"Teˇst"}}
-{"Key":"d"}
-{"Key":"h"}
-{"Get":{"state":"Tˇst","mode":"Normal"}}
-{"Put":{"state":"Tˇest"}}
-{"Key":"d"}
-{"Key":"h"}
-{"Get":{"state":"ˇest","mode":"Normal"}}
-{"Put":{"state":"ˇTest"}}
-{"Key":"d"}
-{"Key":"h"}
-{"Get":{"state":"ˇTest","mode":"Normal"}}
-{"Put":{"state":"Test\nˇtest"}}
-{"Key":"d"}
-{"Key":"h"}
-{"Get":{"state":"Test\nˇtest","mode":"Normal"}}

crates/vim2/test_data/test_delete_j.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"The quick\nbrown ˇfox\njumps over"}}
-{"Key":"d"}
-{"Key":"j"}
-{"Get":{"state":"The quˇick","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown fox\njumps ˇover"}}
-{"Key":"d"}
-{"Key":"j"}
-{"Get":{"state":"The quick\nbrown fox\njumps ˇover","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown fox\njumps over"}}
-{"Key":"d"}
-{"Key":"j"}
-{"Get":{"state":"jumpsˇ over","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown fox\nˇ"}}
-{"Key":"d"}
-{"Key":"j"}
-{"Get":{"state":"The quick\nbrown fox\nˇ","mode":"Normal"}}

crates/vim2/test_data/test_delete_k.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"The quick\nbrown ˇfox\njumps over"}}
-{"Key":"d"}
-{"Key":"k"}
-{"Get":{"state":"jumps ˇover","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown fox\njumps ˇover"}}
-{"Key":"d"}
-{"Key":"k"}
-{"Get":{"state":"The quˇick","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown fox\njumps over"}}
-{"Key":"d"}
-{"Key":"k"}
-{"Get":{"state":"The qˇuick\nbrown fox\njumps over","mode":"Normal"}}
-{"Put":{"state":"ˇbrown fox\njumps over"}}
-{"Key":"d"}
-{"Key":"k"}
-{"Get":{"state":"ˇbrown fox\njumps over","mode":"Normal"}}

crates/vim2/test_data/test_delete_l.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"ˇTest"}}
-{"Key":"d"}
-{"Key":"l"}
-{"Get":{"state":"ˇest","mode":"Normal"}}
-{"Put":{"state":"Teˇst"}}
-{"Key":"d"}
-{"Key":"l"}
-{"Get":{"state":"Teˇt","mode":"Normal"}}
-{"Put":{"state":"Tesˇt"}}
-{"Key":"d"}
-{"Key":"l"}
-{"Get":{"state":"Teˇs","mode":"Normal"}}
-{"Put":{"state":"Tesˇt\ntest"}}
-{"Key":"d"}
-{"Key":"l"}
-{"Get":{"state":"Teˇs\ntest","mode":"Normal"}}

crates/vim2/test_data/test_delete_left.json 🔗

@@ -1,15 +0,0 @@
-{"Put":{"state":"ˇTest"}}
-{"Key":"shift-x"}
-{"Get":{"state":"ˇTest","mode":"Normal"}}
-{"Put":{"state":"Tˇest"}}
-{"Key":"shift-x"}
-{"Get":{"state":"ˇest","mode":"Normal"}}
-{"Put":{"state":"Teˇst"}}
-{"Key":"shift-x"}
-{"Get":{"state":"Tˇst","mode":"Normal"}}
-{"Put":{"state":"Tesˇt"}}
-{"Key":"shift-x"}
-{"Get":{"state":"Teˇt","mode":"Normal"}}
-{"Put":{"state":"Test\nˇtest"}}
-{"Key":"shift-x"}
-{"Get":{"state":"Test\nˇtest","mode":"Normal"}}

crates/vim2/test_data/test_delete_next_word_end.json 🔗

@@ -1,12 +0,0 @@
-{"Put":{"state":"Test teˇst\ntest"}}
-{"Key":"d"}
-{"Key":"e"}
-{"Get":{"state":"Test tˇe\ntest","mode":"Normal"}}
-{"Put":{"state":"Test tesˇt\ntest"}}
-{"Key":"d"}
-{"Key":"e"}
-{"Get":{"state":"Test teˇs","mode":"Normal"}}
-{"Put":{"state":"Test teˇst-test test"}}
-{"Key":"d"}
-{"Key":"shift-e"}
-{"Get":{"state":"Test teˇ test","mode":"Normal"}}

crates/vim2/test_data/test_delete_sentence_object.json 🔗

@@ -1,270 +0,0 @@
-{"Put":{"state":"ˇThe quick brown? Fox Jumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Fox Jumps! Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick ˇbrown? Fox Jumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Fox Jumps! Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ? Fox Jumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Fox Jumps! Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown?ˇ Fox Jumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown?ˇFox Jumps! Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown? ˇFox Jumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇ Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jˇumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇ Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumpsˇ! Over the lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇ Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumps!ˇ Over the lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps!ˇOver the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Ovˇer the lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps!ˇ ","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Over theˇ lazy."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps!ˇ ","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Over the lazyˇ."}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumps!ˇ ","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick ˇbrown\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy doˇg. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dogˇ. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ The quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇThe quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog. ˇThe quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ \n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog. The quick ˇ\nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ \n","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown.)]'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The ˇquick brown.)]'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ.)]'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)ˇ]'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)]ˇ'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)]'ˇ\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"ˇ Brown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)]'\" Brown ˇfox jumps. "}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\" ˇ ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)]'\" Brown fox jumpsˇ. "}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\" ˇ ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)]'\" Brown fox jumps.ˇ "}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\" Brown fox jumpsˇ.","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown? Fox Jumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇFox Jumps! Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick ˇbrown? Fox Jumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇFox Jumps! Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ? Fox Jumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇFox Jumps! Over the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown? ˇFox Jumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇOver the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jˇumps! Over the lazy."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇOver the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumpsˇ! Over the lazy."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? ˇOver the lazy.","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumps!ˇ Over the lazy."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumpsˇ!","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Ovˇer the lazy."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumpsˇ!","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Over theˇ lazy."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumpsˇ!","mode":"Normal"}}
-{"Put":{"state":"The quick brown? Fox Jumps! Over the lazyˇ."}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown? Fox Jumpsˇ!","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick ˇbrown\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ\nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy doˇg. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dogˇ. The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇThe quick \nbrown fox jumps over\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog.ˇ The quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dogˇ.\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog. ˇThe quick \nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dogˇ.\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe lazy dog. The quick ˇ\nbrown fox jumps over\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe lazy dogˇ.\n","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown.)]'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The ˇquick brown.)]'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ.)]'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)ˇ]'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)]ˇ'\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)]'ˇ\" Brown fox jumps. "}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"ˇBrown fox jumps. ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)]'\" Brown ˇfox jumps. "}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\"ˇ ","mode":"Normal"}}
-{"Put":{"state":"The quick brown.)]'\" Brown fox jumpsˇ. "}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"The quick brown.)]'\"ˇ ","mode":"Normal"}}

crates/vim2/test_data/test_delete_surrounding_character_objects.json 🔗

@@ -1,2372 +0,0 @@
-{"Put":{"state":"ˇTh'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'ˇe ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e 'ˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''ˇqui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''quˇi'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck broˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'ˇ'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'ˇfox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox juˇmps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ovˇ'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe ˇlazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇ'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇo'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'oˇ'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'ˇe ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e 'ˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''ˇqui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''quˇi'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''ˇ'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck broˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'ˇ'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'ˇfox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox juˇmps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ovˇ'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'ˇ'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe ˇlazy d'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇ'o'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇo'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'oˇ'g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇ'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'ˇe ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e 'ˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''ˇqui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''quˇi'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''quiˇwn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck broˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck brˇo\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'ˇfox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox juˇmps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ovˇ'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe ˇlazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇ'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇo'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'oˇ'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'ˇe ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ˇ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Thˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e 'ˇ'qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''ˇqui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''quˇi'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e 'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ˇck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''quiˇwn'\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck broˇ'wn'\n'fox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck brˇo\n'fox jumps ov'er\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'ˇfox jumps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox juˇmps ov'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ovˇ'er\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\nˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'ˇer\nthe lazy d'o'g","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe ˇlazy d'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇ'o'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'ˇo'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'oˇ'g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"'"}
-{"Get":{"state":"Th'e ''qui'ck bro'wn'\n'fox jumps ov'er\nthe lazy d'o'ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`ˇe ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e `ˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``ˇqui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``quˇi`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck broˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`ˇ`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`ˇfox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox juˇmps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ovˇ`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe ˇlazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇ`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇo`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`oˇ`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`ˇe ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e `ˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``ˇqui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``quˇi`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``ˇ`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck broˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`ˇ`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`ˇfox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox juˇmps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ovˇ`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`ˇ`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe ˇlazy d`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇ`o`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇo`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`oˇ`g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇ`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`ˇe ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e `ˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``ˇqui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``quˇi`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``quiˇwn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck broˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck brˇo\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`ˇfox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox juˇmps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ovˇ`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe ˇlazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇ`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇo`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`oˇ`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`ˇe ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ˇ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Thˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e `ˇ`qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``ˇqui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``quˇi`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e `ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ˇck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``quiˇwn`\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck broˇ`wn`\n`fox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck brˇo\n`fox jumps ov`er\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`ˇfox jumps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox juˇmps ov`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ovˇ`er\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\nˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`ˇer\nthe lazy d`o`g","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe ˇlazy d`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇ`o`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`ˇo`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`oˇ`g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"`"}
-{"Get":{"state":"Th`e ``qui`ck bro`wn`\n`fox jumps ov`er\nthe lazy d`o`ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"ˇe \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"ˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"ˇqui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"quˇi\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck broˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"ˇ\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇfox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox juˇmps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ovˇ\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe ˇlazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇ\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇo\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"oˇ\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"ˇe \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"ˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"ˇqui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"quˇi\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"ˇ\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck broˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"ˇ\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇfox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox juˇmps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ovˇ\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇ\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe ˇlazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇ\"o\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇo\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"oˇ\"g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇ\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"ˇe \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"ˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"ˇqui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"quˇi\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"quiˇwn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck broˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck brˇo\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇfox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox juˇmps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ovˇ\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe ˇlazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇ\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇo\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"oˇ\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"ˇe \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e ˇ\"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Thˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"ˇ\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"ˇqui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"quˇi\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ˇck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"quiˇwn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck broˇ\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck brˇo\n\"fox jumps ov\"er\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"ˇfox jumps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox juˇmps ov\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ovˇ\"er\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\nˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"ˇer\nthe lazy d\"o\"g","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe ˇlazy d\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇ\"o\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"ˇo\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"oˇ\"g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy dˇg","mode":"Normal"}}
-{"Put":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"Th\"e \"\"qui\"ck bro\"wn\"\n\"fox jumps ov\"er\nthe lazy d\"o\"ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)ˇe ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ˇ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()quˇi(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ˇck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck broˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)ˇfox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox juˇmps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇ(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇer\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe ˇlazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy dˇ)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)ˇe ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ˇ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()quˇi(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ˇck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck broˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)ˇfox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox juˇmps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇ(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇer\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe ˇlazy d)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy dˇ)o(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇ)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)ˇe ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ˇ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()quˇi(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ˇck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck broˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)ˇfox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox juˇmps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇ(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇer\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe ˇlazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy dˇ)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"("}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"ˇTh)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)ˇe ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ˇ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e (ˇ)qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()ˇqui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()quˇi(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ˇck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck broˇ)wn(\n)fox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()quiˇwn(\n)fox jumps ov(er\nthe lazy d)o(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)ˇfox jumps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox juˇmps ov(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇ(er\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(ˇer\nthe lazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe ˇlazy d)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy dˇ)o(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ovˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)ˇo(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)oˇ(g","mode":"Normal"}}
-{"Put":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":")"}
-{"Get":{"state":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]ˇe []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e ˇ[]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []quˇi[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ˇck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck broˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]ˇfox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox juˇmps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇ[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇer\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe ˇlazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy dˇ]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]ˇe []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e ˇ[]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []quˇi[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ˇck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck broˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]ˇfox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox juˇmps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇ[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇer\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe ˇlazy d]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy dˇ]o[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇ]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]ˇe []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e ˇ[]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []quˇi[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ˇck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck broˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]ˇfox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox juˇmps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇ[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇer\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe ˇlazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy dˇ]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"["}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"ˇTh]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]ˇe []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e ˇ[]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e [ˇ]qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []ˇqui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []quˇi[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ˇck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck broˇ]wn[\n]fox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []quiˇwn[\n]fox jumps ov[er\nthe lazy d]o[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]ˇfox jumps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox juˇmps ov[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇ[er\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[ˇer\nthe lazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe ˇlazy d]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy dˇ]o[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ovˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]ˇo[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]oˇ[g","mode":"Normal"}}
-{"Put":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}ˇe {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e ˇ{}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}quˇi{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ˇck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck broˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}ˇfox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox juˇmps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇ{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇer\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe ˇlazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy dˇ}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}ˇe {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e ˇ{}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}quˇi{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ˇck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck broˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}ˇfox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox juˇmps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇ{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇer\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe ˇlazy d}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy dˇ}o{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇ}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}ˇe {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e ˇ{}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}quˇi{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ˇck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck broˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}ˇfox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox juˇmps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇ{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇer\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe ˇlazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy dˇ}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"{"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"ˇTh}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}ˇe {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e ˇ{}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {ˇ}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}ˇqui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}quˇi{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ˇck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck broˇ}wn{\n}fox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}quiˇwn{\n}fox jumps ov{er\nthe lazy d}o{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}ˇfox jumps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox juˇmps ov{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇ{er\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{ˇer\nthe lazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe ˇlazy d}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy dˇ}o{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ovˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}ˇo{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}oˇ{g","mode":"Normal"}}
-{"Put":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"}"}
-{"Get":{"state":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>ˇe <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e ˇ<>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>quˇi<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ˇck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck broˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>ˇfox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox juˇmps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇ<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇer\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe ˇlazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy dˇ>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>ˇe <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e ˇ<>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>quˇi<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ˇck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck broˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>ˇfox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox juˇmps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇ<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇer\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe ˇlazy d>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy dˇ>o<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇ>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>ˇe <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e ˇ<>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>quˇi<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ˇck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck broˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>ˇfox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox juˇmps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇ<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇer\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe ˇlazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy dˇ>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"<"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg","mode":"Normal"}}
-{"Put":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"ˇTh>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>ˇe <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e ˇ<>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <ˇ>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>ˇqui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>quˇi<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ˇck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck broˇ>wn<\n>fox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>quiˇwn<\n>fox jumps ov<er\nthe lazy d>o<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>ˇfox jumps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox juˇmps ov<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇ<er\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<ˇer\nthe lazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe ˇlazy d>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy dˇ>o<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ovˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>ˇo<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>oˇ<g","mode":"Normal"}}
-{"Put":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":">"}
-{"Get":{"state":"Th>e <>qui<ck bro>wn<\n>fox jumps ov<er\nthe lazy d>o<ˇg","mode":"Normal"}}

crates/vim2/test_data/test_delete_to_end_of_line.json 🔗

@@ -1,6 +0,0 @@
-{"Put":{"state":"The qˇuick\nbrown fox"}}
-{"Key":"shift-d"}
-{"Get":{"state":"The ˇq\nbrown fox","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇ\nbrown fox"}}
-{"Key":"shift-d"}
-{"Get":{"state":"The quick\nˇ\nbrown fox","mode":"Normal"}}

crates/vim2/test_data/test_delete_w.json 🔗

@@ -1,28 +0,0 @@
-{"Put":{"state":"Test tesˇt\n    test"}}
-{"Key":"d"}
-{"Key":"w"}
-{"Get":{"state":"Test teˇs\n    test","mode":"Normal"}}
-{"Put":{"state":"Teˇst"}}
-{"Key":"d"}
-{"Key":"w"}
-{"Get":{"state":"Tˇe","mode":"Normal"}}
-{"Put":{"state":"Tˇest test"}}
-{"Key":"d"}
-{"Key":"w"}
-{"Get":{"state":"Tˇtest","mode":"Normal"}}
-{"Put":{"state":"Test teˇst\ntest"}}
-{"Key":"d"}
-{"Key":"w"}
-{"Get":{"state":"Test tˇe\ntest","mode":"Normal"}}
-{"Put":{"state":"Test tesˇt\ntest"}}
-{"Key":"d"}
-{"Key":"w"}
-{"Get":{"state":"Test teˇs\ntest","mode":"Normal"}}
-{"Put":{"state":"Test test\nˇ\ntest"}}
-{"Key":"d"}
-{"Key":"w"}
-{"Get":{"state":"Test test\nˇtest","mode":"Normal"}}
-{"Put":{"state":"Test teˇst-test test"}}
-{"Key":"d"}
-{"Key":"shift-w"}
-{"Get":{"state":"Test teˇtest","mode":"Normal"}}

crates/vim2/test_data/test_delete_with_counts.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"d"}
-{"Key":"2"}
-{"Key":"d"}
-{"Get":{"state":"the ˇlazy dog","mode":"Normal"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"2"}
-{"Key":"d"}
-{"Key":"d"}
-{"Get":{"state":"the ˇlazy dog","mode":"Normal"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe moon,\na star, and\nthe lazy dog"}}
-{"Key":"2"}
-{"Key":"d"}
-{"Key":"2"}
-{"Key":"d"}
-{"Get":{"state":"the ˇlazy dog","mode":"Normal"}}

crates/vim2/test_data/test_delete_word_object.json 🔗

@@ -1,460 +0,0 @@
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick ˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick ˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick browˇn\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox ˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox ˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumpsˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy doˇg\n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick browˇn\n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ\n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ\n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇfox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-ˇ over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy doˇg\n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n","mode":"Normal"}}
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick ˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick ˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick browˇn\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox ˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox ˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumpsˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy doˇg\n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick browˇn\n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ\n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ\n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇfox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  ˇ over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy doˇg\n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n","mode":"Normal"}}
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quickˇ \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quickˇ \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brownˇ jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox ˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox ˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumpˇs\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy doˇg\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-ˇover\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy doˇg\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nˇthe lazy dog ","mode":"Normal"}}
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quickˇ \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quickˇ \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brownˇ jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox ˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox ˇover\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumpˇs\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy doˇg\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ over\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  ˇover\nthe lazy dog \n\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy doˇg\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"d"}
-{"Key":"a"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nˇthe lazy dog ","mode":"Normal"}}

crates/vim2/test_data/test_dot_repeat.json 🔗

@@ -1,38 +0,0 @@
-{"Put":{"state":"ˇhello"}}
-{"Key":"o"}
-{"Key":"w"}
-{"Key":"o"}
-{"Key":"r"}
-{"Key":"l"}
-{"Key":"d"}
-{"Key":"escape"}
-{"Get":{"state":"hello\nworlˇd","mode":"Normal"}}
-{"Key":"."}
-{"Get":{"state":"hello\nworld\nworlˇd","mode":"Normal"}}
-{"Key":"^"}
-{"Key":"d"}
-{"Key":"f"}
-{"Key":"o"}
-{"Key":"g"}
-{"Key":"g"}
-{"Key":"."}
-{"Get":{"state":"ˇ\nworld\nrld","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"y"}
-{"Key":"y"}
-{"Key":"p"}
-{"Key":"shift-g"}
-{"Key":"y"}
-{"Key":"y"}
-{"Key":"."}
-{"Get":{"state":"\nworld\nworld\nrld\nˇrld","mode":"Normal"}}
-{"Put":{"state":"ˇthe quick brown fox"}}
-{"Key":"2"}
-{"Key":"~"}
-{"Key":"."}
-{"Put":{"state":"THE ˇquick brown fox"}}
-{"Key":"3"}
-{"Key":"."}
-{"Put":{"state":"THE QUIˇck brown fox"}}
-{"Key":"."}
-{"Get":{"state":"THE QUICK ˇbrown fox","mode":"Normal"}}

crates/vim2/test_data/test_end_of_document.json 🔗

@@ -1,15 +0,0 @@
-{"Put":{"state":"The qˇuick\n\nbrown fox jumps\nover the lazy dog"}}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\n\nbrown fox jumps\nover ˇthe lazy dog","mode":"Normal"}}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\n\nbrown fox jumps\nover ˇthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick\n\nbrown fox jumps\nover the laˇzy dog"}}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\n\nbrown fox jumps\nover the laˇzy dog","mode":"Normal"}}
-{"Put":{"state":"\n\nbrown fox jumps\nover the laˇzy dog"}}
-{"Key":"shift-g"}
-{"Get":{"state":"\n\nbrown fox jumps\nover the laˇzy dog","mode":"Normal"}}
-{"Put":{"state":"ˇ\n\nbrown fox jumps\nover the lazydog"}}
-{"Key":"2"}
-{"Key":"shift-g"}
-{"Get":{"state":"\nˇ\nbrown fox jumps\nover the lazydog","mode":"Normal"}}

crates/vim2/test_data/test_end_of_word.json 🔗

@@ -1,32 +0,0 @@
-{"Put":{"state":"Thˇe quick-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"e"}
-{"Get":{"state":"The quicˇk-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"e"}
-{"Get":{"state":"The quickˇ-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"e"}
-{"Get":{"state":"The quick-browˇn\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"e"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumpˇs over\nthe","mode":"Normal"}}
-{"Key":"e"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps oveˇr\nthe","mode":"Normal"}}
-{"Key":"e"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nthˇe","mode":"Normal"}}
-{"Key":"e"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nthˇe","mode":"Normal"}}
-{"Put":{"state":"Thˇe quick-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-e"}
-{"Get":{"state":"The quick-browˇn\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quicˇk-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-e"}
-{"Get":{"state":"The quick-browˇn\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quickˇ-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-e"}
-{"Get":{"state":"The quick-browˇn\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"shift-e"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumpˇs over\nthe","mode":"Normal"}}
-{"Key":"shift-e"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps oveˇr\nthe","mode":"Normal"}}
-{"Key":"shift-e"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nthˇe","mode":"Normal"}}
-{"Key":"shift-e"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nthˇe","mode":"Normal"}}

crates/vim2/test_data/test_enter.json 🔗

@@ -1,11 +0,0 @@
-{"Put":{"state":"ˇThe quick brown\nfox jumps"}}
-{"Key":"enter"}
-{"Get":{"state":"The quick brown\nˇfox jumps","mode":"Normal"}}
-{"Put":{"state":"The qˇuick brown\nfox jumps"}}
-{"Key":"enter"}
-{"Get":{"state":"The quick brown\nˇfox jumps","mode":"Normal"}}
-{"Put":{"state":"The quick broˇwn\nfox jumps"}}
-{"Key":"enter"}
-{"Get":{"state":"The quick brown\nˇfox jumps","mode":"Normal"}}
-{"Key":"enter"}
-{"Get":{"state":"The quick brown\nˇfox jumps","mode":"Normal"}}

crates/vim2/test_data/test_enter_visual_line_mode.json 🔗

@@ -1,15 +0,0 @@
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Get":{"state":"The «qˇ»uick brown\nfox jumps over\nthe lazy dog","mode":"VisualLine"}}
-{"Key":"x"}
-{"Get":{"state":"fox ˇjumps over\nthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"a\nˇ\nb"}}
-{"Key":"shift-v"}
-{"Get":{"state":"a\n«\nˇ»b","mode":"VisualLine"}}
-{"Key":"x"}
-{"Get":{"state":"a\nˇb","mode":"Normal"}}
-{"Put":{"state":"a\nb\nˇ"}}
-{"Key":"shift-v"}
-{"Get":{"state":"a\nb\nˇ","mode":"VisualLine"}}
-{"Key":"x"}
-{"Get":{"state":"a\nˇb","mode":"Normal"}}

crates/vim2/test_data/test_enter_visual_mode.json 🔗

@@ -1,20 +0,0 @@
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Get":{"state":"The «qˇ»uick brown\nfox jumps over\nthe lazy dog","mode":"Visual"}}
-{"Key":"w"}
-{"Key":"j"}
-{"Get":{"state":"The «quick brown\nfox jumps oˇ»ver\nthe lazy dog","mode":"Visual"}}
-{"Key":"escape"}
-{"Get":{"state":"The quick brown\nfox jumps ˇover\nthe lazy dog","mode":"Normal"}}
-{"Key":"v"}
-{"Key":"k"}
-{"Key":"b"}
-{"Get":{"state":"The «ˇquick brown\nfox jumps o»ver\nthe lazy dog","mode":"Visual"}}
-{"Put":{"state":"a\nˇ\nb\n"}}
-{"Key":"v"}
-{"Get":{"state":"a\n«\nˇ»b\n","mode":"Visual"}}
-{"Key":"v"}
-{"Get":{"state":"a\nˇ\nb\n","mode":"Normal"}}
-{"Put":{"state":"a\nb\nˇ"}}
-{"Key":"v"}
-{"Get":{"state":"a\nb\nˇ","mode":"Visual"}}

crates/vim2/test_data/test_f_and_t.json 🔗

@@ -1,557 +0,0 @@
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n"}}
-{"Key":"1"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaˇab b   bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b  ˇ bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b  ˇ bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaˇabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaˇabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaˇabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n   ˇ baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaaˇ bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaaˇ bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaaˇ bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n"}}
-{"Key":"1"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n"}}
-{"Key":"2"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b  ˇ bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b  ˇ bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaˇabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaaˇ bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n"}}
-{"Key":"2"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n"}}
-{"Key":"3"}
-{"Key":"f"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n","mode":"Normal"}}
-{"Put":{"state":"ˇaaab b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b  ˇ bb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaaˇb b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaabˇ b   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab ˇb   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaˇabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab bˇ   bb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaˇabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   ˇbb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bˇb aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bbˇ aaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aˇaabaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaaˇbaaa\n    baaa bbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\nˇ    baaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    ˇbaaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    bˇaaa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaˇa bbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa ˇbbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bˇbb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbˇb\n\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\nˇ\nb\n","mode":"Normal"}}
-{"Put":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n"}}
-{"Key":"3"}
-{"Key":"t"}
-{"Key":"b"}
-{"Get":{"state":"aaab b   bb aaabaaa\n    baaa bbb\n\nˇb\n","mode":"Normal"}}

crates/vim2/test_data/test_folds.json 🔗

@@ -1,23 +0,0 @@
-{"SetOption":{"value":"foldmethod=manual"}}
-{"Put":{"state":"fn boop() {\n  ˇbarp()\n  bazp()\n}\n"}}
-{"Key":"shift-v"}
-{"Key":"j"}
-{"Key":"z"}
-{"Key":"f"}
-{"Key":"escape"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"ˇfn boop() {\n  barp()\n  bazp()\n}\n","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"j"}
-{"Get":{"state":"fn boop() {\n  barp()\n  bazp()\nˇ}\n","mode":"Normal"}}
-{"Key":"2"}
-{"Key":"k"}
-{"Get":{"state":"ˇfn boop() {\n  barp()\n  bazp()\n}\n","mode":"Normal"}}
-{"Key":"down"}
-{"Key":"y"}
-{"Key":"y"}
-{"ReadRegister":{"name":"\"","value":"  barp()\n  bazp()\n"}}
-{"Key":"z"}
-{"Key":"o"}
-{"Get":{"state":"fn boop() {\nˇ  barp()\n  bazp()\n}\n","mode":"Normal"}}

crates/vim2/test_data/test_folds_panic.json 🔗

@@ -1,13 +0,0 @@
-{"SetOption":{"value":"foldmethod=manual"}}
-{"Put":{"state":"fn boop() {\n  ˇbarp()\n  bazp()\n}\n"}}
-{"Key":"shift-v"}
-{"Key":"j"}
-{"Key":"z"}
-{"Key":"f"}
-{"Key":"escape"}
-{"Key":"g"}
-{"Key":"g"}
-{"Key":"5"}
-{"Key":"d"}
-{"Key":"j"}
-{"Get":{"state":"ˇ","mode":"Normal"}}

crates/vim2/test_data/test_gg.json 🔗

@@ -1,21 +0,0 @@
-{"Put":{"state":"The qˇuick\n\nbrown fox jumps\nover the lazy dog"}}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"The qˇuick\n\nbrown fox jumps\nover the lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick\n\nbrown fox jumps\nover ˇthe lazy dog"}}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"The qˇuick\n\nbrown fox jumps\nover the lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick\n\nbrown fox jumps\nover the laˇzy dog"}}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"The quicˇk\n\nbrown fox jumps\nover the lazy dog","mode":"Normal"}}
-{"Put":{"state":"\n\nbrown fox jumps\nover the laˇzy dog"}}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"ˇ\n\nbrown fox jumps\nover the lazy dog","mode":"Normal"}}
-{"Put":{"state":"ˇ\n\nbrown fox jumps\nover the lazydog"}}
-{"Key":"2"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"\nˇ\nbrown fox jumps\nover the lazydog","mode":"Normal"}}

crates/vim2/test_data/test_h.json 🔗

@@ -1,9 +0,0 @@
-{"Put":{"state":"ˇThe quick\nbrown"}}
-{"Key":"h"}
-{"Get":{"state":"ˇThe quick\nbrown","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown"}}
-{"Key":"h"}
-{"Get":{"state":"The ˇquick\nbrown","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇbrown"}}
-{"Key":"h"}
-{"Get":{"state":"The quick\nˇbrown","mode":"Normal"}}

crates/vim2/test_data/test_h_through_unicode.json 🔗

@@ -1,12 +0,0 @@
-{"Put":{"state":"Testˇ├──┐Test"}}
-{"Key":"h"}
-{"Get":{"state":"Tesˇt├──┐Test","mode":"Normal"}}
-{"Put":{"state":"Test├ˇ──┐Test"}}
-{"Key":"h"}
-{"Get":{"state":"Testˇ├──┐Test","mode":"Normal"}}
-{"Put":{"state":"Test├──ˇ┐Test"}}
-{"Key":"h"}
-{"Get":{"state":"Test├─ˇ─┐Test","mode":"Normal"}}
-{"Put":{"state":"Test├──┐ˇTest"}}
-{"Key":"h"}
-{"Get":{"state":"Test├──ˇ┐Test","mode":"Normal"}}

crates/vim2/test_data/test_increment.json 🔗

@@ -1,16 +0,0 @@
-{"Put":{"state":"1ˇ2\n"}}
-{"Key":"ctrl-a"}
-{"Get":{"state":"1ˇ3\n","mode":"Normal"}}
-{"Key":"ctrl-x"}
-{"Get":{"state":"1ˇ2\n","mode":"Normal"}}
-{"Key":"9"}
-{"Key":"9"}
-{"Key":"ctrl-a"}
-{"Get":{"state":"11ˇ1\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"1"}
-{"Key":"1"}
-{"Key":"ctrl-x"}
-{"Get":{"state":"ˇ0\n","mode":"Normal"}}
-{"Key":"."}
-{"Get":{"state":"-11ˇ1\n","mode":"Normal"}}

crates/vim2/test_data/test_increment_radix.json 🔗

@@ -1,18 +0,0 @@
-{"Put":{"state":"ˇ total: 0xff"}}
-{"Key":"ctrl-a"}
-{"Get":{"state":" total: 0x10ˇ0","mode":"Normal"}}
-{"Put":{"state":"ˇ total: 0xff"}}
-{"Key":"ctrl-x"}
-{"Get":{"state":" total: 0xfˇe","mode":"Normal"}}
-{"Put":{"state":"ˇ total: 0xFF"}}
-{"Key":"ctrl-x"}
-{"Get":{"state":" total: 0xFˇE","mode":"Normal"}}
-{"Put":{"state":"(ˇ0b10f)"}}
-{"Key":"ctrl-a"}
-{"Get":{"state":"(0b1ˇ1f)","mode":"Normal"}}
-{"Put":{"state":"ˇ-1"}}
-{"Key":"ctrl-a"}
-{"Get":{"state":"ˇ0","mode":"Normal"}}
-{"Put":{"state":"banˇana"}}
-{"Key":"ctrl-a"}
-{"Get":{"state":"banˇana","mode":"Normal"}}

crates/vim2/test_data/test_increment_steps.json 🔗

@@ -1,15 +0,0 @@
-{"Put":{"state":"ˇ1\n1\n1  2\n1\n1"}}
-{"Key":"j"}
-{"Key":"v"}
-{"Key":"shift-g"}
-{"Key":"g"}
-{"Key":"ctrl-a"}
-{"Get":{"state":"1\nˇ2\n3  2\n4\n5","mode":"Normal"}}
-{"Key":"shift-g"}
-{"Key":"ctrl-v"}
-{"Key":"g"}
-{"Key":"g"}
-{"Get":{"state":"«1ˇ»\n«2ˇ»\n«3ˇ»  2\n«4ˇ»\n«5ˇ»","mode":"VisualBlock"}}
-{"Key":"g"}
-{"Key":"ctrl-x"}
-{"Get":{"state":"ˇ0\n0\n0  2\n0\n0","mode":"Normal"}}

crates/vim2/test_data/test_insert_end_of_line.json 🔗

@@ -1,9 +0,0 @@
-{"Put":{"state":"ˇ\nThe quick\nbrown fox "}}
-{"Key":"shift-a"}
-{"Get":{"state":"ˇ\nThe quick\nbrown fox ","mode":"Insert"}}
-{"Put":{"state":"\nThe qˇuick\nbrown fox "}}
-{"Key":"shift-a"}
-{"Get":{"state":"\nThe quickˇ\nbrown fox ","mode":"Insert"}}
-{"Put":{"state":"\nThe quick\nbrown ˇfox "}}
-{"Key":"shift-a"}
-{"Get":{"state":"\nThe quick\nbrown fox ˇ","mode":"Insert"}}

crates/vim2/test_data/test_insert_first_non_whitespace.json 🔗

@@ -1,15 +0,0 @@
-{"Put":{"state":"The qˇuick"}}
-{"Key":"shift-i"}
-{"Get":{"state":"ˇThe quick","mode":"Insert"}}
-{"Put":{"state":" The qˇuick"}}
-{"Key":"shift-i"}
-{"Get":{"state":" ˇThe quick","mode":"Insert"}}
-{"Put":{"state":"ˇ"}}
-{"Key":"shift-i"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The qˇuick\nbrown fox"}}
-{"Key":"shift-i"}
-{"Get":{"state":"ˇThe quick\nbrown fox","mode":"Insert"}}
-{"Put":{"state":"ˇ\nThe quick"}}
-{"Key":"shift-i"}
-{"Get":{"state":"ˇ\nThe quick","mode":"Insert"}}

crates/vim2/test_data/test_insert_line_above.json 🔗

@@ -1,18 +0,0 @@
-{"Put":{"state":"ˇ"}}
-{"Key":"shift-o"}
-{"Get":{"state":"ˇ\n","mode":"Insert"}}
-{"Put":{"state":"The ˇquick"}}
-{"Key":"shift-o"}
-{"Get":{"state":"ˇ\nThe quick","mode":"Insert"}}
-{"Put":{"state":"The qˇuick\nbrown fox\njumps over"}}
-{"Key":"shift-o"}
-{"Get":{"state":"ˇ\nThe quick\nbrown fox\njumps over","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown ˇfox\njumps over"}}
-{"Key":"shift-o"}
-{"Get":{"state":"The quick\nˇ\nbrown fox\njumps over","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown fox\njumps ˇover"}}
-{"Key":"shift-o"}
-{"Get":{"state":"The quick\nbrown fox\nˇ\njumps over","mode":"Insert"}}
-{"Put":{"state":"The quick\nˇ\nbrown fox"}}
-{"Key":"shift-o"}
-{"Get":{"state":"The quick\nˇ\n\nbrown fox","mode":"Insert"}}

crates/vim2/test_data/test_insert_with_counts.json 🔗

@@ -1,36 +0,0 @@
-{"Put":{"state":"ˇhello\n"}}
-{"Key":"5"}
-{"Key":"i"}
-{"Key":"-"}
-{"Key":"escape"}
-{"Get":{"state":"----ˇ-hello\n","mode":"Normal"}}
-{"Put":{"state":"ˇhello\n"}}
-{"Key":"5"}
-{"Key":"a"}
-{"Key":"-"}
-{"Key":"escape"}
-{"Get":{"state":"h----ˇ-ello\n","mode":"Normal"}}
-{"Key":"4"}
-{"Key":"shift-i"}
-{"Key":"-"}
-{"Key":"escape"}
-{"Get":{"state":"---ˇ-h-----ello\n","mode":"Normal"}}
-{"Key":"3"}
-{"Key":"shift-a"}
-{"Key":"-"}
-{"Key":"escape"}
-{"Get":{"state":"----h-----ello--ˇ-\n","mode":"Normal"}}
-{"Put":{"state":"ˇhello\n"}}
-{"Key":"3"}
-{"Key":"o"}
-{"Key":"o"}
-{"Key":"i"}
-{"Key":"escape"}
-{"Get":{"state":"hello\noi\noi\noˇi\n","mode":"Normal"}}
-{"Put":{"state":"ˇhello\n"}}
-{"Key":"3"}
-{"Key":"shift-o"}
-{"Key":"o"}
-{"Key":"i"}
-{"Key":"escape"}
-{"Get":{"state":"oi\noi\noˇi\nhello\n","mode":"Normal"}}

crates/vim2/test_data/test_insert_with_repeat.json 🔗

@@ -1,23 +0,0 @@
-{"Put":{"state":"ˇhello\n"}}
-{"Key":"3"}
-{"Key":"i"}
-{"Key":"-"}
-{"Key":"escape"}
-{"Get":{"state":"--ˇ-hello\n","mode":"Normal"}}
-{"Key":"."}
-{"Get":{"state":"----ˇ--hello\n","mode":"Normal"}}
-{"Key":"2"}
-{"Key":"."}
-{"Get":{"state":"-----ˇ---hello\n","mode":"Normal"}}
-{"Put":{"state":"ˇhello\n"}}
-{"Key":"2"}
-{"Key":"o"}
-{"Key":"k"}
-{"Key":"k"}
-{"Key":"escape"}
-{"Get":{"state":"hello\nkk\nkˇk\n","mode":"Normal"}}
-{"Key":"."}
-{"Get":{"state":"hello\nkk\nkk\nkk\nkˇk\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"."}
-{"Get":{"state":"hello\nkk\nkk\nkk\nkk\nkˇk\n","mode":"Normal"}}

crates/vim2/test_data/test_j.json 🔗

@@ -1,15 +0,0 @@
-{"Put":{"state":"aaˇaa\n😃😃"}}
-{"Key":"j"}
-{"Get":{"state":"aaaa\n😃ˇ😃","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown\nfox jumps"}}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\nˇfox jumps","mode":"Normal"}}
-{"Put":{"state":"The qˇuick brown\nfox jumps"}}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\nfox jˇumps","mode":"Normal"}}
-{"Put":{"state":"The quick broˇwn\nfox jumps"}}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\nfox jumpˇs","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nˇfox jumps"}}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\nˇfox jumps","mode":"Normal"}}

crates/vim2/test_data/test_join_lines.json 🔗

@@ -1,13 +0,0 @@
-{"Put":{"state":"ˇone\ntwo\nthree\nfour\nfive\nsix\n"}}
-{"Key":"shift-j"}
-{"Get":{"state":"oneˇ two\nthree\nfour\nfive\nsix\n","mode":"Normal"}}
-{"Key":"3"}
-{"Key":"shift-j"}
-{"Get":{"state":"one two threeˇ four\nfive\nsix\n","mode":"Normal"}}
-{"Put":{"state":"ˇone\ntwo\nthree\nfour\nfive\nsix\n"}}
-{"Key":"j"}
-{"Key":"v"}
-{"Key":"3"}
-{"Key":"j"}
-{"Key":"shift-j"}
-{"Get":{"state":"one\ntwo three fourˇ five\nsix\n","mode":"Normal"}}

crates/vim2/test_data/test_jump_to_end.json 🔗

@@ -1,14 +0,0 @@
-{"Put":{"state":"The ˇquick\n\nbrown fox jumps\nover the lazy dog"}}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\n\nbrown fox jumps\noverˇ the lazy dog","mode":"Normal"}}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\n\nbrown fox jumps\noverˇ the lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick\n\nbrown fox jumps\nover the lazy doˇg"}}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\n\nbrown fox jumps\nover the lazy doˇg","mode":"Normal"}}
-{"Put":{"state":"The quiˇck\n\nbrown"}}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\n\nbrowˇn","mode":"Normal"}}
-{"Put":{"state":"The quiˇck\n\n"}}
-{"Key":"shift-g"}
-{"Get":{"state":"The quick\n\nˇ","mode":"Normal"}}

crates/vim2/test_data/test_jump_to_first_non_whitespace.json 🔗

@@ -1,18 +0,0 @@
-{"Put":{"state":"The qˇuick"}}
-{"Key":"^"}
-{"Get":{"state":"ˇThe quick","mode":"Normal"}}
-{"Put":{"state":" The qˇuick"}}
-{"Key":"^"}
-{"Get":{"state":" ˇThe quick","mode":"Normal"}}
-{"Put":{"state":"ˇ"}}
-{"Key":"^"}
-{"Get":{"state":"ˇ","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown fox"}}
-{"Key":"^"}
-{"Get":{"state":"ˇThe quick\nbrown fox","mode":"Normal"}}
-{"Put":{"state":"ˇ\nThe quick"}}
-{"Key":"^"}
-{"Get":{"state":"ˇ\nThe quick","mode":"Normal"}}
-{"Put":{"state":"   ˇ \nThe quick"}}
-{"Key":"^"}
-{"Get":{"state":"   ˇ \nThe quick","mode":"Normal"}}

crates/vim2/test_data/test_jump_to_line_boundaries.json 🔗

@@ -1,28 +0,0 @@
-{"Put":{"state":"ˇThe quick\nbrown"}}
-{"Key":"$"}
-{"Get":{"state":"The quicˇk\nbrown","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown"}}
-{"Key":"$"}
-{"Get":{"state":"The quicˇk\nbrown","mode":"Normal"}}
-{"Key":"$"}
-{"Get":{"state":"The quicˇk\nbrown","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇbrown"}}
-{"Key":"$"}
-{"Get":{"state":"The quick\nbrowˇn","mode":"Normal"}}
-{"Key":"$"}
-{"Get":{"state":"The quick\nbrowˇn","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick\nbrown"}}
-{"Key":"0"}
-{"Get":{"state":"ˇThe quick\nbrown","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown"}}
-{"Key":"0"}
-{"Get":{"state":"ˇThe quick\nbrown","mode":"Normal"}}
-{"Put":{"state":"The quicˇk\nbrown"}}
-{"Key":"0"}
-{"Get":{"state":"ˇThe quick\nbrown","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇbrown"}}
-{"Key":"0"}
-{"Get":{"state":"The quick\nˇbrown","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrowˇn"}}
-{"Key":"0"}
-{"Get":{"state":"The quick\nˇbrown","mode":"Normal"}}

crates/vim2/test_data/test_k.json 🔗

@@ -1,15 +0,0 @@
-{"Put":{"state":"ˇThe quick\nbrown fox jumps"}}
-{"Key":"k"}
-{"Get":{"state":"ˇThe quick\nbrown fox jumps","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown fox jumps"}}
-{"Key":"k"}
-{"Get":{"state":"The qˇuick\nbrown fox jumps","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇbrown fox jumps"}}
-{"Key":"k"}
-{"Get":{"state":"ˇThe quick\nbrown fox jumps","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown fˇox jumps"}}
-{"Key":"k"}
-{"Get":{"state":"The quiˇck\nbrown fox jumps","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrown fox jumˇps"}}
-{"Key":"k"}
-{"Get":{"state":"The quicˇk\nbrown fox jumps","mode":"Normal"}}

crates/vim2/test_data/test_l.json 🔗

@@ -1,15 +0,0 @@
-{"Put":{"state":"ˇThe quick\nbrown"}}
-{"Key":"l"}
-{"Get":{"state":"Tˇhe quick\nbrown","mode":"Normal"}}
-{"Put":{"state":"The qˇuick\nbrown"}}
-{"Key":"l"}
-{"Get":{"state":"The quˇick\nbrown","mode":"Normal"}}
-{"Put":{"state":"The quicˇk\nbrown"}}
-{"Key":"l"}
-{"Get":{"state":"The quicˇk\nbrown","mode":"Normal"}}
-{"Put":{"state":"The quick\nˇbrown"}}
-{"Key":"l"}
-{"Get":{"state":"The quick\nbˇrown","mode":"Normal"}}
-{"Put":{"state":"The quick\nbrowˇn"}}
-{"Key":"l"}
-{"Get":{"state":"The quick\nbrowˇn","mode":"Normal"}}

crates/vim2/test_data/test_matching.json 🔗

@@ -1,17 +0,0 @@
-{"Put":{"state":"func ˇ(a string) {\n    do(something(with<Types>.and_arrays[0, 2]))\n}"}}
-{"Key":"%"}
-{"Get":{"state":"func (a stringˇ) {\n    do(something(with<Types>.and_arrays[0, 2]))\n}","mode":"Normal"}}
-{"Put":{"state":"func (a string) ˇ{\ndo(something(with<Types>.and_arrays[0, 2]))\n}"}}
-{"Key":"%"}
-{"Get":{"state":"func (a string) {\ndo(something(with<Types>.and_arrays[0, 2]))\nˇ}","mode":"Normal"}}
-{"Put":{"state":"ˇ{()}"}}
-{"Key":"%"}
-{"Get":{"state":"{()ˇ}","mode":"Normal"}}
-{"Key":"%"}
-{"Get":{"state":"ˇ{()}","mode":"Normal"}}
-{"Put":{"state":"{\n    ˇ{()}\n}"}}
-{"Key":"%"}
-{"Get":{"state":"{\n    {()ˇ}\n}","mode":"Normal"}}
-{"Put":{"state":"func ˇboop() {\n}"}}
-{"Key":"%"}
-{"Get":{"state":"func boop(ˇ) {\n}","mode":"Normal"}}

crates/vim2/test_data/test_multiline_surrounding_character_objects.json 🔗

@@ -1,15 +0,0 @@
-{"Put":{"state":"func empty(a string) bool {\n   if a == \"\" {\n      return true\n   }\n   ˇreturn false\n}"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"func empty(a string) bool {\n«   if a == \"\" {\n      return true\n   }\n   return false\nˇ»}","mode":"Visual"}}
-{"Put":{"state":"func empty(a string) bool {\n     if a == \"\" {\n         ˇreturn true\n     }\n     return false\n}"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"func empty(a string) bool {\n     if a == \"\" {\n«         return true\nˇ»     }\n     return false\n}","mode":"Visual"}}
-{"Put":{"state":"func empty(a string) bool {\n     if a == \"\" ˇ{\n         return true\n     }\n     return false\n}"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"{"}
-{"Get":{"state":"func empty(a string) bool {\n     if a == \"\" {\n«         return true\nˇ»     }\n     return false\n}","mode":"Visual"}}

crates/vim2/test_data/test_neovim.json 🔗

@@ -1,16 +0,0 @@
-{"Key":"i"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Key":"shift-T"}
-{"Key":"e"}
-{"Key":"s"}
-{"Key":"t"}
-{"Key":" "}
-{"Key":"t"}
-{"Key":"e"}
-{"Key":"s"}
-{"Key":"t"}
-{"Key":"escape"}
-{"Key":"0"}
-{"Key":"d"}
-{"Key":"w"}
-{"Get":{"state":"ˇtest","mode":"Normal"}}

crates/vim2/test_data/test_o.json 🔗

@@ -1,18 +0,0 @@
-{"Put":{"state":"ˇ"}}
-{"Key":"o"}
-{"Get":{"state":"\nˇ","mode":"Insert"}}
-{"Put":{"state":"The ˇquick"}}
-{"Key":"o"}
-{"Get":{"state":"The quick\nˇ","mode":"Insert"}}
-{"Put":{"state":"The qˇuick\nbrown fox\njumps over"}}
-{"Key":"o"}
-{"Get":{"state":"The quick\nˇ\nbrown fox\njumps over","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown ˇfox\njumps over"}}
-{"Key":"o"}
-{"Get":{"state":"The quick\nbrown fox\nˇ\njumps over","mode":"Insert"}}
-{"Put":{"state":"The quick\nbrown fox\njumps ˇover"}}
-{"Key":"o"}
-{"Get":{"state":"The quick\nbrown fox\njumps over\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick\nˇ\nbrown fox"}}
-{"Key":"o"}
-{"Get":{"state":"The quick\n\nˇ\nbrown fox","mode":"Insert"}}

crates/vim2/test_data/test_paste.json 🔗

@@ -1,31 +0,0 @@
-{"Put":{"state":"The quick brown\nfox ˇjumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"y"}
-{"ReadRegister":{"name":"\"","value":"jumps o"}}
-{"Put":{"state":"The quick brown\nfox jumps oveˇr\nthe lazy dog"}}
-{"Key":"p"}
-{"Get":{"state":"The quick brown\nfox jumps overjumps ˇo\nthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps oveˇr\nthe lazy dog"}}
-{"Key":"shift-p"}
-{"Get":{"state":"The quick brown\nfox jumps ovejumps ˇor\nthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
-{"Key":"d"}
-{"Key":"d"}
-{"ReadRegister":{"name":"\"","value":"fox jumps over\n"}}
-{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
-{"Key":"p"}
-{"Get":{"state":"The quick brown\nthe lazy dog\nˇfox jumps over","mode":"Normal"}}
-{"Key":"k"}
-{"Key":"shift-p"}
-{"Get":{"state":"The quick brown\nˇfox jumps over\nthe lazy dog\nfox jumps over","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps ˇover\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"j"}
-{"Key":"y"}
-{"ReadRegister":{"name":"\"","value":"over\nthe lazy do"}}
-{"Key":"p"}
-{"Get":{"state":"The quick brown\nfox jumps oˇover\nthe lazy dover\nthe lazy dog","mode":"Normal"}}
-{"Key":"u"}
-{"Key":"shift-p"}
-{"Get":{"state":"The quick brown\nfox jumps ˇover\nthe lazy doover\nthe lazy dog","mode":"Normal"}}

crates/vim2/test_data/test_paste_visual.json 🔗

@@ -1,42 +0,0 @@
-{"Put":{"state":"The quick brown\nfox jˇumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Key":"y"}
-{"Get":{"state":"The quick brown\nfox ˇjumps over\nthe lazy dog","mode":"Normal"}}
-{"Key":"w"}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Key":"p"}
-{"Get":{"state":"The quick brown\nfox jumps jumpˇs\nthe lazy dog","mode":"Normal"}}
-{"ReadRegister":{"name":"\"","value":"over"}}
-{"Key":"up"}
-{"Key":"shift-v"}
-{"Key":"shift-p"}
-{"Get":{"state":"ˇover\nfox jumps jumps\nthe lazy dog","mode":"Normal"}}
-{"ReadRegister":{"name":"\"","value":"over"}}
-{"Key":"ctrl-v"}
-{"Key":"down"}
-{"Key":"down"}
-{"Key":"p"}
-{"Get":{"state":"oveˇrver\noverox jumps jumps\noverhe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"d"}
-{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Key":"p"}
-{"Get":{"state":"The quick brown\nthe \nˇfox jumps over\n dog","mode":"Normal"}}
-{"ReadRegister":{"name":"\"","value":"lazy"}}
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"d"}
-{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
-{"Key":"k"}
-{"Key":"shift-v"}
-{"Key":"p"}
-{"Get":{"state":"ˇfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"ReadRegister":{"name":"\"","value":"The quick brown\n"}}

crates/vim2/test_data/test_paste_visual_block.json 🔗

@@ -1,31 +0,0 @@
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"ctrl-v"}
-{"Key":"2"}
-{"Key":"j"}
-{"Key":"y"}
-{"ReadRegister":{"name":"\"","value":"q\nj\nl"}}
-{"Key":"p"}
-{"Get":{"state":"The qˇquick brown\nfox jjumps over\nthe llazy dog","mode":"Normal"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Key":"shift-p"}
-{"Get":{"state":"The ˇq brown\nfox jjjumps over\nthe lllazy dog","mode":"Normal"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Key":"shift-p"}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"ctrl-v"}
-{"Key":"j"}
-{"Key":"y"}
-{"ReadRegister":{"name":"\"","value":"q\nj"}}
-{"Key":"l"}
-{"Key":"ctrl-v"}
-{"Key":"2"}
-{"Key":"j"}
-{"Key":"shift-p"}
-{"Get":{"state":"The qˇqick brown\nfox jjmps over\nthe lzy dog","mode":"Normal"}}
-{"Key":"shift-v"}
-{"Key":"p"}
-{"Get":{"state":"ˇq\nj\nfox jjmps over\nthe lzy dog","mode":"Normal"}}

crates/vim2/test_data/test_percent.json 🔗

@@ -1,58 +0,0 @@
-{"Put":{"state":"ˇconsole.log(var);"}}
-{"Key":"%"}
-{"Get":{"state":"console.log(varˇ);","mode":"Normal"}}
-{"Put":{"state":"console.logˇ(var);"}}
-{"Key":"%"}
-{"Get":{"state":"console.log(varˇ);","mode":"Normal"}}
-{"Put":{"state":"console.log(ˇvar);"}}
-{"Key":"%"}
-{"Get":{"state":"console.logˇ(var);","mode":"Normal"}}
-{"Put":{"state":"console.log(vaˇr);"}}
-{"Key":"%"}
-{"Get":{"state":"console.logˇ(var);","mode":"Normal"}}
-{"Put":{"state":"console.log(varˇ);"}}
-{"Key":"%"}
-{"Get":{"state":"console.logˇ(var);","mode":"Normal"}}
-{"Put":{"state":"console.log(var)ˇ;"}}
-{"Key":"%"}
-{"Get":{"state":"console.log(var)ˇ;","mode":"Normal"}}
-{"Put":{"state":"ˇconsole.log('var', [1, 2, 3]);"}}
-{"Key":"%"}
-{"Get":{"state":"console.log('var', [1, 2, 3]ˇ);","mode":"Normal"}}
-{"Put":{"state":"console.logˇ('var', [1, 2, 3]);"}}
-{"Key":"%"}
-{"Get":{"state":"console.log('var', [1, 2, 3]ˇ);","mode":"Normal"}}
-{"Put":{"state":"console.log(ˇ'var', [1, 2, 3]);"}}
-{"Key":"%"}
-{"Get":{"state":"console.log('var', [1, 2, 3ˇ]);","mode":"Normal"}}
-{"Put":{"state":"console.log('var', ˇ[1, 2, 3]);"}}
-{"Key":"%"}
-{"Get":{"state":"console.log('var', [1, 2, 3ˇ]);","mode":"Normal"}}
-{"Put":{"state":"console.log('var', [ˇ1, 2, 3]);"}}
-{"Key":"%"}
-{"Get":{"state":"console.log('var', ˇ[1, 2, 3]);","mode":"Normal"}}
-{"Put":{"state":"console.log('var', [1, ˇ2, 3]);"}}
-{"Key":"%"}
-{"Get":{"state":"console.log('var', ˇ[1, 2, 3]);","mode":"Normal"}}
-{"Put":{"state":"console.log('var', [1, 2, 3ˇ]);"}}
-{"Key":"%"}
-{"Get":{"state":"console.log('var', ˇ[1, 2, 3]);","mode":"Normal"}}
-{"Put":{"state":"console.log('var', [1, 2, 3]ˇ);"}}
-{"Key":"%"}
-{"Get":{"state":"console.logˇ('var', [1, 2, 3]);","mode":"Normal"}}
-{"Put":{"state":"console.log('var', [1, 2, 3])ˇ;"}}
-{"Key":"%"}
-{"Get":{"state":"console.log('var', [1, 2, 3])ˇ;","mode":"Normal"}}
-{"Put":{"state":"let result = curried_funˇ()();"}}
-{"Key":"%"}
-{"Get":{"state":"let result = curried_fun(ˇ)();","mode":"Normal"}}
-{"Key":"%"}
-{"Get":{"state":"let result = curried_funˇ()();","mode":"Normal"}}
-{"Put":{"state":"let result = curried_fun()ˇ();"}}
-{"Key":"%"}
-{"Get":{"state":"let result = curried_fun()(ˇ);","mode":"Normal"}}
-{"Key":"%"}
-{"Get":{"state":"let result = curried_fun()ˇ();","mode":"Normal"}}
-{"Put":{"state":"let result = curried_fun()()ˇ;"}}
-{"Key":"%"}
-{"Get":{"state":"let result = curried_fun()()ˇ;","mode":"Normal"}}

crates/vim2/test_data/test_repeat_motion_counts.json 🔗

@@ -1,13 +0,0 @@
-{"Put":{"state":"ˇthe quick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"3"}
-{"Key":"d"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"ˇ brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"."}
-{"Get":{"state":" brown\nˇ over\nthe lazy dog","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"2"}
-{"Key":"."}
-{"Get":{"state":" brown\n over\nˇe lazy dog","mode":"Normal"}}

crates/vim2/test_data/test_repeat_visual.json 🔗

@@ -1,51 +0,0 @@
-{"Put":{"state":"ˇthe quick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Key":"s"}
-{"Key":"o"}
-{"Key":"escape"}
-{"Get":{"state":"ˇo quick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"w"}
-{"Key":"."}
-{"Get":{"state":"o quick brown\nfox ˇops over\nthe lazy dog","mode":"Normal"}}
-{"Key":"f"}
-{"Key":"r"}
-{"Key":"."}
-{"Get":{"state":"o quick brown\nfox ops oveˇothe lazy dog","mode":"Normal"}}
-{"Put":{"state":"the ˇquick brown\nfox jumps over\nfox jumps over\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"j"}
-{"Key":"x"}
-{"Get":{"state":"the ˇumps over\nfox jumps over\nfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"Key":"."}
-{"Get":{"state":"the ˇumps over\nfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"Key":"w"}
-{"Key":"."}
-{"Get":{"state":"the umps ˇumps over\nthe lazy dog","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"."}
-{"Get":{"state":"the umps umps over\nthe ˇog","mode":"Normal"}}
-{"Put":{"state":"ˇthe quick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"ctrl-v"}
-{"Key":"j"}
-{"Key":"j"}
-{"Key":"shift-i"}
-{"Key":"o"}
-{"Key":"escape"}
-{"Get":{"state":"ˇothe quick brown\nofox jumps over\nothe lazy dog","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"4"}
-{"Key":"l"}
-{"Key":"."}
-{"Get":{"state":"othe quick brown\nofoxˇo jumps over\notheo lazy dog","mode":"Normal"}}
-{"Put":{"state":"ˇthe quick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"shift-r"}
-{"Key":"o"}
-{"Key":"escape"}
-{"Get":{"state":"ˇo\nfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"."}
-{"Get":{"state":"o\nˇo\nthe lazy dog","mode":"Normal"}}

crates/vim2/test_data/test_repeated_cb.json 🔗

@@ -1,275 +0,0 @@
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The ˇick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The ˇ brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The quick ˇn\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The quick ˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nˇjumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nfox ˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇver\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"ˇick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"ˇ brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"The ˇn\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"The ˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"The quick ˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\nˇjumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nfox ˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇver\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"ˇick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"ˇ brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"ˇn\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"ˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"The ˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"The quick ˇjumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\nˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nfox ˇver\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nfox ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"ˇick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"ˇ brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"ˇn\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"ˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"ˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"The ˇjumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"The quick ˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\nˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nˇver\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\n\nˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"ˇick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"ˇ brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"ˇn\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"ˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"ˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"ˇjumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"The ˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"The quick ˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\nˇver\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"b"}
-{"Get":{"state":"The quick brown\nˇ\nthe lazy dog\n","mode":"Insert"}}

crates/vim2/test_data/test_repeated_ce.json 🔗

@@ -1,275 +0,0 @@
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"ˇ quick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quˇ brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quickˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quick browˇ jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\nˇ jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nˇ jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox ˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"ˇ brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quickˇ jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quick browˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\nˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox ˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇ lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇ lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"ˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quˇ jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quickˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quick browˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\nˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇ dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇ dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"ˇ jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quickˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quick browˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\nˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox ˇ lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"ˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quickˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quick browˇ lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\nˇ lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nˇ lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox ˇ dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"e"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ","mode":"Insert"}}

crates/vim2/test_data/test_repeated_cj.json 🔗

@@ -1,275 +0,0 @@
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\nˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\nˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"ˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"ˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"j"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ","mode":"Insert"}}

crates/vim2/test_data/test_repeated_cl.json 🔗

@@ -1,275 +0,0 @@
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"ˇhe quick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quˇck brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quickˇbrown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quick browˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nˇox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox ˇumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇover\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇver\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇer\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"1"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇhe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"ˇe quick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quˇk brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quickˇrown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quick browˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nˇx jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox ˇmps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇver\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇer\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇr\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"2"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"ˇ quick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quˇ brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quickˇown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quick browˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nˇ jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox ˇps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇer\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇr\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"3"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇ lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"ˇquick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quˇbrown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quickˇwn\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quick browˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nˇjumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox ˇs-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇr\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"4"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇlazy dog\n","mode":"Insert"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"ˇuick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quˇrown\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quickˇn\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quick browˇ\n\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nˇumps-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox ˇ-over\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-oˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"c"}
-{"Key":"5"}
-{"Key":"l"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇazy dog\n","mode":"Insert"}}

crates/vim2/test_data/test_repeated_word.json 🔗

@@ -1,214 +0,0 @@
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The ˇquick brown\n\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick ˇbrown\n\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick ˇbrown\n\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe ˇlazy dog\n","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick ˇbrown\n\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe ˇlazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe ˇlazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"2"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy ˇdog\n","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe ˇlazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy ˇdog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy ˇdog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"3"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy dog\nˇ","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe ˇlazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy ˇdog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy dog\nˇ","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy dog\nˇ","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"4"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy dog\nˇ","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quˇick brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quickˇ brown\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick browˇn\n\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nˇ\nfox jumps-over\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nˇfox jumps-over\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe ˇlazy dog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox ˇjumps-over\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy ˇdog\n","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumpsˇ-over\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy dog\nˇ","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-ˇover\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy dog\nˇ","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-oˇver\nthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy dog\nˇ","mode":"Normal"}}
-{"Put":{"state":"The quick brown\n\nfox jumps-over\nˇthe lazy dog\n"}}
-{"Key":"5"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n\nfox jumps-over\nthe lazy dog\nˇ","mode":"Normal"}}

crates/vim2/test_data/test_selection_goal.json 🔗

@@ -1,8 +0,0 @@
-{"Put":{"state":";;ˇ;\nLorem Ipsum"}}
-{"Key":"a"}
-{"Key":"down"}
-{"Key":"up"}
-{"Key":";"}
-{"Key":"down"}
-{"Key":"up"}
-{"Get":{"state":";;;;ˇ\nLorem Ipsum","mode":"Insert"}}

crates/vim2/test_data/test_singleline_surrounding_character_objects.json 🔗

@@ -1,27 +0,0 @@
-{"SetOption":{"value":"wrap"}}
-{"SetOption":{"value":"columns=12"}}
-{"Put":{"state":"helˇlo \"world\"!"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"hello \"«worldˇ»\"!","mode":"Visual"}}
-{"Put":{"state":"hello \"wˇorld\"!"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"\""}
-{"Get":{"state":"hello \"«worldˇ»\"!","mode":"Visual"}}
-{"Put":{"state":"hello \"wˇorld\"!"}}
-{"Key":"v"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"hello« \"world\"ˇ»!","mode":"Visual"}}
-{"Put":{"state":"hello \"wˇorld\" !"}}
-{"Key":"v"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"hello «\"world\" ˇ»!","mode":"Visual"}}
-{"Put":{"state":"hello \"wˇorld\"•\ngoodbye"}}
-{"Key":"v"}
-{"Key":"a"}
-{"Key":"\""}
-{"Get":{"state":"hello «\"world\" ˇ»\ngoodbye","mode":"Visual"}}

crates/vim2/test_data/test_start_end_of_paragraph.json 🔗

@@ -1,13 +0,0 @@
-{"Put":{"state":"ˇabc\ndef\n\nparagraph\nthe second\n\n\n\nthird and\nfinal"}}
-{"Key":"}"}
-{"Get":{"state":"abc\ndef\nˇ\nparagraph\nthe second\n\n\n\nthird and\nfinal","mode":"Normal"}}
-{"Key":"{"}
-{"Get":{"state":"ˇabc\ndef\n\nparagraph\nthe second\n\n\n\nthird and\nfinal","mode":"Normal"}}
-{"Key":"2"}
-{"Key":"}"}
-{"Get":{"state":"abc\ndef\n\nparagraph\nthe second\nˇ\n\n\nthird and\nfinal","mode":"Normal"}}
-{"Key":"}"}
-{"Get":{"state":"abc\ndef\n\nparagraph\nthe second\n\n\n\nthird and\nfinaˇl","mode":"Normal"}}
-{"Key":"2"}
-{"Key":"{"}
-{"Get":{"state":"abc\ndef\nˇ\nparagraph\nthe second\n\n\n\nthird and\nfinal","mode":"Normal"}}

crates/vim2/test_data/test_substitute_line.json 🔗

@@ -1,29 +0,0 @@
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog\n"}}
-{"Key":"shift-s"}
-{"Key":"o"}
-{"Get":{"state":"The quick brown\noˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog\n"}}
-{"Key":"v"}
-{"Key":"k"}
-{"Key":"shift-s"}
-{"Key":"o"}
-{"Get":{"state":"oˇ\nthe lazy dog\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog\n"}}
-{"Key":"ctrl-v"}
-{"Key":"j"}
-{"Key":"shift-s"}
-{"Key":"o"}
-{"Get":{"state":"The quick brown\noˇ\n","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog\n"}}
-{"Key":"v"}
-{"Key":"$"}
-{"Key":"shift-s"}
-{"Key":"o"}
-{"Get":{"state":"The quick brown\noˇ\nthe lazy dog\n","mode":"Insert"}}
-{"SetOption":{"value":"shiftwidth=4"}}
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog\n"}}
-{"Key":">"}
-{"Key":">"}
-{"Key":"shift-s"}
-{"Key":"o"}
-{"Get":{"state":"The quick brown\n    oˇ\nthe lazy dog\n","mode":"Insert"}}

crates/vim2/test_data/test_visual_block_insert.json 🔗

@@ -1,18 +0,0 @@
-{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog\n"}}
-{"Key":"ctrl-v"}
-{"Key":"9"}
-{"Key":"down"}
-{"Get":{"state":"«Tˇ»he quick brown\n«fˇ»ox jumps over\n«tˇ»he lazy dog\nˇ","mode":"VisualBlock"}}
-{"Key":"shift-i"}
-{"Key":"k"}
-{"Key":"escape"}
-{"Get":{"state":"ˇkThe quick brown\nkfox jumps over\nkthe lazy dog\nk","mode":"Normal"}}
-{"Put":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog\n"}}
-{"Key":"ctrl-v"}
-{"Key":"9"}
-{"Key":"down"}
-{"Get":{"state":"«Tˇ»he quick brown\n«fˇ»ox jumps over\n«tˇ»he lazy dog\nˇ","mode":"VisualBlock"}}
-{"Key":"c"}
-{"Key":"k"}
-{"Key":"escape"}
-{"Get":{"state":"ˇkhe quick brown\nkox jumps over\nkhe lazy dog\nk","mode":"Normal"}}

crates/vim2/test_data/test_visual_block_issue_2123.json 🔗

@@ -1,5 +0,0 @@
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog\n"}}
-{"Key":"ctrl-v"}
-{"Key":"right"}
-{"Key":"down"}
-{"Get":{"state":"The «quˇ»ick brown\nfox «juˇ»mps over\nthe lazy dog\n","mode":"VisualBlock"}}

crates/vim2/test_data/test_visual_block_mode.json 🔗

@@ -1,38 +0,0 @@
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"ctrl-v"}
-{"Get":{"state":"The «qˇ»uick brown\nfox jumps over\nthe lazy dog","mode":"VisualBlock"}}
-{"Key":"2"}
-{"Key":"down"}
-{"Get":{"state":"The «qˇ»uick brown\nfox «jˇ»umps over\nthe «lˇ»azy dog","mode":"VisualBlock"}}
-{"Key":"e"}
-{"Get":{"state":"The «quicˇ»k brown\nfox «jumpˇ»s over\nthe «lazyˇ» dog","mode":"VisualBlock"}}
-{"Key":"^"}
-{"Get":{"state":"«ˇThe q»uick brown\n«ˇfox j»umps over\n«ˇthe l»azy dog","mode":"VisualBlock"}}
-{"Key":"$"}
-{"Get":{"state":"The «quick brownˇ»\nfox «jumps overˇ»\nthe «lazy dogˇ»","mode":"VisualBlock"}}
-{"Key":"shift-f"}
-{"Key":" "}
-{"Get":{"state":"The «quickˇ» brown\nfox «jumpsˇ» over\nthe «lazy ˇ»dog","mode":"VisualBlock"}}
-{"Key":"v"}
-{"Get":{"state":"The «quick brown\nfox jumps over\nthe lazy ˇ»dog","mode":"Visual"}}
-{"Key":"ctrl-v"}
-{"Get":{"state":"The «quickˇ» brown\nfox «jumpsˇ» over\nthe «lazy ˇ»dog","mode":"VisualBlock"}}
-{"Put":{"state":"The ˇquick\nbrown\nfox\njumps over the\n\nlazy dog\n"}}
-{"Key":"ctrl-v"}
-{"Key":"down"}
-{"Key":"down"}
-{"Get":{"state":"The«ˇ q»uick\nbro«ˇwn»\nfoxˇ\njumps over the\n\nlazy dog\n","mode":"VisualBlock"}}
-{"Key":"down"}
-{"Get":{"state":"The «qˇ»uick\nbrow«nˇ»\nfox\njump«sˇ» over the\n\nlazy dog\n","mode":"VisualBlock"}}
-{"Key":"left"}
-{"Get":{"state":"The«ˇ q»uick\nbro«ˇwn»\nfoxˇ\njum«ˇps» over the\n\nlazy dog\n","mode":"VisualBlock"}}
-{"Key":"s"}
-{"Key":"o"}
-{"Key":"escape"}
-{"Get":{"state":"Theˇouick\nbroo\nfoxo\njumo over the\n\nlazy dog\n","mode":"Normal"}}
-{"Put":{"state":"Theˇ quick brown\n\nfox jumps over\nthe lazy dog\n"}}
-{"Key":"l"}
-{"Key":"ctrl-v"}
-{"Key":"j"}
-{"Key":"j"}
-{"Get":{"state":"The «qˇ»uick brown\n\nfox «jˇ»umps over\nthe lazy dog\n","mode":"VisualBlock"}}

crates/vim2/test_data/test_visual_change.json 🔗

@@ -1,47 +0,0 @@
-{"Put":{"state":"The quick ˇbrown"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"c"}
-{"Get":{"state":"The quick ˇ","mode":"Insert"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"j"}
-{"Key":"c"}
-{"Get":{"state":"The ˇver\nthe lazy dog","mode":"Insert"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"j"}
-{"Key":"c"}
-{"Get":{"state":"The ˇver\nthe lazy dog","mode":"Insert"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"k"}
-{"Key":"c"}
-{"Get":{"state":"The ˇrown\nfox jumps over\nthe lazy dog","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps ˇover\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"j"}
-{"Key":"c"}
-{"Get":{"state":"The quick brown\nfox jumps ˇhe lazy dog","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps ˇover\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"k"}
-{"Key":"c"}
-{"Get":{"state":"The quick brown\nˇver\nthe lazy dog","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"j"}
-{"Key":"c"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe ˇog","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"k"}
-{"Key":"c"}
-{"Get":{"state":"The quick brown\nfox jumpsˇazy dog","mode":"Insert"}}

crates/vim2/test_data/test_visual_delete.json 🔗

@@ -1,48 +0,0 @@
-{"Put":{"state":"The quick ˇbrown"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Get":{"state":"The quick «brownˇ»","mode":"Visual"}}
-{"Put":{"state":"The quick ˇbrown"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"x"}
-{"Get":{"state":"The quickˇ ","mode":"Normal"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"j"}
-{"Key":"x"}
-{"Get":{"state":"The ˇver\nthe lazy dog","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"p"}
-{"Get":{"state":"The ver\nthe lˇquick brown\nfox jumps oazy dog","mode":"Normal"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"j"}
-{"Key":"x"}
-{"Get":{"state":"The ˇver\nthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"j"}
-{"Key":"x"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe ˇog","mode":"Normal"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"b"}
-{"Key":"k"}
-{"Key":"x"}
-{"Get":{"state":"ˇuick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps ˇover\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"b"}
-{"Key":"k"}
-{"Key":"x"}
-{"Get":{"state":"The ˇver\nthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog"}}
-{"Key":"v"}
-{"Key":"b"}
-{"Key":"k"}
-{"Key":"x"}
-{"Get":{"state":"The quick brown\nˇazy dog","mode":"Normal"}}

crates/vim2/test_data/test_visual_line_change.json 🔗

@@ -1,35 +0,0 @@
-{"Put":{"state":"The quˇick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"c"}
-{"Get":{"state":"ˇ\nfox jumps over\nthe lazy dog","mode":"Insert"}}
-{"Key":"escape"}
-{"Key":"j"}
-{"Key":"p"}
-{"Get":{"state":"\nfox jumps over\nˇThe quick brown\nthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"c"}
-{"Get":{"state":"The quick brown\nˇ\nthe lazy dog","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe laˇzy dog"}}
-{"Key":"shift-v"}
-{"Key":"c"}
-{"Get":{"state":"The quick brown\nfox jumps over\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quˇick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"j"}
-{"Key":"c"}
-{"Get":{"state":"ˇ\nthe lazy dog","mode":"Insert"}}
-{"Key":"escape"}
-{"Key":"j"}
-{"Key":"p"}
-{"Get":{"state":"\nthe lazy dog\nˇThe quick brown\nfox jumps over","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"j"}
-{"Key":"c"}
-{"Get":{"state":"The quick brown\nˇ","mode":"Insert"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe laˇzy dog"}}
-{"Key":"shift-v"}
-{"Key":"j"}
-{"Key":"c"}
-{"Get":{"state":"The quick brown\nfox jumps over\nˇ","mode":"Insert"}}

crates/vim2/test_data/test_visual_line_delete.json 🔗

@@ -1,23 +0,0 @@
-{"Put":{"state":"The quˇick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"x"}
-{"Get":{"state":"fox juˇmps over\nthe lazy dog","mode":"Normal"}}
-{"Key":"p"}
-{"Get":{"state":"fox jumps over\nˇThe quick brown\nthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe laˇzy dog"}}
-{"Key":"shift-v"}
-{"Key":"x"}
-{"Get":{"state":"The quick brown\nfox juˇmps over","mode":"Normal"}}
-{"ReadRegister":{"name":"\"","value":"the lazy dog\n"}}
-{"Put":{"state":"The quˇick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"j"}
-{"Key":"x"}
-{"Get":{"state":"the laˇzy dog","mode":"Normal"}}
-{"Key":"p"}
-{"Get":{"state":"the lazy dog\nˇThe quick brown\nfox jumps over","mode":"Normal"}}
-{"Put":{"state":"The ˇlong line\nshould not\ncrash\n"}}
-{"Key":"shift-v"}
-{"Key":"$"}
-{"Key":"x"}
-{"Get":{"state":"should noˇt\ncrash\n","mode":"Normal"}}

crates/vim2/test_data/test_visual_object.json 🔗

@@ -1,19 +0,0 @@
-{"Put":{"state":"hello (in [parˇens] o)"}}
-{"Key":"ctrl-v"}
-{"Key":"l"}
-{"Key":"a"}
-{"Key":"]"}
-{"Get":{"state":"hello (in «[parens]ˇ» o)","mode":"Visual"}}
-{"Key":"i"}
-{"Key":"("}
-{"Get":{"state":"hello («in [parens] oˇ»)","mode":"Visual"}}
-{"Put":{"state":"hello in a wˇord again."}}
-{"Key":"ctrl-v"}
-{"Key":"l"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"hello in a w«ordˇ» again.","mode":"VisualBlock"}}
-{"Key":"o"}
-{"Key":"a"}
-{"Key":"s"}
-{"Get":{"state":"«ˇhello in a word» again.","mode":"VisualBlock"}}

crates/vim2/test_data/test_visual_word_object.json 🔗

@@ -1,236 +0,0 @@
-{"Put":{"state":"The quick brown\nˇ\nfox"}}
-{"Key":"v"}
-{"Get":{"state":"The quick brown\n«\nˇ»fox","mode":"Visual"}}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown\n«\nˇ»fox","mode":"Visual"}}
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick «brownˇ»   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick «brownˇ»   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown«   ˇ»\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox «jumpsˇ» over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox «jumpsˇ» over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps« ˇ»over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog«  ˇ»\n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n«\nˇ»\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n«\nˇ»\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n«\nˇ»The-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\n«Theˇ»-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe«-ˇ»quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-«quickˇ» brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-«quickˇ» brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick« ˇ»brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick «brownˇ» \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown« ˇ»\n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n«  ˇ»\n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n«  ˇ»\n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n«  ˇ»fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-«jumpsˇ» over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog« ˇ»\n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n«\nˇ»","mode":"Visual"}}
-{"Put":{"state":"The quick ˇbrown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick «brownˇ»   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick browˇn   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick «brownˇ»   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brownˇ   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown«   ˇ»\nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox ˇjumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox «jumpsˇ» over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox juˇmps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox «jumpsˇ» over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumpsˇ over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps« ˇ»over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dogˇ  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog«  ˇ»\n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \nˇ\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n«\nˇ»\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\nˇ\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n«\nˇ»\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\nˇ\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n«\nˇ»The-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThˇe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\n«The-quickˇ» brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nTheˇ-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\n«The-quickˇ» brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-ˇquick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\n«The-quickˇ» brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quˇick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\n«The-quickˇ» brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quickˇ brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick« ˇ»brown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick ˇbrown \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick «brownˇ» \n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brownˇ \n  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown« ˇ»\n  \n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \nˇ  \n  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n«  ˇ»\n  \n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \nˇ  \n  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n«  ˇ»\n  fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \nˇ  fox-jumps over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n«  ˇ»fox-jumps over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumpˇs over\nthe lazy dog \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  «fox-jumpsˇ» over\nthe lazy dog \n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dogˇ \n\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog« ˇ»\n\n","mode":"Visual"}}
-{"Put":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \nˇ\n"}}
-{"Key":"v"}
-{"Key":"i"}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick brown   \nfox jumps over\nthe lazy dog  \n\n\n\nThe-quick brown \n  \n  \n  fox-jumps over\nthe lazy dog \n«\nˇ»","mode":"Visual"}}

crates/vim2/test_data/test_visual_yank.json 🔗

@@ -1,35 +0,0 @@
-{"Put":{"state":"The quick ˇbrown"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"y"}
-{"Get":{"state":"The quick ˇbrown","mode":"Normal"}}
-{"ReadRegister":{"name":"\"","value":"brown"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"j"}
-{"Key":"y"}
-{"Get":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"ReadRegister":{"name":"\"","value":"quick brown\nfox jumps o"}}
-{"Put":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog"}}
-{"Key":"v"}
-{"Key":"w"}
-{"Key":"j"}
-{"Key":"y"}
-{"Get":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog","mode":"Normal"}}
-{"ReadRegister":{"name":"\"","value":"lazy d"}}
-{"Key":"shift-v"}
-{"Key":"y"}
-{"ReadRegister":{"name":"\"","value":"the lazy dog\n"}}
-{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"v"}
-{"Key":"b"}
-{"Key":"k"}
-{"Key":"y"}
-{"Get":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"Put":{"state":"The quick brown\nfox ˇjumps over\nthe lazy dog"}}
-{"Key":"shift-v"}
-{"Key":"shift-g"}
-{"Key":"shift-y"}
-{"Get":{"state":"The quick brown\nˇfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"ReadRegister":{"name":"\"","value":"fox jumps over\nthe lazy dog\n"}}

crates/vim2/test_data/test_w.json 🔗

@@ -1,40 +0,0 @@
-{"Put":{"state":"The ˇquick-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"w"}
-{"Get":{"state":"The quickˇ-brown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"w"}
-{"Get":{"state":"The quick-ˇbrown\n\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"w"}
-{"Get":{"state":"The quick-brown\nˇ\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"w"}
-{"Get":{"state":"The quick-brown\n\nˇ\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"w"}
-{"Get":{"state":"The quick-brown\n\n\nˇfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"w"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps ˇover\nthe","mode":"Normal"}}
-{"Key":"w"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nˇthe","mode":"Normal"}}
-{"Key":"w"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nthˇe","mode":"Normal"}}
-{"Key":"w"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nthˇe","mode":"Normal"}}
-{"Put":{"state":"The ˇquick-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick-brown\nˇ\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quickˇ-brown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick-brown\nˇ\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Put":{"state":"The quick-ˇbrown\n\n\nfox_jumps over\nthe"}}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick-brown\nˇ\n\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick-brown\n\nˇ\nfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick-brown\n\n\nˇfox_jumps over\nthe","mode":"Normal"}}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps ˇover\nthe","mode":"Normal"}}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nˇthe","mode":"Normal"}}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nthˇe","mode":"Normal"}}
-{"Key":"shift-w"}
-{"Get":{"state":"The quick-brown\n\n\nfox_jumps over\nthˇe","mode":"Normal"}}

crates/vim2/test_data/test_wrapped_lines.json 🔗

@@ -1,61 +0,0 @@
-{"SetOption":{"value":"wrap"}}
-{"SetOption":{"value":"columns=12"}}
-{"Put":{"state":"tˇwelve char twelve char\ntwelve char\n"}}
-{"Key":"j"}
-{"Get":{"state":"twelve char twelve char\ntˇwelve char\n","mode":"Normal"}}
-{"Key":"k"}
-{"Get":{"state":"tˇwelve char twelve char\ntwelve char\n","mode":"Normal"}}
-{"Key":"g"}
-{"Key":"j"}
-{"Get":{"state":"twelve char tˇwelve char\ntwelve char\n","mode":"Normal"}}
-{"Key":"g"}
-{"Key":"j"}
-{"Get":{"state":"twelve char twelve char\ntˇwelve char\n","mode":"Normal"}}
-{"Key":"g"}
-{"Key":"k"}
-{"Get":{"state":"twelve char tˇwelve char\ntwelve char\n","mode":"Normal"}}
-{"Key":"g"}
-{"Key":"^"}
-{"Get":{"state":"twelve char ˇtwelve char\ntwelve char\n","mode":"Normal"}}
-{"Key":"^"}
-{"Get":{"state":"ˇtwelve char twelve char\ntwelve char\n","mode":"Normal"}}
-{"Key":"g"}
-{"Key":"$"}
-{"Get":{"state":"twelve charˇ twelve char\ntwelve char\n","mode":"Normal"}}
-{"Key":"$"}
-{"Get":{"state":"twelve char twelve chaˇr\ntwelve char\n","mode":"Normal"}}
-{"Put":{"state":"tˇwelve char twelve char\ntwelve char\n"}}
-{"Key":"enter"}
-{"Get":{"state":"twelve char twelve char\nˇtwelve char\n","mode":"Normal"}}
-{"Put":{"state":"twelve char\ntˇwelve char twelve char\ntwelve char\n"}}
-{"Key":"o"}
-{"Key":"o"}
-{"Key":"escape"}
-{"Get":{"state":"twelve char\ntwelve char twelve char\nˇo\ntwelve char\n","mode":"Normal"}}
-{"Put":{"state":"twelve char\ntˇwelve char twelve char\ntwelve char\n"}}
-{"Key":"shift-a"}
-{"Key":"a"}
-{"Key":"escape"}
-{"Get":{"state":"twelve char\ntwelve char twelve charˇa\ntwelve char\n","mode":"Normal"}}
-{"Key":"shift-i"}
-{"Key":"i"}
-{"Key":"escape"}
-{"Get":{"state":"twelve char\nˇitwelve char twelve chara\ntwelve char\n","mode":"Normal"}}
-{"Key":"shift-d"}
-{"Get":{"state":"twelve char\nˇ\ntwelve char\n","mode":"Normal"}}
-{"Put":{"state":"twelve char\ntwelve char tˇwelve char\ntwelve char\n"}}
-{"Key":"shift-o"}
-{"Key":"o"}
-{"Key":"escape"}
-{"Get":{"state":"twelve char\nˇo\ntwelve char twelve char\ntwelve char\n","mode":"Normal"}}
-{"Put":{"state":"fourteen chaˇr\nfourteen char\n"}}
-{"Key":"d"}
-{"Key":"i"}
-{"Key":"w"}
-{"Get":{"state":"fourteenˇ \nfourteen char\n","mode":"Normal"}}
-{"Key":"j"}
-{"Key":"shift-f"}
-{"Key":"e"}
-{"Key":"f"}
-{"Key":"r"}
-{"Get":{"state":"fourteen \nfourteen chaˇr\n","mode":"Normal"}}

crates/vim2/test_data/test_wrapped_motions.json 🔗

@@ -1,15 +0,0 @@
-{"SetOption":{"value":"wrap"}}
-{"SetOption":{"value":"columns=12"}}
-{"Put":{"state":"aaˇaa\n😃😃"}}
-{"Key":"j"}
-{"Get":{"state":"aaaa\n😃ˇ😃","mode":"Normal"}}
-{"Put":{"state":"123456789012aaˇaa\n123456789012😃😃"}}
-{"Key":"j"}
-{"Get":{"state":"123456789012aaaa\n123456789012😃ˇ😃","mode":"Normal"}}
-{"Put":{"state":"123456789012aaˇaa\n123456789012😃😃"}}
-{"Key":"j"}
-{"Get":{"state":"123456789012aaaa\n123456789012😃ˇ😃","mode":"Normal"}}
-{"Put":{"state":"123456789012aaaaˇaaaaaaaa123456789012\nwow\n123456789012😃😃😃😃😃😃123456789012"}}
-{"Key":"j"}
-{"Key":"j"}
-{"Get":{"state":"123456789012aaaaaaaaaaaa123456789012\nwow\n123456789012😃😃ˇ😃😃😃😃123456789012","mode":"Normal"}}

crates/vim2/test_data/test_x.json 🔗

@@ -1,12 +0,0 @@
-{"Put":{"state":"ˇTest"}}
-{"Key":"x"}
-{"Get":{"state":"ˇest","mode":"Normal"}}
-{"Put":{"state":"Teˇst"}}
-{"Key":"x"}
-{"Get":{"state":"Teˇt","mode":"Normal"}}
-{"Put":{"state":"Tesˇt"}}
-{"Key":"x"}
-{"Get":{"state":"Teˇs","mode":"Normal"}}
-{"Put":{"state":"Tesˇt\ntest"}}
-{"Key":"x"}
-{"Get":{"state":"Teˇs\ntest","mode":"Normal"}}

crates/vim2/test_data/test_zero.json 🔗

@@ -1,7 +0,0 @@
-{"Put":{"state":"The quˇick brown\nfox jumps over\nthe lazy dog"}}
-{"Key":"0"}
-{"Get":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
-{"Key":"1"}
-{"Key":"0"}
-{"Key":"l"}
-{"Get":{"state":"The quick ˇbrown\nfox jumps over\nthe lazy dog","mode":"Normal"}}

crates/welcome/Cargo.toml 🔗

@@ -26,7 +26,7 @@ theme_selector = { path = "../theme_selector" }
 util = { path = "../util" }
 picker = { package = "picker2", path = "../picker2" }
 workspace = { package = "workspace2", path = "../workspace2" }
-vim = { package = "vim2", path = "../vim2" }
+vim = { path = "../vim" }
 
 anyhow.workspace = true
 log.workspace = true

crates/zed/Cargo.toml 🔗

@@ -25,21 +25,21 @@ channel = { package = "channel2", path = "../channel2" }
 cli = { path = "../cli" }
 collab_ui = { path = "../collab_ui" }
 collections = { path = "../collections" }
-command_palette = { package="command_palette2", path = "../command_palette2" }
+command_palette = { path = "../command_palette" }
 # component_test = { path = "../component_test" }
 client = { package = "client2", path = "../client2" }
 # clock = { path = "../clock" }
 copilot = { package = "copilot2", path = "../copilot2" }
 copilot_button = { package = "copilot_button2", path = "../copilot_button2" }
-diagnostics = { package = "diagnostics2", path = "../diagnostics2" }
+diagnostics = { path = "../diagnostics" }
 db = { package = "db2", path = "../db2" }
 editor = { package="editor2", path = "../editor2" }
 feedback = { package="feedback2", path = "../feedback2" }
-file_finder = { package="file_finder2", path = "../file_finder2" }
+file_finder = { path = "../file_finder" }
 search = { package = "search2", path = "../search2" }
 fs = { package = "fs2", path = "../fs2" }
 fsevent = { path = "../fsevent" }
-go_to_line = { package = "go_to_line2", path = "../go_to_line2" }
+go_to_line = { path = "../go_to_line" }
 gpui = { package = "gpui2", path = "../gpui2" }
 install_cli = { package = "install_cli2", path = "../install_cli2" }
 journal = { package = "journal2", path = "../journal2" }
@@ -70,7 +70,7 @@ theme = { package = "theme2", path = "../theme2" }
 theme_selector = { path = "../theme_selector" }
 util = { path = "../util" }
 semantic_index = { package = "semantic_index2", path = "../semantic_index2" }
-vim = { package = "vim2", path = "../vim2" }
+vim = { path = "../vim" }
 workspace = { package = "workspace2", path = "../workspace2" }
 welcome = { path = "../welcome" }
 zed_actions = {package = "zed_actions2", path = "../zed_actions2"}