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 8 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
 @orgcmd{C-c C-x C-i,org-clock-in}
 @vindex org-clock-into-drawer
+@vindex org-clock-continuously
 @cindex property, LOG_INTO_DRAWER
 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
@@ -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.
 When called with a @kbd{C-u} prefix argument,
 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: LAST_REPEAT
 @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
 timestamp@footnote{The corresponding in-buffer setting is:
 @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}
 Update the effort estimate for the current clock task.
 @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
 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
 @subsection The clock table
 @cindex clocktable, dynamic block
@@ -6225,7 +6237,9 @@ would be
 @end example
 
 @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 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
 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
 @section Effort estimates
 @cindex effort estimates

+ 43 - 16
lisp/org-clock.el

@@ -325,6 +325,12 @@ play with them."
   :version "24.1"
   :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*"
   "Format string for the total time cells."
   :group 'org-clock
@@ -1054,7 +1060,10 @@ recently clocked tasks to
 clock into.  When SELECT is \\[universal-argument] \\[universal-argument], \
 clock into the current task and mark
 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")
   (setq org-clock-notification-was-shown nil)
   (catch 'abort
@@ -1073,6 +1082,11 @@ the clocking selection, associated with the letter `d'."
 	(let ((org-clock-clocking-in t))
 	  (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))
 	(setq selected-task (org-clock-select-task "Clock-in on 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
 					  (org-clock-get-sum-start)))
 	      (setq org-clock-start-time
-		    (or (and leftover
+		    (or (and org-clock-continuously org-clock-out-time)
+			(and leftover
 			     (y-or-n-p
 			      (format
 			       "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
 (defun org-clock-in-last (&optional arg)
   "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")
-  (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 ()
   "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)
 		 (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)
   "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)
   (catch 'exit
     (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)
       (force-mode-line-update)
       (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'.
 	(org-no-warnings (set-buffer (org-clocking-buffer)))
 	(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))
 	  (delete-region (point) (point-at-eol))
 	  (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)))
 		     (org-float-time (apply 'encode-time (org-parse-time-string ts))))
 		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
       (save-excursion
 	(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))
 	  (org-remove-empty-drawer-at clock-drawer (point))
 	  (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'.
     (org-no-warnings (set-buffer (org-clocking-buffer)))
     (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-hd-marker nil)
   (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-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-j" 'org-clock-goto)
 (org-defkey org-mode-map "\C-c\C-x\C-x" 'org-clock-cancel)