intelli_shell/widgets/
variable.rs1use std::{
2 borrow::Cow,
3 ops::{Deref, DerefMut},
4};
5
6use ratatui::{
7 buffer::Buffer,
8 layout::{Rect, Size},
9 style::Style,
10 text::Line,
11 widgets::Widget,
12};
13
14use crate::{
15 config::Theme,
16 model::VariableValue,
17 widgets::{AsWidget, CustomTextArea},
18};
19
20const SECRET_VARIABLE_TITLE: &str = "(secret)";
21const NEW_VARIABLE_TITLE: &str = "(new)";
22const EDIT_VARIABLE_TITLE: &str = "(edit)";
23
24#[derive(Clone)]
26pub enum VariableSuggestionRow<'a> {
27 New(NewVariableValue<'a>),
28 Environment(LiteralVariableValue<'a>, bool),
29 Existing(ExistingVariableValue<'a>),
30 Derived(LiteralVariableValue<'a>),
31}
32
33#[derive(Clone)]
34pub struct NewVariableValue<'a> {
35 highlighted: bool,
36 primary: Style,
37 highlight_primary: Style,
38 secret: bool,
39 textarea: CustomTextArea<'a>,
40}
41
42#[derive(Clone)]
43pub struct ExistingVariableValue<'a> {
44 highlighted: bool,
45 primary: Style,
46 highlight_primary: Style,
47 pub value: VariableValue,
48 pub editing: Option<CustomTextArea<'a>>,
49}
50
51#[derive(Clone)]
52pub struct LiteralVariableValue<'a> {
53 highlighted: bool,
54 primary: Style,
55 highlight_primary: Style,
56 value: Cow<'a, str>,
57}
58
59impl<'a> NewVariableValue<'a> {
60 pub fn new(theme: &Theme, secret: bool) -> Self {
61 Self {
62 highlighted: false,
63 primary: theme.primary.into(),
64 highlight_primary: theme.highlight_primary_full().into(),
65 secret,
66 textarea: CustomTextArea::new(theme.primary, true, false, "").title(if secret {
67 SECRET_VARIABLE_TITLE
68 } else {
69 NEW_VARIABLE_TITLE
70 }),
71 }
72 }
73
74 pub fn is_secret(&self) -> bool {
75 self.secret
76 }
77}
78
79impl<'a> ExistingVariableValue<'a> {
80 pub fn new(theme: &Theme, value: VariableValue) -> Self {
81 Self {
82 highlighted: false,
83 primary: theme.primary.into(),
84 highlight_primary: theme.highlight_primary_full().into(),
85 value,
86 editing: None,
87 }
88 }
89
90 pub fn enter_edit_mode(&mut self) {
91 if self.editing.is_none() {
92 self.editing = Some(
93 CustomTextArea::new(self.highlight_primary, true, false, self.value.value.clone())
94 .title(EDIT_VARIABLE_TITLE)
95 .focused(),
96 );
97 }
98 }
99}
100
101impl<'a> LiteralVariableValue<'a> {
102 pub fn new(theme: &Theme, value: impl Into<Cow<'a, str>>) -> Self {
103 Self {
104 highlighted: false,
105 primary: theme.primary.into(),
106 highlight_primary: theme.highlight_primary_full().into(),
107 value: value.into(),
108 }
109 }
110}
111
112impl<'a> Widget for &'a VariableSuggestionRow<'a> {
113 fn render(self, area: Rect, buf: &mut Buffer)
114 where
115 Self: Sized,
116 {
117 match &self {
118 VariableSuggestionRow::New(n) => n.render(area, buf),
119 VariableSuggestionRow::Existing(e) => {
120 if let Some(editing) = e.editing.as_ref() {
121 editing.render(area, buf);
122 } else {
123 Line::from(e.value.value.as_str())
124 .style(if e.highlighted { e.highlight_primary } else { e.primary })
125 .render(area, buf);
126 }
127 }
128 VariableSuggestionRow::Environment(l, _) | VariableSuggestionRow::Derived(l) => Line::from(l.as_ref())
129 .style(if l.highlighted { l.highlight_primary } else { l.primary })
130 .render(area, buf),
131 }
132 }
133}
134
135impl<'a> AsWidget for VariableSuggestionRow<'a> {
136 fn set_highlighted(&mut self, is_highlighted: bool) {
137 match self {
138 VariableSuggestionRow::New(n) => {
139 n.highlighted = is_highlighted;
140 let style = if is_highlighted { n.highlight_primary } else { n.primary };
141 n.set_focus(is_highlighted);
142 n.set_style(style);
143 }
144 VariableSuggestionRow::Existing(e) => {
145 e.highlighted = is_highlighted;
146 if let Some(ta) = e.editing.as_mut() {
147 let style = if is_highlighted { e.highlight_primary } else { e.primary };
148 ta.set_focus(is_highlighted);
149 ta.set_style(style);
150 }
151 }
152 VariableSuggestionRow::Environment(l, _) | VariableSuggestionRow::Derived(l) => {
153 l.highlighted = is_highlighted;
154 }
155 }
156 }
157
158 fn as_widget<'b>(&'b self, _is_highlighted: bool) -> (impl Widget + 'b, Size) {
159 (self, Size::new(10, 1))
160 }
161}
162
163impl<'a> From<NewVariableValue<'a>> for CustomTextArea<'a> {
164 fn from(val: NewVariableValue<'a>) -> Self {
165 val.textarea
166 }
167}
168impl<'a> Deref for NewVariableValue<'a> {
169 type Target = CustomTextArea<'a>;
170
171 fn deref(&self) -> &Self::Target {
172 &self.textarea
173 }
174}
175impl<'a> DerefMut for NewVariableValue<'a> {
176 fn deref_mut(&mut self) -> &mut Self::Target {
177 &mut self.textarea
178 }
179}
180
181impl<'a> From<ExistingVariableValue<'a>> for VariableValue {
182 fn from(val: ExistingVariableValue<'a>) -> Self {
183 val.value
184 }
185}
186
187impl<'a> From<LiteralVariableValue<'a>> for Cow<'a, str> {
188 fn from(val: LiteralVariableValue<'a>) -> Self {
189 val.value
190 }
191}
192impl<'a> Deref for LiteralVariableValue<'a> {
193 type Target = str;
194
195 fn deref(&self) -> &Self::Target {
196 self.value.as_ref()
197 }
198}