Detailed changes
@@ -1522,7 +1522,7 @@ struct ActiveDiagnosticGroup {
is_valid: bool,
}
-#[derive(Serialize, Deserialize, Clone)]
+#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ClipboardSelection {
pub len: usize,
pub is_entire_line: bool,
@@ -17,7 +17,6 @@ use crate::{
normal::{mark, normal_motion},
state::{Mode, Operator},
surrounds::SurroundsType,
- utils::coerce_punctuation,
visual::visual_motion,
Vim,
};
@@ -1764,6 +1763,14 @@ fn window_bottom(
}
}
+pub fn coerce_punctuation(kind: CharKind, treat_punctuation_as_word: bool) -> CharKind {
+ if treat_punctuation_as_word && kind == CharKind::Punctuation {
+ CharKind::Word
+ } else {
+ kind
+ }
+}
+
#[cfg(test)]
mod test {
@@ -9,7 +9,7 @@ pub(crate) mod repeat;
mod scroll;
pub(crate) mod search;
pub mod substitute;
-mod yank;
+pub(crate) mod yank;
use std::collections::HashMap;
use std::sync::Arc;
@@ -1,8 +1,8 @@
use crate::{
motion::{self, Motion},
+ normal::yank::copy_selections_content,
object::Object,
state::Mode,
- utils::copy_selections_content,
Vim,
};
use editor::{
@@ -1,4 +1,4 @@
-use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim};
+use crate::{motion::Motion, normal::yank::copy_selections_content, object::Object, Vim};
use collections::{HashMap, HashSet};
use editor::{
display_map::{DisplaySnapshot, ToDisplayPoint},
@@ -1,19 +1,15 @@
use std::cmp;
-use editor::{
- display_map::ToDisplayPoint, movement, scroll::Autoscroll, ClipboardSelection, DisplayPoint,
- RowExt,
-};
-use gpui::{impl_actions, AppContext, ViewContext};
+use editor::{display_map::ToDisplayPoint, movement, scroll::Autoscroll, DisplayPoint, RowExt};
+use gpui::{impl_actions, ViewContext};
use language::{Bias, SelectionGoal};
use serde::Deserialize;
-use settings::Settings;
use workspace::Workspace;
use crate::{
- state::Mode,
- utils::{copy_selections_content, SYSTEM_CLIPBOARD},
- UseSystemClipboard, Vim, VimSettings,
+ normal::yank::copy_selections_content,
+ state::{Mode, Register},
+ Vim,
};
#[derive(Clone, Deserialize, PartialEq)]
@@ -31,16 +27,6 @@ pub(crate) fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>
workspace.register_action(paste);
}
-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_CLIPBOARD) {
- last_state != item.text()
- } else {
- true
- }
- })
-}
-
fn paste(_: &mut Workspace, action: &Paste, cx: &mut ViewContext<Workspace>) {
Vim::update(cx, |vim, cx| {
vim.record_current_action(cx);
@@ -50,40 +36,19 @@ 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 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 {
- ("".into(), None)
- }
- };
+ let selected_register = vim.update_state(|state| state.selected_register.take());
- if clipboard_text.is_empty() {
+ let Some(Register {
+ text,
+ clipboard_selections,
+ }) = vim
+ .read_register(selected_register, Some(editor), cx)
+ .filter(|reg| !reg.text.is_empty())
+ else {
return;
- }
+ };
+ let clipboard_selections = clipboard_selections
+ .filter(|sel| sel.len() > 1 && vim.state().mode != Mode::VisualLine);
if !action.preserve_clipboard && vim.state().mode.is_visual() {
copy_selections_content(vim, editor, vim.state().mode == Mode::VisualLine, cx);
@@ -135,14 +100,14 @@ fn paste(_: &mut Workspace, action: &Paste, cx: &mut ViewContext<Workspace>) {
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();
+ let text = 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)
+ (text.to_string(), first_selection_indent_column)
};
let line_mode = to_insert.ends_with('\n');
let is_multiline = to_insert.contains('\n');
@@ -679,6 +644,7 @@ mod test {
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.shared_register('"').await.assert_eq("jumps over");
cx.simulate_shared_keystrokes("\" a p").await;
cx.shared_state().await.assert_eq(indoc! {"
The quick brown
@@ -719,12 +685,50 @@ mod test {
cx.shared_clipboard().await.assert_eq("lazy dog");
cx.shared_register('"').await.assert_eq("lazy dog");
+ cx.simulate_shared_keystrokes("/ d o g enter").await;
+ cx.shared_register('/').await.assert_eq("dog");
+ cx.simulate_shared_keystrokes("\" / shift-p").await;
+ cx.shared_state().await.assert_eq(indoc! {"
+ The quick brown
+ doˇg"});
+
// 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"},
+ dogdir/file.rˇs"},
+ Mode::Normal,
+ );
+ }
+
+ #[gpui::test]
+ async fn test_multicursor_paste(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.update_global(|store: &mut SettingsStore, cx| {
+ store.update_user_settings::<VimSettings>(cx, |s| {
+ s.use_system_clipboard = Some(UseSystemClipboard::Never)
+ });
+ });
+
+ cx.set_state(
+ indoc! {"
+ ˇfish one
+ fish two
+ fish red
+ fish blue
+ "},
+ Mode::Normal,
+ );
+ cx.simulate_keystrokes("4 g l w escape d i w 0 shift-p");
+ cx.assert_state(
+ indoc! {"
+ onˇefish•
+ twˇofish•
+ reˇdfish•
+ bluˇefish•
+ "},
Mode::Normal,
);
}
@@ -165,6 +165,9 @@ fn search_submit(workspace: &mut Workspace, _: &SearchSubmit, cx: &mut ViewConte
{
count = count.saturating_sub(1)
}
+ vim.workspace_state
+ .registers
+ .insert('/', search_bar.query(cx).into());
state.count = 1;
search_bar.select_match(direction, count, cx);
search_bar.focus_editor(&Default::default(), cx);
@@ -3,7 +3,7 @@ use gpui::{actions, ViewContext, WindowContext};
use language::Point;
use workspace::Workspace;
-use crate::{motion::Motion, utils::copy_selections_content, Mode, Vim};
+use crate::{motion::Motion, normal::yank::copy_selections_content, Mode, Vim};
actions!(vim, [Substitute, SubstituteLine]);
@@ -1,6 +1,17 @@
-use crate::{motion::Motion, object::Object, utils::yank_selections_content, Vim};
+use std::time::Duration;
+
+use crate::{
+ motion::Motion,
+ object::Object,
+ state::{Mode, Register},
+ Vim,
+};
use collections::HashMap;
+use editor::{ClipboardSelection, Editor};
use gpui::WindowContext;
+use language::Point;
+use multi_buffer::MultiBufferRow;
+use ui::ViewContext;
pub fn yank_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
vim.update_active_editor(cx, |vim, editor, cx| {
@@ -48,3 +59,130 @@ pub fn yank_object(vim: &mut Vim, object: Object, around: bool, cx: &mut WindowC
});
});
}
+
+pub fn yank_selections_content(
+ vim: &mut Vim,
+ editor: &mut Editor,
+ linewise: bool,
+ cx: &mut ViewContext<Editor>,
+) {
+ copy_selections_content_internal(vim, editor, linewise, true, cx);
+}
+
+pub fn copy_selections_content(
+ vim: &mut Vim,
+ editor: &mut Editor,
+ linewise: bool,
+ cx: &mut ViewContext<Editor>,
+) {
+ copy_selections_content_internal(vim, editor, linewise, false, cx);
+}
+
+struct HighlightOnYank;
+
+fn copy_selections_content_internal(
+ vim: &mut Vim,
+ editor: &mut Editor,
+ linewise: bool,
+ is_yank: bool,
+ cx: &mut ViewContext<Editor>,
+) {
+ 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 ranges_to_highlight = Vec::new();
+
+ vim.update_state(|state| {
+ state.marks.insert(
+ "[".to_string(),
+ selections
+ .iter()
+ .map(|s| buffer.anchor_before(s.start))
+ .collect(),
+ );
+ state.marks.insert(
+ "]".to_string(),
+ selections
+ .iter()
+ .map(|s| buffer.anchor_after(s.end))
+ .collect(),
+ )
+ });
+
+ {
+ 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().0
+ && buffer.max_point().column > 0
+ && start.row < buffer.max_buffer_row().0
+ && start == Point::new(start.row, buffer.line_len(MultiBufferRow(start.row)));
+
+ if is_last_line {
+ start = Point::new(start.row + 1, 0);
+ }
+
+ let start_anchor = buffer.anchor_after(start);
+ let end_anchor = buffer.anchor_before(end);
+ ranges_to_highlight.push(start_anchor..end_anchor);
+
+ 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(MultiBufferRow(start.row)).len,
+ });
+ }
+ }
+
+ let selected_register = vim.update_state(|state| state.selected_register.take());
+ vim.write_registers(
+ Register {
+ text: text.into(),
+ clipboard_selections: Some(clipboard_selections),
+ },
+ selected_register,
+ is_yank,
+ linewise,
+ cx,
+ );
+
+ if !is_yank || vim.state().mode == Mode::Visual {
+ return;
+ }
+
+ editor.highlight_background::<HighlightOnYank>(
+ &ranges_to_highlight,
+ |colors| colors.editor_document_highlight_read_background,
+ cx,
+ );
+ cx.spawn(|this, mut cx| async move {
+ cx.background_executor()
+ .timer(Duration::from_millis(200))
+ .await;
+ this.update(&mut cx, |editor, cx| {
+ editor.clear_background_highlights::<HighlightOnYank>(cx)
+ })
+ .ok();
+ })
+ .detach();
+}
@@ -1,8 +1,11 @@
use std::ops::Range;
use crate::{
- motion::right, normal::normal_object, state::Mode, utils::coerce_punctuation,
- visual::visual_object, Vim,
+ motion::{coerce_punctuation, right},
+ normal::normal_object,
+ state::Mode,
+ visual::visual_object,
+ Vim,
};
use editor::{
display_map::{DisplaySnapshot, ToDisplayPoint},
@@ -3,10 +3,11 @@ use std::{fmt::Display, ops::Range, sync::Arc};
use crate::surrounds::SurroundsType;
use crate::{motion::Motion, object::Object};
use collections::HashMap;
-use editor::Anchor;
-use gpui::{Action, KeyContext};
+use editor::{Anchor, ClipboardSelection};
+use gpui::{Action, ClipboardItem, KeyContext};
use language::{CursorShape, Selection, TransactionId};
use serde::{Deserialize, Serialize};
+use ui::SharedString;
use workspace::searchable::Direction;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
@@ -113,6 +114,41 @@ pub enum RecordedSelection {
},
}
+#[derive(Default, Clone, Debug)]
+pub struct Register {
+ pub(crate) text: SharedString,
+ pub(crate) clipboard_selections: Option<Vec<ClipboardSelection>>,
+}
+
+impl From<Register> for ClipboardItem {
+ fn from(register: Register) -> Self {
+ let item = ClipboardItem::new(register.text.into());
+ if let Some(clipboard_selections) = register.clipboard_selections {
+ item.with_metadata(clipboard_selections)
+ } else {
+ item
+ }
+ }
+}
+
+impl From<ClipboardItem> for Register {
+ fn from(value: ClipboardItem) -> Self {
+ Register {
+ text: value.text().to_owned().into(),
+ clipboard_selections: value.metadata::<Vec<ClipboardSelection>>(),
+ }
+ }
+}
+
+impl From<String> for Register {
+ fn from(text: String) -> Self {
+ Register {
+ text: text.into(),
+ clipboard_selections: None,
+ }
+ }
+}
+
#[derive(Default, Clone)]
pub struct WorkspaceState {
pub search: SearchState,
@@ -125,7 +161,8 @@ pub struct WorkspaceState {
pub recorded_actions: Vec<ReplayableAction>,
pub recorded_selection: RecordedSelection,
- pub registers: HashMap<char, String>,
+ pub last_yank: Option<SharedString>,
+ pub registers: HashMap<char, Register>,
}
#[derive(Debug)]
@@ -254,7 +254,7 @@ impl NeovimBackedTestContext {
#[must_use]
pub async fn shared_register(&mut self, register: char) -> SharedClipboard {
SharedClipboard {
- register: register,
+ register,
state: self.shared_state().await,
neovim: self.neovim.read_register(register).await,
editor: self.update(|cx| {
@@ -264,6 +264,8 @@ impl NeovimBackedTestContext {
.get(®ister)
.cloned()
.unwrap_or_default()
+ .text
+ .into()
}),
}
}
@@ -1,135 +0,0 @@
-use std::time::Duration;
-
-use editor::{ClipboardSelection, Editor};
-use gpui::ViewContext;
-use language::{CharKind, Point};
-use multi_buffer::MultiBufferRow;
-
-use crate::{state::Mode, Vim};
-
-pub const SYSTEM_CLIPBOARD: char = '\0';
-
-pub struct HighlightOnYank;
-
-pub fn yank_selections_content(
- vim: &mut Vim,
- editor: &mut Editor,
- linewise: bool,
- cx: &mut ViewContext<Editor>,
-) {
- copy_selections_content_internal(vim, editor, linewise, true, cx);
-}
-
-pub fn copy_selections_content(
- vim: &mut Vim,
- editor: &mut Editor,
- linewise: bool,
- cx: &mut ViewContext<Editor>,
-) {
- copy_selections_content_internal(vim, editor, linewise, false, cx);
-}
-
-fn copy_selections_content_internal(
- vim: &mut Vim,
- editor: &mut Editor,
- linewise: bool,
- is_yank: bool,
- cx: &mut ViewContext<Editor>,
-) {
- 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 ranges_to_highlight = Vec::new();
-
- vim.update_state(|state| {
- state.marks.insert(
- "[".to_string(),
- selections
- .iter()
- .map(|s| buffer.anchor_before(s.start))
- .collect(),
- );
- state.marks.insert(
- "]".to_string(),
- selections
- .iter()
- .map(|s| buffer.anchor_after(s.end))
- .collect(),
- )
- });
-
- {
- 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().0
- && buffer.max_point().column > 0
- && start.row < buffer.max_buffer_row().0
- && start == Point::new(start.row, buffer.line_len(MultiBufferRow(start.row)));
-
- if is_last_line {
- start = Point::new(start.row + 1, 0);
- }
-
- let start_anchor = buffer.anchor_after(start);
- let end_anchor = buffer.anchor_before(end);
- ranges_to_highlight.push(start_anchor..end_anchor);
-
- 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(MultiBufferRow(start.row)).len,
- });
- }
- }
-
- vim.write_registers(is_yank, linewise, text, clipboard_selections, cx);
-
- if !is_yank || vim.state().mode == Mode::Visual {
- return;
- }
-
- editor.highlight_background::<HighlightOnYank>(
- &ranges_to_highlight,
- |colors| colors.editor_document_highlight_read_background,
- cx,
- );
- cx.spawn(|this, mut cx| async move {
- cx.background_executor()
- .timer(Duration::from_millis(200))
- .await;
- this.update(&mut cx, |editor, cx| {
- editor.clear_background_highlights::<HighlightOnYank>(cx)
- })
- .ok();
- })
- .detach();
-}
-
-pub fn coerce_punctuation(kind: CharKind, treat_punctuation_as_word: bool) -> CharKind {
- if treat_punctuation_as_word && kind == CharKind::Punctuation {
- CharKind::Word
- } else {
- kind
- }
-}
@@ -14,7 +14,6 @@ mod object;
mod replace;
mod state;
mod surrounds;
-mod utils;
mod visual;
use anyhow::Result;
@@ -23,11 +22,11 @@ use collections::HashMap;
use command_palette_hooks::{CommandPaletteFilter, CommandPaletteInterceptor};
use editor::{
movement::{self, FindRange},
- Anchor, Bias, ClipboardSelection, Editor, EditorEvent, EditorMode, ToPoint,
+ Anchor, Bias, Editor, EditorEvent, EditorMode, ToPoint,
};
use gpui::{
- actions, impl_actions, Action, AppContext, ClipboardItem, EntityId, FocusableView, Global,
- KeystrokeEvent, Subscription, UpdateGlobal, View, ViewContext, WeakView, WindowContext,
+ actions, impl_actions, Action, AppContext, EntityId, FocusableView, Global, KeystrokeEvent,
+ Subscription, UpdateGlobal, View, ViewContext, WeakView, WindowContext,
};
use language::{CursorShape, Point, SelectionGoal, TransactionId};
pub use mode_indicator::ModeIndicator;
@@ -41,11 +40,10 @@ use schemars::JsonSchema;
use serde::Deserialize;
use serde_derive::Serialize;
use settings::{update_settings_file, Settings, SettingsSources, SettingsStore};
-use state::{EditorState, Mode, Operator, RecordedSelection, WorkspaceState};
+use state::{EditorState, Mode, Operator, RecordedSelection, Register, 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};
@@ -551,42 +549,40 @@ impl Vim {
fn write_registers(
&mut self,
+ content: Register,
+ register: Option<char>,
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()) {
+ if let Some(register) = register {
let lower = register.to_lowercase().next().unwrap_or(register);
if lower != register {
let current = self.workspace_state.registers.entry(lower).or_default();
- *current += &text;
+ current.text = (current.text.to_string() + &content.text).into();
+ // not clear how to support appending to registers with multiple cursors
+ current.clipboard_selections.take();
+ let yanked = current.clone();
+ self.workspace_state.registers.insert('"', yanked);
} else {
+ self.workspace_state.registers.insert('"', content.clone());
match lower {
'_' | ':' | '.' | '%' | '#' | '=' | '/' => {}
'+' => {
- cx.write_to_clipboard(
- ClipboardItem::new(text.clone()).with_metadata(clipboard_selections),
- );
+ cx.write_to_clipboard(content.into());
}
'*' => {
#[cfg(target_os = "linux")]
- cx.write_to_primary(
- ClipboardItem::new(text.clone()).with_metadata(clipboard_selections),
- );
+ cx.write_to_primary(content.into());
#[cfg(not(target_os = "linux"))]
- cx.write_to_clipboard(
- ClipboardItem::new(text.clone()).with_metadata(clipboard_selections),
- );
+ cx.write_to_clipboard(content.into());
}
'"' => {
- self.workspace_state.registers.insert('0', text.clone());
- self.workspace_state.registers.insert('"', text);
+ self.workspace_state.registers.insert('0', content.clone());
+ self.workspace_state.registers.insert('"', content);
}
_ => {
- self.workspace_state.registers.insert(lower, text);
+ self.workspace_state.registers.insert(lower, content);
}
}
}
@@ -595,29 +591,24 @@ impl Vim {
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());
+ self.workspace_state.last_yank.replace(content.text.clone());
+ cx.write_to_clipboard(content.clone().into());
} else {
- self.workspace_state.registers.insert(
- SYSTEM_CLIPBOARD,
- cx.read_from_clipboard()
- .map(|item| item.text().clone())
- .unwrap_or_default(),
- );
+ self.workspace_state.last_yank = cx
+ .read_from_clipboard()
+ .map(|item| item.text().to_owned().into());
}
+ self.workspace_state.registers.insert('"', content.clone());
if is_yank {
- self.workspace_state.registers.insert('0', text);
+ self.workspace_state.registers.insert('0', content);
} else {
- if !text.contains('\n') {
- self.workspace_state.registers.insert('-', text.clone());
+ let contains_newline = content.text.contains('\n');
+ if !contains_newline {
+ self.workspace_state.registers.insert('-', content.clone());
}
- if linewise || text.contains('\n') {
- let mut content = text;
+ if linewise || contains_newline {
+ let mut content = content;
for i in '1'..'8' {
if let Some(moved) = self.workspace_state.registers.insert(i, content) {
content = moved;
@@ -632,22 +623,32 @@ impl Vim {
fn read_register(
&mut self,
- register: char,
+ register: Option<char>,
editor: Option<&mut Editor>,
cx: &mut WindowContext,
- ) -> Option<String> {
+ ) -> Option<Register> {
+ let Some(register) = register else {
+ let setting = VimSettings::get_global(cx).use_system_clipboard;
+ return match setting {
+ UseSystemClipboard::Always => cx.read_from_clipboard().map(|item| item.into()),
+ UseSystemClipboard::OnYank if self.system_clipboard_is_newer(cx) => {
+ cx.read_from_clipboard().map(|item| item.into())
+ }
+ _ => self.workspace_state.registers.get(&'"').cloned(),
+ };
+ };
let lower = register.to_lowercase().next().unwrap_or(register);
match lower {
- '_' | ':' | '.' | '#' | '=' | '/' => None,
- '+' => cx.read_from_clipboard().map(|item| item.text().clone()),
+ '_' | ':' | '.' | '#' | '=' => None,
+ '+' => cx.read_from_clipboard().map(|item| item.into()),
'*' => {
#[cfg(target_os = "linux")]
{
- cx.read_from_primary().map(|item| item.text().clone())
+ cx.read_from_primary().map(|item| item.into())
}
#[cfg(not(target_os = "linux"))]
{
- cx.read_from_clipboard().map(|item| item.text().clone())
+ cx.read_from_clipboard().map(|item| item.into())
}
}
'%' => editor.and_then(|editor| {
@@ -660,7 +661,7 @@ impl Vim {
buffer
.read(cx)
.file()
- .map(|file| file.path().to_string_lossy().to_string())
+ .map(|file| file.path().to_string_lossy().to_string().into())
} else {
None
}
@@ -669,6 +670,16 @@ impl Vim {
}
}
+ fn system_clipboard_is_newer(&self, cx: &mut AppContext) -> bool {
+ cx.read_from_clipboard().is_some_and(|item| {
+ if let Some(last_state) = &self.workspace_state.last_yank {
+ last_state != item.text()
+ } else {
+ true
+ }
+ })
+ }
+
fn push_operator(&mut self, operator: Operator, cx: &mut WindowContext) {
if matches!(
operator,
@@ -17,9 +17,9 @@ use workspace::{searchable::Direction, Workspace};
use crate::{
motion::{start_of_line, Motion},
normal::substitute::substitute,
+ normal::yank::{copy_selections_content, yank_selections_content},
object::Object,
state::{Mode, Operator},
- utils::{copy_selections_content, yank_selections_content},
Vim,
};
@@ -13,6 +13,8 @@
{"Key":"w"}
{"Get":{"state":"The quick brown\nfoxˇ \nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"a","value":"jumps over"}}
+{"Get":{"state":"The quick brown\nfoxˇ \nthe lazy dog","mode":"Normal"}}
+{"ReadRegister":{"name":"\"","value":"jumps over"}}
{"Key":"\""}
{"Key":"a"}
{"Key":"p"}
@@ -28,3 +28,14 @@
{"ReadRegister":{"name":"\"","value":"lazy dog"}}
{"Get":{"state":"The quick brown\nˇ","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"lazy dog"}}
+{"Key":"/"}
+{"Key":"d"}
+{"Key":"o"}
+{"Key":"g"}
+{"Key":"enter"}
+{"Get":{"state":"The quick brown\nˇ","mode":"Normal"}}
+{"ReadRegister":{"name":"/","value":"dog"}}
+{"Key":"\""}
+{"Key":"/"}
+{"Key":"shift-p"}
+{"Get":{"state":"The quick brown\ndoˇg","mode":"Normal"}}