From 28ddd8440a560a52594fb8674bd2250e19610f1e Mon Sep 17 00:00:00 2001 From: Larpon Date: Tue, 25 Jan 2022 13:46:48 +0100 Subject: [PATCH] term.ui: add support for multi byte/UTF-8 events (#13274) --- vlib/term/ui/input_nix.c.v | 3 ++ vlib/term/ui/termios_nix.c.v | 53 ++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/vlib/term/ui/input_nix.c.v b/vlib/term/ui/input_nix.c.v index d11f3b4bd8..c83d02eadd 100644 --- a/vlib/term/ui/input_nix.c.v +++ b/vlib/term/ui/input_nix.c.v @@ -6,6 +6,9 @@ module ui struct ExtraContext { mut: read_buf []byte + // read_all_bytes causes all the raw bytes to be read as one event unit. + // This is cruicial for UTF-8 support since Unicode codepoints can span several bytes. + read_all_bytes bool = true } const ctx_ptr = &Context(0) diff --git a/vlib/term/ui/termios_nix.c.v b/vlib/term/ui/termios_nix.c.v index 9bbaed3308..60355fd1ea 100644 --- a/vlib/term/ui/termios_nix.c.v +++ b/vlib/term/ui/termios_nix.c.v @@ -258,8 +258,14 @@ fn (mut ctx Context) parse_events() { event = e ctx.shift(len) } else { - event = single_char(ctx.read_buf.bytestr()) - ctx.shift(1) + if ctx.read_all_bytes { + e, len := multi_char(ctx.read_buf.bytestr()) + event = e + ctx.shift(len) + } else { + event = single_char(ctx.read_buf.bytestr()) + ctx.shift(1) + } } if event != 0 { ctx.event(event) @@ -271,6 +277,47 @@ fn (mut ctx Context) parse_events() { fn single_char(buf string) &Event { ch := buf[0] + mut event := &Event{ + typ: .key_down + ascii: ch + code: KeyCode(ch) + utf8: ch.ascii_str() + } + + match ch { + // special handling for `ctrl + letter` + // TODO: Fix assoc in V and remove this workaround :/ + // 1 ... 26 { event = Event{ ...event, code: KeyCode(96 | ch), modifiers: .ctrl } } + // 65 ... 90 { event = Event{ ...event, code: KeyCode(32 | ch), modifiers: .shift } } + // The bit `or`s here are really just `+`'s, just written in this way for a tiny performance improvement + // don't treat tab, enter as ctrl+i, ctrl+j + 1...8, 11...26 { + event = &Event{ + typ: event.typ + ascii: event.ascii + utf8: event.utf8 + code: KeyCode(96 | ch) + modifiers: .ctrl + } + } + 65...90 { + event = &Event{ + typ: event.typ + ascii: event.ascii + utf8: event.utf8 + code: KeyCode(32 | ch) + modifiers: .shift + } + } + else {} + } + + return event +} + +fn multi_char(buf string) (&Event, int) { + ch := buf[0] + mut event := &Event{ typ: .key_down ascii: ch @@ -306,7 +353,7 @@ fn single_char(buf string) &Event { else {} } - return event + return event, buf.len } // Gets an entire, independent escape sequence from the buffer