Skip to content

Commit

Permalink
Fix behavior on ^A and unicode support.
Browse files Browse the repository at this point in the history
^A now correctly takes the user to the beginning of the current field.

The strategy initially used for string indexing was pretty naive and did
not take Unicode into account (and characters spanning multiple bytes).
This would mess up cursor offset calculations and overall string
indexing (leading to crashes).
  • Loading branch information
apognu committed Jul 6, 2020
1 parent 413689b commit 0bd8dde
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 15 deletions.
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- run: rm -rf .git/
- name: Build
uses: actions-rs/cargo@v1
with:
Expand Down
4 changes: 2 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ impl Greeter {
self.config = match opts.parse(&env::args().collect::<Vec<String>>()) {
Ok(matches) => Some(matches),

Err(usage) => {
println!("{}", usage);
Err(error) => {
println!("{}", error);
print_usage(opts);
process::exit(1);
}
Expand Down
25 changes: 19 additions & 6 deletions src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ pub fn handle(greeter: &mut Greeter, events: &Events) -> Result<(), Box<dyn Erro
}
}

Key::Ctrl('a') => greeter.cursor_offset = -(greeter.username.len() as i16),
Key::Ctrl('a') => {
let value = match greeter.mode {
Mode::Username => &greeter.username,
_ => &greeter.answer,
};

greeter.cursor_offset = -(value.chars().count() as i16);
}

Key::Ctrl('e') => greeter.cursor_offset = 0,

Key::Char('\n') | Key::Char('\t') => match greeter.mode {
Expand Down Expand Up @@ -102,9 +110,11 @@ fn insert_key(greeter: &mut Greeter, c: char) {
Mode::Sessions => return,
};

let index = value.len() as i16 + greeter.cursor_offset;
let index = (value.chars().count() as i16 + greeter.cursor_offset) as usize;
let left = value.chars().take(index);
let right = value.chars().skip(index);

value.insert(index as usize, c);
*value = left.chain(vec![c].into_iter()).chain(right).collect();
}

fn delete_key(greeter: &mut Greeter, key: Key) {
Expand All @@ -116,13 +126,16 @@ fn delete_key(greeter: &mut Greeter, key: Key) {
};

let index = match key {
Key::Backspace => value.len() as i16 + greeter.cursor_offset - 1,
Key::Delete => value.len() as i16 + greeter.cursor_offset,
Key::Backspace => (value.chars().count() as i16 + greeter.cursor_offset - 1) as usize,
Key::Delete => (value.chars().count() as i16 + greeter.cursor_offset) as usize,
_ => 0,
};

if value.chars().nth(index as usize).is_some() {
value.remove(index as usize);
let left = value.chars().take(index);
let right = value.chars().skip(index + 1);

*value = left.chain(right).collect();

if let Key::Delete = key {
greeter.cursor_offset += 1;
Expand Down
2 changes: 1 addition & 1 deletion src/ui/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub fn draw(greeter: &mut Greeter, f: &mut Frame<TermionBackend<RawTerminal<io::
f.render_widget(command_label, chunks[0]);
f.render_widget(command_value, Rect::new(1 + chunks[0].x + COMMAND.len() as u16, chunks[0].y, get_input_width(greeter, COMMAND), 1));

let offset = get_cursor_offset(greeter, greeter.new_command.len());
let offset = get_cursor_offset(greeter, greeter.new_command.chars().count());

Ok((2 + cursor.x + COMMAND.len() as u16 + offset as u16, cursor.y + 1))
}
10 changes: 5 additions & 5 deletions src/ui/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub fn draw(greeter: &mut Greeter, f: &mut Frame<TermionBackend<RawTerminal<io::
f.render_widget(
answer_value,
Rect::new(
chunks[ANSWER_INDEX].x + greeter.prompt.len() as u16,
chunks[ANSWER_INDEX].x + greeter.prompt.chars().count() as u16,
chunks[ANSWER_INDEX].y,
get_input_width(greeter, &greeter.prompt),
1,
Expand All @@ -114,18 +114,18 @@ pub fn draw(greeter: &mut Greeter, f: &mut Frame<TermionBackend<RawTerminal<io::

match greeter.mode {
Mode::Username => {
let offset = get_cursor_offset(greeter, greeter.username.len());
let offset = get_cursor_offset(greeter, greeter.username.chars().count());

Ok((2 + cursor.x + USERNAME.len() as u16 + offset as u16, USERNAME_INDEX as u16 + cursor.y))
}

Mode::Password => {
let offset = get_cursor_offset(greeter, greeter.answer.len());
let offset = get_cursor_offset(greeter, greeter.answer.chars().count());

if greeter.secret {
Ok((1 + cursor.x + greeter.prompt.len() as u16, ANSWER_INDEX as u16 + prompt_padding + cursor.y))
Ok((1 + cursor.x + greeter.prompt.chars().count() as u16, ANSWER_INDEX as u16 + prompt_padding + cursor.y))
} else {
Ok((1 + cursor.x + greeter.prompt.len() as u16 + offset as u16, ANSWER_INDEX as u16 + prompt_padding + cursor.y))
Ok((1 + cursor.x + greeter.prompt.chars().count() as u16 + offset as u16, ANSWER_INDEX as u16 + prompt_padding + cursor.y))
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/ui/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub fn get_height(greeter: &Greeter) -> u16 {
}

pub fn get_input_width(greeter: &Greeter, label: &str) -> u16 {
greeter.width() - label.len() as u16 - 4 - 1
greeter.width() - label.chars().count() as u16 - 4 - 1
}

pub fn get_cursor_offset(greeter: &mut Greeter, length: usize) -> i16 {
Expand Down

0 comments on commit 0bd8dde

Please sign in to comment.