From: Andreas Gerler Date: Wed, 21 May 2025 12:48:37 +0000 (+0200) Subject: update key-chord X-Git-Url: http://git.bundesbrandschatzamt.de/?a=commitdiff_plain;h=a4cb87b1f0a6de1d8f6f1c46657233721018deac;p=emacsconfig.git update key-chord --- diff --git a/bba-programming.org b/bba-programming.org index 3503d4c8..608c55d1 100644 --- a/bba-programming.org +++ b/bba-programming.org @@ -136,7 +136,7 @@ for C-c T you can use a file like this one: combine key strokes to access commands even faster. #+BEGIN_SRC emacs-lisp - (add-to-list 'load-path "~/.emacs.d/elpa/key-chord-20230522.223") + (add-to-list 'load-path "~/.emacs.d/elpa/key-chord-20250330.2011") (require 'key-chord) (key-chord-mode 1) diff --git a/elpa/key-chord-20240910.1441/key-chord-autoloads.el b/elpa/key-chord-20240910.1441/key-chord-autoloads.el deleted file mode 100644 index da24cb89..00000000 --- a/elpa/key-chord-20240910.1441/key-chord-autoloads.el +++ /dev/null @@ -1,97 +0,0 @@ -;;; key-chord-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from key-chord.el - -(defvar key-chord-mode nil "\ -Non-nil if Key-Chord mode is enabled. -See the `key-chord-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `key-chord-mode'.") -(custom-autoload 'key-chord-mode "key-chord" nil) -(autoload 'key-chord-mode "key-chord" "\ -Map pairs of simultaneously pressed keys to commands. - -See functions `key-chord-define-global', `key-chord-define-local', -and `key-chord-define' and variables `key-chord-two-keys-delay' -and `key-chord-one-key-delay'. - -This is a global minor mode. If called interactively, toggle the -`Key-Chord mode' mode. If the prefix argument is positive, enable the -mode, and if it is zero or negative, disable the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable the -mode if ARG is nil, omitted, or is a positive number. Disable the mode -if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='key-chord-mode)'. - -The mode's hook is called both when the mode is enabled and when it is -disabled. - -(fn &optional ARG)" t) -(autoload 'key-chord-define-global "key-chord" "\ -Define a key-chord of the two keys in KEYS starting a COMMAND. - -KEYS can be a string or a vector of two elements. Currently only -elements that corresponds to ascii codes in the range 32 to 126 -can be used. - -COMMAND can be an interactive function, a string, or nil. -If COMMAND is nil, the key-chord is removed. - -Note that KEYS defined locally in the current buffer will have -precedence. - -(fn KEYS COMMAND)" t) -(autoload 'key-chord-define-local "key-chord" "\ -Locally define a key-chord of the two keys in KEYS starting a COMMAND. - -KEYS can be a string or a vector of two elements. Currently only -elements that corresponds to ascii codes in the range 32 to 126 -can be used. - -COMMAND can be an interactive function, a string, or nil. -If COMMAND is nil, the key-chord is removed. - -The binding goes in the current buffer's local map, which in most -cases is shared with all other buffers in the same major mode. - -(fn KEYS COMMAND)" t) -(autoload 'key-chord-define "key-chord" "\ -Define in KEYMAP, a key-chord of the two keys in KEYS starting a COMMAND. - -KEYS can be a string or a vector of two elements. Currently only -elements that corresponds to ascii codes in the range 32 to 126 -can be used. - -COMMAND can be an interactive function, a string, or nil. -If COMMAND is nil, the key-chord is removed. - -(fn KEYMAP KEYS COMMAND)") -(register-definition-prefixes "key-chord" '("key-chord-")) - -;;; End of scraped data - -(provide 'key-chord-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; key-chord-autoloads.el ends here diff --git a/elpa/key-chord-20240910.1441/key-chord-pkg.el b/elpa/key-chord-20240910.1441/key-chord-pkg.el deleted file mode 100644 index 754b0883..00000000 --- a/elpa/key-chord-20240910.1441/key-chord-pkg.el +++ /dev/null @@ -1,10 +0,0 @@ -;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "key-chord" "20240910.1441" - "Map pairs of simultaneously pressed keys to commands." - '((emacs "24")) - :url "https://github.com/emacsorphanage/key-chord" - :commit "fc75b1451759121601110da8ddfadcf5156318af" - :revdesc "fc75b1451759" - :keywords '("keyboard" "chord" "input") - :authors '(("David Andersson" . "l.david.anderssonsverige.nu")) - :maintainers '(("David Andersson" . "l.david.anderssonsverige.nu"))) diff --git a/elpa/key-chord-20240910.1441/key-chord.el b/elpa/key-chord-20240910.1441/key-chord.el deleted file mode 100644 index f849da4a..00000000 --- a/elpa/key-chord-20240910.1441/key-chord.el +++ /dev/null @@ -1,218 +0,0 @@ -;;; key-chord.el --- map pairs of simultaneously pressed keys to commands -*- lexical-binding: t; -*- - -;; Copyright (C) 2003, 2005, 2008, 2012 David Andersson - -;; Author: David Andersson -;; Package-Version: 20240910.1441 -;; Package-Revision: fc75b1451759 -;; Package-Requires: ((emacs "24")) -;; Keywords: keyboard chord input - -;; 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 2 of -;; the License, or (at your option) any later version. - -;; This program 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 this program; if not, write to the Free -;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, -;; MA 02111-1307 USA - -;;; Commentary: - -;; This package implements support for mapping a pair of simultaneously -;; pressed keys to a command and for mapping the same key being pressed -;; twice in quick succession to a command. Such bindings are called -;; "key chords". - -;;; Code: - -(defgroup key-chord nil - "Map pairs of simultaneously pressed keys to commands." - :group 'bindings) - -(defcustom key-chord-two-keys-delay 0.1 - "Max time delay between two key press to be considered a key chord." - :type 'float) - -(defcustom key-chord-one-key-delay 0.2 - "Max time delay between two press of the same key to be considered a key chord. -This should normally be a little longer than `key-chord-two-keys-delay'." - :type 'float) - -(defcustom key-chord-in-macros t - "If nil, don't expand key chords when executing keyboard macros. - -If non-nil, expand chord sequenses in macros, but only if a -similar chord was entered during the last interactive macro -recording. (This carries a bit of guesswork. We can't know for -sure when executing whether two keys were typed quickly or slowly -when recorded.)" - :type 'boolean) - -;; Internal vars -(defvar key-chord-mode nil) - -;; Shortcut for key-chord-input-method: no need to test a key again if it -;; didn't matched a chord the last time. Improves feedback during autorepeat. -(defvar key-chord-last-unmatched nil) - -;; Macro heuristics: Keep track of which chords was used when the last macro -;; was defined. Or rather, only the first-char of the chords. Only expand -;; matching chords during macro execution. -(defvar key-chord-in-last-kbd-macro nil) -(defvar key-chord-defining-kbd-macro nil) - -;;;###autoload -(define-minor-mode key-chord-mode - "Map pairs of simultaneously pressed keys to commands. - -See functions `key-chord-define-global', `key-chord-define-local', -and `key-chord-define' and variables `key-chord-two-keys-delay' -and `key-chord-one-key-delay'." - :global t - (setq input-method-function - (and key-chord-mode - 'key-chord-input-method))) - -;;;###autoload -(defun key-chord-define-global (keys command) - "Define a key-chord of the two keys in KEYS starting a COMMAND. - -KEYS can be a string or a vector of two elements. Currently only -elements that corresponds to ascii codes in the range 32 to 126 -can be used. - -COMMAND can be an interactive function, a string, or nil. -If COMMAND is nil, the key-chord is removed. - -Note that KEYS defined locally in the current buffer will have -precedence." - (interactive "sSet key chord globally (2 keys): \nCSet chord \"%s\" to command: ") - (key-chord-define (current-global-map) keys command)) - -;;;###autoload -(defun key-chord-define-local (keys command) - "Locally define a key-chord of the two keys in KEYS starting a COMMAND. - -KEYS can be a string or a vector of two elements. Currently only -elements that corresponds to ascii codes in the range 32 to 126 -can be used. - -COMMAND can be an interactive function, a string, or nil. -If COMMAND is nil, the key-chord is removed. - -The binding goes in the current buffer's local map, which in most -cases is shared with all other buffers in the same major mode." - (interactive "sSet key chord locally (2 keys): \nCSet chord \"%s\" to command: ") - (key-chord-define (current-local-map) keys command)) - -(defun key-chord-unset-global (keys) - "Remove global key-chord of the two keys in KEYS." - (interactive "sUnset key chord globally (2 keys): ") - (key-chord-define (current-global-map) keys nil)) - -(defun key-chord-unset-local (keys) - "Remove local key-chord of the two keys in KEYS." - (interactive "sUnset key chord locally (2 keys): ") - (key-chord-define (current-local-map) keys nil)) - -;;;###autoload -(defun key-chord-define (keymap keys command) - "Define in KEYMAP, a key-chord of the two keys in KEYS starting a COMMAND. - -KEYS can be a string or a vector of two elements. Currently only -elements that corresponds to ascii codes in the range 32 to 126 -can be used. - -COMMAND can be an interactive function, a string, or nil. -If COMMAND is nil, the key-chord is removed." - (if (/= 2 (length keys)) - (error "Key-chord keys must have two elements")) - ;; Exotic chars in a string are >255 but define-key wants 128..255 - ;; for those. - (let ((key1 (logand 255 (aref keys 0))) - (key2 (logand 255 (aref keys 1)))) - (if (eq key1 key2) - (define-key keymap (vector 'key-chord key1 key2) command) - (define-key keymap (vector 'key-chord key1 key2) command) - (define-key keymap (vector 'key-chord key2 key1) command)))) - -(defun key-chord-lookup-key1 (keymap key) - "Like lookup-key but no third arg and no numeric return value." - (let ((res (lookup-key keymap key))) - (and (not (numberp res)) - res))) - -(defun key-chord-lookup-key (key) - "Lookup KEY in all current key maps." - (let ((maps (current-minor-mode-maps)) - res) - (while (and maps (not res)) - (setq res (key-chord-lookup-key1 (car maps) key)) - (setq maps (cdr maps))) - (or res - (and (current-local-map) - (key-chord-lookup-key1 (current-local-map) key)) - (key-chord-lookup-key1 (current-global-map) key)))) - -(defun key-chord-describe () - "List key chord bindings in a help buffer. - -Two key chords will be listed twice and there will be Prefix -Commands. Please ignore that." - (interactive) - (describe-bindings [key-chord])) - -(defun key-chord-input-method (first-char) - "Input method controlled by key bindings with the prefix `key-chord'." - (cond - ((and (not (eq first-char key-chord-last-unmatched)) - (key-chord-lookup-key (vector 'key-chord first-char))) - (let ((delay (if (key-chord-lookup-key - (vector 'key-chord first-char first-char)) - key-chord-one-key-delay - key-chord-two-keys-delay))) - (cond ((if executing-kbd-macro - (not (memq first-char key-chord-in-last-kbd-macro)) - (when (bound-and-true-p eldoc-mode) - (eldoc-pre-command-refresh-echo-area)) - (sit-for delay 'no-redisplay)) - (setq key-chord-last-unmatched nil) - (list first-char)) - (t ; input-pending-p - (let* ((input-method-function nil) - (next-char (read-event)) - (res (vector 'key-chord first-char next-char))) - (cond ((key-chord-lookup-key res) - (setq key-chord-defining-kbd-macro - (cons first-char key-chord-defining-kbd-macro)) - (list 'key-chord first-char next-char)) - (t ;put back next-char and return first-char - (setq unread-command-events - (cons next-char unread-command-events)) - (when (eq first-char next-char) - (setq key-chord-last-unmatched first-char)) - (list first-char)))))))) - (t ; no key-chord keymap - (setq key-chord-last-unmatched first-char) - (list first-char)))) - -(defun key-chord--start-kbd-macro (_append &optional _no-exec) - (setq key-chord-defining-kbd-macro nil)) -(advice-add 'start-kbd-macro :after #'key-chord--start-kbd-macro) - -(defun key-chord--end-kbd-macro (&optional _repeat _loopfunc) - (setq key-chord-in-last-kbd-macro key-chord-defining-kbd-macro)) -(advice-add 'end-kbd-macro :after #'key-chord--end-kbd-macro) - -(provide 'key-chord) -;; Local Variables: -;; indent-tabs-mode: nil -;; End: -;;; key-chord.el ends here diff --git a/elpa/key-chord-20250330.2011/key-chord-autoloads.el b/elpa/key-chord-20250330.2011/key-chord-autoloads.el new file mode 100644 index 00000000..43c08e1c --- /dev/null +++ b/elpa/key-chord-20250330.2011/key-chord-autoloads.el @@ -0,0 +1,109 @@ +;;; key-chord-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; Generated autoloads from key-chord.el + +(defvar key-chord-mode nil "\ +Non-nil if Key-Chord mode is enabled. +See the `key-chord-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `key-chord-mode'.") +(custom-autoload 'key-chord-mode "key-chord" nil) +(autoload 'key-chord-mode "key-chord" "\ +Map pairs of simultaneously pressed keys to commands. + +See functions `key-chord-define-global', `key-chord-define-local', +and `key-chord-define' and variables `key-chord-two-keys-delay' +and `key-chord-one-key-delay'. + +This is a global minor mode. If called interactively, toggle the +`Key-Chord mode' mode. If the prefix argument is positive, enable the +mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable the +mode if ARG is nil, omitted, or is a positive number. Disable the mode +if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='key-chord-mode)'. + +The mode's hook is called both when the mode is enabled and when it is +disabled. + +(fn &optional ARG)" t) +(autoload 'key-chord-define-global "key-chord" "\ +Define a key-chord of the two keys in KEYS starting a COMMAND. + +KEYS can be a string or a vector of two elements. Currently only +elements that corresponds to ascii codes in the range 32 to 126 +can be used. + +COMMAND can be an interactive function, a string, or nil. +If COMMAND is nil, the key-chord is removed. + +Note that KEYS defined locally in the current buffer will have +precedence. + +(fn KEYS COMMAND)" t) +(autoload 'key-chord-define-local "key-chord" "\ +Locally define a key-chord of the two keys in KEYS starting a COMMAND. + +KEYS can be a string or a vector of two elements. Currently only +elements that corresponds to ascii codes in the range 32 to 126 +can be used. + +COMMAND can be an interactive function, a string, or nil. +If COMMAND is nil, the key-chord is removed. + +The binding goes in the current buffer's local map, which in most +cases is shared with all other buffers in the same major mode. + +(fn KEYS COMMAND)" t) +(autoload 'key-chord-register-keys "key-chord" "\ +Register KEY1 and KEY2 as being used in a key chord. +This function should be called by packages that define key chords +outside of the standard key-chord-define functions. + +(fn KEY1 KEY2)") +(autoload 'key-chord-unregister-keys "key-chord" "\ +Unregister KEY1 and KEY2 as being used in a key chord. +This should only be called if you're certain these keys are not +used in any other chords. + +(fn KEY1 KEY2)") +(autoload 'key-chord-define "key-chord" "\ +Define in KEYMAP, a key-chord of the two keys in KEYS starting a COMMAND. + +KEYS can be a string or a vector of two elements. Currently only +elements that corresponds to ascii codes in the range 32 to 126 +can be used. + +COMMAND can be an interactive function, a string, or nil. +If COMMAND is nil, the key-chord is removed. + +(fn KEYMAP KEYS COMMAND)") +(register-definition-prefixes "key-chord" '("key-chord-")) + +;;; End of scraped data + +(provide 'key-chord-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; key-chord-autoloads.el ends here diff --git a/elpa/key-chord-20250330.2011/key-chord-pkg.el b/elpa/key-chord-20250330.2011/key-chord-pkg.el new file mode 100644 index 00000000..cc98a126 --- /dev/null +++ b/elpa/key-chord-20250330.2011/key-chord-pkg.el @@ -0,0 +1,10 @@ +;; -*- no-byte-compile: t; lexical-binding: nil -*- +(define-package "key-chord" "20250330.2011" + "Map pairs of simultaneously pressed keys to commands." + '((emacs "24")) + :url "https://github.com/LemonBreezes/key-chord" + :commit "cb646e815c61f253ad9fdfbe058049dda4e2b32b" + :revdesc "cb646e815c61" + :keywords '("keyboard" "chord" "input") + :authors '(("David Andersson" . "l.david.anderssonsverige.nu")) + :maintainers '(("LemonBreezes" . "look@strawberrytea.xyz"))) diff --git a/elpa/key-chord-20250330.2011/key-chord.el b/elpa/key-chord-20250330.2011/key-chord.el new file mode 100644 index 00000000..e05ae28e --- /dev/null +++ b/elpa/key-chord-20250330.2011/key-chord.el @@ -0,0 +1,460 @@ +;;; key-chord.el --- map pairs of simultaneously pressed keys to commands +;; -*- lexical-binding: t; -*- + +;; Copyright (C) 2003, 2005, 2008, 2012 David Andersson + +;; Author: David Andersson +;; Maintainer: LemonBreezes +;; Package-Version: 20250330.2011 +;; Package-Revision: cb646e815c61 +;; Package-Requires: ((emacs "24")) +;; Keywords: keyboard chord input +;; URL: https://github.com/LemonBreezes/key-chord + +;; 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 2 of +;; the License, or (at your option) any later version. + +;; This program 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 this program; if not, write to the Free +;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +;; MA 02111-1307 USA + +;;; Commentary: + +;; This package implements support for mapping a pair of simultaneously +;; pressed keys to a command and for mapping the same key being pressed +;; twice in quick succession to a command. Such bindings are called +;; "key chords". + +;;; Code: + +(defgroup key-chord nil + "Map pairs of simultaneously pressed keys to commands." + :group 'bindings) + +(defun key-chord-in-read-char-context-p () + "Return non-nil if we're in a read-char or similar context. +This is determined by checking if `overriding-local-map` is set to +one of the special read keymaps." + (and overriding-local-map + (memq overriding-local-map + (list read-key-empty-map read-key-full-map)))) + +(defcustom key-chord-two-keys-delay 0.1 + "Max time delay between two key press to be considered a key chord." + :type 'float) + +(defcustom key-chord-one-key-delay 0.2 + "Max time delay between two press of the same key for a chord. +This should normally be a little longer than +`key-chord-two-keys-delay'." + :type 'float) + +(defcustom key-chord-in-macros nil + "If nil, don't expand key chords when executing keyboard macros. + +If non-nil, expand chord sequenses in macros, but only if a similar +chord was entered during the last interactive macro recording. (This +carries a bit of guesswork. We can't know for sure when executing +whether two keys were typed quickly or slowly when recorded. Be careful +enabling this as it could cause key chords to be expanded in places +where they shouldn't be)." + :type 'boolean) + +(defcustom key-chord-one-key-min-delay 0.0 + "Minimum delay (in seconds) between two presses for a double-tap key chord. +The delay applies for chords using the same key. If the delay between two +identical key presses is less than this value (as when holding a key), the +chord will not trigger." + :type 'float) + +(defcustom key-chord-typing-detection nil + "If non-nil, detect typing and temporarily disable chord detection. +This helps avoid accidental chord triggering during fast typing. This +also dramatically improves the performance of key-chord when typing text +by acting like a debounce." + :type 'boolean) + +(defcustom key-chord-typing-speed-threshold 0.1 + "Maximum delay between keystrokes to be considered part of typing flow. +Measured in seconds. If keys are pressed faster than this threshold, +key-chord detection will be temporarily disabled." + :type 'float) + +(defcustom key-chord-typing-reset-delay 0.5 + "Time after which to reset typing detection if no keys are pressed. +Measured in seconds. After this much idle time, key-chord detection +will be re-enabled." + :type 'float) + +(defcustom key-chord-use-key-tracking t + "If non-nil, track which keys are used in chords to optimize lookups. +This improves performance by avoiding unnecessary `key-chord-lookup-key' +calls. However, it could potentially cause issues if key chords are +defined outside of the normal key-chord-define* functions." + :type 'boolean) + +;; Internal vars +(defvar key-chord-mode nil) + +;; Shortcut for key-chord-input-method: no need to test a key again if it +;; didn't matched a chord the last time. Improves feedback during autorepeat. +(defvar key-chord-last-unmatched nil) + +;; Macro heuristics: Keep track of which chords was used when the last macro +;; was defined. Or rather, only the first-char of the chords. Only expand +;; matching chords during macro execution. +(defvar key-chord-in-last-kbd-macro nil) +(defvar key-chord-defining-kbd-macro nil) + + +;; Typing detection variables +(defvar key-chord-in-typing-flow nil + "Non-nil when user appears to be typing text rather than executing commands.") +(defvar key-chord-last-key-time nil + "Time when the last key was pressed.") +(defvar key-chord-typing-reset-time nil + "Time after which typing flow should be reset.") + + +;; Key tracking for optimization +(defvar key-chord-keys-in-use (make-vector 256 nil) + "Vector indicating which keys are used in any chord.") + +;;;###autoload +(define-minor-mode key-chord-mode + "Map pairs of simultaneously pressed keys to commands. + +See functions `key-chord-define-global', `key-chord-define-local', +and `key-chord-define' and variables `key-chord-two-keys-delay' +and `key-chord-one-key-delay'." + :global t + (setq input-method-function + (and key-chord-mode + 'key-chord-input-method)) + (key-chord-reset-typing-detection)) + +;;;###autoload +(defun key-chord-define-global (keys command) + "Define a key-chord of the two keys in KEYS starting a COMMAND. + +KEYS can be a string or a vector of two elements. Currently only +elements that corresponds to ascii codes in the range 32 to 126 +can be used. + +COMMAND can be an interactive function, a string, or nil. +If COMMAND is nil, the key-chord is removed. + +Note that KEYS defined locally in the current buffer will have +precedence." + (interactive "sSet key chord globally (2 keys): \nCSet chord \"%s\" to command: ") + (key-chord-define (current-global-map) keys command)) + +;;;###autoload +(defun key-chord-define-local (keys command) + "Locally define a key-chord of the two keys in KEYS starting a COMMAND. + +KEYS can be a string or a vector of two elements. Currently only +elements that corresponds to ascii codes in the range 32 to 126 +can be used. + +COMMAND can be an interactive function, a string, or nil. +If COMMAND is nil, the key-chord is removed. + +The binding goes in the current buffer's local map, which in most +cases is shared with all other buffers in the same major mode." + (interactive "sSet key chord locally (2 keys): \nCSet chord \"%s\" to command: ") + (key-chord-define (current-local-map) keys command)) + +(defun key-chord-unset-global (keys) + "Remove global key-chord of the two keys in KEYS." + (interactive "sUnset key chord globally (2 keys): ") + (key-chord-define (current-global-map) keys nil)) + +(defun key-chord-unset-local (keys) + "Remove local key-chord of the two keys in KEYS." + (interactive "sUnset key chord locally (2 keys): ") + (key-chord-define (current-local-map) keys nil)) + +;;;###autoload +(defun key-chord-register-keys (key1 key2) + "Register KEY1 and KEY2 as being used in a key chord. +This function should be called by packages that define key chords +outside of the standard key-chord-define functions." + ;; Always register keys regardless of key-tracking setting + (when (and (integerp key1) (< key1 256)) + (aset key-chord-keys-in-use key1 t)) + (when (and (integerp key2) (< key2 256)) + (aset key-chord-keys-in-use key2 t))) + +;;;###autoload +(defun key-chord-unregister-keys (key1 key2) + "Unregister KEY1 and KEY2 as being used in a key chord. +This should only be called if you're certain these keys are not +used in any other chords." + ;; Always attempt to unregister keys regardless of key-tracking setting + ;; Only unregister if we're sure the keys aren't used elsewhere + (let ((remove-key1 t) + (remove-key2 t)) + ;; Check if keys are used in any other chords + (let ((maps (list (current-global-map) (current-local-map)))) + (dolist (map (append maps (current-minor-mode-maps))) + (when map + ;; Check if key1 is used in any other chord + (dotimes (k 256) + (when (and (not (and (= k key2) (= key1 key2))) + (or (key-chord-lookup-key1 map (vector 'key-chord key1 k)) + (key-chord-lookup-key1 map (vector 'key-chord k key1)))) + (setq remove-key1 nil))) + + ;; Check if key2 is used in any other chord + (dotimes (k 256) + (when (and (not (and (= k key1) (= key1 key2))) + (or (key-chord-lookup-key1 map (vector 'key-chord key2 k)) + (key-chord-lookup-key1 map (vector 'key-chord k key2)))) + (setq remove-key2 nil)))))) + + ;; Remove keys from tracking vector if not used elsewhere + (when remove-key1 (aset key-chord-keys-in-use key1 nil)) + (when remove-key2 (aset key-chord-keys-in-use key2 nil)))) + +;;;###autoload +(defun key-chord-define (keymap keys command) + "Define in KEYMAP, a key-chord of the two keys in KEYS starting a COMMAND. + +KEYS can be a string or a vector of two elements. Currently only +elements that corresponds to ascii codes in the range 32 to 126 +can be used. + +COMMAND can be an interactive function, a string, or nil. +If COMMAND is nil, the key-chord is removed." + (when (/= 2 (length keys)) + (error "Key-chord keys must have two elements")) + (let ((key1 (aref keys 0)) + (key2 (aref keys 1))) + ;; Check that both keys are bytes + (unless (and (integerp key1) (< key1 256) + (integerp key2) (< key2 256)) + (error "Key-chord keys must both be bytes (characters with codes < 256)")) + + (if (eq key1 key2) + (define-key keymap (vector 'key-chord key1 key2) command) + (define-key keymap (vector 'key-chord key1 key2) command) + (define-key keymap (vector 'key-chord key2 key1) command)) + + ;; Update key tracking directly + (if command + (key-chord-register-keys key1 key2) + (key-chord-unregister-keys key1 key2)))) + +(defun key-chord-lookup-key1 (keymap key) + "Like `lookup-key' but no third arg and no numeric return value. +KEYMAP is the keymap to look up the KEY in." + (let ((res (lookup-key keymap key))) + (and (not (numberp res)) + res))) + +(defun key-chord-lookup-key (key) + "Lookup KEY in all current key maps." + (let ((maps (current-minor-mode-maps)) + res) + (while (and maps (not res)) + (setq res (key-chord-lookup-key1 (car maps) key)) + (setq maps (cdr maps))) + (or res + (and (current-local-map) + (key-chord-lookup-key1 (current-local-map) key)) + (key-chord-lookup-key1 (current-global-map) key)))) + +(defun key-chord-describe () + "List key chord bindings in a help buffer. + +Two key chords will be listed twice and there will be Prefix +Commands. Please ignore that." + (interactive) + (describe-bindings [key-chord])) + +(defun key-chord-reset-all-state () + "Reset all key-chord state variables." + (setq key-chord-last-unmatched nil) + (setq key-chord-in-typing-flow nil) + (setq key-chord-last-key-time nil) + (setq key-chord-typing-reset-time nil)) + +(defun key-chord-reset-typing-detection () + "Reset typing detection state when `key-chord-mode' is toggled." + (key-chord-reset-all-state)) + +(defun key-chord-reset-typing-mode () + "Reset the typing detection mode." + (setq key-chord-in-typing-flow nil) + (setq key-chord-last-unmatched nil)) + +(defun key-chord-pause-typing-flow () + "Temporarily pause typing flow detection to allow chord detection." + (when key-chord-typing-detection + (setq key-chord-in-typing-flow nil))) + +(defun key-chord-valid-chord-char-p (char) + "Return non-nil if CHAR is valid for chord detection. +A valid character is an integer in the range 0-255." + (and (integerp char) (< char 256))) + +(defun key-chord-execute-chord (first-char next-char) + "Execute the chord composed of FIRST-CHAR and NEXT-CHAR. +Returns the key-chord event to be processed." + ;; Record the chord for macro tracking + (setq key-chord-defining-kbd-macro + (cons first-char key-chord-defining-kbd-macro)) + ;; Reset state after successful chord execution + (setq key-chord-last-unmatched nil) + ;; Return the chord event + (list 'key-chord first-char next-char)) + +(defun key-chord-no-chord (first-char &optional next-char) + "Handle the case when no chord is matched. +FIRST-CHAR is the first character pressed. +If NEXT-CHAR is provided, it will be added to `unread-command-events`." + (when next-char + (setq unread-command-events (cons next-char unread-command-events))) + (setq key-chord-last-unmatched first-char) + (list first-char)) + +(defun key-chord-check-typing-flow (current-time) + "Check if user is in typing mode based on timing between keystrokes. +CURRENT-TIME is the time at which the function was called." + (when key-chord-typing-detection + ;; Check if we need to reset typing flow based on elapsed time + (when (and key-chord-typing-reset-time + (> (float-time current-time) key-chord-typing-reset-time)) + (setq key-chord-in-typing-flow nil) + (setq key-chord-last-unmatched nil)) ;; Reset last-unmatched when exiting typing flow + + ;; Set new reset time + (setq key-chord-typing-reset-time + (+ (float-time current-time) key-chord-typing-reset-delay)) + + ;; Check if we're in typing flow based on timing + (unless key-chord-in-typing-flow + (when key-chord-last-key-time + (let ((elapsed (float-time (time-subtract current-time key-chord-last-key-time)))) + (when (< elapsed key-chord-typing-speed-threshold) + (setq key-chord-in-typing-flow t))))) + + ;; Update last key time + (setq key-chord-last-key-time current-time))) + +(defun key-chord-input-method (first-char) + "Input method controlled by key bindings with the prefix `key-chord'. +FIRST-CHAR is the first character input by the user." + ;; Check if we're in a read-char context and bypass key-chord if so + (if (key-chord-in-read-char-context-p) + (progn + (key-chord-reset-all-state) + (list first-char)) + + ;; Not in read-char context, proceed with normal key-chord processing + (progn + ;; Check typing mode (but not during macro execution) + (unless executing-kbd-macro + (key-chord-check-typing-flow (current-time))) + + (cond + ;; For any non-valid chord character, bypass chord detection + ((not (key-chord-valid-chord-char-p first-char)) + (setq key-chord-last-unmatched nil) ;; Reset for safety + (list first-char)) + + ;; Skip chord detection if in typing mode (but not during macro execution) + ((and key-chord-typing-detection + key-chord-in-typing-flow + (not executing-kbd-macro)) + ;; Don't update key-chord-last-unmatched in typing flow mode + (list first-char)) + + ;; Skip chord detection during macro execution if key-chord-in-macros is nil + ;; or if the key is not in a recorded chord + ((and executing-kbd-macro + (or (not key-chord-in-macros) + (not (memq first-char key-chord-in-last-kbd-macro)))) + (key-chord-no-chord first-char)) + + ;; Optimization: Skip chord detection completely if key tracking is enabled + ;; and first-char is not used in any chord + ((and key-chord-use-key-tracking + (not (aref key-chord-keys-in-use first-char))) + (key-chord-no-chord first-char)) + + ;; Continue with chord detection for keys that might be part of a chord + ((not (eq first-char key-chord-last-unmatched)) + (key-chord-pause-typing-flow) ;; Temporarily exit typing flow for chord detection + (let ((res (key-chord-lookup-key (vector 'key-chord first-char)))) + (if (not res) + (key-chord-no-chord first-char) + (let ((start-time (current-time)) + (delay (if (key-chord-lookup-key + (vector 'key-chord first-char first-char)) + key-chord-one-key-delay + key-chord-two-keys-delay))) + + ;; Read second character + (let ((next-char (read-event nil nil delay))) + (cond + ;; No second character received within timeout + ((null next-char) + (key-chord-no-chord first-char)) + + ;; Handle invalid next character + ((not (key-chord-valid-chord-char-p next-char)) + (key-chord-no-chord first-char next-char)) + + ;; Handle double-press of the same key (with timing checks) + ((eq first-char next-char) + (let ((elapsed (float-time (time-subtract (current-time) start-time)))) + (if (< elapsed key-chord-one-key-min-delay) + ;; Too fast (key held down) - not a chord + (key-chord-no-chord first-char next-char) + ;; Check if this is a valid same-key chord + (if (key-chord-lookup-key (vector 'key-chord first-char next-char)) + (key-chord-execute-chord first-char next-char) + ;; Not a valid chord + (key-chord-no-chord first-char next-char))))) + + ;; Handle two different keys + (t + ;; Check if this is a valid two-key chord + (if (key-chord-lookup-key (vector 'key-chord first-char next-char)) + (key-chord-execute-chord first-char next-char) + ;; Not a valid chord + (key-chord-no-chord first-char next-char))))))))) + ;; Key was last unmatched + (t (key-chord-no-chord first-char)))))) + +(defun key-chord--start-kbd-macro (_append &optional _no-exec) + "Reset key chord tracking when a keyboard macro is started. +_APPEND and _NO-EXEC are ignored arguments from `start-kbd-macro'." + (setq key-chord-defining-kbd-macro nil)) + +(advice-add 'start-kbd-macro :after #'key-chord--start-kbd-macro) + + +(defun key-chord--end-kbd-macro (&optional _repeat _loopfunc) + "Save key chord information when a keyboard macro ends. +_REPEAT and _LOOPFUNC are ignored arguments from `end-kbd-macro'." + (setq key-chord-in-last-kbd-macro key-chord-defining-kbd-macro)) + +(advice-add 'end-kbd-macro :after #'key-chord--end-kbd-macro) + + +(provide 'key-chord) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; key-chord.el ends here