diff --git a/README.md b/README.md index bd49aa5..dc93b76 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,9 @@ As another example we will number corollarys after the last theorem. ![image](docs/images/image_1.png) + +## Custom style example + This examples shows how custom style functions can be defined. ```typst #import "@preview/lemmify:0.2.0": default-theorems, get-theorem-parameters diff --git a/docs/generate_pdf.py b/docs/generate_pdf.py new file mode 100644 index 0000000..03aa945 --- /dev/null +++ b/docs/generate_pdf.py @@ -0,0 +1,18 @@ +import os +import subprocess + +README_FILE_PATH = os.path.join("docs", "readme.typ") + +def main(): + print("Compiling " + README_FILE_PATH, end="", flush=True) + completed_process = subprocess.run(["typst", "compile", README_FILE_PATH, "--root", "."], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if completed_process.returncode != 0: + print(" ✗") + print(completed_process.stderr) + print(completed_process.stdout) + exit(1) + else: + print(" ✓") + +if __name__ == "__main__": + main() diff --git a/docs/readme.pdf b/docs/readme.pdf index 29b9e46..1463a58 100644 Binary files a/docs/readme.pdf and b/docs/readme.pdf differ diff --git a/docs/readme.typ b/docs/readme.typ index 1588f33..cd57f90 100644 --- a/docs/readme.typ +++ b/docs/readme.typ @@ -133,6 +133,8 @@ As another example we will number corollarys after the last theorem. export-setup: "#set page(width: 300pt, height: auto, margin: 10pt)" ) +== Custom style example + This examples shows how custom style functions can be defined. #let example2 = ```typst @@ -182,6 +184,8 @@ This examples shows how custom style functions can be defined. export-setup: "#set page(width: 500pt, height: auto, margin: 10pt)" ) +#include "source_docs.typ" + // If you are encountering any bugs, have questions or // are missing features, feel free to open an issue on // #link("https://github.com/Marmare314/lemmify")[GitHub] diff --git a/docs/source_docs.typ b/docs/source_docs.typ new file mode 100644 index 0000000..dd977da --- /dev/null +++ b/docs/source_docs.typ @@ -0,0 +1,41 @@ +#import "@preview/tidy:0.1.0": parse-module, show-module + +#let lib-parsed = parse-module(read("../src/lib.typ")) +#let reset-counter-parsed = parse-module(read("../src/reset-counter.typ")) +#let selectors-parsed = parse-module(read("../src/selectors.typ")) +#let styles-parsed = parse-module(read("../src/styles.typ")) +#let numbered-parsed = parse-module(read("../src/numbered.typ")) +#let theorem-parsed = parse-module(read("../src/theorem.typ")) + += Documentation + +#show-module(lib-parsed) + +== Styles + +There are a few pre-defined `style-function`s and `theorem-numbering-function`s. + +#show-module(styles-parsed) + +== Selectors + +The selectors can be used in show-rules to +customize the `theorem`s styling as well as +with the `link-to` parameter. + +#show-module(selectors-parsed) + +== Resetting counters + +#show-module(reset-counter-parsed) + +== Theorem utilities + +The functions in the remaining two sections are only needed when defining custom style or +theorem-numbering-functions. + +#show-module(theorem-parsed) + +== Numbered utilities + +#show-module(numbered-parsed) diff --git a/src/export-lib.typ b/src/export-lib.typ index c455982..51b6051 100644 --- a/src/export-lib.typ +++ b/src/export-lib.typ @@ -45,25 +45,19 @@ } #let is-theorem -#let assert-theorem #let get-theorem-parameters #let resolve-link #{ import "theorem.typ" is-theorem = theorem.is-theorem - assert-theorem = theorem.assert-theorem get-theorem-parameters = theorem.get-theorem-parameters resolve-link = theorem.resolve-link } -#let is-countable -#let assert-countable -#let get-countable-parameters -#let display-countable +#let is-numbered +#let display-numbered #{ - import "countable.typ" - is-countable = countable.is-countable - assert-countable = countable.assert-countable - get-countable-parameters = countable.get-countable-parameters - display-countable = countable.display-countable + import "numbered.typ" + is-numbered = numbered.is-numbered + display-numbered = numbered.display-numbered } diff --git a/src/lib.typ b/src/lib.typ index c3ac17d..39588b2 100644 --- a/src/lib.typ +++ b/src/lib.typ @@ -8,6 +8,26 @@ #let LEMMIFY-DEFAULT-THEOREM-GROUP = "LEMMIFY-DEFAULT-THEOREM-GROUP" #let LEMMIFY-DEFAULT-PROOF-GROUP = "LEMMIFY-DEFAULT-PROOF-GROUP" +/// Create a new theorem function. The style is only visible +/// once the `theorem-rules` have been applied. +/// The generated functions will be referred to as `kind-function`s +/// and the `figures` created by it will be referred to as `theorem`s. +/// +/// - kind-name (str): The name of the theorem kind. It acts +/// as an identifier together with `group` when using `select-kind`, +/// so it should be unique. +/// - group (str): The group identifier. Each theorem group shares one counter. +/// - link-to (label, selector, selector-function, none): Link the `theorem` to some +/// other content. See `numbering-concat` for how this is used. For `label`s and `selector`s the last one before the `theorem` is used. `selector-function`s are functions `(location) -> content` which can be used for +/// more advanced queries. +/// - numbering (theorem-numbering-function): A function `(theorem, bool) -> content`. +/// The `bool` argument is true only if +/// the numbering is requested from a reference +/// instead of from the theorem itself (see numbering-proof). +/// - subnumbering (numbering-function, str, none): The subnumbering is +/// needed to convert the theorems counter to content, +/// which is then used in the `theorem-numbering-function`. +/// - style (style-function): A function `(thm) -> content` which is used to style the theorem. #let theorem-kind( kind-name, group: LEMMIFY-DEFAULT-THEOREM-GROUP, @@ -41,6 +61,10 @@ } // TODO: handle custom supplements +/// Apply the style of every `theorem` and handle references to `theorem`s. +/// +/// - content (content): +/// -> content #let theorem-rules(content) = { show figure: it => if is-theorem(it) { let params = get-theorem-parameters(it) @@ -66,6 +90,27 @@ content } +/// Generate a few common theorem kinds in the specified language. +/// +/// This function accepts all parameters of `theorem-kind` once for proofs and +/// once for all kinds except for proofs. +/// So for information on the missing parameters refer to `theorem-kind`. +/// +/// - group (str): +/// - proof-group (str): +/// - lang (str): The language in which the theorem kinds are generated. +/// - style (style-function): +/// - proof-style (style-function): +/// - numbering (theorem-numbering-function): +/// - proof-numbering (theorem-numbering-function): +/// - link-to (label, selector, selector-function, none): +/// - proof-link-to (label, selector, selector-function, none): +/// - subnumbering (numbering-function, str, none): +/// - max-reset-level (int, none): If it is not none the theorem counter will +/// be reset on headings below `max-reset-level`. +/// And if `link-to` is set to `last-heading` +/// higher levels will not be displayed in the numbering. +/// -> dictionary #let default-theorems( group: LEMMIFY-DEFAULT-THEOREM-GROUP, proof-group: LEMMIFY-DEFAULT-PROOF-GROUP, diff --git a/src/countable.typ b/src/numbered.typ similarity index 51% rename from src/countable.typ rename to src/numbered.typ index b56e347..c60df9e 100644 --- a/src/countable.typ +++ b/src/numbered.typ @@ -1,6 +1,5 @@ #import "theorem.typ": is-theorem, get-theorem-parameters -// TODO: improve error messages #let is-countable(c) = { return ( type(c) == content @@ -8,15 +7,15 @@ ) } -#let assert-countable(c) = { +#let assert-countable(arg, arg-name) = { assert( - is-countable(c), - message: "expected heading, page, math.equation or figure, got " + type(c) + is-countable(arg), + message: "expected " + arg-name + "to be one of heading, page, math.equation or figure, but got " + str(type(arg)) ) } #let get-countable-parameters(c) = { - assert-countable(c) + assert-countable(c, "c") let counter = if c.func() == heading { counter(heading) @@ -45,24 +44,36 @@ ) } -#let is-numbered(c) = { - if is-countable(c) { - let params = get-countable-parameters(c) +/// Check if argument is numbered. +/// That means it is one of +/// `heading`, `page`, `math.equation` or `figure` +/// and its numbering is not `none`. +/// +/// - n (any): +/// -> bool +#let is-numbered(n) = { + if is-countable(n) { + let params = get-countable-parameters(n) return params.numbering != none } return false } -#let assert-numbered(c) = { +#let assert-numbered(arg, arg-name) = { assert( - is-numbered(c), - message: "expected numbered countable" + is-numbered(arg), + message: "expected " + arg-name + " to be numbered, but got " + str(type(arg)) ) } -#let display-countable(c) = { - assert-numbered(c) +/// Display the numbering of the argument +/// at its location. +/// +/// - n (numbered): +/// -> content +#let display-numbered(n) = { + assert-numbered(n, "n") - let params = get-countable-parameters(c) + let params = get-countable-parameters(n) numbering(params.numbering, ..params.counter.at(params.location)) } diff --git a/src/reset-counter.typ b/src/reset-counter.typ index e9ddc97..e97eef0 100644 --- a/src/reset-counter.typ +++ b/src/reset-counter.typ @@ -9,14 +9,23 @@ functions.fold((c => c), (f, g) => (c => f(g(c)))) } -// Reset theorem group counter to zero. +/// Reset theorem group counter to zero. +/// The result needs to be added to the document. +/// +/// - kind-func (kind-function): The group is obtained from this kind function. +/// -> content #let reset-counter(kind-func) = { assert-type(kind-func, "kind-func", function) - counter(select-group(kind-func)).update(c => 0) + return counter(select-group(kind-func)).update(c => 0) } -// Reset counter of specified theorem group -// on headings with at most the specified level. +/// Reset counter of theorem group +/// on headings with at most the specified level. +/// +/// - kind-func (kind-function): The group is obtained from this kind function. +/// - max-level (int): Should be at least 1. +/// - content (content): +/// -> content #let reset-counter-heading( kind-func, max-level, diff --git a/src/selectors.typ b/src/selectors.typ index d613173..bf6e1f1 100644 --- a/src/selectors.typ +++ b/src/selectors.typ @@ -1,6 +1,14 @@ #import "types.typ": assert-type, None #import "theorem.typ": get-theorem-parameters +// TODO: implement ignore-unnumbered +/// Selector-function which selects the last +/// heading. +/// +/// - ignore-unnumbered (bool): Use the first heading which is numbered. +/// - max-level (int, none): TODO +/// - loc (location): +/// -> heading, none #let last-heading( ignore-unnumbered: false, max-level: none, @@ -37,12 +45,24 @@ } } +/// Generate selector that selects all +/// theorems of the same group as the +/// argument. +/// +/// - kind-func (kind-function): +/// -> selector #let select-group(kind-func) = { assert-type(kind-func, "kind-func", function) let params = get-theorem-parameters(kind-func[]) return figure.where(kind: params.group) } +/// Generate selector that selects only +/// theorems that were create from +/// the provided `kind-function`. +/// +/// - kind-func (kind-function): +/// -> selector #let select-kind(kind-func) = { assert-type(kind-func, "kind-func", function) diff --git a/src/styles.typ b/src/styles.typ index c088c2d..bfb5f96 100644 --- a/src/styles.typ +++ b/src/styles.typ @@ -1,16 +1,28 @@ #import "theorem.typ": resolve-link, get-theorem-parameters, is-theorem -#import "countable.typ": display-countable, is-numbered +#import "numbered.typ": display-numbered, is-numbered +#import "types.typ": assert-type -// TODO: assert type of seperator here +/// If the linked content is numbered combine it with the numbering +/// of the theorem. +/// +/// - thm (theorem): +/// - referenced (bool): +/// - seperator (content, str): The sepeartor is put between both numberings. #let numbering-concat(thm, referenced, seperator: ".") = { + assert-type(seperator, "seperator", content, str) let linked = resolve-link(thm) if linked != none and is-numbered(linked) { - display-countable(linked) + display-numbered(linked) seperator } - display-countable(thm) + display-numbered(thm) } +/// Copy the numbering of a linked `theorem` if referenced. +/// Otherwise no numbering is returned. +/// +/// - thm (theorem): +/// - referenced (bool): #let numbering-proof(thm, referenced) = { let linked = resolve-link(thm) if referenced and linked != none { @@ -20,8 +32,12 @@ } } -// TODO: assert type of qed +/// Simple theorem style. Check the documentation for images. +/// +/// - thm (theorem): +/// - qed (bool): Select if a box should be shown at the end. #let style-simple(thm, qed: false) = { + assert-type(qed, "qed", bool) let params = get-theorem-parameters(thm) block(width: 100%, breakable: true, { strong(params.kind-name) @@ -41,8 +57,12 @@ }) } -// TODO: assert type of qed +/// Reverses numbering and `kind-name`. +/// +/// - thm (theorem): +/// - qed (bool): Select if a box should be shown at the end. #let style-reversed(thm, qed: false) = { + assert-type(qed, "qed", bool) let params = get-theorem-parameters(thm) block(width: 100%, breakable: true, { if params.numbering != none { diff --git a/src/theorem.typ b/src/theorem.typ index d259fed..b67e668 100644 --- a/src/theorem.typ +++ b/src/theorem.typ @@ -36,6 +36,10 @@ ) } +/// Check if argument is of type theorem. +/// +/// - c (any): +/// -> bool #let is-theorem(c) = { if ( type(c) == content @@ -50,12 +54,23 @@ } } -#let assert-theorem(c) = { - assert(is-theorem(c), message: "expected theorem, but got " + type(c)) +#let assert-theorem(arg, arg-name) = { + assert( + is-theorem(arg), + message: "expected " + arg-name + " to be of type theorem, but got " + str(type(arg)) + ) } +/// Extract theorem parameters from figure. +/// Returns a dictionary of the form +/// (body, group, kind-name, +/// name, link-to, numbering, +/// subnumbering, style). +/// +/// - thm (theorem): +/// -> dictionary #let get-theorem-parameters(thm) = { - assert-theorem(thm) + assert-theorem(thm, "thm") let (type, ..hidden-params) = (thm.numbering)() return ( body: thm.body, @@ -66,6 +81,11 @@ ) } +/// Return the content that is linked +/// to the theorem. +/// +/// - thm (theorem): +/// -> content #let resolve-link(thm) = { let (link-to,) = get-theorem-parameters(thm) if type(link-to) == label or type(link-to) == selector { diff --git a/src/translations.typ b/src/translations.typ index 68c0f24..0977775 100644 --- a/src/translations.typ +++ b/src/translations.typ @@ -58,6 +58,6 @@ if lang in translations { return translations.at(lang) } else { - panic("no translation for the specified language") + panic("no translation available for the specified language") } } diff --git a/src/types.typ b/src/types.typ index 871cbb8..4dd9f92 100644 --- a/src/types.typ +++ b/src/types.typ @@ -1,39 +1,35 @@ -// TODO: clean this file up - #let None = type(none) -#let check-arg-sink( - allow-positional: true, - allow-named: true, - expect-argument: false, - args -) = { - if not allow-positional { - assert(args.pos().len() == 0, message: "expected no positional arguments") - } - if not allow-named { - assert(args.named().len() == 0, message: "expected no named arguments") - } - if expect-argument { - assert(args.named().len() + args.pos().len() > 0, message: "expected at least one argument") +#let check-type-args(types) = { + assert(types.named().len() == 0, message: "expected no named arguments") + assert(types.pos().len() > 0, message: "expected at least one argument") + for t in types.pos() { + assert(type(t) == type, message: "expected only type arguments, but got " + str(type(t))) } } #let check-type(arg, ..types) = { - check-arg-sink(types, allow-named: false, expect-argument: true) + check-type-args(types) return type(arg) in types.pos() } #let types-to-string(..types) = { - check-arg-sink(types, allow-named: false, expect-argument: true) + check-type-args(types) return types.pos().map(str).join(", ", last: " or ") } #let assert-type-error-msg(arg, arg-name, ..types) = { - return "expected " + arg-name + " to be one of " + types-to-string(..types) + ", but got " + type(arg) + check-type-args(types) + let s = if types.pos().len() == 1 { + " to be of type " + } else { + " to be one of " + } + return "expected " + arg-name + s + types-to-string(..types) + ", but got " + str(type(arg)) } #let assert-type(arg, arg-name, ..types) = { + check-type-args(types) assert( check-type(arg, ..types), message: assert-type-error-msg(arg, arg-name, ..types)