Skip to content
Open
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
12 changes: 12 additions & 0 deletions helix-loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ static RUNTIME_DIRS: once_cell::sync::Lazy<Vec<PathBuf>> =

static CONFIG_FILE: once_cell::sync::OnceCell<PathBuf> = once_cell::sync::OnceCell::new();

static THEME_FILE: once_cell::sync::OnceCell<PathBuf> = once_cell::sync::OnceCell::new();

static LOG_FILE: once_cell::sync::OnceCell<PathBuf> = once_cell::sync::OnceCell::new();

pub fn initialize_config_file(specified_file: Option<PathBuf>) {
Expand All @@ -21,6 +23,12 @@ pub fn initialize_config_file(specified_file: Option<PathBuf>) {
CONFIG_FILE.set(config_file).ok();
}

pub fn initialize_theme_file(specified_file: Option<PathBuf>) {
if let Some(theme_file) = specified_file {
THEME_FILE.set(theme_file).ok();
}
}

pub fn initialize_log_file(specified_file: Option<PathBuf>) {
let log_file = specified_file.unwrap_or_else(default_log_file);
ensure_parent_dir(&log_file);
Expand Down Expand Up @@ -136,6 +144,10 @@ pub fn config_file() -> PathBuf {
CONFIG_FILE.get().map(|path| path.to_path_buf()).unwrap()
}

pub fn theme_file() -> Option<PathBuf> {
THEME_FILE.get().map(|path| path.to_path_buf())
}

pub fn log_file() -> PathBuf {
LOG_FILE.get().map(|path| path.to_path_buf()).unwrap()
}
Expand Down
77 changes: 51 additions & 26 deletions helix-term/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,32 +462,57 @@ impl Application {
mode: Option<theme::Mode>,
) {
let true_color = terminal_true_color || config.editor.true_color || crate::true_color();
let theme = config
.theme
.as_ref()
.and_then(|theme_config| {
let theme = theme_config.choose(mode);
editor
.theme_loader
.load(theme)
.map_err(|e| {
log::warn!("failed to load theme `{}` - {}", theme, e);
e
})
.ok()
.filter(|theme| {
let colors_ok = true_color || theme.is_16_color();
if !colors_ok {
log::warn!(
"loaded theme `{}` but cannot use it because true color \
support is not enabled",
theme.name()
);
}
colors_ok
})
})
.unwrap_or_else(|| editor.theme_loader.default_theme(true_color));

// Check if a theme file was specified via --theme flag
let theme = if let Some(theme_file) = helix_loader::theme_file() {
editor
.theme_loader
.load_from_file(&theme_file)
.map_err(|e| {
log::warn!("failed to load theme from file `{}` - {}", theme_file.display(), e);
e
})
.ok()
.filter(|theme| {
let colors_ok = true_color || theme.is_16_color();
if !colors_ok {
log::warn!(
"loaded theme from file `{}` but cannot use it because true color \
support is not enabled",
theme_file.display()
);
}
colors_ok
})
} else {
config
.theme
.as_ref()
.and_then(|theme_config| {
let theme = theme_config.choose(mode);
editor
.theme_loader
.load(theme)
.map_err(|e| {
log::warn!("failed to load theme `{}` - {}", theme, e);
e
})
.ok()
.filter(|theme| {
let colors_ok = true_color || theme.is_16_color();
if !colors_ok {
log::warn!(
"loaded theme `{}` but cannot use it because true color \
support is not enabled",
theme.name()
);
}
colors_ok
})
})
}
.unwrap_or_else(|| editor.theme_loader.default_theme(true_color));

editor.set_theme(theme);
}

Expand Down
5 changes: 5 additions & 0 deletions helix-term/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Args {
pub verbosity: u64,
pub log_file: Option<PathBuf>,
pub config_file: Option<PathBuf>,
pub theme_file: Option<PathBuf>,
pub files: IndexMap<PathBuf, Vec<Position>>,
pub working_directory: Option<PathBuf>,
}
Expand Down Expand Up @@ -70,6 +71,10 @@ impl Args {
Some(path) => args.config_file = Some(path.into()),
None => anyhow::bail!("--config must specify a path to read"),
},
"-t" | "--theme" => match argv.next().as_deref() {
Some(path) => args.theme_file = Some(path.into()),
None => anyhow::bail!("--theme must specify a path to read"),
},
"--log" => match argv.next().as_deref() {
Some(path) => args.log_file = Some(path.into()),
None => anyhow::bail!("--log must specify a path to write"),
Expand Down
2 changes: 2 additions & 0 deletions helix-term/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ async fn main_impl() -> Result<i32> {
let args = Args::parse_args().context("could not parse arguments")?;

helix_loader::initialize_config_file(args.config_file.clone());
helix_loader::initialize_theme_file(args.theme_file.clone());
helix_loader::initialize_log_file(args.log_file.clone());

// Help has a higher priority and should be handled separately.
Expand All @@ -68,6 +69,7 @@ FLAGS:
the default is the same as 'all', but with languages filtering.
-g, --grammar {{fetch|build}} Fetch or builds tree-sitter grammars listed in languages.toml
-c, --config <file> Specify a file to use for configuration
-t, --theme <file> Specify a theme file to use
-v Increase logging verbosity each use for up to 3 times
--log <file> Specify a file to use for logging
(default file: {})
Expand Down
53 changes: 53 additions & 0 deletions helix-view/src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,59 @@ impl Loader {
Ok(theme)
}

/// Loads a theme from a direct file path.
pub fn load_from_file(&self, path: &std::path::Path) -> Result<Theme> {
let (theme, warnings) = self.load_from_file_with_warnings(path)?;

for warning in &warnings {
warn!("Theme '{}': {}", path.display(), warning);
}

Ok(theme)
}

/// Loads a theme from a direct file path, returning any warnings
pub fn load_from_file_with_warnings(
&self,
path: &std::path::Path,
) -> Result<(Theme, Vec<String>)> {
let mut visited_paths = HashSet::new();
visited_paths.insert(path.to_path_buf());

let theme_toml = self.load_toml(path.to_path_buf())?;
let inherits = theme_toml.get("inherits");

let theme_toml = if let Some(parent_theme_name) = inherits {
let parent_theme_name = parent_theme_name.as_str().ok_or_else(|| {
anyhow!("Expected 'inherits' to be a string: {}", parent_theme_name)
})?;

let parent_theme_toml = match parent_theme_name {
"default" => DEFAULT_THEME_DATA.clone(),
"base16_default" => BASE16_DEFAULT_THEME_DATA.clone(),
_ => self.load_theme(parent_theme_name, &mut visited_paths)?,
};

self.merge_themes(parent_theme_toml, theme_toml)
} else {
theme_toml
};

let (theme, warnings) = Theme::from_toml(theme_toml);

let theme_name = path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("unknown");

let theme = Theme {
name: theme_name.into(),
..theme
};

Ok((theme, warnings))
}

/// Loads a theme searching directories in priority order, returning any warnings
pub fn load_with_warnings(&self, name: &str) -> Result<(Theme, Vec<String>)> {
if name == "default" {
Expand Down