Skip to content

Commit

Permalink
Flexible AcceptLine, fix bug in multi-line accept
Browse files Browse the repository at this point in the history
* This commit allows keybinding for forcing accept line (even if
  validator fails). This is useful if you have a separate keybinding
  for accepting, such as `Alt-Enter`. It makes sense to show an error
  to user rather than insert an unexpected newline.
* Forcing `Newline` is also accepted
* `AcceptLine` meaning is changed (not it forces accept),
   but default `Enter` keybinding is not
* `AcceptOrInsertLine` not has a flag that modifies it's behavior in
  the middle of the line (at the end it always submits, when validator
  returned false it's always a newline)
* Fixed bug with exiting readline (both on success and error) when
  cursor is not on the last line
  • Loading branch information
tailhook committed May 8, 2020
1 parent ed85f62 commit a0571ba
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 14 deletions.
22 changes: 18 additions & 4 deletions src/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum Cmd {
/// abort
Abort, // Miscellaneous Command
/// accept-line
///
/// See also AcceptOrInsertLine
AcceptLine,
/// beginning-of-history
BeginningOfHistory,
Expand Down Expand Up @@ -94,9 +96,19 @@ pub enum Cmd {
/// moves cursor to the line below or switches to next history entry if
/// the cursor is already on the last line
LineDownOrNextHistory(RepeatCount),
/// accepts the line when cursor is at the end of the text (non including
/// trailing whitespace), inserts newline character otherwise
AcceptOrInsertLine,
/// Inserts a newline
Newline,
/// Either accepts or inserts a newline
///
/// Always inserts newline if input is non-valid. Can also insert newline
/// if cursor is in the middle of the text
///
/// If you support multi-line input:
/// * Use `accept_in_the_middle: true` for mostly single-line cases,
/// for example command-line.
/// * Use `accept_in_the_middle: false` for mostly multi-line cases,
/// for example SQL or JSON input.
AcceptOrInsertLine { accept_in_the_middle: bool },
}

impl Cmd {
Expand Down Expand Up @@ -898,7 +910,9 @@ impl InputState {
}
}
KeyPress::Ctrl('J') |
KeyPress::Enter => Cmd::AcceptLine,
KeyPress::Enter => {
Cmd::AcceptOrInsertLine { accept_in_the_middle: true }
}
KeyPress::Down => Cmd::LineDownOrNextHistory(1),
KeyPress::Up => Cmd::LineUpOrPreviousHistory(1),
KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
Expand Down
44 changes: 34 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,15 +281,16 @@ fn page_completions<C: Candidate, H: Helper>(
&& cmd != Cmd::SelfInsert(1, ' ')
&& cmd != Cmd::Kill(Movement::BackwardChar(1))
&& cmd != Cmd::AcceptLine
&& cmd != Cmd::AcceptOrInsertLine
&& cmd != Cmd::Newline
&& matches!(cmd, Cmd::AcceptOrInsertLine { .. })
{
cmd = s.next_cmd(input_state, rdr, false)?;
}
match cmd {
Cmd::SelfInsert(1, 'y') | Cmd::SelfInsert(1, 'Y') | Cmd::SelfInsert(1, ' ') => {
pause_row += s.out.get_rows() - 1;
}
Cmd::AcceptLine | Cmd::AcceptOrInsertLine => {
Cmd::AcceptLine | Cmd::Newline | Cmd::AcceptOrInsertLine { .. } => {
pause_row += 1;
}
_ => break,
Expand Down Expand Up @@ -425,6 +426,7 @@ fn readline_edit<H: Helper>(
editor.reset_kill_ring(); // TODO recreate a new kill ring vs Arc<Mutex<KillRing>>
let ctx = Context::new(&editor.history);
let mut s = State::new(&mut stdout, prompt, helper, ctx);

let mut input_state = InputState::new(&editor.config, Arc::clone(&editor.custom_bindings));

s.line.set_delete_listener(editor.kill_ring.clone());
Expand Down Expand Up @@ -508,9 +510,12 @@ fn readline_edit<H: Helper>(
}
Cmd::EndOfFile => {
if !input_state.is_emacs_mode() && !s.line.is_empty() {
s.edit_move_end()?;
break;
} else if s.line.is_empty() {
// Move to end, in case cursor was in the middle of the
// line, so that next thing application prints goes after
// the input
s.edit_move_buffer_end()?;
return Err(error::ReadlineError::Eof);
} else {
s.edit_delete(1)?
Expand Down Expand Up @@ -573,7 +578,7 @@ fn readline_edit<H: Helper>(
kill_ring.kill(&text, Mode::Append)
}
}
Cmd::AcceptLine | Cmd::AcceptOrInsertLine => {
Cmd::AcceptLine | Cmd::AcceptOrInsertLine { .. } => {
#[cfg(test)]
{
editor.term.cursor = s.layout.cursor.col;
Expand All @@ -583,13 +588,23 @@ fn readline_edit<H: Helper>(
// line as the user typed it after a newline.
s.refresh_line_with_msg(None)?;
}
// Only accept value if cursor is at the end of the buffer
if s.validate()? && (cmd == Cmd::AcceptLine || s.line.is_end_of_input()) {
break;
} else {
s.edit_insert('\n', 1)?;
let valid = s.validate()?;
let end = s.line.is_end_of_input();
match (cmd, valid, end) {
| (Cmd::AcceptLine, _, _)
| (Cmd::AcceptOrInsertLine { .. }, true, true)
| (Cmd::AcceptOrInsertLine { accept_in_the_middle: true }, true, _)
=> {
break;
}
| (Cmd::Newline, _, _)
| (Cmd::AcceptOrInsertLine { .. }, false, _)
| (Cmd::AcceptOrInsertLine { .. }, true, false)
=> {
s.edit_insert('\n', 1)?;
}
_ => unreachable!(),
}
continue;
}
Cmd::BeginningOfHistory => {
// move to first entry in history
Expand Down Expand Up @@ -654,6 +669,10 @@ fn readline_edit<H: Helper>(
}
}
Cmd::Interrupt => {
// Move to end, in case cursor was in the middle of the
// line, so that next thing application prints goes after
// the input
s.edit_move_end()?;
return Err(error::ReadlineError::Interrupted);
}
#[cfg(unix)]
Expand All @@ -669,6 +688,11 @@ fn readline_edit<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
s.edit_move_buffer_end()?;

if cfg!(windows) {
let _ = original_mode; // silent warning
}
Expand Down

0 comments on commit a0571ba

Please sign in to comment.