1use crate::engine::{self, CompiledRegex, EngineFlags, EngineKind, RegexEngine};
2use crate::explain::{self, ExplainNode};
3use crate::input::editor::Editor;
4
5pub struct App {
6 pub regex_editor: Editor,
7 pub test_editor: Editor,
8 pub replace_editor: Editor,
9 pub focused_panel: u8, pub engine_kind: EngineKind,
11 pub flags: EngineFlags,
12 pub matches: Vec<engine::Match>,
13 pub replace_result: Option<engine::ReplaceResult>,
14 pub explanation: Vec<ExplainNode>,
15 pub error: Option<String>,
16 pub show_help: bool,
17 pub should_quit: bool,
18 pub match_scroll: u16,
19 pub replace_scroll: u16,
20 pub explain_scroll: u16,
21 engine: Box<dyn RegexEngine>,
22 compiled: Option<Box<dyn CompiledRegex>>,
23}
24
25impl App {
26 pub fn new(engine_kind: EngineKind, flags: EngineFlags) -> Self {
27 let engine = engine::create_engine(engine_kind);
28 Self {
29 regex_editor: Editor::new(),
30 test_editor: Editor::new(),
31 replace_editor: Editor::new(),
32 focused_panel: 0,
33 engine_kind,
34 flags,
35 matches: Vec::new(),
36 replace_result: None,
37 explanation: Vec::new(),
38 error: None,
39 show_help: false,
40 should_quit: false,
41 match_scroll: 0,
42 replace_scroll: 0,
43 explain_scroll: 0,
44 engine,
45 compiled: None,
46 }
47 }
48
49 pub fn set_replacement(&mut self, text: &str) {
50 self.replace_editor = Editor::with_content(text.to_string());
51 self.rereplace();
52 }
53
54 pub fn scroll_replace_up(&mut self) {
55 self.replace_scroll = self.replace_scroll.saturating_sub(1);
56 }
57
58 pub fn scroll_replace_down(&mut self) {
59 self.replace_scroll = self.replace_scroll.saturating_add(1);
60 }
61
62 pub fn rereplace(&mut self) {
63 let template = self.replace_editor.content().to_string();
64 if template.is_empty() || self.matches.is_empty() {
65 self.replace_result = None;
66 return;
67 }
68 let text = self.test_editor.content().to_string();
69 self.replace_result = Some(engine::replace_all(&text, &self.matches, &template));
70 }
71
72 pub fn set_pattern(&mut self, pattern: &str) {
73 self.regex_editor = Editor::with_content(pattern.to_string());
74 self.recompute();
75 }
76
77 pub fn set_test_string(&mut self, text: &str) {
78 self.test_editor = Editor::with_content(text.to_string());
79 self.rematch();
80 }
81
82 pub fn switch_engine(&mut self) {
83 self.engine_kind = self.engine_kind.next();
84 self.engine = engine::create_engine(self.engine_kind);
85 self.recompute();
86 }
87
88 pub fn scroll_match_up(&mut self) {
89 self.match_scroll = self.match_scroll.saturating_sub(1);
90 }
91
92 pub fn scroll_match_down(&mut self) {
93 self.match_scroll = self.match_scroll.saturating_add(1);
94 }
95
96 pub fn scroll_explain_up(&mut self) {
97 self.explain_scroll = self.explain_scroll.saturating_sub(1);
98 }
99
100 pub fn scroll_explain_down(&mut self) {
101 self.explain_scroll = self.explain_scroll.saturating_add(1);
102 }
103
104 pub fn recompute(&mut self) {
105 let pattern = self.regex_editor.content().to_string();
106 self.match_scroll = 0;
107 self.explain_scroll = 0;
108
109 if pattern.is_empty() {
110 self.compiled = None;
111 self.matches.clear();
112 self.explanation.clear();
113 self.error = None;
114 return;
115 }
116
117 match self.engine.compile(&pattern, &self.flags) {
119 Ok(compiled) => {
120 self.compiled = Some(compiled);
121 self.error = None;
122 }
123 Err(e) => {
124 self.compiled = None;
125 self.matches.clear();
126 self.error = Some(e.to_string());
127 }
128 }
129
130 match explain::explain(&pattern) {
132 Ok(nodes) => self.explanation = nodes,
133 Err(e) => {
134 self.explanation.clear();
135 if self.error.is_none() {
136 self.error = Some(e);
137 }
138 }
139 }
140
141 self.rematch();
143 }
144
145 pub fn rematch(&mut self) {
146 self.match_scroll = 0;
147 if let Some(compiled) = &self.compiled {
148 let text = self.test_editor.content().to_string();
149 if text.is_empty() {
150 self.matches.clear();
151 self.replace_result = None;
152 return;
153 }
154 match compiled.find_matches(&text) {
155 Ok(m) => self.matches = m,
156 Err(e) => {
157 self.matches.clear();
158 self.error = Some(e.to_string());
159 }
160 }
161 } else {
162 self.matches.clear();
163 }
164 self.rereplace();
165 }
166}