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

Reverse search with a long prompt followed by ctrl-c causes an assertion failure panic #575

Closed
nicklan opened this issue Dec 4, 2021 · 5 comments · Fixed by #576
Closed
Labels

Comments

@nicklan
Copy link

nicklan commented Dec 4, 2021

You can cause a panic with the following:

  1. set a prompt of at least 35 characters
  2. initiate a reverse search (ctrl-r)
  3. type something
  4. hit ctrl-c

Here's the backtrace:

(failed reverse-i-search)`okef': eokf  thread 'main' panicked at 'assertion failed: self.layout.cursor <= self.layout.end', /home/user/rustyline/src/edit.rs:128:13
stack backtrace:
   0: rust_begin_unwind
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/std/src/panicking.rs:517:5
   1: core::panicking::panic_fmt
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14
   2: core::panicking::panic
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:50:5
   3: rustyline::edit::State<H>::move_cursor
             at ./src/edit.rs:128:13
   4: rustyline::edit::State<H>::edit_move_buffer_end
             at ./src/edit.rs:465:13
   5: rustyline::command::execute
             at ./src/command.rs:225:13
   6: rustyline::readline_edit
             at ./src/lib.rs:537:15
   7: rustyline::readline_raw
             at ./src/lib.rs:572:22
   8: rustyline::Editor<H>::readline_with
             at ./src/lib.rs:784:13
   9: rustyline::Editor<H>::readline
             at ./src/lib.rs:760:9
  10: example::main
             at ./examples/example.rs:108:24
  11: core::ops::function::FnOnce::call_once
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

This is just a slightly modified version of the example.rs example:

use std::borrow::Cow::{self, Borrowed, Owned};

use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::config::OutputStreamType;
use rustyline::error::ReadlineError;
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
use rustyline::hint::{Hinter, HistoryHinter};
use rustyline::validate::{self, MatchingBracketValidator, Validator};
use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, KeyEvent};
use rustyline_derive::Helper;

#[derive(Helper)]
struct MyHelper {
    completer: FilenameCompleter,
    highlighter: MatchingBracketHighlighter,
    validator: MatchingBracketValidator,
    hinter: HistoryHinter,
    colored_prompt: String,
}

impl Completer for MyHelper {
    type Candidate = Pair;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        ctx: &Context<'_>,
    ) -> Result<(usize, Vec<Pair>), ReadlineError> {
        self.completer.complete(line, pos, ctx)
    }
}

impl Hinter for MyHelper {
    type Hint = String;

    fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<String> {
        self.hinter.hint(line, pos, ctx)
    }
}

impl Highlighter for MyHelper {
    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
        &'s self,
        prompt: &'p str,
        default: bool,
    ) -> Cow<'b, str> {
        if default {
            Borrowed(&self.colored_prompt)
        } else {
            Borrowed(prompt)
        }
    }

    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
        Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
    }

    fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
        self.highlighter.highlight(line, pos)
    }

    fn highlight_char(&self, line: &str, pos: usize) -> bool {
        self.highlighter.highlight_char(line, pos)
    }
}

impl Validator for MyHelper {
    fn validate(
        &self,
        ctx: &mut validate::ValidationContext,
    ) -> rustyline::Result<validate::ValidationResult> {
        self.validator.validate(ctx)
    }

    fn validate_while_typing(&self) -> bool {
        self.validator.validate_while_typing()
    }
}

// To debug rustyline:
// RUST_LOG=rustyline=debug cargo run --example example 2> debug.log
fn main() -> rustyline::Result<()> {
    env_logger::init();
    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .edit_mode(EditMode::Emacs)
        .output_stream(OutputStreamType::Stdout)
        .build();
    let h = MyHelper {
        completer: FilenameCompleter::new(),
        highlighter: MatchingBracketHighlighter::new(),
        hinter: HistoryHinter {},
        colored_prompt: "".to_owned(),
        validator: MatchingBracketValidator::new(),
    };
    let mut rl = Editor::with_config(config);
    rl.set_helper(Some(h));
    rl.bind_sequence(KeyEvent::alt('n'), Cmd::HistorySearchForward);
    rl.bind_sequence(KeyEvent::alt('p'), Cmd::HistorySearchBackward);
    if rl.load_history("history.txt").is_err() {
        println!("No previous history.");
    }
    loop {
        let p = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>";
        rl.helper_mut().expect("No helper").colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p);
        let readline = rl.readline(&p);
        match readline {
            Ok(line) => {
                rl.add_history_entry(line.as_str());
                println!("Line: {}", line);
            }
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
    rl.append_history("history.txt")
}
@gwenn
Copy link
Collaborator

gwenn commented Dec 4, 2021

use rustyline::{Editor, Result};

fn main() -> Result<()> {
    let mut rl = Editor::<()>::new();
    loop {
        let line = rl.readline("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>")?;
        rl.add_history_entry(line.as_str());
    }
}
rustyline % cargo run --example minimal
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>line1
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>line2
(reverse-i-search)`e': line2            thread 'main' panicked at 'assertion failed: self.layout.cursor <= self.layout.end', /Users/gwen/Rust/rustyline/src/edit.rs:128:13

@gwenn gwenn added the bug label Dec 4, 2021
@gwenn
Copy link
Collaborator

gwenn commented Dec 4, 2021

We try to move cursor to the end of line (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>line2) but this is not the actual line during reverse search ((reverse-i-search)e': line2`):

layout = {rustyline::layout::Layout} 
 prompt_size = {rustyline::layout::Position} 
  col = {usize} 35
  row = {usize} 0
 default_prompt = {bool} false
 cursor = {rustyline::layout::Position} 
  col = {usize} 40 -- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>line2
  row = {usize} 0
 end = {rustyline::layout::Position} 
  col = {usize} 28 -- (reverse-i-search)`e': line2
  row = {usize} 0

@gwenn
Copy link
Collaborator

gwenn commented Dec 4, 2021

Thanks for the detailed bug report.
Could you please give #576 a try ?

@gwenn gwenn closed this as completed in #576 Dec 4, 2021
@gwenn
Copy link
Collaborator

gwenn commented Dec 4, 2021

Version 9.1.0 released.

@nicklan
Copy link
Author

nicklan commented Jan 3, 2022

Thanks for the quick fix! It sorted out my problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants