CP6.13: craft-libs#495 review — Windows drives are probed for LibreOffice once per process; sr@latin is sr-latn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
This commit is contained in:
Martin BallandClaude Opus 5.5 committed 2026-11-02 01:06:58 +00:00
1 parent c5cc9c4b7a
commit 89fa1559e9
2 files changed
+34 -16

No files matched your search

+28 -16
View File
@@ -105,7 +105,7 @@ fn search_tiers(lo_exts: Option<&[String]>) -> (Vec<PathBuf>, Vec<PathBuf>) {
system.extend(lo_dirs(&base.join("LibreOffice").join("share").join("extensions").to_string_lossy()));
}
}
system.extend(lo_drive_dirs(lo_exts, Path::is_dir));
system.extend(lo_drive_dirs(lo_exts, || LO_DRIVE_BASES.get_or_init(|| lo_drive_bases(Path::is_dir))));
} else if cfg!(target_os = "macos") {
if let Some(h) = &home {
// An app started from Finder or the Dock doesn't see `WORDCRAFT_DICTIONARIES`; this
@@ -136,22 +136,25 @@ fn search_tiers(lo_exts: Option<&[String]>) -> (Vec<PathBuf>, Vec<PathBuf>) {
}
/// LibreOffice's language folders (`<drive>:\Program Files\LibreOffice\share\extensions\<ext>`)
/// on the drives C to Z where `is_dir` finds LibreOffice installed. No drive is probed without
/// on the drives C to Z where LibreOffice is installed (`bases`). No drive is probed without
/// `lo_exts` (no LibreOffice folders are wanted then).
fn lo_drive_dirs(lo_exts: Option<&[String]>, is_dir: impl Fn(&Path) -> bool) -> Vec<PathBuf> {
fn lo_drive_dirs<'a>(lo_exts: Option<&[String]>, bases: impl FnOnce() -> &'a [PathBuf]) -> Vec<PathBuf> {
let Some(exts) = lo_exts else { return Vec::new() };
let mut out = Vec::new();
for drive in 'C'..='Z' {
for pf in ["Program Files", "Program Files (x86)"] {
let base = PathBuf::from(format!(r"{drive}:\{pf}\LibreOffice\share\extensions"));
if is_dir(&base) {
out.extend(exts.iter().map(|e| base.join(e)));
}
}
}
out
bases().iter().flat_map(|base| exts.iter().map(|e| base.join(e))).collect()
}
/// The `LibreOffice\share\extensions` folders on drives C to Z (48 probes).
fn lo_drive_bases(is_dir: impl Fn(&Path) -> bool) -> Vec<PathBuf> {
('C'..='Z')
.flat_map(|drive| ["Program Files", "Program Files (x86)"].map(|pf| PathBuf::from(format!(r"{drive}:\{pf}\LibreOffice\share\extensions"))))
.filter(|base| is_dir(base))
.collect()
}
/// [`lo_drive_bases`], probed once per process.
// ponytail: a LibreOffice installed while the app runs is seen after a restart.
static LO_DRIVE_BASES: std::sync::OnceLock<Vec<PathBuf>> = std::sync::OnceLock::new();
/// The user's data folder on Linux and the BSDs: `$XDG_DATA_HOME` when it is an absolute path (the
/// XDG Base Directory spec ignores a relative one), else `~/.local/share`.
fn xdg_data_home(xdg: Option<std::ffi::OsString>, home: Option<&Path>) -> Option<PathBuf> {
@@ -627,11 +630,20 @@ mod tests {
probes.set(probes.get() + 1);
false
};
assert!(lo_drive_dirs(None, count).is_empty());
// craft-libs#495 review: the drives are probed once, not on every lookup.
let cache = std::sync::OnceLock::new();
let cached = || cache.get_or_init(|| lo_drive_bases(count)).as_slice();
assert!(lo_drive_dirs(None, cached).is_empty());
assert_eq!(probes.get(), 0);
let exts = ["dict-tr".to_string()];
let found = lo_drive_dirs(Some(&exts), |p: &Path| p.to_string_lossy().starts_with(r"D:\Program Files\"));
assert_eq!(found, [PathBuf::from(r"D:\Program Files\LibreOffice\share\extensions").join("dict-tr")]);
assert!(lo_drive_dirs(Some(&exts), cached).is_empty());
assert!(lo_drive_dirs(Some(&exts), cached).is_empty());
assert_eq!(probes.get(), 48);
let found = lo_drive_bases(|p: &Path| p.to_string_lossy().starts_with(r"D:\Program Files\"));
assert_eq!(
lo_drive_dirs(Some(&exts), || found.as_slice()),
[PathBuf::from(r"D:\Program Files\LibreOffice\share\extensions").join("dict-tr")]
);
}
/// Another region's Language pack never beats the system's dictionary of the right region
+6
View File
@@ -147,6 +147,9 @@ impl ProofLang {
} else {
ProofLang::De
}
} else if tag.split_once('@').is_some_and(|(_, m)| m.eq_ignore_ascii_case("latin")) {
// POSIX `sr_RS@latin`: the Latin-script language (`sr-latn`).
PackLocale::new(&format!("{primary}-latn")).map_or(ProofLang::Other, ProofLang::Pack)
} else {
PackLocale::new(tag).map_or(ProofLang::Other, ProofLang::Pack)
}
@@ -446,6 +449,9 @@ mod tests {
// craft-libs#482 review: a POSIX charset or modifier with no region.
assert_tags(&["en.UTF-8", "en@euro"], ProofLang::En);
assert_tags(&["de.UTF-8"], ProofLang::De);
let sr_latn = ProofLang::Pack(PackLocale::new("sr-latn").unwrap());
assert_tags(&["sr@latin", "sr_RS@latin", "sr_RS.UTF-8@latin", "sr-Latn"], sr_latn);
assert_tags(&["sr", "sr@cyrillic"], ProofLang::Pack(PackLocale::new("sr").unwrap()));
assert_tags(&["pt", "pt-BR", "PT_br", "pt-BR.UTF-8", "pt-AO", "por"], ProofLang::Pt);
let pt_pt = ProofLang::Pack(PackLocale::new("pt-pt").unwrap());
assert_tags(&[" pt-PT ", "pt_PT.UTF-8", "por-PT"], pt_pt);