Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adaptive Theme Selection Based on the Terminal Color Scheme #12098

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

luca-schlecker
Copy link
Contributor

@luca-schlecker luca-schlecker commented Nov 20, 2024

Fixes #8899, #10281

Overview

Introduce theme = { light = "<name>", dark = "<name>" } syntax inside config.toml to allow adaptive theme selection based on the terminal color scheme.

Theme selection is based on terminal-colorsaurus which has also been used in delta and bat.

Considerations

  • If the theme detection fails, dark mode is chosen by default.
  • The new syntax using an untagged enum ensures existing configs remain valid.
  • Theme validation (ensuring the theme name exists) is only done for the detected theme. This would defer errors if a missing theme is specified but reduces code changes.
  • terminal-colorsaurus' code is only called in case an adaptive theme has been specified, guaranteeing practically zero overhead for static themes.

@cmoog
Copy link
Contributor

cmoog commented Nov 25, 2024

There seems to be a small bug when this runs via :config-reload

@kirawi kirawi added the A-helix-term Area: Helix term improvements label Nov 26, 2024
@luca-schlecker
Copy link
Contributor Author

This bug turns out to be a bit trickier. I've spent a few hours and couldn't come up with a clean solution. Crossterm and Colorsaurus are interfering with each other here because Colorsaurus, instead of querying the operating system, uses ANSI escape codes to get the terminal background color and determines the active color scheme that way. This approach has some benefits like allowing theme detection through an ssh session.

I believe the interference is happening as soon as the future from the async function input_stream.next() (Application::event_loop_until_idle) is still pending whilst trying to detect the color scheme.

I've pondered a bit and could think of the following workarounds:

  • Use dark-light to query the operating system instead (thus no terminal i/o required, but less flexible).
  • Use a Semaphore to prevent input_stream.next() from being called when trying to detect the color scheme. For this to be effective the Semaphore would have to be locked before this tokio::select!, increasing latency of config reloads as the select-call would have to finish first.
  • Tie theme detection directly to input events (Application::event_loop_until_idle):
Some(event) = input_stream.next() => {
    // <-- theme detection works here without interference
    self.handle_terminal_events(event).await;
    // <-- also here
}
  • Only determine the terminal color scheme once at startup and don't update it on config reload.

Let me know what your thoughts on this are.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-helix-term Area: Helix term improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Automatically select light/dark theme variant
3 participants