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

Merge pull request #6094 from sargas/fmt-small-widths

fmt: Make sure goal is always positive
This commit is contained in:
Daniel Hofstetter 2024-03-19 16:55:10 +01:00 committed by GitHub
commit 7c8dfca4a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 11 deletions

View file

@ -95,7 +95,8 @@ impl FmtOptions {
(w, g)
}
(Some(&w), None) => {
let g = (w * DEFAULT_GOAL_TO_WIDTH_RATIO / 100).min(w - 3);
// Only allow a goal of zero if the width is set to be zero
let g = (w * DEFAULT_GOAL_TO_WIDTH_RATIO / 100).max(if w == 0 { 0 } else { 1 });
(w, g)
}
(None, Some(&g)) => {
@ -104,6 +105,7 @@ impl FmtOptions {
}
(None, None) => (75, 70),
};
debug_assert!(width >= goal, "GOAL {goal} should not be greater than WIDTH {width} when given {width_opt:?} and {goal_opt:?}.");
if width > MAX_WIDTH {
return Err(USimpleError::new(
@ -331,7 +333,7 @@ pub fn uu_app() -> Command {
Arg::new(options::GOAL)
.short('g')
.long("goal")
.help("Goal width, default of 93% of WIDTH. Must be less than WIDTH.")
.help("Goal width, default of 93% of WIDTH. Must be less than or equal to WIDTH.")
.value_name("GOAL")
.value_parser(clap::value_parser!(usize)),
)

View file

@ -6,7 +6,7 @@
// spell-checker:ignore (ToDO) INFTY MULT accum breakwords linebreak linebreaking linebreaks linelen maxlength minlength nchars ostream overlen parasplit plass posn powf punct signum slen sstart tabwidth tlen underlen winfo wlen wordlen
use std::io::{BufWriter, Stdout, Write};
use std::{cmp, i64, mem};
use std::{cmp, mem};
use crate::parasplit::{ParaWords, Paragraph, WordInfo};
use crate::FmtOptions;
@ -238,8 +238,8 @@ fn find_kp_breakpoints<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
let mut active_breaks = vec![0];
let mut next_active_breaks = vec![];
let stretch = (args.opts.width - args.opts.goal) as isize;
let minlength = args.opts.goal - stretch as usize;
let stretch = args.opts.width - args.opts.goal;
let minlength = args.opts.goal - stretch;
let mut new_linebreaks = vec![];
let mut is_sentence_start = false;
let mut least_demerits = 0;
@ -300,7 +300,7 @@ fn find_kp_breakpoints<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
compute_demerits(
args.opts.goal as isize - tlen as isize,
stretch,
w.word_nchars as isize,
w.word_nchars,
active.prev_rat,
)
};
@ -393,7 +393,7 @@ const DR_MULT: f32 = 600.0;
// DL_MULT is penalty multiplier for short words at end of line
const DL_MULT: f32 = 300.0;
fn compute_demerits(delta_len: isize, stretch: isize, wlen: isize, prev_rat: f32) -> (i64, f32) {
fn compute_demerits(delta_len: isize, stretch: usize, wlen: usize, prev_rat: f32) -> (i64, f32) {
// how much stretch are we using?
let ratio = if delta_len == 0 {
0.0f32
@ -419,7 +419,7 @@ fn compute_demerits(delta_len: isize, stretch: isize, wlen: isize, prev_rat: f32
};
// we penalize lines that have very different ratios from previous lines
let bad_delta_r = (DR_MULT * (((ratio - prev_rat) / 2.0).powi(3)).abs()) as i64;
let bad_delta_r = (DR_MULT * ((ratio - prev_rat) / 2.0).powi(3).abs()) as i64;
let demerits = i64::pow(1 + bad_linelen + bad_wordlen + bad_delta_r, 2);
@ -440,8 +440,8 @@ fn restart_active_breaks<'a>(
} else {
// choose the lesser evil: breaking too early, or breaking too late
let wlen = w.word_nchars + args.compute_width(w, active.length, active.fresh);
let underlen = (min - active.length) as isize;
let overlen = ((wlen + slen + active.length) - args.opts.width) as isize;
let underlen = min as isize - active.length as isize;
let overlen = (wlen + slen + active.length) as isize - args.opts.width as isize;
if overlen > underlen {
// break early, put this word on the next line
(true, args.indent_len + w.word_nchars)

View file

@ -33,7 +33,19 @@ fn test_fmt_width() {
new_ucmd!()
.args(&["one-word-per-line.txt", param, "10"])
.succeeds()
.stdout_is("this is\na file\nwith one\nword per\nline\n");
.stdout_is("this is a\nfile with\none word\nper line\n");
}
}
#[test]
fn test_small_width() {
for width in ["0", "1", "2", "3"] {
for param in ["-w", "--width"] {
new_ucmd!()
.args(&[param, width, "one-word-per-line.txt"])
.succeeds()
.stdout_is("this\nis\na\nfile\nwith\none\nword\nper\nline\n");
}
}
}