mirror of
https://github.com/nushell/reedline.git
synced 2024-09-11 15:55:50 +03:00
Update crossterm to version 0.26.1 (#560)
* update crossterm to 0.26.1 * add event_listener_kitty_proto example * add comment * remove trait
This commit is contained in:
parent
89cb811838
commit
27f4417191
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -187,9 +187,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
version = "0.24.0"
|
version = "0.26.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab9f7409c70a38a56216480fba371ee460207dd8926ccf5b4160591759559170"
|
checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
|
@ -15,7 +15,7 @@ doctest = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
clipboard = { version = "0.5.0", optional = true }
|
clipboard = { version = "0.5.0", optional = true }
|
||||||
crossterm = { version = "0.24.0", features = ["serde"] }
|
crossterm = { version = "0.26.1", features = ["serde"] }
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.3"
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
@ -12,7 +12,7 @@ use {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crossterm::cursor::CursorShape;
|
use crossterm::cursor::SetCursorStyle;
|
||||||
use reedline::CursorConfig;
|
use reedline::CursorConfig;
|
||||||
#[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))]
|
#[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))]
|
||||||
use reedline::FileBackedHistory;
|
use reedline::FileBackedHistory;
|
||||||
@ -61,8 +61,8 @@ fn main() -> Result<()> {
|
|||||||
let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
|
let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
|
||||||
|
|
||||||
let cursor_config = CursorConfig {
|
let cursor_config = CursorConfig {
|
||||||
vi_insert: Some(CursorShape::Line),
|
vi_insert: Some(SetCursorStyle::BlinkingBar),
|
||||||
vi_normal: Some(CursorShape::Block),
|
vi_normal: Some(SetCursorStyle::SteadyBlock),
|
||||||
emacs: None,
|
emacs: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,11 +38,17 @@ fn print_events_helper() -> Result<()> {
|
|||||||
// It's guaranteed that read() wont block if `poll` returns `Ok(true)`
|
// It's guaranteed that read() wont block if `poll` returns `Ok(true)`
|
||||||
let event = crossterm::event::read()?;
|
let event = crossterm::event::read()?;
|
||||||
|
|
||||||
if let Event::Key(KeyEvent { code, modifiers }) = event {
|
if let Event::Key(KeyEvent {
|
||||||
|
code,
|
||||||
|
modifiers,
|
||||||
|
kind,
|
||||||
|
state,
|
||||||
|
}) = event
|
||||||
|
{
|
||||||
match code {
|
match code {
|
||||||
KeyCode::Char(c) => {
|
KeyCode::Char(c) => {
|
||||||
println!(
|
println!(
|
||||||
"Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}\r",
|
"Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; state {state:?}\r",
|
||||||
c,
|
c,
|
||||||
u32::from(c),
|
u32::from(c),
|
||||||
modifiers,
|
modifiers,
|
||||||
@ -51,7 +57,7 @@ fn print_events_helper() -> Result<()> {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!(
|
println!(
|
||||||
"Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}\r"
|
"Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; Kind {kind:?}; state {state:?}\r"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
104
examples/event_listener_kitty_proto.rs
Normal file
104
examples/event_listener_kitty_proto.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
use crossterm::event::{
|
||||||
|
KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
|
||||||
|
};
|
||||||
|
use crossterm::execute;
|
||||||
|
use {
|
||||||
|
crossterm::{
|
||||||
|
event::{poll, Event, KeyCode, KeyEvent},
|
||||||
|
terminal, Result,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
io::{stdout, Write},
|
||||||
|
time::Duration,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
println!("Ready to print events (Abort with ESC):");
|
||||||
|
print_events()?;
|
||||||
|
println!();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// **For debugging purposes only:** Track the terminal events observed by [`Reedline`] and print them.
|
||||||
|
pub fn print_events() -> Result<()> {
|
||||||
|
stdout().flush()?;
|
||||||
|
terminal::enable_raw_mode()?;
|
||||||
|
// enable kitty protocol
|
||||||
|
//
|
||||||
|
// Note that, currently, only the following support this protocol:
|
||||||
|
// * [kitty terminal](https://sw.kovidgoyal.net/kitty/)
|
||||||
|
// * [foot terminal](https://codeberg.org/dnkl/foot/issues/319)
|
||||||
|
// * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html)
|
||||||
|
// * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131)
|
||||||
|
// * [neovim text editor](https://github.com/neovim/neovim/pull/18181)
|
||||||
|
// * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103)
|
||||||
|
// * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138)
|
||||||
|
//
|
||||||
|
// Refer to https://sw.kovidgoyal.net/kitty/keyboard-protocol/ if you're curious.
|
||||||
|
execute!(
|
||||||
|
stdout(),
|
||||||
|
PushKeyboardEnhancementFlags(
|
||||||
|
KeyboardEnhancementFlags::REPORT_EVENT_TYPES
|
||||||
|
| KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let result = print_events_helper();
|
||||||
|
execute!(stdout(), PopKeyboardEnhancementFlags).unwrap();
|
||||||
|
terminal::disable_raw_mode()?;
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// this fn is totally ripped off from crossterm's examples
|
||||||
|
// it's really a diagnostic routine to see if crossterm is
|
||||||
|
// even seeing the events. if you press a key and no events
|
||||||
|
// are printed, it's a good chance your terminal is eating
|
||||||
|
// those events.
|
||||||
|
fn print_events_helper() -> Result<()> {
|
||||||
|
loop {
|
||||||
|
// Wait up to 5s for another event
|
||||||
|
if poll(Duration::from_millis(5_000))? {
|
||||||
|
// It's guaranteed that read() wont block if `poll` returns `Ok(true)`
|
||||||
|
let event = crossterm::event::read()?;
|
||||||
|
|
||||||
|
if let Event::Key(KeyEvent {
|
||||||
|
code,
|
||||||
|
modifiers,
|
||||||
|
kind,
|
||||||
|
state,
|
||||||
|
}) = event
|
||||||
|
{
|
||||||
|
match code {
|
||||||
|
KeyCode::Char(c) => {
|
||||||
|
println!(
|
||||||
|
"Char: {} code: {:#08x}; Modifier {:?}; Flags {:#08b}; Kind {kind:?}; state {state:?}\r",
|
||||||
|
c,
|
||||||
|
u32::from(c),
|
||||||
|
modifiers,
|
||||||
|
modifiers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"Keycode: {code:?}; Modifier {modifiers:?}; Flags {modifiers:#08b}; Kind {kind:?}; state {state:?}\r"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Event::{event:?}\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
// hit the esc key to git out
|
||||||
|
if event == Event::Key(KeyCode::Esc.into()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Timeout expired, no event for 5s
|
||||||
|
println!("Waiting for you to type...\r");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
use crate::{enums::ReedlineEvent, PromptEditMode};
|
use crate::{
|
||||||
pub use crossterm::event::Event;
|
enums::{ReedlineEvent, ReedlineRawEvent},
|
||||||
|
PromptEditMode,
|
||||||
|
};
|
||||||
|
|
||||||
/// Define the style of parsing for the edit events
|
/// Define the style of parsing for the edit events
|
||||||
/// Available default options:
|
/// Available default options:
|
||||||
@ -7,7 +9,7 @@ pub use crossterm::event::Event;
|
|||||||
/// - Vi
|
/// - Vi
|
||||||
pub trait EditMode: Send {
|
pub trait EditMode: Send {
|
||||||
/// Translate the given user input event into what the `LineEditor` understands
|
/// Translate the given user input event into what the `LineEditor` understands
|
||||||
fn parse_event(&mut self, event: Event) -> ReedlineEvent;
|
fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent;
|
||||||
|
|
||||||
/// What to display in the prompt indicator
|
/// What to display in the prompt indicator
|
||||||
fn edit_mode(&self) -> PromptEditMode;
|
fn edit_mode(&self) -> PromptEditMode;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use crossterm::cursor::CursorShape;
|
use crossterm::cursor::SetCursorStyle;
|
||||||
|
|
||||||
/// Maps cursor shapes to each edit mode (emacs, vi normal & vi insert).
|
/// Maps cursor shapes to each edit mode (emacs, vi normal & vi insert).
|
||||||
/// If any of the fields is `None`, the cursor won't get changed by Reedline for that mode.
|
/// If any of the fields is `None`, the cursor won't get changed by Reedline for that mode.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CursorConfig {
|
pub struct CursorConfig {
|
||||||
/// The cursor to be used when in vi insert mode
|
/// The cursor to be used when in vi insert mode
|
||||||
pub vi_insert: Option<CursorShape>,
|
pub vi_insert: Option<SetCursorStyle>,
|
||||||
/// The cursor to be used when in vi normal mode
|
/// The cursor to be used when in vi normal mode
|
||||||
pub vi_normal: Option<CursorShape>,
|
pub vi_normal: Option<SetCursorStyle>,
|
||||||
/// The cursor to be used when in emacs mode
|
/// The cursor to be used when in emacs mode
|
||||||
pub emacs: Option<CursorShape>,
|
pub emacs: Option<SetCursorStyle>,
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
EditMode,
|
EditMode,
|
||||||
},
|
},
|
||||||
enums::{EditCommand, ReedlineEvent},
|
enums::{EditCommand, ReedlineEvent, ReedlineRawEvent},
|
||||||
PromptEditMode,
|
PromptEditMode,
|
||||||
};
|
};
|
||||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
|
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
|
||||||
@ -108,9 +108,11 @@ impl Default for Emacs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EditMode for Emacs {
|
impl EditMode for Emacs {
|
||||||
fn parse_event(&mut self, event: Event) -> ReedlineEvent {
|
fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent {
|
||||||
match event {
|
match event.into() {
|
||||||
Event::Key(KeyEvent { code, modifiers }) => match (modifiers, code) {
|
Event::Key(KeyEvent {
|
||||||
|
code, modifiers, ..
|
||||||
|
}) => match (modifiers, code) {
|
||||||
(modifier, KeyCode::Char(c)) => {
|
(modifier, KeyCode::Char(c)) => {
|
||||||
// Note. The modifier can also be a combination of modifiers, for
|
// Note. The modifier can also be a combination of modifiers, for
|
||||||
// example:
|
// example:
|
||||||
@ -152,6 +154,9 @@ impl EditMode for Emacs {
|
|||||||
|
|
||||||
Event::Mouse(_) => ReedlineEvent::Mouse,
|
Event::Mouse(_) => ReedlineEvent::Mouse,
|
||||||
Event::Resize(width, height) => ReedlineEvent::Resize(width, height),
|
Event::Resize(width, height) => ReedlineEvent::Resize(width, height),
|
||||||
|
Event::FocusGained => ReedlineEvent::None,
|
||||||
|
Event::FocusLost => ReedlineEvent::None,
|
||||||
|
Event::Paste(_) => ReedlineEvent::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,10 +180,11 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ctrl_l_leads_to_clear_screen_event() {
|
fn ctrl_l_leads_to_clear_screen_event() {
|
||||||
let mut emacs = Emacs::default();
|
let mut emacs = Emacs::default();
|
||||||
let ctrl_l = Event::Key(KeyEvent {
|
let ctrl_l = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::CONTROL,
|
KeyCode::Char('l'),
|
||||||
code: KeyCode::Char('l'),
|
KeyModifiers::CONTROL,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = emacs.parse_event(ctrl_l);
|
let result = emacs.parse_event(ctrl_l);
|
||||||
|
|
||||||
assert_eq!(result, ReedlineEvent::ClearScreen);
|
assert_eq!(result, ReedlineEvent::ClearScreen);
|
||||||
@ -194,10 +200,11 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut emacs = Emacs::new(keybindings);
|
let mut emacs = Emacs::new(keybindings);
|
||||||
let ctrl_l = Event::Key(KeyEvent {
|
let ctrl_l = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::CONTROL,
|
KeyCode::Char('l'),
|
||||||
code: KeyCode::Char('l'),
|
KeyModifiers::CONTROL,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = emacs.parse_event(ctrl_l);
|
let result = emacs.parse_event(ctrl_l);
|
||||||
|
|
||||||
assert_eq!(result, ReedlineEvent::HistoryHintComplete);
|
assert_eq!(result, ReedlineEvent::HistoryHintComplete);
|
||||||
@ -206,10 +213,11 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn inserting_character_works() {
|
fn inserting_character_works() {
|
||||||
let mut emacs = Emacs::default();
|
let mut emacs = Emacs::default();
|
||||||
let l = Event::Key(KeyEvent {
|
let l = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::NONE,
|
KeyCode::Char('l'),
|
||||||
code: KeyCode::Char('l'),
|
KeyModifiers::NONE,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = emacs.parse_event(l);
|
let result = emacs.parse_event(l);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -222,10 +230,11 @@ mod test {
|
|||||||
fn inserting_capital_character_works() {
|
fn inserting_capital_character_works() {
|
||||||
let mut emacs = Emacs::default();
|
let mut emacs = Emacs::default();
|
||||||
|
|
||||||
let uppercase_l = Event::Key(KeyEvent {
|
let uppercase_l = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::SHIFT,
|
KeyCode::Char('l'),
|
||||||
code: KeyCode::Char('l'),
|
KeyModifiers::SHIFT,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = emacs.parse_event(uppercase_l);
|
let result = emacs.parse_event(uppercase_l);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -239,10 +248,11 @@ mod test {
|
|||||||
let keybindings = Keybindings::default();
|
let keybindings = Keybindings::default();
|
||||||
|
|
||||||
let mut emacs = Emacs::new(keybindings);
|
let mut emacs = Emacs::new(keybindings);
|
||||||
let ctrl_l = Event::Key(KeyEvent {
|
let ctrl_l = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::CONTROL,
|
KeyCode::Char('l'),
|
||||||
code: KeyCode::Char('l'),
|
KeyModifiers::CONTROL,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = emacs.parse_event(ctrl_l);
|
let result = emacs.parse_event(ctrl_l);
|
||||||
|
|
||||||
assert_eq!(result, ReedlineEvent::None);
|
assert_eq!(result, ReedlineEvent::None);
|
||||||
@ -252,10 +262,11 @@ mod test {
|
|||||||
fn inserting_capital_character_for_non_ascii_remains_as_is() {
|
fn inserting_capital_character_for_non_ascii_remains_as_is() {
|
||||||
let mut emacs = Emacs::default();
|
let mut emacs = Emacs::default();
|
||||||
|
|
||||||
let uppercase_l = Event::Key(KeyEvent {
|
let uppercase_l = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::SHIFT,
|
KeyCode::Char('😀'),
|
||||||
code: KeyCode::Char('😀'),
|
KeyModifiers::SHIFT,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = emacs.parse_event(uppercase_l);
|
let result = emacs.parse_event(uppercase_l);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -4,7 +4,7 @@ mod emacs;
|
|||||||
mod keybindings;
|
mod keybindings;
|
||||||
mod vi;
|
mod vi;
|
||||||
|
|
||||||
pub use base::{EditMode, Event};
|
pub use base::EditMode;
|
||||||
pub use cursors::CursorConfig;
|
pub use cursors::CursorConfig;
|
||||||
pub use emacs::{default_emacs_keybindings, Emacs};
|
pub use emacs::{default_emacs_keybindings, Emacs};
|
||||||
pub use keybindings::Keybindings;
|
pub use keybindings::Keybindings;
|
||||||
|
@ -11,7 +11,7 @@ use self::motion::ViCharSearch;
|
|||||||
use super::EditMode;
|
use super::EditMode;
|
||||||
use crate::{
|
use crate::{
|
||||||
edit_mode::{keybindings::Keybindings, vi::parser::parse},
|
edit_mode::{keybindings::Keybindings, vi::parser::parse},
|
||||||
enums::{EditCommand, ReedlineEvent},
|
enums::{EditCommand, ReedlineEvent, ReedlineRawEvent},
|
||||||
PromptEditMode, PromptViMode,
|
PromptEditMode, PromptViMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,9 +57,11 @@ impl Vi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EditMode for Vi {
|
impl EditMode for Vi {
|
||||||
fn parse_event(&mut self, event: Event) -> ReedlineEvent {
|
fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent {
|
||||||
match event {
|
match event.into() {
|
||||||
Event::Key(KeyEvent { code, modifiers }) => match (self.mode, modifiers, code) {
|
Event::Key(KeyEvent {
|
||||||
|
code, modifiers, ..
|
||||||
|
}) => match (self.mode, modifiers, code) {
|
||||||
(ViMode::Normal, modifier, KeyCode::Char(c)) => {
|
(ViMode::Normal, modifier, KeyCode::Char(c)) => {
|
||||||
let c = c.to_ascii_lowercase();
|
let c = c.to_ascii_lowercase();
|
||||||
|
|
||||||
@ -149,6 +151,9 @@ impl EditMode for Vi {
|
|||||||
|
|
||||||
Event::Mouse(_) => ReedlineEvent::Mouse,
|
Event::Mouse(_) => ReedlineEvent::Mouse,
|
||||||
Event::Resize(width, height) => ReedlineEvent::Resize(width, height),
|
Event::Resize(width, height) => ReedlineEvent::Resize(width, height),
|
||||||
|
Event::FocusGained => ReedlineEvent::None,
|
||||||
|
Event::FocusLost => ReedlineEvent::None,
|
||||||
|
Event::Paste(_) => ReedlineEvent::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +173,11 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn esc_leads_to_normal_mode_test() {
|
fn esc_leads_to_normal_mode_test() {
|
||||||
let mut vi = Vi::default();
|
let mut vi = Vi::default();
|
||||||
let esc = Event::Key(KeyEvent {
|
let esc = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::NONE,
|
KeyCode::Esc,
|
||||||
code: KeyCode::Esc,
|
KeyModifiers::NONE,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = vi.parse_event(esc);
|
let result = vi.parse_event(esc);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -197,10 +203,11 @@ mod test {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let esc = Event::Key(KeyEvent {
|
let esc = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::NONE,
|
KeyCode::Char('e'),
|
||||||
code: KeyCode::Char('e'),
|
KeyModifiers::NONE,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = vi.parse_event(esc);
|
let result = vi.parse_event(esc);
|
||||||
|
|
||||||
assert_eq!(result, ReedlineEvent::ClearScreen);
|
assert_eq!(result, ReedlineEvent::ClearScreen);
|
||||||
@ -222,10 +229,11 @@ mod test {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let esc = Event::Key(KeyEvent {
|
let esc = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::SHIFT,
|
KeyCode::Char('$'),
|
||||||
code: KeyCode::Char('$'),
|
KeyModifiers::SHIFT,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = vi.parse_event(esc);
|
let result = vi.parse_event(esc);
|
||||||
|
|
||||||
assert_eq!(result, ReedlineEvent::CtrlD);
|
assert_eq!(result, ReedlineEvent::CtrlD);
|
||||||
@ -241,10 +249,11 @@ mod test {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let esc = Event::Key(KeyEvent {
|
let esc = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::NONE,
|
KeyCode::Char('q'),
|
||||||
code: KeyCode::Char('q'),
|
KeyModifiers::NONE,
|
||||||
});
|
)))
|
||||||
|
.unwrap();
|
||||||
let result = vi.parse_event(esc);
|
let result = vi.parse_event(esc);
|
||||||
|
|
||||||
assert_eq!(result, ReedlineEvent::None);
|
assert_eq!(result, ReedlineEvent::None);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::CursorConfig;
|
use crate::{enums::ReedlineRawEvent, CursorConfig};
|
||||||
#[cfg(feature = "bashisms")]
|
#[cfg(feature = "bashisms")]
|
||||||
use crate::{
|
use crate::{
|
||||||
history::SearchFilter,
|
history::SearchFilter,
|
||||||
@ -493,7 +493,7 @@ impl Reedline {
|
|||||||
|
|
||||||
self.repaint(prompt)?;
|
self.repaint(prompt)?;
|
||||||
|
|
||||||
let mut crossterm_events: Vec<Event> = vec![];
|
let mut crossterm_events: Vec<ReedlineRawEvent> = vec![];
|
||||||
let mut reedline_events: Vec<ReedlineEvent> = vec![];
|
let mut reedline_events: Vec<ReedlineEvent> = vec![];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -528,18 +528,29 @@ impl Reedline {
|
|||||||
enter @ Event::Key(KeyEvent {
|
enter @ Event::Key(KeyEvent {
|
||||||
code: KeyCode::Enter,
|
code: KeyCode::Enter,
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
|
..
|
||||||
}) => {
|
}) => {
|
||||||
crossterm_events.push(enter);
|
let enter = ReedlineRawEvent::convert_from(enter);
|
||||||
// Break early to check if the input is complete and
|
match enter {
|
||||||
// can be send to the hosting application. If
|
Some(enter) => {
|
||||||
// multiple complete entries are submitted, events
|
crossterm_events.push(enter);
|
||||||
// are still in the crossterm queue for us to
|
// Break early to check if the input is complete and
|
||||||
// process.
|
// can be send to the hosting application. If
|
||||||
paste_enter_state = crossterm_events.len() > EVENTS_THRESHOLD;
|
// multiple complete entries are submitted, events
|
||||||
break;
|
// are still in the crossterm queue for us to
|
||||||
|
// process.
|
||||||
|
paste_enter_state = crossterm_events.len() > EVENTS_THRESHOLD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
None => continue,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
x => {
|
x => {
|
||||||
crossterm_events.push(x);
|
let raw_event = ReedlineRawEvent::convert_from(x);
|
||||||
|
match raw_event {
|
||||||
|
Some(evt) => crossterm_events.push(evt),
|
||||||
|
None => continue,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
src/enums.rs
40
src/enums.rs
@ -1,3 +1,4 @@
|
|||||||
|
use crossterm::event::{Event, KeyEvent, KeyEventKind};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
@ -560,3 +561,42 @@ pub(crate) enum EventStatus {
|
|||||||
Inapplicable,
|
Inapplicable,
|
||||||
Exits(Signal),
|
Exits(Signal),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A simple wrapper for [crossterm::event::Event]
|
||||||
|
///
|
||||||
|
/// Which will make sure that the given event doesn't contain [KeyEventKind::Release]
|
||||||
|
/// and convert from [KeyEventKind::Repeat] to [KeyEventKind::Press]
|
||||||
|
pub struct ReedlineRawEvent {
|
||||||
|
inner: Event,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReedlineRawEvent {
|
||||||
|
/// It will return None if `evt` is released Key.
|
||||||
|
pub fn convert_from(evt: Event) -> Option<Self> {
|
||||||
|
match evt {
|
||||||
|
Event::Key(KeyEvent {
|
||||||
|
kind: KeyEventKind::Release,
|
||||||
|
..
|
||||||
|
}) => None,
|
||||||
|
Event::Key(KeyEvent {
|
||||||
|
code,
|
||||||
|
modifiers,
|
||||||
|
kind: KeyEventKind::Repeat,
|
||||||
|
state,
|
||||||
|
}) => Some(Self {
|
||||||
|
inner: Event::Key(KeyEvent {
|
||||||
|
code,
|
||||||
|
modifiers,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
|
state,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
other => Some(Self { inner: other }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consume and get crossterm event object.
|
||||||
|
pub fn into(self) -> Event {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -230,7 +230,7 @@ pub use core_editor::Editor;
|
|||||||
pub use core_editor::LineBuffer;
|
pub use core_editor::LineBuffer;
|
||||||
|
|
||||||
mod enums;
|
mod enums;
|
||||||
pub use enums::{EditCommand, ReedlineEvent, Signal, UndoBehavior};
|
pub use enums::{EditCommand, ReedlineEvent, ReedlineRawEvent, Signal, UndoBehavior};
|
||||||
|
|
||||||
mod painting;
|
mod painting;
|
||||||
pub use painting::{Painter, StyledText};
|
pub use painting::{Painter, StyledText};
|
||||||
@ -259,7 +259,7 @@ pub use prompt::{
|
|||||||
mod edit_mode;
|
mod edit_mode;
|
||||||
pub use edit_mode::{
|
pub use edit_mode::{
|
||||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||||
CursorConfig, EditMode, Emacs, Event, Keybindings, Vi,
|
CursorConfig, EditMode, Emacs, Keybindings, Vi,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod highlighter;
|
mod highlighter;
|
||||||
|
@ -186,7 +186,7 @@ impl Painter {
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(shape) = shape {
|
if let Some(shape) = shape {
|
||||||
self.stdout.queue(cursor::SetCursorShape(shape))?;
|
self.stdout.queue(shape)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.stdout.queue(cursor::Show)?;
|
self.stdout.queue(cursor::Show)?;
|
||||||
|
@ -57,6 +57,15 @@ impl Display for ReedLineCrossTermKeyCode {
|
|||||||
KeyCode::Char(_) => write!(f, "Char_<letter>"),
|
KeyCode::Char(_) => write!(f, "Char_<letter>"),
|
||||||
KeyCode::Null => write!(f, "Null"),
|
KeyCode::Null => write!(f, "Null"),
|
||||||
KeyCode::Esc => write!(f, "Esc"),
|
KeyCode::Esc => write!(f, "Esc"),
|
||||||
|
KeyCode::CapsLock => write!(f, "CapsLock"),
|
||||||
|
KeyCode::ScrollLock => write!(f, "ScrollLock"),
|
||||||
|
KeyCode::NumLock => write!(f, "NumLock"),
|
||||||
|
KeyCode::PrintScreen => write!(f, "PrintScreen"),
|
||||||
|
KeyCode::Pause => write!(f, "Pause"),
|
||||||
|
KeyCode::Menu => write!(f, "Menu"),
|
||||||
|
KeyCode::KeypadBegin => write!(f, "KeypadBegin"),
|
||||||
|
KeyCode::Media(_) => write!(f, "Media<media>"),
|
||||||
|
KeyCode::Modifier(_) => write!(f, "Modifier<modifier>"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user