1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

ls: add -T support and fix --classify output (#7616)

* add -T option parsing

* add usage of tab_size in display_grid

* fix test_ls_columns with \t

since this behavior is on by default

* update test_tabsize_formatting

* use grid DEFAULT_SEPARATOR_SIZE

* update Tabs

* fix test with column width

* fix cspell

* fix linter warning on match bool

* add comment for 0 tab_size

* update tabsize test with -C

* update one of the tabs tests to use -x

* remove comment and split tests for both x/C args
This commit is contained in:
Leo Emar-Kar 2025-04-17 13:29:22 +01:00 committed by GitHub
parent 8487e12057
commit 538355fb67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 26 deletions

View file

@ -33,7 +33,7 @@ use clap::{
}; };
use glob::{MatchOptions, Pattern}; use glob::{MatchOptions, Pattern};
use lscolors::LsColors; use lscolors::LsColors;
use term_grid::{Direction, Filling, Grid, GridOptions}; use term_grid::{DEFAULT_SEPARATOR_SIZE, Direction, Filling, Grid, GridOptions, SPACES_IN_TAB};
use thiserror::Error; use thiserror::Error;
use uucore::error::USimpleError; use uucore::error::USimpleError;
use uucore::format::human::{SizeFormat, human_readable}; use uucore::format::human::{SizeFormat, human_readable};
@ -91,7 +91,7 @@ pub mod options {
pub static LONG: &str = "long"; pub static LONG: &str = "long";
pub static COLUMNS: &str = "C"; pub static COLUMNS: &str = "C";
pub static ACROSS: &str = "x"; pub static ACROSS: &str = "x";
pub static TAB_SIZE: &str = "tabsize"; // silently ignored (see #3624) pub static TAB_SIZE: &str = "tabsize";
pub static COMMAS: &str = "m"; pub static COMMAS: &str = "m";
pub static LONG_NO_OWNER: &str = "g"; pub static LONG_NO_OWNER: &str = "g";
pub static LONG_NO_GROUP: &str = "o"; pub static LONG_NO_GROUP: &str = "o";
@ -385,6 +385,7 @@ pub struct Config {
line_ending: LineEnding, line_ending: LineEnding,
dired: bool, dired: bool,
hyperlink: bool, hyperlink: bool,
tab_size: usize,
} }
// Fields that can be removed or added to the long format // Fields that can be removed or added to the long format
@ -1086,6 +1087,16 @@ impl Config {
Dereference::DirArgs Dereference::DirArgs
}; };
let tab_size = if !needs_color {
options
.get_one::<String>(options::format::TAB_SIZE)
.and_then(|size| size.parse::<usize>().ok())
.or_else(|| std::env::var("TABSIZE").ok().and_then(|s| s.parse().ok()))
} else {
Some(0)
}
.unwrap_or(SPACES_IN_TAB);
Ok(Self { Ok(Self {
format, format,
files, files,
@ -1123,6 +1134,7 @@ impl Config {
line_ending: LineEnding::from_zero_flag(options.get_flag(options::ZERO)), line_ending: LineEnding::from_zero_flag(options.get_flag(options::ZERO)),
dired, dired,
hyperlink, hyperlink,
tab_size,
}) })
} }
} }
@ -1239,13 +1251,12 @@ pub fn uu_app() -> Command {
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
) )
.arg( .arg(
// silently ignored (see #3624)
Arg::new(options::format::TAB_SIZE) Arg::new(options::format::TAB_SIZE)
.short('T') .short('T')
.long(options::format::TAB_SIZE) .long(options::format::TAB_SIZE)
.env("TABSIZE") .env("TABSIZE")
.value_name("COLS") .value_name("COLS")
.help("Assume tab stops at each COLS instead of 8 (unimplemented)"), .help("Assume tab stops at each COLS instead of 8"),
) )
.arg( .arg(
Arg::new(options::format::COMMAS) Arg::new(options::format::COMMAS)
@ -2554,10 +2565,24 @@ fn display_items(
match config.format { match config.format {
Format::Columns => { Format::Columns => {
display_grid(names, config.width, Direction::TopToBottom, out, quoted)?; display_grid(
names,
config.width,
Direction::TopToBottom,
out,
quoted,
config.tab_size,
)?;
} }
Format::Across => { Format::Across => {
display_grid(names, config.width, Direction::LeftToRight, out, quoted)?; display_grid(
names,
config.width,
Direction::LeftToRight,
out,
quoted,
config.tab_size,
)?;
} }
Format::Commas => { Format::Commas => {
let mut current_col = 0; let mut current_col = 0;
@ -2625,6 +2650,7 @@ fn display_grid(
direction: Direction, direction: Direction,
out: &mut BufWriter<Stdout>, out: &mut BufWriter<Stdout>,
quoted: bool, quoted: bool,
tab_size: usize,
) -> UResult<()> { ) -> UResult<()> {
if width == 0 { if width == 0 {
// If the width is 0 we print one single line // If the width is 0 we print one single line
@ -2674,14 +2700,13 @@ fn display_grid(
.map(|s| s.to_string_lossy().into_owned()) .map(|s| s.to_string_lossy().into_owned())
.collect(); .collect();
// Determine whether to use tabs for separation based on whether any entry ends with '/'. // Since tab_size=0 means no \t, use Spaces separator for optimization.
// If any entry ends with '/', it indicates that the -F flag is likely used to classify directories. let filling = match tab_size {
let use_tabs = names.iter().any(|name| name.ends_with('/')); 0 => Filling::Spaces(DEFAULT_SEPARATOR_SIZE),
_ => Filling::Tabs {
let filling = if use_tabs { spaces: DEFAULT_SEPARATOR_SIZE,
Filling::Text("\t".to_string()) tab_size,
} else { },
Filling::Spaces(2)
}; };
let grid = Grid::new( let grid = Grid::new(

View file

@ -837,7 +837,7 @@ fn test_ls_columns() {
for option in COLUMN_ARGS { for option in COLUMN_ARGS {
let result = scene.ucmd().arg(option).succeeds(); let result = scene.ucmd().arg(option).succeeds();
result.stdout_only("test-columns-1 test-columns-2 test-columns-3 test-columns-4\n"); result.stdout_only("test-columns-1\ttest-columns-2\ttest-columns-3\ttest-columns-4\n");
} }
for option in COLUMN_ARGS { for option in COLUMN_ARGS {
@ -846,7 +846,7 @@ fn test_ls_columns() {
.arg("-w=40") .arg("-w=40")
.arg(option) .arg(option)
.succeeds() .succeeds()
.stdout_only("test-columns-1 test-columns-3\ntest-columns-2 test-columns-4\n"); .stdout_only("test-columns-1\ttest-columns-3\ntest-columns-2\ttest-columns-4\n");
} }
// On windows we are always able to get the terminal size, so we can't simulate falling back to the // On windows we are always able to get the terminal size, so we can't simulate falling back to the
@ -859,7 +859,7 @@ fn test_ls_columns() {
.env("COLUMNS", "40") .env("COLUMNS", "40")
.arg(option) .arg(option)
.succeeds() .succeeds()
.stdout_only("test-columns-1 test-columns-3\ntest-columns-2 test-columns-4\n"); .stdout_only("test-columns-1\ttest-columns-3\ntest-columns-2\ttest-columns-4\n");
} }
scene scene
@ -867,7 +867,7 @@ fn test_ls_columns() {
.env("COLUMNS", "garbage") .env("COLUMNS", "garbage")
.arg("-C") .arg("-C")
.succeeds() .succeeds()
.stdout_is("test-columns-1 test-columns-2 test-columns-3 test-columns-4\n") .stdout_is("test-columns-1\ttest-columns-2\ttest-columns-3\ttest-columns-4\n")
.stderr_is("ls: ignoring invalid width in environment variable COLUMNS: 'garbage'\n"); .stderr_is("ls: ignoring invalid width in environment variable COLUMNS: 'garbage'\n");
} }
scene scene
@ -4366,28 +4366,52 @@ fn test_tabsize_option() {
scene.ucmd().arg("-T").fails(); scene.ucmd().arg("-T").fails();
} }
#[ignore = "issue #3624"]
#[test] #[test]
fn test_tabsize_formatting() { fn test_tabsize_formatting() {
let (at, mut ucmd) = at_and_ucmd!(); let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("aaaaaaaa"); at.touch("aaaaaaaa");
at.touch("bbbb"); at.touch("bbbb");
at.touch("cccc"); at.touch("cccc");
at.touch("dddddddd"); at.touch("dddddddd");
ucmd.args(&["-T", "4"]) scene
.ucmd()
.args(&["-x", "-w18", "-T4"])
.succeeds() .succeeds()
.stdout_is("aaaaaaaa bbbb\ncccc\t dddddddd"); .stdout_is("aaaaaaaa bbbb\ncccc\t dddddddd\n");
ucmd.args(&["-T", "2"]) scene
.ucmd()
.args(&["-C", "-w18", "-T4"])
.succeeds() .succeeds()
.stdout_is("aaaaaaaa bbbb\ncccc\t\t dddddddd"); .stdout_is("aaaaaaaa cccc\nbbbb\t dddddddd\n");
scene
.ucmd()
.args(&["-x", "-w18", "-T2"])
.succeeds()
.stdout_is("aaaaaaaa\tbbbb\ncccc\t\t\tdddddddd\n");
scene
.ucmd()
.args(&["-C", "-w18", "-T2"])
.succeeds()
.stdout_is("aaaaaaaa\tcccc\nbbbb\t\t\tdddddddd\n");
scene
.ucmd()
.args(&["-x", "-w18", "-T0"])
.succeeds()
.stdout_is("aaaaaaaa bbbb\ncccc dddddddd\n");
// use spaces // use spaces
ucmd.args(&["-T", "0"]) scene
.ucmd()
.args(&["-C", "-w18", "-T0"])
.succeeds() .succeeds()
.stdout_is("aaaaaaaa bbbb\ncccc dddddddd"); .stdout_is("aaaaaaaa cccc\nbbbb dddddddd\n");
} }
#[cfg(any( #[cfg(any(