Version 1 of Command completion in the iFile console

Updated 2005-08-15 21:35:50

if {0} { fr

a prototype of word completion

instant command completion in the iFile 1.1 console

completion works on first word only in ComboBox entry

the string appended is the longest common beginning of all possible endings

clickable continuations are displayed in a toplevel widget

start typing a character, then repeatedly invoke the created buttons to get the wished command

usage:

append this to the iFile script

or save this as q.tcl in same dir as iFile.tcl

append following line to iFile.tcl

PC:

source file join [file dirname [info script] q.tcl

on PDA (WinCE):

source q.tcl

additional requirements:

font_ce, puts_ce

known bug:

continuations not up to date when using the keyboard (e.g. tk_messageBox, open, option)

}

 namespace eval ::q {
   variable tracedvar
   variable entry
   variable pp ""
   variable pattern ""
   variable temp [list]
   variable suffixlist [list]
   variable packages [lsort [package names]]
   variable added_prefixes [list]
 }
 proc ::q::qcoco {entryname varname x mode} {
 #        puts varname=$varname; puts x=$x; puts mode=$mode
   # completion for the first word
   if {$::q::ok} {
           set v ::
           append v $varname\($x\)
           set v [subst $$v]
           if {![regexp {^\S+\s} $v]} {
                   set pos 0
                   # only first word is assumed to be a command
                   set ns ""
                   set colon ""
                   set ::q::pattern ""
                   regexp -nocase {(^[^:]*)(::)*([a-z0-9_:]*)} $v {&1&2&3} ns colon ::q::pattern
                   if {$colon == ""} {
                           set ::q::pattern $ns
                           set ns ::
                   } else {
                           # learn used package names
                           if {[lsearch -sorted $::q::added_prefixes $ns] < 0} {
                                   if {[lsearch -sorted $::q::packages $ns] >=0} {
                                           lappend ::q::added_prefixes $ns
                                           set ::q::added_prefixes [lsort $::q::added_prefixes]
                                   }
                           } 
                   }
                   incr pos [string length $::q::pattern]
                   set pa ^$::q::pattern
                   set ::q::pp $::q::pattern*
                   set l [list]
                   if {[::q::isnopackage $ns]} {
                           namespace eval $ns {set ::q::commands [info command $::q::pp]}
                           foreach x $::q::commands {
                                   lappend l [string range $x $pos end]
                           }
                           foreach idx [lsearch -all -sorted -regexp $::q::added_prefixes $pa] {
                                   lappend l [string range [lindex $::q::added_prefixes $idx] $pos end]
                           }
                           if {0} {
                           # package names
                                   foreach x $::q::packages {
                                           if {[regexp $pa $x]} {
                                                   lappend l [string range $x $pos end]
                                           }
                                   }
                           }
                   } else {
                           set n ::
                           append n $ns
                           append n ::
                           append n $::q::pattern
                           set len [string length $n]
                           append n *
                           foreach x [info proc $n] {
                                   lappend l [string range $x $len end]
                           }
                   }
                   ::q::conti [lsort -unique -dictionary $l]
                   set add ""
                   foreach ff [split [lindex $::q::suffixlist 0] {}] ll [split [lindex $::q::suffixlist end] {}] {
                           expr {$ff==$ll ? [append add $ff] : [break] }
                   }
                   append v $add
                   set xl [llength $l]
                   if {[llength $l] == 1} {
                           # no alternatives exist
                        #append add { }
                           $entryname insert insert $add
                           set ::q::suffixlist [list]  ;# no need to display
                   } else {
                           if {[llength $l]==0} {
                           # invalid character entered
                                   if {![string equal $v :]} {
                                           set prev [$entryname index insert]
                                           incr prev -1
                                           $entryname selection range $prev end
                                   }
                           } else {
                                   $entryname insert insert $add
                                   # display continuations as buttons
                           }
                   }
           } else {
                   set ::q::text ""
                   set ::q::suffixlist [list]
           }
   } else {
           set ::q::ok 1
   }
   ::q::topcon $entryname $varname
 }
 proc ::q::insertpos {entryname x y} {
   # todo
   # calculate position below insert mark of entry
   # add +y positions of .n (notebook).n.f5 (frame) .n.f5.e (ComboBox)
   # plus height of ComboBox
   upvar $x X
   upvar $y Y
   set X 20
   set Y 110
 }

 proc ::q::topcon {w var} {
   set name .qcon
   append name $w
   regsub -all -start 1 {\.} $name _ name
   #catch {destroy $name}
   #destroy & toplevel -> no more winhandle map slots
   set len [llength $::q::suffixlist]
   catch {wm withdraw $name}
   if {$len==0} {return}
   if {$len==1 && [lindex $::q::suffixlist] == { } } {return}
   if {![winfo exists $name]} {
           set widget [toplevel $name]
   } else {
           foreach x [winfo children $name.f] {
                   destroy $x
           }
           destroy $name.f
           set widget $name
   }
   ::q::insertpos $w px py
   wm geometry $widget +$px+$py
   wm overrideredirect $widget 1

   set sw $widget
   set sf [frame $sw.f]
   $sf configure -bg bisque
   set ii 0
   foreach x $::q::suffixlist {
           set j [button $sf.b$ii -text $x -justify left -border 0 -bg white -fg black -activebackground skyblue -activeforeground white -takefocus 0]
           pack $j -side top -padx 2 -pady 1 -anchor w
           $j configure -command [list ::q::button_invoke $w $x]
           incr ii
   }
   pack $sf -fill both
   wm deiconify $name
 }
 proc ::q::button_invoke {entryname suffix} {
   global g
   set t [$entryname cget -text]
   append t $suffix
   $entryname insert insert $suffix
   focus $entryname
   set g(cmd) $t
 }
 proc ::q::isnopackage {v} {
   if {$v==""} {return 1}
   set pattern [format "^%s::" $v]
   foreach x $::q::packages {
           if {[regexp $pattern $x]} {
                   return 0
           }
   }
   return 1
 }
 proc ::q::off {} {
   #set k [trace info variable $::q::tracedvar]
   # trace vdelete ::g(cmd) [lindex $k {0 1}]
   trace vdelete $::q::tracedvar w [list ::q::qcoco $::q::entry]
   set ::q::text "::q::on ;# instant command" 
   bind $::q::entry <Key-BackSpace> {}
 }        
 proc ::q::about {} {
   set t {
 iFile command completion
 basic prototype
 Roland Frank, Aalen 2005
 www.deltadatentechnik.de
 }
   catch {destroy.qab}
   toplevel .qab
   wm title .qab contributors
   label .qab.l -justify center -text $t
   pack .qab.l -pady 10
 }

 proc ::q::on {{entryname .n.f5.e.e}} {
   # activate command completion in "iFile console"
   global g
   set g(cmd) ""
   set ::q::entry $entryname
   set ::q::tracedvar ::[$entryname cget -textvariable]
   set ::q::ok 1
   bind $entryname <Key-BackSpace> {
           set ::q::ok 0
           set ::q::suffixlist [list]
   }
   trace add variable $::q::tracedvar write [list ::q::qcoco $entryname]
   $entryname selection range 0 end
 }
 proc ::q::conti {inlist} {
   set ::q::temp [list]
   foreach x $inlist {
           if {$x == ""} {
                   set x " "
           }
           ::q::merge $x 0
   }
   set ::q::suffixlist $::q::temp
   set text ""
   set n 0
   foreach x $::q::temp {
           incr n [string length $x]
           if {$n>30} {
                   append text \n
                   set n 0
           } else {
                   append text " "
                   incr n
           }
           append text $x
   }
   set ::q::text $text
 }
 proc ::q::merge {value minlen} {
   # merge value to common prefix with last element of list
   set last [lindex $::q::temp end]
   if {$last != ""} {
           set lastlen [string length $last]
           set c ""
           foreach t1 [split $last ""] t2 [split $value ""] {
                   expr {$t1==$t2 ? [append c $t1] : [break]}
           }
           if {$c!=""} {
                   set clen [string length $c]
                   if {$clen>$minlen} {
                           if {$clen==$lastlen} {
                                   # skip
                                   #lappend ::q::temp $value
                           } else {
                                   lset ::q::temp end $c
                           }
                   } else {
                           lappend ::q::temp $value
                   }
           } else {
                   lappend ::q::temp $value
           }
   } else {
           set ::q::temp [list $value]
   }
 }

 .n.f5.e.e configure -text "::q::on ;# instant command, ::q::off" 
 $5.l configure -textvariable ::q::text