mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +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)
|
(w, g)
|
||||||
}
|
}
|
||||||
(Some(&w), None) => {
|
(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)
|
(w, g)
|
||||||
}
|
}
|
||||||
(None, Some(&g)) => {
|
(None, Some(&g)) => {
|
||||||
|
@ -104,6 +105,7 @@ impl FmtOptions {
|
||||||
}
|
}
|
||||||
(None, None) => (75, 70),
|
(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 {
|
if width > MAX_WIDTH {
|
||||||
return Err(USimpleError::new(
|
return Err(USimpleError::new(
|
||||||
|
@ -331,7 +333,7 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(options::GOAL)
|
Arg::new(options::GOAL)
|
||||||
.short('g')
|
.short('g')
|
||||||
.long("goal")
|
.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_name("GOAL")
|
||||||
.value_parser(clap::value_parser!(usize)),
|
.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
|
// 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::io::{BufWriter, Stdout, Write};
|
||||||
use std::{cmp, i64, mem};
|
use std::{cmp, mem};
|
||||||
|
|
||||||
use crate::parasplit::{ParaWords, Paragraph, WordInfo};
|
use crate::parasplit::{ParaWords, Paragraph, WordInfo};
|
||||||
use crate::FmtOptions;
|
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 active_breaks = vec![0];
|
||||||
let mut next_active_breaks = vec![];
|
let mut next_active_breaks = vec![];
|
||||||
|
|
||||||
let stretch = (args.opts.width - args.opts.goal) as isize;
|
let stretch = args.opts.width - args.opts.goal;
|
||||||
let minlength = args.opts.goal - stretch as usize;
|
let minlength = args.opts.goal - stretch;
|
||||||
let mut new_linebreaks = vec![];
|
let mut new_linebreaks = vec![];
|
||||||
let mut is_sentence_start = false;
|
let mut is_sentence_start = false;
|
||||||
let mut least_demerits = 0;
|
let mut least_demerits = 0;
|
||||||
|
@ -300,7 +300,7 @@ fn find_kp_breakpoints<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
|
||||||
compute_demerits(
|
compute_demerits(
|
||||||
args.opts.goal as isize - tlen as isize,
|
args.opts.goal as isize - tlen as isize,
|
||||||
stretch,
|
stretch,
|
||||||
w.word_nchars as isize,
|
w.word_nchars,
|
||||||
active.prev_rat,
|
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
|
// DL_MULT is penalty multiplier for short words at end of line
|
||||||
const DL_MULT: f32 = 300.0;
|
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?
|
// how much stretch are we using?
|
||||||
let ratio = if delta_len == 0 {
|
let ratio = if delta_len == 0 {
|
||||||
0.0f32
|
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
|
// 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);
|
let demerits = i64::pow(1 + bad_linelen + bad_wordlen + bad_delta_r, 2);
|
||||||
|
|
||||||
|
@ -440,8 +440,8 @@ fn restart_active_breaks<'a>(
|
||||||
} else {
|
} else {
|
||||||
// choose the lesser evil: breaking too early, or breaking too late
|
// 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 wlen = w.word_nchars + args.compute_width(w, active.length, active.fresh);
|
||||||
let underlen = (min - active.length) as isize;
|
let underlen = min as isize - active.length as isize;
|
||||||
let overlen = ((wlen + slen + active.length) - args.opts.width) as isize;
|
let overlen = (wlen + slen + active.length) as isize - args.opts.width as isize;
|
||||||
if overlen > underlen {
|
if overlen > underlen {
|
||||||
// break early, put this word on the next line
|
// break early, put this word on the next line
|
||||||
(true, args.indent_len + w.word_nchars)
|
(true, args.indent_len + w.word_nchars)
|
||||||
|
|
|
@ -33,7 +33,19 @@ fn test_fmt_width() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["one-word-per-line.txt", param, "10"])
|
.args(&["one-word-per-line.txt", param, "10"])
|
||||||
.succeeds()
|
.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