Skip to main content

intelli_shell/widgets/
template.rs

1use std::{
2    fmt,
3    ops::{Deref, DerefMut},
4};
5
6use ratatui::{
7    backend::FromCrossterm,
8    buffer::Buffer,
9    layout::Rect,
10    style::Style,
11    text::{Line, Span},
12    widgets::{Block, Borders, Widget},
13};
14
15use crate::{
16    config::Theme,
17    model::{CommandTemplate, TemplatePart},
18};
19
20/// The widget for a command containing variables
21#[derive(Clone)]
22#[cfg_attr(test, derive(Debug))]
23pub struct CommandTemplateWidget {
24    /// The command template
25    pub template: CommandTemplate,
26    /// The index of the current variable being edited (for highlighting)
27    pub current_variable_index: usize,
28    // Internal fields
29    block: Option<Block<'static>>,
30    primary_style: Style,
31    secondary_style: Style,
32}
33
34impl CommandTemplateWidget {
35    /// Creates a new widget for the command template
36    pub fn new(theme: &Theme, inline: bool, template: CommandTemplate) -> Self {
37        let block = if !inline {
38            Some(
39                Block::default()
40                    .borders(Borders::ALL)
41                    .style(Style::from_crossterm(theme.primary))
42                    .title(" Command "),
43            )
44        } else {
45            None
46        };
47        Self {
48            template,
49            current_variable_index: 0,
50            block,
51            primary_style: Style::from_crossterm(theme.primary),
52            secondary_style: Style::from_crossterm(theme.secondary),
53        }
54    }
55}
56
57impl fmt::Display for CommandTemplateWidget {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        write!(f, "{}", self.template)
60    }
61}
62
63impl Widget for &CommandTemplateWidget {
64    fn render(self, mut area: Rect, buf: &mut Buffer) {
65        if let Some(block) = &self.block {
66            block.render(area, buf);
67            area = block.inner(area);
68        }
69
70        let mut variable_index = 0;
71        Line::from_iter(self.template.parts.iter().map(|p| match p {
72            TemplatePart::Text(t) => Span::styled(t, self.secondary_style),
73            TemplatePart::Variable(v) => {
74                let is_current = variable_index == self.current_variable_index;
75                variable_index += 1;
76
77                let style = if is_current {
78                    self.primary_style
79                } else {
80                    self.secondary_style
81                };
82
83                Span::styled(format!("{{{{{}}}}}", v.display), style)
84            }
85            TemplatePart::VariableValue(_v, t) => {
86                let is_current = variable_index == self.current_variable_index;
87                variable_index += 1;
88
89                let style = if is_current {
90                    self.primary_style
91                } else {
92                    self.secondary_style
93                };
94
95                Span::styled(t, style)
96            }
97        }))
98        .render(area, buf);
99    }
100}
101
102impl Deref for CommandTemplateWidget {
103    type Target = CommandTemplate;
104
105    fn deref(&self) -> &Self::Target {
106        &self.template
107    }
108}
109
110impl DerefMut for CommandTemplateWidget {
111    fn deref_mut(&mut self) -> &mut Self::Target {
112        &mut self.template
113    }
114}