This change brings them in-line with the default settings of rust-analyzer. Originally, I disliked this style, but by now, I've grown used to it and prefer it slightly. Also, I like staying on the default path since I usually don't need to check the imports to see if rust-analyzer messed anything up (except for omitting the self:: when importing modules defined in the current module, grr :D). I've also come around to the idea of trailing mod definitions instead of putting them at the top: I would also put module definitions that contain code instead of referencing another file below the imports. It feels weird to have code above imports. So it just makes sense to do the same for all types of mod definitions. If rustfmt ever gains stable support for grouping imports, I'll probably use that instead.
102 lines
2.4 KiB
Rust
102 lines
2.4 KiB
Rust
use std::{io, sync::Arc};
|
|
|
|
pub use cove_macro::KeyGroup;
|
|
use crossterm::event::{Event, KeyEvent, KeyEventKind};
|
|
use parking_lot::FairMutex;
|
|
use toss::{Frame, Terminal, WidthDb};
|
|
|
|
pub use crate::keys::*;
|
|
|
|
mod keys;
|
|
|
|
pub struct KeyBindingInfo<'a> {
|
|
pub name: &'static str,
|
|
pub binding: &'a KeyBinding,
|
|
pub description: &'static str,
|
|
}
|
|
|
|
/// A group of related key bindings.
|
|
pub trait KeyGroup {
|
|
const DESCRIPTION: &'static str;
|
|
|
|
fn bindings(&self) -> Vec<KeyBindingInfo<'_>>;
|
|
}
|
|
|
|
pub struct KeyGroupInfo<'a> {
|
|
pub name: &'static str,
|
|
pub description: &'static str,
|
|
pub bindings: Vec<KeyBindingInfo<'a>>,
|
|
}
|
|
|
|
impl<'a> KeyGroupInfo<'a> {
|
|
pub fn new<G: KeyGroup>(name: &'static str, group: &'a G) -> Self {
|
|
Self {
|
|
name,
|
|
description: G::DESCRIPTION,
|
|
bindings: group.bindings(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct InputEvent<'a> {
|
|
event: Event,
|
|
terminal: &'a mut Terminal,
|
|
crossterm_lock: Arc<FairMutex<()>>,
|
|
}
|
|
|
|
impl<'a> InputEvent<'a> {
|
|
pub fn new(
|
|
event: Event,
|
|
terminal: &'a mut Terminal,
|
|
crossterm_lock: Arc<FairMutex<()>>,
|
|
) -> Self {
|
|
Self {
|
|
event,
|
|
terminal,
|
|
crossterm_lock,
|
|
}
|
|
}
|
|
|
|
/// If the current event represents a key press, returns the [`KeyEvent`]
|
|
/// associated with that key press.
|
|
pub fn key_event(&self) -> Option<KeyEvent> {
|
|
if let Event::Key(event) = &self.event {
|
|
if matches!(event.kind, KeyEventKind::Press | KeyEventKind::Repeat) {
|
|
return Some(*event);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
pub fn paste_event(&self) -> Option<&str> {
|
|
match &self.event {
|
|
Event::Paste(string) => Some(string),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn matches(&self, binding: &KeyBinding) -> bool {
|
|
match self.key_event() {
|
|
Some(event) => binding.matches(event),
|
|
None => false,
|
|
}
|
|
}
|
|
|
|
pub fn frame(&mut self) -> &mut Frame {
|
|
self.terminal.frame()
|
|
}
|
|
|
|
pub fn widthdb(&mut self) -> &mut WidthDb {
|
|
self.terminal.widthdb()
|
|
}
|
|
|
|
pub fn prompt(&mut self, initial_text: &str) -> io::Result<String> {
|
|
let guard = self.crossterm_lock.lock();
|
|
self.terminal.suspend().expect("failed to suspend");
|
|
let content = edit::edit(initial_text);
|
|
self.terminal.unsuspend().expect("fauled to unsuspend");
|
|
drop(guard);
|
|
|
|
content
|
|
}
|
|
}
|