Coder Social home page Coder Social logo

tidy's Introduction

Tidy

Keep it tidy.

Typst Package MIT License User Manual

tidy is a package that generates documentation directly in Typst for your Typst modules. It parses docstring comments similar to javadoc and co. and can be used to easily build a beautiful reference section for the parsed module. Within the docstring you may use (almost) any Typst syntax − so markup, equations and even figures are no problem!

Features:

The guide fully describes the usage of this module and defines the format for the docstrings.

Usage

Using tidy is as simple as writing some docstrings and calling:

#import "@preview/tidy:0.3.0"

#let docs = tidy.parse-module(read("my-module.typ"))
#tidy.show-module(docs, style: tidy.styles.default)

The available predefined styles are currenty tidy.styles.default and tidy.styles.minimal. Custom styles can be added by hand (take a look at the guide).

Example

A full example on how to use this module for your own package (maybe even consisting of multiple files) can be found at examples.

/// This function computes the cardinal sine, $sinc(x)=sin(x)/x$. 
///
/// #example(`#sinc(0)`, mode: "markup")
///
/// - x (int, float): The argument for the cardinal sine function. 
/// -> float
#let sinc(x) = if x == 0 {1} else {calc.sin(x) / x}

tidy turns this into:

Tidy example output

Access user-defined functions and images

The code in the docstrings is evaluated via eval(). In order to access user-defined functions and images, you can make use of the scope argument of tidy.parse-module():

#{
    import "my-module.typ"
    let module = tidy.parse-module(read("my-module.typ"))
    let an-image = image("img.png")
    tidy.show-module(
        module,
        style: tidy.styles.default,
        scope: (my-module: my-module, img: an-image)
    )
}

The docstrings in my-module.typ may now access the image with #img and can call any function or variable from my-module in the style of #my-module.my-function(). This makes rendering examples right in the docstrings as easy as a breeze!

Generate a help command for you package

With tidy, you can add a help command to you package that allows users to obtain the documentation of a specific definition or parameter right in the document. This is similar to CLI-style help commands. If you have already written docstrings for your package, it is quite low-effort to add this feature. Once set up, the end-user can use it like this:

// happily coding, but how do I use this one complex function again?

#mypackage.help("func")
#mypackage.help("func(param1)") // print only parameter description of param1

This will print the documentation of func directly into the document — no need to look it up in a manual. Read up in the guide for setup instructions.

Docstring tests

It is possible to add simple docstring tests — assertions that will be run when the documentation is generated. This is useful if you want to keep small tests and documentation in one place.

/// #test(
///   `num.my-square(2) == 4`,
///   `num.my-square(4) == 16`,
/// )
#let my-square(n) = n * n

With the short-hand syntax, a unfulfilled assertion will even print the line number of the failed test:

/// >>> my-square(2) == 4
/// >>> my-square(4) == 16
#let my-square(n) = n * n

A few test assertion functions are available to improve readability, simplicity, and error messages. Currently, these are eq(a, b) for equality tests, ne(a, b) for inequality tests and approx(a, b, eps: 1e-10) for floating point comparisons. These assertion helper functions are always available within docstring tests (with both test() and >>> syntax).

Changelog

v0.3.0

  • New features:
    • Help feature.
    • preamble option for examples (e.g., to add import statements).
    • more options for show-module: omit-private-definitions, omit-private-parameters, enable-cross-references, local-names (for configuring language-specific strings).
  • Improvements:
    • Allow using show-example() as standalone.
    • Updated type names that changed with Typst 0.8.0, e.g., integer -> int.
  • Fixes:
    • allow examples with ratio widths if scale-preview is not auto.
    • show-outline
    • explicitly use raw(lang: none) for types and function names.

v0.2.0

  • New features:
    • Add executable examples to docstrings.
    • Documentation for variables (as well as functions).
    • Docstring tests.
    • Rainbow-colored types color and gradient.
  • Improvements:
    • Allow customization of cross-references through show-reference().
    • Allow customization of spacing between functions through styles.
    • Allow color customization (especially for the default theme).
  • Fixes:
    • Empty parameter descriptions are omitted (if the corresponding option is set).
    • Trim newline characters from parameter descriptions.
  • ⚠️ Breaking changes:
    • Before, cross-references for functions using the @@ syntax could omit the function parentheses. Now this is not possible anymore, since such references refer to variables now.
    • (only concerning custom styles) The style functions show-outline(), show-parameter-list, and show-type() now take style-args arguments as well.

v0.1.0

Initial Release.

tidy's People

Contributors

mc-zen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

ljgago mkpoli

tidy's Issues

Use alpha in `type-colors` to support dark mode themes

When the default tidy style is applied to a page with a black background & white text, the styling prevents visibility:
image

However, if you added transparency to every color in the style list, the light mode is unaffected and the dark mode looks fine:
image

Relevant changes:

#import "@preview/tidy:0.1.0"
#import tidy.styles.default: *

#let show-parameter-block(
  name, types, content, style-args,
  show-default: false,
  default: none,
) = block(
  inset: 10pt, fill: rgb("ddd3"), width: 100%,
  breakable: style-args.break-param-descriptions,
  [
    #text(weight: "bold", size: 1.1em, name)
    #h(.5cm)
    #types.map(x => (style-args.style.show-type)(x)).join([ #text("or",size:.6em) ])

    #content
    #if show-default [ #parbreak() Default: #raw(lang: "typc", default) ]
  ]
)

#let type-colors = (
  "content": rgb("#a6ebe699"),
  "color": rgb("#a6ebe699"),
  "string": rgb("#d1ffe299"),
  "none": rgb("#ffcbc499"),
  "auto": rgb("#ffcbc499"),
  "boolean": rgb("#ffedc199"),
  "integer": rgb("#e7d9ff99"),
  "float": rgb("#e7d9ff99"),
  "ratio": rgb("#e7d9ff99"),
  "length": rgb("#e7d9ff99"),
  "angle": rgb("#e7d9ff99"),
  "relative-length": rgb("#e7d9ff99"),
  "fraction": rgb("#e7d9ff99"),
  "symbol": rgb("#eff0f399"),
  "array": rgb("#eff0f399"),
  "dictionary": rgb("#eff0f399"),
  "arguments": rgb("#eff0f399"),
  "selector": rgb("#eff0f399"),
  "module": rgb("#eff0f399"),
  "stroke": rgb("#eff0f399"),
  "function": rgb("#f9dfff99"),
)
#let get-type-color(type) = type-colors.at(type, default: rgb("#eff0f333"))

// Create beautiful, colored type box
#let show-type(type) = {
  h(2pt)
  box(outset: 2pt, fill: get-type-color(type), radius: 2pt, raw(type))
  h(2pt)
}

Error using `show-example`

#import "@preview/tidy:0.2.0": show-example

#show raw: show-example.show-example

```typ
Hello world
```

Produces the error

error: variables from outside the function are read-only and cannot be modified
   ┌─ @preview/tidy:0.2.0\src\show-example.typ:32:4

32 │     mode = "markup"
   │     ^^^^

Is it possible to document dictionary schema?

I am curious if it is possible to make documentation showing the "types" of a dictionary variable.

Consider the following two cases:

  1. I have a variable in my package:
    #let themes = (
        light: "light",
        dark: "dark"
    )
  2. I have a function that returns a dictionary of entries. This might look like:
     #let return-value = (
       name: "Mocha",
       emoji: "🌿",
       order: 3,
       dark: true,
       light: false,
       colors: (
         rosewater: (
           name: "Rosewater",
           order: 0,
           hex: "#f5e0dc",
           rgb: rgb(245, 224, 220),
           accent: true,
         ),
     	flamingo: (...),
     	pink: (...),
     	...
       ),
     )

In my documentation, I want to have a result that looks something like:

  1. This
    theme = (
    	light: string,
    	dark: string,
    )
  2. Or this
    return-value = (
      name: string,
      emoji: string,
      order: integer,
      dark: boolean,
      light: boolean,
      colors: dict(
        key: (
          name: string,
          order: integer,
          hex: string,
          rgb: color,
          accent: boolean,
        )
      ),
    )

The second case is a bit more difficult since it has a nested schema. I was originally going to do this manually, but using - name (type): desc notation expects it to be documentation related to arguments, rather than just general text.

Edit:

For additional demonstration, this is how I am currently doing things:

/// Get the color palette for the given theme.
/// ==== Schema
/// - name (```typ string```): The name of the flavor (e.g. Frappé)
/// - emoji (```typ string```): The emoji associated with the flavor.
/// - order (```typ integer```): The order of the flavor in the Catppuccin lineup.
/// - dark (```typ boolean```): Whether the flavor is a dark theme.
/// - light (```typ boolean```): Whether the flavor is a light theme.
/// - colors (```typ dictionary```): A dictionary of colors used in the flavor. Keys are the color names as a ```typ string``` and values are dictionaries with the following keys:
///   - name (```typ string```): The name of the color.
///   - order (```typ integer```): The order of the color in the palette.
///   - hex (```typ string```): The hex value of the color.
///   - rgb (```typ string```): The RGB value of the color.
///   - accent (```typ boolean```): Whether the color is an accent color.
///
/// ==== Example
/// \<removed for brevity\>
///
/// - theme (string): The theme to get the palette for. The dict @@themes can be used to simplify this.
/// -> dictionary
#let get_palette(theme) = { ... }

This ends up looking like this (ignore the applied theming):

raw elements in examples are explicitly set to `block: true`

Reproducing example:

#import "@preview/tidy:0.3.0"

#let module = ````
/// #example(```
/// [a #raw("foo") b]
/// ```)
let x = none
````.text

#let module = tidy.parse-module(
  module,
)
#tidy.show-module(
  module,
  sort-functions: none,
  style: tidy.styles.minimal,
)

This uses #raw("foo") in an example. By default, this would not result in inline raw content, but due to this line it becomes a block. If I understand the intent behind it correctly, I think this could go here instead.

Allow localization through `style-args` for hard-coded strings

For packages, especially templates, if one is only useful in a specific language that is not English, such as University thesis in monolingual countries (in Chinese or Japanese, etc.), or if a pacakge want to provided multi-lingual documentation, localization will become a requirement.

There are some baked-in strings such as Parameters and Defaults: that are currently customizable through custom styles. However, If one only wants to change the locale but keep the styles the same, it would be very much cost to do that. Instead, it would be helpful to allow localization through style-args.

Allow additional markup outside docstrings

It would be nice to be able to structure the documentation by allowing additional markup in a module file, that is not attached to a function. For example:

/// == Utility functions
/// The following functions are utilities.

/// A foo function.
#let foo(a, b) = [#a-#b]

/// == Math functions
/// Some math markup

#let inv(x) = $1/x$

After rendering and parsing, the headings and descriptive text would render between function documentation.

Open question:

  • How to handle this additional markup with sort-function?

Parsing fails for specific order of functions

I encountered an issue where tidy fails with an error in a specific document case.

The module is called qrutil.typ and looks like this:

// aliases
#let mod = calc.rem
#let mod2(x) = calc.rem(x, 2)
#let mod3(x) = calc.rem(x, 3)
#let mod255(x) = calc.rem(x, 255)
#let mod256(x) = calc.rem(x, 256)
#let mod285(x) = calc.rem(x, 285)


/// >>> qrutil.check-version(1)
/// >>> qrutil.check-version(30)
/// >>> qrutil.check-version(40)
/// >>> not qrutil.check-version(0)
/// >>> not qrutil.check-version(41)
#let check-version(version) = version >= 1 and version <= 40

/// >>> qrutil.check-ecl("l")
/// >>> qrutil.check-ecl("h")
/// >>> qrutil.check-ecl("m")
/// >>> qrutil.check-ecl("q")
/// >>> not qrutil.check-ecl("a")
/// >>> not qrutil.check-ecl("Q")
#let check-ecl(ecl) = ecl in ("l", "m", "q", "h")

/// >>> qrutil.size(1) == 21
/// >>> qrutil.size(2) == 25
/// >>> qrutil.size(33) == 149
/// >>> qrutil.size(40) == 177
#let size(version) = { return 21 + (version - 1)*4 }


// =================================
//  Encoding
// =================================
/// >>> qrutil.best-mode("0123") == 0
/// >>> qrutil.best-mode("0000") == 0
/// >>> qrutil.best-mode("1") == 0
/// >>> qrutil.best-mode("A") == 1
/// >>> qrutil.best-mode("ABCD") == 1
/// >>> qrutil.best-mode("ABCD:XYZ$") == 1
/// >>> qrutil.best-mode("a") == 2
/// >>> qrutil.best-mode("abcxyz") == 2
/// >>> qrutil.best-mode("ABCD:XYZ!") == 2
/// >>> qrutil.best-mode("@€") == none
#let best-mode(data) = {
  let nums = regex(`^\d*$`.text)
  let alphnum = regex(`^[\dA-Z $%*+\-./:]*$`.text)
  let byte = regex(`^[\x00-\xff]*$`.text)
  if data.match(nums) != none {
    return 0
  }
  if data.match(alphnum) != none {
    return 1
  }
  if data.match(byte) != none {
    return 2
  }
  return none
}

/// foo
#let mode-bits(mode) = {
  return (
    (false, false, false, true),
    (false, false, true, false),
    (false, true, false, false),
    (true, false, false, false)
  ).at(mode)
}

The minimal manual code:

#import "@local/tidy:0.1.0"

#import "qrutil.typ"

#let doc = tidy.parse-module(
  read("qrutil.typ"),
  name: "qrutil",
  scope: (
    qrutil: qrutil
  )
)
#tidy.show-module(doc)

The error I get:

error: string index 1303 is not a character boundary
    ┌─ @local/tidy:0.1.0/src/tidy-parse.typ:219:7
    │
219 │     if string.at(i) == char { count += 1}
    │        ^^^^^^^^^^^^

help: error occurred in this call of function `count-occurences`
    ┌─ @local/tidy:0.1.0/src/tidy-parse.typ:254:26
    │
254 │   let first-line-number = count-occurences(source-code, "\n", end: match.start) + 1
    │                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

help: error occurred in this call of function `parse-function-docstring`
   ┌─ @local/tidy:0.1.0/src/tidy.typ:57:23
   │
57 │     function-docs.push(tidy-parse.parse-function-docstring(content, match, parse-info))
   │                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

help: error occurred in this call of function `parse-module`
   ┌─ /manual.typ:5:11
   │  
 5 │   #let doc = tidy.parse-module(
   │ ╭────────────^
 6 │ │   read("qrutil.typ"),
 7 │ │   name: "qrutil",
 8 │ │   scope: (
 9 │ │     qrutil: qrutil
10 │ │   )
11 │ │ )
   │ ╰─^

Allow users to pass in additional options

It would be nice to allow arbitrary options to be passed to #show-module that are available to themes in #style-args.options. This would allow themes to define custom options beyond the ones Tidy offers for further customization of the output. For example, a theme could allow users to pass in a primary color, that changes the style of the output.

I see two possible implementations:

  1. #show-module collects any unknown named arguments into #style-args.options.
  2. #show-module accepts a new parameter theme-options.

Compatible with typst 0.11.0?

Dear developer,

I just tied to use tidy with typst 0.11.0.

I copied /SRC to examples and then run

typst c wiggly-doc.typ

instead of building the help, I got several errors.

~/tools/ty/tidy/examples on main !1 ?1 > typst c wiggly-doc.typ
error: unknown variable: draw-sine
┌─ src/show-example.typ:48:23

48 │ let preview = [#eval(preamble + code.text, mode: mode, scope: scope + inherited-scope)]
│ ^^^^^^^^^^^^^^^^^^^^

= hint: if you meant to use subtraction, try adding spaces around the minus sign

help: error occurred in this call of function show-example
┌─ src/styles/minimal.typ:165:2

165 │ ╭ show-ex(
166 │ │ ..args,
167 │ │ code-block: block.with(stroke: .5pt + fn-color),
168 │ │ preview-block: block.with(stroke: .5pt + fn-color),
169 │ │ col-spacing: 0pt
170 │ │ )
│ ╰───^

help: error occurred in this call of function show-example
┌─ src/utilities.typ:35:7

35 │ eval(content, mode: "markup", scope: scope)
│ ^^^^^^^

help: error occurred in this call of function eval
┌─ src/utilities.typ:35:2

35 │ eval(content, mode: "markup", scope: scope)
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

help: error occurred in this call of function eval-docstring
┌─ src/styles/minimal.typ:102:14

102 │ pad(x: 0em, eval-docstring(fn.description, style-args))
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

help: error occurred in this call of function show-function
┌─ src/show-module.typ:120:4

120 │ (style-functions.show-function)(fn, style-args)
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

help: error occurred in this call of function show-module
┌─ wiggly-doc.typ:9:1

9 │ #show-module(docs, style: styles.minimal) │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

what am doing wrong?

Thank you so much for your help.

How to document variadic arguments

Thank you for this project!

In CeTZ we use a lot of var-args my-fun(..options), but I could find no way to
get typst-doc to match the argument to my docstring line:

//// my-fun
/// - ..options (any): Text

does not work. Is this currently possible?

Support for curried functions (func.with())

As per title, functions defined by currying are treated as variables rather than functions. Perhaps, could Tidy check if a variable is set using .with(), and import the documentation (if any) for that function? (recursively)

Help command: sink parameters cannot be accessed

When requesting the description of a sink parameter, e.g.,

#my-module.help("func(children)")

the corresponding parameter is not found and #my-module.help("func(..children)") does not work either.

#example() fails for code that results in content with relative width

Here's a minimal example with three modules given as inline code:

#import "@preview/tidy:0.2.0"

#tidy.show-module(
  tidy.parse-module(````
/// A function
///
/// #example(mode: "markup", ```
/// #rect(width: 2em)
/// ```)
#let func-a() = {}
````.text),
  style: tidy.styles.minimal
)

// #tidy.show-module(
//   tidy.parse-module(````
// /// A function
// ///
// /// #example(mode: "markup", ```
// /// #rect(width: 100%)
// /// ```)
// #let func-b() = {}
// ````.text),
//   style: tidy.styles.minimal
// )

#tidy.show-module(
  tidy.parse-module(````
/// A function
///
/// #example(mode: "markup", scale-preview: 100%, ```
/// #rect(width: 100%)
/// ```)
#let func-c() = {}
````.text),
  style: tidy.styles.minimal
)

The first module contains a func-a, and its documentation has an example. The width is 2em (plus some border) - no problem.

However, the second module (commented out) has a rectangle with width: 100%. This leads to a division by zero, it seems:

error: cannot divide by zero
   ┌─ @preview/tidy:0.2.0/src/show-example.typ:59:16
   │
59 │     calc.min(1, available-preview-width / preview-size.width) * 100%
   │                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The third module circumvents this: the division only happens if scale-preview == auto, so I set scale-preview: 100%. However, this seems to give the example content a "viewport" width of zero: width: preview-size.width * (scale-preview / 100%).

So in total: examples can't have a width that is a ratio, as measuring that leads to either dividing by or multiplying with zero.

Custom command references

The @@ommand() syntax is a nice addition. Maybe the final display of the reference could be passed to the style, to allow customization of the final reference?

For example, I integrated tidy with my template for package manuals Mantys and would like to format all commands in the manual with the same #cmd() function.

It could be done with a new function in the style called show-function-reference( location, fn ) or similar.

A way of displaying images in the documentation

I wanted to add an image to the documentation, but got the error cannot access file system from here. I'm sorry if I overlooked a possibility to already do that, but if not, I think adding it would be awesome.

Remove `show-example` default

Hi, I am working on the package Catppuccin and I am currently writing up the documentation, where I am using the theme that the library provides.

Currently, Tidy makes it very difficult to render examples that adhere to theming. For example, this line makes it impossible to set the preview theme, even if using the preamble argument in parse-module.

Could you please revise this function and remove any such defaults? I do not feel that explicitly setting the fill to white is necessary.

Thanks :)

Document font dependency (Cascadia Mono)

It would be nice if the fact that the default styles require Cascadia Mono is documented somewhere. Although this font is available in the webapp, it's probably not installed by default on non Microsoft systems.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.