diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fc90d2..8da47c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ## Removed ## Changed +The following internal types have been renamed, because the types are only named in the documentation and did not change signature their renames are semver compatible. +- `context` -> `hydra-context` +- `hydra-selector` -> `full-selector` +- `sanitized-selector` -> `hydra-selector` --- diff --git a/Justfile b/Justfile index 7c0e84b..746d86b 100644 --- a/Justfile +++ b/Justfile @@ -32,7 +32,7 @@ gen-doc-examples: $"out/($it){n}.png") }; let pages = (ls out | length) / 2; - { pages: $pages } | to toml | save out.toml + { pages: $pages } | to toml | save -f out.toml } | ignore @@ -42,6 +42,10 @@ gen-doc-examples: doc: gen-doc-examples typst compile doc/manual.typ doc/manual.pdf +# watch the manual +watch-doc: gen-doc-examples + typst watch doc/manual.typ doc/manual.pdf + # copy the files relevant for the package repo publish output: alabaster package {{ output }} diff --git a/doc/chapters/1-intro.typ b/doc/chapters/1-intro.typ index c826a12..0d823d3 100644 --- a/doc/chapters/1-intro.typ +++ b/doc/chapters/1-intro.typ @@ -1,6 +1,7 @@ -Hydra is a package which aims to query and display section elements, such as headings, legal -paragraphs, documentation sections, and whatever else may semantically declare the start of a -document's section. +#import "/doc/util.typ": mantys +#import mantys: package + +Hydra is a package which aims to query and display section elements, such as headings,legal paragraphs, documentation sections, and whatever else may semantically declare the start of a document's section. == Terminology & Semantics The following terms are frequently used in the remainider of this document. @@ -8,17 +9,14 @@ The following terms are frequently used in the remainider of this document. / primary: The element which is primarily looked for and meant to be displayed. / ancestor: An element which is the immediate or transitive ancestor to the primary element. A level 3 heading is ancestor to both level 2 (directly) and level 1 headings (transitively). -/ scope: The scope of a primary element refers to the section of a document which is between the - closest ancestors. -/ active: The active element refers to whatever element is considered for display. While this is - usually the previous primary element, it may sometimes be the next primary element. -/ leading page: A leading page in a book is that, which is further along the content of the two - visible pages at any time, this is the `end` alignement with respect to the document readin - direction. +/ scope: The scope of a primary element refers to the section of a document which is between the closest ancestors. +/ active: The active element refers to whatever element is considered for display. + While this is usually the previous primary element, it may sometimes be the next primary element. +/ leading page: A leading page in a book is that, which is further along the content of the two visible pages at any time, this is the `end` alignment with respect to the document reading direction. / trailing page: A trailing page is that, which is not the leading page in a book. -The search for a primary element is always bounded to it's scope. For the following simplified -document: +The search for a primary element is always bounded to its scope. +For the following simplified document: ```typst = Chapter 1 @@ -36,8 +34,9 @@ Chapter 2 └ Subsection 2.0.1 ``` -hydra will only search within it's current chapter as it is looking for active sections. In this -case hydra would not find a suitable candidate. For this the ancestors of an element must be known. +Hydra will only search within its current chapter as it is looking for active sections. +In this case Hydra would not find a suitable candidate. +For this the ancestors of an element must be known. For headings this is simple: #align(center, ( ``, @@ -47,6 +46,5 @@ For headings this is simple: `...`, ).join([ #sym.arrow ])) -If hydra is used to query for level 2 headings it will only do so within the bounds of the closest -level 1 headings. In principle, elements other than headings can be used (see @custom), as long as -their semantic relationships are established. +If Hydra is used to query for level 2 headings it will only do so within the bounds of the closest level 1 headings. +In principle, elements other than headings can be used (see @custom), as long as their semantic relationships are established. diff --git a/doc/chapters/2-features.typ b/doc/chapters/2-features.typ index c66560a..53c0c5f 100644 --- a/doc/chapters/2-features.typ +++ b/doc/chapters/2-features.typ @@ -1,4 +1,5 @@ -#import "/doc/util.typ": load-examples, show-examples, issue +#import "/doc/util.typ": load-examples, show-examples, issue, mantys +#import mantys: package == Contextual Hydra will take contextual information into account to provide good defaults, such as inferring the @@ -7,7 +8,7 @@ seen in @book-mode. == Custom Elements Because some documents may use custom elements of some kind to display chapters or section like -elements, hydra allows defining its own selectors for tight control over how elements are +elements, Hydra allows defining its own selectors for tight control over how elements are semantically related. Given a custom element like so: @@ -38,20 +39,18 @@ A user my want to query for the current chapter and section respectively: }) ``` -The usage of `custom` allows specifying an element's ancestors, to ensure the scope is correctly -defined. The selectors module also contains some useful default selectors. +The usage of `custom` allows specifying an element's ancestors, to ensure the scope is correctly defined. +The selectors module also contains some useful default selectors. #pagebreak() == Redundancy Checks -Generally hydra is used for heading like elements, i.e. elements which semantically describe a -section of a document. Whenever hydra is used in a place where its output would be redundant, it -will not show any output by default. The following sections explain those checks more closely and -will generally assume that hydra is looking for headings for simplicity. +Generally Hydra is used for heading like elements, i.e. elements which semantically describe a section of a document. +Whenever Hydra is used in a place where its output would be redundant, it will not show any output by default. +The following sections explain those checks more closely and will generally assume that Hydra is looking for headings for simplicity. === Starting Page -Given a page which starts with a primary element, it will not show anything. If `skip-starting` is -set to `false`, it will fallback to the next element, in this case the heading at the top of the -page. +Given a page which starts with a primary element, it will not show anything. +If `skip-starting` is set to `false`, it will fallback to the next element, in this case the heading at the top of the page. #let skip = load-examples("skip") #figure( @@ -61,8 +60,8 @@ page. ], ) -For more complex selectors this will not correctly work if the first element on this page is an -ancestor. See #issue(8). +For more complex selectors this will not correctly work if the first element on this page is an ancestor +See #issue(8). #pagebreak() === Book Mode @@ -75,14 +74,12 @@ visible on the previous (trailing) page it is also skipped. caption: [An example document showing `book: false` (left) and `book: true` (right).], ) -This may produce unexpected results with hydra is used outside the header and the text direction -where it is used is different to where it's anchor (see @anchor) is placed. This can be prevented by -explicitly setting it's `binding` and `dir` arguments. +This may produce unexpected results with Hydra is used outside the header and the text direction where it is used is different to where its anchor (see @anchor) is placed. +This can be prevented by explicitly setting its `binding` and `dir` arguments. == Anchoring -To use hydra outside of the header, an anchor must be placed to get the correct active elements. -hydra will always use the last anchor it finds to search, it doesn't have to be inside the header, -but should generally be, otherwise the behavior may be unexpected. +To use Hydra outside of the header, an anchor must be placed to get the correct active elements. +Hydra will always use the last anchor it finds to search, it doesn't have to be inside the header, but should generally be, otherwise the behavior may be unexpected. ```typst #import "@preview/hydra:{{VERSION}}": hydra, anchor diff --git a/doc/chapters/3-reference.typ b/doc/chapters/3-reference.typ index 4201e04..7e5f86e 100644 --- a/doc/chapters/3-reference.typ +++ b/doc/chapters/3-reference.typ @@ -1,4 +1,4 @@ -#import "@preview/tidy:0.2.0" +#import "@local/mantys:0.1.2": * #import "/doc/util.typ": bbox #let stable(is) = if is { @@ -10,96 +10,112 @@ == Stability The following stability guarantees are made, this package tries to adhere to semantic versioning. -#table(columns: 2, gutter: 0.25em, align: (right, left), - stable(false), [API may change with any version bump.], +#table(columns: 2, gutter: 0.25em, align: (right, left), stroke: none, + stable(false), table.vline(), [API may change with any version bump.], stable(true), [ API will not change without a major version bump or a minor version bump before `1.0.0`, if such - a change occures it is a bug and unintended. + a change occurs it is a bug and unintended. ], ) +#let ref-heading(is-stable, body) = { + heading(depth: 3, body) + place( + right, + dy: -2.4em, + stable(is-stable), + ) +} + == Custom Types -#set heading(outlined: false) -The following custom types are used to pass around information easily: +The following custom types are used to pass around information easily, they are regular Typst dictionaries with expected keys. -=== `sanitized-selector` #stable(true) +#ref-heading(true)[queryable] +Any type which can be used in `query`. + +```typc +let queryable = label | function | selector +``` + +#ref-heading(true)[hydra-selector] Defines a selector for an ancestor or primary element. + ```typc -( +let hydra-selector = ( target: queryable, - filter: ((context, candidates) => bool) | none, + filter: ((hydra-context, candidates) => bool) | none, ) ``` -=== `hydra-selector` #stable(true) +#ref-heading(true)[full-selector] Defines a pair of primary and ancestor element selectors. ```typc -( - primary: sanitized-selector, - ancestors: sanitized-selector | none, +let full-selector = ( + primary: hydra-selector, + ancestors: hydra-selector | none, ) ``` -=== `candidates` #stable(true) +#ref-heading(true)[candidates] Defines the candidates that have been found in a specific context. ```typc -( +let candidates = ( primary: (prev: content | none, next: content | none), ancestor: (prev: content | none, next: content | none), ) ``` -=== `context` #stable(false) -Defines the options passed to hydra nad resolved contextual information needed for querying and +#ref-heading(false)[hydra-context] +Defines the options passed to Hydra and resolved contextual information needed for querying and displaying. ```typc -( - prev-filter: (context, candidates) => bool, - next-filter: (context, candidates) => bool, - display: (context, content) => content, +let hydra-context = ( + prev-filter: (hydra-context, candidates) => bool, + next-filter: (hydra-context, candidates) => bool, + display: (hydra-context, content) => content, skip-starting: bool, book: bool, anchor: label | none, anchor-loc: location, - primary: sanitized-selector, - ancestors: sanitized-selector, + primary: hydra-selector, + ancestors: hydra-selector, ) ``` #pagebreak() -#set heading(numbering: none) #let mods = ( - (`hydra`, "/src/lib.typ", true, [ + ("hydra", "/src/lib.typ", true, [ The package entry point. All functions validate their inputs and panic using error messages directed at the end user. ]), - (`core`, "/src/core.typ", false, [ + ("core", "/src/core.typ", false, [ The core logic module. Some functions may return results with error messages that can be used to panic or recover from instead of panicking themselves. ]), - (`selectors`, "/src/selectors.typ", true, [ + ("selectors", "/src/selectors.typ", true, [ Contains functions used for creating custom selectors. ]), - (`util`, "/src/util.typ", false, [ + ("util", "/src/util.typ", false, [ Utlity functions and values. ]), - (`util/core`, "/src/util/core.typ", false, [ + ("util/core", "/src/util/core.typ", false, [ Utlity functions. ]), - (`util/assert`, "/src/util/assert.typ", false, [ + ("util/assert", "/src/util/assert.typ", false, [ Assertions used for input and state validation. ]), ) -#let render-module(title, path, is-stable, description, style: tidy.styles.minimal) = [ - == #title #stable(is-stable) +#let render-module(name, path, is-stable, description) = [ + #ref-heading(is-stable, name) #description - #tidy.show-module(tidy.parse-module(read(path)), style: style) + #tidy-module(read(path), name: name) ] +== Modules #mods.map(x => render-module(..x)).join(pagebreak(weak: true)) diff --git a/doc/manual.pdf b/doc/manual.pdf index 0d8ee98..2a78663 100644 Binary files a/doc/manual.pdf and b/doc/manual.pdf differ diff --git a/doc/manual.typ b/doc/manual.typ index b707599..5ea00d6 100644 --- a/doc/manual.typ +++ b/doc/manual.typ @@ -1,22 +1,46 @@ #import "template.typ": project, raw-bg -#import "util.typ": package, bbox +#import "util.typ": package, bbox, mantys -#show: project.with( - package: package, - subtitle: [/ˈhaɪdrə/ \ Of headings and headers], - date: datetime.today(), - abstract: package.description, +#show "Hydra": mantys.package + +#show: mantys.mantys.with( + ..package, + title: [hydra], + subtitle: [/ˈhaɪdrə/], + date: datetime.today().display(), + abstract: [ + Hydra provides a simple API to querying for headings und section like elements and displaying them in headers or footers. + It exposes internal modules and functions for maximum control. + ], ) -#[ - #show raw.where(block: false): bbox.with(fill: raw-bg) +#show raw: it => { + show "{{VERSION}}": package.version + it +} + += Manifest +Hydra aims to be: +- simple to use + - importing a function and using it should be all that is needed + - setup required to make the package work should be avoided +- unsurprising + - parameters should have sensible names and behave as one would expect + - deviations from this must be documented and easily accesible to Typst novices +- interoperable + - Hydra should be easy to use with other packages by default or provide sufficient configuration to allow this in other ways +- minimal + - it should only provide features which are specifically used for heading and section querying and display + +If you think its behvior is surprising, you believe you found a bug or you think its defaults or parameters are not sufficient for your use case, please open an issue at #text(eastern, link("https://github.com/tingerrr/hydra")[GitHub:tingerrr/hydra]). +Contributions are also welcome! - = Introduction - #include "chapters/1-intro.typ" += Guide +== Introduction +#include "chapters/1-intro.typ" - = Features - #include "chapters/2-features.typ" -] +== Features +#include "chapters/2-features.typ" = Reference #include "chapters/3-reference.typ" diff --git a/doc/util.typ b/doc/util.typ index 9aa4ac7..2ac0950 100644 --- a/doc/util.typ +++ b/doc/util.typ @@ -1,3 +1,5 @@ +#import "@local/mantys:0.1.2" + #let package = toml("/typst.toml").package #let load-examples(example) = { diff --git a/src/core.typ b/src/core.typ index 0cac6d4..a3d387d 100644 --- a/src/core.typ +++ b/src/core.typ @@ -46,11 +46,12 @@ margin.length.to-absolute() + (min * margin.ratio) } -/// Get the last anchor location. Panics if the last anchor was not on the page of this context. +/// Get the last anchor location. Panics if the last anchor was not on the page of this +/// hydra-context. /// /// This function is contextual. /// -/// - ctx (context): The context from which to start. +/// - ctx (hydra-context): The hydra-context from which to start. /// -> location #let locate-last-anchor(ctx) = { let starting-locs = query(selector(ctx.anchor).before(here())) @@ -74,11 +75,11 @@ anchor } -/// Get the element candidates for the given context. +/// Get the element candidates for the given hydra-context. /// /// This function is contextual. /// -/// - ctx (context): The context for which to get the candidates. +/// - ctx (hydra-context): The hydra-context for which to get the candidates. /// -> candidates #let get-candidates(ctx) = { let look-behind = selector(ctx.primary.target).before(ctx.anchor-loc) @@ -124,13 +125,14 @@ ) } -/// Checks if the current context is on a starting page, i.e. if the next candidates are on top of -/// this context's page. +/// Checks if the current hydra-context is on a starting page, i.e. if the next candidates are on +/// top of this hydra-context's page. /// /// This function is contextual. /// -/// - ctx (context): The context in which the visibility of the next candidates should be checked. -/// - candidates (candidates): The candidates for this context. +/// - ctx (hydra-context): The hydra-context in which the visibility of the next candidates should +/// be checked. +/// - candidates (candidates): The candidates for this hydra-context. /// -> bool #let is-on-starting-page(ctx, candidates) = { let next = if candidates.primary.next != none { candidates.primary.next.location() } @@ -155,9 +157,9 @@ /// /// This function is contextual. /// -/// - ctx (context): The context in which the visibility of the previous primary candidate should be -/// checked. -/// - candidates (candidates): The candidates for this context. +/// - ctx (hydra-context): The hydra-context in which the visibility of the previous primary +/// candidate should be checked. +/// - candidates (candidates): The candidates for this hydra-context. /// -> bool #let is-active-visible(ctx, candidates) = { // depending on the reading direction and binding combination the leading page is either on an odd @@ -179,13 +181,13 @@ is-leading-page and active-on-prev-page } -/// Check if showing the active element would be redudnant in the current context. +/// Check if showing the active element would be redudnant in the current hydra-context. /// /// This function is contextual. /// -/// - ctx (context): The context in which the redundancy of the previous primary candidate should be -/// checked. -/// - candidates (candidates): The candidates for this context. +/// - ctx (hydra-context): The hydra-context in which the redundancy of the previous primary +/// candidate should be checked. +/// - candidates (candidates): The candidates for this hydra-context. /// -> bool #let is-active-redundant(ctx, candidates) = { let active-visible = ( @@ -198,7 +200,7 @@ /// Display a heading's numbering and body. /// -/// - ctx (context): The context in which the element was found. +/// - ctx (hydra-context): The hydra-context in which the element was found. /// - candidate (content): The heading to display, panics if this is not a heading. /// -> content #let display(ctx, candidate) = { @@ -214,11 +216,11 @@ candidate.body } -/// Execute the core logic to find and display elements for the current context. +/// Execute the core logic to find and display elements for the current hydra-context. /// /// This function is contextual. /// -/// - ctx (context): The context for which to find and display the element. +/// - ctx (hydra-context): The hydra-context for which to find and display the element. /// -> content #let execute(ctx) = { ctx.anchor-loc = if ctx.anchor != none and here().position().y >= get-top-margin() { diff --git a/src/lib.typ b/src/lib.typ index 52ead0b..7ee6e60 100644 --- a/src/lib.typ +++ b/src/lib.typ @@ -3,35 +3,36 @@ #import "/src/selectors.typ" /// An anchor used to search from. When using `hydra` ouside of the page header, this should be -/// placed inside the pge header to find the correct searching context. `hydra` always searches from -/// the last anchor it finds, if and only if it detects that it is outside of the top-margin. +/// placed inside the pge header to find the correct searching hydra-context. `hydra` always +/// searches from the last anchor it finds, if and only if it detects that it is outside of the +/// top-margin. #let anchor() = [#metadata(()) ] /// Query for an element within the bounds of its ancestors. /// -/// The context passed to various callbacks contains the resolved top-margin, the current location, -/// as well as the binding direction, primary and ancestor element selectors and customized -/// functions. +/// The hydra-context passed to various callbacks contains the resolved top-margin, the current +/// location, as well as the binding direction, primary and ancestor element selectors and +/// customized functions. /// /// This function is contextual. /// -/// - ..sel (any): The element to look for, to use other elements than headings, read the -/// documentation on selectors. This can be an element function or selector, or an integer -/// declaring a heading level. -/// - prev-filter (function, auto): A function which receives the `context` and `candidates`, and -/// returns if they are eligible for display. This function is called at most once. The primary -/// next candidate may be none. If this is `auto` no filter is applied. -/// - next-filter (function, auto): A function which receives the `context` and `candidates`, and -/// returns if they are eligible for display. This function is called at most once. The primary -/// prev candidate may be none. If this is `auto` no filter is applied. -/// - display (function, auto): A function which receives the `context` and candidate element to -/// display. If this is `auto`, the default implementaion will be used. +/// - ..sel (queryable, full-selector, int): The element to look for, to use other elements than +/// headings, read the documentation on selectors. This can be an element function or selector, or +/// an integer declaring a heading level. +/// - prev-filter (function, auto): A function which receives the `hydra-context` and `candidates`, +/// and returns if they are eligible for display. This function is called at most once. The +/// primary next candidate may be none. If this is `auto` no filter is applied. +/// - next-filter (function, auto): A function which receives the `hydra-context` and `candidates`, +/// and returns if they are eligible for display. This function is called at most once. The +/// primary prev candidate may be none. If this is `auto` no filter is applied. +/// - display (function, auto): A function which receives the `hydra-context` and candidate element +/// to display. If this is `auto`, the default implementaion will be used. /// - skip-starting (bool): Whether `hydra` should show the current candidate even if it's on top of /// the current page. /// - dir (direction, auto): The reading direction of the document. If this is `auto`, the text /// direction is used. Be cautious about leaving this option on `auto` if you switch text /// direction mid-page and use hydra outside of footers or headers. -/// - binding (alignement, auto): The binding of the document. If this is `auto`, the binding is +/// - binding (alignment, auto): The binding of the document. If this is `auto`, the binding is /// inferred from `dir`, similar to how it is done in page. Be cautious about leaving this on /// option on `auto` if you switch text direction mid-page and use hydra outside of footers or /// headers. diff --git a/src/selectors.typ b/src/selectors.typ index 818b245..7aa1f5d 100644 --- a/src/selectors.typ +++ b/src/selectors.typ @@ -2,9 +2,9 @@ /// Create a custom selector for `hydra`. /// -/// - element (function, selector): The primary element to search for. +/// - element (queryable): The primary element to search for. /// - filter (function): The filter to apply to the element. -/// - ancestors (function, selector): The ancestor elements, this should match all of its ancestors. +/// - ancestors (queryable): The ancestor elements, this should match all of its ancestors. /// - ancestors-filter (function): The filter applied to the ancestors. /// -> hydra-selector #let custom( @@ -100,7 +100,7 @@ /// *This function is considered unstable.* /// /// - name (str): The name to use in the assertion message. -/// - sel (any): The selector to sanitize. +/// - sel (queryable, full-selector, int): The selector to sanitize. /// - message (str, auto): The assertion message to use. /// -> hydra-selector #let sanitize(name, sel, message: auto) = { diff --git a/src/util/core.typ b/src/util/core.typ index ba9c94b..de17c74 100644 --- a/src/util/core.typ +++ b/src/util/core.typ @@ -29,7 +29,7 @@ /// Source: #link("https://github.com/typst/typst/blob/9646a132a80d11b37649b82c419833003ac7f455/crates/typst/src/layout/page.rs#L368-L373")[`page.rs#L368-L373`] /// /// dir (direction): The direction to get the page binding for. -/// -> alignement +/// -> alignment #let page-binding(dir) = (ltr: left, rtl: right).at(repr(dir)) /// A list of queryable element functions.