Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract screen painting stuff into a separate struct #80

Merged
merged 1 commit into from
Jul 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 40 additions & 102 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
clip_buffer::{get_default_clipboard, Clipboard},
default_emacs_keybindings,
keybindings::{default_vi_insert_keybindings, default_vi_normal_keybindings, Keybindings},
painter::Painter,
prompt::{PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, PromptViMode},
Prompt,
};
Expand All @@ -12,19 +13,12 @@ use crate::{
};
use crate::{EditCommand, EditMode, Signal, ViEngine};
use crossterm::{
cursor,
cursor::{position, MoveTo, MoveToColumn, RestorePosition, SavePosition},
cursor::position,
event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers},
style::{Color, Print, ResetColor, SetForegroundColor},
terminal::{self, Clear, ClearType},
QueueableCommand, Result,
terminal, Result,
};

use std::{
collections::HashMap,
io::{stdout, Stdout, Write},
time::Duration,
};
use std::{collections::HashMap, io::stdout, time::Duration};

/// Line editor engine
///
Expand Down Expand Up @@ -55,7 +49,7 @@ pub struct Reedline {
history_search: Option<BasicSearch>, // This could be have more features in the future (fzf, configurable?)

// Stdout
stdout: Stdout,
painter: Painter,

// Keybindings
keybindings: HashMap<EditMode, Keybindings>,
Expand Down Expand Up @@ -84,7 +78,7 @@ impl Reedline {
pub fn new() -> Reedline {
let history = History::default();
let cut_buffer = Box::new(get_default_clipboard());
let stdout = stdout();
let painter = Painter::new(stdout());
let mut keybindings_hashmap = HashMap::new();
keybindings_hashmap.insert(EditMode::Emacs, default_emacs_keybindings());
keybindings_hashmap.insert(EditMode::ViInsert, default_vi_insert_keybindings());
Expand All @@ -95,7 +89,7 @@ impl Reedline {
cut_buffer,
history,
history_search: None,
stdout,
painter,
keybindings: keybindings_hashmap,
edit_mode: EditMode::Emacs,
need_full_repaint: false,
Expand Down Expand Up @@ -177,13 +171,6 @@ impl Reedline {
Ok(())
}

pub fn move_to(&mut self, column: u16, row: u16) -> Result<()> {
self.stdout.queue(MoveTo(column, row))?;
self.stdout.flush()?;

Ok(())
}

/// Wait for input and provide the user with a specified [`Prompt`].
///
/// Returns a [`crossterm::Result`] in which the `Err` type is [`crossterm::ErrorKind`]
Expand All @@ -201,23 +188,14 @@ impl Reedline {

/// Writes `msg` to the terminal with a following carriage return and newline
pub fn print_line(&mut self, msg: &str) -> Result<()> {
self.stdout
.queue(Print(msg))?
.queue(Print("\n"))?
.queue(MoveToColumn(1))?;
self.stdout.flush()?;

Ok(())
self.painter.paint_line(msg)
}

/// Goes to the beginning of the next line
///
/// Also works in raw mode
pub fn print_crlf(&mut self) -> Result<()> {
self.stdout.queue(Print("\n"))?.queue(MoveToColumn(1))?;
self.stdout.flush()?;

Ok(())
self.painter.paint_crlf()
}

/// **For debugging purposes only:** Track the terminal events observed by [`Reedline`] and print them.
Expand Down Expand Up @@ -619,28 +597,7 @@ impl Reedline {
/// Clear the screen by printing enough whitespace to start the prompt or
/// other output back at the first line of the terminal.
pub fn clear_screen(&mut self) -> Result<()> {
let (_, num_lines) = terminal::size()?;
for _ in 0..2 * num_lines {
self.stdout.queue(Print("\n"))?;
}
self.stdout.queue(MoveTo(0, 0))?;
self.stdout.flush()?;
Ok(())
}

/// Display the complete prompt including status indicators (e.g. pwd, time)
///
/// Used at the beginning of each [`Reedline::read_line()`] call.
fn queue_prompt(&mut self, prompt: &dyn Prompt, screen_width: usize) -> Result<()> {
// print our prompt
let prompt_mode = self.prompt_edit_mode();

self.stdout
.queue(MoveToColumn(0))?
.queue(SetForegroundColor(prompt.get_prompt_color()))?
.queue(Print(prompt.render_prompt(screen_width)))?
.queue(Print(prompt.render_prompt_indicator(prompt_mode)))?
.queue(ResetColor)?;
self.painter.clear_screen()?;

Ok(())
}
Expand All @@ -652,11 +609,7 @@ impl Reedline {
fn queue_prompt_indicator(&mut self, prompt: &dyn Prompt) -> Result<()> {
// print our prompt
let prompt_mode = self.prompt_edit_mode();
self.stdout
.queue(MoveToColumn(0))?
.queue(SetForegroundColor(prompt.get_prompt_color()))?
.queue(Print(prompt.render_prompt_indicator(prompt_mode)))?
.queue(ResetColor)?;
self.painter.queue_prompt_indicator(prompt, prompt_mode)?;

Ok(())
}
Expand All @@ -665,8 +618,6 @@ impl Reedline {
///
/// Requires coordinates where the input buffer begins after the prompt.
fn buffer_paint(&mut self, prompt_offset: (u16, u16)) -> Result<()> {
let new_index = self.insertion_point().offset;

// Repaint logic:
//
// Start after the prompt
Expand All @@ -675,17 +626,13 @@ impl Reedline {
// Then draw the remainer of the buffer from above
// Finally, reset the cursor to the saved position

// stdout.queue(Print(&engine.line_buffer[..new_index]))?;
let insertion_line = self.insertion_line().to_string();
self.stdout
.queue(MoveTo(prompt_offset.0, prompt_offset.1))?;
self.stdout.queue(Print(&insertion_line[0..new_index]))?;
self.stdout.queue(SavePosition)?;
self.stdout.queue(Print(&insertion_line[new_index..]))?;
self.stdout.queue(Clear(ClearType::FromCursorDown))?;
self.stdout.queue(RestorePosition)?;
let new_index = self.insertion_point().offset;

self.painter
.queue_buffer(insertion_line, prompt_offset, new_index)?;

self.stdout.flush()?;
self.painter.flush()?;

Ok(())
}
Expand All @@ -694,20 +641,21 @@ impl Reedline {
&mut self,
prompt: &dyn Prompt,
prompt_origin: (u16, u16),
terminal_width: u16,
terminal_size: (u16, u16),
) -> Result<(u16, u16)> {
self.stdout
.queue(cursor::Hide)?
.queue(MoveTo(prompt_origin.0, prompt_origin.1))?;
self.queue_prompt(prompt, terminal_width as usize)?;
// set where the input begins
self.stdout.queue(cursor::Show)?.flush()?;

let prompt_offset = position()?;
self.buffer_paint(prompt_offset)?;
self.stdout.queue(cursor::Show)?.flush()?;
let prompt_mode = self.prompt_edit_mode();
let insertion_line = self.insertion_line().to_string();
let new_index = self.insertion_point().offset;
self.painter.repaint_everything(
prompt,
prompt_mode,
prompt_origin,
new_index,
insertion_line,
terminal_size,
)

Ok(prompt_offset)
// Ok(prompt_offset)
}

/// Repaint logic for the history reverse search
Expand All @@ -728,34 +676,24 @@ impl Reedline {
};

let prompt_history_search = PromptHistorySearch::new(status, search.search_string.clone());
let history_indicator =
prompt.render_prompt_history_search_indicator(prompt_history_search);

// print search prompt
self.stdout
.queue(MoveToColumn(0))?
.queue(SetForegroundColor(Color::Blue))?
.queue(Print(history_indicator))?
.queue(ResetColor)?;
self.painter
.queue_history_search_indicator(prompt, prompt_history_search)?;

match search.result {
Some((history_index, offset)) => {
let history_result = self.history.get_nth_newest(history_index).unwrap();

self.stdout.queue(Print(&history_result[..offset]))?;
self.stdout.queue(SavePosition)?;
self.stdout.queue(Print(&history_result[offset..]))?;
self.stdout.queue(Clear(ClearType::UntilNewLine))?;
self.stdout.queue(RestorePosition)?;
self.painter.queue_history_results(history_result, offset)?;
self.painter.flush()?;
}

None => {
self.stdout.queue(Clear(ClearType::UntilNewLine))?;
self.painter.clear_until_newline()?;
}
}

self.stdout.flush()?;

Ok(())
}

Expand All @@ -771,18 +709,18 @@ impl Reedline {
if (column, row) == (0, 0) {
(0, 0)
} else if row + 1 == terminal_size.1 {
self.stdout.queue(Print("\r\n\r\n"))?.flush()?;
self.painter.paint_carrige_return()?;
(0, row.saturating_sub(1))
} else if row + 2 == terminal_size.1 {
self.stdout.queue(Print("\r\n\r\n"))?.flush()?;
self.painter.paint_carrige_return()?;
(0, row)
} else {
(0, row + 1)
}
};

// set where the input begins
let mut prompt_offset = self.full_repaint(prompt, prompt_origin, terminal_size.0)?;
let mut prompt_offset = self.full_repaint(prompt, prompt_origin, terminal_size)?;

// Redraw if Ctrl-L was used
if self.history_search.is_some() {
Expand Down Expand Up @@ -887,20 +825,20 @@ impl Reedline {
terminal_size = (width, height);
// TODO properly adjusting prompt_origin on resizing while lines > 1
prompt_origin.1 = position()?.1.saturating_sub(1);
prompt_offset = self.full_repaint(prompt, prompt_origin, width)?;
prompt_offset = self.full_repaint(prompt, prompt_origin, terminal_size)?;
continue;
}
}
if self.history_search.is_some() {
self.history_search_paint(prompt)?;
} else if self.need_full_repaint {
prompt_offset = self.full_repaint(prompt, prompt_origin, terminal_size.0)?;
prompt_offset = self.full_repaint(prompt, prompt_origin, terminal_size)?;
self.need_full_repaint = false;
} else {
self.buffer_paint(prompt_offset)?;
}
} else {
prompt_offset = self.full_repaint(prompt, prompt_origin, terminal_size.0)?;
prompt_offset = self.full_repaint(prompt, prompt_origin, terminal_size)?;
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ mod clip_buffer;
mod enums;
pub use enums::{EditCommand, EditMode, Signal};

mod painter;

mod engine;
pub use engine::Reedline;

Expand Down
Loading