Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4f71b89
basic sqlite history
phiresky Apr 1, 2022
dd93aba
fix test compilation
phiresky Apr 1, 2022
4c03e68
final touches for MVP
phiresky Apr 1, 2022
a669fb3
better documentation
phiresky Apr 1, 2022
187f867
fix for empty history
phiresky Apr 2, 2022
01b62e7
partial change to non-generic history
phiresky Apr 16, 2022
3e76ae7
mostly working
phiresky Apr 16, 2022
317a69f
fix tests and ci
phiresky Apr 18, 2022
7d4b7ca
fixes, format
phiresky Apr 18, 2022
5fc904e
move history item to new file
phiresky Apr 18, 2022
f756189
fix some comments, fix test compile errors
phiresky Apr 20, 2022
4d60705
ci features matrix
phiresky Apr 20, 2022
13b32b9
fix index creation
phiresky Apr 18, 2022
56a210c
fix file-based tests
phiresky May 8, 2022
7c6b09a
move logic for not saving empty entries to engine
phiresky May 8, 2022
9cab40f
fix update last command on empty, set up application_id and check ver…
phiresky May 8, 2022
37afda0
Merge remote-tracking branch 'origin/main' into sqlite-history-2
phiresky May 8, 2022
556115b
add specific error variants
phiresky May 8, 2022
285ea4b
format
phiresky May 11, 2022
c772fad
fix compile errors
phiresky May 11, 2022
74c8f71
fix fmt
fdncred May 11, 2022
d84fd86
sqlite with bashisms
elferherrera May 21, 2022
a9c9d3a
Merge branch 'main' of https://github.com/nushell/reedline into sqlit…
elferherrera May 21, 2022
428544d
hide with features
elferherrera May 21, 2022
014bd48
cargo fmt
elferherrera May 21, 2022
a8d8703
improve performance of bashisms selectors
phiresky Jun 6, 2022
33b7610
Merge remote-tracking branch 'origin/main' into sqlite-history-2
phiresky Jun 6, 2022
6d803bb
Style: Remove commented out code
sholderbach Jun 6, 2022
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
Prev Previous commit
Next Next commit
mostly working
  • Loading branch information
phiresky committed Apr 16, 2022
commit 3e76ae77a0b4f30231a57f01fd3a775321c43374
37 changes: 22 additions & 15 deletions src/completion/history.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::ops::Deref;

use crate::{menu_functions::parse_selection_char, Completer, History, Span, Suggestion};
use crate::{
history::SearchQuery, menu_functions::parse_selection_char, Completer, History, Span,
Suggestion,
};

const SELECTION_CHAR: char = '!';

Expand All @@ -15,32 +18,36 @@ unsafe impl<'menu> Send for HistoryCompleter<'menu> {}
impl<'menu> Completer for HistoryCompleter<'menu> {
fn complete(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
let parsed = parse_selection_char(line, SELECTION_CHAR);
let values = self.0.query_entries(parsed.remainder);
let values = self
.0
.search(SearchQuery::all_that_contain_rev(
parsed.remainder.to_string(),
))
.expect("todo: error handling");

values
.into_iter()
.map(|value| self.create_suggestion(line, pos, value.deref()))
.map(|value| self.create_suggestion(line, pos, value.command_line.deref()))
.collect()
}

fn partial_complete(
/*TODO: fn partial_complete(
&mut self,
line: &str,
pos: usize,
start: usize,
offset: usize,
) -> Vec<Suggestion> {
self.0
.iter_chronologic()
.rev()
.skip(start)
.take(offset)
.map(|value| self.create_suggestion(line, pos, value.deref()))
.collect()
}
) -> Vec<Suggestion> {*/

fn total_completions(&mut self, _line: &str, _pos: usize) -> usize {
self.0.max_values()
fn total_completions(&mut self, line: &str, _pos: usize) -> usize {
let parsed = parse_selection_char(line, SELECTION_CHAR);
let count = self
.0
.count(SearchQuery::all_that_contain_rev(
parsed.remainder.to_string(),
))
.expect("todo: error handling");
count as usize
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/core_editor/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ impl Default for Editor {
}

impl Editor {
pub fn line_buffer_immut(&self) -> &LineBuffer {
&self.line_buffer
}
pub fn line_buffer(&mut self) -> &mut LineBuffer {
&mut self.line_buffer
}
Expand Down
133 changes: 80 additions & 53 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use {
enums::{EventStatus, ReedlineEvent},
highlighter::SimpleMatchHighlighter,
hinter::Hinter,
history::{FileBackedHistory, HistoryNavigationQuery, Result as HistoryResult},
history::{FileBackedHistory, HistoryNavigationQuery},
painting::{Painter, PromptLines},
prompt::{PromptEditMode, PromptHistorySearchStatus},
utils::text_manipulation,
Expand All @@ -21,9 +21,12 @@ use {
std::{borrow::Borrow, io, time::Duration},
};

use crate::{history::History, ReedlineMenu, Menu, MenuEvent};
#[cfg(feature = "bashisms")]
use crate::menu_functions::{parse_selection_char, ParseAction};
use crate::{
history::{History, HistoryCursor, HistoryItem, HistoryItemId, SearchDirection, SearchQuery},
LineBuffer, Menu, MenuEvent, ReedlineMenu,
};

// The POLL_WAIT is used to specify for how long the POLL should wait for
// events, to accelerate the handling of paste or compound resize events. Having
Expand Down Expand Up @@ -79,6 +82,8 @@ pub struct Reedline {

// History
history: Box<dyn History>,
history_cursor: HistoryCursor,
history_last_run_id: Option<HistoryItemId>,
input_mode: InputMode,

// Validator
Expand Down Expand Up @@ -138,6 +143,10 @@ impl Reedline {
Reedline {
editor: Editor::default(),
history,
history_cursor: HistoryCursor::new(HistoryNavigationQuery::Normal(
LineBuffer::default(),
)),
history_last_run_id: None,
input_mode: InputMode::Regular,
painter,
edit_mode,
Expand Down Expand Up @@ -334,12 +343,11 @@ impl Reedline {
pub fn print_history(&mut self) -> Result<()> {
let history: Vec<_> = self
.history
.iter_chronologic()
.enumerate()
.collect();
.search(SearchQuery::everything(SearchDirection::Forward))
.expect("todo: error handling");

for (i, entry) in history {
self.print_line(&format!("{}\t{}", i, entry))?;
for (i, entry) in history.iter().enumerate() {
self.print_line(&format!("{}\t{}", i, entry.command_line))?;
}
Ok(())
}
Expand All @@ -350,11 +358,24 @@ impl Reedline {
}

/// Update the underlying [`History`] to/from disk
pub fn sync_history(&mut self) -> HistoryResult<()> {
pub fn sync_history(&mut self) -> std::io::Result<()> {
// TODO: check for interactions in the non-submitting events
self.history.sync()
}

/// update the last history item with more information
pub fn update_last_command_context(
&mut self,
f: &dyn Fn(HistoryItem) -> HistoryItem,
) -> crate::history::Result<()> {
if let Some(r) = &self.history_last_run_id {
self.history.update(*r, f)?;
} else {
return Err("No command run".to_string());
}
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 Down Expand Up @@ -524,7 +545,7 @@ impl Reedline {
}
ReedlineEvent::ClearScreen => Ok(EventStatus::Exits(Signal::CtrlL)),
ReedlineEvent::Enter | ReedlineEvent::HistoryHintComplete => {
if let Some(string) = self.history.string_at_cursor() {
if let Some(string) = self.history_cursor.string_at_cursor() {
self.editor.set_buffer(string);
self.editor.remember_undo_state(true);
}
Expand All @@ -550,14 +571,14 @@ impl Reedline {
Ok(EventStatus::Handled)
}
ReedlineEvent::PreviousHistory | ReedlineEvent::Up | ReedlineEvent::SearchHistory => {
self.history.back();
self.history_cursor.back(self.history.as_ref()).expect("todo: error handling");
Ok(EventStatus::Handled)
}
ReedlineEvent::NextHistory | ReedlineEvent::Down => {
self.history.forward();
self.history_cursor.forward(self.history.as_ref()).expect("todo: error handling");
// Hacky way to ensure that we don't fall of into failed search going forward
if self.history.string_at_cursor().is_none() {
self.history.back();
if self.history_cursor.string_at_cursor().is_none() {
self.history_cursor.back(self.history.as_ref()).expect("todo: error handling");
}
Ok(EventStatus::Handled)
}
Expand Down Expand Up @@ -763,7 +784,9 @@ impl Reedline {
self.hide_hints = true;
// Additional repaint to show the content without hints etc.
self.repaint(prompt)?;
self.history.append(self.editor.get_buffer());
let entry = HistoryItem::from_command_line(self.editor.get_buffer().to_string());
let entry = self.history.save(entry).expect("todo: error handling");
self.history_last_run_id = entry.id;
self.run_edit_commands(&[EditCommand::Clear]);
self.editor.reset_undo_stack();

Expand Down Expand Up @@ -894,10 +917,13 @@ impl Reedline {
fn previous_history(&mut self) {
if self.input_mode != InputMode::HistoryTraversal {
self.input_mode = InputMode::HistoryTraversal;
self.set_history_navigation_based_on_line_buffer();
self.history_cursor =
HistoryCursor::new(self.get_history_navigation_based_on_line_buffer());
}

self.history.back();
self.history_cursor
.back(self.history.as_ref())
.expect("todo: error handling");
self.update_buffer_from_history();
self.editor.move_to_start();
self.editor.move_to_line_end();
Expand All @@ -906,43 +932,45 @@ impl Reedline {
fn next_history(&mut self) {
if self.input_mode != InputMode::HistoryTraversal {
self.input_mode = InputMode::HistoryTraversal;
self.set_history_navigation_based_on_line_buffer();
self.history_cursor =
HistoryCursor::new(self.get_history_navigation_based_on_line_buffer());
}

self.history.forward();
self.history_cursor
.forward(self.history.as_ref())
.expect("todo: error handling");
self.update_buffer_from_history();
self.editor.move_to_end();
}

/// Enable the search and navigation through the history from the line buffer prompt
///
/// Enables either prefix search with output in the line buffer or simple traversal
fn set_history_navigation_based_on_line_buffer(&mut self) {
fn get_history_navigation_based_on_line_buffer(&self) -> HistoryNavigationQuery {
if self.editor.is_empty() || !self.editor.is_cursor_at_buffer_end() {
// Perform bash-style basic up/down entry walking
self.history.set_navigation(HistoryNavigationQuery::Normal(
HistoryNavigationQuery::Normal(
// Hack: Tight coupling point to be able to restore previously typed input
self.editor.line_buffer().clone(),
));
self.editor.line_buffer_immut().clone(),
)
} else {
// Prefix search like found in fish, zsh, etc.
// Search string is set once from the current buffer
// Current setup (code in other methods)
// Continuing with typing will leave the search
// but next invocation of this method will start the next search
let buffer = self.editor.get_buffer().to_string();
self.history
.set_navigation(HistoryNavigationQuery::PrefixSearch(buffer));
HistoryNavigationQuery::PrefixSearch(buffer)
}
}

/// Switch into reverse history search mode
///
/// This mode uses a separate prompt and handles keybindings slightly differently!
fn enter_history_search(&mut self) {
self.history_cursor =
HistoryCursor::new(HistoryNavigationQuery::SubstringSearch("".to_string()));
self.input_mode = InputMode::HistorySearch;
self.history
.set_navigation(HistoryNavigationQuery::SubstringSearch("".to_string()));
}

/// Dispatches the applicable [`EditCommand`] actions for editing the history search string.
Expand All @@ -952,30 +980,28 @@ impl Reedline {
for command in commands {
match command {
EditCommand::InsertChar(c) => {
let navigation = self.history.get_navigation();
let navigation = self.history_cursor.get_navigation();
if let HistoryNavigationQuery::SubstringSearch(mut substring) = navigation {
substring.push(*c);
self.history
.set_navigation(HistoryNavigationQuery::SubstringSearch(substring));
self.history_cursor =
HistoryCursor::new(HistoryNavigationQuery::SubstringSearch(substring));
} else {
self.history
.set_navigation(HistoryNavigationQuery::SubstringSearch(String::from(
*c,
)));
self.history_cursor = HistoryCursor::new(
HistoryNavigationQuery::SubstringSearch(String::from(*c)),
);
}
self.history.back();
self.history_cursor.back(self.history.as_mut()).expect("todo: error handling");
}
EditCommand::Backspace => {
let navigation = self.history.get_navigation();
let navigation = self.history_cursor.get_navigation();

if let HistoryNavigationQuery::SubstringSearch(substring) = navigation {
let new_substring = text_manipulation::remove_last_grapheme(&substring);

self.history
.set_navigation(HistoryNavigationQuery::SubstringSearch(
new_substring.to_string(),
));
self.history.back();
self.history_cursor = HistoryCursor::new(
HistoryNavigationQuery::SubstringSearch(new_substring.to_string()),
);
self.history_cursor.back(self.history.as_mut()).expect("todo: error handling");
}
}
_ => {
Expand All @@ -990,9 +1016,9 @@ impl Reedline {
/// When using the up/down traversal or fish/zsh style prefix search update the main line buffer accordingly.
/// Not used for the separate modal reverse search!
fn update_buffer_from_history(&mut self) {
match self.history.get_navigation() {
match self.history_cursor.get_navigation() {
HistoryNavigationQuery::Normal(original) => {
if let Some(buffer_to_paint) = self.history.string_at_cursor() {
if let Some(buffer_to_paint) = self.history_cursor.string_at_cursor() {
self.editor.set_buffer(buffer_to_paint.clone());
self.editor.set_insertion_point(buffer_to_paint.len());
} else {
Expand All @@ -1001,7 +1027,7 @@ impl Reedline {
}
}
HistoryNavigationQuery::PrefixSearch(prefix) => {
if let Some(prefix_result) = self.history.string_at_cursor() {
if let Some(prefix_result) = self.history_cursor.string_at_cursor() {
self.editor.set_buffer(prefix_result.clone());
self.editor.set_insertion_point(prefix_result.len());
} else {
Expand All @@ -1017,10 +1043,10 @@ impl Reedline {
fn run_edit_commands(&mut self, commands: &[EditCommand]) {
if self.input_mode == InputMode::HistoryTraversal {
if matches!(
self.history.get_navigation(),
self.history_cursor.get_navigation(),
HistoryNavigationQuery::Normal(_)
) {
if let Some(string) = self.history.string_at_cursor() {
if let Some(string) = self.history_cursor.string_at_cursor() {
self.editor.set_buffer(string);
}
}
Expand Down Expand Up @@ -1055,7 +1081,7 @@ impl Reedline {

/// Checks if hints should be displayed and are able to be completed
fn hints_active(&self) -> bool {
!self.hide_hints && self.input_mode == InputMode::Regular
!self.hide_hints && matches!(self.input_mode, InputMode::Regular)
}

/// Repaint of either the buffer or the parts for reverse history search
Expand Down Expand Up @@ -1121,18 +1147,19 @@ impl Reedline {
/// Overwrites the prompt indicator and highlights the search string
/// separately from the result buffer.
fn history_search_paint(&mut self, prompt: &dyn Prompt) -> Result<()> {
let navigation = self.history.get_navigation();
let navigation = self.history_cursor.get_navigation();

if let HistoryNavigationQuery::SubstringSearch(substring) = navigation {
let status = if !substring.is_empty() && self.history.string_at_cursor().is_none() {
PromptHistorySearchStatus::Failing
} else {
PromptHistorySearchStatus::Passing
};
let status =
if !substring.is_empty() && self.history_cursor.string_at_cursor().is_none() {
PromptHistorySearchStatus::Failing
} else {
PromptHistorySearchStatus::Passing
};

let prompt_history_search = PromptHistorySearch::new(status, substring.clone());

let res_string = self.history.string_at_cursor().unwrap_or_default();
let res_string = self.history_cursor.string_at_cursor().unwrap_or_default();

// Highlight matches
let res_string = if self.use_ansi_coloring {
Expand Down
Loading