;;; third-timer.el --- Timer ensuring break time = work time / 3 ;; Copyright (C) 2022 Vasilij Schneidermann ;; Author: Vasilij Schneidermann ;; URL: https://depp.brause.cc/dotemacs/unpublished/third-timer.el ;; Version: 0.0.1 ;; Package-Requires: ((emacs "24.3")) ;; Keywords: languages ;; This file is NOT part of GNU Emacs. ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; A simple implementation of "third time" as described on ;; https://www.lesswrong.com/posts/RWu8eZqbwgB9zaerh/third-time-a-better-way-to-work. ;;; Code: (defgroup third-timer nil "Easy third timer time keeping" :group 'calendar :prefix "third-timer-") (defvar third-timer-buffer "*third timer*") (defvar third-timer--timer nil) (defvar third-timer-elapsed-time nil "Amount of time elapsed between start/stop commands.") (make-variable-buffer-local 'third-timer-elapsed-time) (defvar third-timer-spent-time nil "Amount of time spent resting (as opposed to working).") (make-variable-buffer-local 'third-timer-spent-time) (defvar third-timer-working-p t) (make-variable-buffer-local 'third-timer-working-p) (defun third-timer-format-time (seconds) (let ((sign (if (< seconds 0) "-" " "))) (concat sign (format-seconds "%02h:%02m:%02s" (abs seconds))))) ;; FIXME: deal with clock drift by keeping an initial timestamp around ;; instead of incrementing total time ;; NOTE: several (UTC) timestamps would be necessary to do the same ;; for spent time (or coalescing?) (defun third-timer-update-buffer () (setq third-timer-elapsed-time (1+ third-timer-elapsed-time)) (when (not third-timer-working-p) (setq third-timer-spent-time (1+ third-timer-spent-time))) ;; NOTE: scenario: ;; work 60s ;; 60s total, 60s work, 20s rest, 0s spent ;; rest 10s ;; 70s total, 60s work, 10s rest, 10s spent ;; work 60s ;; 130s total, 120s work, 30s rest, 10s spent ;; rest 20s ;; 150s total, 120s work, 10s rest, 30s spent (let* ((buffer-read-only) (total third-timer-elapsed-time) (spent third-timer-spent-time) (work (- total spent)) (rest (round (- (/ work 3) spent)))) (erase-buffer) (insert "Total time: " (third-timer-format-time total) "\n") (insert "Work time: " (third-timer-format-time work) "\n") (insert "Rest time: " (third-timer-format-time rest) "\n") (insert "Spent time: " (third-timer-format-time spent) "\n"))) (defun third-timer-toggle-start-stop () (interactive) (if third-timer--timer (progn (message "Stop") (cancel-timer third-timer--timer) (setq third-timer--timer nil)) (message "Start") (setq third-timer--timer (run-at-time nil 1 #'third-timer-update-buffer)))) (defun third-timer-toggle-work-rest () (interactive) (if third-timer-working-p (message "Rest") (message "Work")) (setq third-timer-working-p (not third-timer-working-p))) (defvar third-timer-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "s") #'third-timer-toggle-start-stop) (define-key map (kbd "SPC") #'third-timer-toggle-work-rest) map)) (define-derived-mode third-timer-mode special-mode "Third Timer") (defun third-timer () (interactive) (with-current-buffer (get-buffer-create third-timer-buffer) (let (buffer-read-only) (erase-buffer) (insert (substitute-command-keys "Press \\[third-timer-start-stop] to start tracking work time")) (third-timer-mode) (setq third-timer-elapsed-time 0) (setq third-timer-spent-time 0))) (pop-to-buffer third-timer-buffer)) (provide 'third-timer) ;;; third-timer.el ends here