diff --git a/common/src/cmd.rs b/common/src/cmd.rs index fa18862587..e67074a5de 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -537,7 +537,7 @@ impl ServerChatCommand { ), ServerChatCommand::GiveItem => cmd( vec![ - Enum("item", ITEM_SPECS.clone(), Required), + AssetPath("item", "common.items.", ITEM_SPECS.clone(), Required), Integer("num", 1, Optional), ], "Give yourself some items.\nFor an example or to auto complete use Tab.", @@ -647,7 +647,12 @@ impl ServerChatCommand { ), ServerChatCommand::MakeNpc => cmd( vec![ - Enum("entity_config", ENTITY_CONFIGS.clone(), Required), + AssetPath( + "entity_config", + "common.entity.", + ENTITY_CONFIGS.clone(), + Required, + ), Integer("num", 1, Optional), ], "Spawn entity from config near you.\nFor an example or to auto complete use Tab.", @@ -1079,6 +1084,7 @@ impl ServerChatCommand { ArgumentSpec::Message(_) => "{/.*/}", ArgumentSpec::SubCommand => "{} {/.*/}", ArgumentSpec::Enum(_, _, _) => "{}", + ArgumentSpec::AssetPath(_, _, _, _) => "{}", ArgumentSpec::Boolean(_, _, _) => "{}", ArgumentSpec::Flag(_) => "{}", }) @@ -1146,6 +1152,12 @@ pub enum ArgumentSpec { /// * Predefined string completions /// * whether it's optional Enum(&'static str, Vec, Requirement), + /// The argument is an asset path. The associated values are + /// * label + /// * Path prefix shared by all assets + /// * List of all asset paths as strings for completion + /// * whether it's optional + AssetPath(&'static str, &'static str, Vec, Requirement), /// The argument is likely a boolean. The associated values are /// * label /// * suggested tab-completion @@ -1222,6 +1234,13 @@ impl ArgumentSpec { format!("[{}]", label) } }, + ArgumentSpec::AssetPath(label, _, _, req) => { + if &Requirement::Required == req { + format!("<{}>", label) + } else { + format!("[{}]", label) + } + }, ArgumentSpec::Boolean(label, _, req) => { if &Requirement::Required == req { format!("<{}>", label) @@ -1246,6 +1265,7 @@ impl ArgumentSpec { | ArgumentSpec::Command(r) | ArgumentSpec::Message(r) | ArgumentSpec::Enum(_, _, r) + | ArgumentSpec::AssetPath(_, _, _, r) | ArgumentSpec::Boolean(_, _, r) => *r, ArgumentSpec::Flag(_) => Requirement::Optional, ArgumentSpec::SubCommand => Requirement::Required, diff --git a/voxygen/src/cmd.rs b/voxygen/src/cmd.rs index 9dffba8372..f1ff2a4cba 100644 --- a/voxygen/src/cmd.rs +++ b/voxygen/src/cmd.rs @@ -17,6 +17,7 @@ use common::{ uuid::Uuid, }; use common_net::sync::WorldSyncExt; +use itertools::Itertools; use levenshtein::levenshtein; use specs::{Join, WorldExt}; use strum::{EnumIter, IntoEnumIterator}; @@ -100,6 +101,7 @@ impl ClientChatCommand { ArgumentSpec::Message(_) => "{/.*/}", ArgumentSpec::SubCommand => "{} {/.*/}", ArgumentSpec::Enum(_, _, _) => "{}", + ArgumentSpec::AssetPath(_, _, _, _) => "{}", ArgumentSpec::Boolean(_, _, _) => "{}", ArgumentSpec::Flag(_) => "{}", }) @@ -210,6 +212,9 @@ fn preproccess_command( break; } }, + ArgumentSpec::AssetPath(_, prefix, _, _) => { + *arg = prefix.to_string() + arg; + }, _ => {}, } if matches!(arg_spec.requirement(), Requirement::Required) { @@ -591,6 +596,17 @@ impl TabComplete for ArgumentSpec { .filter(|string| string.starts_with(part)) .map(|c| c.to_string()) .collect(), + ArgumentSpec::AssetPath(_, prefix, paths, _) => { + let part_with_prefix = prefix.to_string() + part; + let depth = part_with_prefix.split('.').count(); + paths + .iter() + .map(|path| path.as_str().split('.').take(depth).join(".")) + .dedup() + .filter(|string| string.starts_with(&part_with_prefix)) + .filter_map(|c| Some(c.strip_prefix(prefix)?.to_string())) + .collect() + }, ArgumentSpec::Boolean(_, part, _) => ["true", "false"] .iter() .filter(|string| string.starts_with(part))