mirror of
https://github.com/lotabout/skim.git
synced 2026-09-10 07:16:23 -04:00
introduce new trait SkimItem
This commit is contained in:
parent
e9eb7b709f
commit
bbd8f91eac
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -248,7 +248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "fuzzy-matcher"
|
||||
version = "0.3.1"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
@ -460,7 +460,7 @@ dependencies = [
|
|||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"derive_builder 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuzzy-matcher 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuzzy-matcher 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
@ -690,7 +690,7 @@ dependencies = [
|
|||
"checksum either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c67353c641dc847124ea1902d69bd753dee9bb3beff9aa3662ecf86c971d1fac"
|
||||
"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a"
|
||||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||
"checksum fuzzy-matcher 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d860c6f043a7f367ffcbdb5833c36f7dc85fa8bc6e7898e2530f643ec90f9f3"
|
||||
"checksum fuzzy-matcher 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c20c3dd0480475a1e6da2f0d4dad7052d81386f56be9e23622e027c9240839b6"
|
||||
"checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55"
|
||||
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
|
||||
"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ time = "0.1.38"
|
|||
clap = "2.26.2"
|
||||
tuikit = "0.3.0"
|
||||
vte = "0.3.3"
|
||||
fuzzy-matcher = "0.3.1"
|
||||
fuzzy-matcher = "0.3.3"
|
||||
rayon = "1.0.3"
|
||||
derive_builder = "0.9"
|
||||
bitflags = "1.0.4"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
extern crate skim;
|
||||
use skim::{Skim, SkimOptionsBuilder};
|
||||
use skim::{Skim, SkimItem, SkimOptionsBuilder};
|
||||
use std::io::Cursor;
|
||||
|
||||
pub fn main() {
|
||||
|
|
@ -18,7 +18,7 @@ pub fn main() {
|
|||
.unwrap_or_else(|| Vec::new());
|
||||
|
||||
for item in selected_items.iter() {
|
||||
print!("{}: {}{}", item.get_index(), item.get_output_text(), "\n");
|
||||
print!("{}: {}{}", item.get_index(), item.output(), "\n");
|
||||
}
|
||||
|
||||
//==================================================
|
||||
|
|
@ -30,6 +30,6 @@ pub fn main() {
|
|||
.unwrap_or_else(|| Vec::new());
|
||||
|
||||
for item in selected_items.iter() {
|
||||
print!("{}: {}{}", item.get_index(), item.get_output_text(), "\n");
|
||||
print!("{}: {}{}", item.get_index(), item.output(), "\n");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
extern crate skim;
|
||||
use skim::{Skim, SkimOptions};
|
||||
use skim::{Skim, SkimItem, SkimOptions};
|
||||
|
||||
pub fn main() {
|
||||
let options = SkimOptions::default();
|
||||
|
|
@ -9,6 +9,6 @@ pub fn main() {
|
|||
.unwrap_or_else(|| Vec::new());
|
||||
|
||||
for item in selected_items.iter() {
|
||||
print!("{}{}", item.get_output_text(), "\n");
|
||||
print!("{}{}", item.output(), "\n");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
src/ansi.rs
28
src/ansi.rs
|
|
@ -195,7 +195,7 @@ impl ANSIParser {
|
|||
self.last_attr = new_attr;
|
||||
}
|
||||
|
||||
pub fn parse_ansi(&mut self, text: &str) -> AnsiString {
|
||||
pub fn parse_ansi(&mut self, text: &str) -> AnsiString<'static> {
|
||||
let mut statemachine = vte::Parser::new();
|
||||
|
||||
for byte in text.as_bytes() {
|
||||
|
|
@ -213,12 +213,12 @@ impl ANSIParser {
|
|||
/// A String that contains ANSI state (e.g. colors)
|
||||
///
|
||||
/// It is internally represented as Vec<(attr, string)>
|
||||
pub struct AnsiString {
|
||||
stripped: Cow<'static, str>,
|
||||
fragments: Vec<(Attr, Cow<'static, str>)>,
|
||||
pub struct AnsiString<'a> {
|
||||
stripped: Cow<'a, str>,
|
||||
fragments: Vec<(Attr, Cow<'a, str>)>,
|
||||
}
|
||||
|
||||
impl AnsiString {
|
||||
impl<'a> AnsiString<'a> {
|
||||
pub fn new_empty() -> Self {
|
||||
Self {
|
||||
stripped: Cow::Owned(String::new()),
|
||||
|
|
@ -234,6 +234,14 @@ impl AnsiString {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_str(str_ref: &'a str) -> Self {
|
||||
let stripped: Cow<'a, str> = Cow::Borrowed(str_ref);
|
||||
Self {
|
||||
stripped: stripped.clone(),
|
||||
fragments: vec![(Attr::default(), stripped.clone())],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(stripped: String, fragments: Vec<(Attr, Cow<'static, str>)>) -> Self {
|
||||
Self {
|
||||
stripped: Cow::Owned(stripped),
|
||||
|
|
@ -241,6 +249,10 @@ impl AnsiString {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse(raw: &'a str) -> AnsiString<'static> {
|
||||
ANSIParser::default().parse_ansi(raw)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.fragments.is_empty()
|
||||
}
|
||||
|
|
@ -258,11 +270,7 @@ impl AnsiString {
|
|||
self.fragments.len() > 1 || (!self.fragments.is_empty() && self.fragments[0].0 != Attr::default())
|
||||
}
|
||||
|
||||
pub fn from_str(raw: &str) -> AnsiString {
|
||||
ANSIParser::default().parse_ansi(raw)
|
||||
}
|
||||
|
||||
pub fn get_stripped(&self) -> &str {
|
||||
pub fn stripped(&self) -> &str {
|
||||
&self.stripped
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
///! matcher engine
|
||||
use crate::item::{Item, MatchedItem, MatchedRange, Rank};
|
||||
use crate::score;
|
||||
use crate::item::{ItemWrapper, MatchedItem, MatchedRange, Rank};
|
||||
use crate::score::FuzzyAlgorithm;
|
||||
use crate::{score, SkimItem};
|
||||
use regex::Regex;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ pub enum MatcherMode {
|
|||
|
||||
// A match engine will execute the matching algorithm
|
||||
pub trait MatchEngine: Sync + Send {
|
||||
fn match_item(&self, item: Arc<Item>) -> Option<MatchedItem>;
|
||||
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem>;
|
||||
fn display(&self) -> String;
|
||||
}
|
||||
|
||||
|
|
@ -52,9 +52,9 @@ impl RegexEngine {
|
|||
}
|
||||
|
||||
impl MatchEngine for RegexEngine {
|
||||
fn match_item(&self, item: Arc<Item>) -> Option<MatchedItem> {
|
||||
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
|
||||
let mut matched_result = None;
|
||||
for &(start, end) in item.get_matching_ranges() {
|
||||
for &(start, end) in item.get_matching_ranges().as_ref() {
|
||||
if self.query_regex.is_none() {
|
||||
matched_result = Some((0, 0));
|
||||
break;
|
||||
|
|
@ -117,10 +117,10 @@ impl FuzzyEngine {
|
|||
}
|
||||
|
||||
impl MatchEngine for FuzzyEngine {
|
||||
fn match_item(&self, item: Arc<Item>) -> Option<MatchedItem> {
|
||||
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
|
||||
// iterate over all matching fields:
|
||||
let mut matched_result = None;
|
||||
for &(start, end) in item.get_matching_ranges() {
|
||||
for &(start, end) in item.get_matching_ranges().as_ref() {
|
||||
matched_result =
|
||||
score::fuzzy_match(&item.get_text()[start..end], &self.query, self.algorithm).map(|(s, vec)| {
|
||||
if start != 0 {
|
||||
|
|
@ -175,7 +175,7 @@ impl MatchAllEngine {
|
|||
}
|
||||
|
||||
impl MatchEngine for MatchAllEngine {
|
||||
fn match_item(&self, item: Arc<Item>) -> Option<MatchedItem> {
|
||||
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
|
||||
let rank = build_rank(0, item.get_index() as i64, 0, 0);
|
||||
|
||||
Some(
|
||||
|
|
@ -222,14 +222,14 @@ impl ExactEngine {
|
|||
|
||||
fn match_item_exact(
|
||||
&self,
|
||||
item: Arc<Item>,
|
||||
item: Arc<ItemWrapper>,
|
||||
// filter: <Option<(start, end), (start, end)>, item_length> -> Option<(start, end)>
|
||||
filter: impl Fn(&Option<((usize, usize), (usize, usize))>, usize) -> Option<(usize, usize)>,
|
||||
) -> Option<MatchedItem> {
|
||||
let mut matched_result = None;
|
||||
let mut range_start = 0;
|
||||
let mut range_end = 0;
|
||||
for &(start, end) in item.get_matching_ranges() {
|
||||
for &(start, end) in item.get_matching_ranges().as_ref() {
|
||||
if self.query == "" {
|
||||
matched_result = Some(((0, 0), (0, 0)));
|
||||
break;
|
||||
|
|
@ -260,7 +260,7 @@ impl ExactEngine {
|
|||
}
|
||||
|
||||
impl MatchEngine for ExactEngine {
|
||||
fn match_item(&self, item: Arc<Item>) -> Option<MatchedItem> {
|
||||
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
|
||||
self.match_item_exact(item, |match_result, len| {
|
||||
let match_range = match *match_result {
|
||||
Some(((s1, e1), (s2, e2))) => {
|
||||
|
|
@ -335,7 +335,7 @@ impl OrEngine {
|
|||
}
|
||||
|
||||
impl MatchEngine for OrEngine {
|
||||
fn match_item(&self, item: Arc<Item>) -> Option<MatchedItem> {
|
||||
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
|
||||
for engine in &self.engines {
|
||||
let result = engine.match_item(Arc::clone(&item));
|
||||
if result.is_some() {
|
||||
|
|
@ -399,7 +399,7 @@ impl AndEngine {
|
|||
for item in items {
|
||||
match item.matched_range {
|
||||
Some(MatchedRange::ByteRange(..)) => {
|
||||
ranges.extend(item.to_chars().unwrap());
|
||||
ranges.extend(item.range_char_indices().unwrap());
|
||||
}
|
||||
Some(MatchedRange::Chars(vec)) => {
|
||||
ranges.extend(vec.iter());
|
||||
|
|
@ -418,7 +418,7 @@ impl AndEngine {
|
|||
}
|
||||
|
||||
impl MatchEngine for AndEngine {
|
||||
fn match_item(&self, item: Arc<Item>) -> Option<MatchedItem> {
|
||||
fn match_item(&self, item: Arc<ItemWrapper>) -> Option<MatchedItem> {
|
||||
// mock
|
||||
let mut results = vec![];
|
||||
for engine in &self.engines {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use std::sync::Arc;
|
|||
use tuikit::prelude::*;
|
||||
|
||||
pub struct Header {
|
||||
header: AnsiString,
|
||||
header: AnsiString<'static>,
|
||||
tabstop: usize,
|
||||
hscroll_offset: usize,
|
||||
reverse: bool,
|
||||
|
|
@ -58,7 +58,7 @@ impl Header {
|
|||
None => {}
|
||||
Some("") => {}
|
||||
Some(header) => {
|
||||
self.header = AnsiString::from_str(header);
|
||||
self.header = AnsiString::parse(header);
|
||||
}
|
||||
}
|
||||
self
|
||||
|
|
|
|||
181
src/item.rs
181
src/item.rs
|
|
@ -1,9 +1,8 @@
|
|||
///! An item is line of text that read from `find` command or stdin together with
|
||||
///! the internal states, such as selected or not
|
||||
use crate::ansi::{ANSIParser, AnsiString};
|
||||
use crate::field::*;
|
||||
use crate::ansi::AnsiString;
|
||||
use crate::spinlock::{SpinLock, SpinLockGuard};
|
||||
use regex::Regex;
|
||||
use crate::{ItemPreview, SkimItem};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::min;
|
||||
use std::default::Default;
|
||||
|
|
@ -11,141 +10,49 @@ use std::ops::Deref;
|
|||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An item will store everything that one line input will need to be operated and displayed.
|
||||
///
|
||||
/// What's special about an item?
|
||||
/// The simplest version of an item is a line of string, but things are getting more complex:
|
||||
/// - The conversion of lower/upper case is slow in rust, because it involds unicode.
|
||||
/// - We may need to interpret the ANSI codes in the text.
|
||||
/// - The text can be transformed and limited while searching.
|
||||
///
|
||||
/// About the ANSI, we made assumption that it is linewise, that means no ANSI codes will affect
|
||||
/// more than one line.
|
||||
#[derive(Debug)]
|
||||
pub struct Item {
|
||||
pub struct ItemWrapper {
|
||||
// (num of run, number of index)
|
||||
index: (usize, usize),
|
||||
|
||||
// The text that will be ouptut when user press `enter`
|
||||
orig_text: String,
|
||||
|
||||
// The text that will shown into the screen. Can be transformed.
|
||||
text: AnsiString,
|
||||
|
||||
matching_ranges: Vec<(usize, usize)>,
|
||||
|
||||
// For the transformed ANSI case, the output will need another transform.
|
||||
using_transform_fields: bool,
|
||||
ansi_enabled: bool,
|
||||
id: (usize, usize),
|
||||
inner: Arc<dyn SkimItem>,
|
||||
}
|
||||
|
||||
impl<'a> Item {
|
||||
pub fn new(
|
||||
orig_text: Cow<str>,
|
||||
ansi_enabled: bool,
|
||||
trans_fields: &[FieldRange],
|
||||
matching_fields: &[FieldRange],
|
||||
delimiter: &Regex,
|
||||
index: (usize, usize),
|
||||
) -> Self {
|
||||
let using_transform_fields = !trans_fields.is_empty();
|
||||
|
||||
// transformed | ANSI | output
|
||||
//------------------------------------------------------
|
||||
// +- T -> trans+ANSI | ANSI
|
||||
// | |
|
||||
// +- T -> trans +- F -> trans | orig
|
||||
// orig | |
|
||||
// +- F -> orig +- T -> ANSI ==| ANSI
|
||||
// | |
|
||||
// +- F -> orig | orig
|
||||
|
||||
let mut ansi_parser: ANSIParser = Default::default();
|
||||
|
||||
let text = if using_transform_fields && ansi_enabled {
|
||||
// ansi and transform
|
||||
ansi_parser.parse_ansi(&parse_transform_fields(delimiter, &orig_text, trans_fields))
|
||||
} else if using_transform_fields {
|
||||
// transformed, not ansi
|
||||
AnsiString::new_string(parse_transform_fields(delimiter, &orig_text, trans_fields))
|
||||
} else if ansi_enabled {
|
||||
// not transformed, ansi
|
||||
ansi_parser.parse_ansi(&orig_text)
|
||||
} else {
|
||||
// normal case
|
||||
AnsiString::new_empty()
|
||||
};
|
||||
|
||||
let mut ret = Item {
|
||||
index,
|
||||
orig_text: orig_text.into_owned(),
|
||||
text,
|
||||
using_transform_fields: !trans_fields.is_empty(),
|
||||
matching_ranges: Vec::new(),
|
||||
ansi_enabled,
|
||||
};
|
||||
|
||||
let matching_ranges = if !matching_fields.is_empty() {
|
||||
parse_matching_fields(delimiter, ret.get_text(), matching_fields)
|
||||
} else {
|
||||
vec![(0, ret.get_text().len())]
|
||||
};
|
||||
|
||||
ret.matching_ranges = matching_ranges;
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn get_text(&self) -> &str {
|
||||
if !self.using_transform_fields && !self.ansi_enabled {
|
||||
&self.orig_text
|
||||
} else {
|
||||
&self.text.get_stripped()
|
||||
impl ItemWrapper {
|
||||
pub fn new(item: impl SkimItem + 'static, index: (usize, usize)) -> Self {
|
||||
Self {
|
||||
id: index,
|
||||
inner: Arc::new(item),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_text_struct(&self) -> Option<&AnsiString> {
|
||||
if !self.using_transform_fields && !self.ansi_enabled {
|
||||
None
|
||||
} else {
|
||||
Some(&self.text)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_output_text(&'a self) -> Cow<'a, str> {
|
||||
if self.using_transform_fields && self.ansi_enabled {
|
||||
let mut ansi_parser: ANSIParser = Default::default();
|
||||
let text = ansi_parser.parse_ansi(&self.orig_text);
|
||||
Cow::Owned(text.into_inner())
|
||||
} else if !self.using_transform_fields && self.ansi_enabled {
|
||||
Cow::Borrowed(self.text.get_stripped())
|
||||
} else {
|
||||
Cow::Borrowed(&self.orig_text)
|
||||
}
|
||||
pub fn get_id(&self) -> (usize, usize) {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn get_index(&self) -> usize {
|
||||
self.index.1
|
||||
}
|
||||
|
||||
pub fn get_full_index(&self) -> (usize, usize) {
|
||||
self.index
|
||||
}
|
||||
|
||||
pub fn get_matching_ranges(&self) -> &[(usize, usize)] {
|
||||
&self.matching_ranges
|
||||
self.id.1
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Item {
|
||||
fn clone(&self) -> Item {
|
||||
Item {
|
||||
index: self.index,
|
||||
orig_text: self.orig_text.clone(),
|
||||
text: self.text.clone(),
|
||||
using_transform_fields: self.using_transform_fields,
|
||||
matching_ranges: self.matching_ranges.clone(),
|
||||
ansi_enabled: self.ansi_enabled,
|
||||
}
|
||||
/// delegate to inner
|
||||
impl SkimItem for ItemWrapper {
|
||||
fn display(&self) -> Cow<AnsiString> {
|
||||
self.inner.display()
|
||||
}
|
||||
|
||||
fn preview(&self) -> ItemPreview {
|
||||
self.inner.preview()
|
||||
}
|
||||
|
||||
fn get_text(&self) -> Cow<str> {
|
||||
self.inner.get_text()
|
||||
}
|
||||
|
||||
fn output(&self) -> Cow<str> {
|
||||
self.inner.output()
|
||||
}
|
||||
|
||||
fn get_matching_ranges(&self) -> Cow<[(usize, usize)]> {
|
||||
self.inner.get_matching_ranges()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,15 +71,15 @@ pub enum MatchedRange {
|
|||
Chars(Vec<usize>), // individual character indices matched
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub struct MatchedItem {
|
||||
pub item: Arc<Item>,
|
||||
pub item: Arc<ItemWrapper>,
|
||||
pub rank: Rank,
|
||||
pub matched_range: Option<MatchedRange>, // range of chars that matched the pattern
|
||||
}
|
||||
|
||||
impl MatchedItem {
|
||||
pub fn builder(item: Arc<Item>) -> Self {
|
||||
pub fn builder(item: Arc<ItemWrapper>) -> Self {
|
||||
MatchedItem {
|
||||
item,
|
||||
rank: Rank::default(),
|
||||
|
|
@ -194,11 +101,11 @@ impl MatchedItem {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn to_chars(&self) -> Option<Vec<usize>> {
|
||||
pub fn range_char_indices(&self) -> Option<Vec<usize>> {
|
||||
self.matched_range.as_ref().map(|r| match r {
|
||||
MatchedRange::ByteRange(start, end) => {
|
||||
let first = self.item.orig_text[..*start].chars().count();
|
||||
let last = first + self.item.orig_text[*start..*end].chars().count();
|
||||
let first = self.item.get_text()[..*start].chars().count();
|
||||
let last = first + self.item.get_text()[*start..*end].chars().count();
|
||||
(first..last).collect()
|
||||
}
|
||||
MatchedRange::Chars(vec) => vec.clone(),
|
||||
|
|
@ -210,12 +117,12 @@ const ITEM_POOL_CAPACITY: usize = 1024;
|
|||
|
||||
pub struct ItemPool {
|
||||
length: AtomicUsize,
|
||||
pool: SpinLock<Vec<Arc<Item>>>,
|
||||
pool: SpinLock<Vec<Arc<ItemWrapper>>>,
|
||||
/// number of items that was `take`n
|
||||
taken: AtomicUsize,
|
||||
|
||||
/// reverse first N lines as header
|
||||
reserved_items: SpinLock<Vec<Arc<Item>>>,
|
||||
reserved_items: SpinLock<Vec<Arc<ItemWrapper>>>,
|
||||
lines_to_reserve: usize,
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +165,7 @@ impl ItemPool {
|
|||
self.taken.store(0, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn append(&self, mut items: Vec<Arc<Item>>) {
|
||||
pub fn append(&self, mut items: Vec<Arc<ItemWrapper>>) {
|
||||
let mut pool = self.pool.lock();
|
||||
let mut header_items = self.reserved_items.lock();
|
||||
|
||||
|
|
@ -273,13 +180,13 @@ impl ItemPool {
|
|||
self.length.store(pool.len(), Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn take(&self) -> ItemPoolGuard<Arc<Item>> {
|
||||
pub fn take(&self) -> ItemPoolGuard<Arc<ItemWrapper>> {
|
||||
let guard = self.pool.lock();
|
||||
let taken = self.taken.swap(guard.len(), Ordering::SeqCst);
|
||||
ItemPoolGuard { guard, start: taken }
|
||||
}
|
||||
|
||||
pub fn reserved(&self) -> ItemPoolGuard<Arc<Item>> {
|
||||
pub fn reserved(&self) -> ItemPoolGuard<Arc<ItemWrapper>> {
|
||||
let guard = self.reserved_items.lock();
|
||||
ItemPoolGuard { guard, start: 0 }
|
||||
}
|
||||
|
|
|
|||
50
src/lib.rs
50
src/lib.rs
|
|
@ -19,10 +19,12 @@ mod query;
|
|||
mod reader;
|
||||
mod score;
|
||||
mod selection;
|
||||
mod sk;
|
||||
mod spinlock;
|
||||
mod theme;
|
||||
mod util;
|
||||
|
||||
use crate::ansi::AnsiString;
|
||||
use crate::event::Event::*;
|
||||
use crate::event::{EventReceiver, EventSender};
|
||||
use crate::model::Model;
|
||||
|
|
@ -31,6 +33,7 @@ pub use crate::output::SkimOutput;
|
|||
use crate::reader::Reader;
|
||||
pub use crate::score::FuzzyAlgorithm;
|
||||
use nix::unistd::isatty;
|
||||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
|
|
@ -40,6 +43,49 @@ use std::sync::Arc;
|
|||
use std::thread;
|
||||
use tuikit::prelude::{Event as TermEvent, *};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
pub trait SkimItem: Send + Sync {
|
||||
/// The text to be displayed on the item list, could contain ANSI properties
|
||||
fn display(&self) -> Cow<AnsiString>;
|
||||
|
||||
/// helper function to get pure text presentation(without color) of the item
|
||||
fn get_text(&self) -> Cow<str>;
|
||||
|
||||
/// get output text(after accept), could be override
|
||||
fn output(&self) -> Cow<str> {
|
||||
self.get_text()
|
||||
}
|
||||
|
||||
fn preview(&self) -> ItemPreview {
|
||||
ItemPreview::Global
|
||||
}
|
||||
|
||||
/// we could limit the matching ranges of the `get_text` of the item.
|
||||
/// providing (start_byte, end_byte) of the range
|
||||
fn get_matching_ranges(&self) -> Cow<[(usize, usize)]> {
|
||||
Cow::Owned(vec![(0, self.display().stripped().len())])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str> + Send + Sync> SkimItem for T {
|
||||
fn display(&self) -> Cow<AnsiString> {
|
||||
Cow::Owned(AnsiString::new_str(self.as_ref()))
|
||||
}
|
||||
|
||||
fn get_text(&self) -> Cow<str> {
|
||||
Cow::Borrowed(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Preview
|
||||
pub enum ItemPreview<'a> {
|
||||
Command(&'a str),
|
||||
Text(&'a str),
|
||||
Global,
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
pub struct Skim {}
|
||||
|
||||
impl Skim {
|
||||
|
|
@ -163,9 +209,9 @@ impl Skim {
|
|||
for item in reader_control.take().into_iter() {
|
||||
if let Some(matched) = engine.match_item(item) {
|
||||
if options.print_score {
|
||||
println!("{}\t{}", -matched.rank.score, matched.item.get_output_text());
|
||||
println!("{}\t{}", -matched.rank.score, matched.item.output());
|
||||
} else {
|
||||
println!("{}", matched.item.get_output_text());
|
||||
println!("{}", matched.item.output());
|
||||
}
|
||||
match_count += 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ extern crate skim;
|
|||
extern crate time;
|
||||
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use skim::{FuzzyAlgorithm, Skim, SkimOptions, SkimOptionsBuilder};
|
||||
use skim::{FuzzyAlgorithm, Skim, SkimItem, SkimOptions, SkimOptionsBuilder};
|
||||
use std::env;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
|
@ -249,7 +249,7 @@ fn real_main() -> i32 {
|
|||
}
|
||||
|
||||
for item in output.selected_items.iter() {
|
||||
print!("{}{}", item.get_output_text(), output_ending);
|
||||
print!("{}{}", item.output(), output_ending);
|
||||
}
|
||||
|
||||
if output.selected_items.is_empty() {1} else {0}
|
||||
|
|
|
|||
22
src/model.rs
22
src/model.rs
|
|
@ -12,7 +12,7 @@ use tuikit::prelude::{Event as TermEvent, *};
|
|||
use crate::event::{Event, EventHandler, EventReceiver, EventSender};
|
||||
use crate::header::Header;
|
||||
use crate::input::parse_action_arg;
|
||||
use crate::item::{Item, ItemPool};
|
||||
use crate::item::{ItemPool, ItemWrapper};
|
||||
use crate::matcher::{Matcher, MatcherControl, MatcherMode};
|
||||
use crate::options::SkimOptions;
|
||||
use crate::output::SkimOutput;
|
||||
|
|
@ -23,7 +23,8 @@ use crate::selection::Selection;
|
|||
use crate::spinlock::SpinLock;
|
||||
use crate::theme::ColorTheme;
|
||||
use crate::util::{inject_command, margin_string_to_size, parse_margin, InjectContext};
|
||||
use crate::FuzzyAlgorithm;
|
||||
use crate::{FuzzyAlgorithm, SkimItem};
|
||||
use std::borrow::Cow;
|
||||
|
||||
const REFRESH_DURATION: i64 = 100;
|
||||
const SPINNER_DURATION: u32 = 200;
|
||||
|
|
@ -330,12 +331,13 @@ impl Model {
|
|||
|
||||
fn act_execute_silent(&mut self, cmd: &str) {
|
||||
let item = self.selection.get_current_item();
|
||||
let current_selection = item.as_ref().map(|item| item.get_output_text()).unwrap();
|
||||
let current_selection = item.as_ref().map(|item| item.output()).unwrap();
|
||||
let query = self.query.get_query();
|
||||
let cmd_query = self.query.get_cmd_query();
|
||||
|
||||
let selected_items = self.selection.get_selected_items();
|
||||
let selected_texts: Vec<&str> = selected_items.iter().map(|item| item.get_text()).collect();
|
||||
let tmp = self.selection.get_selected_items();
|
||||
let tmp: Vec<Cow<str>> = tmp.iter().map(|item| item.get_text()).collect();
|
||||
let selected_texts: Vec<&str> = tmp.iter().map(|cow| cow.as_ref()).collect();
|
||||
|
||||
let context = InjectContext {
|
||||
delimiter: &self.delimiter,
|
||||
|
|
@ -356,15 +358,7 @@ impl Model {
|
|||
if query.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let item = Arc::new(Item::new(
|
||||
String::from_utf8_lossy(query.as_bytes()),
|
||||
false,
|
||||
&Vec::new(),
|
||||
&Vec::new(),
|
||||
&Regex::new("").unwrap(),
|
||||
(std::usize::MAX, self.next_idx_to_append),
|
||||
));
|
||||
let item: Arc<ItemWrapper> = Arc::new(ItemWrapper::new(query, (std::usize::MAX, self.next_idx_to_append)));
|
||||
|
||||
self.next_idx_to_append += 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
use crate::item::Item;
|
||||
use crate::item::ItemWrapper;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SkimOutput {
|
||||
pub accept_key: Option<String>,
|
||||
pub query: String,
|
||||
pub cmd: String,
|
||||
pub selected_items: Vec<Arc<Item>>,
|
||||
pub selected_items: Vec<Arc<ItemWrapper>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use crate::ansi::AnsiString;
|
||||
use crate::event::{Event, EventHandler, UpdateScreen};
|
||||
use crate::item::Item;
|
||||
use crate::item::ItemWrapper;
|
||||
use crate::spinlock::SpinLock;
|
||||
use crate::util::{inject_command, InjectContext};
|
||||
use crate::SkimItem;
|
||||
use derive_builder::Builder;
|
||||
use nix::libc;
|
||||
use regex::Regex;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::{max, min};
|
||||
use std::env;
|
||||
use std::process::{Command, Stdio};
|
||||
|
|
@ -21,7 +23,7 @@ const DELIMITER_STR: &str = r"[\t\n ]+";
|
|||
|
||||
pub struct Previewer {
|
||||
tx_preview: Sender<PreviewEvent>,
|
||||
content_lines: Arc<SpinLock<Vec<AnsiString>>>,
|
||||
content_lines: Arc<SpinLock<Vec<AnsiString<'static>>>>,
|
||||
|
||||
width: AtomicUsize,
|
||||
height: AtomicUsize,
|
||||
|
|
@ -29,7 +31,7 @@ pub struct Previewer {
|
|||
vscroll_offset: usize,
|
||||
wrap: bool,
|
||||
|
||||
prev_item: Option<Arc<Item>>,
|
||||
prev_item: Option<Arc<ItemWrapper>>,
|
||||
prev_query: Option<String>,
|
||||
prev_cmd_query: Option<String>,
|
||||
prev_num_selected: usize,
|
||||
|
|
@ -87,11 +89,11 @@ impl Previewer {
|
|||
|
||||
pub fn on_item_change(
|
||||
&mut self,
|
||||
new_item: impl Into<Option<Arc<Item>>>,
|
||||
new_item: impl Into<Option<Arc<ItemWrapper>>>,
|
||||
new_query: impl Into<Option<String>>,
|
||||
new_cmd_query: impl Into<Option<String>>,
|
||||
num_selected: usize,
|
||||
get_selected_items: impl Fn() -> Vec<Arc<Item>>, // lazy get
|
||||
get_selected_items: impl Fn() -> Vec<Arc<ItemWrapper>>, // lazy get
|
||||
) {
|
||||
let new_item = new_item.into();
|
||||
let new_query = new_query.into();
|
||||
|
|
@ -101,7 +103,7 @@ impl Previewer {
|
|||
(None, None) => false,
|
||||
(None, Some(_)) => true,
|
||||
(Some(_), None) => true,
|
||||
(Some(prev), Some(cur)) => prev.get_output_text() != cur.get_output_text(),
|
||||
(Some(prev), Some(cur)) => prev.output() != cur.output(),
|
||||
};
|
||||
|
||||
let query_changed = match (self.prev_query.as_ref(), new_query.as_ref()) {
|
||||
|
|
@ -133,12 +135,14 @@ impl Previewer {
|
|||
let current_selection = self
|
||||
.prev_item
|
||||
.as_ref()
|
||||
.map(|item| item.get_output_text())
|
||||
.map(|item| item.output())
|
||||
.unwrap_or_else(|| "".into());
|
||||
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 selected_items = get_selected_items();
|
||||
let selected_texts: Vec<&str> = selected_items.iter().map(|item| item.get_text()).collect();
|
||||
|
||||
let tmp = get_selected_items();
|
||||
let tmp: Vec<Cow<str>> = tmp.iter().map(|item| item.get_text()).collect();
|
||||
let selected_texts: Vec<&str> = tmp.iter().map(|cow| cow.as_ref()).collect();
|
||||
|
||||
let context = InjectContext {
|
||||
delimiter: &self.delimiter,
|
||||
|
|
@ -290,7 +294,7 @@ impl PreviewThread {
|
|||
|
||||
fn run<C>(rx_preview: Receiver<PreviewEvent>, on_return: C)
|
||||
where
|
||||
C: Fn(Vec<AnsiString>) + Send + Sync + 'static,
|
||||
C: Fn(Vec<AnsiString<'static>>) + Send + Sync + 'static,
|
||||
{
|
||||
let callback = Arc::new(on_return);
|
||||
let mut preview_thread: Option<PreviewThread> = None;
|
||||
|
|
@ -330,7 +334,7 @@ where
|
|||
|
||||
match spawned {
|
||||
Err(err) => {
|
||||
let astdout = AnsiString::from_str(format!("Failed to spawn: {} / {}", cmd, err).as_str());
|
||||
let astdout = AnsiString::parse(format!("Failed to spawn: {} / {}", cmd, err).as_str());
|
||||
callback(vec![astdout]);
|
||||
preview_thread = None;
|
||||
}
|
||||
|
|
@ -353,7 +357,7 @@ where
|
|||
|
||||
fn wait<C>(spawned: std::process::Child, callback: C)
|
||||
where
|
||||
C: Fn(Vec<AnsiString>),
|
||||
C: Fn(Vec<AnsiString<'static>>),
|
||||
{
|
||||
let output = spawned.wait_with_output();
|
||||
|
||||
|
|
@ -370,7 +374,7 @@ where
|
|||
&output.stderr
|
||||
});
|
||||
|
||||
let lines = out_str.lines().map(AnsiString::from_str).collect();
|
||||
let lines = out_str.lines().map(AnsiString::parse).collect();
|
||||
callback(lines);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@
|
|||
///!
|
||||
///! After reading in a line, reader will save an item into the pool(items)
|
||||
use crate::field::FieldRange;
|
||||
use crate::item::Item;
|
||||
use crate::item::ItemWrapper;
|
||||
use crate::options::SkimOptions;
|
||||
use crate::sk::item::SkItem;
|
||||
use crate::spinlock::SpinLock;
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -22,7 +23,7 @@ const DELIMITER_STR: &str = r"[\t\n ]+";
|
|||
pub struct ReaderControl {
|
||||
stopped: Arc<AtomicBool>,
|
||||
thread_reader: JoinHandle<()>,
|
||||
items: Arc<SpinLock<Vec<Arc<Item>>>>,
|
||||
items: Arc<SpinLock<Vec<Arc<ItemWrapper>>>>,
|
||||
}
|
||||
|
||||
impl ReaderControl {
|
||||
|
|
@ -31,7 +32,7 @@ impl ReaderControl {
|
|||
let _ = self.thread_reader.join();
|
||||
}
|
||||
|
||||
pub fn take(&self) -> Vec<Arc<Item>> {
|
||||
pub fn take(&self) -> Vec<Arc<ItemWrapper>> {
|
||||
let mut items = self.items.lock();
|
||||
let mut ret = Vec::with_capacity(items.len());
|
||||
ret.append(&mut items);
|
||||
|
|
@ -174,7 +175,7 @@ lazy_static! {
|
|||
fn reader(
|
||||
cmd: &str,
|
||||
stopped: Arc<AtomicBool>,
|
||||
items: Arc<SpinLock<Vec<Arc<Item>>>>,
|
||||
items: Arc<SpinLock<Vec<Arc<ItemWrapper>>>>,
|
||||
option: Arc<ReaderOption>,
|
||||
source_file: Option<Box<dyn BufRead + Send>>,
|
||||
) {
|
||||
|
|
@ -231,15 +232,16 @@ fn reader(
|
|||
buffer.pop();
|
||||
}
|
||||
|
||||
let item = Item::new(
|
||||
let raw_item = SkItem::new(
|
||||
String::from_utf8_lossy(&buffer),
|
||||
opt.use_ansi_color,
|
||||
&opt.transform_fields,
|
||||
&opt.matching_fields,
|
||||
&opt.delimiter,
|
||||
(run_num, index),
|
||||
);
|
||||
|
||||
let item = ItemWrapper::new(raw_item, (run_num, index));
|
||||
|
||||
{
|
||||
// save item into pool
|
||||
let mut vec = items.lock();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
///! Handle the selections of items
|
||||
use crate::event::{Event, EventHandler, UpdateScreen};
|
||||
use crate::item::{parse_criteria, RankCriteria};
|
||||
use crate::item::{Item, MatchedItem, MatchedRange};
|
||||
use crate::item::{ItemWrapper, MatchedItem, MatchedRange};
|
||||
use crate::orderedvec::CompareFunction;
|
||||
use crate::orderedvec::OrderedVec;
|
||||
use crate::spinlock::SpinLock;
|
||||
use crate::theme::{ColorTheme, DEFAULT_THEME};
|
||||
use crate::util::{print_item, reshape_string, LinePrinter};
|
||||
use crate::SkimOptions;
|
||||
use crate::{SkimItem, SkimOptions};
|
||||
use std::cmp::max;
|
||||
use std::cmp::min;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -30,7 +30,7 @@ lazy_static! {
|
|||
pub struct Selection {
|
||||
criterion: Vec<RankCriteria>,
|
||||
items: OrderedVec<MatchedItem>, // all items
|
||||
selected: HashMap<(usize, usize), Arc<Item>>,
|
||||
selected: HashMap<(usize, usize), Arc<ItemWrapper>>,
|
||||
|
||||
//
|
||||
// |>------ items[items.len()-1]
|
||||
|
|
@ -201,7 +201,7 @@ impl Selection {
|
|||
.items
|
||||
.get(cursor)
|
||||
.unwrap_or_else(|| panic!("model:act_toggle: failed to get item {}", cursor));
|
||||
let index = current_item.item.get_full_index();
|
||||
let index = current_item.item.get_id();
|
||||
if !self.selected.contains_key(&index) {
|
||||
self.selected.insert(index, current_item.item.clone());
|
||||
} else {
|
||||
|
|
@ -216,7 +216,7 @@ impl Selection {
|
|||
}
|
||||
|
||||
for current_item in self.items.iter() {
|
||||
let index = current_item.item.get_full_index();
|
||||
let index = current_item.item.get_id();
|
||||
if !self.selected.contains_key(&index) {
|
||||
self.selected.insert(index, current_item.item.clone());
|
||||
} else {
|
||||
|
|
@ -225,12 +225,12 @@ impl Selection {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn act_select_item(&mut self, item: Arc<Item>) {
|
||||
pub fn act_select_item(&mut self, item: Arc<ItemWrapper>) {
|
||||
if !self.multi_selection {
|
||||
return;
|
||||
}
|
||||
|
||||
self.selected.insert(item.get_full_index(), item);
|
||||
self.selected.insert(item.get_id(), item);
|
||||
}
|
||||
|
||||
pub fn act_select_all(&mut self) {
|
||||
|
|
@ -240,7 +240,7 @@ impl Selection {
|
|||
|
||||
for current_item in self.items.iter() {
|
||||
let item = current_item.item.clone();
|
||||
self.selected.insert(item.get_full_index(), item);
|
||||
self.selected.insert(item.get_id(), item);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -255,10 +255,10 @@ impl Selection {
|
|||
self.hscroll_offset = hscroll_offset as usize;
|
||||
}
|
||||
|
||||
pub fn get_selected_items(&self) -> Vec<Arc<Item>> {
|
||||
pub fn get_selected_items(&self) -> Vec<Arc<ItemWrapper>> {
|
||||
// select the current one
|
||||
let select_cursor = !self.multi_selection || self.selected.is_empty();
|
||||
let mut selected: Vec<Arc<Item>> = self.selected.values().cloned().collect();
|
||||
let mut selected: Vec<Arc<ItemWrapper>> = self.selected.values().cloned().collect();
|
||||
|
||||
if select_cursor && !self.items.is_empty() {
|
||||
let cursor = self.item_cursor + self.line_cursor;
|
||||
|
|
@ -270,7 +270,7 @@ impl Selection {
|
|||
selected.push(item);
|
||||
}
|
||||
|
||||
selected.sort_by_key(|item| item.get_full_index());
|
||||
selected.sort_by_key(|item| item.get_id());
|
||||
selected
|
||||
}
|
||||
|
||||
|
|
@ -290,7 +290,7 @@ impl Selection {
|
|||
self.multi_selection
|
||||
}
|
||||
|
||||
pub fn get_current_item(&self) -> Option<Arc<Item>> {
|
||||
pub fn get_current_item(&self) -> Option<Arc<ItemWrapper>> {
|
||||
let item_idx = self.get_current_item_idx();
|
||||
self.items.get(item_idx).map(|item| item.item.clone())
|
||||
}
|
||||
|
|
@ -358,7 +358,7 @@ impl Selection {
|
|||
return Err("screen width is too small".into());
|
||||
}
|
||||
|
||||
let index = matched_item.item.get_full_index();
|
||||
let index = matched_item.item.get_id();
|
||||
|
||||
let default_attr = if is_current {
|
||||
self.theme.current()
|
||||
|
|
|
|||
131
src/sk/item.rs
Normal file
131
src/sk/item.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
use crate::ansi::{ANSIParser, AnsiString};
|
||||
use crate::field::{parse_matching_fields, parse_transform_fields, FieldRange};
|
||||
use crate::SkimItem;
|
||||
use regex::Regex;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// An item will store everything that one line input will need to be operated and displayed.
|
||||
///
|
||||
/// What's special about an item?
|
||||
/// The simplest version of an item is a line of string, but things are getting more complex:
|
||||
/// - The conversion of lower/upper case is slow in rust, because it involds unicode.
|
||||
/// - We may need to interpret the ANSI codes in the text.
|
||||
/// - The text can be transformed and limited while searching.
|
||||
///
|
||||
/// About the ANSI, we made assumption that it is linewise, that means no ANSI codes will affect
|
||||
/// more than one line.
|
||||
#[derive(Debug)]
|
||||
pub struct SkItem {
|
||||
// The text that will be ouptut when user press `enter`
|
||||
orig_text: String,
|
||||
|
||||
// The text that will shown into the screen. Can be transformed.
|
||||
text: AnsiString<'static>,
|
||||
|
||||
matching_ranges: Vec<(usize, usize)>,
|
||||
|
||||
// For the transformed ANSI case, the output will need another transform.
|
||||
using_transform_fields: bool,
|
||||
ansi_enabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> SkItem {
|
||||
pub fn new(
|
||||
orig_text: Cow<str>,
|
||||
ansi_enabled: bool,
|
||||
trans_fields: &[FieldRange],
|
||||
matching_fields: &[FieldRange],
|
||||
delimiter: &Regex,
|
||||
) -> Self {
|
||||
let using_transform_fields = !trans_fields.is_empty();
|
||||
|
||||
// transformed | ANSI | output
|
||||
//------------------------------------------------------
|
||||
// +- T -> trans+ANSI | ANSI
|
||||
// | |
|
||||
// +- T -> trans +- F -> trans | orig
|
||||
// orig | |
|
||||
// +- F -> orig +- T -> ANSI ==| ANSI
|
||||
// | |
|
||||
// +- F -> orig | orig
|
||||
|
||||
let mut ansi_parser: ANSIParser = Default::default();
|
||||
|
||||
let text = if using_transform_fields && ansi_enabled {
|
||||
// ansi and transform
|
||||
ansi_parser.parse_ansi(&parse_transform_fields(delimiter, &orig_text, trans_fields))
|
||||
} else if using_transform_fields {
|
||||
// transformed, not ansi
|
||||
AnsiString::new_string(parse_transform_fields(delimiter, &orig_text, trans_fields))
|
||||
} else if ansi_enabled {
|
||||
// not transformed, ansi
|
||||
ansi_parser.parse_ansi(&orig_text)
|
||||
} else {
|
||||
// normal case
|
||||
AnsiString::new_empty()
|
||||
};
|
||||
|
||||
let mut ret = SkItem {
|
||||
orig_text: orig_text.into(),
|
||||
text,
|
||||
using_transform_fields: !trans_fields.is_empty(),
|
||||
matching_ranges: Vec::new(),
|
||||
ansi_enabled,
|
||||
};
|
||||
|
||||
let matching_ranges = if !matching_fields.is_empty() {
|
||||
parse_matching_fields(delimiter, &ret.get_text(), matching_fields)
|
||||
} else {
|
||||
vec![(0, ret.get_text().len())]
|
||||
};
|
||||
|
||||
ret.matching_ranges = matching_ranges;
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl SkimItem for SkItem {
|
||||
fn display(&self) -> Cow<AnsiString> {
|
||||
if self.using_transform_fields || self.ansi_enabled {
|
||||
Cow::Borrowed(&self.text)
|
||||
} else {
|
||||
Cow::Owned(AnsiString::new_str(&self.orig_text))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_text(&self) -> Cow<str> {
|
||||
if !self.using_transform_fields && !self.ansi_enabled {
|
||||
Cow::Borrowed(&self.orig_text)
|
||||
} else {
|
||||
Cow::Borrowed(self.text.stripped())
|
||||
}
|
||||
}
|
||||
|
||||
fn output(&self) -> Cow<str> {
|
||||
if self.using_transform_fields && self.ansi_enabled {
|
||||
let mut ansi_parser: ANSIParser = Default::default();
|
||||
let text = ansi_parser.parse_ansi(&self.orig_text);
|
||||
Cow::Owned(text.into_inner())
|
||||
} else if !self.using_transform_fields && self.ansi_enabled {
|
||||
Cow::Borrowed(self.text.stripped())
|
||||
} else {
|
||||
Cow::Borrowed(&self.orig_text)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_matching_ranges(&self) -> Cow<[(usize, usize)]> {
|
||||
Cow::Borrowed(&self.matching_ranges)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SkItem {
|
||||
fn clone(&self) -> SkItem {
|
||||
SkItem {
|
||||
orig_text: self.orig_text.clone(),
|
||||
text: self.text.clone(),
|
||||
using_transform_fields: self.using_transform_fields,
|
||||
matching_ranges: self.matching_ranges.clone(),
|
||||
ansi_enabled: self.ansi_enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/sk/mod.rs
Normal file
1
src/sk/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod item;
|
||||
15
src/util.rs
15
src/util.rs
|
|
@ -7,7 +7,8 @@ use tuikit::prelude::*;
|
|||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
use crate::field::get_string_by_range;
|
||||
use crate::item::Item;
|
||||
use crate::item::ItemWrapper;
|
||||
use crate::SkimItem;
|
||||
|
||||
lazy_static! {
|
||||
static ref RE_FIELDS: Regex = Regex::new(r"\\?(\{ *-?[0-9.,cq+]*? *})").unwrap();
|
||||
|
|
@ -172,15 +173,9 @@ impl LinePrinter {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_item(canvas: &mut dyn Canvas, printer: &mut LinePrinter, item: &Item, default_attr: Attr) {
|
||||
if item.get_text_struct().is_some() && item.get_text_struct().as_ref().unwrap().has_attrs() {
|
||||
for (ch, attr) in item.get_text_struct().as_ref().unwrap().iter() {
|
||||
printer.print_char(canvas, ch, default_attr.extend(attr), false);
|
||||
}
|
||||
} else {
|
||||
for ch in item.get_text().chars() {
|
||||
printer.print_char(canvas, ch, default_attr, false);
|
||||
}
|
||||
pub fn print_item(canvas: &mut dyn Canvas, printer: &mut LinePrinter, item: &ItemWrapper, default_attr: Attr) {
|
||||
for (ch, attr) in item.display().iter() {
|
||||
printer.print_char(canvas, ch, default_attr.extend(attr), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue