Eskil

Check-in [4251e290a3]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Better doc of plugins. Allow dump of plugins
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 4251e290a30f52fb32cb59c6473caa5ba31e8371
User & Date: peter 2009-01-23 22:36:06.000
Context
2009-02-12
15:26
Allow stepping down in directory diff check-in: 2bd38e69fc user: peter tags: trunk
2009-01-23
22:36
Better doc of plugins. Allow dump of plugins check-in: 4251e290a3 user: peter tags: trunk
21:22
Don't save unchanged preferences to prefs file. check-in: 2c5e0366e6 user: peter tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to Changes.




1
2
3
4
5
6
7





Release 2.4

2009-01-08
 Added backslash plugin.

2009-01-07
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
2009-01-23
 Don't save unchanged preferences to prefs file.
 Better documentation for plugins.
 Added plugin dump.

Release 2.4

2009-01-08
 Added backslash plugin.

2009-01-07
Changes to doc/plugins.txt.


1




2










3


4




5


6


7
8



To be written...















Example plugins are included in the kit.


Check them to see how they work.







If you can't unpack the kit you can browse it


with eskil's directory diff. Just start eskil
with itself as argument.

>
>
|
>
>
>
>

>
>
>
>
>
>
>
>
>
>

>
>
|
>
>
>
>

>
>
|
>
>
|
<
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

34
Eskil provides a plugin system where a plugin can preprocess data
before being compared and displayed.

The command line options for plugins are:
  -plugin plugin     : Use plugin
  -plugininfo info   : Pass info to plugin (plugin specific)
  -plugindump plugin : Dump plugin source to stdout

A plugin is a Tcl script that must follow a specific format.
Dump one of the included plugins to see what it looks like.
The plugin is executed in a safe interpreter and thus cannot do any
damage.

A plugin is set up with these global variables filled in:
::WhoAmI  : The name of the plugin
::Info    : The contents of -plugininfo parameter
::Pref    : A copy if Eskil's internal preferences array.

Example plugins are included in the kit.
Just give a bad plugin name to the options and you will get a list
of available plugins.

A plugin may give a result that has a line-by-line correspondence to
the original, in which case the preprocessed data is used for comparing
while the original is used for displaying.  The main plugin procedure
returns 0 to signify this case.

If the plugin procedure returns 1, the processed data is used also for
displaying.

When searching for a plugin, files "plugin" and "plugin.tcl" will
match. The search path is current directory, "plugins" directory,
the directory where Eskil is installed, "plugins" directory where

Eskil is installed, and also the internal "plugins" wrapped into Eskil.
Changes to plugins/backslash.tcl.
1
2
3
4

5
6
7
8
9
10
11
##Eskil Plugin

# Example file for a plugin.
# A plugin must start with the exact first line as this one.


# This plugin replaces any backslash-newline with space, thus
# ignoring restructured lines.

# A plugin must define this procedure to do the job.
# side: left or right
# chi:  An input channel for reading the original file.
|


|
>







1
2
3
4
5
6
7
8
9
10
11
12
##Eskil Plugin : Compare with backslash-newline removed

# Example file for a plugin.
# A plugin must start exactly like this one.
# The text after : is the summary you can get at the command line

# This plugin replaces any backslash-newline with space, thus
# ignoring restructured lines.

# A plugin must define this procedure to do the job.
# side: left or right
# chi:  An input channel for reading the original file.
Changes to plugins/nocase.tcl.
1
2
3
4

5
6
7
8
9
10
11
##Eskil Plugin

# Example file for a plugin.
# A plugin must start with the exact first line as this one.


# This plugin implements case insensitive matching, corresponding to the
# -nocase flag.

# A plugin must define this procedure to do the job.
# side: left or right
# chi:  An input channel for reading the original file.
|


|
>







1
2
3
4
5
6
7
8
9
10
11
12
##Eskil Plugin : Case insensitive matching

# Example file for a plugin.
# A plugin must start exactly like this one.
# The text after : is the summary you can get at the command line

# This plugin implements case insensitive matching, corresponding to the
# -nocase flag.

# A plugin must define this procedure to do the job.
# side: left or right
# chi:  An input channel for reading the original file.
Changes to plugins/words.tcl.
1
2
3
4

5
6
7
8
9
10
11
##Eskil Plugin

# Example file for a plugin.
# A plugin must start with the exact first line as this one.


# This plugin compares the set of words in files.

# A plugin must define this procedure to do the job.
# side: left or right
# chi:  An input channel for reading the original file.
# cho:  An output channel for writing the processed file.
|


|
>







1
2
3
4
5
6
7
8
9
10
11
12
##Eskil Plugin : Compare set of words

# Example file for a plugin.
# A plugin must start exactly like this one.
# The text after : is the summary you can get at the command line

# This plugin compares the set of words in files.

# A plugin must define this procedure to do the job.
# side: left or right
# chi:  An input channel for reading the original file.
# cho:  An output channel for writing the processed file.
Changes to src/eskil.tcl.
3285
3286
3287
3288
3289
3290
3291




3292
3293
3294
3295
3296
3297
3298

  -browse     : Automatically bring up file dialog after starting.
  -server     : Set up Eskil to be controllable from the outside.

  -printps <file>  : Generate postscript and exit.
  -printpdf <file> : Generate pdf and exit.





  -limit <lines> : Do not process more than <lines> lines.

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'}
}








>
>
>
>







3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302

  -browse     : Automatically bring up file dialog after starting.
  -server     : Set up Eskil to be controllable from the outside.

  -printps <file>  : Generate postscript and exit.
  -printpdf <file> : Generate pdf and exit.

  -plugin <plugin>     : Use plugin
  -plugininfo <info>   : Pass info to plugin (plugin specific)
  -plugindump <plugin> : Dump plugin source to stdout

  -limit <lines> : Do not process more than <lines> lines.

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'}
}

3311
3312
3313
3314
3315
3316
3317

3318
3319
3320
3321
3322
3323
3324
    
    set allOpts {
        -w --help -help -b -noignore -i -nocase -nodigit -nokeyword -prefix
        -noparse -line -smallblock -block -char -word -limit -nodiff -dir
        -clip -patch -browse -conflict -print -printps -printpdf
        -server -o -r -context -cvs -svn -review
        -foreach -preprocess -close -nonewline -plugin -plugininfo

    }

    # If the first option is "--query", use it to ask about options.
    if {$::eskil(argc) == 2 && [lindex $::eskil(argv) 0] == "--query"} {
        set arg [lindex $::eskil(argv) 1]
        if {[lsearch -exact $allOpts $arg] < 0} {
            set match [lsearch -glob -all -inline $allOpts $arg*]







>







3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
    
    set allOpts {
        -w --help -help -b -noignore -i -nocase -nodigit -nokeyword -prefix
        -noparse -line -smallblock -block -char -word -limit -nodiff -dir
        -clip -patch -browse -conflict -print -printps -printpdf
        -server -o -r -context -cvs -svn -review
        -foreach -preprocess -close -nonewline -plugin -plugininfo
        -plugindump
    }

    # If the first option is "--query", use it to ask about options.
    if {$::eskil(argc) == 2 && [lindex $::eskil(argv) 0] == "--query"} {
        set arg [lindex $::eskil(argv) 1]
        if {[lsearch -exact $allOpts $arg] < 0} {
            set match [lsearch -glob -all -inline $allOpts $arg*]
3338
3339
3340
3341
3342
3343
3344

3345
3346
3347
3348
3349
3350
3351
    set revNo 1
    set dopatch 0
    set doreview 0
    set foreach 0
    set preferedRev "GIT"
    set plugin ""
    set plugininfo ""


    foreach arg $::eskil(argv) {
        if {$nextArg != ""} {
            if {$nextArg eq "mergeFile"} {
                set opts(mergeFile) [file join [pwd] $arg]
            } elseif {$nextArg eq "printFile"} {
                set opts(printFile) [file join [pwd] $arg]







>







3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
    set revNo 1
    set dopatch 0
    set doreview 0
    set foreach 0
    set preferedRev "GIT"
    set plugin ""
    set plugininfo ""
    set plugindump ""

    foreach arg $::eskil(argv) {
        if {$nextArg != ""} {
            if {$nextArg eq "mergeFile"} {
                set opts(mergeFile) [file join [pwd] $arg]
            } elseif {$nextArg eq "printFile"} {
                set opts(printFile) [file join [pwd] $arg]
3362
3363
3364
3365
3366
3367
3368


3369
3370
3371
3372
3373
3374
3375
                    set RE "(?i)$RE"
                }
                lappend ::Pref(regsub) $RE {\1}
            } elseif {$nextArg eq "plugin"} {
                set plugin $arg
            } elseif {$nextArg eq "plugininfo"} {
                set plugininfo $arg


            } elseif {$nextArg eq "preprocess"} {
                if {[catch {llength $arg} len]} {

                } elseif {[llength $arg] % 2 == 1} {

                } else {
                    # FIXA: better validity check







>
>







3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
                    set RE "(?i)$RE"
                }
                lappend ::Pref(regsub) $RE {\1}
            } elseif {$nextArg eq "plugin"} {
                set plugin $arg
            } elseif {$nextArg eq "plugininfo"} {
                set plugininfo $arg
            } elseif {$nextArg eq "plugindump"} {
                set plugindump $arg
            } elseif {$nextArg eq "preprocess"} {
                if {[catch {llength $arg} len]} {

                } elseif {[llength $arg] % 2 == 1} {

                } else {
                    # FIXA: better validity check
3418
3419
3420
3421
3422
3423
3424


3425
3426
3427
3428
3429
3430
3431
            set nextArg prefix
        } elseif {$arg eq "-preprocess"} {
            set nextArg preprocess
        } elseif {$arg eq "-plugin"} {
            set nextArg plugin
        } elseif {$arg eq "-plugininfo"} {
            set nextArg plugininfo


        } elseif {$arg eq "-context"} {
            set nextArg context
        } elseif {$arg eq "-noparse"} {
            set Pref(parse) 0
        } elseif {$arg eq "-line"} {
            set Pref(parse) 1
        } elseif {$arg eq "-smallblock"} {







>
>







3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
            set nextArg prefix
        } elseif {$arg eq "-preprocess"} {
            set nextArg preprocess
        } elseif {$arg eq "-plugin"} {
            set nextArg plugin
        } elseif {$arg eq "-plugininfo"} {
            set nextArg plugininfo
        } elseif {$arg eq "-plugindump"} {
            set nextArg plugindump
        } elseif {$arg eq "-context"} {
            set nextArg context
        } elseif {$arg eq "-noparse"} {
            set Pref(parse) 0
        } elseif {$arg eq "-line"} {
            set Pref(parse) 1
        } elseif {$arg eq "-smallblock"} {
3501
3502
3503
3504
3505
3506
3507




3508
3509
3510
3511

3512
3513
3514
3515
3516
3517
3518
                lappend files $apa
            }
        }
    }

    Init





    if {$plugin ne ""} {
        set pinterp [createPluginInterp $plugin $plugininfo]
        if {$pinterp eq ""} {
            puts "Bad plugin: $plugin"

            exit
        }
        set opts(plugin) $pinterp
    }

    # Do we start in clip diff mode?
    if {$doclip} {







>
>
>
>




>







3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
                lappend files $apa
            }
        }
    }

    Init

    if {$plugindump ne ""} {
        printPlugin $plugindump
        exit
    }
    if {$plugin ne ""} {
        set pinterp [createPluginInterp $plugin $plugininfo]
        if {$pinterp eq ""} {
            puts "Bad plugin: $plugin"
            printPlugins
            exit
        }
        set opts(plugin) $pinterp
    }

    # Do we start in clip diff mode?
    if {$doclip} {
Changes to src/plugin.tcl.
18
19
20
21
22
23
24
25
26
27
28
29
30
31







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49





50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69






























70
71
72
73
74
75
76
#  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
#  Boston, MA 02111-1307, USA.
#
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

proc createPluginInterp {plugin info} {
    # Locate plugin source
    set src ""
    set dirs [list . ./plugins]
    lappend dirs [file join $::thisDir .. ..]
    lappend dirs [file join $::thisDir .. .. plugins]
    lappend dirs [file join $::thisDir .. plugins]








    foreach dir $dirs {
        set files [list [file join $dir $plugin]]
        lappend files [file join $dir $plugin.tcl]
        foreach file $files {
            if {![file exists $file]} continue
            if {![file isfile $file]} continue
            if {![file readable $file]} continue
            set ch [open $file r]
            set data [read $ch 20]
            close $ch
            if {[string match "##Eskil Plugin*" $data]} {
                set src $file
                break
            }
        }
        if {$src ne ""} break
    }






    if {$src eq ""} {
        return ""
    }

    # Create interpreter
    set pi [interp create -safe]

    # Load source
    $pi invokehidden -global source $src
    $pi eval [list set ::WhoAmI [file rootname [file tail $src]]]
    $pi eval [list set ::Info $info]
    interp share {} stdout $pi

    # Expose needed commands
    interp expose $pi fconfigure ;# ??
    interp hide $pi close

    return $pi
}































proc preparePlugin {top} {
    #FIXA: plugin miffo
    disallowEdit $top
    $::diff($top,plugin) eval [list array set ::Pref [array get ::Pref]]
    set out1 [tmpFile]
    set out2 [tmpFile]







<
|
<




>
>
>
>
>
>
>


















>
>
>
>
>




















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







18
19
20
21
22
23
24

25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
#  Boston, MA 02111-1307, USA.
#
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------


proc PluginSearchPath {} {

    set dirs [list . ./plugins]
    lappend dirs [file join $::thisDir .. ..]
    lappend dirs [file join $::thisDir .. .. plugins]
    lappend dirs [file join $::thisDir .. plugins]
    return $dirs
}

# Locate plugin source
proc LocatePlugin {plugin} {
    set src ""
    set dirs [PluginSearchPath]

    foreach dir $dirs {
        set files [list [file join $dir $plugin]]
        lappend files [file join $dir $plugin.tcl]
        foreach file $files {
            if {![file exists $file]} continue
            if {![file isfile $file]} continue
            if {![file readable $file]} continue
            set ch [open $file r]
            set data [read $ch 20]
            close $ch
            if {[string match "##Eskil Plugin*" $data]} {
                set src $file
                break
            }
        }
        if {$src ne ""} break
    }
    return $src
}

proc createPluginInterp {plugin info} {
    set src [LocatePlugin $plugin]

    if {$src eq ""} {
        return ""
    }

    # Create interpreter
    set pi [interp create -safe]

    # Load source
    $pi invokehidden -global source $src
    $pi eval [list set ::WhoAmI [file rootname [file tail $src]]]
    $pi eval [list set ::Info $info]
    interp share {} stdout $pi

    # Expose needed commands
    interp expose $pi fconfigure ;# ??
    interp hide $pi close

    return $pi
}

proc printPlugin {plugin} {
    set src [LocatePlugin $plugin]
    if {$src eq ""} {
        printPlugins
        return
    }
    set ch [open $src]
    puts -nonewline [read $ch]
    close $ch
}

proc printPlugins {} {
    set dirs [PluginSearchPath]

    foreach dir $dirs {
        set files [glob -nocomplain [file join $dir *.tcl]]
        foreach file $files {
            if {![file exists $file]} continue
            if {![file isfile $file]} continue
            if {![file readable $file]} continue
            set ch [open $file r]
            set data [read $ch 100]
            close $ch
            if {[regexp {^\#\#Eskil Plugin :(.*?)(\n|$)} $data -> descr]} {
                puts "Plugin \"[file rootname [file tail $file]]\" : $descr"
            }
        }
    }
}

proc preparePlugin {top} {
    #FIXA: plugin miffo
    disallowEdit $top
    $::diff($top,plugin) eval [list array set ::Pref [array get ::Pref]]
    set out1 [tmpFile]
    set out2 [tmpFile]