Add support for -c short option and fix character mode tab handling (#10530)

This commit adds the `-c` short option for the `--characters` flag and fixes tab handling in character mode. Previously, tabs were only handled in column mode, but now they are properly processed in both character and column modes. The character mode now correctly advances to the next tab stop, handles carriage returns, and backspace characters. Tests have been added to verify the new functionality.
This commit is contained in:
mattsu
2026-01-28 23:17:08 +09:00
committed by GitHub
parent eeca1e1d90
commit aee49f1e10
2 changed files with 55 additions and 5 deletions
+28 -5
View File
@@ -98,6 +98,7 @@ pub fn uu_app() -> Command {
.arg(
Arg::new(options::CHARACTERS)
.long(options::CHARACTERS)
.short('c')
.help(translate!("fold-characters-help"))
.conflicts_with(options::BYTES)
.action(ArgAction::SetTrue),
@@ -260,9 +261,31 @@ fn next_tab_stop(col_count: usize) -> usize {
fn compute_col_count(buffer: &[u8], mode: WidthMode) -> usize {
match mode {
WidthMode::Characters => std::str::from_utf8(buffer)
.map(|s| s.chars().count())
.unwrap_or(buffer.len()),
WidthMode::Characters => {
if let Ok(s) = std::str::from_utf8(buffer) {
let mut width = 0;
for ch in s.chars() {
match ch {
'\r' => width = 0,
'\t' => width = next_tab_stop(width),
'\x08' => width = width.saturating_sub(1),
_ => width += 1,
}
}
width
} else {
let mut width = 0;
for &byte in buffer {
match byte {
CR => width = 0,
TAB => width = next_tab_stop(width),
0x08 => width = width.saturating_sub(1),
_ => width += 1,
}
}
width
}
}
WidthMode::Columns => {
if let Ok(s) = std::str::from_utf8(buffer) {
let mut width = 0;
@@ -382,7 +405,7 @@ fn process_ascii_line<W: Write>(line: &[u8], ctx: &mut FoldContext<'_, W>) -> UR
*ctx.col_count = ctx.col_count.saturating_sub(1);
idx += 1;
}
TAB if ctx.mode == WidthMode::Columns => {
TAB => {
loop {
let next_stop = next_tab_stop(*ctx.col_count);
if next_stop > ctx.width && !ctx.output.is_empty() {
@@ -523,7 +546,7 @@ fn process_utf8_chars<W: Write>(line: &str, ctx: &mut FoldContext<'_, W>) -> URe
continue;
}
if ctx.mode == WidthMode::Columns && ch == '\t' {
if ch == '\t' {
loop {
let next_stop = next_tab_stop(*ctx.col_count);
if next_stop > ctx.width && !ctx.output.is_empty() {
+27
View File
@@ -65,6 +65,15 @@ fn test_wide_characters_with_characters_option() {
.stdout_is("\u{B250}\u{B250}\u{B250}\n");
}
#[test]
fn test_wide_characters_with_characters_short_option() {
new_ucmd!()
.args(&["-c", "-w", "5"])
.pipe_in("\u{B250}\u{B250}\u{B250}\n")
.succeeds()
.stdout_is("\u{B250}\u{B250}\u{B250}\n");
}
#[test]
fn test_multiple_wide_characters_in_column_mode() {
let wide = '\u{FF1A}';
@@ -540,6 +549,24 @@ fn test_fold_after_tab() {
.stdout_is("a\tbb\nb\n");
}
#[test]
fn test_fold_characters_tab_advances_to_next_tab_stop() {
new_ucmd!()
.args(&["-c", "-w", "4"])
.pipe_in("ab\tcd\n")
.succeeds()
.stdout_is("ab\n\t\ncd\n");
}
#[test]
fn test_fold_characters_tab_with_non_ascii() {
new_ucmd!()
.args(&["-c", "-w", "2"])
.pipe_in("\u{00E9}\tb\n")
.succeeds()
.stdout_is("\u{00E9}\n\t\nb\n");
}
#[test]
fn test_fold_at_tab_as_word_boundary() {
new_ucmd!()