azalea_brigadier/suggestion/
suggestions.rs

1#[cfg(feature = "azalea-buf")]
2use std::io::{Cursor, Write};
3use std::{collections::HashSet, hash::Hash};
4
5#[cfg(feature = "azalea-buf")]
6use azalea_buf::{AzBuf, AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
7#[cfg(feature = "azalea-buf")]
8use azalea_chat::FormattedText;
9
10use super::Suggestion;
11use crate::context::StringRange;
12#[cfg(feature = "azalea-buf")]
13use crate::suggestion::SuggestionValue;
14
15#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
16pub struct Suggestions {
17    range: StringRange,
18    suggestions: Vec<Suggestion>,
19}
20
21impl Suggestions {
22    pub fn new(range: StringRange, suggestions: Vec<Suggestion>) -> Self {
23        Self { range, suggestions }
24    }
25
26    pub fn merge(command: &str, input: &[Suggestions]) -> Self {
27        if input.is_empty() {
28            return Suggestions::default();
29        } else if input.len() == 1 {
30            return input[0].clone();
31        };
32
33        let mut texts = HashSet::new();
34        for suggestions in input {
35            texts.extend(suggestions.suggestions.clone());
36        }
37
38        Suggestions::create(command, &texts)
39    }
40
41    pub fn create(command: &str, suggestions: &HashSet<Suggestion>) -> Self {
42        if suggestions.is_empty() {
43            return Suggestions::default();
44        };
45        let mut start = usize::MAX;
46        let mut end = usize::MIN;
47        for suggestion in suggestions {
48            start = suggestion.range.start().min(start);
49            end = suggestion.range.end().max(end);
50        }
51        let range = StringRange::new(start, end);
52        let mut texts = HashSet::new();
53        for suggestion in suggestions {
54            texts.insert(suggestion.expand(command, range));
55        }
56        let mut sorted = texts.into_iter().collect::<Vec<_>>();
57
58        sorted.sort_by(|a, b| a.value.cmp_ignore_case(&b.value));
59
60        Suggestions {
61            range,
62            suggestions: sorted,
63        }
64    }
65
66    pub fn is_empty(&self) -> bool {
67        self.suggestions.is_empty()
68    }
69
70    pub fn list(&self) -> &[Suggestion] {
71        &self.suggestions
72    }
73
74    pub fn range(&self) -> StringRange {
75        self.range
76    }
77}
78
79#[cfg(feature = "azalea-buf")]
80impl AzaleaRead for Suggestions {
81    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
82        #[derive(AzBuf)]
83        struct StandaloneSuggestion {
84            pub text: String,
85            pub tooltip: Option<FormattedText>,
86        }
87
88        let start = u32::azalea_read_var(buf)? as usize;
89        let length = u32::azalea_read_var(buf)? as usize;
90        let range = StringRange::between(start, start + length);
91
92        // the range of a Suggestion depends on the Suggestions containing it,
93        // so we can't just `impl AzaleaRead for Suggestion`
94        let mut suggestions = Vec::<StandaloneSuggestion>::azalea_read(buf)?
95            .into_iter()
96            .map(|s| Suggestion {
97                value: SuggestionValue::Text(s.text),
98                tooltip: s.tooltip.map(|t| t.to_string()),
99                range,
100            })
101            .collect::<Vec<_>>();
102        suggestions.sort_by(|a, b| a.value.cmp(&b.value));
103
104        Ok(Suggestions { range, suggestions })
105    }
106}
107
108#[cfg(feature = "azalea-buf")]
109impl AzaleaWrite for Suggestions {
110    fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
111        (self.range.start() as u32).azalea_write_var(buf)?;
112        (self.range.length() as u32).azalea_write_var(buf)?;
113        self.suggestions.azalea_write(buf)?;
114        Ok(())
115    }
116}