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

Provide a basic default implementation of History #209

Merged
merged 6 commits into from
Sep 1, 2023
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
35 changes: 4 additions & 31 deletions examples/history.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use dialoguer::{theme::ColorfulTheme, History, Input};
use std::{collections::VecDeque, process};
use dialoguer::{theme::ColorfulTheme, BasicHistory, Input};
use std::process;

fn main() {
println!("Use 'exit' to quit the prompt");
println!("In this example, history is limited to 4 entries");
println!("In this example, history is limited to 8 entries and contains no duplicates");
println!("Use the Up/Down arrows to scroll through history");
println!();

let mut history = MyHistory::default();
let mut history = BasicHistory::new().max_entries(8).no_duplicates(true);

loop {
if let Ok(cmd) = Input::<String>::with_theme(&ColorfulTheme::default())
Expand All @@ -22,30 +22,3 @@ fn main() {
}
}
}

struct MyHistory {
max: usize,
history: VecDeque<String>,
}

impl Default for MyHistory {
fn default() -> Self {
MyHistory {
max: 4,
history: VecDeque::new(),
}
}
}

impl<T: ToString> History<T> for MyHistory {
fn read(&self, pos: usize) -> Option<String> {
self.history.get(pos).cloned()
}

fn write(&mut self, val: &T) {
if self.history.len() == self.max {
self.history.pop_back();
}
self.history.push_front(val.to_string());
}
}
51 changes: 51 additions & 0 deletions examples/history_custom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use dialoguer::{theme::ColorfulTheme, History, Input};
use std::{collections::VecDeque, process};

fn main() {
println!("Use 'exit' to quit the prompt");
println!("In this example, history is limited to 4 entries");
println!("Use the Up/Down arrows to scroll through history");
println!();

let mut history = MyHistory::default();

loop {
if let Ok(cmd) = Input::<String>::with_theme(&ColorfulTheme::default())
.with_prompt("dialoguer")
.history_with(&mut history)
.interact_text()
{
if cmd == "exit" {
process::exit(0);
}
println!("Entered {}", cmd);
}
}
}

struct MyHistory {
max: usize,
history: VecDeque<String>,
}

impl Default for MyHistory {
fn default() -> Self {
MyHistory {
max: 4,
history: VecDeque::new(),
}
}
}

impl<T: ToString> History<T> for MyHistory {
fn read(&self, pos: usize) -> Option<String> {
self.history.get(pos).cloned()
}

fn write(&mut self, val: &T) {
if self.history.len() == self.max {
self.history.pop_back();
}
self.history.push_front(val.to_string());
}
}
71 changes: 71 additions & 0 deletions src/history.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::VecDeque;

/// Trait for history handling.
pub trait History<T> {
/// This is called with the current position that should
Expand All @@ -13,3 +15,72 @@ pub trait History<T> {
/// is implemented as a FIFO queue.
fn write(&mut self, val: &T);
}

pub struct BasicHistory {
max_entries: Option<usize>,
deque: VecDeque<String>,
no_duplicates: bool,
}

impl BasicHistory {
/// Creates a new basic history value which has no limit on the number of
/// entries and allows for duplicates.
///
/// # Example
///
/// A history with at most 8 entries and no duplicates:
///
/// ```rs
/// let mut history = BasicHistory::new().max_entries(8).no_duplicates(true);
/// ```
pub fn new() -> Self {
Self {
max_entries: None,
deque: VecDeque::new(),
no_duplicates: false,
}
}

/// Limit the number of entries stored in the history.
pub fn max_entries(self, max_entries: usize) -> Self {
Self {
max_entries: Some(max_entries),
..self
}
}

/// Prevent duplicates in the history. This means that any previous entries
/// that are equal to a new entry are removed before the new entry is added.
pub fn no_duplicates(self, no_duplicates: bool) -> Self {
Self {
no_duplicates,
..self
}
}
}

impl<T: ToString> History<T> for BasicHistory {
fn read(&self, pos: usize) -> Option<String> {
self.deque.get(pos).cloned()
}

fn write(&mut self, val: &T) {
let val = val.to_string();

if self.no_duplicates {
self.deque.retain(|v| v != &val);
}

self.deque.push_front(val);

if let Some(max_entries) = self.max_entries {
self.deque.truncate(max_entries);
}
}
}

impl Default for BasicHistory {
fn default() -> Self {
Self::new()
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub use console;
#[cfg(feature = "editor")]
pub use edit::Editor;
#[cfg(feature = "history")]
pub use history::History;
pub use history::{History, BasicHistory};
use paging::Paging;
pub use prompts::{
confirm::Confirm, input::Input, multi_select::MultiSelect, select::Select, sort::Sort,
Expand Down
1 change: 1 addition & 0 deletions src/prompts/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use console::{Key, Term};

type ValidatorCallback<'a, T> = Box<dyn FnMut(&T) -> Option<String> + 'a>;


/// Renders an input prompt.
///
/// ## Example usage
Expand Down