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
7 # Copyright (c) 1995-1997 Sun Microsystems, Inc.
8 # Copyright (c) 1998-2000 Ajuba Solutions.
9 # Copyright (c) 2007-2008 Daniel A. Steffen <das@users.sourceforge.net>
11 # See the file "license.terms" for information on usage and redistribution
12 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 # TODO: history - remember partially written command
17 namespace eval ::tk::console {
18 variable blinkTime 500 ; # msecs to blink braced range for
19 variable blinkRange 1 ; # enable blinking of the entire braced range
20 variable magicKeys 1 ; # enable brace matching and proc/var recognition
21 variable maxLines 600 ; # maximum # of lines buffered in console
22 variable showMatches 1 ; # show multiple expand matches
23 variable useFontchooser [llength [info command ::tk::fontchooser]]
24 variable inPlugin [info exists embed_args]
25 variable defaultPrompt ; # default prompt if tcl_prompt1 isn't used
28 set defaultPrompt {subst {[history nextid] % }}
30 set defaultPrompt {subst {([file tail [pwd]]) [history nextid] % }}
34 # simple compat function for tkcon code added for this console
35 interp alias {} EvalAttached {} consoleinterp eval
37 # ::tk::ConsoleInit --
38 # This procedure constructs and configures the console windows.
43 proc ::tk::ConsoleInit {} {
46 if {![consoleinterp eval {set tcl_interactive}]} {
50 if {[tk windowingsystem] eq "aqua"} {
56 if {[catch {menu .menubar} err]} {
59 AmpMenuArgs .menubar add cascade -label [mc &File] -menu .menubar.file
60 AmpMenuArgs .menubar add cascade -label [mc &Edit] -menu .menubar.edit
62 menu .menubar.file -tearoff 0
63 AmpMenuArgs .menubar.file add command -label [mc "&Source..."] \
64 -command {tk::ConsoleSource}
65 AmpMenuArgs .menubar.file add command -label [mc "&Hide Console"] \
66 -command {wm withdraw .}
67 AmpMenuArgs .menubar.file add command -label [mc "&Clear Console"] \
68 -command {.console delete 1.0 "promptEnd linestart"}
69 if {[tk windowingsystem] ne "aqua"} {
70 AmpMenuArgs .menubar.file add command -label [mc E&xit] -command {exit}
73 menu .menubar.edit -tearoff 0
74 AmpMenuArgs .menubar.edit add command -label [mc Cu&t] -accel "$mod+X"\
75 -command {event generate .console <<Cut>>}
76 AmpMenuArgs .menubar.edit add command -label [mc &Copy] -accel "$mod+C"\
77 -command {event generate .console <<Copy>>}
78 AmpMenuArgs .menubar.edit add command -label [mc P&aste] -accel "$mod+V"\
79 -command {event generate .console <<Paste>>}
81 if {$tcl_platform(platform) ne "windows"} {
82 AmpMenuArgs .menubar.edit add command -label [mc Cl&ear] \
83 -command {event generate .console <<Clear>>}
85 AmpMenuArgs .menubar.edit add command -label [mc &Delete] \
86 -command {event generate .console <<Clear>>} -accel "Del"
88 AmpMenuArgs .menubar add cascade -label [mc &Help] -menu .menubar.help
89 menu .menubar.help -tearoff 0
90 AmpMenuArgs .menubar.help add command -label [mc &About...] \
91 -command tk::ConsoleAbout
94 AmpMenuArgs .menubar.edit add separator
95 if {$::tk::console::useFontchooser} {
96 if {[tk windowingsystem] eq "aqua"} {
97 .menubar.edit add command -label tk_choose_font_marker
98 set index [.menubar.edit index tk_choose_font_marker]
99 .menubar.edit entryconfigure $index \
100 -label [mc "Show Fonts"]\
101 -accelerator "$mod-T"\
102 -command [list ::tk::console::FontchooserToggle]
103 bind Console <<TkFontchooserVisibility>> \
104 [list ::tk::console::FontchooserVisibility $index]
105 ::tk::console::FontchooserVisibility $index
107 AmpMenuArgs .menubar.edit add command -label [mc "&Font..."] \
108 -command [list ::tk::console::FontchooserToggle]
110 bind Console <FocusIn> [list ::tk::console::FontchooserFocus %W 1]
111 bind Console <FocusOut> [list ::tk::console::FontchooserFocus %W 0]
113 AmpMenuArgs .menubar.edit add command -label [mc "&Increase Font Size"] \
114 -accel "$mod++" -command {event generate .console <<Console_FontSizeIncr>>}
115 AmpMenuArgs .menubar.edit add command -label [mc "&Decrease Font Size"] \
116 -accel "$mod+-" -command {event generate .console <<Console_FontSizeDecr>>}
118 if {[tk windowingsystem] eq "aqua"} {
119 .menubar add cascade -label [mc Window] -menu [menu .menubar.window]
120 .menubar add cascade -label [mc Help] -menu [menu .menubar.help]
123 . configure -menu .menubar
125 # See if we can find a better font than the TkFixedFont
126 catch {font create TkConsoleFont {*}[font configure TkFixedFont]}
127 set families [font families]
128 switch -exact -- [tk windowingsystem] {
129 aqua { set preferred {Monaco 10} }
130 win32 { set preferred {ProFontWindows 8 Consolas 8} }
131 default { set preferred {} }
133 foreach {family size} $preferred {
134 if {[lsearch -exact $families $family] != -1} {
135 font configure TkConsoleFont -family $family -size $size
140 # Provide the right border for the text widget (platform dependent).
141 ::ttk::style layout ConsoleFrame {
142 Entry.field -sticky news -border 1 -children {
143 ConsoleFrame.padding -sticky news
146 ::ttk::frame .consoleframe -style ConsoleFrame
148 set con [text .console -yscrollcommand [list .sb set] -setgrid true \
149 -borderwidth 0 -highlightthickness 0 -font TkConsoleFont]
150 if {[tk windowingsystem] eq "aqua"} {
151 scrollbar .sb -command [list $con yview]
153 ::ttk::scrollbar .sb -command [list $con yview]
155 pack .sb -in .consoleframe -fill both -side right -padx 1 -pady 1
156 pack $con -in .consoleframe -fill both -expand 1 -side left -padx 1 -pady 1
157 pack .consoleframe -fill both -expand 1 -side left
161 $con tag configure stderr -foreground red
162 $con tag configure stdin -foreground blue
163 $con tag configure prompt -foreground \#8F4433
164 $con tag configure proc -foreground \#008800
165 $con tag configure var -background \#FFC0D0
167 $con tag configure blink -background \#FFFF00
168 $con tag configure find -background \#FFFF00
172 # Avoid listing this console in [winfo interps]
173 if {[info command ::send] eq "::send"} {rename ::send {}}
175 wm protocol . WM_DELETE_WINDOW { wm withdraw . }
176 wm title . [mc "Console"]
178 $con mark set output [$con index "end - 1 char"]
179 tk::TextSetCursor $con end
180 $con mark set promptEnd insert
181 $con mark gravity promptEnd left
183 # A variant of ConsolePrompt to avoid a 'puts' call
185 set temp [$w index "end - 1 char"]
186 $w mark set output end
187 if {![consoleinterp eval "info exists tcl_prompt1"]} {
188 set string [EvalAttached $::tk::console::defaultPrompt]
189 $w insert output $string stdout
191 $w mark set output $temp
192 ::tk::TextSetCursor $w end
193 $w mark set promptEnd insert
194 $w mark gravity promptEnd left
196 if {$tcl_platform(platform) eq "windows"} {
197 # Subtle work-around to erase the '% ' that tclMain.c prints out
198 after idle [subst -nocommand {
199 if {[$con get 1.0 output] eq "% "} { $con delete 1.0 output }
204 # ::tk::ConsoleSource --
206 # Prompts the user for a file to source in the main interpreter.
211 proc ::tk::ConsoleSource {} {
212 set filename [tk_getOpenFile -defaultextension .tcl -parent . \
213 -title [mc "Select a file to source"] \
215 [list [mc "Tcl Scripts"] .tcl] \
216 [list [mc "All Files"] *]]]
217 if {$filename ne ""} {
218 set cmd [list source $filename]
219 if {[catch {consoleinterp eval $cmd} result]} {
220 ConsoleOutput stderr "$result\n"
225 # ::tk::ConsoleInvoke --
226 # Processes the command line input. If the command is complete it
227 # is evaled in the main interpreter. Otherwise, the continuation
228 # prompt is added and more input may be added.
233 proc ::tk::ConsoleInvoke {args} {
234 set ranges [.console tag ranges input]
236 if {[llength $ranges]} {
238 while {[lindex $ranges $pos] ne ""} {
239 set start [lindex $ranges $pos]
240 set end [lindex $ranges [incr pos]]
241 append cmd [.console get $start $end]
247 } elseif {[info complete $cmd]} {
248 .console mark set output end
249 .console tag delete input
250 set result [consoleinterp record $cmd]
257 ConsolePrompt partial
259 .console yview -pickplace insert
262 # ::tk::ConsoleHistory --
263 # This procedure implements command line history for the
264 # console. In general is evals the history command in the
265 # main interpreter to obtain the history. The variable
266 # ::tk::HistNum is used to store the current location in the history.
269 # cmd - Which action to take: prev, next, reset.
272 proc ::tk::ConsoleHistory {cmd} {
279 set cmd {history event [expr {[history nextid] -1}]}
281 set cmd "history event $HistNum"
283 if {[catch {consoleinterp eval $cmd} cmd]} {
287 .console delete promptEnd end
288 .console insert promptEnd $cmd {input stdin}
293 set cmd {history event [expr {[history nextid] -1}]}
294 } elseif {$HistNum > 0} {
298 set cmd "history event $HistNum"
301 catch {consoleinterp eval $cmd} cmd
303 .console delete promptEnd end
304 .console insert promptEnd $cmd {input stdin}
312 # ::tk::ConsolePrompt --
313 # This procedure draws the prompt. If tcl_prompt1 or tcl_prompt2
314 # exists in the main interpreter it will be called to generate the
315 # prompt. Otherwise, a hard coded default prompt is printed.
318 # partial - Flag to specify which prompt to print.
320 proc ::tk::ConsolePrompt {{partial normal}} {
322 if {$partial eq "normal"} {
323 set temp [$w index "end - 1 char"]
324 $w mark set output end
325 if {[consoleinterp eval "info exists tcl_prompt1"]} {
326 consoleinterp eval "eval \[set tcl_prompt1\]"
328 puts -nonewline [EvalAttached $::tk::console::defaultPrompt]
331 set temp [$w index output]
332 $w mark set output end
333 if {[consoleinterp eval "info exists tcl_prompt2"]} {
334 consoleinterp eval "eval \[set tcl_prompt2\]"
340 $w mark set output $temp
341 ::tk::TextSetCursor $w end
342 $w mark set promptEnd insert
343 $w mark gravity promptEnd left
344 ::tk::console::ConstrainBuffer $w $::tk::console::maxLines
348 # Copy selected text from the console
349 proc ::tk::console::Copy {w} {
350 if {![catch {set data [$w get sel.first sel.last]}]} {
351 clipboard clear -displayof $w
352 clipboard append -displayof $w $data
355 # Copies selected text. If the selection is within the current active edit
356 # region then it will be cut, if not it is only copied.
357 proc ::tk::console::Cut {w} {
358 if {![catch {set data [$w get sel.first sel.last]}]} {
359 clipboard clear -displayof $w
360 clipboard append -displayof $w $data
361 if {[$w compare sel.first >= output]} {
362 $w delete sel.first sel.last
366 # Paste text from the clipboard
367 proc ::tk::console::Paste {w} {
369 set clip [::tk::GetSelection $w CLIPBOARD]
370 set list [split $clip \n\r]
371 tk::ConsoleInsert $w [lindex $list 0]
372 foreach x [lrange $list 1 end] {
373 $w mark set insert {end - 1c}
374 tk::ConsoleInsert $w "\n"
376 tk::ConsoleInsert $w $x
381 # ::tk::ConsoleBind --
382 # This procedure first ensures that the default bindings for the Text
383 # class have been defined. Then certain bindings are overridden for
389 proc ::tk::ConsoleBind {w} {
390 bindtags $w [list $w Console PostConsole [winfo toplevel $w] all]
392 ## Get all Text bindings into Console
393 foreach ev [bind Text] {
394 bind Console $ev [bind Text $ev]
396 ## We really didn't want the newline insertion...
397 bind Console <Control-Key-o> {}
398 ## ...or any Control-v binding (would block <<Paste>>)
399 bind Console <Control-Key-v> {}
401 # For the moment, transpose isn't enabled until the console
402 # gets and overhaul of how it handles input -- hobbs
403 bind Console <Control-Key-t> {}
405 # Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
406 # Otherwise, if a widget binding for one of these is defined, the
407 # <Keypress> class binding will also fire and insert the character
410 bind Console <Alt-KeyPress> {# nothing }
411 bind Console <Meta-KeyPress> {# nothing}
412 bind Console <Control-KeyPress> {# nothing}
415 <<Console_NextImmediate>> <Control-Key-n>
416 <<Console_PrevImmediate>> <Control-Key-p>
417 <<Console_PrevSearch>> <Control-Key-r>
418 <<Console_NextSearch>> <Control-Key-s>
420 <<Console_Expand>> <Key-Tab>
421 <<Console_Expand>> <Key-Escape>
422 <<Console_ExpandFile>> <Control-Shift-Key-F>
423 <<Console_ExpandProc>> <Control-Shift-Key-P>
424 <<Console_ExpandVar>> <Control-Shift-Key-V>
425 <<Console_Tab>> <Control-Key-i>
426 <<Console_Tab>> <Meta-Key-i>
427 <<Console_Eval>> <Key-Return>
428 <<Console_Eval>> <Key-KP_Enter>
430 <<Console_Clear>> <Control-Key-l>
431 <<Console_KillLine>> <Control-Key-k>
432 <<Console_Transpose>> <Control-Key-t>
433 <<Console_ClearLine>> <Control-Key-u>
434 <<Console_SaveCommand>> <Control-Key-z>
435 <<Console_FontSizeIncr>> <Control-Key-plus>
436 <<Console_FontSizeDecr>> <Control-Key-minus>
441 if {[tk windowingsystem] eq "aqua"} {
443 <<Console_FontSizeIncr>> <Command-Key-plus>
444 <<Console_FontSizeDecr>> <Command-Key-minus>
449 if {$::tk::console::useFontchooser} {
450 bind Console <Command-Key-t> [list ::tk::console::FontchooserToggle]
453 bind Console <<Console_Expand>> {
454 if {[%W compare insert > promptEnd]} {
455 ::tk::console::Expand %W
458 bind Console <<Console_ExpandFile>> {
459 if {[%W compare insert > promptEnd]} {
460 ::tk::console::Expand %W path
463 bind Console <<Console_ExpandProc>> {
464 if {[%W compare insert > promptEnd]} {
465 ::tk::console::Expand %W proc
468 bind Console <<Console_ExpandVar>> {
469 if {[%W compare insert > promptEnd]} {
470 ::tk::console::Expand %W var
473 bind Console <<Console_Eval>> {
474 %W mark set insert {end - 1c}
475 tk::ConsoleInsert %W "\n"
479 bind Console <Delete> {
480 if {{} ne [%W tag nextrange sel 1.0 end] \
481 && [%W compare sel.first >= promptEnd]} {
482 %W delete sel.first sel.last
483 } elseif {[%W compare insert >= promptEnd]} {
488 bind Console <BackSpace> {
489 if {{} ne [%W tag nextrange sel 1.0 end] \
490 && [%W compare sel.first >= promptEnd]} {
491 %W delete sel.first sel.last
492 } elseif {[%W compare insert != 1.0] && \
493 [%W compare insert > promptEnd]} {
498 bind Console <Control-h> [bind Console <BackSpace>]
500 bind Console <<LineStart>> {
501 if {[%W compare insert < promptEnd]} {
502 tk::TextSetCursor %W {insert linestart}
504 tk::TextSetCursor %W promptEnd
507 bind Console <<LineEnd>> {
508 tk::TextSetCursor %W {insert lineend}
510 bind Console <Control-d> {
511 if {[%W compare insert < promptEnd]} {
516 bind Console <<Console_KillLine>> {
517 if {[%W compare insert < promptEnd]} {
520 if {[%W compare insert == {insert lineend}]} {
523 %W delete insert {insert lineend}
526 bind Console <<Console_Clear>> {
527 ## Clear console display
528 %W delete 1.0 "promptEnd linestart"
530 bind Console <<Console_ClearLine>> {
531 ## Clear command line (Unix shell staple)
532 %W delete promptEnd end
534 bind Console <Meta-d> {
535 if {[%W compare insert >= promptEnd]} {
536 %W delete insert {insert wordend}
539 bind Console <Meta-BackSpace> {
540 if {[%W compare {insert -1c wordstart} >= promptEnd]} {
541 %W delete {insert -1c wordstart} insert
544 bind Console <Meta-d> {
545 if {[%W compare insert >= promptEnd]} {
546 %W delete insert {insert wordend}
549 bind Console <Meta-BackSpace> {
550 if {[%W compare {insert -1c wordstart} >= promptEnd]} {
551 %W delete {insert -1c wordstart} insert
554 bind Console <Meta-Delete> {
555 if {[%W compare insert >= promptEnd]} {
556 %W delete insert {insert wordend}
559 bind Console <<PrevLine>> {
560 tk::ConsoleHistory prev
562 bind Console <<NextLine>> {
563 tk::ConsoleHistory next
565 bind Console <Insert> {
566 catch {tk::ConsoleInsert %W [::tk::GetSelection %W PRIMARY]}
568 bind Console <KeyPress> {
569 tk::ConsoleInsert %W %A
572 eval destroy [winfo child .]
573 source [file join $tk_library console.tcl]
575 if {[tk windowingsystem] eq "aqua"} {
576 bind Console <Command-q> {
580 bind Console <<Cut>> { ::tk::console::Cut %W }
581 bind Console <<Copy>> { ::tk::console::Copy %W }
582 bind Console <<Paste>> { ::tk::console::Paste %W }
584 bind Console <<Console_FontSizeIncr>> {
585 set size [font configure TkConsoleFont -size]
586 if {$size < 0} {set sign -1} else {set sign 1}
587 set size [expr {(abs($size) + 1) * $sign}]
588 font configure TkConsoleFont -size $size
589 if {$::tk::console::useFontchooser} {
590 tk fontchooser configure -font TkConsoleFont
593 bind Console <<Console_FontSizeDecr>> {
594 set size [font configure TkConsoleFont -size]
595 if {abs($size) < 2} { return }
596 if {$size < 0} {set sign -1} else {set sign 1}
597 set size [expr {(abs($size) - 1) * $sign}]
598 font configure TkConsoleFont -size $size
599 if {$::tk::console::useFontchooser} {
600 tk fontchooser configure -font TkConsoleFont
605 ## Bindings for doing special things based on certain keys
607 bind PostConsole <Key-parenright> {
608 if {"\\" ne [%W get insert-2c]} {
609 ::tk::console::MatchPair %W \( \) promptEnd
612 bind PostConsole <Key-bracketright> {
613 if {"\\" ne [%W get insert-2c]} {
614 ::tk::console::MatchPair %W \[ \] promptEnd
617 bind PostConsole <Key-braceright> {
618 if {"\\" ne [%W get insert-2c]} {
619 ::tk::console::MatchPair %W \{ \} promptEnd
622 bind PostConsole <Key-quotedbl> {
623 if {"\\" ne [%W get insert-2c]} {
624 ::tk::console::MatchQuote %W promptEnd
628 bind PostConsole <KeyPress> {
630 ::tk::console::TagProc %W
635 # ::tk::ConsoleInsert --
636 # Insert a string into a text at the point of the insertion cursor.
637 # If there is a selection in the text, and it covers the point of the
638 # insertion cursor, then delete the selection before inserting. Insertion
639 # is restricted to the prompt area.
642 # w - The text window in which to insert the string
643 # s - The string to insert (usually just a single character)
645 proc ::tk::ConsoleInsert {w s} {
650 if {[$w compare sel.first <= insert] \
651 && [$w compare sel.last >= insert]} {
652 $w tag remove sel sel.first promptEnd
653 $w delete sel.first sel.last
656 if {[$w compare insert < promptEnd]} {
657 $w mark set insert end
659 $w insert insert $s {input stdin}
663 # ::tk::ConsoleOutput --
665 # This routine is called directly by ConsolePutsCmd to cause a string
666 # to be displayed in the console.
669 # dest - The output tag to be used: either "stderr" or "stdout".
670 # string - The string to be displayed.
672 proc ::tk::ConsoleOutput {dest string} {
674 $w insert output $string $dest
675 ::tk::console::ConstrainBuffer $w $::tk::console::maxLines
679 # ::tk::ConsoleExit --
681 # This routine is called by ConsoleEventProc when the main window of
682 # the application is destroyed. Don't call exit - that probably already
683 # happened. Just delete our window.
688 proc ::tk::ConsoleExit {} {
692 # ::tk::ConsoleAbout --
694 # This routine displays an About box to show Tcl/Tk version info.
699 proc ::tk::ConsoleAbout {} {
700 tk_messageBox -type ok -message "[mc {Tcl for Windows}]
702 Tcl $::tcl_patchLevel
706 # ::tk::console::Fontchooser* --
707 # Let the user select the console font (TIP 324).
709 proc ::tk::console::FontchooserToggle {} {
710 if {[tk fontchooser configure -visible]} {
716 proc ::tk::console::FontchooserVisibility {index} {
717 if {[tk fontchooser configure -visible]} {
718 .menubar.edit entryconfigure $index -label [msgcat::mc "Hide Fonts"]
720 .menubar.edit entryconfigure $index -label [msgcat::mc "Show Fonts"]
723 proc ::tk::console::FontchooserFocus {w isFocusIn} {
725 tk fontchooser configure -parent $w -font TkConsoleFont \
726 -command [namespace code [list FontchooserApply]]
728 tk fontchooser configure -parent $w -font {} -command {}
731 proc ::tk::console::FontchooserApply {font args} {
732 catch {font configure TkConsoleFont {*}[font actual $font]}
735 # ::tk::console::TagProc --
737 # Tags a procedure in the console if it's recognized
738 # This procedure is not perfect. However, making it perfect wastes
739 # too much CPU time...
742 # w - console text widget
744 proc ::tk::console::TagProc w {
745 if {!$::tk::console::magicKeys} {
748 set exp "\[^\\\\\]\[\[ \t\n\r\;{}\"\$\]"
749 set i [$w search -backwards -regexp $exp insert-1c promptEnd-1c]
755 regsub -all "\[\[\\\\\\?\\*\]" [$w get $i "insert-1c wordend"] {\\\0} c
756 if {[llength [EvalAttached [list info commands $c]]]} {
757 $w tag add proc $i "insert-1c wordend"
759 $w tag remove proc $i "insert-1c wordend"
761 if {[llength [EvalAttached [list info vars $c]]]} {
762 $w tag add var $i "insert-1c wordend"
764 $w tag remove var $i "insert-1c wordend"
768 # ::tk::console::MatchPair --
770 # Blinks a matching pair of characters
771 # c2 is assumed to be at the text index 'insert'.
772 # This proc is really loopy and took me an hour to figure out given
773 # all possible combinations with escaping except for escaped \'s.
774 # It doesn't take into account possible commenting... Oh well. If
775 # anyone has something better, I'd like to see/use it. This is really
776 # only efficient for small contexts.
779 # w - console text widget
780 # c1 - first char of pair
781 # c2 - second char of pair
783 # Calls: ::tk::console::Blink
785 proc ::tk::console::MatchPair {w c1 c2 {lim 1.0}} {
786 if {!$::tk::console::magicKeys} {
789 if {{} ne [set ix [$w search -back $c1 insert $lim]]} {
791 [string match {\\} [$w get $ix-1c]] &&
792 [set ix [$w search -back $c1 $ix-1c $lim]] ne {}
798 while {[set i0 [$w search $c2 $i0 $i1]] ne {}} {
800 if {[string match {\\} [$w get $i0-2c]]} {
809 while {$j && [set ix [$w search -back $c1 $ix $lim]] ne {}} {
810 if {[string match {\\} [$w get $ix-1c]]} {
816 if {[string match {} $ix]} {
817 set ix [$w index $lim]
820 set ix [$w index $lim]
822 if {$::tk::console::blinkRange} {
823 Blink $w $ix [$w index insert]
825 Blink $w $ix $ix+1c [$w index insert-1c] [$w index insert]
829 # ::tk::console::MatchQuote --
831 # Blinks between matching quotes.
832 # Blinks just the quote if it's unmatched, otherwise blinks quoted string
833 # The quote to match is assumed to be at the text index 'insert'.
836 # w - console text widget
838 # Calls: ::tk::console::Blink
840 proc ::tk::console::MatchQuote {w {lim 1.0}} {
841 if {!$::tk::console::magicKeys} {
846 while {[set i [$w search -back \" $i $lim]] ne {}} {
847 if {[string match {\\} [$w get $i-1c]]} {
856 if {$::tk::console::blinkRange} {
857 Blink $w $i0 [$w index insert]
859 Blink $w $i0 $i0+1c [$w index insert-1c] [$w index insert]
862 Blink $w [$w index insert-1c] [$w index insert]
866 # ::tk::console::Blink --
868 # Blinks between n index pairs for a specified duration.
871 # w - console text widget
872 # i1 - start index to blink region
873 # i2 - end index of blink region
874 # dur - duration in usecs to blink for
877 # blinks selected characters in $w
879 proc ::tk::console::Blink {w args} {
880 eval [list $w tag add blink] $args
881 after $::tk::console::blinkTime [list $w] tag remove blink $args
884 # ::tk::console::ConstrainBuffer --
886 # This limits the amount of data in the text widget
887 # Called by Prompt and ConsoleOutput
890 # w - console text widget
891 # size - # of lines to constrain to
894 # may delete data in console widget
896 proc ::tk::console::ConstrainBuffer {w size} {
897 if {[$w index end] > $size} {
898 $w delete 1.0 [expr {int([$w index end])-$size}].0
902 # ::tk::console::Expand --
905 # ARGS: w - text widget in which to expand str
906 # type - type of expansion (path / proc / variable)
908 # Calls: ::tk::console::Expand(Pathname|Procname|Variable)
910 # Outputs: The string to match is expanded to the longest possible match.
911 # If ::tk::console::showMatches is non-zero and the longest match
912 # equaled the string to expand, then all possible matches are
913 # output to stdout. Triggers bell if no matches are found.
915 # Returns: number of matches found
917 proc ::tk::console::Expand {w {type ""}} {
918 set exp "\[^\\\\\]\[\[ \t\n\r\\\{\"\\\\\$\]"
919 set tmp [$w search -backwards -regexp $exp insert-1c promptEnd-1c]
925 if {[$w compare $tmp >= insert]} {
928 set str [$w get $tmp insert]
931 set res [ExpandPathname $str]
934 set res [ExpandProcname $str]
937 set res [ExpandVariable $str]
941 foreach t {Pathname Procname Variable} {
942 if {![catch {Expand$t $str} res] && ($res ne "")} {
948 set len [llength $res]
950 set repl [lindex $res 0]
951 $w delete $tmp insert
952 $w insert $tmp $repl {input stdin}
953 if {($len > 1) && ($::tk::console::showMatches) && ($repl eq $str)} {
954 puts stdout [lsort [lreplace $res 0 0]]
962 # ::tk::console::ExpandPathname --
964 # Expand a file pathname based on $str
965 # This is based on UNIX file name conventions
968 # str - partial file pathname to expand
970 # Calls: ::tk::console::ExpandBestMatch
972 # Returns: list containing longest unique match followed by all the
973 # possible further matches
975 proc ::tk::console::ExpandPathname str {
976 set pwd [EvalAttached pwd]
977 if {[catch {EvalAttached [list cd [file dirname $str]]} err opt]} {
978 return -options $opt $err
980 set dir [file tail $str]
981 ## Check to see if it was known to be a directory and keep the trailing
982 ## slash if so (file tail cuts it off)
983 if {[string match */ $str]} {
986 if {[catch {lsort [EvalAttached [list glob $dir*]]} m]} {
989 if {[llength $m] > 1} {
991 if {[string match windows $tcl_platform(platform)]} {
992 ## Windows is screwy because it's case insensitive
993 set tmp [ExpandBestMatch [string tolower $m] \
994 [string tolower $dir]]
995 ## Don't change case if we haven't changed the word
996 if {[string length $dir]==[string length $tmp]} {
1000 set tmp [ExpandBestMatch $m $dir]
1002 if {[string match ?*/* $str]} {
1003 set tmp [file dirname $str]/$tmp
1004 } elseif {[string match /* $str]} {
1007 regsub -all { } $tmp {\\ } tmp
1008 set match [linsert $m 0 $tmp]
1010 ## This may look goofy, but it handles spaces in path names
1011 eval append match $m
1012 if {[file isdir $match]} {
1015 if {[string match ?*/* $str]} {
1016 set match [file dirname $str]/$match
1017 } elseif {[string match /* $str]} {
1020 regsub -all { } $match {\\ } match
1021 ## Why is this one needed and the ones below aren't!!
1022 set match [list $match]
1025 EvalAttached [list cd $pwd]
1029 # ::tk::console::ExpandProcname --
1031 # Expand a tcl proc name based on $str
1034 # str - partial proc name to expand
1036 # Calls: ::tk::console::ExpandBestMatch
1038 # Returns: list containing longest unique match followed by all the
1039 # possible further matches
1041 proc ::tk::console::ExpandProcname str {
1042 set match [EvalAttached [list info commands $str*]]
1043 if {[llength $match] == 0} {
1044 set ns [EvalAttached \
1045 "namespace children \[namespace current\] [list $str*]"]
1046 if {[llength $ns]==1} {
1047 set match [EvalAttached [list info commands ${ns}::*]]
1052 if {[llength $match] > 1} {
1053 regsub -all { } [ExpandBestMatch $match $str] {\\ } str
1054 set match [linsert $match 0 $str]
1056 regsub -all { } $match {\\ } match
1061 # ::tk::console::ExpandVariable --
1063 # Expand a tcl variable name based on $str
1066 # str - partial tcl var name to expand
1068 # Calls: ::tk::console::ExpandBestMatch
1070 # Returns: list containing longest unique match followed by all the
1071 # possible further matches
1073 proc ::tk::console::ExpandVariable str {
1074 if {[regexp {([^\(]*)\((.*)} $str -> ary str]} {
1075 ## Looks like they're trying to expand an array.
1076 set match [EvalAttached [list array names $ary $str*]]
1077 if {[llength $match] > 1} {
1078 set vars $ary\([ExpandBestMatch $match $str]
1079 foreach var $match {
1080 lappend vars $ary\($var\)
1083 } elseif {[llength $match] == 1} {
1084 set match $ary\($match\)
1086 ## Space transformation avoided for array names.
1088 set match [EvalAttached [list info vars $str*]]
1089 if {[llength $match] > 1} {
1090 regsub -all { } [ExpandBestMatch $match $str] {\\ } str
1091 set match [linsert $match 0 $str]
1093 regsub -all { } $match {\\ } match
1099 # ::tk::console::ExpandBestMatch --
1101 # Finds the best unique match in a list of names.
1102 # The extra $e in this argument allows us to limit the innermost loop a little
1103 # further. This improves speed as $l becomes large or $e becomes long.
1106 # l - list to find best unique match in
1107 # e - currently best known unique match
1109 # Returns: longest unique match in the list
1111 proc ::tk::console::ExpandBestMatch {l {e {}}} {
1112 set ec [lindex $l 0]
1113 if {[llength $l]>1} {
1114 set e [expr {[string length $e] - 1}]
1115 set ei [expr {[string length $ec] - 1}]
1117 while {$ei>=$e && [string first $ec $l]} {
1118 set ec [string range $ec 0 [incr ei -1]]
1125 # now initialize the console