1use std::io::stdout;
2use std::io::Result;
3
4use crossterm::cursor::position;
5use crossterm::cursor::SetCursorStyle;
6use crossterm::event;
7use crossterm::event::DisableBracketedPaste;
8use crossterm::event::DisableFocusChange;
9use crossterm::event::DisableMouseCapture;
10use crossterm::event::EnableBracketedPaste;
11use crossterm::event::EnableFocusChange;
12use crossterm::event::EnableMouseCapture;
13use crossterm::event::Event;
14use crossterm::event::KeyCode;
15use crossterm::event::KeyEventKind;
16use crossterm::event::KeyModifiers;
17use crossterm::event::KeyboardEnhancementFlags;
18use crossterm::event::PopKeyboardEnhancementFlags;
19use crossterm::event::PushKeyboardEnhancementFlags;
20use crossterm::execute;
21use crossterm::terminal;
22
23use crate::completion::Suggestion;
24use crate::editor::Editor;
25use crate::event::EditCommand;
26use crate::event::LineEditorEvent;
27use crate::event::MovementCommand;
28use crate::input_filter::filter_input;
29use crate::input_filter::InputFilter;
30use crate::keybindings::KeyCombination;
31use crate::keybindings::Keybindings;
32use crate::style::Style;
33use crate::styled_editor_view::StyledEditorView;
34use crate::AutoPair;
35use crate::Completer;
36use crate::DropDownListView;
37use crate::Highlighter;
38use crate::Hinter;
39use crate::ListView;
40use crate::Prompt;
41use crate::DEFAULT_PAIRS;
42
43#[derive(Debug)]
45pub enum LineEditorResult {
46 Success(String),
48 Interrupted,
50 EndTerminalSession,
52}
53
54enum EventStatus {
56 #[allow(dead_code)]
57 GeneralHandled,
59 EditHandled,
61 MovementHandled,
63 SelectionHandled,
65 AutoCompleteHandled,
67 Inapplicable,
69 Exits(LineEditorResult),
71}
72
73pub struct LineEditor {
75 prompt: Box<dyn Prompt>,
76 editor: Editor,
77 input_filter: InputFilter,
78 styled_editor_text: StyledEditorView,
79 keybindings: Keybindings,
80 auto_pair: Option<Box<dyn AutoPair>>,
81 highlighters: Vec<Box<dyn Highlighter>>,
82 hinters: Vec<Box<dyn Hinter>>,
83
84 completer: Option<Box<dyn Completer>>,
85 auto_complete_view: Box<dyn ListView<Suggestion>>,
86
87 cursor_style: Option<SetCursorStyle>,
88 selection_style: Option<Style>,
89 selected_start: u16,
90 selected_end: u16,
91 enable_surround_selection: bool,
92}
93
94impl LineEditor {
95 #[must_use]
97 pub fn new(prompt: Box<dyn Prompt>) -> Self {
98 LineEditor {
99 prompt,
100 editor: Editor::default(),
101 input_filter: InputFilter::Text,
102 styled_editor_text: StyledEditorView::default(),
103 keybindings: Keybindings::default(),
104 auto_pair: None,
105 highlighters: vec![],
106 hinters: vec![],
107 completer: None,
108 auto_complete_view: Box::<DropDownListView>::default(),
109 cursor_style: None,
110
111 selection_style: None,
112 selected_start: 0,
113 selected_end: 0,
114 enable_surround_selection: false,
115 }
116 }
117
118 pub fn read_line(&mut self) -> Result<LineEditorResult> {
123 if let Some(cursor_style) = self.cursor_style {
124 self.styled_editor_text.set_cursor_style(cursor_style)?;
125 }
126
127 terminal::enable_raw_mode()?;
128 execute!(
129 stdout(),
130 EnableBracketedPaste,
131 EnableFocusChange,
132 EnableMouseCapture,
133 PushKeyboardEnhancementFlags(
134 KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
135 | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
136 | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
137 )
138 )?;
139
140 let result = self.read_line_helper();
141
142 terminal::disable_raw_mode()?;
143 execute!(
144 stdout(),
145 DisableBracketedPaste,
146 PopKeyboardEnhancementFlags,
147 DisableFocusChange,
148 DisableMouseCapture
149 )?;
150
151 let default_cursor_style = SetCursorStyle::DefaultUserShape;
152 self.styled_editor_text
153 .set_cursor_style(default_cursor_style)?;
154 result
155 }
156
157 pub fn set_visual_selection_style(&mut self, style: Option<Style>) {
159 self.selection_style = style;
160 }
161
162 pub fn editor(&mut self) -> &mut Editor {
164 &mut self.editor
165 }
166
167 pub fn keybinding(&mut self) -> &mut Keybindings {
169 &mut self.keybindings
170 }
171
172 pub fn set_input_filter(&mut self, input_filter: InputFilter) {
174 self.input_filter = input_filter;
175 }
176
177 pub fn set_auto_pair(&mut self, auto_pair: Option<Box<dyn AutoPair>>) {
179 self.auto_pair = auto_pair
180 }
181
182 pub fn set_cursor_style(&mut self, style: Option<SetCursorStyle>) {
185 self.cursor_style = style;
186 }
187
188 pub fn highlighters(&mut self) -> &mut Vec<Box<dyn Highlighter>> {
190 &mut self.highlighters
191 }
192
193 pub fn add_highlighter(&mut self, highlighter: Box<dyn Highlighter>) {
195 self.highlighters.push(highlighter);
196 }
197
198 pub fn clear_highlighters(&mut self) {
200 self.highlighters.clear();
201 }
202
203 pub fn hinters(&mut self) -> &mut Vec<Box<dyn Hinter>> {
205 &mut self.hinters
206 }
207
208 pub fn add_hinter(&mut self, hinter: Box<dyn Hinter>) {
210 self.hinters.push(hinter);
211 }
212
213 pub fn clear_hinters(&mut self) {
215 self.hinters.clear();
216 }
217
218 pub fn set_completer(&mut self, completer: Box<dyn Completer>) {
220 self.completer = Some(completer);
221 }
222
223 pub fn clear_completer(&mut self) {
225 self.completer = None
226 }
227
228 pub fn set_auto_complete_view(&mut self, auto_complete_view: Box<dyn ListView<Suggestion>>) {
230 self.auto_complete_view = auto_complete_view;
231 }
232
233 pub fn enable_surround_selection(&mut self, enable: bool) {
235 self.enable_surround_selection = enable;
236 }
237
238 fn read_line_helper(&mut self) -> Result<LineEditorResult> {
241 let mut lineeditor_events: Vec<LineEditorEvent> = vec![];
242
243 let prompt_buffer = self.prompt.prompt();
244 let prompt_len = prompt_buffer.len() as u16;
245
246 let row_start = position().unwrap().1;
247 self.styled_editor_text
248 .set_start_position((prompt_len, row_start));
249 self.styled_editor_text
250 .render_prompt_buffer(&prompt_buffer)?;
251
252 'main: loop {
253 loop {
254 match event::read()? {
255 Event::Key(key_event) => match key_event.code {
256 KeyCode::Char(ch) => {
257 if (key_event.modifiers == KeyModifiers::NONE
258 || key_event.modifiers == KeyModifiers::SHIFT)
259 && key_event.kind == KeyEventKind::Press
260 {
261 if filter_input(ch, &self.input_filter) {
262 let commands = vec![EditCommand::InsertChar(ch)];
263 let edit_command = LineEditorEvent::Edit(commands);
264 lineeditor_events.push(edit_command);
265 }
266 break;
267 }
268
269 let key_combination = KeyCombination::from(key_event);
270 if let Some(command) = self.keybindings.find_binding(key_combination) {
271 lineeditor_events.push(command);
272 break;
273 }
274 }
275 _ => {
276 let key_combination = KeyCombination::from(key_event);
277 if let Some(command) = self.keybindings.find_binding(key_combination) {
278 lineeditor_events.push(command);
279 break;
280 }
281 }
282 },
283 Event::Paste(string) => {
284 lineeditor_events.push(LineEditorEvent::Edit(vec![
285 EditCommand::InsertString(string),
286 ]));
287 break;
288 }
289 _ => {}
290 }
291 }
292
293 let buffer_len_before = self.editor.styled_buffer().len();
295
296 for event in lineeditor_events.drain(..) {
298 match self.handle_editor_event(&event)? {
299 EventStatus::AutoCompleteHandled => {
300 continue 'main;
301 }
302 EventStatus::Inapplicable => {
303 continue 'main;
304 }
305 EventStatus::Exits(result) => return Ok(result),
306 _ => {}
307 }
308 }
309
310 if buffer_len_before < self.editor.styled_buffer().len() {
312 if let Some(auto_pair) = &self.auto_pair {
314 auto_pair.complete_pair(self.editor.styled_buffer());
315 }
316 }
317
318 self.editor.styled_buffer().reset_styles();
320
321 for highlighter in &self.highlighters {
323 highlighter.highlight(self.editor.styled_buffer());
324 }
325
326 self.apply_visual_selection();
328
329 self.styled_editor_text
331 .render_line_buffer(self.editor.styled_buffer())?;
332
333 if self.editor.styled_buffer().position() == self.editor.styled_buffer().len() {
335 for hinter in &self.hinters {
336 if let Some(hint) = hinter.hint(self.editor.styled_buffer()) {
337 self.styled_editor_text.render_hint(&hint)?;
338 break;
339 }
340 }
341 }
342 }
343 }
344
345 fn handle_editor_event(&mut self, event: &LineEditorEvent) -> Result<EventStatus> {
347 match event {
348 LineEditorEvent::Edit(commands) => {
349 for command in commands {
350 if self.enable_surround_selection && self.selected_start != self.selected_end {
351 if let EditCommand::InsertChar(c) = &command {
352 for (key, value) in DEFAULT_PAIRS {
353 if key == c {
354 self.apply_surround_selection(*key, *value);
355 return Ok(EventStatus::EditHandled);
356 }
357 }
358 }
359 }
360 self.editor.run_edit_commands(command);
361 }
362 self.reset_selection_range();
363 Ok(EventStatus::EditHandled)
364 }
365 LineEditorEvent::Movement(commands) => {
366 for command in commands {
367 self.editor.run_movement_commands(command);
368 }
369 self.reset_selection_range();
370 Ok(EventStatus::MovementHandled)
371 }
372 LineEditorEvent::Enter => {
373 if self.auto_complete_view.is_visible() {
374 if let Some(suggestion) = self.auto_complete_view.selected_element() {
375 let literal = &suggestion.content.literal();
376 let span = &suggestion.span;
377
378 let delete_command = EditCommand::DeleteSpan(span.start, span.end);
379 self.editor.run_edit_commands(&delete_command);
380
381 let insert_command = EditCommand::InsertString(literal.to_string());
382 self.editor.run_edit_commands(&insert_command);
383
384 self.auto_complete_view.clear()?;
385 self.auto_complete_view.set_visibility(false);
386 return Ok(EventStatus::SelectionHandled);
387 }
388 }
389
390 let buffer = self.editor.styled_buffer().buffer().iter().collect();
391 self.reset_selection_range();
392
393 self.editor.styled_buffer().clear();
394
395 Ok(EventStatus::Exits(LineEditorResult::Success(buffer)))
396 }
397 LineEditorEvent::Up => {
398 if self.auto_complete_view.is_visible() {
399 self.auto_complete_view.focus_previous();
400 self.auto_complete_view.render()?;
401 return Ok(EventStatus::AutoCompleteHandled);
402 }
403 Ok(EventStatus::Inapplicable)
404 }
405 LineEditorEvent::Down => {
406 if self.auto_complete_view.is_visible() {
407 self.auto_complete_view.focus_next();
408 self.auto_complete_view.clear()?;
409 self.auto_complete_view.render()?;
410 return Ok(EventStatus::AutoCompleteHandled);
411 }
412 Ok(EventStatus::Inapplicable)
413 }
414 LineEditorEvent::Left => {
415 self.editor
416 .run_movement_commands(&MovementCommand::MoveLeftChar);
417 self.reset_selection_range();
418 Ok(EventStatus::MovementHandled)
419 }
420 LineEditorEvent::Right => {
421 self.editor
422 .run_movement_commands(&MovementCommand::MoveRightChar);
423 self.reset_selection_range();
424 Ok(EventStatus::MovementHandled)
425 }
426 LineEditorEvent::Delete => {
427 if self.selected_start != self.selected_end {
428 self.delete_selected_text();
429 } else {
430 self.editor.run_edit_commands(&EditCommand::DeleteRightChar)
431 }
432 Ok(EventStatus::EditHandled)
433 }
434 LineEditorEvent::Backspace => {
435 if self.selected_start != self.selected_end {
436 self.delete_selected_text();
437 } else {
438 self.editor.run_edit_commands(&EditCommand::DeleteLeftChar)
439 }
440 Ok(EventStatus::EditHandled)
441 }
442 LineEditorEvent::SelectLeft => {
443 if self.selected_end < 1 {
444 Ok(EventStatus::Inapplicable)
445 } else {
446 self.selected_end -= 1;
447 Ok(EventStatus::SelectionHandled)
448 }
449 }
450 LineEditorEvent::SelectRight => {
451 if self.selected_end as usize > self.editor.styled_buffer().len() {
452 Ok(EventStatus::Inapplicable)
453 } else {
454 self.selected_end += 1;
455 Ok(EventStatus::SelectionHandled)
456 }
457 }
458 LineEditorEvent::SelectAll => {
459 self.selected_start = 0;
460 self.selected_end = self.editor.styled_buffer().len() as u16;
461 Ok(EventStatus::SelectionHandled)
462 }
463 LineEditorEvent::CutSelected => {
464 Ok(EventStatus::Inapplicable)
482 }
483 LineEditorEvent::CopySelected => {
484 Ok(EventStatus::Inapplicable)
499 }
500 LineEditorEvent::Paste => {
501 Ok(EventStatus::Inapplicable)
516 }
517 LineEditorEvent::ToggleAutoComplete => {
518 if self.auto_complete_view.is_visible() {
519 self.auto_complete_view.clear()?;
520 self.auto_complete_view.set_visibility(false);
521 return Ok(EventStatus::Inapplicable);
522 }
523
524 if let Some(completer) = &self.completer {
525 let mut suggestions = completer.complete(self.editor.styled_buffer());
526 if !suggestions.is_empty() {
527 let prompt_width = self.prompt.prompt().len() as u16;
528 let (_, row) = position()?;
529
530 let mut style = Style::default();
531 style.set_background_color(crossterm::style::Color::Blue);
532 self.auto_complete_view.set_focus_style(style);
533
534 self.auto_complete_view.reset();
535 self.auto_complete_view.set_elements(&mut suggestions);
536 self.auto_complete_view.clear()?;
537 self.auto_complete_view.render()?;
538 self.auto_complete_view.set_visibility(true);
539
540 let auto_complete_height = self.auto_complete_view.len();
541 let (_, max_row) = terminal::size()?;
542
543 if row + auto_complete_height as u16 > max_row {
544 let new_start_row = max_row - 2 - self.auto_complete_view.len() as u16;
545 self.styled_editor_text
546 .set_start_position((prompt_width, new_start_row));
547 }
548
549 return Ok(EventStatus::AutoCompleteHandled);
550 }
551
552 return Ok(EventStatus::Inapplicable);
553 }
554
555 Ok(EventStatus::Inapplicable)
556 }
557 _ => Ok(EventStatus::Inapplicable),
558 }
559 }
560
561 fn apply_visual_selection(&mut self) {
563 if self.selected_start == self.selected_end {
564 return;
565 }
566
567 if let Some(style) = &self.selection_style {
569 let styled_buffer = self.editor.styled_buffer();
570 let from = usize::min(self.selected_start.into(), self.selected_end.into());
572 let to = usize::max(self.selected_start.into(), self.selected_end.into());
573 styled_buffer.style_range(from, to, style.clone());
574 }
575 }
576
577 fn apply_surround_selection(&mut self, start: char, end: char) {
579 let from = usize::min(self.selected_start.into(), self.selected_end.into());
580 let to = usize::max(self.selected_start.into(), self.selected_end.into());
581
582 let editor = self.editor.styled_buffer();
583 editor.set_position(from);
584 editor.insert_char(start);
585 editor.set_position(to + 1);
586 editor.insert_char(end);
587 editor.set_position(from);
588 }
589
590 fn delete_selected_text(&mut self) {
592 if self.selected_start == self.selected_end {
593 return;
594 }
595
596 let from = usize::min(self.selected_start.into(), self.selected_end.into());
597 let to = usize::max(self.selected_start.into(), self.selected_end.into());
598 let delete_selection = EditCommand::DeleteSpan(from, to);
599 self.editor.run_edit_commands(&delete_selection);
600 self.editor.styled_buffer().set_position(from);
601 self.reset_selection_range();
602 }
603
604 fn reset_selection_range(&mut self) {
606 let position = self.editor.styled_buffer().position() as u16;
607 self.selected_start = position;
608 self.selected_end = position;
609 }
610}