use std::sync::{RwLock, RwLockReadGuard};
use FormKind::*;
use crossterm::style::{Attribute, Attributes, ContentStyle};
pub use crossterm::{cursor::SetCursorStyle as CursorShape, style::Color};
pub use self::global::*;
pub(crate) use self::global::{colorscheme_exists, exists};
use crate::{
context::{self, sender},
hook::{self, FormSet},
session::DuatEvent,
text::FormTag,
};
static BASE_FORMS: &[(&str, Form)] = &[
("default", Form::new()),
("accent", Form::new().bold()),
("caret.main", Form::new().reverse()),
("caret.extra", Form {
kind: Ref(2, default_style()),
..Form::new().reverse()
}),
("selection.main", Form::new().white().on_dark_grey()),
("selection.extra", Form::new().white().on_grey()),
("cloak", Form::new().grey().on_black()),
("character.control", Form::new().grey()),
("param.path", Form::new().yellow()),
("param.path.exists", Form {
kind: Ref(8, ContentStyle {
attributes: Attributes::none().with(Attribute::Underlined),
..default_style()
}),
..Form::new().yellow().underlined()
}),
("replace", Form::new().grey()),
];
mod global {
use std::{
any::TypeId,
collections::HashMap,
sync::{Arc, LazyLock, Mutex},
};
use super::{CursorShape, Form, FormId, Painter, Palette};
#[doc(inline)]
pub use crate::__id_of__ as id_of;
use crate::{
context,
form::{FormKind, MaskId},
hook::{self, ColorschemeSet},
};
static PALETTE: LazyLock<Palette> = LazyLock::new(Palette::new);
static COLORSCHEMES: LazyLock<Mutex<HashMap<Arc<str>, ColorschemeFn>>> =
LazyLock::new(Mutex::default);
pub fn set(name: impl ToString, form: Form) -> FormId {
let name = name.to_string();
let cloned_name = name.clone();
match form.kind {
FormKind::Normal => PALETTE.set_form(cloned_name, form),
FormKind::Ref(refed, style) => PALETTE.set_ref(cloned_name, refed, style),
_ => unreachable!(),
}
}
pub fn set_weak(name: impl ToString, form: Form) -> FormId {
let name = name.to_string();
let cloned_name = name.clone();
match form.kind {
FormKind::Normal => PALETTE.set_weak_form(cloned_name, form),
FormKind::Ref(refed, style) => PALETTE.set_weak_ref(cloned_name, refed, style),
_ => unreachable!(),
}
}
pub fn from_id(id: FormId) -> Form {
PALETTE.form_from_id(id).unwrap_or_default()
}
pub fn main_cursor() -> (Form, Option<CursorShape>) {
PALETTE.main_cursor()
}
pub fn extra_cursor() -> (Form, Option<CursorShape>) {
PALETTE.extra_cursor()
}
pub fn set_main_cursor(shape: CursorShape) {
PALETTE.set_main_cursor(shape);
}
pub fn set_extra_cursor(shape: CursorShape) {
PALETTE.set_extra_cursor(shape);
}
pub fn unset_main_cursor() {
PALETTE.unset_main_cursor();
}
pub fn unset_extra_cursor() {
PALETTE.unset_extra_cursor();
}
pub fn unset_cursors() {
PALETTE.unset_main_cursor();
PALETTE.unset_extra_cursor();
}
pub fn painter() -> Painter {
PALETTE.painter(super::DEFAULT_ID)
}
pub(crate) fn painter_with_widget<W: ?Sized + 'static>() -> Painter {
PALETTE.painter(default_id(
TypeId::of::<W>(),
crate::utils::duat_name::<W>(),
))
}
pub fn enable_mask(mask: impl AsRef<str> + Send + Sync + 'static) {
let mask = mask.as_ref();
let mut inner = PALETTE.0.write().unwrap();
if !inner.masks.iter().any(|(m, _)| *m == mask) {
let mut remaps: Vec<u16> = (0..inner.forms.len() as u16).collect();
for (i, (name, ..)) in inner.forms.iter().enumerate() {
if let Some((pref, suf)) = name.rsplit_once('.')
&& suf == mask
&& let Some(j) = inner.forms.iter().position(|(name, ..)| *name == pref)
{
remaps[j] = i as u16;
}
}
inner.masks.push((mask.to_string().leak(), remaps));
}
}
pub(crate) fn mask_id_for(mask: &'static str) -> Option<MaskId> {
let inner = PALETTE.0.read().unwrap();
Some(MaskId(
inner.masks.iter().position(|(m, _)| *m == mask)? as u32
))
}
#[macro_export]
#[doc(hidden)]
macro_rules! __id_of__ {
($form:expr) => {{
use $crate::form::{DEFAULT_ID, FormId, set_many};
static mut WAS_SET: bool = false;
static mut ID: FormId = DEFAULT_ID;
if unsafe { WAS_SET } {
unsafe { ID }
} else {
let name = $form.to_string();
let id = set_many([(name, None)])[0];
unsafe {
ID = id;
WAS_SET = true;
}
id
}
}};
}
pub fn id_of_non_static(name: impl ToString) -> FormId {
let name = name.to_string();
set_many([(name, None)])[0]
}
pub fn ids_of_non_static(names: impl IntoIterator<Item = impl ToString>) -> Vec<FormId> {
set_many(names.into_iter().map(|n| (n.to_string(), None)))
}
#[doc(hidden)]
pub fn set_many<S: AsRef<str>>(
sets: impl IntoIterator<Item = (S, Option<Form>)>,
) -> Vec<FormId> {
PALETTE.set_many(&Vec::from_iter(sets))
}
pub fn add_colorscheme(
name: impl ToString,
pairs: impl FnMut() -> Vec<(String, Form)> + Send + 'static,
) {
let name = name.to_string();
COLORSCHEMES
.lock()
.unwrap()
.insert(Arc::from(name), Box::new(pairs));
}
pub fn set_colorscheme(name: &str) {
let name = name.to_string();
let mut colorschemes = COLORSCHEMES.lock().unwrap();
if let Some(pairs_fn) = colorschemes.get_mut(name.as_str()) {
let Some(pairs) = crate::utils::catch_panic(pairs_fn) else {
context::error!("Failed to set [a]{name}[] colorscheme");
return;
};
set_many(pairs.iter().cloned().map(|(name, form)| (name, Some(form))));
context::queue(move |pa| {
_ = hook::trigger(pa, ColorschemeSet((name.to_string(), pairs)))
});
} else {
context::error!("The colorscheme [a]{name}[] was not found");
}
}
pub fn colorscheme_list() -> Vec<String> {
let mut list = Vec::from_iter(
COLORSCHEMES
.lock()
.unwrap()
.keys()
.map(|name| name.to_string()),
);
list.sort_unstable();
list
}
pub(crate) fn exists(name: &str) -> bool {
let palette = PALETTE.0.read().unwrap();
palette.forms.iter().any(|(n, _)| *n == name)
}
pub(crate) fn colorscheme_exists(name: &str) -> bool {
COLORSCHEMES.lock().unwrap().contains_key(name)
}
pub(super) fn name_of_form(id: FormId) -> &'static str {
PALETTE.0.read().unwrap().forms[id.0 as usize].0
}
pub(super) fn name_of_mask(id: MaskId) -> &'static str {
PALETTE.0.read().unwrap().masks[id.0 as usize].0
}
fn default_id(type_id: TypeId, type_name: &'static str) -> FormId {
static IDS: LazyLock<Mutex<HashMap<TypeId, FormId>>> = LazyLock::new(Mutex::default);
let mut ids = IDS.lock().unwrap();
if let Some(id) = ids.get(&type_id) {
*id
} else {
let name = format!("default.{type_name}");
let id = set_many(vec![(name, None)])[0];
ids.insert(type_id, id);
id
}
}
type ColorschemeFn = Box<dyn FnMut() -> Vec<(String, Form)> + Send>;
}
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FormId(u16);
impl FormId {
pub const fn to_tag(self, prio: u8) -> FormTag {
FormTag { id: self, priority: prio }
}
pub const fn to_u16(self) -> u16 {
self.0
}
pub fn name(self) -> &'static str {
name_of_form(self)
}
}
impl std::fmt::Debug for FormId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "FormId({}: {})", self.0, name_of_form(*self))
}
}
macro_rules! mimic_method {
(#[$attr:meta] $method:ident $attrib:expr) => {
#[$attr]
pub const fn $method(mut self) -> Form {
self.style.attributes = self.style.attributes.with($attrib);
self
}
};
(#[$attr:meta] $fg:ident $bg:ident $ul:ident $color:expr) => {
#[$attr]
pub const fn $fg(mut self) -> Form {
self.style.foreground_color = Some($color);
self
}
#[$attr]
pub const fn $bg(mut self) -> Form {
self.style.background_color = Some($color);
self
}
#[$attr]
pub const fn $ul(mut self) -> Form {
self.style.underline_color = Some($color);
self
}
};
}
#[derive(Default, Clone, Copy)]
pub struct Form {
pub style: ContentStyle,
kind: FormKind,
}
#[rustfmt::skip]
impl Form {
mimic_method!( bold Attribute::Bold);
mimic_method!( dim Attribute::Dim);
mimic_method!( italic Attribute::Italic);
mimic_method!( underlined Attribute::Underlined);
mimic_method!( double_underlined Attribute::DoubleUnderlined);
mimic_method!( undercurled Attribute::Undercurled);
mimic_method!( underdashed Attribute::Underdashed);
mimic_method!( reverse Attribute::Reverse);
mimic_method!( crossed_out Attribute::CrossedOut);
mimic_method!( black on_black underline_black Color::Black);
mimic_method!( dark_grey on_dark_grey underline_dark_grey Color::DarkGrey);
mimic_method!( red on_red underline_red Color::Red);
mimic_method!( dark_red on_dark_red underline_dark_red Color::DarkRed);
mimic_method!( green on_green underline_green Color::Green);
mimic_method!(
dark_green on_dark_green underline_dark_green Color::DarkGreen
);
mimic_method!( yellow on_yellow underline_yellow Color::Yellow);
mimic_method!(
dark_yellow on_dark_yellow underline_dark_yellow Color::DarkYellow
);
mimic_method!( blue on_blue underline_blue Color::Blue);
mimic_method!( dark_blue on_dark_blue underline_dark_blue Color::DarkBlue);
mimic_method!( magenta on_magenta underline_magenta Color::Magenta);
mimic_method!(
dark_magenta on_dark_magenta underline_dark_magenta Color::DarkMagenta
);
mimic_method!( cyan on_cyan underline_cyan Color::Cyan);
mimic_method!( dark_cyan on_dark_cyan underline_dark_cyan Color::DarkCyan);
mimic_method!( white on_white underline_white Color::White);
mimic_method!( grey on_grey underline_grey Color::Grey);
}
impl Form {
pub const fn new() -> Form {
Self {
style: default_style(),
kind: FormKind::Normal,
}
}
pub fn of(form_name: impl AsRef<str>) -> Self {
let mut form = from_id(id_of_non_static(form_name.as_ref()));
form.kind = FormKind::Normal;
form
}
pub fn mimic(form_name: impl AsRef<str>) -> Self {
let id = id_of_non_static(form_name.as_ref());
let mut form = from_id(id);
form.kind = FormKind::Ref(id.0, default_style());
form
}
pub const fn reset(mut self) -> Self {
self.style.attributes = self.style.attributes.with(Attribute::Reset);
if let FormKind::Ref(_, style) = &mut self.kind {
style.attributes = style.attributes.with(Attribute::Reset);
}
self
}
#[track_caller]
pub const fn with(mut self, color: &str) -> Self {
self.style.foreground_color = match str_to_color(color) {
Ok(color) => Some(color),
Err(_) => panic!("Ill-formed color"),
};
if let FormKind::Ref(_, style) = &mut self.kind {
style.foreground_color = self.style.foreground_color;
}
self
}
#[track_caller]
pub const fn on(mut self, color: &str) -> Self {
self.style.background_color = match str_to_color(color) {
Ok(color) => Some(color),
Err(_) => panic!("Ill-formed color"),
};
if let FormKind::Ref(_, style) = &mut self.kind {
style.background_color = self.style.background_color;
}
self
}
#[track_caller]
pub const fn with_on(mut self, color: &str) -> Self {
let color = match str_to_color(color) {
Ok(color) => color,
Err(_) => panic!("Ill-formed color"),
};
self.style.background_color = Some(color);
self.style.foreground_color = Some(color);
if let FormKind::Ref(_, style) = &mut self.kind {
style.background_color = self.style.background_color;
style.foreground_color = self.style.foreground_color;
}
self
}
#[track_caller]
pub fn underline(mut self, color: &str) -> Self {
self.style.underline_color = match str_to_color(color) {
Ok(color) => Some(color),
Err(_) => panic!("Ill-formed color"),
};
if let FormKind::Ref(_, style) = &mut self.kind {
style.underline_color = self.style.underline_color;
}
self
}
#[track_caller]
pub const fn interpolate(mut self, other: Self, factor: u8) -> Self {
const fn interpolate(color: Color, other: Color, factor: u8) -> Color {
if let (Color::Rgb { r, g, b }, Color::Rgb { r: or, g: og, b: ob }) = (color, other) {
let factor = factor as usize;
Color::Rgb {
r: ((r as usize * factor + or as usize * (100 - factor)) / 100) as u8,
g: ((g as usize * factor + og as usize * (100 - factor)) / 100) as u8,
b: ((b as usize * factor + ob as usize * (100 - factor)) / 100) as u8,
}
} else {
color
}
}
assert!(factor <= 100, "factor must be between 0 and 100");
if let (Some(other_fg), Some(self_fg)) = (other.fg(), &mut self.style.foreground_color) {
*self_fg = interpolate(*self_fg, other_fg, factor);
if let FormKind::Ref(_, style) = &mut self.kind {
style.foreground_color = self.style.foreground_color;
}
}
if let (Some(other_bg), Some(self_bg)) = (other.bg(), &mut self.style.background_color) {
*self_bg = interpolate(*self_bg, other_bg, factor);
if let FormKind::Ref(_, style) = &mut self.kind {
style.background_color = self.style.background_color;
}
}
if let (Some(other_ul), Some(self_ul)) = (other.ul(), &mut self.style.underline_color) {
*self_ul = interpolate(*self_ul, other_ul, factor);
if let FormKind::Ref(_, style) = &mut self.kind {
style.underline_color = self.style.underline_color;
}
}
self
}
const fn fg(&self) -> Option<Color> {
self.style.foreground_color
}
const fn bg(&self) -> Option<Color> {
self.style.background_color
}
const fn ul(&self) -> Option<Color> {
self.style.underline_color
}
const fn attrs(&self) -> Attributes {
self.style.attributes
}
}
impl PartialEq for Form {
fn eq(&self, other: &Self) -> bool {
self.style == other.style
}
}
impl Eq for Form {}
#[derive(Debug)]
#[doc(hidden)]
pub struct Palette(RwLock<InnerPalette>);
impl Palette {
fn new() -> Self {
let main_cursor = Some(CursorShape::DefaultUserShape);
Self(RwLock::new(InnerPalette {
main_cursor,
extra_cursor: main_cursor,
forms: BASE_FORMS.iter().map(|(str, form)| (*str, *form)).collect(),
masks: vec![("", (0..BASE_FORMS.len() as u16).collect())],
}))
}
fn set_form(&self, name: impl AsRef<str>, form: Form) -> FormId {
let name = name.as_ref();
self.0.write().unwrap().set_form(name, form)
}
fn set_weak_form(&self, name: impl AsRef<str>, form: Form) -> FormId {
let name = name.as_ref();
self.0.write().unwrap().set_weak_form(name, form)
}
fn set_ref(&self, name: impl AsRef<str>, refed: u16, override_style: ContentStyle) -> FormId {
let name = name.as_ref();
self.0.write().unwrap().set_ref(name, refed, override_style)
}
fn set_weak_ref(
&self,
name: impl AsRef<str>,
refed: u16,
override_style: ContentStyle,
) -> FormId {
let name = name.as_ref();
let mut inner_palette = self.0.write().unwrap();
inner_palette.set_weak_ref(name, refed, override_style)
}
fn set_many<S: AsRef<str>>(&self, sets: &[(S, Option<Form>)]) -> Vec<FormId> {
let mut inner = self.0.write().unwrap();
let mut ids = Vec::new();
for (name, form) in sets {
let Some(form) = *form else {
let (idx, _) = position_and_form(&mut inner.forms, name);
ids.push(FormId(idx as u16));
continue;
};
ids.push(match form.kind {
FormKind::Normal => inner.set_form(name.as_ref(), form),
FormKind::Ref(refed, style) => inner.set_ref(name.as_ref(), refed, style),
FormKind::Weakest => inner.set_weak_form(name.as_ref(), form),
FormKind::WeakestRef(refed, style) => {
inner.set_weak_ref(name.as_ref(), refed, style)
}
});
}
ids
}
fn form_from_id(&self, id: FormId) -> Option<Form> {
let inner = self.0.read().unwrap();
inner.forms.get(id.0 as usize).map(|(_, form)| *form)
}
fn main_cursor(&self) -> (Form, Option<CursorShape>) {
let form = self.form_from_id(M_CAR_ID).unwrap();
(form, self.0.read().unwrap().main_cursor)
}
fn extra_cursor(&self) -> (Form, Option<CursorShape>) {
let form = self.form_from_id(E_CAR_ID).unwrap();
(form, self.0.read().unwrap().extra_cursor)
}
fn set_main_cursor(&self, shape: CursorShape) {
self.0.write().unwrap().main_cursor = Some(shape);
sender().send(DuatEvent::FormChange);
}
fn set_extra_cursor(&self, shape: CursorShape) {
self.0.write().unwrap().extra_cursor = Some(shape);
sender().send(DuatEvent::FormChange);
}
fn unset_main_cursor(&self) {
self.0.write().unwrap().main_cursor = None;
sender().send(DuatEvent::FormChange);
}
fn unset_extra_cursor(&self) {
self.0.write().unwrap().extra_cursor = None;
sender().send(DuatEvent::FormChange);
}
fn painter(&'static self, default_id: FormId) -> Painter {
let inner = self.0.read().unwrap();
let default = inner
.forms
.get(default_id.0 as usize)
.map(|(_, f)| *f)
.unwrap_or_default();
Painter {
inner,
applied_masks: vec![0],
default: (default, default_id),
forms: Vec::new(),
set_fg: true,
set_bg: true,
set_ul: true,
reset_attrs: true,
prev_style: None,
}
}
}
struct InnerPalette {
main_cursor: Option<CursorShape>,
extra_cursor: Option<CursorShape>,
forms: Vec<(&'static str, Form)>,
masks: Vec<(&'static str, Vec<u16>)>,
}
impl InnerPalette {
fn set_form(&mut self, name: &str, form: Form) -> FormId {
let (idx, _) = position_and_form(&mut self.forms, name);
self.forms[idx].1 = form;
for (referee, override_style) in refs_of(self, idx) {
mimic_form_to_referee(&mut self.forms[referee].1, form, override_style);
}
sender().send(DuatEvent::FormChange);
mask_form(name, idx, self);
let form_set = FormSet((self.forms[idx].0, FormId(idx as u16), form));
context::queue(move |pa| _ = hook::trigger(pa, form_set));
FormId(idx as u16)
}
fn set_weak_form(&mut self, name: &str, form: Form) -> FormId {
let (idx, _) = position_and_form(&mut self.forms, name);
let (_, f) = &mut self.forms[idx];
if let FormKind::Weakest | FormKind::WeakestRef(..) = f.kind {
*f = form;
f.kind = FormKind::Normal;
sender().send(DuatEvent::FormChange);
for (referee, override_style) in refs_of(self, idx) {
mimic_form_to_referee(&mut self.forms[referee].1, form, override_style);
}
mask_form(name, idx, self);
}
FormId(idx as u16)
}
fn set_ref(&mut self, name: &str, refed: u16, override_style: ContentStyle) -> FormId {
let (_, form) = self.forms[refed as usize];
let (idx, _) = position_and_form(&mut self.forms, name);
self.forms[idx].1 = form;
for (referee, override_style) in refs_of(self, idx) {
mimic_form_to_referee(&mut self.forms[referee].1, form, override_style);
}
if would_be_circular(self, idx, refed as usize) {
self.forms[idx].1.kind = FormKind::Normal;
} else {
self.forms[idx].1.kind = FormKind::Ref(refed, override_style);
}
sender().send(DuatEvent::FormChange);
mask_form(name, idx, self);
let form_set = FormSet((self.forms[idx].0, FormId(idx as u16), form));
context::queue(move |pa| _ = hook::trigger(pa, form_set));
FormId(idx as u16)
}
fn set_weak_ref(&mut self, name: &str, refed: u16, override_style: ContentStyle) -> FormId {
let (_, form) = self.forms[refed as usize];
let (idx, _) = position_and_form(&mut self.forms, name);
let (_, f) = &mut self.forms[idx];
if let FormKind::Weakest | FormKind::WeakestRef(..) = f.kind {
*f = form;
f.kind = FormKind::WeakestRef(refed, override_style);
sender().send(DuatEvent::FormChange);
for (referee, override_style) in refs_of(self, idx) {
mimic_form_to_referee(&mut self.forms[referee].1, form, override_style);
}
mask_form(name, idx, self);
}
FormId(idx as u16)
}
}
fn mimic_form_to_referee(referee: &mut Form, form: Form, override_style: ContentStyle) {
referee.style = form.style;
referee.style.attributes.extend(override_style.attributes);
if let Some(color) = override_style.foreground_color {
referee.style.foreground_color = Some(color);
}
if let Some(color) = override_style.background_color {
referee.style.background_color = Some(color);
}
if let Some(color) = override_style.underline_color {
referee.style.underline_color = Some(color);
}
}
fn mask_form(name: &str, form_i: usize, inner: &mut InnerPalette) {
if inner.masks[0].1.len() < inner.forms.len() {
for (_, remaps) in inner.masks.iter_mut() {
remaps.extend(remaps.len() as u16..inner.forms.len() as u16);
}
}
if let Some((pref, mask)) = name.rsplit_once(".")
&& let Some((_, remaps)) = inner.masks.iter_mut().find(|(m, _)| *m == mask)
&& let Some(j) = inner.forms.iter().position(|(name, ..)| *name == pref)
{
remaps[j] = form_i as u16;
}
}
pub struct Painter {
inner: RwLockReadGuard<'static, InnerPalette>,
applied_masks: Vec<usize>,
default: (Form, FormId),
forms: Vec<(Form, FormId, u8)>,
set_fg: bool,
set_bg: bool,
set_ul: bool,
reset_attrs: bool,
prev_style: Option<ContentStyle>,
}
impl Painter {
#[inline(always)]
pub fn apply(&mut self, id: FormId, prio: u8) {
let forms = &self.inner.forms;
let form = get_form_for(id, &self.applied_masks, forms, &self.inner.masks);
let gt = |(.., p): &&(_, _, u8)| *p > prio;
let i = self.forms.len() - self.forms.iter().rev().take_while(gt).count();
self.forms.insert(i, (form, id, prio));
self.set_fg |= form.fg().is_some();
self.set_bg |= form.bg().is_some();
self.set_ul |= form.ul().is_some();
self.reset_attrs |= form.attrs().has(Attribute::Reset);
}
#[inline(always)]
pub fn remove(&mut self, id: FormId) {
let mut applied_forms = self.forms.iter().enumerate();
if let Some((i, &(form, ..))) = applied_forms.rfind(|(_, (_, lhs, _))| *lhs == id) {
self.forms.remove(i);
self.set_fg |= form.fg().is_some();
self.set_bg |= form.bg().is_some();
self.set_ul |= form.ul().is_some();
self.reset_attrs |= !form.attrs().is_empty();
};
}
#[inline(always)]
pub fn reset(&mut self) -> ContentStyle {
self.forms.clear();
self.absolute_style()
}
#[inline(always)]
pub fn absolute_style(&self) -> ContentStyle {
let mut style = self.default.0.style;
for &(form, ..) in &self.forms {
style.foreground_color = form.fg().or(style.foreground_color);
style.background_color = form.bg().or(style.background_color);
style.underline_color = form.ul().or(style.underline_color);
style.attributes = if form.attrs().has(Attribute::Reset) {
form.attrs()
} else {
form.attrs() | style.attributes
}
}
style
}
#[inline(always)]
pub fn relative_style(&mut self) -> Option<ContentStyle> {
let abs_style = self.absolute_style();
let mut style = abs_style;
if style.attributes.has(Attribute::Reset) || self.reset_attrs {
style.attributes.set(Attribute::Reset);
} else {
style.foreground_color = self
.set_fg
.then_some(style.foreground_color.unwrap_or(Color::Reset))
.filter(|fg| Some(*fg) != self.prev_style.and_then(|s| s.foreground_color));
style.background_color = self
.set_bg
.then_some(style.background_color.unwrap_or(Color::Reset))
.filter(|bg| Some(*bg) != self.prev_style.and_then(|s| s.background_color));
style.underline_color = self
.set_ul
.then_some(style.underline_color.unwrap_or(Color::Reset))
.filter(|ul| Some(*ul) != self.prev_style.and_then(|s| s.underline_color));
}
self.set_fg = false;
self.set_bg = false;
self.set_ul = false;
self.reset_attrs = false;
if let Some(prev_style) = self.prev_style.replace(abs_style) {
(style != prev_style && style != Default::default()).then_some(style)
} else {
Some(style)
}
}
pub fn reset_prev_style(&mut self) {
self.prev_style = None;
self.set_fg = true;
self.set_bg = true;
self.set_ul = true;
self.reset_attrs = true;
}
#[inline(always)]
pub fn apply_main_selection(&mut self, is_caret: bool, start_range: bool) {
if is_caret {
self.apply(M_CAR_ID, 150);
}
if start_range {
self.apply(M_SEL_ID, 145);
}
}
#[inline(always)]
pub fn remove_main_selection(&mut self, is_caret: bool, end_range: bool) {
if is_caret {
self.remove(M_CAR_ID);
}
if end_range {
self.remove(M_SEL_ID);
}
}
#[inline(always)]
pub fn apply_extra_selection(&mut self, is_caret: bool, start_range: bool) {
if is_caret {
self.apply(E_CAR_ID, 149);
}
if start_range {
self.apply(E_SEL_ID, 144);
}
}
#[inline(always)]
pub fn remove_extra_selection(&mut self, is_caret: bool, end_range: bool) {
if is_caret {
self.remove(E_CAR_ID);
}
if end_range {
self.remove(E_SEL_ID);
}
}
pub fn apply_mask(&mut self, id: MaskId) {
self.reset_prev_style();
self.applied_masks.push(id.0 as usize);
let forms = &self.inner.forms;
for (form, id) in std::iter::once((&mut self.default.0, &mut self.default.1))
.chain(self.forms.iter_mut().map(|(form, id, _)| (form, id)))
{
*form = get_form_for(*id, &self.applied_masks, forms, &self.inner.masks);
}
}
pub fn remove_mask(&mut self, id: MaskId) {
self.reset_prev_style();
self.applied_masks.retain(|idx| *idx != id.0 as usize);
let forms = &self.inner.forms;
for (form, id) in std::iter::once((&mut self.default.0, &mut self.default.1))
.chain(self.forms.iter_mut().map(|(form, id, _)| (form, id)))
{
*form = get_form_for(*id, &self.applied_masks, forms, &self.inner.masks);
}
}
pub fn main_cursor(&self) -> Option<CursorShape> {
self.inner.main_cursor
}
pub fn extra_cursor(&self) -> Option<CursorShape> {
self.inner.extra_cursor
}
pub fn get_default(&self) -> Form {
self.default.0
}
}
#[derive(Default, Clone, Copy)]
enum FormKind {
#[default]
Normal,
Ref(u16, ContentStyle),
Weakest,
WeakestRef(u16, ContentStyle),
}
fn get_form_for(
id: FormId,
applied_masks: &[usize],
forms: &[(&str, Form)],
masks: &[(&'static str, Vec<u16>)],
) -> Form {
unsafe {
applied_masks
.iter()
.rev()
.find_map(|mask_id| {
let (_, mask) = masks.get(*mask_id).unwrap_unchecked();
let idx = mask
.get(id.0 as usize)
.copied()
.filter(|idx| *idx != id.0)?;
Some(forms.get(idx as usize).unwrap_unchecked().1)
})
.unwrap_or(forms.get(id.0 as usize).unwrap_unchecked().1)
}
}
fn refs_of(inner: &InnerPalette, refed: usize) -> Vec<(usize, ContentStyle)> {
let mut refs = Vec::new();
for (i, (_, form)) in inner.forms.iter().enumerate() {
if let FormKind::Ref(id, style) | FormKind::WeakestRef(id, style) = form.kind
&& id as usize == refed
{
refs.push((i, style));
refs.extend(refs_of(inner, i));
}
}
refs
}
fn would_be_circular(inner: &InnerPalette, referee: usize, refed: usize) -> bool {
if let FormKind::Ref(id, _) | FormKind::WeakestRef(id, _) = inner.forms[refed].1.kind {
match id as usize == referee {
true => true,
false => would_be_circular(inner, referee, id as usize),
}
} else {
false
}
}
fn position_and_form(
forms: &mut Vec<(&'static str, Form)>,
name: impl AsRef<str>,
) -> (usize, Form) {
let name = name.as_ref();
if let Some((i, (_, form))) = forms.iter().enumerate().find(|(_, (lhs, _))| *lhs == name) {
(i, *form)
} else if let Some((refed, _)) = name.rsplit_once('.') {
let (i, mut form) = position_and_form(forms, refed);
form.kind = FormKind::WeakestRef(i as u16, default_style());
forms.push((name.to_string().leak(), form));
(forms.len() - 1, form)
} else {
let mut form = Form::new();
form.kind = FormKind::Weakest;
forms.push((name.to_string().leak(), form));
(forms.len() - 1, form)
}
}
const fn str_to_color(str: &str) -> std::result::Result<Color, &'static str> {
const fn strip_prefix<'a>(prefix: &str, str: &'a str) -> Option<&'a str> {
let prefix = prefix.as_bytes();
let mut i = 0;
while i < prefix.len() {
if str.as_bytes()[i] != prefix[i] {
return None;
}
i += 1;
}
Some(str.split_at(prefix.len()).1)
}
const fn strip_suffix<'a>(suffix: &str, str: &'a str) -> Option<&'a str> {
let prefix = suffix.as_bytes();
let mut i = str.len() - 1;
while i >= str.len() - prefix.len() {
if str.as_bytes()[i] != prefix[i - (str.len() - prefix.len())] {
return None;
}
i += 1;
}
Some(str.split_at(str.len() - suffix.len()).0)
}
const fn split_space(str: &str) -> Option<(&str, &str)> {
if str.is_empty() {
return None;
}
let mut i = 0;
while i < str.len() {
if str.as_bytes()[i] == b' ' {
break;
}
i += 1;
}
let (cut, rest) = str.split_at(i);
let (_, rest) = rest.split_at(if rest.is_empty() { 0 } else { 1 });
Some((cut, rest))
}
const fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
t = if t < 0.0 { t + 1.0 } else { t };
t = if t > 1.0 { t - 1.0 } else { t };
if t < 1.0 / 6.0 {
p + (q - p) * 6.0 * t
} else if t < 1.0 / 2.0 {
q
} else if t < 2.0 / 3.0 {
p + (q - p) * (2.0 / 3.0 - t) * 6.0
} else {
p
}
}
if let Some(hex) = strip_prefix("#", str) {
let total = match u32::from_str_radix(hex, 16) {
Ok(total) if hex.len() == 6 => total,
_ => return Err("Hexcode does not contain 6 hex values"),
};
let r = (total >> 16) as u8;
let g = (total >> 8) as u8;
let b = total as u8;
Ok(Color::Rgb { r, g, b })
} else if let Some(mut hsl) = strip_prefix("hsl ", str) {
let mut values = [0.0, 0.0, 0.0];
let mut i = 0;
while i < values.len() {
if let Some((cut, rest)) = split_space(hsl) {
hsl = rest;
let (num, div) = match strip_suffix("%", cut) {
Some(perc) => (perc, 100),
None => (cut, 255),
};
values[i] = match u8::from_str_radix(num, 10) {
Ok(value) if value <= div => value as f32 / div as f32,
_ => return Err("Hsl format property could not be parsed"),
}
} else {
return Err("Missing value in hsl format");
}
i += 1;
}
let [hue, sat, lit] = values;
let (r, g, b) = if sat == 0.0 {
(lit, lit, lit)
} else {
let q = if lit < 0.5 {
lit * (1.0 + sat)
} else {
lit + sat - lit * sat
};
let p = 2.0 * lit - q;
let r = hue_to_rgb(p, q, hue + 1.0 / 3.0);
let g = hue_to_rgb(p, q, hue);
let b = hue_to_rgb(p, q, hue - 1.0 / 3.0);
(r, g, b)
};
let r = (0.5 + r * 255.0) as u8;
let g = (0.5 + g * 255.0) as u8;
let b = (0.5 + b * 255.0) as u8;
Ok(Color::Rgb { r, g, b })
} else {
Err("Color format was not recognized")
}
}
const fn default_style() -> ContentStyle {
ContentStyle {
foreground_color: None,
background_color: None,
underline_color: None,
attributes: Attributes::none(),
}
}
impl std::fmt::Debug for Form {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct DebugColor(Option<Color>);
impl std::fmt::Debug for DebugColor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
Some(Color::Rgb { r, g, b }) => write!(f, "Some(Rgb({r}, {g}, {b}))"),
Some(Color::AnsiValue(ansi)) => write!(f, "Some(Ansi({ansi}))"),
Some(color) => write!(f, "Some({color:?})"),
None => f.write_str("None"),
}
}
}
struct DebugAttributes(Attributes);
impl std::fmt::Debug for DebugAttributes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.0.is_empty() {
f.write_str("None")
} else {
let mut is_first = true;
for attr in Attribute::iterator() {
if self.0.has(attr) {
if !is_first {
f.write_str(" | ")?;
}
is_first = false;
write!(f, "{attr:?}")?;
}
}
Ok(())
}
}
}
f.debug_struct("Form")
.field("fg", &DebugColor(self.style.foreground_color))
.field("bg", &DebugColor(self.style.background_color))
.field("ul", &DebugColor(self.style.underline_color))
.field("attr", &DebugAttributes(self.style.attributes))
.finish()
}
}
impl std::fmt::Debug for InnerPalette {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct DebugForms<'a>(&'a [(&'static str, Form)]);
impl std::fmt::Debug for DebugForms<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
f.write_str("[\n")?;
let max = self.0.len().ilog10() as usize + 3;
for (n, (name, form)) in self.0.iter().enumerate() {
let num = format!("{n}:");
writeln!(f, "{num:<max$}({name}, {form:#?})")?;
}
f.write_str("]")
} else {
write!(f, "{:?}", self.0)
}
}
}
struct DebugCursorShape(CursorShape);
impl std::fmt::Debug for DebugCursorShape {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
CursorShape::DefaultUserShape => f.write_str("DefaultUserShape"),
CursorShape::BlinkingBlock => f.write_str("BlinkingBlock"),
CursorShape::SteadyBlock => f.write_str("SteadyBlock"),
CursorShape::BlinkingUnderScore => f.write_str("BlinkingUnderScore"),
CursorShape::SteadyUnderScore => f.write_str("SteadyUnderScore"),
CursorShape::BlinkingBar => f.write_str("BlinkingBar"),
CursorShape::SteadyBar => f.write_str("SteadyBar"),
}
}
}
f.debug_struct("InnerPalette")
.field("main_cursor", &self.main_cursor.map(DebugCursorShape))
.field("extra_cursor", &self.extra_cursor.map(DebugCursorShape))
.field("forms", &DebugForms(&self.forms))
.field("masks", &self.masks)
.finish()
}
}
impl std::fmt::Debug for FormKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Normal => write!(f, "Normal"),
Self::Ref(refed, _) => write!(f, "Ref({refed})"),
Self::Weakest => write!(f, "Weakest"),
Self::WeakestRef(refed, _) => write!(f, "WeakestRef({refed})"),
}
}
}
pub const DEFAULT_ID: FormId = FormId(0);
pub const ACCENT_ID: FormId = FormId(1);
pub const M_CAR_ID: FormId = FormId(2);
pub const E_CAR_ID: FormId = FormId(3);
pub const M_SEL_ID: FormId = FormId(4);
pub const E_SEL_ID: FormId = FormId(5);
pub const CONTROL_CHAR_ID: FormId = FormId(7);
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MaskId(u32);
impl MaskId {
pub fn name(&self) -> &'static str {
name_of_mask(*self)
}
}
impl std::fmt::Debug for MaskId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MaskId({:?})", name_of_mask(*self))
}
}