mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 02:17:35 +03:00
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:
parent
3c3dad6830
commit
001f17c011
@ -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"
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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("")
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 { .. }
|
||||
|
@ -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(®ister)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub async fn shared_state(&mut self) -> SharedState {
|
||||
let (mode, marked_text) = self.neovim.state().await;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
|
26
crates/vim/test_data/test_named_registers.json
Normal file
26
crates/vim/test_data/test_named_registers.json
Normal 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"}}
|
45
crates/vim/test_data/test_numbered_registers.json
Normal file
45
crates/vim/test_data/test_numbered_registers.json
Normal 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"}}
|
30
crates/vim/test_data/test_special_registers.json
Normal file
30
crates/vim/test_data/test_special_registers.json
Normal 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"}}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user