use crate::view::overlay::{Overlay, OverlayFace};
use crate::view::theme::Theme;
use fresh_core::api::ViewTokenStyle;
use ratatui::style::{Color, Modifier, Style};
pub(super) struct CharStyleContext<'a> {
pub byte_pos: Option<usize>,
pub token_style: Option<&'a ViewTokenStyle>,
pub ansi_style: Style,
pub is_cursor: bool,
pub is_selected: bool,
pub theme: &'a Theme,
pub highlight_color: Option<Color>,
pub highlight_theme_key: Option<&'static str>,
pub semantic_token_color: Option<Color>,
pub active_overlays: &'a [&'a Overlay],
pub primary_cursor_position: usize,
pub is_active: bool,
pub skip_primary_cursor_reverse: bool,
pub is_cursor_line_highlighted: bool,
pub current_line_bg: Color,
}
pub(super) struct CharStyleOutput {
pub style: Style,
pub is_secondary_cursor: bool,
pub fg_theme_key: Option<&'static str>,
pub bg_theme_key: Option<&'static str>,
pub region: &'static str,
}
pub(super) fn compute_char_style(ctx: &CharStyleContext) -> CharStyleOutput {
let highlight_color = ctx.highlight_color;
let mut fg_theme_key: Option<&'static str> = None;
let mut bg_theme_key: Option<&'static str> = Some("editor.bg");
let mut region: &'static str = "Editor Content";
let mut style = if let Some(ts) = ctx.token_style {
let mut s = Style::default();
if let Some((r, g, b)) = ts.fg {
s = s.fg(Color::Rgb(r, g, b));
} else {
s = s.fg(ctx.theme.editor_fg);
fg_theme_key = Some("editor.fg");
}
if let Some((r, g, b)) = ts.bg {
s = s.bg(Color::Rgb(r, g, b));
}
if ts.bold {
s = s.add_modifier(Modifier::BOLD);
}
if ts.italic {
s = s.add_modifier(Modifier::ITALIC);
}
region = "Plugin Token";
s
} else if ctx.ansi_style.fg.is_some()
|| ctx.ansi_style.bg.is_some()
|| !ctx.ansi_style.add_modifier.is_empty()
{
let mut s = Style::default();
if let Some(fg) = ctx.ansi_style.fg {
s = s.fg(fg);
} else {
s = s.fg(ctx.theme.editor_fg);
fg_theme_key = Some("editor.fg");
}
if let Some(bg) = ctx.ansi_style.bg {
s = s.bg(bg);
bg_theme_key = None; }
s = s.add_modifier(ctx.ansi_style.add_modifier);
region = "ANSI Escape";
s
} else if let Some(color) = highlight_color {
fg_theme_key = ctx.highlight_theme_key;
Style::default().fg(color)
} else {
fg_theme_key = Some("editor.fg");
Style::default().fg(ctx.theme.editor_fg)
};
if let Some(color) = highlight_color {
if ctx.ansi_style.fg.is_none()
&& (ctx.ansi_style.bg.is_some() || !ctx.ansi_style.add_modifier.is_empty())
{
style = style.fg(color);
fg_theme_key = ctx.highlight_theme_key;
}
}
if ctx.token_style.is_none() {
if let Some(color) = ctx.semantic_token_color {
style = style.fg(color);
}
}
for overlay in ctx.active_overlays {
match &overlay.face {
OverlayFace::Underline {
color,
style: _underline_style,
} => {
style = style.add_modifier(Modifier::UNDERLINED).fg(*color);
if let Some(key) = overlay.theme_key {
fg_theme_key = Some(key);
}
}
OverlayFace::Background { color } => {
style = style.bg(*color);
if let Some(key) = overlay.theme_key {
bg_theme_key = Some(key);
let m = ctx.theme.modifier_for_bg_key(key);
if !m.is_empty() {
style = style.add_modifier(m);
}
}
}
OverlayFace::Foreground { color } => {
style = style.fg(*color);
if let Some(key) = overlay.theme_key {
fg_theme_key = Some(key);
}
}
OverlayFace::Style {
style: overlay_style,
} => {
style = style.patch(*overlay_style);
if let Some(key) = overlay.theme_key {
if overlay_style.bg.is_some() {
bg_theme_key = Some(key);
}
if overlay_style.fg.is_some() {
fg_theme_key = Some(key);
}
}
}
OverlayFace::ThemedStyle {
fallback_style,
fg_theme,
bg_theme,
} => {
let mut themed_style = *fallback_style;
if let Some(fg_key) = fg_theme {
if let Some(color) = ctx.theme.resolve_theme_key(fg_key) {
themed_style = themed_style.fg(color);
}
}
if let Some(bg_key) = bg_theme {
if let Some(color) = ctx.theme.resolve_theme_key(bg_key) {
themed_style = themed_style.bg(color);
}
let m = ctx.theme.modifier_for_bg_key(bg_key);
if !m.is_empty() {
themed_style = themed_style.add_modifier(m);
}
}
style = style.patch(themed_style);
}
}
}
if ctx.is_cursor_line_highlighted && !ctx.is_selected && style.bg.is_none() {
style = style.bg(ctx.current_line_bg);
}
if ctx.is_selected {
style = style.bg(ctx.theme.selection_bg);
if !ctx.theme.selection_modifier.is_empty() {
style = style.add_modifier(ctx.theme.selection_modifier);
}
bg_theme_key = Some("editor.selection_bg");
region = "Selection";
}
let is_secondary_cursor = ctx.is_cursor && ctx.byte_pos != Some(ctx.primary_cursor_position);
if ctx.is_active {
if ctx.is_cursor {
if ctx.skip_primary_cursor_reverse {
if is_secondary_cursor {
style = style.add_modifier(Modifier::REVERSED);
}
} else {
style = style.add_modifier(Modifier::REVERSED);
}
region = "Cursor";
}
} else if ctx.is_cursor {
style = style.fg(ctx.theme.editor_fg).bg(ctx.theme.inactive_cursor);
fg_theme_key = Some("editor.fg");
bg_theme_key = Some("editor.inactive_cursor");
region = "Inactive Cursor";
}
CharStyleOutput {
style,
is_secondary_cursor,
fg_theme_key,
bg_theme_key,
region,
}
}