Skip to content

Julia 1.13: re-wrap @doc cell result as Docs.Binding#3546

Draft
pankgeorg wants to merge 3 commits into
mainfrom
pg/julia-1.13-docstring-display
Draft

Julia 1.13: re-wrap @doc cell result as Docs.Binding#3546
pankgeorg wants to merge 3 commits into
mainfrom
pg/julia-1.13-docstring-display

Conversation

@pankgeorg

@pankgeorg pankgeorg commented May 14, 2026

Copy link
Copy Markdown
Member

Summary

Restore the docstring-as-cell-output rendering on Julia 1.13+ that was broken by JuliaLang/julia#59882. Fixes the failing assertions in #3449; part of the #3389 umbrella. Continues — but does not stack on — the closed #3452, which had been abandoned because the author could not pin down the upstream behavior change.

What changed in Julia 1.13

Same parsed expression, different result:

julia> Meta.parse("\"::Bool\"\nf(::Bool) = 1") |> e -> Core.eval(Main, e) |> typeof
# 1.12: Base.Docs.Binding
# 1.13: typeof(f)

@doc str def now returns the value of def instead of a Base.Docs.Binding (JuliaLang/julia#60681, closed as not planned). Pluto's format_output(::Base.Docs.Binding) dispatch no longer fires, so documented function cells render as f (generic function with N methods) rather than the docstring HTML.

Docs.doc(binding) itself still works correctly on 1.13 and aggregates all method docstrings as before — only the cell result type changed.

The fix

Detect the (pre-macroexpansion) @doc macrocall in run_expression, extract the bound name from the definition, and re-wrap the result as Docs.Binding(workspace, name) before storing in cell_results. This puts the cell back on the existing rendering path; no display-side changes required.

  • No-op on Julia < 1.13 (where the result is already a Docs.Binding).
  • Skipped for CapturedException results so error rendering is untouched.
  • Skipped if the bound name isn't defined in the workspace, so a half-failed definition still falls through to the default formatter.
  • Handles common doc targets: f(x) = …, function f end, f(::T) where T, parametric methods, struct, macro, module, const.

Test plan

  • CI: test/MacroAnalysis.jl "Doc strings" testset (the 11 failing assertions from Julia 1.13 tests: Macro analysis - docstrings #3449) passes on Julia 1.13.
  • CI: same testset still passes on 1.11 and 1.12 (helper is a no-op).
  • Manual: open a notebook on 1.13, run a cell like "docstring"\nf(::Bool) = 1, verify the rich docstring panel renders.
  • Manual: redefine a method in another cell, verify both method docstrings appear in either cell's output.

Out of scope (follow-ups from #3389)

  • warn_julia_compat() in src/Pluto.jl still warns on 1.13 — should be bumped to 1.14 once the testset is green.
  • Malt.jl#101 and #93 (1.13 interrupt failures) are still open.
  • GracefulPkg.jl 1.13 audit per the umbrella issue.

Try this Pull Request!

Open Julia and type:

julia> import Pkg
julia> Pkg.activate(temp=true)
julia> Pkg.add(url="https://github.com/JuliaPluto/Pluto.jl", rev="pg/julia-1.13-docstring-display")
julia> using Pluto

pankgeorg and others added 3 commits May 14, 2026 18:24
JuliaLang/julia#59882 changed `@doc` to return the documented value (e.g. the
function itself) instead of a `Docs.Binding`. Pluto's docstring cell output
relies on dispatching on `Base.Docs.Binding` via `format_output`, so on 1.13
documented function cells now render as "f (generic function with N methods)"
instead of the rich docstring view. Causes the test failures in #3449.

Detect the pre-macroexpansion `@doc` macrocall, extract the bound name, and
re-wrap the cell result as `Docs.Binding(workspace, name)` so the existing
display path is preserved. No-op on Julia < 1.13.

Refs JuliaLang/julia#60681 (closed as not planned), #3449, #3389.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pluto's Parse.jl always returns Expr(:toplevel, LineNumberNode, inner), so the
original_expr seen by run_expression is wrapped — the earlier helper only
inspected a bare :macrocall and returned nothing for the real shape, which is
why the test/MacroAnalysis.jl "Doc strings" assertions still failed on 1.13 CI.
Walk past :toplevel/:block wrappers (ignoring LineNumberNodes) before checking
for the @doc macrocall.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
doc_target_name returned nothing for `Expr(:., :Base, QuoteNode(:conj))`, so
the cell "An empty conjugate"\nBase.conj() = x left its result as `nothing`
on 1.13 and the last two MacroAnalysis.jl "Doc strings" assertions kept
failing. Pick the rightmost symbol from a qualified name; Docs.Binding in any
workspace resolves to the same docs via Docs.aliasof, so a workspace-rooted
binding still pulls in Base.conj's docstring plus the new one we added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pankgeorg

pankgeorg commented May 25, 2026

Copy link
Copy Markdown
Member Author

Another idea:

  • Make a pluto-specific show method for Functions that always gets the latest docs (after reactivity is done rendering)
    • in order to get the function to display the "same" docstring across methods, and not different, based on the timing of its execution
  • Make that work in the pre - 1.12
  • Keep the docs renderer in case someone actually explicitly requests it.

@fonsp

fonsp commented May 25, 2026

Copy link
Copy Markdown
Member

Little nsippet

<div style="
font-family: JuliaMono;
padding: .4em .4em;
font-size: .8rem;
">

<span style='font-style: italic; opacity: .8;'>function</span>

<span style='font-weight: bold;'>hello_world</span>

<a style='
	font-family: system-ui;
margin-inline-start: 2ch;
' href="#">(View docs)</a>

</div>

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.

2 participants