Skip to content

Commit 7b2a897

Browse files
committed
Capture key combinations like "M-w"
Support symbol, character (integer), key text, or key sequence as key. NOTE: `read-key' cannot capture "M-w", use `read-key-sequence-vector'.
1 parent e6f3d30 commit 7b2a897

File tree

1 file changed

+100
-30
lines changed

1 file changed

+100
-30
lines changed

resize-window.el

Lines changed: 100 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,78 @@ This is just a pass through to message usually. However, it can be
161161
overridden in tests to test the output of message."
162162
(when resize-window-notify-with-messages (apply #'message info)))
163163

164+
(defun resize-window--key-str (key)
165+
"Return the string representation of KEY.
166+
KEY is a symbol, character (integer), key text, or key sequence.
167+
168+
For instance, ?n \"n\" [?n] [(?n)] are considered the same, and
169+
?\\C-n \"C-n\" \"\\C-n\" [?\\C-n] [(?\\C-n)] [(control ?n)] too."
170+
;; NOTE: Fail loudly when KEY is wrong to help debugging.
171+
(key-description
172+
(cond
173+
((and (not (booleanp key))
174+
(or (symbolp key) (integerp key)))
175+
(vector key))
176+
((stringp key)
177+
(kbd key))
178+
((vectorp key)
179+
key)
180+
(t
181+
(signal 'wrong-type-argument
182+
`((symbolp integerp stringp vectorp) ,key))))))
183+
184+
(defun resize-window--keys-equal (&rest keys)
185+
"Return non-nil if KEYS are considered equal.
186+
If there is only one key return non-nil."
187+
(let ((key-str (resize-window--key-str (car keys))))
188+
(not (find-if-not
189+
(lambda (k)
190+
(string= key-str (resize-window--key-str k)))
191+
(cdr keys)))))
192+
193+
(defun resize-window--key-to-lower (key)
194+
"Return the lowercase key sequence of KEY.
195+
Return nil if KEY isn't an uppercase letter."
196+
(let* ((key-str (resize-window--key-str key))
197+
(char (if (= (length key-str) 1) (string-to-char key-str))))
198+
(and char
199+
(member char resize-window--capital-letters)
200+
(vector (+ char 32)))))
201+
202+
(defun resize-window--key-to-upper (key)
203+
"Return the uppercase key sequence of KEY.
204+
Return nil if KEY isn't an lowercase letter."
205+
(let* ((key-str (resize-window--key-str key))
206+
(char (if (= (length key-str) 1) (string-to-char key-str))))
207+
(and char
208+
(member char resize-window--lower-letters)
209+
(vector (- char 32)))))
210+
211+
(defun resize-window--key-element (key sequence)
212+
"Return the first element in SEQUENCE whose car equals KEY."
213+
(let ((key-str (resize-window--key-str key)))
214+
(assoc-if
215+
(lambda (k)
216+
(string= key-str (resize-window--key-str k)))
217+
sequence)))
218+
164219
(defun resize-window--match-alias (key)
165-
"Taken the KEY or keyboard selection from `read-key' check for alias.
220+
"Taken the KEY or keyboard selection check for alias.
166221
Match the KEY against the alias table. If found, return the value that it
167222
points to, which should be a key in the `resize-window-dispatch-alist'.
168223
Otherwise, return the KEY."
169-
(let ((alias (assoc key resize-window-alias-list)))
224+
(let ((alias (resize-window--key-element
225+
key resize-window-alias-list)))
170226
(if alias
171227
(car (cdr alias))
172228
key)))
173229

230+
(defun resize-window--match-dispatch (key)
231+
"Taken the KEY or keyboard selection check for an action.
232+
Match the KEY against the alias table `resize-window-dispatch-alist'."
233+
(resize-window--key-element
234+
key resize-window-dispatch-alist))
235+
174236
(defun resize-window--choice-keybinding (choice)
175237
"Get the keybinding associated with CHOICE."
176238
(car choice))
@@ -195,8 +257,11 @@ nil."
195257
CHOICE is a \(key function documentation allows-capitals\)."
196258
(let ((key (resize-window--choice-keybinding choice)))
197259
(concat (if (resize-window--allows-capitals choice)
198-
(format "%s|%s" (string key) (string (- key 32)))
199-
(format " %s " (string key)))
260+
(format "%s|%s"
261+
(resize-window--key-str key)
262+
(resize-window--key-to-upper key))
263+
(format " %s "
264+
(resize-window--key-str key)))
200265
" : "
201266
(resize-window--choice-documentation choice))))
202267

@@ -232,7 +297,8 @@ CHOICE is a \(key function documentation allows-capitals\).
232297
If SCALED, then call action with the `resize-window-uppercase-argument'."
233298
(let ((action (resize-window--choice-lambda choice))
234299
(description (resize-window--choice-documentation choice)))
235-
(unless (equal (resize-window--choice-keybinding choice) ??)
300+
(unless (resize-window--keys-equal
301+
(resize-window--choice-keybinding choice) [??])
236302
(resize-window--notify "%s" description))
237303
(condition-case nil
238304
(if scaled
@@ -241,7 +307,7 @@ If SCALED, then call action with the `resize-window-uppercase-argument'."
241307
(wrong-number-of-arguments
242308
(resize-window--notify
243309
"Invalid arity in function for %s"
244-
(char-to-string
310+
(resize-window--key-str
245311
(resize-window--choice-keybinding choice)))))))
246312

247313
;;;###autoload
@@ -254,36 +320,36 @@ to resize right."
254320
;; NOTE: Do not trim the stack here. Let stack requests to handle
255321
;; window configurations in excess.
256322
(resize-window--add-backgrounds)
257-
(resize-window--notify "Resize mode: enter character, ? for help")
323+
(resize-window--notify "Resize mode: insert KEY, ? for help")
258324
(condition-case nil
259-
(let ((reading-characters t)
325+
(let ((reading-keys t)
260326
;; allow mini-buffer to collapse after displaying menu
261327
(resize-mini-windows t))
262-
(while reading-characters
263-
(let* ((char (resize-window--match-alias (read-key)))
264-
(choice (assoc char resize-window-dispatch-alist))
265-
(capital (when (numberp char)
266-
(assoc (+ char 32) resize-window-dispatch-alist))))
328+
(while reading-keys
329+
(let* ((kin (read-key-sequence-vector nil nil t))
330+
(key (and kin (resize-window--match-alias kin)))
331+
(choice (and key (resize-window--match-dispatch key)))
332+
(lower (and key (resize-window--key-to-lower key)))
333+
(capital (and lower (resize-window--match-dispatch lower))))
267334
(cond
268335
(choice (resize-window--execute-action choice))
269336
((and capital (resize-window--allows-capitals capital))
270337
;; rather than pass an argument, we tell it to "scale" it
271338
;; with t and that method can worry about how to get that
272339
;; action
273340
(resize-window--execute-action capital t))
274-
(;; NOTE: Don't use `=', if `char' is a symbol like
275-
;; 'insertchar it will fail. Use `equal' instead.
276-
(or (equal char ?q)
277-
(equal char ?Q)
278-
(equal char (string-to-char " ")))
279-
(setq reading-characters nil)
341+
((or (resize-window--keys-equal key [?q])
342+
(resize-window--keys-equal key [?Q])
343+
(resize-window--keys-equal key [? ])
344+
(resize-window--keys-equal key "C-g"))
345+
(setq reading-keys nil)
280346
(resize-window--display-menu 'kill)
281347
(resize-window--remove-backgrounds))
282348
(t
283349
(resize-window--notify
284350
(format
285-
"Unregistered key: (%s) %s"
286-
char (single-key-description char))))))))
351+
"Unregistered key: %s -> %s"
352+
key (resize-window--key-str key))))))))
287353
(quit
288354
(resize-window--display-menu 'kill)
289355
(resize-window--remove-backgrounds))))
@@ -532,22 +598,26 @@ See also `resize-window-stack-size'."
532598

533599
(defun resize-window--key-available? (key)
534600
"Return non-nil if KEY is bound, otherwise return nil."
535-
(and (not (assoc key resize-window-alias-list))
536-
(not (assoc key resize-window-dispatch-alist))))
601+
(and (not (resize-window--key-element
602+
key resize-window-alias-list))
603+
(not (resize-window--key-element
604+
key resize-window-dispatch-alist))))
537605

538606
(defun resize-window-add-choice (key func doc &optional allows-capitals)
539607
"Register a new binding for `resize-window'.
540-
KEY is the char (eg ?c) that should invoke the FUNC. DOC is a doc
541-
string for the help menu, and optional ALLOWS-CAPITALS should be
542-
t or nil. Functions should be of zero arity if they do not allow
543-
capitals, and should be of optional single arity if they allow
544-
capitals. Invoking with the capital will pass the capital
545-
argument."
608+
609+
KEY is the key (e.g. ?c) that invokes the function FUNC. DOC is a
610+
docstring for the help menu. A non-nil ALLOWS-CAPITALS tells FUNC
611+
accepts capital letters. FUNC should be of zero arity if does not
612+
allow capitals, otherwise to allow capitals should be of optional
613+
single arity so a capital KEY may be passed to FUNC when pressed.
614+
615+
See also `resize-window--key-str'."
546616
(if (resize-window--key-available? key)
547617
(push (list key func doc allows-capitals)
548618
resize-window-dispatch-alist)
549619
(message "The `%s` key is already taken for resize-window."
550-
(char-to-string key))))
620+
(resize-window--key-str key))))
551621

552622
(provide 'resize-window)
553623
;;; resize-window.el ends here

0 commit comments

Comments
 (0)