⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 execgrepdriver.tcl

📁 This Source-Navigator, an IDE for C/C++/Fortran/Java/Tcl/PHP/Python and a host of other languages.
💻 TCL
字号:
# Copyright (c) 2002, Mo DeJong# Copyright (c) 2000, 2001, Red Hat, Inc.# # This file is part of Source-Navigator.# # Source-Navigator is free software; you can redistribute it and/or# modify it under the terms of the GNU General Public License as published# by the Free Software Foundation; either version 2, or (at your option)# any later version.# # Source-Navigator is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU# General Public License for more details.# # You should have received a copy of the GNU General Public License along# with Source-Navigator; see the file COPYING.  If not, write to# the Free Software Foundation, 59 Temple Place - Suite 330, Boston,# MA 02111-1307, USA.itcl::class sourcenav::ExecGrepDriver {    inherit sourcenav::GrepDriver    destructor {}    public method start { pat files nocase max }    public method finish {}    public method cancel {}    public method getScaleValueVariable {}    public proc isAvailable {}    public method setTextWidget { widget }    public method isValidPattern { pat }    private method ExecGrepEvent {cmd}    private method grepReadableEvent {}    private method close {}    # Variables    private common grep_exe    private variable grep_running    private variable grep_canceled    private variable grep_fd ""    private variable case_sensitive    # Max number of matches to insert into text widget    private variable maxmatches    # Set to "1" if max seach limit is hit.    private variable max_search_reached    # Set to the pattern we are searching for only while    # a grep is actually active, otherwise set to ""    private variable activePattern ""    # The text widget grep results will be added to    private variable text ""    # Map from file name to files list index    private variable files_Matched    # The number of files that are being searched through in a grep    private variable files_Count 0    # Variable linked to the progress bar display    private variable scalevalue 0}itcl::body sourcenav::ExecGrepDriver::destructor { } {    # Make sure any grep process is terminated    if {${grep_fd} != "" && [catch {::close ${grep_fd}} err]} {        sn_log "caught error in ExecGrepDriver dtor : \"$err\""    }}itcl::body sourcenav::ExecGrepDriver::start { pat files nocase max } {    global sn_path    global errorInfo errorCode     # We should have an empty buffer to start with    if {$text == ""} {        error "no text widget"    }    if {[$text index end] != "2.0"} {        error "text widget $text is not empty"    }    if {$nocase} {        set case_sensitive 0    } else {        set case_sensitive 1    }    set maxmatches $max    # Count files    set files_Count [llength ${files}]    # The actual count is off by one because we update the % done meter    # based on the match read back from grep    incr files_Count -1    # isAvailable has not been invoked, do it before using grep_exe    if {![info exists grep_exe]} {        if {![isAvailable]} {            error "exec grep driver not available"        }    }    # grep command    set cmd [list $grep_exe "-n"]    # Ignore case    if {! ${case_sensitive}} {        lappend cmd "-i"    }    lappend cmd "--"    # The pattern to search for    set activePattern ${pat}    lappend cmd ${pat}    sn_log "execing: ${cmd} \$FILES"    # Add the files to search for to the    # grep command. We also create an array    # that mapps the file name to its    # position in the search so that we    # can update our % done meter.     # Clear out old array (it could be huge!)    catch {unset files_Matched}    set filecount 0    set groupcount 0    set groupsize 40    set filegroups($groupcount) [list]    # If there is only one file, add an empty dummy file    # so that the first file's name will appear in    # the grep output.    if {[llength $files] == 1} {        set dummy [sn_tmpFileName]        ::close [open $dummy w]        lappend files $dummy    }    set len [llength $files]    for {set i 0} {$i < $len} {incr i} {        set file [lindex $files $i]        set files_Matched($file) $i        if {$filecount > $groupsize} {            set filecount 0            incr groupcount            set filegroups($groupcount) [list]	}        lappend filegroups($groupcount) $file        incr filecount    }    set scalevalue 0    set max_search_reached 0    set errorInfo ""    set counter 0    set grep_canceled 0    for {set groupindex 0} {$groupindex <= $groupcount} {incr groupindex} {        sn_log "grouploop $groupindex : will run $cmd"        set execCmd "$cmd $filegroups($groupindex)"        if {$grep_canceled} {            sn_log "breaking out of group loop because of grep_canceled"            break        }        if {$max_search_reached} {            sn_log "breaking out of group loop because of max_search_reached"            break        }        set grep_running 1        ExecGrepEvent $execCmd        vwait [itcl::scope grep_running]        set scalevalue [expr $groupindex * $groupsize + $groupsize]    }    if {[info exists dummy]} {        file delete $dummy    }}itcl::body sourcenav::ExecGrepDriver::finish { } {#    sn_log "ExecGrepDriver::finish : [clock format [clock seconds]]"    # Set progress bar/meter to 100%.    set scalevalue ${files_Count}    set t $text    $t configure -state normal    set pat $activePattern    set pat_len [string length $pat]    set numlines [expr {int([$t index end]) - 1}]    set index_list [list]    if {! ${case_sensitive}} {        set case -nocase    } else {        set case -exact    }    # Preform highlighing (colorization) of the results.    set patt_len 0    set line_index [$t search -count patt_len -regexp -- \        {^(([A-Z].)(:?)([^:]*)|([^:]*))(:?)([0-9]+)(:?)} 1.0 end]      while {$line_index != ""} {        set start_next_line [list $line_index + 1 lines]        set line_index [list $line_index + $patt_len chars]        set index [$t search $case -- $pat $line_index $start_next_line]        if {$index == ""} {            sn_log "GREP: Colorizing error at ($line_index,$start_next_line)\                or attempting to colorize a Formatted pattern."            break        }        # Now that we've skipped over the filename and        # line number we can tag every match until the        # the line.        while {$index != ""} {            set end_index [list $index + $pat_len chars]            lappend index_list $index $end_index            set index [$t search $case -- $pat $end_index $start_next_line]        }        # Skip the filename and line number at the begining again.        # {^(([A-Z].)(:?)([^:]*)|([^:]*))(:?)([0-9]+)(:?)}        # matches things like:        #        #        /home/user/work/files/project.c:108:        #        C:/home/user/work/files/project.c:108:        #        # So we don't color parts of the filename by mistake.	set line_index [$t search -count patt_len -regexp \            -- {^(([A-Z].)(:?)([^:]*)|([^:]*))(:?)([0-9]+)(:?)} \            $end_index end]    }    if {$index_list != {}} {        eval {$t tag add grep} ${index_list}    }    if {$max_search_reached} {        $t insert end \n        $t insert end [get_indep String GrepTruncatedString]        $t insert end \n    } elseif {$grep_canceled} {        # Check if the last character in the buffer is a        # newline and add one if needed.        for {set i 1} {1} {incr i} {            set index [$t index [list end - $i char]]            if {[$t get $index] != "\n" || $index == "1.0"} {                break            }        }        set lastline [expr {int($index)}]        set lastline_text [$t get $lastline.0 {end - 1 char}]        if {[string index $lastline_text end] != "\n"} {            $t insert end \n        }        $t insert end [get_indep String GrepCanceledString]        $t insert end \n    }    $t configure -state disabled    sn_log "Done tagging : [clock seconds], $numlines lines"}itcl::body sourcenav::ExecGrepDriver::ExecGrepEvent {cmd} {    global sn_options    global errorInfo errorCode env    global tcl_platform    #make sure we stay in project directory (different interpeters).    catch {cd $sn_options(sys,project-dir)}    if {$tcl_platform(platform) != "windows"} {        set home $env(HOME)        if {[string compare ${home} "/"] != 0} {            append home "/"        }        regsub -all "~/" ${cmd} ${home} cmd    }    sn_log "Start exec grep : [clock seconds]"    if {[catch {set grep_fd [open "| ${cmd}" r]} errmsg]} {        sn_error_dialog "-code error -errorinfo ${errorInfo}\            -errorcode ${errorCode} ${errmsg}"        return    }# FIXME: we should play around with the default buffer size to see# if we can improve the grep read performance.    fconfigure ${grep_fd} \        -encoding $sn_options(def,system-encoding) \        -blocking 0 \        -buffering full    if {[catch {fileevent ${grep_fd} readable \            [itcl::code ${this} grepReadableEvent]} err]} {        sn_log "caught error assigning fileevent : \"$err\""        ${this} close    }}itcl::body sourcenav::ExecGrepDriver::close {} {    global sn_options    sn_log "Close exec grep : [clock seconds]"    # close older descriptor to be secure that no other grep    # is running    if {${grep_fd} != "" && [catch {::close ${grep_fd}} err]} {        sn_log "caught error while closing grep fd : \"$err\""    }    sn_log "closed grep fd ${grep_fd}"    set grep_fd ""    set grep_running 0}itcl::body sourcenav::ExecGrepDriver::grepReadableEvent {} {    set close_it 0    if {[catch {read ${grep_fd}} buf] || [eof ${grep_fd}]} {        sn_log "closing grep fd because of read error or EOF"        set close_it 1    } else {        sn_log "grepReadableEvent read [string length $buf] bytes\            ([llength [split $buf \n]] lines)"        #sn_log "\"$buf\""        set t $text        $t config -state normal        $t insert end $buf        # Trim the grep to the maximum number of lines        # Walk backwards from the end of the buffer a        # character at a time until we find a non-newline.        for {set i 1} {1} {incr i} {            set index [$t index [list end - $i char]]            if {[$t get $index] != "\n" || $index == "1.0"} {                break            }        }        set numlines [expr {int($index)}]                if {$numlines > $maxmatches} {            set close_it 1            set max_search_reached 1            $t delete [list $maxmatches.0 + 1 lines] end            # Walk back again to get the trimmed numlines            for {set i 1} {1} {incr i} {                set index [$t index [list end - $i char]]                if {[$t get $index] != "\n" || $index == "1.0"} {                    break                }            }            set numlines [expr {int($index)}]            sn_log "trimmed results to $numlines lines"        }        $t see end        # Update the % done meter, we grab the last full        # line out of the match, get the file name, and        # then map that to the file position in the search.        set last [$t get $numlines.0 $numlines.end]        sn_log "Last line from grep is \"$last\""        if {$last != ""} {	    set last_file [lindex [split $last :] 0]            if {[info exists files_Matched($last_file)]} {                sn_log "Last file in read buffer is \"$last_file\""                set scalevalue $files_Matched($last_file)                sn_log "New scale value is $scalevalue of ${files_Count}"            }        }        $t config -state disabled    }    if {$close_it} {        ${this} close    }    # Note: It is critically important that we never    # call update inside this fileevent callback!    # If we did, it would recursively process fileevent    # callbacks. That could trash the stack if the    # grep results were large enough.}itcl::body sourcenav::ExecGrepDriver::cancel {} {    set grep_canceled 1    ${this} close}itcl::body sourcenav::ExecGrepDriver::getScaleValueVariable {} {    return [itcl::scope scalevalue]}itcl::body sourcenav::ExecGrepDriver::isAvailable {} {    global tcl_platform sn_path    if {[info exists grep_exe]} {        return 1    }    # If there is a grep in the directory with the other binaries, use it.    # Otherwise, check for grep on the path    if {$tcl_platform(platform) == "windows"} {        set exeext .exe    } else {        set exeext ""    }    set grep_cmd [file join $sn_path(bindir) "grep$exeext"]    if {! [file exists $grep_cmd]} {        set grep_cmd "grep"    }    # Pass in an empty second file to make the first file's name    # actually gets printed in the grep output.    set f1 [sn_tmpFileName]    set fd [open $f1 w]    puts $fd "ONE"    ::close $fd    set f2 [sn_tmpFileName]    ::close [open $f2 w]    sn_log "Attempting to grep exec check : $grep_cmd -n -i -- one $f1 $f2"    if {[catch {exec $grep_cmd -n -i -- one $f1 $f2} str]} {        sn_log "grep exec check failed \"$str\""    } else {        set expected "$f1:1:ONE"        if {[string equal $str $expected]} {            set grep_exe $grep_cmd            sn_log "Found usable grep: $grep_exe"            file delete $f1 $f2            return 1        } else {            sn_log "Expected \"$expected\" but got \"$str\""        }    }    file delete $f1 $f2    return 0}itcl::body sourcenav::ExecGrepDriver::setTextWidget { widget } {    set text $widget}itcl::body sourcenav::ExecGrepDriver::isValidPattern { pat } {    return 1}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -