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