use std::ffi::CStr;
use std::io::Read;
use std::io::Result as IoResult;
use std::io::stdin;
use std::io::stdout;
use std::io::Write;
use std::str::Utf8Error;
use termion::clear;
use termion::cursor;
use termion::raw::IntoRawMode;
use unicode_segmentation::UnicodeSegmentation;
use rline::Readline;
const EOT: u8 = 0x04;
fn grapheme_index(s: &CStr, pos: usize) -> Result<usize, Utf8Error> {
let s = s.to_str()?;
let extended = true;
let mut count = 0;
for (idx, grapheme) in s.grapheme_indices(extended) {
if pos < idx + grapheme.len() {
break
}
count += 1;
}
Ok(count)
}
fn process_input<R, W>(mut r: R, mut w: W, rl: &mut Readline, line: &mut u16) -> IoResult<bool>
where
R: Read,
W: Write,
{
let mut buffer = [0u8; 16];
let n = r.read(&mut buffer)?;
if n >= 1 && buffer[0] == EOT {
return Ok(true)
}
write!(w, "{}{}", clear::CurrentLine, cursor::Goto(1, *line))?;
if let Some(text) = rl.feed(&buffer[..n]) {
if text.as_bytes() == b"quit" {
return Ok(true)
}
*line += 1;
w.write_all(text.as_bytes())?;
write!(w, "{}", cursor::Goto(1, *line))?
} else {
rl.peek(|text, cursor| {
w.write_all(text.to_bytes())?;
let cursor = grapheme_index(text, cursor).unwrap();
write!(w, "{}", cursor::Goto(cursor as u16 + 1, *line))
})?
};
w.flush()?;
Ok(false)
}
fn main() -> IoResult<()> {
let mut w = stdout().into_raw_mode()?;
write!(w, "{}{}", clear::All, cursor::Goto(1, 1))?;
write!(w, "> Your system's readline configuration is in effect.\n\r")?;
write!(w, "> Please enter some text. Use Ctrl-D (EOT) or type \"quit\" to exit.\n\r")?;
write!(w, "\n\r")?;
let mut line = 4;
write!(w, "{}", cursor::Goto(1, line))?;
w.flush()?;
let mut rl = Readline::new();
loop {
if process_input(stdin(), &mut w, &mut rl, &mut line)? {
write!(w, "> Bye.\n\r")?;
break Ok(())
}
}
}