Skip to content

Commit

Permalink
feat: support descriptions from comments
Browse files Browse the repository at this point in the history
Check if the first node of a given nix file is a comment
and if so, insert them after the files heading while correcting
whitespace.
  • Loading branch information
phaer committed Nov 9, 2023
1 parent 7682e3c commit 7e9c303
Show file tree
Hide file tree
Showing 3 changed files with 614 additions and 6 deletions.
48 changes: 42 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,26 @@ fn collect_entries(root: rnix::Root, category: &str) -> Vec<ManualEntry> {
vec![]
}

fn retrieve_description(nix: &rnix::Root, description: &str, category: &str) -> String {
format!(
"# {} {{#sec-functions-library-{}}}\n{}",
description,
category,
dedent(
&nix.syntax()
.first_child()
.and_then(|node| retrieve_doc_comment(&node, true)
// we add two spaces to the beginning after comment
// markers are stripped to keep dedent correct.
.map(|mut s| {
s.insert_str(0, " ");
s
}))
.unwrap_or_default()
)
)
}

fn main() {
let mut output = io::stdout();
let opts = Options::parse();
Expand All @@ -406,14 +426,10 @@ fn main() {
.expect("could not read location information"),
};
let nix = rnix::Root::parse(&src).ok().expect("failed to parse input");
let description = retrieve_description(&nix, &opts.description, &opts.category);

// TODO: move this to commonmark.rs
writeln!(
output,
"# {} {{#sec-functions-library-{}}}\n",
&opts.description, opts.category
)
.expect("Failed to write header");
writeln!(output, "{}", description).expect("Failed to write header");

for entry in collect_entries(nix, &opts.category) {
entry
Expand Down Expand Up @@ -450,6 +466,26 @@ fn test_main() {
insta::assert_snapshot!(output);
}

#[test]
fn test_description_of_lib_debug() {
let mut output = Vec::new();
let src = fs::read_to_string("test/lib-debug.nix").unwrap();
let nix = rnix::Root::parse(&src).ok().expect("failed to parse input");
let category = "debug";
let desc = retrieve_description(&nix, &"Debug", category);
writeln!(output, "{}", desc).expect("Failed to write header");

for entry in collect_entries(nix, category) {
entry
.write_section(&Default::default(), &mut output)
.expect("Failed to write section")
}

let output = String::from_utf8(output).expect("not utf8");

insta::assert_snapshot!(output);
}

#[test]
fn test_arg_formatting() {
let mut output = Vec::new();
Expand Down
326 changes: 326 additions & 0 deletions src/snapshots/nixdoc__description_of_lib_debug.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
---
source: src/main.rs
expression: output
---
# Debug {#sec-functions-library-debug}
Collection of functions useful for debugging
broken nix expressions.

* `trace`-like functions take two values, print
the first to stderr and return the second.
* `traceVal`-like functions take one argument
which both printed and returned.
* `traceSeq`-like functions fully evaluate their
traced value before printing (not just toweak
head normal formlike trace does by default).
* Functions that end in `-Fn` take an additional
function as their first argument, which is applied
to the traced value before it is printed.

## `lib.debug.traceIf` {#function-library-lib.debug.traceIf}

**Type**: `traceIf :: bool -> string -> a -> a`

Conditionally trace the supplied message, based on a predicate.

`pred`

: Predicate to check


`msg`

: Message that should be traced


`x`

: Value to return


::: {.example #function-library-example-lib.debug.traceIf}
# `lib.debug.traceIf` usage example

```nix
traceIf true "hello" 3
trace: hello
=> 3
```
:::

## `lib.debug.traceValFn` {#function-library-lib.debug.traceValFn}

**Type**: `traceValFn :: (a -> b) -> a -> a`

Trace the supplied value after applying a function to it, and
return the original value.

`f`

: Function to apply


`x`

: Value to trace and return


::: {.example #function-library-example-lib.debug.traceValFn}
# `lib.debug.traceValFn` usage example

```nix
traceValFn (v: "mystring ${v}") "foo"
trace: mystring foo
=> "foo"
```
:::

## `lib.debug.traceVal` {#function-library-lib.debug.traceVal}

**Type**: `traceVal :: a -> a`

Trace the supplied value and return it.

::: {.example #function-library-example-lib.debug.traceVal}
# `lib.debug.traceVal` usage example

```nix
traceVal 42
# trace: 42
=> 42
```
:::

## `lib.debug.traceSeq` {#function-library-lib.debug.traceSeq}

**Type**: `traceSeq :: a -> b -> b`

`builtins.trace`, but the value is `builtins.deepSeq`ed first.

`x`

: The value to trace


`y`

: The value to return


::: {.example #function-library-example-lib.debug.traceSeq}
# `lib.debug.traceSeq` usage example

```nix
trace { a.b.c = 3; } null
trace: { a = <CODE>; }
=> null
traceSeq { a.b.c = 3; } null
trace: { a = { b = { c = 3; }; }; }
=> null
```
:::
## `lib.debug.traceSeqN` {#function-library-lib.debug.traceSeqN}
**Type**: `traceSeqN :: Int -> a -> b -> b`
Like `traceSeq`, but only evaluate down to depth n.
This is very useful because lots of `traceSeq` usages
lead to an infinite recursion.
`depth`
: Function argument
`x`
: Function argument
`y`
: Function argument
::: {.example #function-library-example-lib.debug.traceSeqN}
# `lib.debug.traceSeqN` usage example
```nix
traceSeqN 2 { a.b.c = 3; } null
trace: { a = { b = {…}; }; }
=> null
```
:::
## `lib.debug.traceValSeqFn` {#function-library-lib.debug.traceValSeqFn}
A combination of `traceVal` and `traceSeq` that applies a
provided function to the value to be traced after `deepSeq`ing
it.
`f`
: Function to apply
`v`
: Value to trace
## `lib.debug.traceValSeq` {#function-library-lib.debug.traceValSeq}
A combination of `traceVal` and `traceSeq`.
## `lib.debug.traceValSeqNFn` {#function-library-lib.debug.traceValSeqNFn}
A combination of `traceVal` and `traceSeqN` that applies a
provided function to the value to be traced.
`f`
: Function to apply
`depth`
: Function argument
`v`
: Value to trace
## `lib.debug.traceValSeqN` {#function-library-lib.debug.traceValSeqN}
A combination of `traceVal` and `traceSeqN`.
## `lib.debug.traceFnSeqN` {#function-library-lib.debug.traceFnSeqN}
Trace the input and output of a function `f` named `name`,
both down to `depth`.
This is useful for adding around a function call,
to see the before/after of values as they are transformed.
`depth`
: Function argument
`name`
: Function argument
`f`
: Function argument
`v`
: Function argument
::: {.example #function-library-example-lib.debug.traceFnSeqN}
# `lib.debug.traceFnSeqN` usage example
```nix
traceFnSeqN 2 "id" (x: x) { a.b.c = 3; }
trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; }
=> { a.b.c = 3; }
```
:::
## `lib.debug.runTests` {#function-library-lib.debug.runTests}
**Type**:
```
runTests :: {
tests = [ String ];
${testName} :: {
expr :: a;
expected :: a;
};
}
->
[
{
name :: String;
expected :: a;
result :: a;
}
]
```
Evaluates a set of tests.
A test is an attribute set `{expr, expected}`,
denoting an expression and its expected result.
The result is a `list` of __failed tests__, each represented as
`{name, expected, result}`,
- expected
- What was passed as `expected`
- result
- The actual `result` of the test
Used for regression testing of the functions in lib; see
tests.nix for more examples.
Important: Only attributes that start with `test` are executed.
- If you want to run only a subset of the tests add the attribute `tests = ["testName"];`
`tests`
: Tests to run
::: {.example #function-library-example-lib.debug.runTests}
# `lib.debug.runTests` usage example
```nix
runTests {
testAndOk = {
expr = lib.and true false;
expected = false;
};
testAndFail = {
expr = lib.and true false;
expected = true;
};
}
->
[
{
name = "testAndFail";
expected = true;
result = false;
}
]
```
:::
## `lib.debug.testAllTrue` {#function-library-lib.debug.testAllTrue}
Create a test assuming that list elements are `true`.
`expr`
: Function argument
::: {.example #function-library-example-lib.debug.testAllTrue}
# `lib.debug.testAllTrue` usage example
```nix
{ testX = allTrue [ true ]; }
```
:::

0 comments on commit 7e9c303

Please sign in to comment.