1use ratatui::{
2 buffer::Buffer,
3 layout::Rect,
4 style::{Modifier, Style},
5 text::{Line, Span},
6 widgets::{Block, Borders, Paragraph, Widget},
7};
8
9use crate::input::editor::Editor;
10use crate::ui::{syntax_highlight, theme};
11
12pub struct RegexInput<'a> {
13 pub editor: &'a Editor,
14 pub focused: bool,
15 pub error: Option<&'a str>,
16}
17
18impl<'a> Widget for RegexInput<'a> {
19 fn render(self, area: Rect, buf: &mut Buffer) {
20 let border_style = if self.focused {
21 Style::default().fg(theme::BLUE)
22 } else {
23 Style::default().fg(theme::OVERLAY)
24 };
25
26 let title = if let Some(err) = self.error {
27 let truncated: String = err
28 .chars()
29 .take((area.width as usize).saturating_sub(10))
30 .collect();
31 format!(" Pattern (Error: {truncated}) ")
32 } else {
33 " Pattern ".to_string()
34 };
35
36 let title_style = if self.error.is_some() {
37 Style::default().fg(theme::RED)
38 } else {
39 Style::default().fg(theme::TEXT)
40 };
41
42 let block = Block::default()
43 .borders(Borders::ALL)
44 .border_style(border_style)
45 .title(Span::styled(title, title_style));
46
47 let content = self.editor.content();
48 let tokens = syntax_highlight::highlight(content);
49 let spans = if tokens.is_empty() {
50 vec![Span::styled(
51 content.to_string(),
52 Style::default().fg(theme::TEXT),
53 )]
54 } else {
55 syntax_highlight::build_highlighted_spans(content, &tokens)
56 };
57 let line = Line::from(spans);
58
59 let paragraph = Paragraph::new(line)
60 .block(block)
61 .style(Style::default().bg(theme::BASE));
62
63 paragraph.render(area, buf);
64
65 if self.focused {
67 let cursor_x = area.x + 1 + self.editor.visual_cursor() as u16;
68 let cursor_y = area.y + 1;
69 if cursor_x < area.x + area.width.saturating_sub(1)
70 && cursor_y < area.y + area.height.saturating_sub(1)
71 {
72 if let Some(cell) = buf.cell_mut((cursor_x, cursor_y)) {
73 cell.set_style(
74 Style::default()
75 .fg(theme::BASE)
76 .bg(theme::TEXT)
77 .add_modifier(Modifier::BOLD),
78 );
79 }
80 }
81 }
82 }
83}