aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src
diff options
context:
space:
mode:
authorCharlie Groves2022-08-29 00:48:49 +0000
committerGitHub2022-08-29 00:48:49 +0000
commitf38ede8631b083c1c74d31b7658ad162d31c3972 (patch)
tree58e93b5bf7a330b9d60a2c1c5a52d67fcd32971f /helix-term/src
parent51b62230da81913564692482d8f365e27d6f6cec (diff)
Add bracketed paste (#3233)
Diffstat (limited to 'helix-term/src')
-rw-r--r--helix-term/src/application.rs22
-rw-r--r--helix-term/src/commands.rs41
-rw-r--r--helix-term/src/compositor.rs6
-rw-r--r--helix-term/src/ui/completion.rs2
-rw-r--r--helix-term/src/ui/editor.rs23
-rw-r--r--helix-term/src/ui/menu.rs4
-rw-r--r--helix-term/src/ui/overlay.rs2
-rw-r--r--helix-term/src/ui/picker.rs20
-rw-r--r--helix-term/src/ui/popup.rs4
-rw-r--r--helix-term/src/ui/prompt.rs8
10 files changed, 79 insertions, 53 deletions
diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index 21be7db0..3124beb0 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -29,7 +29,10 @@ use std::{
use anyhow::{Context, Error};
use crossterm::{
- event::{DisableMouseCapture, EnableMouseCapture, Event as CrosstermEvent},
+ event::{
+ DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture,
+ Event as CrosstermEvent,
+ },
execute, terminal,
tty::IsTty,
};
@@ -425,14 +428,13 @@ impl Application {
scroll: None,
};
// Handle key events
- let should_redraw = match event {
- Ok(CrosstermEvent::Resize(width, height)) => {
+ let should_redraw = match event.unwrap() {
+ CrosstermEvent::Resize(width, height) => {
self.compositor.resize(width, height);
self.compositor
- .handle_event(Event::Resize(width, height), &mut cx)
+ .handle_event(&Event::Resize(width, height), &mut cx)
}
- Ok(event) => self.compositor.handle_event(event.into(), &mut cx),
- Err(x) => panic!("{}", x),
+ event => self.compositor.handle_event(&event.into(), &mut cx),
};
if should_redraw && !self.editor.should_close() {
@@ -788,7 +790,7 @@ impl Application {
async fn claim_term(&mut self) -> Result<(), Error> {
terminal::enable_raw_mode()?;
let mut stdout = stdout();
- execute!(stdout, terminal::EnterAlternateScreen)?;
+ execute!(stdout, terminal::EnterAlternateScreen, EnableBracketedPaste)?;
execute!(stdout, terminal::Clear(terminal::ClearType::All))?;
if self.config.load().editor.mouse {
execute!(stdout, EnableMouseCapture)?;
@@ -821,7 +823,11 @@ impl Application {
// probably not a good idea to `unwrap()` inside a panic handler.
// So we just ignore the `Result`s.
let _ = execute!(std::io::stdout(), DisableMouseCapture);
- let _ = execute!(std::io::stdout(), terminal::LeaveAlternateScreen);
+ let _ = execute!(
+ std::io::stdout(),
+ terminal::LeaveAlternateScreen,
+ DisableBracketedPaste
+ );
let _ = terminal::disable_raw_mode();
hook(info);
}));
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 16f7e601..ffcccec3 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -3369,13 +3369,7 @@ enum Paste {
Cursor,
}
-fn paste_impl(
- values: &[String],
- doc: &mut Document,
- view: &View,
- action: Paste,
- count: usize,
-) -> Option<Transaction> {
+fn paste_impl(values: &[String], doc: &mut Document, view: &View, action: Paste, count: usize) {
let repeat = std::iter::repeat(
values
.last()
@@ -3418,8 +3412,17 @@ fn paste_impl(
};
(pos, pos, values.next())
});
+ doc.apply(&transaction, view.id);
+}
- Some(transaction)
+pub(crate) fn paste_bracketed_value(cx: &mut Context, contents: String) {
+ let count = cx.count();
+ let (view, doc) = current!(cx.editor);
+ let paste = match doc.mode {
+ Mode::Insert | Mode::Select => Paste::Cursor,
+ Mode::Normal => Paste::Before,
+ };
+ paste_impl(&[contents], doc, view, paste, count);
}
fn paste_clipboard_impl(
@@ -3429,18 +3432,11 @@ fn paste_clipboard_impl(
count: usize,
) -> anyhow::Result<()> {
let (view, doc) = current!(editor);
-
- match editor
- .clipboard_provider
- .get_contents(clipboard_type)
- .map(|contents| paste_impl(&[contents], doc, view, action, count))
- {
- Ok(Some(transaction)) => {
- doc.apply(&transaction, view.id);
- doc.append_changes_to_history(view.id);
+ match editor.clipboard_provider.get_contents(clipboard_type) {
+ Ok(contents) => {
+ paste_impl(&[contents], doc, view, action, count);
Ok(())
}
- Ok(None) => Ok(()),
Err(e) => Err(e.context("Couldn't get system clipboard contents")),
}
}
@@ -3553,11 +3549,8 @@ fn paste(cx: &mut Context, pos: Paste) {
let (view, doc) = current!(cx.editor);
let registers = &mut cx.editor.registers;
- if let Some(transaction) = registers
- .read(reg_name)
- .and_then(|values| paste_impl(values, doc, view, pos, count))
- {
- doc.apply(&transaction, view.id);
+ if let Some(values) = registers.read(reg_name) {
+ paste_impl(values, doc, view, pos, count);
}
}
@@ -4849,7 +4842,7 @@ fn replay_macro(cx: &mut Context) {
cx.callback = Some(Box::new(move |compositor, cx| {
for _ in 0..count {
for &key in keys.iter() {
- compositor.handle_event(compositor::Event::Key(key), cx);
+ compositor.handle_event(&compositor::Event::Key(key), cx);
}
}
// The macro under replay is cleared at the end of the callback, not in the
diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs
index bda38c59..c0898dae 100644
--- a/helix-term/src/compositor.rs
+++ b/helix-term/src/compositor.rs
@@ -29,7 +29,7 @@ pub struct Context<'a> {
pub trait Component: Any + AnyComponent {
/// Process input events, return true if handled.
- fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult {
+ fn handle_event(&mut self, _event: &Event, _ctx: &mut Context) -> EventResult {
EventResult::Ignored(None)
}
// , args: ()
@@ -157,10 +157,10 @@ impl Compositor {
Some(self.layers.remove(idx))
}
- pub fn handle_event(&mut self, event: Event, cx: &mut Context) -> bool {
+ pub fn handle_event(&mut self, event: &Event, cx: &mut Context) -> bool {
// If it is a key event and a macro is being recorded, push the key event to the recording.
if let (Event::Key(key), Some((_, keys))) = (event, &mut cx.editor.macro_recording) {
- keys.push(key);
+ keys.push(*key);
}
let mut callbacks = Vec::new();
diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs
index 6a743632..87913a8c 100644
--- a/helix-term/src/ui/completion.rs
+++ b/helix-term/src/ui/completion.rs
@@ -298,7 +298,7 @@ impl Completion {
}
impl Component for Completion {
- fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
+ fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
// let the Editor handle Esc instead
if let Event::Key(KeyEvent {
code: KeyCode::Esc, ..
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 60cab905..7326b70d 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -936,7 +936,7 @@ impl EditorView {
impl EditorView {
fn handle_mouse_event(
&mut self,
- event: MouseEvent,
+ event: &MouseEvent,
cxt: &mut commands::Context,
) -> EventResult {
let config = cxt.editor.config();
@@ -946,7 +946,7 @@ impl EditorView {
column,
modifiers,
..
- } = event;
+ } = *event;
let pos_and_view = |editor: &Editor, row, column| {
editor.tree.views().find_map(|(view, _focus)| {
@@ -1115,7 +1115,7 @@ impl EditorView {
impl Component for EditorView {
fn handle_event(
&mut self,
- event: Event,
+ event: &Event,
context: &mut crate::compositor::Context,
) -> EventResult {
let mut cx = commands::Context {
@@ -1128,6 +1128,23 @@ impl Component for EditorView {
};
match event {
+ Event::Paste(contents) => {
+ cx.count = cx.editor.count;
+ commands::paste_bracketed_value(&mut cx, contents.clone());
+ cx.editor.count = None;
+
+ let config = cx.editor.config();
+ let (view, doc) = current!(cx.editor);
+ view.ensure_cursor_in_view(doc, config.scrolloff);
+
+ // Store a history state if not in insert mode. Otherwise wait till we exit insert
+ // to include any edits to the paste in the history state.
+ if doc.mode() != Mode::Insert {
+ doc.append_changes_to_history(view.id);
+ }
+
+ EventResult::Consumed(None)
+ }
Event::Resize(_width, _height) => {
// Ignore this event, we handle resizing just before rendering to screen.
// Handling it here but not re-rendering will cause flashing
diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs
index ce51ecbc..1d247b1a 100644
--- a/helix-term/src/ui/menu.rs
+++ b/helix-term/src/ui/menu.rs
@@ -225,9 +225,9 @@ impl<T: Item> Menu<T> {
use super::PromptEvent as MenuEvent;
impl<T: Item + 'static> Component for Menu<T> {
- fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
+ fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let event = match event {
- Event::Key(event) => event,
+ Event::Key(event) => *event,
_ => return EventResult::Ignored(None),
};
diff --git a/helix-term/src/ui/overlay.rs b/helix-term/src/ui/overlay.rs
index 1cd60be5..0b8a93ae 100644
--- a/helix-term/src/ui/overlay.rs
+++ b/helix-term/src/ui/overlay.rs
@@ -61,7 +61,7 @@ impl<T: Component + 'static> Component for Overlay<T> {
Some((width, height))
}
- fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult {
+ fn handle_event(&mut self, event: &Event, ctx: &mut Context) -> EventResult {
self.content.handle_event(event, ctx)
}
diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs
index 169aeadd..2878fc90 100644
--- a/helix-term/src/ui/picker.rs
+++ b/helix-term/src/ui/picker.rs
@@ -260,7 +260,7 @@ impl<T: Item + 'static> Component for FilePicker<T> {
}
}
- fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult {
+ fn handle_event(&mut self, event: &Event, ctx: &mut Context) -> EventResult {
// TODO: keybinds for scrolling preview
self.picker.handle_event(event, ctx)
}
@@ -476,6 +476,14 @@ impl<T: Item> Picker<T> {
pub fn toggle_preview(&mut self) {
self.show_preview = !self.show_preview;
}
+
+ fn prompt_handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
+ if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
+ // TODO: recalculate only if pattern changed
+ self.score();
+ }
+ EventResult::Consumed(None)
+ }
}
// process:
@@ -489,9 +497,10 @@ impl<T: Item + 'static> Component for Picker<T> {
Some(viewport)
}
- fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
+ fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let key_event = match event {
- Event::Key(event) => event,
+ Event::Key(event) => *event,
+ Event::Paste(..) => return self.prompt_handle_event(event, cx),
Event::Resize(..) => return EventResult::Consumed(None),
_ => return EventResult::Ignored(None),
};
@@ -548,10 +557,7 @@ impl<T: Item + 'static> Component for Picker<T> {
self.toggle_preview();
}
_ => {
- if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
- // TODO: recalculate only if pattern changed
- self.score();
- }
+ self.prompt_handle_event(event, cx);
}
}
diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs
index af8e53c5..3c140da4 100644
--- a/helix-term/src/ui/popup.rs
+++ b/helix-term/src/ui/popup.rs
@@ -138,9 +138,9 @@ impl<T: Component> Popup<T> {
}
impl<T: Component> Component for Popup<T> {
- fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
+ fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let key = match event {
- Event::Key(event) => event,
+ Event::Key(event) => *event,
Event::Resize(_, _) => {
// TODO: calculate inner area, call component's handle_event with that area
return EventResult::Ignored(None);
diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs
index 4cb38fb0..5e8cd1f5 100644
--- a/helix-term/src/ui/prompt.rs
+++ b/helix-term/src/ui/prompt.rs
@@ -466,9 +466,13 @@ impl Prompt {
}
impl Component for Prompt {
- fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
+ fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let event = match event {
- Event::Key(event) => event,
+ Event::Paste(data) => {
+ self.insert_str(data);
+ return EventResult::Consumed(None);
+ }
+ Event::Key(event) => *event,
Event::Resize(..) => return EventResult::Consumed(None),
_ => return EventResult::Ignored(None),
};