lib / class.tclon commit git-gui: Define a simple class/method system (1f07c4e)
   1# git-gui simple class/object fake-alike
   2# Copyright (C) 2007 Shawn Pearce
   3
   4proc class {class body} {
   5        if {[namespace exists $class]} {
   6                error "class $class already declared"
   7        }
   8        namespace eval $class {
   9                variable __nextid     0
  10                variable __sealed     0
  11                variable __field_list {}
  12                variable __field_array
  13
  14                proc cb {name args} {
  15                        upvar this this
  16                        set args [linsert $args 0 $name $this]
  17                        return [uplevel [list namespace code $args]]
  18                }
  19        }
  20        namespace eval $class $body
  21}
  22
  23proc field {name args} {
  24        set class [uplevel {namespace current}]
  25        variable ${class}::__sealed
  26        variable ${class}::__field_array
  27
  28        switch [llength $args] {
  29        0 { set new [list $name] }
  30        1 { set new [list $name [lindex $args 0]] }
  31        default { error "wrong # args: field name value?" }
  32        }
  33
  34        if {$__sealed} {
  35                error "class $class is sealed (cannot add new fields)"
  36        }
  37
  38        if {[catch {set old $__field_array($name)}]} {
  39                variable ${class}::__field_list
  40                lappend __field_list $new
  41                set __field_array($name) 1
  42        } else {
  43                error "field $name already declared"
  44        }
  45}
  46
  47proc constructor {name params body} {
  48        set class [uplevel {namespace current}]
  49        set ${class}::__sealed 1
  50        variable ${class}::__field_list
  51        set mbodyc {}
  52
  53        append mbodyc {set this } $class
  54        append mbodyc {::__o[incr } $class {::__nextid]} \;
  55        append mbodyc {namespace eval $this {}} \;
  56
  57        if {$__field_list ne {}} {
  58                append mbodyc {upvar #0}
  59                foreach n $__field_list {
  60                        set n [lindex $n 0]
  61                        append mbodyc { ${this}::} $n { } $n
  62                        regsub -all @$n\\M $body "\${this}::$n" body
  63                }
  64                append mbodyc \;
  65                foreach n $__field_list {
  66                        if {[llength $n] == 2} {
  67                                append mbodyc \
  68                                {set } [lindex $n 0] { } [list [lindex $n 1]] \;
  69                        }
  70                }
  71        }
  72        append mbodyc $body
  73        namespace eval $class [list proc $name $params $mbodyc]
  74}
  75
  76proc method {name params body {deleted {}} {del_body {}}} {
  77        set class [uplevel {namespace current}]
  78        set ${class}::__sealed 1
  79        variable ${class}::__field_list
  80        set params [linsert $params 0 this]
  81        set mbodyc {}
  82
  83        switch $deleted {
  84        {} {}
  85        ifdeleted {
  86                append mbodyc {if {![namespace exists $this]} }
  87                append mbodyc \{ $del_body \; return \} \;
  88        }
  89        default {
  90                error "wrong # args: method name args body (ifdeleted body)?"
  91        }
  92        }
  93
  94        set decl {}
  95        foreach n $__field_list {
  96                set n [lindex $n 0]
  97                if {[regexp -- $n\\M $body]} {
  98                        if {   [regexp -all -- $n\\M $body] == 1
  99                                && [regexp -all -- \\\$$n\\M $body] == 1} {
 100                                regsub -all \\\$$n\\M $body "\[set \${this}::$n\]" body
 101                        } else {
 102                                append decl { ${this}::} $n { } $n
 103                                regsub -all @$n\\M $body "\${this}::$n" body
 104                        }
 105                }
 106        }
 107        if {$decl ne {}} {
 108                append mbodyc {upvar #0} $decl \;
 109        }
 110        append mbodyc $body
 111        namespace eval $class [list proc $name $params $mbodyc]
 112}
 113
 114proc delete_this {{t {}}} {
 115        if {$t eq {}} {
 116                upvar this this
 117                set t $this
 118        }
 119        if {[namespace exists $t]} {namespace delete $t}
 120}
 121
 122proc make_toplevel {t w} {
 123        upvar $t top $w pfx
 124        if {[winfo ismapped .]} {
 125                upvar this this
 126                regsub -all {::} $this {__} w
 127                set top .$w
 128                set pfx $top
 129                toplevel $top
 130        } else {
 131                set top .
 132                set pfx {}
 133        }
 134}
 135
 136
 137## auto_mkindex support for class/constructor/method
 138##
 139auto_mkindex_parser::command class {name body} {
 140        variable parser
 141        variable contextStack
 142        set contextStack [linsert $contextStack 0 $name]
 143        $parser eval [list _%@namespace eval $name] $body
 144        set contextStack [lrange $contextStack 1 end]
 145}
 146auto_mkindex_parser::command constructor {name args} {
 147        variable index
 148        variable scriptFile
 149        append index [list set auto_index([fullname $name])] \
 150                [format { [list source [file join $dir %s]]} \
 151                [file split $scriptFile]] "\n"
 152}
 153