mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Healed field for RgbaColors, CodeRev feedback 🤝
This commit is contained in:
parent
28c3a797ec
commit
1683ca87f7
@ -116,42 +116,57 @@ impl Debug for Ident {
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct RgbaColor {
|
pub struct RgbaColor {
|
||||||
r: u8,
|
/// Red channel.
|
||||||
g: u8,
|
pub r: u8,
|
||||||
b: u8,
|
/// Green channel.
|
||||||
a: u8,
|
pub g: u8,
|
||||||
|
/// Blue channel.
|
||||||
|
pub b: u8,
|
||||||
|
/// Alpha channel.
|
||||||
|
pub a: u8,
|
||||||
|
/// Indicates whether this is a user-provided value or a
|
||||||
|
/// default value provided as a fail-over by the parser.
|
||||||
|
/// This color may be overwritten if this property is true.
|
||||||
|
pub healed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RgbaColor {
|
impl RgbaColor {
|
||||||
|
/// Constructs a new color.
|
||||||
pub fn new(r: u8, g: u8, b: u8, a: u8) -> RgbaColor {
|
pub fn new(r: u8, g: u8, b: u8, a: u8) -> RgbaColor {
|
||||||
RgbaColor { r, g, b, a }
|
RgbaColor { r, g, b, a, healed: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a new color with the healed property set to true.
|
||||||
|
pub fn new_healed(r: u8, g: u8, b: u8, a: u8) -> RgbaColor {
|
||||||
|
RgbaColor { r, g, b, a, healed: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a new color from a hex string like `7a03c2`.
|
||||||
|
/// Do not specify a leading `#`.
|
||||||
pub fn from_str(hex_str: &str) -> Option<RgbaColor> {
|
pub fn from_str(hex_str: &str) -> Option<RgbaColor> {
|
||||||
let len = hex_str.len();
|
if !hex_str.is_ascii() {
|
||||||
let permissable = &[3, 4, 6, 8];
|
|
||||||
|
|
||||||
if !permissable.contains(&len) {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let long = len == 6 || len == 8;
|
let len = hex_str.len();
|
||||||
|
let long = len == 6 || len == 8;
|
||||||
|
let short = len == 3 || len == 4;
|
||||||
let alpha = len == 4 || len == 8;
|
let alpha = len == 4 || len == 8;
|
||||||
|
|
||||||
|
if !long && !short {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let mut values: [u8; 4] = [255; 4];
|
let mut values: [u8; 4] = [255; 4];
|
||||||
|
|
||||||
for elem in if alpha { 0..4 } else { 0..3 } {
|
for elem in if alpha { 0..4 } else { 0..3 } {
|
||||||
let item_len = if long { 2 } else { 1 };
|
let item_len = if long { 2 } else { 1 };
|
||||||
let pos = elem * item_len;
|
let pos = elem * item_len;
|
||||||
|
|
||||||
if let Ok(val) = u8::from_str_radix(
|
let item = &hex_str[pos..(pos+item_len)];
|
||||||
&hex_str[pos..(pos+item_len)], 16) {
|
values[elem] = u8::from_str_radix(item, 16).ok()?;
|
||||||
values[elem] = val;
|
|
||||||
} else {
|
|
||||||
// Some non-hexadecimal characters slipped into the color
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !long {
|
if short {
|
||||||
// Duplicate number for shorthand notation, i.e. `a` -> `aa`
|
// Duplicate number for shorthand notation, i.e. `a` -> `aa`
|
||||||
values[elem] += values[elem] * 16;
|
values[elem] += values[elem] * 16;
|
||||||
}
|
}
|
||||||
@ -171,13 +186,18 @@ impl Debug for RgbaColor {
|
|||||||
f.write_fmt(format_args!("g: {:02}, ", self.g))?;
|
f.write_fmt(format_args!("g: {:02}, ", self.g))?;
|
||||||
f.write_fmt(format_args!("b: {:02}, ", self.b))?;
|
f.write_fmt(format_args!("b: {:02}, ", self.b))?;
|
||||||
f.write_fmt(format_args!("a: {:02}", self.a))?;
|
f.write_fmt(format_args!("a: {:02}", self.a))?;
|
||||||
f.write_char(')')
|
f.write_char(')')?;
|
||||||
} else {
|
} else {
|
||||||
f.write_char('#')?;
|
f.write_char('#')?;
|
||||||
f.write_fmt(format_args!("{:02x}", self.r))?;
|
f.write_fmt(format_args!("{:02x}", self.r))?;
|
||||||
f.write_fmt(format_args!("{:02x}", self.g))?;
|
f.write_fmt(format_args!("{:02x}", self.g))?;
|
||||||
f.write_fmt(format_args!("{:02x}", self.b))?;
|
f.write_fmt(format_args!("{:02x}", self.b))?;
|
||||||
f.write_fmt(format_args!("{:02x}", self.a))
|
f.write_fmt(format_args!("{:02x}", self.a))?;
|
||||||
|
}
|
||||||
|
if self.healed {
|
||||||
|
f.write_fmt(format_args!(" [healed]"))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,8 +296,9 @@ impl Debug for Tuple {
|
|||||||
/// ```typst
|
/// ```typst
|
||||||
/// hsl(93, 10, 19.4)
|
/// hsl(93, 10, 19.4)
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct NamedTuple {
|
pub struct NamedTuple {
|
||||||
|
/// The name of the tuple and where it is in the user source.
|
||||||
pub name: Spanned<Ident>,
|
pub name: Spanned<Ident>,
|
||||||
/// The elements of the tuple.
|
/// The elements of the tuple.
|
||||||
pub tuple: Spanned<Tuple>,
|
pub tuple: Spanned<Tuple>,
|
||||||
@ -298,15 +319,6 @@ impl Deref for NamedTuple {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for NamedTuple {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
f.debug_struct("named tuple")
|
|
||||||
.field("name", &self.name)
|
|
||||||
.field("values", &self.tuple)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A key-value collection of identifiers and associated expressions.
|
/// A key-value collection of identifiers and associated expressions.
|
||||||
///
|
///
|
||||||
/// The pairs themselves are not spanned, but the combined spans can easily be
|
/// The pairs themselves are not spanned, but the combined spans can easily be
|
||||||
|
@ -253,15 +253,13 @@ impl<'s> FuncParser<'s> {
|
|||||||
Token::ExprSize(s) => take!(Expr::Size(s)),
|
Token::ExprSize(s) => take!(Expr::Size(s)),
|
||||||
Token::ExprBool(b) => take!(Expr::Bool(b)),
|
Token::ExprBool(b) => take!(Expr::Bool(b)),
|
||||||
Token::ExprHex(s) => {
|
Token::ExprHex(s) => {
|
||||||
let color_opt = RgbaColor::from_str(s);
|
if let Some(color) = RgbaColor::from_str(s) {
|
||||||
|
|
||||||
if let Some(color) = color_opt {
|
|
||||||
take!(Expr::Color(color))
|
take!(Expr::Color(color))
|
||||||
} else {
|
} else {
|
||||||
// Heal color by assuming black
|
// Heal color by assuming black
|
||||||
self.feedback.errors.push(err!(first.span;
|
self.feedback.errors.push(err!(first.span;
|
||||||
"expected valid color, found invalid color #{}", s));
|
"invalid color"));
|
||||||
take!(Expr::Color(RgbaColor::new(0, 0, 0, 255)))
|
take!(Expr::Color(RgbaColor::new_healed(0, 0, 0, 255)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -286,8 +284,8 @@ impl<'s> FuncParser<'s> {
|
|||||||
/// Parse a tuple expression: `name(<expr>, ...)` with a given identifier.
|
/// Parse a tuple expression: `name(<expr>, ...)` with a given identifier.
|
||||||
fn parse_named_tuple(&mut self, name: Spanned<Ident>) -> Spanned<NamedTuple> {
|
fn parse_named_tuple(&mut self, name: Spanned<Ident>) -> Spanned<NamedTuple> {
|
||||||
let tuple = self.parse_tuple();
|
let tuple = self.parse_tuple();
|
||||||
let start = name.span.start.clone();
|
let start = name.span.start;
|
||||||
let end = tuple.span.end.clone();
|
let end = tuple.span.end;
|
||||||
Spanned::new(NamedTuple::new(name, tuple), Span::new(start, end))
|
Spanned::new(NamedTuple::new(name, tuple), Span::new(start, end))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,14 +547,22 @@ mod tests {
|
|||||||
fn Id(text: &str) -> Expr { Expr::Ident(Ident(text.to_string())) }
|
fn Id(text: &str) -> Expr { Expr::Ident(Ident(text.to_string())) }
|
||||||
fn Str(text: &str) -> Expr { Expr::Str(text.to_string()) }
|
fn Str(text: &str) -> Expr { Expr::Str(text.to_string()) }
|
||||||
fn Pt(points: f32) -> Expr { Expr::Size(Size::pt(points)) }
|
fn Pt(points: f32) -> Expr { Expr::Size(Size::pt(points)) }
|
||||||
|
|
||||||
fn ClrS(color: &str) -> Expr {
|
fn ClrS(color: &str) -> Expr {
|
||||||
Expr::Color(
|
Expr::Color(
|
||||||
RgbaColor::from_str(color).expect("Test color invalid")
|
RgbaColor::from_str(color).expect("Test color invalid")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
fn ClrS_Healed() -> Expr {
|
||||||
|
let mut c = RgbaColor::from_str("000f")
|
||||||
|
.expect("Test color invalid");
|
||||||
|
c.healed = true;
|
||||||
|
Expr::Color(c)
|
||||||
|
}
|
||||||
fn Clr(r: u8, g: u8, b: u8, a: u8) -> Expr {
|
fn Clr(r: u8, g: u8, b: u8, a: u8) -> Expr {
|
||||||
Expr::Color(RgbaColor::new(r, g, b, a))
|
Expr::Color(RgbaColor::new(r, g, b, a))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn T(text: &str) -> Node { Node::Text(text.to_string()) }
|
fn T(text: &str) -> Node { Node::Text(text.to_string()) }
|
||||||
|
|
||||||
/// Create a raw text node.
|
/// Create a raw text node.
|
||||||
@ -819,17 +825,17 @@ mod tests {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
//Invalid colors
|
//Invalid colors
|
||||||
p!("[val: #12345]" => [func!("val": (ClrS("000f")), {})], [
|
p!("[val: #12345]" => [func!("val": (ClrS_Healed()), {})], [
|
||||||
(0:6, 0:12, "expected valid color, found invalid color #12345"),
|
(0:6, 0:12, "invalid color"),
|
||||||
]);
|
]);
|
||||||
p!("[val: #a5]" => [func!("val": (ClrS("000f")), {})], [
|
p!("[val: #a5]" => [func!("val": (ClrS_Healed()), {})], [
|
||||||
(0:6, 0:9, "expected valid color, found invalid color #a5"),
|
(0:6, 0:9, "invalid color"),
|
||||||
]);
|
]);
|
||||||
p!("[val: #14b2ah]" => [func!("val": (ClrS("000f")), {})], [
|
p!("[val: #14b2ah]" => [func!("val": (ClrS_Healed()), {})], [
|
||||||
(0:6, 0:13, "expected valid color, found invalid color #14b2a"),
|
(0:6, 0:13, "invalid color"),
|
||||||
]);
|
]);
|
||||||
p!("[val: #f075ff011]" => [func!("val": (ClrS("000f")), {})], [
|
p!("[val: #f075ff011]" => [func!("val": (ClrS_Healed()), {})], [
|
||||||
(0:6, 0:16, "expected valid color, found invalid color #f075ff011"),
|
(0:6, 0:16, "invalid color"),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,24 +224,13 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
// An escaped thing.
|
// An escaped thing.
|
||||||
'\\' if self.mode == Body => self.parse_escaped(),
|
'\\' if self.mode == Body => self.parse_escaped(),
|
||||||
|
|
||||||
|
// A hex expression.
|
||||||
|
'#' if self.mode == Header => self.parse_hex_value(),
|
||||||
|
|
||||||
// Expressions or just strings.
|
// Expressions or just strings.
|
||||||
c => {
|
c => {
|
||||||
let body = self.mode == Body;
|
let body = self.mode == Body;
|
||||||
|
|
||||||
// This may be a hex expression
|
|
||||||
let hex_expr = if c == '#' && !body {
|
|
||||||
let payload = self.read_string_until(|n| {
|
|
||||||
match n {
|
|
||||||
'0'..='9' | 'a'..='f' | 'A'..='F' => false,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}, false, 0, 0).0;
|
|
||||||
|
|
||||||
Some(ExprHex(payload))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let text = self.read_string_until(|n| {
|
let text = self.read_string_until(|n| {
|
||||||
match n {
|
match n {
|
||||||
c if c.is_whitespace() => true,
|
c if c.is_whitespace() => true,
|
||||||
@ -253,9 +242,7 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
}
|
}
|
||||||
}, false, -(c.len_utf8() as isize), 0).0;
|
}, false, -(c.len_utf8() as isize), 0).0;
|
||||||
|
|
||||||
if let Some(hex_expr) = hex_expr {
|
if self.mode == Header {
|
||||||
hex_expr
|
|
||||||
} else if self.mode == Header {
|
|
||||||
self.parse_expr(text)
|
self.parse_expr(text)
|
||||||
} else {
|
} else {
|
||||||
Text(text)
|
Text(text)
|
||||||
@ -399,6 +386,17 @@ impl<'s> Tokens<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_hex_value(&mut self) -> Token<'s> {
|
||||||
|
// This will parse more than the permissable 0-9, a-f, A-F character
|
||||||
|
// ranges to provide nicer error messages later.
|
||||||
|
let payload = self.read_string_until(
|
||||||
|
|n| !n.is_ascii_alphanumeric(),
|
||||||
|
false, 0, 0
|
||||||
|
).0;
|
||||||
|
|
||||||
|
ExprHex(payload)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_expr(&mut self, text: &'s str) -> Token<'s> {
|
fn parse_expr(&mut self, text: &'s str) -> Token<'s> {
|
||||||
if let Ok(b) = text.parse::<bool>() {
|
if let Ok(b) = text.parse::<bool>() {
|
||||||
ExprBool(b)
|
ExprBool(b)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user