Skip to content

Commit

Permalink
Re-implement smart copy from alacritty#3873
Browse files Browse the repository at this point in the history
  • Loading branch information
sigurdo committed Aug 17, 2023
1 parent 9d9982d commit 398e508
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/upload_asset.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if [ $# -lt 2 ]; then
exit 1
fi

repo="alacritty/alacritty"
repo="sigurdo/alacritty-smart-copy"
file_path=$1
bearer=$2

Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,38 @@
src="https://user-images.githubusercontent.com/8886672/103264352-5ab0d500-49a2-11eb-8961-02f7da66c855.png">
</p>

## About this fork

This fork of Alacritty lets you copy and paste with `ctrl+c` and `ctrl+v`, like in most modern Windows and Linux software. If no text is selected, `ctrl+c` sends `SIGINT` like usual. The selection is cleared when you copy with `ctrl+c`. In this way, pressing `ctrl+c` twice will always send minimum one `SIGINT`. Copying without clearing the selection can still be achieved with `ctrl+shift+c`.

On the technical side, a `CopyDynamic` action is added. It copies the selected text and clears the selection. When no text is selected, the keystroke is passed through, thus causing a `SIGINT`.

"Smart copy" was first propsed and rejected already [back in 2018](https://github.com/alacritty/alacritty/issues/1919) for reasons I do not agree with. In 2020, dtheodor provided a [fantastic explanation](https://github.com/alacritty/alacritty/issues/1919#issuecomment-588188924) of why smart copy is such a brilliant solution. A [pull request implementing this feature](https://github.com/alacritty/alacritty/pull/3873) was proposed by ambiso later in 2020, which was also rejected. [Their fork](https://github.com/ambiso/alacritty/tree/master) is sadly outdated now. Therefore, I created this updated forked with the same approach. I plan to keep the fork up to date for as long as I use Alacritty as my daily driver terminal emulator.

The following key bindings are provided by default for Windows, Linux and BSD:

```yaml
key_bindings:
- { key: V, mods: Control, mode: ~Vi, action: Paste }
- { key: C, mods: Control, mode: ~Vi, action: CopyDynamic }
```
and in TOML (for Alacritty v13+),
```toml
[[keyboard.bindings]]
key = "V"
mods = "Control"
mode = "~Vi"
action = "Paste"

[[keyboard.bindings]]
key = "C"
mods = "Control"
mode = "~Vi"
action = "CopyDynamic"
```

## About

Alacritty is a modern terminal emulator that comes with sensible defaults, but
Expand Down
5 changes: 5 additions & 0 deletions alacritty/src/config/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ pub enum Action {
/// Store current selection into clipboard.
Copy,

/// Copy selection and clear it, or forward the keybinding to the program if there is no selection.
CopyDynamic,

#[cfg(not(any(target_os = "macos", windows)))]
/// Store current selection into selection buffer.
CopySelection,
Expand Down Expand Up @@ -666,7 +669,9 @@ fn common_keybindings() -> Vec<KeyBinding> {
bindings!(
KeyBinding;
V, ModifiersState::CTRL | ModifiersState::SHIFT, ~BindingMode::VI; Action::Paste;
V, ModifiersState::CTRL, ~BindingMode::VI; Action::Paste;
C, ModifiersState::CTRL | ModifiersState::SHIFT; Action::Copy;
C, ModifiersState::CTRL, ~BindingMode::VI; Action::CopyDynamic;
F, ModifiersState::CTRL | ModifiersState::SHIFT, ~BindingMode::SEARCH;
Action::SearchForward;
B, ModifiersState::CTRL | ModifiersState::SHIFT, ~BindingMode::SEARCH;
Expand Down
11 changes: 11 additions & 0 deletions alacritty/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ impl<T: EventListener> Execute<T> for Action {
Action::Copy => ctx.copy_selection(ClipboardType::Clipboard),
#[cfg(not(any(target_os = "macos", windows)))]
Action::CopySelection => ctx.copy_selection(ClipboardType::Selection),
Action::CopyDynamic => {
if !ctx.selection_is_empty() {
ctx.copy_selection(ClipboardType::Clipboard);
ctx.clear_selection();
}
},
Action::ClearSelection => ctx.clear_selection(),
Action::Paste => {
let text = ctx.clipboard_mut().load(ClipboardType::Clipboard);
Expand Down Expand Up @@ -1043,6 +1049,11 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
// Pass through the key if any of the bindings has the `ReceiveChar` action.
*suppress_chars.get_or_insert(true) &= binding.action != Action::ReceiveChar;

// Pass through the key if any of the bindings has the `CopyDynamic` action and the selection is empty.
if binding.action == Action::CopyDynamic && self.ctx.selection_is_empty() {
suppress_chars = Some(false);
}

// Binding was triggered; run the action.
binding.action.clone().execute(&mut self.ctx);
}
Expand Down

0 comments on commit 398e508

Please sign in to comment.