Browse Source

Implement continuous clocking. Small other improvements. Update documentation.

* org.el (org-mode-map): Add `C-c C-x C-I' as a keybinding for
`org-clock-in-last'.

* org-clock.el (org-clock-continuously): New option.
(org-clock-in): Three universal prefix arguments set
`org-clock-continuously' to `t' temporarily.
(org-clock-in-last): Fix call to `org-clock-select-task' and
support continuous clocking.
(org-clock-out-time): New variable.
(org-clock-out): set `org-clock-out-time' when clocking out.
Small docstring rewriting.
(org-clock-remove-empty-clock-drawer): Fix "invalid search
bound" bug when trying to delete empty logbook drawer.
(org-clock-cancel): If the clock log is gone, send a warning
instead of deleting the region that is supposed to contain it.

* org.texi (Clocking commands): New cindex.
(Clocking commands): Update documentation for `org-clock-in'.
Document `org-clock-in-last'.  Mention `org-clock-out' and
`org-clock-in-last' as commands that can be globally bound.
(Resolving idle time): Document continuous clocking.
Bastien Guerry 13 years ago
parent
commit
3528fc6b42
3 changed files with 74 additions and 20 deletions
  1. 30 4
      doc/org.texi
  2. 43 16
      lisp/org-clock.el
  3. 1 0
      lisp/org.el

+ 30 - 4
doc/org.texi

@@ -6012,6 +6012,7 @@ what to do with it.
 @table @kbd
 @table @kbd
 @orgcmd{C-c C-x C-i,org-clock-in}
 @orgcmd{C-c C-x C-i,org-clock-in}
 @vindex org-clock-into-drawer
 @vindex org-clock-into-drawer
+@vindex org-clock-continuously
 @cindex property, LOG_INTO_DRAWER
 @cindex property, LOG_INTO_DRAWER
 Start the clock on the current item (clock-in).  This inserts the CLOCK
 Start the clock on the current item (clock-in).  This inserts the CLOCK
 keyword together with a timestamp.  If this is not the first clocking of
 keyword together with a timestamp.  If this is not the first clocking of
@@ -6022,9 +6023,10 @@ the setting of this variable for a subtree by setting a
 @code{CLOCK_INTO_DRAWER} or @code{LOG_INTO_DRAWER} property.
 @code{CLOCK_INTO_DRAWER} or @code{LOG_INTO_DRAWER} property.
 When called with a @kbd{C-u} prefix argument,
 When called with a @kbd{C-u} prefix argument,
 select the task from a list of recently clocked tasks.  With two @kbd{C-u
 select the task from a list of recently clocked tasks.  With two @kbd{C-u
-C-u} prefixes, clock into the task at point and mark it as the default task.
-The default task will always be available when selecting a clocking task,
-with letter @kbd{d}.@*
+C-u} prefixes, clock into the task at point and mark it as the default task;
+the default task will then always be available with letter @kbd{d} when
+selecting a clocking task.  With three @kbd{C-u C-u C-u} prefixes, force
+continuous clocking by starting the clock when the last clock stopped.@*
 @cindex property: CLOCK_MODELINE_TOTAL
 @cindex property: CLOCK_MODELINE_TOTAL
 @cindex property: LAST_REPEAT
 @cindex property: LAST_REPEAT
 @vindex org-clock-modeline-total
 @vindex org-clock-modeline-total
@@ -6054,6 +6056,12 @@ HH:MM}.  See the variable @code{org-log-note-clock-out} for the
 possibility to record an additional note together with the clock-out
 possibility to record an additional note together with the clock-out
 timestamp@footnote{The corresponding in-buffer setting is:
 timestamp@footnote{The corresponding in-buffer setting is:
 @code{#+STARTUP: lognoteclock-out}}.
 @code{#+STARTUP: lognoteclock-out}}.
+@orgcmd{C-c C-x C-I,org-clock-in-last}
+@vindex org-clock-continuously
+Reclock the last clocked task.  With one @kbd{C-u} prefix argument,
+select the task from the clock history.  With two @kbd{C-u} prefixes,
+force continuous clocking by starting the clock when the last clock
+stopped.
 @orgcmd{C-c C-x C-e,org-clock-modify-effort-estimate}
 @orgcmd{C-c C-x C-e,org-clock-modify-effort-estimate}
 Update the effort estimate for the current clock task.
 Update the effort estimate for the current clock task.
 @kindex C-c C-y
 @kindex C-c C-y
@@ -6088,6 +6096,10 @@ The @kbd{l} key may be used in the timeline (@pxref{Timeline}) and in
 the agenda (@pxref{Weekly/daily agenda}) to show which tasks have been
 the agenda (@pxref{Weekly/daily agenda}) to show which tasks have been
 worked on or closed during a day.
 worked on or closed during a day.
 
 
+@strong{Important:} note that both @code{org-clock-out} and
+@code{org-clock-in-last} can have a global keybinding and will not
+modify the window disposition.
+
 @node The clock table, Resolving idle time, Clocking commands, Clocking work time
 @node The clock table, Resolving idle time, Clocking commands, Clocking work time
 @subsection The clock table
 @subsection The clock table
 @cindex clocktable, dynamic block
 @cindex clocktable, dynamic block
@@ -6225,7 +6237,9 @@ would be
 @end example
 @end example
 
 
 @node Resolving idle time,  , The clock table, Clocking work time
 @node Resolving idle time,  , The clock table, Clocking work time
-@subsection Resolving idle time
+@subsection Resolving idle time and continuous clocking
+
+@subsubheading Resolving idle time
 @cindex resolve idle time
 @cindex resolve idle time
 
 
 @cindex idle, resolve, dangling
 @cindex idle, resolve, dangling
@@ -6292,6 +6306,18 @@ to a recovery event rather than a set amount of idle time.
 You can also check all the files visited by your Org agenda for dangling
 You can also check all the files visited by your Org agenda for dangling
 clocks at any time using @kbd{M-x org-resolve-clocks}.
 clocks at any time using @kbd{M-x org-resolve-clocks}.
 
 
+@subsubheading Continuous clocking
+@cindex continuous clocking
+@vindex org-clock-continuously
+
+You may want to start clocking from the time when you clocked out the
+previous task.  To enable this systematically, set @code{org-clock-continuously}
+to @code{t}.  Each time you clock in, Org retrieves the clock-out time of the
+last clocked entry for this session, and start the new clock from there.
+
+If you only want this from time to time, use three universal prefix arguments
+with @code{org-clock-in} and two @kbd{C-u C-u} with @code{org-clock-in-last}.
+
 @node Effort estimates, Relative timer, Clocking work time, Dates and Times
 @node Effort estimates, Relative timer, Clocking work time, Dates and Times
 @section Effort estimates
 @section Effort estimates
 @cindex effort estimates
 @cindex effort estimates

+ 43 - 16
lisp/org-clock.el

@@ -325,6 +325,12 @@ play with them."
   :version "24.1"
   :version "24.1"
   :type 'boolean)
   :type 'boolean)
 
 
+(defcustom org-clock-continuously nil
+  "Non-nil means to start clocking from the last clock-out time, if any."
+  :type 'boolean
+  :version "24.1"
+  :group 'org-clock)
+
 (defcustom org-clock-total-time-cell-format "*%s*"
 (defcustom org-clock-total-time-cell-format "*%s*"
   "Format string for the total time cells."
   "Format string for the total time cells."
   :group 'org-clock
   :group 'org-clock
@@ -1054,7 +1060,10 @@ recently clocked tasks to
 clock into.  When SELECT is \\[universal-argument] \\[universal-argument], \
 clock into.  When SELECT is \\[universal-argument] \\[universal-argument], \
 clock into the current task and mark
 clock into the current task and mark
 it as the default task, a special task that will always be offered in
 it as the default task, a special task that will always be offered in
-the clocking selection, associated with the letter `d'."
+the clocking selection, associated with the letter `d'.
+When SELECT is \\[universal-argument] \\[universal-argument] \\[universal-argument], \
+clock in by using the last clock-out time as the start time
+\(see `org-clock-continuously' to make this the default behavior.)"
   (interactive "P")
   (interactive "P")
   (setq org-clock-notification-was-shown nil)
   (setq org-clock-notification-was-shown nil)
   (catch 'abort
   (catch 'abort
@@ -1073,6 +1082,11 @@ the clocking selection, associated with the letter `d'."
 	(let ((org-clock-clocking-in t))
 	(let ((org-clock-clocking-in t))
 	  (org-resolve-clocks)))	; check if any clocks are dangling
 	  (org-resolve-clocks)))	; check if any clocks are dangling
 
 
+      (when (equal select '(64))
+	;; Set start-time to `org-clock-out-time'
+	(let ((org-clock-continuously t))
+	  (org-clock-in nil org-clock-out-time)))
+
       (when (equal select '(4))
       (when (equal select '(4))
 	(setq selected-task (org-clock-select-task "Clock-in on task: "))
 	(setq selected-task (org-clock-select-task "Clock-in on task: "))
 	(if selected-task
 	(if selected-task
@@ -1191,7 +1205,8 @@ the clocking selection, associated with the letter `d'."
 	      (setq org-clock-total-time (org-clock-sum-current-item
 	      (setq org-clock-total-time (org-clock-sum-current-item
 					  (org-clock-get-sum-start)))
 					  (org-clock-get-sum-start)))
 	      (setq org-clock-start-time
 	      (setq org-clock-start-time
-		    (or (and leftover
+		    (or (and org-clock-continuously org-clock-out-time)
+			(and leftover
 			     (y-or-n-p
 			     (y-or-n-p
 			      (format
 			      (format
 			       "You stopped another clock %d mins ago; start this one from then? "
 			       "You stopped another clock %d mins ago; start this one from then? "
@@ -1238,13 +1253,20 @@ the clocking selection, associated with the letter `d'."
 ;;;###autoload
 ;;;###autoload
 (defun org-clock-in-last (&optional arg)
 (defun org-clock-in-last (&optional arg)
   "Clock in the last closed clocked item.
   "Clock in the last closed clocked item.
-When already clocking in, send an warning."
+When already clocking in, send an warning.
+With a universal prefix argument, select the task you want to
+clock in from the last clocked in tasks.
+With two universal prefix arguments, start clocking using the
+last clock-out time, if any."
   (interactive "P")
   (interactive "P")
-  (if arg (org-clock-select-task)
-    (org-clock-clock-in (cons (car org-clock-history) (current-time)))
-    (message "Now clocking in: %s (in %s)"
-	     org-clock-current-task
-	     (buffer-name (marker-buffer org-clock-marker)))))
+  (if (equal arg '(4)) (org-clock-in (org-clock-select-task))
+    (let ((start-time (if (or org-clock-continuously (equal arg '(16)))
+			  (or org-clock-out-time (current-time))
+			(current-time))))
+      (org-clock-clock-in (list (car org-clock-history)) nil start-time)
+      (message "Now clocking in: %s (in %s)"
+	       org-clock-current-task
+	       (buffer-name (marker-buffer org-clock-marker))))))
 
 
 (defun org-clock-mark-default-task ()
 (defun org-clock-mark-default-task ()
   "Mark current task as default task."
   "Mark current task as default task."
@@ -1377,9 +1399,10 @@ line and position cursor in that line."
 	    (and (re-search-forward org-property-end-re nil t)
 	    (and (re-search-forward org-property-end-re nil t)
 		 (goto-char (match-beginning 0))))))))
 		 (goto-char (match-beginning 0))))))))
 
 
+(defvar org-clock-out-time nil) ; store the time of the last clock-out
 (defun org-clock-out (&optional fail-quietly at-time)
 (defun org-clock-out (&optional fail-quietly at-time)
   "Stop the currently running clock.
   "Stop the currently running clock.
-If there is no running clock, throw an error, unless FAIL-QUIETLY is set."
+Throw an error if there is no running clock and FAIL-QUIETLY is nil."
   (interactive)
   (interactive)
   (catch 'exit
   (catch 'exit
     (when (not (org-clocking-p))
     (when (not (org-clocking-p))
@@ -1388,7 +1411,8 @@ If there is no running clock, throw an error, unless FAIL-QUIETLY is set."
       (setq frame-title-format org-frame-title-format-backup)
       (setq frame-title-format org-frame-title-format-backup)
       (force-mode-line-update)
       (force-mode-line-update)
       (if fail-quietly (throw 'exit t) (error "No active clock")))
       (if fail-quietly (throw 'exit t) (error "No active clock")))
-    (let (ts te s h m remove)
+    (let ((now (current-time)) ts te s h m remove)
+      (setq org-clock-out-time now)
       (save-excursion ; Do not replace this with `with-current-buffer'.
       (save-excursion ; Do not replace this with `with-current-buffer'.
 	(org-no-warnings (set-buffer (org-clocking-buffer)))
 	(org-no-warnings (set-buffer (org-clocking-buffer)))
 	(save-restriction
 	(save-restriction
@@ -1402,8 +1426,7 @@ If there is no running clock, throw an error, unless FAIL-QUIETLY is set."
 	  (goto-char (match-end 0))
 	  (goto-char (match-end 0))
 	  (delete-region (point) (point-at-eol))
 	  (delete-region (point) (point-at-eol))
 	  (insert "--")
 	  (insert "--")
-	  (setq te (org-insert-time-stamp (or at-time (current-time))
-					  'with-hm 'inactive))
+	  (setq te (org-insert-time-stamp (or at-time now) 'with-hm 'inactive))
 	  (setq s (- (org-float-time (apply 'encode-time (org-parse-time-string te)))
 	  (setq s (- (org-float-time (apply 'encode-time (org-parse-time-string te)))
 		     (org-float-time (apply 'encode-time (org-parse-time-string ts))))
 		     (org-float-time (apply 'encode-time (org-parse-time-string ts))))
 		h (floor (/ s 3600))
 		h (floor (/ s 3600))
@@ -1465,7 +1488,8 @@ If there is no running clock, throw an error, unless FAIL-QUIETLY is set."
     (when clock-drawer
     (when clock-drawer
       (save-excursion
       (save-excursion
 	(org-back-to-heading t)
 	(org-back-to-heading t)
-	(while (search-forward clock-drawer end t)
+	(while (and (< (point) end)
+		    (search-forward clock-drawer end t))
 	  (goto-char (match-beginning 0))
 	  (goto-char (match-beginning 0))
 	  (org-remove-empty-drawer-at clock-drawer (point))
 	  (org-remove-empty-drawer-at clock-drawer (point))
 	  (forward-line 1))))))
 	  (forward-line 1))))))
@@ -1536,9 +1560,12 @@ UPDOWN tells whether to change 'up or 'down."
   (save-excursion ; Do not replace this with `with-current-buffer'.
   (save-excursion ; Do not replace this with `with-current-buffer'.
     (org-no-warnings (set-buffer (org-clocking-buffer)))
     (org-no-warnings (set-buffer (org-clocking-buffer)))
     (goto-char org-clock-marker)
     (goto-char org-clock-marker)
-    (delete-region (1- (point-at-bol)) (point-at-eol))
-    ;; Just in case, remove any empty LOGBOOK left over
-    (org-remove-empty-drawer-at "LOGBOOK" (point)))
+    (if (save-excursion (move-beginning-of-line 1)
+			(looking-at (concat "^[ \t]*" org-clock-string)))
+	(progn (delete-region (1- (point-at-bol)) (point-at-eol))
+	       (org-remove-empty-drawer-at "LOGBOOK" (point)))
+      (message "Clock gone, cancel the timer anyway")
+      (sit-for 2)))
   (move-marker org-clock-marker nil)
   (move-marker org-clock-marker nil)
   (move-marker org-clock-hd-marker nil)
   (move-marker org-clock-hd-marker nil)
   (setq global-mode-string
   (setq global-mode-string

+ 1 - 0
lisp/org.el

@@ -17793,6 +17793,7 @@ BEG and END default to the buffer boundaries."
 
 
 (org-defkey org-mode-map "\C-c\C-x\C-t" 'org-toggle-time-stamp-overlays)
 (org-defkey org-mode-map "\C-c\C-x\C-t" 'org-toggle-time-stamp-overlays)
 (org-defkey org-mode-map "\C-c\C-x\C-i" 'org-clock-in)
 (org-defkey org-mode-map "\C-c\C-x\C-i" 'org-clock-in)
+(org-defkey org-mode-map "\C-c\C-x\C-I" 'org-clock-in-last)
 (org-defkey org-mode-map "\C-c\C-x\C-o" 'org-clock-out)
 (org-defkey org-mode-map "\C-c\C-x\C-o" 'org-clock-out)
 (org-defkey org-mode-map "\C-c\C-x\C-j" 'org-clock-goto)
 (org-defkey org-mode-map "\C-c\C-x\C-j" 'org-clock-goto)
 (org-defkey org-mode-map "\C-c\C-x\C-x" 'org-clock-cancel)
 (org-defkey org-mode-map "\C-c\C-x\C-x" 'org-clock-cancel)