cursive/src/utils/mod.rs

121 lines
3.4 KiB
Rust
Raw Normal View History

//! Toolbox to make text layout easier.
use unicode_segmentation::UnicodeSegmentation;
2016-10-02 22:22:29 +00:00
use unicode_width::UnicodeWidthStr;
mod lines_iterator;
mod reader;
2018-01-07 23:48:20 +00:00
pub mod span_lines_iterator;
pub use self::lines_iterator::{LinesIterator, Row};
pub use self::reader::ProgressReader;
/// The length and width of a part of a string.
2017-10-15 04:18:50 +00:00
pub struct Span {
/// The length (in bytes) of the string.
pub length: usize,
/// The unicode-width of the string.
pub width: usize,
}
2017-10-15 04:18:50 +00:00
/// Computes a prefix that fits in the given `width`.
///
2017-10-15 04:18:50 +00:00
/// Takes non-breakable elements from `iter`, while keeping the string width
/// under `width` (and adding `delimiter` between each element).
///
2017-10-15 04:18:50 +00:00
/// Given `total_text = iter.collect().join(delimiter)`, the result is the
/// length of the longest prefix of `width` or less cells, without breaking
/// inside an element.
///
/// Example:
///
/// ```
/// # extern crate cursive;
/// extern crate unicode_segmentation;
/// use unicode_segmentation::UnicodeSegmentation;
///
/// # use cursive::utils::prefix;
/// # fn main() {
/// let my_text = "blah...";
/// // This returns the number of bytes for a prefix of `my_text` that
/// // fits within 5 cells.
/// prefix(my_text.graphemes(true), 5, "");
/// # }
/// ```
2017-10-15 04:18:50 +00:00
pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str) -> Span
2017-10-12 23:38:55 +00:00
where
I: Iterator<Item = &'a str>,
{
let delimiter_width = delimiter.width();
let delimiter_len = delimiter.len();
2017-03-06 18:38:18 +00:00
// `current_width` is the width of everything
// before the next token, including any space.
let mut current_width = 0;
let sum: usize = iter.take_while(|token| {
2017-10-12 23:38:55 +00:00
let width = token.width();
if current_width + width > available_width {
false
} else {
// Include the delimiter after this token.
current_width += width;
current_width += delimiter_width;
true
}
}).map(|token| token.len() + delimiter_len)
.sum();
// We counted delimiter once too many times,
// but only if the iterator was non empty.
2017-03-06 14:32:03 +00:00
let length = sum.saturating_sub(delimiter_len);
2017-03-06 18:38:18 +00:00
// `current_width` includes a delimiter after the last token
2017-03-06 18:34:58 +00:00
debug_assert!(current_width <= available_width + delimiter_width);
2017-10-15 04:18:50 +00:00
Span {
length: length,
width: current_width,
}
}
2017-10-15 04:18:50 +00:00
/// Computes the longest suffix that fits in the given `width`.
///
/// Doesn't break inside elements returned by `iter`.
///
/// Returns the number of bytes of the longest
/// suffix from `text` that fits in `width`.
///
/// This is a shortcut for `prefix_length(iter.rev(), width, delimiter)`
2017-10-15 04:18:50 +00:00
pub fn suffix<'a, I>(iter: I, width: usize, delimiter: &str) -> Span
2017-10-12 23:38:55 +00:00
where
I: DoubleEndedIterator<Item = &'a str>,
{
prefix(iter.rev(), width, delimiter)
}
2017-10-15 04:18:50 +00:00
/// Computes the longest suffix that fits in the given `width`.
///
/// Breaks between any two graphemes.
2017-10-15 04:18:50 +00:00
pub fn simple_suffix(text: &str, width: usize) -> Span {
suffix(text.graphemes(true), width, "")
}
2017-03-06 18:34:58 +00:00
2017-10-15 04:18:50 +00:00
/// Computes the longest prefix that fits in the given width.
2017-10-13 00:38:21 +00:00
///
/// Breaks between any two graphemes.
2017-10-15 04:18:50 +00:00
pub fn simple_prefix(text: &str, width: usize) -> Span {
2017-10-13 00:38:21 +00:00
prefix(text.graphemes(true), width, "")
}
2017-03-06 18:34:58 +00:00
#[cfg(test)]
mod tests {
use utils;
#[test]
fn test_prefix() {
assert_eq!(utils::prefix(" abra ".split(' '), 5, " ").length, 5);
assert_eq!(utils::prefix("abra a".split(' '), 5, " ").length, 4);
assert_eq!(utils::prefix("a a br".split(' '), 5, " ").length, 3);
}
}