jbms / sphinx-immaterial

Adaptation of the popular mkdocs-material material design theme to the sphinx documentation system
https://jbms.github.io/sphinx-immaterial/
Other
196 stars 29 forks source link

[theme's graphviz ext] xref not friendly with node labels that require HTML chars #164

Closed 2bndy5 closed 1 year ago

2bndy5 commented 1 year ago

I'm writing a graph that shows data structures using record-based nodes.

Here's a sample without any xrefs:

homie-topology Click this picture to go to the graphviz playground. Notice that cells in a record need to use the <symbol> syntax to identify the cell within the node when drawing connecting edges.

What I'd like to do is have a the top cell in the record link to a python class, but the replacements from this theme's graphviz ext crash the dot executable.

The warning emitted in the build log includes all my env vars (which I have removed), but doesn't show any of the dot code that was passed.

This code causes the following warning ```rst .. graphviz:: digraph g { graph [rankdir = "LR"] "device" [ xref = " :py:class`HomieDevice` | attributes | nodes" shape = "record" ] "node" [ label = " node | attributes | properties" shape = "record" ] "property" [ label = " property | attributes" shape = "record" ] "device":f2 -> "node":f0 "node":f2 -> "property":f0 } ```
WARNING: Error running ['dot', '-Ncolor=#123457', '-Nstyle=solid,filled', '-Nfillcolor=#123458', '-Nfontcolor=#123456', '-Nfontsize=12', '-Nfontname=Roboto', '-Ecolor=#123459', '-Efontcolor=#123456', '-Efontsize=12', '-Efontname=Roboto', '-Gbgcolor=transparent', '-Gcolor=#123457', '-Gfontcolor=#123456', '-Gfontsize=12', '-Gfontname=Roboto', '-Tsvg'] with env {...} (dot.exe:17860): Pango-WARNING **: couldn't load font "Roboto Not-Rotated 12", falling back to "Sans Not-Rotated 12", expect ugly output.
Warning: node device, port f2 unrecognized

By adjusting the warning prompt in the theme's source, I was able to see the resulting altered dot syntax:

digraph g {
    graph [rankdir = "LR"]
    "device" [
        label=<&lt;f0&gt; :py:class`HomieDevice` | &lt;f1&gt; attributes | &lt;f2&gt; nodes>
        shape = "record"
    ]
    "node" [
        label = "<f0> node | <f1> attributes | <f2> properties"
        shape = "record"
    ]
    "property" [
        label = "<f0> property | <f1> attributes"
        shape = "record"
    ]
    "device":f2 -> "node":f0
    "node":f2 -> "property":f0
}

So, it looks like the line that escapes HTML syntax in the replaced label is causing dot to misinterpret the record-based node.

https://github.com/jbms/sphinx-immaterial/blob/21f2aeeb7fd182955a78893c30aaa6b84d6d5e8e/sphinx_immaterial/graphviz.py#L44

2bndy5 commented 1 year ago

As a workaround, I think I can restructure the graph with clusters, but I wanted to document the experience here.

The easiest course of actions, although disappointing, would be to explicitly not support record-based graphs with this theme's modified graphviz extension. I'm not sure how to robustly support the xref feature when the label uses HTML special characters. There are similar instances in dot syntax that allow for HTML-like labels to be used as a node's content (which is great for making graph legends).

jbms commented 1 year ago

It does not appear that it is possible to make just an individual field of a record node a hyperlink, but we should be able to make the entire thing a hyperlink.

The theme currently generates a label attribute using the graphviz "HTML" syntax label=<...> simply because then I could use html.escape to ensure any special characters were properly escaped. I wasn't sure if escaping is possible in the regular label="..." syntax. If so, we could just use the regular double-quoted syntax, which I think would basically make record nodes work, as long as the xref doesn't expand to something with angle brackets that would interfere with the record syntax. Even if escaping is not possible with the plain label = "..." syntax, we could still use that syntax in cases where the text does not contain any embedded double quotes.

2bndy5 commented 1 year ago

I wasn't sure if escaping is possible in the regular label="..." syntax.

As far as I know you can escape special chars in quoted labels with \

label = "<f0> \<device\> | <f1> attributes | <f2> nodes"

results in image

as long as the xref doesn't expand to something with angle brackets that would interfere with the record syntax

This would be harder to avoid. Its not something we can tell users to avoid since its more of an under-the-hood implementation detail.

cases where the text does not contain any embedded double quotes

When I experimented with HTML-like labels to make a graph legend, I found that quotes are very important for label values that span multiple lines (in the dot src).


I find the docs about graphviz xref attribute a little confusing because it uses the word "node" (as in docutils node) when talking about a graph node's attributes.

If there is at least one reference node, only the last such node is considered

jbms commented 1 year ago

I wasn't sure if escaping is possible in the regular label="..." syntax.

As far as I know you can escape special chars in quoted labels with \

label = "<f0> \<device\> | <f1> attributes | <f2> nodes"

results in image

I think this quoting is being handled by the graphviz parser of the record node syntax. What if you want the label to include a literal double quote " character?

as long as the xref doesn't expand to something with angle brackets that would interfere with the record syntax

This would be harder to avoid. Its not something we can tell users to avoid since its more of an under-the-hood implementation detail.

I guess as you noted it is possible to escape the angle brackets. However, to do that we would need to know that it is a record node, and to figure that out automatically would, I think, require a real DOT parser.

Another option is that users could specify xref to get a cross reference and then manually specify the label as well, to override the label generated for the xref. That might make more sense than trying to handle rST syntax inside of the record node syntax.

cases where the text does not contain any embedded double quotes

When I experimented with HTML-like labels to make a graph legend, I found that quotes are very important for label values that span multiple lines (in the dot src).

I find the docs about graphviz xref attribute a little confusing because it uses the word "node" (as in docutils node) when talking about a graph node's attributes.

If there is at least one reference node, only the last such node is considered

Agreed that this could be made clearer.

2bndy5 commented 1 year ago

What if you want the label to include a literal double quote " character?

You can use \", but you can't use ' " ' as it complains about the string format (at least in a record label).

Another option is that users could specify xref to get a cross reference and then manually specify the label as well, to override the label generated for the xref

I especially like this idea. 💯 TBH, this is kinda what I was expecting when I first tried it on this graph.

It does not appear that it is possible to make just an individual field of a record node a hyperlink, but we should be able to make the entire thing a hyperlink.

Yeah, this is my first attempt at using record-based labels. I'm totally fine with making the whole node a hyperlink. I can override the CSS for all but the first field so the font color only changes for the first field.

2bndy5 commented 1 year ago

Another option is that users could specify xref to get a cross reference and then manually specify the label as well, to override the label generated for the xref

I took a swing at doing this and it works, but my approach is to assume that label attr immediately follows the xref attr in the dot syntax. I'm not sure if there's a better way to parse the dot syntax since the code style is rather flexible.