summaryrefslogtreecommitdiff
path: root/crates/typst-docs/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-docs/src')
-rw-r--r--crates/typst-docs/src/contribs.rs2
-rw-r--r--crates/typst-docs/src/html.rs13
-rw-r--r--crates/typst-docs/src/lib.rs252
-rw-r--r--crates/typst-docs/src/link.rs30
-rw-r--r--crates/typst-docs/src/model.rs6
5 files changed, 162 insertions, 141 deletions
diff --git a/crates/typst-docs/src/contribs.rs b/crates/typst-docs/src/contribs.rs
index cbd87dc6..58a730e2 100644
--- a/crates/typst-docs/src/contribs.rs
+++ b/crates/typst-docs/src/contribs.rs
@@ -4,7 +4,7 @@ use std::fmt::Write;
use serde::{Deserialize, Serialize};
-use super::{Html, Resolver};
+use crate::{Html, Resolver};
/// Build HTML detailing the contributors between two tags.
pub fn contributors(resolver: &dyn Resolver, from: &str, to: &str) -> Option<Html> {
diff --git a/crates/typst-docs/src/html.rs b/crates/typst-docs/src/html.rs
index 1210545f..7481b050 100644
--- a/crates/typst-docs/src/html.rs
+++ b/crates/typst-docs/src/html.rs
@@ -8,11 +8,12 @@ use pulldown_cmark as md;
use serde::{Deserialize, Serialize};
use typed_arena::Arena;
use typst::diag::{FileResult, StrResult};
-use typst::eval::{Bytes, Datetime, Library, Tracer};
-use typst::font::{Font, FontBook};
-use typst::geom::{Abs, Point, Size};
+use typst::eval::Tracer;
+use typst::foundations::{Bytes, Datetime};
+use typst::layout::{Abs, Point, Size};
use typst::syntax::{FileId, Source, VirtualPath};
-use typst::World;
+use typst::text::{Font, FontBook};
+use typst::{Library, World};
use unscanny::Scanner;
use yaml_front_matter::YamlFrontMatter;
@@ -360,13 +361,13 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html {
buf.push_str("</pre>");
return Html::new(buf);
} else if !matches!(lang, "example" | "typ" | "preview") {
- let set = &*typst_library::text::SYNTAXES;
+ let set = &*typst::text::RAW_SYNTAXES;
let buf = syntect::html::highlighted_html_for_string(
&display,
set,
set.find_syntax_by_token(lang)
.unwrap_or_else(|| panic!("unsupported highlighting language: {lang}")),
- &typst_library::text::THEME,
+ &typst::text::RAW_THEME,
)
.expect("failed to highlight code");
return Html::new(buf);
diff --git a/crates/typst-docs/src/lib.rs b/crates/typst-docs/src/lib.rs
index d58734c6..444dda32 100644
--- a/crates/typst-docs/src/lib.rs
+++ b/crates/typst-docs/src/lib.rs
@@ -5,9 +5,9 @@ mod html;
mod link;
mod model;
-pub use contribs::{contributors, Author, Commit};
-pub use html::Html;
-pub use model::*;
+pub use self::contribs::*;
+pub use self::html::*;
+pub use self::model::*;
use std::path::Path;
@@ -20,30 +20,48 @@ use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde_yaml as yaml;
use typst::diag::{bail, StrResult};
-use typst::doc::Frame;
-use typst::eval::{
- CastInfo, Func, Library, Module, ParamInfo, Repr, Scope, Smart, Type, Value,
+use typst::foundations::{
+ CastInfo, Category, Func, Module, ParamInfo, Repr, Scope, Smart, Type, Value,
+ FOUNDATIONS,
};
-use typst::font::{Font, FontBook};
-use typst::geom::Abs;
-use typst_library::layout::{Margin, PageElem};
+use typst::introspection::INTROSPECTION;
+use typst::layout::{Abs, Frame, Margin, PageElem, LAYOUT};
+use typst::loading::DATA_LOADING;
+use typst::math::MATH;
+use typst::model::MODEL;
+use typst::symbols::SYMBOLS;
+use typst::text::{Font, FontBook, TEXT};
+use typst::visualize::VISUALIZE;
+use typst::Library;
static DOCS_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../docs");
static FILE_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../assets/files");
static FONT_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../assets/fonts");
-static CATEGORIES: Lazy<yaml::Mapping> = Lazy::new(|| yaml("reference/categories.yml"));
-static GROUPS: Lazy<Vec<GroupData>> = Lazy::new(|| yaml("reference/groups.yml"));
+static GROUPS: Lazy<Vec<GroupData>> = Lazy::new(|| {
+ let mut groups: Vec<GroupData> = yaml("reference/groups.yml");
+ for group in &mut groups {
+ if group.filter.is_empty() {
+ group.filter = group
+ .module()
+ .scope()
+ .iter()
+ .filter(|(_, v)| matches!(v, Value::Func(_)))
+ .map(|(k, _)| k.clone())
+ .collect();
+ }
+ }
+ groups
+});
static LIBRARY: Lazy<Prehashed<Library>> = Lazy::new(|| {
- let mut lib = typst_library::build();
+ let mut lib = Library::build();
lib.styles
.set(PageElem::set_width(Smart::Custom(Abs::pt(240.0).into())));
lib.styles.set(PageElem::set_height(Smart::Auto));
lib.styles.set(PageElem::set_margin(Margin::splat(Some(Smart::Custom(
Abs::pt(15.0).into(),
)))));
- typst::eval::set_lang_items(lib.items.clone());
Prehashed::new(lib)
});
@@ -128,14 +146,15 @@ fn reference_pages(resolver: &dyn Resolver) -> PageModel {
.with_part("Language"),
markdown_page(resolver, "/docs/reference/", "reference/styling.md"),
markdown_page(resolver, "/docs/reference/", "reference/scripting.md"),
- category_page(resolver, "foundations").with_part("Library"),
- category_page(resolver, "text"),
- category_page(resolver, "math"),
- category_page(resolver, "layout"),
- category_page(resolver, "visualize"),
- category_page(resolver, "meta"),
- category_page(resolver, "symbols"),
- category_page(resolver, "data-loading"),
+ category_page(resolver, FOUNDATIONS).with_part("Library"),
+ category_page(resolver, MODEL),
+ category_page(resolver, TEXT),
+ category_page(resolver, MATH),
+ category_page(resolver, SYMBOLS),
+ category_page(resolver, LAYOUT),
+ category_page(resolver, VISUALIZE),
+ category_page(resolver, INTROSPECTION),
+ category_page(resolver, DATA_LOADING),
];
page
}
@@ -152,50 +171,73 @@ fn guide_pages(resolver: &dyn Resolver) -> PageModel {
/// Build the packages section.
fn packages_page(resolver: &dyn Resolver) -> PageModel {
+ let md = DOCS_DIR
+ .get_file("reference/packages.md")
+ .unwrap()
+ .contents_utf8()
+ .unwrap();
PageModel {
route: "/docs/packages/".into(),
title: "Packages".into(),
description: "Packages for Typst.".into(),
part: None,
outline: vec![],
- body: BodyModel::Packages(Html::markdown(
- resolver,
- category_details("packages"),
- Some(1),
- )),
+ body: BodyModel::Packages(Html::markdown(resolver, md, Some(1))),
children: vec![],
}
}
/// Create a page for a category.
#[track_caller]
-fn category_page(resolver: &dyn Resolver, category: &str) -> PageModel {
- let route = eco_format!("/docs/reference/{category}/");
+fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel {
+ let route = eco_format!("/docs/reference/{}/", category.name());
let mut children = vec![];
let mut items = vec![];
+ let mut shorthands = None;
+ let mut markup = vec![];
+ let mut math = vec![];
- let (module, path): (&Module, &[&str]) = match category {
- "math" => (&LIBRARY.math, &["math"]),
- _ => (&LIBRARY.global, &[]),
+ let (module, path): (&Module, &[&str]) = if category == MATH {
+ (&LIBRARY.math, &["math"])
+ } else {
+ (&LIBRARY.global, &[])
};
// Add groups.
- for mut group in GROUPS.iter().filter(|g| g.category == category).cloned() {
- let mut focus = module;
- if matches!(group.name.as_str(), "calc" | "sys") {
- focus = get_module(focus, &group.name).unwrap();
- group.functions = focus
- .scope()
- .iter()
- .filter(|(_, v)| matches!(v, Value::Func(_)))
- .map(|(k, _)| k.clone())
- .collect();
+ for group in GROUPS.iter().filter(|g| g.category == category.name()).cloned() {
+ if matches!(group.name.as_str(), "sym" | "emoji") {
+ let subpage = symbols_page(resolver, &route, &group);
+ let BodyModel::Symbols(model) = &subpage.body else { continue };
+ let list = &model.list;
+ markup.extend(
+ list.iter()
+ .filter(|symbol| symbol.markup_shorthand.is_some())
+ .cloned(),
+ );
+ math.extend(
+ list.iter().filter(|symbol| symbol.math_shorthand.is_some()).cloned(),
+ );
+
+ items.push(CategoryItem {
+ name: group.name.clone(),
+ route: subpage.route.clone(),
+ oneliner: oneliner(category.docs()).into(),
+ code: true,
+ });
+ children.push(subpage);
+ continue;
}
- let (child, item) = group_page(resolver, &route, &group, focus.scope());
+
+ let (child, item) = group_page(resolver, &route, &group);
children.push(child);
items.push(item);
}
+ // Add symbol pages. These are ordered manually.
+ if category == SYMBOLS {
+ shorthands = Some(ShorthandsModel { markup, math });
+ }
+
// Add functions.
let scope = module.scope();
for (name, value) in scope.iter() {
@@ -203,9 +245,9 @@ fn category_page(resolver: &dyn Resolver, category: &str) -> PageModel {
continue;
}
- if category == "math" {
+ if category == MATH {
// Skip grouped functions.
- if GROUPS.iter().flat_map(|group| &group.functions).any(|f| f == name) {
+ if GROUPS.iter().flat_map(|group| &group.filter).any(|f| f == name) {
continue;
}
@@ -242,54 +284,35 @@ fn category_page(resolver: &dyn Resolver, category: &str) -> PageModel {
}
}
- children.sort_by_cached_key(|child| child.title.clone());
- items.sort_by_cached_key(|item| item.name.clone());
-
- // Add symbol pages. These are ordered manually.
- let mut shorthands = None;
- if category == "symbols" {
- let mut markup = vec![];
- let mut math = vec![];
- for module in ["sym", "emoji"] {
- let subpage = symbols_page(resolver, &route, module);
- let BodyModel::Symbols(model) = &subpage.body else { continue };
- let list = &model.list;
- markup.extend(
- list.iter()
- .filter(|symbol| symbol.markup_shorthand.is_some())
- .cloned(),
- );
- math.extend(
- list.iter().filter(|symbol| symbol.math_shorthand.is_some()).cloned(),
- );
-
- items.push(CategoryItem {
- name: module.into(),
- route: subpage.route.clone(),
- oneliner: oneliner(category_details(module)).into(),
- code: true,
- });
- children.push(subpage);
- }
- shorthands = Some(ShorthandsModel { markup, math });
+ if category != SYMBOLS {
+ children.sort_by_cached_key(|child| child.title.clone());
+ items.sort_by_cached_key(|item| item.name.clone());
}
- let name: EcoString = category.to_title_case().into();
-
- let details = Html::markdown(resolver, category_details(category), Some(1));
+ let name = category.title();
+ let details = Html::markdown(resolver, category.docs(), Some(1));
let mut outline = vec![OutlineItem::from_name("Summary")];
outline.extend(details.outline());
outline.push(OutlineItem::from_name("Definitions"));
+ if shorthands.is_some() {
+ outline.push(OutlineItem::from_name("Shorthands"));
+ }
PageModel {
route,
- title: name.clone(),
+ title: name.into(),
description: eco_format!(
"Documentation for functions related to {name} in Typst."
),
part: None,
outline,
- body: BodyModel::Category(CategoryModel { name, details, items, shorthands }),
+ body: BodyModel::Category(CategoryModel {
+ name: category.name(),
+ title: category.title(),
+ details,
+ items,
+ shorthands,
+ }),
children,
}
}
@@ -498,18 +521,17 @@ fn group_page(
resolver: &dyn Resolver,
parent: &str,
group: &GroupData,
- scope: &Scope,
) -> (PageModel, CategoryItem) {
let mut functions = vec![];
let mut outline = vec![OutlineItem::from_name("Summary")];
let path: Vec<_> = group.path.iter().map(|s| s.as_str()).collect();
- let details = Html::markdown(resolver, &group.description, Some(1));
+ let details = Html::markdown(resolver, &group.details, Some(1));
outline.extend(details.outline());
let mut outline_items = vec![];
- for name in &group.functions {
- let value = scope.get(name).unwrap();
+ for name in &group.filter {
+ let value = group.module().scope().get(name).unwrap();
let Value::Func(func) = value else { panic!("not a function") };
let func = func_model(resolver, func, &path, true);
let id_base = urlify(&eco_format!("functions-{}", func.name));
@@ -530,13 +552,13 @@ fn group_page(
let model = PageModel {
route: eco_format!("{parent}{}", group.name),
- title: group.display.clone(),
+ title: group.title.clone(),
description: eco_format!("Documentation for the {} functions.", group.name),
part: None,
outline,
body: BodyModel::Group(GroupModel {
name: group.name.clone(),
- title: group.display.clone(),
+ title: group.title.clone(),
details,
functions,
}),
@@ -546,7 +568,7 @@ fn group_page(
let item = CategoryItem {
name: group.name.clone(),
route: model.route.clone(),
- oneliner: oneliner(&group.description).into(),
+ oneliner: oneliner(&group.details).into(),
code: false,
};
@@ -601,19 +623,12 @@ fn type_outline(model: &TypeModel) -> Vec<OutlineItem> {
}
/// Create a page for symbols.
-fn symbols_page(resolver: &dyn Resolver, parent: &str, name: &str) -> PageModel {
- let module = get_module(&LIBRARY.global, name).unwrap();
- let title = match name {
- "sym" => "General",
- "emoji" => "Emoji",
- _ => unreachable!(),
- };
-
- let model = symbols_model(resolver, name, title, module.scope());
+fn symbols_page(resolver: &dyn Resolver, parent: &str, group: &GroupData) -> PageModel {
+ let model = symbols_model(resolver, group);
PageModel {
- route: eco_format!("{parent}{name}/"),
- title: title.into(),
- description: eco_format!("Documentation for the `{name}` module."),
+ route: eco_format!("{parent}{}/", group.name),
+ title: group.title.clone(),
+ description: eco_format!("Documentation for the `{}` module.", group.name),
part: None,
outline: vec![],
body: BodyModel::Symbols(model),
@@ -622,14 +637,9 @@ fn symbols_page(resolver: &dyn Resolver, parent: &str, name: &str) -> PageModel
}
/// Produce a symbol list's model.
-fn symbols_model(
- resolver: &dyn Resolver,
- name: &str,
- title: &'static str,
- scope: &Scope,
-) -> SymbolsModel {
+fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
let mut list = vec![];
- for (name, value) in scope.iter() {
+ for (name, value) in group.module().scope().iter() {
let Value::Symbol(symbol) = value else { continue };
let complete = |variant: &str| {
if variant.is_empty() {
@@ -649,7 +659,7 @@ fn symbols_model(
markup_shorthand: shorthand(typst::syntax::ast::Shorthand::MARKUP_LIST),
math_shorthand: shorthand(typst::syntax::ast::Shorthand::MATH_LIST),
codepoint: c as u32,
- accent: typst::eval::Symbol::combining_accent(c).is_some(),
+ accent: typst::symbols::Symbol::combining_accent(c).is_some(),
unicode_name: unicode_names2::name(c)
.map(|s| s.to_string().to_title_case().into()),
alternates: symbol
@@ -662,8 +672,9 @@ fn symbols_model(
}
SymbolsModel {
- name: title,
- details: Html::markdown(resolver, category_details(name), Some(1)),
+ name: group.name.clone(),
+ title: group.title.clone(),
+ details: Html::markdown(resolver, &group.details, Some(1)),
list,
}
}
@@ -684,15 +695,6 @@ fn yaml<T: DeserializeOwned>(path: &str) -> T {
yaml::from_slice(file.contents()).unwrap()
}
-/// Load details for an identifying key.
-#[track_caller]
-fn category_details(key: &str) -> &str {
- CATEGORIES
- .get(&yaml::Value::String(key.into()))
- .and_then(|value| value.as_str())
- .unwrap_or_else(|| panic!("missing details for {key}"))
-}
-
/// Turn a title into an URL fragment.
pub fn urlify(title: &str) -> EcoString {
title
@@ -752,13 +754,23 @@ const TYPE_ORDER: &[&str] = &[
#[derive(Debug, Clone, Deserialize)]
struct GroupData {
name: EcoString,
+ title: EcoString,
category: EcoString,
- display: EcoString,
#[serde(default)]
path: Vec<EcoString>,
#[serde(default)]
- functions: Vec<EcoString>,
- description: EcoString,
+ filter: Vec<EcoString>,
+ details: EcoString,
+}
+
+impl GroupData {
+ fn module(&self) -> &'static Module {
+ let mut focus = &LIBRARY.global;
+ for path in &self.path {
+ focus = get_module(focus, path).unwrap();
+ }
+ focus
+ }
}
#[cfg(test)]
diff --git a/crates/typst-docs/src/link.rs b/crates/typst-docs/src/link.rs
index 38730c32..e2721b95 100644
--- a/crates/typst-docs/src/link.rs
+++ b/crates/typst-docs/src/link.rs
@@ -1,5 +1,5 @@
use typst::diag::{bail, StrResult};
-use typst::eval::Func;
+use typst::foundations::Func;
use crate::{get_module, GROUPS, LIBRARY};
@@ -55,6 +55,15 @@ fn resolve_known(head: &str) -> Option<&'static str> {
fn resolve_definition(head: &str) -> StrResult<String> {
let mut parts = head.trim_start_matches('$').split('.').peekable();
let mut focus = &LIBRARY.global;
+
+ let Some(name) = parts.peek() else {
+ bail!("missing first link component");
+ };
+
+ let Some(category) = focus.scope().get_category(name) else {
+ bail!("{name} has no category");
+ };
+
while let Some(m) = parts.peek().and_then(|&name| get_module(focus, name).ok()) {
focus = m;
parts.next();
@@ -62,18 +71,15 @@ fn resolve_definition(head: &str) -> StrResult<String> {
let name = parts.next().ok_or("link is missing first part")?;
let value = focus.field(name)?;
- let Some(category) = focus.scope().get_category(name) else {
- bail!("{name} has no category");
- };
// Handle grouped functions.
- if let Some(group) = GROUPS
- .iter()
- .filter(|_| category == "math")
- .find(|group| group.functions.iter().any(|func| func == name))
- {
- let mut route =
- format!("/docs/reference/math/{}/#functions-{}", group.name, name);
+ if let Some(group) = GROUPS.iter().find(|group| {
+ group.category == category.name() && group.filter.iter().any(|func| func == name)
+ }) {
+ let mut route = format!(
+ "/docs/reference/{}/{}/#functions-{}",
+ group.category, group.name, name
+ );
if let Some(param) = parts.next() {
route.push('-');
route.push_str(param);
@@ -81,7 +87,7 @@ fn resolve_definition(head: &str) -> StrResult<String> {
return Ok(route);
}
- let mut route = format!("/docs/reference/{category}/{name}/");
+ let mut route = format!("/docs/reference/{}/{name}/", category.name());
if let Some(next) = parts.next() {
if value.field(next).is_ok() {
route.push_str("#definitions-");
diff --git a/crates/typst-docs/src/model.rs b/crates/typst-docs/src/model.rs
index 580ae0d3..93742825 100644
--- a/crates/typst-docs/src/model.rs
+++ b/crates/typst-docs/src/model.rs
@@ -62,7 +62,8 @@ pub enum BodyModel {
/// Details about a category.
#[derive(Debug, Serialize)]
pub struct CategoryModel {
- pub name: EcoString,
+ pub name: &'static str,
+ pub title: &'static str,
pub details: Html,
pub items: Vec<CategoryItem>,
pub shorthands: Option<ShorthandsModel>,
@@ -144,7 +145,8 @@ pub struct TypeModel {
/// A collection of symbols.
#[derive(Debug, Serialize)]
pub struct SymbolsModel {
- pub name: &'static str,
+ pub name: EcoString,
+ pub title: EcoString,
pub details: Html,
pub list: Vec<SymbolModel>,
}