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

feat: add symbols to alerts #430

Merged
merged 2 commits into from
Feb 1, 2025
Merged
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
31 changes: 8 additions & 23 deletions .github/workflows/merge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,48 +8,33 @@ name: Merge checks

jobs:
check:
name: Check
name: Checks
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4

- name: Install rust toolchain
uses: dtolnay/[email protected]
with:
components: clippy

- name: Run cargo check
run: cargo check --features sixel

test:
name: Tests
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4

- name: Install rust toolchain
uses: dtolnay/[email protected]

- name: Run cargo test
run: cargo test

lints:
name: Lints
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: Run cargo clippy
run: cargo clippy -- -D warnings

- name: Install stable toolchain
- name: Install nightly toolchain
uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt, clippy
components: rustfmt

- name: Run cargo fmt
run: cargo fmt --all -- --check

- name: Run cargo clippy
run: cargo clippy -- -D warnings
run: cargo +nightly fmt --all -- --check

nix-flake:
name: Validate nix flake
Expand Down
11 changes: 4 additions & 7 deletions src/code/snippet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,13 +745,10 @@ mod test {
#[test]
fn highlight_line_range() {
let attributes = parse_attributes("bash { 1, 2-4,6 , all , 10 - 12 }");
assert_eq!(attributes.highlight_groups, &[HighlightGroup::new(vec![
Single(1),
Range(2..5),
Single(6),
All,
Range(10..13)
])]);
assert_eq!(
attributes.highlight_groups,
&[HighlightGroup::new(vec![Single(1), Range(2..5), Single(6), All, Range(10..13)])]
);
}

#[test]
Expand Down
39 changes: 21 additions & 18 deletions src/presentation/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,21 +733,25 @@ impl<'a> PresentationBuilder<'a> {
}

fn push_alert(&mut self, alert_type: AlertType, title: Option<String>, mut lines: Vec<Line>) -> BuildResult {
let (default_title, prefix_color) = match alert_type {
AlertType::Note => ("Note", self.theme.alert.colors.types.note),
AlertType::Tip => ("Tip", self.theme.alert.colors.types.tip),
AlertType::Important => ("Important", self.theme.alert.colors.types.important),
AlertType::Warning => ("Warning", self.theme.alert.colors.types.warning),
AlertType::Caution => ("Caution", self.theme.alert.colors.types.caution),
let (prefix_color, default_title, symbol) = match alert_type {
AlertType::Note => self.theme.alert.styles.note.as_parts(),
AlertType::Tip => self.theme.alert.styles.tip.as_parts(),
AlertType::Important => self.theme.alert.styles.important.as_parts(),
AlertType::Warning => self.theme.alert.styles.warning.as_parts(),
AlertType::Caution => self.theme.alert.styles.caution.as_parts(),
};
let prefix_color = prefix_color.or(self.theme.alert.colors.base.foreground);
let prefix_color = prefix_color.or(self.theme.alert.base_colors.foreground);
let title = title.unwrap_or_else(|| default_title.to_string());
let title_colors = Colors { foreground: prefix_color, background: self.theme.alert.colors.base.background };
let title = match symbol {
Some(symbol) => format!("{symbol} {title}"),
None => title,
};
let title_colors = Colors { foreground: prefix_color, background: self.theme.alert.base_colors.background };
lines.insert(0, Line::from(Text::from("")));
lines.insert(0, Line::from(Text::new(title, TextStyle::default().colors(title_colors))));

let prefix = self.theme.block_quote.prefix.clone().unwrap_or_default();
self.push_quoted_text(lines, prefix, self.theme.alert.colors.base, prefix_color)
self.push_quoted_text(lines, prefix, self.theme.alert.base_colors, prefix_color)
}

fn push_quoted_text(
Expand Down Expand Up @@ -1681,11 +1685,10 @@ mod test {
#[test]
fn iterate_list_starting_from_other() {
let list = ListIterator::new(
vec![ListItem { depth: 0, contents: "0".into(), item_type: ListItemType::Unordered }, ListItem {
depth: 0,
contents: "1".into(),
item_type: ListItemType::Unordered,
}],
vec![
ListItem { depth: 0, contents: "0".into(), item_type: ListItemType::Unordered },
ListItem { depth: 0, contents: "1".into(), item_type: ListItemType::Unordered },
],
3,
);
let expected_indexes = [3, 4];
Expand Down Expand Up @@ -1772,10 +1775,10 @@ mod test {

#[test]
fn implicit_slide_ends_with_front_matter() {
let elements =
vec![MarkdownElement::FrontMatter("theme:\n name: light".into()), MarkdownElement::SetexHeading {
text: "hi".into(),
}];
let elements = vec![
MarkdownElement::FrontMatter("theme:\n name: light".into()),
MarkdownElement::SetexHeading { text: "hi".into() },
];
let options = PresentationBuilderOptions { implicit_slide_ends: true, ..Default::default() };
let slides = build_presentation_with_options(elements, options).into_slides();
assert_eq!(slides.len(), 1);
Expand Down
183 changes: 142 additions & 41 deletions src/theme.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::markdown::text_style::{Color, Colors, FixedStr, UndefinedPaletteColorError};
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, fs, io, path::Path};
use std::{collections::BTreeMap, fmt, fs, io, marker::PhantomData, path::Path};

include!(concat!(env!("OUT_DIR"), "/themes.rs"));

Expand Down Expand Up @@ -395,7 +395,7 @@ pub(crate) struct BlockQuoteStyle {

impl BlockQuoteStyle {
fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
let Self { colors, alignment: _alignment, prefix: _prefix } = self;
let Self { colors, alignment: _, prefix: _ } = self;
colors.resolve_palette_colors(palette)?;
Ok(())
}
Expand Down Expand Up @@ -431,82 +431,183 @@ pub(crate) struct AlertStyle {
#[serde(flatten, default)]
pub(crate) alignment: Option<Alignment>,

/// The base colors.
#[serde(default)]
pub(crate) base_colors: Colors,

/// The prefix to be added to this block quote.
///
/// This allows adding something like a vertical bar before the text.
#[serde(default)]
pub(crate) prefix: Option<String>,

/// The colors to be used.
/// The style for each alert type.
#[serde(default)]
pub(crate) colors: AlertColors,
pub(crate) styles: AlertTypeStyles,
}

impl AlertStyle {
fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
let Self { colors, alignment: _alignment, prefix: _prefix } = self;
colors.resolve_palette_colors(palette)?;
let Self { base_colors, styles, alignment: _, prefix: _ } = self;
*base_colors = base_colors.resolve(palette)?;
styles.resolve_palette_colors(palette)?;
Ok(())
}
}

/// The colors of an alert.
/// The style for each alert type.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct AlertColors {
/// The foreground/background colors.
#[serde(flatten)]
pub(crate) base: Colors,
pub(crate) struct AlertTypeStyles {
/// The style for note alert types.
#[serde(default)]
pub(crate) note: AlertTypeStyle<NoteAlertType>,

/// The color of the vertical bar that prefixes each line in the quote.
/// The style for tip alert types.
#[serde(default)]
pub(crate) types: AlertTypeColors,
pub(crate) tip: AlertTypeStyle<TipAlertType>,

/// The style for important alert types.
#[serde(default)]
pub(crate) important: AlertTypeStyle<ImportantAlertType>,

/// The style for warning alert types.
#[serde(default)]
pub(crate) warning: AlertTypeStyle<WarningAlertType>,

/// The style for caution alert types.
#[serde(default)]
pub(crate) caution: AlertTypeStyle<CautionAlertType>,
}

impl AlertColors {
impl AlertTypeStyles {
fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
let Self { base, types } = self;
*base = base.resolve(palette)?;
types.resolve_palette_colors(palette)?;
let Self { note, tip, important, warning, caution } = self;
note.resolve_palette_colors(palette)?;
tip.resolve_palette_colors(palette)?;
important.resolve_palette_colors(palette)?;
warning.resolve_palette_colors(palette)?;
caution.resolve_palette_colors(palette)?;
Ok(())
}
}

/// The colors of each alert type.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct AlertTypeColors {
/// The color for note type alerts.
#[serde(default)]
pub(crate) note: Option<Color>,

/// The color for tip type alerts.
/// The style for an alert type.
#[derive(Deserialize, Serialize)]
pub(crate) struct AlertTypeStyle<S: AlertTypeProperties> {
/// The color to be used.
#[serde(default)]
pub(crate) tip: Option<Color>,
pub(crate) color: Option<Color>,

/// The color for important type alerts.
#[serde(default)]
pub(crate) important: Option<Color>,
/// The title to be used.
#[serde(default = "S::default_title")]
pub(crate) title: String,

/// The color for warning type alerts.
#[serde(default)]
pub(crate) warning: Option<Color>,
/// The symbol to be used.
#[serde(default = "S::default_symbol")]
pub(crate) symbol: Option<String>,

/// The color for caution type alerts.
#[serde(default)]
pub(crate) caution: Option<Color>,
#[serde(skip)]
_unused: PhantomData<S>,
}

impl AlertTypeColors {
impl<S: AlertTypeProperties> AlertTypeStyle<S> {
pub(crate) fn as_parts(&self) -> (&Option<Color>, &str, Option<&str>) {
(&self.color, &self.title, self.symbol.as_deref())
}

fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
let Self { note, tip, important, warning, caution } = self;
for c in [note, tip, important, warning, caution] {
if let Some(c) = c.as_mut() {
*c = c.resolve(palette)?;
}
let Self { color, title: _, symbol: _, _unused: _ } = self;
if let Some(color) = color.as_mut() {
*color = color.resolve(palette)?;
}
Ok(())
}
}

impl<S: AlertTypeProperties> fmt::Debug for AlertTypeStyle<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AlertTypeStyle")
.field("color", &self.color)
.field("title", &self.title)
.field("symbol", &self.symbol)
.field("_unused", &self._unused)
.finish()
}
}

impl<S: AlertTypeProperties> Clone for AlertTypeStyle<S> {
fn clone(&self) -> Self {
Self { color: self.color, title: self.title.clone(), symbol: self.symbol.clone(), _unused: PhantomData }
}
}

impl<S: AlertTypeProperties> Default for AlertTypeStyle<S> {
fn default() -> Self {
Self { color: None, title: S::default_title(), symbol: S::default_symbol(), _unused: PhantomData }
}
}

pub(crate) trait AlertTypeProperties {
fn default_title() -> String;
fn default_symbol() -> Option<String>;
}

pub(crate) struct NoteAlertType;
pub(crate) struct TipAlertType;
pub(crate) struct ImportantAlertType;
pub(crate) struct WarningAlertType;
pub(crate) struct CautionAlertType;

impl AlertTypeProperties for NoteAlertType {
fn default_title() -> String {
"Note".into()
}

fn default_symbol() -> Option<String> {
Some("󰋽".into())
}
}

impl AlertTypeProperties for TipAlertType {
fn default_title() -> String {
"Tip".into()
}

fn default_symbol() -> Option<String> {
Some("".into())
}
}

impl AlertTypeProperties for ImportantAlertType {
fn default_title() -> String {
"Important".into()
}

fn default_symbol() -> Option<String> {
Some("".into())
}
}

impl AlertTypeProperties for WarningAlertType {
fn default_title() -> String {
"Warning".into()
}

fn default_symbol() -> Option<String> {
Some("".into())
}
}

impl AlertTypeProperties for CautionAlertType {
fn default_title() -> String {
"Caution".into()
}

fn default_symbol() -> Option<String> {
Some("󰳦".into())
}
}

/// The style for the presentation introduction slide.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct IntroSlideStyle {
Expand Down
Loading