Skip to content

Commit

Permalink
feat: migrate and extend history search
Browse files Browse the repository at this point in the history
* This PR addresses this Issue #55 
* BasicSearch was removed and its behavior was 
  migrated to the history module.
* Additional features are an interactively displayed
  list, that is scrollable with `CTRL-n`/`CTRL-r` or `CTRL-p`
* Maybe in the future make the displayed list optional?
* The logic that makes this behavior possible is contained 
  inside the history module. 
* The result is, that the main loop in readline_helper no longer needs to
  handle history_state and clean up in the right places
  • Loading branch information
ahkrr committed Jun 17, 2021
1 parent 4433bfc commit ea0cc22
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 213 deletions.
184 changes: 40 additions & 144 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ use crate::{
prompt::PromptMode,
DefaultPrompt, Prompt,
};
use crate::{history::History, line_buffer::LineBuffer};
use crate::{
history_search::{BasicSearch, BasicSearchCommand},
line_buffer::InsertionPoint,
history::History,
line_buffer::{InsertionPoint, LineBuffer},
};
use crate::{EditCommand, EditMode, Signal, ViEngine};
use crossterm::{
cursor::{position, MoveTo, MoveToColumn, RestorePosition, SavePosition},
event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers},
style::{Color, Print, ResetColor, SetForegroundColor},
style::{Print, ResetColor, SetForegroundColor},
terminal::{self, Clear, ClearType},
QueueableCommand, Result,
};
Expand Down Expand Up @@ -51,7 +50,6 @@ pub struct Reedline {

// History
history: History,
history_search: Option<BasicSearch>, // This could be have more features in the future (fzf, configurable?)

// Stdout
stdout: Stdout,
Expand Down Expand Up @@ -96,7 +94,6 @@ impl Reedline {
line_buffer: LineBuffer::new(),
cut_buffer,
history,
history_search: None,
stdout,
keybindings: keybindings_hashmap,
edit_mode: EditMode::Emacs,
Expand Down Expand Up @@ -231,42 +228,6 @@ impl Reedline {
result
}

/// Dispatches the applicable [`EditCommand`] actions for editing the history search string.
///
/// Only modifies internal state, does not perform regular output!
fn run_history_commands(&mut self, commands: &[EditCommand]) {
for command in commands {
match command {
EditCommand::InsertChar(c) => {
let search = self
.history_search
.as_mut()
.expect("couldn't get history_search as mutable"); // We checked it is some
search.step(BasicSearchCommand::InsertChar(*c), &self.history);
}
EditCommand::Backspace => {
let search = self
.history_search
.as_mut()
.expect("couldn't get history_search as mutable"); // We checked it is some
search.step(BasicSearchCommand::Backspace, &self.history);
}
EditCommand::SearchHistory => {
let search = self
.history_search
.as_mut()
.expect("couldn't get history_search as mutable"); // We checked it is some
search.step(BasicSearchCommand::Next, &self.history);
}
EditCommand::MoveRight => {
// Ignore move right, it is currently emited with InsertChar
}
// Leave history search otherwise
_ => self.history_search = None,
}
}
}

fn move_to_start(&mut self) {
self.line_buffer.move_to_start()
}
Expand Down Expand Up @@ -359,10 +320,6 @@ impl Reedline {
}
}

fn search_history(&mut self) {
self.history_search = Some(BasicSearch::new(self.insertion_line().to_string()));
}

fn cut_from_start(&mut self) {
let insertion_offset = self.insertion_point().offset;
if insertion_offset > 0 {
Expand Down Expand Up @@ -508,13 +465,12 @@ impl Reedline {
}

/// Executes [`EditCommand`] actions by modifying the internal state appropriately. Does not output itself.
fn run_edit_commands(&mut self, commands: &[EditCommand]) {
// Handle command for history inputs
if self.history_search.is_some() {
self.run_history_commands(commands);
return;
}
fn run_edit_commands(
&mut self,
commands: &[EditCommand],
prompt_offset: &mut (u16, u16),
) -> Result<()> {
// Vim mode transformations
let commands = match self.edit_mode {
EditMode::ViNormal => self.vi_engine.handle(commands),
Expand Down Expand Up @@ -564,7 +520,12 @@ impl Reedline {
self.next_history();
}
EditCommand::SearchHistory => {
self.search_history();
self.history.interactive_search(
&mut self.line_buffer,
&mut self.stdout,
&mut prompt_offset.1,
)?;
self.queue_prompt_indicator()?;
}
EditCommand::CutFromStart => {
self.cut_from_start();
Expand Down Expand Up @@ -619,6 +580,7 @@ impl Reedline {
}
}
}
Ok(())
}

/// Get the cursor position as understood by the underlying [`LineBuffer`]
Expand Down Expand Up @@ -787,54 +749,6 @@ impl Reedline {
Ok(prompt_offset)
}

/// Repaint logic for the history reverse search
///
/// Overwrites the prompt indicator and highlights the search string
/// separately from the result bufer.
fn history_search_paint(&mut self) -> Result<()> {
// Assuming we are currently searching
let search = self
.history_search
.as_ref()
.expect("couldn't get history_search reference");

let status = if search.result.is_none() && !search.search_string.is_empty() {
"failed "
} else {
""
};

// print search prompt
self.stdout
.queue(MoveToColumn(0))?
.queue(SetForegroundColor(Color::Blue))?
.queue(Print(format!(
"({}reverse-search)`{}':",
status, search.search_string
)))?
.queue(ResetColor)?;

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)?;
}

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

self.stdout.flush()?;

Ok(())
}

/// Helper implemting the logic for [`Reedline::read_line()`] to be wrapped
/// in a `raw_mode` context.
fn read_line_helper(&mut self, prompt: Box<dyn Prompt>) -> Result<Signal> {
Expand All @@ -855,11 +769,8 @@ impl Reedline {
let mut line_count = 1;

// Redraw if Ctrl-L was used
if self.history_search.is_some() {
self.history_search_paint()?;
} else {
self.buffer_paint(prompt_offset)?;
}
self.buffer_paint(prompt_offset)?;

self.stdout.flush()?;

loop {
Expand All @@ -872,12 +783,12 @@ impl Reedline {
return Ok(Signal::CtrlD);
} else if let Some(binding) = self.find_keybinding(modifiers, code)
{
self.run_edit_commands(&binding);
self.run_edit_commands(&binding, &mut prompt_offset)?;
}
}
(KeyModifiers::CONTROL, KeyCode::Char('c'), _) => {
if let Some(binding) = self.find_keybinding(modifiers, code) {
self.run_edit_commands(&binding);
self.run_edit_commands(&binding, &mut prompt_offset)?;
}
return Ok(Signal::CtrlC);
}
Expand All @@ -888,7 +799,10 @@ impl Reedline {
| (KeyModifiers::SHIFT, KeyCode::Char(c), x)
if x == EditMode::ViNormal =>
{
self.run_edit_commands(&[EditCommand::ViCommandFragment(c)]);
self.run_edit_commands(
&[EditCommand::ViCommandFragment(c)],
&mut prompt_offset,
)?;
}
(KeyModifiers::NONE, KeyCode::Char(c), x)
| (KeyModifiers::SHIFT, KeyCode::Char(c), x)
Expand All @@ -901,10 +815,10 @@ impl Reedline {
};
if self.maybe_wrap(terminal_size.0, line_start, c) {
let (original_column, original_row) = position()?;
self.run_edit_commands(&[
EditCommand::InsertChar(c),
EditCommand::MoveRight,
]);
self.run_edit_commands(
&[EditCommand::InsertChar(c), EditCommand::MoveRight],
&mut prompt_offset,
)?;
self.buffer_paint(prompt_offset)?;

let (new_column, _) = position()?;
Expand All @@ -919,43 +833,27 @@ impl Reedline {
line_count += 1;
}
} else {
self.run_edit_commands(&[
EditCommand::InsertChar(c),
EditCommand::MoveRight,
]);
self.run_edit_commands(
&[EditCommand::InsertChar(c), EditCommand::MoveRight],
&mut prompt_offset,
)?;
}
}
(KeyModifiers::NONE, KeyCode::Enter, x) if x != EditMode::ViNormal => {
match self.history_search.clone() {
Some(search) => {
self.queue_prompt_indicator()?;
if let Some((history_index, _)) = search.result {
self.line_buffer.set_buffer(
self.history
.get_nth_newest(history_index)
.unwrap()
.clone(),
);
}
self.history_search = None;
}
None => {
let buffer = self.insertion_line().to_string();
let buffer = self.insertion_line().to_string();

self.run_edit_commands(&[
EditCommand::AppendToHistory,
EditCommand::Clear,
]);
self.print_crlf()?;
self.run_edit_commands(
&[EditCommand::AppendToHistory, EditCommand::Clear],
&mut prompt_offset,
)?;
self.print_crlf()?;

return Ok(Signal::Success(buffer));
}
}
return Ok(Signal::Success(buffer));
}

_ => {
if let Some(binding) = self.find_keybinding(modifiers, code) {
self.run_edit_commands(&binding);
self.run_edit_commands(&binding, &mut prompt_offset)?;
}
}
}
Expand All @@ -968,9 +866,7 @@ impl Reedline {
self.full_repaint(prompt_origin, width)?;
}
}
if self.history_search.is_some() {
self.history_search_paint()?;
} else if self.need_full_repaint {
if self.need_full_repaint {
self.full_repaint(prompt_origin, terminal_size.0)?;
self.need_full_repaint = false;
} else {
Expand Down
Loading

0 comments on commit ea0cc22

Please sign in to comment.