fold: variable width tabs, guard treating tab as whitespace

Treat tab chars as advancing to the next tab stop rather than having a fixed
8-column width.

Also treat tab as a whitespace split target only when splitting on word
boundaries.
This commit is contained in:
Daniel Rocco 2021-04-05 00:16:21 -04:00
parent bd8b129d9a
commit e5c61a28be
5 changed files with 114 additions and 4 deletions

View file

@ -14,6 +14,8 @@ use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Read};
use std::path::Path;
const TAB_WIDTH: usize = 8;
static SYNTAX: &str = "[OPTION]... [FILE]...";
static SUMMARY: &str = "Writes each file (or standard input if no files are given)
to standard output whilst breaking long lines";
@ -220,11 +222,14 @@ fn fold_file<T: Read>(mut file: BufReader<T>, spaces: bool, width: usize) {
match ch {
'\t' => {
if col_count + 8 > width && !output.is_empty() {
let next_tab_stop = col_count + TAB_WIDTH - col_count % TAB_WIDTH;
if next_tab_stop > width && !output.is_empty() {
emit_output!();
}
col_count += 8;
last_space = Some(char_count);
col_count = next_tab_stop;
last_space = if spaces { Some(char_count) } else { None };
}
'\x08' => {
// FIXME: does not match GNU's handling of backspace

View file

@ -132,7 +132,7 @@ fn test_single_tab_should_not_add_extra_newline() {
}
#[test]
fn test_tab_counts_as_8_columns() {
fn test_initial_tab_counts_as_8_columns() {
new_ucmd!()
.arg("-w8")
.pipe_in("\t1")
@ -140,6 +140,33 @@ fn test_tab_counts_as_8_columns() {
.stdout_is("\t\n1");
}
#[test]
fn test_tab_should_advance_to_next_tab_stop() {
// tab advances the column count to the next tab stop, i.e. the width
// of the tab varies based on the leading text
new_ucmd!()
.args(&["-w8", "tab_stops.input"])
.succeeds()
.stdout_is_fixture("tab_stops_w8.expected");
}
#[test]
fn test_all_tabs_should_advance_to_next_tab_stops() {
new_ucmd!()
.args(&["-w16", "tab_stops.input"])
.succeeds()
.stdout_is_fixture("tab_stops_w16.expected");
}
#[test]
fn test_fold_before_tab_with_narrow_width() {
new_ucmd!()
.arg("-w7")
.pipe_in("a\t1")
.succeeds()
.stdout_is("a\n\t\n1");
}
#[test]
fn test_fold_at_word_boundary() {
new_ucmd!()
@ -167,8 +194,35 @@ fn test_fold_at_word_boundary_preserve_final_newline() {
.stdout_is("one \ntwo\n");
}
#[test]
fn test_fold_at_tab() {
new_ucmd!()
.arg("-w8")
.pipe_in("a\tbbb\n")
.succeeds()
.stdout_is("a\t\nbbb\n");
}
#[test]
fn test_fold_after_tab() {
new_ucmd!()
.arg("-w10")
.pipe_in("a\tbbb\n")
.succeeds()
.stdout_is("a\tbb\nb\n");
}
#[test]
fn test_fold_at_tab_as_word_boundary() {
new_ucmd!()
.args(&["-w8", "-s"])
.pipe_in("a\tbbb\n")
.succeeds()
.stdout_is("a\t\nbbb\n");
}
#[test]
fn test_fold_after_tab_as_word_boundary() {
new_ucmd!()
.args(&["-w10", "-s"])
.pipe_in("a\tbbb\n")
@ -317,6 +371,15 @@ fn test_tab_counts_as_one_byte() {
.stdout_is("1\t\n2\n");
}
#[test]
fn test_bytewise_fold_before_tab_with_narrow_width() {
new_ucmd!()
.args(&["-w7", "-b"])
.pipe_in("a\t1")
.succeeds()
.stdout_is("a\t1");
}
#[test]
fn test_bytewise_fold_at_word_boundary_only_whitespace() {
new_ucmd!()

11
tests/fixtures/fold/tab_stops.input vendored Normal file
View file

@ -0,0 +1,11 @@
1
12
123
1234
12345
123456
1234567
12345678
123456781
12345678 2
12345678 2 4

View file

@ -0,0 +1,13 @@
1
12
123
1234
12345
123456
1234567
12345678
123456781
12345678
2
12345678
2 4

View file

@ -0,0 +1,18 @@
1
12
123
1234
12345
123456
1234567
12345678
12345678
1
12345678
2
12345678
2
4