zard 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -1
- data/CONTEXT.md +16 -0
- data/README.md +10 -13
- data/docs/adr/0002-em-dash-zard-documentation-marker.md +1 -0
- data/docs/adr/0004-documentation-reference-syntax.md +112 -0
- data/docs/adr/0005-safe-annotation-formatting.md +28 -0
- data/docs/adr/0006-hierarchical-documentation-output.md +85 -0
- data/docs/json-export.md +103 -0
- data/docs/usage.md +109 -11
- data/lib/zard/formatting/formatter.rb +179 -0
- data/lib/zard/model/v1.rb +17 -2
- data/lib/zard/parsing/parser.rb +43 -0
- data/lib/zard/version.rb +1 -1
- data/lib/zard.rb +6 -1
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9c9ab0c9576a9523bbbf9e28e700547c6867639e731cf7fb3365f5b1e6384c4f
|
|
4
|
+
data.tar.gz: 37182c341bb8ca6cb2cc1f4f27527d0551a28d78cf0c9d47867db31fcb8ae216
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: adcab0e387452ebea5e0fa5b978600ecfcccfd1938bef002b19ea75699793680338cfcd0a2a08b481bb09a430f5931e37154b7f551d750e0709a27abef283066
|
|
7
|
+
data.tar.gz: b5b76cd72e4b9a03530f187719ea891597f52cbdb67abee884df86c90a20f0afe8b0b7e88cb323759fd59175ced12c751aa0eb9bd197f5c887dad1596719a2af
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.0.2] - 2026-10-19
|
|
10
|
+
|
|
11
|
+
Generate scoped API documentation and versioned JSON for static sites, optionally enriched by released Rigor lens facts. This release also adds annotation formatting and an unstyled Astro presentation package; consumer sites own styling and publication.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Build hierarchical Markdown documentation for explicitly configured monorepo scopes, with namespace or file organization, direct dependency links, navigation indexes, and managed output updates.
|
|
16
|
+
- Link YARD-style `@see` references and URLs in generated Markdown, including cross-file CLI references and equivalent `.#` and `?.` module-function spellings.
|
|
17
|
+
- Link BitClust Markdown and RD-compatible references in prose and documentation tags while preserving code regions, existing Markdown links, and source provenance.
|
|
18
|
+
- Format annotation layout through `Zard.format` and `zard-doc format`, with safe contract ordering, redundant-marker removal, check/write modes, and reparsed source provenance.
|
|
19
|
+
- Export experimental `zard-doc/v0` revision 1 JSON with public declarations, exact Ruby parameter syntax, source snapshots, scoped links and written superclass/mixin relationships ([#22](https://github.com/rigortype/zard/pull/22), [#23](https://github.com/rigortype/zard/pull/23), [#25](https://github.com/rigortype/zard/pull/25)).
|
|
20
|
+
- Enrich exports through the optional `zard-rigor` gem and released Rigor 0.4.5 CLI, preserving shared signatures, per-definition returns, provenance and explicit unavailable reasons without loading analyzer internals ([#24](https://github.com/rigortype/zard/pull/24), [#27](https://github.com/rigortype/zard/pull/27)).
|
|
21
|
+
- Consume constant value types, aliases, module functions and readable declared block types with exact source matching and separate instance/singleton member bindings; only Rigor 0.4.5 is supported.
|
|
22
|
+
- Provide the experimental, unpublished `zard-doc-astro` package with unstyled accessible HTML, Starlight routes, grouped member navigation, definition/name sorting, native documentation disclosures and class/module relationship browsing.
|
|
23
|
+
|
|
9
24
|
## [0.0.1] - 2026-10-11
|
|
10
25
|
|
|
11
26
|
The first ZARD release separates Ruby API documentation from RBS and Rigor contracts. Parse Ruby source into a versioned model, generate public API Markdown, and lint documentation from files, directories, or standard input without installing Rigor.
|
|
@@ -23,5 +38,6 @@ The first ZARD release separates Ruby API documentation from RBS and Rigor contr
|
|
|
23
38
|
- Provide `zard-doc render` and `zard-doc lint` for files, recursive directories, and standard input, with deterministic path ordering and no partial Markdown on input errors.
|
|
24
39
|
- Distribute `zard` and `zard-doc` as separate gems for Ruby 3.2 and newer, with a usage guide and a complete multi-file example.
|
|
25
40
|
|
|
26
|
-
[Unreleased]: https://github.com/rigortype/zard/compare/v0.0.
|
|
41
|
+
[Unreleased]: https://github.com/rigortype/zard/compare/v0.0.2...HEAD
|
|
42
|
+
[0.0.2]: https://github.com/rigortype/zard/compare/v0.0.1...v0.0.2
|
|
27
43
|
[0.0.1]: https://github.com/rigortype/zard/tree/v0.0.1
|
data/CONTEXT.md
CHANGED
|
@@ -4,6 +4,22 @@ ZARD is a Ruby self-documentation notation for people and AI agents. It uses pla
|
|
|
4
4
|
|
|
5
5
|
## Language
|
|
6
6
|
|
|
7
|
+
**Source mapping**:
|
|
8
|
+
A correspondence between a source tree and a destination within the API documentation tree.
|
|
9
|
+
_Avoid_: Ruby module, gem boundary
|
|
10
|
+
|
|
11
|
+
**Documentation scope**:
|
|
12
|
+
A named, explicitly defined API documentation context containing one or more source trees and their declarations, contracts, and facts. Applications have separate documentation scopes by default, even when they share Ruby namespace names.
|
|
13
|
+
_Avoid_: Ruby namespace, source mapping, automatically detected application
|
|
14
|
+
|
|
15
|
+
**Documentation dependency**:
|
|
16
|
+
An explicitly declared documentation scope whose API declarations may be referenced from another scope.
|
|
17
|
+
_Avoid_: runtime dependency, implicit global lookup
|
|
18
|
+
|
|
19
|
+
**Documentation root**:
|
|
20
|
+
The common destination for API documentation trees produced by source mappings.
|
|
21
|
+
_Avoid_: source root, repository root
|
|
22
|
+
|
|
7
23
|
**ZARD**:
|
|
8
24
|
The self-documentation language defined by this project.
|
|
9
25
|
_Avoid_: YARD, RDoc
|
data/README.md
CHANGED
|
@@ -16,7 +16,7 @@ ZARD separates contracts from API documentation:
|
|
|
16
16
|
- **API Documentation (`@param`, `@return`, etc.)**: Human- and AI-facing prose explains *what* and *why* without repeating type signatures.
|
|
17
17
|
- **Structured Declaration Model**: The parser captures declarations, contracts, documentation, and diagnostics with precise source spans and provenance—ideal for IDEs, automated tooling, and AI agents.
|
|
18
18
|
|
|
19
|
-
The current release preserves contract payloads as text. It does not parse or validate their types
|
|
19
|
+
The current release preserves contract payloads as text. It does not parse or validate their types or check documentation claims against contracts. The development checkout also provides an optional experimental Rigor lens adapter, described below.
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -46,31 +46,24 @@ end
|
|
|
46
46
|
|
|
47
47
|
## Architecture & Packages
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
The 0.0.2 release contains parser, documentation and optional adapter gems:
|
|
50
50
|
|
|
51
51
|
| Gem | Role | Description |
|
|
52
52
|
| --- | --- | --- |
|
|
53
53
|
| [`zard`](.) | Parser & Model | Parses Ruby source into a versioned, static document model (`Zard::Model::V1`) with source spans and provenance. |
|
|
54
54
|
| [`zard-doc`](zard-doc) | Renderer & CLI | Renders API documentation from the ZARD model into Markdown, and provides the `zard-doc` CLI tool. |
|
|
55
|
+
| [`zard-rigor`](zard-rigor) | Optional adapter | Enriches JSON exports through the released Rigor 0.4.5 CLI (`lens/v0` revision 1), without loading analyzer internals. |
|
|
55
56
|
|
|
56
57
|
---
|
|
57
58
|
|
|
58
59
|
## Installation
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
Install [zard](https://rubygems.org/gems/zard) and [zard-doc](https://rubygems.org/gems/zard-doc) 0.0.2 from RubyGems by adding the pair to your Gemfile:
|
|
61
62
|
|
|
62
63
|
```ruby
|
|
63
64
|
# Gemfile
|
|
64
|
-
gem "zard",
|
|
65
|
-
gem "zard-doc",
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Once published, install with:
|
|
69
|
-
|
|
70
|
-
```ruby
|
|
71
|
-
# Gemfile
|
|
72
|
-
gem "zard", "~> 0.0.1"
|
|
73
|
-
gem "zard-doc", "~> 0.0.1"
|
|
65
|
+
gem "zard", "~> 0.0.2"
|
|
66
|
+
gem "zard-doc", "~> 0.0.2"
|
|
74
67
|
```
|
|
75
68
|
|
|
76
69
|
Run `bundle install` after adding the gems to your Gemfile.
|
|
@@ -127,6 +120,10 @@ markdown = Zard::Doc.render(document)
|
|
|
127
120
|
|
|
128
121
|
The renderer emits documented public declarations and supports class, module, constant, attribute, and method prose, as well as `@param`, `@return`, `@yieldparam`, `@yieldreturn`, `@option`, `@raise`, `@note`, `@see`, `@deprecated`, and `@example`. Contracts remain in the model and are not duplicated into API prose.
|
|
129
122
|
|
|
123
|
+
Version 0.0.2 provides `zard-doc build --config zard-doc.yml --format json` for static-site consumers. It exports public declarations, documentation, unparsed contracts, source provenance, and scoped page/link metadata to stdout without publishing files. This experimental [JSON export](docs/json-export.md) works independently of Rigor; install the optional `zard-rigor` gem to enrich it with reviewed lens facts.
|
|
124
|
+
|
|
125
|
+
The experimental [`zard-doc-astro`](zard-doc-astro/README.md) JavaScript package owns unstyled API presentation and a separate Starlight adapter. Consumer sites own CSS, source acquisition, localization and publication; the Ruby gems do not depend on Astro.
|
|
126
|
+
|
|
130
127
|
---
|
|
131
128
|
|
|
132
129
|
## Supported Ruby Semantics
|
|
@@ -14,6 +14,7 @@ ZARD uses an em dash `—` when a named documentation tag or an optional type mu
|
|
|
14
14
|
- `@option options :format — Output format.` is ZARD documentation.
|
|
15
15
|
- `@option options :format [Symbol] — Output format.` is ZARD documentation with a claim.
|
|
16
16
|
- Tags with no name and no type, such as `@note`, `@see`, `@deprecated`, and `@example`, write their description directly.
|
|
17
|
+
- For YARD compatibility, `@see` descriptions may contain a reference followed by a whitespace-separated display label; this form does not require `—`.
|
|
17
18
|
- `@param value [String] Input text.` is preserved as raw text, not as a type hint.
|
|
18
19
|
- Named tags such as `@param`, `@option`, `@yieldparam`, and `@raise` require `—` before their description.
|
|
19
20
|
- `zard-doc lint` warns about YARD-like tags and can promote the warning to an error.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Use BitClust references and YARD-compatible see tags
|
|
2
|
+
|
|
3
|
+
ZARD adopts BitClust Markdown references, accepts their RD link forms for compatibility, and supports YARD-style shorthand in `@see`. All forms share reference resolution, while original comment text and source provenance remain available. Compatibility makes existing Ruby documentation familiar and avoids a separate ZARD spelling for each kind of method.
|
|
4
|
+
|
|
5
|
+
Status: Accepted. The current implementation covers shorthand `@see` references, URLs, and the explicit BitClust and RD forms below. Availability describes repository support rather than a published gem version.
|
|
6
|
+
|
|
7
|
+
## Problem Statement
|
|
8
|
+
|
|
9
|
+
Authors need links between Ruby declarations in API documentation. BitClust and YARD provide familiar conventions, but differ in delimiters, labels, and module-function spelling. Documentation must retain its original text even when a reference cannot be linked.
|
|
10
|
+
|
|
11
|
+
## Solution
|
|
12
|
+
|
|
13
|
+
Use explicit BitClust forms in prose and allow concise YARD-compatible targets in `@see`. Accept both `Kernel.#puts` and `Kernel?.puts` with identical meaning. Generated documentation links only to destinations it can identify reliably.
|
|
14
|
+
|
|
15
|
+
## User Stories
|
|
16
|
+
|
|
17
|
+
1. As an author, I want to link an instance method, so that readers can find related behavior.
|
|
18
|
+
2. As an author, I want to link a class method, so that readers can find construction and utility APIs.
|
|
19
|
+
3. As an author, I want either module-function spelling to work, so that existing comments remain usable.
|
|
20
|
+
4. As an author, I want relative method targets, so that comments need not repeat their owner.
|
|
21
|
+
5. As an author, I want an absolute target, so that a local namespace cannot change its meaning.
|
|
22
|
+
6. As an author, I want a custom label, so that references fit the surrounding explanation.
|
|
23
|
+
7. As an author, I want RD links accepted, so that migrating documentation does not require rewriting every reference.
|
|
24
|
+
8. As an author, I want links in prose and documentation tags, so that related APIs can be mentioned where they matter.
|
|
25
|
+
9. As an author, I want code examples preserved, so that literal reference syntax is not expanded inside code.
|
|
26
|
+
10. As a reader, I want links across generated input files, so that related declarations remain reachable.
|
|
27
|
+
11. As a reader, I want unresolved references preserved, so that incomplete documentation retains useful context.
|
|
28
|
+
12. As a tool author, I want original comments and source spans, so that normalization does not erase provenance.
|
|
29
|
+
|
|
30
|
+
## Implementation Decisions
|
|
31
|
+
|
|
32
|
+
### Accepted forms
|
|
33
|
+
|
|
34
|
+
| Form | Meaning | Availability |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| `[m:Foo#bar]` | Explicit instance-method reference | Implemented |
|
|
37
|
+
| `[m:Foo.bar]` | Explicit class-method reference | Implemented |
|
|
38
|
+
| `[m:Kernel?.puts]` | Explicit module-function reference | Implemented |
|
|
39
|
+
| `[label](m:Foo#bar)` | Explicit reference with a display label | Implemented |
|
|
40
|
+
| `[c:Foo]` | Class or module reference | Implemented |
|
|
41
|
+
| `[m:Math::PI]` | Constant reference | Implemented |
|
|
42
|
+
| `[[m:Foo#bar]]` | RD-compatible method reference | Implemented |
|
|
43
|
+
| `[[m:Kernel.#puts]]` | RD-compatible module-function reference | Implemented |
|
|
44
|
+
| `[[c:Foo]]` | RD-compatible class or module reference | Implemented |
|
|
45
|
+
| `@see Foo#bar` | Shorthand instance-method reference | Implemented |
|
|
46
|
+
| `@see Foo` / `@see Foo::CONST` | Shorthand class, module, or constant reference | Implemented |
|
|
47
|
+
| `@see Foo.bar Label` | Shorthand class-method reference with a label | Implemented |
|
|
48
|
+
| `@see #bar` / `@see .bar` | Reference relative to the current owner | Implemented |
|
|
49
|
+
| `@see Kernel.#puts` / `@see Kernel?.puts` | Equivalent module-function references | Implemented |
|
|
50
|
+
| `@see https://example.com Label` | External URL with an optional label | Implemented |
|
|
51
|
+
|
|
52
|
+
The `@see` examples are comment payloads, written as `# @see ...` in Ruby source. Explicit forms also belong in `@see` descriptions; they are not shorthand target tokens and must be recognized before shorthand parsing.
|
|
53
|
+
|
|
54
|
+
### Targets and normalization
|
|
55
|
+
|
|
56
|
+
- `Foo#bar` selects an instance method. `Foo.bar` selects a singleton method.
|
|
57
|
+
- `Kernel.#puts` and `Kernel?.puts` select the same module-function reference and link to its public singleton copy. Normalization preserves the original spelling for display and provenance. Relative `.#puts` and `?.puts` follow the same rule.
|
|
58
|
+
- A leading `::` requests a root target. Relative `#bar` and `.bar` use the current class or module; shorthand top-level instance methods use the empty owner.
|
|
59
|
+
- Qualified targets search the current namespace and its namespace prefixes. This matches YARD's namespace-based convention, including qualified openings such as `class Outer::Caller`; it does not reproduce Ruby's lexical constant lookup.
|
|
60
|
+
- A nearer declared owner shadows outer owners. If its requested member is missing, private, or undocumented, resolution stops and preserves the reference as text.
|
|
61
|
+
- Attribute writers use their actual method name with `=`. An accessor exposes reader and writer references, which may share one rendered declaration. A replacement reader remains distinct from a retained writer.
|
|
62
|
+
- Valid Unicode owners and method names participate in lookup. Method punctuation remains part of the target; normalization changes only the owner/method separator.
|
|
63
|
+
- Explicit constant receiver paths are matched against declared namespace candidates. Dynamic receiver expressions and runtime aliases do not establish object identity.
|
|
64
|
+
|
|
65
|
+
### Labels and comment boundaries
|
|
66
|
+
|
|
67
|
+
- Shorthand `@see` uses the first whitespace-separated token as its target and the remaining text as an optional display label. Leading and trailing whitespace is trimmed for rendering. No `—` is required, as clarified by ADR 0002.
|
|
68
|
+
- If no label is supplied, the original target spelling supplies the display text. Labels and destinations must be escaped appropriately for generated Markdown.
|
|
69
|
+
- Existing documentation continuation rules apply. Continuation lines remain part of the same `@see` description and, for a resolved shorthand reference, its label.
|
|
70
|
+
- Explicit Markdown forms use `[label](m:target)` for labels. A label consisting entirely of a backtick code span keeps that code presentation, following BitClust.
|
|
71
|
+
- Method names containing brackets use BitClust escaping: `[m:Hash#\[\]]`. RD compatibility accepts the historical form `[[m:Hash#[] ]]`, including its delimiter-separating space.
|
|
72
|
+
- References are recognized in prose, including documentation tag descriptions, while inline code and fenced or indented code blocks remain literal. An `@example` code region is not implicitly a reference region.
|
|
73
|
+
- RD compatibility covers link syntax only. It does not reinterpret the entire comment as an RD document.
|
|
74
|
+
|
|
75
|
+
### Resolution and output
|
|
76
|
+
|
|
77
|
+
- Resolve against explicit declarations in the generated input set. The single-document API uses that document; the CLI uses all input files in its combined output.
|
|
78
|
+
- A destination must identify exactly one public declaration with rendered API documentation. Private, undocumented, ambiguous, unresolved, and refinement targets remain text with their original description intact.
|
|
79
|
+
- Equivalent forms resolve to the same destination. References to a shared declaration use one stable, collision-free anchor, independent of parameter display and heading slug rules.
|
|
80
|
+
- URL shorthand supports HTTP, HTTPS, and mailto. It does not require declaration lookup.
|
|
81
|
+
- Declaration lookup does not follow inheritance or mixins, evaluate Ruby, infer runtime receiver identity, or fetch external API inventories. These limits are deliberate even where YARD can resolve more broadly.
|
|
82
|
+
- The core retains documentation descriptions, raw source, and spans. Reference interpretation belongs to documentation generation and does not turn a reference into a contract or documentation claim.
|
|
83
|
+
- A future structured reference representation must preserve provenance and respect the versioned model boundary; this ADR does not add a public model field or resolver API.
|
|
84
|
+
|
|
85
|
+
## Testing Decisions
|
|
86
|
+
|
|
87
|
+
Use the existing parse API, Markdown generation API, and CLI as the acceptance seams. Assert rendered destinations and preserved source text rather than requiring a new resolver interface.
|
|
88
|
+
|
|
89
|
+
- Cover qualified and relative instance/class methods, root targets, namespace-prefix lookup, and shadowing.
|
|
90
|
+
- Verify that both module-function spellings, with or without an owner, produce the same destination and one anchor.
|
|
91
|
+
- Cover reader/writer accessors, aliases, operator methods, Unicode owners, and Unicode method names.
|
|
92
|
+
- Verify labels, continuation lines, spacing, URL escaping, empty descriptions, and punctuation-only unresolved targets.
|
|
93
|
+
- Verify private, undocumented, missing, ambiguous, dynamic-receiver, and refinement cases retain text rather than linking to an unrelated declaration.
|
|
94
|
+
- Exercise multiple CLI input files, deterministic output, and cross-file ambiguity through the existing CLI.
|
|
95
|
+
- Verify BitClust/RD equivalence, bracket escaping, custom labels, literal code regions, and unchanged raw comments and source spans through the same seams.
|
|
96
|
+
|
|
97
|
+
## Out of Scope
|
|
98
|
+
|
|
99
|
+
Full RD rendering, YARD's `{target}` prose syntax, documentation copying via `(see target)`, runtime ancestry resolution, external reference databases, BitClust schemes beyond `m:` and `c:`, BitClust document/method fragment schemes, and the proposed `[label][m:target]` reference-style form are outside this decision. Unresolved-reference lint diagnostics are a separate decision; this ADR requires lossless fallback rather than a new warning policy.
|
|
100
|
+
|
|
101
|
+
## Further Notes
|
|
102
|
+
|
|
103
|
+
The explicit forms are a compatibility subset, not a claim of complete BitClust or YARD rendering compatibility. ADR 0001 keeps API documentation separate from contracts, ADR 0002 governs documentation markers and continuation, and ADR 0003 preserves the core/documentation package boundary.
|
|
104
|
+
|
|
105
|
+
Primary references:
|
|
106
|
+
|
|
107
|
+
- [BitClust Markdown specification, cross references](https://github.com/rurema/bitclust/blob/581943661862910ff2035df2974709aef816a1c0/doc/markdown-samples/MARKUP_SPEC.md#7-クロスリファレンスハイパーリンク).
|
|
108
|
+
- [BitClust Markdown reference implementation](https://github.com/rurema/bitclust/blob/581943661862910ff2035df2974709aef816a1c0/lib/bitclust/mdcompiler.rb#L849).
|
|
109
|
+
- [Rurema Markdown migration notes](https://blog.n-z.jp/blog/2026-07-23-rurema-to-markdown.html).
|
|
110
|
+
- [YARD reference syntax and automatic `@see` linking](https://github.com/lsegal/yard/blob/3d8670543d2408baea3a1360b2258fde2ee65917/docs/GettingStarted.md#L395-L409).
|
|
111
|
+
- [YARD `@see` tag definition](https://github.com/lsegal/yard/blob/3d8670543d2408baea3a1360b2258fde2ee65917/lib/yard/tags/library.rb#L503-L513).
|
|
112
|
+
- [YARD namespace lookup](https://github.com/lsegal/yard/blob/3d8670543d2408baea3a1360b2258fde2ee65917/lib/yard/registry_resolver.rb#L50-L100).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Format annotations with semantic preservation checks
|
|
2
|
+
|
|
3
|
+
Status: Accepted.
|
|
4
|
+
|
|
5
|
+
ZARD formatting normalizes annotation layout without interpreting contract payloads or converting ambiguous documentation into claims. Moving a contract can change the ownership of documentation continuation lines, so source layout alone is not sufficient to establish a safe edit.
|
|
6
|
+
|
|
7
|
+
## Interface and package
|
|
8
|
+
|
|
9
|
+
The core provides `Zard.format(source, path:)`. Its result exposes `source`, `document`, `diagnostics`, and `changed?`. Formatting does not mutate the input or read or write files. The result contains a freshly parsed document whose raw text and spans refer to the formatted source. Invalid byte encodings that cannot enter the parser produce an empty document with an error diagnostic. Formatting diagnostics also refer to the returned source.
|
|
10
|
+
|
|
11
|
+
The existing `zard-doc` executable provides `format PATH`, `format --check PATH...`, and `format --write PATH...`. Plain mode prints one formatted input; `-` reads standard input. Check and write modes support recursive directory inputs. Write mode rejects standard input. Check and write are mutually exclusive.
|
|
12
|
+
|
|
13
|
+
Exit status is 0 on success, 1 for source errors or changes required in check mode, and 2 for invocation or I/O errors. Warnings, including ambiguous documentation left unchanged, do not fail formatting. All inputs are read and validated before write mode changes any file. It leaves unchanged files untouched. Filesystem write failures are reported, but multi-file writes are not transactional.
|
|
14
|
+
|
|
15
|
+
## Allowed transformations
|
|
16
|
+
|
|
17
|
+
- Normalize the comment prefix of standalone `@extrbs` annotations to `# @extrbs`, retaining indentation, payloads, notes, and trailing whitespace. A noncanonical annotation with nonblank continuation text is left unchanged because its intended boundary is ambiguous.
|
|
18
|
+
- Stably move `@extrbs` lines before other annotations within an attached contiguous comment block. Preserve the relative order of contract lines and all other lines. Preserve the original sequence of line terminators, including mixed LF/CRLF and an absent final newline.
|
|
19
|
+
- Remove a redundant em dash from parsed `@return` and `@yieldreturn` tags without claims, except when removal would reinterpret the description's leading bracket or em dash. Keep those ambiguous descriptions with the existing diagnostic. Canonical named or typed markers already have their required delimiter and need no conversion.
|
|
20
|
+
- Keep missing markers, YARD-like tags, unknown tags, indented documentation, examples, string literals, embedded documents, and detached comments unchanged. Retain the parser's diagnostics rather than guessing a documentation boundary.
|
|
21
|
+
|
|
22
|
+
Before accepting a contract reorder, reparse it and compare declarations, documentation fields and order, and contract payloads/notes. Contract ordering may change only through stable `@extrbs` placement. Reject a reorder that changes these values, keep the safe spacing/marker edits, and emit `format.unsafe-extrbs-order`. This protects continuation ownership and declaration attachment. Noncanonical `@extrbs` prefix repair intentionally makes a previously raw annotation a contract; it is the only such conversion.
|
|
23
|
+
|
|
24
|
+
Malformed Ruby and non-UTF-8 source are returned unchanged with errors. The formatter does not validate types, rewrite Ruby code, generalize comment spacing, format RBS payloads, infer missing documentation markers, or migrate YARD documentation.
|
|
25
|
+
|
|
26
|
+
## Verification
|
|
27
|
+
|
|
28
|
+
Use the formatting API, existing parse API, and real CLI. Verify payloads, notes, multiline descriptions, raw text, byte spans, indentation, line terminators, safe and unsafe reorder cases, ambiguous comments, and a byte-identical second pass. Exercise stdout, stdin, check and write modes, directories, read errors, and invalid inputs before writing.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: accepted
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Separate documentation scopes from page organization
|
|
6
|
+
|
|
7
|
+
Monorepos need separate application documentation even when applications share Ruby namespace names. Applications have separate documentation scopes by default. Each scope is named and explicitly configured; directory conventions and gemspecs do not automatically establish scope boundaries. Multiple source trees assigned to one scope aggregate together. A shared documentation root accommodates hierarchical output, while page organization can use source files or Ruby namespaces; a CLI option overrides the page organization selected in the configuration file. Contracts and Rigor facts must retain their association with declarations independently of page organization.
|
|
8
|
+
|
|
9
|
+
Source files without publishable declarations produce no API page. In namespace organization, documented top-level methods and constants appear on a dedicated top-level page. Script explanations and execution instructions belong in separate guides.
|
|
10
|
+
|
|
11
|
+
References first resolve within their own scope. If no local target is found, lookup searches explicitly configured documentation dependencies. Sharing Ruby namespace names does not implicitly enable references between scopes.
|
|
12
|
+
|
|
13
|
+
Each source mapping selects files using include and exclude globs relative to its source root, independently of Git tracking and gemspec contents. Namespace organization is the default; file organization remains selectable. Both modes resolve references against all selected files in their scope.
|
|
14
|
+
|
|
15
|
+
Shared source files may belong to multiple scopes, with separate scope-specific declaration identities. Within one scope, overlapping inputs parse the same physical source file once. Ambiguous local references do not fall back to dependencies; ambiguous dependency references do not select the first match. Ambiguous references remain literal and produce warnings identifying candidate scopes and source locations.
|
|
16
|
+
|
|
17
|
+
Generation validates all selected scopes before updating output. Parse errors, unreadable inputs, and output collisions prevent publication of the entire selected output set; warnings alone permit generation. Filesystem publication failures use the recovery policy below.
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
Use YAML with `docroot`, `layout`, and named `scopes`. Each scope defines a `destination`, explicit `references`, and `sources` containing a `root` with include and exclude globs. Paths are relative to the configuration file. Source mappings may define an output prefix to distinguish source trees in file organization. CLI options override configured page organization and select scopes to generate.
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
docroot: docs/api
|
|
25
|
+
layout: namespace
|
|
26
|
+
scopes:
|
|
27
|
+
rigor:
|
|
28
|
+
destination: rigor
|
|
29
|
+
sources:
|
|
30
|
+
- root: lib
|
|
31
|
+
include: ["**/*.rb"]
|
|
32
|
+
- root: plugins
|
|
33
|
+
include: ["*/lib/**/*.rb"]
|
|
34
|
+
playground:
|
|
35
|
+
destination: playground
|
|
36
|
+
references: [rigor]
|
|
37
|
+
sources:
|
|
38
|
+
- root: apps/rigor-playground/lib
|
|
39
|
+
include: ["**/*.rb"]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Aggregation and initial delivery
|
|
43
|
+
|
|
44
|
+
Reopened classes and modules share one namespace page, retaining descriptions, contracts, and provenance from each definition rather than replacing one description with another. Repeated method definitions remain separately represented; no runtime winner is inferred, and references to multiple competing method definitions are ambiguous. A namespace reference identifies the aggregate namespace page.
|
|
45
|
+
|
|
46
|
+
The initial implementation covers configuration, declaration aggregation, page organization, and cross-scope links using the existing parse model. External RBS ingestion requires a separate specification. Rigor lens integration is deferred to the `v0.1.0` goal and is not required for this workflow. Existing contract payloads retain their association with source declarations; this work does not introduce contract text rendering or type inference.
|
|
47
|
+
|
|
48
|
+
## Generated output ownership
|
|
49
|
+
|
|
50
|
+
A generation manifest records ZARD-managed pages. Regeneration removes obsolete managed pages while preserving unowned files, including handwritten guides. If a managed page has been edited since generation, publication stops before overwriting or deleting it. Output is prepared in a staging area; publication failures trigger recovery attempts. Failed recovery produces a non-success exit and actionable recovery information. This does not promise an atomic filesystem transaction across all pages.
|
|
51
|
+
|
|
52
|
+
## CLI and dependency generation
|
|
53
|
+
|
|
54
|
+
Add `zard-doc build --config zard-doc.yml`; retain the existing `render PATH...` combined Markdown stream and existing APIs. Configuration is explicitly selected. Without `--scope`, build all configured scopes; repeated `--scope` selects named scopes. `--layout namespace|file` overrides the configured layout.
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
zard-doc build --config zard-doc.yml
|
|
58
|
+
zard-doc build --config zard-doc.yml --scope playground --layout file
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Build selected scopes together with their recursive documentation dependency closure so generated links have destinations. Reference lookup still searches only the current scope and its directly declared dependencies; transitive generation does not expand lookup visibility. Dependency cycles are allowed, and each scope is generated once.
|
|
62
|
+
|
|
63
|
+
## Path validation and navigation
|
|
64
|
+
|
|
65
|
+
Missing source roots, unknown documentation dependencies, scopes selecting no Ruby files, and conflicting output paths are errors before publication. Output destinations must stay within `docroot`. Input symlinks must not lead outside explicitly declared input roots. Within one scope, repeated inclusion of one physical file is deduplicated when the output assignment agrees, but conflicting output assignments are errors.
|
|
66
|
+
|
|
67
|
+
Generate `docroot/index.md` listing scopes and a scope-local `index.md` listing its pages. In namespace organization, publishable top-level declarations use `_top-level.md` within their scope. Links use relative output paths and stable anchors. Collisions between API pages and reserved navigation paths are errors.
|
|
68
|
+
|
|
69
|
+
## Acceptance scenarios
|
|
70
|
+
|
|
71
|
+
- Generate two applications defining the same namespace without mixing declarations or links.
|
|
72
|
+
- Aggregate multiple source trees and reopened namespaces within one scope while preserving all definition provenance.
|
|
73
|
+
- Generate namespace and file layouts from the same selected declarations; CLI layout overrides configuration.
|
|
74
|
+
- Honor include and exclude globs independently of Git and gem packaging.
|
|
75
|
+
- Include shared source in separate scopes without cross-scope identity leakage; deduplicate identical assignments within one scope.
|
|
76
|
+
- Resolve local references before direct documentation dependencies; preserve and warn on ambiguous targets.
|
|
77
|
+
- Generate selected scopes and recursive dependency closure, including cycles, without making transitive dependencies visible to reference lookup.
|
|
78
|
+
- Omit API pages for files without publishable declarations and place top-level declarations in the dedicated namespace-layout page.
|
|
79
|
+
- Reject invalid roots, dependencies, empty source selections, escaped paths, and output collisions before changing output.
|
|
80
|
+
- Generate relative links and navigation pages; reject collisions with reserved paths.
|
|
81
|
+
- Remove obsolete managed pages, preserve handwritten guides, and reject overwriting edited managed pages.
|
|
82
|
+
- Preserve existing output when pre-publication validation fails; exercise publication failure recovery and report unrecovered failures.
|
|
83
|
+
- Preserve existing parse, render, lint, and format behavior outside the new build workflow.
|
|
84
|
+
|
|
85
|
+
The initial file-oriented example maps `app/foo/hoge/fuga.rb` to `docs/api/foo/hoge/fuga.md` and `ext/foo-bar/hoge/fuga_foo.rb` to `docs/api/foo-bar/hoge/fuga_foo.md`. It illustrates file organization, rather than requiring that hierarchy for namespace organization. External contract and fact ingestion is deferred to a separate design.
|
data/docs/json-export.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# JSON API documentation export
|
|
2
|
+
|
|
3
|
+
Repository builds support an experimental static-site producer:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
zard-doc build --config zard-doc.yml --format json > api.json
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
This is ZARD documentation data, not a Rigor lens result. Plain generation does not execute Ruby source, parse contract types, infer signatures, or require Rigor. The optional `zard-rigor` adapter can associate facts with source declarations without replacing their documentation or provenance.
|
|
10
|
+
|
|
11
|
+
Framework-specific HTML lives in [`zard-doc-astro`](../zard-doc-astro/README.md), with Starlight conventions isolated in its adapter. Styling, source acquisition and publication belong to each consumer. This export does not embed an Astro component or stylesheet.
|
|
12
|
+
|
|
13
|
+
## Invocation and publication
|
|
14
|
+
|
|
15
|
+
The existing build configuration, source selection, explicit documentation scopes, dependency closure and `--layout` / `--scope` options apply. `--format markdown` remains the default and retains its managed-page behavior.
|
|
16
|
+
|
|
17
|
+
JSON exports the selected scopes and their recursive documentation dependencies as one document to stdout, after validation. It does not read a previous generation manifest or create, edit or remove anything under `docroot`. The export is self-contained for those selected scopes; it does not merge a previous partial export. Consumers own their output publication. Shell redirection itself can truncate an existing destination before ZARD runs; callers that publish artifacts should write a temporary file and replace the artifact only after success.
|
|
18
|
+
|
|
19
|
+
Source errors return 1; invalid configuration or invocation, unreadable inputs, output-path collisions and serialization errors return 2. Neither emits partial JSON. Warnings permit export and also appear on stderr. A process or stdout I/O failure can leave truncated output, so consumers must check both exit status and JSON parsing before accepting an artifact.
|
|
20
|
+
|
|
21
|
+
## Envelope
|
|
22
|
+
|
|
23
|
+
| Field | Meaning |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `schema` | Exactly `zard-doc/v0`. |
|
|
26
|
+
| `revision` | Integer `1` for this experimental representation. |
|
|
27
|
+
| `producer` | `zard` and `zard_doc` gem versions. |
|
|
28
|
+
| `layout` | `namespace` or `file`. |
|
|
29
|
+
| `scopes` | Selected documentation scopes, ordered by name. |
|
|
30
|
+
| `links` | Resolved documentation links grouped by source page path, with target `path` and `anchor`. |
|
|
31
|
+
| `warnings` | Reference-resolution warning strings, including ambiguous candidates; diagnostic prose is not a machine-readable enum. |
|
|
32
|
+
|
|
33
|
+
Consumers should reject unsupported schema/revision pairs. This format is experimental; a breaking change increments the revision before any stable schema commitment. It has its own lifecycle, independent of `lens/v0`, `lens/v1` and the `rigor:v1` type grammar revision. No generation timestamp, absolute configuration path or inferred fact is added. Repeated exports of identical inputs with the same producer versions are byte-identical.
|
|
34
|
+
|
|
35
|
+
## Documentation scopes and pages
|
|
36
|
+
|
|
37
|
+
Each scope has `name`, `destination`, its explicit direct documentation dependency names in `references`, `files`, `declarations`, and `pages`.
|
|
38
|
+
|
|
39
|
+
Each page has a `path` and an ordered array of declaration `id` values. Reopened classes/modules share a namespace page while retaining distinct source declarations. Namespace pages can exist for members of an undocumented container. Top-level declarations use the existing `_top-level.md` assignment. File organization uses source mappings and prefixes. Page paths are logical paths relative to `docroot`, with Markdown extensions, not a promise of emitted Markdown files or final website URLs. Website consumers map these paths to their routes.
|
|
40
|
+
|
|
41
|
+
The site can derive scope landing pages and navigation from this metadata. Generated Markdown index pages are not included in `pages` because they own no declarations. A file without public declarations produces no page.
|
|
42
|
+
|
|
43
|
+
JSON exports all public declarations, including those without API prose, so a site can present the selected API surface before documentation is complete. Private/protected declarations remain available to internal reference lookup but are not exported or linked. The Markdown format continues to publish only documented public declarations. This publication distinction is intentional and is not an inference that a Ruby-public declaration is a stable supported product API.
|
|
44
|
+
|
|
45
|
+
## Declarations
|
|
46
|
+
|
|
47
|
+
| Fields | Meaning |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `id`, `page`, `anchor` | Scope-specific identity and logical page destination; `id` combines the documentation scope with the existing stable anchor. |
|
|
50
|
+
| `kind`, `name`, `namespace`, `display_name`, `visibility` | Original declaration kind/name, lexical namespace, display/reference name and visibility. |
|
|
51
|
+
| `parameters` | Existing source parameter labels, not a structured typed signature. |
|
|
52
|
+
| `parameter_details` | Additive Ruby syntax metadata: ordered `kind`, nullable `name`, exact `source`, source `span`, nullable default-expression `default` and `default_span`. No types or evaluated values. |
|
|
53
|
+
| `references` | Names by which this declaration participates in documentation reference lookup. |
|
|
54
|
+
| `span`, `comment_span` | Declaration and attached comment provenance. |
|
|
55
|
+
| `documentation` | Original tag data plus resolved Markdown descriptions. |
|
|
56
|
+
| `contracts` | Unparsed contract channels, payloads, notes and provenance. |
|
|
57
|
+
| `receiver`, `refinement`, `superclass`, `container_builder` | Written source expressions with their corresponding `*_span` fields. |
|
|
58
|
+
| `alias_target` | Recorded alias target, without copying the target's documentation. |
|
|
59
|
+
| `mixins` | Written mixin `kind`, `target` and `span`. |
|
|
60
|
+
|
|
61
|
+
Absent optional fields are `null`; arrays remain arrays. A repeated method definition has its own identity/anchor. Nothing chooses a runtime-winning definition, resolves dynamic receivers, or asserts effective ancestry. Attribute accessors remain a source declaration with multiple reference names, rather than separate inferred getter/setter signatures.
|
|
62
|
+
|
|
63
|
+
`parameter_details` is additive within revision 1; older exports may omit it and consumers fall back to `parameters`. It is `null` when no source-backed parameter list is available (including aliases and generated attributes), and `[]` for an explicit parameterless method. Kinds are `required`, `optional`, `rest`, `post`, `required_keyword`, `optional_keyword`, `keyword_rest`, `block`, `forwarding`, `no_keywords` and `implicit_rest`. Anonymous and destructured forms retain their exact source patterns with a nullable name; destructured required/post parameters keep their positional kind. Visibility changes and module-function copies retain metadata. Default expressions are never executed. A heredoc default's body is not contiguous with its parameter header: its header remains in `source`, `default`/`default_span` are unavailable and `parameter.unsupported-default` is emitted rather than presenting the opener as a complete expression.
|
|
64
|
+
|
|
65
|
+
Documentation entries carry `name`, `owner`, `subject`, optional `claim`, `description`, `raw`, and `span`, matching the source model. `rendered_description` contains Markdown with resolvable ZARD references linked, using the same resolution rules as a documentation build. Original descriptions remain unchanged. Examples, raw text and inline/fenced code preserve literal reference syntax. Consumers must use their normal safe Markdown rendering policy; strings in JSON are not trusted HTML.
|
|
66
|
+
|
|
67
|
+
Contract entries carry `channel`, `payload`, `note`, `raw`, and `span`. They are source text, not parsed or validated types. A documentation claim does not become a contract, signature or Rigor fact.
|
|
68
|
+
|
|
69
|
+
## Source provenance and diagnostics
|
|
70
|
+
|
|
71
|
+
Each selected source file has `path`, its full SHA-256 `digest` over the bytes parsed, and structured parser `diagnostics` with `code`, `severity`, `message` and `span`. Files without public declarations remain listed for provenance and diagnostics.
|
|
72
|
+
|
|
73
|
+
All generated source paths are relative to the real directory containing the build configuration. Explicit source roots outside that directory can produce `../` paths; these are source identifiers, not output destinations. No absolute checkout paths are generated in provenance or reference warnings. Source-authored prose and raw text are preserved, including any paths the author wrote.
|
|
74
|
+
|
|
75
|
+
Spans retain the model's 1-based lines, 0-based columns and byte offsets, including an exclusive `end_offset`. Digests cover the entire source file, including documentation comments. Neither the digest nor a declaration ID proves the semantic inputs of a future Rigor analysis are unchanged.
|
|
76
|
+
|
|
77
|
+
Reference lookup searches the current documentation scope, then only its explicit direct documentation dependencies. Transitive dependencies are generated without becoming visible to lookup. Ambiguous references remain literal with warnings. The `links` entries target actual exported pages/anchors and provide route mapping input without requiring consumers to reverse-engineer references from text.
|
|
78
|
+
|
|
79
|
+
## Written class and module relationships
|
|
80
|
+
|
|
81
|
+
The additive `relationships` array on each declaration records written
|
|
82
|
+
`superclass`, `include`, `prepend`, `extend` and `container_builder` references.
|
|
83
|
+
Each entry carries the original `expression` and `span`, a `status` (`resolved`,
|
|
84
|
+
`unresolved` or `expression`), and `targets`. Each target has `scope`,
|
|
85
|
+
`declaration_id`, `path` and `anchor`, and names a published class or module.
|
|
86
|
+
Resolved reopenings retain all published definition targets, including file
|
|
87
|
+
layout. Older exports may omit the field.
|
|
88
|
+
|
|
89
|
+
Resolution uses the existing documentation lookup: local before direct scope
|
|
90
|
+
dependencies, without transitive fallback or first-match ambiguity resolution.
|
|
91
|
+
Superclass expressions use the enclosing lexical namespace; mixins use the
|
|
92
|
+
class/module body context. A local constant or unpublished target prevents
|
|
93
|
+
fallthrough to a dependency. Non-constant expressions and container builders
|
|
94
|
+
remain literal; no application code is loaded or evaluated. Unresolved
|
|
95
|
+
relationships produce diagnostics. Target links are included in `links`.
|
|
96
|
+
|
|
97
|
+
These are documentation relationships, not runtime ancestry, lookup precedence,
|
|
98
|
+
overrides or inherited member facts. Consumers must retain conflicting written
|
|
99
|
+
parents and guard traversal against cycles. The Astro presentation generates a
|
|
100
|
+
per-scope hierarchy with separate module usage, reports cycles/conflicts, and
|
|
101
|
+
preserves each definition's own source location. The `hierarchy` route within a
|
|
102
|
+
scope is reserved when the extension is present; route collisions fail before
|
|
103
|
+
publication. Legacy exports without the extension keep their existing routes.
|
data/docs/usage.md
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
# ZARD 0.0.
|
|
1
|
+
# ZARD 0.0.2 usage
|
|
2
2
|
|
|
3
|
-
ZARD parses Ruby source into a versioned model. The separate `zard-doc` gem renders
|
|
3
|
+
ZARD parses Ruby source into a versioned model. The separate `zard-doc` gem renders human-facing API documentation. Install the 0.0.2 pair with Bundler:
|
|
4
4
|
|
|
5
5
|
```ruby
|
|
6
|
-
gem "zard",
|
|
7
|
-
gem "zard-doc",
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
After `0.0.1` is published, install the released pair with Bundler:
|
|
11
|
-
|
|
12
|
-
```ruby
|
|
13
|
-
gem "zard", "~> 0.0.1"
|
|
14
|
-
gem "zard-doc", "~> 0.0.1"
|
|
6
|
+
gem "zard", "~> 0.0.2"
|
|
7
|
+
gem "zard-doc", "~> 0.0.2"
|
|
15
8
|
```
|
|
16
9
|
|
|
17
10
|
The gems require Ruby 3.2 or newer. The project is pre-1.0: the model has an explicit `Zard::Model::V1` namespace, but no general compatibility guarantee has been established yet. Review release notes when upgrading; pre-1.0 releases may change APIs and behavior.
|
|
@@ -29,6 +22,30 @@ markdown = Zard::Doc.render(document)
|
|
|
29
22
|
|
|
30
23
|
`Zard.parse` accepts Ruby source text and a required `path:` used in provenance and diagnostics. The path labels the source; the parser does not read that file. For in-memory input, use a label such as `"example.rb"`. `Zard::Doc.render` accepts the resulting document and returns Markdown. The renderer emits documented public declarations; it does not print contracts as API prose.
|
|
31
24
|
|
|
25
|
+
## Format annotations
|
|
26
|
+
|
|
27
|
+
Version 0.0.2 provides `Zard.format` and `zard-doc format`:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
result = Zard.format(source, path: "lib/example.rb")
|
|
31
|
+
puts result.source if result.changed?
|
|
32
|
+
document = result.document
|
|
33
|
+
diagnostics = result.diagnostics
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The result contains formatted source and a freshly parsed document with spans and raw text referring to that source. The input remains unchanged. `diagnostics` includes parser diagnostics and formatting warnings.
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
zard-doc format lib/example.rb
|
|
40
|
+
zard-doc format --check lib
|
|
41
|
+
zard-doc format --write lib
|
|
42
|
+
printf '# @return — Value.\ndef read = nil\n' | zard-doc format -
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Plain mode prints one input to stdout. Check mode returns 1 if any input would change; write mode updates changed files after reading and validating all inputs. Both modes accept files and recursive directories. Standard input is supported in plain and check modes. Invocation and I/O errors return 2; source errors return 1. Warnings are printed to stderr and do not fail formatting.
|
|
46
|
+
|
|
47
|
+
Formatting normalizes standalone `@extrbs` prefixes and moves them before other annotations only when reparsing preserves documentation and contract content. It removes redundant markers from untyped `@return` and `@yieldreturn`. Payloads, notes, multiline descriptions, unrelated bytes, and the original sequence of line endings are retained. YARD-like tags and ambiguous continuation boundaries are diagnosed without conversion. Invalid Ruby and non-UTF-8 source remain unchanged with errors. See [ADR 0005](adr/0005-safe-annotation-formatting.md) for the transformation limits.
|
|
48
|
+
|
|
32
49
|
## Inspect the model
|
|
33
50
|
|
|
34
51
|
The document is available through `document.path`, `document.declarations`, and `document.diagnostics`. A declaration exposes `kind`, `name`, `namespace`, `visibility`, `parameters`, `receiver`, `receiver_span`, `refinement`, `refinement_span`, `alias_target`, `superclass`, `superclass_span`, `container_builder`, `container_builder_span`, `mixins`, `span`, `comment_span`, `documentation`, and `contracts`.
|
|
@@ -72,6 +89,41 @@ For example, `@param path [String] — The file path.` carries an optional docum
|
|
|
72
89
|
|
|
73
90
|
Tags without a name or type write their description directly: `@return Normalized value.`, `@note Keep this in mind.`, and `@example Call the method.` Named tags such as `@param`, `@yieldparam`, `@raise`, and `@option` require the marker. `@option options :format — Output format.` documents an option. `@yieldreturn yielded value.` describes a yielded result. Supported tags include `@param`, `@return`, `@yieldparam`, `@yieldreturn`, `@option`, `@raise`, `@note`, `@see`, `@deprecated`, and `@example`.
|
|
74
91
|
|
|
92
|
+
### See also references
|
|
93
|
+
|
|
94
|
+
The complete syntax decision, including BitClust and RD compatibility forms, is recorded in [ADR 0004](adr/0004-documentation-reference-syntax.md).
|
|
95
|
+
|
|
96
|
+
`@see` accepts YARD-style references followed by an optional display label, without an em dash:
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
# @see Foo#bar
|
|
100
|
+
# @see Foo.build Build a Foo
|
|
101
|
+
# @see #bar Related instance method
|
|
102
|
+
# @see .build Related class method
|
|
103
|
+
# @see Kernel.#puts Legacy module-function spelling
|
|
104
|
+
# @see Kernel?.puts Modern module-function spelling
|
|
105
|
+
# @see https://example.com/reference Reference guide
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`#` selects an instance method and `.` selects a class method. `.#` and `?.` are equivalent module-function spellings and link to the public singleton copy. Relative references use the current class or module; qualified references search namespace prefixes as YARD does, and a leading `::` starts at the root. This lookup also applies to qualified class openings such as `class Outer::Caller`; it does not reproduce Ruby's lexical constant lookup. A nearer declared owner prevents fallback to an unrelated outer owner when its member is missing. The first whitespace-separated token is the target and the remaining text is the display label. HTTP, HTTPS, and mailto targets become external links.
|
|
109
|
+
|
|
110
|
+
`zard-doc` links references to uniquely identified public declarations with documentation in the generated output. The CLI resolves references across all input files. Unresolved, undocumented, private, ambiguous, and refinement references remain plain text. Lookup uses explicit declarations, without following inheritance or mixins or evaluating dynamic receivers. The document model retains the original description, raw comment, and source span.
|
|
111
|
+
|
|
112
|
+
Explicit references work in prose and documentation tag descriptions, including `@see`:
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
# Read [m:Foo#bar] or [[m:Foo#bar]].
|
|
116
|
+
# @note See [c:Foo] and [m:Foo::LIMIT].
|
|
117
|
+
# @see [Read a value](m:Foo#bar)
|
|
118
|
+
# @see [`Foo#bar()`](m:Foo#bar)
|
|
119
|
+
# @see [m:Kernel?.puts]
|
|
120
|
+
# @see [[m:Kernel.#puts]]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`m:` selects methods or constants; `c:` selects classes or modules. BitClust's single brackets and RD's double brackets resolve to the same destinations. `[label](m:target)` supplies a display label; a label consisting entirely of a backtick code span keeps its code presentation. Escape operator brackets as `[m:Foo#\[\]]`, or use the historical RD form `[[m:Foo#[] ]]` with its delimiter-separating space.
|
|
124
|
+
|
|
125
|
+
Inline code, fenced and indented code blocks, and `@example` content retain literal reference syntax. Existing Markdown links and images are preserved. Explicit forms in `@see` take precedence over shorthand parsing; unresolved forms retain their original text.
|
|
126
|
+
|
|
75
127
|
Plain comment lines continue the preceding canonical documentation tag until another annotation or contract begins. No continuation marker is needed. Contract notes use ` -- ` after the contract payload and remain contract notes, not API prose. Familiar YARD-like tags without the em dash can be retained as raw text, but are not treated as documentation claims.
|
|
76
128
|
|
|
77
129
|
ZARD documentation requires UTF-8 source text. Ruby source encoding declarations are respected; non-UTF-8 source can still contain contracts, but documentation text in it receives a diagnostic.
|
|
@@ -84,6 +136,52 @@ Visibility forms supported by the parser include bare visibility calls, inline f
|
|
|
84
136
|
|
|
85
137
|
## Command line
|
|
86
138
|
|
|
139
|
+
### Build a documentation tree
|
|
140
|
+
|
|
141
|
+
Version 0.0.2 provides `zard-doc build`. Define named documentation scopes in a YAML file:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
docroot: docs/api
|
|
145
|
+
layout: namespace
|
|
146
|
+
scopes:
|
|
147
|
+
core:
|
|
148
|
+
destination: core
|
|
149
|
+
sources:
|
|
150
|
+
- root: lib
|
|
151
|
+
include: ["**/*.rb"]
|
|
152
|
+
exclude: ["fixtures/**"]
|
|
153
|
+
playground:
|
|
154
|
+
destination: playground
|
|
155
|
+
references: [core]
|
|
156
|
+
sources:
|
|
157
|
+
- root: apps/playground/lib
|
|
158
|
+
include: ["**/*.rb"]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
zard-doc build --config zard-doc.yml
|
|
163
|
+
zard-doc build --config zard-doc.yml --scope playground --layout file
|
|
164
|
+
zard-doc build --config zard-doc.yml --format json > api.json
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Paths are relative to the configuration file. The default layout is `namespace`: reopened classes and modules share a page, preserving each definition's description and source location. `file` preserves each source root's relative file hierarchy with `.md` extensions. A source mapping can set `prefix` to distinguish multiple roots in file layout. `include` defaults to `["**/*.rb"]`; `exclude` defaults to `[]`. Source selection uses globs independently of Git and gemspecs.
|
|
168
|
+
|
|
169
|
+
CLI layout overrides configuration. With no `--scope`, all scopes are built; repeat `--scope` to select several. Selected scopes include their recursive documentation dependencies for generation. References search the current scope first, then only directly declared dependencies. Separate scopes do not merge declarations even when Ruby namespace names match. Ambiguous references remain text with warnings; local shadowing prevents fallback to unrelated dependency declarations.
|
|
170
|
+
|
|
171
|
+
The default `--format markdown` publishes managed Markdown pages. `--format json` instead prints one experimental, versioned JSON document after validation and leaves `docroot` untouched. JSON includes public declarations even without prose, their original documentation and unparsed contracts, source spans and digests, page assignments, stable anchors, and resolved documentation links. Private and protected declarations are excluded. See the [JSON export contract](json-export.md) for the schema, path conventions, and differences from Markdown publication. This export does not require Rigor or a Rigor lens result.
|
|
172
|
+
|
|
173
|
+
`Declaration#parameter_details` additionally records ordered Ruby parameter syntax and default-expression provenance. `Declaration#parameters` retains its existing labels for compatibility, and the Markdown renderer remains unchanged. Astro presentation uses the richer syntax when available, without assigning parameter types or evaluating defaults.
|
|
174
|
+
|
|
175
|
+
Each scope has an index. Namespace pages follow Ruby namespace hierarchy, with documented top-level members in `_top-level.md`. Files without publishable declarations produce no API page. The documentation root has an index linking the scopes. Links use relative paths and stable anchors.
|
|
176
|
+
|
|
177
|
+
Build reports diagnostics to stderr and does not print generated Markdown to stdout. Source errors return 1; configuration, input, ownership, and publication errors return 2. Warnings permit generation. All selected inputs and destinations are validated before publication. Missing roots or dependencies, empty source selections, conflicting destinations, and paths escaping declared roots are errors.
|
|
178
|
+
|
|
179
|
+
The output manifest tracks generated pages and resolved API links. Regeneration removes obsolete managed pages, retains unrelated handwritten files, and refuses to overwrite edited managed pages or existing unowned destination files. Partial scope builds retain other scopes' managed pages and refuse changes that would invalidate their generated links; include the affected scopes or run a full build instead. Publication prepares output before writing, attempts recovery on filesystem failures, and reports recovery failures; it is not a filesystem-wide atomic transaction.
|
|
180
|
+
|
|
181
|
+
This workflow uses the existing ZARD parse model. The optional `zard-rigor` gem enriches JSON through the released Rigor CLI; see its [setup and limitations](../zard-rigor/README.md). External RBS ingestion and stable lens semantics remain separate work. See [ADR 0006](adr/0006-hierarchical-documentation-output.md).
|
|
182
|
+
|
|
183
|
+
### Lint and render to stdout
|
|
184
|
+
|
|
87
185
|
`zard-doc lint PATH...` checks Ruby files. Directories are searched recursively for `*.rb`; paths are sorted and duplicates removed. Warnings do not fail by default. Use `--fail-on warning` to make warnings fail:
|
|
88
186
|
|
|
89
187
|
```sh
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zard
|
|
4
|
+
class FormatResult
|
|
5
|
+
attr_reader :source, :document, :diagnostics
|
|
6
|
+
|
|
7
|
+
def initialize(source:, document:, diagnostics:, changed:)
|
|
8
|
+
@source = source.dup.freeze
|
|
9
|
+
@document = document
|
|
10
|
+
@diagnostics = diagnostics.freeze
|
|
11
|
+
@changed = changed
|
|
12
|
+
freeze
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def changed?
|
|
16
|
+
@changed
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module Formatting
|
|
21
|
+
class Formatter
|
|
22
|
+
def self.call(source, path:)
|
|
23
|
+
new(source, path).call
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize(source, path)
|
|
27
|
+
@source = source
|
|
28
|
+
@path = path
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def call
|
|
32
|
+
unless @source.encoding.ascii_compatible?
|
|
33
|
+
return encoding_error("format.unsupported-encoding", "Formatting requires UTF-8 source text.")
|
|
34
|
+
end
|
|
35
|
+
parsed = Prism.parse(@source.b)
|
|
36
|
+
unless @source.dup.force_encoding(parsed.encoding).valid_encoding?
|
|
37
|
+
return encoding_error("format.invalid-encoding", "Source bytes are invalid for the declared encoding.")
|
|
38
|
+
end
|
|
39
|
+
original = Zard.parse(@source, path: @path)
|
|
40
|
+
if original.diagnostics.any? { |diagnostic| diagnostic.severity == :error }
|
|
41
|
+
return result(@source, original)
|
|
42
|
+
end
|
|
43
|
+
unless parsed.encoding == Encoding::UTF_8
|
|
44
|
+
diagnostic = format_diagnostic("format.unsupported-encoding", :error, "Formatting requires UTF-8 source text.", original.declarations.first&.span)
|
|
45
|
+
return result(@source, original, [diagnostic])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
output = @source.b.dup
|
|
49
|
+
unsafe_lines = []
|
|
50
|
+
blocks = original.declarations.filter_map(&:comment_span).uniq { |span| [span.start_offset, span.end_offset] }
|
|
51
|
+
blocks.sort_by(&:start_offset).reverse_each do |span|
|
|
52
|
+
start = line_start(span.start_offset)
|
|
53
|
+
ending = (@source.b.index("\n", span.end_offset) || (@source.bytesize - 1)) + 1
|
|
54
|
+
block = @source.byteslice(start, ending - start).dup.force_encoding(parsed.encoding)
|
|
55
|
+
normalized = normalize(block, span, original)
|
|
56
|
+
ordered = reorder(normalized, literal_lines(normalized))
|
|
57
|
+
if ordered != normalized
|
|
58
|
+
baseline = replace(output, start, block.bytesize, normalized)
|
|
59
|
+
candidate = replace(output, start, block.bytesize, ordered)
|
|
60
|
+
if snapshot(Zard.parse(baseline.force_encoding(@source.encoding), path: @path)) == snapshot(Zard.parse(candidate.force_encoding(@source.encoding), path: @path))
|
|
61
|
+
normalized = ordered
|
|
62
|
+
else
|
|
63
|
+
unsafe_lines << span.start_line
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
output = replace(output, start, block.bytesize, normalized)
|
|
67
|
+
end
|
|
68
|
+
output.force_encoding(@source.encoding)
|
|
69
|
+
document = Zard.parse(output, path: @path)
|
|
70
|
+
diagnostics = unsafe_lines.sort.map do |line|
|
|
71
|
+
span = document.declarations.filter_map(&:comment_span).find { |item| item.start_line == line }
|
|
72
|
+
format_diagnostic("format.unsafe-extrbs-order", :warning, "Leave @extrbs in place because moving it would change documentation or contract content.", span)
|
|
73
|
+
end
|
|
74
|
+
result(output, document, diagnostics)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def result(source, document, diagnostics = [])
|
|
80
|
+
FormatResult.new(source: source, document: document, diagnostics: document.diagnostics + diagnostics, changed: source.b != @source.b)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def encoding_error(code, message)
|
|
84
|
+
diagnostic = format_diagnostic(code, :error, message, nil)
|
|
85
|
+
document = Model::V1::Document.new(path: @path, declarations: [].freeze, diagnostics: [diagnostic].freeze)
|
|
86
|
+
result(@source, document)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def line_start(offset)
|
|
90
|
+
offset.zero? ? 0 : (@source.b.rindex("\n", offset - 1) || -1) + 1
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def replace(source, start, length, replacement)
|
|
94
|
+
source.byteslice(0, start).b + replacement.b + source.byteslice(start + length, source.bytesize).b
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def normalize(block, span, document)
|
|
98
|
+
literal = literal_lines(block)
|
|
99
|
+
blocked = document.declarations.flat_map do |declaration|
|
|
100
|
+
declaration.documentation.each_with_index.filter_map do |tag, index|
|
|
101
|
+
next unless tag.name == :raw
|
|
102
|
+
|
|
103
|
+
continuation = tag.raw.lines.drop(1).any? { |line| !line.sub(/\A[ \t]*# ?/, "").strip.empty? }
|
|
104
|
+
example = index.positive? && declaration.documentation[index - 1].name == :example
|
|
105
|
+
tag.span.start_line if continuation || example
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
redundant = document.diagnostics.select { |diagnostic| diagnostic.code == "documentation.redundant-marker" }.map { |diagnostic| diagnostic.span.start_line }
|
|
109
|
+
ambiguous = document.declarations.flat_map(&:documentation).select do |tag|
|
|
110
|
+
%i[return yieldreturn].include?(tag.name) && tag.claim.nil? && tag.description.start_with?("[", "—")
|
|
111
|
+
end.map { |tag| tag.span.start_line }
|
|
112
|
+
block.lines.each_with_index.map do |line, index|
|
|
113
|
+
number = span.start_line + index
|
|
114
|
+
if literal.include?(index)
|
|
115
|
+
line
|
|
116
|
+
elsif !blocked.include?(number) && line.match?(/\A[ \t]*#[ \t]*@extrbs(?:[ \t]|\r?\n|\z)/)
|
|
117
|
+
line.sub(/\A([ \t]*)#[ \t]*@extrbs/, '\1# @extrbs')
|
|
118
|
+
elsif redundant.include?(number) && !ambiguous.include?(number)
|
|
119
|
+
line.sub(/(@(?:return|yieldreturn)[ \t])— ?/, '\1')
|
|
120
|
+
else
|
|
121
|
+
line
|
|
122
|
+
end
|
|
123
|
+
end.join
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def literal_lines(block)
|
|
127
|
+
protected = []
|
|
128
|
+
example = false
|
|
129
|
+
fence = nil
|
|
130
|
+
block.lines.each_with_index do |line, index|
|
|
131
|
+
body = line.sub(/\A[ \t]*# ?/, "")
|
|
132
|
+
content = fence ? body : body.sub(/\A@(?:example|note|deprecated|see)(?:[ \t]|\z)/, "")
|
|
133
|
+
marker = content.match(/\A {0,3}(`{3,}|~{3,})([^\r\n]*)/)
|
|
134
|
+
if fence
|
|
135
|
+
protected << index
|
|
136
|
+
fence = nil if marker && marker[1][0] == fence[0] && marker[1].length >= fence.length && marker[2].strip.empty?
|
|
137
|
+
next
|
|
138
|
+
end
|
|
139
|
+
canonical = line.match?(/\A[ \t]*#(?: @|:)/)
|
|
140
|
+
if canonical
|
|
141
|
+
example = body.match?(/\A@example(?:[ \t]|\r?\n|\z)/)
|
|
142
|
+
elsif example
|
|
143
|
+
protected << index
|
|
144
|
+
end
|
|
145
|
+
if marker && (marker[1][0] != "`" || !marker[2].include?("`"))
|
|
146
|
+
fence = marker[1]
|
|
147
|
+
protected << index
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
protected
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def reorder(block, protected)
|
|
154
|
+
lines = block.lines
|
|
155
|
+
first = lines.index { |line| line.match?(/\A[ \t]*#(?: @|:)/) }
|
|
156
|
+
return block unless first
|
|
157
|
+
|
|
158
|
+
terminators = lines.map { |line| line[/\r?\n\z/].to_s }
|
|
159
|
+
bodies = lines.map { |line| line.delete_suffix(line[/\r?\n\z/].to_s) }
|
|
160
|
+
annotations = bodies.drop(first)
|
|
161
|
+
extrbs, remaining = annotations.each_with_index.partition { |line, index| !protected.include?(first + index) && line.match?(/\A[ \t]*# @extrbs(?:[ \t]|\z)/) }
|
|
162
|
+
(bodies.take(first) + (extrbs + remaining).map(&:first)).each_with_index.map { |line, index| line + terminators[index] }.join
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def snapshot(document)
|
|
166
|
+
document.declarations.map do |declaration|
|
|
167
|
+
tags = declaration.documentation.map { |tag| [tag.name, tag.owner, tag.subject, tag.claim, tag.description] }
|
|
168
|
+
contracts = declaration.contracts.map { |contract| [contract.channel, contract.payload, contract.note] }.sort_by(&:inspect)
|
|
169
|
+
[declaration.kind, declaration.name, declaration.namespace, declaration.receiver, declaration.refinement, tags, contracts]
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def format_diagnostic(code, severity, message, span)
|
|
174
|
+
span ||= Model::V1::SourceSpan.new(path: @path, start_line: 1, start_column: 0, end_line: 1, end_column: 0, start_offset: 0, end_offset: 0)
|
|
175
|
+
Model::V1::Diagnostic.new(code: code, severity: severity, message: message, span: span)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
data/lib/zard/model/v1.rb
CHANGED
|
@@ -69,15 +69,30 @@ module Zard
|
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
class Parameter
|
|
73
|
+
attr_reader :kind, :name, :source, :span, :default, :default_span
|
|
74
|
+
|
|
75
|
+
def initialize(kind:, name:, source:, span:, default: nil, default_span: nil)
|
|
76
|
+
@kind = kind
|
|
77
|
+
@name = name
|
|
78
|
+
@source = source
|
|
79
|
+
@span = span
|
|
80
|
+
@default = default
|
|
81
|
+
@default_span = default_span
|
|
82
|
+
freeze
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
72
86
|
class Declaration
|
|
73
|
-
attr_reader :kind, :name, :namespace, :visibility, :parameters, :receiver, :receiver_span, :refinement, :refinement_span, :alias_target, :superclass, :superclass_span, :container_builder, :container_builder_span, :mixins, :span, :comment_span, :documentation, :contracts
|
|
87
|
+
attr_reader :kind, :name, :namespace, :visibility, :parameters, :parameter_details, :receiver, :receiver_span, :refinement, :refinement_span, :alias_target, :superclass, :superclass_span, :container_builder, :container_builder_span, :mixins, :span, :comment_span, :documentation, :contracts
|
|
74
88
|
|
|
75
|
-
def initialize(kind:, name:, namespace:, visibility:, parameters:, span:, comment_span:, documentation:, contracts:, receiver: nil, receiver_span: nil, refinement: nil, refinement_span: nil, alias_target: nil, superclass: nil, superclass_span: nil, container_builder: nil, container_builder_span: nil, mixins: [].freeze)
|
|
89
|
+
def initialize(kind:, name:, namespace:, visibility:, parameters:, span:, comment_span:, documentation:, contracts:, parameter_details: nil, receiver: nil, receiver_span: nil, refinement: nil, refinement_span: nil, alias_target: nil, superclass: nil, superclass_span: nil, container_builder: nil, container_builder_span: nil, mixins: [].freeze)
|
|
76
90
|
@kind = kind
|
|
77
91
|
@name = name
|
|
78
92
|
@namespace = namespace
|
|
79
93
|
@visibility = visibility
|
|
80
94
|
@parameters = parameters
|
|
95
|
+
@parameter_details = parameter_details
|
|
81
96
|
@receiver = receiver
|
|
82
97
|
@receiver_span = receiver_span
|
|
83
98
|
@refinement = refinement
|
data/lib/zard/parsing/parser.rb
CHANGED
|
@@ -924,6 +924,7 @@ module Zard
|
|
|
924
924
|
namespace: namespace,
|
|
925
925
|
visibility: visibility,
|
|
926
926
|
parameters: parameters,
|
|
927
|
+
parameter_details: declaration_parameters(node, kind, alias_target),
|
|
927
928
|
receiver: receiver,
|
|
928
929
|
receiver_span: receiver_span,
|
|
929
930
|
refinement: refinement,
|
|
@@ -1315,6 +1316,7 @@ module Zard
|
|
|
1315
1316
|
namespace: declaration.namespace,
|
|
1316
1317
|
visibility: visibility,
|
|
1317
1318
|
parameters: parameters,
|
|
1319
|
+
parameter_details: declaration.parameter_details,
|
|
1318
1320
|
receiver: declaration.receiver,
|
|
1319
1321
|
receiver_span: declaration.receiver_span,
|
|
1320
1322
|
refinement: declaration.refinement,
|
|
@@ -1332,6 +1334,47 @@ module Zard
|
|
|
1332
1334
|
)
|
|
1333
1335
|
end
|
|
1334
1336
|
|
|
1337
|
+
def declaration_parameters(node, kind, alias_target)
|
|
1338
|
+
return if alias_target || !%i[instance_method singleton_method].include?(kind)
|
|
1339
|
+
|
|
1340
|
+
parameters = if node.is_a?(Prism::DefNode)
|
|
1341
|
+
node.parameters
|
|
1342
|
+
elsif node.is_a?(Prism::CallNode) && node.block.is_a?(Prism::BlockNode)
|
|
1343
|
+
node.block.parameters&.parameters
|
|
1344
|
+
else
|
|
1345
|
+
return
|
|
1346
|
+
end
|
|
1347
|
+
return [].freeze unless parameters
|
|
1348
|
+
|
|
1349
|
+
groups = [[parameters.requireds, :required], [parameters.optionals, :optional],
|
|
1350
|
+
[[parameters.rest].compact, :rest], [parameters.posts, :post],
|
|
1351
|
+
[parameters.keywords, :required_keyword], [[parameters.keyword_rest].compact, :keyword_rest],
|
|
1352
|
+
[[parameters.block].compact, :block]]
|
|
1353
|
+
groups.flat_map do |nodes, kind|
|
|
1354
|
+
nodes.map { |parameter| parameter_detail(parameter, kind) }
|
|
1355
|
+
end.sort_by { |parameter| parameter.span.start_offset }.freeze
|
|
1356
|
+
end
|
|
1357
|
+
|
|
1358
|
+
def parameter_detail(parameter, kind)
|
|
1359
|
+
kind = case parameter
|
|
1360
|
+
when Prism::OptionalKeywordParameterNode then :optional_keyword
|
|
1361
|
+
when Prism::ForwardingParameterNode then :forwarding
|
|
1362
|
+
when Prism::NoKeywordsParameterNode then :no_keywords
|
|
1363
|
+
when Prism::ImplicitRestNode then :implicit_rest
|
|
1364
|
+
else kind
|
|
1365
|
+
end
|
|
1366
|
+
value = parameter.value if parameter.is_a?(Prism::OptionalParameterNode) || parameter.is_a?(Prism::OptionalKeywordParameterNode)
|
|
1367
|
+
if value.respond_to?(:heredoc?) && value.heredoc?
|
|
1368
|
+
@diagnostics << Model::V1::Diagnostic.new(code: "parameter.unsupported-default", severity: :warning,
|
|
1369
|
+
message: "Heredoc parameter defaults retain their header; a contiguous default expression is unavailable.", span: span(value.location))
|
|
1370
|
+
value = nil
|
|
1371
|
+
end
|
|
1372
|
+
Model::V1::Parameter.new(kind: kind,
|
|
1373
|
+
name: parameter.respond_to?(:name) ? parameter.name&.to_s : nil,
|
|
1374
|
+
source: parameter.location.slice, span: span(parameter.location),
|
|
1375
|
+
default: value&.location&.slice, default_span: value ? span(value.location) : nil)
|
|
1376
|
+
end
|
|
1377
|
+
|
|
1335
1378
|
def parameter_names(parameters)
|
|
1336
1379
|
return [] unless parameters
|
|
1337
1380
|
|
data/lib/zard/version.rb
CHANGED
data/lib/zard.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "zard/version"
|
|
4
4
|
require_relative "zard/model/v1"
|
|
5
5
|
require_relative "zard/parsing/parser"
|
|
6
|
+
require_relative "zard/formatting/formatter"
|
|
6
7
|
|
|
7
8
|
module Zard
|
|
8
9
|
class Error < StandardError; end
|
|
@@ -11,5 +12,9 @@ module Zard
|
|
|
11
12
|
Parsing::Parser.call(source, path: path)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
def self.format(source, path:)
|
|
16
|
+
Formatting::Formatter.call(source, path: path)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private_constant :Parsing, :Formatting
|
|
15
20
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- USAMI Kenta
|
|
@@ -38,15 +38,20 @@ files:
|
|
|
38
38
|
- docs/adr/0001-contract-and-documentation-channels.md
|
|
39
39
|
- docs/adr/0002-em-dash-zard-documentation-marker.md
|
|
40
40
|
- docs/adr/0003-package-boundaries-and-rigor-lens.md
|
|
41
|
+
- docs/adr/0004-documentation-reference-syntax.md
|
|
42
|
+
- docs/adr/0005-safe-annotation-formatting.md
|
|
43
|
+
- docs/adr/0006-hierarchical-documentation-output.md
|
|
41
44
|
- docs/agents/domain.md
|
|
42
45
|
- docs/agents/issue-tracker.md
|
|
43
46
|
- docs/agents/triage-labels.md
|
|
47
|
+
- docs/json-export.md
|
|
44
48
|
- docs/supported-ruby-syntax.md
|
|
45
49
|
- docs/usage.md
|
|
46
50
|
- examples/catalog/expected.md
|
|
47
51
|
- examples/catalog/lib/catalog.rb
|
|
48
52
|
- examples/catalog/lib/catalog/entry.rb
|
|
49
53
|
- lib/zard.rb
|
|
54
|
+
- lib/zard/formatting/formatter.rb
|
|
50
55
|
- lib/zard/model/v1.rb
|
|
51
56
|
- lib/zard/parsing/parser.rb
|
|
52
57
|
- lib/zard/version.rb
|
|
@@ -74,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
74
79
|
- !ruby/object:Gem::Version
|
|
75
80
|
version: '0'
|
|
76
81
|
requirements: []
|
|
77
|
-
rubygems_version: 4.0.
|
|
82
|
+
rubygems_version: 4.0.16
|
|
78
83
|
specification_version: 4
|
|
79
84
|
summary: AI-friendly Ruby documentation built on RBS and Rigor extensions
|
|
80
85
|
test_files: []
|