intelli_shell/widgets/items/
variable.rs1use ratatui::{
2 backend::FromCrossterm,
3 buffer::Buffer,
4 layout::{Rect, Size},
5 style::Style,
6 text::Line,
7 widgets::Widget,
8};
9
10use crate::{
11 config::Theme,
12 model::{VariableSuggestion, VariableValue},
13 utils::format_env_var,
14 widgets::{CustomListItem, CustomTextArea},
15};
16
17const SECRET_VARIABLE_TITLE: &str = "(secret)";
18const NEW_VARIABLE_TITLE: &str = "(new)";
19const EDIT_VARIABLE_TITLE: &str = "(edit)";
20
21#[derive(PartialEq, Eq)]
23pub enum VariableSuggestionItemIdentifier {
24 New,
25 Previous(String),
26 Environment(String),
27 Existing(Option<i32>, String),
28 Completion(String),
29 Derived(String),
30}
31
32#[derive(Clone)]
34pub enum VariableSuggestionItem<'a> {
35 New {
36 sort_index: u8,
37 is_secret: bool,
38 textarea: CustomTextArea<'a>,
39 },
40 Previous {
41 sort_index: u8,
42 value: String,
43 score: f64,
44 },
45 Environment {
46 sort_index: u8,
47 content: String,
48 is_value: bool,
49 score: f64,
50 },
51 Existing {
52 sort_index: u8,
53 value: VariableValue,
54 score: f64,
55 completion_merged: bool,
56 editing: Option<CustomTextArea<'a>>,
57 },
58 Completion {
59 sort_index: u8,
60 value: String,
61 score: f64,
62 },
63 Derived {
64 sort_index: u8,
65 value: String,
66 },
67}
68
69impl<'a> VariableSuggestionItem<'a> {
70 pub fn identifier(&self) -> VariableSuggestionItemIdentifier {
72 match self {
73 VariableSuggestionItem::New { .. } => VariableSuggestionItemIdentifier::New,
74 VariableSuggestionItem::Previous { value, .. } => VariableSuggestionItemIdentifier::Previous(value.clone()),
75 VariableSuggestionItem::Environment { content, .. } => {
76 VariableSuggestionItemIdentifier::Environment(content.clone())
77 }
78 VariableSuggestionItem::Existing { value, .. } => {
79 VariableSuggestionItemIdentifier::Existing(value.id, value.value.clone())
80 }
81 VariableSuggestionItem::Completion { value, .. } => {
82 VariableSuggestionItemIdentifier::Completion(value.clone())
83 }
84 VariableSuggestionItem::Derived { value, .. } => VariableSuggestionItemIdentifier::Derived(value.clone()),
85 }
86 }
87
88 pub fn enter_edit_mode(&mut self) {
90 if let VariableSuggestionItem::Existing { value, editing, .. } = self
91 && editing.is_none()
92 {
93 *editing = Some(
94 CustomTextArea::new(Style::default(), true, false, value.value.clone())
95 .title(EDIT_VARIABLE_TITLE)
96 .focused(),
97 );
98 }
99 }
100
101 pub fn sort_index(&self) -> u8 {
102 match self {
103 VariableSuggestionItem::New { sort_index, .. }
104 | VariableSuggestionItem::Previous { sort_index, .. }
105 | VariableSuggestionItem::Environment { sort_index, .. }
106 | VariableSuggestionItem::Existing { sort_index, .. }
107 | VariableSuggestionItem::Completion { sort_index, .. }
108 | VariableSuggestionItem::Derived { sort_index, .. } => *sort_index,
109 }
110 }
111
112 pub fn score(&self) -> f64 {
113 match self {
114 VariableSuggestionItem::Previous { score, .. }
115 | VariableSuggestionItem::Environment { score, .. }
116 | VariableSuggestionItem::Existing { score, .. }
117 | VariableSuggestionItem::Completion { score, .. } => *score,
118 _ => 0.0,
119 }
120 }
121}
122
123impl<'a> From<(u8, VariableSuggestion, f64)> for VariableSuggestionItem<'a> {
124 fn from((sort_index, suggestion, score): (u8, VariableSuggestion, f64)) -> Self {
125 match suggestion {
126 VariableSuggestion::Secret => Self::New {
127 sort_index,
128 is_secret: true,
129 textarea: CustomTextArea::new(Style::default(), true, false, "")
130 .title(SECRET_VARIABLE_TITLE)
131 .focused(),
132 },
133 VariableSuggestion::New => Self::New {
134 sort_index,
135 is_secret: false,
136 textarea: CustomTextArea::new(Style::default(), true, false, "")
137 .title(NEW_VARIABLE_TITLE)
138 .focused(),
139 },
140 VariableSuggestion::Previous(value) => Self::Previous {
141 sort_index,
142 value,
143 score,
144 },
145 VariableSuggestion::Environment { env_var_name, value } => {
146 if let Some(value) = value {
147 Self::Environment {
148 sort_index,
149 content: value,
150 is_value: true,
151 score,
152 }
153 } else {
154 Self::Environment {
155 sort_index,
156 content: format_env_var(env_var_name),
157 is_value: false,
158 score,
159 }
160 }
161 }
162 VariableSuggestion::Existing(value) => Self::Existing {
163 sort_index,
164 value,
165 score,
166 completion_merged: false,
167 editing: None,
168 },
169 VariableSuggestion::Completion(value) => Self::Completion {
170 sort_index,
171 value,
172 score,
173 },
174 VariableSuggestion::Derived(value) => Self::Derived { sort_index, value },
175 }
176 }
177}
178
179impl<'i> CustomListItem for VariableSuggestionItem<'i> {
180 type Widget<'w>
181 = VariableSuggestionItemWidget<'w>
182 where
183 'i: 'w;
184
185 fn as_widget<'a>(
186 &'a self,
187 theme: &Theme,
188 inline: bool,
189 is_highlighted: bool,
190 is_discarded: bool,
191 ) -> (Self::Widget<'a>, Size) {
192 match self {
193 VariableSuggestionItem::New { textarea, .. }
194 | VariableSuggestionItem::Existing {
195 editing: Some(textarea),
196 ..
197 } => {
198 let style = match (is_highlighted, is_discarded) {
199 (true, true) => theme.highlight_secondary_full(),
200 (true, false) => theme.highlight_primary_full(),
201 (false, true) => theme.secondary,
202 (false, false) => theme.primary,
203 };
204
205 let mut ta_render = textarea.clone();
206 ta_render.set_focus(is_highlighted);
207 ta_render.set_style(Style::from_crossterm(style));
208 (
209 VariableSuggestionItemWidget(VariableSuggestionItemWidgetInner::TextArea(ta_render)),
210 Size::new(10, 1),
211 )
212 }
213 VariableSuggestionItem::Existing {
214 value: VariableValue { value: text, .. },
215 ..
216 }
217 | VariableSuggestionItem::Previous { value: text, .. }
218 | VariableSuggestionItem::Environment { content: text, .. }
219 | VariableSuggestionItem::Completion { value: text, .. }
220 | VariableSuggestionItem::Derived { value: text, .. } => {
221 let (line, size) = text.as_widget(theme, inline, is_highlighted, is_discarded);
222 (
223 VariableSuggestionItemWidget(VariableSuggestionItemWidgetInner::Literal(line)),
224 size,
225 )
226 }
227 }
228 }
229}
230
231pub struct VariableSuggestionItemWidget<'a>(VariableSuggestionItemWidgetInner<'a>);
233#[allow(clippy::large_enum_variant)]
234enum VariableSuggestionItemWidgetInner<'a> {
235 TextArea(CustomTextArea<'a>),
236 Literal(Line<'a>),
237}
238
239impl<'a> Widget for VariableSuggestionItemWidget<'a> {
240 fn render(self, area: Rect, buf: &mut Buffer)
241 where
242 Self: Sized,
243 {
244 match self.0 {
245 VariableSuggestionItemWidgetInner::TextArea(ta) => ta.render(area, buf),
246 VariableSuggestionItemWidgetInner::Literal(l) => l.render(area, buf),
247 }
248 }
249}