1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
|
;;; orderless.el --- Completion style for matching regexps in any order -*- lexical-binding: t; -*-
;; Copyright (C) 2020 Omar Antolín Camarena
;; Author: Omar Antolín Camarena <omar@matem.unam.mx>
;; Keywords: extensions
;; Version: 0.4
;; Homepage: https://github.com/oantolin/orderless
;; Package-Requires: ((emacs "24.4"))
;; 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 3 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, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This package provides an `orderless' completion style that divides
;; the pattern into components (space-separated by default), and
;; matches candidates that match all of the components in any order.
;; Completion styles are used as entries in the variables
;; `completion-styles' and `completion-category-overrides', see their
;; documentation.
;; To use this completion style you can use the following minimal
;; configuration:
;; (setq completion-styles '(orderless))
;; You can customize the `orderless-component-separator' to decide how
;; the input pattern is split into component regexps. The default
;; splits on spaces. You might want to add hyphens and slashes, for
;; example, to ease completion of symbols and file paths,
;; respectively.
;; Each component can match in any one of several matching styles:
;; literally, as a regexp, as an initialism, in the flex style, or as
;; word prefixes. It is easy to add new styles: they are functions
;; from strings to strings that map a component to a regexp to match
;; against. The variable `orderless-component-matching-styles' lists
;; the matching styles to be used for components, by default it allows
;; regexp and initialism matching.
;;; Code:
(require 'cl-lib)
(defgroup orderless nil
"Completion method that matches space-separated regexps in any order."
:group 'completion)
(defface orderless-match-face-0
'((default :weight bold)
(((class color) (min-colors 88) (background dark)) :foreground "#72a4ff")
(((class color) (min-colors 88) (background light)) :foreground "#223fbf")
(t :foreground "blue"))
"Face for matches of components numbered 0 mod 4."
:group 'orderless)
(defface orderless-match-face-1
'((default :weight bold)
(((class color) (min-colors 88) (background dark)) :foreground "#ed92f8")
(((class color) (min-colors 88) (background light)) :foreground "#8f0075")
(t :foreground "magenta"))
"Face for matches of components numbered 1 mod 4."
:group 'orderless)
(defface orderless-match-face-2
'((default :weight bold)
(((class color) (min-colors 88) (background dark)) :foreground "#90d800")
(((class color) (min-colors 88) (background light)) :foreground "#145a00")
(t :foreground "green"))
"Face for matches of components numbered 2 mod 4."
:group 'orderless)
(defface orderless-match-face-3
'((default :weight bold)
(((class color) (min-colors 88) (background dark)) :foreground "#f0ce43")
(((class color) (min-colors 88) (background light)) :foreground "#804000")
(t :foreground "yellow"))
"Face for matches of components numbered 3 mod 4."
:group 'orderless)
(defcustom orderless-component-separator " +"
"Regexp to match component separators for orderless completion.
This is passed to `split-string' to divide the pattern into
component regexps."
:type '(choice (const :tag "Spaces" " +")
(const :tag "Spaces, hyphen or slash" " +\\|[-/]")
(regexp :tag "Custom regexp"))
:group 'orderless)
(defcustom orderless-transient-component-separator nil
"Override for `orderless-component-separator'.
This variable should be either nil or a regexp, when it is a
regexp it overrides `orderless-component-separator'. It is meant
to be set by commands that interactively change the separator.
No such commands are provided with this package, but this
variable is meant to make writing them simple. If you do use
this variable you are likely to want to reset it to nil after
every completion session, which can be achieved by adding the
function `orderless-remove-transient-configuration' to the
`minibuffer-exit-hook'."
:type '(choice string nil)
:group 'orderless)
(defcustom orderless-match-faces
[orderless-match-face-0
orderless-match-face-1
orderless-match-face-2
orderless-match-face-3]
"Vector of faces used (cyclically) for component matches."
:type '(vector 'face)
:group 'orderless)
(defcustom orderless-component-matching-styles
'(orderless-regexp orderless-initialism)
"Component matching style configuration.
This should be a list of functions optionally with the symbols
`:global-dispatchers' and `:component-dispatcher' used to delimit
portions:
(matching-styles...
:global-dispatchers global-dispatchers...
:component-dispatcher component-dispatchers...)
Here matching-styles... denotes a sequence of matching style
functions, and each of the *-dispatcher... denotes a sequence of
style dispatcher functions. The `:global-dispatchers' and
`:component-dispatcher' sections are both optional and can occur
in either order.
If this variable is nil, regexp matching is assumed.
A matching style is simply a function from strings to strings
that takes a component to a regexp to match against. If the
resulting regexp has no capturing groups, the entire match is
highlighted, otherwise just the captured groups are.
The list of matching styles at the beginning provides the default
matching styles for components when the global style dispatchers
decline to compute a list of default matching styles. (Even if
those dispatchers do compute a list of matching styles, it is
only the default used for components, and can be overridden for
individual components by the component style dispatchers).
For information on how the lists of dispatchers are used, see
`orderless-dispatch'."
:type '(set
(const :tag "Regexp" orderless-regexp)
(const :tag "Literal" orderless-literal)
(const :tag "Initialism" orderless-initialism)
(const :tag "Strict initialism" orderless-strict-initialism)
(const :tag "Strict leading initialism"
orderless-strict-leading-initialism)
(const :tag "Strict full initialism"
orderless-strict-full-initialism)
(const :tag "Flex" orderless-flex)
(const :tag "Prefixes" orderless-prefixes)
(const :tag "Global dispatchers" :global-dispatchers)
(const :tag "Component dispatchers" :component-dispatchers)
(function :tag "Custom function"))
:group 'orderless)
(defcustom orderless-transient-matching-styles nil
"Component matching style configuration override.
This variable has the format and meaning as
`orderless-component-matching-styles' and overrides it when this
one is non-nil. It is meant to be set by commands that
interactively change the matching style configuration. No such
commands are provided with this package, but this variable is
meant to make writing them simple. If you do use this variable
you are likely to want to reset it to nil after every completion
session, which can be achieved by adding the function
`orderless-remove-transient-configuration' to the
`minibuffer-exit-hook'."
:type 'sexp
:group 'orderless)
(defcustom orderless-pattern-compiler #'orderless-default-pattern-compiler
"The `orderless' pattern compiler.
This should be a function that takes an input pattern and returns
a list of regexps that must all match a candidate in order for
the candidate to be considered a completion of the pattern.
The default pattern compiler is probably flexible enough for most
users. See `orderless-default-pattern-compiler' for details.
The documentation for `orderless-component-matching-styles' is
written assuming the default pattern compiler is used, if you
change the pattern compiler it can, of course, do anything and
need not consult this variable at all."
:type 'function
:group 'orderless)
;;; Matching styles
(defalias 'orderless-regexp #'identity
"Match a component as a regexp.
This is simply the identity function.")
(defalias 'orderless-literal #'regexp-quote
"Match a component as a literal string.
This is simply `regexp-quote'.")
(defun orderless--separated-by (sep rxs &optional before after)
"Return a regexp to match the rx-regexps RXS with SEP in between.
If BEFORE is specified, add it to the beginning of the rx
sequence. If AFTER is specified, add it to the end of the rx
sequence."
(rx-to-string
`(seq
,(or before "")
,@(cl-loop for (sexp . more) on rxs
collect `(group ,sexp)
when more collect sep)
,(or after ""))))
(defun orderless-flex (component)
"Match a component in flex style.
This means the characters in COMPONENT must occur in the
candidate in that order, but not necessarily consecutively."
(orderless--separated-by '(zero-or-more nonl)
(cl-loop for char across component collect char)))
(defun orderless-initialism (component)
"Match a component as an initialism.
This means the characters in COMPONENT must occur in the
candidate, in that order, at the beginning of words."
(orderless--separated-by '(zero-or-more nonl)
(cl-loop for char across component collect `(seq word-start ,char))))
(defun orderless--strict-*-initialism (component &optional start end)
"Match a COMPONENT as a strict initialism, optionally anchored.
The characters in COMPONENT must occur in the candidate in that
order at the beginning of subsequent words comprised of letters.
Only non-letters can be in between the words that start with the
initials.
If START is non-nil, require that the first initial appear at the
first word of the candidate. Similarly, if END is non-nil
require the last initial appear in the last word."
(orderless--separated-by
'(seq (zero-or-more word) word-end (zero-or-more (not alpha)))
(cl-loop for char across component collect `(seq word-start ,char))
(when start '(seq buffer-start (zero-or-more (not alpha))))
(when end
'(seq (zero-or-more word) word-end (zero-or-more (not alpha)) eol))))
(defun orderless-strict-initialism (component)
"Match a COMPONENT as a strict initialism.
This means the characters in COMPONENT must occur in the
candidate in that order at the beginning of subsequent words
comprised of letters. Only non-letters can be in between the
words that start with the initials."
(orderless--strict-*-initialism component))
(defun orderless-strict-leading-initialism (component)
"Match a COMPONENT as a strict initialism, anchored at start.
See `orderless-strict-initialism'. Additionally require that the
first initial appear in the first word of the candidate."
(orderless--strict-*-initialism component t))
(defun orderless-strict-full-initialism (component)
"Match a COMPONENT as a strict initialism, anchored at both ends.
See `orderless-strict-initialism'. Additionally require that the
first and last initials appear in the first and last words of the
candidate, respectively."
(orderless--strict-*-initialism component t t))
(defun orderless-prefixes (component)
"Match a component as multiple word prefixes.
The COMPONENT is split at word endings, and each piece must match
at a word boundary in the candidate. This is similar to the
`partial-completion' completion style."
(orderless--separated-by '(zero-or-more nonl)
(cl-loop for prefix in (split-string component "\\>" t)
collect `(seq word-boundary ,prefix))))
;;; Highlighting matches
(defun orderless--highlight (regexps string)
"Propertize STRING to highlight a match of each of the REGEXPS.
Warning: only use this if you know all REGEXPs match!"
(cl-loop with n = (length orderless-match-faces)
for regexp in regexps and i from 0 do
(string-match regexp string)
(cl-loop
for (x y) on (or (cddr (match-data)) (match-data)) by #'cddr
when x do
(font-lock-prepend-text-property
x y
'face (aref orderless-match-faces (mod i n))
string)))
string)
(defun orderless-highlight-matches (regexps strings)
"Highlight a match of each of the REGEXPS in each of the STRINGS.
Warning: only use this if you know all REGEXPs match all STRINGS!
For the user's convenience, if REGEXPS is a string, it is
converted to a list of regexps according to the value of
`orderless-component-matching-styles'."
(when (stringp regexps)
(setq regexps (funcall orderless-pattern-compiler regexps)))
(cl-loop for original in strings
for string = (copy-sequence original)
collect (orderless--highlight regexps string)))
;;; Compiling patterns to lists of regexps
(defun orderless-remove-transient-configuration ()
"Remove all transient orderless configuration.
Meant to be added to `exit-minibuffer-hook'."
(setq orderless-transient-matching-styles nil
orderless-transient-component-separator nil))
(defun orderless--parse-matching-styles (configuration)
"Parse a matching styles CONFIGURATION.
The CONFIGURATION argument should be a list of functions
optionally with the symbols `:global-dispatchers' and
`:component-dispatcher' used to delimit portions:
(matching-styles...
:global-dispatchers global-dispatchers...
:component-dispatcher component-dispatchers...)
Here matching-styles... denotes a sequence of matching style
functions, and each of the *-dispatcher... denotes a sequence of
style dispatcher functions. The `:global-dispatchers' and
`:component-dispatcher' sections are both optional and can occur
in either order.
This function returns a 3-element lists of lists: the styles, the
global dispatchers and the component dispatchers."
(let ((result (vector (list) (list) (list)))
(type 0))
(dolist (elt configuration)
(pcase elt
(:global-dispatchers (setq type 1))
(:component-dispatchers (setq type 2))
(_ (push elt (aref result type)))))
(mapcar #'nreverse result)))
(defun orderless-dispatch (dispatchers default string &rest args)
"Run DISPATCHERS to compute matching styles for STRING.
A style dispatcher is a function that takes a string and possibly
some extra arguments. It should either return (a) nil to
indicate the dispatcher will not handle the string, (b) a new
string to replace the current string and continue dispatch,
or (c) the matching styles to use and, if needed, a new string to
use in place of the current one (for example, a dispatcher can
decide which style to use based on a suffix of the string and
then it must also return the component stripped of the suffix).
More precisely, the return value of a style dispatcher can be of
one of the following forms:
- nil (to continue dispatching)
- a string (to replace the component and continue dispatching),
- a matching style or non-empty list of matching styles to
return,
- a `cons' whose `car' is either as in the previous case or
nil (to request returning the DEFAULT matching styles), and
whose `cdr' is a string (to replace the current one).
This function tries all DISPATCHERS in sequence until one returns
a list of styles (passing any extra ARGS to every style
dispatcher). When that happens it returns a `cons' of the list
of styles and the possibly updated STRING. If none of the
DISPATCHERS returns a list of styles, the return value will use
DEFAULT as the list of styles."
(cl-loop for dispatcher in dispatchers
for result = (apply dispatcher string args)
if (stringp result)
do (setq string result result nil)
else if (and (consp result) (null (car result)))
do (setf (car result) default)
else if (and (consp result) (stringp (cdr result)))
do (setq string (cdr result) result (car result))
when result return (cons result string)
finally (return (cons default string))))
(defun orderless-default-pattern-compiler (pattern)
"Build regexps to match the components of PATTERN.
Split PATTERN on `orderless-component-separator' and compute
matching styles for each component. The matching styles are
computed according to `orderless-component-matching-styles',
unless the variable `orderless-transient-matching-styles' is
non-nil, in which case it is used instead. Whichever variable is
used is split into a list of default matching styles, a list of
global dispatchers and a list of component dispatchers. For each
component of the PATTERN, the component dispatchers are run to
determine the matching styles to be used for that component. If
the component dispatchers decline to handle the component, then
the global dispatchers are run on the PATTERN to determine
matching styles. Finally, if the global dispatchers decline to
handle the PATTERN, the list of default matching styles is used.
See `orderless-dispatch' for details on how the lists of
dispatchers are used.
This is the default value of `orderless-pattern-compiler'."
(cl-loop
with (matching-styles global-dispatchers component-dispatchers) =
(orderless--parse-matching-styles
(or orderless-transient-matching-styles
orderless-component-matching-styles))
with (default . newpat) = (orderless-dispatch
global-dispatchers
(or matching-styles 'orderless-regexp)
pattern)
with components = (split-string newpat orderless-component-separator)
with total = (length components)
for component in components and index from 0
for (styles . newcomp) = (orderless-dispatch
component-dispatchers
default component index total)
collect
(if (functionp styles)
(funcall styles newcomp)
(rx-to-string
`(or
,@(cl-loop for style in styles
collect `(regexp ,(funcall style newcomp))))))))
;;; Completion style implementation
(defun orderless--prefix+pattern (string table pred)
"Split STRING into prefix and pattern according to TABLE.
The predicate PRED is used to constrain the entries in TABLE."
(let ((limit (car (completion-boundaries string table pred ""))))
(cons (substring string 0 limit) (substring string limit))))
;;;###autoload
(defun orderless-filter (string table &optional pred)
"Split STRING into components and find entries TABLE matching all.
The predicate PRED is used to constrain the entries in TABLE."
(condition-case nil
(save-match-data
(pcase-let* ((`(,prefix . ,pattern)
(orderless--prefix+pattern string table pred))
(completion-regexp-list
(funcall orderless-pattern-compiler pattern)))
(all-completions prefix table pred)))
(invalid-regexp nil)))
;;;###autoload
(defun orderless-all-completions (string table pred _point)
"Split STRING into components and find entries TABLE matching all.
The predicate PRED is used to constrain the entries in TABLE. The
matching portions of each candidate are highlighted.
This function is part of the `orderless' completion style."
(let ((completions (orderless-filter string table pred)))
(when completions
(pcase-let ((`(,prefix . ,pattern)
(orderless--prefix+pattern string table pred)))
(nconc
(orderless-highlight-matches pattern completions)
(length prefix))))))
;;;###autoload
(defun orderless-try-completion (string table pred point &optional _metadata)
"Complete STRING to unique matching entry in TABLE.
This uses `orderless-all-completions' to find matches for STRING
in TABLE among entries satisfying PRED. If there is only one
match, it completes to that match. If there are no matches, it
returns nil. In any other case it \"completes\" STRING to
itself, without moving POINT.
This function is part of the `orderless' completion style."
(let ((all (orderless-filter string table pred)))
(cond
((null all) nil)
((null (cdr all))
(let ((full (concat
(car (orderless--prefix+pattern string table pred))
(car all))))
(cons full (length full))))
(t (cons string point)))))
;;;###autoload
(add-to-list 'completion-styles-alist
'(orderless
orderless-try-completion orderless-all-completions
"Completion of multiple components, in any order."))
;;; Temporary separator change (does anyone use this?)
(defvar orderless-old-component-separator nil
"Stores the old value of `orderless-component-separator'.")
(defun orderless--restore-component-separator ()
"Restore old value of `orderless-component-separator'."
(when orderless-old-component-separator
(setq orderless-component-separator orderless-old-component-separator
orderless-old-component-separator nil))
(remove-hook 'minibuffer-exit-hook #'orderless--restore-component-separator))
(defun orderless-temporarily-change-separator (separator)
"Use SEPARATOR to split the input for the current completion session."
(interactive
(list (let ((enable-recursive-minibuffers t))
(read-string "Orderless regexp separator: "))))
(unless orderless-old-component-separator
(setq orderless-old-component-separator orderless-component-separator))
(setq orderless-component-separator separator)
(add-to-list 'minibuffer-exit-hook #'orderless--restore-component-separator))
;;; Ivy integration
(defvar ivy-regex)
(defvar ivy-highlight-functions-alist)
;;;###autoload
(defun orderless-ivy-re-builder (str)
"Convert STR into regexps for use with ivy.
This function is for integration of orderless with ivy, use it as
a value in `ivy-re-builders-alist'."
(or (mapcar (lambda (x) (cons x t))
(funcall orderless-pattern-compiler str))
""))
(defun orderless-ivy-highlight (str)
"Highlight a match in STR of each regexp in `ivy-regex'.
This function is for integration of orderless with ivy."
(orderless--highlight (mapcar #'car ivy-regex) str) str)
;;;###autoload
(with-eval-after-load 'ivy
(add-to-list 'ivy-highlight-functions-alist
'(orderless-ivy-re-builder . orderless-ivy-highlight)))
(provide 'orderless)
;;; orderless.el ends here
|