Fixes issue 3264 by adding a fallback read mode when reading from non-tty stdin on Windows

This commit is contained in:
Alexander McLin
2026-03-29 21:29:48 -04:00
parent 43b6afcdf3
commit 533594b6b6
4 changed files with 92 additions and 25 deletions

View File

@@ -68,9 +68,7 @@ use cpu_time::ProcessTime;
use std::time::{Duration, SystemTime};
#[cfg(feature = "repl")]
use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
#[cfg(feature = "repl")]
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use crate::read::user_interaction::{get_key, KeyCode, KeyModifiers};
use blake2::{Blake2b512, Blake2s256};
@@ -147,28 +145,6 @@ impl ModuleQuantification {
}
}
#[cfg(feature = "repl")]
pub(crate) fn get_key() -> KeyEvent {
let key;
enable_raw_mode().expect("failed to enable raw mode");
loop {
let key_ = read();
if let Ok(Event::Key(key_)) = key_ {
if key_.kind != KeyEventKind::Release {
match key_.code {
KeyCode::Char(_) | KeyCode::Enter | KeyCode::Tab => {
key = key_;
break;
}
_ => (),
}
}
}
}
disable_raw_mode().expect("failed to disable raw mode");
key
}
fn pstr_segment_char_count_and_tail(heap: &Heap, pstr_loc: usize) -> (usize, usize) {
let char_iter = heap.char_iter(pstr_loc);

View File

@@ -1,3 +1,8 @@
#[cfg(feature = "repl")]
pub mod fallback_mode;
#[cfg(feature = "repl")]
pub mod user_interaction;
use crate::parser::ast::*;
use crate::parser::lexer::Lexer;
use crate::parser::parser::*;

37
src/read/fallback_mode.rs Normal file
View File

@@ -0,0 +1,37 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers};
use std::io::{Read, Error, ErrorKind};
// Provide graceful degradation limited support mode if reading from stdin that does not
// support terminal features.
//
// Retrieve a single byte from stdin, does not support full Unicode decoding; only convert
// byte to KeyEvent char if it falls within the ASCII subset of UTF-8.
//
// Does not support passing through Ctrl-C as a KeyEvent.
pub(crate) fn limited_support_read() -> std::io::Result<KeyEvent> {
let byte_or_none = std::io::stdin().bytes().next();
match byte_or_none {
Some(byte) => match byte {
Ok(b) => {
if b.is_ascii() {
Ok(map_ascii_to_keyevent(b as char))
}
else {
Err(Error::new(ErrorKind::Unsupported, "not supported input"))
}
}
Err(e) => Err(e),
},
None => Err(Error::new(ErrorKind::UnexpectedEof, "EOF")),
}
}
fn map_ascii_to_keyevent(c: char) -> KeyEvent {
KeyEvent {
code: KeyCode::Char(c),
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::empty(),
}
}

View File

@@ -0,0 +1,49 @@
use crossterm::event::{read, Event, KeyEventKind};
pub(crate) use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use crossterm::tty::IsTty;
use crate::read::fallback_mode;
pub(crate) fn get_key() -> KeyEvent {
let key;
if supported_terminal() {
enable_raw_mode().expect("failed to enable raw mode");
loop {
let key_ = read();
if let Ok(Event::Key(key_)) = key_ {
if key_.kind != KeyEventKind::Release {
match key_.code {
KeyCode::Char(_) | KeyCode::Enter | KeyCode::Tab => {
key = key_;
break;
}
_ => (),
}
}
}
}
disable_raw_mode().expect("failed to disable raw mode");
} else {
// stdin is not supported terminal type, fallback to limited support mode.
loop {
let key_ = fallback_mode::limited_support_read();
if let Ok(key_) = key_ {
key = key_;
break;
}
}
}
key
}
fn supported_terminal() -> bool {
#[cfg(windows)]
let windows_os = true;
#[cfg(not(windows))]
let windows_os = false;
// If OS is Windows and stdin is not a tty, it's either a pipe or an unsupported terminal configuration.
!windows_os || std::io::stdin().is_tty()
}