use crate::commands;
use crate::EditorState;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Debug, Default, Clone)]
pub struct Command {
pub name: &'static str,
pub arguments: Vec<&'static str>,
pub description: &'static str,
pub documentation: &'static str,
pub scope: CommandScope,
pub action: CommandAction,
}
impl Command {
pub fn new() -> Command {
Command::default()
}
pub fn matches(&self, command: &str) -> bool {
self.name == command
}
pub fn to_string(&self) -> String {
format!("{}", self)
}
}
impl std::fmt::Display for Command {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
let scope_name: &str = match self.scope {
CommandScope::Global => "global",
CommandScope::REPLOnly => "repl",
CommandScope::FileOnly => "file",
CommandScope::Private => "private",
};
write!(
fmt,
"{}: {}\nscope: {}\nfunction: {:#?}",
self.name, self.description, scope_name, self.action
)
}
}
impl Ord for Command {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.name.cmp(other.name)
}
}
impl PartialOrd for Command {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Eq for Command {}
impl PartialEq for Command {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
#[derive(Debug, Default, Clone)]
pub enum CommandScope {
#[default]
Global,
REPLOnly,
FileOnly,
Private,
}
impl std::fmt::Display for CommandScope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CommandScope::Global => write!(f, "global"),
CommandScope::REPLOnly => write!(f, "editor only"),
CommandScope::FileOnly => write!(f, "library only"),
CommandScope::Private => write!(f, "private"),
}
}
}
#[derive(Clone)]
pub struct CommandAction {
pub action: Arc<Mutex<dyn FnMut(Vec<&str>, &mut EditorState) -> String>>,
}
impl CommandAction {
pub fn new<A>(action: A) -> Self
where
A: FnMut(Vec<&str>, &mut EditorState) -> String + 'static,
{
CommandAction {
action: Arc::new(Mutex::new(action)),
}
}
pub fn call(&mut self, args: Vec<&str>, state: &mut EditorState) -> String {
let mut action = self.action.lock().unwrap();
action(args, state)
}
}
impl std::fmt::Debug for CommandAction {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "CommandAction is a closure and cannot be debugged")
}
}
impl Default for CommandAction {
fn default() -> Self {
CommandAction::new(|_args: Vec<&str>, _state: &mut EditorState| {
format!("command has no action")
})
}
}
#[derive(Clone, Default)]
pub struct CommandRegistry {
pub commands: HashMap<String, Arc<Command>>,
}
impl CommandRegistry {
pub fn new() -> CommandRegistry {
CommandRegistry {
commands: HashMap::new(),
}
}
pub fn instantiate() -> CommandRegistry {
let mut r = CommandRegistry::new();
commands::register_all(&mut r);
r
}
pub fn add_command(&mut self, command: Command) {
let command = Arc::new(command);
let command_already_exists = self.commands.contains_key(command.name);
if command_already_exists {
eprintln!("command {} already exists", command.name);
return;
}
self.commands
.insert(command.name.to_string(), Arc::clone(&command));
}
pub fn remove_command(&mut self, command: &str) {
let result = self.commands.remove(command);
if result.is_none() {
eprintln!("command {} is not registered", command);
}
}
pub fn get_command(&self, command: &str) -> Option<Arc<Command>> {
self.commands.get(command).cloned()
}
pub fn get_all_commands(&self) -> Vec<Arc<Command>> {
self.commands.values().cloned().collect()
}
}