Merge pull request #41 from teohhanhui/riir

Put fore-back color indices in ascii art structs
This commit is contained in:
Teoh Han Hui 2024-07-27 04:41:44 +08:00 committed by GitHub
commit 52844b55ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 158 additions and 139 deletions

View file

@ -93,7 +93,7 @@ impl Distro {
for (variant, AsciiDistro { pattern, .. }) in &variants {
let patterns = pattern.split('|').map(|s| s.trim());
let mut conds = vec![];
let mut conds = Vec::new();
for m in patterns {
let stripped = m.trim_matches(['*', '\'', '"']).to_lowercase();
@ -214,7 +214,7 @@ where
let eof_re = Regex::new(r"EOF[ \n]*?;;").expect("couldn't compile eof regex");
// Split by blocks
let mut blocks = vec![];
let mut blocks = Vec::new();
for b in case_re.split(&nf) {
blocks.extend(eof_re.split(b).map(|sub| sub.trim()));
}

View file

@ -19,7 +19,9 @@ use crate::types::{AnsiMode, TerminalTheme};
/// Raw ascii art before any processing.
#[derive(Clone, Debug)]
pub struct RawAsciiArt {
pub art: String,
pub asc: String,
pub fg: Vec<NeofetchAsciiIndexedColor>,
pub bg: Vec<NeofetchAsciiIndexedColor>,
}
/// Normalized ascii art where every line has the same width.
@ -28,6 +30,8 @@ pub struct NormalizedAsciiArt {
pub lines: Vec<String>,
pub w: NonZeroU8,
pub h: NonZeroU8,
pub fg: Vec<NeofetchAsciiIndexedColor>,
pub bg: Vec<NeofetchAsciiIndexedColor>,
}
/// Recolored ascii art with all color codes replaced.
@ -44,11 +48,10 @@ impl RawAsciiArt {
pub fn to_normalized(&self) -> Result<NormalizedAsciiArt> {
debug!("normalize ascii");
let asc = &self.art;
let (w, h) = ascii_size(&self.asc).context("failed to get ascii size")?;
let (w, h) = ascii_size(asc).context("failed to get ascii size")?;
let lines = asc
let lines = self
.asc
.lines()
.map(|line| {
let (line_w, _) = ascii_size(line).unwrap();
@ -57,7 +60,13 @@ impl RawAsciiArt {
})
.collect();
Ok(NormalizedAsciiArt { lines, w, h })
Ok(NormalizedAsciiArt {
lines,
w,
h,
fg: self.fg.clone(),
bg: self.bg.clone(),
})
}
}
@ -75,28 +84,34 @@ impl NormalizedAsciiArt {
let reset = color("&~&*", color_mode).expect("color reset should not be invalid");
let lines = match color_align {
&ColorAlignment::Horizontal {
fore_back: Some((fore, back)),
} => {
let lines = match (color_align, self) {
(ColorAlignment::Horizontal, Self { fg, bg, .. })
if !fg.is_empty() || !bg.is_empty() =>
{
let Self { lines, .. } = self
.fill_starting()
.context("failed to fill in starting neofetch color codes")?;
let ac = NEOFETCH_COLORS_AC
.get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap());
// Replace foreground colors
let asc = {
let asc = lines.join("\n");
asc.replace(
&format!("${{c{fore}}}", fore = u8::from(fore)),
&color(
match theme {
TerminalTheme::Light => "&0",
TerminalTheme::Dark => "&f",
},
color_mode,
)
.expect("foreground color should not be invalid"),
let mut replacements = NEOFETCH_COLOR_PATTERNS;
let fg_color = color(
match theme {
TerminalTheme::Light => "&0",
TerminalTheme::Dark => "&f",
},
color_mode,
)
.expect("foreground color should not be invalid");
for &fore in fg {
replacements[usize::from(u8::from(fore)).checked_sub(1).unwrap()] =
&fg_color;
}
ac.replace_all(&asc, &replacements)
};
let lines = asc.lines();
@ -107,15 +122,17 @@ impl NormalizedAsciiArt {
format!("failed to spread color profile to length {h}", h = self.h)
})?;
lines.enumerate().map(move |(i, line)| {
let line = line.replace(
&format!("${{c{back}}}", back = u8::from(back)),
&colors[i].to_ansi_string(color_mode, {
// This is "background" in the ascii art, but foreground text in
// terminal
ForegroundBackground::Foreground
}),
);
format!("{line}{reset}")
let mut replacements = NEOFETCH_COLOR_PATTERNS;
let bg_color = colors[i].to_ansi_string(color_mode, {
// This is "background" in the ascii art, but foreground text in
// terminal
ForegroundBackground::Foreground
});
for &back in bg {
replacements[usize::from(u8::from(back)).checked_sub(1).unwrap()] =
&bg_color;
}
ac.replace_all(line, &replacements)
})
};
@ -123,19 +140,18 @@ impl NormalizedAsciiArt {
let asc = {
let mut lines = lines;
let asc = lines.join("\n");
let ac = NEOFETCH_COLORS_AC
.get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap());
const N: usize = NEOFETCH_COLOR_PATTERNS.len();
const REPLACEMENTS: [&str; N] = [""; N];
ac.replace_all(&asc, &REPLACEMENTS)
let replacements: [&str; N] = [&reset; N];
ac.replace_all(&asc, &replacements)
};
let lines = asc.lines();
lines.map(ToOwned::to_owned).collect()
// Reset colors at end of each line to prevent color bleeding
let lines = lines.map(|line| format!("{line}{reset}"));
lines.collect()
},
&ColorAlignment::Vertical {
fore_back: Some((fore, back)),
} => {
(ColorAlignment::Vertical, Self { fg, bg, .. }) if !fg.is_empty() || !bg.is_empty() => {
let Self { lines, .. } = self
.fill_starting()
.context("failed to fill in starting neofetch color codes")?;
@ -201,7 +217,7 @@ impl NormalizedAsciiArt {
};
let txt = &line[span];
if neofetch_color_idx == fore {
if fg.contains(&neofetch_color_idx) {
let fore = color(
match theme {
TerminalTheme::Light => "&0",
@ -211,7 +227,7 @@ impl NormalizedAsciiArt {
)
.expect("foreground color should not be invalid");
write!(dst, "{fore}{txt}{reset}").unwrap();
} else if neofetch_color_idx == back {
} else if bg.contains(&neofetch_color_idx) {
let adjusted_start =
span.start.checked_sub(usize::from(offset)).unwrap();
let adjusted_end =
@ -247,8 +263,10 @@ impl NormalizedAsciiArt {
lines
},
ColorAlignment::Horizontal { fore_back: None }
| ColorAlignment::Vertical { fore_back: None } => {
(ColorAlignment::Horizontal, Self { fg, bg, .. })
| (ColorAlignment::Vertical, Self { fg, bg, .. })
if fg.is_empty() && bg.is_empty() =>
{
// Remove existing colors
let asc = {
let asc = self.lines.join("\n");
@ -262,7 +280,7 @@ impl NormalizedAsciiArt {
// Add new colors
match color_align {
ColorAlignment::Horizontal { .. } => {
ColorAlignment::Horizontal => {
let ColorProfile { colors } =
color_profile.with_length(self.h).with_context(|| {
format!("failed to spread color profile to length {h}", h = self.h)
@ -276,7 +294,7 @@ impl NormalizedAsciiArt {
})
.collect()
},
ColorAlignment::Vertical { .. } => lines
ColorAlignment::Vertical => lines
.map(|line| {
let line = color_profile
.color_text(
@ -294,9 +312,12 @@ impl NormalizedAsciiArt {
},
}
},
ColorAlignment::Custom {
colors: custom_colors,
} => {
(
ColorAlignment::Custom {
colors: custom_colors,
},
_,
) => {
let Self { lines, .. } = self
.fill_starting()
.context("failed to fill in starting neofetch color codes")?;
@ -327,6 +348,9 @@ impl NormalizedAsciiArt {
lines.collect()
},
_ => {
unreachable!()
},
};
Ok(RecoloredAsciiArt {
@ -377,6 +401,11 @@ impl NormalizedAsciiArt {
})
.collect::<Result<_>>()?;
Ok(Self { lines, ..*self })
Ok(Self {
lines,
fg: self.fg.clone(),
bg: self.bg.clone(),
..*self
})
}
}

View file

@ -60,8 +60,8 @@ fn main() -> Result<()> {
let backend = options.backend.unwrap_or(Backend::Neofetch);
if options.test_print {
let (asc, _) = get_distro_ascii(distro, backend).context("failed to get distro ascii")?;
writeln!(io::stdout(), "{asc}", asc = asc.art)
let asc = get_distro_ascii(distro, backend).context("failed to get distro ascii")?;
writeln!(io::stdout(), "{asc}", asc = asc.asc)
.context("failed to write ascii to stdout")?;
return Ok(());
}
@ -127,27 +127,18 @@ fn main() -> Result<()> {
};
debug!(?color_profile, "lightened color profile");
let (asc, fore_back) = if let Some(path) = options.ascii_file {
(
RawAsciiArt {
art: fs::read_to_string(&path)
.with_context(|| format!("failed to read ascii from {path:?}"))?,
},
None,
)
let asc = if let Some(path) = options.ascii_file {
RawAsciiArt {
asc: fs::read_to_string(&path)
.with_context(|| format!("failed to read ascii from {path:?}"))?,
fg: Vec::new(),
bg: Vec::new(),
}
} else {
get_distro_ascii(distro, backend).context("failed to get distro ascii")?
};
let asc = asc.to_normalized().context("failed to normalize ascii")?;
let color_align = if fore_back.is_some() {
match config.color_align {
ColorAlignment::Horizontal { .. } => ColorAlignment::Horizontal { fore_back },
ColorAlignment::Vertical { .. } => ColorAlignment::Vertical { fore_back },
ca @ ColorAlignment::Custom { .. } => ca,
}
} else {
config.color_align
};
let color_align = config.color_align;
let asc = asc
.to_recolored(&color_align, &color_profile, color_mode, theme)
.context("failed to recolor ascii")?;
@ -229,8 +220,7 @@ fn create_config(
});
debug!(?det_ansi, "detected color mode");
let (asc, fore_back) =
get_distro_ascii(distro, backend).context("failed to get distro ascii")?;
let asc = get_distro_ascii(distro, backend).context("failed to get distro ascii")?;
let asc = asc.to_normalized().context("failed to normalize ascii")?;
let theme = det_bg.map(|bg| bg.theme()).unwrap_or(TerminalTheme::Light);
let color_mode = det_ansi.unwrap_or(AnsiMode::Ansi256);
@ -645,7 +635,7 @@ fn create_config(
)
.context("failed to write message to stdout")?;
let color_align = ColorAlignment::Horizontal { fore_back: None };
let color_align = ColorAlignment::Horizontal;
// Print cats
{
@ -672,13 +662,15 @@ fn create_config(
let row: Vec<Vec<String>> = ratios
.map(|r| {
let asc = RawAsciiArt {
art: test_ascii.replace(
asc: test_ascii.replace(
"{txt}",
&format!(
"{lightness:^5}",
lightness = format!("{lightness:.0}%", lightness = r * 100.0)
),
),
fg: Vec::new(),
bg: Vec::new(),
};
let asc = asc
.to_normalized()
@ -797,8 +789,8 @@ fn create_config(
// Displays horizontal and vertical arrangements in the first iteration, but
// hide them in later iterations
let hv_arrangements = [
("Horizontal", ColorAlignment::Horizontal { fore_back }),
("Vertical", ColorAlignment::Vertical { fore_back }),
("Horizontal", ColorAlignment::Horizontal),
("Vertical", ColorAlignment::Vertical),
];
let mut arrangements: IndexMap<Cow<str>, ColorAlignment> =
hv_arrangements.map(|(k, ca)| (k.into(), ca)).into();

View file

@ -51,21 +51,13 @@ pub const NEOFETCH_COLOR_PATTERNS: [&str; 6] =
["${c1}", "${c2}", "${c3}", "${c4}", "${c5}", "${c6}"];
pub static NEOFETCH_COLORS_AC: OnceLock<AhoCorasick> = OnceLock::new();
type ForeBackColorPair = (NeofetchAsciiIndexedColor, NeofetchAsciiIndexedColor);
#[derive(Clone, Eq, PartialEq, Debug, AsRefStr, Deserialize, Serialize)]
#[serde(tag = "mode")]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum ColorAlignment {
Horizontal {
#[serde(skip)]
fore_back: Option<ForeBackColorPair>,
},
Vertical {
#[serde(skip)]
fore_back: Option<ForeBackColorPair>,
},
Horizontal,
Vertical,
Custom {
#[serde(rename = "custom_colors")]
#[serde(deserialize_with = "crate::utils::index_map_serde::deserialize")]
@ -73,49 +65,6 @@ pub enum ColorAlignment {
},
}
impl ColorAlignment {
/// Gets recommended foreground-background configuration for distro, or
/// `None` if the distro ascii is not suitable for fore-back configuration.
fn fore_back(distro: Distro) -> Option<ForeBackColorPair> {
match distro {
Distro::Anarchy
| Distro::ArchStrike
| Distro::Astra_Linux
| Distro::Chapeau
| Distro::Fedora
| Distro::GalliumOS
| Distro::KrassOS
| Distro::Kubuntu
| Distro::Lubuntu
| Distro::openEuler
| Distro::Peppermint
| Distro::Pop__OS
| Distro::Ubuntu_Cinnamon
| Distro::Ubuntu_Kylin
| Distro::Ubuntu_MATE
| Distro::Ubuntu_old
| Distro::Ubuntu_Studio
| Distro::Ubuntu_Sway
| Distro::Ultramarine_Linux
| Distro::Univention
| Distro::Vanilla
| Distro::Xubuntu => Some((2, 1)),
Distro::Antergos => Some((1, 2)),
_ => None,
}
.map(|(fore, back): (u8, u8)| {
(
fore.try_into()
.expect("`fore` should be a valid neofetch color index"),
back.try_into()
.expect("`back` should be a valid neofetch color index"),
)
})
}
}
/// Asks the user to provide an input among a list of options.
pub fn literal_input<'a, S1, S2>(
prompt: S1,
@ -325,10 +274,7 @@ pub fn macchina_path() -> Result<Option<PathBuf>> {
/// Gets the distro ascii of the current distro. Or if distro is specified, get
/// the specific distro's ascii art instead.
#[tracing::instrument(level = "debug")]
pub fn get_distro_ascii<S>(
distro: Option<S>,
backend: Backend,
) -> Result<(RawAsciiArt, Option<ForeBackColorPair>)>
pub fn get_distro_ascii<S>(distro: Option<S>, backend: Backend) -> Result<RawAsciiArt>
where
S: AsRef<str> + fmt::Debug,
{
@ -343,12 +289,10 @@ where
// Try new codegen-based detection method
if let Some(distro) = Distro::detect(&distro) {
return Ok((
RawAsciiArt {
art: distro.ascii_art().to_owned(),
},
ColorAlignment::fore_back(distro),
));
let asc = distro.ascii_art().to_owned();
let (fg, bg) = fore_back(&distro);
return Ok(RawAsciiArt { asc, fg, bg });
}
debug!(%distro, "could not find a match for distro; falling back to neofetch");
@ -361,7 +305,11 @@ where
// printf
let asc = asc.replace(r"\\", r"\");
Ok((RawAsciiArt { art: asc }, None))
Ok(RawAsciiArt {
asc,
fg: Vec::new(),
bg: Vec::new(),
})
}
#[tracing::instrument(level = "debug", skip(asc), fields(asc.w = asc.w, asc.h = asc.h))]
@ -878,3 +826,53 @@ fn run_macchina(asc: String, args: Option<&Vec<String>>) -> Result<()> {
Ok(())
}
/// Gets recommended foreground-background configuration for distro.
fn fore_back(
distro: &Distro,
) -> (
Vec<NeofetchAsciiIndexedColor>,
Vec<NeofetchAsciiIndexedColor>,
) {
let (fg, bg): (Vec<u8>, Vec<u8>) = match distro {
Distro::Anarchy => (vec![2], vec![1]),
Distro::Antergos => (vec![1], vec![2]),
Distro::ArchStrike => (vec![2], vec![1]),
Distro::Astra_Linux => (vec![2], vec![1]),
Distro::Chapeau => (vec![2], vec![1]),
Distro::Fedora => (vec![2], vec![1]),
Distro::Fedora_Silverblue => (vec![2], vec![1, 3]),
Distro::GalliumOS => (vec![2], vec![1]),
Distro::KrassOS => (vec![2], vec![1]),
Distro::Kubuntu => (vec![2], vec![1]),
Distro::Lubuntu => (vec![2], vec![1]),
Distro::openEuler => (vec![2], vec![1]),
Distro::Peppermint => (vec![2], vec![1]),
Distro::Pop__OS => (vec![2], vec![1]),
Distro::Ubuntu_Cinnamon => (vec![2], vec![1]),
Distro::Ubuntu_Kylin => (vec![2], vec![1]),
Distro::Ubuntu_MATE => (vec![2], vec![1]),
Distro::Ubuntu_old => (vec![2], vec![1]),
Distro::Ubuntu_Studio => (vec![2], vec![1]),
Distro::Ubuntu_Sway => (vec![2], vec![1]),
Distro::Ultramarine_Linux => (vec![2], vec![1]),
Distro::Univention => (vec![2], vec![1]),
Distro::Vanilla => (vec![2], vec![1]),
Distro::Xubuntu => (vec![2], vec![1]),
_ => (Vec::new(), Vec::new()),
};
(
fg.into_iter()
.map(|fore| {
fore.try_into()
.expect("`fore` should be a valid neofetch color index")
})
.collect(),
bg.into_iter()
.map(|back| {
back.try_into()
.expect("`back` should be a valid neofetch color index")
})
.collect(),
)
}

View file

@ -492,7 +492,7 @@ impl ColorProfile {
));
}
let mut weighted_colors = vec![];
let mut weighted_colors = Vec::new();
for (i, w) in weights.into_iter().enumerate() {
weighted_colors.extend(iter::repeat(self.colors[i]).take(usize::from(w)));