Visual REGEXP , by Laurent Riesterer, is a program that illustrates the mechanics of regular expressions, quickly build efficient regular expressions, etc. Currently at
My raving was deleted, but I reiterate it: never mind similar tools: this app can be improved, but it can't be beaten.
RLH: I use this app all the time. I have tweaked it (wider buttons and a quit button) but other than that I really like it.
aspect: as of 2015-02-26 I have made some tiny tweaks for this so I don't have to be this guy . They are hiding behind the discussion tag below, as the 16k-char base64 encoded image on one line made fossil treat the file as binary. Thanks to LW and RLH for encouraging me to actually post this:
--- visual_regexp-3.1.tcl 2006-03-08 12:21:56.000000000 +1100 +++ visual_regexp-3.2.tcl 2015-11-04 09:19:31.019661182 +1100 @@ -1,7 +1,20 @@ +# +# Changelog since 3.1: +# - remove stale manual undo code (handled by text widgets) +# - collapse delete/insert into a single replace to leave undo clean +# - get rid of extra newline from [.t get 1.0 end] +# - change select buttons in replace mode (only) to insert \\$x in replace.text +# - tag/colour inserted \\$x in replace.text +# - default to replace mode +# - bump version number to 3.2. filename said 3.1; source said 3.0 +# +# original was downloaded from http://laurent.riesterer.free.fr/regexp/visual_regexp-3.1.tcl +# +package require Tk package require starkit starkit::startup -set version 3.0 +set version 3.2 ############################################################################################### # @@ -68,7 +81,7 @@ # show/hide history windows on startup set history 0 # mode to use on startup (select/concat = raw, select/insert new lines = nl, replace = replace) -set mode nl +set mode replace # database of some regexp to appear in the "Insert regexp" menu set regexp_db { "URL" {(?:^|")(http|ftp|mailto):(?://)?(\w+(?:[\.:@]\w+)*?)(?:/|@)([^"\?]*?)(?:\?([^\?"]*?))?(?:$|")} @@ -88,8 +101,6 @@ #---------------------------------------------------------------------------------------------- namespace eval regexp {} { - set data(v:undo:index) 0 - set data(v:undo:sample) "" set data(v:dir) "." set data(v:file) "untitled.txt" @@ -172,13 +183,11 @@ } } - if {$tcl_platform(platform) == "windows"} { - set sfont {Courier 8}; - set sbfont {Courier 8 bold}; - } else { - set sfont 6x13; - set sbfont 6x13bold; - } + set sfont TkFixedFont + set sbfont TkFixedFontBold + set fa [font actual $sfont] + dict set da -weight bold + font create $sbfont {*}$fa set data(w:help) [text .top.regexp.help \ -font $sfont \ @@ -191,7 +200,7 @@ .top.regexp.help insert 1.0 "\n\n\n\n\n\n\n\n"; .top.regexp.help insert 1.0 {\a alert \n newline \0 char 0 \d [[:digit:]] \A beginning of the string }; .top.regexp.help insert 2.0 {\b backspace \r carriage \xyz octal code \D [^[:digit:]] \Z end of string }; - .top.regexp.help insert 3.0 {\B synomyn for \ \t tab \s [[:space:]] \m beginning of a word}; + .top.regexp.help insert 3.0 {\B synonym for \ \t tab \s [[:space:]] \m beginning of a word}; .top.regexp.help insert 4.0 {\cX same as X & 0x1F \uwxyz unicode \x backref \S [^[:space:]] \M end of a word}; .top.regexp.help insert 5.0 {\e ESC \v vert tab \w [[:alnum:]_] \y beginning or end of a word}; .top.regexp.help insert 6.0 {\f form feed \xhhh hexa code \W [^[:alnum:]_] \Y not beginning or end of a word}; @@ -356,6 +365,7 @@ foreach level $data(v:levels) color $colors { $data(w:regexp) tag configure $level -foreground $color; + $data(w:replace) tag configure $level -foreground $color; $data(w:history) tag configure $level -foreground $color; $data(w:sample) tag configure $level -foreground $color; } @@ -693,6 +703,10 @@ proc regexp::select {level} { variable data + if {$data(v:mode) eq "replace"} { + $data(w:replace) insert insert \\\\$level e$level + return + } # update go if {[llength $data(v:result)] == 0} { @@ -722,8 +736,7 @@ } incr i } - $data(w:sample) delete 1.0 end - $data(w:sample) insert 1.0 $newsample + $data(w:sample) replace 1.0 end $newsample # update with regexp go } @@ -809,48 +822,6 @@ } #---------------------------------------------------------------------------------------------- -# Undo/redo (quick and dirty UNDO/REDO support) -#---------------------------------------------------------------------------------------------- - -proc regexp::undo:sample {} { -variable data - - # display result - $data(w:sample) delete 1.0 end - $data(w:sample) insert 1.0 $data(v:undo:sample) - # colorize - go -} - -proc regexp::unredo:regexp {dir} { -variable data - - set index [expr ($data(v:undo:index)+$dir) % 100] - if {![info exists data(v:undo:r$index)]} { - return - } - set data(v:undo:index) $index - - set t $data(w:regexp) - $t delete 1.0 end - $t insert 1.0 [lindex $data(v:undo:r$index) 1] - $t mark set insert [lindex $data(v:undo:r$index) 0] -} - -proc regexp::undo:regexp:compute {w k a} { -variable data - - if {[string match -nocase "*control*" $k] - || [string match -nocase "*shift*" $k] - || [string match -nocase "*alt*" $k]} { - return - } - - set data(v:undo:r$data(v:undo:index)) [list [$w index insert] [$w get 1.0 end-1char]] - set data(v:undo:index) [expr ($data(v:undo:index)+1) % 100] -} - -#---------------------------------------------------------------------------------------------- # Replace #---------------------------------------------------------------------------------------------- @@ -864,23 +835,24 @@ return } - # get sample & store it for undo set sample [$data(w:sample) get 1.0 end] - set data(v:undo:sample) $sample - set result [eval regsub $data(v:all) \ - $data(v:line) $data(v:lineanchor) $data(v:linestop) \ - $data(v:nocase) -- \ - [list $exp] [list $sample] [list [subst -nocommands -novariables $subst]] sample] + regsub {(.*)\n} $sample {\1} sample + set subst [subst -nocommands -novariables $subst] + + set flags [list {*}$data(v:all) {*}$data(v:line) {*}$data(v:lineanchor) {*}$data(v:linestop) {*}$data(v:nocase)] + set result [regsub {*}$flags -- $exp $sample $subst sample] + set regexp::data(v:nbreplace) "$result replaced" # display result - $data(w:sample) delete 1.0 end - $data(w:sample) insert 1.0 $sample + #$data(w:sample) edit separator + $data(w:sample) replace 1.0 end $sample + #$data(w:sample) edit separator } proc regexp::replace:toggle {} { variable data - if {$regexp::data(v:mode) == "replace"} { + if {$data(v:mode) == "replace"} { bind $data(w:regexp) <Tab> "focus $data(w:replace); break;" bind $data(w:regexp) <Shift-Tab> "focus $data(w:sample); break;" catch { bind $data(w:regexp) <ISO_Left_Tab> "focus $data(w:sample); break;" } @@ -918,8 +890,7 @@ proc regexp::regexp:set {text} { variable data - $data(w:regexp) delete 1.0 end - $data(w:regexp) insert 1.0 $text + $data(w:regexp) replace 1.0 end $text } proc regexp::regexp:colorize {} { @@ -1138,8 +1109,7 @@ proc regexp::sample:set {text} { variable data - $data(w:sample) delete 1.0 end - $data(w:sample) insert 1.0 $text + $data(w:sample) replace 1.0 end $text set data(v:undo:sample) $text } @@ -1471,8 +1441,7 @@ variable data set words [$data(w:make:list) get 1.0 end-1c] - $data(w:make:output) delete 1.0 end - $data(w:make:output) insert 1.0 [make-regexp::make-regexp $words] + $data(w:make:output) replace 1.0 end [make-regexp::make-regexp $words] } proc regexp::make-regexp:ok {w} {