diff options
| -rw-r--r-- | README.org | 74 | ||||
| -rw-r--r-- | orderless.el | 110 |
2 files changed, 155 insertions, 29 deletions
@@ -1,9 +1,11 @@ * Overview This package provides an =orderless= completion style that divides the -pattern into space-separated components, treats each one as a regexp, -and matches candidates that match all of the component regexps in any -order. +pattern into space-separated components, and matches candidates that +match all of the components in any order. Each component can match in +any one of several matching styles: literally, as a regexp, as an +initialism or in the "flex" style. The regexp and initialism styles +are enabled by default. Completion styles are used as entries in the variables =completion-styles= and =completion-category-overrides=, see their @@ -20,13 +22,52 @@ on your =load-path=, and use the following configuration: Bug reports are highly welcome and appreciated! * Customization + +** Component matching styles + +Each component of a pattern can match in any of several matching +styles. A matching style is simply a function from strings to strings +that maps a component to a regexp to match against, so it is easy to +add new matching styles. The predefined one are: + +- orderless-regexp :: the component is treated as a regexp that must + match somewhere in the candidate. + + This is simply the identity function! + +- orderless-literal :: the component is treated as a literal string + that must occur in the candidate. + + This is just =regexp-quote=. + +- orderless-initialism :: each character of the component should appear + as at the beginning of a word in the candidate, in order. + + This maps =abc= to =\<a.*\<b.*\c=. + +- orderless-flex :: the characters of the component should appear in + that order in the candidate, but not necessarily consecutively. + + This maps =abc= to =a.*b.*c=. + +- orderless-prefixes :: the component is split on hyphens and slashes + and each piece must be a word prefix in the candidate, occurring in + that order. + + This is similar to the built-in =partial-completion= completion-style. + For example =re-re= matches =query-replace-regexp= and =recode-region=. + +The variable =orderless-component-matching-style= should be set to a +list of the desired styles to use. By default it enables the regexp +and initialism styles. + ** Component separator regexp -The regexp components by default are space-separated, but this is +The pattern components by default are space-separated, but this is controlled by the variable =orderless-regexp-separator=, which should be set to a regexp that matches the desired component separator. The default value matches a sequence of spaces. It may be useful to add -hypens or slashes (or both), to match symbols or file paths, +hyphens or slashes (or both), to match symbols or file paths, respectively. If you are implementing a command for which you know you want a @@ -58,6 +99,8 @@ being used, of course. * Related packages +** Ivy and Helm + The well-known and hugely powerful completion frameworks [[https://github.com/abo-abo/swiper][Ivy]] and [[https://github.com/emacs-helm/helm][Helm]] also provide for matching space-separated component regexps in any order. In Ivy, this is done with the =ivy--regex-ignore-order= matcher. @@ -69,9 +112,10 @@ Icomplete completion UI, while both of those provide their own completion UI (and many other cool features!). It is worth pointing out that Helm does provide its multi pattern -matching as a completion style which could be used with Icomplete! So, -Icomplete users could, instead of using this package, instead install -Helm and configure Icomplete to use it as follows: +matching as a completion style which could be used with Icomplete! +(Ivy does not.) So, Icomplete users could, instead of using this +package, instead install Helm and configure Icomplete to use it as +follows: #+begin_src emacs-lisp (require 'helm) @@ -82,7 +126,13 @@ Helm and configure Icomplete to use it as follows: (Of course, if you install Helm, you probably might as well use the Helm UI in =helm-mode= rather than using Icomplete.) -The combination of [[https://github.com/raxod502/selectrum][Selectrum]] and [[https://github.com/raxod502/prescient.el][prescient.el]] also provides matching -of space-separated components in any order, but each component can be -matched not only as a regexp, but also a literal string or an -initialism, so the set of candidates returned may be larger. +** Prescient + +The [[https://github.com/raxod502/prescient.el][prescient.el]] library also provides matching of space-separated +components in any order and it can be used with either the [[https://github.com/raxod502/selectrum][Selectrum]] +or [[https://github.com/abo-abo/swiper][Ivy]] completion UIs (it does not provide a completion-style that +could be used with Emacs' default completion UI or with Icomplete). +The components can be matched literally, as regexps, as initialisms or +in the flex style (called "fuzzy" in prescient). In addition to +matching, =prescient.el= also provides sorting of candidates (=orderless= +leaves that up to the candidate source and the completion UI). diff --git a/orderless.el b/orderless.el index 7061764..67dca3a 100644 --- a/orderless.el +++ b/orderless.el @@ -4,7 +4,7 @@ ;; Author: Omar AntolĂn Camarena <omar@matem.unam.mx> ;; Keywords: extensions -;; Version: 0.1 +;; Version: 0.3 ;; Homepage: https://github.com/oantolin/orderless ;; Package-Requires: ((emacs "24.3")) @@ -24,25 +24,32 @@ ;;; Commentary: ;; This package provides an `orderless' completion style that divides -;; the pattern into components chunks (space-separated by default), -;; treats each on as a regexp, and matches candidates that match all of -;; the regexps in any order. -;; +;; 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-regexp-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) @@ -101,20 +108,86 @@ component regexps." :type '(vector 'face) :group 'orderless) +(defcustom orderless-component-matching-styles + '(orderless-regexp orderless-initialism) + "List of allowed component matching styles. +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." + :type '(set + (const :tag "Regexp" orderless-regexp) + (const :tag "Literal" orderless-literal) + (const :tag "Initialism" orderless-initialism) + (const :tag "Flex" orderless-flex) + (const :tag "Prefixes" orderless-prefixes) + (function :tag "Custom matching style")) + :group 'orderless) + +(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--anything-between (rxs) + "Return a regexp to match the rx-regexps RXS with .* in between." + (rx-to-string + `(seq ,@(cl-loop for (sexp . more) on rxs + collect `(group ,sexp) + when more collect `(zero-or-more nonl))))) + +(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--anything-between + (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--anything-between + (cl-loop for char across component collect `(seq word-start ,char)))) + +(defun orderless-prefixes (component) + "Match a component as slash-or-hyphen-separated word prefixes. +The COMPONENT is split on slashes and hyphens, and each piece +must match a prefix of a word in the candidate. This is similar +to the `partial-completion' completion style." + (orderless--anything-between + (cl-loop for prefix in (split-string component "[/-]") + collect `(seq word-start ,prefix)))) + (defun orderless--highlight-matches (regexps string) - "Highlight matches of REGEXPS in STRING. + "Highlight a match of each of the REGEXPS in STRING. Warning: only call this if you know all REGEXPs match STRING!" (setq string (copy-sequence string)) (cl-loop with n = (length orderless-match-faces) for regexp in regexps and i from 0 do (string-match regexp string) - (font-lock-prepend-text-property - (match-beginning 0) - (match-end 0) - 'face (aref orderless-match-faces (mod i n)) - 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--component-regexp (component) + "Build regexp to match COMPONENT. +Consults `orderless-component-matching-styles' to decide what to +match." + (rx-to-string + `(or ,@(cl-loop for style in orderless-component-matching-styles + collect `(regexp ,(funcall style component)))))) + (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. @@ -123,10 +196,13 @@ This function is part of the `orderless' completion style." (save-match-data (let* ((limit (car (completion-boundaries string table pred ""))) (prefix (substring string 0 limit)) + (components (split-string (substring string limit) + orderless-regexp-separator + t)) (completion-regexp-list ; used by all-completions!!! - (split-string (substring string limit) - orderless-regexp-separator - t)) + (if orderless-component-matching-styles + (mapcar #'orderless--component-regexp components) + components)) (completions (all-completions prefix table pred))) (when completions (when minibuffer-completing-file-name |
