Refactor text for strict Clippy checks

This commit is contained in:
Dylan M. Taylor
2026-10-28 06:02:27 +00:00
parent f441ea93a2
commit 4de3a4dc8b
7 changed files with 826 additions and 619 deletions
+116 -100
View File
@@ -66,109 +66,16 @@ pub(crate) fn levels_lang(text: &str, chars: &[char], rtl: bool, rtl_lang: &[boo
if classes.iter().any(|c| matches!(c, LRE | RLE | LRO | RLO | PDF | LRI | RLI | FSI | PDI)) {
return levels(text, chars, rtl);
}
let n = chars.len();
let rl = |i: usize| rtl_lang.get(i).copied().unwrap_or(false);
let sos = if rtl { Dir::R } else { Dir::L };
// W1: combining marks take the class of the character they sit on.
let mut cls = classes;
for i in 0..n {
if cls.get(i) == Some(&NSM) {
let prev = if i == 0 { if rtl { R } else { L } } else { cls.get(i - 1).copied().unwrap_or(L) };
if let Some(c) = cls.get_mut(i) {
*c = prev;
}
}
}
let mut dir = vec![Dir::Open; n];
// Strong letters and digits.
let mut last_strong: Option<BidiClass> = None;
for i in 0..n {
let c = cls.get(i).copied().unwrap_or(ON);
let d = match c {
L => Dir::L,
R | AL => Dir::R,
AN => Dir::Num,
EN if !rl(i) => Dir::L,
// W7: European digits after left-to-right text are left to right.
EN if last_strong == Some(L) => Dir::L,
EN => Dir::Num,
_ => Dir::Open,
};
if matches!(c, L | R | AL) {
last_strong = Some(c);
}
if let Some(x) = dir.get_mut(i) {
*x = d;
}
}
// W4/W5 in right-to-left runs: a separator between digits, and currency or percent signs next
// to them, belong to the number. `after_et[i]` is the first index after `i` that is not ET,
// computed once so a long run of currency signs costs linear work, not a scan per sign.
let after_et = next_non_et(&cls);
for i in 0..n {
if !rl(i) || dir.get(i) != Some(&Dir::Open) {
continue;
}
let c = cls.get(i).copied().unwrap_or(ON);
let num = |j: Option<usize>| j.and_then(|j| dir.get(j)).is_some_and(|d| *d == Dir::Num);
let joined = match c {
ES | CS => num(i.checked_sub(1)) && num(Some(i + 1)),
ET => num(i.checked_sub(1)) || num(after_et.get(i).copied().flatten()),
_ => false,
};
if joined && let Some(x) = dir.get_mut(i) {
*x = Dir::Num;
}
}
let cls = resolve_nsm(classes, rtl);
let mut dir = initial_dirs(&cls, rtl_lang);
join_numbers_in_rtl_runs(&mut dir, &cls, rtl_lang);
// Punctuation in left-to-right runs is left to right.
for i in 0..n {
if !rl(i)
&& dir.get(i) == Some(&Dir::Open)
&& !matches!(cls.get(i), Some(WS | S))
&& let Some(x) = dir.get_mut(i)
{
*x = Dir::L;
}
}
// Neutrals in right-to-left runs: the direction around them when both sides agree, else
// right to left. Unresolved spaces of left-to-right runs are looked through.
let strong_at = |dir: &[Dir], j: usize| match dir.get(j) {
Some(Dir::L) => Some(Dir::L),
Some(Dir::R | Dir::Num) => Some(Dir::R),
_ => None,
};
// Resolving a span only changes characters before the scan position, so the nearest strong
// direction after a span can be computed once up front, and the one before it is carried along:
// linear work however many neutral spans the paragraph has.
let mut next_strong: Vec<Option<Dir>> = vec![None; n + 1];
for j in (0..n).rev() {
let here = strong_at(&dir, j).or(next_strong.get(j + 1).copied().flatten());
if let Some(x) = next_strong.get_mut(j) {
*x = here;
}
}
let mut last_strong: Option<Dir> = None;
let mut i = 0;
while i < n {
if !(rl(i) && dir.get(i) == Some(&Dir::Open)) {
last_strong = strong_at(&dir, i).or(last_strong);
i += 1;
continue;
}
let start = i;
while i < n && rl(i) && dir.get(i) == Some(&Dir::Open) {
i += 1;
}
let before = last_strong.unwrap_or(sos);
let after = next_strong.get(i).copied().flatten().unwrap_or(sos);
let d = if before == after { before } else { Dir::R };
for x in dir.get_mut(start..i).into_iter().flatten() {
*x = d;
}
if start < i {
last_strong = Some(if d == Dir::Num { Dir::R } else { d });
for (i, d) in dir.iter_mut().enumerate() {
if !in_rtl_lang(rtl_lang, i) && *d == Dir::Open && !matches!(cls.get(i), Some(WS | S)) {
*d = Dir::L;
}
}
resolve_rtl_neutrals(&mut dir, rtl_lang, if rtl { Dir::R } else { Dir::L });
// Spaces in left-to-right runs follow the character before them.
let mut prev = Dir::L;
for d in dir.iter_mut() {
@@ -187,6 +94,115 @@ pub(crate) fn levels_lang(text: &str, chars: &[char], rtl: bool, rtl_lang: &[boo
.collect()
}
fn in_rtl_lang(rtl_lang: &[bool], i: usize) -> bool {
rtl_lang.get(i).copied().unwrap_or(false)
}
/// W1: combining marks take the class of the character they sit on.
fn resolve_nsm(mut cls: Vec<BidiClass>, rtl: bool) -> Vec<BidiClass> {
for i in 0..cls.len() {
if cls.get(i) == Some(&NSM) {
let prev = if i == 0 { if rtl { R } else { L } } else { cls.get(i - 1).copied().unwrap_or(L) };
if let Some(c) = cls.get_mut(i) {
*c = prev;
}
}
}
cls
}
/// Strong letters and digits get a direction; everything else stays open.
fn initial_dirs(cls: &[BidiClass], rtl_lang: &[bool]) -> Vec<Dir> {
let mut last_strong: Option<BidiClass> = None;
cls.iter()
.enumerate()
.map(|(i, &c)| {
let d = match c {
L => Dir::L,
R | AL => Dir::R,
AN => Dir::Num,
EN if !in_rtl_lang(rtl_lang, i) => Dir::L,
// W7: European digits after left-to-right text are left to right.
EN if last_strong == Some(L) => Dir::L,
EN => Dir::Num,
_ => Dir::Open,
};
if matches!(c, L | R | AL) {
last_strong = Some(c);
}
d
})
.collect()
}
/// W4/W5 in right-to-left runs: a separator between digits, and currency or percent signs next
/// to them, belong to the number. `after_et[i]` is the first index after `i` that is not ET,
/// computed once so a long run of currency signs costs linear work, not a scan per sign.
fn join_numbers_in_rtl_runs(dir: &mut [Dir], cls: &[BidiClass], rtl_lang: &[bool]) {
let after_et = next_non_et(cls);
for i in 0..dir.len() {
if !in_rtl_lang(rtl_lang, i) || dir.get(i) != Some(&Dir::Open) {
continue;
}
let c = cls.get(i).copied().unwrap_or(ON);
let num = |j: Option<usize>| j.and_then(|j| dir.get(j)).is_some_and(|d| *d == Dir::Num);
let joined = match c {
ES | CS => num(i.checked_sub(1)) && num(Some(i + 1)),
ET => num(i.checked_sub(1)) || num(after_et.get(i).copied().flatten()),
_ => false,
};
if joined && let Some(x) = dir.get_mut(i) {
*x = Dir::Num;
}
}
}
fn strong_at(dir: &[Dir], j: usize) -> Option<Dir> {
match dir.get(j) {
Some(Dir::L) => Some(Dir::L),
Some(Dir::R | Dir::Num) => Some(Dir::R),
_ => None,
}
}
/// Neutrals in right-to-left runs: the direction around them when both sides agree, else right to
/// left. Unresolved spaces of left-to-right runs are looked through.
///
/// Resolving a span only changes characters before the scan position, so the nearest strong
/// direction after a span can be computed once up front, and the one before it is carried along:
/// linear work however many neutral spans the paragraph has.
fn resolve_rtl_neutrals(dir: &mut [Dir], rtl_lang: &[bool], sos: Dir) {
let n = dir.len();
let rl = |dir: &[Dir], i: usize| in_rtl_lang(rtl_lang, i) && dir.get(i) == Some(&Dir::Open);
let mut next_strong: Vec<Option<Dir>> = vec![None; n + 1];
for j in (0..n).rev() {
let here = strong_at(dir, j).or(next_strong.get(j + 1).copied().flatten());
if let Some(x) = next_strong.get_mut(j) {
*x = here;
}
}
let mut last_strong: Option<Dir> = None;
let mut i = 0;
while i < n {
if !rl(dir, i) {
last_strong = strong_at(dir, i).or(last_strong);
i += 1;
continue;
}
let start = i;
while i < n && rl(dir, i) {
i += 1;
}
let before = last_strong.unwrap_or(sos);
let after = next_strong.get(i).copied().flatten().unwrap_or(sos);
let d = if before == after { before } else { Dir::R };
for x in dir.get_mut(start..i).into_iter().flatten() {
*x = d;
}
last_strong = Some(if d == Dir::Num { Dir::R } else { d });
}
}
/// For each index, the first later index whose class is not ET (`None` when only ET follows).
fn next_non_et(cls: &[BidiClass]) -> Vec<Option<usize>> {
let mut out = vec![None; cls.len()];
+14 -11
View File
@@ -1,5 +1,5 @@
use super::style::{Cell, ParaShaped};
use super::*;
use super::{FontFace, ParaProps};
pub(crate) fn measure_str(face: &FontFace, size: f64, s: &str) -> (f64, Vec<(u32, f64)>) {
let g = deckcraft_fonts::shape(face, s, &[], |c| c);
@@ -132,16 +132,19 @@ pub(crate) struct RawLine {
next: usize,
}
pub(crate) fn break_lines(
p: &ParaShaped,
first_w: f64,
rest_w: f64,
wrap: bool,
tab_stops: &[Tab],
default_tab: f64,
x0_first: f64,
x0_rest: f64,
) -> Vec<RawLine> {
/// Wrapping geometry of one paragraph: line widths, start offsets, wrap flag and tab settings.
pub(crate) struct BreakParams<'a> {
pub(crate) first_w: f64,
pub(crate) rest_w: f64,
pub(crate) wrap: bool,
pub(crate) tab_stops: &'a [Tab],
pub(crate) default_tab: f64,
pub(crate) x0_first: f64,
pub(crate) x0_rest: f64,
}
pub(crate) fn break_lines(p: &ParaShaped, params: &BreakParams) -> Vec<RawLine> {
let BreakParams { first_w, rest_w, wrap, tab_stops, default_tab, x0_first, x0_rest } = *params;
let text: String = p.cells.iter().map(|c| c.ch).collect();
let (allowed, _) = break_opportunities(&text, p.props.latin_line_break.unwrap_or(false));
let hang = p.props.hanging_punct.unwrap_or(false);
+689 -504
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Arc, FontFace, Point, Rect, Rgba};
/// A run of glyphs in one face, size and colour.
#[derive(Clone)]
+4 -1
View File
@@ -2,7 +2,10 @@ use super::autonum::autonum;
use super::run::{RunEffects, TextShadow};
use super::style::{Cell, Opts, ParaShaped, Style, face_chain, make_style};
use super::symbol::{covers_symbol, map_symbol_bullet, symbol_font_char, symbol_map_char};
use super::*;
use super::{
Arc, Bullet, Caps, Ctx, Fill, FontDb, FontFace, FontSlot, HashMap, ParaProps, RunKind, RunProps, ScriptTag, Shape, ShapeParams, TextBody,
UnicodeSegmentation, bidi, resolve, script,
};
#[allow(clippy::too_many_arguments)]
pub(crate) fn shape_para(
+1 -1
View File
@@ -1,6 +1,6 @@
use super::run::{RunEffects, Underline};
use super::shape::run_effects;
use super::*;
use super::{Arc, Caps, Ctx, Fill, FontDb, FontFace, FontSlot, ParaProps, Rect, Rgba, RunProps, ScriptTag, Spacing, Strike, resolve};
/// Values for fields (`slidenum`, `datetime…`, `footer`). `None` keeps the field's saved text.
pub trait Fields {
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{FontFace, UnicodeSegmentation};
/// Is `family` a symbol font (characters at U+F000 + code)?
pub(crate) fn is_symbol_font(family: &str) -> bool {