Skip to content

feat: parse rustdoc item pages (decl, fields, variants, methods) - #34

Merged
fpt merged 2 commits into
mainfrom
rustdoc-item-pages
Jul 18, 2026
Merged

fpt merged 2 commits into
mainfrom
rustdoc-item-pages

Conversation

@fpt

@fpt fpt commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Problem

read_rustdoc (and the underlying parseDocsRsDocument) only understood module/index pages — title, overview, and dl.item-table lists. On item pages (struct.*, enum.*, fn.*, trait.*) the URL resolved and the page loaded, but the parser never looked at where the real content lives. Reading e.g. serde_json/struct.Map.html or rlua/struct.Lua.html returned just the title, a one-line overview, and a few empty section headers — forcing a raw HTML fetch to confirm method signatures.

Change

Additive matchers under the existing section#main-content root (verified against the live docs.rs DOM):

  • pre.item-decl → fenced code block with the canonical declaration. Uniform across every item kind (struct/enum/fn/trait/type alias).
  • Impl / trait methods — h3.code-header (impl header) + h4.code-header (method signature) + docblock, scoped to the inherent-impl and trait-impl lists. The auto-trait and blanket-impl lists (Send/Sync/From<T>/…) are skipped as noise, along with their now-empty section headers.
  • Enum variants — div.variants > section.variant h3.code-header + variant docs.
  • Struct fields — span.structfield code + the following div.docblock, gated on an inFields flag (HasPrefix "Fields", so #[non_exhaustive] Fields (Non-exhaustive) headers still match) since field spans and their docblocks are bare direct children of section#main-content.

Signatures use RawInnerText so punctuation from text nodes is preserved exactly, e.g. pub fn insert(&mut self, k: String, v: Value) -> Option<Value>.

Example (httparse::Request)

```

Fields

  • `method: Option<&'buf str>`
    The request method, such as GET .
    ...

Implementations

impl<'h, 'b> Request<'h, 'b>

  • `pub fn parse(&mut self, buf: &'b [u8]) -> Result`
    Try to parse a buffer of bytes into the Request.
    ```

Tests

  • New golden fixtures: struct (serde_json::Map), function (serde_json::to_string), enum (serde_json::Value), and a documented-field struct (httparse::Request).
  • Existing module-page goldens (serde/tokio/regex) are unchanged — module pages contain none of this markup, so there's no regression.
  • make lint clean, make test green, plus live end-to-end reads of the affected pages.

🤖 Generated with Claude Code

parseDocsRsDocument only understood module/index pages (title, overview,
dl.item-table). On item pages the URL resolved and the page loaded, but
the parser never looked at where the real content lives, so reading a
struct/enum/fn/trait yielded just the title, a one-line overview, and a
few empty section headers — forcing a raw HTML fetch to see signatures.

Add matchers, scoped under the existing section#main-content root, for:

- pre.item-decl -> fenced code block with the canonical declaration
  (uniform across struct/enum/fn/trait/type alias).
- Impl/trait methods: h3.code-header (impl header) + h4.code-header
  (method signature) + docblock, scoped to the inherent- and trait-impl
  lists. Auto-trait and blanket-impl lists (Send/Sync/From<T>/…) are
  skipped as noise, along with their now-empty section headers.
- Enum variants: div.variants > section.variant h3.code-header + docs.
- Struct fields: span.structfield code + the following div.docblock,
  gated on an inFields flag (HasPrefix "Fields", so #[non_exhaustive]
  "Fields (Non-exhaustive)" headers still match) since field spans and
  their docblocks are bare direct children of section#main-content.

Signatures use RawInnerText so punctuation from text nodes is preserved
exactly (e.g. pub fn insert(&mut self, k: String, v: Value) -> Option<Value>).

Add golden fixtures for a struct (serde_json::Map), function
(serde_json::to_string), enum (serde_json::Value), and a struct with
documented fields (httparse::Request). Module-page goldens are unchanged
(module pages have none of this markup), so there is no regression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 18, 2026 •

Copy link
Copy Markdown

🤖 AI Review (klein) — turn 2

The change extends rustdoc parsing to item declarations, implementations, variants, fields, and richer nested documentation. The previously reported loss of headings, lists, and code examples in item docblocks is fixed by routing those blocks through writeRustDocBlock, and this was verified in the current parser. I found no additional verified correctness or regression issues in this incremental diff. The added golden output demonstrates preservation of examples across variants and implementation methods.


turn 2 (incremental review of e8b089f) · comments: total 1, active 0 (+0 new, −1 resolved) · verdict: approve

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

klein AI review — details in the summary comment.

Comment thread internal/app/rustdoc.go Outdated
Comment on lines +435 to +445
dq.NewMatchFunc("div.docblock"),
nil,
dq.NewNodeMatcher(
dq.NewMatchFunc("p"),
func(n *html.Node) {
text := strings.TrimSpace(dq.InnerText(n, true))
if text != "" {
builder.WriteString(fmt.Sprintf(" %s\n", text))
}
},
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Do not limit impl/variant documentation to direct p elements; reuse the existing docblock content matchers (or add equivalent handling for headings, lists, and <pre> blocks) so the full docblock is emitted.

Rationale

Verified against the parser above: overview docblocks preserve headings, code blocks, and lists via docblockHeaderMatcher, docblockPreMatcher, and docblockListMatcher, but this matcher only visits p. Any method or enum-variant documentation containing an example, list, nested heading, or code block is therefore silently omitted; the new fixtures already show variant examples in the HTML (for example rustdoc_doc_serdejson_value.html:329-345).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e8b089f. Added a shared writeRustDocBlock helper that renders the full docblock — paragraphs, <pre> code blocks, lists, and headings — for method, enum-variant, and struct-field docs, indented to nest under each item. Headings are emitted as bold text to avoid clashing with the document's ## section structure. Overview matchers are untouched, so module-page goldens are unchanged. The serde_json::Value golden now carries 104 method/variant example code blocks that were previously dropped.

The reduced matcher for method/variant/field docs only visited <p>, so
code examples, lists, and headings in that documentation were silently
dropped — unlike overview docblocks, which preserve them. Add a shared
writeRustDocBlock helper that renders paragraphs, <pre> code blocks,
lists, and headings from a div.docblock, indented to nest under the
enclosing item. Headings become bold text to avoid clashing with the
document's "##" section structure. Overview matchers are unchanged, so
module-page goldens are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions
github-actions Bot dismissed their stale review July 18, 2026 05:11

Superseded: the latest AI review no longer requests changes.

@fpt
fpt merged commit b791d84 into main Jul 18, 2026
2 checks passed
@fpt
fpt deleted the rustdoc-item-pages branch July 18, 2026 05:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant