summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-02-06 11:15:48 +0100
committerLaurenz <laurmaedje@gmail.com>2020-02-06 11:15:48 +0100
commit02dc29d18a7b67edf0eaa5d125be22eec6cfebb7 (patch)
tree2079ec1be481265655c798adc94d5e9e0166cfc2
parent751812f45141a7b2022d0dba138457f3c21950b0 (diff)
Highlight bold / italic / monospace 🎨
-rw-r--r--src/error.rs1
-rw-r--r--src/layout/model.rs59
-rw-r--r--src/layout/text.rs25
-rw-r--r--src/lib.rs10
-rw-r--r--src/library/mod.rs2
-rw-r--r--src/style.rs3
-rw-r--r--src/syntax/mod.rs8
-rw-r--r--src/syntax/parsing.rs30
-rw-r--r--src/syntax/tokens.rs6
9 files changed, 113 insertions, 31 deletions
diff --git a/src/error.rs b/src/error.rs
index 3a095eef..24b1b79c 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -22,6 +22,7 @@ pub struct Error {
/// How severe / important an error is.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
+#[serde(rename_all = "camelCase")]
pub enum Severity {
/// Something in the code is not good.
Warning,
diff --git a/src/layout/model.rs b/src/layout/model.rs
index 343205ec..7e899f2e 100644
--- a/src/layout/model.rs
+++ b/src/layout/model.rs
@@ -5,12 +5,13 @@
use std::future::Future;
use std::pin::Pin;
use smallvec::smallvec;
+use toddle::query::FontStyle;
use crate::{Pass, Feedback};
use crate::GlobalFontLoader;
use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::size::{Size, Size2D};
-use crate::syntax::{Model, SyntaxModel, Node};
+use crate::syntax::{Model, SyntaxModel, Node, Decoration};
use crate::syntax::span::{Span, Spanned};
use super::line::{LineLayouter, LineContext};
use super::text::{layout_text, TextContext};
@@ -47,7 +48,7 @@ pub struct LayoutContext<'a> {
/// Whether the layout that is to be created will be nested in a parent
/// container.
pub nested: bool,
- /// Whether to debug render a box around the layout.
+ /// Whether to render debug boxs around layouts if `nested` is true.
pub debug: bool,
}
@@ -118,7 +119,7 @@ impl<'a> ModelLayouter<'a> {
axes: ctx.axes,
alignment: ctx.alignment,
repeat: ctx.repeat,
- debug: ctx.debug,
+ debug: ctx.debug && ctx.nested,
line_spacing: ctx.style.text.line_spacing(),
}),
style: ctx.style.clone(),
@@ -137,7 +138,6 @@ impl<'a> ModelLayouter<'a> {
style: &self.style,
spaces: self.layouter.remaining(),
nested: true,
- debug: false,
.. self.ctx
}).await;
@@ -157,29 +157,48 @@ impl<'a> ModelLayouter<'a> {
) -> DynFuture<'r, ()> { Box::pin(async move {
use Node::*;
- for node in &model.nodes {
- match &node.v {
+ for Spanned { v: node, span } in &model.nodes {
+ let decorate = |this: &mut ModelLayouter, deco| {
+ this.feedback.decos.push(Spanned::new(deco, *span));
+ };
+
+ match node {
Space => self.layout_space(),
Newline => self.layout_paragraph(),
- Text(text) => self.layout_text(text).await,
- ToggleItalic => self.style.text.variant.style.toggle(),
+ Text(text) => {
+ if self.style.text.variant.style == FontStyle::Italic {
+ decorate(self, Decoration::Italic);
+ }
+
+ if self.style.text.bolder {
+ decorate(self, Decoration::Bold);
+ }
+
+ if self.style.text.monospace {
+ decorate(self, Decoration::Monospace);
+ }
+
+ self.layout_text(text).await;
+ }
+
+ ToggleItalic => {
+ self.style.text.variant.style.toggle();
+ decorate(self, Decoration::Italic);
+ }
+
ToggleBolder => {
- let fac = if self.style.text.bolder { -1 } else { 1 };
- self.style.text.variant.weight.0 += 300 * fac;
self.style.text.bolder = !self.style.text.bolder;
+ decorate(self, Decoration::Bold);
}
+
ToggleMonospace => {
- let list = &mut self.style.text.fallback.list;
- match list.get(0).map(|s| s.as_str()) {
- Some("monospace") => { list.remove(0); },
- _ => list.insert(0, "monospace".to_string()),
- }
- self.style.text.fallback.flatten();
+ self.style.text.monospace = !self.style.text.monospace;
+ decorate(self, Decoration::Monospace);
}
Node::Model(model) => {
- self.layout(Spanned::new(model.as_ref(), node.span)).await;
+ self.layout(Spanned::new(model.as_ref(), *span)).await;
}
}
}
@@ -195,7 +214,7 @@ impl<'a> ModelLayouter<'a> {
fn execute_command<'r>(
&'r mut self,
command: Command<'r>,
- span: Span,
+ model_span: Span,
) -> DynFuture<'r, ()> { Box::pin(async move {
use Command::*;
@@ -213,7 +232,7 @@ impl<'a> ModelLayouter<'a> {
BreakParagraph => self.layout_paragraph(),
BreakPage => {
if self.ctx.nested {
- self.feedback.errors.push(err!(span;
+ self.feedback.errors.push(err!(model_span;
"page break cannot be issued from nested context"));
} else {
self.layouter.finish_space(true)
@@ -226,7 +245,7 @@ impl<'a> ModelLayouter<'a> {
}
SetPageStyle(style) => {
if self.ctx.nested {
- self.feedback.errors.push(err!(span;
+ self.feedback.errors.push(err!(model_span;
"page style cannot be changed from nested context"));
} else {
self.style.page = style;
diff --git a/src/layout/text.rs b/src/layout/text.rs
index c6fa45d1..614d59fd 100644
--- a/src/layout/text.rs
+++ b/src/layout/text.rs
@@ -113,13 +113,28 @@ impl<'a> TextLayouter<'a> {
async fn select_font(&mut self, c: char) -> Option<(FontIndex, Size)> {
let mut loader = self.ctx.loader.borrow_mut();
- let query = FontQuery {
- fallback: &self.ctx.style.fallback,
- variant: self.ctx.style.variant,
- c,
+ let mut variant = self.ctx.style.variant;
+ if self.ctx.style.bolder {
+ variant.weight.0 += 300;
+ }
+
+ let queried = if self.ctx.style.monospace {
+ loader.get(FontQuery {
+ // FIXME: This is a hack.
+ fallback: std::iter::once("source code pro")
+ .chain(self.ctx.style.fallback.iter()),
+ variant,
+ c,
+ }).await
+ } else {
+ loader.get(FontQuery {
+ fallback: self.ctx.style.fallback.iter(),
+ variant,
+ c,
+ }).await
};
- if let Some((font, index)) = loader.get(query).await {
+ if let Some((font, index)) = queried {
// Determine the width of the char.
let header = font.read_table::<Header>().ok()?;
let font_unit_ratio = 1.0 / (header.units_per_em as f32);
diff --git a/src/lib.rs b/src/lib.rs
index 7136b2bd..4a92d096 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -65,6 +65,8 @@ pub struct Typesetter {
style: LayoutStyle,
/// The standard library scope.
scope: Scope,
+ /// Whether to render debug boxes.
+ debug: bool,
}
/// The font loader type used in the [`Typesetter`].
@@ -83,6 +85,7 @@ impl Typesetter {
loader: RefCell::new(FontLoader::new(provider)),
style: LayoutStyle::default(),
scope: Scope::with_std(),
+ debug: false,
}
}
@@ -96,6 +99,11 @@ impl Typesetter {
self.style.page = style;
}
+ /// Set whether to render debug boxes.
+ pub fn set_debug(&mut self, debug: bool) {
+ self.debug = debug;
+ }
+
/// A reference to the backing font loader.
pub fn loader(&self) -> &GlobalFontLoader {
&self.loader
@@ -126,7 +134,7 @@ impl Typesetter {
axes: LayoutAxes::new(LeftToRight, TopToBottom),
alignment: LayoutAlignment::new(Origin, Origin),
nested: false,
- debug: false,
+ debug: self.debug,
},
).await
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 08dffdd7..08608d05 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -54,6 +54,8 @@ function! {
}
parse(header, body, ctx, f) {
+ header.args.pos.items.clear();
+ header.args.key.pairs.clear();
ValFunc { body: body!(opt: body, ctx, f) }
}
diff --git a/src/style.rs b/src/style.rs
index 075baa5a..57d6b6f3 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -24,6 +24,8 @@ pub struct TextStyle {
/// Whether the bolder toggle is active or inactive. This determines
/// whether the next `*` adds or removes font weight.
pub bolder: bool,
+ /// Whether the monospace toggle is active or inactive.
+ pub monospace: bool,
/// The base font size.
pub base_font_size: Size,
/// The font scale to apply on the base font size.
@@ -77,6 +79,7 @@ impl Default for TextStyle {
weight: FontWeight(400),
},
bolder: false,
+ monospace: false,
base_font_size: Size::pt(11.0),
font_scale: 1.0,
word_spacing_scale: 0.25,
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 8596a618..620a929e 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -100,7 +100,6 @@ pub enum Decoration {
/// ^^^
/// ```
ValidFuncName,
-
/// An invalid function name:
/// ```typst
/// [blabla]
@@ -114,6 +113,13 @@ pub enum Decoration {
/// ^^^^^
/// ```
ArgumentKey,
+
+ /// Italic.
+ Italic,
+ /// Bold.
+ Bold,
+ /// Monospace.
+ Monospace,
}
impl dyn Model {
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index 1526a5cb..d24985a6 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -375,6 +375,7 @@ mod tests {
Space as S, Newline as N,
ToggleItalic as Italic, ToggleBolder as Bold, ToggleMonospace as Mono,
};
+ use Decoration::*;
pub use Expr::{Number as Num, Bool};
pub fn Id(text: &str) -> Expr { Expr::Ident(Ident(text.to_string())) }
@@ -421,9 +422,32 @@ mod tests {
};
}
+ /// Test whether the given string yields the given decorations.
+ macro_rules! d {
+ ($s:expr => [$(($sl:tt:$sc:tt, $el:tt:$ec:tt, $d:expr)),* $(,)?]) => {
+ let ctx = ParseContext { scope: &scope() };
+ let decos = parse(Position::ZERO, $s, ctx).feedback.decos;
+
+ let expected = vec![
+ $(Spanned {
+ v: $d,
+ span: Span {
+ start: Position { line: $sl, column: $sc },
+ end: Position { line: $el, column: $ec },
+ },
+ }),*
+ ];
+
+ if decos != expected {
+ fail($s, decos, expected);
+ }
+ };
+ }
+
fn scope() -> Scope {
let mut scope = Scope::new::<DebugFn>();
scope.add::<DebugFn>("f");
+ scope.add::<DebugFn>("n");
scope.add::<DebugFn>("box");
scope
}
@@ -552,4 +576,10 @@ mod tests {
(0:21, 0:21, "expected closing bracket"),
]);
}
+
+ #[test]
+ fn parse_decos() {
+ d!("*Technische Universität Berlin* [n]\n [n]"
+ => [(0:33, 0:34, ValidFuncName), (1:33, 1:34, ValidFuncName)]);
+ }
}
diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs
index 7b52f655..f4ea5daf 100644
--- a/src/syntax/tokens.rs
+++ b/src/syntax/tokens.rs
@@ -403,15 +403,13 @@ impl<'s> Tokens<'s> {
fn eat(&mut self) -> Option<char> {
let c = self.iter.next()?;
- let len = c.len_utf8();
-
- self.index += len;
+ self.index += c.len_utf8();
if is_newline_char(c) && !(c == '\r' && self.peek() == Some('\n')) {
self.position.line += 1;
self.position.column = 0;
} else {
- self.position.column += len;
+ self.position.column += 1;
}
Some(c)