Bryan Oakley writes:
This is something I've kept in my back pocket for a while. It hasn't gone through rigorous testing but seems to work well enough for my needs.
Usage:
forText pathName ?-elide? ?-regexp? ?-nocase? ?-exact? pattern start end script
The
-elide,
-regexp,
-nocase and
-exact options are passed directly to the
text widget's search command.
This command will loop over all of the text in the widget specified by
pathName. Each time a range of characters matches
pattern the marks
matchStart and
matchEnd will be set and the specified
script will be run.
Example:
# this example adds the tag 'highlight' to all occurrences
# of text inside <>
pack [text .t] -side top -fill both -expand y
.t tag configure highlight -foreground red
<insert text into widget>
forText .t -regexp {***:<.*?>} 1.0 end {
.t tag add highlight matchStart matchEnd
}
proc forText {w args} {
# initialize search command; we may add to it, depending on the
# arguments passed in...
set searchCommand [list $w search -count count]
# Poor man's switch detection
set i 0
while {[string match {-*} [set arg [lindex $args $i]]]} {
if {[string match $arg* -regexp]} {
lappend searchCommand -regexp
incr i
} elseif {[string match $arg* -elide]} {
lappend searchCommand -elide
incr i
} elseif {[string match $arg* -nocase]} {
lappend searchCommand -nocase
incr i
} elseif {[string match $arg* -exact]} {
lappend searchCommand -exact
incr i
} elseif {[string compare $arg --] == 0} {
incr i
break
} else {
return -code error "bad switch \"$arg\": must be\
--, -elide, -exact, -nocase or -regexp"
}
}
# parse remaining arguments, and finish building search command
foreach {pattern start end script} [lrange $args $i end] {break}
lappend searchCommand $pattern matchEnd searchLimit
# make sure these are of the canonical form
set start [$w index $start]
set end [$w index $end]
# place marks in the text to keep track of where we've been
# and where we're going
$w mark set matchStart $start
$w mark set matchEnd $start
$w mark set searchLimit $end
# default gravity is right, but we're setting it here just to
# be pedantic. It's critical that matchStart and matchEnd have
# left and right gravity, respectively, so that any text inserted
# by the caller duing the search won't normally (*) cause an infinite
# loop.
# (*) If the script inserts text after the matchEnd mark, and the
# text that was added matches the pattern, madness will ensue.
$w mark gravity searchLimit right
$w mark gravity matchStart left
$w mark gravity matchEnd right
# finally, the part that does useful work. Keep running the search
# command until we don't find anything else. Each time we find
# something, adjust the marks and execute the script
while {1} {
set cmd $searchCommand
set index [eval $searchCommand]
if {[string length $index] == 0} break
$w mark set matchStart $index
$w mark set matchEnd [$w index "$index + $count c"]
uplevel $script
}
}