use std::time::Duration;
use crate::widgets::chat::mouse::TerminalKind;
pub const DEFAULT_FLUSH_GAP: Duration = Duration::from_millis(80);
const EPT_MIN: u8 = 1;
const EPT_MAX: u8 = 10;
pub fn ept_override() -> Option<u8> {
let s = std::env::var("OXI_SCROLL_EPT").ok()?;
let n: u8 = s.parse().ok()?;
if (EPT_MIN..=EPT_MAX).contains(&n) {
Some(n)
} else {
None
}
}
pub fn effective_ept(kind: TerminalKind) -> u8 {
ept_override().unwrap_or_else(|| built_in_ept(kind))
}
pub fn built_in_ept(kind: TerminalKind) -> u8 {
match kind {
TerminalKind::AppleTerminal | TerminalKind::Ghostty => 3,
TerminalKind::ITerm2
| TerminalKind::VSCode
| TerminalKind::Multiplexer
| TerminalKind::Unknown => 1,
}
}
pub fn flush_gap_override() -> Option<Duration> {
let s = std::env::var("OXI_SCROLL_FLUSH_MS").ok()?;
let ms: u64 = s.parse().ok()?;
if ms == 0 || ms > 5000 {
None
} else {
Some(Duration::from_millis(ms))
}
}
pub fn effective_flush_gap() -> Duration {
flush_gap_override().unwrap_or(DEFAULT_FLUSH_GAP)
}
#[cfg(test)]
mod tests {
use super::*;
static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
fn with_env_lock<R>(f: impl FnOnce() -> R) -> R {
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
f()
}
#[test]
fn ept_override_unset_returns_none() {
with_env_lock(|| {
unsafe {
std::env::remove_var("OXI_SCROLL_EPT");
}
assert_eq!(ept_override(), None);
});
}
#[test]
fn ept_override_valid_values() {
with_env_lock(|| {
for v in 1..=10u8 {
unsafe {
std::env::set_var("OXI_SCROLL_EPT", v.to_string());
}
assert_eq!(ept_override(), Some(v));
}
unsafe {
std::env::remove_var("OXI_SCROLL_EPT");
}
});
}
#[test]
fn ept_override_out_of_range_returns_none() {
with_env_lock(|| {
unsafe {
std::env::set_var("OXI_SCROLL_EPT", "0");
}
assert_eq!(ept_override(), None);
unsafe {
std::env::set_var("OXI_SCROLL_EPT", "11");
}
assert_eq!(ept_override(), None);
unsafe {
std::env::set_var("OXI_SCROLL_EPT", "100");
}
assert_eq!(ept_override(), None);
unsafe {
std::env::remove_var("OXI_SCROLL_EPT");
}
});
}
#[test]
fn ept_override_non_numeric_returns_none() {
with_env_lock(|| {
unsafe {
std::env::set_var("OXI_SCROLL_EPT", "abc");
}
assert_eq!(ept_override(), None);
unsafe {
std::env::remove_var("OXI_SCROLL_EPT");
}
});
}
#[test]
fn effective_ept_uses_override_when_present() {
with_env_lock(|| {
unsafe {
std::env::set_var("OXI_SCROLL_EPT", "7");
}
assert_eq!(effective_ept(TerminalKind::AppleTerminal), 7);
unsafe {
std::env::remove_var("OXI_SCROLL_EPT");
}
});
}
#[test]
fn effective_ept_falls_back_to_table() {
with_env_lock(|| {
unsafe {
std::env::remove_var("OXI_SCROLL_EPT");
}
assert_eq!(effective_ept(TerminalKind::AppleTerminal), 3);
assert_eq!(effective_ept(TerminalKind::Ghostty), 3);
assert_eq!(effective_ept(TerminalKind::ITerm2), 1);
assert_eq!(effective_ept(TerminalKind::VSCode), 1);
assert_eq!(effective_ept(TerminalKind::Multiplexer), 1);
assert_eq!(effective_ept(TerminalKind::Unknown), 1);
});
}
#[test]
fn built_in_ept_matches_terminal_kind_table() {
assert_eq!(built_in_ept(TerminalKind::AppleTerminal), 3);
assert_eq!(built_in_ept(TerminalKind::Ghostty), 3);
assert_eq!(built_in_ept(TerminalKind::ITerm2), 1);
assert_eq!(built_in_ept(TerminalKind::VSCode), 1);
assert_eq!(built_in_ept(TerminalKind::Multiplexer), 1);
assert_eq!(built_in_ept(TerminalKind::Unknown), 1);
}
#[test]
fn flush_gap_override_valid() {
with_env_lock(|| {
unsafe {
std::env::set_var("OXI_SCROLL_FLUSH_MS", "120");
}
assert_eq!(flush_gap_override(), Some(Duration::from_millis(120)));
unsafe {
std::env::remove_var("OXI_SCROLL_FLUSH_MS");
}
});
}
#[test]
fn flush_gap_override_zero_or_huge_returns_none() {
with_env_lock(|| {
unsafe {
std::env::set_var("OXI_SCROLL_FLUSH_MS", "0");
}
assert_eq!(flush_gap_override(), None);
unsafe {
std::env::set_var("OXI_SCROLL_FLUSH_MS", "60000"); }
assert_eq!(flush_gap_override(), None);
unsafe {
std::env::remove_var("OXI_SCROLL_FLUSH_MS");
}
});
}
#[test]
fn effective_flush_gap_default_is_80ms() {
with_env_lock(|| {
unsafe {
std::env::remove_var("OXI_SCROLL_FLUSH_MS");
}
assert_eq!(effective_flush_gap(), Duration::from_millis(80));
});
}
#[test]
fn effective_flush_gap_honors_override() {
with_env_lock(|| {
unsafe {
std::env::set_var("OXI_SCROLL_FLUSH_MS", "200");
}
assert_eq!(effective_flush_gap(), Duration::from_millis(200));
unsafe {
std::env::remove_var("OXI_SCROLL_FLUSH_MS");
}
});
}
}