Skip to content

Add latexify hooks for metadata and ops#1835

Open
oameye wants to merge 1 commit into
JuliaSymbolics:masterfrom
oameye:hooks/latexify-hooks
Open

Add latexify hooks for metadata and ops#1835
oameye wants to merge 1 commit into
JuliaSymbolics:masterfrom
oameye:hooks/latexify-hooks

Conversation

@oameye
Copy link
Copy Markdown

@oameye oameye commented Apr 7, 2026

Summary

Add extension points to the Latexify pipeline so downstream packages can customize LaTeX rendering based on:

  1. metadata attached to BasicSymbolic nodes, and
  2. the operator of a call.

This enables non-pirating, opt-in rendering for DSLs (e.g., angle-bracket averages, sum prefixes) without forking Symbolics' LaTeX code.

What changed

  • Introduce _toexpr_metadata(O, ::Type{Ctx}, val; latexwrapper) to allow metadata-driven LaTeX overrides.
  • Introduce _toexpr_op(op, args; latexwrapper) to allow operator-driven LaTeX overrides.
  • Add _toexpr_plain so hooks can fall back to the default LaTeX formatting.

Example usage

function LatexifyExt._toexpr_op(::typeof(avg), args; latexwrapper=LatexifyExt.default_latex_wrapper)
    inner = LatexifyExt._toexpr_plain(args[1]; latexwrapper)
    inner_s = strip(latexify(inner).s, '$')
    return LaTeXString("\\langle " * inner_s * " \\rangle")
end
function LatexifyExt._toexpr_metadata(O, ::Type{SumIndices}, val; latexwrapper=LatexifyExt.default_latex_wrapper)
    # return a LaTeX AST or LaTeXString that prefixes a sum, e.g. "\\Sigma( ... )"
end

Example output

  • avg(x + y) renders as \langle x + y \rangle
  • A sum metadata hook can render as \Sigma(i=1:N) ...

Tests

  • Added a test in test/latexify.jl showing a custom op hook returning a LaTeXString.

Motivation

Downstream packages currently have no hook to customize LaTeX output for custom operators or metadata-rich nodes (e.g., average brackets, sum prefixes). This PR adds general hooks without changing default behavior.

@oameye
Copy link
Copy Markdown
Author

oameye commented May 16, 2026

@AayushSabharwal Can also you have a look at this PR? :)

Comment thread test/latexify.jl
@test_reference "latexify_refs/call_with_metadata.txt" latexify(f)

struct LatexHookCtx end
function LatexifyExt._toexpr_metadata(O, ::Type{LatexHookCtx}, val; latexwrapper = LatexifyExt.default_latex_wrapper)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is defining methods of functions in extensions like this valid? I thought Base.get_extension was internal and only for debugging?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yeah it is heavily discouraged by the Julia Base maintainers. Never really understood why.

The real issue is that the hookable functions live in the extension, so downstream packages have no clean way to extend them. We can move _toexpr_metadata and _toexpr_op to the main Symbolics module with nothing-returning fallback methods. The extension then calls those Symbolics-defined functions. Downstream packages can extend them directly via import Symbolics without touching Base.get_extension. _toexpr_plain and default_latex_wrapper are Latexify-coupled helpers and stay in the extension; they'd only be needed for composing output inside a hook, and at that point the user already has Latexify loaded.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Do you think this is the correct solution?

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