Skip to content

Commit

Permalink
Add bracketed paste
Browse files Browse the repository at this point in the history
  • Loading branch information
groves committed Aug 3, 2022
1 parent 0777d85 commit 9d310b1
Show file tree
Hide file tree
Showing 14 changed files with 98 additions and 56 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion helix-term/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ which = "4.2"

tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] }
crossterm = { version = "0.24", git = "https://github.com/crossterm-rs/crossterm.git", features = ["event-stream"] }
crossterm = { version = "0.24.0", features = ["event-stream"], git = "https://github.com/groves/crossterm.git", branch = "add-bracketed-paste" }
signal-hook = "0.3"
tokio-stream = "0.1"
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
Expand Down
26 changes: 15 additions & 11 deletions helix-term/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use std::{
use anyhow::{Context, Error};

use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture, Event},
event::{DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture},
execute, terminal,
tty::IsTty,
};
Expand Down Expand Up @@ -419,22 +419,22 @@ impl Application {
}
}

pub fn handle_terminal_events(&mut self, event: Result<Event, crossterm::ErrorKind>) {
pub fn handle_terminal_events(
&mut self,
event: Result<crossterm::event::Event, crossterm::ErrorKind>,
) {
let mut cx = crate::compositor::Context {
editor: &mut self.editor,
jobs: &mut self.jobs,
scroll: None,
};
// Handle key events
let should_redraw = match event {
Ok(Event::Resize(width, height)) => {
let should_redraw = match event.unwrap() {
crossterm::event::Event::Resize(width, height) => {
self.compositor.resize(width, height);

self.compositor
.handle_event(Event::Resize(width, height), &mut cx)
.handle_event(&crossterm::event::Event::Resize(width, height), &mut cx)
}
Ok(event) => self.compositor.handle_event(event, &mut cx),
Err(x) => panic!("{}", x),
event => self.compositor.handle_event(&event, &mut cx),
};

if should_redraw && !self.editor.should_close() {
Expand Down Expand Up @@ -790,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)?;
Expand Down Expand Up @@ -823,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);
}));
Expand Down
52 changes: 33 additions & 19 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3370,7 +3370,7 @@ fn paste_impl(
view: &View,
action: Paste,
count: usize,
) -> Option<Transaction> {
) -> Transaction {
let repeat = std::iter::repeat(
values
.last()
Expand All @@ -3395,7 +3395,7 @@ fn paste_impl(
let text = doc.text();
let selection = doc.selection(view.id);

let transaction = Transaction::change_by_selection(text, selection, |range| {
Transaction::change_by_selection(text, selection, |range| {
let pos = match (action, linewise) {
// paste linewise before
(Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())),
Expand All @@ -3412,9 +3412,32 @@ fn paste_impl(
(Paste::Cursor, _) => range.cursor(text.slice(..)),
};
(pos, pos, values.next())
});
})
}

Some(transaction)
pub(crate) fn paste_bracketed_value(
contents: String,
doc: &mut Document,
view: &View,
count: usize,
) {
let paste = match doc.mode {
Mode::Insert | Mode::Select => Paste::Cursor,
Mode::Normal => Paste::Before,
};
paste_external_value(contents, doc, view, paste, count);
}

fn paste_external_value(
contents: String,
doc: &mut Document,
view: &View,
action: Paste,
count: usize,
) {
let transaction = paste_impl(&[contents], doc, view, action, count);
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id);
}

fn paste_clipboard_impl(
Expand All @@ -3424,18 +3447,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_external_value(contents, doc, view, action, count);
Ok(())
}
Ok(None) => Ok(()),
Err(e) => Err(e.context("Couldn't get system clipboard contents")),
}
}
Expand Down Expand Up @@ -3548,10 +3564,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))
{
if let Some(values) = registers.read(reg_name) {
let transaction = paste_impl(values, doc, view, pos, count);
doc.apply(&transaction, view.id);
}
}
Expand Down Expand Up @@ -4836,7 +4850,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(crossterm::event::Event::Key(key.into()), cx);
compositor.handle_event(&crossterm::event::Event::Key(key.into()), cx);
}
}
// The macro under replay is cleared at the end of the callback, not in the
Expand Down
6 changes: 3 additions & 3 deletions helix-term/src/compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,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: ()
Expand Down Expand Up @@ -158,10 +158,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.into());
keys.push((*key).into());
}

let mut callbacks = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/ui/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,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, ..
Expand Down
22 changes: 18 additions & 4 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,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();
Expand All @@ -925,7 +925,7 @@ impl EditorView {
column,
modifiers,
..
} = event;
} = *event;

let pos_and_view = |editor: &Editor, row, column| {
editor.tree.views().find_map(|(view, _focus)| {
Expand Down Expand Up @@ -1096,7 +1096,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 {
Expand All @@ -1109,6 +1109,20 @@ impl Component for EditorView {
};

match event {
Event::Paste(contents) => {
let config = cx.editor.config();
let (view, doc) = current!(cx.editor);
// Repeat Count?
commands::paste_bracketed_value(contents.clone(), doc, view, 1);
view.ensure_cursor_in_view(doc, config.scrolloff);

// Store a history state if not in insert mode. This also takes care of
// committing changes when leaving insert mode.
if doc.mode() != Mode::Insert {
doc.append_changes_to_history(view.id);
}
EventResult::Consumed(None)
}
Event::FocusGained | Event::FocusLost => unreachable!(
"Focus events should not be emitted as we're not enabling Focus capture"
),
Expand All @@ -1119,7 +1133,7 @@ impl Component for EditorView {
}
Event::Key(key) => {
cx.editor.reset_idle_timer();
let mut key = KeyEvent::from(key);
let mut key = KeyEvent::from(*key);
canonicalize_key(&mut key);

// clear status
Expand Down
4 changes: 2 additions & 2 deletions helix-term/src/ui/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,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),
};

Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/ui/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,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)
}

Expand Down
20 changes: 13 additions & 7 deletions helix-term/src/ui/picker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,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)
}
Expand Down Expand Up @@ -477,6 +477,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:
Expand All @@ -490,9 +498,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),
};
Expand Down Expand Up @@ -549,10 +558,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);
}
}

Expand Down
4 changes: 2 additions & 2 deletions helix-term/src/ui/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,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);
Expand Down
8 changes: 6 additions & 2 deletions helix-term/src/ui/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,9 +467,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),
};
Expand Down
2 changes: 1 addition & 1 deletion helix-tui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ default = ["crossterm"]
bitflags = "1.3"
cassowary = "0.3"
unicode-segmentation = "1.9"
crossterm = { version = "0.24", git = "https://github.com/crossterm-rs/crossterm.git", optional = true }
crossterm = { version = "0.24.0", optional = true, git = "https://github.com/groves/crossterm.git", branch = "add-bracketed-paste" }
serde = { version = "1", "optional" = true, features = ["derive"]}
helix-view = { version = "0.6", path = "../helix-view", features = ["term"] }
helix-core = { version = "0.6", path = "../helix-core" }
2 changes: 1 addition & 1 deletion helix-view/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ anyhow = "1"
helix-core = { version = "0.6", path = "../helix-core" }
helix-lsp = { version = "0.6", path = "../helix-lsp" }
helix-dap = { version = "0.6", path = "../helix-dap" }
crossterm = { version = "0.24", git = "https://github.com/crossterm-rs/crossterm.git", optional = true }
crossterm = { version = "0.24.0", optional = true, git = "https://github.com/groves/crossterm.git", branch = "add-bracketed-paste" }

# Conversion traits
once_cell = "1.13"
Expand Down

0 comments on commit 9d310b1

Please sign in to comment.