mirror of
https://github.com/typst/typst
synced 2025-05-22 21:15:28 +08:00
Write spaces and linebreaks into text runs ✒
This commit is contained in:
parent
318eb9021e
commit
8c27dc1010
@ -8,7 +8,6 @@ use crate::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size};
|
|||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
AnyNode, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode, TextNode, Tree,
|
AnyNode, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode, TextNode, Tree,
|
||||||
};
|
};
|
||||||
use crate::parse::{is_newline, Scanner};
|
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
|
|
||||||
/// The context for execution.
|
/// The context for execution.
|
||||||
@ -73,28 +72,14 @@ impl<'a> ExecContext<'a> {
|
|||||||
|
|
||||||
/// Push a word space into the active paragraph.
|
/// Push a word space into the active paragraph.
|
||||||
pub fn push_word_space(&mut self) {
|
pub fn push_word_space(&mut self) {
|
||||||
let em = self.state.font.resolve_size();
|
self.stack.par.push_soft(self.make_text_node(" "));
|
||||||
let amount = self.state.par.word_spacing.resolve(em);
|
|
||||||
self.stack.par.push_soft(ParChild::Spacing(amount));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push text into the active paragraph.
|
/// Push text into the active paragraph.
|
||||||
///
|
///
|
||||||
/// The text is split into lines at newlines.
|
/// The text is split into lines at newlines.
|
||||||
pub fn push_text(&mut self, text: &str) {
|
pub fn push_text(&mut self, text: impl Into<String>) {
|
||||||
let mut scanner = Scanner::new(text);
|
self.stack.par.push(self.make_text_node(text));
|
||||||
let mut text = String::new();
|
|
||||||
|
|
||||||
while let Some(c) = scanner.eat_merging_crlf() {
|
|
||||||
if is_newline(c) {
|
|
||||||
self.stack.par.push_text(mem::take(&mut text), &self.state);
|
|
||||||
self.linebreak();
|
|
||||||
} else {
|
|
||||||
text.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.stack.par.push_text(text, &self.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push spacing into paragraph or stack depending on `axis`.
|
/// Push spacing into paragraph or stack depending on `axis`.
|
||||||
@ -112,7 +97,7 @@ impl<'a> ExecContext<'a> {
|
|||||||
|
|
||||||
/// Apply a forced line break.
|
/// Apply a forced line break.
|
||||||
pub fn linebreak(&mut self) {
|
pub fn linebreak(&mut self) {
|
||||||
self.stack.par.push_hard(ParChild::Linebreak);
|
self.stack.par.push_hard(self.make_text_node("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a forced paragraph break.
|
/// Apply a forced paragraph break.
|
||||||
@ -140,6 +125,12 @@ impl<'a> ExecContext<'a> {
|
|||||||
self.pagebreak(true, false, Span::default());
|
self.pagebreak(true, false, Span::default());
|
||||||
Pass::new(self.tree, self.diags)
|
Pass::new(self.tree, self.diags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_text_node(&self, text: impl Into<String>) -> ParChild {
|
||||||
|
let align = self.state.aligns.cross;
|
||||||
|
let props = self.state.font.resolve_props();
|
||||||
|
ParChild::Text(TextNode { text: text.into(), props }, align)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PageBuilder {
|
struct PageBuilder {
|
||||||
@ -231,24 +222,10 @@ impl ParBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn push(&mut self, child: ParChild) {
|
fn push(&mut self, child: ParChild) {
|
||||||
self.children.extend(self.last.any());
|
if let Some(soft) = self.last.any() {
|
||||||
self.children.push(child);
|
self.push_inner(soft);
|
||||||
}
|
|
||||||
|
|
||||||
fn push_text(&mut self, text: String, state: &State) {
|
|
||||||
self.children.extend(self.last.any());
|
|
||||||
|
|
||||||
let align = state.aligns.cross;
|
|
||||||
let props = state.font.resolve_props();
|
|
||||||
|
|
||||||
if let Some(ParChild::Text(prev, prev_align)) = self.children.last_mut() {
|
|
||||||
if *prev_align == align && prev.props == props {
|
|
||||||
prev.text.push_str(&text);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.push_inner(child);
|
||||||
self.children.push(ParChild::Text(TextNode { text, props }, align));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_soft(&mut self, child: ParChild) {
|
fn push_soft(&mut self, child: ParChild) {
|
||||||
@ -257,6 +234,19 @@ impl ParBuilder {
|
|||||||
|
|
||||||
fn push_hard(&mut self, child: ParChild) {
|
fn push_hard(&mut self, child: ParChild) {
|
||||||
self.last.hard();
|
self.last.hard();
|
||||||
|
self.push_inner(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_inner(&mut self, child: ParChild) {
|
||||||
|
if let ParChild::Text(curr, curr_align) = &child {
|
||||||
|
if let Some(ParChild::Text(prev, prev_align)) = self.children.last_mut() {
|
||||||
|
if prev_align == curr_align && prev.props == curr.props {
|
||||||
|
prev.text.push_str(&curr.text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.children.push(child);
|
self.children.push(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ impl ExecWithMap for Tree {
|
|||||||
impl ExecWithMap for Node {
|
impl ExecWithMap for Node {
|
||||||
fn exec_with_map(&self, ctx: &mut ExecContext, map: &NodeMap) {
|
fn exec_with_map(&self, ctx: &mut ExecContext, map: &NodeMap) {
|
||||||
match self {
|
match self {
|
||||||
Node::Text(text) => ctx.push_text(text),
|
Node::Text(text) => ctx.push_text(text.clone()),
|
||||||
Node::Space => ctx.push_word_space(),
|
Node::Space => ctx.push_word_space(),
|
||||||
_ => map[&(self as *const _)].exec(ctx),
|
_ => map[&(self as *const _)].exec(ctx),
|
||||||
}
|
}
|
||||||
@ -75,9 +75,9 @@ impl Exec for Value {
|
|||||||
fn exec(&self, ctx: &mut ExecContext) {
|
fn exec(&self, ctx: &mut ExecContext) {
|
||||||
match self {
|
match self {
|
||||||
Value::None => {}
|
Value::None => {}
|
||||||
Value::Int(v) => ctx.push_text(&pretty(v)),
|
Value::Int(v) => ctx.push_text(pretty(v)),
|
||||||
Value::Float(v) => ctx.push_text(&pretty(v)),
|
Value::Float(v) => ctx.push_text(pretty(v)),
|
||||||
Value::Str(v) => ctx.push_text(v),
|
Value::Str(v) => ctx.push_text(v.clone()),
|
||||||
Value::Template(v) => v.exec(ctx),
|
Value::Template(v) => v.exec(ctx),
|
||||||
Value::Error => {}
|
Value::Error => {}
|
||||||
other => {
|
other => {
|
||||||
@ -85,7 +85,7 @@ impl Exec for Value {
|
|||||||
// the representation in monospace.
|
// the representation in monospace.
|
||||||
let prev = Rc::clone(&ctx.state.font.families);
|
let prev = Rc::clone(&ctx.state.font.families);
|
||||||
ctx.set_monospace();
|
ctx.set_monospace();
|
||||||
ctx.push_text(&pretty(other));
|
ctx.push_text(pretty(other));
|
||||||
ctx.state.font.families = prev;
|
ctx.state.font.families = prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ impl Exec for TemplateNode {
|
|||||||
fn exec(&self, ctx: &mut ExecContext) {
|
fn exec(&self, ctx: &mut ExecContext) {
|
||||||
match self {
|
match self {
|
||||||
Self::Tree { tree, map } => tree.exec_with_map(ctx, &map),
|
Self::Tree { tree, map } => tree.exec_with_map(ctx, &map),
|
||||||
Self::Str(v) => ctx.push_text(v),
|
Self::Str(v) => ctx.push_text(v.clone()),
|
||||||
Self::Func(v) => v.exec(ctx),
|
Self::Func(v) => v.exec(ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ pub struct ParNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A child of a paragraph node.
|
/// A child of a paragraph node.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum ParChild {
|
pub enum ParChild {
|
||||||
/// Spacing between other nodes.
|
/// Spacing between other nodes.
|
||||||
Spacing(Length),
|
Spacing(Length),
|
||||||
@ -23,8 +23,18 @@ pub enum ParChild {
|
|||||||
Text(TextNode, Align),
|
Text(TextNode, Align),
|
||||||
/// Any child node and how to align it in its line.
|
/// Any child node and how to align it in its line.
|
||||||
Any(AnyNode, Align),
|
Any(AnyNode, Align),
|
||||||
/// A forced linebreak.
|
}
|
||||||
Linebreak,
|
|
||||||
|
impl Debug for ParChild {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Spacing(amount) => write!(f, "Spacing({:?})", amount),
|
||||||
|
Self::Text(node, align) => write!(f, "Text({:?}, {:?})", node.text, align),
|
||||||
|
Self::Any(any, align) => {
|
||||||
|
f.debug_tuple("Any").field(any).field(align).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A consecutive, styled run of text.
|
/// A consecutive, styled run of text.
|
||||||
@ -38,7 +48,7 @@ pub struct TextNode {
|
|||||||
|
|
||||||
impl Debug for TextNode {
|
impl Debug for TextNode {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "Text({})", self.text)
|
write!(f, "Text({:?})", self.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +67,6 @@ impl Layout for ParNode {
|
|||||||
layouter.push_frame(frame, align);
|
layouter.push_frame(frame, align);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParChild::Linebreak => layouter.finish_line(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
layouter.finish()
|
layouter.finish()
|
||||||
|
@ -160,7 +160,7 @@ pub fn raw(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|||||||
|
|
||||||
let snapshot = ctx.state.clone();
|
let snapshot = ctx.state.clone();
|
||||||
ctx.set_monospace();
|
ctx.set_monospace();
|
||||||
ctx.push_text(&text);
|
ctx.push_text(text.clone());
|
||||||
ctx.state = snapshot;
|
ctx.state = snapshot;
|
||||||
|
|
||||||
if block {
|
if block {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user