Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,10 @@ src/nf_docs/
`render_to_directory()`. Factory function `get_renderer(format)` returns the appropriate renderer.
HTML uses a single Jinja2 template with inline CSS (Tailwind) and JavaScript for search. Adding a
new output format means updating `RENDERERS` in `renderers/__init__.py`,
`SINGLE_FILE_OUTPUT_POLICY` and (if it writes a directory) `DIRECTORY_FORMATS` in `output.py`, the
CLI's `--format` choices, and tests. Format aliases live only in `output.FORMAT_ALIASES`;
`get_renderer()` resolves through `normalize_format()` so there is one alias table.
`SINGLE_FILE_OUTPUT_POLICY` and (if it writes a directory) `DIRECTORY_FORMATS` in `output.py`, and
tests. The CLI's `--format` choices come from `output.supported_formats()`, so they follow
automatically. Format aliases live only in `output.FORMAT_ALIASES`; `get_renderer()` resolves
through `normalize_format()` so there is one alias table.
- **Config parsing**: `config_parser.py` shells out to `nextflow config -flat`. `nextflow_env.py`
sets an isolated `NXF_HOME` so this doesn't pollute the user's Nextflow state.
- **Two entry points**: `cli.py` (rich-click) and `api.py` (library). All source/output path and
Expand Down
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,26 @@
- `PipelineExtractor` accepts a `config=` argument, so callers can supply an `NfDocsConfig` instead
of relying on the global instance
- New "Python API" documentation page
- Every `NfDocsConfig` option now takes effect. `ignore_input_prefixes`, `include_hidden_params`,
`max_readme_length` and `exclude_patterns` were documented but never consulted; `default_format`
was ignored in favour of a hardcoded `html`
- Restored the `nf-docs config` command (`--path`, `--show-example`, `--init`), which had been
removed by accident along with the config wiring

### Fixed

- `strip_readme_badges` is consulted again when parsing a README. It was wired up when the option
was introduced, then dropped by an unrelated revert in the same release
- A parameter excluded by `include_hidden_params` or `ignore_input_prefixes` no longer reappears in
the Configuration section
- A config option set to the wrong type falls back to its default with a warning, rather than
failing somewhere downstream. `default_format: 3` used to be an uncaught `AttributeError`,
`max_readme_length: "lots"` silently dropped the README, and `exclude_patterns: "tests"` sent the
language server one exclusion per character
- `NfDocsConfig.from_dict()` copies its list defaults, so mutating a returned config no longer
changes `DEFAULT_CONFIG` for every later caller
- `default_format` is excluded from the extraction cache key. It only picks the CLI's output format,
so changing it no longer evicts every cached pipeline
- The extraction cache is now keyed on the configuration as well as the pipeline contents.
Previously a CLI run (which loads `~/.config/nf-docs/config.yaml`) and a library call (which uses
defaults) shared a cache entry for the same unchanged pipeline and returned each other's results
Expand All @@ -23,6 +40,22 @@

### Changed

- `include_hidden_params` now defaults to `true`, matching the behaviour nf-docs has always had of
showing hidden schema parameters. Set it to `false` to leave them out

### Upgrading

Six config options previously had no effect. If you already have a
`~/.config/nf-docs/config.yaml`, its settings now apply for the first time, and your output may
change:

- `include_hidden_params: false` — parameters marked `hidden` in `nextflow_schema.json` disappear
from the documentation. This is the option doing what it says, but it is a visible change; delete
the line (or set `true`) to keep the previous output
- `ignore_input_prefixes`, `max_readme_length`, `exclude_patterns` — likewise now honoured
- `default_format` — `nf-docs generate` without `-f` uses it instead of always producing HTML

Run `nf-docs config` to see the settings currently in effect.
- Output path policy (format aliases, single-module detection, default filenames) moved from
`cli.py` into a new `nf_docs.output` module so the CLI and the Python API share one implementation
- Library callers now get `NfDocsConfig()` defaults rather than the user's
Expand Down
11 changes: 8 additions & 3 deletions docs/python-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,14 @@ config = nf_docs.NfDocsConfig(ignore_config_prefixes=["genomes.", "test."])
nf_docs.extract("./my_pipeline", config=config)
```

`ignore_config_prefixes` drops matching parameters from the Configuration section, and defaults to
`["genomes."]` for nf-core pipelines. The other `NfDocsConfig` fields exist but aren't yet wired
into extraction, so don't rely on them.
The fields, their defaults and what each one does are listed under
[Configuration file](running.md#configuration-file). Two things specific to the API:

- `default_format` is read only by `nf-docs generate`. Setting it on a config you pass to
`extract()` or `generate()` does nothing — the API takes its format as an argument.
- Passing a config that isn't an `NfDocsConfig` (say, a plain dict) isn't supported. Build one with
`NfDocsConfig(...)`, or `NfDocsConfig.from_dict(...)` if you have a mapping and want the same
wrong-type checking `load_config()` applies.

## Caching

Expand Down
32 changes: 29 additions & 3 deletions docs/running.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ standalone module or subworkflow — see [Document a single module](#document-a-

| Option | Description |
| ------------------- | -------------------------------------------------------------------- |
| `--format`, `-f` | Output format: `html` (default), `markdown`, `table`, `json`, `yaml` |
| `--format`, `-f` | Output format: `markdown`, `table`, `json`, `yaml`, `html` (default) |
| `--output`, `-o` | Output file or directory (default: `docs/`) |
| `--title`, `-t` | Custom title |
| `--no-cache` | Force fresh extraction |
Expand Down Expand Up @@ -111,15 +111,41 @@ nf-docs clear-cache /path/to/pipeline

# Clear the entire extraction cache
nf-docs clear-cache --all

# Show configuration, its file path, or create a starter config file
nf-docs config
nf-docs config --path
nf-docs config --init
```

## Configuration file

nf-docs reads an optional config file at `~/.config/nf-docs/config.yaml` (respects
`$XDG_CONFIG_HOME`). It works without one. `nf-docs config --init` writes a commented starter file,
and `nf-docs config` shows the settings currently in effect.

| Option | Default | Effect |
| ------------------------ | -------------- | ----------------------------------------------------------------------------------- |
| `ignore_config_prefixes` | `["genomes."]` | Drop matching parameters from the Configuration section |
| `ignore_input_prefixes` | `[]` | Drop matching parameters from the Parameters section |
| `include_hidden_params` | `true` | Set `false` to leave out parameters marked `hidden` in `nextflow_schema.json` |
| `max_readme_length` | `0` | Trim README source text to this many characters, on a line boundary. `0` = no limit |
| `strip_readme_badges` | `true` | Set `false` to keep the badge lines below a README's title |
| `exclude_patterns` | `[]` | Extra paths the Language Server skips when indexing, added to the built-in ones |
| `default_format` | `html` | Format `nf-docs generate` uses when `-f/--format` isn't given |

An option set to the wrong type falls back to its default with a warning. These settings apply to
the CLI; the [Python API](python-api.md#configuration) uses defaults unless you pass a config
explicitly.

## Caching

nf-docs caches extraction results to avoid re-running the Language Server on every invocation.
Cached data is stored in `~/.cache/nf-docs/` (respects `$XDG_CACHE_HOME`).

The cache is keyed by pipeline path, a SHA-256 hash of all pipeline source files, and the nf-docs
version, so it is automatically invalidated whenever files change or nf-docs is upgraded.
The cache is keyed by pipeline path, a SHA-256 hash of all pipeline source files, the nf-docs
version, and the configuration options that affect extraction — so it is automatically invalidated
whenever files change, nf-docs is upgraded, or you edit a setting that changes the output.

Use `--no-cache` to skip the cache for a single run, or `nf-docs clear-cache` to remove entries
manually.
Expand Down
107 changes: 98 additions & 9 deletions src/nf_docs/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from pathlib import Path

import rich_click as click
import yaml
from rich.console import Console
from rich.logging import RichHandler
from rich.progress import (
Expand All @@ -23,7 +24,7 @@

from nf_docs import __version__
from nf_docs.api import extract as extract_pipeline
from nf_docs.config import load_config
from nf_docs.config import get_config_path, get_example_config, load_config
from nf_docs.extractor import ExtractionError, PipelineExtractor
from nf_docs.lsp_client import LSPError
from nf_docs.output import (
Expand All @@ -34,6 +35,7 @@
resolve_single_file_path,
resolve_source,
streams_by_default,
supported_formats,
)
from nf_docs.progress import ProgressUpdate
from nf_docs.renderers import get_renderer
Expand Down Expand Up @@ -183,9 +185,12 @@ def _display_path(path: Path) -> str:
"--format",
"-f",
"output_format",
type=click.Choice(["json", "yaml", "markdown", "md", "html", "table"], case_sensitive=False),
default="html",
help="Output format: json, yaml, markdown (or md), html, table (default: html)",
type=click.Choice(supported_formats(), case_sensitive=False),
default=None,
help=(
f"Output format: {', '.join(supported_formats())} "
"(default: the config file's default_format, otherwise html)"
),
)
@click.option(
"--output",
Expand Down Expand Up @@ -253,7 +258,13 @@ def generate(
"""
setup_logging(verbose)

output_format = normalize_format(output_format)
# The CLI honours the user's ~/.config/nf-docs/config.yaml; the Python API
# deliberately does not (see nf_docs.api.extract).
user_config = load_config()

# An explicit --format wins; otherwise the config file's default_format,
# which load_config() has already checked is a format we can render.
output_format = normalize_format(output_format or user_config.default_format)

# Decide single-file vs directory mode up front. A directory holding only a
# module-style main.nf is auto-detected as a single module.
Expand All @@ -266,10 +277,6 @@ def generate(
if single_file_mode:
logging.getLogger("nf_docs").info("Generating single module documentation")

# The CLI honours the user's ~/.config/nf-docs/config.yaml; the Python API
# deliberately does not (see nf_docs.api.extract).
user_config = load_config()

try:
with ExtractionProgressDisplay(console) as progress_display:
pipeline = extract_pipeline(
Expand Down Expand Up @@ -586,5 +593,87 @@ def clear_cache(clear_all: bool, pipeline_path: Path | None) -> None:
raise SystemExit(1)


@main.command()
@click.option(
"--init",
"init_config",
is_flag=True,
help="Create an example config file at the default location",
)
@click.option(
"--show-example",
is_flag=True,
help="Print an example config file to stdout",
)
@click.option(
"--path",
is_flag=True,
help="Print the config file path",
)
def config(init_config: bool, show_example: bool, path: bool) -> None:
"""
Show or manage nf-docs configuration.

Without options, shows the current configuration values.

Examples:

# Show current config
nf-docs config

# Show config file path
nf-docs config --path

# Print example config
nf-docs config --show-example

# Create config file with defaults
nf-docs config --init
"""
# Route load_config()'s warnings through the console, so a broken config
# file says so here rather than reaching stderr unformatted.
setup_logging(verbose=False)
config_path = get_config_path()

if path:
# click.echo, not console.print: rich hard-wraps at the terminal width,
# which would split a long path across lines in `$(nf-docs config --path)`.
click.echo(str(config_path))
return

if show_example:
click.echo(get_example_config())
return

if init_config:
if config_path.exists():
console.print(f"[yellow]Config file already exists at {config_path}[/yellow]")
console.print("Edit it directly or delete it first to recreate.")
return

config_path.parent.mkdir(parents=True, exist_ok=True)
config_path.write_text(get_example_config(), encoding="utf-8")
console.print(f"[green]Created config file at {config_path}[/green]")
return

# Default: show current config
console.print(f"[bold]Config file:[/bold] {config_path}")
if config_path.exists():
console.print("[dim] (file exists)[/dim]")
else:
console.print("[dim] (using defaults - file does not exist)[/dim]")

# Load before printing the header: a file that fails to parse warns here,
# so "settings in effect" isn't read as "contents of that file".
settings = load_config().to_dict()

console.print()
console.print("[bold]Settings in effect:[/bold]")

# Dump as YAML so what's printed is exactly what you'd write in the file.
# click.echo rather than console.print, so rich doesn't read "[]" as markup.
click.echo(yaml.safe_dump(settings, sort_keys=False))


if __name__ == "__main__":
main()
Loading