///|
using @ports {trait ConsolePort}
///|
pub struct KeyPress {
console_key_info : @console.ConsoleKeyInfo
pasted_text : String?
}
///|
pub fn KeyPress::from_console_key_info(
console_key_info : @console.ConsoleKeyInfo,
) -> KeyPress {
let normalized = normalize_enter(console_key_info)
{ console_key_info: normalized, pasted_text: None }
}
///|
pub fn KeyPress::from_pasted_text(pasted_text : String) -> KeyPress {
let paste_key = @console.ConsoleKeyInfo::new(
'\u0000',
@console.Insert,
true,
false,
false,
)
{ console_key_info: paste_key, pasted_text: Some(pasted_text) }
}
///|
fn read_key_presses(console : &ConsolePort) -> Array[KeyPress] {
let first = console.read_key(true)
if !console.key_available() {
return [KeyPress::from_console_key_info(first)]
}
// If first key is ESC, this is likely an Alt+Key combination
// (terminal sends ESC prefix for Alt-modified keys), not paste
if first.key_char == '\u001b' {
return [KeyPress::from_console_key_info(first)]
}
let keys : Array[@console.ConsoleKeyInfo] = [first]
while console.key_available() {
keys.push(console.read_key(true))
}
if keys.length() < 4 || all_control_chars(keys) {
return keys_to_key_presses(keys)
}
let pasted = StringBuilder::new()
for key in keys {
pasted.write_string(key.key_char.to_string())
}
[KeyPress::from_pasted_text(pasted.to_string())]
}
///|
fn normalize_enter(
key_info : @console.ConsoleKeyInfo,
) -> @console.ConsoleKeyInfo {
if key_info.key is @console.Enter && key_info.key_char == '\r' {
@console.ConsoleKeyInfo::new(
'\n',
@console.Enter,
key_info.shift,
key_info.alt,
key_info.control,
)
} else {
key_info
}
}
///|
fn keys_to_key_presses(
keys : Array[@console.ConsoleKeyInfo],
) -> Array[KeyPress] {
keys.map(KeyPress::from_console_key_info)
}
///|
fn all_control_chars(keys : Array[@console.ConsoleKeyInfo]) -> Bool {
keys.iter().all(key => key.key_char.is_ascii_control())
}