Skip to content

Commit

Permalink
Add tab_width support
Browse files Browse the repository at this point in the history
  • Loading branch information
mtoohey31 committed Feb 16, 2023
1 parent edc56bf commit 8d7b0c2
Show file tree
Hide file tree
Showing 15 changed files with 248 additions and 103 deletions.
5 changes: 5 additions & 0 deletions benchmarks/Cargo.toml
Expand Up @@ -23,6 +23,11 @@ name = "unfill"
harness = false
path = "unfill.rs"

[[bench]]
name = "display_width"
harness = false
path = "display_width.rs"

[dependencies]
textwrap = { path = "../", features = ["hyphenation"] }

Expand Down
26 changes: 26 additions & 0 deletions benchmarks/display_width.rs
@@ -0,0 +1,26 @@
use criterion::{criterion_group, criterion_main, Criterion};

pub fn benchmark(c: &mut Criterion) {
let words_per_line = [
5, 10, 15, 5, 5, 10, 5, 5, 5, 10, // 10 lines
10, 10, 5, 5, 5, 5, 15, 10, 5, 5, // 20 lines
10, 5, 5, 5, 15, 10, 10, 5, 5, 5, // 30 lines
15, 5, 5, 10, 5, 5, 5, 15, 5, 10, // 40 lines
5, 15, 5, 5, 15, 5, 10, 10, 5, 5, // 50 lines
];
let mut text = String::new();
for (line_no, word_count) in words_per_line.iter().enumerate() {
text.push_str("\t\t\t");
text.push_str(&lipsum::lipsum_words_from_seed(*word_count, line_no as u64));
text.push('\n');
}
text.push_str("\n\n\n\n");
assert_eq!(text.len(), 2800); // The size for reference.

c.bench_function("display_width", |b| {
b.iter(|| textwrap::core::display_width(&text, 2))
});
}

criterion_group!(benches, benchmark);
criterion_main!(benches);
8 changes: 4 additions & 4 deletions examples/wasm/src/lib.rs
Expand Up @@ -152,7 +152,7 @@ impl textwrap::core::Fragment for CanvasWord<'_> {
}

#[inline]
fn whitespace_width(&self) -> f64 {
fn whitespace_width(&self, _: u8) -> f64 {
self.whitespace_width
}

Expand Down Expand Up @@ -351,7 +351,7 @@ pub fn draw_wrapped_text(
let mut lineno = 0;
for line in text.split('\n') {
let words = word_separator.find_words(line);
let split_words = split_words(words, &word_splitter);
let split_words = split_words(words, &word_splitter, 0);

let canvas_words = split_words
.flat_map(|word| {
Expand All @@ -366,10 +366,10 @@ pub fn draw_wrapped_text(

let line_lengths = [options.width];
let wrapped_words = match options.wrap_algorithm {
WasmWrapAlgorithm::FirstFit => wrap_first_fit(&canvas_words, &line_lengths),
WasmWrapAlgorithm::FirstFit => wrap_first_fit(&canvas_words, &line_lengths, 0),
WasmWrapAlgorithm::OptimalFit => {
let penalties = options.penalties.into();
wrap_optimal_fit(&canvas_words, &line_lengths, &penalties).unwrap()
wrap_optimal_fit(&canvas_words, &line_lengths, 0, &penalties).unwrap()
}
_ => Err("WasmOptions has an invalid wrap_algorithm field")?,
};
Expand Down
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/wrap_first_fit.rs
Expand Up @@ -14,12 +14,12 @@ struct Word {
#[rustfmt::skip]
impl core::Fragment for Word {
fn width(&self) -> f64 { self.width }
fn whitespace_width(&self) -> f64 { self.whitespace_width }
fn whitespace_width(&self, _: u8) -> f64 { self.whitespace_width }
fn penalty_width(&self) -> f64 { self.penalty_width }
}

fuzz_target!(|input: (f64, Vec<Word>)| {
let width = input.0;
let words = input.1;
let _ = wrap_first_fit(&words, &[width]);
let _ = wrap_first_fit(&words, &[width], 0);
});
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/wrap_optimal_fit.rs
Expand Up @@ -35,7 +35,7 @@ struct Word {
#[rustfmt::skip]
impl core::Fragment for Word {
fn width(&self) -> f64 { self.width }
fn whitespace_width(&self) -> f64 { self.whitespace_width }
fn whitespace_width(&self, _: u8) -> f64 { self.whitespace_width }
fn penalty_width(&self) -> f64 { self.penalty_width }
}

Expand All @@ -57,5 +57,5 @@ fuzz_target!(|input: (usize, Vec<Word>, Penalties)| {
}
}

let _ = wrap_optimal_fit(&words, &[width as f64], &penalties);
let _ = wrap_optimal_fit(&words, &[width as f64], 0, &penalties);
});
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/wrap_optimal_fit_usize.rs
Expand Up @@ -35,7 +35,7 @@ struct Word {
#[rustfmt::skip]
impl core::Fragment for Word {
fn width(&self) -> f64 { self.width as f64 }
fn whitespace_width(&self) -> f64 { self.whitespace_width as f64 }
fn whitespace_width(&self, _: u8) -> f64 { self.whitespace_width as f64 }
fn penalty_width(&self) -> f64 { self.penalty_width as f64 }
}

Expand All @@ -45,5 +45,5 @@ fuzz_target!(|input: (usize, Vec<Word>, Penalties)| {
let width = input.0;
let words = input.1;
let penalties = input.2.into();
let _ = wrap_optimal_fit(&words, &[width as f64], &penalties);
let _ = wrap_optimal_fit(&words, &[width as f64], 0, &penalties);
});
28 changes: 23 additions & 5 deletions src/columns.rs
@@ -1,7 +1,6 @@
//! Functionality for wrapping text into columns.

use crate::core::display_width;
use crate::{wrap, Options};
use crate::{core, wrap, Options};

/// Wrap text into columns with a given total width.
///
Expand All @@ -23,9 +22,9 @@ use crate::{wrap, Options};
/// # let columns = 2;
/// # let options = textwrap::Options::new(80);
/// let inner_width = options.width
/// - textwrap::core::display_width(left_gap)
/// - textwrap::core::display_width(right_gap)
/// - textwrap::core::display_width(middle_gap) * (columns - 1);
/// - textwrap::core::display_width(left_gap, options.tab_width)
/// - textwrap::core::display_width(right_gap, options.tab_width)
/// - textwrap::core::display_width(middle_gap, options.tab_width) * (columns - 1);
/// let column_width = inner_width / columns;
/// ```
///
Expand Down Expand Up @@ -74,6 +73,8 @@ where
assert!(columns > 0);

let mut options: Options = total_width_or_options.into();
let tab_width = options.tab_width;
let display_width = |text| core::display_width(text, tab_width);

let inner_width = options
.width
Expand Down Expand Up @@ -190,4 +191,21 @@ mod tests {
fn wrap_columns_panic_with_zero_columns() {
wrap_columns("", 0, 10, "", "", "");
}

#[test]
fn wrap_columns_with_tabs() {
let options = Options::new(23).tab_width(4);

#[cfg(feature = "smawk")]
let expected = vec!["|hello |is\tlong|", "|this |yeah |"];
#[cfg(all(not(feature = "smawk"), feature = "unicode-linebreak"))]
let expected = vec!["|hello |long |", "|this\tis|yeah |"];
#[cfg(not(any(feature = "smawk", feature = "unicode-linebreak")))]
let expected = vec!["|hello\tt|\tlong |", "|his\tis |\tyeah |"];

assert_eq!(
wrap_columns("hello\tthis\tis\tlong\tyeah", 2, options, "|", "|", "|"),
expected
)
}
}

0 comments on commit 8d7b0c2

Please sign in to comment.