history.tcl 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. # history.tcl --
  2. #
  3. # Implementation of the history command.
  4. #
  5. # Copyright (c) 1997 Sun Microsystems, Inc.
  6. #
  7. # See the file "license.terms" for information on usage and redistribution of
  8. # this file, and for a DISCLAIMER OF ALL WARRANTIES.
  9. #
  10. # The tcl::history array holds the history list and some additional
  11. # bookkeeping variables.
  12. #
  13. # nextid the index used for the next history list item.
  14. # keep the max size of the history list
  15. # oldest the index of the oldest item in the history.
  16. namespace eval ::tcl {
  17. variable history
  18. if {![info exists history]} {
  19. array set history {
  20. nextid 0
  21. keep 20
  22. oldest -20
  23. }
  24. }
  25. namespace ensemble create -command ::tcl::history -map {
  26. add ::tcl::HistAdd
  27. change ::tcl::HistChange
  28. clear ::tcl::HistClear
  29. event ::tcl::HistEvent
  30. info ::tcl::HistInfo
  31. keep ::tcl::HistKeep
  32. nextid ::tcl::HistNextID
  33. redo ::tcl::HistRedo
  34. }
  35. }
  36. # history --
  37. #
  38. # This is the main history command. See the man page for its interface.
  39. # This does some argument checking and calls the helper ensemble in the
  40. # tcl namespace.
  41. proc ::history {args} {
  42. # If no command given, we're doing 'history info'. Can't be done with an
  43. # ensemble unknown handler, as those don't fire when no subcommand is
  44. # given at all.
  45. if {![llength $args]} {
  46. set args info
  47. }
  48. # Tricky stuff needed to make stack and errors come out right!
  49. tailcall apply {arglist {tailcall history {*}$arglist} ::tcl} $args
  50. }
  51. # (unnamed) --
  52. #
  53. # Callback when [::history] is destroyed. Destroys the implementation.
  54. #
  55. # Parameters:
  56. # oldName what the command was called.
  57. # newName what the command is now called (an empty string).
  58. # op the operation (= delete).
  59. #
  60. # Results:
  61. # none
  62. #
  63. # Side Effects:
  64. # The implementation of the [::history] command ceases to exist.
  65. trace add command ::history delete [list apply {{oldName newName op} {
  66. variable history
  67. unset -nocomplain history
  68. foreach c [info procs ::tcl::Hist*] {
  69. rename $c {}
  70. }
  71. rename ::tcl::history {}
  72. } ::tcl}]
  73. # tcl::HistAdd --
  74. #
  75. # Add an item to the history, and optionally eval it at the global scope
  76. #
  77. # Parameters:
  78. # event the command to add
  79. # exec (optional) a substring of "exec" causes the command to
  80. # be evaled.
  81. # Results:
  82. # If executing, then the results of the command are returned
  83. #
  84. # Side Effects:
  85. # Adds to the history list
  86. proc ::tcl::HistAdd {event {exec {}}} {
  87. variable history
  88. if {
  89. [prefix longest {exec {}} $exec] eq ""
  90. && [llength [info level 0]] == 3
  91. } then {
  92. return -code error "bad argument \"$exec\": should be \"exec\""
  93. }
  94. # Do not add empty commands to the history
  95. if {[string trim $event] eq ""} {
  96. return ""
  97. }
  98. # Maintain the history
  99. set history([incr history(nextid)]) $event
  100. unset -nocomplain history([incr history(oldest)])
  101. # Only execute if 'exec' (or non-empty prefix of it) given
  102. if {$exec eq ""} {
  103. return ""
  104. }
  105. tailcall eval $event
  106. }
  107. # tcl::HistKeep --
  108. #
  109. # Set or query the limit on the length of the history list
  110. #
  111. # Parameters:
  112. # limit (optional) the length of the history list
  113. #
  114. # Results:
  115. # If no limit is specified, the current limit is returned
  116. #
  117. # Side Effects:
  118. # Updates history(keep) if a limit is specified
  119. proc ::tcl::HistKeep {{count {}}} {
  120. variable history
  121. if {[llength [info level 0]] == 1} {
  122. return $history(keep)
  123. }
  124. if {![string is integer -strict $count] || ($count < 0)} {
  125. return -code error "illegal keep count \"$count\""
  126. }
  127. set oldold $history(oldest)
  128. set history(oldest) [expr {$history(nextid) - $count}]
  129. for {} {$oldold <= $history(oldest)} {incr oldold} {
  130. unset -nocomplain history($oldold)
  131. }
  132. set history(keep) $count
  133. }
  134. # tcl::HistClear --
  135. #
  136. # Erase the history list
  137. #
  138. # Parameters:
  139. # none
  140. #
  141. # Results:
  142. # none
  143. #
  144. # Side Effects:
  145. # Resets the history array, except for the keep limit
  146. proc ::tcl::HistClear {} {
  147. variable history
  148. set keep $history(keep)
  149. unset history
  150. array set history [list \
  151. nextid 0 \
  152. keep $keep \
  153. oldest -$keep \
  154. ]
  155. }
  156. # tcl::HistInfo --
  157. #
  158. # Return a pretty-printed version of the history list
  159. #
  160. # Parameters:
  161. # num (optional) the length of the history list to return
  162. #
  163. # Results:
  164. # A formatted history list
  165. proc ::tcl::HistInfo {{count {}}} {
  166. variable history
  167. if {[llength [info level 0]] == 1} {
  168. set count [expr {$history(keep) + 1}]
  169. } elseif {![string is integer -strict $count]} {
  170. return -code error "bad integer \"$count\""
  171. }
  172. set result {}
  173. set newline ""
  174. for {set i [expr {$history(nextid) - $count + 1}]} \
  175. {$i <= $history(nextid)} {incr i} {
  176. if {![info exists history($i)]} {
  177. continue
  178. }
  179. set cmd [string map [list \n \n\t] [string trimright $history($i) \ \n]]
  180. append result $newline[format "%6d %s" $i $cmd]
  181. set newline \n
  182. }
  183. return $result
  184. }
  185. # tcl::HistRedo --
  186. #
  187. # Fetch the previous or specified event, execute it, and then replace
  188. # the current history item with that event.
  189. #
  190. # Parameters:
  191. # event (optional) index of history item to redo. Defaults to -1,
  192. # which means the previous event.
  193. #
  194. # Results:
  195. # Those of the command being redone.
  196. #
  197. # Side Effects:
  198. # Replaces the current history list item with the one being redone.
  199. proc ::tcl::HistRedo {{event -1}} {
  200. variable history
  201. set i [HistIndex $event]
  202. if {$i == $history(nextid)} {
  203. return -code error "cannot redo the current event"
  204. }
  205. set cmd $history($i)
  206. HistChange $cmd 0
  207. tailcall eval $cmd
  208. }
  209. # tcl::HistIndex --
  210. #
  211. # Map from an event specifier to an index in the history list.
  212. #
  213. # Parameters:
  214. # event index of history item to redo.
  215. # If this is a positive number, it is used directly.
  216. # If it is a negative number, then it counts back to a previous
  217. # event, where -1 is the most recent event.
  218. # A string can be matched, either by being the prefix of a
  219. # command or by matching a command with string match.
  220. #
  221. # Results:
  222. # The index into history, or an error if the index didn't match.
  223. proc ::tcl::HistIndex {event} {
  224. variable history
  225. if {![string is integer -strict $event]} {
  226. for {set i [expr {$history(nextid)-1}]} {[info exists history($i)]} \
  227. {incr i -1} {
  228. if {[string match $event* $history($i)]} {
  229. return $i
  230. }
  231. if {[string match $event $history($i)]} {
  232. return $i
  233. }
  234. }
  235. return -code error "no event matches \"$event\""
  236. } elseif {$event <= 0} {
  237. set i [expr {$history(nextid) + $event}]
  238. } else {
  239. set i $event
  240. }
  241. if {$i <= $history(oldest)} {
  242. return -code error "event \"$event\" is too far in the past"
  243. }
  244. if {$i > $history(nextid)} {
  245. return -code error "event \"$event\" hasn't occured yet"
  246. }
  247. return $i
  248. }
  249. # tcl::HistEvent --
  250. #
  251. # Map from an event specifier to the value in the history list.
  252. #
  253. # Parameters:
  254. # event index of history item to redo. See index for a description of
  255. # possible event patterns.
  256. #
  257. # Results:
  258. # The value from the history list.
  259. proc ::tcl::HistEvent {{event -1}} {
  260. variable history
  261. set i [HistIndex $event]
  262. if {![info exists history($i)]} {
  263. return ""
  264. }
  265. return [string trimright $history($i) \ \n]
  266. }
  267. # tcl::HistChange --
  268. #
  269. # Replace a value in the history list.
  270. #
  271. # Parameters:
  272. # newValue The new value to put into the history list.
  273. # event (optional) index of history item to redo. See index for a
  274. # description of possible event patterns. This defaults to 0,
  275. # which specifies the current event.
  276. #
  277. # Side Effects:
  278. # Changes the history list.
  279. proc ::tcl::HistChange {newValue {event 0}} {
  280. variable history
  281. set i [HistIndex $event]
  282. set history($i) $newValue
  283. }
  284. # tcl::HistNextID --
  285. #
  286. # Returns the number of the next history event.
  287. #
  288. # Parameters:
  289. # None.
  290. #
  291. # Side Effects:
  292. # None.
  293. proc ::tcl::HistNextID {} {
  294. variable history
  295. return [expr {$history(nextid) + 1}]
  296. }
  297. return
  298. # Local Variables:
  299. # mode: tcl
  300. # fill-column: 78
  301. # End: