threads / rfc / 25747

RFC patchgit-gui: grep prototype

Subject: [PATCH/RFC/WIP/POC] git-gui: grep prototype

## tl;dr

9 messages between Nov 14, 2010 and Nov 19, 2010. Diffs are folded; open one to read it.

replies: 8people: 2as markdown or json

Bert Wesarg· Nov 14, 2010, 21:41 UTC · lore
This is a prototype gui to show the result from git grep.

It has a similar layout to git gui. That is, left is the list of matched files. And the right side shows the grep result for one file.

The prototype lives completely outside of git gui at the moment. The usage is:
    $ git-gui-grep.tcl [git-grep-match-options]

-p and -C 3 are currently hard coded in the file. And you should also not use any pathspecs after a --. Though, patchspecs without the -- separator may work.

There is one feature that the upstream git gui lacks[1]: This grep gui can start the git editor on every line by clicking with button 2 in the result area. The git editor will than be started with the environment variable FILENAME and LINENUMBER set but no argument given. This makes it possible to distinguish between normal, blocking edit operation (like git commit) from this open-and-forget type of operation.

How to proceed from this prototype isn't perfectly clear for me at the moment. I don't know if we need to enter/edit grep match options? If this should be a new mode for git gui like blame and therefore in its one windows. I can imagine that a tabbed interface would be nice for git gui. That would include having a blame view inside a tab.

So any advice would be very welcome here.
Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com>
---
[1]: I have done this for git-gui first, but it lacks the precise linenumber
     detection used here. Ie. I plan to add these line number columns to the
     diff view.
 git-gui-grep.tcl |  320 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 320 insertions(+), 0 deletions(-)
Show changes to git-gui-grep.tcl +320 −0
diff --git a/git-gui-grep.tcl b/git-gui-grep.tcl
new file mode 100755
index 0000000..fb61745
--- /dev/null
+++ b/git-gui-grep.tcl
@@ -0,0 +1,320 @@
+#!/bin/sh
+# Tcl ignores the next line -*- tcl -*- \
+ argv0=$0; \
+ exec 'wish8.5' "$argv0" -- "$@"
+
+if {[catch {package require Tcl 8.5} err]
+ || [catch {package require Tk  8.5} err]
+} {
+	catch {wm withdraw .}
+	tk_messageBox \
+		-icon error \
+		-type ok \
+		-title "git gui-grep: fatal error" \
+		-message $err
+	exit 1
+}
+
+proc scrollbar2many {list mode args} {
+	foreach w $list {eval $w $mode $args}
+}
+
+proc many2scrollbar {list mode sb top bottom} {
+	$sb set $top $bottom
+	foreach w $list {$w $mode moveto $top}
+}
+
+wm title . "git gui-grep"
+
+ttk::panedwindow .pane -orient horizontal
+ttk::labelframe .pane.files -borderwidth 0 -text "Matched files"
+ttk::labelframe .pane.out -borderwidth 0 -text "Matches"
+.pane add .pane.files
+.pane add .pane.out
+
+pack .pane -expand 1 -fill both
+
+text .pane.out.lnos \
+	-takefocus 0 \
+	-highlightthickness 0 \
+	-padx 0 -pady 0 \
+	-background white \
+	-foreground black \
+	-borderwidth 0 \
+	-width 6 -height 25 \
+	-wrap none \
+	-state disabled
+.pane.out.lnos tag conf linenumber -justify right -rmargin 5
+bind .pane.out.lnos <ButtonRelease-2> {open_from_grep_view .pane.out.lnos %x %y}
+
+text .pane.out.grep \
+	-takefocus 0 \
+	-highlightthickness 0 \
+	-padx 0 -pady 0 \
+	-background white \
+	-foreground black \
+	-borderwidth 0 \
+	-width 80 -height 25 \
+	-wrap none \
+	-xscrollcommand {.pane.out.sx set} \
+	-state disabled
+.pane.out.grep tag conf match -foreground red
+foreach {n c} {0 black 1 red4 2 green4 3 yellow4 4 blue4 5 magenta4 6 cyan4 7 grey60} {
+	.pane.out.grep tag configure clr4$n -background $c
+	.pane.out.grep tag configure clri4$n -foreground $c
+	.pane.out.grep tag configure clr3$n -foreground $c
+	.pane.out.grep tag configure clri3$n -background $c
+}
+bind .pane.out.grep <ButtonRelease-2> {open_from_grep_view .pane.out.grep %x %y}
+
+set outcols [list .pane.out.lnos .pane.out.grep]
+
+ttk::scrollbar .pane.out.sx \
+	-orient h \
+	-command [list .pane.out.grep xview]
+ttk::scrollbar .pane.out.sy \
+	-orient v \
+	-command [list scrollbar2many $outcols yview]
+
+grid .pane.out.lnos .pane.out.grep .pane.out.sy -sticky nsew
+grid conf .pane.out.sx \
+	-column 1 \
+	-sticky we
+grid columnconfigure .pane.out \
+	1 \
+	-weight 1
+grid rowconfigure .pane.out \
+	0 \
+	-weight 1
+
+text .pane.files.list \
+	-background white \
+	-foreground black \
+	-borderwidth 0 \
+	-state disabled \
+	-wrap none \
+	-height 25 \
+	-width 16 \
+	-cursor arrow \
+	-xscrollcommand [list .pane.files.sbx set] \
+	-yscrollcommand [list .pane.files.sby set]
+.pane.files.list tag raise sel
+ttk::scrollbar .pane.files.sbx \
+	-orient h \
+	-command [list .pane.files.list xview]
+ttk::scrollbar .pane.files.sby \
+	-orient v \
+	-command [list .pane.files.list yview]
+pack .pane.files.sbx -side bottom -fill x
+pack .pane.files.sby -side right -fill y
+pack .pane.files.list -side left -expand 1 -fill both
+bind .pane.files.list <Button-1> "select_to_grep %x %y; break"
+
+foreach i $outcols {
+	$i tag raise sel
+
+	$i conf -cursor arrow
+	$i conf -yscrollcommand \
+		"[list many2scrollbar $outcols yview .pane.out.sy]"
+}
+
+proc escape_path {path} {
+	regsub -all {\\} $path "\\\\" path
+	regsub -all "\n" $path "\\n" path
+	return $path
+}
+
+proc parse_color_line {line} {
+	set start 0
+	set result ""
+	set markup [list]
+	set regexp {\033\[((?:\d+;)*\d+)?m}
+	while {[regexp -indices -start $start $regexp $line match code]} {
+		foreach {begin end} $match break
+		append result [string range $line $start [expr {$begin - 1}]]
+		lappend markup [string length $result] \
+		[eval [linsert $code 0 string range $line]]
+		set start [incr end]
+	}
+	append result [string range $line $start end]
+	if {[llength $markup] < 4} {set markup {}}
+	return [list $result $markup]
+}
+
+
+proc scan_grep {} {
+	global buf_rgl grep_patterns
+
+	set buf_rgl {}
+	set cmd [concat [list | git grep -l -z] $grep_patterns]
+	if {[catch {set fd [open $cmd r]} err]} {
+		tk_messageBox \
+			-icon error \
+			-type ok \
+			-title {git gui-grep: fatal error} \
+			-message $err
+		exit 1
+	}
+	fconfigure $fd -eofchar {}
+	fconfigure $fd \
+		-blocking 0 \
+		-buffering full \
+		-buffersize 512 \
+		-translation binary
+	fileevent $fd readable [list read_grep_l $fd]
+}
+
+proc read_grep_l {fd} {
+	global buf_rgl grep_file_list grep_patterns current_grep_path
+
+	append buf_rgl [read $fd]
+	set pck [split $buf_rgl "\0"]
+	set buf_rgl [lindex $pck end]
+
+	.pane.files.list conf -state normal
+	foreach p [lrange $pck 0 end-1] {
+		#set p [encoding convertfrom $p]
+		if {[string index $p end] eq {/}} {
+			set p [string range $p 0 end-1]
+		}
+		lappend grep_file_list $p
+		.pane.files.list insert end "[escape_path $p]\n"
+	}
+	.pane.files.list conf -state disabled
+
+	if {![eof $fd]} return
+	set buf_rgl {}
+	close $fd
+
+	if {[llength $grep_file_list] eq 0} {
+		exit 0
+	}
+	grep_file [lindex $grep_file_list 0]
+}
+
+proc grep_file {path} {
+	global outcols grep_patterns
+	set cmd [concat [list | git grep --color -h -n -p -3] $grep_patterns [list -- $path]]
+
+	foreach i $outcols {
+		$i conf -state normal
+		$i delete 0.0 end
+		$i conf -state disabled
+	}
+
+	if {[catch {set fd [open $cmd r]} err]} {
+		tk_messageBox \
+			-icon error \
+			-type ok \
+			-title {gui-grep: fatal error} \
+			-message $err
+		exit 1
+	}
+	fconfigure $fd -eofchar {}
+	fconfigure $fd \
+		-blocking 0 \
+		-buffering full \
+		-buffersize 512 \
+		-translation lf
+	fileevent $fd readable [list read_grep $fd $path]
+}
+
+proc read_grep {fd path} {
+	global outcols current_grep_path
+
+	foreach i $outcols {$i conf -state normal}
+
+	set l 0
+	while {[gets $fd line] >= 0} {
+		incr l
+		if {$l > 1} {
+			foreach i $outcols {$i insert end "\n"}
+		}
+
+		# catch hunk sep --
+		if {[regexp {^(?:\033\[(?:(?:\d+;)*\d+)m)?--(?:\033\[m)?} $line]} {
+			.pane.out.lnos insert end "--" linenumber
+			continue
+		}
+
+		# remove any color from lno and sep
+		regexp {^(?:\033\[(?:(?:\d+;)*\d+)m)?(\d+)(?:\033\[m)?(?:\033\[(?:(?:\d+;)*\d+)m)?[-:=](?:\033\[m)?(.*)$} $line all lno mline
+		foreach {mline markup} [parse_color_line $mline] break
+		set mline [string map {\033 ^} $mline]
+		#regsub "\r\$" $line {} line
+
+		set mark [.pane.out.grep index "end - 1 line linestart"]
+
+		.pane.out.lnos insert end $lno linenumber
+		.pane.out.grep insert end $mline
+
+		foreach {posbegin colbegin posend colend} $markup {
+			set prefix clr
+			foreach style [split $colbegin ";"] {
+				if {$style eq "7"} {append prefix i; continue}
+				if {$style < 30 || $style > 47} {continue}
+				set a "$mark linestart + $posbegin chars"
+				set b "$mark linestart + $posend chars"
+				catch {.pane.out.grep tag add $prefix$style $a $b}
+			}
+		}
+	}
+
+	foreach i $outcols {$i conf -state disabled}
+
+	if {[eof $fd]} {
+		close $fd
+		set current_grep_path $path
+		wm title . "git gui-grep: [escape_path $path]"
+	}
+}
+
+proc select_to_grep {x y} {
+	global grep_file_list current_grep_path
+
+	set lno [lindex [split [.pane.files.list index @$x,$y] .] 0]
+	set path [lindex $grep_file_list [expr {$lno - 1}]]
+	if {$path eq {}} {
+		return
+	}
+	if {$path eq $current_grep_path} {
+		return
+	}
+	grep_file $path
+}
+
+proc open_in_git_editor {path {lno 0}} {
+	global env
+
+	set env(FILENAME) $path
+	if {$lno != 0} {
+		set env(LINENUMBER) $lno
+	}
+	exec [exec  -- git var GIT_EDITOR] &
+	unset env(FILENAME)
+	catch { unset env(LINENUMBER) }
+}
+
+proc open_from_grep_view {w x y} {
+	global current_grep_path
+	global grep_file_list
+
+	if {$current_grep_path eq {}} {
+		return
+	}
+
+	set grep_lno [lindex [split [$w index @$x,$y] .] 0]
+	set lno [.pane.out.lnos get $grep_lno.0 $grep_lno.end]
+	if {$lno eq {--}} {
+		incr grep_lno
+		set lno [.pane.out.lnos get $grep_lno.0 $grep_lno.end]
+	}
+
+	open_in_git_editor $current_grep_path $lno
+}
+
+
+set grep_file_list [list]
+set current_grep_path {}
+set grep_patterns $argv
+scan_grep
-- 
tg: (6f10c41..) bw/git-gui/grep-support-prototype (depends on: master)
Jonathan Nieder· Nov 14, 2010, 21:54 UTC · re: Bert Wesarg · lore

Re: [PATCH/RFC/WIP/POC] git-gui: grep prototype

Bert Wesarg wrote:
Show 6 quoted lines
> There is one feature that the upstream git gui lacks[1]: This grep gui can start
> the git editor on every line by clicking with button 2 in the result area. The
> git editor will than be started with the environment variable FILENAME and
> LINENUMBER set but no argument given. This makes it possible to distinguish
> between normal, blocking edit operation (like git commit) from this
> open-and-forget type of operation.

This sounds like an excellent sort of thing to add to git grep -O, too (which currently has very limited support for editors' line number features). But what will it do with typical non-git-specific setups like

	VISUAL=vim
or
	EDITOR="gvim --nofork"
?
Do existing editors support LINENUMBER in the environment?
Bert Wesarg· Nov 14, 2010, 22:04 UTC · re: Jonathan Nieder · lore

Re: [PATCH/RFC/WIP/POC] git-gui: grep prototype

On Sun, Nov 14, 2010 at 22:54, Jonathan Nieder <jrnieder@gmail.com> wrote:
Show 23 quoted lines
> Bert Wesarg wrote:
>
>> There is one feature that the upstream git gui lacks[1]: This grep gui can start
>> the git editor on every line by clicking with button 2 in the result area. The
>> git editor will than be started with the environment variable FILENAME and
>> LINENUMBER set but no argument given. This makes it possible to distinguish
>> between normal, blocking edit operation (like git commit) from this
>> open-and-forget type of operation.
>
> This sounds like an excellent sort of thing to add to git grep -O, too
> (which currently has very limited support for editors' line number
> features).  But what will it do with typical non-git-specific setups
> like
>
>        VISUAL=vim
>
> or
>
>        EDITOR="gvim --nofork"
>
> ?
>
> Do existing editors support LINENUMBER in the environment?

I don't expect this, I have a wrapper script around my editor to scan the environment for these variables and pass the block/non-block option to the editor.

Bert
Jonathan Nieder· Nov 14, 2010, 22:09 UTC · re: Bert Wesarg · lore

Re: [PATCH/RFC/WIP/POC] git-gui: grep prototype

Bert Wesarg wrote:
> On Sun, Nov 14, 2010 at 22:54, Jonathan Nieder <jrnieder@gmail.com> wrote:
Show 18 quoted lines
>> This sounds like an excellent sort of thing to add to git grep -O, too
>> (which currently has very limited support for editors' line number
>> features).  But what will it do with typical non-git-specific setups
>> like
>>
>>        VISUAL=vim
>>
>> or
>>
>>        EDITOR="gvim --nofork"
>>
>> ?
>>
>> Do existing editors support LINENUMBER in the environment?
>
> I don't expect this, I have a wrapper script around my editor to scan
> the environment for these variables and pass the block/non-block
> option to the editor.
In that case, perhaps something like
	GIT_EDITOR=$(git var GIT_EDITOR)
	set -- --open-file-named-in-the-environment
	eval "$GIT_EDITOR" '"$@"' 

would be appropriate? This way, existing editors like vi, ed, and emacs fail:

	$ ed --fjdkaslfjdas
	ed: unrecognized option `--fjdkaslfjdas'
rather than opening a new, empty file.
Bert Wesarg· Nov 14, 2010, 22:15 UTC · re: Jonathan Nieder · lore

Re: [PATCH/RFC/WIP/POC] git-gui: grep prototype

On Sun, Nov 14, 2010 at 23:09, Jonathan Nieder <jrnieder@gmail.com> wrote:
Show 36 quoted lines
> Bert Wesarg wrote:
>> On Sun, Nov 14, 2010 at 22:54, Jonathan Nieder <jrnieder@gmail.com> wrote:
>
>>> This sounds like an excellent sort of thing to add to git grep -O, too
>>> (which currently has very limited support for editors' line number
>>> features).  But what will it do with typical non-git-specific setups
>>> like
>>>
>>>        VISUAL=vim
>>>
>>> or
>>>
>>>        EDITOR="gvim --nofork"
>>>
>>> ?
>>>
>>> Do existing editors support LINENUMBER in the environment?
>>
>> I don't expect this, I have a wrapper script around my editor to scan
>> the environment for these variables and pass the block/non-block
>> option to the editor.
>
> In that case, perhaps something like
>
>        GIT_EDITOR=$(git var GIT_EDITOR)
>        set -- --open-file-named-in-the-environment
>        eval "$GIT_EDITOR" '"$@"'
>
> would be appropriate?  This way, existing editors like vi, ed, and
> emacs fail:
>
>        $ ed --fjdkaslfjdas
>        ed: unrecognized option `--fjdkaslfjdas'
>
> rather than opening a new, empty file.
>

Now I got your point. Maybe I should call GIT_EDITOR with $FILENAME as argument but with a new variable to indicate open'n'forget. So existing setups would always open the file (even without honoring the line number) and new/smart wrappers can honor the open'n'forget and line number flag.

Bert
Jonathan Nieder· Nov 14, 2010, 23:48 UTC · re: Bert Wesarg · lore

Re: [PATCH/RFC/WIP/POC] git-gui: grep prototype

Bert Wesarg wrote:
Show 5 quoted lines
> Now I got your point. Maybe I should call GIT_EDITOR with $FILENAME as
> argument but with a new variable to indicate open'n'forget. So
> existing setups would always open the file (even without honoring the
> line number) and new/smart wrappers can honor the open'n'forget and
> line number flag.
Yes, I like the communication-via-environment idea.
In fact, there are multiple questions an editor might want answered:
1. GUI or no GUI?  (The combination of $DISPLAY and $(tty) often
   yields a reasonable answer.)
2. open'n'forget or wait for answer?  (gvim's --nofork option)
3. What line number?
4. regex to use to highlight matches?

Ideally $LINENUMBER would only be used represent (3), while a separate variable would indicate (2).

Bert Wesarg· Nov 19, 2010, 12:15 UTC · re: Jonathan Nieder · lore

Re: [PATCH/RFC/WIP/POC] git-gui: grep prototype

On Mon, Nov 15, 2010 at 00:48, Jonathan Nieder <jrnieder@gmail.com> wrote:
Show 14 quoted lines
> Bert Wesarg wrote:
>
>> Now I got your point. Maybe I should call GIT_EDITOR with $FILENAME as
>> argument but with a new variable to indicate open'n'forget. So
>> existing setups would always open the file (even without honoring the
>> line number) and new/smart wrappers can honor the open'n'forget and
>> line number flag.
>
> Yes, I like the communication-via-environment idea.
>
> In fact, there are multiple questions an editor might want answered:
>
> 1. GUI or no GUI?  (The combination of $DISPLAY and $(tty) often
>   yields a reasonable answer.)

I think we can generalize this by using the name of the caller (ie binary name). This is easy to get in a unix/linux shell with the help of /proc/$PPID/exe, but maybe not so in others OS's. I just remembered that quilt has this feature, the name is QUILT_COMMAND and I use is in my wrapper script to detect whether the caller wants to wait or not (I block only on the header and mail command).

Show 10 quoted lines
>
> 2. open'n'forget or wait for answer?  (gvim's --nofork option)
>
> 3. What line number?
>
> 4. regex to use to highlight matches?
>
> Ideally $LINENUMBER would only be used represent (3), while a
> separate variable would indicate (2).
>

Using for each case its own flag may sound silly, but I think this makes it more clear, what the caller expects. For example in my git-patch for calling GIT_EDITOR I provide a flag whether the given path is in the index or not (ie the action comes from the index list). If it is from the index, I use checkout-index --temp and update-index to edit the content from the index. This call to GIT_EDITOR should be blocking but it is better to request this explicitly.

Could we settle on a name space for such variables? GIT_EDITOR_* makes it obvious that it is for the editor started from git.

Bert
Jonathan Nieder· Nov 19, 2010, 19:49 UTC · re: Bert Wesarg · lore

Re: [PATCH/RFC/WIP/POC] git-gui: grep prototype

[resending to list; sorry for the noise]
Bert Wesarg wrote:
> Could we settle on a name space for such variables? GIT_EDITOR_* makes
> it obvious that it is for the editor started from git.
Sounds good to me.

If the choice of variables ends up looking especially good, maybe we can pass it along to the Portland group and call it xdg-editor for use by other apps, too. :)

Bert Wesarg· Nov 19, 2010, 20:16 UTC · re: Jonathan Nieder · lore

Re: [PATCH/RFC/WIP/POC] git-gui: grep prototype

On Fri, Nov 19, 2010 at 20:49, Jonathan Nieder <jrnieder@gmail.com> wrote:
> [resending to list; sorry for the noise]
No problem.
Show 12 quoted lines
>
> Bert Wesarg wrote:
>
>> Could we settle on a name space for such variables? GIT_EDITOR_* makes
>> it obvious that it is for the editor started from git.
>
> Sounds good to me.
>
> If the choice of variables ends up looking especially good, maybe
> we can pass it along to the Portland group and call it xdg-editor for
> use by other apps, too. :)
>
Interesting idea.

There is one more thing to discuss before starting collecting the variables. Has each flag its own env-variable, or do we put flags into one variable named GIT_EDITOR_FLAGS? They could be space or comma separated.

The second option sounds a little bit more future prove to me.

For non-flag arguments, there is the obvious LINENUMBER. But how do we pass columns or even a character range to the editor. They would be good for compiler output. Sure its out of score for git, but a little future-thinking will not hurt. We could add it to the LINENUMBER with a syntax like:

    <line>[:<column-start[-<column-end>]]
Bert

← back to recent threads