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