diff --git a/.github/flake.lock b/.github/flake.lock index a033b73..0c87c29 100644 --- a/.github/flake.lock +++ b/.github/flake.lock @@ -7,14 +7,15 @@ ], "nixpkgs": [ "nixpkgs" - ] + ], + "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1670637348, - "narHash": "sha256-4FLOEi02WS+St6i1MSUxGfA32FL1SFIpwWCDsABAZkk=", + "lastModified": 1700039860, + "narHash": "sha256-khtUI3bCyjaXTviQBhd0m991Tj/GWDB6c535LdCin1I=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "3a0b5dd7756173e63c2bbbe70dd5484a7463257d", + "rev": "accc684eb6fceac1b504fc6bec7298263dae3f86", "type": "github" }, "original": { @@ -40,11 +41,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1670482022, - "narHash": "sha256-xzzjWulnNMCSTEGosGaAg7sLsAtF7stA3sQljZ410/w=", + "lastModified": 1688392541, + "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c50e5b63e9d65ff0e70cc06b7042a72c6a6583bc", + "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", "type": "github" }, "original": { @@ -54,6 +55,22 @@ "type": "github" } }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1699596684, + "narHash": "sha256-XSXP8zjBZJBVvpNb2WmY0eW8O2ce+sVyj1T0/iBRIvg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "da4024d0ead5d7820f6bd15147d3fe2a0c0cec73", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "emacs-overlay": "emacs-overlay", diff --git a/.github/flake.nix b/.github/flake.nix index 121ba0e..bd51f18 100644 --- a/.github/flake.nix +++ b/.github/flake.nix @@ -21,7 +21,7 @@ description = "This flake provides CI & local development dependencies"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs?ref=release-22.11"; + nixpkgs.url = "github:nixos/nixpkgs?ref=release-23.05"; flake-utils.url = "github:numtide/flake-utils"; emacs-overlay = { url = "github:nix-community/emacs-overlay"; @@ -42,7 +42,7 @@ emacsPackages = [ "emacs" - "emacsGit" # to see changes in upstreams + "emacs29" # to see changes in upstreams ]; devShells = pkgs.lib.genAttrs emacsPackages (emacsPkg: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 913bb9b..97e981d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest ] - emacsPkg: [ emacs, emacsGit ] + emacsPkg: [ emacs, emacs29 ] runs-on: ${{ matrix.os }} steps: @@ -58,10 +58,10 @@ jobs: # Linting the package is less useful on multiple version. Modify the # logic if you wish to expand lint coverage. - name: lint package - if: matrix.emacsPkg == 'emacs' + if: matrix.emacsPkg == 'emacs29' run: | eval "$(nix print-dev-env \ - --override-input nixpkgs github:nixos/nixpkgs/release-22.11 \ + --override-input nixpkgs github:nixos/nixpkgs/release-23.05 \ --update-input emacs-overlay \ .github#${{ matrix.emacsPkg }})" @@ -71,7 +71,7 @@ jobs: - name: load package run: | eval "$(nix print-dev-env \ - --override-input nixpkgs github:nixos/nixpkgs/release-22.11 \ + --override-input nixpkgs github:nixos/nixpkgs/release-23.05 \ --update-input emacs-overlay \ .github#${{ matrix.emacsPkg }})" diff --git a/README.org b/README.org index 8bcaadf..7c70c2e 100644 --- a/README.org +++ b/README.org @@ -4,10 +4,10 @@ #+HTML: CI workflow status #+HTML: DCO Check -Please see the [[https://github.com/tarsius/keycast/tree/master][keycast]] package, which has options to display in a posframe, -headline, or modeline. It's a pretty well written package. This package was -started to clean up the [[https://github.com/lewang/command-log-mode][command-log-mode]] package that is still ranking highly in -a lot of search results. +Please see the [[https://github.com/tarsius/keycast/tree/master][keycast]] and [[https://github.com/chuntaro/emacs-keypression][keypression]] package, which has options to display in +a posframe, headline, or modeline. It's a pretty well written package. This +package was forked to clean up the [[https://github.com/lewang/command-log-mode][command-log-mode]] package that is still +ranking highly in a lot of search results. ** What it do? @@ -28,17 +28,16 @@ a lot of search results. should support pointing at specific repositories) to use this repo. #+begin_src elisp + ;; using elpaca (recommended to add a hash for reproducibility) + (use-package + :elpaca (command-log + :host github + :repo "positron-solutions/command-log")) ;; using straight use-package with custom recipe (use-package command-log :straight '(command-log :type git :host github :repo "positron-solutions/command-log")) - - ;; using elpaca (recommended to add a hash for reproducibility) - (elpaca-use-package - (command-log :host github - :repo "positron-solutions/command-log")) - #+end_src ** How do I use it? @@ -49,21 +48,27 @@ a lot of search results. Customize the =command-log= group for more options. #+begin_src elisp - (use-package command-log - :custom - (command-log-window-text-scale 2 "Command log two steps higher text scale") - (command-log-logging-shows-buffer t "Toggling will show the buffer.") - (command-log-hiding-disables-logging t "Toggling visible buffer turns off logging.") - (command-log-disabling-logging-kills-buffer t "The buffer will be new when displayed again.") - (command-log-log-globally t "Auto-enable with global minor mode (including minibuffer)") - (command-log-filter-commands '(self-insert-command) "Be chatty. - Show everything besides self-insert-command")) - + :config + (setopt command-log-filter-commands '(self-insert-command) ; commands you don't care about + (setopt command-log-mouse t) + (setopt command-log-text t) ; print strings rather than streams of `self-insert-command'. + + (setopt command-log-merge-repeats t) ; show repeat counts without making new entries + ;; This following option and some related options can be powerful for + ;; revealing how commands are delegating out to other commands, such as M-x + ;; and ivy. Read the docs ;-) + (setopt command-log-merge-repeat-targets 'post-command) #+end_src - You can reveal all commands by running [M-x] - =command-log-toggle-show-all-commands=. +*** Unveiling More Details + + You can reveal all commands temporarily by running [M-x] + ~command-log-toggle-show-all~. It overrides several behaviors to try to reveal + more. + + There is also ~command-log-tail-dribble~ if you need to see your inputs for + debugging. Don't forget about simple ~view-lossage~ and refreshing with =g=. Commands to toggle buffer showing and to turn everything off without doing it on every toggle are sorely needed. PR's welcome! @@ -71,7 +76,7 @@ a lot of search results. ** License All post-fork work is GPL licensed. This increasingly covers most of the - package. The CI is MIT licensed for ease of use. See [[./CONTRIBUTING.org][CONTRIBUTING.org]] for + package. The CI is MIT licensed for convenience. See [[./CONTRIBUTING.org][CONTRIBUTING.org]] for information about submitting changes correctly. This package is a fork of [[http://www.foldr.org/~michaelw/emacs/mwe-log-commands.el][mwe-log-commands.el]] by Michael Weber diff --git a/command-log.el b/command-log.el index 6974645..de82e6d 100644 --- a/command-log.el +++ b/command-log.el @@ -1,19 +1,21 @@ ;;; command-log.el --- Log user inputs & commands -*- lexical-binding: t -*- -;; homepage: https://github.com/positron-solutons/command-log - -;; Copyright (C) 2022 Positron Solutions +;; Copyright (C) 2022-2023 Positron Solutions ;; Copyright (C) 2013 Nic Ferrier ;; Copyright (C) 2012 Le Wang ;; Copyright (C) 2004 Free Software Foundation, Inc. -;; Author: Michael Weber -;; Keywords: help docs -;; Version: 0.1.0 +;; Author: Michael Weber +;; Maintainer: Positron Solutions +;; Keywords: help, docs +;; Version: 0.2.0 ;; Package-Requires: ((emacs "28.0")) +;; homepage: https://github.com/positron-solutons/command-log ;; Initial-version: <2004-10-07 11:41:28 michaelw> ;; Time-stamp: <2004-11-06 17:08:11 michaelw> +;; This file is not part of GNU Emacs. + ;; This program 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 of the License, or (at your option) any later @@ -30,7 +32,7 @@ ;;; Commentary: ;; This package is an updated fork of `command-log-mode'. Also see the -;; `keycast' package. +;; `keycast' and `keypression' packages. ;; This add-on can be used to demo Emacs to an audience. When activated, ;; keystrokes get logged into a designated buffer, along with the command bound @@ -48,6 +50,7 @@ ;;; Code: +(require 'autorevert) (require 'cl-lib) (require 'comint) (require 'button) @@ -61,97 +64,272 @@ :prefix 'command-log :group 'convenience) -(defcustom command-log-window-size 40 - "The size of the command-log window." - :group 'command-log - :type 'integer) - (defcustom command-log-default-side 'right "Which side for use in `display-buffer-in-side-window'." :group 'command-log :type 'symbol :options '(right left top bottom)) -(defcustom command-log-window-text-scale 0 - "The text scale of the command-log window. +(define-obsolete-variable-alias 'command-log-window-text-scale + 'command-log-text-scale "0.2.0") + +(define-obsolete-variable-alias 'command-log-mode-window-font-size + 'command-log-text-scale "0.2.0") + +(defcustom command-log-text-scale 0 + "The text scale of the command-log output. +1,+2,... increase and -1,-2,... decrease the font size." :group 'command-log :type 'integer) -(defcustom command-log-log-command-indentation 11 - "Indentation of commands in command log buffer." +(defcustom command-log-repeat-format " #%s" + "How to display repeats." + :group 'command-log + :type 'string) + +(defcustom command-log-keys-min-width 8 + "How wide to make keys. +One way to help indentation. Default will align up to two chords +with a trailing space." :group 'command-log :type 'integer) +(defcustom command-log-text-format "\"%s\"" + "How to display text. +Only applies when `command-log-text' is non-nil." + :group 'command-log + :type 'string) + +(defcustom command-log-text-space "␣" + "How to draw spaces in text." + :group 'command-log + :type 'string) + (defface command-log-key-face '((t :inherit 'font-lock-keyword-face)) "Face for keys in command log." :group 'command-log) (defface command-log-command-face - '((t :inherit font-lock-doc-markup-face)) + '((t :inherit font-lock-function-name-face)) "Face for commands in command log." :group 'command-log) (defface command-log-repeat-face - '((t :inherit 'font-lock-doc-face)) + '((t :inherit 'shadow)) "Face for commands in command log." :group 'command-log) +(defface command-log-text-face + '((t :inherit 'font-lock-string-face)) + "Face for text echo'ing in the command log." + :group 'command-log) + +(define-obsolete-variable-alias 'clm/time-string + 'command-log-time-string "0.2.0") + (defcustom command-log-time-string "%Y-%m-%dT%H:%M:%S" - "The string sent to `format-time-string' when command time is logged." + "Sent to `format-time-string' if time logging enabled." + :group 'command-log + :type 'string) + +(defcustom command-log-max-log-lines 256 + "Set higher if you need to save long sessions." :group 'command-log :type 'string) -(defcustom command-log-logging-shows-buffer t +(define-obsolete-variable-alias 'command-log-mode-auto-show + 'command-log-enable-shows "0.2.0") + +(defcustom command-log-enable-shows t "Turning on logging shows the buffer if it's not visible." :group 'command-log :type 'boolean) -(defcustom command-log-hiding-disables-logging t +(define-obsolete-variable-alias 'command-log-hiding-disables-logging + 'command-log-hide-disables "0.2.0") + +(defcustom command-log-hide-disables t "Hiding the buffer deactivates logging modes." :group 'command-log :type 'boolean) -(defcustom command-log-disabling-logging-kills-buffer t +(define-obsolete-variable-alias 'command-log-disabling-logging-kills-buffer + 'command-log-disable-kills "0.2.0") + +(defcustom command-log-disable-kills t "Turning off all logging kills the buffer." :group 'command-log :type 'boolean) -(defcustom command-log-log-globally t - "Does turning on `command-log-mode' happen globally?" +(define-obsolete-variable-alias 'command-log-log-globally + 'command-log-prefer-global "0.2.0") + +(defcustom command-log-prefer-global t + "When logging is enabled automatically, is it global?" :group 'command-log :type 'boolean) -(defcustom command-log-filter-commands + +(define-obsolete-variable-alias 'clm/log-command-exceptions* + 'command-log-ignored "0.2.0") + +(define-obsolete-variable-alias 'command-log-filter-commands + 'command-log-ignored "0.2.0") + +(defcustom command-log-ignored '(self-insert-command handle-switch-frame) - "A list commands which should not be logged, despite logging being enabled. -Frequently used non-interesting commands (like cursor movements) -should be put here." + "A list commands which should be ignored. +For `self-insert-command', logging text overrides this." :group 'command-log :type '(repeat (symbol :tag "command function name"))) -(defcustom command-log-log-text nil + +(define-obsolete-variable-alias 'command-log-log-text 'command-log-text "0.2.0") + +(define-obsolete-variable-alias 'clm/log-text 'command-log-text "0.2.0") + +(defcustom command-log-text nil "Log text as strings instead of `self-insert-commands'. You may want to just except `self-insert-command' by adding it to `command-log-filter-commands'." :group 'command-log :type 'boolean) -(defcustom command-log-log-mouse nil +(define-obsolete-variable-alias 'command-log-log-mouse 'command-log-muose + "0.2.0") + +(defcustom command-log-mouse nil "Log mouse events. Toggling this is more conveneint than setting `command-log-ignored-commands'." :group 'command-log :type 'boolean) +(define-obsolete-variable-alias 'clm/log-repeat 'command-log-merge-repeats + "0.2.0") + (defcustom command-log-merge-repeats t "Merge repetitions of the same command." :group 'command-log :type 'boolean) -(defcustom command-log-logging-dir (locate-user-emacs-file - "var/command-log-mode/") +(defcustom command-log-pre-command-target 'this-command + "Symbol to report during the pre-command. +When non-nil, the symbol will be read during the +`pre-command-hook'. Normally we are interested in +`this-command', but special debugging circumstances may be +interested in `last-repeatable-command' or `this-real-command' +etc. Any Lisp variable that is always bound will work, but could +break features such as text logging, which watch for +`self-insert-command' in the post-command. + +Experience shows that many commands which delegate out to other +commands, such `ivy-done' or lispy commands, will have the effect +of resetting `this-command' and `this-real-command' both between +the pre & post command hook. In the pre-command hook we see the +command that was called, but for commands such as \`M-x', it' is +potentially not interesting to see the pre-command `this-command' +value, such as `ivy-done', the result of pressing \`RET' in an +\`M-x' menu. + +The following is a log output of all likely events you will want +to log during the pre and post command: + +;; pre-command-hook: +;; this-command: ivy-done +;; real-this-command: ivy-done +;; real-last-command: self-insert-command +;; last-repeatable-command: self-insert-command +;; +;; post-command-hook: +;; this-command: next-line +;; real-this-command: next-line +;; real-last-command: self-insert-command +;; last-repeatable-command: self-insert-command + +Note: `self-insert-command' was the actual command that preceded +pressing \`RET' to cause an `ivy-done' command. +`real-last-command' is not terribly interesting. It does not +change between the pre & post command hooks in any observed +cases. + +However, also note that `real-this-command' was modified between +pre & post-command hooks. This contradicts the semantic meaning +and docstring of `real-this-command'. Practically, we don't care +because we caught the change via the `pre-command-hook' rather +than blindly trusting the words of the `real-last-command' +docstring author. We obtain a useful result, and that is what +matters. + +\`M-x' is known to not update `this-command' in the case of +counsel's \`M-x' command. + +When developing a package, you may be very interested in +different values pre and post command. It is very easy to modify +the point that values are recorded to fit your use case. See the +body of `command-log--log-pre-command' and +`command-log--log-post-command' and submit a patch if you find a +use case for making these support function calls for example. + +See `command-log-merge-targets' information about output +formatting." + :group 'command-log + :type 'symbol) + +(defcustom command-log-post-command-target 'this-command + "Symbol to report during the post-command. +When non-nil, the symbol will be read during the +`post-command-hook'. Normally we are interested in +`this-command', but special debugging circumstances may be +interested in `last-repeatable-command' or `this-real-command' +etc. Any Lisp variable that is always bound will work. + +See `command-log-pre-command-target' for more information." + :group 'command-log + :type 'symbol) + +(defcustom command-log-merge-repeat-targets 'post-command + "Controls merging behavior when targets match or not. +We can prefer the `'pre-command' or the `'post-command'. Setting +to `'nil' or `'both' will not merge the values at all. The +pre-command will be first in the output. + +When `command-log-pre-command-target' and +`command-log--log-post-command' target are the same or different, +how should we behave? Since the user did not press additional +keys, there are no inputs, we will only display keys once. The +same is true for repeats, but the repeat counter will only count +a repeat if everything matches. Since the pre-command comes +before the post-command, we always show the pre-command before +the post command when both are shown. + +See `command-log-post-excepting-pre-commands' for information +about commands that will override the setting of `'post-command'." + :group 'command-log + :type 'symbol + :options '(pre-command post-command both)) + +(defcustom command-log-post-excepting-pre-commands + `(counsel-M-x + execute-extended-command + ,(command-remapping 'execute-extended-command)) ; whatever the user's remap is + "A list of commands that do not update `this-command'. +We decline to print the post-command value when the pre-command +value of `this-command' is a member of this list. It's tricky. +There isn't a great way to detect this. The only known use is +however terribly common, `execute-extended-command' and it's +re-mappings." + :group 'command-log + :type '(repeat symbol)) + +(define-obsolete-variable-alias 'command-log-logging-dir + 'command-log-save-dir "0.2.0") + +(define-obsolete-variable-alias 'clm/logging-dir 'command-log-save-dir "0.2.0") + +(defcustom command-log-save-dir (locate-user-emacs-file + "var/command-log-mode/") "Directory in which to store files containing logged commands." :group 'command-log :type 'directory) @@ -164,18 +342,42 @@ Toggling this is more conveneint than setting `command-log-ignored-commands'." (defvar command-log--command-repetitions 0 "Count of how often the last keyboard commands has been repeated.") -(defvar command-log--last-keyboard-command nil - "Last logged keyboard command.") +(defvar command-log--pre-command nil + "Command after remapping.") + +(defvar command-log--pre-command-keys nil + "Command keys after remapping.") + +(defvar command-log--last-pre-command nil + "Last logged pre command.") + +(defvar command-log--last-post-command nil + "Last logged post command.") (defvar command-log--last-command-keys nil "Last key description for `this-command-keys'.") -(defvar command-log--recent-history-string "" +(defvar command-log--repeat-start-marker nil + "Marker for updating the repeat counter.") + +(defvar command-log--repeat-end-marker nil + "Marker for updating the repeat counter.") + +(defvar command-log--self-insert-start nil + "Marker for updating sequences of `self-insert-command'.") + +(defvar command-log--self-insert-end nil + "Marker for updating sequences of `self-insert-command'.") + +(defvar command-log--self-insert-string "" "This string will hold recently typed text.") (defvar command-log--show-all-commands nil "Override `command-log-filter-commands' and show all commands instead.") +(defvar command-log--last-log-marker nil + "Saving our location so we can update repeat commands.") + (declare-function helpful-at-point "helpful" ()) (defun command-log--push-button () "Open help for command at point. @@ -204,10 +406,14 @@ Use `helpful' package if loaded." :init-value nil :lighter " command-log" :keymap nil - (if command-log-mode - (add-hook 'pre-command-hook - #'command-log--log-command 'default-depth) - (remove-hook 'pre-command-hook #'command-log--log-command))) + (cond (command-log-mode + (add-hook 'pre-command-hook + #'command-log--log-pre-command 'default-depth) + (add-hook 'post-command-hook + #'command-log--log-post-command 'default-depth)) + (t + (remove-hook 'pre-command-hook #'command-log--log-pre-command) + (remove-hook 'post-command-hook #'command-log--log-post-command)))) ;;;###autoload (define-globalized-minor-mode global-command-log-mode command-log-mode command-log-mode @@ -220,7 +426,7 @@ Use `helpful' package if loaded." The following variables are used to configure this toggle: -`command-log-log-globally' controls the preference for +`command-log-prefer-global' controls the preference for `command-log-mode` minor mode or `global-command-log-mode. `command-log-open-log-turns-on-mode' will activate modes if showing the log buffer. `command-log-close-log-turns-off-mode' @@ -232,27 +438,40 @@ Passing a prefix CLEAR will clear the buffer before display." (progn (command-log--hide-buffer) (when command-log-hiding-disables-logging - (if command-log-log-globally + (if command-log-prefer-global (global-command-log-mode nil) (command-log-mode nil)) (when command-log-disabling-logging-kills-buffer (command-log--hide-buffer t)))) (progn - (if command-log-log-globally + (if command-log-prefer-global (global-command-log-mode t) (command-log-mode t)) - (when command-log-logging-shows-buffer + (when command-log-enable-shows (command-log--show-buffer clear))))) +(declare-function 'clm/toggle-command-log-buffer "command-log-mode" ()) +(declare-function 'clm/open-command-log-buffer "command-log-mode" ()) +(define-obsolete-function-alias + 'clm/toggle-command-log-buffer #'command-log-toggle "0.2.0") +(define-obsolete-function-alias + 'clm/open-command-log-buffer #'command-log-toggle "0.2.0") + ;;;###autoload -(defun command-log-close-command-log-buffer (&optional kill) +(defun command-log-close-buffer (&optional kill) "Close the command log window. Prefix argument will KILL buffer." (interactive "P") (command-log--hide-buffer kill)) +(declare-function 'clm/close-command-log-buffer "command-log-mode" ()) +(define-obsolete-function-alias + 'clm/close-command-log-buffer #'command-log-close-buffer "0.2.0") +(define-obsolete-function-alias + 'command-log-close-command-log-buffer #'command-log-close-buffer "0.2.0") + ;;;###autoload -(defun command-log-command-log-clear () +(defun command-log-clear () "Clear the command log buffer." (interactive) (let ((buffer (command-log--get-buffer))) @@ -261,7 +480,7 @@ Prefix argument will KILL buffer." (erase-buffer))))) ;;;###autoload -(defun command-log-toggle-show-all-commands (&optional arg) +(defun command-log-toggle-show-all (&optional arg) "Override `command-log-filter-commands' and show everything. ARG can be passed for direct setting." (interactive) @@ -274,15 +493,18 @@ ARG can be passed for direct setting." (format "Show all commands: %s" command-log--show-all-commands) 'face 'success))))) +(define-obsolete-function-alias + 'command-log-toggle-show-all-commands #'command-log-toggle-show-all "0.2.0") + ;;;###autoload -(defun command-log-save-command-log () +(defun command-log-save () "Save commands to today's log. Clears the command log buffer after saving." (interactive) (let ((buffer (command-log--get-buffer))) (when buffer (with-current-buffer buffer - (make-directory command-log-logging-dir :parents) + (make-directory command-log-save-dir :parents) (goto-char (point-min)) (let ((now (format-time-string "%Y-%02m-%02d %02H:%02M:%02S")) (write-region-annotate-functions '(command-log--line-time))) @@ -290,7 +512,7 @@ Clears the command log buffer after saving." (not (eobp))) (append-to-file (line-beginning-position) (1+ (line-end-position)) - (concat command-log-logging-dir now)))) + (concat command-log-save-dir now)))) (when (y-or-n-p "Erase buffer?") (erase-buffer)))))) @@ -360,12 +582,6 @@ KILL will kill the buffer after deleting its window." (when kill (kill-buffer buffer))))) -(defun command-log--push-history () - "Push the character entered into the buffer into the recent history." - (setq command-log--recent-history-string - (concat command-log--recent-history-string - (key-description (this-command-keys))))) - (defun command-log--mouse-event-p (event) "Return t if EVENT is mouse event. Emacs `mouse-event-p' reports nil for movement." @@ -384,11 +600,11 @@ EVENT is the last input event that triggered the command." (buffer-local-value 'major-mode (current-buffer))))) (or command-log--show-all-commands (and (not in-log-buffer) - (or (if command-log-log-mouse + (or (if command-log-mouse (not filtered) (and (not mouse) (not filtered))) - (and command-log-log-text text)))))) + (and command-log-text text)))))) (defun command-log--scroll-buffer-windows () "Move `point' to end of windows containing log buffer." @@ -404,74 +620,167 @@ EVENT is the last input event that triggered the command." (when (or command-log--show-all-commands (not (member cmd command-log-filter-commands)) (not (eq cmd #'self-insert-command))) - (setq command-log--recent-history-string ""))) - -(defun command-log--log-command (&optional cmd) - "Log CMD to the command-log--buffer." + (setq command-log--self-insert-string ""))) + +(defun command-log--format-command (cmd) + "Make CMD human pretty and clickable." + (if (byte-code-function-p cmd) + (propertize "" 'face 'command-log-command-face) + (propertize (symbol-name cmd) + 'face 'command-log-command-face + 'button '(t) + 'category 'default-button))) + +(defun command-log--log-pre-command () + "Record the pre-command state. +This enables us to differentiate commands that delegate out to other commands by +reading before the command and comparing the state during the post command +hook." + (setq command-log--pre-command-keys (key-description (this-command-keys))) + (setq command-log--pre-command (symbol-value command-log-pre-command-target))) + +(defun command-log--log-post-command () + "Write the command information to the output." (let ((deactivate-mark nil) ; do not deactivate mark in transient mark mode - ;; Don't let random commands change `this-command' Emacs global - ;; variables by creating local lexical variables with their values. - (this-command this-command) (buffer (command-log--get-buffer)) - (cmd (or cmd this-command)) + (pre-cmd command-log--pre-command) + (post-cmd (symbol-value command-log-post-command-target)) (event last-command-event) - (keys (key-description (this-command-keys)))) - (when (and buffer (command-log--should-log-command-p cmd event)) + (keys command-log--pre-command-keys)) + ;; During updates, use `real-this-command' for logic in case the user + ;; has selected some random value for the targets. + (when (and buffer (command-log--should-log-command-p real-this-command event)) (with-current-buffer buffer (goto-char (point-max)) (cond ((and command-log-merge-repeats - (not (and command-log-log-text - (eq cmd #'self-insert-command) + (not (and command-log-text + (eq post-cmd #'self-insert-command) (not command-log--show-all-commands))) - (and (eq cmd command-log--last-keyboard-command) + ;; must completely match + (and (eq pre-cmd command-log--last-pre-command) + (eq post-cmd command-log--last-post-command) (string= keys command-log--last-command-keys))) + ;; Either set up repeat or delete marked region of old repeat and + ;; re-insert between markers. (cl-incf command-log--command-repetitions) - (save-match-data - (when (and (> command-log--command-repetitions 1) - (search-backward "[" (line-beginning-position -1) t)) - (delete-region (point) (line-end-position)))) - (backward-char) ; skip over either ?\newline or ?\space before ?\[ - (insert (propertize (concat " [" - (number-to-string (1+ command-log--command-repetitions)) - " times]") + (if (> command-log--command-repetitions 1) + (progn (delete-region command-log--repeat-start-marker + command-log--repeat-end-marker) + (goto-char command-log--repeat-start-marker)) + (backward-char) + (setq command-log--repeat-start-marker (point-marker) + command-log--repeat-end-marker (point-marker)) + (set-marker-insertion-type command-log--repeat-end-marker t)) + (insert (propertize (format command-log-repeat-format + (1+ command-log--command-repetitions)) 'face 'command-log-repeat-face))) - ((and (and command-log-log-text (not command-log--show-all-commands)) - (eq cmd #'self-insert-command)) - (when (eq command-log--last-keyboard-command #'self-insert-command) - (delete-char -1) - (delete-region (line-beginning-position) (line-end-position))) - (setq command-log--recent-history-string - (concat command-log--recent-history-string (kbd keys))) - (setq command-log--last-keyboard-command cmd) - (setq command-log--last-command-keys keys) - (insert (propertize - (concat "[text: " command-log--recent-history-string "]\n") - 'face 'command-log-repeat-face))) + + ((and (and command-log-text (not command-log--show-all-commands)) + (eq post-cmd #'self-insert-command)) + ;; TODO the only reason we can't log text and all commands + ;; simultaneously is because of this condition statement. + + ;; Either set up string or delete marked region of old string and + ;; re-insert between markers. + (if (eq command-log--last-post-command #'self-insert-command) + (progn (delete-region command-log--self-insert-start + command-log--self-insert-end) + (goto-char command-log--self-insert-start)) + (setq command-log--self-insert-start (point-marker) + command-log--self-insert-end (point-marker)) + (set-marker-insertion-type command-log--self-insert-end t)) + (setq command-log--self-insert-string + (concat command-log--self-insert-string (kbd keys))) + (insert + (propertize + (format + command-log-text-format + ;; 32 is space and I always wish I could write it more + ;; explicitly lol + (subst-char-in-string 32 + (string-to-char command-log-text-space) + command-log--self-insert-string) + 'face 'command-log-text-face))) + (newline)) (t - (setq command-log--command-repetitions 0) (insert (propertize keys :time (format-time-string command-log-time-string (current-time)) 'face 'command-log-key-face)) - (when (>= (current-column) command-log-log-command-indentation) - (newline)) - (move-to-column command-log-log-command-indentation t) - (insert - (if (byte-code-function-p cmd) - (propertize "" 'face 'command-log-command-face) - (propertize (symbol-name cmd) - 'face 'command-log-command-face - 'button '(t) - 'category 'default-button))) + + (when (< (length keys) command-log-keys-min-width) + (insert (make-string (- command-log-keys-min-width + (length keys)) + 32))) + (when (>= (length keys) command-log-keys-min-width) + (insert 32)) + + (if (and (not (eq pre-cmd post-cmd)) + (not (member + pre-cmd + command-log-post-excepting-pre-commands)) + (eq command-log-merge-repeat-targets 'post-command)) + (insert (command-log--format-command post-cmd)) + (insert (command-log--format-command pre-cmd))) (newline) - (setq command-log--last-command-keys keys) - (setq command-log--last-keyboard-command cmd))) - (when (> (count-lines (point-min) (point-max)) comint-max-line-length) - (goto-char (point-min)) - (delete-line)) - (command-log--zap-recent-history cmd) ; could be inside condition expression + (when (and (not (eq pre-cmd post-cmd)) + (not (member + pre-cmd + command-log-post-excepting-pre-commands)) + (not (member command-log-merge-repeat-targets + '(pre-command post-command)))) + (insert (make-string command-log-keys-min-width 32)) + (insert (command-log--format-command post-cmd)) + (newline)) + + ;; non-string command. unset string tracking. + (setq command-log--self-insert-end nil + command-log--self-insert-start nil + command-log--self-insert-string nil) + ;; non-repeat command. unset repetition tracking. + (setq command-log--command-repetitions 0 + command-log--repeat-start-marker nil + command-log--repeat-end-marker nil))) + + ;; every non-skipped command updates the last command + (setq command-log--last-post-command post-cmd + command-log--last-pre-command pre-cmd + command-log--last-command-keys keys) + + (when (> (count-lines (point-min) (point-max)) + command-log-max-log-lines) + (delete-region (goto-char (point-min)) + (progn (forward-line) (point)))) (command-log--scroll-buffer-windows))))) +(defvar-local command-log--dribble-file nil + "Clean up this dribble file.") + +(defun command-log--dribble-cleanup () + "Delete `command-log--dribble-file'." + (when (file-exists-p command-log--dribble-file) + (delete-file command-log--dribble-file ))) + +;;;###autoload +(defun command-log-tail-dribble () + "Open a dribble file and tail the contents." + (interactive) + (let* ((dribble-buffer "*Dribble*") + (buffer (get-buffer dribble-buffer))) + (if buffer + (kill-buffer buffer) + (with-current-buffer (get-buffer-create dribble-buffer) + (setq-local command-log--dribble-file + (make-temp-file "dribble")) + (open-dribble-file command-log--dribble-file) + (insert-file-contents command-log--dribble-file 'visit) + (add-hook 'kill-buffer-hook #'command-log--dribble-cleanup + nil 'local) + (auto-revert-tail-mode) + (setq-local auto-revert-verbose nil + buffer-read-only t)) + (switch-to-buffer-other-window dribble-buffer)))) + (provide 'command-log) ;;; command-log.el ends here