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
19 changes: 19 additions & 0 deletions src/app/app_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ use {
verb::*,
},
crokey::crossterm::tty::IsTty,
syntect::highlighting::{
Theme,
ThemeSet,
},
std::{
convert::{
TryFrom,
Expand Down Expand Up @@ -91,6 +95,10 @@ pub struct AppContext {
/// the syntect theme to use for text files previewing
pub syntax_theme: Option<SyntaxTheme>,

/// a theme loaded from a .tmTheme file, used when syntax_theme is None,
/// which it is until a theme is chosen with :set_syntax_theme
pub custom_syntax_theme: Option<Theme>,

/// precomputed status to display in standard cases
/// (ie when no verb is involved)
pub standard_status: StandardStatus,
Expand Down Expand Up @@ -261,6 +269,16 @@ impl AppContext {
}
let reset_terminal_title_on_exit = config.reset_terminal_title_on_exit.unwrap_or(false);
let preview_transformers = PreviewTransformers::new(&config.preview_transformers)?;
let custom_syntax_theme = config
.syntax_theme_file
.as_ref()
.map(|path| {
ThemeSet::get_theme(path).map_err(|e| ConfError::InvalidSyntaxThemeFile {
path: path.display().to_string(),
details: e.to_string(),
})
})
.transpose()?;
let layout_instructions = config.layout_instructions.clone().unwrap_or_default();
let kept_kitty_temp_files = config.kept_kitty_temp_files.unwrap_or(
#[expect(clippy::missing_panics_doc, reason = "infallible")]
Expand All @@ -284,6 +302,7 @@ impl AppContext {
preview_overflow: config.wrap_previews.map(Overflow::from_wrap_bool).unwrap_or_default(),
ext_colors,
syntax_theme: config.syntax_theme,
custom_syntax_theme,
standard_status,
true_colors,
icons,
Expand Down
21 changes: 20 additions & 1 deletion src/conf/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ pub struct Conf {
#[serde(alias = "syntax-theme")]
pub syntax_theme: Option<SyntaxTheme>,

/// path to a syntect compatible .tmTheme file, used for previews
/// instead of syntax_theme. An empty path unsets it.
#[serde(alias = "syntax-theme-file")]
pub syntax_theme_file: Option<PathBuf>,

#[serde(alias = "terminal-title")]
pub terminal_title: Option<ExecPattern>,

Expand Down Expand Up @@ -248,7 +253,21 @@ impl Conf {
overwrite!(self, default_flags, conf);
overwrite!(self, date_time_format, conf);
overwrite!(self, icon_theme, conf);
overwrite!(self, syntax_theme, conf);
// syntax_theme and syntax_theme_file replace each other, so that the
// last one read wins; when a file sets both, syntax_theme_file wins
if conf.syntax_theme.is_some() {
self.syntax_theme = conf.syntax_theme.take();
self.syntax_theme_file = None;
}
if let Some(file) = conf.syntax_theme_file.take() {
let file = file.to_string_lossy();
// an empty path unsets it
self.syntax_theme_file = (!file.is_empty()).then(|| {
// relative to the conf file declaring it
path_from(&path, PathAnchor::Parent, &file)
});
self.syntax_theme = None;
}
overwrite!(self, disable_mouse_capture, conf);
overwrite!(self, capture_mouse, conf);
overwrite!(self, true_colors, conf);
Expand Down
2 changes: 2 additions & 0 deletions src/conf/conf_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ pub enum ConfError {
InvalidDefaultFlags { flags: String },
#[error("invalid syntax theme: `{name}`")]
InvalidSyntaxTheme { name: String },
#[error("invalid syntax theme file `{path}`: {details}")]
InvalidSyntaxThemeFile { path: String, details: String },
#[error("invalid glob pattern: `{pattern}`")]
InvalidGlobPattern { pattern: String },
#[error(
Expand Down
22 changes: 12 additions & 10 deletions src/syntactic/syntaxer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,23 @@ impl Syntaxer {
self.theme_set.themes.keys()
}

pub fn highlighter_for(
&self,
pub fn highlighter_for<'s>(
&'s self,
path: &Path,
con: &AppContext,
) -> Option<HighlightLines<'_>> {
con: &'s AppContext,
) -> Option<HighlightLines<'s>> {
path.extension()
.and_then(|e| e.to_str())
.and_then(|ext| self.syntax_set.find_syntax_by_extension(ext))
.map(|syntax| {
let theme = con.syntax_theme.unwrap_or_default();
let theme = self
.theme_set
.themes
.get(theme.syntect_name())
.unwrap_or_else(|| self.theme_set.themes.iter().next().unwrap().1);
let theme = match (con.syntax_theme, &con.custom_syntax_theme) {
(None, Some(theme)) => theme,
(theme, _) => self
.theme_set
.themes
.get(theme.unwrap_or_default().syntect_name())
.unwrap_or_else(|| self.theme_set.themes.iter().next().unwrap().1),
};
let options = HighlightOptions {
ignore_errors: true,
};
Expand Down
25 changes: 17 additions & 8 deletions src/syntactic/text_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ pub static SEPARATOR_FILLING: Lazy<Filling> = Lazy::new(|| Filling::from_char('
/// Homogeneously colored piece of a line
#[derive(Debug, Clone)]
pub struct Region {
pub fg: Color,
/// None when the preview's normal foreground is to be used
pub fg: Option<Color>,
pub string: String,
}

Expand All @@ -77,10 +78,18 @@ const INITIAL_LOAD: usize = 4_000_000;

impl Region {
pub fn from_syntect(region: &(Style, &str)) -> Self {
let fg = Color::Rgb {
r: region.0.foreground.r,
g: region.0.foreground.g,
b: region.0.foreground.b,
// As in bat's ansi and base16 themes, an alpha of 0 makes the red
// channel a terminal palette index, and an alpha of 1 means the
// default foreground
let c = region.0.foreground;
let fg = match c.a {
0 => Some(Color::AnsiValue(c.r)),
1 => None,
_ => Some(Color::Rgb {
r: c.r,
g: c.g,
b: c.b,
}),
};
let string = region.1.to_string();
Self { fg, string }
Expand Down Expand Up @@ -572,7 +581,7 @@ impl TextView {
let regions = if line.regions.is_empty() && line.len > 0 {
match rows.read(line) {
Some(string) => Cow::Owned(vec![Region {
fg: normal_fg,
fg: None,
string,
}]),
None => {
Expand Down Expand Up @@ -618,7 +627,7 @@ impl TextView {
let pos_list = line.name_match.as_ref().map(|nm| &nm.pos);
if overflow == Overflow::NoWrap && pos_list.is_none() {
for region in regions {
cw.w.queue(SetForegroundColor(region.fg))?;
cw.w.queue(SetForegroundColor(region.fg.unwrap_or(normal_fg)))?;
let s = region.string.trim_end_matches(is_char_end_of_line);
if s.contains('\t') {
cw.queue_unstyled_str(&s.replace('\t', &" ".repeat(TAB_WIDTH)))?;
Expand All @@ -634,7 +643,7 @@ impl TextView {
let mut run = String::new();
'regions: for region in regions {
flush(&mut cw, &mut run)?;
cw.w.queue(SetForegroundColor(region.fg))?;
cw.w.queue(SetForegroundColor(region.fg.unwrap_or(normal_fg)))?;
for c in region.string.chars() {
if ci >= to {
break 'regions;
Expand Down
18 changes: 18 additions & 0 deletions website/src/conf_file.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,24 @@ syntax_theme = "OceanLight"

Those themes come from [syntect](https://github.com/trishume/syntect) and are bundled in broot.

You may also use your own theme, in the `.tmTheme` format read by syntect (and by [bat](https://github.com/sharkdp/bat)), by giving its path:

```Hjson
syntax_theme_file: ~/.config/bat/themes/my-theme.tmTheme
```
```TOML
syntax_theme_file = "~/.config/bat/themes/my-theme.tmTheme"
```

A relative path is resolved from the configuration file declaring it.

`syntax_theme` and `syntax_theme_file` replace each other: the one read last wins.
As imported files are read after the file importing them, a `syntax_theme` set in your skin replaces a `syntax_theme_file` set in `conf.hjson`, so it's usually best to set `syntax_theme_file` in the skin file your `conf.hjson` imports (for example `skins/dark-blue.hjson`), in place of its `syntax_theme`.
An empty `syntax_theme_file` unsets it.
A theme chosen with `:set_syntax_theme` replaces the theme file until broot is closed.

Themes made for the terminal's palette, like bat's `ansi` and `base16` themes, are supported.


# Terminal title

Expand Down
Loading