console.tcl 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. # console.tcl --
  2. #
  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.
  6. #
  7. # Copyright (c) 1995-1997 Sun Microsystems, Inc.
  8. # Copyright (c) 1998-2000 Ajuba Solutions.
  9. # Copyright (c) 2007 Daniel A. Steffen <das@users.sourceforge.net>
  10. #
  11. # See the file "license.terms" for information on usage and redistribution
  12. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13. #
  14. # TODO: history - remember partially written command
  15. namespace eval ::tk::console {
  16. variable blinkTime 500 ; # msecs to blink braced range for
  17. variable blinkRange 1 ; # enable blinking of the entire braced range
  18. variable magicKeys 1 ; # enable brace matching and proc/var recognition
  19. variable maxLines 600 ; # maximum # of lines buffered in console
  20. variable showMatches 1 ; # show multiple expand matches
  21. variable inPlugin [info exists embed_args]
  22. variable defaultPrompt ; # default prompt if tcl_prompt1 isn't used
  23. if {$inPlugin} {
  24. set defaultPrompt {subst {[history nextid] % }}
  25. } else {
  26. set defaultPrompt {subst {([file tail [pwd]]) [history nextid] % }}
  27. }
  28. }
  29. # simple compat function for tkcon code added for this console
  30. interp alias {} EvalAttached {} consoleinterp eval
  31. # ::tk::ConsoleInit --
  32. # This procedure constructs and configures the console windows.
  33. #
  34. # Arguments:
  35. # None.
  36. proc ::tk::ConsoleInit {} {
  37. global tcl_platform
  38. if {![consoleinterp eval {set tcl_interactive}]} {
  39. wm withdraw .
  40. }
  41. if {[tk windowingsystem] eq "aqua"} {
  42. set mod "Cmd"
  43. } else {
  44. set mod "Ctrl"
  45. }
  46. if {[catch {menu .menubar} err]} {
  47. bgerror "INIT: $err"
  48. }
  49. AmpMenuArgs .menubar add cascade -label [mc &File] -menu .menubar.file
  50. AmpMenuArgs .menubar add cascade -label [mc &Edit] -menu .menubar.edit
  51. menu .menubar.file -tearoff 0
  52. AmpMenuArgs .menubar.file add command -label [mc "&Source..."] \
  53. -command {tk::ConsoleSource}
  54. AmpMenuArgs .menubar.file add command -label [mc "&Hide Console"] \
  55. -command {wm withdraw .}
  56. AmpMenuArgs .menubar.file add command -label [mc "&Clear Console"] \
  57. -command {.console delete 1.0 "promptEnd linestart"}
  58. if {[tk windowingsystem] ne "aqua"} {
  59. AmpMenuArgs .menubar.file add command -label [mc E&xit] -command {exit}
  60. }
  61. menu .menubar.edit -tearoff 0
  62. AmpMenuArgs .menubar.edit add command -label [mc Cu&t] -accel "$mod+X"\
  63. -command {event generate .console <<Cut>>}
  64. AmpMenuArgs .menubar.edit add command -label [mc &Copy] -accel "$mod+C"\
  65. -command {event generate .console <<Copy>>}
  66. AmpMenuArgs .menubar.edit add command -label [mc P&aste] -accel "$mod+V"\
  67. -command {event generate .console <<Paste>>}
  68. if {$tcl_platform(platform) ne "windows"} {
  69. AmpMenuArgs .menubar.edit add command -label [mc Cl&ear] \
  70. -command {event generate .console <<Clear>>}
  71. } else {
  72. AmpMenuArgs .menubar.edit add command -label [mc &Delete] \
  73. -command {event generate .console <<Clear>>} -accel "Del"
  74. AmpMenuArgs .menubar add cascade -label [mc &Help] -menu .menubar.help
  75. menu .menubar.help -tearoff 0
  76. AmpMenuArgs .menubar.help add command -label [mc &About...] \
  77. -command tk::ConsoleAbout
  78. }
  79. AmpMenuArgs .menubar.edit add separator
  80. AmpMenuArgs .menubar.edit add command -label [mc "&Increase Font Size"] \
  81. -accel "$mod++" -command {event generate .console <<Console_FontSizeIncr>>}
  82. AmpMenuArgs .menubar.edit add command -label [mc "&Decrease Font Size"] \
  83. -accel "$mod+-" -command {event generate .console <<Console_FontSizeDecr>>}
  84. . configure -menu .menubar
  85. # See if we can find a better font than the TkFixedFont
  86. catch {font create TkConsoleFont {*}[font configure TkFixedFont]}
  87. set families [font families]
  88. switch -exact -- [tk windowingsystem] {
  89. aqua { set preferred {Monaco 10} }
  90. win32 { set preferred {ProFontWindows 8 Consolas 8} }
  91. default { set preferred {} }
  92. }
  93. foreach {family size} $preferred {
  94. if {[lsearch -exact $families $family] != -1} {
  95. font configure TkConsoleFont -family $family -size $size
  96. break
  97. }
  98. }
  99. # Provide the right border for the text widget (platform dependent).
  100. ::ttk::style layout ConsoleFrame {
  101. Entry.field -sticky news -border 1 -children {
  102. ConsoleFrame.padding -sticky news
  103. }
  104. }
  105. ::ttk::frame .consoleframe -style ConsoleFrame
  106. set con [text .console -yscrollcommand [list .sb set] -setgrid true \
  107. -borderwidth 0 -highlightthickness 0 -font TkConsoleFont]
  108. if {[tk windowingsystem] eq "aqua"} {
  109. scrollbar .sb -command [list $con yview]
  110. } else {
  111. ::ttk::scrollbar .sb -command [list $con yview]
  112. }
  113. pack .sb -in .consoleframe -fill both -side right -padx 1 -pady 1
  114. pack $con -in .consoleframe -fill both -expand 1 -side left -padx 1 -pady 1
  115. pack .consoleframe -fill both -expand 1 -side left
  116. ConsoleBind $con
  117. $con tag configure stderr -foreground red
  118. $con tag configure stdin -foreground blue
  119. $con tag configure prompt -foreground \#8F4433
  120. $con tag configure proc -foreground \#008800
  121. $con tag configure var -background \#FFC0D0
  122. $con tag raise sel
  123. $con tag configure blink -background \#FFFF00
  124. $con tag configure find -background \#FFFF00
  125. focus $con
  126. # Avoid listing this console in [winfo interps]
  127. if {[info command ::send] eq "::send"} {rename ::send {}}
  128. wm protocol . WM_DELETE_WINDOW { wm withdraw . }
  129. wm title . [mc "Console"]
  130. flush stdout
  131. $con mark set output [$con index "end - 1 char"]
  132. tk::TextSetCursor $con end
  133. $con mark set promptEnd insert
  134. $con mark gravity promptEnd left
  135. # A variant of ConsolePrompt to avoid a 'puts' call
  136. set w $con
  137. set temp [$w index "end - 1 char"]
  138. $w mark set output end
  139. if {![consoleinterp eval "info exists tcl_prompt1"]} {
  140. set string [EvalAttached $::tk::console::defaultPrompt]
  141. $w insert output $string stdout
  142. }
  143. $w mark set output $temp
  144. ::tk::TextSetCursor $w end
  145. $w mark set promptEnd insert
  146. $w mark gravity promptEnd left
  147. if {$tcl_platform(platform) eq "windows"} {
  148. # Subtle work-around to erase the '% ' that tclMain.c prints out
  149. after idle [subst -nocommand {
  150. if {[$con get 1.0 output] eq "% "} { $con delete 1.0 output }
  151. }]
  152. }
  153. }
  154. # ::tk::ConsoleSource --
  155. #
  156. # Prompts the user for a file to source in the main interpreter.
  157. #
  158. # Arguments:
  159. # None.
  160. proc ::tk::ConsoleSource {} {
  161. set filename [tk_getOpenFile -defaultextension .tcl -parent . \
  162. -title [mc "Select a file to source"] \
  163. -filetypes [list \
  164. [list [mc "Tcl Scripts"] .tcl] \
  165. [list [mc "All Files"] *]]]
  166. if {$filename ne ""} {
  167. set cmd [list source $filename]
  168. if {[catch {consoleinterp eval $cmd} result]} {
  169. ConsoleOutput stderr "$result\n"
  170. }
  171. }
  172. }
  173. # ::tk::ConsoleInvoke --
  174. # Processes the command line input. If the command is complete it
  175. # is evaled in the main interpreter. Otherwise, the continuation
  176. # prompt is added and more input may be added.
  177. #
  178. # Arguments:
  179. # None.
  180. proc ::tk::ConsoleInvoke {args} {
  181. set ranges [.console tag ranges input]
  182. set cmd ""
  183. if {[llength $ranges]} {
  184. set pos 0
  185. while {[lindex $ranges $pos] ne ""} {
  186. set start [lindex $ranges $pos]
  187. set end [lindex $ranges [incr pos]]
  188. append cmd [.console get $start $end]
  189. incr pos
  190. }
  191. }
  192. if {$cmd eq ""} {
  193. ConsolePrompt
  194. } elseif {[info complete $cmd]} {
  195. .console mark set output end
  196. .console tag delete input
  197. set result [consoleinterp record $cmd]
  198. if {$result ne ""} {
  199. puts $result
  200. }
  201. ConsoleHistory reset
  202. ConsolePrompt
  203. } else {
  204. ConsolePrompt partial
  205. }
  206. .console yview -pickplace insert
  207. }
  208. # ::tk::ConsoleHistory --
  209. # This procedure implements command line history for the
  210. # console. In general is evals the history command in the
  211. # main interpreter to obtain the history. The variable
  212. # ::tk::HistNum is used to store the current location in the history.
  213. #
  214. # Arguments:
  215. # cmd - Which action to take: prev, next, reset.
  216. set ::tk::HistNum 1
  217. proc ::tk::ConsoleHistory {cmd} {
  218. variable HistNum
  219. switch $cmd {
  220. prev {
  221. incr HistNum -1
  222. if {$HistNum == 0} {
  223. set cmd {history event [expr {[history nextid] -1}]}
  224. } else {
  225. set cmd "history event $HistNum"
  226. }
  227. if {[catch {consoleinterp eval $cmd} cmd]} {
  228. incr HistNum
  229. return
  230. }
  231. .console delete promptEnd end
  232. .console insert promptEnd $cmd {input stdin}
  233. }
  234. next {
  235. incr HistNum
  236. if {$HistNum == 0} {
  237. set cmd {history event [expr {[history nextid] -1}]}
  238. } elseif {$HistNum > 0} {
  239. set cmd ""
  240. set HistNum 1
  241. } else {
  242. set cmd "history event $HistNum"
  243. }
  244. if {$cmd ne ""} {
  245. catch {consoleinterp eval $cmd} cmd
  246. }
  247. .console delete promptEnd end
  248. .console insert promptEnd $cmd {input stdin}
  249. }
  250. reset {
  251. set HistNum 1
  252. }
  253. }
  254. }
  255. # ::tk::ConsolePrompt --
  256. # This procedure draws the prompt. If tcl_prompt1 or tcl_prompt2
  257. # exists in the main interpreter it will be called to generate the
  258. # prompt. Otherwise, a hard coded default prompt is printed.
  259. #
  260. # Arguments:
  261. # partial - Flag to specify which prompt to print.
  262. proc ::tk::ConsolePrompt {{partial normal}} {
  263. set w .console
  264. if {$partial eq "normal"} {
  265. set temp [$w index "end - 1 char"]
  266. $w mark set output end
  267. if {[consoleinterp eval "info exists tcl_prompt1"]} {
  268. consoleinterp eval "eval \[set tcl_prompt1\]"
  269. } else {
  270. puts -nonewline [EvalAttached $::tk::console::defaultPrompt]
  271. }
  272. } else {
  273. set temp [$w index output]
  274. $w mark set output end
  275. if {[consoleinterp eval "info exists tcl_prompt2"]} {
  276. consoleinterp eval "eval \[set tcl_prompt2\]"
  277. } else {
  278. puts -nonewline "> "
  279. }
  280. }
  281. flush stdout
  282. $w mark set output $temp
  283. ::tk::TextSetCursor $w end
  284. $w mark set promptEnd insert
  285. $w mark gravity promptEnd left
  286. ::tk::console::ConstrainBuffer $w $::tk::console::maxLines
  287. $w see end
  288. }
  289. # Copy selected text from the console
  290. proc ::tk::console::Copy {w} {
  291. if {![catch {set data [$w get sel.first sel.last]}]} {
  292. clipboard clear -displayof $w
  293. clipboard append -displayof $w $data
  294. }
  295. }
  296. # Copies selected text. If the selection is within the current active edit
  297. # region then it will be cut, if not it is only copied.
  298. proc ::tk::console::Cut {w} {
  299. if {![catch {set data [$w get sel.first sel.last]}]} {
  300. clipboard clear -displayof $w
  301. clipboard append -displayof $w $data
  302. if {[$w compare sel.first >= output]} {
  303. $w delete sel.first sel.last
  304. }
  305. }
  306. }
  307. # Paste text from the clipboard
  308. proc ::tk::console::Paste {w} {
  309. catch {
  310. set clip [::tk::GetSelection $w CLIPBOARD]
  311. set list [split $clip \n\r]
  312. tk::ConsoleInsert $w [lindex $list 0]
  313. foreach x [lrange $list 1 end] {
  314. $w mark set insert {end - 1c}
  315. tk::ConsoleInsert $w "\n"
  316. tk::ConsoleInvoke
  317. tk::ConsoleInsert $w $x
  318. }
  319. }
  320. }
  321. # ::tk::ConsoleBind --
  322. # This procedure first ensures that the default bindings for the Text
  323. # class have been defined. Then certain bindings are overridden for
  324. # the class.
  325. #
  326. # Arguments:
  327. # None.
  328. proc ::tk::ConsoleBind {w} {
  329. bindtags $w [list $w Console PostConsole [winfo toplevel $w] all]
  330. ## Get all Text bindings into Console
  331. foreach ev [bind Text] {
  332. bind Console $ev [bind Text $ev]
  333. }
  334. ## We really didn't want the newline insertion...
  335. bind Console <Control-Key-o> {}
  336. ## ...or any Control-v binding (would block <<Paste>>)
  337. bind Console <Control-Key-v> {}
  338. # For the moment, transpose isn't enabled until the console
  339. # gets and overhaul of how it handles input -- hobbs
  340. bind Console <Control-Key-t> {}
  341. # Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
  342. # Otherwise, if a widget binding for one of these is defined, the
  343. # <Keypress> class binding will also fire and insert the character
  344. # which is wrong.
  345. bind Console <Alt-KeyPress> {# nothing }
  346. bind Console <Meta-KeyPress> {# nothing}
  347. bind Console <Control-KeyPress> {# nothing}
  348. foreach {ev key} {
  349. <<Console_Prev>> <Key-Up>
  350. <<Console_Next>> <Key-Down>
  351. <<Console_NextImmediate>> <Control-Key-n>
  352. <<Console_PrevImmediate>> <Control-Key-p>
  353. <<Console_PrevSearch>> <Control-Key-r>
  354. <<Console_NextSearch>> <Control-Key-s>
  355. <<Console_Expand>> <Key-Tab>
  356. <<Console_Expand>> <Key-Escape>
  357. <<Console_ExpandFile>> <Control-Shift-Key-F>
  358. <<Console_ExpandProc>> <Control-Shift-Key-P>
  359. <<Console_ExpandVar>> <Control-Shift-Key-V>
  360. <<Console_Tab>> <Control-Key-i>
  361. <<Console_Tab>> <Meta-Key-i>
  362. <<Console_Eval>> <Key-Return>
  363. <<Console_Eval>> <Key-KP_Enter>
  364. <<Console_Clear>> <Control-Key-l>
  365. <<Console_KillLine>> <Control-Key-k>
  366. <<Console_Transpose>> <Control-Key-t>
  367. <<Console_ClearLine>> <Control-Key-u>
  368. <<Console_SaveCommand>> <Control-Key-z>
  369. <<Console_FontSizeIncr>> <Control-Key-plus>
  370. <<Console_FontSizeDecr>> <Control-Key-minus>
  371. } {
  372. event add $ev $key
  373. bind Console $key {}
  374. }
  375. if {[tk windowingsystem] eq "aqua"} {
  376. foreach {ev key} {
  377. <<Console_FontSizeIncr>> <Command-Key-plus>
  378. <<Console_FontSizeDecr>> <Command-Key-minus>
  379. } {
  380. event add $ev $key
  381. bind Console $key {}
  382. }
  383. }
  384. bind Console <<Console_Expand>> {
  385. if {[%W compare insert > promptEnd]} {
  386. ::tk::console::Expand %W
  387. }
  388. }
  389. bind Console <<Console_ExpandFile>> {
  390. if {[%W compare insert > promptEnd]} {
  391. ::tk::console::Expand %W path
  392. }
  393. }
  394. bind Console <<Console_ExpandProc>> {
  395. if {[%W compare insert > promptEnd]} {
  396. ::tk::console::Expand %W proc
  397. }
  398. }
  399. bind Console <<Console_ExpandVar>> {
  400. if {[%W compare insert > promptEnd]} {
  401. ::tk::console::Expand %W var
  402. }
  403. }
  404. bind Console <<Console_Eval>> {
  405. %W mark set insert {end - 1c}
  406. tk::ConsoleInsert %W "\n"
  407. tk::ConsoleInvoke
  408. break
  409. }
  410. bind Console <Delete> {
  411. if {{} ne [%W tag nextrange sel 1.0 end] \
  412. && [%W compare sel.first >= promptEnd]} {
  413. %W delete sel.first sel.last
  414. } elseif {[%W compare insert >= promptEnd]} {
  415. %W delete insert
  416. %W see insert
  417. }
  418. }
  419. bind Console <BackSpace> {
  420. if {{} ne [%W tag nextrange sel 1.0 end] \
  421. && [%W compare sel.first >= promptEnd]} {
  422. %W delete sel.first sel.last
  423. } elseif {[%W compare insert != 1.0] && \
  424. [%W compare insert > promptEnd]} {
  425. %W delete insert-1c
  426. %W see insert
  427. }
  428. }
  429. bind Console <Control-h> [bind Console <BackSpace>]
  430. bind Console <Home> {
  431. if {[%W compare insert < promptEnd]} {
  432. tk::TextSetCursor %W {insert linestart}
  433. } else {
  434. tk::TextSetCursor %W promptEnd
  435. }
  436. }
  437. bind Console <Control-a> [bind Console <Home>]
  438. bind Console <End> {
  439. tk::TextSetCursor %W {insert lineend}
  440. }
  441. bind Console <Control-e> [bind Console <End>]
  442. bind Console <Control-d> {
  443. if {[%W compare insert < promptEnd]} {
  444. break
  445. }
  446. %W delete insert
  447. }
  448. bind Console <<Console_KillLine>> {
  449. if {[%W compare insert < promptEnd]} {
  450. break
  451. }
  452. if {[%W compare insert == {insert lineend}]} {
  453. %W delete insert
  454. } else {
  455. %W delete insert {insert lineend}
  456. }
  457. }
  458. bind Console <<Console_Clear>> {
  459. ## Clear console display
  460. %W delete 1.0 "promptEnd linestart"
  461. }
  462. bind Console <<Console_ClearLine>> {
  463. ## Clear command line (Unix shell staple)
  464. %W delete promptEnd end
  465. }
  466. bind Console <Meta-d> {
  467. if {[%W compare insert >= promptEnd]} {
  468. %W delete insert {insert wordend}
  469. }
  470. }
  471. bind Console <Meta-BackSpace> {
  472. if {[%W compare {insert -1c wordstart} >= promptEnd]} {
  473. %W delete {insert -1c wordstart} insert
  474. }
  475. }
  476. bind Console <Meta-d> {
  477. if {[%W compare insert >= promptEnd]} {
  478. %W delete insert {insert wordend}
  479. }
  480. }
  481. bind Console <Meta-BackSpace> {
  482. if {[%W compare {insert -1c wordstart} >= promptEnd]} {
  483. %W delete {insert -1c wordstart} insert
  484. }
  485. }
  486. bind Console <Meta-Delete> {
  487. if {[%W compare insert >= promptEnd]} {
  488. %W delete insert {insert wordend}
  489. }
  490. }
  491. bind Console <<Console_Prev>> {
  492. tk::ConsoleHistory prev
  493. }
  494. bind Console <<Console_Next>> {
  495. tk::ConsoleHistory next
  496. }
  497. bind Console <Insert> {
  498. catch {tk::ConsoleInsert %W [::tk::GetSelection %W PRIMARY]}
  499. }
  500. bind Console <KeyPress> {
  501. tk::ConsoleInsert %W %A
  502. }
  503. bind Console <F9> {
  504. eval destroy [winfo child .]
  505. source [file join $tk_library console.tcl]
  506. }
  507. if {[tk windowingsystem] eq "aqua"} {
  508. bind Console <Command-q> {
  509. exit
  510. }
  511. }
  512. bind Console <<Cut>> { ::tk::console::Cut %W }
  513. bind Console <<Copy>> { ::tk::console::Copy %W }
  514. bind Console <<Paste>> { ::tk::console::Paste %W }
  515. bind Console <<Console_FontSizeIncr>> {
  516. set size [font configure TkConsoleFont -size]
  517. font configure TkConsoleFont -size [incr size]
  518. }
  519. bind Console <<Console_FontSizeDecr>> {
  520. set size [font configure TkConsoleFont -size]
  521. font configure TkConsoleFont -size [incr size -1]
  522. }
  523. ##
  524. ## Bindings for doing special things based on certain keys
  525. ##
  526. bind PostConsole <Key-parenright> {
  527. if {"\\" ne [%W get insert-2c]} {
  528. ::tk::console::MatchPair %W \( \) promptEnd
  529. }
  530. }
  531. bind PostConsole <Key-bracketright> {
  532. if {"\\" ne [%W get insert-2c]} {
  533. ::tk::console::MatchPair %W \[ \] promptEnd
  534. }
  535. }
  536. bind PostConsole <Key-braceright> {
  537. if {"\\" ne [%W get insert-2c]} {
  538. ::tk::console::MatchPair %W \{ \} promptEnd
  539. }
  540. }
  541. bind PostConsole <Key-quotedbl> {
  542. if {"\\" ne [%W get insert-2c]} {
  543. ::tk::console::MatchQuote %W promptEnd
  544. }
  545. }
  546. bind PostConsole <KeyPress> {
  547. if {"%A" ne ""} {
  548. ::tk::console::TagProc %W
  549. }
  550. }
  551. }
  552. # ::tk::ConsoleInsert --
  553. # Insert a string into a text at the point of the insertion cursor.
  554. # If there is a selection in the text, and it covers the point of the
  555. # insertion cursor, then delete the selection before inserting. Insertion
  556. # is restricted to the prompt area.
  557. #
  558. # Arguments:
  559. # w - The text window in which to insert the string
  560. # s - The string to insert (usually just a single character)
  561. proc ::tk::ConsoleInsert {w s} {
  562. if {$s eq ""} {
  563. return
  564. }
  565. catch {
  566. if {[$w compare sel.first <= insert] \
  567. && [$w compare sel.last >= insert]} {
  568. $w tag remove sel sel.first promptEnd
  569. $w delete sel.first sel.last
  570. }
  571. }
  572. if {[$w compare insert < promptEnd]} {
  573. $w mark set insert end
  574. }
  575. $w insert insert $s {input stdin}
  576. $w see insert
  577. }
  578. # ::tk::ConsoleOutput --
  579. #
  580. # This routine is called directly by ConsolePutsCmd to cause a string
  581. # to be displayed in the console.
  582. #
  583. # Arguments:
  584. # dest - The output tag to be used: either "stderr" or "stdout".
  585. # string - The string to be displayed.
  586. proc ::tk::ConsoleOutput {dest string} {
  587. set w .console
  588. $w insert output $string $dest
  589. ::tk::console::ConstrainBuffer $w $::tk::console::maxLines
  590. $w see insert
  591. }
  592. # ::tk::ConsoleExit --
  593. #
  594. # This routine is called by ConsoleEventProc when the main window of
  595. # the application is destroyed. Don't call exit - that probably already
  596. # happened. Just delete our window.
  597. #
  598. # Arguments:
  599. # None.
  600. proc ::tk::ConsoleExit {} {
  601. destroy .
  602. }
  603. # ::tk::ConsoleAbout --
  604. #
  605. # This routine displays an About box to show Tcl/Tk version info.
  606. #
  607. # Arguments:
  608. # None.
  609. proc ::tk::ConsoleAbout {} {
  610. tk_messageBox -type ok -message "[mc {Tcl for Windows}]
  611. Tcl $::tcl_patchLevel
  612. Tk $::tk_patchLevel"
  613. }
  614. # ::tk::console::TagProc --
  615. #
  616. # Tags a procedure in the console if it's recognized
  617. # This procedure is not perfect. However, making it perfect wastes
  618. # too much CPU time...
  619. #
  620. # Arguments:
  621. # w - console text widget
  622. proc ::tk::console::TagProc w {
  623. if {!$::tk::console::magicKeys} {
  624. return
  625. }
  626. set exp "\[^\\\\\]\[\[ \t\n\r\;{}\"\$\]"
  627. set i [$w search -backwards -regexp $exp insert-1c promptEnd-1c]
  628. if {$i eq ""} {
  629. set i promptEnd
  630. } else {
  631. append i +2c
  632. }
  633. regsub -all "\[\[\\\\\\?\\*\]" [$w get $i "insert-1c wordend"] {\\\0} c
  634. if {[llength [EvalAttached [list info commands $c]]]} {
  635. $w tag add proc $i "insert-1c wordend"
  636. } else {
  637. $w tag remove proc $i "insert-1c wordend"
  638. }
  639. if {[llength [EvalAttached [list info vars $c]]]} {
  640. $w tag add var $i "insert-1c wordend"
  641. } else {
  642. $w tag remove var $i "insert-1c wordend"
  643. }
  644. }
  645. # ::tk::console::MatchPair --
  646. #
  647. # Blinks a matching pair of characters
  648. # c2 is assumed to be at the text index 'insert'.
  649. # This proc is really loopy and took me an hour to figure out given
  650. # all possible combinations with escaping except for escaped \'s.
  651. # It doesn't take into account possible commenting... Oh well. If
  652. # anyone has something better, I'd like to see/use it. This is really
  653. # only efficient for small contexts.
  654. #
  655. # Arguments:
  656. # w - console text widget
  657. # c1 - first char of pair
  658. # c2 - second char of pair
  659. #
  660. # Calls: ::tk::console::Blink
  661. proc ::tk::console::MatchPair {w c1 c2 {lim 1.0}} {
  662. if {!$::tk::console::magicKeys} {
  663. return
  664. }
  665. if {{} ne [set ix [$w search -back $c1 insert $lim]]} {
  666. while {
  667. [string match {\\} [$w get $ix-1c]] &&
  668. [set ix [$w search -back $c1 $ix-1c $lim]] ne {}
  669. } {}
  670. set i1 insert-1c
  671. while {$ix ne {}} {
  672. set i0 $ix
  673. set j 0
  674. while {[set i0 [$w search $c2 $i0 $i1]] ne {}} {
  675. append i0 +1c
  676. if {[string match {\\} [$w get $i0-2c]]} {
  677. continue
  678. }
  679. incr j
  680. }
  681. if {!$j} {
  682. break
  683. }
  684. set i1 $ix
  685. while {$j && [set ix [$w search -back $c1 $ix $lim]] ne {}} {
  686. if {[string match {\\} [$w get $ix-1c]]} {
  687. continue
  688. }
  689. incr j -1
  690. }
  691. }
  692. if {[string match {} $ix]} {
  693. set ix [$w index $lim]
  694. }
  695. } else {
  696. set ix [$w index $lim]
  697. }
  698. if {$::tk::console::blinkRange} {
  699. Blink $w $ix [$w index insert]
  700. } else {
  701. Blink $w $ix $ix+1c [$w index insert-1c] [$w index insert]
  702. }
  703. }
  704. # ::tk::console::MatchQuote --
  705. #
  706. # Blinks between matching quotes.
  707. # Blinks just the quote if it's unmatched, otherwise blinks quoted string
  708. # The quote to match is assumed to be at the text index 'insert'.
  709. #
  710. # Arguments:
  711. # w - console text widget
  712. #
  713. # Calls: ::tk::console::Blink
  714. proc ::tk::console::MatchQuote {w {lim 1.0}} {
  715. if {!$::tk::console::magicKeys} {
  716. return
  717. }
  718. set i insert-1c
  719. set j 0
  720. while {[set i [$w search -back \" $i $lim]] ne {}} {
  721. if {[string match {\\} [$w get $i-1c]]} {
  722. continue
  723. }
  724. if {!$j} {
  725. set i0 $i
  726. }
  727. incr j
  728. }
  729. if {$j&1} {
  730. if {$::tk::console::blinkRange} {
  731. Blink $w $i0 [$w index insert]
  732. } else {
  733. Blink $w $i0 $i0+1c [$w index insert-1c] [$w index insert]
  734. }
  735. } else {
  736. Blink $w [$w index insert-1c] [$w index insert]
  737. }
  738. }
  739. # ::tk::console::Blink --
  740. #
  741. # Blinks between n index pairs for a specified duration.
  742. #
  743. # Arguments:
  744. # w - console text widget
  745. # i1 - start index to blink region
  746. # i2 - end index of blink region
  747. # dur - duration in usecs to blink for
  748. #
  749. # Outputs:
  750. # blinks selected characters in $w
  751. proc ::tk::console::Blink {w args} {
  752. eval [list $w tag add blink] $args
  753. after $::tk::console::blinkTime [list $w] tag remove blink $args
  754. }
  755. # ::tk::console::ConstrainBuffer --
  756. #
  757. # This limits the amount of data in the text widget
  758. # Called by Prompt and ConsoleOutput
  759. #
  760. # Arguments:
  761. # w - console text widget
  762. # size - # of lines to constrain to
  763. #
  764. # Outputs:
  765. # may delete data in console widget
  766. proc ::tk::console::ConstrainBuffer {w size} {
  767. if {[$w index end] > $size} {
  768. $w delete 1.0 [expr {int([$w index end])-$size}].0
  769. }
  770. }
  771. # ::tk::console::Expand --
  772. #
  773. # Arguments:
  774. # ARGS: w - text widget in which to expand str
  775. # type - type of expansion (path / proc / variable)
  776. #
  777. # Calls: ::tk::console::Expand(Pathname|Procname|Variable)
  778. #
  779. # Outputs: The string to match is expanded to the longest possible match.
  780. # If ::tk::console::showMatches is non-zero and the longest match
  781. # equaled the string to expand, then all possible matches are
  782. # output to stdout. Triggers bell if no matches are found.
  783. #
  784. # Returns: number of matches found
  785. proc ::tk::console::Expand {w {type ""}} {
  786. set exp "\[^\\\\\]\[\[ \t\n\r\\\{\"\\\\\$\]"
  787. set tmp [$w search -backwards -regexp $exp insert-1c promptEnd-1c]
  788. if {$tmp eq ""} {
  789. set tmp promptEnd
  790. } else {
  791. append tmp +2c
  792. }
  793. if {[$w compare $tmp >= insert]} {
  794. return
  795. }
  796. set str [$w get $tmp insert]
  797. switch -glob $type {
  798. path* {
  799. set res [ExpandPathname $str]
  800. }
  801. proc* {
  802. set res [ExpandProcname $str]
  803. }
  804. var* {
  805. set res [ExpandVariable $str]
  806. }
  807. default {
  808. set res {}
  809. foreach t {Pathname Procname Variable} {
  810. if {![catch {Expand$t $str} res] && ($res ne "")} {
  811. break
  812. }
  813. }
  814. }
  815. }
  816. set len [llength $res]
  817. if {$len} {
  818. set repl [lindex $res 0]
  819. $w delete $tmp insert
  820. $w insert $tmp $repl {input stdin}
  821. if {($len > 1) && ($::tk::console::showMatches) && ($repl eq $str)} {
  822. puts stdout [lsort [lreplace $res 0 0]]
  823. }
  824. } else {
  825. bell
  826. }
  827. return [incr len -1]
  828. }
  829. # ::tk::console::ExpandPathname --
  830. #
  831. # Expand a file pathname based on $str
  832. # This is based on UNIX file name conventions
  833. #
  834. # Arguments:
  835. # str - partial file pathname to expand
  836. #
  837. # Calls: ::tk::console::ExpandBestMatch
  838. #
  839. # Returns: list containing longest unique match followed by all the
  840. # possible further matches
  841. proc ::tk::console::ExpandPathname str {
  842. set pwd [EvalAttached pwd]
  843. if {[catch {EvalAttached [list cd [file dirname $str]]} err]} {
  844. return -code error $err
  845. }
  846. set dir [file tail $str]
  847. ## Check to see if it was known to be a directory and keep the trailing
  848. ## slash if so (file tail cuts it off)
  849. if {[string match */ $str]} {
  850. append dir /
  851. }
  852. if {[catch {lsort [EvalAttached [list glob $dir*]]} m]} {
  853. set match {}
  854. } else {
  855. if {[llength $m] > 1} {
  856. global tcl_platform
  857. if {[string match windows $tcl_platform(platform)]} {
  858. ## Windows is screwy because it's case insensitive
  859. set tmp [ExpandBestMatch [string tolower $m] \
  860. [string tolower $dir]]
  861. ## Don't change case if we haven't changed the word
  862. if {[string length $dir]==[string length $tmp]} {
  863. set tmp $dir
  864. }
  865. } else {
  866. set tmp [ExpandBestMatch $m $dir]
  867. }
  868. if {[string match ?*/* $str]} {
  869. set tmp [file dirname $str]/$tmp
  870. } elseif {[string match /* $str]} {
  871. set tmp /$tmp
  872. }
  873. regsub -all { } $tmp {\\ } tmp
  874. set match [linsert $m 0 $tmp]
  875. } else {
  876. ## This may look goofy, but it handles spaces in path names
  877. eval append match $m
  878. if {[file isdir $match]} {
  879. append match /
  880. }
  881. if {[string match ?*/* $str]} {
  882. set match [file dirname $str]/$match
  883. } elseif {[string match /* $str]} {
  884. set match /$match
  885. }
  886. regsub -all { } $match {\\ } match
  887. ## Why is this one needed and the ones below aren't!!
  888. set match [list $match]
  889. }
  890. }
  891. EvalAttached [list cd $pwd]
  892. return $match
  893. }
  894. # ::tk::console::ExpandProcname --
  895. #
  896. # Expand a tcl proc name based on $str
  897. #
  898. # Arguments:
  899. # str - partial proc name to expand
  900. #
  901. # Calls: ::tk::console::ExpandBestMatch
  902. #
  903. # Returns: list containing longest unique match followed by all the
  904. # possible further matches
  905. proc ::tk::console::ExpandProcname str {
  906. set match [EvalAttached [list info commands $str*]]
  907. if {[llength $match] == 0} {
  908. set ns [EvalAttached \
  909. "namespace children \[namespace current\] [list $str*]"]
  910. if {[llength $ns]==1} {
  911. set match [EvalAttached [list info commands ${ns}::*]]
  912. } else {
  913. set match $ns
  914. }
  915. }
  916. if {[llength $match] > 1} {
  917. regsub -all { } [ExpandBestMatch $match $str] {\\ } str
  918. set match [linsert $match 0 $str]
  919. } else {
  920. regsub -all { } $match {\\ } match
  921. }
  922. return $match
  923. }
  924. # ::tk::console::ExpandVariable --
  925. #
  926. # Expand a tcl variable name based on $str
  927. #
  928. # Arguments:
  929. # str - partial tcl var name to expand
  930. #
  931. # Calls: ::tk::console::ExpandBestMatch
  932. #
  933. # Returns: list containing longest unique match followed by all the
  934. # possible further matches
  935. proc ::tk::console::ExpandVariable str {
  936. if {[regexp {([^\(]*)\((.*)} $str -> ary str]} {
  937. ## Looks like they're trying to expand an array.
  938. set match [EvalAttached [list array names $ary $str*]]
  939. if {[llength $match] > 1} {
  940. set vars $ary\([ExpandBestMatch $match $str]
  941. foreach var $match {
  942. lappend vars $ary\($var\)
  943. }
  944. return $vars
  945. } elseif {[llength $match] == 1} {
  946. set match $ary\($match\)
  947. }
  948. ## Space transformation avoided for array names.
  949. } else {
  950. set match [EvalAttached [list info vars $str*]]
  951. if {[llength $match] > 1} {
  952. regsub -all { } [ExpandBestMatch $match $str] {\\ } str
  953. set match [linsert $match 0 $str]
  954. } else {
  955. regsub -all { } $match {\\ } match
  956. }
  957. }
  958. return $match
  959. }
  960. # ::tk::console::ExpandBestMatch --
  961. #
  962. # Finds the best unique match in a list of names.
  963. # The extra $e in this argument allows us to limit the innermost loop a little
  964. # further. This improves speed as $l becomes large or $e becomes long.
  965. #
  966. # Arguments:
  967. # l - list to find best unique match in
  968. # e - currently best known unique match
  969. #
  970. # Returns: longest unique match in the list
  971. proc ::tk::console::ExpandBestMatch {l {e {}}} {
  972. set ec [lindex $l 0]
  973. if {[llength $l]>1} {
  974. set e [expr {[string length $e] - 1}]
  975. set ei [expr {[string length $ec] - 1}]
  976. foreach l $l {
  977. while {$ei>=$e && [string first $ec $l]} {
  978. set ec [string range $ec 0 [incr ei -1]]
  979. }
  980. }
  981. }
  982. return $ec
  983. }
  984. # now initialize the console
  985. ::tk::ConsoleInit