[
  {
    "path": "README.org",
    "content": "** What it do?\n\nShow event history and command history of some or all buffers.\n\n** What it look like?\n\n[[https://github.com/lewang/command-log-mode/raw/master/screenshot1.png]]\n\n** Where it from?\n\nThis package is a fork of [[http://www.foldr.org/~michaelw/emacs/mwe-log-commands.el][mwe-log-commands.el]] by Michael Weber <michaelw@foldr.org>\n\n** How it different?\n\n1. Remove \"mwe-\" prefix, but keep credits to mwe.\n2. Make minor-mode and global minor-mode.\n3. Hosted on github so pull requests, etc are easy.\n"
  },
  {
    "path": "command-log-mode.el",
    "content": ";;; command-log-mode.el --- log keyboard commands to buffer\n\n;; homepage: https://github.com/lewang/command-log-mode\n\n;; Copyright (C) 2013 Nic Ferrier\n;; Copyright (C) 2012 Le Wang\n;; Copyright (C) 2004  Free Software Foundation, Inc.\n\n;; Author: Michael Weber <michaelw@foldr.org>\n;; Keywords: help\n;; Initial-version: <2004-10-07 11:41:28 michaelw>\n;; Time-stamp: <2004-11-06 17:08:11 michaelw>\n\n;; This file is free software; you can redistribute it and/or modify\n;; it under the terms of the GNU General Public License as published by\n;; the Free Software Foundation; either version 2, or (at your option)\n;; any later version.\n\n;; This file is distributed in the hope that it will be useful,\n;; but WITHOUT ANY WARRANTY; without even the implied warranty of\n;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n;; GNU General Public License for more details.\n\n;; You should have received a copy of the GNU General Public License\n;; along with GNU Emacs; see the file COPYING.  If not, write to\n;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,\n;; Boston, MA 02111-1307, USA.\n\n;;; Commentary:\n\n;; This add-on can be used to demo Emacs to an audience.  When\n;; activated, keystrokes get logged into a designated buffer, along\n;; with the command bound to them.\n\n;; To enable, use e.g.:\n;;\n;; (require 'command-log-mode)\n;; (add-hook 'LaTeX-mode-hook 'command-log-mode)\n;;\n;; To see the log buffer, call M-x clm/open-command-log-buffer.\n\n;; The key strokes in the log are decorated with ISO9601 timestamps on\n;; the property `:time' so if you want to convert the log for\n;; screencasting purposes you could use the time stamp as a key into\n;; the video beginning.\n\n;;; Code:\n\n(eval-when-compile (require 'cl))\n\n(defvar clm/log-text t\n  \"A non-nil setting means text will be saved to the command log.\")\n\n(defvar clm/log-repeat nil\n  \"A nil setting means repetitions of the same command are merged into the single log line.\")\n\n(defvar clm/recent-history-string \"\"\n  \"This string will hold recently typed text.\")\n\n(defun clm/recent-history ()\n  (setq clm/recent-history-string\n\t(concat clm/recent-history-string\n\t\t(buffer-substring-no-properties (- (point) 1) (point)))))\n\n(add-hook 'post-self-insert-hook 'clm/recent-history)\n\n(defun clm/zap-recent-history ()\n  (unless (or (member this-original-command\n\t\t      clm/log-command-exceptions*)\n\t      (eq this-original-command #'self-insert-command))\n    (setq clm/recent-history-string \"\")))\n\n(add-hook 'post-command-hook 'clm/zap-recent-history)\n\n(defvar clm/time-string \"%Y-%m-%dT%H:%M:%S\"\n  \"The string sent to `format-time-string' when command time is logged.\")\n\n(defvar clm/logging-dir \"~/log/\"\n  \"Directory in which to store files containing logged commands.\")\n\n(defvar clm/log-command-exceptions*\n  '(nil self-insert-command backward-char forward-char\n        delete-char delete-backward-char backward-delete-char\n        backward-delete-char-untabify\n        universal-argument universal-argument-other-key\n        universal-argument-minus universal-argument-more\n        beginning-of-line end-of-line recenter\n        move-end-of-line move-beginning-of-line\n        handle-switch-frame\n        newline previous-line next-line)\n  \"A list commands which should not be logged, despite logging being enabled.\nFrequently used non-interesting commands (like cursor movements) should be put here.\")\n\n(defvar clm/command-log-buffer nil\n  \"Reference of the currenly used buffer to display logged commands.\")\n(defvar clm/command-repetitions 0\n  \"Count of how often the last keyboard commands has been repeated.\")\n(defvar clm/last-keyboard-command nil\n  \"Last logged keyboard command.\")\n\n\n(defvar clm/log-command-indentation 11\n  \"*Indentation of commands in command log buffer.\")\n\n(defgroup command-log nil\n  \"Customization for the command log.\")\n\n(defcustom command-log-mode-auto-show nil\n  \"Show the command-log window or frame automatically.\"\n  :group 'command-log\n  :type 'boolean)\n\n(defcustom command-log-mode-window-size 40\n  \"The size of the command-log window.\"\n  :group 'command-log\n  :type 'integer)\n\n(defcustom command-log-mode-window-font-size 2\n  \"The font-size of the command-log window.\"\n  :group 'command-log\n  :type 'integer)\n\n(defcustom command-log-mode-key-binding-open-log \"C-c o\"\n  \"The key binding used to toggle the log window.\"\n  :group 'command-log\n  :type '(radio\n          (const :tag \"No key\" nil)\n          (key-sequence \"C-c o\"))) ;; this is not right though it works for kbd\n\n(defcustom command-log-mode-open-log-turns-on-mode nil\n  \"Does opening the command log turn on the mode?\"\n  :group 'command-log\n  :type 'boolean)\n\n(defcustom command-log-mode-is-global nil\n  \"Does turning on command-log-mode happen globally?\"\n  :group 'command-log\n  :type 'boolean)\n\n;;;###autoload\n(define-minor-mode command-log-mode\n  \"Toggle keyboard command logging.\"\n  :init-value nil\n  :lighter \" command-log\"\n  :keymap nil\n  (if command-log-mode\n      (when (and\n             command-log-mode-auto-show\n             (not (get-buffer-window clm/command-log-buffer)))\n        (clm/open-command-log-buffer))\n      ;; We can close the window though\n      (clm/close-command-log-buffer)))\n\n(define-global-minor-mode global-command-log-mode command-log-mode\n  command-log-mode)\n\n(defun clm/buffer-log-command-p (cmd &optional buffer)\n  \"Determines whether keyboard command CMD should be logged.\nIf non-nil, BUFFER specifies the buffer used to determine whether CMD should be logged.\nIf BUFFER is nil, the current buffer is assumed.\"\n  (let ((val (if buffer\n\t\t (buffer-local-value command-log-mode buffer)\n\t       command-log-mode)))\n    (and (not (null val))\n\t (null (member cmd clm/log-command-exceptions*)))))\n\n(defmacro clm/save-command-environment (&rest body)\n  (declare (indent 0))\n  `(let ((deactivate-mark nil) ; do not deactivate mark in transient\n                                        ; mark mode\n\t ;; do not let random commands scribble over\n\t ;; {THIS,LAST}-COMMAND\n\t (this-command this-command)\n\t (last-command last-command))\n     ,@body))\n\n(defun clm/open-command-log-buffer (&optional arg)\n  \"Opens (and creates, if non-existant) a buffer used for logging keyboard commands.\nIf ARG is Non-nil, the existing command log buffer is cleared.\"\n  (interactive \"P\")\n  (with-current-buffer \n      (setq clm/command-log-buffer\n            (get-buffer-create \" *command-log*\"))\n    (text-scale-set 1))\n  (when arg\n    (with-current-buffer clm/command-log-buffer\n      (erase-buffer)))\n  (let ((new-win (split-window-horizontally\n                  (- 0 command-log-mode-window-size))))\n    (set-window-buffer new-win clm/command-log-buffer)\n    (set-window-dedicated-p new-win t)))\n\n(defun clm/close-command-log-buffer ()\n  \"Close the command log window.\"\n  (interactive)\n  (with-current-buffer\n      (setq clm/command-log-buffer\n            (get-buffer-create \" *command-log*\"))\n    (let ((win (get-buffer-window (current-buffer))))\n      (when (windowp win)\n        (delete-window win)))))\n\n;;;###autoload\n(defun clm/toggle-command-log-buffer (&optional arg)\n  \"Toggle the command log showing or not.\"\n  (interactive \"P\")\n  (when (and command-log-mode-open-log-turns-on-mode\n             (not command-log-mode))\n    (if command-log-mode-is-global\n        (global-command-log-mode)\n        (command-log-mode)))\n  (with-current-buffer\n      (setq clm/command-log-buffer\n            (get-buffer-create \" *command-log*\"))\n    (let ((win (get-buffer-window (current-buffer))))\n      (if (windowp win)\n          (clm/close-command-log-buffer)\n          ;; Else open the window\n          (clm/open-command-log-buffer arg)))))\n\n(defun clm/scroll-buffer-window (buffer &optional move-fn)\n  \"Updates `point' of windows containing BUFFER according to MOVE-FN.\nIf non-nil, MOVE-FN is called on every window which displays BUFFER.\nIf nil, MOVE-FN defaults to scrolling to the bottom, making the last line visible.\n\nScrolling up can be accomplished with:\n\\(clm/scroll-buffer-window buf (lambda () (goto-char (point-min))))\n\"\n  (let ((selected (selected-window))\n\t(point-mover (or move-fn\n\t\t\t (function (lambda () (goto-char (point-max)))))))\n    (walk-windows (function (lambda (window)\n\t\t\t      (when (eq (window-buffer window) buffer)\n\t\t\t\t(select-window window)\n\t\t\t\t(funcall point-mover)\n\t\t\t\t(select-window selected))))\n\t\t  nil t)))\n\n(defmacro clm/with-command-log-buffer (&rest body)\n  (declare (indent 0))\n  `(when (and (not (null clm/command-log-buffer))\n\t      (buffer-name clm/command-log-buffer))\n     (with-current-buffer clm/command-log-buffer\n       ,@body)))\n\n(defun clm/log-command (&optional cmd)\n  \"Hook into `pre-command-hook' to intercept command activation.\"\n  (clm/save-command-environment\n    (setq cmd (or cmd this-command))\n    (when (clm/buffer-log-command-p cmd)\n      (clm/with-command-log-buffer\n        (let ((current (current-buffer)))\n          (goto-char (point-max))\n          (cond ((and (not clm/log-repeat) (eq cmd clm/last-keyboard-command))\n                 (incf clm/command-repetitions)\n                 (save-match-data\n                   (when (and (> clm/command-repetitions 1)\n                              (search-backward \"[\" (line-beginning-position -1) t))\n                     (delete-region (point) (line-end-position))))\n                 (backward-char) ; skip over either ?\\newline or ?\\space before ?\\[\n                 (insert \" [\")\n                 (princ (1+ clm/command-repetitions) current)\n                 (insert \" times]\"))\n                (t ;; (message \"last cmd: %s cur: %s\" last-command cmd)\n                 ;; showing accumulated text with interleaved key presses isn't very useful\n\t\t (when (and clm/log-text (not clm/log-repeat))\n\t\t   (if (eq clm/last-keyboard-command 'self-insert-command)\n\t\t       (insert \"[text: \" clm/recent-history-string \"]\\n\")))\n                 (setq clm/command-repetitions 0)\n                 (insert\n                  (propertize\n                   (key-description (this-command-keys))\n                   :time  (format-time-string clm/time-string (current-time))))\n                 (when (>= (current-column) clm/log-command-indentation)\n                   (newline))\n                 (move-to-column clm/log-command-indentation t)\n                 (princ (if (byte-code-function-p cmd) \"<bytecode>\" cmd) current)\n                 (newline)\n                 (setq clm/last-keyboard-command cmd)))\n          (clm/scroll-buffer-window current))))))\n\n(defun clm/command-log-clear ()\n  \"Clear the command log buffer.\"\n  (interactive)\n  (with-current-buffer clm/command-log-buffer\n    (erase-buffer)))\n\n(defun clm/save-log-line (start end)\n  \"Helper function for `clm/save-command-log' to export text properties.\"\n  (save-excursion\n    (goto-char start)\n    (let ((time (get-text-property (point) :time)))\n      (if time\n\t  (list (cons start (if time \n\t\t\t\t(concat \"[\" (get-text-property (point) :time) \"] \")\n\t\t\t      \"\")))))))\n\n(defun clm/save-command-log ()\n  \"Save commands to today's log.\nClears the command log buffer after saving.\"\n  (interactive)\n  (save-window-excursion\n    (set-buffer (get-buffer \" *command-log*\"))\n    (goto-char (point-min))\n    (let ((now (format-time-string \"%Y-%m-%d\"))\n\t  (write-region-annotate-functions '(clm/save-log-line)))\n      (while (and (re-search-forward \"^.*\" nil t)\n\t\t  (not (eobp)))\n\t(append-to-file (line-beginning-position) (1+ (line-end-position)) (concat clm/logging-dir now))))\n    (clm/command-log-clear)))\n\n(add-hook 'pre-command-hook 'clm/log-command)\n\n(eval-after-load 'command-log-mode\n  '(when command-log-mode-key-binding-open-log\n    (global-set-key\n     (kbd command-log-mode-key-binding-open-log)\n     'clm/toggle-command-log-buffer)))\n\n(provide 'command-log-mode)\n\n;;; command-log-mode.el ends here\n"
  }
]