Add a basic command system instead of refering to actions directly from the config
This commit is contained in:
@ -1,10 +1,10 @@
|
||||
{
|
||||
"keybindings": {
|
||||
"Home": {
|
||||
"<q>": "Quit", // Quit the application
|
||||
"<Ctrl-d>": "Quit", // Another way to quit
|
||||
"<Ctrl-c>": "Quit", // Yet another way to quit
|
||||
"<Ctrl-z>": "Suspend" // Suspend the application
|
||||
"<q>": "/quit", // Quit the application
|
||||
"<Ctrl-d>": "/quit", // Another way to quit
|
||||
"<Ctrl-c>": "/quit", // Yet another way to quit
|
||||
"<Ctrl-z>": "/suspend" // Suspend the application
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ derive_deref = "1.1.1"
|
||||
directories = "5.0.1"
|
||||
futures = "0.3.28"
|
||||
human-panic = "1.2.0"
|
||||
inventory = "0.3"
|
||||
json5 = "0.4.1"
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2.148"
|
||||
|
@ -5,7 +5,7 @@ use serde::{
|
||||
Deserialize, Serialize,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Action {
|
||||
Tick,
|
||||
Render,
|
||||
@ -18,6 +18,7 @@ pub enum Action {
|
||||
Help,
|
||||
}
|
||||
|
||||
/*
|
||||
impl<'de> Deserialize<'de> for Action {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
@ -70,3 +71,4 @@ impl<'de> Deserialize<'de> for Action {
|
||||
deserializer.deserialize_str(ActionVisitor)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
12
src/app.rs
12
src/app.rs
@ -71,18 +71,18 @@ impl App {
|
||||
tui::Event::Resize(x, y) => action_tx.send(Action::Resize(x, y))?,
|
||||
tui::Event::Key(key) => {
|
||||
if let Some(keymap) = self.config.keybindings.get(&self.mode) {
|
||||
if let Some(action) = keymap.get(&vec![key]) {
|
||||
log::info!("Got action: {action:?}");
|
||||
action_tx.send(action.clone())?;
|
||||
if let Some(command_line) = keymap.get(&vec![key]) {
|
||||
log::info!("Got command: {command_line}");
|
||||
crate::commands::run_command(command_line, &action_tx)?;
|
||||
} else {
|
||||
// If the key was not handled as a single key action,
|
||||
// then consider it for multi-key combinations.
|
||||
self.last_tick_key_events.push(key);
|
||||
|
||||
// Check for multi-key combinations
|
||||
if let Some(action) = keymap.get(&self.last_tick_key_events) {
|
||||
log::info!("Got action: {action:?}");
|
||||
action_tx.send(action.clone())?;
|
||||
if let Some(command_line) = keymap.get(&self.last_tick_key_events) {
|
||||
log::info!("Got command: {command_line}");
|
||||
crate::commands::run_command(command_line, &action_tx)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
99
src/commands/mod.rs
Normal file
99
src/commands/mod.rs
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Valentin Lorentz
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use clap::{ArgMatches, Command, Parser};
|
||||
use color_eyre::eyre::{anyhow, bail, Result, WrapErr};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::action::Action;
|
||||
|
||||
pub type CommandHandler = fn(&str, &str, &mpsc::UnboundedSender<Action>) -> Result<()>;
|
||||
|
||||
pub struct RataCommand {
|
||||
pub name: &'static str,
|
||||
pub help: &'static str,
|
||||
pub handler: CommandHandler,
|
||||
}
|
||||
|
||||
inventory::collect!(RataCommand);
|
||||
|
||||
/*
|
||||
pub fn parser() -> Command {
|
||||
inventory::iter::<RataCommand>().fold(
|
||||
Command::new("ratatrix")
|
||||
.bin_name("")
|
||||
.subcommand_required(true),
|
||||
|parser, command| {
|
||||
if command.command.get_bin_name().is_none() {
|
||||
panic!("Command {:?} has no bin_name", command.command);
|
||||
}
|
||||
parser.subcommand(command.command.clone())
|
||||
},
|
||||
)
|
||||
}*/
|
||||
|
||||
pub fn run_command(command_line: &str, action_tx: &mpsc::UnboundedSender<Action>) -> Result<()> {
|
||||
if command_line.bytes().nth(0) != Some(b'/') {
|
||||
bail!("Not a command: {}", command_line);
|
||||
}
|
||||
|
||||
let (command_name, args) = match command_line[1..].split_once(" ") {
|
||||
Some((command_name, args)) => (command_name, args),
|
||||
None => (&command_line[1..], ""),
|
||||
};
|
||||
|
||||
for command in inventory::iter::<RataCommand>() {
|
||||
if command.name == command_name.to_ascii_lowercase() {
|
||||
return (command.handler)(command_name, args, action_tx);
|
||||
}
|
||||
}
|
||||
|
||||
bail!("Unknown command /{}", command_name)
|
||||
}
|
||||
|
||||
inventory::submit! {
|
||||
RataCommand {
|
||||
name: "quit",
|
||||
help: "Exits the process",
|
||||
handler: quit_handler,
|
||||
}
|
||||
}
|
||||
|
||||
fn quit_handler(_name: &str, _args: &str, action_tx: &mpsc::UnboundedSender<Action>) -> Result<()> {
|
||||
action_tx
|
||||
.send(Action::Quit)
|
||||
.context("Could not queue quit action")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
inventory::submit! {
|
||||
RataCommand {
|
||||
name: "suspend",
|
||||
help: "Puts the process in the background",
|
||||
handler: suspend_handler,
|
||||
}
|
||||
}
|
||||
|
||||
fn suspend_handler(
|
||||
_name: &str,
|
||||
_args: &str,
|
||||
action_tx: &mpsc::UnboundedSender<Action>,
|
||||
) -> Result<()> {
|
||||
action_tx
|
||||
.send(Action::Suspend)
|
||||
.context("Could not queue suspend action")?;
|
||||
Ok(())
|
||||
}
|
@ -88,14 +88,14 @@ impl Config {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deref, DerefMut)]
|
||||
pub struct KeyBindings(pub HashMap<Mode, HashMap<Vec<KeyEvent>, Action>>);
|
||||
pub struct KeyBindings(pub HashMap<Mode, HashMap<Vec<KeyEvent>, String>>);
|
||||
|
||||
impl<'de> Deserialize<'de> for KeyBindings {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let parsed_map = HashMap::<Mode, HashMap<String, Action>>::deserialize(deserializer)?;
|
||||
let parsed_map = HashMap::<Mode, HashMap<String, String>>::deserialize(deserializer)?;
|
||||
|
||||
let keybindings = parsed_map
|
||||
.into_iter()
|
||||
|
@ -5,6 +5,7 @@
|
||||
pub mod action;
|
||||
pub mod app;
|
||||
pub mod cli;
|
||||
pub mod commands;
|
||||
pub mod components;
|
||||
pub mod config;
|
||||
pub mod mode;
|
||||
|
Reference in New Issue
Block a user