1#![feature(test)]
2#[macro_use]
3extern crate clap;
4extern crate terminal_size;
5extern crate test;
6
7pub mod bonus;
8pub mod choices;
9pub mod choice;
10pub mod color;
11pub mod config;
12pub mod cursor;
13pub mod matcher;
14pub mod matrix;
15pub mod scorer;
16pub mod search;
17pub mod terminal;
18
19use choice::Choice;
20use choices::Choices;
21use config::Config;
22use search::Search;
23use std::error::Error;
24use std::io::{self, Read};
25use terminal::Terminal;
26use termion::event::Key;
27use rayon::prelude::*;
28
29pub struct App {}
30
31impl App {
32 pub fn run() -> Result<i32, Box<dyn Error>> {
33 let config = Config::new();
34 let mut exit_code = 0;
35
36 let mut terminal = Terminal::new()?;
37 let stdin = io::stdin();
38 let mut stdin_lock = stdin.lock();
39
40 let mut buffer = String::new();
41
42 if termion::is_tty(&stdin_lock) {
43 clap::Error::with_description(
44 "No input given", clap::ErrorKind::MissingRequiredArgument
45 ).exit();
46 }
47
48 stdin_lock.read_to_string(&mut buffer)?;
49 let parsed_choices: Vec<Choice> =
50 buffer.
51 par_lines().
52 map(|choice| Choice::new(choice, &config)).
53 collect();
54
55 let mut search = Search::new(config.prompt);
56 let mut choices = Choices::new(config.lines, &parsed_choices, config.show_scores);
57
58 if config.benchmark {
59 let query = config.query.unwrap().chars().collect::<Vec<char>>();
60 for _ in 0..100 {
61 choices.filter(&query);
62 }
63 } else {
64 choices.initial_draw(&mut terminal);
65 match config.query {
66 Some(query) => {
67 terminal.print(&search.set_query(&query));
68 terminal.print(&choices.filter(&search.query));
69 }
70 None => terminal.print(&search.draw())
71 }
72
73 for c in terminal.keys()? {
74 match c.unwrap() {
75 Key::Alt(c) => match c as u8 {
76 b'b' => {
77 terminal.print(&search.left_word());
78 terminal.print(&choices.filter(&search.query));
79 },
80 b'f' => {
81 terminal.print(&search.right_word());
82 terminal.print(&choices.filter(&search.query));
83 },
84 127 => {
85 terminal.print(&search.backspace_word());
86 terminal.print(&choices.filter(&search.query));
87 },
88 100 => {
89 terminal.print(&search.delete_word());
90 terminal.print(&choices.filter(&search.query));
91 },
92 _ => {}
93 },
94 Key::Char('\n') => {
95 choices.select(&mut terminal);
96 break;
97 }
98 Key::Char('\t') => {
99 terminal.print(&search.set_query(choices.current_match().searchable));
100 terminal.print(&choices.filter(&search.query));
101 }
102 Key::Char(c) => {
103 terminal.print(&search.keypress(c));
104 terminal.print(&choices.filter(&search.query));
105 }
106 Key::Ctrl('u') => terminal.print(&search.clear()),
107 Key::Up => terminal.print(&choices.previous()),
108 Key::Down => terminal.print(&choices.next()),
109 Key::Esc | Key::Ctrl('c') => {
110 exit_code = 1;
111 terminal.print(&choices.cancel());
112 break;
113 }
114 Key::Left => {
115 if let Some(text) = search.left() {
116 terminal.print(text);
117 }
118 }
119 Key::Right => {
120 if let Some(text) = search.right() {
121 terminal.print(text);
122 }
123 }
124 Key::Backspace => {
125 if let Some(text) = search.backspace() {
126 terminal.print(&text);
127 terminal.print(&choices.filter(&search.query));
128 }
129 }
130 Key::Ctrl('d') => {
131 if let Some(text) = search.delete() {
132 terminal.print(&text);
133 terminal.print(&choices.filter(&search.query));
134 }
135 }
136 _ => {}
137 }
138 }
139 }
140
141 Ok(exit_code)
142 }
143}