Finish the basic interaction

1. toggle selection of an entry
2. scroll up/down
3. output the selected item
This commit is contained in:
zhang_ji 2016-06-15 16:03:51 +08:00
parent 72252243d1
commit 6ec6cfb978

View file

@ -6,24 +6,18 @@ mod item;
use std::io::{stdin, Read, BufRead, BufReader}; use std::io::{stdin, Read, BufRead, BufReader};
use std::error::Error; use std::error::Error;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, RwLock};
use std::thread; use std::thread;
use std::process::{Command, Stdio, exit}; use std::process::{Command, Stdio};
use std::char; use std::char;
use std::mem; use std::cmp;
use std::sync::mpsc::{Sender, Receiver, channel}; use std::sync::mpsc::{Sender, channel};
use util::eventbox::EventBox; use util::eventbox::EventBox;
use ncurses::*; use ncurses::*;
use item::{Item, MatchedItem}; use item::{Item, MatchedItem};
//==============================================================================
struct FZF {
query: String,
}
//============================================================================== //==============================================================================
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
enum Event{ enum Event{
@ -32,7 +26,6 @@ enum Event{
EvMatcherNewItem, EvMatcherNewItem,
EvMatcherResetQuery, EvMatcherResetQuery,
EvMatcherUpdateProcess, EvMatcherUpdateProcess,
EvMatcherFinished,
EvQueryChange, EvQueryChange,
EvInputToggle, EvInputToggle,
EvInputUp, EvInputUp,
@ -88,7 +81,7 @@ impl Matcher {
//self.tx_output.send(string.clone()); //self.tx_output.send(string.clone());
if self.match_str(&item.text) { if self.match_str(&item.text) {
self.num_matched += 1; self.num_matched += 1;
self.tx_output.send(MatchedItem::new(self.item_pos)); let _ = self.tx_output.send(MatchedItem::new(self.item_pos));
} }
@ -175,8 +168,6 @@ impl Input {
fn handle_char(&mut self) { fn handle_char(&mut self) {
let ch = wget_wch(stdscr); let ch = wget_wch(stdscr);
let orig_query = self.query.clone();
match ch { match ch {
Some(WchResult::KeyCode(_)) => { Some(WchResult::KeyCode(_)) => {
// will later handle readline-like shortcuts // will later handle readline-like shortcuts
@ -195,6 +186,18 @@ impl Input {
self.eb.set(Event::EvInputSelect, Box::new(true)); self.eb.set(Event::EvInputSelect, Box::new(true));
} }
'\x09' => { // tab
self.eb.set(Event::EvInputToggle, Box::new(true));
}
'\x10' => { // ctrl-p
self.eb.set(Event::EvInputUp, Box::new(true));
}
'\x0E' => { // ctrl-n
self.eb.set(Event::EvInputDown, Box::new(true));
}
ch => { // other characters ch => { // other characters
self.add_char(ch); self.add_char(ch);
self.eb.set(Event::EvQueryChange, Box::new((self.get_query(), self.pos))); self.eb.set(Event::EvQueryChange, Box::new((self.get_query(), self.pos)));
@ -210,16 +213,12 @@ impl Input {
//============================================================================== //==============================================================================
// Reader: fetch a list of lines from stdin or command output // Reader: fetch a list of lines from stdin or command output
const READER_LINES_CACHED: usize = 100;
struct Reader { struct Reader {
cmd: Option<&'static str>, // command to invoke cmd: Option<&'static str>, // command to invoke
eb: Arc<EventBox<Event>>, // eventbox eb: Arc<EventBox<Event>>, // eventbox
tx: Sender<Item>, // sender to send the string read from command output tx: Sender<Item>, // sender to send the string read from command output
} }
impl Reader { impl Reader {
pub fn new(cmd: Option<&'static str>, eb: Arc<EventBox<Event>>, tx: Sender<Item>) -> Self { pub fn new(cmd: Option<&'static str>, eb: Arc<EventBox<Event>>, tx: Sender<Item>) -> Self {
@ -261,7 +260,7 @@ impl Reader {
input.pop(); input.pop();
} }
} }
self.tx.send(Item::new(input)); let _ = self.tx.send(Item::new(input));
} }
Err(_err) => { break; } Err(_err) => { break; }
} }
@ -275,13 +274,14 @@ impl Reader {
// Model: data structure for display the result // Model: data structure for display the result
struct Model { struct Model {
query: String, query: String,
query_cursor: i32, query_cursor: i32, // > qu<query_cursor>ery
num_matched: u64, num_matched: u64,
num_total: u64, num_total: u64,
items: Arc<RwLock<Vec<Item>>>, items: Arc<RwLock<Vec<Item>>>, // all items
matched_items: Vec<MatchedItem>, matched_items: Vec<MatchedItem>,
item_start_pos: i64, item_cursor: usize, // the index of matched item currently highlighted.
line_cursor: i32, line_cursor: usize, // line No.
item_start_pos: usize, // for screen scroll.
max_y: i32, max_y: i32,
max_x: i32, max_x: i32,
} }
@ -299,13 +299,30 @@ impl Model {
num_total: 0, num_total: 0,
items: Arc::new(RwLock::new(Vec::new())), items: Arc::new(RwLock::new(Vec::new())),
matched_items: Vec::new(), matched_items: Vec::new(),
item_cursor: 0,
line_cursor: (max_y - 3) as usize,
item_start_pos: 0, item_start_pos: 0,
line_cursor: 0,
max_y: max_y, max_y: max_y,
max_x: max_x, max_x: max_x,
} }
} }
pub fn output(&self) {
let items = self.items.read().unwrap();
for item in items.iter() {
if item.selected {
println!("{}", item.text);
}
}
//println!("{:?}", items[self.matched_items[self.item_cursor].index].text);
//items[self.matched_items[self.item_cursor].index].selected = s;
}
pub fn toggle_select(&self, selected: Option<bool>) {
let mut items = self.items.write().unwrap();
items[self.matched_items[self.item_cursor].index].toggle_select(selected);
}
pub fn update_query(&mut self, query: String, cursor: i32) { pub fn update_query(&mut self, query: String, cursor: i32) {
self.query = query; self.query = query;
self.query_cursor = cursor; self.query_cursor = cursor;
@ -325,7 +342,29 @@ impl Model {
} }
pub fn move_line_cursor(&mut self, diff: i32) { pub fn move_line_cursor(&mut self, diff: i32) {
self.line_cursor += diff;
let y = self.line_cursor as i32 + diff;
let item_y = cmp::max(0, self.item_cursor as i32 - diff);
let screen_height = (self.max_y - 3) as usize;
match y {
y if y < 0 => {
self.line_cursor = 0;
self.item_cursor = cmp::min(item_y as usize, self.matched_items.len()-1);
self.item_start_pos = self.item_cursor - screen_height;
}
y if y > screen_height as i32 => {
self.line_cursor = screen_height;
self.item_cursor = cmp::max(0, item_y as usize);
self.item_start_pos = self.item_cursor;
}
y => {
self.line_cursor = y as usize;
self.item_cursor = item_y as usize;
}
}
} }
pub fn print_query(&self) { pub fn print_query(&self) {
@ -341,17 +380,35 @@ impl Model {
addstr(format!(" {}/{}", self.num_matched, self.num_total).as_str()); addstr(format!(" {}/{}", self.num_matched, self.num_total).as_str());
} }
fn print_item(&self, item: &Item) {
let shown_str: String = item.text.chars().take((self.max_x-1) as usize).collect();
if item.selected {
printw(">");
} else {
printw(" ");
}
addstr(&shown_str);
}
pub fn print_items(&self) { pub fn print_items(&self) {
let items = self.items.read().unwrap(); let items = self.items.read().unwrap();
let mut y = self.max_y - 2;
for item in self.matched_items.iter() {
mv(y, 2);
let shown_str: String = items[item.index].text.chars().take((self.max_x-1) as usize).collect(); let mut y = self.max_y - 3;
addstr(&shown_str); for matched in self.matched_items[self.item_start_pos..].into_iter() {
mv(y, 0);
let is_current_line = y == self.line_cursor as i32;
if is_current_line {
printw(">");
} else {
printw(" ");
}
self.print_item(&items[matched.index]);
y -= 1; y -= 1;
if y <= 0 { if y < 0 {
break; break;
} }
} }
@ -430,8 +487,8 @@ fn main() {
let (matched, total) : (u64, u64) = *val.downcast().unwrap(); let (matched, total) : (u64, u64) = *val.downcast().unwrap();
model.update_process_info(matched, total); model.update_process_info(matched, total);
while let Ok(matchedItem) = rx_matched.try_recv() { while let Ok(matched_item) = rx_matched.try_recv() {
model.push_item(matchedItem); model.push_item(matched_item);
} }
} }
@ -451,6 +508,17 @@ fn main() {
break 'outer; break 'outer;
} }
Event::EvInputToggle => {
model.toggle_select(None);
model.move_line_cursor(1);
}
Event::EvInputUp=> {
model.move_line_cursor(-1);
}
Event::EvInputDown=> {
model.move_line_cursor(1);
}
_ => { _ => {
printw(format!("{}\n", e as i32).as_str()); printw(format!("{}\n", e as i32).as_str());
} }
@ -461,4 +529,5 @@ fn main() {
}; };
endwin(); endwin();
model.output();
} }