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

Unexpected ParserError #12

Open
Tim-Paik opened this issue Oct 3, 2022 · 10 comments
Open

Unexpected ParserError #12

Tim-Paik opened this issue Oct 3, 2022 · 10 comments

Comments

@Tim-Paik
Copy link

Tim-Paik commented Oct 3, 2022

I'm having some issues trying to use this crate to make an interaction for my no_std project.
I tried implementing my own IO type (for my environment), but always panic when building the editor:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParserError', src/main.rs:9:10
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panicking.rs:142:14
   2: core::result::unwrap_failed
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/result.rs:1814:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/result.rs:1107:23
   4: testkey::main
             at ./src/main.rs:6:22
   5: core::ops::function::FnOnce::call_once
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/ops/function.rs:248:5

here is my code:

// main.rs
use noline::builder::EditorBuilder;
use std::io::Write;

fn main() {
    let mut io = Console::new();
    let mut editor = EditorBuilder::new_unbounded()
        .with_unbounded_history()
        .build_sync(&mut io)
        .unwrap();
    loop {
        if let Ok(line) = editor.readline("> ", &mut io) {
            write!(io, "Read: '{}'\n\r", line).unwrap();
        } else {
            break;
        }
    }
}

pub struct Console(Vec<u8>);

impl Console {
    pub fn new() -> Self {
        let vec = Vec::from([b'a', b'b', b'c']);
        Self(vec)
    }
}

impl noline::sync::Read for Console {
    type Error = core::convert::Infallible;

    fn read(&mut self) -> Result<u8, Self::Error> {
        if let Some(byte) = self.0.last() {
            return Ok(*byte);
        } else {
            panic!("should panic here");
        }
    }
}

impl noline::sync::Write for Console {
    type Error = std::io::Error;

    fn write(&mut self, word: &[u8]) -> Result<(), Self::Error> {
        std::io::Write::write(&mut std::io::stdout(), word).map(|_| ())
    }

    fn flush(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl Write for Console {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        std::io::Write::write(&mut std::io::stdout(), buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        std::io::stdout().flush()
    }
}

Did I write something wrong? It looks like everything works fine

@dimpolo
Copy link

dimpolo commented Dec 6, 2022

I had a similar problem so I went digging:

EditorBuilder::build_sync will send these bytes and expect a response indicating the terminal size

pub fn init() -> &'static [u8] {
// There is no command to request the size of the terminal window,
// so to probe the size we move the cursor way out of the screen
// and then request the position, because the cursor must be in
// the screen this gives us the size.
"\r\x1b[J\x1b7\x1b[6n\x1b[999;999H\x1b[6n\x1b8".as_bytes()
}

Your Console provides an invalid response which results in the error.

Btw, you should use self.0.pop() instead of self.0.last() in your Read impl.

@eivindbergem
Copy link
Contributor

@Tim-Paik Sorry for not responding, I missed the github notification so I didn't see this before now.

Do you have a full example I can have a look at?

@Tim-Paik
Copy link
Author

@Tim-Paik Sorry for not responding, I missed the github notification so I didn't see this before now.

Do you have a full example I can have a look at?

You can execute the following command to reproduce my problem

cargo new nolinetest
cd nolinetest
cargo add noline --features alloc

Populate src/main.rs with the code from main.rs that I provided at the beginning, after that, you can use cargo run to run it

I had a similar problem so I went digging:

EditorBuilder::build_sync will send these bytes and expect a response indicating the terminal size

pub fn init() -> &'static [u8] {
// There is no command to request the size of the terminal window,
// so to probe the size we move the cursor way out of the screen
// and then request the position, because the cursor must be in
// the screen this gives us the size.
"\r\x1b[J\x1b7\x1b[6n\x1b[999;999H\x1b[6n\x1b8".as_bytes()
}

Your Console provides an invalid response which results in the error.

Btw, you should use self.0.pop() instead of self.0.last() in your Read impl.

Looks like you're making sense, since I'm not providing a terminal width, which noline definitely needs. But I don't know how should I provide the size of the terminal...

@eivindbergem
Copy link
Contributor

eivindbergem commented Feb 26, 2023

Noline expects to be connected to an ANSI terminal. During init, noline probes the terminal using ANSI escape codes to get the size of the terminal. You can have a look at here for an example of a mockterminal.

But, what is your use case here? Without an actual terminal, noline isn't of much use.

@rmsc
Copy link

rmsc commented Sep 6, 2024

Not entirely sure if related, but I'm consistently getting this panic on the rp2040 example. Regardless of the dumb terminal emulator I use, the first time I hit "enter" it panics at the unwrap:

    let mut editor = EditorBuilder::from_slice(&mut buffer)
        .with_slice_history(&mut history)
        .build_sync(&mut io)
        .unwrap();

I had to modify the example slightly for it to work on the second attempt:

    let mut editor = loop {
        break match EditorBuilder::from_slice(&mut buffer)
            .with_slice_history(&mut history)
            .build_sync(&mut io) {
                Ok(ed) => ed,
                Err(err) => {
                    let error = match err {
                        NolineError::IoError(_) => "IoError",
                        NolineError::ParserError => "ParseError",
                        NolineError::Aborted=> "Aborted",
                    };
                    writeln!(io, "Error: {}\r", error).unwrap();
                    continue;
                }
        }
    };

Now it works, but the error still happens:

$ picocom /dev/ttyACM1
(...)
Terminal ready
Error: ParseError
>

Is this something that's fixable on noline's side, or should the example be modified to keep retrying?

@eivindbergem
Copy link
Contributor

I would need to be able to reproduce this in order to debug it. Could you provide more details of your setup:

  • What operating system are you running?
  • Does this happen with other terminal emulators (minicom, etc)?
  • What terminal settings are you using (output of stty -F /dev/ttyACM?
  • What UART-to-USB adapter are you using?

@rmsc
Copy link

rmsc commented Sep 9, 2024

Thanks!

  • What operating system are you running?

Linux 6.10.8 (archlinux)

  • Does this happen with other terminal emulators (minicom, etc)?

Yes, I've tried minicom and screen, same thing on both.

  • What terminal settings are you using (output of stty -F /dev/ttyACM?
$ stty -F /dev/ttyACM1
speed 9600 baud; line = 0;
min = 1; time = 0;
-brkint -icrnl -imaxbel
-opost
-isig -icanon -iexten -echo
  • What UART-to-USB adapter are you using?

This is a straight USB connection to the rp-pico (using rp2040 usb serial example).

@eivindbergem
Copy link
Contributor

Thanks! I don't have an RP pico here now, but I'll try to bring one tomorrow and have look.

@eivindbergem
Copy link
Contributor

I've done some testing, and it appears that when using minicom the serial port is opened for read/write before configuring it, which means that dtr() is true on the device, but raw -echo hasn't been set yet. Try running stty raw -echo -F /dev/ttyACM1 before starting minicom and see if that helps.

I guess noline should be a bit more robust in the face of unexpected data, but I'm not sure how this should be handled.

@eivindbergem
Copy link
Contributor

@rmsc Check out #36. It will hopefully solve this problem.

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

No branches or pull requests

4 participants