Load alt default theme if true color is not supported

* Move `runtime/themes/base16_default_terminal.toml` to
  `base16_theme.toml` alongside `theme.toml`
* Use `terminfo` crate to detect whether the terminal supports true
  color and, if the user has no theme configured and their terminal does
  not support true color, load the alt default theme instead of the
  normal default.

Remove `terminfo` dependency, use `COLORTERM` env instead

Prevent user from switching to an unsupported theme

Add `true-color-override` option

If the terminal is wrongly detected to not support true color,
`true-color-override = true` will override the detection.

Rename `true-color-override` to `true-color`
This commit is contained in:
Omnikar 2021-11-14 07:26:48 -05:00 committed by Blaž Hrastnik
parent 43d17c482c
commit 98ce2a301d
8 changed files with 62 additions and 20 deletions

View file

@ -23,6 +23,7 @@ To override global configuration parameters, create a `config.toml` file located
| `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | `400` | | `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | `400` |
| `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` | | `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` |
| `auto-info` | Whether to display infoboxes | `true` | | `auto-info` | Whether to display infoboxes | `true` |
| `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. | `false` |
`[editor.filepicker]` section of the config. Sets options for file picker and global search. All but the last key listed in the default file-picker configuration below are IgnoreOptions: whether hidden files and files listed within ignore files are ignored by (not visible in) the helix file picker and global search. There is also one other key, `max-depth` available, which is not defined by default. `[editor.filepicker]` section of the config. Sets options for file picker and global search. All but the last key listed in the default file-picker configuration below are IgnoreOptions: whether hidden files and files listed within ignore files are ignored by (not visible in) the helix file picker and global search. There is also one other key, `max-depth` available, which is not defined by default.

View file

@ -76,17 +76,27 @@ impl Application {
None => Ok(def_lang_conf), None => Ok(def_lang_conf),
}; };
let theme = if let Some(theme) = &config.theme { let true_color = config.editor.true_color || crate::true_color();
match theme_loader.load(theme) { let theme = config
Ok(theme) => theme, .theme
Err(e) => { .as_ref()
log::warn!("failed to load theme `{}` - {}", theme, e); .and_then(|theme| {
theme_loader
.load(theme)
.map_err(|e| {
log::warn!("failed to load theme `{}` - {}", theme, e);
e
})
.ok()
.filter(|theme| (true_color || theme.is_16_color()))
})
.unwrap_or_else(|| {
if true_color {
theme_loader.default() theme_loader.default()
} else {
theme_loader.base16_default()
} }
} });
} else {
theme_loader.default()
};
let syn_loader_conf: helix_core::syntax::Configuration = lang_conf let syn_loader_conf: helix_core::syntax::Configuration = lang_conf
.and_then(|conf| conf.try_into()) .and_then(|conf| conf.try_into())

View file

@ -2366,8 +2366,18 @@ pub mod cmd {
args: &[Cow<str>], args: &[Cow<str>],
_event: PromptEvent, _event: PromptEvent,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let theme = args.first().context("theme not provided")?; let theme = args.first().context("Theme not provided")?;
cx.editor.set_theme_from_name(theme) let theme = cx
.editor
.theme_loader
.load(theme)
.with_context(|| format!("Failed setting theme {}", theme))?;
let true_color = cx.editor.config.true_color || crate::true_color();
if !(true_color || theme.is_16_color()) {
bail!("Unsupported theme: theme requires true color support");
}
cx.editor.set_theme(theme);
Ok(())
} }
fn yank_main_selection_to_clipboard( fn yank_main_selection_to_clipboard(

View file

@ -9,3 +9,9 @@ pub mod config;
pub mod job; pub mod job;
pub mod keymap; pub mod keymap;
pub mod ui; pub mod ui;
fn true_color() -> bool {
std::env::var("COLORTERM")
.map(|v| matches!(v.as_str(), "truecolor" | "24bit"))
.unwrap_or(false)
}

View file

@ -186,6 +186,7 @@ pub mod completers {
&helix_core::config_dir().join("themes"), &helix_core::config_dir().join("themes"),
)); ));
names.push("default".into()); names.push("default".into());
names.push("base16_default".into());
let mut names: Vec<_> = names let mut names: Vec<_> = names
.into_iter() .into_iter()

View file

@ -105,6 +105,8 @@ pub struct Config {
/// Whether to display infoboxes. Defaults to true. /// Whether to display infoboxes. Defaults to true.
pub auto_info: bool, pub auto_info: bool,
pub file_picker: FilePickerConfig, pub file_picker: FilePickerConfig,
/// Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. Defaults to `false`.
pub true_color: bool,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
@ -137,6 +139,7 @@ impl Default for Config {
completion_trigger_len: 2, completion_trigger_len: 2,
auto_info: true, auto_info: true,
file_picker: FilePickerConfig::default(), file_picker: FilePickerConfig::default(),
true_color: false,
} }
} }
} }
@ -265,15 +268,6 @@ impl Editor {
self._refresh(); self._refresh();
} }
pub fn set_theme_from_name(&mut self, theme: &str) -> anyhow::Result<()> {
let theme = self
.theme_loader
.load(theme.as_ref())
.with_context(|| format!("failed setting theme `{}`", theme))?;
self.set_theme(theme);
Ok(())
}
/// Refreshes the language server for a given document /// Refreshes the language server for a given document
pub fn refresh_language_server(&mut self, doc_id: DocumentId) -> Option<()> { pub fn refresh_language_server(&mut self, doc_id: DocumentId) -> Option<()> {
let doc = self.documents.get_mut(&doc_id)?; let doc = self.documents.get_mut(&doc_id)?;

View file

@ -15,6 +15,10 @@ pub use crate::graphics::{Color, Modifier, Style};
pub static DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| { pub static DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| {
toml::from_slice(include_bytes!("../../theme.toml")).expect("Failed to parse default theme") toml::from_slice(include_bytes!("../../theme.toml")).expect("Failed to parse default theme")
}); });
pub static BASE16_DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| {
toml::from_slice(include_bytes!("../../base16_theme.toml"))
.expect("Failed to parse base 16 default theme")
});
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Loader { pub struct Loader {
@ -35,6 +39,9 @@ impl Loader {
if name == "default" { if name == "default" {
return Ok(self.default()); return Ok(self.default());
} }
if name == "base16_default" {
return Ok(self.base16_default());
}
let filename = format!("{}.toml", name); let filename = format!("{}.toml", name);
let user_path = self.user_dir.join(&filename); let user_path = self.user_dir.join(&filename);
@ -74,6 +81,11 @@ impl Loader {
pub fn default(&self) -> Theme { pub fn default(&self) -> Theme {
DEFAULT_THEME.clone() DEFAULT_THEME.clone()
} }
/// Returns the alternative 16-color default theme
pub fn base16_default(&self) -> Theme {
BASE16_DEFAULT_THEME.clone()
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -154,6 +166,14 @@ impl Theme {
pub fn find_scope_index(&self, scope: &str) -> Option<usize> { pub fn find_scope_index(&self, scope: &str) -> Option<usize> {
self.scopes().iter().position(|s| s == scope) self.scopes().iter().position(|s| s == scope)
} }
pub fn is_16_color(&self) -> bool {
self.styles.iter().all(|(_, style)| {
[style.fg, style.bg]
.into_iter()
.all(|color| !matches!(color, Some(Color::Rgb(..))))
})
}
} }
struct ThemePalette { struct ThemePalette {