history.tcl 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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
  8. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  9. #
  10. # The tcl::history array holds the history list and
  11. # some additional 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. }
  26. # history --
  27. #
  28. # This is the main history command. See the man page for its interface.
  29. # This does argument checking and calls helper procedures in the
  30. # history namespace.
  31. proc history {args} {
  32. set len [llength $args]
  33. if {$len == 0} {
  34. return [tcl::HistInfo]
  35. }
  36. set key [lindex $args 0]
  37. set options "add, change, clear, event, info, keep, nextid, or redo"
  38. switch -glob -- $key {
  39. a* { # history add
  40. if {$len > 3} {
  41. return -code error "wrong # args: should be \"history add event ?exec?\""
  42. }
  43. if {![string match $key* add]} {
  44. return -code error "bad option \"$key\": must be $options"
  45. }
  46. if {$len == 3} {
  47. set arg [lindex $args 2]
  48. if {! ([string match e* $arg] && [string match $arg* exec])} {
  49. return -code error "bad argument \"$arg\": should be \"exec\""
  50. }
  51. }
  52. return [tcl::HistAdd [lindex $args 1] [lindex $args 2]]
  53. }
  54. ch* { # history change
  55. if {($len > 3) || ($len < 2)} {
  56. return -code error "wrong # args: should be \"history change newValue ?event?\""
  57. }
  58. if {![string match $key* change]} {
  59. return -code error "bad option \"$key\": must be $options"
  60. }
  61. if {$len == 2} {
  62. set event 0
  63. } else {
  64. set event [lindex $args 2]
  65. }
  66. return [tcl::HistChange [lindex $args 1] $event]
  67. }
  68. cl* { # history clear
  69. if {($len > 1)} {
  70. return -code error "wrong # args: should be \"history clear\""
  71. }
  72. if {![string match $key* clear]} {
  73. return -code error "bad option \"$key\": must be $options"
  74. }
  75. return [tcl::HistClear]
  76. }
  77. e* { # history event
  78. if {$len > 2} {
  79. return -code error "wrong # args: should be \"history event ?event?\""
  80. }
  81. if {![string match $key* event]} {
  82. return -code error "bad option \"$key\": must be $options"
  83. }
  84. if {$len == 1} {
  85. set event -1
  86. } else {
  87. set event [lindex $args 1]
  88. }
  89. return [tcl::HistEvent $event]
  90. }
  91. i* { # history info
  92. if {$len > 2} {
  93. return -code error "wrong # args: should be \"history info ?count?\""
  94. }
  95. if {![string match $key* info]} {
  96. return -code error "bad option \"$key\": must be $options"
  97. }
  98. return [tcl::HistInfo [lindex $args 1]]
  99. }
  100. k* { # history keep
  101. if {$len > 2} {
  102. return -code error "wrong # args: should be \"history keep ?count?\""
  103. }
  104. if {$len == 1} {
  105. return [tcl::HistKeep]
  106. } else {
  107. set limit [lindex $args 1]
  108. if {[catch {expr {~$limit}}] || ($limit < 0)} {
  109. return -code error "illegal keep count \"$limit\""
  110. }
  111. return [tcl::HistKeep $limit]
  112. }
  113. }
  114. n* { # history nextid
  115. if {$len > 1} {
  116. return -code error "wrong # args: should be \"history nextid\""
  117. }
  118. if {![string match $key* nextid]} {
  119. return -code error "bad option \"$key\": must be $options"
  120. }
  121. return [expr {$tcl::history(nextid) + 1}]
  122. }
  123. r* { # history redo
  124. if {$len > 2} {
  125. return -code error "wrong # args: should be \"history redo ?event?\""
  126. }
  127. if {![string match $key* redo]} {
  128. return -code error "bad option \"$key\": must be $options"
  129. }
  130. return [tcl::HistRedo [lindex $args 1]]
  131. }
  132. default {
  133. return -code error "bad option \"$key\": must be $options"
  134. }
  135. }
  136. }
  137. # tcl::HistAdd --
  138. #
  139. # Add an item to the history, and optionally eval it at the global scope
  140. #
  141. # Parameters:
  142. # command the command to add
  143. # exec (optional) a substring of "exec" causes the
  144. # command to be evaled.
  145. # Results:
  146. # If executing, then the results of the command are returned
  147. #
  148. # Side Effects:
  149. # Adds to the history list
  150. proc tcl::HistAdd {command {exec {}}} {
  151. variable history
  152. # Do not add empty commands to the history
  153. if {[string trim $command] eq ""} {
  154. return ""
  155. }
  156. set i [incr history(nextid)]
  157. set history($i) $command
  158. set j [incr history(oldest)]
  159. unset -nocomplain history($j)
  160. if {[string match e* $exec]} {
  161. return [uplevel #0 $command]
  162. } else {
  163. return {}
  164. }
  165. }
  166. # tcl::HistKeep --
  167. #
  168. # Set or query the limit on the length of the history list
  169. #
  170. # Parameters:
  171. # limit (optional) the length of the history list
  172. #
  173. # Results:
  174. # If no limit is specified, the current limit is returned
  175. #
  176. # Side Effects:
  177. # Updates history(keep) if a limit is specified
  178. proc tcl::HistKeep {{limit {}}} {
  179. variable history
  180. if {$limit eq ""} {
  181. return $history(keep)
  182. } else {
  183. set oldold $history(oldest)
  184. set history(oldest) [expr {$history(nextid) - $limit}]
  185. for {} {$oldold <= $history(oldest)} {incr oldold} {
  186. unset -nocomplain history($oldold)
  187. }
  188. set history(keep) $limit
  189. }
  190. }
  191. # tcl::HistClear --
  192. #
  193. # Erase the history list
  194. #
  195. # Parameters:
  196. # none
  197. #
  198. # Results:
  199. # none
  200. #
  201. # Side Effects:
  202. # Resets the history array, except for the keep limit
  203. proc tcl::HistClear {} {
  204. variable history
  205. set keep $history(keep)
  206. unset history
  207. array set history [list \
  208. nextid 0 \
  209. keep $keep \
  210. oldest -$keep \
  211. ]
  212. }
  213. # tcl::HistInfo --
  214. #
  215. # Return a pretty-printed version of the history list
  216. #
  217. # Parameters:
  218. # num (optional) the length of the history list to return
  219. #
  220. # Results:
  221. # A formatted history list
  222. proc tcl::HistInfo {{num {}}} {
  223. variable history
  224. if {$num eq ""} {
  225. set num [expr {$history(keep) + 1}]
  226. }
  227. set result {}
  228. set newline ""
  229. for {set i [expr {$history(nextid) - $num + 1}]} \
  230. {$i <= $history(nextid)} {incr i} {
  231. if {![info exists history($i)]} {
  232. continue
  233. }
  234. set cmd [string map [list \n \n\t] [string trimright $history($i) \ \n]]
  235. append result $newline[format "%6d %s" $i $cmd]
  236. set newline \n
  237. }
  238. return $result
  239. }
  240. # tcl::HistRedo --
  241. #
  242. # Fetch the previous or specified event, execute it, and then
  243. # replace the current history item with that event.
  244. #
  245. # Parameters:
  246. # event (optional) index of history item to redo. Defaults to -1,
  247. # which means the previous event.
  248. #
  249. # Results:
  250. # Those of the command being redone.
  251. #
  252. # Side Effects:
  253. # Replaces the current history list item with the one being redone.
  254. proc tcl::HistRedo {{event -1}} {
  255. variable history
  256. if {$event eq ""} {
  257. set event -1
  258. }
  259. set i [HistIndex $event]
  260. if {$i == $history(nextid)} {
  261. return -code error "cannot redo the current event"
  262. }
  263. set cmd $history($i)
  264. HistChange $cmd 0
  265. uplevel #0 $cmd
  266. }
  267. # tcl::HistIndex --
  268. #
  269. # Map from an event specifier to an index in the history list.
  270. #
  271. # Parameters:
  272. # event index of history item to redo.
  273. # If this is a positive number, it is used directly.
  274. # If it is a negative number, then it counts back to a previous
  275. # event, where -1 is the most recent event.
  276. # A string can be matched, either by being the prefix of
  277. # a command or by matching a command with string match.
  278. #
  279. # Results:
  280. # The index into history, or an error if the index didn't match.
  281. proc tcl::HistIndex {event} {
  282. variable history
  283. if {[catch {expr {~$event}}]} {
  284. for {set i [expr {$history(nextid)-1}]} {[info exists history($i)]} \
  285. {incr i -1} {
  286. if {[string match $event* $history($i)]} {
  287. return $i;
  288. }
  289. if {[string match $event $history($i)]} {
  290. return $i;
  291. }
  292. }
  293. return -code error "no event matches \"$event\""
  294. } elseif {$event <= 0} {
  295. set i [expr {$history(nextid) + $event}]
  296. } else {
  297. set i $event
  298. }
  299. if {$i <= $history(oldest)} {
  300. return -code error "event \"$event\" is too far in the past"
  301. }
  302. if {$i > $history(nextid)} {
  303. return -code error "event \"$event\" hasn't occured yet"
  304. }
  305. return $i
  306. }
  307. # tcl::HistEvent --
  308. #
  309. # Map from an event specifier to the value in the history list.
  310. #
  311. # Parameters:
  312. # event index of history item to redo. See index for a
  313. # description of possible event patterns.
  314. #
  315. # Results:
  316. # The value from the history list.
  317. proc tcl::HistEvent {event} {
  318. variable history
  319. set i [HistIndex $event]
  320. if {[info exists history($i)]} {
  321. return [string trimright $history($i) \ \n]
  322. } else {
  323. return "";
  324. }
  325. }
  326. # tcl::HistChange --
  327. #
  328. # Replace a value in the history list.
  329. #
  330. # Parameters:
  331. # cmd The new value to put into the history list.
  332. # event (optional) index of history item to redo. See index for a
  333. # description of possible event patterns. This defaults
  334. # to 0, which specifies the current event.
  335. #
  336. # Side Effects:
  337. # Changes the history list.
  338. proc tcl::HistChange {cmd {event 0}} {
  339. variable history
  340. set i [HistIndex $event]
  341. set history($i) $cmd
  342. }