Fix behavior on ^A and unicode support.

^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).
This commit is contained in:
Antoine POPINEAU 2020-07-06 13:43:56 +02:00
parent 413689bf80
commit 0bd8dde043
No known key found for this signature in database
GPG Key ID: A78AC64694F84063
6 changed files with 29 additions and 15 deletions

View File

@ -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:

View File

@ -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);
}

View File

@ -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 {
@ -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) {
@ -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;

View File

@ -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))
}

View File

@ -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,
@ -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))
}
}

View File

@ -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 {