azalea_brigadier/suggestion/
mod.rs1mod suggestion_provider;
2mod suggestions;
3mod suggestions_builder;
4
5#[cfg(feature = "azalea-buf")]
6use std::io::Write;
7use std::{
8 cmp::Ordering,
9 fmt::{self, Display},
10 hash::Hash,
11 io,
12};
13
14#[cfg(feature = "azalea-buf")]
15use azalea_buf::AzaleaWrite;
16#[cfg(feature = "azalea-buf")]
17use azalea_chat::FormattedText;
18pub use suggestion_provider::SuggestionProvider;
19pub use suggestions::Suggestions;
20pub use suggestions_builder::SuggestionsBuilder;
21
22use crate::context::StringRange;
23
24#[derive(Debug, Clone, Hash, Eq, PartialEq)]
29pub struct Suggestion {
30 pub range: StringRange,
31 value: SuggestionValue,
32 pub tooltip: Option<String>,
33}
34
35#[derive(Debug, Clone, Hash, Eq, PartialEq)]
36pub enum SuggestionValue {
37 Integer(i32),
38 Text(String),
39}
40
41impl Suggestion {
42 pub fn new(range: StringRange, text: &str) -> Suggestion {
43 Suggestion {
44 range,
45 value: SuggestionValue::Text(text.to_string()),
46 tooltip: None,
47 }
48 }
49
50 pub fn new_with_tooltip(range: StringRange, text: &str, tooltip: String) -> Self {
51 Self {
52 range,
53 value: SuggestionValue::Text(text.to_string()),
54 tooltip: Some(tooltip),
55 }
56 }
57
58 pub fn apply(&self, input: &str) -> String {
59 let text = self.value.to_string();
60 if self.range.start() == 0 && self.range.end() == input.len() {
61 return text;
62 }
63 let mut result = String::with_capacity(text.len());
64 if self.range.start() > 0 {
65 result.push_str(&input[0..self.range.start()]);
66 }
67 result.push_str(&text);
68 if self.range.end() < input.len() {
69 result.push_str(&input[self.range.end()..]);
70 }
71
72 result
73 }
74
75 pub fn expand(&self, command: &str, range: StringRange) -> Suggestion {
76 if range == self.range {
77 return self.clone();
78 }
79 let mut result = String::new();
80 if range.start() < self.range.start() {
81 result.push_str(&command[range.start()..self.range.start()]);
82 }
83 result.push_str(&self.value.to_string());
84 if range.end() > self.range.end() {
85 result.push_str(&command[self.range.end()..range.end()]);
86 }
87 Suggestion {
88 range,
89 value: SuggestionValue::Text(result),
90 tooltip: self.tooltip.clone(),
91 }
92 }
93
94 pub fn text(&self) -> String {
95 self.value.to_string()
96 }
97}
98
99impl SuggestionValue {
100 pub fn cmp_ignore_case(&self, other: &Self) -> Ordering {
101 match (self, other) {
102 (SuggestionValue::Text(a), SuggestionValue::Text(b)) => {
103 a.to_lowercase().cmp(&b.to_lowercase())
104 }
105 (SuggestionValue::Integer(a), SuggestionValue::Integer(b)) => a.cmp(b),
106 _ => {
107 let a = self.to_string();
108 let b = other.to_string();
109 a.cmp(&b)
110 }
111 }
112 }
113}
114
115impl Display for SuggestionValue {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 SuggestionValue::Text(text) => write!(f, "{text}"),
119 SuggestionValue::Integer(value) => write!(f, "{value}"),
120 }
121 }
122}
123
124impl Ord for SuggestionValue {
125 fn cmp(&self, other: &Self) -> Ordering {
126 match (self, other) {
127 (SuggestionValue::Text(a), SuggestionValue::Text(b)) => a.cmp(b),
128 (SuggestionValue::Integer(a), SuggestionValue::Integer(b)) => a.cmp(b),
129 _ => {
130 let a = self.to_string();
131 let b = other.to_string();
132 a.cmp(&b)
133 }
134 }
135 }
136}
137impl PartialOrd for SuggestionValue {
138 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
139 Some(self.cmp(other))
140 }
141}
142
143#[cfg(feature = "azalea-buf")]
144impl AzaleaWrite for Suggestion {
145 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
146 self.value.to_string().azalea_write(buf)?;
147 self.tooltip
148 .clone()
149 .map(FormattedText::from)
150 .azalea_write(buf)?;
151 Ok(())
152 }
153}