I've been doing some minor hacking on the wiki package, namely adding the ability to see diffs between versions. I looked around for a diff procedure coded in TCL, but couldn't find one. I did find some procs in tcllib which compute the longest common subsequence. From those, you can get a list of the changes between 2 pieces of text. So, I created this proc which takes 2 pieces of text and then returns a HTML fragment with the changes outlined with the CSS tags 'diff-added' or 'diff-deleted'.
You can see what this looks like at my dev server (if it's
up). Click on 'history', then pick 2 versions to diff. Feel free to poke around.
I haven't had a chance to do much testing of it yet, so I wouldn't venture to say that it's complete. But it's a start. Here's the proc:
ad_proc -public wiki::diff {
-old
-new
} {
Perform a UNIX diff on 'old' and 'new', and return a HTML fragment of the changes.
Requires struct::list (from tcllib)
@author Vinod Kurup vinod@kurup.com
@creation-date 2005-10-18
@param old original text
@param new new text
@return HTML fragment of differences between 'old' and 'new'
} {
package require struct::list
set old [split $old " "]
set new [split $new " "]
# tcllib procs to get a list of differences between 2 lists
# see: http://tcllib.sourceforge.net/doc/struct_list.html
set len1 [llength $old]
set len2 [llength $new]
set result [::struct::list longestCommonSubsequence $old $new]
set result [::struct::list lcsInvert $result $len1 $len2]
# each chunk is either 'deleted', 'added', or 'changed'
set i 0
foreach chunk $result {
set action [lindex $chunk 0]
set old_index1 [lindex [lindex $chunk 1] 0]
set old_index2 [lindex [lindex $chunk 1] 1]
set new_index1 [lindex [lindex $chunk 2] 0]
set new_index2 [lindex [lindex $chunk 2] 1]
while {$i < $old_index1} {
lappend output [lindex $old $i]
incr i
}
if { $action eq "changed" } {
lappend output <d>
foreach item [lrange $old $old_index1 $old_index2] {
lappend output $item
}
lappend output </d>
lappend output <a>
foreach item [lrange $new $new_index1 $new_index2] {
lappend output $item
}
lappend output </a>
incr i [expr $old_index2 - $old_index1 + 1]
} elseif { $action eq "deleted" } {
lappend output <d>
foreach item [lrange $old $old_index1 $old_index2] {
lappend output $item
}
lappend output </d>
incr i [expr $old_index2 - $old_index1 + 1]
} elseif { $action eq "added" } {
while {$i < $old_index2} {
lappend output [lindex $old $i]
incr i
}
lappend output <a>
foreach item [lrange $new $new_index1 $new_index2] {
lappend output $item
}
lappend output </a>
}
}
# add any remaining words at the end.
while {$i < $len1} {
lappend output [lindex $old $i]
incr i
}
set output [join $output " "]
set output [string map {"<d>" {<span class="diff-deleted">}
"</d>" </span>
"<a>" {<span class="diff-added">}
"</a>" </span>} $output]
return "$output"
}