3

I'm trying to implement a simple text editor using the termion crate. When the user presses Backspace, I'd like to erase a single character - but I can't figure out how to achieve that.

The crate seems to only offer these options:

AfterCursor     Clear everything after the cursor.
All             Clear the entire screen.
BeforeCursor    Clear everything before the cursor.
CurrentLine     Clear the current line.
UntilNewline    Clear from cursor to newline.

None of which sound fitting. UntilNewLine sort of works, unless you let the user move the cursor to the left, in which case it deletes more than 1 character (basically the remainder of the line).

My current code:

fn main() {
    let stdin = io::stdin();
    let mut stdout = stdout().into_raw_mode().unwrap();

    write!(stdout, "{}{}", termion::clear::All, termion::cursor::Goto(1, 1));
    stdout.flush().unwrap();

    for c in stdin.keys() {
        match c.unwrap() {
            Key::Esc => break,
            Key::Left => {
                let (x, y) = stdout.cursor_pos().unwrap();
                write!(stdout, "{}", termion::cursor::Goto(x-1, y));
            },
            Key::Right => {
                let (x, y) = stdout.cursor_pos().unwrap();
                write!(stdout, "{}", termion::cursor::Goto(x+1, y));
            },
            Key::Up => {
                let (x, y) = stdout.cursor_pos().unwrap();
                write!(stdout, "{}", termion::cursor::Goto(x, y-1));
            },
            Key::Down => {
                let (x, y) = stdout.cursor_pos().unwrap();
                write!(stdout, "{}", termion::cursor::Goto(x, y+1));
            },
            Key::Backspace => {
                let (x, y) = stdout.cursor_pos().unwrap();
                write!(stdout, "{}{}", termion::cursor::Goto(x-1, y), termion::clear::UntilNewline );
            },
            Key::Char('\n') => { write!(stdout, "\r\n"); },
            Key::Char(x) => { write!(stdout, "{}", x); },
            _ => ()
        }
        stdout.flush().unwrap();
    }
}

I guess I have a two-part question:

  1. How do I solve this particular problem (clear a single char)?
  2. If the solution is somewhere in the docs, how would I go about finding it? I tried searching for delete / backspace / clear - but that didn't work. Is there something I could have done here, or am I running into the limitation of the library?
ilmoi
  • 1,994
  • 2
  • 21
  • 45
  • In the past I've done it like this: https://github.com/peterjoel/inspector/blob/9d924e52862476b80fb143e6ef415e1f7878d7af/cli/src/console.rs#L299. That is, define a more abstract model which can be updated however you please and rendered in a separate step. – Peter Hall Jul 03 '21 at 21:15
  • If you want to implement more complicated features., like multi-line selection or different editing modes, you'll find this approach much easier to work with. – Peter Hall Jul 03 '21 at 21:18
  • I haven't tried [tui](https://docs.rs/tui/0.15.0/tui/), but it offers more abstraction and features on top of termion. I would _guess_ it will do what you want. – Peter Hall Jul 03 '21 at 21:21

1 Answers1

3

To erase a single character, position the cursor before it, and then print a space to overwrite the character.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
  • 3
    Smart! for Anyone looking in the future this code worked: `let (x, y) = stdout.cursor_pos().unwrap(); write!(stdout, "{}{}", " ", termion::cursor::Goto(x-1, y));` Thanks for the help. – ilmoi Jul 04 '21 at 06:18