summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-05-17 14:41:46 +0200
committerLaurenz <laurmaedje@gmail.com>2023-05-17 14:41:46 +0200
commit551ea99d05166b0be50792f767ddd38b996e32fa (patch)
treeec5e86a087e79e8c181c7d4b904216a775227e2d /src
parent46aace78ac4ac1c075b9b1670dbb7372df1a0a82 (diff)
Show default values in documentation
Fixes #169 Fixes #1102
Diffstat (limited to 'src')
-rw-r--r--src/eval/cast.rs6
-rw-r--r--src/eval/func.rs2
-rw-r--r--src/eval/value.rs2
-rw-r--r--src/geom/align.rs12
-rw-r--r--src/geom/axes.rs4
-rw-r--r--src/geom/corners.rs107
-rw-r--r--src/geom/rel.rs4
-rw-r--r--src/geom/sides.rs45
-rw-r--r--src/geom/stroke.rs54
-rw-r--r--src/syntax/parser.rs19
10 files changed, 153 insertions, 102 deletions
diff --git a/src/eval/cast.rs b/src/eval/cast.rs
index b85d6e76..7ef2f1d0 100644
--- a/src/eval/cast.rs
+++ b/src/eval/cast.rs
@@ -230,6 +230,12 @@ impl<T: Cast> Cast for Option<T> {
}
}
+impl<T: Into<Value>> From<Spanned<T>> for Value {
+ fn from(spanned: Spanned<T>) -> Self {
+ spanned.v.into()
+ }
+}
+
impl<T: Into<Value>> From<Option<T>> for Value {
fn from(v: Option<T>) -> Self {
match v {
diff --git a/src/eval/func.rs b/src/eval/func.rs
index 75231fc9..a224c5b8 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -279,6 +279,8 @@ pub struct ParamInfo {
pub docs: &'static str,
/// Valid values for the parameter.
pub cast: CastInfo,
+ /// Creates an instance of the parameter's default value.
+ pub default: Option<fn() -> Value>,
/// Is the parameter positional?
pub positional: bool,
/// Is the parameter named?
diff --git a/src/eval/value.rs b/src/eval/value.rs
index bd612cce..36cda80b 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -54,7 +54,7 @@ pub enum Value {
Styles(Styles),
/// An array of values: `(1, "hi", 12cm)`.
Array(Array),
- /// A dictionary value: `(color: #f79143, pattern: dashed)`.
+ /// A dictionary value: `(a: 1, b: "hi")`.
Dict(Dict),
/// An executable function.
Func(Func),
diff --git a/src/geom/align.rs b/src/geom/align.rs
index a15ec4e6..42fc493e 100644
--- a/src/geom/align.rs
+++ b/src/geom/align.rs
@@ -147,6 +147,10 @@ cast_from_value! {
}
cast_to_value! {
+ v: Axes<Align> => v.map(GenAlign::from).into()
+}
+
+cast_to_value! {
v: Axes<Option<GenAlign>> => match (v.x, v.y) {
(Some(x), Some(y)) => Axes::new(x, y).into(),
(Some(x), None) => x.into(),
@@ -196,6 +200,14 @@ impl Fold for GenAlign {
}
}
+impl Fold for Align {
+ type Output = Self;
+
+ fn fold(self, _: Self::Output) -> Self::Output {
+ self
+ }
+}
+
/// Utility struct to restrict a passed alignment value to the horizontal axis
/// on cast.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
diff --git a/src/geom/axes.rs b/src/geom/axes.rs
index 8bc2456a..511e6ff5 100644
--- a/src/geom/axes.rs
+++ b/src/geom/axes.rs
@@ -141,9 +141,9 @@ where
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if let Axes { x: Some(x), y: Some(y) } =
- self.as_ref().map(|v| (v as &dyn Any).downcast_ref::<Align>())
+ self.as_ref().map(|v| (v as &dyn Any).downcast_ref::<GenAlign>())
{
- write!(f, "{:?}-{:?}", x, y)
+ write!(f, "{:?} + {:?}", x, y)
} else if (&self.x as &dyn Any).is::<Abs>() {
write!(f, "Size({:?}, {:?})", self.x, self.y)
} else {
diff --git a/src/geom/corners.rs b/src/geom/corners.rs
index 26d4082b..20e9bed0 100644
--- a/src/geom/corners.rs
+++ b/src/geom/corners.rs
@@ -117,43 +117,47 @@ where
}
fn cast(mut value: Value) -> StrResult<Self> {
+ let keys = [
+ "top-left",
+ "top-right",
+ "bottom-right",
+ "bottom-left",
+ "left",
+ "top",
+ "right",
+ "bottom",
+ "rest",
+ ];
+
if let Value::Dict(dict) = &mut value {
- let mut take = |key| dict.take(key).ok().map(T::cast).transpose();
-
- let rest = take("rest")?;
- let left = take("left")?.or_else(|| rest.clone());
- let top = take("top")?.or_else(|| rest.clone());
- let right = take("right")?.or_else(|| rest.clone());
- let bottom = take("bottom")?.or_else(|| rest.clone());
- let corners = Corners {
- top_left: take("top-left")?
- .or_else(|| top.clone())
- .or_else(|| left.clone()),
- top_right: take("top-right")?
- .or_else(|| top.clone())
- .or_else(|| right.clone()),
- bottom_right: take("bottom-right")?
- .or_else(|| bottom.clone())
- .or_else(|| right.clone()),
- bottom_left: take("bottom-left")?
- .or_else(|| bottom.clone())
- .or_else(|| left.clone()),
- };
-
- dict.finish(&[
- "top-left",
- "top-right",
- "bottom-right",
- "bottom-left",
- "left",
- "top",
- "right",
- "bottom",
- "rest",
- ])?;
-
- Ok(corners)
- } else if T::is(&value) {
+ if dict.iter().any(|(key, _)| keys.contains(&key.as_str())) {
+ let mut take = |key| dict.take(key).ok().map(T::cast).transpose();
+ let rest = take("rest")?;
+ let left = take("left")?.or_else(|| rest.clone());
+ let top = take("top")?.or_else(|| rest.clone());
+ let right = take("right")?.or_else(|| rest.clone());
+ let bottom = take("bottom")?.or_else(|| rest.clone());
+ let corners = Corners {
+ top_left: take("top-left")?
+ .or_else(|| top.clone())
+ .or_else(|| left.clone()),
+ top_right: take("top-right")?
+ .or_else(|| top.clone())
+ .or_else(|| right.clone()),
+ bottom_right: take("bottom-right")?
+ .or_else(|| bottom.clone())
+ .or_else(|| right.clone()),
+ bottom_left: take("bottom-left")?
+ .or_else(|| bottom.clone())
+ .or_else(|| left.clone()),
+ };
+
+ dict.finish(&keys)?;
+ return Ok(corners);
+ }
+ }
+
+ if T::is(&value) {
Ok(Self::splat(Some(T::cast(value)?)))
} else {
<Self as Cast>::error(value)
@@ -184,30 +188,27 @@ impl<T: Fold> Fold for Corners<Option<T>> {
}
}
-impl<T> From<Corners<Option<T>>> for Value
+impl<T> From<Corners<T>> for Value
where
T: PartialEq + Into<Value>,
{
- fn from(corners: Corners<Option<T>>) -> Self {
+ fn from(corners: Corners<T>) -> Self {
if corners.is_uniform() {
- if let Some(value) = corners.top_left {
- return value.into();
- }
+ return corners.top_left.into();
}
let mut dict = Dict::new();
- if let Some(top_left) = corners.top_left {
- dict.insert("top-left".into(), top_left.into());
- }
- if let Some(top_right) = corners.top_right {
- dict.insert("top-right".into(), top_right.into());
- }
- if let Some(bottom_right) = corners.bottom_right {
- dict.insert("bottom-right".into(), bottom_right.into());
- }
- if let Some(bottom_left) = corners.bottom_left {
- dict.insert("bottom-left".into(), bottom_left.into());
- }
+ let mut handle = |key: &str, component: T| {
+ let value = component.into();
+ if value != Value::None {
+ dict.insert(key.into(), value);
+ }
+ };
+
+ handle("top-left", corners.top_left);
+ handle("top-right", corners.top_right);
+ handle("bottom-right", corners.bottom_right);
+ handle("bottom-left", corners.bottom_left);
Value::Dict(dict)
}
diff --git a/src/geom/rel.rs b/src/geom/rel.rs
index 7288f380..aaa784f9 100644
--- a/src/geom/rel.rs
+++ b/src/geom/rel.rs
@@ -227,3 +227,7 @@ impl Fold for Rel<Length> {
self
}
}
+
+cast_to_value! {
+ v: Rel<Abs> => v.map(Length::from).into()
+}
diff --git a/src/geom/sides.rs b/src/geom/sides.rs
index d9a020da..a905a5f8 100644
--- a/src/geom/sides.rs
+++ b/src/geom/sides.rs
@@ -187,10 +187,10 @@ where
}
fn cast(mut value: Value) -> StrResult<Self> {
+ let keys = ["left", "top", "right", "bottom", "x", "y", "rest"];
if let Value::Dict(dict) = &mut value {
- let mut try_cast = || -> StrResult<_> {
+ if dict.iter().any(|(key, _)| keys.contains(&key.as_str())) {
let mut take = |key| dict.take(key).ok().map(T::cast).transpose();
-
let rest = take("rest")?;
let x = take("x")?.or_else(|| rest.clone());
let y = take("y")?.or_else(|| rest.clone());
@@ -201,13 +201,8 @@ where
bottom: take("bottom")?.or_else(|| y.clone()),
};
- dict.finish(&["left", "top", "right", "bottom", "x", "y", "rest"])?;
-
- Ok(sides)
- };
-
- if let Ok(res) = try_cast() {
- return Ok(res);
+ dict.finish(&keys)?;
+ return Ok(sides);
}
}
@@ -223,35 +218,31 @@ where
}
}
-impl<T> From<Sides<Option<T>>> for Value
+impl<T> From<Sides<T>> for Value
where
T: PartialEq + Into<Value>,
{
- fn from(sides: Sides<Option<T>>) -> Self {
+ fn from(sides: Sides<T>) -> Self {
if sides.is_uniform() {
- if let Some(value) = sides.left {
- return value.into();
- }
+ return sides.left.into();
}
let mut dict = Dict::new();
- if let Some(left) = sides.left {
- dict.insert("left".into(), left.into());
- }
- if let Some(top) = sides.top {
- dict.insert("top".into(), top.into());
- }
- if let Some(right) = sides.right {
- dict.insert("right".into(), right.into());
- }
- if let Some(bottom) = sides.bottom {
- dict.insert("bottom".into(), bottom.into());
- }
+ let mut handle = |key: &str, component: T| {
+ let value = component.into();
+ if value != Value::None {
+ dict.insert(key.into(), value);
+ }
+ };
+
+ handle("left", sides.left);
+ handle("top", sides.top);
+ handle("right", sides.right);
+ handle("bottom", sides.bottom);
Value::Dict(dict)
}
}
-
impl<T: Resolve> Resolve for Sides<T> {
type Output = Sides<T::Output>;
diff --git a/src/geom/stroke.rs b/src/geom/stroke.rs
index ead30cbb..6539922c 100644
--- a/src/geom/stroke.rs
+++ b/src/geom/stroke.rs
@@ -51,6 +51,35 @@ pub struct PartialStroke<T = Length> {
pub miter_limit: Smart<Scalar>,
}
+impl<T> PartialStroke<T> {
+ /// Map the contained lengths with `f`.
+ pub fn map<F, U>(self, f: F) -> PartialStroke<U>
+ where
+ F: Fn(T) -> U,
+ {
+ PartialStroke {
+ paint: self.paint,
+ thickness: self.thickness.map(&f),
+ line_cap: self.line_cap,
+ line_join: self.line_join,
+ dash_pattern: self.dash_pattern.map(|pattern| {
+ pattern.map(|pattern| DashPattern {
+ array: pattern
+ .array
+ .into_iter()
+ .map(|l| match l {
+ DashLength::Length(v) => DashLength::Length(f(v)),
+ DashLength::LineWidth => DashLength::LineWidth,
+ })
+ .collect(),
+ phase: f(pattern.phase),
+ })
+ }),
+ miter_limit: self.miter_limit,
+ }
+ }
+}
+
impl PartialStroke<Abs> {
/// Unpack the stroke, filling missing fields from the `default`.
pub fn unwrap_or(self, default: Stroke) -> Stroke {
@@ -106,13 +135,13 @@ impl<T: Debug> Debug for PartialStroke<T> {
}
(Smart::Custom(paint), Smart::Auto) => paint.fmt(f),
(Smart::Auto, Smart::Custom(thickness)) => thickness.fmt(f),
- (Smart::Auto, Smart::Auto) => f.pad("<stroke>"),
+ (Smart::Auto, Smart::Auto) => f.pad("1pt + black"),
}
} else {
write!(f, "(")?;
let mut sep = "";
if let Smart::Custom(paint) = &paint {
- write!(f, "{}color: {:?}", sep, paint)?;
+ write!(f, "{}paint: {:?}", sep, paint)?;
sep = ", ";
}
if let Smart::Custom(thickness) = &thickness {
@@ -176,7 +205,7 @@ impl Debug for LineJoin {
}
}
-/// A line dash pattern
+/// A line dash pattern.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct DashPattern<T = Length, DT = DashLength<T>> {
/// The dash array.
@@ -293,20 +322,12 @@ impl Resolve for DashPattern {
cast_from_value! {
PartialStroke: "stroke",
thickness: Length => Self {
- paint: Smart::Auto,
thickness: Smart::Custom(thickness),
- line_cap: Smart::Auto,
- line_join: Smart::Auto,
- dash_pattern: Smart::Auto,
- miter_limit: Smart::Auto,
+ ..Default::default()
},
color: Color => Self {
paint: Smart::Custom(color.into()),
- thickness: Smart::Auto,
- line_cap: Smart::Auto,
- line_join: Smart::Auto,
- dash_pattern: Smart::Auto,
- miter_limit: Smart::Auto,
+ ..Default::default()
},
mut dict: Dict => {
fn take<T: Cast<Value>>(dict: &mut Dict, key: &str) -> StrResult<Smart<T>> {
@@ -320,7 +341,6 @@ cast_from_value! {
let line_join = take::<LineJoin>(&mut dict, "join")?;
let dash_pattern = take::<Option<DashPattern>>(&mut dict, "dash")?;
let miter_limit = take::<f64>(&mut dict, "miter-limit")?;
-
dict.finish(&["paint", "thickness", "cap", "join", "dash", "miter-limit"])?;
Self {
@@ -359,7 +379,11 @@ impl Fold for PartialStroke<Abs> {
line_cap: self.line_cap.or(outer.line_cap),
line_join: self.line_join.or(outer.line_join),
dash_pattern: self.dash_pattern.or(outer.dash_pattern),
- miter_limit: self.miter_limit,
+ miter_limit: self.miter_limit.or(outer.miter_limit),
}
}
}
+
+cast_to_value! {
+ v: PartialStroke<Abs> => v.map(Length::from).into()
+}
diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs
index 7fdba4bd..c7dbc936 100644
--- a/src/syntax/parser.rs
+++ b/src/syntax/parser.rs
@@ -18,7 +18,10 @@ pub fn parse(text: &str) -> SyntaxNode {
/// This is only used for syntax highlighting.
pub fn parse_code(text: &str) -> SyntaxNode {
let mut p = Parser::new(text, 0, LexMode::Code);
- code(&mut p, |_| false);
+ let m = p.marker();
+ p.skip();
+ code_exprs(&mut p, |_| false);
+ p.wrap_skipless(m, SyntaxKind::Code);
p.finish().into_iter().next().unwrap()
}
@@ -511,8 +514,13 @@ fn maybe_wrap_in_math(p: &mut Parser, arg: Marker, named: Option<Marker>) {
}
}
-fn code(p: &mut Parser, mut stop: impl FnMut(SyntaxKind) -> bool) {
+fn code(p: &mut Parser, stop: impl FnMut(SyntaxKind) -> bool) {
let m = p.marker();
+ code_exprs(p, stop);
+ p.wrap(m, SyntaxKind::Code);
+}
+
+fn code_exprs(p: &mut Parser, mut stop: impl FnMut(SyntaxKind) -> bool) {
while !p.eof() && !stop(p.current()) {
p.stop_at_newline(true);
let prev = p.prev_end();
@@ -529,7 +537,6 @@ fn code(p: &mut Parser, mut stop: impl FnMut(SyntaxKind) -> bool) {
p.unexpected();
}
}
- p.wrap(m, SyntaxKind::Code);
}
fn code_expr(p: &mut Parser) {
@@ -1474,10 +1481,14 @@ impl<'s> Parser<'s> {
fn wrap(&mut self, m: Marker, kind: SyntaxKind) {
self.unskip();
+ self.wrap_skipless(m, kind);
+ self.skip();
+ }
+
+ fn wrap_skipless(&mut self, m: Marker, kind: SyntaxKind) {
let from = m.0.min(self.nodes.len());
let children = self.nodes.drain(from..).collect();
self.nodes.push(SyntaxNode::inner(kind, children));
- self.skip();
}
fn progress(&self, offset: usize) -> bool {