fix #300: move reader related options to default reader

This commit is contained in:
Jinzhou Zhang 2020-10-05 18:05:23 +08:00
parent 44c61d64f9
commit 6876ef469d
7 changed files with 117 additions and 97 deletions

19
examples/nth.rs Normal file
View file

@ -0,0 +1,19 @@
extern crate skim;
use skim::prelude::*;
use std::io::Cursor;
pub fn main() {
let input = "foo 123";
let options = SkimOptionsBuilder::default().query(Some("f")).build().unwrap();
let item_reader = SkimItemReader::new(SkimItemReaderOption::default().nth("2").build());
let items = item_reader.of_bufread(Cursor::new(input));
let selected_items = Skim::run_with(&options, Some(items))
.map(|out| out.selected_items)
.unwrap_or_else(|| Vec::new());
for item in selected_items.iter() {
print!("{}{}", item.output(), "\n");
}
}

View file

@ -233,7 +233,7 @@ fn real_main() -> Result<i32, std::io::Error> {
}
//------------------------------------------------------------------------------
// initialize collector
let collector_option = CollectorOption::default()
let item_reader_option = SkimItemReaderOption::default()
.ansi(opts.is_present("ansi"))
.delimiter(opts.values_of("delimiter").and_then(|vals| vals.last()).unwrap_or(""))
.with_nth(opts.values_of("with-nth").and_then(|vals| vals.last()).unwrap_or(""))
@ -241,7 +241,7 @@ fn real_main() -> Result<i32, std::io::Error> {
.read0(opts.is_present("read0"))
.build();
let cmd_collector = Rc::new(RefCell::new(DefaultSkimCollector::new(collector_option)));
let cmd_collector = Rc::new(RefCell::new(SkimItemReader::new(item_reader_option)));
//------------------------------------------------------------------------------
// read in the history file
@ -274,10 +274,9 @@ fn real_main() -> Result<i32, std::io::Error> {
//------------------------------------------------------------------------------
// read from pipe or command
let stdin = std::io::stdin();
let components_to_stop = Arc::new(AtomicUsize::new(0));
let rx_item = match isatty(stdin.as_raw_fd()) {
Ok(false) | Err(nix::Error::Sys(nix::errno::Errno::EINVAL)) => {
let (rx_item, _) = cmd_collector.borrow().read_and_collect_from_command(components_to_stop, CollectorInput::Pipe(Box::new(BufReader::new(stdin))));
let rx_item = cmd_collector.borrow().of_bufread(BufReader::new(stdin));
Some(rx_item)
}
Ok(true) | Err(_) => None,

View file

@ -1,3 +1,4 @@
/// helper for turn a BufRead into a skim stream
use crate::field::FieldRange;
use crate::helper::item::DefaultSkimItem;
use crate::reader::CommandCollector;
@ -17,8 +18,13 @@ const ITEM_CHANNEL_SIZE: usize = 10240;
const DELIMITER_STR: &str = r"[\t\n ]+";
const READ_BUFFER_SIZE: usize = 1024;
#[derive(Clone, Debug)]
pub struct CollectorOption {
pub enum CollectorInput {
Pipe(Box<dyn BufRead + Send>),
Command(String),
}
pub struct SkimItemReaderOption {
buf_size: usize,
use_ansi_color: bool,
transform_fields: Vec<FieldRange>,
matching_fields: Vec<FieldRange>,
@ -26,19 +32,30 @@ pub struct CollectorOption {
line_ending: u8,
}
impl Default for CollectorOption {
impl Default for SkimItemReaderOption {
fn default() -> Self {
Self {
buf_size: READ_BUFFER_SIZE,
line_ending: b'\n',
use_ansi_color: false,
transform_fields: Vec::new(),
matching_fields: Vec::new(),
delimiter: Regex::new(DELIMITER_STR).unwrap(),
line_ending: b'\n',
}
}
}
impl CollectorOption {
impl SkimItemReaderOption {
pub fn buf_size(mut self, buf_size: usize) -> Self {
self.buf_size = buf_size;
self
}
pub fn line_ending(mut self, line_ending: u8) -> Self {
self.line_ending = line_ending;
self
}
pub fn ansi(mut self, enable: bool) -> Self {
self.use_ansi_color = enable;
self
@ -93,27 +110,85 @@ impl CollectorOption {
pub fn build(self) -> Self {
self
}
pub fn is_simple(&self) -> bool {
!self.use_ansi_color && self.matching_fields.is_empty() && self.transform_fields.is_empty()
}
}
pub enum CollectorInput {
Pipe(Box<dyn BufRead + Send>),
Command(String),
pub struct SkimItemReader {
option: Arc<SkimItemReaderOption>,
}
pub struct DefaultSkimCollector {
option: Arc<CollectorOption>,
impl Default for SkimItemReader {
fn default() -> Self {
Self {
option: Arc::new(Default::default()),
}
}
}
impl DefaultSkimCollector {
pub fn new(option: CollectorOption) -> Self {
impl SkimItemReader {
pub fn new(option: SkimItemReaderOption) -> Self {
Self {
option: Arc::new(option),
}
}
pub fn option(mut self, option: SkimItemReaderOption) -> Self {
self.option = Arc::new(option);
self
}
}
impl SkimItemReader {
pub fn of_bufread(&self, source: impl BufRead + Send + 'static) -> SkimItemReceiver {
if self.option.is_simple() {
self.raw_bufread(source)
} else {
self.read_and_collect_from_command(Arc::new(AtomicUsize::new(0)), CollectorInput::Pipe(Box::new(source)))
.0
}
}
/// helper: convert bufread into SkimItemReceiver
fn raw_bufread(&self, mut source: impl BufRead + Send + 'static) -> SkimItemReceiver {
let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = bounded(self.option.buf_size);
let line_ending = self.option.line_ending;
thread::spawn(move || {
let mut buffer = Vec::with_capacity(1024);
loop {
buffer.clear();
// start reading
match source.read_until(line_ending, &mut buffer) {
Ok(n) => {
if n == 0 {
break;
}
if buffer.ends_with(&[b'\r', b'\n']) {
buffer.pop();
buffer.pop();
} else if buffer.ends_with(&[b'\n']) || buffer.ends_with(&[b'\0']) {
buffer.pop();
}
let string = String::from_utf8_lossy(&buffer);
let result = tx_item.send(Arc::new(string.into_owned()));
if result.is_err() {
break;
}
}
Err(_err) => {} // String not UTF8 or other error, skip.
}
}
});
rx_item
}
/// components_to_stop == 0 => all the threads have been stopped
/// return (channel_for_receive_item, channel_to_stop_command)
pub fn read_and_collect_from_command(
fn read_and_collect_from_command(
&self,
components_to_stop: Arc<AtomicUsize>,
input: CollectorInput,
@ -129,7 +204,6 @@ impl DefaultSkimCollector {
let started = Arc::new(AtomicBool::new(false));
let started_clone = started.clone();
let components_to_stop_clone = components_to_stop.clone();
let option = self.option.clone();
// listening to close signal and kill command if needed
thread::spawn(move || {
debug!("collector: command killer start");
@ -154,12 +228,13 @@ impl DefaultSkimCollector {
let started = Arc::new(AtomicBool::new(false));
let started_clone = started.clone();
let tx_interrupt_clone = tx_interrupt.clone();
let option = self.option.clone();
thread::spawn(move || {
debug!("collector: command collector start");
components_to_stop.fetch_add(1, Ordering::SeqCst);
started_clone.store(true, Ordering::SeqCst); // notify parent that it is started
let mut buffer = Vec::with_capacity(READ_BUFFER_SIZE);
let mut buffer = Vec::with_capacity(option.buf_size);
loop {
buffer.clear();
@ -212,7 +287,7 @@ impl DefaultSkimCollector {
}
}
impl CommandCollector for DefaultSkimCollector {
impl CommandCollector for SkimItemReader {
fn invoke(&mut self, cmd: &str, components_to_stop: Arc<AtomicUsize>) -> (SkimItemReceiver, Sender<i32>) {
self.read_and_collect_from_command(components_to_stop, CollectorInput::Command(cmd.to_string()))
}

View file

@ -1,3 +1,2 @@
pub mod item;
pub mod item_collector;
pub mod string_reader;
pub mod item_reader;

View file

@ -1,71 +0,0 @@
use crate::prelude::bounded;
use crate::{SkimItemReceiver, SkimItemSender};
/// helper for turn a BufRead into a skim stream
use std::io::BufRead;
use std::sync::Arc;
use std::thread;
const ITEM_CHANNEL_SIZE: usize = 10240;
pub struct SkimItemReader {
buf_size: usize,
line_ending: u8,
}
impl Default for SkimItemReader {
fn default() -> Self {
Self {
buf_size: ITEM_CHANNEL_SIZE,
line_ending: b'\n',
}
}
}
impl SkimItemReader {
pub fn buf_size(mut self, buf_size: usize) -> Self {
self.buf_size = buf_size;
self
}
pub fn line_ending(mut self, line_ending: u8) -> Self {
self.line_ending = line_ending;
self
}
}
impl SkimItemReader {
/// helper: convert bufread into SkimItemReceiver
pub fn of_bufread(&self, mut source: impl BufRead + Send + 'static) -> SkimItemReceiver {
let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = bounded(self.buf_size);
let line_ending = self.line_ending;
thread::spawn(move || {
let mut buffer = Vec::with_capacity(1024);
loop {
buffer.clear();
// start reading
match source.read_until(line_ending, &mut buffer) {
Ok(n) => {
if n == 0 {
break;
}
if buffer.ends_with(&[b'\r', b'\n']) {
buffer.pop();
buffer.pop();
} else if buffer.ends_with(&[b'\n']) || buffer.ends_with(&[b'\0']) {
buffer.pop();
}
let string = String::from_utf8_lossy(&buffer);
let result = tx_item.send(Arc::new(string.into_owned()));
if result.is_err() {
break;
}
}
Err(_err) => {} // String not UTF8 or other error, skip.
}
}
});
rx_item
}
}

View file

@ -2,7 +2,7 @@ use std::rc::Rc;
use derive_builder::Builder;
use crate::helper::item_collector::DefaultSkimCollector;
use crate::helper::item_reader::SkimItemReader;
use crate::reader::CommandCollector;
use crate::{CaseMatching, FuzzyAlgorithm, MatchEngineFactory};
use std::cell::RefCell;
@ -91,7 +91,7 @@ impl<'a> Default for SkimOptions<'a> {
engine_factory: None,
query_history: &[],
cmd_history: &[],
cmd_collector: Rc::new(RefCell::new(DefaultSkimCollector::new(Default::default()))),
cmd_collector: Rc::new(RefCell::new(SkimItemReader::new(Default::default()))),
}
}
}

View file

@ -1,7 +1,6 @@
pub use crate::ansi::AnsiString;
pub use crate::engine::{factory::*, fuzzy::FuzzyAlgorithm};
pub use crate::helper::item_collector::{CollectorInput, CollectorOption, DefaultSkimCollector};
pub use crate::helper::string_reader::SkimItemReader;
pub use crate::helper::item_reader::{SkimItemReader, SkimItemReaderOption};
pub use crate::options::{SkimOptions, SkimOptionsBuilder};
pub use crate::output::SkimOutput;
pub use crate::*;