Skip to content

Commit

Permalink
WIP: Introduce Prompt trait
Browse files Browse the repository at this point in the history
For line continuations.
And Vi input mode indicator.
  • Loading branch information
gwenn committed May 4, 2020
1 parent 2c90178 commit 4ec26e8
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 26 deletions.
25 changes: 13 additions & 12 deletions src/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ use crate::keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
use crate::keymap::{InputState, Invoke, Refresher};
use crate::layout::{Layout, Position};
use crate::line_buffer::{LineBuffer, WordAction, MAX_LINE};
use crate::prompt::Prompt;
use crate::tty::{Renderer, Term, Terminal};
use crate::undo::Changeset;
use crate::validate::{ValidationContext, ValidationResult};

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

impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
impl<'out, 'prompt, H: Helper, P: Prompt + ?Sized> State<'out, 'prompt, H, P> {
pub fn new(
out: &'out mut <Terminal as Term>::Writer,
prompt: &'prompt str,
prompt: &'prompt P,
helper: Option<&'out H>,
ctx: Context<'out>,
) -> State<'out, 'prompt, H> {
) -> State<'out, 'prompt, H, P> {
let prompt_size = out.calculate_position(prompt, Position::default());
State {
out,
Expand Down Expand Up @@ -135,7 +136,7 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {

fn refresh(
&mut self,
prompt: &str,
prompt: &P,
prompt_size: Position,
default_prompt: bool,
info: Info<'_>,
Expand Down Expand Up @@ -231,13 +232,13 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
}
}

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

impl<'out, 'prompt, H: Helper> Refresher for State<'out, 'prompt, H> {
impl<'out, 'prompt, H: Helper, P: Prompt + ?Sized> Refresher for State<'out, 'prompt, H, P> {
fn refresh_line(&mut self) -> Result<()> {
let prompt_size = self.prompt_size;
self.hint();
Expand Down Expand Up @@ -280,10 +281,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, 'prompt, H: Helper, P: Prompt + ?Sized> fmt::Debug for State<'out, 'prompt, H, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("State")
.field("prompt", &self.prompt)
.field("prompt", &self.prompt.get_prompt(&()))
.field("prompt_size", &self.prompt_size)
.field("buf", &self.line)
.field("cols", &self.out.get_columns())
Expand All @@ -293,7 +294,7 @@ impl<'out, 'prompt, H: Helper> fmt::Debug for State<'out, 'prompt, H> {
}
}

impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
impl<'out, 'prompt, H: Helper, P: Prompt + ?Sized> State<'out, 'prompt, H, P> {
pub fn clear_screen(&mut self) -> Result<()> {
self.out.clear_screen()?;
self.layout.cursor = Position::default();
Expand Down Expand Up @@ -661,7 +662,7 @@ pub fn init_state<'out, H: Helper>(
pos: usize,
helper: Option<&'out H>,
history: &'out crate::history::History,
) -> State<'out, 'static, H> {
) -> State<'out, 'static, H, str> {
State {
out,
prompt: "",
Expand Down
34 changes: 20 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mod keys;
mod kill_ring;
mod layout;
pub mod line_buffer;
pub mod prompt;
mod tty;
mod undo;
pub mod validate;
Expand Down Expand Up @@ -58,15 +59,16 @@ use crate::keymap::{InputState, Refresher};
pub use crate::keys::KeyPress;
use crate::kill_ring::{KillRing, Mode};
use crate::line_buffer::WordAction;
pub use crate::prompt::Prompt;
use crate::validate::Validator;

/// The error type for I/O and Linux Syscalls (Errno)
pub type Result<T> = result::Result<T, error::ReadlineError>;

/// Completes the line/word
fn complete_line<H: Helper>(
fn complete_line<H: Helper, P: Prompt + ?Sized>(
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 @@ -231,7 +233,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: Prompt + ?Sized>(s: &mut State<'_, '_, H, P>) -> Result<()> {
let hint = match s.hint.as_ref() {
Some(hint) => hint,
None => return Ok(()),
Expand All @@ -244,9 +246,9 @@ fn complete_hint_line<H: Helper>(s: &mut State<'_, '_, H>) -> Result<()> {
Ok(())
}

fn page_completions<C: Candidate, H: Helper>(
fn page_completions<C: Candidate, H: Helper, P: Prompt + ?Sized>(
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 @@ -324,9 +326,9 @@ fn page_completions<C: Candidate, H: Helper>(
}

/// Incremental search
fn reverse_incremental_search<H: Helper>(
fn reverse_incremental_search<H: Helper, P: Prompt + ?Sized>(
rdr: &mut <Terminal as Term>::Reader,
s: &mut State<'_, '_, H>,
s: &mut State<'_, '_, H, P>,
input_state: &mut InputState,
history: &History,
) -> Result<Option<Cmd>> {
Expand Down Expand Up @@ -412,8 +414,8 @@ fn reverse_incremental_search<H: Helper>(
/// 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<H: Helper>(
prompt: &str,
fn readline_edit<H: Helper, P: Prompt + ?Sized>(
prompt: &P,
initial: Option<(&str, &str)>,
editor: &mut Editor<H>,
original_mode: &tty::Mode,
Expand Down Expand Up @@ -687,8 +689,8 @@ impl Drop for Guard<'_> {

/// Readline method that will enable RAW mode, call the `readline_edit()`
/// method and disable raw mode
fn readline_raw<H: Helper>(
prompt: &str,
fn readline_raw<H: Helper, P: Prompt + ?Sized>(
prompt: &P,
initial: Option<(&str, &str)>,
editor: &mut Editor<H>,
) -> Result<String> {
Expand Down Expand Up @@ -798,7 +800,7 @@ impl<H: Helper> Editor<H> {
/// 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: Prompt + ?Sized>(&mut self, prompt: &P) -> Result<String> {
self.readline_with(prompt, None)
}

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

fn readline_with(&mut self, prompt: &str, initial: Option<(&str, &str)>) -> Result<String> {
fn readline_with<P: Prompt + ?Sized>(
&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.get_prompt(&()).as_bytes())?;
stdout.flush()?;

readline_direct()
Expand Down
46 changes: 46 additions & 0 deletions src/prompt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Prompt and line continuations
use std::borrow::Cow::{self, Borrowed};

/// Prompt and line continuations
pub trait Prompt {
/// Returns text to be shown as the prompt at the first line.
/// Or text for the next lines of the input when
/// `Prompt::has_continuation()`.
fn get_prompt<'p>(&'p self, ctx: &dyn PromptContext) -> Cow<'p, str>;
/// Returns `true` when line continuations should be displayed. `false` by
/// default.
fn has_continuation(&self) -> bool {
false
}
}

/*
We can cache Prompt::get_prompt result(s) if:
* if `input_mode` is kept untouched
* or (if `Prompt::has_continuation`), if `wrap_count`/`line_number` are kept untouched
TODO
* Layout impacts
- compute_layout
- State.prompt_size
- ...
* Highlight impacts
- highlight_prompt
*/

impl PromptContext for () {}

pub trait PromptContext {
// Current line number. Computed/meaningful only when
// `Prompt::has_continuation()`. fn line_number(&self) -> usize;
// Soft wrap count. Computed/meaningful only when `Prompt::has_continuation()`.
//fn wrap_count(&self) -> usize;
// Vi input mode. `None` with default emacs editing mode.
//fn input_mode(&self) -> Option<InputMode>;
}

impl Prompt for str {
fn get_prompt<'p>(&'p self, _: &dyn PromptContext) -> Cow<'p, str> {
Borrowed(self)
}
}

0 comments on commit 4ec26e8

Please sign in to comment.