vim: Implement named registers (#12895)

Release Notes:

- vim: Add support for register selection `"a`-`"z`, `"0`-`"9`, `"-`.
`"_` and `"%`
([#11511](https://github.com/zed-industries/zed/issues/11511))

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Paul Eguisier 2024-06-12 18:40:27 +02:00 committed by GitHub
parent 3c3dad6830
commit 001f17c011
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 454 additions and 66 deletions

View File

@ -385,6 +385,7 @@
"g u": ["vim::PushOperator", "Lowercase"],
"g shift-u": ["vim::PushOperator", "Uppercase"],
"g ~": ["vim::PushOperator", "OppositeCase"],
"\"": ["vim::PushOperator", "Register"],
"ctrl-pagedown": "pane::ActivateNextItem",
"ctrl-pageup": "pane::ActivatePrevItem",
// tree-sitter related commands
@ -399,6 +400,7 @@
{
"context": "Editor && vim_mode == visual && vim_operator == none && !VimWaiting",
"bindings": {
"\"": ["vim::PushOperator", "Register"],
// tree-sitter related commands
"[ x": "editor::SelectLargerSyntaxNode",
"] x": "editor::SelectSmallerSyntaxNode"

View File

@ -1502,7 +1502,7 @@ struct ActiveDiagnosticGroup {
is_valid: bool,
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone)]
pub struct ClipboardSelection {
pub len: usize,
pub is_entire_line: bool,

View File

@ -356,7 +356,7 @@ fn is_identifier_char(c: char) -> bool {
}
fn is_vim_operator_char(c: char) -> bool {
c == '>' || c == '<' || c == '~'
c == '>' || c == '<' || c == '~' || c == '"'
}
fn skip_whitespace(source: &str) -> &str {

View File

@ -39,9 +39,17 @@ impl ModeIndicator {
fn current_operators_description(&self, vim: &Vim) -> String {
vim.state()
.operator_stack
.iter()
.map(|item| item.id())
.pre_count
.map(|count| format!("{}", count))
.into_iter()
.chain(vim.state().selected_register.map(|reg| format!("\"{reg}")))
.chain(
vim.state()
.operator_stack
.iter()
.map(|item| item.id().to_string()),
)
.chain(vim.state().post_count.map(|count| format!("{}", count)))
.collect::<Vec<_>>()
.join("")
}

View File

@ -10,7 +10,11 @@ use serde::Deserialize;
use settings::Settings;
use workspace::Workspace;
use crate::{state::Mode, utils::copy_selections_content, UseSystemClipboard, Vim, VimSettings};
use crate::{
state::Mode,
utils::{copy_selections_content, SYSTEM_CLIPBOARD},
UseSystemClipboard, Vim, VimSettings,
};
#[derive(Clone, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
@ -29,7 +33,7 @@ pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>
fn system_clipboard_is_newer(vim: &Vim, cx: &mut AppContext) -> bool {
cx.read_from_clipboard().is_some_and(|item| {
if let Some(last_state) = vim.workspace_state.registers.get(".system.") {
if let Some(last_state) = vim.workspace_state.registers.get(&SYSTEM_CLIPBOARD) {
last_state != item.text()
} else {
true
@ -46,33 +50,36 @@ fn paste(_: &mut Workspace, action: &Paste, cx: &mut ViewContext<Workspace>) {
editor.transact(cx, |editor, cx| {
editor.set_clip_at_line_ends(false, cx);
let (clipboard_text, clipboard_selections): (String, Option<_>) =
if VimSettings::get_global(cx).use_system_clipboard == UseSystemClipboard::Never
|| VimSettings::get_global(cx).use_system_clipboard
== UseSystemClipboard::OnYank
&& !system_clipboard_is_newer(vim, cx)
{
(
vim.workspace_state
.registers
.get("\"")
.cloned()
.unwrap_or_else(|| "".to_string()),
None,
)
let (clipboard_text, clipboard_selections): (String, Option<_>) = if let Some(
register,
) =
vim.update_state(|state| state.selected_register.take())
{
(
vim.read_register(register, Some(editor), cx)
.unwrap_or_default(),
None,
)
} else if VimSettings::get_global(cx).use_system_clipboard
== UseSystemClipboard::Never
|| VimSettings::get_global(cx).use_system_clipboard
== UseSystemClipboard::OnYank
&& !system_clipboard_is_newer(vim, cx)
{
(vim.read_register('"', None, cx).unwrap_or_default(), None)
} else {
if let Some(item) = cx.read_from_clipboard() {
let clipboard_selections = item
.metadata::<Vec<ClipboardSelection>>()
.filter(|clipboard_selections| {
clipboard_selections.len() > 1
&& vim.state().mode != Mode::VisualLine
});
(item.text().clone(), clipboard_selections)
} else {
if let Some(item) = cx.read_from_clipboard() {
let clipboard_selections = item
.metadata::<Vec<ClipboardSelection>>()
.filter(|clipboard_selections| {
clipboard_selections.len() > 1
&& vim.state().mode != Mode::VisualLine
});
(item.text().clone(), clipboard_selections)
} else {
("".into(), None)
}
};
("".into(), None)
}
};
if clipboard_text.is_empty() {
return;
@ -606,4 +613,119 @@ mod test {
three
"});
}
#[gpui::test]
async fn test_numbered_registers(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_system_clipboard = Some(UseSystemClipboard::Never)
});
});
cx.set_shared_state(indoc! {"
The quick brown
fox jˇumps over
the lazy dog"})
.await;
cx.simulate_shared_keystrokes("y y \" 0 p").await;
cx.shared_register('0').await.assert_eq("fox jumps over\n");
cx.shared_register('"').await.assert_eq("fox jumps over\n");
cx.shared_state().await.assert_eq(indoc! {"
The quick brown
fox jumps over
ˇfox jumps over
the lazy dog"});
cx.simulate_shared_keystrokes("k k d d").await;
cx.shared_register('0').await.assert_eq("fox jumps over\n");
cx.shared_register('1').await.assert_eq("The quick brown\n");
cx.shared_register('"').await.assert_eq("The quick brown\n");
cx.simulate_shared_keystrokes("d d shift-g d d").await;
cx.shared_register('0').await.assert_eq("fox jumps over\n");
cx.shared_register('3').await.assert_eq("The quick brown\n");
cx.shared_register('2').await.assert_eq("fox jumps over\n");
cx.shared_register('1').await.assert_eq("the lazy dog\n");
cx.shared_state().await.assert_eq(indoc! {"
ˇfox jumps over"});
cx.simulate_shared_keystrokes("d d \" 3 p p \" 1 p").await;
cx.set_shared_state(indoc! {"
The quick brown
fox jumps over
ˇthe lazy dog"})
.await;
}
#[gpui::test]
async fn test_named_registers(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_system_clipboard = Some(UseSystemClipboard::Never)
});
});
cx.set_shared_state(indoc! {"
The quick brown
fox jˇumps over
the lazy dog"})
.await;
cx.simulate_shared_keystrokes("\" a d a w").await;
cx.shared_register('a').await.assert_eq("jumps ");
cx.simulate_shared_keystrokes("\" shift-a d i w").await;
cx.shared_register('a').await.assert_eq("jumps over");
cx.simulate_shared_keystrokes("\" a p").await;
cx.shared_state().await.assert_eq(indoc! {"
The quick brown
fox jumps oveˇr
the lazy dog"});
cx.simulate_shared_keystrokes("\" a d a w").await;
cx.shared_register('a').await.assert_eq(" over");
}
#[gpui::test]
async fn test_special_registers(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimSettings>(cx, |s| {
s.use_system_clipboard = Some(UseSystemClipboard::Never)
});
});
cx.set_shared_state(indoc! {"
The quick brown
fox jˇumps over
the lazy dog"})
.await;
cx.simulate_shared_keystrokes("d i w").await;
cx.shared_register('-').await.assert_eq("jumps");
cx.simulate_shared_keystrokes("\" _ d d").await;
cx.shared_register('_').await.assert_eq("");
cx.shared_state().await.assert_eq(indoc! {"
The quick brown
the ˇlazy dog"});
cx.simulate_shared_keystrokes("\" \" d ^").await;
cx.shared_register('0').await.assert_eq("the ");
cx.shared_register('"').await.assert_eq("the ");
cx.simulate_shared_keystrokes("^ \" + d $").await;
cx.shared_clipboard().await.assert_eq("lazy dog");
cx.shared_register('"').await.assert_eq("lazy dog");
// not testing nvim as it doesn't have a filename
cx.simulate_keystrokes("\" % p");
cx.assert_state(
indoc! {"
The quick brown
dir/file.rˇs"},
Mode::Normal,
);
}
}

View File

@ -63,10 +63,10 @@ pub enum Operator {
Jump { line: bool },
Indent,
Outdent,
Lowercase,
Uppercase,
OppositeCase,
Register,
}
#[derive(Default, Clone)]
@ -89,6 +89,8 @@ pub struct EditorState {
pub current_tx: Option<TransactionId>,
pub current_anchor: Option<Selection<Anchor>>,
pub undo_modes: HashMap<TransactionId, Mode>,
pub selected_register: Option<char>,
}
#[derive(Default, Clone, Debug)]
@ -123,7 +125,7 @@ pub struct WorkspaceState {
pub recorded_actions: Vec<ReplayableAction>,
pub recorded_selection: RecordedSelection,
pub registers: HashMap<String, String>,
pub registers: HashMap<char, String>,
}
#[derive(Debug)]
@ -277,6 +279,7 @@ impl Operator {
Operator::Uppercase => "gU",
Operator::Lowercase => "gu",
Operator::OppositeCase => "g~",
Operator::Register => "\"",
}
}
@ -287,6 +290,7 @@ impl Operator {
| Operator::Mark
| Operator::Jump { .. }
| Operator::FindBackward { .. }
| Operator::Register
| Operator::Replace
| Operator::AddSurrounds { target: Some(_) }
| Operator::ChangeSurrounds { .. }

View File

@ -10,7 +10,7 @@ use language::language_settings::{AllLanguageSettings, SoftWrap};
use util::test::marked_text_offsets;
use super::{neovim_connection::NeovimConnection, VimTestContext};
use crate::state::Mode;
use crate::{state::Mode, Vim};
pub struct NeovimBackedTestContext {
cx: VimTestContext,
@ -94,6 +94,7 @@ impl SharedState {
}
pub struct SharedClipboard {
register: char,
neovim: String,
editor: String,
state: SharedState,
@ -120,15 +121,17 @@ impl SharedClipboard {
{}
# currently expected:
{}
# neovim clipboard:
# neovim register \"{}:
{}
# zed clipboard:
# zed register \"{}:
{}"},
message,
self.state.initial,
self.state.recent_keystrokes,
expected,
self.register,
self.neovim,
self.register,
self.editor
)
}
@ -241,12 +244,30 @@ impl NeovimBackedTestContext {
#[must_use]
pub async fn shared_clipboard(&mut self) -> SharedClipboard {
SharedClipboard {
register: '"',
state: self.shared_state().await,
neovim: self.neovim.read_register('"').await,
editor: self.read_from_clipboard().unwrap().text().clone(),
}
}
#[must_use]
pub async fn shared_register(&mut self, register: char) -> SharedClipboard {
SharedClipboard {
register: register,
state: self.shared_state().await,
neovim: self.neovim.read_register(register).await,
editor: self.update(|cx| {
Vim::read(cx)
.workspace_state
.registers
.get(&register)
.cloned()
.unwrap_or_default()
}),
}
}
#[must_use]
pub async fn shared_state(&mut self) -> SharedState {
let (mode, marked_text) = self.neovim.state().await;

View File

@ -1,12 +1,13 @@
use std::time::Duration;
use editor::{ClipboardSelection, Editor};
use gpui::{ClipboardItem, ViewContext};
use gpui::ViewContext;
use language::{CharKind, Point};
use multi_buffer::MultiBufferRow;
use settings::Settings;
use crate::{state::Mode, UseSystemClipboard, Vim, VimSettings};
use crate::{state::Mode, Vim};
pub const SYSTEM_CLIPBOARD: char = '\0';
pub struct HighlightOnYank;
@ -102,21 +103,8 @@ fn copy_selections_content_internal(
}
}
let setting = VimSettings::get_global(cx).use_system_clipboard;
if setting == UseSystemClipboard::Always || setting == UseSystemClipboard::OnYank && is_yank {
cx.write_to_clipboard(ClipboardItem::new(text.clone()).with_metadata(clipboard_selections));
vim.workspace_state
.registers
.insert(".system.".to_string(), text.clone());
} else {
vim.workspace_state.registers.insert(
".system.".to_string(),
cx.read_from_clipboard()
.map(|item| item.text().clone())
.unwrap_or_default(),
);
}
vim.workspace_state.registers.insert("\"".to_string(), text);
vim.write_registers(is_yank, linewise, text, clipboard_selections, cx);
if !is_yank || vim.state().mode == Mode::Visual {
return;
}

View File

@ -23,11 +23,11 @@ use collections::HashMap;
use command_palette_hooks::{CommandPaletteFilter, CommandPaletteInterceptor};
use editor::{
movement::{self, FindRange},
Anchor, Bias, Editor, EditorEvent, EditorMode, ToPoint,
Anchor, Bias, ClipboardSelection, Editor, EditorEvent, EditorMode, ToPoint,
};
use gpui::{
actions, impl_actions, Action, AppContext, EntityId, FocusableView, Global, KeystrokeEvent,
Subscription, UpdateGlobal, View, ViewContext, WeakView, WindowContext,
actions, impl_actions, Action, AppContext, ClipboardItem, EntityId, FocusableView, Global,
KeystrokeEvent, Subscription, UpdateGlobal, View, ViewContext, WeakView, WindowContext,
};
use language::{CursorShape, Point, SelectionGoal, TransactionId};
pub use mode_indicator::ModeIndicator;
@ -45,6 +45,7 @@ use state::{EditorState, Mode, Operator, RecordedSelection, WorkspaceState};
use std::{ops::Range, sync::Arc};
use surrounds::{add_surrounds, change_surrounds, delete_surrounds};
use ui::BorrowAppContext;
use utils::SYSTEM_CLIPBOARD;
use visual::{visual_block_motion, visual_replace};
use workspace::{self, Workspace};
@ -70,6 +71,9 @@ pub struct PushOperator(pub Operator);
#[derive(Clone, Deserialize, PartialEq)]
struct Number(usize);
#[derive(Clone, Deserialize, PartialEq)]
struct SelectRegister(String);
actions!(
vim,
[
@ -86,7 +90,7 @@ actions!(
// in the workspace namespace so it's not filtered out when vim is disabled.
actions!(workspace, [ToggleVimMode]);
impl_actions!(vim, [SwitchMode, PushOperator, Number]);
impl_actions!(vim, [SwitchMode, PushOperator, Number, SelectRegister]);
/// Initializes the `vim` crate.
pub fn init(cx: &mut AppContext) {
@ -129,7 +133,6 @@ fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
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)
});
@ -202,7 +205,8 @@ fn observe_keystrokes(keystroke_event: &KeystrokeEvent, cx: &mut WindowContext)
| Operator::ChangeSurrounds { .. }
| Operator::DeleteSurrounds
| Operator::Mark
| Operator::Jump { .. },
| Operator::Jump { .. }
| Operator::Register,
) => {}
Some(_) => {
vim.clear_operator(cx);
@ -531,6 +535,138 @@ impl Vim {
count
}
fn select_register(&mut self, register: Arc<str>, cx: &mut WindowContext) {
self.update_state(|state| {
if register.chars().count() == 1 {
state
.selected_register
.replace(register.chars().next().unwrap());
}
state.operator_stack.clear();
});
self.sync_vim_settings(cx);
}
fn write_registers(
&mut self,
is_yank: bool,
linewise: bool,
text: String,
clipboard_selections: Vec<ClipboardSelection>,
cx: &mut ViewContext<Editor>,
) {
self.workspace_state.registers.insert('"', text.clone());
if let Some(register) = self.update_state(|vim| vim.selected_register.take()) {
let lower = register.to_lowercase().next().unwrap_or(register);
if lower != register {
let current = self.workspace_state.registers.entry(lower).or_default();
*current += &text;
} else {
match lower {
'_' | ':' | '.' | '%' | '#' | '=' | '/' => {}
'+' => {
cx.write_to_clipboard(
ClipboardItem::new(text.clone()).with_metadata(clipboard_selections),
);
}
'*' => {
#[cfg(target_os = "linux")]
cx.write_to_primary(
ClipboardItem::new(text.clone()).with_metadata(clipboard_selections),
);
#[cfg(not(target_os = "linux"))]
cx.write_to_clipboard(
ClipboardItem::new(text.clone()).with_metadata(clipboard_selections),
);
}
'"' => {
self.workspace_state.registers.insert('0', text.clone());
self.workspace_state.registers.insert('"', text);
}
_ => {
self.workspace_state.registers.insert(lower, text);
}
}
}
} else {
let setting = VimSettings::get_global(cx).use_system_clipboard;
if setting == UseSystemClipboard::Always
|| setting == UseSystemClipboard::OnYank && is_yank
{
cx.write_to_clipboard(
ClipboardItem::new(text.clone()).with_metadata(clipboard_selections.clone()),
);
self.workspace_state
.registers
.insert(SYSTEM_CLIPBOARD, text.clone());
} else {
self.workspace_state.registers.insert(
SYSTEM_CLIPBOARD,
cx.read_from_clipboard()
.map(|item| item.text().clone())
.unwrap_or_default(),
);
}
if is_yank {
self.workspace_state.registers.insert('0', text);
} else {
if !text.contains('\n') {
self.workspace_state.registers.insert('-', text.clone());
}
if linewise || text.contains('\n') {
let mut content = text;
for i in '1'..'8' {
if let Some(moved) = self.workspace_state.registers.insert(i, content) {
content = moved;
} else {
break;
}
}
}
}
}
}
fn read_register(
&mut self,
register: char,
editor: Option<&mut Editor>,
cx: &mut WindowContext,
) -> Option<String> {
let lower = register.to_lowercase().next().unwrap_or(register);
match lower {
'_' | ':' | '.' | '#' | '=' | '/' => None,
'+' => cx.read_from_clipboard().map(|item| item.text().clone()),
'*' => {
#[cfg(target_os = "linux")]
{
cx.read_from_primary().map(|item| item.text().clone())
}
#[cfg(not(target_os = "linux"))]
{
cx.read_from_clipboard().map(|item| item.text().clone())
}
}
'%' => editor.and_then(|editor| {
let selection = editor.selections.newest::<Point>(cx);
if let Some((_, buffer, _)) = editor
.buffer()
.read(cx)
.excerpt_containing(selection.head(), cx)
{
buffer
.read(cx)
.file()
.map(|file| file.path().to_string_lossy().to_string())
} else {
None
}
}),
_ => self.workspace_state.registers.get(&lower).cloned(),
}
}
fn push_operator(&mut self, operator: Operator, cx: &mut WindowContext) {
if matches!(
operator,
@ -573,7 +709,10 @@ impl Vim {
fn clear_operator(&mut self, cx: &mut WindowContext) {
self.take_count(cx);
self.update_state(|state| state.operator_stack.clear());
self.update_state(|state| {
state.selected_register.take();
state.operator_stack.clear()
});
self.sync_vim_settings(cx);
}
@ -741,6 +880,9 @@ impl Vim {
Some(Operator::Mark) => Vim::update(cx, |vim, cx| {
normal::mark::create_mark(vim, text, false, cx)
}),
Some(Operator::Register) => Vim::update(cx, |vim, cx| {
vim.select_register(text, cx);
}),
Some(Operator::Jump { line }) => normal::mark::jump(text, line, cx),
_ => match Vim::read(cx).state().mode {
Mode::Replace => multi_replace(text, cx),

View File

@ -0,0 +1,26 @@
{"Put":{"state":"The quick brown\nfox jˇumps over\nthe lazy dog"}}
{"Key":"\""}
{"Key":"a"}
{"Key":"d"}
{"Key":"a"}
{"Key":"w"}
{"Get":{"state":"The quick brown\nfox ˇover\nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"a","value":"jumps "}}
{"Key":"\""}
{"Key":"shift-a"}
{"Key":"d"}
{"Key":"i"}
{"Key":"w"}
{"Get":{"state":"The quick brown\nfoxˇ \nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"a","value":"jumps over"}}
{"Key":"\""}
{"Key":"a"}
{"Key":"p"}
{"Get":{"state":"The quick brown\nfox jumps oveˇr\nthe lazy dog","mode":"Normal"}}
{"Key":"\""}
{"Key":"a"}
{"Key":"d"}
{"Key":"a"}
{"Key":"w"}
{"Get":{"state":"The quick brown\nfox jumpˇs\nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"a","value":" over"}}

View File

@ -0,0 +1,45 @@
{"Put":{"state":"The quick brown\nfox jˇumps over\nthe lazy dog"}}
{"Key":"y"}
{"Key":"y"}
{"Key":"\""}
{"Key":"0"}
{"Key":"p"}
{"Get":{"state":"The quick brown\nfox jumps over\nˇfox jumps over\nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"0","value":"fox jumps over\n"}}
{"Get":{"state":"The quick brown\nfox jumps over\nˇfox jumps over\nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"fox jumps over\n"}}
{"Get":{"state":"The quick brown\nfox jumps over\nˇfox jumps over\nthe lazy dog","mode":"Normal"}}
{"Key":"k"}
{"Key":"k"}
{"Key":"d"}
{"Key":"d"}
{"Get":{"state":"ˇfox jumps over\nfox jumps over\nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"0","value":"fox jumps over\n"}}
{"Get":{"state":"ˇfox jumps over\nfox jumps over\nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"1","value":"The quick brown\n"}}
{"Get":{"state":"ˇfox jumps over\nfox jumps over\nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"The quick brown\n"}}
{"Key":"d"}
{"Key":"d"}
{"Key":"shift-g"}
{"Key":"d"}
{"Key":"d"}
{"Get":{"state":"ˇfox jumps over","mode":"Normal"}}
{"ReadRegister":{"name":"0","value":"fox jumps over\n"}}
{"Get":{"state":"ˇfox jumps over","mode":"Normal"}}
{"ReadRegister":{"name":"3","value":"The quick brown\n"}}
{"Get":{"state":"ˇfox jumps over","mode":"Normal"}}
{"ReadRegister":{"name":"2","value":"fox jumps over\n"}}
{"Get":{"state":"ˇfox jumps over","mode":"Normal"}}
{"ReadRegister":{"name":"1","value":"the lazy dog\n"}}
{"Get":{"state":"ˇfox jumps over","mode":"Normal"}}
{"Key":"d"}
{"Key":"d"}
{"Key":"\""}
{"Key":"3"}
{"Key":"p"}
{"Key":"p"}
{"Key":"\""}
{"Key":"1"}
{"Key":"p"}
{"Put":{"state":"The quick brown\nfox jumps over\nˇthe lazy dog"}}

View File

@ -0,0 +1,30 @@
{"Put":{"state":"The quick brown\nfox jˇumps over\nthe lazy dog"}}
{"Key":"d"}
{"Key":"i"}
{"Key":"w"}
{"Get":{"state":"The quick brown\nfox ˇ over\nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"-","value":"jumps"}}
{"Key":"\""}
{"Key":"_"}
{"Key":"d"}
{"Key":"d"}
{"Get":{"state":"The quick brown\nthe ˇlazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"_","value":""}}
{"Get":{"state":"The quick brown\nthe ˇlazy dog","mode":"Normal"}}
{"Key":"\""}
{"Key":"\""}
{"Key":"d"}
{"Key":"^"}
{"Get":{"state":"The quick brown\nˇlazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"0","value":"the "}}
{"Get":{"state":"The quick brown\nˇlazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"the "}}
{"Key":"^"}
{"Key":"\""}
{"Key":"+"}
{"Key":"d"}
{"Key":"$"}
{"Get":{"state":"The quick brown\nˇ","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"lazy dog"}}
{"Get":{"state":"The quick brown\nˇ","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"lazy dog"}}

View File

@ -215,9 +215,9 @@ Some vim settings are available to modify the default vim behavior:
```json
{
"vim": {
// "always": use system clipboard
// "never": don't use system clipboard
// "on_yank": use system clipboard for yank operations
// "always": use system clipboard when no register is specified
// "never": don't use system clipboard unless "+ or "* is specified
// "on_yank": use system clipboard for yank operations when no register is specified
"use_system_clipboard": "always",
// Lets `f` and `t` motions extend across multiple lines
"use_multiline_find": true