git-gui: Add a search command to the blame viewer.
One of the largest deficiencies in the blame viewer at the moment is the impossibility to search for a text string. This commit fixes it by adding a Firefox-like search panel to the viewer. The panel can be shown by pressing F7 or clicking a menu entry, and is hidden by pressing Esc. Find Next is available through the F3 key. Implementation is based on the gitk code, but heavily refactored. It now also supports case-insensitive searches, and uses the text box background color to signal success or failure of the search. Signed-off-by: Alexander Gavrilov <angavrilov@gmail.com> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Alexander Gavrilov committed
Oct 3, 2008 at 11:36 UTC
f10d5b064ae5c9bcd6e353211f128f5c4072ed4f
3 files changed
+227
-3
git-gui.sh
+3
@@ -591,6 +591,7 @@ bind . <Visibility> {
591
592
if {[is_Windows]} {
593
wm iconbitmap . -default $oguilib/git-gui.ico
594
+ set ::tk::AlwaysShowSelection 1
595
}
596
597
######################################################################
@@ -1067,6 +1068,8 @@ set selected_commit_type new
1068
set nullid "0000000000000000000000000000000000000000"
1069
set nullid2 "0000000000000000000000000000000000000001"
1070
1071
+set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
1072
+
1073
######################################################################
1074
##
1075
## task management
lib/blame.tcl
+34
-3
@@ -21,9 +21,11 @@ field w_amov ; # text column: annotations + move tracking
21
field w_asim ; # text column: annotations (simple computation)
22
field w_file ; # text column: actual file data
23
field w_cviewer ; # pane showing commit message
24
+field finder ; # find mini-dialog frame
25
field status ; # status mega-widget instance
26
field old_height ; # last known height of $w.file_pane
27
28
+
29
# Tk UI colors
30
#
31
variable active_color #c0edc5
@@ -59,7 +61,7 @@ field tooltip_timer {} ; # Current timer event for our tooltip
61
field tooltip_commit {} ; # Commit(s) in tooltip
62
63
constructor new {i_commit i_path i_jump} {
62
- global cursor_ptr
64
+ global cursor_ptr M1B M1T have_tk85
65
variable active_color
66
variable group_colors
67
@@ -199,6 +201,11 @@ constructor new {i_commit i_path i_jump} {
201
-width 80 \
202
-xscrollcommand [list $w.file_pane.out.sbx set] \
203
-font font_diff
204
+ if {$have_tk85} {
205
+ $w_file configure -inactiveselectbackground darkblue
206
+ }
207
+ $w_file tag conf found \
208
+ -background yellow
209
210
set w_columns [list $w_amov $w_asim $w_line $w_file]
211
@@ -219,6 +226,11 @@ constructor new {i_commit i_path i_jump} {
226
-weight 1
227
grid rowconfigure $w.file_pane.out 0 -weight 1
228
229
+ set finder [::searchbar::new \
230
+ $w.file_pane.out.ff $w_file \
231
+ -column [expr {[llength $w_columns] - 1}] \
232
+ ]
233
+
234
set w_cviewer $w.file_pane.cm.t
235
text $w_cviewer \
236
-background white \
@@ -259,6 +271,10 @@ constructor new {i_commit i_path i_jump} {
271
-label [mc "Copy Commit"] \
272
-command [cb _copycommit]
273
$w.ctxm add separator
274
+ $w.ctxm add command \
275
+ -label [mc "Find Text..."] \
276
+ -accelerator F7 \
277
+ -command [list searchbar::show $finder]
278
menu $w.ctxm.enc
279
build_encoding_menu $w.ctxm.enc [cb _setencoding]
280
$w.ctxm add cascade \
@@ -280,9 +296,15 @@ constructor new {i_commit i_path i_jump} {
296
$i tag conf color$g -background [lindex $group_colors $g]
297
}
298
299
+ if {$i eq $w_file} {
300
+ $w_file tag raise found
301
+ }
302
+ $i tag raise sel
303
+
304
$i conf -cursor $cursor_ptr
284
- $i conf -yscrollcommand [list many2scrollbar \
285
- $w_columns yview $w.file_pane.out.sby]
305
+ $i conf -yscrollcommand \
306
+ "[list ::searchbar::scrolled $finder]
307
+ [list many2scrollbar $w_columns yview $w.file_pane.out.sby]"
308
bind $i <Button-1> "
309
[cb _hide_tooltip]
310
[cb _click $i @%x,%y]
@@ -319,6 +341,11 @@ constructor new {i_commit i_path i_jump} {
341
bind $w_cviewer <Tab> "[list focus $w_file];break"
342
bind $w_cviewer <Button-1> [list focus $w_cviewer]
343
bind $w_file <Visibility> [list focus $w_file]
344
+ bind $top <F7> [list searchbar::show $finder]
345
+ bind $top <Escape> [list searchbar::hide $finder]
346
+ bind $top <F3> [list searchbar::find_next $finder]
347
+ bind $top <Shift-F3> [list searchbar::find_prev $finder]
348
+ catch { bind $top <Shift-Key-XF86_Switch_VT_3> [list searchbar::find_prev $finder] }
349
350
grid configure $w.header -sticky ew
351
grid configure $w.file_pane -sticky nsew
@@ -873,6 +900,10 @@ method _showcommit {cur_w lno} {
900
foreach i $w_columns {
901
$i tag conf g$cmit -background $active_color
902
$i tag raise g$cmit
903
+ if {$i eq $w_file} {
904
+ $w_file tag raise found
905
+ }
906
+ $i tag raise sel
907
}
908
909
set author_name {}
lib/search.tcl
new
+190
@@ -0,0 +1,190 @@
1
+# incremental search panel
2
+# based on code from gitk, Copyright (C) Paul Mackerras
3
+
4
+class searchbar {
5
+
6
+field w
7
+field ctext
8
+
9
+field searchstring {}
10
+field casesensitive 1
11
+field searchdirn -forwards
12
+
13
+field smarktop
14
+field smarkbot
15
+
16
+constructor new {i_w i_text args} {
17
+ set w $i_w
18
+ set ctext $i_text
19
+
20
+ frame $w
21
+ label $w.l -text [mc Find:]
22
+ button $w.bn -text [mc Next] -command [cb find_next]
23
+ button $w.bp -text [mc Prev] -command [cb find_prev]
24
+ checkbutton $w.cs -text [mc Case-Sensitive] \
25
+ -variable ${__this}::casesensitive -command [cb _incrsearch]
26
+ entry $w.ent -textvariable ${__this}::searchstring -background lightgreen
27
+ pack $w.l -side left
28
+ pack $w.cs -side right
29
+ pack $w.bp -side right
30
+ pack $w.bn -side right
31
+ pack $w.ent -side left -expand 1 -fill x
32
+
33
+ eval grid conf $w -sticky we $args
34
+ grid remove $w
35
+
36
+ trace add variable searchstring write [cb _incrsearch_cb]
37
+
38
+ bind $w <Destroy> [cb delete_this]
39
+ return $this
40
+}
41
+
42
+method show {} {
43
+ if {![winfo ismapped $w]} {
44
+ grid $w
45
+ }
46
+ focus -force $w.ent
47
+}
48
+
49
+method hide {} {
50
+ if {[winfo ismapped $w]} {
51
+ focus $ctext
52
+ grid remove $w
53
+ }
54
+}
55
+
56
+method _get_new_anchor {} {
57
+ # use start of selection if it is visible,
58
+ # or the bounds of the visible area
59
+ set top [$ctext index @0,0]
60
+ set bottom [$ctext index @0,[winfo height $ctext]]
61
+ set sel [$ctext tag ranges sel]
62
+ if {$sel ne {}} {
63
+ set spos [lindex $sel 0]
64
+ if {[lindex $spos 0] >= [lindex $top 0] &&
65
+ [lindex $spos 0] <= [lindex $bottom 0]} {
66
+ return $spos
67
+ }
68
+ }
69
+ if {$searchdirn eq "-forwards"} {
70
+ return $top
71
+ } else {
72
+ return $bottom
73
+ }
74
+}
75
+
76
+method _get_wrap_anchor {dir} {
77
+ if {$dir eq "-forwards"} {
78
+ return 1.0
79
+ } else {
80
+ return end
81
+ }
82
+}
83
+
84
+method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} {
85
+ set cmd [list $ctext search]
86
+ if {$mlenvar ne {}} {
87
+ upvar $mlenvar mlen
88
+ lappend cmd -count mlen
89
+ }
90
+ if {!$casesensitive} {
91
+ lappend cmd -nocase
92
+ }
93
+ if {$dir eq {}} {
94
+ set dir $searchdirn
95
+ }
96
+ lappend cmd $dir -- $searchstring
97
+ if {$endbound ne {}} {
98
+ set here [eval $cmd [list $start] [list $endbound]]
99
+ } else {
100
+ set here [eval $cmd [list $start]]
101
+ if {$here eq {}} {
102
+ set here [eval $cmd [_get_wrap_anchor $this $dir]]
103
+ }
104
+ }
105
+ return $here
106
+}
107
+
108
+method _incrsearch_cb {name ix op} {
109
+ after idle [cb _incrsearch]
110
+}
111
+
112
+method _incrsearch {} {
113
+ $ctext tag remove found 1.0 end
114
+ if {[catch {$ctext index anchor}]} {
115
+ $ctext mark set anchor [_get_new_anchor $this]
116
+ }
117
+ if {$searchstring ne {}} {
118
+ set here [_do_search $this anchor mlen]
119
+ if {$here ne {}} {
120
+ $ctext see $here
121
+ $ctext tag remove sel 1.0 end
122
+ $ctext tag add sel $here "$here + $mlen c"
123
+ $w.ent configure -background lightgreen
124
+ _set_marks $this 1
125
+ } else {
126
+ $w.ent configure -background lightpink
127
+ }
128
+ }
129
+}
130
+
131
+method find_prev {} {
132
+ find_next $this -backwards
133
+}
134
+
135
+method find_next {{dir -forwards}} {
136
+ focus $w.ent
137
+ $w.ent icursor end
138
+ set searchdirn $dir
139
+ $ctext mark unset anchor
140
+ if {$searchstring ne {}} {
141
+ set start [_get_new_anchor $this]
142
+ if {$dir eq "-forwards"} {
143
+ set start "$start + 1c"
144
+ }
145
+ set match [_do_search $this $start mlen]
146
+ $ctext tag remove sel 1.0 end
147
+ if {$match ne {}} {
148
+ $ctext see $match
149
+ $ctext tag add sel $match "$match + $mlen c"
150
+ }
151
+ }
152
+}
153
+
154
+method _mark_range {first last} {
155
+ set mend $first.0
156
+ while {1} {
157
+ set match [_do_search $this $mend mlen -forwards $last.end]
158
+ if {$match eq {}} break
159
+ set mend "$match + $mlen c"
160
+ $ctext tag add found $match $mend
161
+ }
162
+}
163
+
164
+method _set_marks {doall} {
165
+ set topline [lindex [split [$ctext index @0,0] .] 0]
166
+ set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
167
+ if {$doall || $botline < $smarktop || $topline > $smarkbot} {
168
+ # no overlap with previous
169
+ _mark_range $this $topline $botline
170
+ set smarktop $topline
171
+ set smarkbot $botline
172
+ } else {
173
+ if {$topline < $smarktop} {
174
+ _mark_range $this $topline [expr {$smarktop-1}]
175
+ set smarktop $topline
176
+ }
177
+ if {$botline > $smarkbot} {
178
+ _mark_range $this [expr {$smarkbot+1}] $botline
179
+ set smarkbot $botline
180
+ }
181
+ }
182
+}
183
+
184
+method scrolled {} {
185
+ if {$searchstring ne {}} {
186
+ after idle [cb _set_marks 0]
187
+ }
188
+}
189
+
190
+}
\ No newline at end of file