summaryrefslogtreecommitdiffstats
path: root/git-gui/lib/search.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'git-gui/lib/search.tcl')
-rw-r--r--git-gui/lib/search.tcl300
1 files changed, 300 insertions, 0 deletions
diff --git a/git-gui/lib/search.tcl b/git-gui/lib/search.tcl
new file mode 100644
index 0000000..ef1e555
--- /dev/null
+++ b/git-gui/lib/search.tcl
@@ -0,0 +1,300 @@
+# incremental search panel
+# based on code from gitk, Copyright (C) Paul Mackerras
+
+class searchbar {
+
+field w
+field ctext
+
+field searchstring {}
+field regexpsearch
+field default_regexpsearch
+field casesensitive
+field default_casesensitive
+field smartcase
+field searchdirn -forwards
+
+field history
+field history_index
+
+field smarktop
+field smarkbot
+
+constructor new {i_w i_text args} {
+ global use_ttk NS
+ set w $i_w
+ set ctext $i_text
+
+ set default_regexpsearch [is_config_true gui.search.regexp]
+ switch -- [get_config gui.search.case] {
+ no {
+ set default_casesensitive 0
+ set smartcase 0
+ }
+ smart {
+ set default_casesensitive 0
+ set smartcase 1
+ }
+ yes -
+ default {
+ set default_casesensitive 1
+ set smartcase 0
+ }
+ }
+
+ set history [list]
+
+ ${NS}::frame $w
+ ${NS}::label $w.l -text [mc Find:]
+ tentry $w.ent -textvariable ${__this}::searchstring -background lightgreen
+ ${NS}::button $w.bn -text [mc Next] -command [cb find_next]
+ ${NS}::button $w.bp -text [mc Prev] -command [cb find_prev]
+ ${NS}::checkbutton $w.re -text [mc RegExp] \
+ -variable ${__this}::regexpsearch -command [cb _incrsearch]
+ ${NS}::checkbutton $w.cs -text [mc Case] \
+ -variable ${__this}::casesensitive -command [cb _incrsearch]
+ pack $w.l -side left
+ pack $w.cs -side right
+ pack $w.re -side right
+ pack $w.bp -side right
+ pack $w.bn -side right
+ pack $w.ent -side left -expand 1 -fill x
+
+ eval grid conf $w -sticky we $args
+ grid remove $w
+
+ trace add variable searchstring write [cb _incrsearch_cb]
+ bind $w.ent <Return> [cb find_next]
+ bind $w.ent <Shift-Return> [cb find_prev]
+ bind $w.ent <Key-Up> [cb _prev_search]
+ bind $w.ent <Key-Down> [cb _next_search]
+
+ bind $w <Destroy> [list delete_this $this]
+ return $this
+}
+
+method show {} {
+ if {![visible $this]} {
+ grid $w
+ $w.ent delete 0 end
+ set regexpsearch $default_regexpsearch
+ set casesensitive $default_casesensitive
+ set history_index [llength $history]
+ }
+ focus -force $w.ent
+}
+
+method hide {} {
+ if {[visible $this]} {
+ focus $ctext
+ grid remove $w
+ _save_search $this
+ }
+}
+
+method visible {} {
+ return [winfo ismapped $w]
+}
+
+method editor {} {
+ return $w.ent
+}
+
+method _get_new_anchor {} {
+ # use start of selection if it is visible,
+ # or the bounds of the visible area
+ set top [$ctext index @0,0]
+ set bottom [$ctext index @0,[winfo height $ctext]]
+ set sel [$ctext tag ranges sel]
+ if {$sel ne {}} {
+ set spos [lindex $sel 0]
+ if {[lindex $spos 0] >= [lindex $top 0] &&
+ [lindex $spos 0] <= [lindex $bottom 0]} {
+ return $spos
+ }
+ }
+ if {$searchdirn eq "-forwards"} {
+ return $top
+ } else {
+ return $bottom
+ }
+}
+
+method _get_wrap_anchor {dir} {
+ if {$dir eq "-forwards"} {
+ return 1.0
+ } else {
+ return end
+ }
+}
+
+method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} {
+ set cmd [list $ctext search]
+ if {$mlenvar ne {}} {
+ upvar $mlenvar mlen
+ lappend cmd -count mlen
+ }
+ if {$regexpsearch} {
+ lappend cmd -regexp
+ }
+ if {!$casesensitive} {
+ lappend cmd -nocase
+ }
+ if {$dir eq {}} {
+ set dir $searchdirn
+ }
+ lappend cmd $dir -- $searchstring
+ if {[catch {
+ if {$endbound ne {}} {
+ set here [eval $cmd [list $start] [list $endbound]]
+ } else {
+ set here [eval $cmd [list $start]]
+ if {$here eq {}} {
+ set here [eval $cmd [_get_wrap_anchor $this $dir]]
+ }
+ }
+ } err]} { set here {} }
+ return $here
+}
+
+method _incrsearch_cb {name ix op} {
+ after idle [cb _incrsearch]
+}
+
+method _incrsearch {} {
+ $ctext tag remove found 1.0 end
+ if {[catch {$ctext index anchor}]} {
+ $ctext mark set anchor [_get_new_anchor $this]
+ }
+ if {$searchstring ne {}} {
+ if {$smartcase && [regexp {[[:upper:]]} $searchstring]} {
+ set casesensitive 1
+ }
+ set here [_do_search $this anchor mlen]
+ if {$here ne {}} {
+ $ctext see $here
+ $ctext tag remove sel 1.0 end
+ $ctext tag add sel $here "$here + $mlen c"
+ #$w.ent configure -background lightgreen
+ $w.ent state !pressed
+ _set_marks $this 1
+ } else {
+ #$w.ent configure -background lightpink
+ $w.ent state pressed
+ }
+ } elseif {$smartcase} {
+ # clearing the field resets the smart case detection
+ set casesensitive 0
+ }
+}
+
+method _save_search {} {
+ if {$searchstring eq {}} {
+ return
+ }
+ if {[llength $history] > 0} {
+ foreach {s_regexp s_case s_expr} [lindex $history end] break
+ } else {
+ set s_regexp $regexpsearch
+ set s_case $casesensitive
+ set s_expr ""
+ }
+ if {$searchstring eq $s_expr} {
+ # update modes
+ set history [lreplace $history end end \
+ [list $regexpsearch $casesensitive $searchstring]]
+ } else {
+ lappend history [list $regexpsearch $casesensitive $searchstring]
+ }
+ set history_index [llength $history]
+}
+
+method _prev_search {} {
+ if {$history_index > 0} {
+ incr history_index -1
+ foreach {s_regexp s_case s_expr} [lindex $history $history_index] break
+ $w.ent delete 0 end
+ $w.ent insert 0 $s_expr
+ set regexpsearch $s_regexp
+ set casesensitive $s_case
+ }
+}
+
+method _next_search {} {
+ if {$history_index < [llength $history]} {
+ incr history_index
+ }
+ if {$history_index < [llength $history]} {
+ foreach {s_regexp s_case s_expr} [lindex $history $history_index] break
+ } else {
+ set s_regexp $default_regexpsearch
+ set s_case $default_casesensitive
+ set s_expr ""
+ }
+ $w.ent delete 0 end
+ $w.ent insert 0 $s_expr
+ set regexpsearch $s_regexp
+ set casesensitive $s_case
+}
+
+method find_prev {} {
+ find_next $this -backwards
+}
+
+method find_next {{dir -forwards}} {
+ focus $w.ent
+ $w.ent icursor end
+ set searchdirn $dir
+ $ctext mark unset anchor
+ if {$searchstring ne {}} {
+ _save_search $this
+ set start [_get_new_anchor $this]
+ if {$dir eq "-forwards"} {
+ set start "$start + 1c"
+ }
+ set match [_do_search $this $start mlen]
+ $ctext tag remove sel 1.0 end
+ if {$match ne {}} {
+ $ctext see $match
+ $ctext tag add sel $match "$match + $mlen c"
+ }
+ }
+}
+
+method _mark_range {first last} {
+ set mend $first.0
+ while {1} {
+ set match [_do_search $this $mend mlen -forwards $last.end]
+ if {$match eq {}} break
+ set mend "$match + $mlen c"
+ $ctext tag add found $match $mend
+ }
+}
+
+method _set_marks {doall} {
+ set topline [lindex [split [$ctext index @0,0] .] 0]
+ set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
+ if {$doall || $botline < $smarktop || $topline > $smarkbot} {
+ # no overlap with previous
+ _mark_range $this $topline $botline
+ set smarktop $topline
+ set smarkbot $botline
+ } else {
+ if {$topline < $smarktop} {
+ _mark_range $this $topline [expr {$smarktop-1}]
+ set smarktop $topline
+ }
+ if {$botline > $smarkbot} {
+ _mark_range $this [expr {$smarkbot+1}] $botline
+ set smarkbot $botline
+ }
+ }
+}
+
+method scrolled {} {
+ if {$searchstring ne {}} {
+ after idle [cb _set_marks 0]
+ }
+}
+
+}