Reimplement clipboard commands in terms of special regs

Since the clipboard provider now lives on the Registers type, we want
to eliminate it from the Editor. We can do that and clean up the
commands that interact with the clipboard by calling regular yank,
paste and replace impls on the clipboard special registers.

Eventually the clipboard commands could be removed once macro keybinding
is supported.
main
Michael Davis 10 months ago committed by Blaž Hrastnik
parent 2d838d729c
commit 4555a6b433

@ -32,7 +32,6 @@ use helix_core::{
RopeReader, RopeSlice, Selection, SmallVec, Tendril, Transaction, RopeReader, RopeSlice, Selection, SmallVec, Tendril, Transaction,
}; };
use helix_view::{ use helix_view::{
clipboard::ClipboardType,
document::{FormatterError, Mode, SCRATCH_BUFFER_NAME}, document::{FormatterError, Mode, SCRATCH_BUFFER_NAME},
editor::{Action, CompleteAction}, editor::{Action, CompleteAction},
info::Info, info::Info,
@ -3759,7 +3758,12 @@ fn commit_undo_checkpoint(cx: &mut Context) {
// Yank / Paste // Yank / Paste
fn yank(cx: &mut Context) { fn yank(cx: &mut Context) {
let (view, doc) = current!(cx.editor); yank_impl(cx.editor, cx.register.unwrap_or('"'));
exit_select_mode(cx);
}
fn yank_impl(editor: &mut Editor, register: char) {
let (view, doc) = current!(editor);
let text = doc.text().slice(..); let text = doc.text().slice(..);
let values: Vec<String> = doc let values: Vec<String> = doc
@ -3768,16 +3772,14 @@ fn yank(cx: &mut Context) {
.map(Cow::into_owned) .map(Cow::into_owned)
.collect(); .collect();
let selections = values.len(); let selections = values.len();
let register = cx.register.unwrap_or('"');
match cx.editor.registers.write(register, values) { match editor.registers.write(register, values) {
Ok(_) => cx.editor.set_status(format!( Ok(_) => editor.set_status(format!(
"yanked {selections} selection(s) to register {register}", "yanked {selections} selection{} to register {register}",
if selections == 1 { "" } else { "s" }
)), )),
Err(err) => cx.editor.set_error(err.to_string()), Err(err) => editor.set_error(err.to_string()),
} }
exit_select_mode(cx);
} }
fn yank_joined_impl(editor: &mut Editor, separator: &str, register: char) { fn yank_joined_impl(editor: &mut Editor, separator: &str, register: char) {
@ -3798,100 +3800,50 @@ fn yank_joined_impl(editor: &mut Editor, separator: &str, register: char) {
match editor.registers.write(register, vec![joined]) { match editor.registers.write(register, vec![joined]) {
Ok(_) => editor.set_status(format!( Ok(_) => editor.set_status(format!(
"joined and yanked {selections} selection(s) to register {register}", "joined and yanked {selections} selection{} to register {register}",
if selections == 1 { "" } else { "s" }
)), )),
Err(err) => editor.set_error(err.to_string()), Err(err) => editor.set_error(err.to_string()),
} }
} }
fn yank_joined(cx: &mut Context) { fn yank_joined(cx: &mut Context) {
let line_ending = doc!(cx.editor).line_ending; let separator = doc!(cx.editor).line_ending.as_str();
let register = cx.register.unwrap_or('"'); yank_joined_impl(cx.editor, separator, cx.register.unwrap_or('"'));
yank_joined_impl(cx.editor, line_ending.as_str(), register);
exit_select_mode(cx); exit_select_mode(cx);
} }
fn yank_joined_to_clipboard_impl( fn yank_joined_to_clipboard(cx: &mut Context) {
editor: &mut Editor, let line_ending = doc!(cx.editor).line_ending;
separator: &str, yank_joined_impl(cx.editor, line_ending.as_str(), '*');
clipboard_type: ClipboardType, exit_select_mode(cx);
) -> anyhow::Result<()> {
let (view, doc) = current!(editor);
let text = doc.text().slice(..);
let values: Vec<String> = doc
.selection(view.id)
.fragments(text)
.map(Cow::into_owned)
.collect();
let clipboard_text = match clipboard_type {
ClipboardType::Clipboard => "system clipboard",
ClipboardType::Selection => "primary clipboard",
};
let msg = format!(
"joined and yanked {} selection(s) to {}",
values.len(),
clipboard_text,
);
let joined = values.join(separator);
editor
.clipboard_provider
.set_contents(joined, clipboard_type)
.context("Couldn't set system clipboard content")?;
editor.set_status(msg);
Ok(())
} }
fn yank_joined_to_clipboard(cx: &mut Context) { fn yank_joined_to_primary_clipboard(cx: &mut Context) {
let line_ending = doc!(cx.editor).line_ending; let line_ending = doc!(cx.editor).line_ending;
let _ = yank_joined_impl(cx.editor, line_ending.as_str(), '+');
yank_joined_to_clipboard_impl(cx.editor, line_ending.as_str(), ClipboardType::Clipboard);
exit_select_mode(cx); exit_select_mode(cx);
} }
fn yank_main_selection_to_clipboard_impl( fn yank_primary_selection_impl(editor: &mut Editor, register: char) {
editor: &mut Editor,
clipboard_type: ClipboardType,
) -> anyhow::Result<()> {
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
let text = doc.text().slice(..); let text = doc.text().slice(..);
let message_text = match clipboard_type { let selection = doc.selection(view.id).primary().fragment(text).to_string();
ClipboardType::Clipboard => "yanked main selection to system clipboard",
ClipboardType::Selection => "yanked main selection to primary clipboard",
};
let value = doc.selection(view.id).primary().fragment(text);
if let Err(e) = editor match editor.registers.write(register, vec![selection]) {
.clipboard_provider Ok(_) => editor.set_status(format!("yanked primary selection to register {register}",)),
.set_contents(value.into_owned(), clipboard_type) Err(err) => editor.set_error(err.to_string()),
{
bail!("Couldn't set system clipboard content: {}", e);
} }
editor.set_status(message_text);
Ok(())
} }
fn yank_main_selection_to_clipboard(cx: &mut Context) { fn yank_main_selection_to_clipboard(cx: &mut Context) {
let _ = yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard); yank_primary_selection_impl(cx.editor, '*');
} exit_select_mode(cx);
fn yank_joined_to_primary_clipboard(cx: &mut Context) {
let line_ending = doc!(cx.editor).line_ending;
let _ =
yank_joined_to_clipboard_impl(cx.editor, line_ending.as_str(), ClipboardType::Selection);
} }
fn yank_main_selection_to_primary_clipboard(cx: &mut Context) { fn yank_main_selection_to_primary_clipboard(cx: &mut Context) {
let _ = yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection); yank_primary_selection_impl(cx.editor, '+');
exit_select_mode(cx); exit_select_mode(cx);
} }
@ -3991,68 +3943,34 @@ pub(crate) fn paste_bracketed_value(cx: &mut Context, contents: String) {
paste_impl(&[contents], doc, view, paste, count, cx.editor.mode); paste_impl(&[contents], doc, view, paste, count, cx.editor.mode);
} }
fn paste_clipboard_impl(
editor: &mut Editor,
action: Paste,
clipboard_type: ClipboardType,
count: usize,
) -> anyhow::Result<()> {
let (view, doc) = current!(editor);
match editor.clipboard_provider.get_contents(clipboard_type) {
Ok(contents) => {
paste_impl(&[contents], doc, view, action, count, editor.mode);
Ok(())
}
Err(e) => Err(e.context("Couldn't get system clipboard contents")),
}
}
fn paste_clipboard_after(cx: &mut Context) { fn paste_clipboard_after(cx: &mut Context) {
let _ = paste_clipboard_impl( paste(cx.editor, '*', Paste::After, cx.count());
cx.editor,
Paste::After,
ClipboardType::Clipboard,
cx.count(),
);
} }
fn paste_clipboard_before(cx: &mut Context) { fn paste_clipboard_before(cx: &mut Context) {
let _ = paste_clipboard_impl( paste(cx.editor, '*', Paste::Before, cx.count());
cx.editor,
Paste::Before,
ClipboardType::Clipboard,
cx.count(),
);
} }
fn paste_primary_clipboard_after(cx: &mut Context) { fn paste_primary_clipboard_after(cx: &mut Context) {
let _ = paste_clipboard_impl( paste(cx.editor, '+', Paste::After, cx.count());
cx.editor,
Paste::After,
ClipboardType::Selection,
cx.count(),
);
} }
fn paste_primary_clipboard_before(cx: &mut Context) { fn paste_primary_clipboard_before(cx: &mut Context) {
let _ = paste_clipboard_impl( paste(cx.editor, '+', Paste::Before, cx.count());
cx.editor,
Paste::Before,
ClipboardType::Selection,
cx.count(),
);
} }
fn replace_with_yanked(cx: &mut Context) { fn replace_with_yanked(cx: &mut Context) {
let count = cx.count(); replace_with_yanked_impl(cx.editor, cx.register.unwrap_or('"'), cx.count());
let reg_name = cx.register.unwrap_or('"'); exit_select_mode(cx);
}
let Some(values) = cx.editor.registers fn replace_with_yanked_impl(editor: &mut Editor, register: char, count: usize) {
.read(reg_name, cx.editor) let Some(values) = editor.registers
.read(register, editor)
.filter(|values| values.len() > 0) else { return }; .filter(|values| values.len() > 0) else { return };
let values: Vec<_> = values.map(|value| value.to_string()).collect(); let values: Vec<_> = values.map(|value| value.to_string()).collect();
let (view, doc) = current!(cx.editor); let (view, doc) = current!(editor);
let repeat = std::iter::repeat( let repeat = std::iter::repeat(
values values
.last() .last()
@ -4073,62 +3991,40 @@ fn replace_with_yanked(cx: &mut Context) {
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
exit_select_mode(cx);
}
fn replace_selections_with_clipboard_impl(
cx: &mut Context,
clipboard_type: ClipboardType,
) -> anyhow::Result<()> {
let count = cx.count();
let (view, doc) = current!(cx.editor);
match cx.editor.clipboard_provider.get_contents(clipboard_type) {
Ok(contents) => {
let selection = doc.selection(view.id);
let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
(
range.from(),
range.to(),
Some(contents.repeat(count).as_str().into()),
)
});
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view);
}
Err(e) => return Err(e.context("Couldn't get system clipboard contents")),
}
exit_select_mode(cx);
Ok(())
} }
fn replace_selections_with_clipboard(cx: &mut Context) { fn replace_selections_with_clipboard(cx: &mut Context) {
let _ = replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard); replace_with_yanked_impl(cx.editor, '*', cx.count());
} }
fn replace_selections_with_primary_clipboard(cx: &mut Context) { fn replace_selections_with_primary_clipboard(cx: &mut Context) {
let _ = replace_selections_with_clipboard_impl(cx, ClipboardType::Selection); replace_with_yanked_impl(cx.editor, '+', cx.count());
} }
fn paste(cx: &mut Context, pos: Paste) { fn paste(editor: &mut Editor, register: char, pos: Paste, count: usize) {
let count = cx.count(); let Some(values) = editor.registers.read(register, editor) else { return };
let reg_name = cx.register.unwrap_or('"');
let Some(values) = cx.editor.registers.read(reg_name, cx.editor) else { return };
let values: Vec<_> = values.map(|value| value.to_string()).collect(); let values: Vec<_> = values.map(|value| value.to_string()).collect();
let (view, doc) = current!(cx.editor); let (view, doc) = current!(editor);
paste_impl(&values, doc, view, pos, count, cx.editor.mode); paste_impl(&values, doc, view, pos, count, editor.mode);
} }
fn paste_after(cx: &mut Context) { fn paste_after(cx: &mut Context) {
paste(cx, Paste::After) paste(
cx.editor,
cx.register.unwrap_or('"'),
Paste::After,
cx.count(),
);
} }
fn paste_before(cx: &mut Context) { fn paste_before(cx: &mut Context) {
paste(cx, Paste::Before) paste(
cx.editor,
cx.register.unwrap_or('"'),
Paste::Before,
cx.count(),
);
} }
fn get_lines(doc: &Document, view_id: ViewId) -> Vec<usize> { fn get_lines(doc: &Document, view_id: ViewId) -> Vec<usize> {
@ -4885,7 +4781,12 @@ fn insert_register(cx: &mut Context) {
if let Some(ch) = event.char() { if let Some(ch) = event.char() {
cx.editor.autoinfo = None; cx.editor.autoinfo = None;
cx.register = Some(ch); cx.register = Some(ch);
paste(cx, Paste::Cursor); paste(
cx.editor,
cx.register.unwrap_or('"'),
Paste::Cursor,
cx.count(),
);
} }
}) })
} }

@ -904,7 +904,8 @@ fn yank_main_selection_to_clipboard(
return Ok(()); return Ok(());
} }
yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard) yank_primary_selection_impl(cx.editor, '*');
Ok(())
} }
fn yank_joined( fn yank_joined(
@ -938,7 +939,8 @@ fn yank_joined_to_clipboard(
let doc = doc!(cx.editor); let doc = doc!(cx.editor);
let default_sep = Cow::Borrowed(doc.line_ending.as_str()); let default_sep = Cow::Borrowed(doc.line_ending.as_str());
let separator = args.first().unwrap_or(&default_sep); let separator = args.first().unwrap_or(&default_sep);
yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Clipboard) yank_joined_impl(cx.editor, separator, '*');
Ok(())
} }
fn yank_main_selection_to_primary_clipboard( fn yank_main_selection_to_primary_clipboard(
@ -950,7 +952,8 @@ fn yank_main_selection_to_primary_clipboard(
return Ok(()); return Ok(());
} }
yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection) yank_primary_selection_impl(cx.editor, '+');
Ok(())
} }
fn yank_joined_to_primary_clipboard( fn yank_joined_to_primary_clipboard(
@ -965,7 +968,8 @@ fn yank_joined_to_primary_clipboard(
let doc = doc!(cx.editor); let doc = doc!(cx.editor);
let default_sep = Cow::Borrowed(doc.line_ending.as_str()); let default_sep = Cow::Borrowed(doc.line_ending.as_str());
let separator = args.first().unwrap_or(&default_sep); let separator = args.first().unwrap_or(&default_sep);
yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Selection) yank_joined_impl(cx.editor, separator, '+');
Ok(())
} }
fn paste_clipboard_after( fn paste_clipboard_after(
@ -977,7 +981,8 @@ fn paste_clipboard_after(
return Ok(()); return Ok(());
} }
paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard, 1) paste(cx.editor, '*', Paste::After, 1);
Ok(())
} }
fn paste_clipboard_before( fn paste_clipboard_before(
@ -989,7 +994,8 @@ fn paste_clipboard_before(
return Ok(()); return Ok(());
} }
paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Clipboard, 1) paste(cx.editor, '*', Paste::Before, 1);
Ok(())
} }
fn paste_primary_clipboard_after( fn paste_primary_clipboard_after(
@ -1001,7 +1007,8 @@ fn paste_primary_clipboard_after(
return Ok(()); return Ok(());
} }
paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection, 1) paste(cx.editor, '+', Paste::After, 1);
Ok(())
} }
fn paste_primary_clipboard_before( fn paste_primary_clipboard_before(
@ -1013,30 +1020,8 @@ fn paste_primary_clipboard_before(
return Ok(()); return Ok(());
} }
paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Selection, 1) paste(cx.editor, '+', Paste::Before, 1);
} Ok(())
fn replace_selections_with_clipboard_impl(
cx: &mut compositor::Context,
clipboard_type: ClipboardType,
) -> anyhow::Result<()> {
let scrolloff = cx.editor.config().scrolloff;
let (view, doc) = current!(cx.editor);
match cx.editor.clipboard_provider.get_contents(clipboard_type) {
Ok(contents) => {
let selection = doc.selection(view.id);
let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
(range.from(), range.to(), Some(contents.as_str().into()))
});
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view);
view.ensure_cursor_in_view(doc, scrolloff);
Ok(())
}
Err(e) => Err(e.context("Couldn't get system clipboard contents")),
}
} }
fn replace_selections_with_clipboard( fn replace_selections_with_clipboard(
@ -1048,7 +1033,8 @@ fn replace_selections_with_clipboard(
return Ok(()); return Ok(());
} }
replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard) replace_with_yanked_impl(cx.editor, '*', 1);
Ok(())
} }
fn replace_selections_with_primary_clipboard( fn replace_selections_with_primary_clipboard(
@ -1060,7 +1046,8 @@ fn replace_selections_with_primary_clipboard(
return Ok(()); return Ok(());
} }
replace_selections_with_clipboard_impl(cx, ClipboardType::Selection) replace_with_yanked_impl(cx.editor, '+', 1);
Ok(())
} }
fn show_clipboard_provider( fn show_clipboard_provider(
@ -1073,7 +1060,7 @@ fn show_clipboard_provider(
} }
cx.editor cx.editor
.set_status(cx.editor.clipboard_provider.name().to_string()); .set_status(cx.editor.registers.clipboard_provider_name().to_string());
Ok(()) Ok(())
} }

@ -1,6 +1,5 @@
use crate::{ use crate::{
align_view, align_view,
clipboard::{get_clipboard_provider, ClipboardProvider},
document::{DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint}, document::{DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint},
graphics::{CursorKind, Rect}, graphics::{CursorKind, Rect},
info::Info, info::Info,
@ -875,8 +874,6 @@ pub struct Editor {
pub debugger_events: SelectAll<UnboundedReceiverStream<dap::Payload>>, pub debugger_events: SelectAll<UnboundedReceiverStream<dap::Payload>>,
pub breakpoints: HashMap<PathBuf, Vec<Breakpoint>>, pub breakpoints: HashMap<PathBuf, Vec<Breakpoint>>,
pub clipboard_provider: Box<dyn ClipboardProvider>,
pub syn_loader: Arc<syntax::Loader>, pub syn_loader: Arc<syntax::Loader>,
pub theme_loader: Arc<theme::Loader>, pub theme_loader: Arc<theme::Loader>,
/// last_theme is used for theme previews. We store the current theme here, /// last_theme is used for theme previews. We store the current theme here,
@ -1023,7 +1020,6 @@ impl Editor {
last_theme: None, last_theme: None,
last_selection: None, last_selection: None,
registers: Registers::default(), registers: Registers::default(),
clipboard_provider: get_clipboard_provider(),
status_msg: None, status_msg: None,
autoinfo: None, autoinfo: None,
idle_timer: Box::pin(sleep(conf.idle_timeout)), idle_timer: Box::pin(sleep(conf.idle_timeout)),

@ -215,6 +215,10 @@ impl Registers {
) )
} }
} }
pub fn clipboard_provider_name(&self) -> Cow<str> {
self.clipboard_provider.name()
}
} }
fn read_from_clipboard<'a>( fn read_from_clipboard<'a>(

Loading…
Cancel
Save