3 # Implementation of the history command.
5 # Copyright (c) 1997 Sun Microsystems, Inc.
7 # See the file "license.terms" for information on usage and redistribution of
8 # this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 # The tcl::history array holds the history list and some additional
12 # bookkeeping variables.
14 # nextid the index used for the next history list item.
15 # keep the max size of the history list
16 # oldest the index of the oldest item in the history.
18 namespace eval ::tcl {
20 if {![info exists history]} {
28 namespace ensemble create -command ::tcl::history -map {
30 change ::tcl::HistChange
31 clear ::tcl::HistClear
32 event ::tcl::HistEvent
35 nextid ::tcl::HistNextID
42 # This is the main history command. See the man page for its interface.
43 # This does some argument checking and calls the helper ensemble in the
46 proc ::history {args} {
47 # If no command given, we're doing 'history info'. Can't be done with an
48 # ensemble unknown handler, as those don't fire when no subcommand is
51 if {![llength $args]} {
55 # Tricky stuff needed to make stack and errors come out right!
56 tailcall apply {arglist {tailcall history {*}$arglist} ::tcl} $args
61 # Add an item to the history, and optionally eval it at the global scope
64 # event the command to add
65 # exec (optional) a substring of "exec" causes the command to
68 # If executing, then the results of the command are returned
71 # Adds to the history list
73 proc ::tcl::HistAdd {event {exec {}}} {
77 [prefix longest {exec {}} $exec] eq ""
78 && [llength [info level 0]] == 3
80 return -code error "bad argument \"$exec\": should be \"exec\""
83 # Do not add empty commands to the history
84 if {[string trim $event] eq ""} {
88 # Maintain the history
89 set history([incr history(nextid)]) $event
90 unset -nocomplain history([incr history(oldest)])
92 # Only execute if 'exec' (or non-empty prefix of it) given
101 # Set or query the limit on the length of the history list
104 # limit (optional) the length of the history list
107 # If no limit is specified, the current limit is returned
110 # Updates history(keep) if a limit is specified
112 proc ::tcl::HistKeep {{count {}}} {
114 if {[llength [info level 0]] == 1} {
115 return $history(keep)
117 if {![string is integer -strict $count] || ($count < 0)} {
118 return -code error "illegal keep count \"$count\""
120 set oldold $history(oldest)
121 set history(oldest) [expr {$history(nextid) - $count}]
122 for {} {$oldold <= $history(oldest)} {incr oldold} {
123 unset -nocomplain history($oldold)
125 set history(keep) $count
130 # Erase the history list
139 # Resets the history array, except for the keep limit
141 proc ::tcl::HistClear {} {
143 set keep $history(keep)
145 array set history [list \
154 # Return a pretty-printed version of the history list
157 # num (optional) the length of the history list to return
160 # A formatted history list
162 proc ::tcl::HistInfo {{count {}}} {
164 if {[llength [info level 0]] == 1} {
165 set count [expr {$history(keep) + 1}]
166 } elseif {![string is integer -strict $count]} {
167 return -code error "bad integer \"$count\""
171 for {set i [expr {$history(nextid) - $count + 1}]} \
172 {$i <= $history(nextid)} {incr i} {
173 if {![info exists history($i)]} {
176 set cmd [string map [list \n \n\t] [string trimright $history($i) \ \n]]
177 append result $newline[format "%6d %s" $i $cmd]
185 # Fetch the previous or specified event, execute it, and then replace
186 # the current history item with that event.
189 # event (optional) index of history item to redo. Defaults to -1,
190 # which means the previous event.
193 # Those of the command being redone.
196 # Replaces the current history list item with the one being redone.
198 proc ::tcl::HistRedo {{event -1}} {
201 set i [HistIndex $event]
202 if {$i == $history(nextid)} {
203 return -code error "cannot redo the current event"
212 # Map from an event specifier to an index in the history list.
215 # event index of history item to redo.
216 # If this is a positive number, it is used directly.
217 # If it is a negative number, then it counts back to a previous
218 # event, where -1 is the most recent event.
219 # A string can be matched, either by being the prefix of a
220 # command or by matching a command with string match.
223 # The index into history, or an error if the index didn't match.
225 proc ::tcl::HistIndex {event} {
227 if {![string is integer -strict $event]} {
228 for {set i [expr {$history(nextid)-1}]} {[info exists history($i)]} \
230 if {[string match $event* $history($i)]} {
233 if {[string match $event $history($i)]} {
237 return -code error "no event matches \"$event\""
238 } elseif {$event <= 0} {
239 set i [expr {$history(nextid) + $event}]
243 if {$i <= $history(oldest)} {
244 return -code error "event \"$event\" is too far in the past"
246 if {$i > $history(nextid)} {
247 return -code error "event \"$event\" hasn't occured yet"
254 # Map from an event specifier to the value in the history list.
257 # event index of history item to redo. See index for a description of
258 # possible event patterns.
261 # The value from the history list.
263 proc ::tcl::HistEvent {{event -1}} {
265 set i [HistIndex $event]
266 if {![info exists history($i)]} {
269 return [string trimright $history($i) \ \n]
274 # Replace a value in the history list.
277 # newValue The new value to put into the history list.
278 # event (optional) index of history item to redo. See index for a
279 # description of possible event patterns. This defaults to 0,
280 # which specifies the current event.
283 # Changes the history list.
285 proc ::tcl::HistChange {newValue {event 0}} {
287 set i [HistIndex $event]
288 set history($i) $newValue
293 # Returns the number of the next history event.
301 proc ::tcl::HistNextID {} {
303 return [expr {$history(nextid) + 1}]