aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-03-12 00:33:26 -0500
committerJesse Luehrs <doy@tozt.net>2021-03-12 01:31:04 -0500
commit9885f5ca8db5060f95066b16f68f69c1a858cf0f (patch)
treefc8de46ffcbbfaf58a40b111ef85e056c0ccf72b
parent2071214d70206e0dad43222c350ce3cd1ca31192 (diff)
downloadtextmode-9885f5ca8db5060f95066b16f68f69c1a858cf0f.tar.gz
textmode-9885f5ca8db5060f95066b16f68f69c1a858cf0f.zip
refactor input implementations to share some code
-rw-r--r--src/blocking/input.rs367
-rw-r--r--src/input.rs374
-rw-r--r--src/lib.rs20
-rw-r--r--src/private.rs339
4 files changed, 507 insertions, 593 deletions
diff --git a/src/blocking/input.rs b/src/blocking/input.rs
index bdc916b..fc173f0 100644
--- a/src/blocking/input.rs
+++ b/src/blocking/input.rs
@@ -3,6 +3,8 @@ use crate::error::*;
use std::io::Read as _;
use std::os::unix::io::AsRawFd as _;
+use crate::private::InputImpl as _;
+
pub struct RawGuard {
termios: nix::sys::termios::Termios,
cleaned_up: bool,
@@ -60,6 +62,56 @@ pub struct Input {
parse_single: bool,
}
+impl crate::private::InputImpl for Input {
+ fn buf(&self) -> &[u8] {
+ &self.buf[self.pos..]
+ }
+
+ fn buf_mut(&mut self) -> &mut [u8] {
+ &mut self.buf[self.pos..]
+ }
+
+ fn buf_mut_vec(&mut self) -> &mut Vec<u8> {
+ &mut self.buf
+ }
+
+ fn consume(&mut self, n: usize) {
+ self.pos += n;
+ }
+
+ fn unconsume(&mut self, n: usize) {
+ self.pos -= n;
+ }
+
+ fn buf_is_empty(&self) -> bool {
+ self.pos >= self.buf.len()
+ }
+
+ fn buf_at_beginning(&self) -> bool {
+ self.pos == 0
+ }
+
+ fn should_parse_utf8(&self) -> bool {
+ self.parse_utf8
+ }
+
+ fn should_parse_ctrl(&self) -> bool {
+ self.parse_ctrl
+ }
+
+ fn should_parse_meta(&self) -> bool {
+ self.parse_meta
+ }
+
+ fn should_parse_special_keys(&self) -> bool {
+ self.parse_special_keys
+ }
+
+ fn should_parse_single(&self) -> bool {
+ self.parse_single
+ }
+}
+
#[allow(clippy::new_without_default)]
impl Input {
pub fn new() -> Result<(Self, RawGuard)> {
@@ -99,305 +151,32 @@ impl Input {
}
pub fn read_key(&mut self) -> Result<Option<crate::Key>> {
+ self.fill_buf()?;
+
if self.parse_single {
self.read_single_key()
} else {
- self.maybe_fill_buf()?;
- if self.parse_utf8 {
- let prefix: Vec<_> = self
- .buf
- .iter()
- .copied()
- .skip(self.pos)
- .take_while(|&c| matches!(c, 32..=126 | 128..=255))
- .collect();
- if !prefix.is_empty() {
- self.pos += prefix.len();
- match std::string::String::from_utf8(prefix) {
- Ok(s) => return Ok(Some(crate::Key::String(s))),
- Err(e) => {
- return Ok(Some(crate::Key::Bytes(
- e.into_bytes(),
- )))
- }
- }
- }
- }
-
- let prefix: Vec<_> = self
- .buf
- .iter()
- .copied()
- .skip(self.pos)
- .take_while(|&c| match c {
- 0 => true,
- 1..=26 => !self.parse_ctrl,
- 27 => !self.parse_meta && !self.parse_special_keys,
- 28..=31 => true,
- 32..=126 => true,
- 127 => !self.parse_special_keys,
- 128..=255 => true,
- })
- .collect();
- if !prefix.is_empty() {
- self.pos += prefix.len();
- return Ok(Some(crate::Key::Bytes(prefix)));
- }
-
- self.read_single_key().map(|key| {
- if let Some(crate::Key::Byte(c)) = key {
- Some(crate::Key::Bytes(vec![c]))
- } else {
- key
- }
- })
- }
- }
-
- fn read_single_key(&mut self) -> Result<Option<crate::Key>> {
- match self.getc(true)? {
- Some(0) => Ok(Some(crate::Key::Byte(0))),
- Some(c @ 1..=26) => {
- if self.parse_ctrl {
- Ok(Some(crate::Key::Ctrl(b'a' + c - 1)))
- } else {
- Ok(Some(crate::Key::Byte(c)))
- }
- }
- Some(27) => {
- if self.parse_meta || self.parse_special_keys {
- self.read_escape_sequence()
- } else {
- Ok(Some(crate::Key::Byte(27)))
- }
- }
- Some(c @ 28..=31) => Ok(Some(crate::Key::Byte(c))),
- Some(c @ 32..=126) => {
- if self.parse_utf8 {
- Ok(Some(crate::Key::Char(c as char)))
- } else {
- Ok(Some(crate::Key::Byte(c)))
- }
- }
- Some(127) => {
- if self.parse_special_keys {
- Ok(Some(crate::Key::Backspace))
- } else {
- Ok(Some(crate::Key::Byte(127)))
- }
- }
- Some(c @ 128..=255) => {
- if self.parse_utf8 {
- self.read_utf8_char(c)
- } else {
- Ok(Some(crate::Key::Byte(c)))
- }
- }
- None => Ok(None),
- }
- }
-
- fn read_escape_sequence(&mut self) -> Result<Option<crate::Key>> {
- let mut seen = vec![b'\x1b'];
-
- macro_rules! fail {
- () => {{
- for &c in seen.iter().skip(1).rev() {
- self.ungetc(c);
- }
- if self.parse_special_keys {
- return Ok(Some(crate::Key::Escape));
- } else {
- return Ok(Some(crate::Key::Byte(27)));
- }
- }};
- }
- macro_rules! next_byte {
- () => {
- match self.getc(false)? {
- Some(c) => c,
- None => {
- fail!()
- }
- }
- };
- }
-
- enum EscapeState {
- Escape,
- CSI(Vec<u8>),
- CKM,
- }
-
- let mut state = EscapeState::Escape;
- loop {
- let c = next_byte!();
- seen.push(c);
- match state {
- EscapeState::Escape => match c {
- b'[' => {
- if self.parse_special_keys {
- state = EscapeState::CSI(vec![]);
- } else {
- fail!()
- }
- }
- b'O' => {
- if self.parse_special_keys {
- state = EscapeState::CKM;
- } else {
- fail!()
- }
- }
- b' '..=b'N' | b'P'..=b'Z' | b'\\'..=b'~' => {
- if self.parse_meta {
- return Ok(Some(crate::Key::Meta(c)));
- } else {
- fail!()
- }
- }
- _ => fail!(),
- },
- EscapeState::CSI(ref mut param) => match c {
- b'A' => return Ok(Some(crate::Key::Up)),
- b'B' => return Ok(Some(crate::Key::Down)),
- b'C' => return Ok(Some(crate::Key::Right)),
- b'D' => return Ok(Some(crate::Key::Left)),
- b'H' => return Ok(Some(crate::Key::Home)),
- b'F' => return Ok(Some(crate::Key::End)),
- b'0'..=b'9' => param.push(c),
- b'~' => match param.as_slice() {
- [b'2'] => return Ok(Some(crate::Key::Insert)),
- [b'3'] => return Ok(Some(crate::Key::Delete)),
- [b'5'] => return Ok(Some(crate::Key::PageUp)),
- [b'6'] => return Ok(Some(crate::Key::PageDown)),
- [b'1', b'5'] => return Ok(Some(crate::Key::F(5))),
- [b'1', b'7'] => return Ok(Some(crate::Key::F(6))),
- [b'1', b'8'] => return Ok(Some(crate::Key::F(7))),
- [b'1', b'9'] => return Ok(Some(crate::Key::F(8))),
- [b'2', b'0'] => return Ok(Some(crate::Key::F(9))),
- [b'2', b'1'] => return Ok(Some(crate::Key::F(10))),
- [b'2', b'3'] => return Ok(Some(crate::Key::F(11))),
- [b'2', b'4'] => return Ok(Some(crate::Key::F(12))),
- [b'2', b'5'] => return Ok(Some(crate::Key::F(13))),
- [b'2', b'6'] => return Ok(Some(crate::Key::F(14))),
- [b'2', b'8'] => return Ok(Some(crate::Key::F(15))),
- [b'2', b'9'] => return Ok(Some(crate::Key::F(16))),
- [b'3', b'1'] => return Ok(Some(crate::Key::F(17))),
- [b'3', b'2'] => return Ok(Some(crate::Key::F(18))),
- [b'3', b'3'] => return Ok(Some(crate::Key::F(19))),
- [b'3', b'4'] => return Ok(Some(crate::Key::F(20))),
- _ => fail!(),
- },
- _ => fail!(),
- },
- EscapeState::CKM => match c {
- b'A' => return Ok(Some(crate::Key::KeypadUp)),
- b'B' => return Ok(Some(crate::Key::KeypadDown)),
- b'C' => return Ok(Some(crate::Key::KeypadRight)),
- b'D' => return Ok(Some(crate::Key::KeypadLeft)),
- b'P' => return Ok(Some(crate::Key::F(1))),
- b'Q' => return Ok(Some(crate::Key::F(2))),
- b'R' => return Ok(Some(crate::Key::F(3))),
- b'S' => return Ok(Some(crate::Key::F(4))),
- _ => fail!(),
- },
- }
- }
- }
-
- fn read_utf8_char(&mut self, initial: u8) -> Result<Option<crate::Key>> {
- let mut buf = vec![initial];
-
- macro_rules! fail {
- () => {{
- for &c in buf.iter().skip(1).rev() {
- self.ungetc(c);
- }
- return Ok(Some(crate::Key::Byte(initial)));
- }};
- }
- macro_rules! next_byte {
- () => {
- match self.getc(true)? {
- Some(c) => {
- if (0b1000_0000..=0b1011_1111).contains(&c) {
- c
- } else {
- fail!()
- }
- }
- None => return Ok(None),
- }
- };
- }
-
- match initial {
- 0b0000_0000..=0b0111_1111 => {}
- 0b1100_0000..=0b1101_1111 => {
- buf.push(next_byte!());
- }
- 0b1110_0000..=0b1110_1111 => {
- buf.push(next_byte!());
- buf.push(next_byte!());
+ if let Some(s) = self.try_read_string()? {
+ return Ok(Some(s));
}
- 0b1111_0000..=0b1111_0111 => {
- buf.push(next_byte!());
- buf.push(next_byte!());
- buf.push(next_byte!());
- }
- _ => fail!(),
- }
- match std::string::String::from_utf8(buf) {
- // unwrap is fine because buf always contains at least the initial
- // character, and we have already done the parsing to ensure that
- // it contains a valid utf8 character before getting here
- Ok(s) => Ok(Some(crate::Key::Char(s.chars().next().unwrap()))),
- Err(e) => {
- buf = e.into_bytes();
- fail!()
+ if let Some(s) = self.try_read_bytes()? {
+ return Ok(Some(s));
}
- }
- }
- fn getc(&mut self, fill: bool) -> Result<Option<u8>> {
- if fill {
- if !self.maybe_fill_buf()? {
- return Ok(None);
+ if let Some(key) = self.read_single_key()? {
+ return Ok(Some(self.normalize_to_bytes(key)));
}
- } else {
- if self.buf_is_empty() {
- return Ok(None);
- }
- }
- let c = self.buf[self.pos];
- self.pos += 1;
- Ok(Some(c))
- }
- fn ungetc(&mut self, c: u8) {
- if self.pos == 0 {
- self.buf.insert(0, c);
- } else {
- self.pos -= 1;
- self.buf[self.pos] = c;
+ Ok(None)
}
}
- fn maybe_fill_buf(&mut self) -> Result<bool> {
- if self.buf_is_empty() {
- self.fill_buf()
- } else {
- Ok(true)
+ fn fill_buf(&mut self) -> Result<bool> {
+ if !self.buf_is_empty() {
+ return Ok(true);
}
- }
-
- fn buf_is_empty(&self) -> bool {
- self.pos >= self.buf.len()
- }
- fn fill_buf(&mut self) -> Result<bool> {
self.buf.resize(4096, 0);
self.pos = 0;
let bytes = read_stdin(&mut self.buf)?;
@@ -405,6 +184,24 @@ impl Input {
return Ok(false);
}
self.buf.truncate(bytes);
+
+ if self.parse_utf8 {
+ let mut extra = self.find_truncated_utf8();
+ if extra > 0 {
+ let mut cur = self.buf.len();
+ self.buf.resize(4096 + extra, 0);
+ while extra > 0 {
+ let bytes = read_stdin(&mut self.buf[cur..])?;
+ if bytes == 0 {
+ return Ok(false);
+ }
+ cur += bytes;
+ extra = extra.saturating_sub(bytes);
+ }
+ self.buf.truncate(cur);
+ }
+ }
+
Ok(true)
}
}
diff --git a/src/input.rs b/src/input.rs
index a324c85..7a87c8c 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -3,6 +3,8 @@ use crate::error::*;
use futures_lite::io::AsyncReadExt as _;
use std::os::unix::io::AsRawFd as _;
+use crate::private::InputImpl as _;
+
pub struct RawGuard {
termios: nix::sys::termios::Termios,
cleaned_up: bool,
@@ -73,6 +75,56 @@ pub struct Input {
parse_single: bool,
}
+impl super::private::InputImpl for Input {
+ fn buf(&self) -> &[u8] {
+ &self.buf[self.pos..]
+ }
+
+ fn buf_mut(&mut self) -> &mut [u8] {
+ &mut self.buf[self.pos..]
+ }
+
+ fn buf_mut_vec(&mut self) -> &mut Vec<u8> {
+ &mut self.buf
+ }
+
+ fn consume(&mut self, n: usize) {
+ self.pos += n;
+ }
+
+ fn unconsume(&mut self, n: usize) {
+ self.pos -= n;
+ }
+
+ fn buf_is_empty(&self) -> bool {
+ self.pos >= self.buf.len()
+ }
+
+ fn buf_at_beginning(&self) -> bool {
+ self.pos == 0
+ }
+
+ fn should_parse_utf8(&self) -> bool {
+ self.parse_utf8
+ }
+
+ fn should_parse_ctrl(&self) -> bool {
+ self.parse_ctrl
+ }
+
+ fn should_parse_meta(&self) -> bool {
+ self.parse_meta
+ }
+
+ fn should_parse_special_keys(&self) -> bool {
+ self.parse_special_keys
+ }
+
+ fn should_parse_single(&self) -> bool {
+ self.parse_single
+ }
+}
+
#[allow(clippy::new_without_default)]
impl Input {
pub async fn new() -> Result<(Self, RawGuard)> {
@@ -113,308 +165,32 @@ impl Input {
}
pub async fn read_key(&mut self) -> Result<Option<crate::Key>> {
+ self.fill_buf().await?;
+
if self.parse_single {
- self.read_single_key().await
+ self.read_single_key()
} else {
- self.maybe_fill_buf().await?;
- if self.parse_utf8 {
- let prefix: Vec<_> = self
- .buf
- .iter()
- .copied()
- .skip(self.pos)
- .take_while(|&c| matches!(c, 32..=126 | 128..=255))
- .collect();
- if !prefix.is_empty() {
- self.pos += prefix.len();
- match std::string::String::from_utf8(prefix) {
- Ok(s) => return Ok(Some(crate::Key::String(s))),
- Err(e) => {
- return Ok(Some(crate::Key::Bytes(
- e.into_bytes(),
- )))
- }
- }
- }
- }
-
- let prefix: Vec<_> = self
- .buf
- .iter()
- .copied()
- .skip(self.pos)
- .take_while(|&c| match c {
- 0 => true,
- 1..=26 => !self.parse_ctrl,
- 27 => !self.parse_meta && !self.parse_special_keys,
- 28..=31 => true,
- 32..=126 => true,
- 127 => !self.parse_special_keys,
- 128..=255 => true,
- })
- .collect();
- if !prefix.is_empty() {
- self.pos += prefix.len();
- return Ok(Some(crate::Key::Bytes(prefix)));
- }
-
- self.read_single_key().await.map(|key| {
- if let Some(crate::Key::Byte(c)) = key {
- Some(crate::Key::Bytes(vec![c]))
- } else {
- key
- }
- })
- }
- }
-
- async fn read_single_key(&mut self) -> Result<Option<crate::Key>> {
- match self.getc(true).await? {
- Some(0) => Ok(Some(crate::Key::Byte(0))),
- Some(c @ 1..=26) => {
- if self.parse_ctrl {
- Ok(Some(crate::Key::Ctrl(b'a' + c - 1)))
- } else {
- Ok(Some(crate::Key::Byte(c)))
- }
- }
- Some(27) => {
- if self.parse_meta || self.parse_special_keys {
- self.read_escape_sequence().await
- } else {
- Ok(Some(crate::Key::Byte(27)))
- }
- }
- Some(c @ 28..=31) => Ok(Some(crate::Key::Byte(c))),
- Some(c @ 32..=126) => {
- if self.parse_utf8 {
- Ok(Some(crate::Key::Char(c as char)))
- } else {
- Ok(Some(crate::Key::Byte(c)))
- }
- }
- Some(127) => {
- if self.parse_special_keys {
- Ok(Some(crate::Key::Backspace))
- } else {
- Ok(Some(crate::Key::Byte(127)))
- }
- }
- Some(c @ 128..=255) => {
- if self.parse_utf8 {
- self.read_utf8_char(c).await
- } else {
- Ok(Some(crate::Key::Byte(c)))
- }
- }
- None => Ok(None),
- }
- }
-
- async fn read_escape_sequence(&mut self) -> Result<Option<crate::Key>> {
- let mut seen = vec![b'\x1b'];
-
- macro_rules! fail {
- () => {{
- for &c in seen.iter().skip(1).rev() {
- self.ungetc(c);
- }
- if self.parse_special_keys {
- return Ok(Some(crate::Key::Escape));
- } else {
- return Ok(Some(crate::Key::Byte(27)));
- }
- }};
- }
- macro_rules! next_byte {
- () => {
- match self.getc(false).await? {
- Some(c) => c,
- None => {
- fail!()
- }
- }
- };
- }
-
- enum EscapeState {
- Escape,
- CSI(Vec<u8>),
- CKM,
- }
-
- let mut state = EscapeState::Escape;
- loop {
- let c = next_byte!();
- seen.push(c);
- match state {
- EscapeState::Escape => match c {
- b'[' => {
- if self.parse_special_keys {
- state = EscapeState::CSI(vec![]);
- } else {
- fail!()
- }
- }
- b'O' => {
- if self.parse_special_keys {
- state = EscapeState::CKM;
- } else {
- fail!()
- }
- }
- b' '..=b'N' | b'P'..=b'Z' | b'\\'..=b'~' => {
- if self.parse_meta {
- return Ok(Some(crate::Key::Meta(c)));
- } else {
- fail!()
- }
- }
- _ => fail!(),
- },
- EscapeState::CSI(ref mut param) => match c {
- b'A' => return Ok(Some(crate::Key::Up)),
- b'B' => return Ok(Some(crate::Key::Down)),
- b'C' => return Ok(Some(crate::Key::Right)),
- b'D' => return Ok(Some(crate::Key::Left)),
- b'H' => return Ok(Some(crate::Key::Home)),
- b'F' => return Ok(Some(crate::Key::End)),
- b'0'..=b'9' => param.push(c),
- b'~' => match param.as_slice() {
- [b'2'] => return Ok(Some(crate::Key::Insert)),
- [b'3'] => return Ok(Some(crate::Key::Delete)),
- [b'5'] => return Ok(Some(crate::Key::PageUp)),
- [b'6'] => return Ok(Some(crate::Key::PageDown)),
- [b'1', b'5'] => return Ok(Some(crate::Key::F(5))),
- [b'1', b'7'] => return Ok(Some(crate::Key::F(6))),
- [b'1', b'8'] => return Ok(Some(crate::Key::F(7))),
- [b'1', b'9'] => return Ok(Some(crate::Key::F(8))),
- [b'2', b'0'] => return Ok(Some(crate::Key::F(9))),
- [b'2', b'1'] => return Ok(Some(crate::Key::F(10))),
- [b'2', b'3'] => return Ok(Some(crate::Key::F(11))),
- [b'2', b'4'] => return Ok(Some(crate::Key::F(12))),
- [b'2', b'5'] => return Ok(Some(crate::Key::F(13))),
- [b'2', b'6'] => return Ok(Some(crate::Key::F(14))),
- [b'2', b'8'] => return Ok(Some(crate::Key::F(15))),
- [b'2', b'9'] => return Ok(Some(crate::Key::F(16))),
- [b'3', b'1'] => return Ok(Some(crate::Key::F(17))),
- [b'3', b'2'] => return Ok(Some(crate::Key::F(18))),
- [b'3', b'3'] => return Ok(Some(crate::Key::F(19))),
- [b'3', b'4'] => return Ok(Some(crate::Key::F(20))),
- _ => fail!(),
- },
- _ => fail!(),
- },
- EscapeState::CKM => match c {
- b'A' => return Ok(Some(crate::Key::KeypadUp)),
- b'B' => return Ok(Some(crate::Key::KeypadDown)),
- b'C' => return Ok(Some(crate::Key::KeypadRight)),
- b'D' => return Ok(Some(crate::Key::KeypadLeft)),
- b'P' => return Ok(Some(crate::Key::F(1))),
- b'Q' => return Ok(Some(crate::Key::F(2))),
- b'R' => return Ok(Some(crate::Key::F(3))),
- b'S' => return Ok(Some(crate::Key::F(4))),
- _ => fail!(),
- },
- }
- }
- }
-
- async fn read_utf8_char(
- &mut self,
- initial: u8,
- ) -> Result<Option<crate::Key>> {
- let mut buf = vec![initial];
-
- macro_rules! fail {
- () => {{
- for &c in buf.iter().skip(1).rev() {
- self.ungetc(c);
- }
- return Ok(Some(crate::Key::Byte(initial)));
- }};
- }
- macro_rules! next_byte {
- () => {
- match self.getc(true).await? {
- Some(c) => {
- if (0b1000_0000..=0b1011_1111).contains(&c) {
- c
- } else {
- fail!()
- }
- }
- None => return Ok(None),
- }
- };
- }
-
- match initial {
- 0b0000_0000..=0b0111_1111 => {}
- 0b1100_0000..=0b1101_1111 => {
- buf.push(next_byte!());
- }
- 0b1110_0000..=0b1110_1111 => {
- buf.push(next_byte!());
- buf.push(next_byte!());
+ if let Some(s) = self.try_read_string()? {
+ return Ok(Some(s));
}
- 0b1111_0000..=0b1111_0111 => {
- buf.push(next_byte!());
- buf.push(next_byte!());
- buf.push(next_byte!());
- }
- _ => fail!(),
- }
- match std::string::String::from_utf8(buf) {
- // unwrap is fine because buf always contains at least the initial
- // character, and we have already done the parsing to ensure that
- // it contains a valid utf8 character before getting here
- Ok(s) => Ok(Some(crate::Key::Char(s.chars().next().unwrap()))),
- Err(e) => {
- buf = e.into_bytes();
- fail!()
+ if let Some(s) = self.try_read_bytes()? {
+ return Ok(Some(s));
}
- }
- }
- async fn getc(&mut self, fill: bool) -> Result<Option<u8>> {
- if fill {
- if !self.maybe_fill_buf().await? {
- return Ok(None);
+ if let Some(key) = self.read_single_key()? {
+ return Ok(Some(self.normalize_to_bytes(key)));
}
- } else {
- if self.buf_is_empty() {
- return Ok(None);
- }
- }
- let c = self.buf[self.pos];
- self.pos += 1;
- Ok(Some(c))
- }
- fn ungetc(&mut self, c: u8) {
- if self.pos == 0 {
- self.buf.insert(0, c);
- } else {
- self.pos -= 1;
- self.buf[self.pos] = c;
+ Ok(None)
}
}
- async fn maybe_fill_buf(&mut self) -> Result<bool> {
- if self.buf_is_empty() {
- self.fill_buf().await
- } else {
- Ok(true)
+ async fn fill_buf(&mut self) -> Result<bool> {
+ if !self.buf_is_empty() {
+ return Ok(true);
}
- }
-
- fn buf_is_empty(&self) -> bool {
- self.pos >= self.buf.len()
- }
- async fn fill_buf(&mut self) -> Result<bool> {
self.buf.resize(4096, 0);
self.pos = 0;
let bytes = read_stdin(&mut self.stdin, &mut self.buf).await?;
@@ -422,6 +198,26 @@ impl Input {
return Ok(false);
}
self.buf.truncate(bytes);
+
+ if self.parse_utf8 {
+ let mut extra = self.find_truncated_utf8();
+ if extra > 0 {
+ let mut cur = self.buf.len();
+ self.buf.resize(4096 + extra, 0);
+ while extra > 0 {
+ let bytes =
+ read_stdin(&mut self.stdin, &mut self.buf[cur..])
+ .await?;
+ if bytes == 0 {
+ return Ok(false);
+ }
+ cur += bytes;
+ extra = extra.saturating_sub(bytes);
+ }
+ self.buf.truncate(cur);
+ }
+ }
+
Ok(true)
}
}
diff --git a/src/lib.rs b/src/lib.rs
index e4abf7b..79caedc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,6 +7,7 @@ mod error;
pub use error::{Error, Result};
mod key;
pub use key::Key;
+mod private;
#[cfg(feature = "async")]
mod output;
@@ -20,25 +21,6 @@ pub use input::{Input, RawGuard};
const INIT: &[u8] = b"\x1b7\x1b[?47h\x1b[2J\x1b[H\x1b[?25h";
const DEINIT: &[u8] = b"\x1b[?47l\x1b8\x1b[?25h";
-mod private {
- pub trait TextmodeImpl {
- fn cur(&self) -> &vt100::Parser;
- fn cur_mut(&mut self) -> &mut vt100::Parser;
- fn next(&self) -> &vt100::Parser;
- fn next_mut(&mut self) -> &mut vt100::Parser;
-
- fn write_u16(&mut self, i: u16) {
- // unwrap is fine because vt100::Parser::write can never fail
- itoa::write(self.next_mut(), i).unwrap();
- }
-
- fn write_u8(&mut self, i: u8) {
- // unwrap is fine because vt100::Parser::write can never fail
- itoa::write(self.next_mut(), i).unwrap();
- }
- }
-}
-
pub trait Textmode: private::TextmodeImpl {
fn screen(&self) -> &vt100::Screen {
self.next().screen()
diff --git a/src/private.rs b/src/private.rs
new file mode 100644
index 0000000..a925a27
--- /dev/null
+++ b/src/private.rs
@@ -0,0 +1,339 @@
+pub trait TextmodeImpl {
+ fn cur(&self) -> &vt100::Parser;
+ fn cur_mut(&mut self) -> &mut vt100::Parser;
+ fn next(&self) -> &vt100::Parser;
+ fn next_mut(&mut self) -> &mut vt100::Parser;
+
+ fn write_u16(&mut self, i: u16) {
+ // unwrap is fine because vt100::Parser::write can never fail
+ itoa::write(self.next_mut(), i).unwrap();
+ }
+
+ fn write_u8(&mut self, i: u8) {
+ // unwrap is fine because vt100::Parser::write can never fail
+ itoa::write(self.next_mut(), i).unwrap();
+ }
+}
+
+pub trait InputImpl {
+ fn buf(&self) -> &[u8];
+ fn buf_mut(&mut self) -> &mut [u8];
+ fn buf_mut_vec(&mut self) -> &mut Vec<u8>;
+ fn consume(&mut self, n: usize);
+ fn unconsume(&mut self, n: usize);
+ fn buf_is_empty(&self) -> bool;
+ fn buf_at_beginning(&self) -> bool;
+
+ fn should_parse_utf8(&self) -> bool;
+ fn should_parse_ctrl(&self) -> bool;
+ fn should_parse_meta(&self) -> bool;
+ fn should_parse_special_keys(&self) -> bool;
+ fn should_parse_single(&self) -> bool;
+
+ fn try_read_string(&mut self) -> crate::Result<Option<crate::Key>> {
+ if !self.should_parse_utf8() {
+ return Ok(None);
+ }
+
+ let prefix: Vec<_> = self
+ .buf()
+ .iter()
+ .copied()
+ .take_while(|&c| matches!(c, 32..=126 | 128..=255))
+ .collect();
+ if !prefix.is_empty() {
+ self.consume(prefix.len());
+ match std::string::String::from_utf8(prefix) {
+ Ok(s) => return Ok(Some(crate::Key::String(s))),
+ Err(e) => return Ok(Some(crate::Key::Bytes(e.into_bytes()))),
+ }
+ }
+
+ Ok(None)
+ }
+
+ fn try_read_bytes(&mut self) -> crate::Result<Option<crate::Key>> {
+ let prefix: Vec<_> = self
+ .buf()
+ .iter()
+ .copied()
+ .take_while(|&c| match c {
+ 0 => true,
+ 1..=26 => !self.should_parse_ctrl(),
+ 27 => {
+ !self.should_parse_meta()
+ && !self.should_parse_special_keys()
+ }
+ 28..=31 => true,
+ 32..=126 => true,
+ 127 => !self.should_parse_special_keys(),
+ 128..=255 => true,
+ })
+ .collect();
+ if !prefix.is_empty() {
+ self.consume(prefix.len());
+ return Ok(Some(crate::Key::Bytes(prefix)));
+ }
+
+ Ok(None)
+ }
+
+ fn normalize_to_bytes(&self, key: crate::Key) -> crate::Key {
+ if let crate::Key::Byte(c) = key {
+ crate::Key::Bytes(vec![c])
+ } else {
+ key
+ }
+ }
+
+ fn read_single_key(&mut self) -> crate::Result<Option<crate::Key>> {
+ match self.getc() {
+ Some(0) => Ok(Some(crate::Key::Byte(0))),
+ Some(c @ 1..=26) => {
+ if self.should_parse_ctrl() {
+ Ok(Some(crate::Key::Ctrl(b'a' + c - 1)))
+ } else {
+ Ok(Some(crate::Key::Byte(c)))
+ }
+ }
+ Some(27) => {
+ if self.should_parse_meta()
+ || self.should_parse_special_keys()
+ {
+ self.read_escape_sequence()
+ } else {
+ Ok(Some(crate::Key::Byte(27)))
+ }
+ }
+ Some(c @ 28..=31) => Ok(Some(crate::Key::Byte(c))),
+ Some(c @ 32..=126) => {
+ if self.should_parse_utf8() {
+ Ok(Some(crate::Key::Char(c as char)))
+ } else {
+ Ok(Some(crate::Key::Byte(c)))
+ }
+ }
+ Some(127) => {
+ if self.should_parse_special_keys() {
+ Ok(Some(crate::Key::Backspace))
+ } else {
+ Ok(Some(crate::Key::Byte(127)))
+ }
+ }
+ Some(c @ 128..=255) => {
+ if self.should_parse_utf8() {
+ self.read_utf8_char(c)
+ } else {
+ Ok(Some(crate::Key::Byte(c)))
+ }
+ }
+ None => Ok(None),
+ }
+ }
+
+ fn read_escape_sequence(&mut self) -> crate::Result<Option<crate::Key>> {
+ let mut seen = vec![b'\x1b'];
+
+ macro_rules! fail {
+ () => {{
+ for &c in seen.iter().skip(1).rev() {
+ self.ungetc(c);
+ }
+ if self.should_parse_special_keys() {
+ return Ok(Some(crate::Key::Escape));
+ } else {
+ return Ok(Some(crate::Key::Byte(27)));
+ }
+ }};
+ }
+ macro_rules! next_byte {
+ () => {
+ match self.getc() {
+ Some(c) => c,
+ None => {
+ fail!()
+ }
+ }
+ };
+ }
+
+ enum EscapeState {
+ Escape,
+ CSI(Vec<u8>),
+ CKM,
+ }
+
+ let mut state = EscapeState::Escape;
+ loop {
+ let c = next_byte!();
+ seen.push(c);
+ match state {
+ EscapeState::Escape => match c {
+ b'[' => {
+ if self.should_parse_special_keys() {
+ state = EscapeState::CSI(vec![]);
+ } else {
+ fail!()
+ }
+ }
+ b'O' => {
+ if self.should_parse_special_keys() {
+ state = EscapeState::CKM;
+ } else {
+ fail!()
+ }
+ }
+ b' '..=b'N' | b'P'..=b'Z' | b'\\'..=b'~' => {
+ if self.should_parse_meta() {
+ return Ok(Some(crate::Key::Meta(c)));
+ } else {
+ fail!()
+ }
+ }
+ _ => fail!(),
+ },
+ EscapeState::CSI(ref mut param) => match c {
+ b'A' => return Ok(Some(crate::Key::Up)),
+ b'B' => return Ok(Some(crate::Key::Down)),
+ b'C' => return Ok(Some(crate::Key::Right)),
+ b'D' => return Ok(Some(crate::Key::Left)),
+ b'H' => return Ok(Some(crate::Key::Home)),
+ b'F' => return Ok(Some(crate::Key::End)),
+ b'0'..=b'9' => param.push(c),
+ b'~' => match param.as_slice() {
+ [b'2'] => return Ok(Some(crate::Key::Insert)),
+ [b'3'] => return Ok(Some(crate::Key::Delete)),
+ [b'5'] => return Ok(Some(crate::Key::PageUp)),
+ [b'6'] => return Ok(Some(crate::Key::PageDown)),
+ [b'1', b'5'] => return Ok(Some(crate::Key::F(5))),
+ [b'1', b'7'] => return Ok(Some(crate::Key::F(6))),
+ [b'1', b'8'] => return Ok(Some(crate::Key::F(7))),
+ [b'1', b'9'] => return Ok(Some(crate::Key::F(8))),
+ [b'2', b'0'] => return Ok(Some(crate::Key::F(9))),
+ [b'2', b'1'] => return Ok(Some(crate::Key::F(10))),
+ [b'2', b'3'] => return Ok(Some(crate::Key::F(11))),
+ [b'2', b'4'] => return Ok(Some(crate::Key::F(12))),
+ [b'2', b'5'] => return Ok(Some(crate::Key::F(13))),
+ [b'2', b'6'] => return Ok(Some(crate::Key::F(14))),
+ [b'2', b'8'] => return Ok(Some(crate::Key::F(15))),
+ [b'2', b'9'] => return Ok(Some(crate::Key::F(16))),
+ [b'3', b'1'] => return Ok(Some(crate::Key::F(17))),
+ [b'3', b'2'] => return Ok(Some(crate::Key::F(18))),
+ [b'3', b'3'] => return Ok(Some(crate::Key::F(19))),
+ [b'3', b'4'] => return Ok(Some(crate::Key::F(20))),
+ _ => fail!(),
+ },
+ _ => fail!(),
+ },
+ EscapeState::CKM => match c {
+ b'A' => return Ok(Some(crate::Key::KeypadUp)),
+ b'B' => return Ok(Some(crate::Key::KeypadDown)),
+ b'C' => return Ok(Some(crate::Key::KeypadRight)),
+ b'D' => return Ok(Some(crate::Key::KeypadLeft)),
+ b'P' => return Ok(Some(crate::Key::F(1))),
+ b'Q' => return Ok(Some(crate::Key::F(2))),
+ b'R' => return Ok(Some(crate::Key::F(3))),
+ b'S' => return Ok(Some(crate::Key::F(4))),
+ _ => fail!(),
+ },
+ }
+ }
+ }
+
+ fn read_utf8_char(
+ &mut self,
+ initial: u8,
+ ) -> crate::Result<Option<crate::Key>> {
+ let mut buf = vec![initial];
+
+ macro_rules! fail {
+ () => {{
+ for &c in buf.iter().skip(1).rev() {
+ self.ungetc(c);
+ }
+ return Ok(Some(crate::Key::Byte(initial)));
+ }};
+ }
+ macro_rules! next_byte {
+ () => {
+ match self.getc() {
+ Some(c) => {
+ if (0b1000_0000..=0b1011_1111).contains(&c) {
+ c
+ } else {
+ fail!()
+ }
+ }
+ None => return Ok(None),
+ }
+ };
+ }
+
+ match initial {
+ 0b0000_0000..=0b0111_1111 => {}
+ 0b1100_0000..=0b1101_1111 => {
+ buf.push(next_byte!());
+ }
+ 0b1110_0000..=0b1110_1111 => {
+ buf.push(next_byte!());
+ buf.push(next_byte!());
+ }
+ 0b1111_0000..=0b1111_0111 => {
+ buf.push(next_byte!());
+ buf.push(next_byte!());
+ buf.push(next_byte!());
+ }
+ _ => fail!(),
+ }
+
+ match std::string::String::from_utf8(buf) {
+ // unwrap is fine because buf always contains at least the
+ // initial character, and we have already done the parsing to
+ // ensure that it contains a valid utf8 character before
+ // getting here
+ Ok(s) => Ok(Some(crate::Key::Char(s.chars().next().unwrap()))),
+ Err(e) => {
+ buf = e.into_bytes();
+ fail!()
+ }
+ }
+ }
+
+ fn getc(&mut self) -> Option<u8> {
+ if self.buf_is_empty() {
+ return None;
+ }
+ let c = self.buf()[0];
+ self.consume(1);
+ Some(c)
+ }
+
+ fn ungetc(&mut self, c: u8) {
+ if self.buf_at_beginning() {
+ self.buf_mut_vec().insert(0, c);
+ } else {
+ self.unconsume(1);
+ self.buf_mut()[0] = c;
+ }
+ }
+
+ fn find_truncated_utf8(&self) -> usize {
+ for i in 0..4 {
+ match self.buf()[self.buf().len() - 1 - i] {
+ 0b0000_0000..=0b0111_1111 => return 0,
+ 0b1100_0000..=0b1101_1111 => {
+ return 1usize.saturating_sub(i);
+ }
+ 0b1110_0000..=0b1110_1111 => {
+ return 2usize.saturating_sub(i);
+ }
+ 0b1111_0000..=0b1111_0111 => {
+ return 3usize.saturating_sub(i);
+ }
+ 0b1000_0000..=0b1011_1111 => {}
+ _ => return 0,
+ }
+ }
+ 0
+ }
+}