mirror of https://github.com/stelzo/typst.git
184 lines
4.6 KiB
Rust
184 lines
4.6 KiB
Rust
//! Syntax models, parsing and tokenization.
|
|
|
|
use std::any::Any;
|
|
use std::fmt::Debug;
|
|
use async_trait::async_trait;
|
|
use serde::Serialize;
|
|
|
|
use crate::{Pass, Feedback};
|
|
use crate::layout::{LayoutContext, Commands, Command};
|
|
use self::span::{Spanned, SpanVec};
|
|
|
|
#[cfg(test)]
|
|
#[macro_use]
|
|
mod test;
|
|
|
|
pub mod expr;
|
|
pub mod func;
|
|
pub mod span;
|
|
pub_use_mod!(scope);
|
|
pub_use_mod!(parsing);
|
|
pub_use_mod!(tokens);
|
|
|
|
/// Represents a parsed piece of source that can be layouted and in the future
|
|
/// also be queried for information used for refactorings, autocomplete, etc.
|
|
#[async_trait(?Send)]
|
|
pub trait Model: Debug + ModelBounds {
|
|
/// Layout the model into a sequence of commands processed by a
|
|
/// [`ModelLayouter`](crate::layout::ModelLayouter).
|
|
async fn layout<'a>(&'a self, ctx: LayoutContext<'_>) -> Pass<Commands<'a>>;
|
|
}
|
|
|
|
/// A tree representation of source code.
|
|
#[derive(Debug, Default, Clone, PartialEq)]
|
|
pub struct SyntaxModel {
|
|
/// The syntactical elements making up this model.
|
|
pub nodes: SpanVec<Node>,
|
|
}
|
|
|
|
impl SyntaxModel {
|
|
/// Create an empty syntax model.
|
|
pub fn new() -> SyntaxModel {
|
|
SyntaxModel { nodes: vec![] }
|
|
}
|
|
|
|
/// Add a node to the model.
|
|
pub fn add(&mut self, node: Spanned<Node>) {
|
|
self.nodes.push(node);
|
|
}
|
|
}
|
|
|
|
#[async_trait(?Send)]
|
|
impl Model for SyntaxModel {
|
|
async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>> {
|
|
Pass::new(vec![Command::LayoutSyntaxModel(self)], Feedback::new())
|
|
}
|
|
}
|
|
|
|
/// A node in the [syntax model](SyntaxModel).
|
|
#[derive(Debug, Clone)]
|
|
pub enum Node {
|
|
/// Whitespace containing less than two newlines.
|
|
Space,
|
|
/// Whitespace with more than two newlines.
|
|
Parbreak,
|
|
/// A forced line break.
|
|
Linebreak,
|
|
/// Plain text.
|
|
Text(String),
|
|
/// Lines of raw text.
|
|
Raw(Vec<String>),
|
|
/// Italics were enabled / disabled.
|
|
ToggleItalic,
|
|
/// Bolder was enabled / disabled.
|
|
ToggleBolder,
|
|
/// A submodel, typically a function invocation.
|
|
Model(Box<dyn Model>),
|
|
}
|
|
|
|
impl PartialEq for Node {
|
|
fn eq(&self, other: &Node) -> bool {
|
|
use Node::*;
|
|
match (self, other) {
|
|
(Space, Space) => true,
|
|
(Parbreak, Parbreak) => true,
|
|
(Linebreak, Linebreak) => true,
|
|
(Text(a), Text(b)) => a == b,
|
|
(Raw(a), Raw(b)) => a == b,
|
|
(ToggleItalic, ToggleItalic) => true,
|
|
(ToggleBolder, ToggleBolder) => true,
|
|
(Model(a), Model(b)) => a == b,
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A list of spanned decorations.
|
|
pub type Decorations = SpanVec<Decoration>;
|
|
|
|
/// Decorations for semantic syntax highlighting.
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum Decoration {
|
|
/// A valid function name:
|
|
/// ```typst
|
|
/// [box]
|
|
/// ^^^
|
|
/// ```
|
|
ValidFuncName,
|
|
/// An invalid function name:
|
|
/// ```typst
|
|
/// [blabla]
|
|
/// ^^^^^^
|
|
/// ```
|
|
InvalidFuncName,
|
|
/// A key of a keyword argument:
|
|
/// ```typst
|
|
/// [box: width=5cm]
|
|
/// ^^^^^
|
|
/// ```
|
|
ArgumentKey,
|
|
/// A key in an object.
|
|
/// ```typst
|
|
/// [box: padding={ left: 1cm, right: 2cm}]
|
|
/// ^^^^ ^^^^^
|
|
/// ```
|
|
ObjectKey,
|
|
/// An italic word.
|
|
Italic,
|
|
/// A bold word.
|
|
Bold,
|
|
}
|
|
|
|
impl dyn Model {
|
|
/// Downcast this model to a concrete type implementing [`Model`].
|
|
pub fn downcast<T>(&self) -> Option<&T> where T: Model + 'static {
|
|
self.as_any().downcast_ref::<T>()
|
|
}
|
|
}
|
|
|
|
impl PartialEq for dyn Model {
|
|
fn eq(&self, other: &dyn Model) -> bool {
|
|
self.bound_eq(other)
|
|
}
|
|
}
|
|
|
|
impl Clone for Box<dyn Model> {
|
|
fn clone(&self) -> Self {
|
|
self.bound_clone()
|
|
}
|
|
}
|
|
|
|
/// This trait describes bounds necessary for types implementing [`Model`]. It is
|
|
/// automatically implemented for all types that are [`Model`], [`PartialEq`],
|
|
/// [`Clone`] and `'static`.
|
|
///
|
|
/// It is necessary to make models comparable and clonable.
|
|
pub trait ModelBounds {
|
|
/// Convert into a `dyn Any`.
|
|
fn as_any(&self) -> &dyn Any;
|
|
|
|
/// Check for equality with another model.
|
|
fn bound_eq(&self, other: &dyn Model) -> bool;
|
|
|
|
/// Clone into a boxed model trait object.
|
|
fn bound_clone(&self) -> Box<dyn Model>;
|
|
}
|
|
|
|
impl<T> ModelBounds for T where T: Model + PartialEq + Clone + 'static {
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
|
|
fn bound_eq(&self, other: &dyn Model) -> bool {
|
|
match other.as_any().downcast_ref::<Self>() {
|
|
Some(other) => self == other,
|
|
None => false,
|
|
}
|
|
}
|
|
|
|
fn bound_clone(&self) -> Box<dyn Model> {
|
|
Box::new(self.clone())
|
|
}
|
|
}
|