-
Notifications
You must be signed in to change notification settings - Fork 607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
internal/graph: Escape labels to support double quotes #689
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -247,7 +247,7 @@ func (b *builder) addNodelets(node *Node, nodeID int) bool { | |
continue | ||
} | ||
weight := b.config.FormatValue(w) | ||
nodelets += fmt.Sprintf(`N%d_%d [label = "%s" id="N%d_%d" fontsize=8 shape=box3d tooltip="%s"]`+"\n", nodeID, i, t.Name, nodeID, i, weight) | ||
nodelets += fmt.Sprintf(`N%d_%d [label = "%s" id="N%d_%d" fontsize=8 shape=box3d tooltip="%s"]`+"\n", nodeID, i, escapeForDotCentered(t.Name), nodeID, i, weight) | ||
nodelets += fmt.Sprintf(`N%d -> N%d_%d [label=" %s" weight=100 tooltip="%s" labeltooltip="%s"]`+"\n", nodeID, nodeID, i, weight, weight, weight) | ||
if nts := lnts[t.Name]; nts != nil { | ||
nodelets += b.numericNodelets(nts, maxNodelets, flatTags, fmt.Sprintf(`N%d_%d`, nodeID, i)) | ||
|
@@ -274,7 +274,7 @@ func (b *builder) numericNodelets(nts []*Tag, maxNumNodelets int, flatTags bool, | |
} | ||
if w != 0 { | ||
weight := b.config.FormatValue(w) | ||
nodelets += fmt.Sprintf(`N%s_%d [label = "%s" id="N%s_%d" fontsize=8 shape=box3d tooltip="%s"]`+"\n", source, j, t.Name, source, j, weight) | ||
nodelets += fmt.Sprintf(`N%s_%d [label = "%s" id="N%s_%d" fontsize=8 shape=box3d tooltip="%s"]`+"\n", source, j, escapeForDotCentered(t.Name), source, j, weight) | ||
nodelets += fmt.Sprintf(`%s -> N%s_%d [label=" %s" weight=100 tooltip="%s" labeltooltip="%s"%s]`+"\n", source, source, j, weight, weight, weight, attr) | ||
} | ||
} | ||
|
@@ -486,9 +486,16 @@ func escapeAllForDot(in []string) []string { | |
return out | ||
} | ||
|
||
// escapeForDot escapes double quotes and backslashes, and replaces Graphviz's | ||
// "center" character (\n) with a left-justified character. | ||
// escapeForDot escapes double quotes and backslashes, and replaces newlines | ||
// with a left-justified escape (\l). | ||
// See https://graphviz.org/docs/attr-types/escString/ for more info. | ||
func escapeForDot(str string) string { | ||
return strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(str, `\`, `\\`), `"`, `\"`), "\n", `\l`) | ||
} | ||
|
||
// escapeForDotCentered escapes double quotes and backslashes, and replaces | ||
// newlines with Graphviz's center escape (\n). | ||
// See https://graphviz.org/docs/attr-types/escString/ for more info. | ||
func escapeForDotCentered(str string) string { | ||
return strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(str, `\`, `\\`), `"`, `\"`), "\n", `\n`) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could pull the common parts of these two functions out into a single shared function, if that seems better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replacing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The original |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
digraph "testtitle" { | ||
node [style=filled fillcolor="#f8f8f8"] | ||
subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\llabel3: \"foo\"\l" tooltip="testtitle"] } | ||
N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"] | ||
N1_0 [label = "label\"quote\"\nline2" id="N1_0" fontsize=8 shape=box3d tooltip="10"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I mentioned in another comment
and as far as I can tell, we render the newline as actual newline here: Unlike we do for comments, for label values we should escape the newlines here to render them as "\n". |
||
N1 -> N1_0 [label=" 10" weight=100 tooltip="10" labeltooltip="10"] | ||
NN1_0 [label = "numeric\"quote\"\nline2" id="NN1_0" fontsize=8 shape=box3d tooltip="20"] | ||
N1 -> NN1_0 [label=" 20" weight=100 tooltip="20" labeltooltip="20"] | ||
N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"] | ||
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src -> dest (10)" labeltooltip="src -> dest (10)" minlen=2] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing something but quoting myself from #683:
So for function names and tag values I'd expect the newline to be escaped into a printable character, not rendered as either (left or center) actual newline character...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The newline handling is confusing, and there could be a better place to put it. However, I believe this version produces the desired output. Here is an example with my Go test output that currently fails to render with pprof, which shows the newlines in the key/value pairs being displayed as
\n
:I tried to change the existing logic as little as possible. My logic was the following:
The
joinLabels
function combines multiple key/value pairs, and returns a string as it should be displayed to a human, without any dot/graphviz details. It replaces newline characters with a\n
sequence, since we want a key/value pair to be on a single line. However, it then combines the multiple key/value pairs with newlines, since we want to display separate key/value pairs on separate lines. As a result, the new lines that remain should be "visible." TheTestJoinLabels
attempts to demonstrate this.Next,
escapeForDotCentered
must convert this "human string" into a version that can be embedded into Graphviz's "escString". To do that, it must escape backslashes, escape quotes, and replace newline characters with\n
. It turns out: I can remove the newline escaping from this function, since "centered text" in graphviz's default. However, this will make this function different fromescapeForDot
. Is that desirable?I totally forgot about other non-printable characters though, so thanks! I will add that to joinLabels. My logic is if we were to us a different graph rendering tool, we would still want this escaping, so it should not be "dot specific".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest we have a single
escapeForDot()
function with a bool argument likeescapeNewlines
and set that argument to true when escaping comments and to false when escaping label values.The
joinLabels
function should only join lines with newlines (actual ones), it should not do any escaping to the joined values itself, it will assume the values have been escaped as necessary already.