remove the usage of ItemWrapper to reduce memory

This commit is contained in:
Jinzhou Zhang 2020-06-27 22:13:42 +08:00
parent a1f17d533c
commit 1d72fcac57
14 changed files with 77 additions and 137 deletions

View file

@ -10,7 +10,6 @@ use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::os::unix::io::AsRawFd;
use std::sync::atomic::AtomicU32;
use clap::{App, Arg, ArgMatches};
use nix::unistd::isatty;
@ -454,7 +453,6 @@ pub fn filter(options: &SkimOptions, source: Option<SkimItemReceiver>) -> Result
//------------------------------------------------------------------------------
// start
let item_index = AtomicU32::new(0);
let components_to_stop = Arc::new(AtomicUsize::new(0));
let collector_option = CollectorOption::with_options(&options);
@ -467,8 +465,7 @@ pub fn filter(options: &SkimOptions, source: Option<SkimItemReceiver>) -> Result
let mut num_matched = 0;
stream_of_item
.into_iter()
.map(|item| ItemWrapper::new(item, (0, item_index.fetch_add(0, Ordering::SeqCst))))
.filter_map(|wrapped| engine.match_item(Arc::new(wrapped)))
.filter_map(|item| engine.match_item(item))
.try_for_each(|matched| {
num_matched += 1;
if options.print_score {

View file

@ -1,8 +1,8 @@
use std::fmt::{Display, Error, Formatter};
use std::sync::Arc;
use crate::item::{ItemWrapper, MatchedItem, MatchedRange, Rank};
use crate::MatchEngine;
use crate::item::{MatchedItem, MatchedRange, Rank};
use crate::{MatchEngine, SkimItem};
//------------------------------------------------------------------------------
#[derive(Debug)]
@ -19,7 +19,7 @@ impl MatchAllEngine {
}
impl MatchEngine for MatchAllEngine {
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchedItem> {
let rank = Rank {
score: 0,
begin: 0,

View file

@ -1,8 +1,8 @@
use std::fmt::{Display, Error, Formatter};
use std::sync::Arc;
use crate::item::{ItemWrapper, MatchedItem, MatchedRange};
use crate::MatchEngine;
use crate::item::{MatchedItem, MatchedRange};
use crate::{MatchEngine, SkimItem};
//------------------------------------------------------------------------------
// OrEngine, a combinator
@ -26,7 +26,7 @@ impl OrEngine {
}
impl MatchEngine for OrEngine {
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchedItem> {
for engine in &self.engines {
let result = engine.match_item(Arc::clone(&item));
if result.is_some() {
@ -98,7 +98,7 @@ impl AndEngine {
}
impl MatchEngine for AndEngine {
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchedItem> {
// mock
let mut results = vec![];
for engine in &self.engines {

View file

@ -1,7 +1,6 @@
use crate::engine::util::{contains_upper, regex_match};
use crate::item::{ItemWrapper, MatchedItem, MatchedRange, Rank};
use crate::SkimItem;
use crate::{CaseMatching, MatchEngine};
use crate::item::{MatchedItem, MatchedRange, Rank};
use crate::{CaseMatching, MatchEngine, SkimItem};
use regex::{escape, Regex};
use std::fmt::{Display, Error, Formatter};
use std::sync::Arc;
@ -66,7 +65,7 @@ impl ExactEngine {
}
impl MatchEngine for ExactEngine {
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchedItem> {
let mut matched_result = None;
for &(start, end) in item.get_matching_ranges().as_ref() {
if self.query_regex.is_none() {

View file

@ -5,7 +5,7 @@ use fuzzy_matcher::clangd::ClangdMatcher;
use fuzzy_matcher::skim::{SkimMatcher, SkimMatcherV2};
use fuzzy_matcher::FuzzyMatcher;
use crate::item::{ItemWrapper, MatchedItem, MatchedRange, Rank};
use crate::item::{MatchedItem, MatchedRange, Rank};
use crate::SkimItem;
use crate::{CaseMatching, MatchEngine};
@ -113,7 +113,7 @@ impl FuzzyEngine {
}
impl MatchEngine for FuzzyEngine {
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchedItem> {
// iterate over all matching fields:
let mut matched_result = None;
for &(start, end) in item.get_matching_ranges().as_ref() {

View file

@ -4,7 +4,7 @@ use std::sync::Arc;
use regex::Regex;
use crate::engine::util::regex_match;
use crate::item::{ItemWrapper, MatchedItem, MatchedRange, Rank};
use crate::item::{MatchedItem, MatchedRange, Rank};
use crate::SkimItem;
use crate::{CaseMatching, MatchEngine};
@ -38,7 +38,7 @@ impl RegexEngine {
}
impl MatchEngine for RegexEngine {
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchedItem> {
let mut matched_result = None;
for &(start, end) in item.get_matching_ranges().as_ref() {
if self.query_regex.is_none() {

View file

@ -130,7 +130,7 @@ impl Draw for Header {
.text_width(screen_width - 2)
.hscroll_offset(self.hscroll_offset)
.build();
print_item(canvas, &mut printer, &item, self.theme.header());
print_item(canvas, &mut printer, item, self.theme.header());
}
Ok(())

View file

@ -12,7 +12,7 @@ use regex::Regex;
use crate::ansi::{ANSIParser, AnsiString};
use crate::field::{parse_matching_fields, parse_transform_fields, FieldRange};
use crate::spinlock::{SpinLock, SpinLockGuard};
use crate::{ItemPreview, SkimItem};
use crate::SkimItem;
//------------------------------------------------------------------------------
/// An item will store everything that one line input will need to be operated and displayed.
@ -123,53 +123,6 @@ impl SkimItem for DefaultSkimItem {
//------------------------------------------------------------------------------
pub type ItemIndex = (u32, u32);
pub struct ItemWrapper {
inner: Arc<dyn SkimItem>,
// (num of run, number of index)
id: ItemIndex,
}
impl ItemWrapper {
pub fn new(item: Arc<dyn SkimItem>, index: ItemIndex) -> Self {
Self { id: index, inner: item }
}
pub fn get_id(&self) -> ItemIndex {
self.id
}
pub fn get_index(&self) -> usize {
self.id.1 as usize
}
pub fn get_inner(&self) -> Arc<dyn SkimItem> {
self.inner.clone()
}
}
/// delegate to inner
impl SkimItem for ItemWrapper {
fn display(&self) -> Cow<AnsiString> {
self.inner.display()
}
fn text(&self) -> Cow<str> {
self.inner.text()
}
fn preview(&self) -> ItemPreview {
self.inner.preview()
}
fn output(&self) -> Cow<str> {
self.inner.output()
}
fn get_matching_ranges(&self) -> Cow<[(usize, usize)]> {
self.inner.get_matching_ranges()
}
}
//------------------------------------------------------------------------------
#[derive(Debug, Copy, Clone, PartialEq, Default)]
pub struct Rank {
@ -188,13 +141,13 @@ pub enum MatchedRange {
#[derive(Clone)]
pub struct MatchedItem {
pub item: Arc<ItemWrapper>,
pub item: Arc<dyn SkimItem>,
pub rank: Rank,
pub matched_range: Option<MatchedRange>, // range of chars that matched the pattern
}
impl MatchedItem {
pub fn builder(item: Arc<ItemWrapper>) -> Self {
pub fn builder(item: Arc<dyn SkimItem>) -> Self {
MatchedItem {
item,
rank: Rank::default(),
@ -233,12 +186,12 @@ const ITEM_POOL_CAPACITY: usize = 1024;
pub struct ItemPool {
length: AtomicUsize,
pool: SpinLock<Vec<Arc<ItemWrapper>>>,
pool: SpinLock<Vec<Arc<dyn SkimItem>>>,
/// number of items that was `take`n
taken: AtomicUsize,
/// reverse first N lines as header
reserved_items: SpinLock<Vec<Arc<ItemWrapper>>>,
reserved_items: SpinLock<Vec<Arc<dyn SkimItem>>>,
lines_to_reserve: usize,
}
@ -281,7 +234,7 @@ impl ItemPool {
self.taken.store(0, Ordering::SeqCst);
}
pub fn append(&self, mut items: Vec<Arc<ItemWrapper>>) {
pub fn append(&self, mut items: Vec<Arc<dyn SkimItem>>) {
let mut pool = self.pool.lock();
let mut header_items = self.reserved_items.lock();
@ -296,13 +249,13 @@ impl ItemPool {
self.length.store(pool.len(), Ordering::SeqCst);
}
pub fn take(&self) -> ItemPoolGuard<Arc<ItemWrapper>> {
pub fn take(&self) -> ItemPoolGuard<Arc<dyn SkimItem>> {
let guard = self.pool.lock();
let taken = self.taken.swap(guard.len(), Ordering::SeqCst);
ItemPoolGuard { guard, start: taken }
}
pub fn reserved(&self) -> ItemPoolGuard<Arc<ItemWrapper>> {
pub fn reserved(&self) -> ItemPoolGuard<Arc<dyn SkimItem>> {
let guard = self.reserved_items.lock();
ItemPoolGuard { guard, start: 0 }
}

View file

@ -16,7 +16,7 @@ use tuikit::prelude::{Event as TermEvent, *};
pub use crate::ansi::AnsiString;
pub use crate::engine::fuzzy::FuzzyAlgorithm;
use crate::event::{EventReceiver, EventSender};
pub use crate::item::{ItemWrapper, MatchedItem};
pub use crate::item::MatchedItem;
use crate::model::Model;
pub use crate::options::SkimOptions;
pub use crate::output::SkimOutput;
@ -174,7 +174,7 @@ impl Default for CaseMatching {
}
pub trait MatchEngine: Sync + Send + Display {
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem>;
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchedItem>;
}
pub trait MatchEngineFactory {

View file

@ -15,7 +15,7 @@ use crate::engine::factory::{AndOrEngineFactory, ExactOrFuzzyEngineFactory, Rege
use crate::event::{Event, EventHandler, EventReceiver, EventSender};
use crate::header::Header;
use crate::input::parse_action_arg;
use crate::item::{ItemPool, ItemWrapper};
use crate::item::ItemPool;
use crate::matcher::{Matcher, MatcherControl};
use crate::options::SkimOptions;
use crate::output::SkimOutput;
@ -352,27 +352,27 @@ impl Model {
}
fn act_execute_silent(&mut self, cmd: &str) {
let item = self.selection.get_current_item();
if depends_on_items(cmd) && item.is_none() {
let current_index = self.selection.get_current_item_idx();
let current_item = self.selection.get_current_item();
if depends_on_items(cmd) && current_item.is_none() {
debug!("act_execute_silent: command refers to items and there is no item for now");
debug!("command to execute: [{}]", cmd);
return;
}
let current_selection = item
let current_selection = current_item
.as_ref()
.map(|item| item.output())
.unwrap_or_else(|| Cow::Borrowed(""));
let query = self.query.get_fz_query();
let cmd_query = self.query.get_cmd_query();
let selections = self.selection.get_selected_wrapped_items();
let (indices, selections) = self.selection.get_selected_indices_and_items();
let tmp: Vec<Cow<str>> = selections.iter().map(|item| item.text()).collect();
let selected_texts: Vec<&str> = tmp.iter().map(|cow| cow.as_ref()).collect();
let indices: Vec<usize> = selections.iter().map(|x| x.get_index()).collect();
let context = InjectContext {
current_index: item.as_ref().map(|x| x.get_index()).unwrap_or(0),
current_index,
delimiter: &self.delimiter,
current_selection: &current_selection,
selections: &selected_texts,
@ -395,10 +395,7 @@ impl Model {
let item_index = (std::u32::MAX, self.next_idx_to_append);
let item: Arc<ItemWrapper> = Arc::new(ItemWrapper::new(
Arc::new(query),
(std::u32::MAX, self.next_idx_to_append),
));
let item: Arc<dyn SkimItem> = Arc::new(query);
self.next_idx_to_append += 1;
@ -475,7 +472,7 @@ impl Model {
accept_key,
query: self.query.get_fz_query(),
cmd: self.query.get_cmd_query(),
selected_items: self.selection.get_selected_items(),
selected_items: self.selection.get_selected_indices_and_items().1,
});
}
@ -555,11 +552,13 @@ impl Model {
// re-draw
if !self.preview_hidden {
let item_index = self.selection.get_current_item_idx();
let item = self.selection.get_current_item();
if let Some(previewer) = self.previewer.as_mut() {
let selections = &self.selection;
let get_selected_items = || selections.get_selected_wrapped_items();
let get_selected_items = || selections.get_selected_indices_and_items();
previewer.on_item_change(
item_index,
item,
env.query.to_string(),
env.cmd_query.to_string(),

View file

@ -1,12 +1,3 @@
use crate::ansi::{ANSIParser, AnsiString};
use crate::event::{Event, EventHandler, UpdateScreen};
use crate::item::ItemWrapper;
use crate::spinlock::SpinLock;
use crate::util::{depends_on_items, inject_command, InjectContext};
use crate::{ItemPreview, SkimItem};
use derive_builder::Builder;
use nix::libc;
use regex::Regex;
use std::borrow::Cow;
use std::cmp::{max, min};
use std::env;
@ -16,8 +7,18 @@ use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use derive_builder::Builder;
use nix::libc;
use regex::Regex;
use tuikit::prelude::{Event as TermEvent, *};
use crate::ansi::{ANSIParser, AnsiString};
use crate::event::{Event, EventHandler, UpdateScreen};
use crate::spinlock::SpinLock;
use crate::util::{depends_on_items, inject_command, InjectContext};
use crate::{ItemPreview, SkimItem};
const TAB_STOP: usize = 8;
const DELIMITER_STR: &str = r"[\t\n ]+";
@ -31,7 +32,8 @@ pub struct Previewer {
vscroll_offset: usize,
wrap: bool,
prev_item: Option<Arc<ItemWrapper>>,
prev_item: Option<Arc<dyn SkimItem>>,
prev_item_index: Option<usize>,
prev_query: Option<String>,
prev_cmd_query: Option<String>,
prev_num_selected: usize,
@ -67,6 +69,7 @@ impl Previewer {
wrap: false,
prev_item: None,
prev_item_index: None,
prev_query: None,
prev_cmd_query: None,
prev_num_selected: 0,
@ -89,21 +92,22 @@ impl Previewer {
pub fn on_item_change(
&mut self,
new_item: impl Into<Option<Arc<ItemWrapper>>>,
new_item_index: usize,
new_item: impl Into<Option<Arc<dyn SkimItem>>>,
new_query: impl Into<Option<String>>,
new_cmd_query: impl Into<Option<String>>,
num_selected: usize,
get_selected_items: impl Fn() -> Vec<Arc<ItemWrapper>>, // lazy get
get_selected_items: impl Fn() -> (Vec<usize>, Vec<Arc<dyn SkimItem>>), // lazy get
) {
let new_item = new_item.into();
let new_query = new_query.into();
let new_cmd_query = new_cmd_query.into();
let item_changed = match (self.prev_item.as_ref(), new_item.as_ref()) {
let item_changed = match (self.prev_item_index.as_ref(), new_item.as_ref()) {
(None, None) => false,
(None, Some(_)) => true,
(Some(_), None) => true,
(Some(prev), Some(cur)) => prev.get_id() != cur.get_id(),
(Some(&prev_index), Some(_)) => prev_index == new_item_index,
};
let query_changed = match (self.prev_query.as_ref(), new_query.as_ref()) {
@ -127,6 +131,7 @@ impl Previewer {
}
self.prev_item = new_item.clone();
self.prev_item_index = Some(new_item_index);
self.prev_query = new_query;
self.prev_cmd_query = new_cmd_query;
self.prev_num_selected = num_selected;
@ -156,13 +161,12 @@ impl Previewer {
let query = self.prev_query.as_ref().map(|s| &**s).unwrap_or("");
let cmd_query = self.prev_cmd_query.as_ref().map(|s| &**s).unwrap_or("");
let selections = get_selected_items();
let (indices, selections) = get_selected_items();
let tmp: Vec<Cow<str>> = selections.iter().map(|item| item.text()).collect();
let selected_texts: Vec<&str> = tmp.iter().map(|cow| cow.as_ref()).collect();
let indices: Vec<usize> = selections.iter().map(|x| x.get_index()).collect();
let context = InjectContext {
current_index: item.get_index(),
current_index: new_item_index,
delimiter: &self.delimiter,
current_selection: &current_selection,
selections: &selected_texts,

View file

@ -1,12 +1,11 @@
use crate::global::{current_run_num, mark_new_run};
use crate::global::mark_new_run;
///! Reader is used for reading items from datasource (e.g. stdin or command output)
///!
///! After reading in a line, reader will save an item into the pool(items)
use crate::item::ItemWrapper;
use crate::item_collector::{read_and_collect_from_command, CollectorInput, CollectorOption};
use crate::options::SkimOptions;
use crate::spinlock::SpinLock;
use crate::SkimItemReceiver;
use crate::{SkimItem, SkimItemReceiver};
use crossbeam::channel::{bounded, select, Sender};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
@ -18,7 +17,7 @@ pub struct ReaderControl {
tx_interrupt: Sender<i32>,
tx_interrupt_cmd: Option<Sender<i32>>,
components_to_stop: Arc<AtomicUsize>,
items: Arc<SpinLock<Vec<Arc<ItemWrapper>>>>,
items: Arc<SpinLock<Vec<Arc<dyn SkimItem>>>>,
}
impl ReaderControl {
@ -33,7 +32,7 @@ impl ReaderControl {
while self.components_to_stop.load(Ordering::SeqCst) != 0 {}
}
pub fn take(&self) -> Vec<Arc<ItemWrapper>> {
pub fn take(&self) -> Vec<Arc<dyn SkimItem>> {
let mut items = self.items.lock();
let mut ret = Vec::with_capacity(items.len());
ret.append(&mut items);
@ -66,7 +65,6 @@ impl Reader {
pub fn run(&mut self, cmd: &str) -> ReaderControl {
mark_new_run(cmd);
let run_num = current_run_num();
let components_to_stop: Arc<AtomicUsize> = Arc::new(AtomicUsize::new(0));
let items = Arc::new(SpinLock::new(Vec::new()));
@ -82,7 +80,7 @@ impl Reader {
});
let components_to_stop_clone = components_to_stop.clone();
let tx_interrupt = collect_item(components_to_stop_clone, rx_item, run_num, items_clone);
let tx_interrupt = collect_item(components_to_stop_clone, rx_item, items_clone);
ReaderControl {
tx_interrupt,
@ -96,8 +94,7 @@ impl Reader {
fn collect_item(
components_to_stop: Arc<AtomicUsize>,
rx_item: SkimItemReceiver,
run_num: u32,
items: Arc<SpinLock<Vec<Arc<ItemWrapper>>>>,
items: Arc<SpinLock<Vec<Arc<dyn SkimItem>>>>,
) -> Sender<i32> {
let (tx_interrupt, rx_interrupt) = bounded(CHANNEL_SIZE);
@ -108,15 +105,12 @@ fn collect_item(
components_to_stop.fetch_add(1, Ordering::SeqCst);
started_clone.store(true, Ordering::SeqCst); // notify parent that it is started
let mut index = 0;
loop {
select! {
recv(rx_item) -> new_item => match new_item {
Ok(item) => {
let item_wrapped = ItemWrapper::new(item, (run_num, index));
let mut vec = items.lock();
vec.push(Arc::new(item_wrapped));
index += 1;
vec.push(item);
}
Err(_) => break,
},

View file

@ -11,7 +11,7 @@ use tuikit::prelude::{Event as TermEvent, *};
use crate::event::{Event, EventHandler, UpdateScreen};
use crate::global::current_run_num;
use crate::item::{parse_criteria, ItemIndex, RankCriteria};
use crate::item::{ItemWrapper, MatchedItem, MatchedRange};
use crate::item::{MatchedItem, MatchedRange};
use crate::orderedvec::CompareFunction;
use crate::orderedvec::OrderedVec;
use crate::spinlock::SpinLock;
@ -29,7 +29,7 @@ lazy_static! {
pub struct Selection {
// all items
items: OrderedVec<MatchedItem>,
selected: BTreeMap<ItemIndex, Arc<ItemWrapper>>,
selected: BTreeMap<ItemIndex, Arc<dyn SkimItem>>,
//
// |>------ items[items.len()-1]
@ -219,7 +219,7 @@ impl Selection {
}
}
pub fn act_select_item(&mut self, item_index: ItemIndex, item: Arc<ItemWrapper>) {
pub fn act_select_item(&mut self, item_index: ItemIndex, item: Arc<dyn SkimItem>) {
if !self.multi_selection {
return;
}
@ -250,10 +250,11 @@ impl Selection {
self.hscroll_offset = hscroll_offset as usize;
}
pub fn get_selected_wrapped_items(&self) -> Vec<Arc<ItemWrapper>> {
pub fn get_selected_indices_and_items(&self) -> (Vec<usize>, Vec<Arc<dyn SkimItem>>) {
// select the current one
let select_cursor = !self.multi_selection || self.selected.is_empty();
let mut selected: Vec<Arc<ItemWrapper>> = self.selected.values().cloned().collect();
let mut selected: Vec<Arc<dyn SkimItem>> = self.selected.values().cloned().collect();
let mut item_indices: Vec<usize> = self.selected.keys().map(|(_run, idx)| *idx as usize).collect();
if select_cursor && !self.items.is_empty() {
let cursor = self.item_cursor + self.line_cursor;
@ -262,18 +263,11 @@ impl Selection {
.get(cursor)
.unwrap_or_else(|| panic!("model:act_output: failed to get item {}", cursor));
let item = current_item.item.clone();
item_indices.push(cursor);
selected.push(item);
}
selected.sort_by_key(|item| item.get_id());
selected
}
pub fn get_selected_items(&self) -> Vec<Arc<dyn SkimItem>> {
self.get_selected_wrapped_items()
.into_iter()
.map(|wrapped| wrapped.get_inner())
.collect()
(item_indices, selected)
}
pub fn get_num_of_selected_exclude_current(&self) -> usize {
@ -292,7 +286,7 @@ impl Selection {
self.multi_selection
}
pub fn get_current_item(&self) -> Option<Arc<ItemWrapper>> {
pub fn get_current_item(&self) -> Option<Arc<dyn SkimItem>> {
let item_idx = self.get_current_item_idx();
self.items.get(item_idx).map(|item| item.item.clone())
}

View file

@ -7,8 +7,8 @@ use tuikit::prelude::*;
use unicode_width::UnicodeWidthChar;
use crate::field::get_string_by_range;
use crate::item::ItemWrapper;
use crate::SkimItem;
use std::sync::Arc;
lazy_static! {
static ref RE_ESCAPE: Regex = Regex::new(r"['\U{00}]").unwrap();
@ -179,7 +179,7 @@ impl LinePrinter {
}
}
pub fn print_item(canvas: &mut dyn Canvas, printer: &mut LinePrinter, item: &ItemWrapper, default_attr: Attr) {
pub fn print_item(canvas: &mut dyn Canvas, printer: &mut LinePrinter, item: &Arc<dyn SkimItem>, default_attr: Attr) {
for (ch, attr) in item.display().iter() {
printer.print_char(canvas, ch, default_attr.extend(attr), false);
}