# Formatting text through pattern matching

I am trying to format some text based on some patterns.

Clear[highlight];
SetAttributes[highlight, HoldAll];
highlight[pattern_, style_] :=
s_String :> Row[List @@ StringReplace[s, t : pattern :> style[t]]]


This works fine and dandy if a match is found:

"foo bar baz" /. highlight["foo", Style[#, Red] &]


But doesn't if no match is found:

"fu bar baz" /. highlight["foo", Style[#, Red] &]


Row["fu bar baz"]

The failure to match has left us with an ugly Row. I'm having a bit of trouble fixing this case, perhaps you can help?

-

Here is what I'd do:

Clear[highlight];
SetAttributes[highlight, HoldAll];
highlight[pattern_, style_] :=
s_String :>
With[{replaced = StringReplace[s, t : pattern :> style[t]]},
Row[List @@ replaced] /; replaced =!= s]


What happens here is that I use the variable replaced shared between the body of With and the condition. This is a very handy construct in many cases. It has the effect that while we do some computations in the body, at the end the rule may be considered not matched by the pattern-matcher. This is often convenient in cases when the fact of the match requires some computations, to be established.

This form has also a global version, which has many applications as well, inlcuding the Trott-Strzebonski in-place evaluation technique, discussed e.g. here.

-

Update: I believe the best way is to use Apply, exploiting the behavior that f @@ x yields x when x is atomic (like a String). Therefore with the code below a raw string remains a string, whereas a StringExpression is converted to a Row.

Clear[highlight];
SetAttributes[highlight, HoldAll];

highlight[pattern_, style_] :=
s_String :> (Row[{##}] & @@ StringReplace[s, t : pattern :> style[t]])


Test:

{"foo bar baz", "fu bar baz"} /.
highlight["foo", Style[#, Red] &]


-