terminal: Implement text selection and copy/paste functionality in terminal mode (#1163)

This commit is contained in:
Mark
2022-09-12 22:11:15 +02:00
committed by GitHub
parent a0b735d11a
commit bbbc3ffdc8
5 changed files with 116 additions and 16 deletions
+12
View File
@@ -27,6 +27,18 @@ command = "open_settings"
key = "ctrl+k ctrl+s"
command = "open_keyboard_shortcuts"
# --------------------------------- Terminal copy/paste ---------------------------------
[[keymaps]]
key = "ctrl+shift+c"
command = "clipboard_copy"
mode = "t"
[[keymaps]]
key = "ctrl+shift+v"
command = "clipboard_paste"
mode = "t"
# --------------------------------- Basic editing ---------------------------------------
[[keymaps]]
+1 -1
View File
@@ -34,7 +34,7 @@ pub enum Mode {
bitflags! {
pub struct Modes: u32 {
const NORMAL = 0x01;
const NORMAL = 0x1;
const INSERT = 0x2;
const VISUAL = 0x4;
const TERMINAL = 0x8;
+5 -1
View File
@@ -101,7 +101,11 @@ impl KeyMapLoader {
let modes = get_modes(toml_keymap);
// If not using modal editing, remove keymaps that only make sense in modal.
if !modal && !modes.is_empty() && !modes.contains(Modes::INSERT) {
if !modal
&& !modes.is_empty()
&& !modes.contains(Modes::INSERT)
&& !modes.contains(Modes::TERMINAL)
{
log::debug!("Keymap ignored: {}", key);
return Ok(None);
}
+15 -5
View File
@@ -11,14 +11,15 @@ use alacritty_terminal::{
Term,
};
use druid::{
keyboard_types::Key, Application, Color, Command, Env, EventCtx, ExtEventSink,
KeyEvent, Modifiers, Target, WidgetId,
keyboard_types::Key, Color, Command, Env, EventCtx, ExtEventSink, KeyEvent,
Modifiers, Target, WidgetId,
};
use hashbrown::HashMap;
use lapce_core::{
command::{EditCommand, FocusCommand},
mode::{Mode, VisualMode},
movement::{LinePosition, Movement},
register::Clipboard,
};
use lapce_rpc::terminal::TermId;
use parking_lot::Mutex;
@@ -29,6 +30,7 @@ use crate::{
},
config::{Config, LapceTheme},
data::LapceWorkspace,
document::SystemClipboard,
find::Find,
keypress::KeyPressFocus,
proxy::LapceProxy,
@@ -263,6 +265,7 @@ impl KeyPressFocus for LapceTerminalViewData {
_mods: Modifiers,
_env: &Env,
) -> CommandExecuted {
let mut clipboard = SystemClipboard {};
ctx.request_paint();
match &command.kind {
CommandKind::Move(cmd) => {
@@ -357,12 +360,19 @@ impl KeyPressFocus for LapceTerminalViewData {
let mut raw = self.terminal.raw.lock();
let term = &mut raw.term;
if let Some(content) = term.selection_to_string() {
Application::global().clipboard().put_string(content);
clipboard.put_string(content);
}
if self.terminal.mode != Mode::Terminal {
self.terminal.clear_selection(term);
}
self.terminal.clear_selection(term);
}
EditCommand::ClipboardPaste => {
if let Some(s) = Application::global().clipboard().get_string() {
if self.terminal.mode == Mode::Terminal {
let mut raw = self.terminal.raw.lock();
let term = &mut raw.term;
self.terminal.clear_selection(term);
}
if let Some(s) = clipboard.get_string() {
self.receive_char(ctx, &s);
}
}
+83 -9
View File
@@ -1,23 +1,25 @@
use std::sync::Arc;
use alacritty_terminal::{
grid::Dimensions,
index::{Direction, Side},
term::{cell::Flags, search::RegexSearch},
grid::{Dimensions, Scroll},
index::{Column, Direction, Line, Side},
selection::{Selection, SelectionType},
term::{cell::Flags, search::RegexSearch, Term},
};
use druid::{
piet::{Text, TextAttribute, TextLayoutBuilder},
BoxConstraints, Command, Data, Env, Event, EventCtx, FontWeight, LayoutCtx,
LifeCycle, LifeCycleCtx, MouseEvent, PaintCtx, Point, Rect, RenderContext, Size,
Target, UpdateCtx, Widget, WidgetExt, WidgetId, WidgetPod,
BoxConstraints, Command, Cursor, Data, Env, Event, EventCtx, FontWeight,
LayoutCtx, LifeCycle, LifeCycleCtx, MouseEvent, PaintCtx, Point, Rect,
RenderContext, Size, Target, UpdateCtx, Widget, WidgetExt, WidgetId, WidgetPod,
};
use lapce_core::mode::Mode;
use lapce_core::{mode::Mode, register::Clipboard};
use lapce_data::{
command::{LapceUICommand, LAPCE_UI_COMMAND},
config::LapceTheme,
data::{FocusArea, LapceTabData},
document::SystemClipboard,
panel::PanelKind,
terminal::{LapceTerminalData, LapceTerminalViewData},
terminal::{EventProxy, LapceTerminalData, LapceTerminalViewData},
};
use lapce_rpc::terminal::TermId;
use unicode_width::UnicodeWidthChar;
@@ -498,6 +500,32 @@ impl LapceTerminal {
panel.active = position;
}
}
fn select(
&self,
term: &mut Term<EventProxy>,
mouse_event: &MouseEvent,
ty: SelectionType,
) {
let row_size = self.height / term.screen_lines() as f64;
let col_size = self.width / term.columns() as f64;
let offset = term.grid().display_offset();
let column = Column((mouse_event.pos.x / col_size) as usize);
let line = Line((mouse_event.pos.y / row_size) as i32 - offset as i32);
match &mut term.selection {
Some(selection) => selection.update(
alacritty_terminal::index::Point { line, column },
Direction::Left,
),
None => {
term.selection = Some(Selection::new(
ty,
alacritty_terminal::index::Point { line, column },
Direction::Left,
));
}
}
}
}
impl Widget<LapceTabData> for LapceTerminal {
@@ -519,9 +547,55 @@ impl Widget<LapceTabData> for LapceTerminal {
config: data.config.clone(),
find: data.find.clone(),
};
ctx.set_cursor(&Cursor::IBeam);
match event {
Event::MouseDown(_mouse_event) => {
Event::MouseDown(mouse_event) => {
self.request_focus(ctx, data);
let terminal = data.terminal.terminals.get(&self.term_id).unwrap();
let term = &mut terminal.raw.lock().term;
if mouse_event.button.is_right() {
let mut clipboard = SystemClipboard {};
match term.selection_to_string() {
Some(selection) => {
clipboard.put_string(selection);
term.selection = None;
}
None => match clipboard.get_string() {
Some(string) => {
terminal.proxy.proxy_rpc.terminal_write(
terminal.term_id,
string.as_str(),
);
term.scroll_display(Scroll::Bottom);
}
None => {}
},
}
} else if mouse_event.button.is_left() {
match mouse_event.count {
2 => self.select(term, mouse_event, SelectionType::Semantic),
_ => {
term.selection = None;
if mouse_event.count == 3 {
self.select(term, mouse_event, SelectionType::Lines);
}
}
}
}
}
Event::MouseMove(mouse_event) => {
if mouse_event.buttons.has_left() {
let term = &mut data
.terminal
.terminals
.get(&self.term_id)
.unwrap()
.raw
.lock()
.term;
self.select(term, mouse_event, SelectionType::Simple);
ctx.request_paint();
}
}
Event::Wheel(wheel_event) => {
data.terminal