intelli_shell/widgets/
completion.rs

1use ratatui::{
2    buffer::Buffer,
3    layout::{Rect, Size},
4    style::Style,
5    text::{Line, Span, Text},
6    widgets::Widget,
7};
8
9use crate::{config::Theme, model::VariableCompletion};
10
11const DEFAULT_STYLE: Style = Style::new();
12
13/// Widget to render a [`VariableCompletion`]
14#[derive(Clone)]
15pub struct VariableCompletionWidget<'a>(Text<'a>, Size);
16impl<'a> VariableCompletionWidget<'a> {
17    /// Builds a new [`VariableCompletionWidget`]
18    pub fn new(
19        completion: &'a VariableCompletion,
20        theme: &Theme,
21        is_highlighted: bool,
22        is_discarded: bool,
23        plain_style: bool,
24        full_content: bool,
25    ) -> Self {
26        let mut line_style = DEFAULT_STYLE;
27        if is_highlighted && let Some(bg_color) = theme.highlight {
28            line_style = line_style.bg(bg_color.into());
29        }
30        // Determine the right styles to use based on highlighted and discarded status
31        let (primary_style, secondary_style) = match (plain_style, is_discarded, is_highlighted) {
32            // Discarded
33            (_, true, false) => (theme.secondary, theme.secondary),
34            // Discarded & highlighted
35            (_, true, true) => (theme.highlight_secondary, theme.highlight_secondary),
36            // Plain style, regular
37            (true, false, false) => (theme.primary, theme.primary),
38            // Plain style, highlighted
39            (true, false, true) => (theme.highlight_primary, theme.highlight_primary),
40            // Regular
41            (false, false, false) => (theme.primary, theme.secondary),
42            // Highlighted
43            (false, false, true) => (theme.highlight_primary, theme.highlight_secondary),
44        };
45
46        // Setup the parts always present: variable and provider
47        let mut parts = vec![
48            Span::styled(&completion.variable, primary_style),
49            Span::styled(": ", primary_style),
50            Span::styled(&completion.suggestions_provider, secondary_style),
51        ];
52
53        // If the full content has to be rendered
54        if full_content {
55            // Include the prefix
56            parts.insert(0, Span::styled("$ ", primary_style));
57            // And the root command for non-global completions
58            if !completion.is_global() {
59                parts.insert(1, Span::styled("(", primary_style));
60                parts.insert(2, Span::styled(&completion.root_cmd, primary_style));
61                parts.insert(3, Span::styled(") ", primary_style));
62            }
63        }
64
65        let text = Text::from(vec![Line::from(parts)]).style(line_style);
66        let width = text.width() as u16;
67        let height = text.height() as u16;
68        VariableCompletionWidget(text, Size::new(width, height))
69    }
70
71    /// Retrieves the size of this widget
72    pub fn size(&self) -> Size {
73        self.1
74    }
75}
76
77impl<'a> Widget for VariableCompletionWidget<'a> {
78    fn render(self, area: Rect, buf: &mut Buffer)
79    where
80        Self: Sized,
81    {
82        self.0.render(area, buf);
83    }
84}