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

Generic Prompt Type #696

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
7 changes: 4 additions & 3 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ pub enum Status {
Submit,
}

pub fn execute<H: Helper>(
pub fn execute<H: Helper, P: ToString>(
cmd: Cmd,
s: &mut State<'_, '_, H>,
s: &mut State<'_, H, P>,
input_state: &InputState,
kill_ring: &mut KillRing,
config: &Config,
Expand Down Expand Up @@ -221,8 +221,9 @@ pub fn execute<H: Helper>(
// Move to end, in case cursor was in the middle of the
// line, so that next thing application prints goes after
// the input
let buf = s.line.as_str().to_owned();
s.move_cursor_to_end()?;
return Err(error::ReadlineError::Interrupted);
return Err(error::ReadlineError::Interrupted(buf));
}
_ => {
// Ignore the character typed.
Expand Down
51 changes: 22 additions & 29 deletions src/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ use crate::KillRing;

/// Represent the state during line editing.
/// Implement rendering.
pub struct State<'out, 'prompt, H: Helper> {
pub struct State<'out, H: Helper, P> {
pub out: &'out mut <Terminal as Term>::Writer,
prompt: &'prompt str, // Prompt to display (rl_prompt)
prompt_size: Position, // Prompt Unicode/visible width and height
prompt: P, // Prompt to display (rl_prompt)
pub line: LineBuffer, // Edited line buffer
pub layout: Layout,
saved_line_for_history: LineBuffer, // Current edited line before history browsing
Expand All @@ -44,18 +43,16 @@ enum Info<'m> {
Msg(Option<&'m str>),
}

impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
impl<'out, H: Helper, P: ToString> State<'out, H, P> {
pub fn new(
out: &'out mut <Terminal as Term>::Writer,
prompt: &'prompt str,
prompt: P,
helper: Option<&'out H>,
ctx: Context<'out>,
) -> State<'out, 'prompt, H> {
let prompt_size = out.calculate_position(prompt, Position::default());
) -> Self {
State {
out,
prompt,
prompt_size,
line: LineBuffer::with_capacity(MAX_LINE).can_growth(true),
layout: Layout::default(),
saved_line_for_history: LineBuffer::with_capacity(MAX_LINE).can_growth(true),
Expand Down Expand Up @@ -93,9 +90,6 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
if new_cols != old_cols
&& (self.layout.end.row > 0 || self.layout.end.col >= new_cols)
{
self.prompt_size = self
.out
.calculate_position(self.prompt, Position::default());
self.refresh_line()?;
}
continue;
Expand All @@ -121,19 +115,20 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
}

pub fn move_cursor(&mut self) -> Result<()> {
let prompt_size = self.out.calculate_position(self.prompt.to_string().as_str(), Position::default());
// calculate the desired position of the cursor
let cursor = self
.out
.calculate_position(&self.line[..self.line.pos()], self.prompt_size);
.calculate_position(&self.line[..self.line.pos()], prompt_size);
if self.layout.cursor == cursor {
return Ok(());
}
if self.highlight_char() {
let prompt_size = self.prompt_size;
self.refresh(self.prompt, prompt_size, true, Info::NoHint)?;
let prompt_size = prompt_size;
self.refresh(self.prompt.to_string().as_str(), prompt_size, true, Info::NoHint)?;
} else {
self.out.move_cursor(self.layout.cursor, cursor)?;
self.layout.prompt_size = self.prompt_size;
self.layout.prompt_size = prompt_size;
self.layout.cursor = cursor;
debug_assert!(self.layout.prompt_size <= self.layout.cursor);
debug_assert!(self.layout.cursor <= self.layout.end);
Expand Down Expand Up @@ -253,25 +248,25 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
}
}

impl<'out, 'prompt, H: Helper> Invoke for State<'out, 'prompt, H> {
impl<'out, P, H: Helper> Invoke for State<'out, H, P> {
fn input(&self) -> &str {
self.line.as_str()
}
}

impl<'out, 'prompt, H: Helper> Refresher for State<'out, 'prompt, H> {
impl<'out, P: ToString, H: Helper> Refresher for State<'out, H, P> {
fn refresh_line(&mut self) -> Result<()> {
let prompt_size = self.prompt_size;
let prompt_size = self.out.calculate_position(self.prompt.to_string().as_str(), Position::default());
self.hint();
self.highlight_char();
self.refresh(self.prompt, prompt_size, true, Info::Hint)
self.refresh(self.prompt.to_string().as_str(), prompt_size, true, Info::Hint)
}

fn refresh_line_with_msg(&mut self, msg: Option<&str>) -> Result<()> {
let prompt_size = self.prompt_size;
let prompt_size = self.out.calculate_position(self.prompt.to_string().as_str(), Position::default());
self.hint = None;
self.highlight_char();
self.refresh(self.prompt, prompt_size, true, Info::Msg(msg))
self.refresh(self.prompt.to_string().as_str(), prompt_size, true, Info::Msg(msg))
}

fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()> {
Expand Down Expand Up @@ -325,11 +320,10 @@ impl<'out, 'prompt, H: Helper> Refresher for State<'out, 'prompt, H> {
}
}

impl<'out, 'prompt, H: Helper> fmt::Debug for State<'out, 'prompt, H> {
impl<'out, P: ToString, H: Helper> fmt::Debug for State<'out, H, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("State")
.field("prompt", &self.prompt)
.field("prompt_size", &self.prompt_size)
.field("prompt", &self.prompt.to_string().as_str())
.field("buf", &self.line)
.field("cols", &self.out.get_columns())
.field("layout", &self.layout)
Expand All @@ -338,7 +332,7 @@ impl<'out, 'prompt, H: Helper> fmt::Debug for State<'out, 'prompt, H> {
}
}

impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
impl<'out, P: ToString, H: Helper> State<'out, H, P> {
pub fn clear_screen(&mut self) -> Result<()> {
self.out.clear_screen()?;
self.layout.cursor = Position::default();
Expand All @@ -350,7 +344,7 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
pub fn edit_insert(&mut self, ch: char, n: RepeatCount) -> Result<()> {
if let Some(push) = self.line.insert(ch, n, &mut self.changes) {
if push {
let prompt_size = self.prompt_size;
let prompt_size = self.out.calculate_position(self.prompt.to_string().as_str(), Position::default());
let no_previous_hint = self.hint.is_none();
self.hint();
let width = ch.width().unwrap_or(0);
Expand All @@ -368,7 +362,7 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
let bits = ch.encode_utf8(&mut self.byte_buffer);
self.out.write_and_flush(bits)
} else {
self.refresh(self.prompt, prompt_size, true, Info::Hint)
self.refresh(self.prompt.to_string().as_str(), prompt_size, true, Info::Hint)
}
} else {
self.refresh_line()
Expand Down Expand Up @@ -748,11 +742,10 @@ pub fn init_state<'out, H: Helper>(
pos: usize,
helper: Option<&'out H>,
history: &'out crate::history::DefaultHistory,
) -> State<'out, 'static, H> {
) -> State<'out, H, &'static str> {
State {
out,
prompt: "",
prompt_size: Position::default(),
line: LineBuffer::init(line, pos),
layout: Layout::default(),
saved_line_for_history: LineBuffer::with_capacity(100),
Expand Down
8 changes: 4 additions & 4 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub enum ReadlineError {
/// EOF (VEOF / Ctrl-D)
Eof,
/// Interrupt signal (VINTR / VQUIT / Ctrl-C)
Interrupted,
Interrupted(String),
/// Unix Error from syscall
#[cfg(unix)]
Errno(nix::Error),
Expand All @@ -36,10 +36,10 @@ pub enum ReadlineError {

impl fmt::Display for ReadlineError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
match self {
ReadlineError::Io(ref err) => err.fmt(f),
ReadlineError::Eof => write!(f, "EOF"),
ReadlineError::Interrupted => write!(f, "Interrupted"),
ReadlineError::Interrupted(s) => write!(f, "Interrupted({s})"),
#[cfg(unix)]
ReadlineError::Errno(ref err) => err.fmt(f),
ReadlineError::WindowResized => write!(f, "WindowResized"),
Expand All @@ -58,7 +58,7 @@ impl Error for ReadlineError {
match *self {
ReadlineError::Io(ref err) => Some(err),
ReadlineError::Eof => None,
ReadlineError::Interrupted => None,
ReadlineError::Interrupted(_) => None,
#[cfg(unix)]
ReadlineError::Errno(ref err) => Some(err),
ReadlineError::WindowResized => None,
Expand Down
24 changes: 12 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ use crate::validate::Validator;
pub type Result<T> = result::Result<T, ReadlineError>;

/// Completes the line/word
fn complete_line<H: Helper>(
fn complete_line<H: Helper, P: ToString>(
rdr: &mut <Terminal as Term>::Reader,
s: &mut State<'_, '_, H>,
s: &mut State<'_, H, P>,
input_state: &mut InputState,
config: &Config,
) -> Result<Option<Cmd>> {
Expand Down Expand Up @@ -263,7 +263,7 @@ fn complete_line<H: Helper>(
}

/// Completes the current hint
fn complete_hint_line<H: Helper>(s: &mut State<'_, '_, H>) -> Result<()> {
fn complete_hint_line<H: Helper, P: ToString>(s: &mut State<'_, H, P>) -> Result<()> {
let hint = match s.hint.as_ref() {
Some(hint) => hint,
None => return Ok(()),
Expand All @@ -279,9 +279,9 @@ fn complete_hint_line<H: Helper>(s: &mut State<'_, '_, H>) -> Result<()> {
s.refresh_line()
}

fn page_completions<C: Candidate, H: Helper>(
fn page_completions<C: Candidate, H: Helper, P: ToString>(
rdr: &mut <Terminal as Term>::Reader,
s: &mut State<'_, '_, H>,
s: &mut State<'_, H, P>,
input_state: &mut InputState,
candidates: &[C],
) -> Result<Option<Cmd>> {
Expand Down Expand Up @@ -360,9 +360,9 @@ fn page_completions<C: Candidate, H: Helper>(
}

/// Incremental search
fn reverse_incremental_search<H: Helper, I: History>(
fn reverse_incremental_search<H: Helper, I: History, P: ToString>(
rdr: &mut <Terminal as Term>::Reader,
s: &mut State<'_, '_, H>,
s: &mut State<'_, H, P>,
input_state: &mut InputState,
history: &I,
) -> Result<Option<Cmd>> {
Expand Down Expand Up @@ -635,7 +635,7 @@ impl<H: Helper, I: History> Editor<H, I> {
/// terminal.
/// Otherwise (e.g., if `stdin` is a pipe or the terminal is not supported),
/// it uses file-style interaction.
pub fn readline(&mut self, prompt: &str) -> Result<String> {
pub fn readline<P: ToString>(&mut self, prompt: P) -> Result<String> {
self.readline_with(prompt, None)
}

Expand All @@ -650,12 +650,12 @@ impl<H: Helper, I: History> Editor<H, I> {
self.readline_with(prompt, Some(initial))
}

fn readline_with(&mut self, prompt: &str, initial: Option<(&str, &str)>) -> Result<String> {
fn readline_with<P: ToString>(&mut self, prompt: P, initial: Option<(&str, &str)>) -> Result<String> {
if self.term.is_unsupported() {
debug!(target: "rustyline", "unsupported terminal");
// Write prompt and flush it to stdout
let mut stdout = io::stdout();
stdout.write_all(prompt.as_bytes())?;
stdout.write_all(prompt.to_string().as_bytes())?;
stdout.flush()?;

readline_direct(io::stdin().lock(), io::stderr(), &self.helper)
Expand All @@ -681,9 +681,9 @@ impl<H: Helper, I: History> Editor<H, I> {
/// Handles reading and editing the readline buffer.
/// It will also handle special inputs in an appropriate fashion
/// (e.g., C-c will exit readline)
fn readline_edit(
fn readline_edit<P: ToString>(
&mut self,
prompt: &str,
prompt: P,
initial: Option<(&str, &str)>,
original_mode: &tty::Mode,
term_key_map: tty::KeyMap,
Expand Down
6 changes: 3 additions & 3 deletions src/test/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,16 @@ fn interrupt_key() {
for mode in &[EditMode::Emacs, EditMode::Vi] {
let mut editor = init_editor(*mode, &[E::ctrl('C')]);
let err = editor.readline(">>");
assert_matches!(err, Err(ReadlineError::Interrupted));
assert_matches!(err, Err(ReadlineError::Interrupted(_)));

let mut editor = init_editor(*mode, &[E::ctrl('C')]);
let err = editor.readline_with_initial(">>", ("Hi", ""));
assert_matches!(err, Err(ReadlineError::Interrupted));
assert_matches!(err, Err(ReadlineError::Interrupted(_)));
if *mode == EditMode::Vi {
// vi command mode
let mut editor = init_editor(*mode, &[E::ESC, E::ctrl('C')]);
let err = editor.readline_with_initial(">>", ("Hi", ""));
assert_matches!(err, Err(ReadlineError::Interrupted));
assert_matches!(err, Err(ReadlineError::Interrupted(_)));
}
}
}
Expand Down