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 pub error_offset: Option<usize>,
17}
18
19impl<'a> Widget for RegexInput<'a> {
20 fn render(self, area: Rect, buf: &mut Buffer) {
21 let border_style = if self.focused {
22 Style::default().fg(theme::BLUE)
23 } else {
24 Style::default().fg(theme::OVERLAY)
25 };
26
27 let title = if let Some(err) = self.error {
28 let truncated: String = err
29 .chars()
30 .take((area.width as usize).saturating_sub(10))
31 .collect();
32 format!(" Pattern (Error: {truncated}) ")
33 } else {
34 " Pattern ".to_string()
35 };
36
37 let title_style = if self.error.is_some() {
38 Style::default().fg(theme::RED)
39 } else {
40 Style::default().fg(theme::TEXT)
41 };
42
43 let block = Block::default()
44 .borders(Borders::ALL)
45 .border_style(border_style)
46 .title(Span::styled(title, title_style));
47
48 let content = self.editor.content();
49 let tokens = syntax_highlight::highlight(content);
50 let spans = if tokens.is_empty() {
51 vec![Span::styled(
52 content.to_string(),
53 Style::default().fg(theme::TEXT),
54 )]
55 } else {
56 syntax_highlight::build_highlighted_spans(content, &tokens)
57 };
58 let line = Line::from(spans);
59
60 let paragraph = Paragraph::new(line)
61 .block(block)
62 .style(Style::default().bg(theme::BASE));
63
64 paragraph.render(area, buf);
65
66 if let Some(offset) = self.error_offset {
68 let scroll = self.editor.scroll_offset();
69 if offset >= scroll {
70 let err_x = area.x + 1 + (offset - scroll) as u16;
71 let err_y = area.y + 1;
72 if err_x < area.x + area.width.saturating_sub(1)
73 && err_y < area.y + area.height.saturating_sub(1)
74 {
75 if let Some(cell) = buf.cell_mut((err_x, err_y)) {
76 cell.set_style(Style::default().fg(theme::TEXT).bg(theme::RED));
77 }
78 }
79 }
80 }
81
82 if self.focused {
84 let cursor_x = area.x + 1 + self.editor.visual_cursor() as u16;
85 let cursor_y = area.y + 1;
86 if cursor_x < area.x + area.width.saturating_sub(1)
87 && cursor_y < area.y + area.height.saturating_sub(1)
88 {
89 if let Some(cell) = buf.cell_mut((cursor_x, cursor_y)) {
90 cell.set_style(
91 Style::default()
92 .fg(theme::BASE)
93 .bg(theme::TEXT)
94 .add_modifier(Modifier::BOLD),
95 );
96 }
97 }
98 }
99 }
100}