home *** CD-ROM | disk | FTP | other *** search
Text File | 2001-01-22 | 40.2 KB | 1,377 lines |
- ## -*-Tcl-*-
- # ###################################################################
- # Vince's Additions - an extension package for Alpha
- #
- # FILE: "diffMode.tcl"
- # created: 7/3/95 {11:15:02 pm}
- # last update: 01/22/2001 {23:34:05 PM}
- # Author: Vince Darley
- # E-mail: <vince@santafe.edu>
- # mail: 317 Paseo de Peralta
- # Santa Fe, NM 87501, USA
- # www: <http://www.santafe.edu/~vince/>
- #
- # improvements Copyright (c) 1997-2000 Vince Darley, all rights reserved
- #
- # Description:
- #
- # Largely re-written Diff mode for Alpha. Still under construction,
- # but already a lot better than the old one. Basic features:
- #
- # A 'Diff' menu, which contains commonly used options.
- #
- # Uses Alpha's 'marks' so that you can patch diffs back and forth
- # between files without losing the correct location in the file.
- # (previously if you modified one of the original windows, all line
- # numbers after that would be incorrect)
- #
- # Note: the various additions to cope with context sensitive versus
- # normal diffs versus cvs-style versus perforce ... mean that this
- # code is beginning to get rather spaghetti like. It really needs
- # a complete overhaul (and probably a small test suite so that we can
- # ensure the code works for all these formats).
- #
- # Limitations:
- #
- # Sadly a lot of Alpha's window manipulation commands only work
- # on the foremost window. This means this code is slowed down a
- # lot because it often has to bring a window to the front before
- # reading/writing into it. There is a flag to setup a hack which
- # helps with this, at the expense of colours in the windows.
- #
- # The code in this file for patching (and perhaps viewing etc), and
- # for finding the right file, for deciding whether to display left
- # or right window or both, etc. is way too convoluted and difficult
- # to comprehend. This is basically as a result of the gradual evolution
- # of this file. It needs to be rewritten from scratch in much
- # simplified form.
- #
- # History:
- #
- # modified by rev reason
- # -------- --- --- -----------
- # 7/3/95 PJK?1.0 original
- # 3/9/97 VMD 2.0 much improved version
- # 03/23/98 VMD and Jon Guyer 2.0-3.0 various fixes and Voodoo
- # 05/04/2000 JEG 3.1-2 fixed patching
- # 2000-05-05 VMD 3.2.1 minor fix to better handle funny filenames.
- # 2000-06-15 VMD 3.3.1 copes with various cvs/perforce style diffs
- # 2000-08-22 JEG 3.3.2 fixed bug in directory searches with 0 lines of context
- # fixed bug in scrolling to previous files in directory searches
- # added option (default on) to close document windows when done
- # attempts to clean up marks when finished
- # ###################################################################
- ##
-
- # Usage: diff [-#] [-abBcdefhHilnNprstTuvw] [-C lines] [-F regexp] [-I regexp]
- # [-L label [-L label]] [-S file] [-D symbol] [+ignore-blank-lines]
- # [+context[=lines]] [+unified[=lines]] [+ifdef=symbol]
- # [+show-function-line=regexp]
- # [+speed-large-files] [+ignore-matching-lines=regexp] [+new-file]
- # [+initial-tab] [+starting-file=file] [+text] [+all-text] [+ascii]
- # [+minimal] [+ignore-space-change] [+ed] [+reversed-ed] [+ignore-case]
- # [+print] [+rcs] [+show-c-function] [+binary] [+brief] [+recursive]
- # [+report-identical-files] [+expand-tabs] [+ignore-all-space]
- # [+file-label=label [+file-label=label]] [+version] path1 path2
-
- alpha::mode Diff 3.3.3 diffMenu {*.diff *.patch} {diffMenu} {
- alpha::package require AlphaTcl 7.2.1b5
- addMenu diffMenu •288 Diff
- namespace eval compare {}
- menu::insert Utils submenu 0 compare
- menu::insert compare items end "windows" "files…" "directories…"
- hook::register requireOpenWindowsHook [list compare windows] 2
- newPref sig DiffSig DIFF
- set Diff::handlers(Diff-mode) Diff::runInsideAlpha
- ensureset Diff::handler Diff-mode
- lunion varPrefs(Files) Diff::handler
- # By default Alpha handles the results of diff internally using
- # its Diff mode. However add-on packages to Alpha can provide
- # alternative Diff handlers.
- newPref var Diff::handler "Diff-mode" global "" Diff::handlers array
- } uninstall {
- file delete "$pkg_file"
- file delete [file join ${HOME} Tools "GNU Diff"]
- } maintainer {
- "Vince Darley" vince@santafe.edu <http://www.santafe.edu/~vince/>
- } help {file "Diff Help"}
-
- array set DiffAppSignatures {
- GnuDiff DIFF
- }
- array set DiffAppScripts {
- GnuDiff {
- {dosc -c $quotedSig -s $flags}
- }
- }
-
- proc diffMenu {} {}
-
- # Generally best to use this setting, but some actions can be a bit
- # slower with it on. Allows you to patch changes back and forth
- # between windows automatically, which is otherwise not possible
- newPref f useSophisticatedDiffMarking 1 Diff
- # A good idea, but can mess up window colours sometimes
- # (it's a bit of a hack)
- newPref f useFastWindowSwapping 1 Diff
- # Slows things down in that it has to scan through Alpha's list of marks
- # to find the correct positions for each window, but speeds things up
- # because it doesn't need to activate each window in turn. Try it and see
- # for yourself.
- newPref f useMarksDontBringToFront 1 Diff
- # Up/Down arrows both scroll the diff window and synchronise the viewed
- # portion of text in the document windows
- newPref f synchroniseMoveAndView 1 Diff Diff::bindUpDown
- # You'll probably want this; may slow things down a bit though
- newPref f workaroundAlphaColourBug 1 Diff
- # Default lines of context to generate when asking Diff to do its magic
- newPref var linesOfContext 3 Diff
- # Other diff flags you want to send (ignore whitespace etc)
- newPref var diffFlags { } Diff
- # If you've imported a diff file from a Unix system, this option allows
- # you to use it with Alpha too.
- newPref f translatePathDelimiters 1 Diff
- # If you've imported a diff file from a different directory structure,
- # you may need to remove a given prefix so Alpha can find your files
- # correctly.
- newPref v removeFilePrefix "" Diff
- # If the document windows were not already open before the diff, automatically
- # close them when finished.
- newPref f killWindowsWhenDone 1 Diff
-
- Menu -n $diffMenu -p Diff::menuProc -M Diff {
- "rerunDiff"
- "(-"
- "/<I<BpatchIntoLeftWindow"
- "/<I<BpatchIntoRightWindow"
- "(-"
- "cleanUpAndCloseWindows"
- "(-"
- "locateLeftWindow"
- "locateRightWindow"
- "locateLeftDir"
- "locateRightDir"
- "parseDiffWin"
- }
- Bind 0x7b <z> Diff::patchIntoLeftWindow Diff
- Bind 0x7c <z> Diff::patchIntoRightWindow Diff
-
- if {[info tclversion] < 8.0} {
- # Bind manually due to bug
- Bind 0x7b <oz> Diff::patchIntoLeftWindow Diff
- Bind 0x7c <oz> Diff::patchIntoRightWindow Diff
- }
- # do the rest
- Bind '\r' Diff::Select Diff
- Bind '\t' Diff::View Diff
- Bind Kpad. <c> Diff::Win
- Bind Enter {Diff::Down;Diff::Select} Diff
- Bind Kpad0 {Diff::Up;Diff::Select} Diff
-
- hook::register closeHook Diff::closing Diff
- hook::register openHook Diff::opening Diff
-
- proc Diff::bindUpDown {} {
- global DiffmodeVars
- if {$DiffmodeVars(synchroniseMoveAndView)} {
- catch {unBind down Diff::Down Diff}
- catch {unBind up Diff::Up Diff}
- Bind down {Diff::Down;Diff::View} Diff
- Bind up {Diff::Up;Diff::View} Diff
- } else {
- catch {unBind down {Diff::Down;Diff::View} Diff}
- catch {unBind up {Diff::Up;Diff::View} Diff}
- Bind down Diff::Down Diff
- Bind up Diff::Up Diff
- }
- }
-
- Diff::bindUpDown
-
- proc Diff::menuProc {menu item} {
- Diff::$item
- }
-
- proc Diff::locateLeftWindow {} {
- global Diff::1
- set Diff::1 [getfile "Select your left (old) file:"]
- Diff::Display Diff::1 1 0 1
- Diff::setMarksUp
- if {[info exists Diff::1]} {Diff::mark ${Diff::1} 1 ""}
- Diff::diffWinFront
- }
-
- proc Diff::locateRightWindow {} {
- global Diff::2
- set Diff::2 [getfile "Select your right (new) file:"]
- Diff::Display Diff::2 0 0 1
- Diff::setMarksUp
- if {[info exists Diff::2]} {Diff::mark ${Diff::2} 0 ""}
- Diff::diffWinFront
- }
-
- proc Diff::locateLeftDir {} {
- global Diff::leftDir
- set Diff::leftDir [get_directory "Select your left (old) directory:"]
- }
- proc Diff::locateRightDir {} {
- global Diff::rightDir
- set Diff::rightDir [get_directory "Select your right (new) directory:"]
- }
-
- proc Diff::rerunDiff {} {
- global diffDir Diff::1 Diff::2
- Diff::diffWinFront
- catch {set d1 ${Diff::1}}
- catch {set d2 ${Diff::2}}
- killWindow
- catch {set Diff::1 $d1}
- catch {set Diff::2 $d2}
- if {$diffDir} {
- Diff::execute 1 {* Directory Comparison *}
- } else {
- Diff::files
- }
- }
-
- proc Diff::cleanUpAndCloseWindows {} {
- global Diff::1 Diff::2 diffDir
- if {![catch {bringToFront ${Diff::1}}]} {
- removeAllMarks diff-*
- shrinkFull
- killWindow
- }
-
- if {![catch {bringToFront ${Diff::2}}]} {
- removeAllMarks diff-*
- shrinkFull
- killWindow
- }
- Diff::diffWinFront
- killWindow
- }
-
- proc Diff::closing {{name ""}} {
- global Diff::array Diff::Marked Diff::1 Diff::2
- foreach var [uplevel \#0 info vars Diff::array*] {
- global $var
- if {[uplevel \#0 array exists $var]} { unset $var }
- }
- catch {unset Diff::Marked}
- Diff::cleanup ${Diff::1}
- catch {unset Diff::1}
- Diff::cleanup ${Diff::2}
- catch {unset Diff::2}
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Diff::opening" --
- #
- # This procedure is called whenever we open a diff window, whether
- # a '.diff' file, or whether a window produced by this mode using
- # 'Diff::execute'. We parse its contents.
- # -------------------------------------------------------------------------
- ##
- proc Diff::opening {name} {
- global Diff::window DiffmodeVars Diff::leftDir Diff::rightDir Diff::1 Diff::2
- set Diff::window $name
- set Diff::leftDir ""
- set Diff::rightDir ""
-
- set files [Diff::getFiles [minPos]]
- if {[file exists [set cur [win::StripCount [win::Current]]]]} {
- set cur [file dirname $cur]
- set f0 [lindex $files 0]
- if {[file exists [file join $cur $f0]]} {
- set Diff::leftDir $cur
- set Diff::1 [file join $cur $f0]
- }
- set f1 [lindex $files 1]
- if {[file exists [file join $cur $f1]]} {
- set Diff::rightDir $cur
- set Diff::2 [file join $cur $f1]
- }
- }
-
- global Diff::usesAts
- set Diff::usesAts 0
- if {$DiffmodeVars(useSophisticatedDiffMarking)} {
- Diff::parseDiffWin
- }
- }
-
- # ◊◊◊◊ Parsing diff information ◊◊◊◊ #
-
- proc Diff::parseDiffWin {} {
- Diff::diffWinFront
- global diffDir Diff::window DiffmodeVars Diff::usesAts
-
- # Should really by a per-window basis. This is the default
- # pattern. Cvs style diffs (with @@) need a different pattern,
- # since this pattern can select lines of diff-content rather
- # than the preamble.
- set Diff::usesAts 0
- # By default its not a directory
- set diffDir 0
-
- set pos [minPos]
- while 1 {
- set res [search -s -n -f 1 -r 1 "^((diff\[^\r\n\]*|==== \[^\r\n\]*|\[^- \n\r\]+)(\r|\n|\$)|@@ )" $pos]
- if {[llength $res]} {
- set pos [pos::math [lindex $res 0] + 1]
- # If we picked up a 'diff...' line followed by a line starting with one
- # or more *'s, it a part of a context diff, announcing a new file
- # we simply ignore this.
- set foundText [getText [lindex $res 0] [pos::math [lindex $res 0] + 4]]
- if {$foundText == "diff" || $foundText == "===="} {
- set nextStart [getText [nextLineStart [lindex $res 0]] [pos::math [nextLineStart [lindex $res 0]] +3]]
- # The first case is a context diff, the second a Cvs context diff on a single
- # file.
- if {$nextStart == "***" || $nextStart == "---"} {
- continue
- }
- if {$foundText == "diff"} {set diffDir 1}
- }
- set t [getText [lindex $res 0] [pos::math [lindex $res 1] - 1]]
- if {[string length $t] == 1} {
- continue
- }
- if {[regexp {^=+$} $t]} {
- # A dividing line in a cvs-style diff
- continue
- } elseif {[regexp {^\*+$} $t]} {
- # It's a diff over a directory
- set diffDir 1
- # check if the file has changed
- if {[string index [set tt [getText [prevLineStart $pos] $pos]] 0] != " " \
- && [lookAt [pos::math $pos - 3]] != "-" } {
- newforeach {from to} [Diff::parseLineIntoFromTo $tt $pos] {break}
- if {$DiffmodeVars(translatePathDelimiters)} {
- set from [file::translatePathDelimiters $from]
- set to [file::translatePathDelimiters $to]
- }
- lappend got [list "diff" $from $to]
- }
- set from [lindex [eval getText [search -s -n -f 1 -r 1 {^\*\*\* [0-9]+,[0-9]+} $pos]] 1]
- set to [lindex [eval getText [search -s -n -f 1 -r 1 {^--- [0-9]+,[0-9]+} $pos]] 1]
- lappend got [list $from $to]
- } elseif {$t == "@@"} {
- # cvs diff over a single file.
- set Diff::usesAts 1
- set line [getText [lindex $res 0] [nextLineStart [lindex $res 0]]]
- lappend got [Diff::parseAtsIntoLine $line]
- set res [search -s -n -f 1 -r 1 "^@@ " [lindex $res 1]]
- if {[llength $res]} {
- set pos [lindex $res 0]
- } else {
- break
- }
- } else {
- lappend got $t
- }
- } else {
- break
- }
- }
- set Diff::window [win::Current]
- # now stored all diff items in the list 'got'
- if {[info exists got]} {
- if {!$diffDir} {
- set f [lindex $got 0]
- if {[string range $f 0 3] == "diff"} {
- set got [lrange $got 1 end]
- }
- }
- Diff::storeMarks $got
- }
- Diff::diffWinFront
- global tileTop tileWidth tileHeight tileLeft
- set top [expr {$tileTop + $tileHeight - 178}]
- sizeWin ${Diff::window} [expr {$tileWidth - 6}] 178
- moveWin ${Diff::window} $tileLeft $top
-
- }
-
- proc Diff::parseLineIntoFromTo {tt pos} {
- if {[regexp {^==== } $tt]} {
- # probably a perforce style diff
- regexp "^==== (\[^\r\n\]*) - (\[^\r\n\]*) ===" $tt "" from to
- } else {
- set to [lindex $tt 1]
- regexp " (\[^\t\]*)" $tt "" to
- set p [prevLineStart $pos]
- set fileline [getText [prevLineStart $p] $p]
- regexp " (\[^\t\]*)" $fileline "" from
- }
- return [list $from $to]
- }
-
- proc Diff::parseAtsIntoLine {line} {
- regexp { -([0-9,]+) \+([0-9,]+) } $line "" from to
- set from [split $from ,]
- set to [split $to ,]
- set from "[lindex $from 0],[expr {[lindex $from 0] + [lindex $from 1] -1}]"
- set to "[lindex $to 0],[expr {[lindex $to 0] + [lindex $to 1] -1}]"
- return [list $from $to]
- }
-
- proc Diff::storeMarks {diffs} {
- global Diff::1 Diff::2 Diff::array
- set suff ""
- foreach m $diffs {
- if {[regexp {^diff} $m]} {
- set suff "/[file tail [lindex $m end]]"
- global Diff::array${suff}
- continue
- }
- set Diff::array${suff}($m) ""
- }
- }
-
- proc Diff::setMarksUp {{suff ""}} {
- global Diff::array${suff}
- foreach m [array names Diff::array$suff] {
- set scanned [Diff::parseDiffString $m]
- if {[scan $scanned "%s %f %f %f %f" \
- char start1 end1 start2 end2] != 5} { error "Bad diff list!" }
- if {$scanned != ""} {
- set Diff::array${suff}($m) $scanned
- }
- }
- }
-
- proc Diff::cleanup {win} {
- global DiffmodeVars Diff::killWhenDone
- if {$win != "" && [lsearch [winNames -f] "[quote::Glob $win]"] >= 0} {
- # Alpha somehow remembers the last mode in which it adjusts
- # the window and so forgets all the colours if we cheat the
- # mode switch.
- if {$DiffmodeVars(workaroundAlphaColourBug)} {
- bringToFront $win
- } else {
- Diff::BringToFront $win
- }
- removeAllMarks diff-*
-
- if {$DiffmodeVars(killWindowsWhenDone)
- && [info exists Diff::killWhenDone($win)]} {
- killWindow
- unset Diff::killWhenDone($win)
- }
- }
- }
-
- proc Diff::mark {win left {suff ""}} {
- global Diff::array$suff DiffmodeVars
- if {$win != ""} {
- # Alpha somehow remembers the last mode in which it adjusts
- # the window and so forgets all the colours if we cheat the
- # mode switch.
- if {$DiffmodeVars(workaroundAlphaColourBug)} {
- bringToFront $win
- } else {
- Diff::BringToFront $win
- }
- # not strictly necessary, but cleaner
- removeAllMarks diff-*
- if {$left} {
- foreach m [array names Diff::array$suff] {
- scan [set Diff::array${suff}($m)] "%s %f %f" char start1 end1
- setNamedMark "diff-$m" $start1 $start1 $end1
- }
- } else {
- foreach m [array names Diff::array$suff] {
- scan [set Diff::array${suff}($m)] "%s %f %f %f %f" char start1 end1 start2 end2
- setNamedMark "diff-$m" $start2 $start2 $end2
- }
- }
- }
- }
-
- proc Diff::markUpWindow {diffs} {
- alertnote "Currently a little obsolete; shouldn't be called!"
- if {[info exists Diff::1]} {
- Diff::BringToFront ${Diff::1}
- # not strictly necessry, but cleaner
- removeAllMarks diff-*
- foreach m $diffs {
- scan [set Diff::array($m)] "%s %f %f" char start1 end1
- setNamedMark "diff-$m" $start1 $start1 $end1
- }
- }
- if {[info exists Diff::2]} {
- Diff::BringToFront ${Diff::2}
- # not strictly necessry, but cleaner
- removeAllMarks diff-*
- foreach m $diffs {
- scan [set Diff::array($m)] "%s %f %f %f %f" char start1 end1 start2 end2
- setNamedMark "diff-$m" $start2 $start2 $end2
- }
- }
-
- }
-
- proc Diff::parseDiffString {text} {
- global Diff::1 Diff::2
- if {![regexp {[acd]} $text char]} {
- # context sensitive
- set char "c"
- if {[scan $text "%d,%d %d,%d" one oned two twod] != 4} {
- return
- }
- } else {
- set res [split $text $char]
- if {![scan [lindex $res 0] "%d,%d" one oned]} return
- if {![scan [lindex $res 1] "%d,%d" two twod]} return
- if {![info exists oned]} { set oned $one }
- if {![info exists twod]} { set twod $two }
- }
-
- if {[info exists Diff::1]} {
- if {$char != "a"} {
- set res [list $char [rowColToPos -w ${Diff::1} $one 0]]
- lappend res [rowColToPos -w ${Diff::1} [expr {$oned + 1}] 0]
- } else {
- # Can $one and $oned ever be different for an 'a'?
- # If so, this will be Bad
- set res [list $char [rowColToPos -w ${Diff::1} [expr {$one + 1}] 0]]
- lappend res [rowColToPos -w ${Diff::1} [expr {$oned + 1}] 1]
- }
- } else {
- set res [list $char -1 -1]
- }
-
- if {[info exists Diff::2]} {
- if {$char != "d"} {
- lappend res [rowColToPos -w ${Diff::2} $two 0]
- lappend res [rowColToPos -w ${Diff::2} [expr {$twod + 1}] 0]
- } else {
- # Can $two and $twod ever be different for a 'd'?
- # If so, this will be Bad
- lappend res [rowColToPos -w ${Diff::2} [expr {$two + 1}] 0]
- lappend res [rowColToPos -w ${Diff::2} [expr {$twod + 1}] 1]
- }
- } else {
- lappend res -1 -1
- }
- return $res
- }
-
- proc Diff::parseDiffLine {text {is_pos 0}} {
- if {$is_pos} {
- set text [lindex [Diff::line $text] 0]
- }
- return [Diff::parseDiffString $text]
- }
-
- proc Diff::getFiles {pos} {
- global DiffmodeVars
- set llen [llength [set files [getText $pos [nextLineStart $pos]]]]
- set files [lrange $files [expr {$llen -2}] end]
- if {$DiffmodeVars(translatePathDelimiters)} {
- set files [file::translatePathDelimiters $files]
- }
- return $files
- }
-
- proc Diff::line {pos {f ""}} {
- global diffDir Diff::window DiffmodeVars Diff::usesAts
- set context 0
- if {$diffDir} {
- if {$f != ""} {upvar $f files}
- if {[lookAt $pos] == "*" || [catch {search -s -f 0 -r 1 "^(diff|==== )\[^\r\n\]*(\r|\n|\$)" $pos} res]} {
- set p $pos
- while 1 {
- set res [search -s -f 0 -r 1 "^\\*+(\r|\n|\$)" $p]
- set p [pos::math [lindex $res 0] - 2]
- if {[lookAt [lineStart $p]] != " " && [lookAt $p] != "-"} break
- }
- set toline [getText [lineStart $p] $p]
- if {[regexp {^==== } $toline]} {
- newforeach {from to} [Diff::parseLineIntoFromTo $toline $p] {break}
- } else {
- regexp " (.*)\t" $toline "" to
- regexp " (.*)\t" [getText [prevLineStart $p] [lineStart $p]] "" from
- }
- if {[set pr $DiffmodeVars(removeFilePrefix)] != ""} {
- regsub -all "/\./" $to "/" to
- if {[string first $pr $to] == 0} {
- set to [string range $to [string length $pr] end]
- }
- regsub -all "/\./" $from "/" from
- if {[string first $pr $from] == 0} {
- set from [string range $from [string length $pr] end]
- }
- }
- set files [list [file::ensureStandardPath $from] [file::ensureStandardPath $to]]
- set tfrom [lindex [eval getText [search -s -n -f 1 -r 1 {^\*\*\* [0-9]+,[0-9]+} [getPos]]] 1]
- set tto [lindex [eval getText [search -s -n -f 1 -r 1 {^--- [0-9]+,[0-9]+} [getPos]]] 1]
- set context 1
- set text "$tfrom $tto"
- } else {
- set llen [llength [set files [eval getText $res]]]
- set files [lrange $files [expr {$llen -2}] end]
- set text [getText [lineStart $pos] [pos::math [nextLineStart $pos] - 1]]
- }
- if {$DiffmodeVars(translatePathDelimiters)} {
- foreach ff $files {
- lappend nfiles [file::translatePathDelimiters $ff]
- }
- set files $nfiles
- }
- set f [lindex $files end]
- set suff "/[file tail $f]"
- } else {
- set suff ""
- set text [getText [lineStart $pos] [pos::math [nextLineStart $pos] - 1]]
- if {${Diff::usesAts}} {
- set text [Diff::parseAtsIntoLine $text]
- }
- }
- return [list ${text}${suff} $context]
- }
-
- # ◊◊◊◊ Patching routines ◊◊◊◊ #
- proc Diff::patch {w1 w2 left} {
- global DiffmodeVars
- if {$DiffmodeVars(useSophisticatedDiffMarking)} {
- Diff::patchSophisticated $w1 $w2 $left
- } else {
- Diff::patchOld $w1 $w2 $left
- }
- }
- proc Diff::patchSophisticated {ww1 ww2 left} {
- upvar \#0 $ww1 w1
- upvar \#0 $ww2 w2
- set code [Diff::line [getPos]]
- regexp {([^/]+)(.*)} [lindex $code 0] "" mark suff
- set context [lindex $code 1]
- global Diff::array${suff}
- if {![info exists w1]} { dialog::errorAlert "No such window" }
- switch -- "[lindex [set Diff::array${suff}($mark)] 0]${left}" {
- "c1" -
- "c0" {
- if {[info exists w2]} {
- Diff::BringToFront ${w2}
- gotoMark "diff-$mark"
- set text [getSelect]
- } else {
- # we assume the line is selected in the diff-win
- if {$left} {
- set p [selEnd]
- set ee [search -s -f 1 -r 1 "^---\[^\r\n\]*\$" $p]
- set p [lindex $ee 1]
- if {$context} {
- set e [lindex [search -s -f 1 -r 1 -n {^(\*\*\*|diff)} $p] 0]
- if {$e == ""} {
- set e [maxPos]
- }
- set text [getText $p $e]
- if {$text == "\n" || $text == "\r"} {
- # It was an empty context diff, which means the diff was just
- # contained in the previous half with '-' signs.
- set e [lindex $ee 0]
- set p [nextLineStart [lindex [search -s -f 0 -r 1 {^\*\*\*} $e] 0]]
- set text [getText $p $e]
- regsub -all "\[\n\r\]- \[^\n\r\]*" $text "" text
- regsub -all "\[\n\r\]. " $text "\r" text
- } else {
- regsub -all "\[\n\r\]. " $text "\r" text
- }
- } else {
- set e [search -s -f 1 -r 1 {^[^>]} $p]
- set text [getText $p [lindex $e 0]]
- regsub -all "\[\n\r\]> " $text "\r" text
- }
- set text [string range $text 1 end]
- } else {
- set p [selEnd]
- set e [search -s -f 1 -r 1 {^---} $p]
- if {$context} {
- set text [getText $p [lindex $e 0]]
- regsub -all "\[\n\r\]. " $text "\r" text
- } else {
- set text [getText $p [lindex $e 0]]
- regsub -all "\[\r\n\]< " $text "\r" text
- }
- set text [string range $text 1 end]
- }
- }
- Diff::BringToFront ${w1}
- gotoMark "diff-$mark"
- replaceText [getPos] [selEnd] $text
- }
- "d1" -
- "a0" {
- Diff::BringToFront ${w1}
- gotoMark "diff-$mark"
- deleteText [getPos] [selEnd]
- }
- "a1" -
- "d0" {
- if {[info exists w2]} {
- Diff::BringToFront ${w2}
- gotoMark "diff-$mark"
- set text [getSelect]
- } else {
- # we assume the line is selected in the diff-win
- if {$left} {
- set p [selEnd]
- set e [search -s -f 1 -r 1 "^---\[^\r\n\]*\$" $p]
- set p [lindex $e 1]
- if {$context} {
- set e [lindex [search -s -f 1 -r 1 -n {^(\*\*\*|diff)} $p] 0]
- if {$e == ""} {
- set e [maxPos]
- }
- set text [getText $p $e]
- regsub -all "\[\n\r\]. " $text "\r" text
- } else {
- set e [search -s -f 1 -r 1 {^[^>]} $p]
- set text [getText $p [lindex $e 0]]
- regsub -all "\[\n\r\]> " $text "\r" text
- }
- set text [string range $text 1 end]
- } else {
- set p [selEnd]
- set e [search -s -f 1 -r 1 {^---} $p]
- set text [getText $p [lindex $e 0]]
- regsub -all "\[\n\r\]< " $text "\r" text
- set text [string range $text 1 end]
- }
- }
- Diff::BringToFront ${w1}
- gotoMark "diff-$mark"
- previousLine
- insertText -w ${w1} $text
- }
- default {
- error "Didn't understand the diff to patch!"
- }
-
- }
- Diff::diffWinFront
- }
- proc Diff::patchOld {ww1 ww2 left} {
- upvar \#0 $ww1 w1
- upvar \#0 $ww2 w2
- set code [lindex [Diff::line [getPos]] 0]
- if {[scan [Diff::parseDiffLine $code] "%s %f %f %f %f" \
- char start1 end1 start2 end2] != 5} { return }
-
- switch $char${left} {
- "c1" {
- set text [getText -w ${w2} $start2 $end2]
- bringToFront ${w1}
- replaceText $start1 $end1 $text
- }
- "d1" {
- bringToFront ${w1}
- deleteText $start1 $end1
- }
- "a1" {
- set text [getText -w ${w2} $start2 $end2]
- set p [nextLineStart $start1]
- # for some reason this single line won't work instead of the
- # next two!
- #select -w ${Diff::1} $p $p
- bringToFront ${w1}
- goto $p
- insertText -w ${w1} $text
- }
- "c0" {
- set text [getText -w ${w2} $start1 $end1]
- bringToFront ${w1}
- replaceText $start2 $end2 $text
- }
- "d0" {
- set text [getText -w ${w2} $start1 $end1]
- bringToFront ${w1}
- goto $start2
- nextLine
- insertText $text
- }
- "a0" {
- bringToFront ${w1}
- deleteText $start2 $end2
- }
- }
- message "Subsequent insertions will be screwed up"
- }
-
- # In the diff-window, 'c' = cut from left, replace with given lines,
- # 'd' = delete from left, 'a' = add to left.
- proc Diff::patchIntoLeftWindow {} {
- Diff::patch Diff::1 Diff::2 1
- }
-
- proc Diff::patchIntoRightWindow {} {
- Diff::patch Diff::2 Diff::1 0
- }
-
- # ◊◊◊◊ Main comparison routines ◊◊◊◊ #
-
- proc Diff::files {{orderByDate 1}} {
- global Diff::1 Diff::2
- foreach f [list ${Diff::1} ${Diff::2}] {
- if {[lsearch [winNames -f] [quote::Glob $f]] >= 0} {
- getWinInfo -w $f arr
- if {$arr(dirty)} {
- bringToFront $f
- if {![dialog::yesno "Save this window?"]} { error "Cancel"}
- save
- }
- }
- }
- if {$orderByDate} {
- # make sure newer file is on the right
- if {[file::secondIsOlder ${Diff::1} ${Diff::2}]} {
- set d ${Diff::2}
- set Diff::2 ${Diff::1}
- set Diff::1 $d
- unset d
- }
- }
- Diff::run
- }
-
- proc Diff::run {} {
- global Diff::handler Diff::handlers
- # call the registered procedure
- [set Diff::handlers([set Diff::handler])]
- }
-
- proc Diff::runInsideAlpha {} {
- global Diff::1 Diff::2
- Diff::Display Diff::1 1 0 1
- Diff::Display Diff::2 0 0 1
-
- Diff::execute
- }
-
- proc compare::directories {} {
- global Diff::1 Diff::2
-
- set Diff::1 [get_directory -p "Select 'old' dir 1:"]
- set Diff::2 [get_directory -p "Select 'new' dir 2:"]
-
- Diff::execute 1 {* Directory Comparison *}
- }
-
- proc compare::files {{f1 ""} {f2 ""} {orderByDate 1}} {
- global Diff::1 Diff::2
-
- if {![string length $f1]} {
- set Diff::1 [getfile "Select your 'old' file:"]
- } else {
- set Diff::1 $f1
- }
-
- if {![string length $f2]} {
- set Diff::2 [getfile "Select your 'new' file:"]
- } else {
- set Diff::2 $f2
- }
-
- Diff::files $orderByDate
- }
-
- proc compare::windows {} {
- global tileHeight tileWidth tileTop tileLeft
- global Diff::1 Diff::2
-
- set wins [winNames -f]
- if {[llength $wins] < 2} { message "Need 2 windows"; return }
-
- set Diff::1 [lindex $wins 0]
- set Diff::2 [lindex $wins 1]
- Diff::files
- }
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Diff::execute" --
- #
- # Modification of the original to optionally return the diff
- # result, rather than opening it in a window
- #
- # Results:
- #
- # Returns 1 if the files are the same and 0 if they differ
- #
- # If storeResult is true, the result of the diff operation is stored
- # in the global Diff::result, rather than being opened in a window
- #
- # --Version--Author------------------Changes-------------------------------
- # 1.0 <keleher@cs.umd.edu> original
- # 1.1 <j-guyer@nwu.edu> optionally return diff result in a global
- # 1.2 <j-guyer@nwu.edu> flags set if files were open before compare
- # -------------------------------------------------------------------------
- ##
- proc Diff::execute {{isdir 0} {name {* File Comparison *}} {storeResult 0}} {
- global DiffmodeVars Diff::1 Diff::2 win::Modes HOME \
- diffDir Diff::result Diff::1Open Diff::2Open \
- Diff::leftDir Diff::rightDir DiffSig tcl_platform
-
- set Diff::leftDir ""
- set Diff::rightDir ""
- set diffDir $isdir
-
- message "Launching 'GNU Diff'"
- set flags $DiffmodeVars(diffFlags)
- # Could have problem if 'flags' is not a valid Tcl list.
- if {$DiffmodeVars(linesOfContext) != 0} {
- lappend flags -C $DiffmodeVars(linesOfContext)
- }
- message "Starting diff…"
- # The MacOS diff is a bit peculiar with funny filenames.
- if {$tcl_platform(platform) == "macintosh"} {
- append flags " \"[win::StripCount ${Diff::1}]\" \"[win::StripCount ${Diff::2}]\""
- } else {
- lappend flags [win::StripCount ${Diff::1}] [win::StripCount ${Diff::2}]
- }
- set dtext [app::runScript Diff "Diff application" "" 1 0 $flags]
- message "Starting diff…done"
-
- if {[lsearch -exact [winNames -f] ${Diff::1}] >= 0} {
- set Diff::1Open 1
- } else {
- set Diff::1Open 0
- }
- if {[lsearch -exact [winNames -f] ${Diff::2}] >= 0} {
- set Diff::2Open 1
- } else {
- set Diff::2Open 0
- }
-
- if {![string length $dtext]} {
- if {!$storeResult} {
- alertnote "No difference:\r${Diff::1}\r${Diff::2}"
- }
- return 0
- } else {
- # If requested, return the diff result in Diff::result,
- # rather than opening a diff window
- if {$storeResult} {
- set Diff::result $dtext
- } else {
- Diff::diffWindow $dtext $name
- }
- return 1
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Diff::of" --
- #
- # Used by code like version control stuff which can provide us with
- # the name of a file/window (accessible to Alpha), and the differences
- # between that file/window and some other version. We handle this in
- # the same way as if the user opens a patch file. We open the given
- # $name, and show the differences in a standard Diff window. Then we
- # let the user examine and apply various changes as desired.
- # -------------------------------------------------------------------------
- ##
- proc Diff::of {name difference} {
- global Diff::1 Diff::2 Diff::1Open Diff::2Open diffDir
-
- set diffDir 0
- set Diff::2 $name
- catch {unset Diff::1}
- set Diff::1Open 0
- set Diff::2Open 2
- Diff::diffWindow $difference "Diff of '[file tail $name]'"
- }
-
- proc Diff::displayAll {{name "* File Comparison *"}} {
- global Diff::1 Diff::2 Diff::result
-
- Diff::Display Diff::1 1 0 1
- Diff::Display Diff::2 0 0 1
- Diff::diffWindow ${Diff::result} $name
- }
-
- proc Diff::diffWindow {diffText {name {* File Comparison *}}} {
- global tileLeft tileTop tileWidth tileHeight
-
- set top [expr {$tileTop + $tileHeight - 178}]
- set n [new -n $name -g $tileLeft $top [expr {$tileWidth - 6}] 178 \
- -m Diff -info "\r$diffText\r"]
- select [minPos] [nextLineStart [minPos]]
- Diff::opening $n
- }
-
- # ◊◊◊◊ Moving around ◊◊◊◊ #
- proc Diff::Up {} {
- Diff::move 0
- }
-
- proc Diff::Down {} {
- Diff::move 1
- }
-
- proc Diff::move {dir} {
- global Diff::usesAts
- if {$dir} {
- set pos [pos::math [getPos] + 1]
- } else {
- set pos [pos::math [getPos] - 1]
- }
-
- if {${Diff::usesAts}} {
- set movePattern "^@@ \[^\n\r\]+(\r|\n|\$)"
- } else {
- set movePattern "^\[^-= \n\r\]+(\r|\n|\$)"
- }
-
- if {[catch {search -s -f $dir -r 1 -- ${movePattern} $pos} res]} {
- message "No more diffs"
- return
- }
- set pos [lindex $res 0]
- set line [getText $pos [nextLineStart $pos]]
- if {[string length [string trim $line]] < 2} {
- goto $pos
- return [Diff::move $dir]
- }
- select $pos [nextLineStart $pos]
- display $pos
- message ""
- refresh
- }
-
- proc Diff::Select {} {
- global Diff::1 Diff::2 diffDir
-
- set text [getText [lineStart [getPos]] [pos::math [nextLineStart [getPos]] - 1]]
-
- if {![regexp {[acd]} $text char]} return
- set res [split $text $char]
- if {![scan [lindex $res 0] "%d" one]} return
- if {![scan [lindex $res 1] "%d" two]} return
- if {$one == 1} {incr one}
- if {$two == 1} {incr two}
-
- if {$diffDir} {
- set res [search -s -f 0 -r 1 "^diff\[^\r\n\]*(\r|\n|\$)" [getPos]]
- set text [eval getText $res]
- set len [llength $text]
- set Diff::1 [file::ensureStandardPath [lindex $text [expr {$len - 2}]]]
- set Diff::2 [file::ensureStandardPath [lindex $text [expr {$len - 1}]]]
- }
- Diff::Display Diff::1 1 [expr {$one - 1}] $diffDir
- Diff::Display Diff::2 0 [expr {$two - 1}] $diffDir
-
- if {$diffDir} {
- catch {bringToFront ${Diff::window}}
- }
- }
-
- proc Diff::Display {name left {row 0} {check 0}} {
- global Diff::killWhenDone
-
- upvar $name wname
-
- catch {unset Diff::killWhenDone($wname)}
-
- if {![info exists wname]} {
- if {$left} {
- message "Diff window for left doesn't exist"
- } else {
- message "Diff window for right doesn't exist"
- }
- return
- }
- if {$check} {
- set geo [Diff::Geo $left]
- set res [lsearch -exact [winNames -f] $wname]
- if { $res < 0 } {
- set res [lsearch [winNames -f] "[quote::Glob $wname] <*>"]
- }
- if { $res < 0} {
- eval edit -g $geo [list [win::StripCount $wname]]
- set wname [win::Current]
- # This window was only opend for Diff, so close it when finished
- set Diff::killWhenDone($wname) 1
- } else {
- set wname [lindex [winNames -f] $res]
- if {[getGeometry $wname] != $geo} {
- sizeWin $wname [lindex $geo 2] [lindex $geo 3]
- moveWin $wname [lindex $geo 0] [lindex $geo 1]
- }
- if {$res > 2} {
- bringToFront $wname
- }
- }
- }
- display -w $wname [rowColToPos -w $wname $row 0]
- }
-
- proc Diff::viewSophisticated {} {
- global Diff::1 Diff::2 diffDir DiffmodeVars Diff::Marked
- global Diff::leftDir Diff::rightDir
-
- set text [lindex [Diff::line [getPos] files] 0]
-
- if {[info exists Diff::1]} {
- set old1 ${Diff::1}
- }
- if {[info exists Diff::2]} {
- set old2 ${Diff::2}
- }
-
- if {$diffDir} {
- set Diff::1 [lindex $files 0]
- if {![file exists ${Diff::1}]} {
- set Diff::1 [file join ${Diff::leftDir} ${Diff::1}]
- }
- if {![file exists ${Diff::1}]} {
- if {${Diff::leftDir} == "" && ([set res [lsearch [winNames] "[file tail ${Diff::1}]*"]] != -1)} {
- set Diff::1 [lindex [winNames -f] $res]
- } else {
- unset Diff::1
- }
- } else {
- if {[set res [lsearch [winNames -f] "[quote::Glob ${Diff::1}]*"]] != -1} {
- set Diff::1 [lindex [winNames -f] $res]
- }
- }
- set Diff::2 [lindex $files 1]
- if {![file exists ${Diff::2}]} {
- set Diff::2 [file join ${Diff::rightDir} ${Diff::2}]
- }
- if {![file exists ${Diff::2}]} {
- if {${Diff::rightDir} == "" && ([set res [lsearch [winNames] "[file tail ${Diff::2}]*"]] != -1)} {
- set Diff::2 [lindex [winNames -f] $res]
- } else {
- unset Diff::2
- }
- } else {
- if {[set res [lsearch [winNames -f] "[quote::Glob ${Diff::2}]*"]] != -1} {
- set Diff::2 [lindex [winNames -f] $res]
- }
- }
- }
- if {[info exists Diff::1]} {
- set Diff::1 [file nativename ${Diff::1}]
- }
- if {[info exists Diff::2]} {
- set Diff::2 [file nativename ${Diff::2}]
- # Can happen when the right file doesn't exist.
- if {[info exists Diff::1] && (${Diff::1} == ${Diff::2})} {
- unset Diff::2
- }
- }
- regexp {([^/]+)(.*)} $text "" mark suff
- if {![info exists "Diff::Marked($suff)"]} {
- # Clean up previous Diff pair(s)
- foreach m [array names Diff::Marked] {
- unset Diff::Marked($m)
- }
-
- if {[info exists old1]} {
- Diff::cleanup $old1
- }
- if {[info exists old2]} {
- Diff::cleanup $old2
- }
-
- if {[info exists Diff::1]} {
- Diff::Display Diff::1 1 0 1
- }
- if {[info exists Diff::2]} {
- Diff::Display Diff::2 0 0 1
- }
- Diff::setMarksUp $suff
- if {[info exists Diff::1]} {
- Diff::mark ${Diff::1} 1 $suff
- set Diff::Marked($suff) 1
- }
- if {[info exists Diff::2]} {
- Diff::mark ${Diff::2} 0 $suff
- set Diff::Marked($suff) 1
- }
- Diff::diffWinFront
- }
- set text $mark
- if {$DiffmodeVars(useMarksDontBringToFront)} {
- if {![catch {mark::getRange diff-$text ${Diff::1}} range]} {
- set beg [lindex $range 0]
- set end [lindex $range 2]
- Diff::displayLines ${Diff::1} $beg $end
- }
-
- if {![catch {mark::getRange diff-$text ${Diff::2}} range]} {
- set beg [lindex $range 0]
- set end [lindex $range 2]
- Diff::displayLines ${Diff::2} $beg $end
- }
- # we need this line because of an Alpha visual bug.
- # Alpha will often draw the text in the wrong window when we
- # hit 'down'. It does correct itself, but it looks silly.
- Diff::diffWinFront
- } else {
- if {![catch {Diff::BringToFront ${Diff::1}}]} {
- gotoMark "diff-$text"
- }
- if {![catch {Diff::BringToFront ${Diff::2}}]} {
- gotoMark "diff-$text"
- }
- Diff::diffWinFront
- }
- }
-
- proc Diff::displayLines {win beg end} {
- display -w $win $end
- display -w $win [expr \
- {[pos::compare $beg > [minPos]] \
- ? [pos::math $beg - 1] : $beg}]
- select -w $win $beg $end
- if {[pos::compare $beg > [minPos]]} {
- set beg [pos::math $beg - 1]
- }
- #display -w $win $beg
- #refresh $win
- }
-
- proc Diff::viewOld {} {
- global Diff::1 Diff::2 diffDir
-
- set text [lindex [Diff::line [getPos]] 0]
- if {![regexp {[acd]} $text char]} return
- set res [split $text $char]
- if {![scan [lindex $res 0] "%d,%d" one oned]} return
- if {![scan [lindex $res 1] "%d,%d" two twod]} return
- set on $one
- set tw $two
- if {$on == 1} {incr on}
- if {$tw == 1} {incr tw}
- if {![info exists oned]} {set oned $one}
- if {![info exists twod]} {set twod $two}
-
- if {$diffDir} {
- set res [search -s -f 0 -r 1 "^diff\[^\r\n\]*(\r|\n|\$)" [getPos]]
- set text [eval getText $res]
- set Diff::1 [lindex $text 1]
- set Diff::2 [lindex $text 2]
- }
- Diff::Sel Diff::1 [expr {$on - 1}] $one $oned 1
- Diff::Sel Diff::2 [expr {$tw - 1}] $two $twod 0
- set wins [lremove [lrange [winNames -f] 0 2] ${Diff::1} ${Diff::2}]
- set wins [lremove -glob $wins *Comparison*]
- if {$wins != ""} {
- bringToFront ${Diff::1}
- bringToFront ${Diff::2}
- }
- Diff::diffWinFront
- }
-
- proc Diff::View {} {
- global DiffmodeVars
- if {$DiffmodeVars(useSophisticatedDiffMarking)} {
- Diff::viewSophisticated
- } else {
- Diff::viewOld
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Diff::Sel" --
- #
- # This handles a name either with or without trailing '<n>' and fixes
- # the given name if it isn't right.
- # -------------------------------------------------------------------------
- ##
- proc Diff::Sel {wnamev ro row rowd left} {
- global diffDir
- upvar $wnamev wname
- if {$diffDir} {
- set geo [Diff::Geo $left]
- if {[set res [lsearch [winNames -f] "[quote::Glob $wname]*"]] < 0} {
- eval edit -g $geo [list $wname]
- set wname [win::Current]
- } else {
- set wname [lindex [winNames -f] $res]
- if {[getGeometry $wname] != $geo} {
- sizeWin $wname [lindex $geo 2] [lindex $geo 3]
- moveWin $wname [lindex $geo 0] [lindex $geo 1]
- }
- }
- }
- display -w $wname [rowColToPos -w $wname $ro 0]
- select -w $wname [rowColToPos -w $wname $row 0] \
- [rowColToPos -w $wname [expr {$rowd + 1}] 0]
- }
-
- # ◊◊◊◊ Utilities ◊◊◊◊ #
-
- proc Diff::Win {} {
- global win::Modes
- set files [winNames -f]
- set len [llength $files]
- for {set i 0} {$i < $len} {incr i} {
- if {[set win::Modes([lindex [winNames -f] $i])] == "Diff"} {
- bringToFront [lindex [winNames] $i]
- return
- }
- }
- beep
- message "No Diff window."
- }
-
- proc Diff::Geo {left} {
- global tileWidth tileHeight tileTop tileLeft
-
- set margin 4
- set width [expr {($tileWidth - $margin)/2}]
- set height [expr {$tileHeight - 200}]
- set hor $tileLeft
-
- if {!$left} {incr hor [expr {$width+$margin}]}
-
- return [list $hor $tileTop $width $height]
- }
-
- proc Diff::diffWinFront {} {
- global Diff::window
- catch {bringToFront ${Diff::window}}
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Diff::BringToFront" --
- #
- # Hack to make it quicker to switch between windows. We often want
- # the 'Diff' window to be in the front all the time, but have to
- # bring others to the front temporarily for manipulation. This proc
- # brings a different window to the front more quickly by avoiding
- # all mode-changing code. Of course you should only call this proc
- # when you will _very_ soon bring a different window to the front.
- # -------------------------------------------------------------------------
- ##
- proc Diff::BringToFront {w} {
- global win::Modes DiffmodeVars
- if {$DiffmodeVars(useFastWindowSwapping)} {
- set oldm [set win::Modes($w)]
- set win::Modes($w) Diff
- if {[catch {bringToFront $w}]} {
- unset win::Modes($w)
- beep
- error "no such win"
- } else {
- set win::Modes($w) $oldm
- }
- } else {
- bringToFront $w
- }
- }
-