Say if I’ve a buffer, is there a package or hack to track time that’s spent typing/editing on this buffer (ignoring idle time)?
Depending on how automatic you want it, it may be difficult. You could consider using the clock-in/-out mechanism for org-mode, and that will let you track the time, and can handle idle time.
You may even be able to write a function for a hook that automatically clocks in/out when you switch buffers.
The minor mode
buftime-mode defined below measures the active buffer time. By active buffer time I mean the time you are running commands interactively in the buffer.
The modeline displays the active buffer time in seconds as minor mode lighter in angular braces.
For an instance if you stop typing to overthink what you have typed that is work-time but it is not included in the active buffer time.
(require 'type-break) ;; for `timep' (defvar-local buftime-start nil "Time at which `buftime-timer' started. The timer is started in `pre-command-hook'.") (defvar-local buftime-time nil "Time during which the current buffer was active in seconds.") (defvar-local buftime-timer nil "Timer stopping measurement of the active buffer time.") (defvar-local buftime-cancelled-timer nil "Timer cons returned by `timer-cancel'.") (defcustom buftime-idle-time 1 "Idle time before `buftime-timer' fires." :type 'number :group 'buftime) (defvar buftime-string "" "String showing the active time in current buffer.") (defun buftime-update-string () "Update `buftime-string' for `global-mode-string'." (if (timep buftime-time) (setq buftime-string (format-time-string "<%s>" buftime-time)) (setq buftime-string "")) (force-mode-line-update)) (defun buftime-start () "Start `buftime-timer' measuring active buffer time. Use this function for `pre-command-hook'." (unless (eq this-command 'ignore) (unless buftime-timer (setq buftime-timer (timer-create)) (timer-set-function buftime-timer #'buftime-stop)) (let ((time (current-time))) (timer-set-time buftime-timer (timer-relative-time time buftime-idle-time)) (unless (memq buftime-timer timer-list) (timer-activate buftime-timer)) (unless buftime-start (setq buftime-start time))) (buftime-update-string))) (defun buftime-stop () "Timer function for `buftime-timer'. Stops measurement for active buffer time." (when (timep buftime-start) (let ((timediff (time-subtract (current-time) buftime-start))) (setq buftime-time (if (timep buftime-time) (time-add buftime-time timediff) timediff) buftime-start nil) ;; We do not need to cancel the timer since it does not repeat. ) (buftime-update-string))) (define-minor-mode buftime-mode "Minor mode for measuring the active buffer time. Active buffer time starts with any command running with the buffer being active. It starts after the buffer was idle for buftime-idle-time seconds." :lighter (" " buftime-string) (if buftime-mode (add-hook 'pre-command-hook #'buftime-start nil t) (remove-hook 'pre-command-hook #'buftime-start t)))
The stuff is new and only coarsely tested with Emacs 26.1.