home *** CD-ROM | disk | FTP | other *** search
/ Macwelt 1 / Macwelt DVD 1.toast / Web-Publishing / HTML-Editoren / Alpha ƒ / Tcl / Modes / diffMode.tcl < prev    next >
Encoding:
Text File  |  2001-01-22  |  40.2 KB  |  1,377 lines

  1. ## -*-Tcl-*-
  2.  # ###################################################################
  3.  #  Vince's Additions - an extension package for Alpha
  4.  # 
  5.  #  FILE: "diffMode.tcl"
  6.  #                                    created: 7/3/95 {11:15:02 pm} 
  7.  #                                last update: 01/22/2001 {23:34:05 PM} 
  8.  #  Author: Vince Darley
  9.  #  E-mail: <vince@santafe.edu>
  10.  #    mail: 317 Paseo de Peralta
  11.  #          Santa Fe, NM 87501, USA
  12.  #     www: <http://www.santafe.edu/~vince/>
  13.  #  
  14.  # improvements Copyright (c) 1997-2000  Vince Darley, all rights reserved
  15.  # 
  16.  #  Description: 
  17.  #  
  18.  #  Largely re-written Diff mode for Alpha.  Still under construction,
  19.  #  but already a lot better than the old one.  Basic features:
  20.  #  
  21.  #  A 'Diff' menu, which contains commonly used options.
  22.  #  
  23.  #  Uses Alpha's 'marks' so that you can patch diffs back and forth
  24.  #  between files without losing the correct location in the file.
  25.  #  (previously if you modified one of the original windows, all line
  26.  #  numbers after that would be incorrect)
  27.  #  
  28.  #  Note: the various additions to cope with context sensitive versus
  29.  #  normal diffs versus cvs-style versus perforce ... mean that this
  30.  #  code is beginning to get rather spaghetti like.  It really needs
  31.  #  a complete overhaul (and probably a small test suite so that we can
  32.  #  ensure the code works for all these formats).
  33.  #  
  34.  #  Limitations:
  35.  #  
  36.  #  Sadly a lot of Alpha's window manipulation commands only work
  37.  #  on the foremost window.  This means this code is slowed down a
  38.  #  lot because it often has to bring a window to the front before
  39.  #  reading/writing into it.  There is a flag to setup a hack which
  40.  #  helps with this, at the expense of colours in the windows.
  41.  #  
  42.  #  The code in this file for patching (and perhaps viewing etc), and
  43.  #  for finding the right file, for deciding whether to display left
  44.  #  or right window or both, etc. is way too convoluted and difficult
  45.  #  to comprehend.  This is basically as a result of the gradual evolution
  46.  #  of this file.  It needs to be rewritten from scratch in much
  47.  #  simplified form.
  48.  # 
  49.  #  History:
  50.  # 
  51.  #  modified   by  rev reason
  52.  #  --------   --- --- -----------
  53.  #  7/3/95     PJK?1.0 original
  54.  #  3/9/97     VMD 2.0 much improved version
  55.  #  03/23/98   VMD and Jon Guyer 2.0-3.0 various fixes and Voodoo 
  56.  #  05/04/2000 JEG 3.1-2 fixed patching
  57.  #  2000-05-05 VMD 3.2.1 minor fix to better handle funny filenames.
  58.  #  2000-06-15 VMD 3.3.1 copes with various cvs/perforce style diffs
  59.  #  2000-08-22 JEG 3.3.2 fixed bug in directory searches with 0 lines of context
  60.  #                       fixed bug in scrolling to previous files in directory searches
  61.  #                       added option (default on) to close document windows when done
  62.  #                       attempts to clean up marks when finished
  63.  # ###################################################################
  64.  ##
  65.  
  66. # Usage: diff [-#] [-abBcdefhHilnNprstTuvw] [-C lines] [-F regexp] [-I regexp]
  67. #        [-L label [-L label]] [-S file] [-D symbol] [+ignore-blank-lines]
  68. #        [+context[=lines]] [+unified[=lines]] [+ifdef=symbol]
  69. #        [+show-function-line=regexp]
  70. #        [+speed-large-files] [+ignore-matching-lines=regexp] [+new-file]
  71. #        [+initial-tab] [+starting-file=file] [+text] [+all-text] [+ascii]
  72. #        [+minimal] [+ignore-space-change] [+ed] [+reversed-ed] [+ignore-case]
  73. #        [+print] [+rcs] [+show-c-function] [+binary] [+brief] [+recursive]
  74. #        [+report-identical-files] [+expand-tabs] [+ignore-all-space]
  75. #        [+file-label=label [+file-label=label]] [+version] path1 path2
  76.  
  77. alpha::mode Diff 3.3.3 diffMenu {*.diff *.patch} {diffMenu} {
  78.     alpha::package require AlphaTcl 7.2.1b5
  79.     addMenu diffMenu •288 Diff
  80.     namespace eval compare {}
  81.     menu::insert Utils submenu 0 compare
  82.     menu::insert compare items end "windows" "files…" "directories…"
  83.     hook::register requireOpenWindowsHook [list compare windows] 2
  84.     newPref sig DiffSig DIFF
  85.     set Diff::handlers(Diff-mode) Diff::runInsideAlpha
  86.     ensureset Diff::handler Diff-mode
  87.     lunion varPrefs(Files) Diff::handler
  88.     # By default Alpha handles the results of diff internally using
  89.     # its Diff mode.  However add-on packages to Alpha can provide
  90.     # alternative Diff handlers.
  91.     newPref var Diff::handler "Diff-mode" global "" Diff::handlers array
  92. } uninstall {
  93.     file delete "$pkg_file"
  94.     file delete [file join ${HOME} Tools "GNU Diff"]
  95. } maintainer {
  96.     "Vince Darley" vince@santafe.edu <http://www.santafe.edu/~vince/>
  97. } help {file "Diff Help"}
  98.  
  99. array set DiffAppSignatures {
  100.     GnuDiff DIFF
  101. }
  102. array set DiffAppScripts {
  103.     GnuDiff {
  104.     {dosc -c $quotedSig -s $flags}
  105.     }
  106. }
  107.  
  108. proc diffMenu {} {}
  109.  
  110. # Generally best to use this setting, but some actions can be a bit
  111. # slower with it on.  Allows you to patch changes back and forth
  112. # between windows automatically, which is otherwise not possible
  113. newPref f useSophisticatedDiffMarking 1 Diff
  114. # A good idea, but can mess up window colours sometimes
  115. # (it's a bit of a hack)
  116. newPref f useFastWindowSwapping 1 Diff
  117. # Slows things down in that it has to scan through Alpha's list of marks
  118. # to find the correct positions for each window, but speeds things up
  119. # because it doesn't need to activate each window in turn.  Try it and see
  120. # for yourself.
  121. newPref f useMarksDontBringToFront 1 Diff
  122. # Up/Down arrows both scroll the diff window and synchronise the viewed
  123. # portion of text in the document windows
  124. newPref f synchroniseMoveAndView 1 Diff Diff::bindUpDown
  125. # You'll probably want this; may slow things down a bit though
  126. newPref f workaroundAlphaColourBug 1 Diff
  127. # Default lines of context to generate when asking Diff to do its magic
  128. newPref var linesOfContext 3 Diff
  129. # Other diff flags you want to send (ignore whitespace etc)
  130. newPref var diffFlags { } Diff
  131. # If you've imported a diff file from a Unix system, this option allows
  132. # you to use it with Alpha too.
  133. newPref f translatePathDelimiters 1 Diff
  134. # If you've imported a diff file from a different directory structure,
  135. # you may need to remove a given prefix so Alpha can find your files
  136. # correctly.
  137. newPref v removeFilePrefix "" Diff
  138. # If the document windows were not already open before the diff, automatically
  139. # close them when finished.
  140. newPref f killWindowsWhenDone 1 Diff
  141.  
  142. Menu -n $diffMenu -p Diff::menuProc -M Diff {
  143.     "rerunDiff"
  144.     "(-"
  145.     "/<I<BpatchIntoLeftWindow"
  146.     "/<I<BpatchIntoRightWindow"
  147.     "(-"
  148.     "cleanUpAndCloseWindows"
  149.     "(-"
  150.     "locateLeftWindow"
  151.     "locateRightWindow"
  152.     "locateLeftDir"
  153.     "locateRightDir"
  154.     "parseDiffWin"
  155. }
  156. Bind 0x7b <z> Diff::patchIntoLeftWindow Diff
  157. Bind 0x7c <z> Diff::patchIntoRightWindow Diff
  158.  
  159. if {[info tclversion] < 8.0} {
  160.     # Bind manually due to bug
  161.     Bind 0x7b <oz> Diff::patchIntoLeftWindow Diff
  162.     Bind 0x7c <oz> Diff::patchIntoRightWindow Diff
  163. }
  164. # do the rest
  165. Bind '\r'        Diff::Select    Diff
  166. Bind '\t'        Diff::View    Diff
  167. Bind Kpad. <c>        Diff::Win
  168. Bind Enter        {Diff::Down;Diff::Select}    Diff
  169. Bind Kpad0        {Diff::Up;Diff::Select}    Diff
  170.  
  171. hook::register closeHook Diff::closing Diff
  172. hook::register openHook Diff::opening Diff
  173.  
  174. proc Diff::bindUpDown {} {
  175.     global DiffmodeVars
  176.     if {$DiffmodeVars(synchroniseMoveAndView)} {
  177.     catch {unBind down         Diff::Down Diff}
  178.     catch {unBind up         Diff::Up Diff}
  179.     Bind down        {Diff::Down;Diff::View}    Diff
  180.     Bind up        {Diff::Up;Diff::View}    Diff
  181.     } else {
  182.     catch {unBind down        {Diff::Down;Diff::View}    Diff}
  183.     catch {unBind up        {Diff::Up;Diff::View}    Diff}
  184.     Bind down         Diff::Down Diff
  185.     Bind up         Diff::Up Diff
  186.     }
  187. }
  188.  
  189. Diff::bindUpDown
  190.  
  191. proc Diff::menuProc {menu item} {
  192.     Diff::$item
  193. }
  194.  
  195. proc Diff::locateLeftWindow {} {
  196.     global Diff::1
  197.     set Diff::1 [getfile "Select your left (old) file:"]
  198.     Diff::Display Diff::1 1 0 1
  199.     Diff::setMarksUp
  200.     if {[info exists Diff::1]} {Diff::mark ${Diff::1} 1 ""}
  201.     Diff::diffWinFront
  202. }
  203.  
  204. proc Diff::locateRightWindow {} {
  205.     global Diff::2
  206.     set Diff::2 [getfile "Select your right (new) file:"]
  207.     Diff::Display Diff::2 0 0 1
  208.     Diff::setMarksUp
  209.     if {[info exists Diff::2]} {Diff::mark ${Diff::2} 0 ""}
  210.     Diff::diffWinFront
  211. }
  212.  
  213. proc Diff::locateLeftDir {} {
  214.     global Diff::leftDir
  215.     set Diff::leftDir [get_directory "Select your left (old) directory:"]
  216. }
  217. proc Diff::locateRightDir {} {
  218.     global Diff::rightDir
  219.     set Diff::rightDir [get_directory "Select your right (new) directory:"]
  220. }
  221.  
  222. proc Diff::rerunDiff {} {
  223.     global diffDir Diff::1 Diff::2
  224.     Diff::diffWinFront
  225.     catch {set d1 ${Diff::1}}
  226.     catch {set d2 ${Diff::2}}
  227.     killWindow
  228.     catch {set Diff::1 $d1}
  229.     catch {set Diff::2 $d2}
  230.     if {$diffDir} {
  231.     Diff::execute 1 {* Directory Comparison *}
  232.     } else {
  233.     Diff::files
  234.     }
  235. }
  236.  
  237. proc Diff::cleanUpAndCloseWindows {} {
  238.     global Diff::1 Diff::2 diffDir
  239.     if {![catch {bringToFront ${Diff::1}}]} {
  240.     removeAllMarks diff-*
  241.     shrinkFull
  242.     killWindow
  243.     }
  244.     
  245.     if {![catch {bringToFront ${Diff::2}}]} {
  246.     removeAllMarks diff-*
  247.     shrinkFull
  248.     killWindow
  249.     }
  250.     Diff::diffWinFront
  251.     killWindow
  252. }
  253.  
  254. proc Diff::closing {{name ""}} {
  255.     global Diff::array Diff::Marked Diff::1 Diff::2
  256.     foreach var [uplevel \#0 info vars Diff::array*] {
  257.     global $var
  258.     if {[uplevel \#0 array exists $var]} { unset $var }
  259.     }
  260.     catch {unset Diff::Marked}
  261.     Diff::cleanup ${Diff::1}
  262.     catch {unset Diff::1}
  263.     Diff::cleanup ${Diff::2}
  264.     catch {unset Diff::2}
  265. }
  266.  
  267. ## 
  268.  # -------------------------------------------------------------------------
  269.  # 
  270.  # "Diff::opening" --
  271.  # 
  272.  #  This procedure is called whenever we open a diff window, whether 
  273.  #  a '.diff' file, or whether a window produced by this mode using
  274.  #  'Diff::execute'.  We parse its contents.
  275.  # -------------------------------------------------------------------------
  276.  ##
  277. proc Diff::opening {name} {
  278.     global Diff::window DiffmodeVars Diff::leftDir Diff::rightDir Diff::1 Diff::2
  279.     set Diff::window $name
  280.     set Diff::leftDir ""
  281.     set Diff::rightDir ""
  282.     
  283.     set files [Diff::getFiles [minPos]]
  284.     if {[file exists [set cur [win::StripCount [win::Current]]]]} {
  285.     set cur [file dirname $cur]
  286.     set f0 [lindex $files 0]
  287.     if {[file exists [file join $cur $f0]]} {
  288.         set Diff::leftDir $cur
  289.         set Diff::1 [file join $cur $f0]
  290.     }
  291.     set f1 [lindex $files 1]
  292.     if {[file exists [file join $cur $f1]]} {
  293.         set Diff::rightDir $cur
  294.         set Diff::2 [file join $cur $f1]
  295.     }
  296.     }
  297.  
  298.     global Diff::usesAts
  299.     set Diff::usesAts 0
  300.     if {$DiffmodeVars(useSophisticatedDiffMarking)} {
  301.     Diff::parseDiffWin
  302.     }
  303. }
  304.  
  305. # ◊◊◊◊ Parsing diff information ◊◊◊◊ #
  306.  
  307. proc Diff::parseDiffWin {} {
  308.     Diff::diffWinFront
  309.     global diffDir Diff::window DiffmodeVars Diff::usesAts
  310.     
  311.     # Should really by a per-window basis.  This is the default
  312.     # pattern.  Cvs style diffs (with @@) need a different pattern,
  313.     # since this pattern can select lines of diff-content rather
  314.     # than the preamble.
  315.     set Diff::usesAts 0 
  316.     # By default its not a directory
  317.     set diffDir 0
  318.     
  319.     set pos [minPos]
  320.     while 1 {
  321.     set res [search -s -n -f 1 -r 1 "^((diff\[^\r\n\]*|==== \[^\r\n\]*|\[^- \n\r\]+)(\r|\n|\$)|@@ )" $pos]
  322.     if {[llength $res]} {
  323.         set pos [pos::math [lindex $res 0] + 1]
  324.         # If we picked up a 'diff...' line followed by a line starting with one
  325.         # or more *'s, it a part of a context diff, announcing a new file
  326.         # we simply ignore this.
  327.         set foundText [getText [lindex $res 0] [pos::math [lindex $res 0] + 4]]
  328.         if {$foundText == "diff" || $foundText == "===="} {
  329.         set nextStart [getText [nextLineStart [lindex $res 0]] [pos::math [nextLineStart [lindex $res 0]] +3]]
  330.         # The first case is a context diff, the second a Cvs context diff on a single
  331.         # file.
  332.         if {$nextStart == "***" || $nextStart == "---"} {
  333.             continue
  334.         }
  335.         if {$foundText == "diff"} {set diffDir 1}
  336.         }
  337.         set t [getText [lindex $res 0] [pos::math [lindex $res 1] - 1]]
  338.         if {[string length $t] == 1} {
  339.         continue
  340.         }
  341.         if {[regexp {^=+$} $t]} {
  342.         # A dividing line in a cvs-style diff
  343.         continue
  344.         } elseif {[regexp {^\*+$} $t]} {
  345.         # It's a diff over a directory
  346.         set diffDir 1
  347.         # check if the file has changed
  348.         if {[string index [set tt [getText [prevLineStart $pos] $pos]] 0] != " " \
  349.           && [lookAt [pos::math $pos - 3]] != "-" } {
  350.             newforeach {from to} [Diff::parseLineIntoFromTo $tt $pos] {break}
  351.             if {$DiffmodeVars(translatePathDelimiters)} {
  352.             set from [file::translatePathDelimiters $from]
  353.             set to [file::translatePathDelimiters $to]
  354.             }
  355.             lappend got [list "diff" $from $to]
  356.         }
  357.         set from [lindex [eval getText [search -s -n -f 1 -r 1 {^\*\*\* [0-9]+,[0-9]+} $pos]] 1]
  358.         set to [lindex [eval getText [search -s -n -f 1 -r 1 {^--- [0-9]+,[0-9]+} $pos]] 1]
  359.         lappend got [list $from $to]
  360.         } elseif {$t == "@@"} {
  361.         # cvs diff over a single file.
  362.         set Diff::usesAts 1
  363.         set line [getText [lindex $res 0] [nextLineStart [lindex $res 0]]]
  364.         lappend got [Diff::parseAtsIntoLine $line]
  365.         set res [search -s -n -f 1 -r 1 "^@@ " [lindex $res 1]]
  366.         if {[llength $res]} {
  367.             set pos [lindex $res 0]
  368.         } else {
  369.             break
  370.         }
  371.         } else {
  372.         lappend got $t
  373.         }
  374.     } else {
  375.         break
  376.     }
  377.     }
  378.     set Diff::window [win::Current]
  379.     # now stored all diff items in the list 'got'
  380.     if {[info exists got]} {
  381.     if {!$diffDir} {
  382.         set f [lindex $got 0]
  383.         if {[string range $f 0 3] == "diff"} {
  384.         set got [lrange $got 1 end]
  385.         }
  386.     }
  387.     Diff::storeMarks $got
  388.     }
  389.     Diff::diffWinFront
  390.     global tileTop tileWidth tileHeight tileLeft
  391.     set top [expr {$tileTop + $tileHeight - 178}]
  392.     sizeWin ${Diff::window} [expr {$tileWidth - 6}] 178
  393.     moveWin ${Diff::window} $tileLeft $top
  394.     
  395. }
  396.  
  397. proc Diff::parseLineIntoFromTo {tt pos} {
  398.     if {[regexp {^==== } $tt]} {
  399.     # probably a perforce style diff
  400.     regexp "^==== (\[^\r\n\]*) - (\[^\r\n\]*) ===" $tt "" from to
  401.     } else {
  402.     set to [lindex $tt 1]
  403.     regexp " (\[^\t\]*)" $tt "" to
  404.     set p [prevLineStart $pos]
  405.     set fileline [getText [prevLineStart $p] $p]
  406.     regexp " (\[^\t\]*)" $fileline "" from
  407.     }
  408.     return [list $from $to]
  409. }
  410.  
  411. proc Diff::parseAtsIntoLine {line} {
  412.     regexp { -([0-9,]+) \+([0-9,]+) } $line "" from to
  413.     set from [split $from ,]
  414.     set to [split $to ,]
  415.     set from "[lindex $from 0],[expr {[lindex $from 0] + [lindex $from 1] -1}]"
  416.     set to "[lindex $to 0],[expr {[lindex $to 0] + [lindex $to 1] -1}]"
  417.     return [list $from $to]
  418. }
  419.  
  420. proc Diff::storeMarks {diffs} {
  421.     global Diff::1 Diff::2 Diff::array
  422.     set suff ""
  423.     foreach m $diffs {
  424.     if {[regexp {^diff} $m]} {
  425.         set suff "/[file tail [lindex $m end]]"
  426.         global Diff::array${suff}
  427.         continue
  428.     }
  429.     set Diff::array${suff}($m) ""
  430.     }
  431. }
  432.  
  433. proc Diff::setMarksUp {{suff ""}} {
  434.     global Diff::array${suff}
  435.     foreach m [array names Diff::array$suff] {
  436.     set scanned [Diff::parseDiffString $m]
  437.     if {[scan $scanned "%s %f %f %f %f" \
  438.       char start1 end1 start2 end2] != 5} { error "Bad diff list!" }
  439.     if {$scanned != ""} {
  440.         set Diff::array${suff}($m) $scanned
  441.     }
  442.     }
  443. }
  444.  
  445. proc Diff::cleanup {win} {
  446.     global DiffmodeVars Diff::killWhenDone
  447.     if {$win != "" && [lsearch [winNames -f] "[quote::Glob $win]"] >= 0} {
  448.     # Alpha somehow remembers the last mode in which it adjusts
  449.     # the window and so forgets all the colours if we cheat the
  450.     # mode switch.
  451.     if {$DiffmodeVars(workaroundAlphaColourBug)} {
  452.         bringToFront $win
  453.     } else {
  454.         Diff::BringToFront $win
  455.     }
  456.     removeAllMarks diff-*
  457.     
  458.     if {$DiffmodeVars(killWindowsWhenDone)
  459.     && [info exists Diff::killWhenDone($win)]} {
  460.         killWindow
  461.         unset Diff::killWhenDone($win)
  462.     }
  463.     }
  464. }
  465.  
  466. proc Diff::mark {win left {suff ""}} {
  467.     global Diff::array$suff DiffmodeVars
  468.     if {$win != ""} {
  469.     # Alpha somehow remembers the last mode in which it adjusts
  470.     # the window and so forgets all the colours if we cheat the
  471.     # mode switch.
  472.     if {$DiffmodeVars(workaroundAlphaColourBug)} {
  473.         bringToFront $win
  474.     } else {
  475.         Diff::BringToFront $win
  476.     }
  477.     # not strictly necessary, but cleaner
  478.     removeAllMarks diff-*
  479.     if {$left} {
  480.         foreach m [array names Diff::array$suff] {
  481.         scan [set Diff::array${suff}($m)] "%s %f %f" char start1 end1
  482.         setNamedMark "diff-$m" $start1 $start1 $end1
  483.         }
  484.     } else {
  485.         foreach m [array names Diff::array$suff] {
  486.         scan [set Diff::array${suff}($m)] "%s %f %f %f %f" char start1 end1 start2 end2
  487.         setNamedMark "diff-$m" $start2 $start2 $end2
  488.         }
  489.     }
  490.     }
  491. }
  492.  
  493. proc Diff::markUpWindow {diffs} {
  494.     alertnote "Currently a little obsolete; shouldn't be called!"
  495.     if {[info exists Diff::1]} {
  496.     Diff::BringToFront ${Diff::1}
  497.     # not strictly necessry, but cleaner
  498.     removeAllMarks diff-*
  499.     foreach m $diffs {
  500.         scan [set Diff::array($m)] "%s %f %f" char start1 end1
  501.         setNamedMark "diff-$m" $start1 $start1 $end1
  502.     }
  503.     }
  504.     if {[info exists Diff::2]} {
  505.     Diff::BringToFront ${Diff::2}
  506.     # not strictly necessry, but cleaner
  507.     removeAllMarks diff-*
  508.     foreach m $diffs {
  509.         scan [set Diff::array($m)] "%s %f %f %f %f" char start1 end1 start2 end2
  510.         setNamedMark "diff-$m" $start2 $start2 $end2
  511.     }
  512.     }
  513.     
  514. }
  515.  
  516. proc Diff::parseDiffString {text} {
  517.     global Diff::1 Diff::2
  518.     if {![regexp {[acd]} $text char]} {
  519.     # context sensitive
  520.     set char "c"
  521.     if {[scan $text "%d,%d %d,%d" one oned two twod] != 4} {
  522.         return
  523.     }
  524.     } else {
  525.     set res [split $text $char]
  526.     if {![scan [lindex $res 0] "%d,%d" one oned]} return
  527.     if {![scan [lindex $res 1] "%d,%d" two twod]} return
  528.     if {![info exists oned]} { set oned $one }
  529.     if {![info exists twod]} { set twod $two }
  530.     }
  531.     
  532.     if {[info exists Diff::1]} {
  533.     if {$char != "a"} {
  534.         set res [list $char [rowColToPos -w ${Diff::1} $one 0]]
  535.         lappend res [rowColToPos -w ${Diff::1} [expr {$oned + 1}] 0]
  536.     } else {
  537.         # Can $one and $oned ever be different for an 'a'?
  538.         # If so, this will be Bad
  539.         set res [list $char [rowColToPos -w ${Diff::1} [expr {$one + 1}] 0]]
  540.         lappend res [rowColToPos -w ${Diff::1} [expr {$oned + 1}] 1]
  541.     }
  542.     } else {
  543.     set res [list $char -1 -1]
  544.     }
  545.     
  546.     if {[info exists Diff::2]} {
  547.     if {$char != "d"} {
  548.         lappend res [rowColToPos -w ${Diff::2} $two 0]
  549.         lappend res [rowColToPos -w ${Diff::2} [expr {$twod + 1}] 0]
  550.     } else {
  551.         # Can $two and $twod ever be different for a 'd'?
  552.         # If so, this will be Bad
  553.         lappend res [rowColToPos -w ${Diff::2} [expr {$two + 1}] 0]
  554.         lappend res [rowColToPos -w ${Diff::2} [expr {$twod + 1}] 1]
  555.     }
  556.     } else {
  557.     lappend res -1 -1
  558.     }
  559.     return $res
  560. }
  561.  
  562. proc Diff::parseDiffLine {text {is_pos 0}} {
  563.     if {$is_pos} {
  564.     set text [lindex [Diff::line $text] 0]
  565.     }
  566.     return [Diff::parseDiffString $text]
  567. }
  568.  
  569. proc Diff::getFiles {pos} {
  570.     global DiffmodeVars
  571.     set llen [llength [set files [getText $pos [nextLineStart $pos]]]]
  572.     set files [lrange $files [expr {$llen -2}] end]
  573.     if {$DiffmodeVars(translatePathDelimiters)} {
  574.     set files [file::translatePathDelimiters $files]
  575.     }
  576.     return $files
  577. }
  578.  
  579. proc Diff::line {pos {f ""}} {
  580.     global diffDir Diff::window DiffmodeVars Diff::usesAts
  581.     set context 0
  582.     if {$diffDir} {
  583.     if {$f != ""} {upvar $f files}
  584.     if {[lookAt $pos] == "*" || [catch {search -s -f 0 -r 1 "^(diff|==== )\[^\r\n\]*(\r|\n|\$)" $pos} res]} {
  585.         set p $pos
  586.         while 1 {
  587.         set res [search -s -f 0 -r 1 "^\\*+(\r|\n|\$)" $p]
  588.         set p [pos::math [lindex $res 0] - 2]
  589.         if {[lookAt [lineStart $p]] != " " && [lookAt $p] != "-"} break
  590.         }
  591.         set toline [getText [lineStart $p] $p]
  592.         if {[regexp {^==== } $toline]} {
  593.         newforeach {from to} [Diff::parseLineIntoFromTo $toline $p] {break}
  594.         } else {
  595.         regexp " (.*)\t" $toline "" to
  596.         regexp " (.*)\t" [getText [prevLineStart $p] [lineStart $p]] "" from
  597.         }
  598.         if {[set pr $DiffmodeVars(removeFilePrefix)] != ""} {
  599.         regsub -all "/\./" $to "/" to
  600.         if {[string first $pr $to] == 0} {
  601.             set to [string range $to [string length $pr] end]
  602.         }
  603.         regsub -all "/\./" $from "/" from
  604.         if {[string first $pr $from] == 0} {
  605.             set from [string range $from [string length $pr] end]
  606.         }
  607.         }
  608.         set files [list [file::ensureStandardPath $from] [file::ensureStandardPath $to]]
  609.         set tfrom [lindex [eval getText [search -s -n -f 1 -r 1 {^\*\*\* [0-9]+,[0-9]+} [getPos]]] 1]
  610.         set tto [lindex [eval getText [search -s -n -f 1 -r 1 {^--- [0-9]+,[0-9]+} [getPos]]] 1]
  611.         set context 1
  612.         set text "$tfrom $tto"        
  613.     } else {
  614.         set llen [llength [set files [eval getText $res]]]
  615.         set files [lrange $files [expr {$llen -2}] end]
  616.         set text [getText [lineStart $pos] [pos::math [nextLineStart $pos] - 1]]
  617.     }
  618.     if {$DiffmodeVars(translatePathDelimiters)} {
  619.         foreach ff $files {
  620.         lappend nfiles [file::translatePathDelimiters $ff]
  621.         }
  622.         set files $nfiles
  623.     }
  624.     set f [lindex $files end]
  625.     set suff "/[file tail $f]"
  626.     } else {
  627.     set suff ""
  628.     set text [getText [lineStart $pos] [pos::math [nextLineStart $pos] - 1]]
  629.     if {${Diff::usesAts}} {
  630.         set text [Diff::parseAtsIntoLine $text]
  631.     }
  632.     }
  633.     return [list ${text}${suff} $context]
  634. }
  635.  
  636. # ◊◊◊◊ Patching routines ◊◊◊◊ #
  637. proc Diff::patch {w1 w2 left} {
  638.     global DiffmodeVars
  639.     if {$DiffmodeVars(useSophisticatedDiffMarking)} {
  640.     Diff::patchSophisticated $w1 $w2 $left
  641.     } else {
  642.     Diff::patchOld $w1 $w2 $left
  643.     }
  644. }
  645. proc Diff::patchSophisticated {ww1 ww2 left} {
  646.     upvar \#0 $ww1 w1
  647.     upvar \#0 $ww2 w2
  648.     set code [Diff::line [getPos]]
  649.     regexp {([^/]+)(.*)} [lindex $code 0] "" mark suff
  650.     set context [lindex $code 1]
  651.     global Diff::array${suff}
  652.     if {![info exists w1]} { dialog::errorAlert "No such window" }
  653.     switch -- "[lindex [set Diff::array${suff}($mark)] 0]${left}" {
  654.     "c1" -
  655.     "c0" {
  656.         if {[info exists w2]} { 
  657.         Diff::BringToFront ${w2}
  658.         gotoMark "diff-$mark"
  659.         set text [getSelect]
  660.         } else {
  661.         # we assume the line is selected in the diff-win
  662.         if {$left} {
  663.             set p [selEnd]
  664.             set ee [search -s -f 1 -r 1 "^---\[^\r\n\]*\$" $p]
  665.             set p [lindex $ee 1]
  666.             if {$context} {
  667.             set e [lindex [search -s -f 1 -r 1 -n {^(\*\*\*|diff)} $p] 0]
  668.             if {$e == ""} {
  669.                 set e [maxPos]
  670.             } 
  671.             set text [getText $p $e]
  672.             if {$text == "\n" || $text == "\r"} {
  673.                 # It was an empty context diff, which means the diff was just
  674.                 # contained in the previous half with '-' signs.
  675.                 set e [lindex $ee 0]
  676.                 set p [nextLineStart [lindex [search -s -f 0 -r 1  {^\*\*\*} $e] 0]]
  677.                 set text [getText $p $e]
  678.                 regsub -all "\[\n\r\]- \[^\n\r\]*" $text "" text
  679.                 regsub -all "\[\n\r\]. " $text "\r" text
  680.             } else {
  681.                 regsub -all "\[\n\r\]. " $text "\r" text
  682.             }
  683.             } else {
  684.             set e [search -s -f 1 -r 1 {^[^>]} $p]
  685.             set text [getText $p [lindex $e 0]]
  686.             regsub -all "\[\n\r\]> " $text "\r" text
  687.             }
  688.             set text [string range $text 1 end]
  689.         } else {
  690.             set p [selEnd]
  691.             set e [search -s -f 1 -r 1 {^---} $p]
  692.             if {$context} {
  693.             set text [getText $p [lindex $e 0]]
  694.             regsub -all "\[\n\r\]. " $text "\r" text
  695.             } else {
  696.             set text [getText $p [lindex $e 0]]
  697.             regsub -all "\[\r\n\]< " $text "\r" text
  698.             }
  699.             set text [string range $text 1 end]
  700.         }
  701.         }
  702.         Diff::BringToFront ${w1}
  703.         gotoMark "diff-$mark"
  704.         replaceText [getPos] [selEnd] $text
  705.     }
  706.     "d1" -
  707.     "a0" {
  708.         Diff::BringToFront ${w1}
  709.         gotoMark "diff-$mark"
  710.         deleteText [getPos] [selEnd]
  711.     }
  712.     "a1" -
  713.     "d0" {
  714.         if {[info exists w2]} { 
  715.         Diff::BringToFront ${w2}
  716.         gotoMark "diff-$mark"
  717.         set text [getSelect]
  718.         } else {
  719.         # we assume the line is selected in the diff-win
  720.         if {$left} {
  721.             set p [selEnd]
  722.             set e [search -s -f 1 -r 1 "^---\[^\r\n\]*\$" $p]
  723.             set p [lindex $e 1]
  724.             if {$context} {
  725.             set e [lindex [search -s -f 1 -r 1 -n {^(\*\*\*|diff)} $p] 0]
  726.             if {$e == ""} {
  727.                 set e [maxPos]
  728.             } 
  729.             set text [getText $p $e]
  730.             regsub -all "\[\n\r\]. " $text "\r" text
  731.             } else {
  732.             set e [search -s -f 1 -r 1 {^[^>]} $p]
  733.             set text [getText $p [lindex $e 0]]
  734.             regsub -all "\[\n\r\]> " $text "\r" text
  735.             }
  736.             set text [string range $text 1 end]
  737.         } else {
  738.             set p [selEnd]
  739.             set e [search -s -f 1 -r 1 {^---} $p]
  740.             set text [getText $p [lindex $e 0]]
  741.             regsub -all "\[\n\r\]< " $text "\r" text
  742.             set text [string range $text 1 end]
  743.         }
  744.         }
  745.         Diff::BringToFront ${w1}
  746.         gotoMark "diff-$mark"
  747.         previousLine
  748.         insertText -w ${w1} $text
  749.     }
  750.     default {
  751.         error "Didn't understand the diff to patch!"
  752.     }
  753.     
  754.     }
  755.     Diff::diffWinFront
  756. }
  757. proc Diff::patchOld {ww1 ww2 left} {
  758.     upvar \#0 $ww1 w1
  759.     upvar \#0 $ww2 w2
  760.     set code [lindex [Diff::line [getPos]] 0]
  761.     if {[scan [Diff::parseDiffLine $code] "%s %f %f %f %f" \
  762.       char start1 end1 start2 end2] != 5} { return }
  763.     
  764.     switch $char${left} {
  765.     "c1" {
  766.         set text [getText -w ${w2} $start2 $end2]
  767.         bringToFront ${w1}
  768.         replaceText $start1 $end1 $text
  769.     }
  770.     "d1" {
  771.         bringToFront ${w1}
  772.         deleteText $start1 $end1
  773.     }
  774.     "a1" {
  775.         set text [getText -w ${w2} $start2 $end2]
  776.         set p [nextLineStart $start1]
  777.         # for some reason this single line won't work instead of the
  778.         # next two!
  779.         #select -w ${Diff::1} $p $p
  780.         bringToFront ${w1}
  781.         goto $p
  782.         insertText -w ${w1} $text
  783.     }
  784.     "c0" {
  785.         set text [getText -w ${w2} $start1 $end1]
  786.         bringToFront ${w1}
  787.         replaceText $start2 $end2 $text
  788.     }
  789.     "d0" {
  790.         set text [getText -w ${w2} $start1 $end1]
  791.         bringToFront ${w1}
  792.         goto $start2
  793.         nextLine
  794.         insertText $text
  795.     }
  796.     "a0" {
  797.         bringToFront ${w1}
  798.         deleteText $start2 $end2
  799.     }
  800.     }
  801.     message "Subsequent insertions will be screwed up"
  802. }
  803.  
  804. # In the diff-window, 'c' = cut from left, replace with given lines,
  805. # 'd' = delete from left, 'a' = add to left.
  806. proc Diff::patchIntoLeftWindow {} {
  807.     Diff::patch Diff::1 Diff::2 1
  808. }
  809.  
  810. proc Diff::patchIntoRightWindow {} {
  811.     Diff::patch Diff::2 Diff::1 0
  812. }
  813.  
  814. # ◊◊◊◊ Main comparison routines ◊◊◊◊ #
  815.  
  816. proc Diff::files {{orderByDate 1}} {
  817.     global Diff::1 Diff::2
  818.     foreach f [list ${Diff::1} ${Diff::2}] {
  819.     if {[lsearch [winNames -f] [quote::Glob $f]] >= 0} {
  820.         getWinInfo -w $f arr
  821.         if {$arr(dirty)} {
  822.         bringToFront $f
  823.         if {![dialog::yesno "Save this window?"]} { error "Cancel"}
  824.         save
  825.         }
  826.     }
  827.     }
  828.     if {$orderByDate} {
  829.     # make sure newer file is on the right
  830.     if {[file::secondIsOlder ${Diff::1} ${Diff::2}]} {
  831.         set d ${Diff::2}
  832.         set Diff::2 ${Diff::1}
  833.         set Diff::1 $d
  834.         unset d
  835.     }
  836.     }
  837.     Diff::run
  838. }
  839.  
  840. proc Diff::run {} {
  841.     global Diff::handler Diff::handlers
  842.     # call the registered procedure
  843.     [set Diff::handlers([set Diff::handler])]
  844. }
  845.  
  846. proc Diff::runInsideAlpha {} {
  847.     global Diff::1 Diff::2
  848.     Diff::Display Diff::1 1 0 1
  849.     Diff::Display Diff::2 0 0 1
  850.  
  851.     Diff::execute
  852. }
  853.  
  854. proc compare::directories {} {
  855.     global Diff::1 Diff::2
  856.     
  857.     set Diff::1 [get_directory -p "Select 'old' dir 1:"]
  858.     set Diff::2 [get_directory -p "Select 'new' dir 2:"]
  859.     
  860.     Diff::execute 1 {* Directory Comparison *}
  861. }
  862.  
  863. proc compare::files {{f1 ""} {f2 ""} {orderByDate 1}} {
  864.     global Diff::1 Diff::2
  865.     
  866.     if {![string length $f1]} {
  867.     set Diff::1 [getfile "Select your 'old' file:"]
  868.     } else {
  869.     set Diff::1 $f1
  870.     }
  871.     
  872.     if {![string length $f2]} {
  873.     set Diff::2 [getfile "Select your 'new' file:"]
  874.     } else {
  875.     set Diff::2 $f2
  876.     }
  877.     
  878.     Diff::files $orderByDate
  879. }
  880.  
  881. proc compare::windows {} {
  882.     global tileHeight tileWidth tileTop tileLeft
  883.     global Diff::1 Diff::2
  884.     
  885.     set wins [winNames -f]
  886.     if {[llength $wins] < 2} { message "Need 2 windows"; return }
  887.     
  888.     set Diff::1 [lindex $wins 0]
  889.     set Diff::2 [lindex $wins 1]
  890.     Diff::files
  891. }
  892.  
  893.  
  894. ## 
  895.  # -------------------------------------------------------------------------
  896.  # 
  897.  # "Diff::execute" --
  898.  # 
  899.  #  Modification of the original to optionally return the diff 
  900.  #  result, rather than opening it in a window
  901.  # 
  902.  # Results:
  903.  # 
  904.  #  Returns 1 if the files are the same and 0 if they differ
  905.  #  
  906.  #  If storeResult is true, the result of the diff operation is stored 
  907.  #  in the global Diff::result, rather than being opened in a window
  908.  # 
  909.  # --Version--Author------------------Changes-------------------------------
  910.  #    1.0     <keleher@cs.umd.edu> original
  911.  #    1.1     <j-guyer@nwu.edu> optionally return diff result in a global
  912.  #    1.2     <j-guyer@nwu.edu> flags set if files were open before compare
  913.  # -------------------------------------------------------------------------
  914.  ##
  915. proc Diff::execute {{isdir 0} {name {* File Comparison *}} {storeResult 0}} {
  916.     global DiffmodeVars Diff::1 Diff::2 win::Modes HOME \
  917.       diffDir Diff::result Diff::1Open Diff::2Open \
  918.       Diff::leftDir Diff::rightDir DiffSig tcl_platform
  919.     
  920.     set Diff::leftDir ""
  921.     set Diff::rightDir ""
  922.     set diffDir $isdir
  923.     
  924.     message "Launching 'GNU Diff'"
  925.     set flags $DiffmodeVars(diffFlags)
  926.     # Could have problem if 'flags' is not a valid Tcl list.
  927.     if {$DiffmodeVars(linesOfContext) != 0} {
  928.     lappend flags -C $DiffmodeVars(linesOfContext)
  929.     }
  930.     message "Starting diff…"
  931.     # The MacOS diff is a bit peculiar with funny filenames.
  932.     if {$tcl_platform(platform) == "macintosh"} {
  933.       append flags " \"[win::StripCount ${Diff::1}]\" \"[win::StripCount ${Diff::2}]\""
  934.     } else {
  935.     lappend flags [win::StripCount ${Diff::1}] [win::StripCount ${Diff::2}]
  936.     }
  937.     set dtext [app::runScript Diff "Diff application" "" 1 0 $flags]
  938.     message "Starting diff…done"
  939.     
  940.     if {[lsearch -exact [winNames -f] ${Diff::1}] >= 0} {
  941.     set Diff::1Open 1
  942.     } else {
  943.     set Diff::1Open 0
  944.     }
  945.     if {[lsearch -exact [winNames -f] ${Diff::2}] >= 0} {
  946.     set Diff::2Open 1
  947.     } else {
  948.     set Diff::2Open 0
  949.     }
  950.     
  951.     if {![string length $dtext]} {
  952.     if {!$storeResult} {
  953.         alertnote    "No difference:\r${Diff::1}\r${Diff::2}"
  954.     }
  955.     return 0
  956.     } else {    
  957.     # If requested, return the diff result in Diff::result, 
  958.     # rather than opening a diff window
  959.     if {$storeResult} {
  960.         set Diff::result $dtext
  961.     } else {
  962.         Diff::diffWindow $dtext $name            
  963.     }
  964.     return 1
  965.     }
  966. }
  967.  
  968. ## 
  969.  # -------------------------------------------------------------------------
  970.  # 
  971.  # "Diff::of" --
  972.  # 
  973.  #  Used by code like version control stuff which can provide us with
  974.  #  the name of a file/window (accessible to Alpha), and the differences 
  975.  #  between that file/window and some other version.  We handle this in
  976.  #  the same way as if the user opens a patch file.  We open the given
  977.  #  $name, and show the differences in a standard Diff window.  Then we
  978.  #  let the user examine and apply various changes as desired.
  979.  # -------------------------------------------------------------------------
  980.  ##
  981. proc Diff::of {name difference} {
  982.     global Diff::1 Diff::2 Diff::1Open Diff::2Open diffDir
  983.  
  984.     set diffDir 0
  985.     set Diff::2 $name
  986.     catch {unset Diff::1}
  987.     set Diff::1Open 0
  988.     set Diff::2Open 2
  989.     Diff::diffWindow $difference "Diff of '[file tail $name]'"            
  990. }
  991.  
  992. proc Diff::displayAll {{name "* File Comparison *"}} {
  993.     global Diff::1 Diff::2 Diff::result
  994.     
  995.     Diff::Display Diff::1 1 0 1
  996.     Diff::Display Diff::2 0 0 1
  997.     Diff::diffWindow ${Diff::result} $name
  998. }
  999.  
  1000. proc Diff::diffWindow {diffText {name {* File Comparison *}}} {
  1001.     global tileLeft tileTop tileWidth tileHeight    
  1002.     
  1003.     set top [expr {$tileTop + $tileHeight - 178}]
  1004.     set n [new -n $name -g $tileLeft $top [expr {$tileWidth - 6}] 178 \
  1005.       -m Diff -info "\r$diffText\r"]
  1006.     select [minPos] [nextLineStart [minPos]]
  1007.     Diff::opening $n
  1008. }
  1009.  
  1010. # ◊◊◊◊ Moving around ◊◊◊◊ #
  1011. proc Diff::Up {} {
  1012.     Diff::move 0
  1013. }
  1014.  
  1015. proc Diff::Down {} {
  1016.     Diff::move 1
  1017. }
  1018.  
  1019. proc Diff::move {dir} {
  1020.     global Diff::usesAts
  1021.     if {$dir} {
  1022.     set pos [pos::math [getPos] + 1]
  1023.     } else {
  1024.     set pos [pos::math [getPos] - 1]
  1025.     }
  1026.     
  1027.     if {${Diff::usesAts}} {
  1028.     set movePattern "^@@ \[^\n\r\]+(\r|\n|\$)"
  1029.     } else {
  1030.     set movePattern "^\[^-= \n\r\]+(\r|\n|\$)"
  1031.     }
  1032.     
  1033.     if {[catch {search -s -f $dir -r 1 -- ${movePattern} $pos} res]} {
  1034.     message "No more diffs"
  1035.     return
  1036.     }
  1037.     set pos [lindex $res 0]
  1038.     set line [getText $pos [nextLineStart $pos]]
  1039.     if {[string length [string trim $line]] < 2} {
  1040.     goto $pos
  1041.     return [Diff::move $dir]
  1042.     }
  1043.     select $pos [nextLineStart $pos]    
  1044.     display $pos
  1045.     message ""
  1046.     refresh
  1047. }
  1048.  
  1049. proc Diff::Select {} {
  1050.     global Diff::1 Diff::2 diffDir
  1051.     
  1052.     set text [getText [lineStart [getPos]] [pos::math [nextLineStart [getPos]] - 1]]
  1053.     
  1054.     if {![regexp {[acd]} $text char]} return
  1055.     set res [split $text $char]
  1056.     if {![scan [lindex $res 0] "%d" one]} return
  1057.     if {![scan [lindex $res 1] "%d" two]} return
  1058.     if {$one == 1} {incr one}
  1059.     if {$two == 1} {incr two}
  1060.     
  1061.     if {$diffDir} {
  1062.     set res [search -s -f 0 -r 1 "^diff\[^\r\n\]*(\r|\n|\$)" [getPos]]
  1063.     set text [eval getText $res]
  1064.     set len [llength $text]
  1065.     set Diff::1 [file::ensureStandardPath [lindex $text [expr {$len - 2}]]]
  1066.     set Diff::2 [file::ensureStandardPath [lindex $text [expr {$len - 1}]]]
  1067.     }
  1068.     Diff::Display Diff::1 1 [expr {$one - 1}] $diffDir
  1069.     Diff::Display Diff::2 0 [expr {$two - 1}] $diffDir
  1070.     
  1071.     if {$diffDir} {
  1072.     catch {bringToFront ${Diff::window}}
  1073.     }
  1074. }
  1075.  
  1076. proc Diff::Display {name left {row 0} {check 0}} {
  1077.     global Diff::killWhenDone
  1078.     
  1079.     upvar $name wname
  1080.     
  1081.     catch {unset Diff::killWhenDone($wname)}
  1082.     
  1083.     if {![info exists wname]} {
  1084.     if {$left} {
  1085.         message "Diff window for left doesn't exist"
  1086.     } else {
  1087.         message "Diff window for right doesn't exist"
  1088.     }
  1089.     return
  1090.     }
  1091.     if {$check} {
  1092.     set geo [Diff::Geo $left]
  1093.     set res [lsearch -exact [winNames -f] $wname]
  1094.     if { $res < 0 } {
  1095.         set res [lsearch [winNames -f] "[quote::Glob $wname] <*>"]
  1096.     }
  1097.     if { $res < 0} {
  1098.         eval edit -g $geo [list [win::StripCount $wname]]
  1099.         set wname [win::Current]
  1100.         # This window was only opend for Diff, so close it when finished
  1101.         set Diff::killWhenDone($wname) 1
  1102.     } else {
  1103.         set wname [lindex [winNames -f] $res]
  1104.         if {[getGeometry $wname] != $geo} {
  1105.         sizeWin $wname [lindex $geo 2] [lindex $geo 3]
  1106.         moveWin $wname [lindex $geo 0] [lindex $geo 1]
  1107.         }
  1108.         if {$res > 2} {
  1109.         bringToFront $wname
  1110.         }
  1111.     }
  1112.     }
  1113.     display -w $wname [rowColToPos -w $wname $row 0]
  1114. }
  1115.  
  1116. proc Diff::viewSophisticated {} {
  1117.     global Diff::1 Diff::2 diffDir DiffmodeVars Diff::Marked
  1118.     global Diff::leftDir Diff::rightDir
  1119.     
  1120.     set text [lindex [Diff::line [getPos] files] 0]
  1121.     
  1122.     if {[info exists Diff::1]} {
  1123.     set old1 ${Diff::1}
  1124.     }
  1125.     if {[info exists Diff::2]} {
  1126.     set old2 ${Diff::2}
  1127.     }
  1128.  
  1129.     if {$diffDir} {
  1130.     set Diff::1 [lindex $files 0]
  1131.     if {![file exists ${Diff::1}]} {
  1132.         set Diff::1 [file join ${Diff::leftDir} ${Diff::1}]
  1133.     }
  1134.     if {![file exists ${Diff::1}]} {
  1135.         if {${Diff::leftDir} == "" && ([set res [lsearch [winNames] "[file tail ${Diff::1}]*"]] != -1)} {
  1136.         set Diff::1 [lindex [winNames -f] $res]
  1137.         } else {
  1138.         unset Diff::1
  1139.         }
  1140.     } else {
  1141.         if {[set res [lsearch [winNames -f] "[quote::Glob ${Diff::1}]*"]] != -1} {
  1142.         set Diff::1 [lindex [winNames -f] $res]
  1143.         }
  1144.     }
  1145.     set Diff::2 [lindex $files 1]
  1146.     if {![file exists ${Diff::2}]} {
  1147.         set Diff::2 [file join ${Diff::rightDir} ${Diff::2}]
  1148.     }
  1149.     if {![file exists ${Diff::2}]} {
  1150.         if {${Diff::rightDir} == "" && ([set res [lsearch [winNames] "[file tail ${Diff::2}]*"]] != -1)} {
  1151.         set Diff::2 [lindex [winNames -f] $res]
  1152.         } else {
  1153.         unset Diff::2
  1154.         }
  1155.     } else {
  1156.         if {[set res [lsearch [winNames -f] "[quote::Glob ${Diff::2}]*"]] != -1} {
  1157.         set Diff::2 [lindex [winNames -f] $res]
  1158.         }
  1159.     }
  1160.     }
  1161.     if {[info exists Diff::1]} {
  1162.     set Diff::1 [file nativename ${Diff::1}]
  1163.     }
  1164.     if {[info exists Diff::2]} {
  1165.     set Diff::2 [file nativename ${Diff::2}]
  1166.     # Can happen when the right file doesn't exist.
  1167.     if {[info exists Diff::1] && (${Diff::1} == ${Diff::2})} {
  1168.         unset Diff::2
  1169.     }
  1170.     }
  1171.     regexp {([^/]+)(.*)} $text "" mark suff
  1172.     if {![info exists "Diff::Marked($suff)"]} {
  1173.     # Clean up previous Diff pair(s)
  1174.     foreach m [array names Diff::Marked] {
  1175.         unset Diff::Marked($m)
  1176.     }
  1177.     
  1178.     if {[info exists old1]} {
  1179.         Diff::cleanup $old1
  1180.     }
  1181.     if {[info exists old2]} {
  1182.         Diff::cleanup $old2
  1183.     }
  1184.     
  1185.     if {[info exists Diff::1]} {
  1186.         Diff::Display Diff::1 1 0 1
  1187.     }
  1188.     if {[info exists Diff::2]} {
  1189.         Diff::Display Diff::2 0 0 1
  1190.     }
  1191.     Diff::setMarksUp $suff
  1192.     if {[info exists Diff::1]} {
  1193.         Diff::mark ${Diff::1} 1 $suff
  1194.         set Diff::Marked($suff) 1
  1195.     }
  1196.     if {[info exists Diff::2]} {
  1197.         Diff::mark ${Diff::2} 0 $suff
  1198.         set Diff::Marked($suff) 1
  1199.     }
  1200.     Diff::diffWinFront
  1201.     }
  1202.     set text $mark
  1203.     if {$DiffmodeVars(useMarksDontBringToFront)} {
  1204.     if {![catch {mark::getRange diff-$text ${Diff::1}} range]} {
  1205.         set beg [lindex $range 0]
  1206.         set end [lindex $range 2]
  1207.         Diff::displayLines ${Diff::1} $beg $end
  1208.     }
  1209.     
  1210.     if {![catch {mark::getRange diff-$text ${Diff::2}} range]} {
  1211.         set beg [lindex $range 0]
  1212.         set end [lindex $range 2]
  1213.         Diff::displayLines ${Diff::2} $beg $end
  1214.     }
  1215.     # we need this line because of an Alpha visual bug.
  1216.     # Alpha will often draw the text in the wrong window when we 
  1217.     # hit 'down'.  It does correct itself, but it looks silly.
  1218.     Diff::diffWinFront
  1219.     } else {            
  1220.     if {![catch {Diff::BringToFront ${Diff::1}}]} {
  1221.         gotoMark "diff-$text"
  1222.     }
  1223.     if {![catch {Diff::BringToFront ${Diff::2}}]} {
  1224.         gotoMark "diff-$text"        
  1225.     }
  1226.     Diff::diffWinFront
  1227.     }
  1228. }
  1229.  
  1230. proc Diff::displayLines {win beg end} {
  1231.     display -w $win $end
  1232.     display -w $win [expr \
  1233.       {[pos::compare $beg > [minPos]] \
  1234.       ? [pos::math $beg - 1] : $beg}]
  1235.     select -w $win $beg $end
  1236.     if {[pos::compare $beg > [minPos]]} {
  1237.     set beg [pos::math $beg - 1]
  1238.     }
  1239.     #display -w $win $beg
  1240.     #refresh $win
  1241. }
  1242.  
  1243. proc Diff::viewOld {} {
  1244.     global Diff::1 Diff::2 diffDir 
  1245.     
  1246.     set text [lindex [Diff::line [getPos]] 0]
  1247.     if {![regexp {[acd]} $text char]} return
  1248.     set res [split $text $char]
  1249.     if {![scan [lindex $res 0] "%d,%d" one oned]} return
  1250.     if {![scan [lindex $res 1] "%d,%d" two twod]} return
  1251.     set on $one
  1252.     set tw $two
  1253.     if {$on == 1} {incr on}
  1254.     if {$tw == 1} {incr tw}
  1255.     if {![info exists oned]} {set oned $one}
  1256.     if {![info exists twod]} {set twod $two}
  1257.     
  1258.     if {$diffDir} {
  1259.     set res [search -s -f 0 -r 1 "^diff\[^\r\n\]*(\r|\n|\$)" [getPos]]
  1260.     set text [eval getText $res]
  1261.     set Diff::1 [lindex $text 1]
  1262.     set Diff::2 [lindex $text 2]
  1263.     }
  1264.     Diff::Sel Diff::1 [expr {$on - 1}] $one $oned 1
  1265.     Diff::Sel Diff::2 [expr {$tw - 1}] $two $twod 0
  1266.     set wins [lremove [lrange [winNames -f] 0 2] ${Diff::1} ${Diff::2}]
  1267.     set wins [lremove -glob $wins *Comparison*]
  1268.     if {$wins != ""} {
  1269.     bringToFront ${Diff::1}
  1270.     bringToFront ${Diff::2}
  1271.     }
  1272.     Diff::diffWinFront
  1273. }
  1274.  
  1275. proc Diff::View {} {
  1276.     global DiffmodeVars
  1277.     if {$DiffmodeVars(useSophisticatedDiffMarking)} {
  1278.     Diff::viewSophisticated
  1279.     } else {
  1280.     Diff::viewOld
  1281.     }
  1282. }
  1283.  
  1284. ## 
  1285.  # -------------------------------------------------------------------------
  1286.  # 
  1287.  # "Diff::Sel" --
  1288.  # 
  1289.  #  This handles a name either with or without trailing '<n>' and fixes
  1290.  #  the given name if it isn't right.
  1291.  # -------------------------------------------------------------------------
  1292.  ##
  1293. proc Diff::Sel {wnamev ro row rowd left} {
  1294.     global diffDir
  1295.     upvar $wnamev wname
  1296.     if {$diffDir} {
  1297.     set geo [Diff::Geo $left]
  1298.     if {[set res [lsearch [winNames -f] "[quote::Glob $wname]*"]] < 0} {
  1299.         eval edit -g $geo [list $wname]
  1300.         set wname [win::Current]
  1301.     } else {
  1302.         set wname [lindex [winNames -f] $res]
  1303.         if {[getGeometry $wname] != $geo} {
  1304.         sizeWin $wname [lindex $geo 2] [lindex $geo 3]
  1305.         moveWin $wname [lindex $geo 0] [lindex $geo 1]
  1306.         }
  1307.     }
  1308.     }
  1309.     display -w $wname [rowColToPos -w $wname $ro 0]
  1310.     select -w $wname [rowColToPos -w $wname $row 0] \
  1311.       [rowColToPos -w $wname [expr {$rowd + 1}] 0]
  1312. }
  1313.  
  1314. # ◊◊◊◊ Utilities ◊◊◊◊ #
  1315.  
  1316. proc Diff::Win {} {
  1317.     global win::Modes
  1318.     set files [winNames -f]
  1319.     set len [llength $files]
  1320.     for {set i 0} {$i < $len} {incr i} {
  1321.     if {[set win::Modes([lindex [winNames -f] $i])] == "Diff"} {
  1322.         bringToFront [lindex [winNames] $i]
  1323.         return
  1324.     }
  1325.     }
  1326.     beep
  1327.     message "No Diff window."
  1328. }
  1329.  
  1330. proc Diff::Geo {left} {
  1331.     global tileWidth tileHeight tileTop tileLeft
  1332.     
  1333.     set margin 4
  1334.     set width [expr {($tileWidth - $margin)/2}]
  1335.     set height [expr {$tileHeight - 200}]
  1336.     set hor $tileLeft
  1337.     
  1338.     if {!$left} {incr hor [expr {$width+$margin}]}
  1339.     
  1340.     return [list $hor $tileTop $width $height]
  1341. }
  1342.  
  1343. proc Diff::diffWinFront {} {
  1344.     global Diff::window
  1345.     catch {bringToFront ${Diff::window}}
  1346. }
  1347.  
  1348. ## 
  1349.  # -------------------------------------------------------------------------
  1350.  # 
  1351.  # "Diff::BringToFront" --
  1352.  # 
  1353.  #  Hack to make it quicker to switch between windows.  We often want
  1354.  #  the 'Diff' window to be in the front all the time, but have to
  1355.  #  bring others to the front temporarily for manipulation.  This proc
  1356.  #  brings a different window to the front more quickly by avoiding
  1357.  #  all mode-changing code.  Of course you should only call this proc
  1358.  #  when you will _very_ soon bring a different window to the front.
  1359.  # -------------------------------------------------------------------------
  1360.  ##
  1361. proc Diff::BringToFront {w} {
  1362.     global win::Modes DiffmodeVars
  1363.     if {$DiffmodeVars(useFastWindowSwapping)} {
  1364.     set oldm [set win::Modes($w)]
  1365.     set win::Modes($w) Diff
  1366.     if {[catch {bringToFront $w}]} {
  1367.         unset win::Modes($w)
  1368.         beep
  1369.         error "no such win"
  1370.     } else {
  1371.         set win::Modes($w) $oldm
  1372.     }
  1373.     } else {
  1374.     bringToFront $w
  1375.     }
  1376. }
  1377.