From 38db231ea8fa0fb223e16637db0b6ec65b2b81ef Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 12 Oct 2023 20:14:29 -0500 Subject: brigadier usages --- azalea-brigadier/src/suggestion/mod.rs | 102 ++++++++++++++++++--- azalea-brigadier/src/suggestion/suggestions.rs | 39 ++++++-- .../src/suggestion/suggestions_builder.rs | 50 +++++++--- 3 files changed, 158 insertions(+), 33 deletions(-) (limited to 'azalea-brigadier/src/suggestion') diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs index c404adc7..bc0e7608 100755 --- a/azalea-brigadier/src/suggestion/mod.rs +++ b/azalea-brigadier/src/suggestion/mod.rs @@ -8,6 +8,10 @@ use azalea_buf::McBufWritable; use azalea_chat::FormattedText; #[cfg(feature = "azalea-buf")] use std::io::Write; +use std::{ + fmt::{self, Display}, + hash::Hash, +}; pub use suggestions::Suggestions; pub use suggestions_builder::SuggestionsBuilder; @@ -16,22 +20,50 @@ pub use suggestions_builder::SuggestionsBuilder; /// The `M` generic is the type of the tooltip, so for example a `String` or /// just `()` if you don't care about it. #[derive(Debug, Clone, Hash, Eq, PartialEq)] -pub struct Suggestion { - pub text: String, +pub struct Suggestion +where + M: Clone, +{ pub range: StringRange, + value: SuggestionValue, pub tooltip: Option, } +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub enum SuggestionValue { + Integer(i32), + Text(String), +} + +impl Suggestion<()> { + pub fn new(range: StringRange, text: &str) -> Suggestion<()> { + Suggestion { + range, + value: SuggestionValue::Text(text.to_string()), + tooltip: None, + } + } +} + impl Suggestion { + pub fn new_with_tooltip(range: StringRange, text: &str, tooltip: M) -> Self { + Self { + range, + value: SuggestionValue::Text(text.to_string()), + tooltip: Some(tooltip), + } + } + pub fn apply(&self, input: &str) -> String { + let text = self.value.to_string(); if self.range.start() == 0 && self.range.end() == input.len() { - return input.to_string(); + return text; } - let mut result = String::with_capacity(self.text.len()); + let mut result = String::with_capacity(text.len()); if self.range.start() > 0 { result.push_str(&input[0..self.range.start()]); } - result.push_str(&self.text); + result.push_str(&text); if self.range.end() < input.len() { result.push_str(&input[self.range.end()..]); } @@ -39,30 +71,78 @@ impl Suggestion { result } - pub fn expand(&self, command: &str, range: &StringRange) -> Suggestion { - if range == &self.range { + pub fn expand(&self, command: &str, range: StringRange) -> Suggestion { + if range == self.range { return self.clone(); } let mut result = String::new(); if range.start() < self.range.start() { result.push_str(&command[range.start()..self.range.start()]); } - result.push_str(&self.text); + result.push_str(&self.value.to_string()); if range.end() > self.range.end() { result.push_str(&command[self.range.end()..range.end()]); } Suggestion { - range: range.clone(), - text: result, + range, + value: SuggestionValue::Text(result), tooltip: self.tooltip.clone(), } } + + pub fn text(&self) -> String { + self.value.to_string() + } +} + +impl SuggestionValue { + pub fn cmp_ignore_case(&self, other: &Self) -> std::cmp::Ordering { + match (self, other) { + (SuggestionValue::Text(a), SuggestionValue::Text(b)) => { + a.to_lowercase().cmp(&b.to_lowercase()) + } + (SuggestionValue::Integer(a), SuggestionValue::Integer(b)) => a.cmp(b), + _ => { + let a = self.to_string(); + let b = other.to_string(); + a.cmp(&b) + } + } + } +} + +impl Display for SuggestionValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SuggestionValue::Text(text) => write!(f, "{text}"), + SuggestionValue::Integer(value) => write!(f, "{value}"), + } + } +} + +impl Ord for SuggestionValue { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match (self, other) { + (SuggestionValue::Text(a), SuggestionValue::Text(b)) => a.cmp(b), + (SuggestionValue::Integer(a), SuggestionValue::Integer(b)) => a.cmp(b), + _ => { + let a = self.to_string(); + let b = other.to_string(); + a.cmp(&b) + } + } + } +} +impl PartialOrd for SuggestionValue { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } #[cfg(feature = "azalea-buf")] impl McBufWritable for Suggestion { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - self.text.write_into(buf)?; + self.value.to_string().write_into(buf)?; self.tooltip.write_into(buf)?; Ok(()) } diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs index 2a8b5e9e..69877786 100755 --- a/azalea-brigadier/src/suggestion/suggestions.rs +++ b/azalea-brigadier/src/suggestion/suggestions.rs @@ -1,6 +1,8 @@ use super::Suggestion; use crate::context::StringRange; #[cfg(feature = "azalea-buf")] +use crate::suggestion::SuggestionValue; +#[cfg(feature = "azalea-buf")] use azalea_buf::{ BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable, }; @@ -11,12 +13,19 @@ use std::io::{Cursor, Write}; use std::{collections::HashSet, hash::Hash}; #[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct Suggestions { - pub range: StringRange, - pub suggestions: Vec>, +pub struct Suggestions +where + M: Clone + PartialEq + Hash, +{ + range: StringRange, + suggestions: Vec>, } impl Suggestions { + pub fn new(range: StringRange, suggestions: Vec>) -> Self { + Self { range, suggestions } + } + pub fn merge(command: &str, input: &[Suggestions]) -> Self { if input.is_empty() { return Suggestions::default(); @@ -45,20 +54,34 @@ impl Suggestions { let range = StringRange::new(start, end); let mut texts = HashSet::new(); for suggestion in suggestions { - texts.insert(suggestion.expand(command, &range)); + texts.insert(suggestion.expand(command, range)); } let mut sorted = texts.into_iter().collect::>(); - sorted.sort_by(|a, b| a.text.cmp(&b.text)); + + sorted.sort_by(|a, b| a.value.cmp_ignore_case(&b.value)); + Suggestions { range, suggestions: sorted, } } + + pub fn is_empty(&self) -> bool { + self.suggestions.is_empty() + } + + pub fn list(&self) -> &[Suggestion] { + &self.suggestions + } + + pub fn range(&self) -> StringRange { + self.range + } } // this can't be derived because that'd require the generic to have `Default` // too even if it's not actually necessary -impl Default for Suggestions { +impl Default for Suggestions { fn default() -> Self { Self { range: StringRange::default(), @@ -85,12 +108,12 @@ impl McBufReadable for Suggestions { let mut suggestions = Vec::::read_from(buf)? .into_iter() .map(|s| Suggestion { - text: s.text, + value: SuggestionValue::Text(s.text), tooltip: s.tooltip, range: range.clone(), }) .collect::>(); - suggestions.sort_by(|a, b| a.text.cmp(&b.text)); + suggestions.sort_by(|a, b| a.value.cmp(&b.value)); Ok(Suggestions { range, suggestions }) } diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs index 66f17fb1..469a7b98 100755 --- a/azalea-brigadier/src/suggestion/suggestions_builder.rs +++ b/azalea-brigadier/src/suggestion/suggestions_builder.rs @@ -1,19 +1,24 @@ use std::collections::HashSet; +use std::hash::Hash; use crate::context::StringRange; -use super::{Suggestion, Suggestions}; +use super::{Suggestion, SuggestionValue, Suggestions}; -pub struct SuggestionsBuilder { +#[derive(PartialEq, Debug)] +pub struct SuggestionsBuilder +where + M: Clone + Eq + Hash, +{ input: String, input_lowercase: String, start: usize, remaining: String, remaining_lowercase: String, - result: HashSet, + result: HashSet>, } -impl SuggestionsBuilder { +impl SuggestionsBuilder<()> { pub fn new(input: &str, start: usize) -> Self { Self::new_with_lowercase(input, input.to_lowercase().as_str(), start) } @@ -28,7 +33,9 @@ impl SuggestionsBuilder { result: HashSet::new(), } } +} +impl SuggestionsBuilder { pub fn input(&self) -> &str { &self.input } @@ -37,7 +44,7 @@ impl SuggestionsBuilder { self.start } - pub fn remianing(&self) -> &str { + pub fn remaining(&self) -> &str { &self.remaining } @@ -45,7 +52,7 @@ impl SuggestionsBuilder { &self.remaining_lowercase } - pub fn build(&self) -> Suggestions { + pub fn build(&self) -> Suggestions { Suggestions::create(&self.input, &self.result) } @@ -55,38 +62,53 @@ impl SuggestionsBuilder { } self.result.insert(Suggestion { range: StringRange::between(self.start, self.input.len()), - text: text.to_string(), + value: SuggestionValue::Text(text.to_string()), tooltip: None, }); self } - pub fn suggest_with_tooltip(mut self, text: &str, tooltip: String) -> Self { + pub fn suggest_with_tooltip(mut self, text: &str, tooltip: M) -> Self { if text == self.remaining { return self; } self.result.insert(Suggestion { range: StringRange::between(self.start, self.input.len()), - text: text.to_string(), + value: SuggestionValue::Text(text.to_string()), tooltip: Some(tooltip), }); self } - // TODO: integer suggestions - // https://github.com/Mojang/brigadier/blob/master/src/main/java/com/mojang/brigadier/suggestion/SuggestionsBuilder.java#L74 + pub fn suggest_integer(mut self, value: i32) -> Self { + self.result.insert(Suggestion { + range: StringRange::between(self.start, self.input.len()), + value: SuggestionValue::Integer(value), + tooltip: None, + }); + self + } + + pub fn suggest_integer_with_tooltip(mut self, value: i32, tooltip: M) -> Self { + self.result.insert(Suggestion { + range: StringRange::between(self.start, self.input.len()), + value: SuggestionValue::Integer(value), + tooltip: Some(tooltip), + }); + self + } #[allow(clippy::should_implement_trait)] - pub fn add(mut self, other: SuggestionsBuilder) -> Self { + pub fn add(mut self, other: SuggestionsBuilder) -> Self { self.result.extend(other.result); self } - pub fn create_offset(&self, start: usize) -> Self { + pub fn create_offset(&self, start: usize) -> SuggestionsBuilder<()> { SuggestionsBuilder::new_with_lowercase(&self.input, &self.input_lowercase, start) } - pub fn restart(self) -> Self { + pub fn restart(&self) -> SuggestionsBuilder<()> { self.create_offset(self.start) } } -- cgit v1.2.3