Commit dace60cf authored by John Wiegley's avatar John Wiegley
Browse files

See ChangeLog

parent 657f9cb8
2000-10-18 John Wiegley <>
* NEWS: Added text for pcomplete.el.
2000-10-16 Gerd Moellmann <>
......@@ -1786,7 +1786,10 @@ Fill mode.
*** gnus-mule.el is now just a compatibility layer over the built-in
Gnus facilities.
*** pcomplete.el ??
*** pcomplete.el is a library that provides programmable completion
facilities for Emacs, similar to what zsh and tcsh offer. The main
difference is that completion functions are written in Lisp, meaning
they can be profiled, debugged, etc.
** Withdrawn packages
2000-10-28 John Wiegley <>
* textmodes/flyspell.el (flyspell-maybe-correct-transposition):
Changed this function to operate on a temporary buffer instead of
the main buffer. This not only keeps flyspell from marking a
buffer as changed that wasn't, but it solves the jumpy cursor
problem when attempts are made to edit incorrect words.
(flyspell-maybe-correct-doubling): Same change as for
* calendar/timeclock.el (timeclock-log): Doc fix.
(timeclock-last-event): Doc fix.
(timeclock-log): Kill the timelog buffer after appending a new
(timeclock-find-discrep): Use a temp buffer to read in the
timelog, instead of visiting the file.
(timeclock-log-data): A new function, along with a host of helper
functions, for the purpose of making timelog data accessible to
* eshell/esh-mode.el (window-height test): Make certain that
`eshell-stringify-t' is non-nil.
(eshell-password-prompt-regexp): Changed to a much simpler
password regexp.
(eshell-send-input): If `eshell-invoke-directly' returns t,
directly invoke the parsed command using `eval'. This improves
turn-around time on simple commands by a factor of three or
greater, such as cd, ls, pwd, etc. -- which get used very often.
It also conserves thousands of cons cells per call (since
`eshell-do-eval' consumes memory like a Cookie Monster set loose
in the Pacific Cookie Company).
* eshell/esh-test.el (eshell-test): Whitespace fix.
* eshell/em-ls.el (eshell-ls-insert-directory): Make
`eshell-ls-initial-args' nil when inserting directory contents.
* eshell/em-script.el (eshell-script-initialize): Add names to
`eshell-complex-commands, since `source' and `.' are complex.
* eshell/esh-cmd.el (eshell-rewrite-for-command,
eshell-rewrite-while-command): Use `eshell-protect' instead of
(eshell-rewrite-if-command): Use `eshell-protect' to wrap the call
(eshell-separate-commands): Whitespace fix.
(eshell-complex-commands): Added a new list of names, for
determining whether a given command is as simple as it looks.
(eshell-invoke-directly): New function. Returns t if a command
should be invoked directly (using `eval'), rather than indirectly
using `eshell-do-eval'.
(eshell-do-eval): Whitespace fix.
* eshell/em-unix.el (eshell-default-target-is-dot): New variable,
which provides an emulation of the DOS shell behavior of assuming
that cp/mv/ln should copy/move/link to the current directory.
(eshell-remove-entries): Added a doc string.
(eshell-shuffle-files): Removed the check for `target' being null.
(eshell-mvcp-template, eshell-mvcpln-template): Renamed
`eshell-mvcp-template' to `eshell-mvcpln-template', and extended
it to do a smarter check of whether a destination was provided.
(eshell/mv, eshell/cp): Enable `:preserve-args'.
(eshell/ln): Enable `:preserve-args', and use
`eshell-mvcpln-template' to implement the body of the function.
(eshell/cat, eshell/make, eshell-poor-mans-grep, eshell-grep,
eshell/du, eshell/diff, eshell/locate): Stringify the argument
list after flattening it. This makes it possible to cat files
with numerical names.
(eshell-unix-initialize): Added several names to
(eshell-unix-command-complex-p): Return t if a given command name
may result in external processes being invoked.
* eshell/em-glob.el (eshell-glob-show-progress): Make this
variable nil by default, since it slows down glob processing by a
factor of two or more, and increases memory consumption.
* eshell/em-smart.el: Added a note about how memory consumptive
smart display mode can be (at least this is true in Emacs 21).
(eshell-smart-initialize): Whitespace fix.
(eshell-refresh-windows): Use `if' instead of `when'.
(eshell-smart-scroll-window): Calling `save-current-buffer' was
not necessary.
(eshell-currently-handling-window): Added a missing global
* eshell/em-ls.el (eshell-do-ls): Code simplification.
(eshell-ls-sort-entries, eshell-ls-entries, eshell-ls-dir):
Whitespace fix.
(eshell-ls-exclude-hidden): Added this variable in addition to
`eshell-ls-exclude-regexp'. This one prevents files beginning
with . from even being read, which can improve memory consumption
quite a bit.
(eshell-ls-dir): If `eshell-ls-exclude-hidden' is non-nil, do not
read file entries beginning with a dot. In home directories with
lots of hidden files, fully two-thirds of the time spent in ls is
used to read directory entries that are immediately thrown away.
(eshell-ls-initial-args): Added back this configuration variable,
for specifying default initial arguments to every call to ls.
Much faster than using an alias to do the same thing.
(eshell-do-ls): Use `eshell-ls-initial-args', if set.
(eshell-ls-dir): Whitespace change.
* eshell/em-dirs.el (eshell/pwd): Small code simplification.
* eshell/esh-util.el: Don't require `ange-ftp' if it's not
(eshell-stringify-t): Added a customization variable, to indicate
whether `t' should be rendered as a string at all. If not, one
can still determine if the result of an expression is true using
"file-exists-p FILE && echo true".
(eshell-stringify): If `eshell-stringify-t' is nil, don't
stringify t!
* eshell/esh-module.el: Whitespace fix.
* eshell/em-alias.el (eshell-alias-initialize): Added
`eshell-command-aliased-p' to `eshell-complex-commands'.
(eshell-command-aliased-p): New function that returns t if a
command name names an aliased.
2000-10-29 Michael Kifer <>
* viper-cmd.el (viper-preserve-cursor-color): new test that avoids
......@@ -865,7 +986,7 @@
* align.el, pcomplete.el, calendar/timeclock.el,
eshell/esh-module.el, eshell/eshell.el: Removed URL reference.
* calendar/timeclock.el (timeclock-find-discrep): A fix to same
* calendar/timeclock.el (timeclock-find-discrep): A fix to some
faulty math, where holiday hours were being computing as seconds.
2000-10-13 John Wiegley <>
......@@ -4,7 +4,7 @@
;; Author: John Wiegley <>
;; Created: 25 Mar 1999
;; Version: 2.2
;; Version: 2.3
;; Keywords: calendar data
;; This file is part of GNU Emacs.
......@@ -222,8 +222,7 @@ in the modeline. See the variable `timeclock-modeline-display'."
(defvar timeclock-last-event nil
"A list containing the last event that was recorded.
The format of this list is (CODE TIME PROJECT). PROJECT will be
non-nil only if CODE is \"o\" or \"O\".")
The format of this list is (CODE TIME PROJECT).")
(defvar timeclock-last-event-workday nil
"The number of seconds in the workday of `timeclock-last-event'.")
......@@ -455,7 +454,7 @@ as with time remaining, where negative time really means overtime)."
(truncate (/ (abs seconds) 60 60))
(% (truncate (/ (abs seconds) 60)) 60))))
(defun timeclock-workday-remaining (&optional today-only)
(defsubst timeclock-workday-remaining (&optional today-only)
"Return a the number of seconds until the workday is complete.
The amount returned is relative to the value of `timeclock-workday'.
If TODAY-ONLY is non-nil, the value returned will be relative only to
......@@ -463,7 +462,7 @@ the time worked today, and not to past time. This argument only makes
a difference if `timeclock-relative' is non-nil."
(- (timeclock-find-discrep today-only)))
(defun timeclock-currently-in-p ()
(defsubst timeclock-currently-in-p ()
"Return non-nil if the user is currently clocked in."
(equal (car timeclock-last-event) "i"))
......@@ -483,7 +482,7 @@ See `timeclock-relative' for more information about the meaning of
(message string)
(defun timeclock-workday-elapsed (&optional relative)
(defsubst timeclock-workday-elapsed (&optional relative)
"Return a the number of seconds worked so far today.
If RELATIVE is non-nil, the amount returned will be relative to past
time worked. The default is to return only the time that has elapsed
......@@ -505,7 +504,7 @@ non-nil, the amount returned will be relative to past time worked."
(message string)
(defun timeclock-when-to-leave (&optional today-only)
(defsubst timeclock-when-to-leave (&optional today-only)
"Return a time value representing at when the workday ends today.
If TODAY-ONLY is non-nil, the value returned will be relative only to
the time worked today, and not to past time. This argument only makes
......@@ -578,9 +577,8 @@ non-nil."
(defun timeclock-log (code &optional project)
"Log the event CODE to the timeclock log, at the time of call.
If PROJECT is a string, it represents the project which the event is
being logged for. Normally only \"out\" events specify a project."
(set-buffer (find-file-noselect timeclock-file))
being logged for. Normally only \"in\" events specify a project."
(with-current-buffer (find-file-noselect timeclock-file)
(goto-char (point-max))
(if (not (bolp))
(insert "\n"))
......@@ -603,42 +601,40 @@ being logged for. Normally only \"out\" events specify a project."
(setq timeclock-last-event (list code now project)))
(run-hooks 'timeclock-event-hook)))
(run-hooks 'timeclock-event-hook)
(kill-buffer (current-buffer))))
(defun timeclock-read-moment ()
(defvar timeclock-moment-regexp
(concat "\\([bhioO]\\)\\s-+"
"\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)[ \t]*" "\\([^\n]*\\)"))
(defsubst timeclock-read-moment ()
"Read the moment under point from the timelog."
(let ((eol (save-excursion (end-of-line) (point))))
(if (re-search-forward
(concat "^\\(.\\)\\s-+"
"\\(.*\\)") eol t)
(let ((code (match-string 1))
(year (string-to-number (match-string 2)))
(mon (string-to-number (match-string 3)))
(mday (string-to-number (match-string 4)))
(hour (string-to-number (match-string 5)))
(min (string-to-number (match-string 6)))
(sec (string-to-number (match-string 7)))
(project (match-string 8)))
(list code (encode-time sec min hour mday mon year)
(defun timeclock-time-to-seconds (time)
(if (looking-at timeclock-moment-regexp)
(let ((code (match-string 1))
(year (string-to-number (match-string 2)))
(mon (string-to-number (match-string 3)))
(mday (string-to-number (match-string 4)))
(hour (string-to-number (match-string 5)))
(min (string-to-number (match-string 6)))
(sec (string-to-number (match-string 7)))
(project (match-string 8)))
(list code (encode-time sec min hour mday mon year) project))))
(defsubst timeclock-time-to-seconds (time)
"Convert TIME to a floating point number."
(+ (* (car time) 65536.0)
(cadr time)
(/ (or (car (cdr (cdr time))) 0) 1000000.0)))
(defun timeclock-seconds-to-time (seconds)
(defsubst timeclock-seconds-to-time (seconds)
"Convert SECONDS (a floating point number) to an Emacs time structure."
(list (floor seconds 65536)
(floor (mod seconds 65536))
(floor (* (- seconds (ffloor seconds)) 1000000))))
(defun timeclock-time-to-date (time)
(defsubst timeclock-time-to-date (time)
"Convert the TIME value to a textual date string."
(format-time-string "%Y/%m/%d" time))
......@@ -655,49 +651,376 @@ This is only provided for coherency when used by
(cadr timeclock-last-event)))
(defsubst timeclock-entry-length (entry)
(- (timeclock-time-to-seconds (cadr entry))
(timeclock-time-to-seconds (car entry))))
(defsubst timeclock-entry-begin (entry)
(car entry))
(defsubst timeclock-entry-end (entry)
(cadr entry))
(defsubst timeclock-entry-project (entry)
(nth 2 entry))
(defsubst timeclock-entry-comment (entry)
(nth 3 entry))
(defsubst timeclock-entry-list-length (entry-list)
(let ((length 0))
(while entry-list
(setq length (+ length (timeclock-entry-length (car entry-list))))
(setq entry-list (cdr entry-list)))
(defsubst timeclock-entry-list-begin (entry-list)
(timeclock-entry-begin (car entry-list)))
(defsubst timeclock-entry-list-end (entry-list)
(timeclock-entry-end (car (last entry-list))))
(defsubst timeclock-entry-list-span (entry-list)
(- (timeclock-time-to-seconds (timeclock-entry-list-end entry-list))
(timeclock-time-to-seconds (timeclock-entry-list-begin entry-list))))
(defsubst timeclock-entry-list-break (entry-list)
(- (timeclock-entry-list-span entry-list)
(timeclock-entry-list-length entry-list)))
(defsubst timeclock-entry-list-projects (entry-list)
(let (projects)
(while entry-list
(let ((project (timeclock-entry-project (car entry-list))))
(if projects
(add-to-list 'projects project)
(setq projects (list project))))
(setq entry-list (cdr entry-list)))
(defsubst timeclock-day-required (day)
(car day))
(defsubst timeclock-day-length (day)
(timeclock-entry-list-length (cdr day)))
(defsubst timeclock-day-debt (day)
(- (timeclock-day-required day)
(timeclock-day-length day)))
(defsubst timeclock-day-begin (day)
(timeclock-entry-list-begin (cdr day)))
(defsubst timeclock-day-end (day)
(timeclock-entry-list-end (cdr day)))
(defsubst timeclock-day-span (day)
(timeclock-entry-list-span (cdr day)))
(defsubst timeclock-day-break (day)
(timeclock-entry-list-break (cdr day)))
(defsubst timeclock-day-projects (day)
(timeclock-entry-list-projects (cdr day)))
(defmacro timeclock-day-list-template (func)
`(let ((length 0))
(while day-list
(setq length (+ length (,(eval func) (car day-list))))
(setq day-list (cdr day-list)))
(defun timeclock-day-list-required (day-list)
(timeclock-day-list-template 'timeclock-day-required))
(defun timeclock-day-list-length (day-list)
(timeclock-day-list-template 'timeclock-day-length))
(defun timeclock-day-list-debt (day-list)
(timeclock-day-list-template 'timeclock-day-debt))
(defsubst timeclock-day-list-begin (day-list)
(timeclock-day-begin (car day-list)))
(defsubst timeclock-day-list-end (day-list)
(timeclock-day-end (car (last day-list))))
(defun timeclock-day-list-span (day-list)
(timeclock-day-list-template 'timeclock-day-span))
(defun timeclock-day-list-break (day-list)
(timeclock-day-list-template 'timeclock-day-break))
(defun timeclock-day-list-projects (day-list)
(let (projects)
(while day-list
(let ((projs (timeclock-day-projects (car day-list))))
(while projs
(if projects
(add-to-list 'projects (car projs))
(setq projects (list (car projs))))
(setq projs (cdr projs))))
(setq day-list (cdr day-list)))
(defsubst timeclock-current-debt (&optional log-data)
(nth 0 (or log-data (timeclock-log-data))))
(defsubst timeclock-day-alist (&optional log-data)
(nth 1 (or log-data (timeclock-log-data))))
(defun timeclock-day-list (&optional log-data)
(let ((alist (timeclock-day-alist log-data))
(while alist
(setq day-list (cons (cdar alist) day-list)
alist (cdr alist)))
(defsubst timeclock-project-alist (&optional log-data)
(nth 2 (or log-data (timeclock-log-data))))
(defun timeclock-log-data (&optional recent-only filename)
"Return the contents of the timelog file, in a useful format.
A timelog contains data in the form of a single entry per line.
Each entry has the form:
CODE is one of: b, h, i, o or O. COMMENT is optional when the code is
i, o or O. The meanings of the codes are:
b Set the current time balance, or \"time debt\". Useful when
archiving old log data, when a debt must be carried forward.
The COMMENT here is the number of seconds of debt.
h Set the required working time for the given day. This must
be the first entry for that day. The COMMENT in this case is
the number of hours that must be worked. Floating point
amounts are allowed.
i Clock in. The COMMENT in this case should be the name of the
project worked on.
o Clock out. COMMENT is unnecessary, but can be used to provide
a description of how the period went, for example.
O Final clock out. Whatever project was being worked on, it is
now finished. Useful for creating summary reports.
When this function is called, it will return a data structure with the
following format:
DEBT is a floating point number representing the number of seconds
\"owed\" before any work was done. For a new file (one without a 'b'
entry), this is always zero.
The two entries lists have similar formats. They are both alists,
where the CAR is the index, and the CDR is a list of time entries.
For ENTRIES-BY-DAY, the CAR is a textual date string, of the form
YYYY/MM/DD. For ENTRIES-BY-PROJECT, it is the name of the project
worked on, or t for the default project.
The CDR for ENTRIES-BY-DAY is slightly different than for
ENTRIES-BY-PROJECT. It has the following form:
For ENTRIES-BY-PROJECT, there is no DAY-LENGTH member. It is simply a
list of TIME-ENTRIES. Note that if DAY-LENGTH is nil, it means
whatever is the default should be used.
A TIME-ENTRY is a recorded time interval. It has the following format
\(although generally one does not have to manipulate these entries
directly; see below):
Anyway, suffice it to say there are a lot of structures. Typically
the user is expected to manipulate to the day(s) or project(s) that he
or she wants, at which point the following helper functions may be
A few comments should make the use of the above functions obvious:
`required' is the amount of time that must be spent during a day, or
sequence of days, in order to have no debt.
`length' is the actual amount of time that was spent.
`debt' is the difference between required time and length. A
negative debt signifies overtime.
`begin' is the earliest moment at which work began.
`end' is the final moment work was done.
`span' is the difference between begin and end.
`break' is the difference between span and length.
`project' is the project that was worked on, and `projects' is a
list of all the projects that were worked on during a given period.
`comment', where it applies, could mean anything.
There are a few more functions available, for locating day and entry
timeclock-day-alist LOG-DATA
timeclock-project-alist LOG-DATA
timeclock-current-debt LOG-DATA
See the documentation for the given function if more info is needed."
(let* ((log-data (list 0.0 nil nil))
(now (current-time))
(todays-date (timeclock-time-to-date now))
last-date-limited last-date-seconds last-date
(line 0) last beg day entry)
(insert-file-contents (or filename timeclock-file))
(when recent-only
(goto-char (point-max))
(unless (re-search-backward "^b\\s-+" nil t)
(goto-char (point-min))))
(while (or (setq event (timeclock-read-moment))
(and beg (not last)
(setq last t event (list "o" now))))
(setq line (1+ line))
(cond ((equal (car event) "b")
(setcar log-data (string-to-number (nth 2 event))))
((equal (car event) "h")
(setq last-date-limited (timeclock-time-to-date (cadr event))
last-date-seconds (* (string-to-number (nth 2 event))
((equal (car event) "i")
(if beg
(error "Error in format of timelog file, line %d" line)
(setq beg t))
(setq entry (list (cadr event) nil
(and (> (length (nth 2 event)) 0)
(nth 2 event))))
(let ((date (timeclock-time-to-date (cadr event))))
(if (and last-date
(not (equal date last-date)))
(setcar (cdr log-data)
(cons (cons last-date day)
(cadr log-data)))
(setq day (list (and last-date-limited
(setq last-date date
last-date-limited nil)))
((equal (downcase (car event)) "o")
(if (not beg)
(error "Error in format of timelog file, line %d" line)
(setq beg nil))
(setcar (cdr entry) (cadr event))
(let ((desc (and (> (length (nth 2 event)) 0)
(nth 2 event))))
(if desc
(nconc entry (list (nth 2 event))))
(if (equal (car event) "O")
(nconc entry (if desc
(list t)
(list nil t))))
(nconc day (list entry))
(setq desc (nth 2 entry))
(let ((proj (assoc desc (nth 2 log-data))))
(if (not proj)
(setcar (cddr log-data)
(cons (cons desc (list entry))
(car (cddr log-data))))
(nconc (cdr proj) (list entry)))))))
(if day
(setcar (cdr log-data)
(cons (cons last-date day)
(cadr log-data))))
(defun timeclock-find-discrep (&optional today-only)
"Find overall discrepancy from `timeclock-workday' (in seconds).
If TODAY-ONLY is non-nil, the discrepancy will be not be relative, and
will correspond only to the amount of time elapsed today. This is
identical to what would be return if `timeclock-relative' were nil."
(let* ((now (current-time)) (first t)
;; This is not implemented in terms of the functions above, because
;; it's a bit wasteful to read all of that data in, just to throw
;; away more than 90% of the information afterwards.
(let* ((now (current-time))
(todays-date (timeclock-time-to-date now))
accum event beg last-date
last-date-limited last-date-seconds avg)