use std::fmt::Write;
pub const GROUPVAR: &str = ".zle.hlgroups";
pub fn convertattr(attrstr: &str, sgr: bool) -> String { let mut esc_stream = String::new();
for part in attrstr.split(',') {
let part = part.trim();
let attr_n: Option<i32> = match part {
"" | "none" | "reset" => Some(0),
"bold" => Some(1),
"dim" | "faint" => Some(2),
"italic" => Some(3),
"underline" => Some(4),
"blink" => Some(5),
"reverse" | "inverse" => Some(7),
"hidden" | "invisible" => Some(8),
"strikethrough" => Some(9),
_ => None,
};
if let Some(n) = attr_n {
let _ = write!(esc_stream, "\x1b[{}m", n);
continue;
}
let (is_fg, rest) = if let Some(r) = part.strip_prefix("fg=") {
(true, r)
} else if let Some(r) = part.strip_prefix("bg=") {
(false, r)
} else {
continue;
};
let base = if is_fg { 30 } else { 40 };
let bright_base = if is_fg { 90 } else { 100 };
let prefix = if is_fg { 38 } else { 48 };
let named: Option<i32> = match rest {
"black" => Some(base),
"red" => Some(base + 1),
"green" => Some(base + 2),
"yellow" => Some(base + 3),
"blue" => Some(base + 4),
"magenta" => Some(base + 5),
"cyan" => Some(base + 6),
"white" => Some(base + 7),
"default" => Some(base + 9),
_ => None,
};
if let Some(n) = named {
let _ = write!(esc_stream, "\x1b[{}m", n);
continue;
}
if let Some(inner) = rest.strip_prefix("bright-")
.or_else(|| rest.strip_prefix("light-"))
{
let bn: Option<i32> = match inner {
"black" => Some(bright_base),
"red" => Some(bright_base + 1),
"green" => Some(bright_base + 2),
"yellow" => Some(bright_base + 3),
"blue" => Some(bright_base + 4),
"magenta" => Some(bright_base + 5),
"cyan" => Some(bright_base + 6),
"white" => Some(bright_base + 7),
_ => None,
};
if let Some(n) = bn {
let _ = write!(esc_stream, "\x1b[{}m", n);
continue;
}
}
if let Ok(n) = rest.parse::<u8>() {
let _ = write!(esc_stream, "\x1b[{};5;{}m", prefix, n);
continue;
}
if let Some(hex) = rest.strip_prefix('#') {
if hex.len() == 6 {
let r = u8::from_str_radix(&hex[0..2], 16);
let g = u8::from_str_radix(&hex[2..4], 16);
let b = u8::from_str_radix(&hex[4..6], 16);
if let (Ok(r), Ok(g), Ok(b)) = (r, g, b) {
let _ = write!(esc_stream, "\x1b[{};2;{};{};{}m",
prefix, r, g, b);
}
}
}
}
if sgr {
let bytes = esc_stream.as_bytes();
let mut out = String::new();
let mut i = 0;
while i + 1 < bytes.len() && bytes[i] == 0x1b && bytes[i + 1] == b'[' {
i += 2; while i < bytes.len() {
let b = bytes[i];
if b.is_ascii_digit() { out.push(b as char); i += 1;
} else if b == b';' || b == b':' { out.push(';'); i += 1;
} else {
break; }
}
if i >= bytes.len() || bytes[i] != b'm' {
break; }
out.push(';'); i += 1; }
while out.ends_with(';') {
out.pop();
}
if out.is_empty() {
out.push('0');
}
out
} else {
esc_stream }
}
pub fn getgroup(_name: &str, _sgr: bool) -> Option<String> { None }
pub fn scangroup(_sgr: bool) -> Vec<(String, String)> { Vec::new() }
pub fn getpmesc(name: &str) -> Option<String> { getgroup(name, false) }
pub fn scanpmesc() -> Vec<(String, String)> { scangroup(false) }
pub fn getpmsgr(name: &str) -> Option<String> { getgroup(name, true) }
pub fn scanpmsgr() -> Vec<(String, String)> { scangroup(true) }
use crate::ported::zsh_h::module;
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 { 0 }
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 { *features = featuresarray(m, module_features());
0 }
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 { handlefeatures(m, module_features(), enables) }
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 { 0 }
pub fn cleanup_(m: *const module) -> i32 { setfeatureenables(m, module_features(), None) }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 { 0 }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn convertattr_bold_escape() {
assert_eq!(convertattr("bold", false), "\x1b[1m");
}
#[test]
fn convertattr_chained_escape() {
let s = convertattr("bold,underline", false);
assert!(s.contains("\x1b[1m"));
assert!(s.contains("\x1b[4m"));
}
#[test]
fn convertattr_fg_red_escape() {
let s = convertattr("fg=red", false);
assert!(s.contains("\x1b[31m"));
}
#[test]
fn convertattr_sgr_bold() {
assert_eq!(convertattr("bold", true), "1");
}
#[test]
fn convertattr_sgr_chain() {
let s = convertattr("bold,underline", true);
assert!(s.contains('1'));
assert!(s.contains('4'));
}
#[test]
fn convertattr_sgr_empty_returns_zero() {
assert_eq!(convertattr("", true), "0");
}
#[test]
fn convertattr_256_color() {
let s = convertattr("fg=196", false);
assert!(s.contains("\x1b[38;5;196m"));
}
#[test]
fn convertattr_truecolor() {
let s = convertattr("fg=#ff0000", false);
assert!(s.contains("\x1b[38;2;255;0;0m"));
}
#[test]
fn convertattr_sgr_256_color() {
let s = convertattr("fg=196", true);
assert!(s.contains("38;5;196"));
}
#[test]
fn convertattr_sgr_truecolor() {
let s = convertattr("fg=#00ff00", true);
assert!(s.contains("38;2;0;255;0"));
}
#[test]
fn getgroup_returns_none_until_paramtable_wired() {
assert_eq!(getgroup("any", false), None);
assert_eq!(getgroup("any", true), None);
}
#[test]
fn scangroup_returns_empty_until_paramtable_wired() {
assert!(scangroup(false).is_empty());
assert!(scangroup(true).is_empty());
}
}
use crate::ported::zsh_h::features as features_t;
use std::sync::{Mutex, OnceLock};
static MODULE_FEATURES: OnceLock<Mutex<features_t>> = OnceLock::new();
fn module_features() -> &'static Mutex<features_t> {
MODULE_FEATURES.get_or_init(|| Mutex::new(features_t {
bn_list: None,
bn_size: 0,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 2,
n_abstract: 0,
}))
}
fn featuresarray(_m: *const module, _f: &Mutex<features_t>) -> Vec<String> {
vec!["p:.zle.esc".to_string(), "p:.zle.sgr".to_string()]
}
fn handlefeatures(
_m: *const module,
_f: &Mutex<features_t>,
enables: &mut Option<Vec<i32>>,
) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 2]);
}
0
}
fn setfeatureenables(
_m: *const module,
_f: &Mutex<features_t>,
_e: Option<&[i32]>,
) -> i32 {
0
}