<commit_before>/// Input will listens to user input, modify the query string, send special
/// keystrokes(such as Enter, Ctrl-p, Ctrl-n, etc) to the controller.

use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use std::io::prelude::*;
use std::fs::File;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::time::Duration;
use utf8parse;

use event::{parse_action, Event, EventSender};

pub struct Input {
    tx_input: EventSender,
    keyboard: KeyBoard,
    keymap: HashMap<Key, (Event, Option<String>)>,
}

impl Input {
    pub fn new(tx_input: EventSender) -> Self {
        let f = File::open("/dev/tty").expect("failed on open /dev/tty");
        let keyboard = KeyBoard::new(f);
        Input {
            tx_input: tx_input,
            keyboard: keyboard,
            keymap: get_default_key_map(),
        }
    }

    pub fn run(&mut self) {
        loop {
            match self.keyboard.get_key() {
                Some(Key::Pos(row, col)) => {
                    let _ = self.tx_input
                        .send((Event::EvReportCursorPos, Box::new((row, col))));
                }
                Some(key) => {
                    // search event from keymap
                    match self.keymap.get(&key) {
                        Some(&(ev, Some(ref args))) => {
                            let _ = self.tx_input.send((ev, Box::new(Some(args.clone()))));
                        }
                        Some(&(ev, None)) => {
                            let _ = self.tx_input.send((ev, Box::new(None as Option<String>)));
                        }
                        None => {
                            if let Key::Char(ch) = key {
                                let _ = self.tx_input.send((Event::EvActAddChar, Box::new(ch)));
                            } else {
                                let _ = self.tx_input.send((Event::EvInputKey, Box::new(key)));
                            }
                        }
                    }
                }
                None => {
                    let _ = self.tx_input.send((Event::EvInputInvalid, Box::new(true)));
                }
            }
        }
    }

    pub fn bind(&mut self, key: &str, action: &str, args: Option<String>) {
        let key = parse_key(key);
        let action = parse_action(action);
        if key == None || action == None {
            return;
        }

        let key = key.unwrap();
        let act = action.unwrap();

        // remove the key for existing keymap;
        let _ = self.keymap.remove(&key);
        self.keymap.entry(key).or_insert((act, args));
    }

    pub fn parse_keymaps(&mut self, maps: &[&str]) {
        for &map in maps {
            self.parse_keymap(map);
        }
    }

    // key_action is comma separated: 'ctrl-j:accept,ctrl-k:kill-line'
    pub fn parse_keymap(&mut self, key_action: &str) {
        for pair in key_action.split(',') {
            let vec: Vec<&str> = pair.split(':').collect();
            if vec.len() < 2 {
                continue;
            }
            self.bind(vec[0], vec[1], vec.get(2).map(|&string| string.to_string()));
        }
    }

    pub fn parse_expect_keys(&mut self, keys: Option<&str>) {
        if let Some(keys) = keys {
            self.bind("enter", "accept", Some("".to_string()));
            for key in keys.split(',') {
                self.bind(key, "accept", Some(key.to_string()));
            }
        }
    }
}

// check https://github.com/rust-lang/rust/issues/27802#issuecomment-270555935
struct SimpleUtf8Receiver {
    tx: Sender<char>,
}

impl SimpleUtf8Receiver {
    pub fn new(tx: Sender<char>) -> Self {
        SimpleUtf8Receiver { tx: tx }
    }
}

impl utf8parse::Receiver for SimpleUtf8Receiver {
    fn codepoint(&mut self, ch: char) {
        let _ = self.tx.send(ch);
    }

    fn invalid_sequence(&mut self) {
        // ignore it
    }
}

struct KeyBoard {
    rx: Receiver<char>,
    buf: VecDeque<char>,
}

impl KeyBoard {
    pub fn new(f: File) -> Self {
        let (tx, rx) = channel();
        thread::spawn(move || {
            let mut utf8_receiver = SimpleUtf8Receiver::new(tx);
            let mut utf8_parser = utf8parse::Parser::new();
            for byte in f.bytes() {
                utf8_parser.advance(
                    &mut utf8_receiver,
                    byte.expect("fail to parse keyboard input"),
                );
            }
        });

        KeyBoard {
            rx: rx,
            buf: VecDeque::new(),
        }
    }
<commit_msg>[input] kill the input thread on exit<commit_after>/// Input will listens to user input, modify the query string, send special
/// keystrokes(such as Enter, Ctrl-p, Ctrl-n, etc) to the controller.

use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use std::os::unix::thread::{RawPthread, JoinHandleExt};
use std::io::prelude::*;
use std::fs::File;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::time::Duration;
use utf8parse;
use libc::pthread_cancel;
use event::{parse_action, Event, EventSender};

pub struct Input {
    tx_input: EventSender,
    keyboard: KeyBoard,
    keymap: HashMap<Key, (Event, Option<String>)>,
}

impl Input {
    pub fn new(tx_input: EventSender) -> Self {
        let f = File::open("/dev/tty").expect("failed on open /dev/tty");
        let keyboard = KeyBoard::new(f);
        Input {
            tx_input: tx_input,
            keyboard: keyboard,
            keymap: get_default_key_map(),
        }
    }

    pub fn run(&mut self) {
        loop {
            match self.keyboard.get_key() {
                Some(Key::Pos(row, col)) => {
                    let _ = self.tx_input
                        .send((Event::EvReportCursorPos, Box::new((row, col))));
                }
                Some(key) => {
                    // search event from keymap
                    match self.keymap.get(&key) {
                        Some(&(ev @ Event::EvActAccept, None))
                            | Some(&(ev @ Event::EvActAbort, None)) => {
                            let _ = self.tx_input.send((ev, Box::new(None as Option<String>)));
                            break;
                        }
                        Some(&(ev, Some(ref args))) => {
                            let _ = self.tx_input.send((ev, Box::new(Some(args.clone()))));
                        }
                        Some(&(ev, None)) => {
                            let _ = self.tx_input.send((ev, Box::new(None as Option<String>)));
                        }
                        None => {
                            if let Key::Char(ch) = key {
                                let _ = self.tx_input.send((Event::EvActAddChar, Box::new(ch)));
                            } else {
                                let _ = self.tx_input.send((Event::EvInputKey, Box::new(key)));
                            }
                        }
                    }
                }
                None => {
                    let _ = self.tx_input.send((Event::EvInputInvalid, Box::new(true)));
                }
            }
        }
    }

    pub fn bind(&mut self, key: &str, action: &str, args: Option<String>) {
        let key = parse_key(key);
        let action = parse_action(action);
        if key == None || action == None {
            return;
        }

        let key = key.unwrap();
        let act = action.unwrap();

        // remove the key for existing keymap;
        let _ = self.keymap.remove(&key);
        self.keymap.entry(key).or_insert((act, args));
    }

    pub fn parse_keymaps(&mut self, maps: &[&str]) {
        for &map in maps {
            self.parse_keymap(map);
        }
    }

    // key_action is comma separated: 'ctrl-j:accept,ctrl-k:kill-line'
    pub fn parse_keymap(&mut self, key_action: &str) {
        for pair in key_action.split(',') {
            let vec: Vec<&str> = pair.split(':').collect();
            if vec.len() < 2 {
                continue;
            }
            self.bind(vec[0], vec[1], vec.get(2).map(|&string| string.to_string()));
        }
    }

    pub fn parse_expect_keys(&mut self, keys: Option<&str>) {
        if let Some(keys) = keys {
            self.bind("enter", "accept", Some("".to_string()));
            for key in keys.split(',') {
                self.bind(key, "accept", Some(key.to_string()));
            }
        }
    }
}

// check https://github.com/rust-lang/rust/issues/27802#issuecomment-270555935
struct SimpleUtf8Receiver {
    tx: Sender<char>,
}

impl SimpleUtf8Receiver {
    pub fn new(tx: Sender<char>) -> Self {
        SimpleUtf8Receiver { tx: tx }
    }
}

impl utf8parse::Receiver for SimpleUtf8Receiver {
    fn codepoint(&mut self, ch: char) {
        let _ = self.tx.send(ch);
    }

    fn invalid_sequence(&mut self) {
        // ignore it
    }
}

struct KeyBoard {
    handle: RawPthread,
    rx: Receiver<char>,
    buf: VecDeque<char>,
}

impl Drop for KeyBoard {
    fn drop(&mut self) {
        // Don't try this at home!
        unsafe {pthread_cancel(self.handle);}
    }
}

impl KeyBoard {
    pub fn new(f: File) -> Self {
        let (tx, rx) = channel();
        let handle = thread::spawn(move || {
            let mut utf8_receiver = SimpleUtf8Receiver::new(tx);
            let mut utf8_parser = utf8parse::Parser::new();
            for byte in f.bytes() {
                utf8_parser.advance(
                    &mut utf8_receiver,
                    byte.expect("fail to parse keyboard input"),
                );
            }
        });

        KeyBoard {
            handle: handle.into_pthread_t(),
            rx,
            buf: VecDeque::new(),
        }
    }

{'fraction_non_alphanumeric': 0.1393627954779034, 'fraction_numerical': 0.006166495375128468, 'mean_word_length': 1.568223805753497, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 43, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4172982', 'n_tokens_mistral': 2972, 'n_tokens_neox': 2881, 'n_words': 640}