Multicursor vim registers (#13025)

Release Notes:

- vim: Added support for multicursor registers (#11687)
- vim: Added support for the `"/` register
This commit is contained in:
Conrad Irwin 2024-06-13 20:32:58 -06:00 committed by GitHub
parent 068b1c235c
commit a5af5b2883
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 333 additions and 250 deletions

View file

@ -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,