From 2606de9f4c1563c10a616925a87905ea1ffd2298 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 1 Oct 2023 19:12:27 -0500 Subject: [PATCH] start adding brigadier suggestions --- azalea-brigadier/src/suggestion/mod.rs | 4 +- .../src/suggestion/suggestions_builder.rs | 92 +++++++++++++++++++ .../tests/suggestion/suggestion_test.rs | 71 ++++++++++++++ .../tests/suggestion/suggestions_test.rs | 20 ++++ 4 files changed, 186 insertions(+), 1 deletion(-) create mode 100755 azalea-brigadier/src/suggestion/suggestions_builder.rs diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs index 592c674c..22257423 100755 --- a/azalea-brigadier/src/suggestion/mod.rs +++ b/azalea-brigadier/src/suggestion/mod.rs @@ -1,4 +1,5 @@ mod suggestions; + mod suggestions_builder; use crate::context::StringRange; #[cfg(feature = "azalea-buf")] @@ -7,7 +8,8 @@ use azalea_buf::McBufWritable; use azalea_chat::FormattedText; #[cfg(feature = "azalea-buf")] use std::io::Write; -pub use suggestions::*; +pub use suggestions::Suggestions; +pub use suggestions_builder::SuggestionsBuilder; /// A suggestion given to the user for what they might want to type next. /// diff --git a/azalea-brigadier/src/suggestion/suggestions_builder.rs b/azalea-brigadier/src/suggestion/suggestions_builder.rs new file mode 100755 index 00000000..66f17fb1 --- /dev/null +++ b/azalea-brigadier/src/suggestion/suggestions_builder.rs @@ -0,0 +1,92 @@ +use std::collections::HashSet; + +use crate::context::StringRange; + +use super::{Suggestion, Suggestions}; + +pub struct SuggestionsBuilder { + input: String, + input_lowercase: String, + start: usize, + remaining: String, + remaining_lowercase: String, + result: HashSet, +} + +impl SuggestionsBuilder { + pub fn new(input: &str, start: usize) -> Self { + Self::new_with_lowercase(input, input.to_lowercase().as_str(), start) + } + + pub fn new_with_lowercase(input: &str, input_lowercase: &str, start: usize) -> Self { + Self { + start, + input: input.to_string(), + input_lowercase: input_lowercase.to_string(), + remaining: input[start..].to_string(), + remaining_lowercase: input_lowercase[start..].to_string(), + result: HashSet::new(), + } + } + + pub fn input(&self) -> &str { + &self.input + } + + pub fn start(&self) -> usize { + self.start + } + + pub fn remianing(&self) -> &str { + &self.remaining + } + + pub fn remaining_lowercase(&self) -> &str { + &self.remaining_lowercase + } + + pub fn build(&self) -> Suggestions { + Suggestions::create(&self.input, &self.result) + } + + pub fn suggest(mut self, text: &str) -> Self { + if text == self.remaining { + return self; + } + self.result.insert(Suggestion { + range: StringRange::between(self.start, self.input.len()), + text: text.to_string(), + tooltip: None, + }); + self + } + + pub fn suggest_with_tooltip(mut self, text: &str, tooltip: String) -> Self { + if text == self.remaining { + return self; + } + self.result.insert(Suggestion { + range: StringRange::between(self.start, self.input.len()), + 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 + + #[allow(clippy::should_implement_trait)] + pub fn add(mut self, other: SuggestionsBuilder) -> Self { + self.result.extend(other.result); + self + } + + pub fn create_offset(&self, start: usize) -> Self { + SuggestionsBuilder::new_with_lowercase(&self.input, &self.input_lowercase, start) + } + + pub fn restart(self) -> Self { + self.create_offset(self.start) + } +} diff --git a/azalea-brigadier/tests/suggestion/suggestion_test.rs b/azalea-brigadier/tests/suggestion/suggestion_test.rs index e69de29b..9ba95807 100755 --- a/azalea-brigadier/tests/suggestion/suggestion_test.rs +++ b/azalea-brigadier/tests/suggestion/suggestion_test.rs @@ -0,0 +1,71 @@ +#[test] +fn apply_insertation_start() { + let suggestion = Suggestion::new(StringRange::at(0), "And so I said: "); + assert_eq!(suggestion.apply("Hello world!"), "And so I said: Hello world!"); +} + +#[test] +fn apply_insertation_middle() { + let suggestion = Suggestion::new(StringRange::at(6), "small "); + assert_eq!(suggestion.apply("Hello world!"), "Hello small world!"); +} + +#[test] +fn apply_insertation_end() { + let suggestion = Suggestion::new(StringRange::at(5), " world!"); + assert_eq!(suggestion.apply("Hello"), "Hello world!"); +} + +#[test] +fn apply_replacement_start() { + let suggestion = Suggestion::new(StringRange::between(0, 5), "Goodbye"); + assert_eq!(suggestion.apply("Hello world!"), "Goodbye world!"); +} + +#[test] +fn apply_replacement_middle() { + let suggestion = Suggestion::new(StringRange::between(6, 11), "Alex"); + assert_eq!(suggestion.apply("Hello world!"), "Hello Alex!"); +} + +#[test] +fn apply_replacement_end() { + let suggestion = Suggestion::new(StringRange::between(6, 12), "Creeper!"); + assert_eq!(suggestion.apply("Hello world!"), "Hello Creeper!"); +} + +#[test] +fn apply_replacement_everything() { + let suggestion = Suggestion::new(StringRange::between(0, 12), "Oh dear."); + assert_eq!(suggestion.apply("Hello world!"), "Oh dear."); +} + +#[test] +fn expand_unchanged() { + let suggestion = Suggestion::new(StringRange::at(1), "oo"); + assert_eq!(suggestion.expand("f", StringRange::at(1)), suggestion); +} + +#[test] +fn expand_left() { + let suggestion = Suggestion::new(StringRange::at(1), "oo"); + assert_eq!(suggestion.expand("f", StringRange::between(0, 1)), Suggestion::new(StringRange::between(0, 1), "foo")); +} + +#[test] +fn expand_right() { + let suggestion = Suggestion::new(StringRange::at(0), "minecraft:"); + assert_eq!(suggestion.expand("fish", StringRange::between(0, 4)), Suggestion::new(StringRange::between(0, 4), "minecraft:fish")); +} + +#[test] +fn expand_both() { + let suggestion = Suggestion::new(StringRange::at(11), "minecraft:"); + assert_eq!(suggestion.expand("give Steve fish_block", StringRange::between(5, 21)), Suggestion::new(StringRange::between(5, 21), "Steve minecraft:fish_block")); +} + +#[test] +fn expand_replacement() { + let suggestion = Suggestion::new(StringRange::between(6, 11), "strangers"); + assert_eq!(suggestion.expand("Hello world!", StringRange::between(0, 12)), Suggestion::new(StringRange::between(0, 12), "Hello strangers!")); +} \ No newline at end of file diff --git a/azalea-brigadier/tests/suggestion/suggestions_test.rs b/azalea-brigadier/tests/suggestion/suggestions_test.rs index e69de29b..28a8266d 100755 --- a/azalea-brigadier/tests/suggestion/suggestions_test.rs +++ b/azalea-brigadier/tests/suggestion/suggestions_test.rs @@ -0,0 +1,20 @@ +#[test] +fn merge_empty() { + let merged = Suggestions::merge("foo b", vec![]); + assert!(merged.is_empty()); +} + +#[test] +fn merge_single() { + let suggestions = Suggestions::new(StringRange::at(5), vec![Suggestion::new(StringRange::at(5), "ar")]); + let merged = Suggestions::merge("foo b", vec![suggestions]); + assert_eq!(merged, suggestions); +} + +#[test] +fn merge_multiple() { + let a = Suggestions::new(StringRange::at(5), vec![Suggestion::new(StringRange::at(5), "ar"), Suggestion::new(StringRange::at(5), "az"), Suggestion::new(StringRange::at(5), "Az")]); + let b = Suggestions::new(StringRange::between(4, 5), vec![Suggestion::new(StringRange::between(4, 5), "foo"), Suggestion::new(StringRange::between(4, 5), "qux"), Suggestion::new(StringRange::between(4, 5), "apple"), Suggestion::new(StringRange::between(4, 5), "Bar")]); + let merged = Suggestions::merge("foo b", vec![a, b]); + assert_eq!(merged.get_list(), vec![Suggestion::new(StringRange::between(4, 5), "apple"), Suggestion::new(StringRange::between(4, 5), "ar"), Suggestion::new(StringRange::between(4, 5), "Az"), Suggestion::new(StringRange::between(4, 5), "bar"), Suggestion::new(StringRange::between(4, 5), "Bar"), Suggestion::new(StringRange::between(4, 5), "baz"), Suggestion::new(StringRange::between(4, 5), "bAz"), Suggestion::new(StringRange::between(4, 5), "foo"), Suggestion::new(StringRange::between(4, 5), "qux")]); +} \ No newline at end of file