From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Philip Kaludercic Newsgroups: gmane.emacs.devel Subject: Re: [NonGNU ELPA] New package: devil Date: Tue, 09 May 2023 08:42:20 +0000 Message-ID: <875y91uc9f.fsf@posteo.net> References: Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="27311"; mail-complaints-to="usenet@ciao.gmane.io" Cc: emacs-devel@gnu.org To: Susam Pal Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Tue May 09 10:43:23 2023 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pwIwM-0006vA-34 for ged-emacs-devel@m.gmane-mx.org; Tue, 09 May 2023 10:43:22 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pwIvf-0002FY-7b; Tue, 09 May 2023 04:42:39 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pwIvd-0002FI-UK for emacs-devel@gnu.org; Tue, 09 May 2023 04:42:37 -0400 Original-Received: from mout02.posteo.de ([185.67.36.66]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pwIvZ-0007ql-6j for emacs-devel@gnu.org; Tue, 09 May 2023 04:42:36 -0400 Original-Received: from submission (posteo.de [185.67.36.169]) by mout02.posteo.de (Postfix) with ESMTPS id BB39C2402F8 for ; Tue, 9 May 2023 10:42:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1683621749; bh=ATFhFUZgiAhPVh+Ju2pDRWw6Y/nHUkZMw6X8nev/GpE=; h=From:To:Cc:Subject:Autocrypt:Date:From; b=GU6nx5H6PmEEiToUJgH88Jl/99NxK1jlvoCDENDi1jsrbSOXtTzhX/tOOIa6eOeMe Q6e2P4yXDUQ+K4lBtkv4fuQVYwUAlyaxDS2Y5wjqByPWlJFMnYtw2OZMj6sIgv8YgZ nwy+i1FTbGeGGb65Sxq6krz6ewiw+HTTqoa3ShhW5NfFCX4TLi2gmkcJhNaXdUZaBj ZwEYNP0M7MLQVUg6ZAJ0qB7G8bqvhBDGCp/uGyLC4E9CqhevCRntrRMG+SOqjnUs7x hEH4DW8r0RN8CpTPp51ULj0ElzFzPZ51C0wl+jy65YCC6Zsh5cA2XlxJyEg5vwJ2y1 4Lbrysa0+TR7w== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4QFs99014cz9rxf; Tue, 9 May 2023 10:42:20 +0200 (CEST) In-Reply-To: (Susam Pal's message of "Tue, 9 May 2023 02:57:49 +0100") Autocrypt: addr=philipk@posteo.net; keydata= mDMEZBBQQhYJKwYBBAHaRw8BAQdAHJuofBrfqFh12uQu0Yi7mrl525F28eTmwUDflFNmdui0QlBo aWxpcCBLYWx1ZGVyY2ljIChnZW5lcmF0ZWQgYnkgYXV0b2NyeXB0LmVsKSA8cGhpbGlwa0Bwb3N0 ZW8ubmV0PoiWBBMWCAA+FiEEDg7HY17ghYlni8XN8xYDWXahwukFAmQQUEICGwMFCQHhM4AFCwkI BwIGFQoJCAsCBBYCAwECHgECF4AACgkQ8xYDWXahwulikAEA77hloUiSrXgFkUVJhlKBpLCHUjA0 mWZ9j9w5d08+jVwBAK6c4iGP7j+/PhbkxaEKa4V3MzIl7zJkcNNjHCXmvFcEuDgEZBBQQhIKKwYB BAGXVQEFAQEHQI5NLiLRjZy3OfSt1dhCmFyn+fN/QKELUYQetiaoe+MMAwEIB4h+BBgWCAAmFiEE Dg7HY17ghYlni8XN8xYDWXahwukFAmQQUEICGwwFCQHhM4AACgkQ8xYDWXahwukm+wEA8cml4JpK NeAu65rg+auKrPOP6TP/4YWRCTIvuYDm0joBALw98AMz7/qMHvSCeU/hw9PL6u6R2EScxtpKnWof z4oM Received-SPF: pass client-ip=185.67.36.66; envelope-from=philipk@posteo.net; helo=mout02.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:305999 Archived-At: --=-=-= Content-Type: text/plain Susam Pal writes: > Hello! > > I am the author and maintainer of a new package named Devil. This package > intercepts keystrokes entered by the user and applies translation rules to > translate the keystrokes into Emacs key sequences. It supports three types > of rules: special keys that map to custom commands that are invoked > immediately prior to any translations, translation rules to translate Devil > key sequences to regular Emacs key sequences, and repeatable keys to allow > a Devil key sequence to be repeated by typing the last keystroke over and > over again using a transient map. > > See the README at https://github.com/susam/devil for more details. Looks interesting, here is a diff with a few comments: --=-=-= Content-Type: text/plain Content-Disposition: inline diff -u /home/philip/devil.el.1 /home/philip/devil.el --- /home/philip/devil.el.1 2023-05-09 10:37:55.243222106 +0200 +++ /home/philip/devil.el 2023-05-09 10:39:50.110922443 +0200 @@ -36,34 +36,42 @@ ;; key sequences without using modifier keys. ;;; Code: + +(defgroup devil '() + "Minor mode for Devil-like command entering." ;is there a clearer description? + :prefix "devil-" + :group 'editing) + (defconst devil-version "0.2.0" "Devil version number.") -(defun devil-show-version () +(defun devil-show-version () ;is this really needed? "Show Devil version number in the echo area." (interactive) (message "Devil %s" devil-version)) -(defvar devil-key "," +(defcustom devil-key "," "The key sequence that begins Devil input. The key sequence must be specified in the format returned by `C-h -k' (`describe-key'). This variable should be set before enabling -Devil mode for it to take effect.") - -(defvar devil-lighter " Devil" - "String displayed on the mode line when Devil mode is enabled.") +k' (`describe-key'). This variable should be set before enabling +Devil mode for it to take effect." + :type 'key-sequence) + +(defcustom devil-lighter " Devil" + "String displayed on the mode line when Devil mode is enabled." + :type 'string) (defvar devil-mode-map (let ((map (make-sparse-keymap))) - (define-key map (kbd devil-key) #'devil) + (define-key map devil-key #'devil) map) "Keymap to wake up Devil when `devil-key' is typed. By default, only `devil-key' is added to this keymap so that -Devil can be activated using it. To support multiple activation +Devil can be activated using it. To support multiple activation keys, this variable may be modified to a new keymap that defines -multiple different keys to activate Devil. This variable should +multiple different keys to activate Devil. This variable should be modified before loading Devil for it to take effect.") ;;;###autoload @@ -74,15 +82,16 @@ ;;;###autoload (define-globalized-minor-mode - global-devil-mode devil-mode devil--on :group 'devil + global-devil-mode devil-mode devil--on ;; :group 'devil (not needed with the defgroup above) (if global-devil-mode (devil-add-extra-keys) (devil-remove-extra-keys))) (defun devil--on () "Turn Devil mode on." (devil-mode 1)) -(defvar devil-logging nil - "Non-nil if and only if Devil should print log messages.") +(defcustom devil-logging nil + "Non-nil if and only if Devil should print log messages." + :type 'boolean) (defvar devil-special-keys (list (cons "%k %k" (lambda () (interactive) (devil-run-key "%k"))) @@ -91,12 +100,12 @@ "Special Devil keys that are executed as soon as they are typed. The value of this variable is an alist where each key represents -a Devil key sequence. If a Devil key sequence matches any key in +a Devil key sequence. If a Devil key sequence matches any key in this alist, the function or lambda in the corresponding value is -invoked. The format control specifier `%k' may be used to +invoked. The format control specifier `%k' may be used to represent `devil-key' in the keys.") -(defvar devil-translations +(defcustom devil-translations (list (cons "%k z" "C-") (cons "%k %k" "%k") (cons "%k m m" "M-") @@ -108,12 +117,13 @@ a translation rule that is applied on the Devil key sequence read from the user to obtain the Emacs key sequence to be executed. The translation rules are applied in the sequence they occur in -the alist. For each rule, if the key occurs anywhere in the Devil +the alist. For each rule, if the key occurs anywhere in the Devil key sequence, it is replaced with the corresponding value in the -translation rule. The format control specifier `%k' may be used -to represent `devil-key' in the keys.") +translation rule. The format control specifier `%k' may be used +to represent `devil-key' in the keys." + :type '(alist :key-type string :value-type string)) -(defvar devil-repeatable-keys +(defcustom devil-repeatable-keys (list "%k p" "%k n" "%k f" @@ -128,8 +138,9 @@ The value of this variable is a list where each item represents a key sequence that may be repeated merely by typing the last -character in the key sequence. The format control specified `%k' -may be used to represent `devil-key' in the keys.") +character in the key sequence. The format control specified `%k' +may be used to represent `devil-key' in the keys." + :type '(repeat string)) (defun devil-run-key (key) "Execute the given key sequence KEY. @@ -140,7 +151,7 @@ (dolist (key (split-string key)) (if (string= key "%k") (insert devil-key) (execute-kbd-macro (kbd key))))) -(defvar devil--saved-keys +(defvar devil--saved-keys nil ;otherwise `devil--saved-keys' is assigned a string "Original key bindings saved by Devil.") (defun devil-add-extra-keys () @@ -160,8 +171,9 @@ (defun devil--original-keys-to-be-saved () "Return an alist of keys that will be modified by Devil." - (list (cons 'isearch-comma (lookup-key isearch-mode-map (kbd devil-key))) - (cons 'universal-u (lookup-key universal-argument-map (kbd "u"))))) + ;; Weak suggestions + `((isearch-comma . ,(lookup-key isearch-mode-map (kbd devil-key))) + universal-u . ,(lookup-key universal-argument-map (kbd "u")))) (defun devil () "Wake up Devil to read and translate Devil key sequences." @@ -178,27 +190,30 @@ after translation to Emacs key sequence. The argument KEY is a vector that represents the key sequence -read so far. This function reads a new key from the user, appends +read so far. This function reads a new key from the user, appends it to KEY, and then checks if the result is a valid key sequence -or an undefined key sequence. If the result is a valid key +or an undefined key sequence. If the result is a valid key sequence for a special key command or an Emacs command, then the -command is executed. Otherwise, this function calls itself +command is executed. Otherwise, this function calls itself recursively to read yet another key from the user." (setq key (vconcat key (vector (read-key (devil--make-prompt key))))) (unless (devil--run-command key) (devil--read-key key))) -(defvar devil-prompt "Devil: %t" +(defcustom devil-prompt "Devil: %t" "A format control string that determines the Devil prompt. The following format control sequences are supported: %k - Devil key sequence read by Devil so far. %t - Emacs key sequence translated from Devil key sequence read so far. -%% - The percent sign.") +%% - The percent sign." + :type 'string) (defun devil--make-prompt (key) "Create Devil prompt based on the given KEY." + ;; If you are interested in adding Compat as a dependency, you can + ;; make use of `format-spec' without raining the minimum version. (let ((result devil-prompt) (controls (list (cons "%k" (key-description key)) (cons "%t" (devil-translate key)) @@ -210,16 +225,16 @@ (defun devil--run-command (key) "Try running the command bound to the key sequence in KEY. -KEY is a vector that represents a sequence of keystrokes. If KEY +KEY is a vector that represents a sequence of keystrokes. If KEY is found to be a special key in `devil-special-keys', the corresponding special command is executed immediately and t is returned. Otherwise, it is translated to an Emacs key sequence using -`devil-translations'. If the resulting Emacs key sequence is +`devil-translations'. If the resulting Emacs key sequence is found to be a complete key sequence, the command it is bound to -is executed interactively and t is returned. If it is found to be -an undefined key sequence, then t is returned. If the resulting +is executed interactively and t is returned. If it is found to be +an undefined key sequence, then t is returned. If the resulting Emacs key sequence is found to be an incomplete key sequence, then nil is returned." (devil--log "Trying to execute key: %s" (key-description key)) @@ -231,7 +246,7 @@ If the given key sequence KEY is found to be a special key in `devil-special-keys', the corresponding special command is -executed, and t is returned. Otherwise nil is returned." +executed, and t is returned. Otherwise nil is returned." (catch 'break (dolist (entry devil-special-keys) (when (string= (key-description key) (devil-format (car entry))) @@ -245,14 +260,14 @@ After translating KEY to an Emacs key sequence, if the resulting key sequence turns out to be an incomplete key, then nil is -returned. If it turns out to be a complete key sequence, the -corresponding Emacs command is executed, and t is returned. If it -turns out to be an undefined key sequence, t is returned. The +returned. If it turns out to be a complete key sequence, the +corresponding Emacs command is executed, and t is returned. If it +turns out to be an undefined key sequence, t is returned. The return value t indicates to the caller that no more Devil key sequences should be read from the user." (let* ((described-key (key-description key)) (translated-key (devil-translate key)) - (parsed-key (condition-case nil (kbd translated-key) (error nil))) + (parsed-key (condition-case nil (kbd translated-key) (error nil))) ;or `ignore-errors' (binding (when parsed-key (key-binding parsed-key)))) (cond ((string-match "[ACHMSs]-$" translated-key) (devil--log "Ignoring incomplete key: %s => %s" @@ -307,27 +322,27 @@ "Update variables that maintain command loop information. The given KEY and BINDING is used to update variables that -maintain command loop information. This allows the commands that +maintain command loop information. This allows the commands that depend on them behave as if they were being invoked directly with the original Emacs key sequence." ;; ;; Set `last-command-event' so that `digit-argument' can determine - ;; the correct digit for key sequences like , 5 (C-5). See M-x + ;; the correct digit for key sequences like , 5 (C-5). See M-x ;; find-function RET digit-argument RET for details. (setq last-command-event (aref key (- (length key) 1))) ;; ;; Set `this-command' to make several commands like , z SPC , z SPC - ;; (C-SPC C-SPC) and , p (C-p) work correctly. Emacs copies - ;; `this-command' to `last-command'. Both variables are used by + ;; (C-SPC C-SPC) and , p (C-p) work correctly. Emacs copies + ;; `this-command' to `last-command'. Both variables are used by ;; `set-mark-command' to decide whether to activate/deactivate the - ;; current mark. The first variable is used by vertical motion - ;; commands to keep the cursor at the `temporary-goal-column'. There + ;; current mark. The first variable is used by vertical motion + ;; commands to keep the cursor at the `temporary-goal-column'. There ;; may be other commands too that depend on this variable. (setq this-command binding) ;; ;; Set `real-this-command' to make , x z (C-x z) work correctly. ;; Emacs copies it to `last-repeatable-command' which is then used - ;; by repeat. See the following for more details: + ;; by repeat. See the following for more details: ;; ;; - M-x find-function RET repeat RET ;; - C-h v last-repeatable-command RET @@ -337,11 +352,12 @@ (defun devil--log-command-loop-info () "Log command loop information for debugging purpose." (devil--log - (concat "Found " - (format "current-prefix-arg: %s; " current-prefix-arg) - (format "this-command: %s; " this-command) - (format "last-command: %s; " last-command) - (format "last-repeatable-command: %s" last-repeatable-command)))) + (format "Found current-prefix-arg: %s; \ +this-command: %s; last-command: %s; last-repeatable-command: %s" + current-prefix-arg + this-command + last-command + last-repeatable-command))) (defun devil--repeatable-key-p (described-key) "Return t iff DESCRIBED-KEY belongs to `devil-repeatable-keys'." @@ -379,28 +395,25 @@ (when devil-logging (apply #'message (concat "Devil: " format-string) args))) -(defmacro devil--assert (form) - "Evaluate FORM and cause error if the result is nil." - `(unless ,form - (error "Assertion failed: %s" ',form))) - -(defun devil--tests () - "Test Devil functions assuming Devil has not been customized." - (devil--assert (devil--invalid-key-p "")) - (devil--assert (devil--invalid-key-p "C-x-C-f")) - (devil--assert (devil--invalid-key-p "C-x CC-f")) - (devil--assert (not (devil--invalid-key-p "C-x C-f"))) - (devil--assert (not (devil--invalid-key-p "C-M-x"))) - (devil--assert (string= (devil-translate (vconcat ",")) "C-")) - (devil--assert (string= (devil-translate (vconcat ",x")) "C-x")) - (devil--assert (string= (devil-translate (vconcat ",x,")) "C-x C-")) - (devil--assert (string= (devil-translate (vconcat ",x,f")) "C-x C-f")) - (devil--assert (string= (devil-translate (vconcat ",,")) ",")) - (devil--assert (string= (devil-translate (vconcat ",,,,")) ", ,")) - (devil--assert (string= (devil-translate (vconcat ",mx")) "C-M-x")) - (devil--assert (string= (devil-translate (vconcat ",mmx")) "M-x")) - (devil--assert (string= (devil-translate (vconcat ",mmm")) "M-m")) - (message "Done")) +(ert-deftest devil-invalid-key-p () + "Test if `devil--invalid-key-p' words as expected." + (should (devil--invalid-key-p "")) + (should (devil--invalid-key-p "C-x-C-f")) + (should (devil--invalid-key-p "C-x CC-f")) + (should (not (devil--invalid-key-p "C-x C-f"))) + (should (not (devil--invalid-key-p "C-M-x")))) + +(ert-deftest devil-translate () + "Test if `devil-translate' works as expected." + (should (string= (devil-translate (vconcat ",")) "C-")) + (should (string= (devil-translate (vconcat ",x")) "C-x")) + (should (string= (devil-translate (vconcat ",x,")) "C-x C-")) + (should (string= (devil-translate (vconcat ",x,f")) "C-x C-f")) + (should (string= (devil-translate (vconcat ",,")) ",")) + (should (string= (devil-translate (vconcat ",,,,")) ", ,")) + (should (string= (devil-translate (vconcat ",mx")) "C-M-x")) + (should (string= (devil-translate (vconcat ",mmx")) "M-x")) + (should (string= (devil-translate (vconcat ",mmm")) "M-m"))) (provide 'devil) Diff finished. Tue May 9 10:39:57 2023 --=-=-= Content-Type: text/plain > Also, see https://www.reddit.com/r/emacs/comments/13aj99j/ for some > discussion on this new package. > > Please let me know if this package can be added to NonGNU ELPA. If there > are any questions or feedback about this, please let me know. I see no reason why not. Thanks for contributing. One point I have already mentioned on IRC is that a separate manual and a shorter README would be nice. We can generate a TeXinfo manual from either .texi or .org files, would you be interested in setting that up? > Regards, > Susam > From 3499d93502b130b1dcb8a648e73e7a614cd24abd Mon Sep 17 00:00:00 2001 > From: Susam Pal > Date: Tue, 9 May 2023 02:36:08 +0100 > Subject: [PATCH] * elpa-packages (devil): New package > > --- > elpa-packages | 2 ++ > 1 file changed, 2 insertions(+) > > diff --git a/elpa-packages b/elpa-packages > index c333cc8bb3..f6fd68edaf 100644 > --- a/elpa-packages > +++ b/elpa-packages > @@ -112,6 +112,8 @@ > > (devhelp :url "https://codeberg.org/akib/emacs-devhelp") > > + (devil :url "https://github.com/susam/devil") As you have a CHANGES.md file, we can add a :news entry here as well. > + > (diff-ansi :url "https://codeberg.org/ideasman42/emacs-diff-ansi" > :ignored-files ("LICENSE")) --=-=-=--