mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
fmt: Make sure goal is always positive
A debug assertion was added to enforce "width >= goal" to catch that case before a panic in linebreak.rs. A few warnings in linebreak.rs were addressed as well, and some isize's that should always be positive (if there's no width/goal bugs) were changed to usizes to catch bugs earlier. test_fmt_width is updated to test for the same result as GNU fmt
This commit is contained in:
parent
696615099c
commit
f456b9531f
3 changed files with 25 additions and 11 deletions
|
@ -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)),
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue