diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6b13b4984..fc01b1531 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -42,6 +42,7 @@ Deprecations and removed features Scripting improvements ---------------------- - The ``psub`` command now allows combining ``--suffix`` with ``--fifo`` (:issue:`11729`). +- The ``string pad`` command now has a ``-C/--center`` option. Interactive improvements ------------------------ diff --git a/doc_src/cmds/string-pad.rst b/doc_src/cmds/string-pad.rst index 5a255d487..66bab0641 100644 --- a/doc_src/cmds/string-pad.rst +++ b/doc_src/cmds/string-pad.rst @@ -8,7 +8,7 @@ Synopsis .. synopsis:: - string pad [-r | --right] [(-c | --char) CHAR] [(-w | --width) INTEGER] + string pad [-r | --right] [-C | --center] [(-c | --char) CHAR] [(-w | --width) INTEGER] [STRING ...] .. END SYNOPSIS @@ -22,6 +22,8 @@ Description The escape sequences reflect what fish knows about, and how it computes its output. Your terminal might support more escapes, or not support escape sequences that fish knows about. +If **-C** or **--center** is given, add the padding to before and after the string. If it is impossible to perfectly center the result (because the required amount of padding is an odd number), extra padding will be added to the left, unless **--right** is also given. + If **-r** or **--right** is given, add the padding after a string. If **-c** or **--char** is given, pad with *CHAR* instead of whitespace. diff --git a/doc_src/cmds/string.rst b/doc_src/cmds/string.rst index f53e6725f..653029965 100644 --- a/doc_src/cmds/string.rst +++ b/doc_src/cmds/string.rst @@ -18,7 +18,7 @@ Synopsis [-g | --groups-only] [-r | --regex] [-n | --index] [-q | --quiet] [-v | --invert] PATTERN [STRING ...] - string pad [-r | --right] [(-c | --char) CHAR] [(-w | --width) INTEGER] + string pad [-r | --right] [-C | --center] [(-c | --char) CHAR] [(-w | --width) INTEGER] [STRING ...] string repeat [(-n | --count) COUNT] [(-m | --max) MAX] [-N | --no-newline] [-q | --quiet] [STRING ...] diff --git a/po/de.po b/po/de.po index e9235b796..43c1b7f67 100644 --- a/po/de.po +++ b/po/de.po @@ -40692,6 +40692,9 @@ msgstr "" msgid "Pacman log actions" msgstr "" +msgid "Pad both left and right" +msgstr "" + msgid "Pad right instead of left" msgstr "" diff --git a/po/en.po b/po/en.po index b7a9c3c0d..b33b60227 100644 --- a/po/en.po +++ b/po/en.po @@ -40688,6 +40688,9 @@ msgstr "" msgid "Pacman log actions" msgstr "" +msgid "Pad both left and right" +msgstr "" + msgid "Pad right instead of left" msgstr "" diff --git a/po/fr.po b/po/fr.po index ee121ee30..f0e81025d 100644 --- a/po/fr.po +++ b/po/fr.po @@ -40789,6 +40789,9 @@ msgstr "" msgid "Pacman log actions" msgstr "" +msgid "Pad both left and right" +msgstr "" + msgid "Pad right instead of left" msgstr "" diff --git a/po/pl.po b/po/pl.po index 9b010a881..8ededaed9 100644 --- a/po/pl.po +++ b/po/pl.po @@ -40684,6 +40684,9 @@ msgstr "" msgid "Pacman log actions" msgstr "" +msgid "Pad both left and right" +msgstr "" + msgid "Pad right instead of left" msgstr "" diff --git a/po/pt_BR.po b/po/pt_BR.po index cf5f8feb6..4b455ff02 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -40705,6 +40705,9 @@ msgstr "" msgid "Pacman log actions" msgstr "" +msgid "Pad both left and right" +msgstr "" + msgid "Pad right instead of left" msgstr "" diff --git a/po/sv.po b/po/sv.po index 15d7f525c..0c36e6740 100644 --- a/po/sv.po +++ b/po/sv.po @@ -40687,6 +40687,9 @@ msgstr "" msgid "Pacman log actions" msgstr "" +msgid "Pad both left and right" +msgstr "" + msgid "Pad right instead of left" msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index c7fbfadc2..deac096ee 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -40687,6 +40687,9 @@ msgstr "将本地仓库中已有的文物装入捆绑, 用于上传请求" msgid "Pacman log actions" msgstr "Pacman 日志动作" +msgid "Pad both left and right" +msgstr "" + msgid "Pad right instead of left" msgstr "右侧而不是左侧" diff --git a/share/completions/string.fish b/share/completions/string.fish index b4a5c366d..b36f6d97a 100644 --- a/share/completions/string.fish +++ b/share/completions/string.fish @@ -54,6 +54,7 @@ complete -x -c string -n "test (count (commandline -xpc)) -ge 2" -n "contains -- complete -f -c string -n "test (count (commandline -xpc)) -ge 2" -n "contains -- (commandline -xpc)[2] repeat" -s N -l no-newline -d "Remove newline" complete -f -c string -n "test (count (commandline -xpc)) -lt 2" -a pad complete -x -c string -n "test (count (commandline -xpc)) -ge 2" -n "contains -- (commandline -xpc)[2] pad" -s r -l right -d "Pad right instead of left" +complete -x -c string -n "test (count (commandline -xpc)) -ge 2" -n "contains -- (commandline -xpc)[2] pad" -s r -l center -d "Pad both left and right" complete -x -c string -n "test (count (commandline -xpc)) -ge 2" -n "contains -- (commandline -xpc)[2] pad" -s c -l char -x -d "Character to use for padding" complete -x -c string -n "test (count (commandline -xpc)) -ge 2" -n "contains -- (commandline -xpc)[2] pad" -s w -l width -x -d "Integer width of the result, default is maximum width of inputs" complete -f -c string -n "test (count (commandline -xpc)) -lt 2" -a shorten diff --git a/src/builtins/string/pad.rs b/src/builtins/string/pad.rs index 9b704dbf1..213ad1a4f 100644 --- a/src/builtins/string/pad.rs +++ b/src/builtins/string/pad.rs @@ -5,6 +5,7 @@ pub struct Pad { char_to_pad: char, pad_char_width: usize, pad_from: Direction, + center: bool, width: usize, } @@ -14,6 +15,7 @@ fn default() -> Self { char_to_pad: ' ', pad_char_width: 1, pad_from: Direction::Left, + center: false, width: 0, } } @@ -24,9 +26,10 @@ impl StringSubCommand<'_> for Pad { // FIXME docs say `--char`, there was no long_opt with `--char` in C++ wopt(L!("chars"), RequiredArgument, 'c'), wopt(L!("right"), NoArgument, 'r'), + wopt(L!("center"), NoArgument, 'C'), wopt(L!("width"), RequiredArgument, 'w'), ]; - const SHORT_OPTIONS: &'static wstr = L!(":c:rw:"); + const SHORT_OPTIONS: &'static wstr = L!(":c:rCw:"); fn parse_opt(&mut self, name: &wstr, c: char, arg: Option<&wstr>) -> Result<(), StringError> { match c { @@ -55,6 +58,7 @@ fn parse_opt(&mut self, name: &wstr, c: char, arg: Option<&wstr>) -> Result<(), .try_into() .map_err(|_| invalid_args!("%ls: Invalid width value '%ls'\n", name, arg))? } + 'C' => self.center = true, _ => return Err(StringError::UnknownOption), } return Ok(()); @@ -83,21 +87,22 @@ fn handle<'args>( for (input, width) in inputs { use std::iter::repeat; - let pad = (pad_width - width) / self.pad_char_width; - let remaining_width = (pad_width - width) % self.pad_char_width; - let mut padded: WString = match self.pad_from { - Direction::Left => repeat(self.char_to_pad) - .take(pad) - .chain(repeat(' ').take(remaining_width)) - .chain(input.chars()) - .collect(), - Direction::Right => input - .chars() - .chain(repeat(' ').take(remaining_width)) - .chain(repeat(self.char_to_pad).take(pad)) - .collect(), + let total_pad = pad_width - width; + let (left_pad, right_pad) = match (self.pad_from, self.center) { + (Direction::Left, false) => (total_pad, 0), + (Direction::Right, false) => (0, total_pad), + (Direction::Left, true) => (total_pad - total_pad / 2, total_pad / 2), + (Direction::Right, true) => (total_pad / 2, total_pad - total_pad / 2), }; + let chars = |w| repeat(self.char_to_pad).take(w / self.pad_char_width); + let spaces = |w| repeat(' ').take(w % self.pad_char_width); + let mut padded: WString = chars(left_pad) + .chain(spaces(left_pad)) + .chain(input.chars()) + .chain(spaces(right_pad)) + .chain(chars(right_pad)) + .collect(); if print_trailing_newline { padded.push('\n'); } diff --git a/tests/checks/string.fish b/tests/checks/string.fish index c64ab789b..e7bc81ad7 100644 --- a/tests/checks/string.fish +++ b/tests/checks/string.fish @@ -43,10 +43,14 @@ string length -q ""; and echo not zero length; or echo zero length # CHECK: zero length string pad foo +#CHECK: foo +string pad -C foo # CHECK: foo string pad -r -w 7 --chars - foo # CHECK: foo---- +string pad -r -w 7 --chars - --center foo +# CHECK: --foo-- # might overflow when converting sign string sub --start -9223372036854775808 abc @@ -54,24 +58,48 @@ string sub --start -9223372036854775808 abc string pad --width 7 -c '=' foo # CHECK: ====foo +string pad --width 7 -c '=' -C foo +# CHECK: ==foo== +string pad --width 8 -c '=' -C foo +# CHECK: ===foo== +string pad --width 8 -c '=' -Cr foo +# CHECK: ==foo=== echo \|(string pad --width 10 --right foo)\| # CHECK: |foo | +echo \|(string pad --width 10 --right --center foo)\| +# CHECK: | foo | +echo \|(string pad --width 10 --center foo)\| +# CHECK: | foo | begin set -l fish_emoji_width 2 # Pad string with multi-width emoji. string pad -w 4 -c . 🐟 # CHECK: ..🐟 + string pad -w 4 -c . -C 🐟 + # CHECK: .🐟. # Pad with multi-width character. string pad -w 3 -c 🐟 . # CHECK: 🐟. + # string pad would rather the result actually be centerd, than it actually contain + # the padding character (so since it can't print half a 🐟, it instead prints a space which is half as wide) + string collect \|(string pad -w 3 -c 🐟 -C .)\| + # CHECK: | . | + string collect \|(string pad -w 7 -c 🐟 -C .)\| + # CHECK: |🐟 . 🐟| # Multi-width pad with remainder, complemented with a space. string pad -w 4 -c 🐟 . .. # CHECK: 🐟 . # CHECK: 🐟.. + string collect \|(string pad -w 7 -c 🐟 -C . ..)\| + # CHECK: |🐟 . 🐟| + # CHECK: |🐟 ..🐟| + string collect \|(string pad -w 7 -c 🐟 -Cr . ..)\| + # CHECK: |🐟 . 🐟| + # CHECK: |🐟.. 🐟| end # Pad to the maximum length. @@ -79,12 +107,26 @@ string pad -c . long longer longest # CHECK: ...long # CHECK: .longer # CHECK: longest +string pad -c . -C long longer longest +# CHECK: ..long. +# CHECK: .longer +# CHECK: longest +string pad -c . -Cr long longer longest +# CHECK: .long.. +# CHECK: longer. +# CHECK: longest # This tests current behavior where the max width of an argument overrules # the width parameter. This could be changed if needed. string pad -c_ --width 5 longer-than-width-param x # CHECK: longer-than-width-param # CHECK: ______________________x +string pad -c_ --width 5 --center longer-than-width-param x +# CHECK: longer-than-width-param +# CHECK: ___________x___________ +string pad -c_ --width 5 --center --right longer-than-width-param x +# CHECK: longer-than-width-param +# CHECK: ___________x___________ # Current behavior is that only a single padding character is supported. # We can support longer strings in future without breaking compatibility.