Thursday, June 03, 2010

UltraEdit macro to select HTML/XML tag

Editing HTML or XML in UltraEdit is now just a little bit easier for me with this macro that incrementally selects HTML/XML elements.

Thanks to Bulgrien in my UltraEdit forum post “macro to select HTML tag” the base case macro to select tag start to finish is very simple. Consider the text below. I wanted a macro to select the DIV and its contents (including the DIV tag itself).

<DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>

Here is a very simple macro that will do that.

InsertMode
ColumnModeOff
HexOff
Find Select "</^s>"
Find Up Select "<"

The ^s means “selected text”. To use this macro on the example text, double click the first DIV token and run the macro. Unfortunately, this macro is not so useful if the element contains nested elements with the same tag name, such as in the example below. If you double click on the first DIV token in the text below and run the macro, it will select only to the first DIV close (as the coloured text shows).

<DIV>
   <DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>
</DIV>

AFAIK UltraEdit’s macro language doesn’t have the logic structures to repeat through nested tags (recursion or looping and arithmetic) - let alone deal with broken nested tags. I could write a Javascript to do this, but I don’t want to invest the time to get it right. So my compromise is to put the decision logic in the hands of a human operator. (Probably for the best; wouldn’t want SkyNet) to start with a run-away UltraEdit macro.)

Here is the new and improved macro.

InsertMode
ColumnModeOff
HexOff
UltraEditReOn
IfSel
Find RegExp Select "</++^c>++"
Else
SelectWord
Copy
Find Select "</^c>"
Find Up Select "<"
EndIf

To use it, put the cursor in the first DIV (don’t select it or copy it) and run the macro. It will still select only to the first close tag, but each time you re-run the macro (control+m) it will expand the selection to the next tag opening or closing. This way, you can count for yourself how many nested tags there are and stop when you need to.

First run:

<DIV>
   <DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>
   <DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>
</DIV>

Second run (control+m):

<DIV>
   <DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>
   <DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>
</DIV>

Third run (control+m):

<DIV>
     <DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>
     <DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>
</DIV>

Fourth run (control+m):

<DIV>
   <DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>
   <DIV style="...">abc <SPAN>def</SPAN> ghi.</DIV>
</DIV>

And to do the same thing the other way, use this one.

InsertMode
ColumnModeOff
HexOff
UltraEditReOn
IfSel
Find RegExp Up Select "</++^c>++"
Else
SelectWord
Copy
Find Up Select "</^c>"
Find Select ">"
EndIf

Personally, I have the select forward macro mapped to Control+Shift+. and the select backwards macro mapped to Control+Shift+,

Notes.

  • A very simple Perl regex will do almost the same thing as Bulgrien’s macro above: <DIV[^>]*>([^ΓΏ]*?)</DIV>. Unfortunately you can’t use ^s in regular expressions though.