5
$\begingroup$

I'm trying to generalize some very nice code posted by Mr. Wizard (Notebook formatting - easier descriptions for equations and results?); this code allows one to follow a MMA command with text, on a single line, with minimal added syntax (while not having any effect on the ability of the output of the MMA command to be evaluated). Here's Mr. Wizard's original code, and a sample input and output:

$note = Null;

$PreRead =
Replace[#,
RowBox[{body__, ";", note_String?(StringMatchQ[#, "\"*\""] &)}] :>
 ($note = Style[ToExpression@note, Italic, Red]; RowBox[{body}])
] &;

$PrePrint = 
If[$note =!= Null,
 # &[Row[{Pane@#, Spacer[50], $note}], $note = Null],
 #
] &;

Integrate[x^2, x]; "Text after."

$\frac{x^3}{3} \>\>\>\>\>\>\>\>\>\> \color{red}{\textit {Text after.}}$

I'd like to generalize Mr. Wizard's code to allow one to insert text either before, or after, or both, to allow, in addition to the above:

"Text before."; Integrate[x^2, x]

$\color{red}{\textit {Text before.}} \>\>\>\>\>\>\>\>\>\> \frac{x^3}{3}$

and

"Text before."; Integrate[x^2, x]; "Text after."

$\color{red}{\textit {Text before.}} \>\>\>\>\>\>\>\>\>\> \frac{x^3}{3} \>\>\>\>\>\>\>\>\>\> \color{red}{\textit {Text after.}}$

In attempting to modify the code, I've been able to get it to work for each of the above conditions individually, but not to handle all three together. I suspect the main problem is my \$PreRead statement, which may need a set of Which[ ] conditions to allow it to recognize all three patterns; but I haven't been able to figure out how to write such a pattern-matcher within a \$PreRead. Here's an example of what I have thus far, which works only if the input has text both before and after the MMA command, or no text at all:

$note1 = Null;
$note2 = Null;

$PreRead = 
Replace[#, 
RowBox[{note1_String?(StringMatchQ[#, "\"*\""] &), ";", body__, 
   ";"  , note2_String?(StringMatchQ[#, "\"*\""] &)}] :> ($note1 =
    Style[ToExpression@note1, Italic, Red]; $note2 = 
   Style[ToExpression@note2, Italic, Red]; 
  RowBox[{body}])] &;

$PrePrint = Which[
($note1 =!= Null && $note2 =!= Null), # &[
 Row[{$note1, Spacer[50], Pane@#, Spacer[50], $note2}], $note1 = 
  Null, $note2 = Null],
($note1 =!= Null && $note2 === Null), # &[
 Row[{$note1, Spacer[50], Pane@#}], $note1 = Null],
($note1 === Null && $note2 =!= Null), # &[
 Row[{Pane@#, Spacer[50], $note2}], $note2 = Null],
True, #
] &;

The above only works for this:

"Text before."; Integrate[x^2, x]; "Text after."

$\color{red}{\textit {Text before.}} \>\>\>\>\>\>\>\>\>\> \frac{x^3}{3} \>\>\>\>\>\>\>\>\>\> \color{red}{\textit {Text after.}}$

...and this:

Integrate[x^2, x]

$\frac{x^3}{3}$


UPDATE: For the convenience of the reader, here is the corrected code, which incorporates the answer provided by MB1965. This generalizes Mr.Wizard's code, allowing inline text to be easily inserted before MMA code, after MMA code, both before and after, or by itself (the latter maintains consistent text output formatting when you want to enter just text into a cell). Syntax for text+math is as shown above. For text by itself, just use "Text";

$note1 = Null;
$note2 = Null;
$note3 = Null;
$PreRead = 
Replace[#, {RowBox[{note1_String?(StringMatchQ[#, "\"*\""] &), ";", 
    body__, ";", 
    note2_String?(StringMatchQ[#, "\"*\""] &)}] :> ($note1 = 
    Style[ToExpression@note1, Italic, Red]; $note2 = 
    Style[ToExpression@note2, Italic, Red];
   RowBox[{body}]),
 RowBox[{body__, ";", 
    note_String?(StringMatchQ[#, "\"*\""] &)}] :> ($note2 = 
    Style[ToExpression@note, Italic, Red];
   $note1 = Null;
   RowBox[{body}]),
 RowBox[{note_String?(StringMatchQ[#, "\"*\""] &), ";", 
    body__}] :> ($note1 = Style[ToExpression@note, Italic, Red];
   $note2 = Null;
   RowBox[{body}]),
 RowBox[{note_String?(StringMatchQ[#, "\"*\""] &), 
    ";"}] :> ($note3 = Style[ToExpression@note, Italic, Red];
   $note2 = Null; $note1 = Null;
   RowBox[{note}]),
 e_ :> ($note1 = Null; $note2 = Null; e)}] &;
$PrePrint = Which[($note1 == Null && $note2 == Null && $note3 =!= 
   Null), # &[$note3, $note3 = Null],
($note1 =!= Null && $note2 =!= Null && $note3 == Null), # &[
 Row[{$note1, Spacer[50], Pane@#, Spacer[50], $note2}], $note1 = 
  Null, $note2 = Null],
($note1 =!= Null && $note2 =!= Null && $note3 == Null), # &[
 Row[{$note1, Spacer[50], Pane@#, Spacer[50], $note2}], $note1 = 
  Null, $note2 = Null],
($note1 =!= Null && $note2 === Null && $note3 == Null), # &[
 Row[{$note1, Spacer[50], Pane@#}], $note1 = Null],
($note1 === Null && $note2 =!= Null && $note3 == Null), # &[
 Row[{Pane@#, Spacer[50], $note2}], $note2 = Null], True, #] &;
$\endgroup$
5
$\begingroup$

Replace can take a list of replacements to try as its second argument. Simply catenate all your patterns in a list and use Replace with that. i.e.,

$PreRead = Replace[#, {
     RowBox[{note1_String?(StringMatchQ[#, "\"*\""] &), ";", body__, 
        ";", note2_String?(StringMatchQ[#, "\"*\""] &)}] :> ($note1 = 
        Style[ToExpression@note1, Italic, Red]; $note2 = 
        Style[ToExpression@note2, Italic, Red];
       RowBox[{body}]),
     RowBox[{body__, ";", 
        note_String?(StringMatchQ[#, "\"*\""] &)}] :> ($note2 = 
        Style[ToExpression@note, Italic, Red];
       $note1 = Null;
       RowBox[{body}]), 
     RowBox[{note_String?(StringMatchQ[#, "\"*\""] &), ";", 
        body__}] :> ($note1 = Style[ToExpression@note, Italic, Red];
       $note2 = Null;
       RowBox[{body}]), e_ :> ($note1 = Null; $note2 = Null; e)}] &;

Note that I also put in a "doesn't match" pattern at the end to make sure the notes are cleared if not used.

$\endgroup$
  • $\begingroup$ Changing note_String to note_String2 will actually invalidate it. That checks for a String2 expression rather than just a String. Also yeah, I'll change the order in which Replace checks. $\endgroup$ – b3m2a1 Dec 31 '16 at 2:41
  • 1
    $\begingroup$ Updated. Now it checks for both first, then before, then after. An oversight on my part. Also if the answer doesn't actually do what you need it to, consider waiting to accept it. $\endgroup$ – b3m2a1 Dec 31 '16 at 2:43
  • $\begingroup$ Thanks for the correction. I've confirmed this works. [BTW, in my earlier comment, I meant to write note2_String not note_String2.] $\endgroup$ – theorist Dec 31 '16 at 2:50
  • 2
    $\begingroup$ The underscore is a shorthand for Blank[] which matches anything. e is the symbol that captures the matched value. I had to put it at the end because we need to return the thing $PreRead is reading or else it won't do anything. Run this to learn more: Documentation`HelpLookup@"tutorial/PatternsAndTransformationRules" $\endgroup$ – b3m2a1 Dec 31 '16 at 3:02
  • 1
    $\begingroup$ At some lower level it may get used as a regex but at this level, yes, it is a string with a special meaning in string matching functions. To match an explicit "*" you have to escape it. There are various tutorials linked in the docs that can explain all this much better than I, but basically the RegularExpression head simply forces interpretation as a regex but for a few commonly used cases WRI has allowed regex-like matching in plain strings. $\endgroup$ – b3m2a1 Dec 31 '16 at 18:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.