1
0
mirror of https://github.com/bobwen-dev/hunter synced 2025-04-12 00:55:41 +02:00

speed up sized_string_u

This commit is contained in:
rabite 2019-06-17 18:02:50 +02:00
parent 327fbc6cd2
commit a676851aed
3 changed files with 98 additions and 30 deletions

View File

@ -57,6 +57,7 @@ mod config;
mod stats; mod stats;
mod icon; mod icon;
mod quick_actions; mod quick_actions;
mod trait_ext;
#[cfg(feature = "img")] #[cfg(feature = "img")]
mod imgview; mod imgview;

View File

@ -6,8 +6,10 @@ use termion::screen::AlternateScreen;
use termion::raw::{IntoRawMode, RawTerminal}; use termion::raw::{IntoRawMode, RawTerminal};
use parse_ansi::parse_bytes; use parse_ansi::parse_bytes;
use crate::unicode_width::{UnicodeWidthStr, UnicodeWidthChar};
use crate::fail::{HResult, ErrorLog}; use crate::fail::{HResult, ErrorLog};
use crate::trait_ext::ExtractResult;
pub type TermMode = AlternateScreen<RawTerminal<BufWriter<Stdout>>>; pub type TermMode = AlternateScreen<RawTerminal<BufWriter<Stdout>>>;
@ -203,7 +205,7 @@ pub fn size() -> HResult<(usize, usize)> {
pub fn sized_string(string: &str, xsize: u16) -> String { pub fn sized_string(string: &str, xsize: u16) -> String {
string.chars().fold("".to_string(), |acc, ch| { string.chars().fold("".to_string(), |acc, ch| {
let width: usize = unicode_width::UnicodeWidthStr::width_cjk(acc.as_str()); let width: usize = unicode_width::UnicodeWidthStr::width(acc.as_str());
if width + 1 >= xsize as usize { if width + 1 >= xsize as usize {
acc acc
} else { } else {
@ -212,46 +214,99 @@ pub fn sized_string(string: &str, xsize: u16) -> String {
}) })
} }
fn is_ansi(ansi_pos: &Vec<(usize, usize)>, char_pos: &usize) -> bool { #[derive(Debug)]
ansi_pos.iter().fold(false, |is_ansi, (start, end)| { enum Token<'a> {
if char_pos >= start && char_pos <= end { Text(&'a str),
true Ansi(&'a str)
} else { is_ansi }
})
} }
fn ansi_len_at(ansi_pos: &Vec<(usize, usize)>, char_pos: &usize) -> usize { fn get_tokens(string: &str) -> Vec<Token> {
ansi_pos.iter().fold(0, |len, (start, end)| { let mut tokens = parse_bytes(string.as_bytes())
if char_pos >= start && char_pos <= end { .fold((Vec::new(), 0), |(mut tokens, last_tok), ansi_pos| {
len + (char_pos - start) if last_tok == 0 {
} else if char_pos >= end { // first iteration
len + (end - start) if ansi_pos.start() != 0 {
} else { // there is text before first ansi code
len tokens.push(Token::Text(&string[0..ansi_pos.start()]));
} }
}) tokens.push(Token::Ansi(&string[ansi_pos.start()..ansi_pos.end()]));
(tokens, ansi_pos.end())
} else if last_tok == ansi_pos.start() {
// next token is another ansi code
tokens.push(Token::Ansi(&string[ansi_pos.start()..ansi_pos.end()]));
(tokens, ansi_pos.end())
} else {
// there is text before the next ansi code
tokens.push(Token::Text(&string[last_tok..ansi_pos.start()]));
tokens.push(Token::Ansi(&string[ansi_pos.start()..ansi_pos.end()]));
(tokens, ansi_pos.end())
}
});
// last part is just text, add it to tokens
if string.len() > tokens.1 {
tokens.0.push(Token::Text(&string[tokens.1..string.len()]));
}
tokens.0
} }
pub fn sized_string_u(string: &str, xsize: usize) -> String { pub fn sized_string_u(string: &str, xsize: usize) -> String {
let ansi_pos = parse_bytes(string.as_bytes()).map(|m| { let tokens = get_tokens(&string);
(m.start(), m.end())
}).collect();
let sized = string.chars().fold(String::new(), |acc, ch| { let sized = tokens.iter().try_fold((String::new(), 0), |(mut sized, width), token| {
let width: usize = unicode_width::UnicodeWidthStr::width_cjk(acc.as_str()); let (tok, tok_width) = match token {
let ansi_len = ansi_len_at(&ansi_pos, &acc.len()); Token::Text(text) => {
let unprinted = acc.len() - width; let tok_str = text;
let tok_width = text.width();
(tok_str, tok_width)
},
Token::Ansi(ansi) => (ansi, 0)
};
if width + unprinted >= xsize + ansi_len + 1{ // adding this token makes string larger than xsise
acc if width + tok_width > xsize {
let chars_left = xsize + 1 - width;
// fill up with chars from token until xsize is reached
let fillup = tok.chars().try_fold((String::new(), 0),
|(mut fillup, fillup_width), chr| {
let chr_width = chr.width().unwrap_or(0);
if fillup_width + chr_width > chars_left {
Err((fillup, fillup_width))
} else { } else {
acc + &ch.to_string() fillup.push(chr);
Ok((fillup, fillup_width + chr_width))
}
});
let (fillup, fillup_width) = fillup.extract();
sized.push_str(&fillup);
// we're done here, stop looping
Err((sized, width + fillup_width))
} else {
sized.push_str(&tok);
Ok((sized, width + tok_width))
} }
}); });
let ansi_len = ansi_len_at(&ansi_pos, &sized.len());
let padded = format!("{:padding$}", sized, padding=xsize + ansi_len + 1);
padded let (mut sized_str, sized_width) = sized.extract();
// pad out string
if sized_width < xsize {
let padding = xsize-sized_width;
for _ in 0..padding {
sized_str += " ";
}
}
sized_str
} }

12
src/trait_ext.rs Normal file
View File

@ -0,0 +1,12 @@
pub trait ExtractResult<T> {
fn extract(self) -> T;
}
impl<T> ExtractResult<T> for Result<T,T> {
fn extract(self) -> T {
match self {
Ok(val) => val,
Err(val) => val
}
}
}