Start anew
[msysgit.git] / mingw / lib / tk8.4 / console.tcl
blob037eb59c78332347d6e29ae2ab38d39c13b980da
1 # console.tcl --
3 # This code constructs the console window for an application. It
4 # can be used by non-unix systems that do not have built-in support
5 # for shells.
7 # RCS: @(#) $Id: console.tcl,v 1.22.2.5 2006/01/25 18:21:41 dgp Exp $
9 # Copyright (c) 1995-1997 Sun Microsystems, Inc.
10 # Copyright (c) 1998-2000 Ajuba Solutions.
12 # See the file "license.terms" for information on usage and redistribution
13 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 # TODO: history - remember partially written command
18 namespace eval ::tk::console {
19 variable blinkTime 500 ; # msecs to blink braced range for
20 variable blinkRange 1 ; # enable blinking of the entire braced range
21 variable magicKeys 1 ; # enable brace matching and proc/var recognition
22 variable maxLines 600 ; # maximum # of lines buffered in console
23 variable showMatches 1 ; # show multiple expand matches
25 variable inPlugin [info exists embed_args]
26 variable defaultPrompt ; # default prompt if tcl_prompt1 isn't used
29 if {$inPlugin} {
30 set defaultPrompt {subst {[history nextid] % }}
31 } else {
32 set defaultPrompt {subst {([file tail [pwd]]) [history nextid] % }}
36 # simple compat function for tkcon code added for this console
37 interp alias {} EvalAttached {} consoleinterp eval
39 # ::tk::ConsoleInit --
40 # This procedure constructs and configures the console windows.
42 # Arguments:
43 # None.
45 proc ::tk::ConsoleInit {} {
46 global tcl_platform
48 if {![consoleinterp eval {set tcl_interactive}]} {
49 wm withdraw .
52 if {$tcl_platform(platform) eq "macintosh"
53 || [tk windowingsystem] eq "aqua"} {
54 set mod "Cmd"
55 } else {
56 set mod "Ctrl"
59 if {[catch {menu .menubar} err]} { bgerror "INIT: $err" }
60 .menubar add cascade -label File -menu .menubar.file -underline 0
61 .menubar add cascade -label Edit -menu .menubar.edit -underline 0
63 menu .menubar.file -tearoff 0
64 .menubar.file add command -label [mc "Source..."] \
65 -underline 0 -command tk::ConsoleSource
66 .menubar.file add command -label [mc "Hide Console"] \
67 -underline 0 -command {wm withdraw .}
68 .menubar.file add command -label [mc "Clear Console"] \
69 -underline 0 -command {.console delete 1.0 "promptEnd linestart"}
70 if {$tcl_platform(platform) eq "macintosh"
71 || [tk windowingsystem] eq "aqua"} {
72 .menubar.file add command -label [mc "Quit"] \
73 -command exit -accel Cmd-Q
74 } else {
75 .menubar.file add command -label [mc "Exit"] \
76 -underline 1 -command exit
79 menu .menubar.edit -tearoff 0
80 .menubar.edit add command -label [mc "Cut"] -underline 2 \
81 -command { event generate .console <<Cut>> } -accel "$mod+X"
82 .menubar.edit add command -label [mc "Copy"] -underline 0 \
83 -command { event generate .console <<Copy>> } -accel "$mod+C"
84 .menubar.edit add command -label [mc "Paste"] -underline 1 \
85 -command { event generate .console <<Paste>> } -accel "$mod+V"
87 if {$tcl_platform(platform) ne "windows"} {
88 .menubar.edit add command -label [mc "Clear"] -underline 2 \
89 -command { event generate .console <<Clear>> }
90 } else {
91 .menubar.edit add command -label [mc "Delete"] -underline 0 \
92 -command { event generate .console <<Clear>> } -accel "Del"
94 .menubar add cascade -label Help -menu .menubar.help -underline 0
95 menu .menubar.help -tearoff 0
96 .menubar.help add command -label [mc "About..."] \
97 -underline 0 -command tk::ConsoleAbout
100 . configure -menu .menubar
102 set con [text .console -yscrollcommand [list .sb set] -setgrid true]
103 scrollbar .sb -command [list $con yview]
104 pack .sb -side right -fill both
105 pack $con -fill both -expand 1 -side left
106 switch -exact $tcl_platform(platform) {
107 "macintosh" {
108 $con configure -font {Monaco 9 normal} -highlightthickness 0
110 "windows" {
111 $con configure -font systemfixed
113 "unix" {
114 if {[tk windowingsystem] eq "aqua"} {
115 $con configure -font {Monaco 9 normal} -highlightthickness 0
120 ConsoleBind $con
122 $con tag configure stderr -foreground red
123 $con tag configure stdin -foreground blue
124 $con tag configure prompt -foreground \#8F4433
125 $con tag configure proc -foreground \#008800
126 $con tag configure var -background \#FFC0D0
127 $con tag raise sel
128 $con tag configure blink -background \#FFFF00
129 $con tag configure find -background \#FFFF00
131 focus $con
133 wm protocol . WM_DELETE_WINDOW { wm withdraw . }
134 wm title . [mc "Console"]
135 flush stdout
136 $con mark set output [$con index "end - 1 char"]
137 tk::TextSetCursor $con end
138 $con mark set promptEnd insert
139 $con mark gravity promptEnd left
141 # A variant of ConsolePrompt to avoid a 'puts' call
142 set w $con
143 set temp [$w index "end - 1 char"]
144 $w mark set output end
145 if {![consoleinterp eval "info exists tcl_prompt1"]} {
146 set string [EvalAttached $::tk::console::defaultPrompt]
147 $w insert output $string stdout
149 $w mark set output $temp
150 ::tk::TextSetCursor $w end
151 $w mark set promptEnd insert
152 $w mark gravity promptEnd left
154 if {$tcl_platform(platform) eq "windows"} {
155 # Subtle work-around to erase the '% ' that tclMain.c prints out
156 after idle [subst -nocommand {
157 if {[$con get 1.0 output] eq "% "} { $con delete 1.0 output }
162 # ::tk::ConsoleSource --
164 # Prompts the user for a file to source in the main interpreter.
166 # Arguments:
167 # None.
169 proc ::tk::ConsoleSource {} {
170 set filename [tk_getOpenFile -defaultextension .tcl -parent . \
171 -title [mc "Select a file to source"] \
172 -filetypes [list \
173 [list [mc "Tcl Scripts"] .tcl] \
174 [list [mc "All Files"] *]]]
175 if {$filename ne ""} {
176 set cmd [list source $filename]
177 if {[catch {consoleinterp eval $cmd} result]} {
178 ConsoleOutput stderr "$result\n"
183 # ::tk::ConsoleInvoke --
184 # Processes the command line input. If the command is complete it
185 # is evaled in the main interpreter. Otherwise, the continuation
186 # prompt is added and more input may be added.
188 # Arguments:
189 # None.
191 proc ::tk::ConsoleInvoke {args} {
192 set ranges [.console tag ranges input]
193 set cmd ""
194 if {[llength $ranges]} {
195 set pos 0
196 while {[lindex $ranges $pos] ne ""} {
197 set start [lindex $ranges $pos]
198 set end [lindex $ranges [incr pos]]
199 append cmd [.console get $start $end]
200 incr pos
203 if {$cmd eq ""} {
204 ConsolePrompt
205 } elseif {[info complete $cmd]} {
206 .console mark set output end
207 .console tag delete input
208 set result [consoleinterp record $cmd]
209 if {$result ne ""} {
210 puts $result
212 ConsoleHistory reset
213 ConsolePrompt
214 } else {
215 ConsolePrompt partial
217 .console yview -pickplace insert
220 # ::tk::ConsoleHistory --
221 # This procedure implements command line history for the
222 # console. In general is evals the history command in the
223 # main interpreter to obtain the history. The variable
224 # ::tk::HistNum is used to store the current location in the history.
226 # Arguments:
227 # cmd - Which action to take: prev, next, reset.
229 set ::tk::HistNum 1
230 proc ::tk::ConsoleHistory {cmd} {
231 variable HistNum
233 switch $cmd {
234 prev {
235 incr HistNum -1
236 if {$HistNum == 0} {
237 set cmd {history event [expr {[history nextid] -1}]}
238 } else {
239 set cmd "history event $HistNum"
241 if {[catch {consoleinterp eval $cmd} cmd]} {
242 incr HistNum
243 return
245 .console delete promptEnd end
246 .console insert promptEnd $cmd {input stdin}
248 next {
249 incr HistNum
250 if {$HistNum == 0} {
251 set cmd {history event [expr {[history nextid] -1}]}
252 } elseif {$HistNum > 0} {
253 set cmd ""
254 set HistNum 1
255 } else {
256 set cmd "history event $HistNum"
258 if {$cmd ne ""} {
259 catch {consoleinterp eval $cmd} cmd
261 .console delete promptEnd end
262 .console insert promptEnd $cmd {input stdin}
264 reset {
265 set HistNum 1
270 # ::tk::ConsolePrompt --
271 # This procedure draws the prompt. If tcl_prompt1 or tcl_prompt2
272 # exists in the main interpreter it will be called to generate the
273 # prompt. Otherwise, a hard coded default prompt is printed.
275 # Arguments:
276 # partial - Flag to specify which prompt to print.
278 proc ::tk::ConsolePrompt {{partial normal}} {
279 set w .console
280 if {$partial eq "normal"} {
281 set temp [$w index "end - 1 char"]
282 $w mark set output end
283 if {[consoleinterp eval "info exists tcl_prompt1"]} {
284 consoleinterp eval "eval \[set tcl_prompt1\]"
285 } else {
286 puts -nonewline [EvalAttached $::tk::console::defaultPrompt]
288 } else {
289 set temp [$w index output]
290 $w mark set output end
291 if {[consoleinterp eval "info exists tcl_prompt2"]} {
292 consoleinterp eval "eval \[set tcl_prompt2\]"
293 } else {
294 puts -nonewline "> "
297 flush stdout
298 $w mark set output $temp
299 ::tk::TextSetCursor $w end
300 $w mark set promptEnd insert
301 $w mark gravity promptEnd left
302 ::tk::console::ConstrainBuffer $w $::tk::console::maxLines
303 $w see end
306 # ::tk::ConsoleBind --
307 # This procedure first ensures that the default bindings for the Text
308 # class have been defined. Then certain bindings are overridden for
309 # the class.
311 # Arguments:
312 # None.
314 proc ::tk::ConsoleBind {w} {
315 bindtags $w [list $w Console PostConsole [winfo toplevel $w] all]
317 ## Get all Text bindings into Console
318 foreach ev [bind Text] { bind Console $ev [bind Text $ev] }
319 ## We really didn't want the newline insertion...
320 bind Console <Control-Key-o> {}
321 ## ...or any Control-v binding (would block <<Paste>>)
322 bind Console <Control-Key-v> {}
324 # For the moment, transpose isn't enabled until the console
325 # gets and overhaul of how it handles input -- hobbs
326 bind Console <Control-Key-t> {}
328 # Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
329 # Otherwise, if a widget binding for one of these is defined, the
331 bind Console <Alt-KeyPress> {# nothing }
332 bind Console <Meta-KeyPress> {# nothing}
333 bind Console <Control-KeyPress> {# nothing}
335 foreach {ev key} {
336 <<Console_Prev>> <Key-Up>
337 <<Console_Next>> <Key-Down>
338 <<Console_NextImmediate>> <Control-Key-n>
339 <<Console_PrevImmediate>> <Control-Key-p>
340 <<Console_PrevSearch>> <Control-Key-r>
341 <<Console_NextSearch>> <Control-Key-s>
343 <<Console_Expand>> <Key-Tab>
344 <<Console_Expand>> <Key-Escape>
345 <<Console_ExpandFile>> <Control-Shift-Key-F>
346 <<Console_ExpandProc>> <Control-Shift-Key-P>
347 <<Console_ExpandVar>> <Control-Shift-Key-V>
348 <<Console_Tab>> <Control-Key-i>
349 <<Console_Tab>> <Meta-Key-i>
350 <<Console_Eval>> <Key-Return>
351 <<Console_Eval>> <Key-KP_Enter>
353 <<Console_Clear>> <Control-Key-l>
354 <<Console_KillLine>> <Control-Key-k>
355 <<Console_Transpose>> <Control-Key-t>
356 <<Console_ClearLine>> <Control-Key-u>
357 <<Console_SaveCommand>> <Control-Key-z>
359 event add $ev $key
360 bind Console $key {}
363 bind Console <<Console_Expand>> {
364 if {[%W compare insert > promptEnd]} {::tk::console::Expand %W}
366 bind Console <<Console_ExpandFile>> {
367 if {[%W compare insert > promptEnd]} {::tk::console::Expand %W path}
369 bind Console <<Console_ExpandProc>> {
370 if {[%W compare insert > promptEnd]} {::tk::console::Expand %W proc}
372 bind Console <<Console_ExpandVar>> {
373 if {[%W compare insert > promptEnd]} {::tk::console::Expand %W var}
375 bind Console <<Console_Eval>> {
376 %W mark set insert {end - 1c}
377 tk::ConsoleInsert %W "\n"
378 tk::ConsoleInvoke
379 break
381 bind Console <Delete> {
382 if {[%W tag nextrange sel 1.0 end] ne "" && [%W compare sel.first >= promptEnd]} {
383 %W delete sel.first sel.last
384 } elseif {[%W compare insert >= promptEnd]} {
385 %W delete insert
386 %W see insert
389 bind Console <BackSpace> {
390 if {[%W tag nextrange sel 1.0 end] ne "" && [%W compare sel.first >= promptEnd]} {
391 %W delete sel.first sel.last
392 } elseif {[%W compare insert != 1.0] && \
393 [%W compare insert > promptEnd]} {
394 %W delete insert-1c
395 %W see insert
398 bind Console <Control-h> [bind Console <BackSpace>]
400 bind Console <Home> {
401 if {[%W compare insert < promptEnd]} {
402 tk::TextSetCursor %W {insert linestart}
403 } else {
404 tk::TextSetCursor %W promptEnd
407 bind Console <Control-a> [bind Console <Home>]
408 bind Console <End> {
409 tk::TextSetCursor %W {insert lineend}
411 bind Console <Control-e> [bind Console <End>]
412 bind Console <Control-d> {
413 if {[%W compare insert < promptEnd]} break
414 %W delete insert
416 bind Console <<Console_KillLine>> {
417 if {[%W compare insert < promptEnd]} break
418 if {[%W compare insert == {insert lineend}]} {
419 %W delete insert
420 } else {
421 %W delete insert {insert lineend}
424 bind Console <<Console_Clear>> {
425 ## Clear console display
426 %W delete 1.0 "promptEnd linestart"
428 bind Console <<Console_ClearLine>> {
429 ## Clear command line (Unix shell staple)
430 %W delete promptEnd end
432 bind Console <Meta-d> {
433 if {[%W compare insert >= promptEnd]} {
434 %W delete insert {insert wordend}
437 bind Console <Meta-BackSpace> {
438 if {[%W compare {insert -1c wordstart} >= promptEnd]} {
439 %W delete {insert -1c wordstart} insert
442 bind Console <Meta-d> {
443 if {[%W compare insert >= promptEnd]} {
444 %W delete insert {insert wordend}
447 bind Console <Meta-BackSpace> {
448 if {[%W compare {insert -1c wordstart} >= promptEnd]} {
449 %W delete {insert -1c wordstart} insert
452 bind Console <Meta-Delete> {
453 if {[%W compare insert >= promptEnd]} {
454 %W delete insert {insert wordend}
457 bind Console <<Console_Prev>> {
458 tk::ConsoleHistory prev
460 bind Console <<Console_Next>> {
461 tk::ConsoleHistory next
463 bind Console <Insert> {
464 catch {tk::ConsoleInsert %W [::tk::GetSelection %W PRIMARY]}
466 bind Console <KeyPress> {
467 tk::ConsoleInsert %W %A
469 bind Console <F9> {
470 eval destroy [winfo child .]
471 if {$tcl_platform(platform) eq "macintosh"} {
472 if {[catch {source [file join $tk_library console.tcl]}]} {source -rsrc console}
473 } else {
474 source [file join $tk_library console.tcl]
477 if {$::tcl_platform(platform) eq "macintosh" || [tk windowingsystem] eq "aqua"} {
478 bind Console <Command-q> {
479 exit
482 bind Console <<Cut>> {
483 # Same as the copy event
484 if {![catch {set data [%W get sel.first sel.last]}]} {
485 clipboard clear -displayof %W
486 clipboard append -displayof %W $data
489 bind Console <<Copy>> {
490 if {![catch {set data [%W get sel.first sel.last]}]} {
491 clipboard clear -displayof %W
492 clipboard append -displayof %W $data
495 bind Console <<Paste>> {
496 catch {
497 set clip [::tk::GetSelection %W CLIPBOARD]
498 set list [split $clip \n\r]
499 tk::ConsoleInsert %W [lindex $list 0]
500 foreach x [lrange $list 1 end] {
501 %W mark set insert {end - 1c}
502 tk::ConsoleInsert %W "\n"
503 tk::ConsoleInvoke
504 tk::ConsoleInsert %W $x
510 ## Bindings for doing special things based on certain keys
512 bind PostConsole <Key-parenright> {
513 if {"\\" ne [%W get insert-2c]} {
514 ::tk::console::MatchPair %W \( \) promptEnd
517 bind PostConsole <Key-bracketright> {
518 if {"\\" ne [%W get insert-2c]} {
519 ::tk::console::MatchPair %W \[ \] promptEnd
522 bind PostConsole <Key-braceright> {
523 if {"\\" ne [%W get insert-2c]} {
524 ::tk::console::MatchPair %W \{ \} promptEnd
527 bind PostConsole <Key-quotedbl> {
528 if {"\\" ne [%W get insert-2c]} {
529 ::tk::console::MatchQuote %W promptEnd
533 bind PostConsole <KeyPress> {
534 if {"%A" ne ""} {
535 ::tk::console::TagProc %W
537 break
541 # ::tk::ConsoleInsert --
542 # Insert a string into a text at the point of the insertion cursor.
543 # If there is a selection in the text, and it covers the point of the
544 # insertion cursor, then delete the selection before inserting. Insertion
545 # is restricted to the prompt area.
547 # Arguments:
548 # w - The text window in which to insert the string
549 # s - The string to insert (usually just a single character)
551 proc ::tk::ConsoleInsert {w s} {
552 if {$s eq ""} {
553 return
555 catch {
556 if {[$w compare sel.first <= insert]
557 && [$w compare sel.last >= insert]} {
558 $w tag remove sel sel.first promptEnd
559 $w delete sel.first sel.last
562 if {[$w compare insert < promptEnd]} {
563 $w mark set insert end
565 $w insert insert $s {input stdin}
566 $w see insert
569 # ::tk::ConsoleOutput --
571 # This routine is called directly by ConsolePutsCmd to cause a string
572 # to be displayed in the console.
574 # Arguments:
575 # dest - The output tag to be used: either "stderr" or "stdout".
576 # string - The string to be displayed.
578 proc ::tk::ConsoleOutput {dest string} {
579 set w .console
580 $w insert output $string $dest
581 ::tk::console::ConstrainBuffer $w $::tk::console::maxLines
582 $w see insert
585 # ::tk::ConsoleExit --
587 # This routine is called by ConsoleEventProc when the main window of
588 # the application is destroyed. Don't call exit - that probably already
589 # happened. Just delete our window.
591 # Arguments:
592 # None.
594 proc ::tk::ConsoleExit {} {
595 destroy .
598 # ::tk::ConsoleAbout --
600 # This routine displays an About box to show Tcl/Tk version info.
602 # Arguments:
603 # None.
605 proc ::tk::ConsoleAbout {} {
606 tk_messageBox -type ok -message "[mc {Tcl for Windows}]
608 Tcl $::tcl_patchLevel
609 Tk $::tk_patchLevel"
612 # ::tk::console::TagProc --
614 # Tags a procedure in the console if it's recognized
615 # This procedure is not perfect. However, making it perfect wastes
616 # too much CPU time...
618 # Arguments:
619 # w - console text widget
621 proc ::tk::console::TagProc w {
622 if {!$::tk::console::magicKeys} { return }
623 set exp "\[^\\\\\]\[\[ \t\n\r\;{}\"\$\]"
624 set i [$w search -backwards -regexp $exp insert-1c promptEnd-1c]
625 if {$i eq ""} {set i promptEnd} else {append i +2c}
626 regsub -all "\[\[\\\\\\?\\*\]" [$w get $i "insert-1c wordend"] {\\\0} c
627 if {[llength [EvalAttached [list info commands $c]]]} {
628 $w tag add proc $i "insert-1c wordend"
629 } else {
630 $w tag remove proc $i "insert-1c wordend"
632 if {[llength [EvalAttached [list info vars $c]]]} {
633 $w tag add var $i "insert-1c wordend"
634 } else {
635 $w tag remove var $i "insert-1c wordend"
639 # ::tk::console::MatchPair --
641 # Blinks a matching pair of characters
642 # c2 is assumed to be at the text index 'insert'.
643 # This proc is really loopy and took me an hour to figure out given
644 # all possible combinations with escaping except for escaped \'s.
645 # It doesn't take into account possible commenting... Oh well. If
646 # anyone has something better, I'd like to see/use it. This is really
647 # only efficient for small contexts.
649 # Arguments:
650 # w - console text widget
651 # c1 - first char of pair
652 # c2 - second char of pair
654 # Calls: ::tk::console::Blink
656 proc ::tk::console::MatchPair {w c1 c2 {lim 1.0}} {
657 if {!$::tk::console::magicKeys} { return }
658 if {[set ix [$w search -back $c1 insert $lim]] ne ""} {
659 while {
660 [string match {\\} [$w get $ix-1c]] &&
661 [set ix [$w search -back $c1 $ix-1c $lim]] ne ""
662 } {}
663 set i1 insert-1c
664 while {$ix ne ""} {
665 set i0 $ix
666 set j 0
667 while {[set i0 [$w search $c2 $i0 $i1]] ne ""} {
668 append i0 +1c
669 if {[string match {\\} [$w get $i0-2c]]} continue
670 incr j
672 if {!$j} break
673 set i1 $ix
674 while {$j && [set ix [$w search -back $c1 $ix $lim]] ne ""} {
675 if {[string match {\\} [$w get $ix-1c]]} continue
676 incr j -1
679 if {[string match {} $ix]} { set ix [$w index $lim] }
680 } else { set ix [$w index $lim] }
681 if {$::tk::console::blinkRange} {
682 Blink $w $ix [$w index insert]
683 } else {
684 Blink $w $ix $ix+1c [$w index insert-1c] [$w index insert]
688 # ::tk::console::MatchQuote --
690 # Blinks between matching quotes.
691 # Blinks just the quote if it's unmatched, otherwise blinks quoted string
692 # The quote to match is assumed to be at the text index 'insert'.
694 # Arguments:
695 # w - console text widget
697 # Calls: ::tk::console::Blink
699 proc ::tk::console::MatchQuote {w {lim 1.0}} {
700 if {!$::tk::console::magicKeys} { return }
701 set i insert-1c
702 set j 0
703 while {[set i [$w search -back \" $i $lim]] ne ""} {
704 if {[string match {\\} [$w get $i-1c]]} continue
705 if {!$j} {set i0 $i}
706 incr j
708 if {$j&1} {
709 if {$::tk::console::blinkRange} {
710 Blink $w $i0 [$w index insert]
711 } else {
712 Blink $w $i0 $i0+1c [$w index insert-1c] [$w index insert]
714 } else {
715 Blink $w [$w index insert-1c] [$w index insert]
719 # ::tk::console::Blink --
721 # Blinks between n index pairs for a specified duration.
723 # Arguments:
724 # w - console text widget
725 # i1 - start index to blink region
726 # i2 - end index of blink region
727 # dur - duration in usecs to blink for
729 # Outputs:
730 # blinks selected characters in $w
732 proc ::tk::console::Blink {w args} {
733 eval [list $w tag add blink] $args
734 after $::tk::console::blinkTime [list $w] tag remove blink $args
737 # ::tk::console::ConstrainBuffer --
739 # This limits the amount of data in the text widget
740 # Called by Prompt and ConsoleOutput
742 # Arguments:
743 # w - console text widget
744 # size - # of lines to constrain to
746 # Outputs:
747 # may delete data in console widget
749 proc ::tk::console::ConstrainBuffer {w size} {
750 if {[$w index end] > $size} {
751 $w delete 1.0 [expr {int([$w index end])-$size}].0
755 # ::tk::console::Expand --
757 # Arguments:
758 # ARGS: w - text widget in which to expand str
759 # type - type of expansion (path / proc / variable)
761 # Calls: ::tk::console::Expand(Pathname|Procname|Variable)
763 # Outputs: The string to match is expanded to the longest possible match.
764 # If ::tk::console::showMatches is non-zero and the longest match
765 # equaled the string to expand, then all possible matches are
766 # output to stdout. Triggers bell if no matches are found.
768 # Returns: number of matches found
770 proc ::tk::console::Expand {w {type ""}} {
771 set exp "\[^\\\\\]\[\[ \t\n\r\\\{\"\\\\\$\]"
772 set tmp [$w search -backwards -regexp $exp insert-1c promptEnd-1c]
773 if {$tmp eq ""} {set tmp promptEnd} else {append tmp +2c}
774 if {[$w compare $tmp >= insert]} { return }
775 set str [$w get $tmp insert]
776 switch -glob $type {
777 path* { set res [ExpandPathname $str] }
778 proc* { set res [ExpandProcname $str] }
779 var* { set res [ExpandVariable $str] }
780 default {
781 set res {}
782 foreach t {Pathname Procname Variable} {
783 if {![catch {Expand$t $str} res] && ($res ne "")} { break }
787 set len [llength $res]
788 if {$len} {
789 set repl [lindex $res 0]
790 $w delete $tmp insert
791 $w insert $tmp $repl {input stdin}
792 if {($len > 1) && $::tk::console::showMatches && $repl eq $str} {
793 puts stdout [lsort [lreplace $res 0 0]]
795 } else { bell }
796 return [incr len -1]
799 # ::tk::console::ExpandPathname --
801 # Expand a file pathname based on $str
802 # This is based on UNIX file name conventions
804 # Arguments:
805 # str - partial file pathname to expand
807 # Calls: ::tk::console::ExpandBestMatch
809 # Returns: list containing longest unique match followed by all the
810 # possible further matches
812 proc ::tk::console::ExpandPathname str {
813 set pwd [EvalAttached pwd]
814 if {[catch {EvalAttached [list cd [file dirname $str]]} err]} {
815 return -code error $err
817 set dir [file tail $str]
818 ## Check to see if it was known to be a directory and keep the trailing
819 ## slash if so (file tail cuts it off)
820 if {[string match */ $str]} { append dir / }
821 if {[catch {lsort [EvalAttached [list glob $dir*]]} m]} {
822 set match {}
823 } else {
824 if {[llength $m] > 1} {
825 global tcl_platform
826 if {[string match windows $tcl_platform(platform)]} {
827 ## Windows is screwy because it's case insensitive
828 set tmp [ExpandBestMatch [string tolower $m] \
829 [string tolower $dir]]
830 ## Don't change case if we haven't changed the word
831 if {[string length $dir]==[string length $tmp]} {
832 set tmp $dir
834 } else {
835 set tmp [ExpandBestMatch $m $dir]
837 if {[string match ?*/* $str]} {
838 set tmp [file dirname $str]/$tmp
839 } elseif {[string match /* $str]} {
840 set tmp /$tmp
842 regsub -all { } $tmp {\\ } tmp
843 set match [linsert $m 0 $tmp]
844 } else {
845 ## This may look goofy, but it handles spaces in path names
846 eval append match $m
847 if {[file isdir $match]} {append match /}
848 if {[string match ?*/* $str]} {
849 set match [file dirname $str]/$match
850 } elseif {[string match /* $str]} {
851 set match /$match
853 regsub -all { } $match {\\ } match
854 ## Why is this one needed and the ones below aren't!!
855 set match [list $match]
858 EvalAttached [list cd $pwd]
859 return $match
862 # ::tk::console::ExpandProcname --
864 # Expand a tcl proc name based on $str
866 # Arguments:
867 # str - partial proc name to expand
869 # Calls: ::tk::console::ExpandBestMatch
871 # Returns: list containing longest unique match followed by all the
872 # possible further matches
874 proc ::tk::console::ExpandProcname str {
875 set match [EvalAttached [list info commands $str*]]
876 if {[llength $match] == 0} {
877 set ns [EvalAttached \
878 "namespace children \[namespace current\] [list $str*]"]
879 if {[llength $ns]==1} {
880 set match [EvalAttached [list info commands ${ns}::*]]
881 } else {
882 set match $ns
885 if {[llength $match] > 1} {
886 regsub -all { } [ExpandBestMatch $match $str] {\\ } str
887 set match [linsert $match 0 $str]
888 } else {
889 regsub -all { } $match {\\ } match
891 return $match
894 # ::tk::console::ExpandVariable --
896 # Expand a tcl variable name based on $str
898 # Arguments:
899 # str - partial tcl var name to expand
901 # Calls: ::tk::console::ExpandBestMatch
903 # Returns: list containing longest unique match followed by all the
904 # possible further matches
906 proc ::tk::console::ExpandVariable str {
907 if {[regexp {([^\(]*)\((.*)} $str junk ary str]} {
908 ## Looks like they're trying to expand an array.
909 set match [EvalAttached [list array names $ary $str*]]
910 if {[llength $match] > 1} {
911 set vars $ary\([ExpandBestMatch $match $str]
912 foreach var $match {lappend vars $ary\($var\)}
913 return $vars
914 } elseif {[llength $match] == 1} {
915 set match $ary\($match\)
917 ## Space transformation avoided for array names.
918 } else {
919 set match [EvalAttached [list info vars $str*]]
920 if {[llength $match] > 1} {
921 regsub -all { } [ExpandBestMatch $match $str] {\\ } str
922 set match [linsert $match 0 $str]
923 } else {
924 regsub -all { } $match {\\ } match
927 return $match
930 # ::tk::console::ExpandBestMatch --
932 # Finds the best unique match in a list of names.
933 # The extra $e in this argument allows us to limit the innermost loop a little
934 # further. This improves speed as $l becomes large or $e becomes long.
936 # Arguments:
937 # l - list to find best unique match in
938 # e - currently best known unique match
940 # Returns: longest unique match in the list
942 proc ::tk::console::ExpandBestMatch {l {e {}}} {
943 set ec [lindex $l 0]
944 if {[llength $l]>1} {
945 set e [string length $e]; incr e -1
946 set ei [string length $ec]; incr ei -1
947 foreach l $l {
948 while {$ei>=$e && [string first $ec $l]} {
949 set ec [string range $ec 0 [incr ei -1]]
953 return $ec
956 # now initialize the console
957 ::tk::ConsoleInit