Merge branch 'CapsizeGlimmer/tab_completion' into 'master'

Tab completion code review suggestions

Closes #553

See merge request veloren/veloren!979
This commit is contained in:
Imbris 2020-05-11 23:56:48 +00:00
commit 7233dbee3e
4 changed files with 49 additions and 32 deletions

View File

@ -74,7 +74,7 @@ fn nth_word(line: &str, n: usize) -> Option<usize> {
return Some(i); return Some(i);
} }
} }
return None; None
} }
pub fn complete(line: &str, client: &Client) -> Vec<String> { pub fn complete(line: &str, client: &Client) -> Vec<String> {

View File

@ -12,7 +12,7 @@ use std::{
fmt, fmt,
fs::{self, File, ReadDir}, fs::{self, File, ReadDir},
io::{BufReader, Read}, io::{BufReader, Read},
path::{Path, PathBuf}, path::PathBuf,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
@ -57,33 +57,8 @@ impl From<std::io::Error> for Error {
lazy_static! { lazy_static! {
/// The HashMap where all loaded assets are stored in. /// The HashMap where all loaded assets are stored in.
pub static ref ASSETS: RwLock<HashMap<String, Arc<dyn Any + 'static + Sync + Send>>> = static ref ASSETS: RwLock<HashMap<String, Arc<dyn Any + 'static + Sync + Send>>> =
RwLock::new(HashMap::new()); RwLock::new(HashMap::new());
/// List of item specifiers. Useful for tab completing
pub static ref ITEM_SPECS: Vec<String> = {
let base = ASSETS_PATH.join("common").join("items");
let mut items = vec![];
fn list_items (path: &Path, base: &Path, mut items: &mut Vec<String>) -> std::io::Result<()>{
for entry in std::fs::read_dir(path)? {
let path = entry?.path();
if path.is_dir(){
list_items(&path, &base, &mut items)?;
} else {
if let Ok(path) = path.strip_prefix(base) {
let path = path.to_string_lossy().trim_end_matches(".ron").replace('/', ".");
items.push(path);
}
}
}
Ok(())
}
if list_items(&base, &ASSETS_PATH, &mut items).is_err() {
warn!("There was a problem listing some item assets");
}
items.sort();
items
};
} }
// TODO: Remove this function. It's only used in world/ in a really ugly way.To // TODO: Remove this function. It's only used in world/ in a really ugly way.To
@ -301,7 +276,7 @@ impl Asset for String {
lazy_static! { lazy_static! {
/// Lazy static to find and cache where the asset directory is. /// Lazy static to find and cache where the asset directory is.
static ref ASSETS_PATH: PathBuf = { pub static ref ASSETS_PATH: PathBuf = {
let mut paths = Vec::new(); let mut paths = Vec::new();
// VELOREN_ASSETS environment variable // VELOREN_ASSETS environment variable

View File

@ -1,10 +1,10 @@
use crate::{assets, comp, npc}; use crate::{assets, comp, npc};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::{ops::Deref, str::FromStr}; use std::{ops::Deref, path::Path, str::FromStr};
/// Struct representing a command that a user can run from server chat. /// Struct representing a command that a user can run from server chat.
pub struct ChatCommandData { pub struct ChatCommandData {
/// A format string for parsing arguments. /// A list of arguments useful for both tab completion and parsing
pub args: Vec<ArgumentSpec>, pub args: Vec<ArgumentSpec>,
/// A one-line message that explains what the command does /// A one-line message that explains what the command does
pub description: &'static str, pub description: &'static str,
@ -108,6 +108,31 @@ lazy_static! {
.iter() .iter()
.map(|s| s.to_string()) .map(|s| s.to_string())
.collect(); .collect();
/// List of item specifiers. Useful for tab completing
static ref ITEM_SPECS: Vec<String> = {
let path = assets::ASSETS_PATH.join("common").join("items");
let mut items = vec![];
fn list_items (path: &Path, base: &Path, mut items: &mut Vec<String>) -> std::io::Result<()>{
for entry in std::fs::read_dir(path)? {
let path = entry?.path();
if path.is_dir(){
list_items(&path, &base, &mut items)?;
} else {
if let Ok(path) = path.strip_prefix(base) {
let path = path.to_string_lossy().trim_end_matches(".ron").replace('/', ".");
items.push(path);
}
}
}
Ok(())
}
if list_items(&path, &assets::ASSETS_PATH, &mut items).is_err() {
warn!("There was a problem listing item assets");
}
items.sort();
items
};
} }
impl ChatCommand { impl ChatCommand {
@ -141,7 +166,7 @@ impl ChatCommand {
), ),
ChatCommand::GiveItem => cmd( ChatCommand::GiveItem => cmd(
vec![ vec![
Enum("item", assets::ITEM_SPECS.clone(), Required), Enum("item", ITEM_SPECS.clone(), Required),
Integer("num", 1, Optional), Integer("num", 1, Optional),
], ],
"Give yourself some items", "Give yourself some items",
@ -252,6 +277,7 @@ impl ChatCommand {
} }
} }
/// The keyword used to invoke the command, omitting the leading '/'.
pub fn keyword(&self) -> &'static str { pub fn keyword(&self) -> &'static str {
match self { match self {
ChatCommand::Adminify => "adminify", ChatCommand::Adminify => "adminify",
@ -284,6 +310,7 @@ impl ChatCommand {
} }
} }
/// A message that explains what the command does
pub fn help_string(&self) -> String { pub fn help_string(&self) -> String {
let data = self.data(); let data = self.data();
let usage = std::iter::once(format!("/{}", self.keyword())) let usage = std::iter::once(format!("/{}", self.keyword()))
@ -293,8 +320,11 @@ impl ChatCommand {
format!("{}: {}", usage, data.description) format!("{}: {}", usage, data.description)
} }
/// A boolean that is used to check whether the command requires
/// administrator permissions or not.
pub fn needs_admin(&self) -> bool { self.data().needs_admin } pub fn needs_admin(&self) -> bool { self.data().needs_admin }
/// Returns a format string for parsing arguments with scan_fmt
pub fn arg_fmt(&self) -> String { pub fn arg_fmt(&self) -> String {
self.data() self.data()
.args .args

View File

@ -46,6 +46,18 @@ impl ChatCommandExt for ChatCommand {
} }
} }
/// Handler function called when the command is executed.
/// # Arguments
/// * `&mut Server` - the `Server` instance executing the command.
/// * `EcsEntity` - an `Entity` corresponding to the player that invoked the
/// command.
/// * `EcsEntity` - an `Entity` for the player on whom the command is invoked.
/// This differs from the previous argument when using /sudo
/// * `String` - a `String` containing the part of the command after the
/// keyword.
/// * `&ChatCommand` - the command to execute with the above arguments.
/// Handler functions must parse arguments from the the given `String`
/// (`scan_fmt!` is included for this purpose).
type CommandHandler = fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand); type CommandHandler = fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand);
fn get_handler(cmd: &ChatCommand) -> CommandHandler { fn get_handler(cmd: &ChatCommand) -> CommandHandler {
match cmd { match cmd {