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 None;
None
}
pub fn complete(line: &str, client: &Client) -> Vec<String> {

View File

@ -12,7 +12,7 @@ use std::{
fmt,
fs::{self, File, ReadDir},
io::{BufReader, Read},
path::{Path, PathBuf},
path::PathBuf,
sync::{Arc, RwLock},
};
@ -57,33 +57,8 @@ impl From<std::io::Error> for Error {
lazy_static! {
/// 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());
/// 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
@ -301,7 +276,7 @@ impl Asset for String {
lazy_static! {
/// 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();
// VELOREN_ASSETS environment variable

View File

@ -1,10 +1,10 @@
use crate::{assets, comp, npc};
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.
pub struct ChatCommandData {
/// A format string for parsing arguments.
/// A list of arguments useful for both tab completion and parsing
pub args: Vec<ArgumentSpec>,
/// A one-line message that explains what the command does
pub description: &'static str,
@ -108,6 +108,31 @@ lazy_static! {
.iter()
.map(|s| s.to_string())
.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 {
@ -141,7 +166,7 @@ impl ChatCommand {
),
ChatCommand::GiveItem => cmd(
vec![
Enum("item", assets::ITEM_SPECS.clone(), Required),
Enum("item", ITEM_SPECS.clone(), Required),
Integer("num", 1, Optional),
],
"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 {
match self {
ChatCommand::Adminify => "adminify",
@ -284,6 +310,7 @@ impl ChatCommand {
}
}
/// A message that explains what the command does
pub fn help_string(&self) -> String {
let data = self.data();
let usage = std::iter::once(format!("/{}", self.keyword()))
@ -293,8 +320,11 @@ impl ChatCommand {
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 }
/// Returns a format string for parsing arguments with scan_fmt
pub fn arg_fmt(&self) -> String {
self.data()
.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);
fn get_handler(cmd: &ChatCommand) -> CommandHandler {
match cmd {