1use crate::highlight_map::HighlightId;
2use std::ops::Range;
3
4#[derive(Debug, Clone)]
5pub struct Symbol {
6 pub name: String,
7 pub kind: lsp::SymbolKind,
8 pub container_name: Option<String>,
9}
10
11#[derive(Clone, Debug, Default, PartialEq, Eq)]
12pub struct CodeLabel {
13 /// The text to display.
14 pub text: String,
15 /// Syntax highlighting runs.
16 pub runs: Vec<(Range<usize>, HighlightId)>,
17 /// The portion of the text that should be used in fuzzy filtering.
18 pub filter_range: Range<usize>,
19}
20
21#[derive(Clone, Debug, Default, PartialEq, Eq)]
22pub struct CodeLabelBuilder {
23 /// The text to display.
24 text: String,
25 /// Syntax highlighting runs.
26 runs: Vec<(Range<usize>, HighlightId)>,
27 /// The portion of the text that should be used in fuzzy filtering.
28 filter_range: Range<usize>,
29}
30
31impl CodeLabel {
32 pub fn plain(text: String, filter_text: Option<&str>) -> Self {
33 Self::filtered(text.clone(), text.len(), filter_text, Vec::new())
34 }
35
36 pub fn filtered(
37 text: String,
38 label_len: usize,
39 filter_text: Option<&str>,
40 runs: Vec<(Range<usize>, HighlightId)>,
41 ) -> Self {
42 assert!(label_len <= text.len());
43 let filter_range = filter_text
44 .and_then(|filter| text.find(filter).map(|index| index..index + filter.len()))
45 .unwrap_or(0..label_len);
46 Self::new(text, filter_range, runs)
47 }
48
49 pub fn new(
50 text: String,
51 filter_range: Range<usize>,
52 runs: Vec<(Range<usize>, HighlightId)>,
53 ) -> Self {
54 assert!(
55 text.get(filter_range.clone()).is_some(),
56 "invalid filter range"
57 );
58 runs.iter().for_each(|(range, _)| {
59 assert!(
60 text.get(range.clone()).is_some(),
61 "invalid run range with inputs. Requested range {range:?} in text '{text}'",
62 );
63 });
64 Self {
65 runs,
66 filter_range,
67 text,
68 }
69 }
70
71 pub fn text(&self) -> &str {
72 self.text.as_str()
73 }
74
75 pub fn filter_text(&self) -> &str {
76 &self.text[self.filter_range.clone()]
77 }
78}
79
80impl From<String> for CodeLabel {
81 fn from(value: String) -> Self {
82 Self::plain(value, None)
83 }
84}
85
86impl From<&str> for CodeLabel {
87 fn from(value: &str) -> Self {
88 Self::plain(value.to_string(), None)
89 }
90}
91
92impl CodeLabelBuilder {
93 pub fn respan_filter_range(&mut self, filter_text: Option<&str>) {
94 self.filter_range = filter_text
95 .and_then(|filter| {
96 self.text
97 .find(filter)
98 .map(|index| index..index + filter.len())
99 })
100 .unwrap_or(0..self.text.len());
101 }
102
103 pub fn push_str(&mut self, text: &str, highlight: Option<HighlightId>) {
104 let start_index = self.text.len();
105 self.text.push_str(text);
106 if let Some(highlight) = highlight {
107 let end_index = self.text.len();
108 self.runs.push((start_index..end_index, highlight));
109 }
110 }
111
112 pub fn build(mut self) -> CodeLabel {
113 if self.filter_range.end == 0 {
114 self.respan_filter_range(None);
115 }
116 CodeLabel {
117 text: self.text,
118 runs: self.runs,
119 filter_range: self.filter_range,
120 }
121 }
122}