Skip to content

Commit 32b44b0

Browse files
committed
Add bracketed paste
1 parent 2af90fa commit 32b44b0

File tree

14 files changed

+98
-56
lines changed

14 files changed

+98
-56
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

helix-term/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ which = "4.2"
3838

3939
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
4040
tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] }
41-
crossterm = { version = "0.24", git = "https://github.com/crossterm-rs/crossterm.git", features = ["event-stream"] }
41+
crossterm = { version = "0.24.0", features = ["event-stream"], git = "https://github.com/groves/crossterm.git", branch = "add-bracketed-paste" }
4242
signal-hook = "0.3"
4343
tokio-stream = "0.1"
4444
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }

helix-term/src/application.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use std::{
2929
use anyhow::{Context, Error};
3030

3131
use crossterm::{
32-
event::{DisableMouseCapture, EnableMouseCapture, Event},
32+
event::{DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture},
3333
execute, terminal,
3434
tty::IsTty,
3535
};
@@ -418,22 +418,22 @@ impl Application {
418418
}
419419
}
420420

421-
pub fn handle_terminal_events(&mut self, event: Result<Event, crossterm::ErrorKind>) {
421+
pub fn handle_terminal_events(
422+
&mut self,
423+
event: Result<crossterm::event::Event, crossterm::ErrorKind>,
424+
) {
422425
let mut cx = crate::compositor::Context {
423426
editor: &mut self.editor,
424427
jobs: &mut self.jobs,
425428
scroll: None,
426429
};
427-
// Handle key events
428-
let should_redraw = match event {
429-
Ok(Event::Resize(width, height)) => {
430+
let should_redraw = match event.unwrap() {
431+
crossterm::event::Event::Resize(width, height) => {
430432
self.compositor.resize(width, height);
431-
432433
self.compositor
433-
.handle_event(Event::Resize(width, height), &mut cx)
434+
.handle_event(&crossterm::event::Event::Resize(width, height), &mut cx)
434435
}
435-
Ok(event) => self.compositor.handle_event(event, &mut cx),
436-
Err(x) => panic!("{}", x),
436+
event => self.compositor.handle_event(&event, &mut cx),
437437
};
438438

439439
if should_redraw && !self.editor.should_close() {
@@ -789,7 +789,7 @@ impl Application {
789789
async fn claim_term(&mut self) -> Result<(), Error> {
790790
terminal::enable_raw_mode()?;
791791
let mut stdout = stdout();
792-
execute!(stdout, terminal::EnterAlternateScreen)?;
792+
execute!(stdout, terminal::EnterAlternateScreen, EnableBracketedPaste)?;
793793
execute!(stdout, terminal::Clear(terminal::ClearType::All))?;
794794
if self.config.load().editor.mouse {
795795
execute!(stdout, EnableMouseCapture)?;
@@ -822,7 +822,11 @@ impl Application {
822822
// probably not a good idea to `unwrap()` inside a panic handler.
823823
// So we just ignore the `Result`s.
824824
let _ = execute!(std::io::stdout(), DisableMouseCapture);
825-
let _ = execute!(std::io::stdout(), terminal::LeaveAlternateScreen);
825+
let _ = execute!(
826+
std::io::stdout(),
827+
terminal::LeaveAlternateScreen,
828+
DisableBracketedPaste
829+
);
826830
let _ = terminal::disable_raw_mode();
827831
hook(info);
828832
}));

helix-term/src/commands.rs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3374,7 +3374,7 @@ fn paste_impl(
33743374
view: &View,
33753375
action: Paste,
33763376
count: usize,
3377-
) -> Option<Transaction> {
3377+
) -> Transaction {
33783378
let repeat = std::iter::repeat(
33793379
values
33803380
.last()
@@ -3399,7 +3399,7 @@ fn paste_impl(
33993399
let text = doc.text();
34003400
let selection = doc.selection(view.id);
34013401

3402-
let transaction = Transaction::change_by_selection(text, selection, |range| {
3402+
Transaction::change_by_selection(text, selection, |range| {
34033403
let pos = match (action, linewise) {
34043404
// paste linewise before
34053405
(Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())),
@@ -3416,9 +3416,32 @@ fn paste_impl(
34163416
(Paste::Cursor, _) => range.cursor(text.slice(..)),
34173417
};
34183418
(pos, pos, values.next())
3419-
});
3419+
})
3420+
}
34203421

3421-
Some(transaction)
3422+
pub(crate) fn paste_bracketed_value(
3423+
contents: String,
3424+
doc: &mut Document,
3425+
view: &View,
3426+
count: usize,
3427+
) {
3428+
let paste = match doc.mode {
3429+
Mode::Insert | Mode::Select => Paste::Cursor,
3430+
Mode::Normal => Paste::Before,
3431+
};
3432+
paste_external_value(contents, doc, view, paste, count);
3433+
}
3434+
3435+
fn paste_external_value(
3436+
contents: String,
3437+
doc: &mut Document,
3438+
view: &View,
3439+
action: Paste,
3440+
count: usize,
3441+
) {
3442+
let transaction = paste_impl(&[contents], doc, view, action, count);
3443+
doc.apply(&transaction, view.id);
3444+
doc.append_changes_to_history(view.id);
34223445
}
34233446

34243447
fn paste_clipboard_impl(
@@ -3428,18 +3451,11 @@ fn paste_clipboard_impl(
34283451
count: usize,
34293452
) -> anyhow::Result<()> {
34303453
let (view, doc) = current!(editor);
3431-
3432-
match editor
3433-
.clipboard_provider
3434-
.get_contents(clipboard_type)
3435-
.map(|contents| paste_impl(&[contents], doc, view, action, count))
3436-
{
3437-
Ok(Some(transaction)) => {
3438-
doc.apply(&transaction, view.id);
3439-
doc.append_changes_to_history(view.id);
3454+
match editor.clipboard_provider.get_contents(clipboard_type) {
3455+
Ok(contents) => {
3456+
paste_external_value(contents, doc, view, action, count);
34403457
Ok(())
34413458
}
3442-
Ok(None) => Ok(()),
34433459
Err(e) => Err(e.context("Couldn't get system clipboard contents")),
34443460
}
34453461
}
@@ -3552,10 +3568,8 @@ fn paste(cx: &mut Context, pos: Paste) {
35523568
let (view, doc) = current!(cx.editor);
35533569
let registers = &mut cx.editor.registers;
35543570

3555-
if let Some(transaction) = registers
3556-
.read(reg_name)
3557-
.and_then(|values| paste_impl(values, doc, view, pos, count))
3558-
{
3571+
if let Some(values) = registers.read(reg_name) {
3572+
let transaction = paste_impl(values, doc, view, pos, count);
35593573
doc.apply(&transaction, view.id);
35603574
}
35613575
}
@@ -4842,7 +4856,7 @@ fn replay_macro(cx: &mut Context) {
48424856
cx.callback = Some(Box::new(move |compositor, cx| {
48434857
for _ in 0..count {
48444858
for &key in keys.iter() {
4845-
compositor.handle_event(crossterm::event::Event::Key(key.into()), cx);
4859+
compositor.handle_event(&crossterm::event::Event::Key(key.into()), cx);
48464860
}
48474861
}
48484862
// The macro under replay is cleared at the end of the callback, not in the

helix-term/src/compositor.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct Context<'a> {
3030

3131
pub trait Component: Any + AnyComponent {
3232
/// Process input events, return true if handled.
33-
fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult {
33+
fn handle_event(&mut self, _event: &Event, _ctx: &mut Context) -> EventResult {
3434
EventResult::Ignored(None)
3535
}
3636
// , args: ()
@@ -158,10 +158,10 @@ impl Compositor {
158158
Some(self.layers.remove(idx))
159159
}
160160

161-
pub fn handle_event(&mut self, event: Event, cx: &mut Context) -> bool {
161+
pub fn handle_event(&mut self, event: &Event, cx: &mut Context) -> bool {
162162
// If it is a key event and a macro is being recorded, push the key event to the recording.
163163
if let (Event::Key(key), Some((_, keys))) = (event, &mut cx.editor.macro_recording) {
164-
keys.push(key.into());
164+
keys.push((*key).into());
165165
}
166166

167167
let mut callbacks = Vec::new();

helix-term/src/ui/completion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ impl Completion {
295295
}
296296

297297
impl Component for Completion {
298-
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
298+
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
299299
// let the Editor handle Esc instead
300300
if let Event::Key(KeyEvent {
301301
code: KeyCode::Esc, ..

helix-term/src/ui/editor.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,7 @@ impl EditorView {
915915
impl EditorView {
916916
fn handle_mouse_event(
917917
&mut self,
918-
event: MouseEvent,
918+
event: &MouseEvent,
919919
cxt: &mut commands::Context,
920920
) -> EventResult {
921921
let config = cxt.editor.config();
@@ -925,7 +925,7 @@ impl EditorView {
925925
column,
926926
modifiers,
927927
..
928-
} = event;
928+
} = *event;
929929

930930
let pos_and_view = |editor: &Editor, row, column| {
931931
editor.tree.views().find_map(|(view, _focus)| {
@@ -1096,7 +1096,7 @@ impl EditorView {
10961096
impl Component for EditorView {
10971097
fn handle_event(
10981098
&mut self,
1099-
event: Event,
1099+
event: &Event,
11001100
context: &mut crate::compositor::Context,
11011101
) -> EventResult {
11021102
let mut cx = commands::Context {
@@ -1109,6 +1109,20 @@ impl Component for EditorView {
11091109
};
11101110

11111111
match event {
1112+
Event::Paste(contents) => {
1113+
let config = cx.editor.config();
1114+
let (view, doc) = current!(cx.editor);
1115+
// Repeat Count?
1116+
commands::paste_bracketed_value(contents.clone(), doc, view, 1);
1117+
view.ensure_cursor_in_view(doc, config.scrolloff);
1118+
1119+
// Store a history state if not in insert mode. This also takes care of
1120+
// committing changes when leaving insert mode.
1121+
if doc.mode() != Mode::Insert {
1122+
doc.append_changes_to_history(view.id);
1123+
}
1124+
EventResult::Consumed(None)
1125+
}
11121126
Event::FocusGained | Event::FocusLost => unreachable!(
11131127
"Focus events should not be emitted as we're not enabling Focus capture"
11141128
),
@@ -1119,7 +1133,7 @@ impl Component for EditorView {
11191133
}
11201134
Event::Key(key) => {
11211135
cx.editor.reset_idle_timer();
1122-
let mut key = KeyEvent::from(key);
1136+
let mut key = KeyEvent::from(*key);
11231137
canonicalize_key(&mut key);
11241138

11251139
// clear status

helix-term/src/ui/menu.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,9 @@ impl<T: Item> Menu<T> {
226226
use super::PromptEvent as MenuEvent;
227227

228228
impl<T: Item + 'static> Component for Menu<T> {
229-
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
229+
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
230230
let event = match event {
231-
Event::Key(event) => event,
231+
Event::Key(event) => *event,
232232
_ => return EventResult::Ignored(None),
233233
};
234234

helix-term/src/ui/overlay.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ impl<T: Component + 'static> Component for Overlay<T> {
6262
Some((width, height))
6363
}
6464

65-
fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult {
65+
fn handle_event(&mut self, event: &Event, ctx: &mut Context) -> EventResult {
6666
self.content.handle_event(event, ctx)
6767
}
6868

helix-term/src/ui/picker.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ impl<T: Item + 'static> Component for FilePicker<T> {
261261
}
262262
}
263263

264-
fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult {
264+
fn handle_event(&mut self, event: &Event, ctx: &mut Context) -> EventResult {
265265
// TODO: keybinds for scrolling preview
266266
self.picker.handle_event(event, ctx)
267267
}
@@ -477,6 +477,14 @@ impl<T: Item> Picker<T> {
477477
pub fn toggle_preview(&mut self) {
478478
self.show_preview = !self.show_preview;
479479
}
480+
481+
fn prompt_handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
482+
if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
483+
// TODO: recalculate only if pattern changed
484+
self.score();
485+
}
486+
EventResult::Consumed(None)
487+
}
480488
}
481489

482490
// process:
@@ -490,9 +498,10 @@ impl<T: Item + 'static> Component for Picker<T> {
490498
Some(viewport)
491499
}
492500

493-
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
501+
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
494502
let key_event = match event {
495-
Event::Key(event) => event,
503+
Event::Key(event) => *event,
504+
Event::Paste(..) => return self.prompt_handle_event(event, cx),
496505
Event::Resize(..) => return EventResult::Consumed(None),
497506
_ => return EventResult::Ignored(None),
498507
};
@@ -549,10 +558,7 @@ impl<T: Item + 'static> Component for Picker<T> {
549558
self.toggle_preview();
550559
}
551560
_ => {
552-
if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
553-
// TODO: recalculate only if pattern changed
554-
self.score();
555-
}
561+
self.prompt_handle_event(event, cx);
556562
}
557563
}
558564

helix-term/src/ui/popup.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,9 @@ impl<T: Component> Popup<T> {
139139
}
140140

141141
impl<T: Component> Component for Popup<T> {
142-
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
142+
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
143143
let key = match event {
144-
Event::Key(event) => event,
144+
Event::Key(event) => *event,
145145
Event::Resize(_, _) => {
146146
// TODO: calculate inner area, call component's handle_event with that area
147147
return EventResult::Ignored(None);

helix-term/src/ui/prompt.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,9 +467,13 @@ impl Prompt {
467467
}
468468

469469
impl Component for Prompt {
470-
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
470+
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
471471
let event = match event {
472-
Event::Key(event) => event,
472+
Event::Paste(data) => {
473+
self.insert_str(data);
474+
return EventResult::Consumed(None);
475+
}
476+
Event::Key(event) => *event,
473477
Event::Resize(..) => return EventResult::Consumed(None),
474478
_ => return EventResult::Ignored(None),
475479
};

helix-tui/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ default = ["crossterm"]
1919
bitflags = "1.3"
2020
cassowary = "0.3"
2121
unicode-segmentation = "1.9"
22-
crossterm = { version = "0.24", git = "https://github.com/crossterm-rs/crossterm.git", optional = true }
22+
crossterm = { version = "0.24.0", optional = true, git = "https://github.com/groves/crossterm.git", branch = "add-bracketed-paste" }
2323
serde = { version = "1", "optional" = true, features = ["derive"]}
2424
helix-view = { version = "0.6", path = "../helix-view", features = ["term"] }
2525
helix-core = { version = "0.6", path = "../helix-core" }

helix-view/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ anyhow = "1"
1919
helix-core = { version = "0.6", path = "../helix-core" }
2020
helix-lsp = { version = "0.6", path = "../helix-lsp" }
2121
helix-dap = { version = "0.6", path = "../helix-dap" }
22-
crossterm = { version = "0.24", git = "https://github.com/crossterm-rs/crossterm.git", optional = true }
22+
crossterm = { version = "0.24.0", optional = true, git = "https://github.com/groves/crossterm.git", branch = "add-bracketed-paste" }
2323

2424
# Conversion traits
2525
once_cell = "1.13"

0 commit comments

Comments
 (0)