Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add quick keys #479

Merged
merged 34 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6f39853
Add quick keys
clemera Mar 2, 2021
90ec66e
Merge branch 'master' into add-quick-keys
clemera Mar 2, 2021
f7886e4
Add keys
clemera Mar 2, 2021
8810fe9
Fix linting errors
clemera Mar 2, 2021
7766492
Cleanup
clemera Mar 2, 2021
0d47edc
Change default binding
clemera Mar 3, 2021
9417d31
Add face for quick chars
clemera Mar 3, 2021
ccf5273
Change face
clemera Mar 3, 2021
3483f1b
Change face name
clemera Mar 3, 2021
1502409
Change face
clemera Mar 3, 2021
1c2b8e5
Merge branch 'master' into add-quick-keys
clemera Mar 3, 2021
80a0fee
Skip invisible parts when replacing parts for keys
clemera Mar 3, 2021
c297875
Skip invisible parts in general
clemera Mar 3, 2021
f148fde
Add match face
clemera Mar 4, 2021
7c8ec92
Rephrase docstring
clemera Mar 4, 2021
0ee91f6
Give feedback for wrong configured quick keys
clemera Mar 4, 2021
6c015b2
Change faces
clemera Mar 4, 2021
81454d7
Adjust docstring
clemera Mar 4, 2021
f5cfaa0
Merge branch 'add-quick-keys' of github.com:clemera/selectrum into ad…
clemera Mar 5, 2021
4e6b10f
Merge branch 'master' into add-quick-keys
clemera Mar 5, 2021
8b96e72
Refactor for post formatting
clemera Mar 6, 2021
dcca1c5
Merge branch 'add-quick-keys' into refactor-for-post-formatting-test-…
clemera Mar 6, 2021
ac66e50
Merge branch 'master' into add-quick-keys
clemera Mar 6, 2021
cc0b47b
Update changelog
clemera Mar 7, 2021
f29a26c
Merge branch 'master' into add-quick-keys
clemera Mar 7, 2021
ba5cc2f
Update readme
clemera Mar 7, 2021
ef485db
Fix punctuation
clemera Mar 7, 2021
9208444
Rephrase
clemera Mar 7, 2021
2cba5f8
Rephrase
clemera Mar 7, 2021
4bcc243
Refactor
clemera Mar 9, 2021
1b4d19f
Improve exit behavior
clemera Mar 9, 2021
d9bd0ec
Error when to few candidates
clemera Mar 9, 2021
acecceb
Fix indent
clemera Mar 9, 2021
1246b77
Also reset for non char input events
clemera Mar 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ The format is based on [Keep a Changelog].
([#313], [#466]).

### Features
* You can quickly select and insert a candidate using the commands
`selectrum-quick-select` and `selectrum-quick-insert` which are
bound to `M-i` and `M-m` (analogue to `C-i` and `C-m` used for
normal insertion and selection). These commands are similar in
functionality to `ivy-avy`. You can configure the used keys via
`selectrum-quick-keys` option and their appearance via
`selectrum-quick-keys-highlight` and `selectrum-quick-keys-match`
face ([#16], [#304], [#479]).
* Add support for `x-group-function` completion metadata. The group
title formatting is controlled by the customization variable
`selectrum-group-format` and the faces `selectrum-group-separator`
Expand Down Expand Up @@ -41,6 +49,8 @@ packages. Users of `selectrum-prescient` can update to configure
candidate and exiting by pressing `RET` no longer fails when there
are existing candidates already selected using `TAB` ([#460]).

[#16]: https://github.com/raxod502/selectrum/issues/16
[#304]: https://github.com/raxod502/selectrum/issues/304
[#313]: https://github.com/raxod502/selectrum/issues/313
[#419]: https://github.com/raxod502/selectrum/issues/419
[#450]: https://github.com/raxod502/selectrum/issues/450
Expand All @@ -54,6 +64,7 @@ packages. Users of `selectrum-prescient` can update to configure
[#463]: https://github.com/raxod502/selectrum/pull/463
[#465]: https://github.com/raxod502/selectrum/pull/465
[#466]: https://github.com/raxod502/selectrum/pull/466
[#479]: https://github.com/raxod502/selectrum/pull/479
[#488]: https://github.com/raxod502/selectrum/pull/488

## 3.1 (released 2021-02-21)
Expand Down
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,24 +146,26 @@ how to fix it.
* *To navigate to a candidate:* use the standard motion commands
(`<up>`, `<down>`, `C-v`, `M-v`, `M-<`, `M->`). If you prefer, you
can use `C-p` and `C-n` instead of the arrow keys.
* *To accept the currently selected candidate:* type `RET`. (With a
prefix argument, accept instead the candidate at that point in the
list, counting from one. See `selectrum-show-indices`. The value
zero means to accept exactly what you've typed, as in the next
* *To accept the currently selected candidate:* type `RET`/`C-m`.
(With a prefix argument, accept instead the candidate at that point
in the list, counting from one. See `selectrum-show-indices`. The
value zero means to accept exactly what you've typed, as in the next
bullet point.) You can also click the left mouse button on a
candidate to choose it.
candidate to choose it or use `M-m` to select one using
`selectrum-quick-keys`.
* *To submit what you've typed, even if it's not a candidate:* you can
use `<up>` or `C-p` to select the user input just like a regular
candidate, and type `RET` as usual. (Alternatively, you can type
`C-j` to submit your exact input without selecting it first.)
* *To abort:* as per usual, type `C-g`.
* *To navigate into the currently selected directory while finding a
file\:* type `TAB`. (What this actually does is insert the currently
selected candidate into the minibuffer, which for `find-file` has
the effect of navigating into a directory.) With a positive prefix
argument, insert the candidate at that display position (see
`selectrum-show-indices`). You can also right click on a candidate
to insert it into the minibuffer.
file\:* type `TAB`/`C-i`. (What this actually does is insert the
currently selected candidate into the minibuffer, which for
`find-file` has the effect of navigating into a directory.) With a
positive prefix argument, insert the candidate at that display
position (see `selectrum-show-indices`). You can also right click on
a candidate to insert it into the minibuffer or use `M-i` for
inserting one using `selectrum-quick-keys`.
* *To copy the current candidate:* type `M-w` or what is bound to
`kill-ring-save`. When there's an active region in your input, this
still copies the active region. The behavior of `M-w` is not
Expand Down Expand Up @@ -336,6 +338,13 @@ used refinement function. The built-in `completion-styles` support the
`selectrum-completion-in-region-styles`.
* The option `selectrum-should-sort` controls whether preprocessing
functions should sort.
* You can configure the keys for quick candidate insertion and
selection using `selectrum-quick-keys`. These are used when using
the commands `selectrum-quick-select` or `selectrum-quick-insert`
which provide you an `ivy-avy` like interface to quickly select a
candidate via key annotations. You can configure the appearance of
these key annotations with `selectrum-quick-keys-highlight` and
`selectrum-quick-keys-match` face.

### Complementary extensions

Expand Down
109 changes: 109 additions & 0 deletions selectrum.el
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ list of strings."

;;; Faces

(defface selectrum-quick-keys-highlight
'((t :inherit lazy-highlight))
"Face used for `selectrum-quick-keys'."
:group 'selectrum-faces)

(defface selectrum-quick-keys-match
'((t :inherit isearch))
"Face used for matches of `selectrum-quick-keys'."
:group 'selectrum-faces)

(defface selectrum-group-title
'((t :inherit shadow :slant italic))
"Face used for the title text of the candidate group headlines."
Expand Down Expand Up @@ -150,6 +160,11 @@ parts of the input."
"Format string for the default value in the minibuffer."
:type '(choice (const nil) string))

(defcustom selectrum-quick-keys '(?a ?s ?d ?f ?j ?k ?l ?i ?g ?h)
"Keys for quick selection.
Used by `selectrum-quick-select' and `selectrum-quick-insert'."
:type 'character)

(defcustom selectrum-group-format
(concat
#(" " 0 4 (face selectrum-group-separator))
Expand Down Expand Up @@ -521,6 +536,8 @@ function and BODY opens the minibuffer."
(define-key map (kbd "C-j") #'selectrum-submit-exact-input)
(define-key map (kbd "TAB") #'selectrum-insert-current-candidate)
(define-key map (kbd "M-q") 'selectrum-cycle-display-style)
(define-key map (kbd "M-i") 'selectrum-quick-insert)
(define-key map (kbd "M-m") 'selectrum-quick-select)
;; Return the map.
map)
"Keymap used by Selectrum in the minibuffer.")
Expand All @@ -533,6 +550,12 @@ at the start of the list.")
(defvar selectrum--display-action-buffer " *selectrum*"
"Buffer to display candidates using `selectrum-display-action'.")

(defvar selectrum--quick-fun nil
"Function for quick selection.
Used by `selectrum-quick-select' and `selectrum-quick-insert'.
Receives the display index and candidate and should return the
new candidate string used for display.")

(defvar selectrum--crm-separator-alist
'((":\\|,\\|\\s-" . ",")
("[ \t]*:[ \t]*" . ":")
Expand Down Expand Up @@ -1778,6 +1801,10 @@ which is displayed in the UI."
(when hl
(setq displayed-candidate
(selectrum--selection-highlight displayed-candidate)))
(when (and selectrum--quick-fun
(not hl))
(setq displayed-candidate
(funcall selectrum--quick-fun display-index displayed-candidate)))
(when-let (show-indices
(cond
((functionp selectrum-show-indices) selectrum-show-indices)
Expand Down Expand Up @@ -2105,6 +2132,88 @@ Only to be used from `selectrum-select-from-history'"
(propertize (selectrum-get-current-candidate 'notfull)
'selectum--insert t)))

(defun selectrum--quick-keys (len keys)
"Get list of key combinations up to key length LEN.
KEYS is a list of key strings to combine."
(unless (zerop len)
(cl-loop with list = keys
with olist = keys
repeat (1- len)
do (setq olist
(cl-loop
for char in list
nconc (cl-loop for ochar in olist
collect (concat char ochar))))
finally return olist)))

(defun selectrum--quick-read ()
"Read index interactively using `selectrum-quick-keys'."
(unless (cdr selectrum-quick-keys)
(user-error "`selectrum-quick-keys' needs at least two keys"))
(when (< selectrum--actual-num-candidates-displayed 2)
(user-error "No candidates for quick selection"))
(let* ((qkeys (mapcar #'char-to-string selectrum-quick-keys))
(nkeys (length qkeys))
(needed selectrum--actual-num-candidates-displayed)
(len (ceiling (log needed nkeys)))
(keys (seq-take (selectrum--quick-keys len qkeys) needed))
(input nil)
(read-char (lambda ()
(let ((char nil))
(unwind-protect
(when (characterp (setq char (read-char)))
char)
(when (or (eq ?\C-g char)
(not (characterp char)))
(let ((selectrum--quick-fun nil))
(selectrum--update)))))))
(selectrum--quick-fun
(lambda (i cand)
(let ((str (propertize (or (nth i keys) "")
'face 'selectrum-quick-keys-highlight)))
(when (and input (string-match (concat "\\`" input) str))
(setq str (copy-sequence str))
(add-face-text-property 0 (match-end 0)
'selectrum-quick-keys-match nil str))
(concat str (substring cand (min (length cand)
(length str))))))))
(if-let* ((input
(cl-loop with pressed = 0
while (< pressed len)
do (selectrum--update)
for char = (funcall read-char)
for key = (when char
(char-to-string char))
if (and (not (zerop pressed))
(equal char ?\C-?))
do (setq pressed (1- pressed)
input (substring
input 0 (1- (length input))))
else if (not (member key qkeys))
return nil
else
do (setq pressed (1+ pressed)
input (concat input key))
finally return input))
(pos (cl-position input keys :test #'string=)))
(+ selectrum--first-index-displayed pos)
(prog1 nil
(message "No matching key")))))

(defun selectrum-quick-select ()
"Select a candidate using `selectrum-quick-keys'."
(interactive)
(when-let (index (selectrum--quick-read))
(let ((selectrum--current-candidate-index index))
(selectrum-select-current-candidate))))

(defun selectrum-quick-insert ()
"Insert a candidate using `selectrum-quick-keys'."
(interactive)
(when-let (index (selectrum--quick-read))
(let ((selectrum--current-candidate-index index))
(selectrum-insert-current-candidate))))

;;; Main entry points

(cl-defun selectrum--read
Expand Down