diff options
author | Nathan Vegdahl | 2021-06-20 23:09:14 +0000 |
---|---|---|
committer | Nathan Vegdahl | 2021-06-20 23:09:14 +0000 |
commit | e686c3e4626fdafbcc2dab9d381eba83a5f6f974 (patch) | |
tree | a598e3fedc1f2ae78ebc6f132c81b37cedf5415d /helix-term/src/commands.rs | |
parent | 4efd6713c5b30b33c497a1f85b77a7b0a7fd17e0 (diff) | |
parent | 985625763addd839a101263ae90cfb2f205830fc (diff) |
Merge branch 'master' of github.com:helix-editor/helix into line_ending_detection
Rebasing was making me manually fix conflicts on every commit, so
merging instead.
Diffstat (limited to 'helix-term/src/commands.rs')
-rw-r--r-- | helix-term/src/commands.rs | 294 |
1 files changed, 232 insertions, 62 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index b006504b..28c4fe3a 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -11,7 +11,6 @@ use helix_core::{ use helix_view::{ document::{IndentStyle, Mode}, - input::{KeyCode, KeyEvent}, view::{View, PADDING}, Document, DocumentId, Editor, ViewId, }; @@ -39,8 +38,8 @@ use std::{ path::{Path, PathBuf}, }; +use crossterm::event::{KeyCode, KeyEvent}; use once_cell::sync::Lazy; -use serde::de::{self, Deserialize, Deserializer}; pub struct Context<'a> { pub selected_register: helix_view::RegisterSelection, @@ -186,7 +185,6 @@ impl Command { search_next, extend_search_next, search_selection, - select_line, extend_line, delete_selection, change_selection, @@ -223,9 +221,14 @@ impl Command { undo, redo, yank, + yank_joined_to_clipboard, + yank_main_selection_to_clipboard, replace_with_yanked, + replace_selections_with_clipboard, paste_after, paste_before, + paste_clipboard_after, + paste_clipboard_before, indent, unindent, format_selections, @@ -253,48 +256,6 @@ impl Command { ); } -impl fmt::Debug for Command { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Command(name, _) = self; - f.debug_tuple("Command").field(name).finish() - } -} - -impl fmt::Display for Command { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Command(name, _) = self; - f.write_str(name) - } -} - -impl std::str::FromStr for Command { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - Command::COMMAND_LIST - .iter() - .copied() - .find(|cmd| cmd.0 == s) - .ok_or_else(|| anyhow!("No command named '{}'", s)) - } -} - -impl<'de> Deserialize<'de> for Command { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - s.parse().map_err(de::Error::custom) - } -} - -impl PartialEq for Command { - fn eq(&self, other: &Self) -> bool { - self.name() == other.name() - } -} - fn move_char_left(cx: &mut Context) { let count = cx.count(); let (view, doc) = current!(cx.editor); @@ -926,21 +887,6 @@ fn search_selection(cx: &mut Context) { // -fn select_line(cx: &mut Context) { - let count = cx.count(); - let (view, doc) = current!(cx.editor); - - let pos = doc.selection(view.id).primary(); - let text = doc.text(); - - let line = text.char_to_line(pos.head); - let start = text.line_to_char(line); - let end = text - .line_to_char(std::cmp::min(doc.text().len_lines(), line + count)) - .saturating_sub(1); - - doc.set_selection(view.id, Selection::single(start, end)); -} fn extend_line(cx: &mut Context) { let count = cx.count(); let (view, doc) = current!(cx.editor); @@ -1318,6 +1264,57 @@ mod cmd { quit_all_impl(editor, args, event, true) } + fn theme(editor: &mut Editor, args: &[&str], event: PromptEvent) { + let theme = if let Some(theme) = args.first() { + theme + } else { + editor.set_error("theme name not provided".into()); + return; + }; + + editor.set_theme_from_name(theme); + } + + fn yank_main_selection_to_clipboard(editor: &mut Editor, _: &[&str], _: PromptEvent) { + yank_main_selection_to_clipboard_impl(editor); + } + + fn yank_joined_to_clipboard(editor: &mut Editor, args: &[&str], _: PromptEvent) { + let separator = args.first().copied().unwrap_or("\n"); + yank_joined_to_clipboard_impl(editor, separator); + } + + fn paste_clipboard_after(editor: &mut Editor, _: &[&str], _: PromptEvent) { + paste_clipboard_impl(editor, Paste::After); + } + + fn paste_clipboard_before(editor: &mut Editor, _: &[&str], _: PromptEvent) { + paste_clipboard_impl(editor, Paste::After); + } + + fn replace_selections_with_clipboard(editor: &mut Editor, _: &[&str], _: PromptEvent) { + let (view, doc) = current!(editor); + + match editor.clipboard_provider.get_contents() { + Ok(contents) => { + let transaction = + Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { + let max_to = doc.text().len_chars().saturating_sub(1); + let to = std::cmp::min(max_to, range.to() + 1); + (range.from(), to, Some(contents.as_str().into())) + }); + + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); + } + Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e), + } + } + + fn show_clipboard_provider(editor: &mut Editor, _: &[&str], _: PromptEvent) { + editor.set_status(editor.clipboard_provider.name().into()); + } + pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "quit", @@ -1431,7 +1428,55 @@ mod cmd { fun: force_quit_all, completer: None, }, - + TypableCommand { + name: "theme", + alias: None, + doc: "Change the theme of current view. Requires theme name as argument (:theme <name>)", + fun: theme, + completer: Some(completers::theme), + }, + TypableCommand { + name: "clipboard-yank", + alias: None, + doc: "Yank main selection into system clipboard.", + fun: yank_main_selection_to_clipboard, + completer: None, + }, + TypableCommand { + name: "clipboard-yank-join", + alias: None, + doc: "Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline.", // FIXME: current UI can't display long doc. + fun: yank_joined_to_clipboard, + completer: None, + }, + TypableCommand { + name: "clipboard-paste-after", + alias: None, + doc: "Paste system clipboard after selections.", + fun: paste_clipboard_after, + completer: None, + }, + TypableCommand { + name: "clipboard-paste-before", + alias: None, + doc: "Paste system clipboard before selections.", + fun: paste_clipboard_before, + completer: None, + }, + TypableCommand { + name: "clipboard-paste-replace", + alias: None, + doc: "Replace selections with content of system clipboard.", + fun: replace_selections_with_clipboard, + completer: None, + }, + TypableCommand { + name: "show-clipboard-provider", + alias: None, + doc: "Show clipboard provider name in status bar.", + fun: show_clipboard_provider, + completer: None, + }, ]; pub static COMMANDS: Lazy<HashMap<&'static str, &'static TypableCommand>> = Lazy::new(|| { @@ -2424,6 +2469,52 @@ fn yank(cx: &mut Context) { cx.editor.set_status(msg) } +fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) { + let (view, doc) = current!(editor); + + let values: Vec<String> = doc + .selection(view.id) + .fragments(doc.text().slice(..)) + .map(Cow::into_owned) + .collect(); + + let msg = format!( + "joined and yanked {} selection(s) to system clipboard", + values.len(), + ); + + let joined = values.join(separator); + + if let Err(e) = editor.clipboard_provider.set_contents(joined) { + log::error!("Couldn't set system clipboard content: {:?}", e); + } + + editor.set_status(msg); +} + +fn yank_joined_to_clipboard(cx: &mut Context) { + yank_joined_to_clipboard_impl(&mut cx.editor, "\n"); +} + +fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) { + let (view, doc) = current!(editor); + + let value = doc + .selection(view.id) + .primary() + .fragment(doc.text().slice(..)); + + if let Err(e) = editor.clipboard_provider.set_contents(value.into_owned()) { + log::error!("Couldn't set system clipboard content: {:?}", e); + } + + editor.set_status("yanked main selection to system clipboard".to_owned()); +} + +fn yank_main_selection_to_clipboard(cx: &mut Context) { + yank_main_selection_to_clipboard_impl(&mut cx.editor); +} + #[derive(Copy, Clone)] enum Paste { Before, @@ -2469,6 +2560,31 @@ fn paste_impl( Some(transaction) } +fn paste_clipboard_impl(editor: &mut Editor, action: Paste) { + let (view, doc) = current!(editor); + + match editor + .clipboard_provider + .get_contents() + .map(|contents| paste_impl(&[contents], doc, view, action)) + { + Ok(Some(transaction)) => { + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); + } + Ok(None) => {} + Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e), + } +} + +fn paste_clipboard_after(cx: &mut Context) { + paste_clipboard_impl(&mut cx.editor, Paste::After); +} + +fn paste_clipboard_before(cx: &mut Context) { + paste_clipboard_impl(&mut cx.editor, Paste::Before); +} + fn replace_with_yanked(cx: &mut Context) { let reg_name = cx.selected_register.name(); let (view, doc) = current!(cx.editor); @@ -2489,6 +2605,29 @@ fn replace_with_yanked(cx: &mut Context) { } } +fn replace_selections_with_clipboard_impl(editor: &mut Editor) { + let (view, doc) = current!(editor); + + match editor.clipboard_provider.get_contents() { + Ok(contents) => { + let transaction = + Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { + let max_to = doc.text().len_chars().saturating_sub(1); + let to = std::cmp::min(max_to, range.to() + 1); + (range.from(), to, Some(contents.as_str().into())) + }); + + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); + } + Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e), + } +} + +fn replace_selections_with_clipboard(cx: &mut Context) { + replace_selections_with_clipboard_impl(&mut cx.editor); +} + // alt-p => paste every yanked selection after selected text // alt-P => paste every yanked selection before selected text // R => replace selected text with yanked text @@ -2854,7 +2993,7 @@ fn hover(cx: &mut Context) { // skip if contents empty - let contents = ui::Markdown::new(contents); + let contents = ui::Markdown::new(contents, editor.syn_loader.clone()); let mut popup = Popup::new(contents); compositor.push(Box::new(popup)); } @@ -3009,6 +3148,11 @@ fn space_mode(cx: &mut Context) { 'b' => buffer_picker(cx), 's' => symbol_picker(cx), 'w' => window_mode(cx), + 'y' => yank_joined_to_clipboard(cx), + 'Y' => yank_main_selection_to_clipboard(cx), + 'p' => paste_clipboard_after(cx), + 'P' => paste_clipboard_before(cx), + 'R' => replace_selections_with_clipboard(cx), // ' ' => toggle_alternate_buffer(cx), // TODO: temporary since space mode took its old key ' ' => keep_primary_selection(cx), @@ -3092,3 +3236,29 @@ fn right_bracket_mode(cx: &mut Context) { } }) } + +impl fmt::Display for Command { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Command(name, _) = self; + f.write_str(name) + } +} + +impl std::str::FromStr for Command { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + Command::COMMAND_LIST + .iter() + .copied() + .find(|cmd| cmd.0 == s) + .ok_or_else(|| anyhow!("No command named '{}'", s)) + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Command(name, _) = self; + f.debug_tuple("Command").field(name).finish() + } +} |