diff --git a/crates/typst-docs/src/html.rs b/crates/typst-docs/src/html.rs index 8f6d1366d..0ec0ddf02 100644 --- a/crates/typst-docs/src/html.rs +++ b/crates/typst-docs/src/html.rs @@ -307,7 +307,7 @@ impl<'a> Handler<'a> { return Ok(link); } - crate::link::resolve(link) + crate::link::resolve(link, self.resolver.base()) } fn nesting(&self) -> usize { diff --git a/crates/typst-docs/src/lib.rs b/crates/typst-docs/src/lib.rs index f25314629..315a2ed58 100644 --- a/crates/typst-docs/src/lib.rs +++ b/crates/typst-docs/src/lib.rs @@ -78,14 +78,15 @@ static FONTS: Lazy<(Prehashed, Vec)> = Lazy::new(|| { /// Build documentation pages. pub fn provide(resolver: &dyn Resolver) -> Vec { vec![ - markdown_page(resolver, "/docs/", "overview.md").with_route("/docs/"), + markdown_page(resolver, resolver.base(), "overview.md") + .with_route(resolver.base()), tutorial_pages(resolver), reference_pages(resolver), guide_pages(resolver), packages_page(resolver), - markdown_page(resolver, "/docs/", "changelog.md"), - markdown_page(resolver, "/docs/", "roadmap.md"), - markdown_page(resolver, "/docs/", "community.md"), + markdown_page(resolver, resolver.base(), "changelog.md"), + markdown_page(resolver, resolver.base(), "roadmap.md"), + markdown_page(resolver, resolver.base(), "community.md"), ] } @@ -102,6 +103,9 @@ pub trait Resolver { /// Determine the commits between two tags. fn commits(&self, from: &str, to: &str) -> Vec; + + /// Get the base URL for the routes and links. This must end with a slash. + fn base(&self) -> &str; } /// Create a page from a markdown file. @@ -128,25 +132,39 @@ fn markdown_page( /// Build the tutorial. fn tutorial_pages(resolver: &dyn Resolver) -> PageModel { - let mut page = markdown_page(resolver, "/docs/", "tutorial/welcome.md"); + let mut page = markdown_page(resolver, resolver.base(), "tutorial/welcome.md"); page.children = DOCS_DIR .get_dir("tutorial") .unwrap() .files() .filter(|file| file.path() != Path::new("tutorial/welcome.md")) - .map(|file| markdown_page(resolver, "/docs/tutorial/", file.path())) + .map(|file| { + markdown_page(resolver, &format!("{}tutorial/", resolver.base()), file.path()) + }) .collect(); page } /// Build the reference. fn reference_pages(resolver: &dyn Resolver) -> PageModel { - let mut page = markdown_page(resolver, "/docs/", "reference/welcome.md"); + let mut page = markdown_page(resolver, resolver.base(), "reference/welcome.md"); page.children = vec![ - markdown_page(resolver, "/docs/reference/", "reference/syntax.md") - .with_part("Language"), - markdown_page(resolver, "/docs/reference/", "reference/styling.md"), - markdown_page(resolver, "/docs/reference/", "reference/scripting.md"), + markdown_page( + resolver, + &format!("{}reference/", resolver.base()), + "reference/syntax.md", + ) + .with_part("Language"), + markdown_page( + resolver, + &format!("{}reference/", resolver.base()), + "reference/styling.md", + ), + markdown_page( + resolver, + &format!("{}reference/", resolver.base()), + "reference/scripting.md", + ), category_page(resolver, FOUNDATIONS).with_part("Library"), category_page(resolver, MODEL), category_page(resolver, TEXT), @@ -162,10 +180,18 @@ fn reference_pages(resolver: &dyn Resolver) -> PageModel { /// Build the guides section. fn guide_pages(resolver: &dyn Resolver) -> PageModel { - let mut page = markdown_page(resolver, "/docs/", "guides/welcome.md"); + let mut page = markdown_page(resolver, resolver.base(), "guides/welcome.md"); page.children = vec![ - markdown_page(resolver, "/docs/guides/", "guides/guide-for-latex-users.md"), - markdown_page(resolver, "/docs/guides/", "guides/page-setup.md"), + markdown_page( + resolver, + &format!("{}guides/", resolver.base()), + "guides/guide-for-latex-users.md", + ), + markdown_page( + resolver, + &format!("{}guides/", resolver.base()), + "guides/page-setup.md", + ), ]; page } @@ -178,7 +204,7 @@ fn packages_page(resolver: &dyn Resolver) -> PageModel { .contents_utf8() .unwrap(); PageModel { - route: "/docs/packages/".into(), + route: eco_format!("{}packages/", resolver.base()), title: "Packages".into(), description: "Packages for Typst.".into(), part: None, @@ -191,7 +217,7 @@ fn packages_page(resolver: &dyn Resolver) -> PageModel { /// Create a page for a category. #[track_caller] fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel { - let route = eco_format!("/docs/reference/{}/", category.name()); + let route = eco_format!("{}reference/{}/", resolver.base(), category.name()); let mut children = vec![]; let mut items = vec![]; let mut shorthands = None; @@ -801,5 +827,9 @@ mod tests { fn commits(&self, _: &str, _: &str) -> Vec { vec![] } + + fn base(&self) -> &str { + "/" + } } } diff --git a/crates/typst-docs/src/link.rs b/crates/typst-docs/src/link.rs index e7e191fe6..20a4f6e4a 100644 --- a/crates/typst-docs/src/link.rs +++ b/crates/typst-docs/src/link.rs @@ -4,15 +4,15 @@ use typst::foundations::Func; use crate::{get_module, GROUPS, LIBRARY}; /// Resolve an intra-doc link. -pub fn resolve(link: &str) -> StrResult { +pub fn resolve(link: &str, base: &str) -> StrResult { if link.starts_with('#') || link.starts_with("http") { return Ok(link.to_string()); } let (head, tail) = split_link(link)?; - let mut route = match resolve_known(head) { - Some(route) => route.into(), - None => resolve_definition(head)?, + let mut route = match resolve_known(head, base) { + Some(route) => route, + None => resolve_definition(head, base)?, }; if !tail.is_empty() { @@ -35,24 +35,24 @@ fn split_link(link: &str) -> StrResult<(&str, &str)> { } /// Resolve a `$` link head to a known destination. -fn resolve_known(head: &str) -> Option<&'static str> { +fn resolve_known(head: &str, base: &str) -> Option { Some(match head { - "$tutorial" => "/docs/tutorial", - "$reference" => "/docs/reference", - "$category" => "/docs/reference", - "$syntax" => "/docs/reference/syntax", - "$styling" => "/docs/reference/styling", - "$scripting" => "/docs/reference/scripting", - "$guides" => "/docs/guides", - "$packages" => "/docs/packages", - "$changelog" => "/docs/changelog", - "$community" => "/docs/community", + "$tutorial" => format!("{base}tutorial"), + "$reference" => format!("{base}reference"), + "$category" => format!("{base}reference"), + "$syntax" => format!("{base}reference/syntax"), + "$styling" => format!("{base}reference/styling"), + "$scripting" => format!("{base}reference/scripting"), + "$guides" => format!("{base}guides"), + "$packages" => format!("{base}packages"), + "$changelog" => format!("{base}changelog"), + "$community" => format!("{base}community"), _ => return None, }) } /// Resolve a `$` link to a global definition. -fn resolve_definition(head: &str) -> StrResult { +fn resolve_definition(head: &str, base: &str) -> StrResult { let mut parts = head.trim_start_matches('$').split('.').peekable(); let mut focus = &LIBRARY.global; let mut category = None; @@ -76,8 +76,8 @@ fn resolve_definition(head: &str) -> StrResult { group.category == category.name() && group.filter.iter().any(|func| func == name) }) { let mut route = format!( - "/docs/reference/{}/{}/#functions-{}", - group.category, group.name, name + "{}reference/{}/{}/#functions-{}", + base, group.category, group.name, name ); if let Some(param) = parts.next() { route.push('-'); @@ -86,7 +86,7 @@ fn resolve_definition(head: &str) -> StrResult { return Ok(route); } - let mut route = format!("/docs/reference/{}/{name}/", category.name()); + let mut route = format!("{}reference/{}/{name}/", base, category.name()); if let Some(next) = parts.next() { if value.field(next).is_ok() { route.push_str("#definitions-");