feat(tui): harden theme lock recovery and ordering

This commit is contained in:
Felipe Coury
2026-02-11 00:48:00 -03:00
parent 18d62ac80f
commit 44a7ba0dda
2 changed files with 34 additions and 3 deletions

View File

@@ -665,6 +665,8 @@ fn wrap_styled_spans(spans: &[RtSpan<'static>], max_cols: usize) -> Vec<Vec<RtSp
};
let ch_len = ch.len_utf8();
current_line.push(RtSpan::styled(remaining[..ch_len].to_string(), style));
// Use fallback width 1 (not 0) so this branch always advances
// even if `ch` has unknown/zero display width.
col = ch.width().unwrap_or(if ch == '\t' { TAB_WIDTH } else { 1 });
remaining = &remaining[ch_len..];
continue;

View File

@@ -226,9 +226,11 @@ fn theme_lock() -> &'static RwLock<Theme> {
/// Swap the active syntax theme at runtime (for live preview).
pub(crate) fn set_syntax_theme(theme: Theme) {
if let Ok(mut guard) = theme_lock().write() {
*guard = theme;
}
let mut guard = match theme_lock().write() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
};
*guard = theme;
}
/// Clone the current syntax theme (e.g. to save for cancel-restore).
@@ -317,6 +319,9 @@ pub(crate) fn list_available_themes(codex_home: Option<&Path>) -> Vec<ThemeEntry
}
}
// Keep picker ordering stable across platforms/filesystems.
entries.sort_by(|a, b| (a.is_custom, a.name.as_str()).cmp(&(b.is_custom, b.name.as_str())));
entries
}
@@ -999,6 +1004,30 @@ mod tests {
);
}
#[test]
fn list_available_themes_returns_stable_sorted_order() {
let dir = tempfile::tempdir().unwrap();
let themes_dir = dir.path().join("themes");
std::fs::create_dir(&themes_dir).unwrap();
write_minimal_tmtheme(&themes_dir.join("zzz-custom.tmTheme"));
write_minimal_tmtheme(&themes_dir.join("aaa-custom.tmTheme"));
write_minimal_tmtheme(&themes_dir.join("mmm-custom.tmTheme"));
let entries = list_available_themes(Some(dir.path()));
let actual: Vec<(bool, String)> = entries
.iter()
.map(|entry| (entry.is_custom, entry.name.clone()))
.collect();
let mut expected = actual.clone();
expected.sort_by(|a, b| (a.0, a.1.as_str()).cmp(&(b.0, b.1.as_str())));
assert_eq!(
actual, expected,
"theme entries should be stable and sorted (builtins first, then custom by name)"
);
}
#[test]
fn parse_theme_name_is_exhaustive() {
use two_face::theme::EmbeddedLazyThemeSet;