azalea_brigadier/suggestion/
mod.rs

1mod 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/// A suggestion given to the user for what they might want to type next.
25///
26/// The `M` generic is the type of the tooltip, so for example a `String` or
27/// just `()` if you don't care about it.
28#[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}