Forum OpenACS Development: Re: Wiki modifications for mediawiki format

Collapse
Posted by Vinod Kurup on

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"
}
Collapse
11: diff in Tcl (response to 8)
Posted by Andrew Piskorski on
Vinod, nice, I like the colored diff output.

See also the Wiki page diff in Tcl and Wiki History Diff. That includes the same longestCommonSubsequence code that was later included in Tcllib, plus other misc. info.