# Programmatically annotate code?

Is there a known way to programmatically annotate code like workflows (without using boxforms or drawing tools):

Minimal example that doesn't work:

HoldForm[
"This is a test";
1 + Labeled[(1 + 1), "2"]]


Ideally, there would be two silent heads AnnotateRectangle[code] and AnnotateLabel[code, label, pos] that "surround with a rectangle" and callout/label parts of code respectively. These would hopefully work with multiple input/output cells and could be nested and formatted nicely.

• Workflows' examples are not evaluatable, is this a problem? What do you mean by would work with multiple in out cells? – Kuba Nov 19 '18 at 7:11
• @Kuba not a problem, but evaluatable would be nice, sometimes the workflows involve multiple cells with arrows and such - a good solution wouldn't be limited to a single cell – M.R. Nov 20 '18 at 7:35
• Are there editors you could use with MMA code that would offer such a feature? I don't know if these would offer the type of annotation you desire, but examples of editors used for MMA code include Wolfram's Workbench 3 and the IntelliJIDEA plugin. – theorist Dec 7 '18 at 19:27
• @theorist No, not to my knowledge. – M.R. Dec 7 '18 at 20:07
• @M.R. Can you be more specific about what you mean by, "without using boxforms"? And are you looking for a built-in function? Or are you interested in seeing how it could be done in general? – Robert Jacobson Dec 8 '18 at 5:24

You may use Interpretation. You will need some fancy formatting function that I will proxy here with Style.

The following will create a column of the code snippet and its annotation that can be directly used in input cells.

Interpretation[
Column[
{
HoldForm[RandomVariate[NormalDistribution[], 20]],
Style["Twenty random numbers", Blue, "Output"]
},
BaselinePosition -> 1]
,
RandomVariate[NormalDistribution[], 20]
]


For example it can be pasted into a Histogram and each evaluation will yield a different set of random numbers.

Interpretation can also be used inline with code by highlighting the function and its arguments and selecting Ctrl+Shift+Enter.

A fancier function than Style is needed to get to the workflow look.

### Update for slightly fancy format

Style'd strings may not be enough. You may use ListPlot and Callout as a means to get a fancier output for little additional effort.

First a function with the fancier annotation is created. An attempt to correctly size it to its text is made using a couple of the font values from CurrentValue on EvaluationCell.

ClearAll[calloutSnippet]
SetAttributes[calloutSnippet, {HoldFirst}]
calloutSnippet[snippet_, anno_String] :=
With[{snippetLength =
CurrentValue[EvaluationCell, "FontNWidth"] *
StringLength@ToString[HoldForm[snippet]]},
ListPlot[
{Callout [{0, 0}, anno, Below, Appearance -> "Leader",
CalloutStyle -> Directive[Blue, Thin]]},
PlotStyle -> Transparent,
PlotRange -> {Automatic, {0, -1}},
Axes -> None,
Frame -> {{False, False}, {False, True}},
FrameStyle -> Blue,
FrameTicks -> None,
AspectRatio ->
(4*CurrentValue[EvaluationCell, "FontCapHeight"])/snippetLength,
ImageSize -> snippetLength
]
]


This gives

calloutSnippet[
RandomVariate[NormalDistribution[], 20], "Twenty random numbers"]


This function combined with Interpretation.

ClearAll[annotateSnippet]
SetAttributes[annotateSnippet, {HoldFirst}]
annotateSnippet[snippet_, anno_String] :=
Interpretation[
Column[
{
HoldForm[snippet],
calloutSnippet[snippet, anno]
},
BaselinePosition -> 1],
snippet
]


gives

annotateSnippet[
RandomVariate[NormalDistribution[], 20], "Twenty random numbers"]


This expression can be used the same as before with both cut past and inline evaluation (Ctrl+Shift+Enter)

### Update for slightly fancier format

New format function with some of the features requested by OP.

ClearAll[calloutSnippet2]
SetAttributes[calloutSnippet2, {HoldFirst}]
Options[calloutSnippet2] =
{
ItemSize -> 20,
Spacer -> 1,
CalloutStyle -> Directive[Blue, Thin],
LabelStyle -> {FontColor -> Blue, FontSlant -> Italic}
};
calloutSnippet2[snippet_, anno_String,
opts : OptionsPattern[calloutSnippet2]] :=
Grid[{
{HoldForm[snippet], SpanFromLeft},
{
Item["",
Frame -> {{OptionValue[CalloutStyle],
None}, {OptionValue[CalloutStyle], None}}],
Item["",
Frame -> {{None,
OptionValue[CalloutStyle]}, {OptionValue[CalloutStyle], None}}]
},
{
Item["",
Frame -> {{None, OptionValue[CalloutStyle]}, {None,
OptionValue[CalloutStyle]}}],
Item["",
Frame -> {{OptionValue[CalloutStyle], None}, {None,
OptionValue[CalloutStyle]}}]
},
{Style[anno, OptionValue[LabelStyle], "Output"], SpanFromLeft}
},
ItemSize -> {
{{OptionValue[ItemSize]/2}},
{{Automatic}, {2 -> OptionValue[Spacer], 3 -> OptionValue[LeaderSize]}}
},
Alignment -> Center,
Spacings -> {0, 0},
BaselinePosition -> 1,
BaseStyle -> "Output"]


The options are as follows:

• ItemSize - controls width of annotation.
• LeaderSize - controls length of leader.
• Spacer - controls length of "bucket" sides of callout.
• CalloutStyle - controls formatting of callout.
• LabelStyle - controls formatting of annotation.

Combined with Interpretation.

ClearAll[annotateSnippet2]
SetAttributes[annotateSnippet2, {HoldFirst}]
annotateSnippet2[snippet_, anno_String, opts : OptionsPattern[calloutSnippet2]] :=
Interpretation[
calloutSnippet2[snippet, anno, opts],
snippet
]


Giving

annotateSnippet2[
RandomVariate[NormalDistribution[], 20],
"Twenty random numbers",
ItemSize -> 22]


and

Hope this helps.

• Nice work. However, I've seen that the length of the blue underline is off in most cases, is there any way you can fix this? – M.R. Dec 10 '18 at 21:17
• @M.R. Yes, I have been thinking about that and I have a very simple solution. Will update later today. – Edmund Dec 10 '18 at 21:20
• Other than making the snippetLength calculation exact, my only addition requests would be to add control for the callout stem and label positions. E.g. recreating the example screenshot would be cool! – M.R. Dec 10 '18 at 21:26
• – M.R. Dec 10 '18 at 21:39
• Would be nice to somehow make the lines the same thickness and connect them too: imgur.com/Fa1tfBk – M.R. Dec 10 '18 at 22:41