extern crate linefeed;
extern crate rand;
use std::io;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use rand::{Rng, thread_rng};
use linefeed::{Interface, Prompter, ReadResult};
use linefeed::chars::escape_sequence;
use linefeed::command::COMMANDS;
use linefeed::complete::{Completer, Completion};
use linefeed::inputrc::parse_text;
use linefeed::terminal::Terminal;
const HISTORY_FILE: &str = "linefeed.hst";
fn main() -> io::Result<()> {
let interface = Arc::new(Interface::new("demo")?);
let mut thread_id = 0;
println!("This is the linefeed demo program.");
println!("Enter \"help\" for a list of commands.");
println!("Press Ctrl-D or enter \"quit\" to exit.");
println!("");
interface.set_completer(Arc::new(DemoCompleter));
interface.set_prompt("demo> ")?;
if let Err(e) = interface.load_history(HISTORY_FILE) {
if e.kind() == io::ErrorKind::NotFound {
println!("History file {} doesn't exist, not loading history.", HISTORY_FILE);
} else {
eprintln!("Could not load history file {}: {}", HISTORY_FILE, e);
}
}
while let ReadResult::Input(line) = interface.read_line()? {
if !line.trim().is_empty() {
interface.add_history_unique(line.clone());
}
let (cmd, args) = split_first_word(&line);
match cmd {
"help" => {
println!("linefeed demo commands:");
println!();
for &(cmd, help) in DEMO_COMMANDS {
println!(" {:15} - {}", cmd, help);
}
println!();
}
"bind" => {
let d = parse_text("", args);
interface.evaluate_directives(d);
}
"get" => {
if let Some(var) = interface.get_variable(args) {
println!("{} = {}", args, var);
} else {
println!("no variable named `{}`", args);
}
}
"list-bindings" => {
for (seq, cmd) in interface.lock_reader().bindings() {
let seq = format!("\"{}\"", escape_sequence(seq));
println!("{:20}: {}", seq, cmd);
}
}
"list-commands" => {
for cmd in COMMANDS {
println!("{}", cmd);
}
}
"list-variables" => {
for (name, var) in interface.lock_reader().variables() {
println!("{:30} = {}", name, var);
}
}
"spawn-log-thread" => {
let my_thread_id = thread_id;
println!("Spawning log thread #{}", my_thread_id);
let iface = interface.clone();
thread::spawn(move || {
let mut rng = thread_rng();
let mut i = 0usize;
loop {
writeln!(iface, "[#{}] Concurrent message #{}",
my_thread_id, i).unwrap();
let wait_ms = rng.gen_range(1, 500);
thread::sleep(Duration::from_millis(wait_ms));
i += 1;
}
});
thread_id += 1;
}
"history" => {
let w = interface.lock_writer_erase()?;
for (i, entry) in w.history().enumerate() {
println!("{}: {}", i, entry);
}
}
"save-history" => {
if let Err(e) = interface.save_history(HISTORY_FILE) {
eprintln!("Could not save history file {}: {}", HISTORY_FILE, e);
} else {
println!("History saved to {}", HISTORY_FILE);
}
}
"quit" => break,
"set" => {
let d = parse_text("", &line);
interface.evaluate_directives(d);
}
_ => println!("read input: {:?}", line)
}
}
println!("Goodbye.");
Ok(())
}
fn split_first_word(s: &str) -> (&str, &str) {
let s = s.trim();
match s.find(|ch: char| ch.is_whitespace()) {
Some(pos) => (&s[..pos], s[pos..].trim_start()),
None => (s, "")
}
}
static DEMO_COMMANDS: &[(&str, &str)] = &[
("bind", "Set bindings in inputrc format"),
("get", "Print the value of a variable"),
("help", "You're looking at it"),
("list-bindings", "List bound sequences"),
("list-commands", "List command names"),
("list-variables", "List variables"),
("spawn-log-thread", "Spawns a thread that concurrently logs messages"),
("history", "Print history"),
("save-history", "Write history to file"),
("quit", "Quit the demo"),
("set", "Assign a value to a variable"),
];
struct DemoCompleter;
impl Completer for DemoCompleter {
fn complete(&self, word: &str, prompter: &Prompter,
start: usize, _end: usize) -> Option> {
let line = prompter.buffer();
let mut words = line[..start].split_whitespace();
match words.next() {
// Complete command name
None => {
let mut compls = Vec::new();
for &(cmd, _) in DEMO_COMMANDS {
if cmd.starts_with(word) {
compls.push(Completion::simple(cmd.to_owned()));
}
}
Some(compls)
}
// Complete command parameters
Some("get") | Some("set") => {
if words.count() == 0 {
let mut res = Vec::new();
for (name, _) in prompter.variables() {
if name.starts_with(word) {
res.push(Completion::simple(name.to_owned()));
}
}
Some(res)
} else {
None
}
}
_ => None
}
}
}