history.tcl 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. # tcl::HistAdd --
  52. #
  53. # Add an item to the history, and optionally eval it at the global scope
  54. #
  55. # Parameters:
  56. # event the command to add
  57. # exec (optional) a substring of "exec" causes the command to
  58. # be evaled.
  59. # Results:
  60. # If executing, then the results of the command are returned
  61. #
  62. # Side Effects:
  63. # Adds to the history list
  64. proc ::tcl::HistAdd {event {exec {}}} {
  65. variable history
  66. if {
  67. [prefix longest {exec {}} $exec] eq ""
  68. && [llength [info level 0]] == 3
  69. } then {
  70. return -code error "bad argument \"$exec\": should be \"exec\""
  71. }
  72. # Do not add empty commands to the history
  73. if {[string trim $event] eq ""} {
  74. return ""
  75. }
  76. # Maintain the history
  77. set history([incr history(nextid)]) $event
  78. unset -nocomplain history([incr history(oldest)])
  79. # Only execute if 'exec' (or non-empty prefix of it) given
  80. if {$exec eq ""} {
  81. return ""
  82. }
  83. tailcall eval $event
  84. }
  85. # tcl::HistKeep --
  86. #
  87. # Set or query the limit on the length of the history list
  88. #
  89. # Parameters:
  90. # limit (optional) the length of the history list
  91. #
  92. # Results:
  93. # If no limit is specified, the current limit is returned
  94. #
  95. # Side Effects:
  96. # Updates history(keep) if a limit is specified
  97. proc ::tcl::HistKeep {{count {}}} {
  98. variable history
  99. if {[llength [info level 0]] == 1} {
  100. return $history(keep)
  101. }
  102. if {![string is integer -strict $count] || ($count < 0)} {
  103. return -code error "illegal keep count \"$count\""
  104. }
  105. set oldold $history(oldest)
  106. set history(oldest) [expr {$history(nextid) - $count}]
  107. for {} {$oldold <= $history(oldest)} {incr oldold} {
  108. unset -nocomplain history($oldold)
  109. }
  110. set history(keep) $count
  111. }
  112. # tcl::HistClear --
  113. #
  114. # Erase the history list
  115. #
  116. # Parameters:
  117. # none
  118. #
  119. # Results:
  120. # none
  121. #
  122. # Side Effects:
  123. # Resets the history array, except for the keep limit
  124. proc ::tcl::HistClear {} {
  125. variable history
  126. set keep $history(keep)
  127. unset history
  128. array set history [list \
  129. nextid 0 \
  130. keep $keep \
  131. oldest -$keep \
  132. ]
  133. }
  134. # tcl::HistInfo --
  135. #
  136. # Return a pretty-printed version of the history list
  137. #
  138. # Parameters:
  139. # num (optional) the length of the history list to return
  140. #
  141. # Results:
  142. # A formatted history list
  143. proc ::tcl::HistInfo {{count {}}} {
  144. variable history
  145. if {[llength [info level 0]] == 1} {
  146. set count [expr {$history(keep) + 1}]
  147. } elseif {![string is integer -strict $count]} {
  148. return -code error "bad integer \"$count\""
  149. }
  150. set result {}
  151. set newline ""
  152. for {set i [expr {$history(nextid) - $count + 1}]} \
  153. {$i <= $history(nextid)} {incr i} {
  154. if {![info exists history($i)]} {
  155. continue
  156. }
  157. set cmd [string map [list \n \n\t] [string trimright $history($i) \ \n]]
  158. append result $newline[format "%6d %s" $i $cmd]
  159. set newline \n
  160. }
  161. return $result
  162. }
  163. # tcl::HistRedo --
  164. #
  165. # Fetch the previous or specified event, execute it, and then replace
  166. # the current history item with that event.
  167. #
  168. # Parameters:
  169. # event (optional) index of history item to redo. Defaults to -1,
  170. # which means the previous event.
  171. #
  172. # Results:
  173. # Those of the command being redone.
  174. #
  175. # Side Effects:
  176. # Replaces the current history list item with the one being redone.
  177. proc ::tcl::HistRedo {{event -1}} {
  178. variable history
  179. set i [HistIndex $event]
  180. if {$i == $history(nextid)} {
  181. return -code error "cannot redo the current event"
  182. }
  183. set cmd $history($i)
  184. HistChange $cmd 0
  185. tailcall eval $cmd
  186. }
  187. # tcl::HistIndex --
  188. #
  189. # Map from an event specifier to an index in the history list.
  190. #
  191. # Parameters:
  192. # event index of history item to redo.
  193. # If this is a positive number, it is used directly.
  194. # If it is a negative number, then it counts back to a previous
  195. # event, where -1 is the most recent event.
  196. # A string can be matched, either by being the prefix of a
  197. # command or by matching a command with string match.
  198. #
  199. # Results:
  200. # The index into history, or an error if the index didn't match.
  201. proc ::tcl::HistIndex {event} {
  202. variable history
  203. if {![string is integer -strict $event]} {
  204. for {set i [expr {$history(nextid)-1}]} {[info exists history($i)]} \
  205. {incr i -1} {
  206. if {[string match $event* $history($i)]} {
  207. return $i
  208. }
  209. if {[string match $event $history($i)]} {
  210. return $i
  211. }
  212. }
  213. return -code error "no event matches \"$event\""
  214. } elseif {$event <= 0} {
  215. set i [expr {$history(nextid) + $event}]
  216. } else {
  217. set i $event
  218. }
  219. if {$i <= $history(oldest)} {
  220. return -code error "event \"$event\" is too far in the past"
  221. }
  222. if {$i > $history(nextid)} {
  223. return -code error "event \"$event\" hasn't occured yet"
  224. }
  225. return $i
  226. }
  227. # tcl::HistEvent --
  228. #
  229. # Map from an event specifier to the value in the history list.
  230. #
  231. # Parameters:
  232. # event index of history item to redo. See index for a description of
  233. # possible event patterns.
  234. #
  235. # Results:
  236. # The value from the history list.
  237. proc ::tcl::HistEvent {{event -1}} {
  238. variable history
  239. set i [HistIndex $event]
  240. if {![info exists history($i)]} {
  241. return ""
  242. }
  243. return [string trimright $history($i) \ \n]
  244. }
  245. # tcl::HistChange --
  246. #
  247. # Replace a value in the history list.
  248. #
  249. # Parameters:
  250. # newValue The new value to put into the history list.
  251. # event (optional) index of history item to redo. See index for a
  252. # description of possible event patterns. This defaults to 0,
  253. # which specifies the current event.
  254. #
  255. # Side Effects:
  256. # Changes the history list.
  257. proc ::tcl::HistChange {newValue {event 0}} {
  258. variable history
  259. set i [HistIndex $event]
  260. set history($i) $newValue
  261. }
  262. # tcl::HistNextID --
  263. #
  264. # Returns the number of the next history event.
  265. #
  266. # Parameters:
  267. # None.
  268. #
  269. # Side Effects:
  270. # None.
  271. proc ::tcl::HistNextID {} {
  272. variable history
  273. return [expr {$history(nextid) + 1}]
  274. }
  275. return
  276. # Local Variables:
  277. # mode: tcl
  278. # fill-column: 78
  279. # End: