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
18 changes: 18 additions & 0 deletions docs/src/features/themes/definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,24 @@ code:
vertical: 1
```

#### Background

By default the code block background comes from theme given theme, but you're able to override it or disable it completely:

```yaml
code:
# Use the theme's default background color (the default)
background: true

# Disable the background (transparent)
background: false

# Use a specific color
background: "898989"
```

This is particularly useful when combining presentation themes with code highlighting themes that have matching or conflicting backgrounds. For example, you might want to use a Catppuccin presentation theme with a Catppuccin code highlighting theme, but override the background to avoid having identical colors.

#### Custom highlighting themes

Besides the built-in highlighting themes, you can drop any `.tmTheme` theme in the `themes/highlighting` directory under
Expand Down
8 changes: 5 additions & 3 deletions src/code/highlighting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
elements::{Line, Text},
text_style::{Color, TextStyle},
},
theme::CodeBlockStyle,
theme::{CodeBlockStyle, CodeBlockStyleBackground},
};
use flate2::read::ZlibDecoder;
use once_cell::sync::Lazy;
Expand Down Expand Up @@ -228,8 +228,10 @@ pub(crate) struct StyledTokens<'a> {

impl<'a> StyledTokens<'a> {
pub(crate) fn new(style: Style, tokens: &'a str, block_style: &CodeBlockStyle) -> Self {
let has_background = block_style.background;
let background = has_background.then_some(parse_color(style.background)).flatten();
let background = match block_style.background {
CodeBlockStyleBackground::Color(color) => color,
CodeBlockStyleBackground::Enabled(enabled) => enabled.then_some(parse_color(style.background)).flatten(),
};
let foreground = parse_color(style.foreground);
let mut style = TextStyle::default();
style.colors.background = background;
Expand Down
4 changes: 2 additions & 2 deletions src/presentation/builder/snippet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
operation::{AsRenderOperations, RenderAsyncStartPolicy, RenderOperation},
properties::WindowSize,
},
theme::{Alignment, CodeBlockStyle},
theme::{Alignment, CodeBlockStyle, CodeBlockStyleBackground},
third_party::ThirdPartyRenderRequest,
ui::execution::{
RunAcquireTerminalSnippet, RunImageSnippet, SnippetExecutionDisabledOperation, SnippetOutputOperation,
Expand Down Expand Up @@ -302,7 +302,7 @@ impl PresentationBuilder<'_, '_> {
fn code_style(&self, snippet: &Snippet) -> CodeBlockStyle {
let mut style = self.theme.code.clone();
if snippet.attributes.no_background {
style.background = false;
style.background = CodeBlockStyleBackground::Enabled(false);
}
style
}
Expand Down
127 changes: 121 additions & 6 deletions src/theme/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl PresentationTheme {
let default_style = DefaultStyle::new(default_style, &palette)?;
Ok(Self {
slide_title: SlideTitleStyle::new(slide_title, &palette, options)?,
code: CodeBlockStyle::new(code),
code: CodeBlockStyle::new(code, &palette)?,
execution_output: ExecutionOutputBlockStyle::new(execution_output, &palette)?,
pty_output: PtyOutputBlockStyle::new(pty_output, &palette)?,
inline_code: ModifierStyle::new(inline_code, &palette)?,
Expand Down Expand Up @@ -569,29 +569,45 @@ impl FooterContent {
}
}

#[derive(Clone, Debug)]
pub(crate) enum CodeBlockStyleBackground {
Color(Option<Color>),
Enabled(bool),
}

impl Default for CodeBlockStyleBackground {
fn default() -> Self {
CodeBlockStyleBackground::Enabled(true)
}
}

#[derive(Clone, Debug, Default)]
pub(crate) struct CodeBlockStyle {
pub(crate) alignment: Alignment,
pub(crate) padding: PaddingRect,
pub(crate) theme_name: String,
pub(crate) background: bool,
pub(crate) background: CodeBlockStyleBackground,
pub(crate) line_numbers: bool,
}

impl CodeBlockStyle {
fn new(raw: &raw::CodeBlockStyle) -> Self {
fn new(raw: &raw::CodeBlockStyle, palette: &ColorPalette) -> Result<Self, ProcessingThemeError> {
let raw::CodeBlockStyle { alignment, padding, theme_name, background, line_numbers } = raw;
let padding = PaddingRect {
horizontal: padding.horizontal.unwrap_or_default(),
vertical: padding.vertical.unwrap_or_default(),
};
Self {
let background = match background.clone().unwrap_or_default() {
raw::CodeBlockStyleBackground::Color(color) => CodeBlockStyleBackground::Color(color.resolve(palette)?),
raw::CodeBlockStyleBackground::Enabled(enabled) => CodeBlockStyleBackground::Enabled(enabled),
};
Ok(Self {
alignment: alignment.clone().unwrap_or_default().into(),
padding,
theme_name: theme_name.as_deref().unwrap_or(DEFAULT_CODE_HIGHLIGHT_THEME).to_string(),
background: background.unwrap_or(true),
background,
line_numbers: line_numbers.unwrap_or_default(),
}
})
}
}

Expand Down Expand Up @@ -708,6 +724,105 @@ impl PtyStandbyStyle {
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::theme::raw;

#[test]
fn code_block_style_background_enabled() {
let palette = ColorPalette::default();

let raw = raw::CodeBlockStyle {
alignment: None,
padding: Default::default(),
theme_name: Some("test".to_string()),
background: Some(raw::CodeBlockStyleBackground::Enabled(true)),
line_numbers: None,
};

let style = CodeBlockStyle::new(&raw, &palette).unwrap();
assert!(matches!(style.background, CodeBlockStyleBackground::Enabled(true)));
}

#[test]
fn code_block_style_background_disabled() {
let palette = ColorPalette::default();

let raw = raw::CodeBlockStyle {
alignment: None,
padding: Default::default(),
theme_name: None,
background: Some(raw::CodeBlockStyleBackground::Enabled(false)),
line_numbers: None,
};

let style = CodeBlockStyle::new(&raw, &palette).unwrap();
assert!(matches!(style.background, CodeBlockStyleBackground::Enabled(false)));
}

#[test]
fn code_block_style_background_color() {
let palette = ColorPalette::default();

let raw = raw::CodeBlockStyle {
alignment: None,
padding: Default::default(),
theme_name: None,
background: Some(raw::CodeBlockStyleBackground::Color(raw::RawColor::Color(Color::new(46, 52, 64)))),
line_numbers: None,
};

let style = CodeBlockStyle::new(&raw, &palette).unwrap();
assert!(
matches!(
style.background,
CodeBlockStyleBackground::Color(Some(color)) if color == Color::new(46, 52, 64)
),
"Expected Color variant with specific color Color::new(46, 52, 64)"
);
}

#[test]
fn code_block_style_background_palette_color() {
let mut palette = ColorPalette::default();
palette.colors.insert("surface0".to_string(), Color::new(49, 50, 68));

let raw = raw::CodeBlockStyle {
alignment: None,
padding: Default::default(),
theme_name: None,
background: Some(raw::CodeBlockStyleBackground::Color(raw::RawColor::Palette("surface0".to_string()))),
line_numbers: None,
};

let style = CodeBlockStyle::new(&raw, &palette).unwrap();
assert!(
matches!(
style.background,
CodeBlockStyleBackground::Color(Some(color)) if color == Color::new(49, 50, 68)
),
"Expected Color variant with palette color Color::new(49, 50, 68)"
);
}

#[test]
fn code_block_style_background_default() {
let palette = ColorPalette::default();

let raw = raw::CodeBlockStyle {
alignment: None,
padding: Default::default(),
theme_name: None,
background: None,
line_numbers: None,
};

let style = CodeBlockStyle::new(&raw, &palette).unwrap();
assert!(matches!(style.background, CodeBlockStyleBackground::Enabled(true)));
}
}

#[derive(Clone, Debug, Default)]
pub(crate) struct ModifierStyle {
pub(crate) style: TextStyle,
Expand Down
72 changes: 71 additions & 1 deletion src/theme/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,19 @@ pub(crate) enum ParseFooterTemplateError {
UnsupportedVariable(String),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum CodeBlockStyleBackground {
Color(RawColor),
Enabled(bool),
}

impl Default for CodeBlockStyleBackground {
fn default() -> Self {
CodeBlockStyleBackground::Enabled(true)
}
}

/// The style for a piece of code.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct CodeBlockStyle {
Expand All @@ -673,7 +686,7 @@ pub(crate) struct CodeBlockStyle {
pub(crate) theme_name: Option<String>,

/// Whether to use the theme's background color.
pub(crate) background: Option<bool>,
pub(crate) background: Option<CodeBlockStyleBackground>,

/// Whether to show line numbers in all code blocks.
#[serde(default)]
Expand Down Expand Up @@ -1075,4 +1088,61 @@ mod test {
let RawColor::Palette(name) = color else { panic!("not a palette color") };
assert_eq!(name, expected);
}

#[test]
fn code_block_background_serde() {
let bg_true: CodeBlockStyleBackground = serde_yaml::from_str("true").unwrap();
assert!(matches!(bg_true, CodeBlockStyleBackground::Enabled(true)));

let bg_false: CodeBlockStyleBackground = serde_yaml::from_str("false").unwrap();
assert!(matches!(bg_false, CodeBlockStyleBackground::Enabled(false)));

let bg_color: CodeBlockStyleBackground = serde_yaml::from_str("\"2e3440\"").unwrap();
assert!(
matches!(bg_color, CodeBlockStyleBackground::Color(RawColor::Color(_))),
"Expected Color variant with hex color"
);

let bg_palette: CodeBlockStyleBackground = serde_yaml::from_str("\"palette:surface0\"").unwrap();
assert!(
matches!(bg_palette, CodeBlockStyleBackground::Color(RawColor::Palette(ref name)) if name == "surface0"),
"Expected Color variant with palette color 'surface0'"
);
}

#[test]
fn code_block_background_default() {
let default = CodeBlockStyleBackground::default();
assert!(matches!(default, CodeBlockStyleBackground::Enabled(true)));
}

#[test]
fn code_block_style_with_background() {
let yaml = r#"
theme_name: "base16-mocha.dark"
background: false
"#;
let style: CodeBlockStyle = serde_yaml::from_str(yaml).unwrap();
assert!(matches!(style.background, Some(CodeBlockStyleBackground::Enabled(false))));

let yaml = r##"
theme_name: "Catppuccin Mocha"
background: "2e3440"
"##;
let style: CodeBlockStyle = serde_yaml::from_str(yaml).unwrap();
assert!(
matches!(style.background, Some(CodeBlockStyleBackground::Color(RawColor::Color(_)))),
"Expected Color variant with hex color"
);

let yaml = r##"
theme_name: "Catppuccin Frappe"
background: "palette:surface0"
"##;
let style: CodeBlockStyle = serde_yaml::from_str(yaml).unwrap();
assert!(
matches!(style.background, Some(CodeBlockStyleBackground::Color(RawColor::Palette(ref name))) if name == "surface0"),
"Expected Palette variant with 'surface0'"
);
}
}