Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions crates/forge_display/src/code.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::sync::Arc;
use std::sync::{Arc, OnceLock};
use std::time::Duration;

use syntect::easy::HighlightLines;
use syntect::highlighting::ThemeSet;
Expand All @@ -7,6 +8,12 @@ use syntect::util::as_24_bit_terminal_escaped;
use terminal_colorsaurus::{QueryOptions, ThemeMode, theme_mode};
use two_face::theme::EmbeddedThemeName;

/// Maximum time to wait for a terminal color query response.
const THEME_DETECT_TIMEOUT: Duration = Duration::from_millis(100);

/// Process-wide cache for whether the terminal uses a dark background.
static IS_DARK_THEME: OnceLock<bool> = OnceLock::new();

/// Loads and caches syntax highlighting resources.
#[derive(Clone)]
pub struct SyntaxHighlighter {
Expand All @@ -25,12 +32,19 @@ impl Default for SyntaxHighlighter {
}

impl SyntaxHighlighter {
/// Detects whether the terminal is using a dark or light background.
/// Detects whether the terminal is using a dark or light background,
/// querying the terminal at most once per process lifetime. Subsequent
/// calls return the cached result. Falls back to dark mode on timeout or
/// if the terminal does not support color queries.
fn is_dark_theme() -> bool {
match theme_mode(QueryOptions::default()) {
Ok(ThemeMode::Light) => false,
Ok(ThemeMode::Dark) | Err(_) => true,
}
*IS_DARK_THEME.get_or_init(|| {
let mut opts = QueryOptions::default();
opts.timeout = THEME_DETECT_TIMEOUT;
match theme_mode(opts) {
Ok(ThemeMode::Light) => false,
Ok(ThemeMode::Dark) | Err(_) => true,
}
})
}

/// Syntax-highlights `code` for the given language token (e.g. `"toml"`,
Expand Down
27 changes: 21 additions & 6 deletions crates/forge_markdown_stream/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Utility functions for the markdown renderer.

use std::sync::OnceLock;
use std::time::Duration;

/// Terminal theme mode (dark or light).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ThemeMode {
Expand All @@ -9,12 +12,24 @@ pub enum ThemeMode {
Light,
}

/// Detects the terminal theme mode (dark or light).
/// Maximum time to wait for a terminal color query response.
const THEME_DETECT_TIMEOUT: Duration = Duration::from_millis(100);

/// Process-wide cache for the detected terminal theme mode.
static THEME_MODE: OnceLock<ThemeMode> = OnceLock::new();

/// Detects the terminal theme mode (dark or light), querying the terminal at
/// most once per process lifetime. Subsequent calls return the cached result.
/// Falls back to dark mode if the terminal does not respond within the timeout.
pub fn detect_theme_mode() -> ThemeMode {
use terminal_colorsaurus::{QueryOptions, ThemeMode as ColorsaurusThemeMode, theme_mode};
*THEME_MODE.get_or_init(|| {
use terminal_colorsaurus::{QueryOptions, ThemeMode as ColorsaurusThemeMode, theme_mode};

match theme_mode(QueryOptions::default()) {
Ok(ColorsaurusThemeMode::Light) => ThemeMode::Light,
Ok(ColorsaurusThemeMode::Dark) | Err(_) => ThemeMode::Dark,
}
let mut opts = QueryOptions::default();
opts.timeout = THEME_DETECT_TIMEOUT;
match theme_mode(opts) {
Ok(ColorsaurusThemeMode::Light) => ThemeMode::Light,
Ok(ColorsaurusThemeMode::Dark) | Err(_) => ThemeMode::Dark,
}
})
}
Loading