Quickly exclude several types of candidates
DamienCassou opened this issue · comments
I frequently search my code base for regexp patterns using consult-ripgrep
. Very often, I want to exclude test files from search results. My test files have one of these suffixes: "-tests.js" or "spec.component.js". I could type "!-tests.js !spec.component.js" as orderless input but that is tedious.
I thought I could configure a special dispatcher with a dedicated matching style like this:
(defun my/orderless-no-test-dispatcher (component _index _total)
(when (string= component "=")
(cons (list 'my/orderless-no-component-test) "")))
(defun my/orderless-no-component-test (_component)
(rx-to-string `(or (regexp
,(orderless-without-literal ".spec.component.js"))
(regexp
,(orderless-without-literal "-tests.js")))))
(add-to-list 'orderless-style-dispatchers #'my/orderless-no-test-dispatcher)
With this, I thought I could just type "=" and get non-test candidates. I thought this was brilliant until I realized that the regexp returned by my/orderless-no-component-test
is going to match any possible candidate and is thus 100% stupid :-D.
Something that actually seems to work is to write a dispatcher that can be used several times:
(defun my/orderless-no-test-dispatcher (component index _total)
(let ((suffixes '(".spec.component.js" "-tests.js")))
(when (and (<= 0 index (1- (length suffixes))) (string= component "="))
(let ((result (cons #'orderless-without-literal
(elt suffixes index))))
result))))
With this, I can type "= =" (2 components) and get the desired filter. This is much better than typing the suffixes manually and I can't say I'm proud :-).
Is there a way to configure orderless to reject a candidate if it matches any of several suffixes?
An implementation of orderless-without-regexp
could solve my problem (see #88) but it doesn't look like it is possible to implement.
I don't think you can do better than your second attempt if you limit yourself to matching styles and style dispatchers. Those mechanisms only give you a single regexp per component of the input string and what you really want is to turn =
into two negative literals. There is however a different mechanism that allows you to produce as many components as you wish: the orderless-component-separator
.
So you can abandon your dispatchers and instead set orderless-component-separator
to a function that replaces an =
with as many negative literals as you need. Here's a simple example that only checks for =
at the beginning of the list of components, and relies on the default style dispatcher to kick in later and deal with the !
s:
(defun my-whacky-separator (string)
(let ((components (split-string string " +" t)))
(if (equal (car components) "=")
(cons '("!-tests.js" "!.spec.component.js")
(cdr components))
components)))
(setq orderless-component-separator #'my-whacky-separator)
Let me know if there's still any issue left to address with that solution.
Of course you could also store the text !-tests.js !spec.component.js
in a text register or bind =
to a keyboard macro that types that text for you.
A text register is just perfect. Thanks for the reminder :-).