Skip to content

Commit 71e2a2b

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 b275e58 commit 71e2a2b

File tree

1 file changed

+102
-31
lines changed

1 file changed

+102
-31
lines changed

resize-window.el

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

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

236+
(defun resize-window--match-dispatch (key)
237+
"Taken the KEY or keyboard selection check for an action.
238+
Match the KEY against the alias table `resize-window-dispatch-alist'."
239+
(resize-window--key-element
240+
key resize-window-dispatch-alist))
241+
180242
(defun resize-window--choice-keybinding (choice)
181243
"Get the keybinding associated with CHOICE."
182244
(car choice))
@@ -201,8 +263,12 @@ nil."
201263
CHOICE is a \(key function documentation allows-capitals\)."
202264
(let ((key (resize-window--choice-keybinding choice)))
203265
(concat (if (resize-window--allows-capitals choice)
204-
(format "%s|%s" (string key) (string (- key 32)))
205-
(format " %s " (string key)))
266+
(format "%s|%s"
267+
(resize-window--key-str key)
268+
(resize-window--key-str
269+
(resize-window--key-to-upper key)))
270+
(format " %s "
271+
(resize-window--key-str key)))
206272
" : "
207273
(resize-window--choice-documentation choice))))
208274

@@ -238,7 +304,8 @@ CHOICE is a \(key function documentation allows-capitals\).
238304
If SCALED, then call action with the `resize-window-uppercase-argument'."
239305
(let ((action (resize-window--choice-lambda choice))
240306
(description (resize-window--choice-documentation choice)))
241-
(unless (equal (resize-window--choice-keybinding choice) ??)
307+
(unless (resize-window--keys-equal
308+
(resize-window--choice-keybinding choice) [??])
242309
(resize-window--notify "%s" description))
243310
(condition-case nil
244311
(if scaled
@@ -247,7 +314,7 @@ If SCALED, then call action with the `resize-window-uppercase-argument'."
247314
(wrong-number-of-arguments
248315
(resize-window--notify
249316
"Invalid arity in function for %s"
250-
(char-to-string
317+
(resize-window--key-str
251318
(resize-window--choice-keybinding choice)))))))
252319

253320
;;;###autoload
@@ -260,37 +327,37 @@ to resize right."
260327
;; NOTE: Do not trim the stack here. Let stack requests to handle
261328
;; window configurations in excess.
262329
(resize-window--add-backgrounds)
263-
(resize-window--notify "Resize mode: enter character, ? for help")
330+
(resize-window--notify "Resize mode: insert KEY, ? for help")
264331
(condition-case nil
265-
(let ((reading-characters t)
332+
(let ((reading-keys t)
266333
;; allow mini-buffer to collapse after displaying menu
267334
(resize-mini-windows t))
268-
(while reading-characters
269-
(let* ((char (resize-window--match-alias (read-key)))
270-
(choice (assoc char resize-window-dispatch-alist))
271-
(capital (when (numberp char)
272-
(assoc (+ char 32) resize-window-dispatch-alist))))
335+
(while reading-keys
336+
(let* ((kin (read-key-sequence-vector nil nil t))
337+
(key (and kin (resize-window--match-alias kin)))
338+
(choice (and key (resize-window--match-dispatch key)))
339+
(lower (and key (resize-window--key-to-lower key)))
340+
(capital (and lower (resize-window--match-dispatch lower))))
273341
(cond
274342
(choice (resize-window--execute-action choice))
275343
((and capital (resize-window--allows-capitals capital))
276344
;; rather than pass an argument, we tell it to "scale" it
277345
;; with t and that method can worry about how to get that
278346
;; action
279347
(resize-window--execute-action capital t))
280-
(;; NOTE: Don't use `=', if `char' is a symbol like
281-
;; 'insertchar it will fail. Use `equal' instead.
282-
(or resize-window-unregistered-key-quit
283-
(equal char ?q)
284-
(equal char ?Q)
285-
(equal char (string-to-char " ")))
286-
(setq reading-characters nil)
348+
((or resize-window-unregistered-key-quit
349+
(resize-window--keys-equal key [?q])
350+
(resize-window--keys-equal key [?Q])
351+
(resize-window--keys-equal key [? ])
352+
(resize-window--keys-equal key "C-g"))
353+
(setq reading-keys nil)
287354
(resize-window--display-menu 'kill)
288355
(resize-window--remove-backgrounds))
289356
(t
290357
(resize-window--notify
291358
(format
292-
"Unregistered key: (%s) %s"
293-
char (single-key-description char))))))))
359+
"Unregistered key: %s -> %s"
360+
key (resize-window--key-str key))))))))
294361
(quit
295362
(resize-window--display-menu 'kill)
296363
(resize-window--remove-backgrounds))))
@@ -539,22 +606,26 @@ See also `resize-window-stack-size'."
539606

540607
(defun resize-window--key-available? (key)
541608
"Return non-nil if KEY is bound, otherwise return nil."
542-
(and (not (assoc key resize-window-alias-list))
543-
(not (assoc key resize-window-dispatch-alist))))
609+
(and (not (resize-window--key-element
610+
key resize-window-alias-list))
611+
(not (resize-window--key-element
612+
key resize-window-dispatch-alist))))
544613

545614
(defun resize-window-add-choice (key func doc &optional allows-capitals)
546615
"Register a new binding for `resize-window'.
547-
KEY is the char (eg ?c) that should invoke the FUNC. DOC is a doc
548-
string for the help menu, and optional ALLOWS-CAPITALS should be
549-
t or nil. Functions should be of zero arity if they do not allow
550-
capitals, and should be of optional single arity if they allow
551-
capitals. Invoking with the capital will pass the capital
552-
argument."
616+
617+
KEY is the key (e.g. ?c) that invokes the function FUNC. DOC is a
618+
docstring for the help menu. A non-nil ALLOWS-CAPITALS tells FUNC
619+
accepts capital letters. FUNC should be of zero arity if does not
620+
allow capitals, otherwise to allow capitals should be of optional
621+
single arity so a capital KEY may be passed to FUNC when pressed.
622+
623+
See also `resize-window--key-str'."
553624
(if (resize-window--key-available? key)
554625
(push (list key func doc allows-capitals)
555626
resize-window-dispatch-alist)
556627
(message "The `%s` key is already taken for resize-window."
557-
(char-to-string key))))
628+
(resize-window--key-str key))))
558629

559630
(provide 'resize-window)
560631
;;; resize-window.el ends here

0 commit comments

Comments
 (0)