azalea_brigadier/suggestion/
mod.rs

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