Skip to main content

azalea_brigadier/suggestion/
mod.rs

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