I really like rx
but I can't use it interactively.
Has someone already tried to extend isearch to support rx
syntax?
I really like rx
but I can't use it interactively.
Has someone already tried to extend isearch to support rx
syntax?
With isearch
you can search almost everything.
(defun isearch-rx-forward ()
"Do incremental search forward for rx expression."
(interactive)
(let ((isearch-message-prefix-add "[RX]")
(isearch-search-fun-function
(lambda ()
(lambda (form &optional bound noerror)
(funcall (isearch-search-fun-default)
(condition-case nil
(rx-to-string (read form))
(error
(signal 'invalid-regexp (list "Invalid "))))
bound noerror)))))
(isearch-forward t)))
OK, here goes. This might sound convoluted, but it just puts together a few ordinary Emacs things that you probably use everyday. (If you don't use them everyday, perhaps you should. ;-))
Set option enable-recursive-minibuffers
to non-nil
(at least temporarily). E.g.:
M-x set-variable RET enable-recursive-minibuffers RET t RET
Start regexp search: C-M-s
.
Edit the (as yet empty) search string: M-e
. This puts you in a normal editing mode, in the minibuffer.
Evaluate an rx sexp in a recursive minibuffer for eval-expression
.
C-u M-: (rx (and line-start (1+ (in "a-z"))) RET
You now have the regexp you want, "^[a-z]+"
, in the minibuffer, but it is surrounded by double-quote chars. So remove those chars. You now see this in the minibuffer:
Regexp I-search: ^[a-z]+
End editing and resume searching, by hitting C-s
.
The bottom line is really that it is not true that you cannot use something interactively when in Isearch. You just need to (a) use M-e
to get into editing mode, and then (b) use M-:
to interactively evaluate a Lisp sexp.
You talk about extending isearch
, which suggests you also want to search incrementally, not just interactively (interactive only meaning that you call the function from a key binding or from M-x
). I'm not sure that incremental is such a good fit for rx
: unless you enter closing parenthesis at the same time as opening ones, the vast majority of the time a partially entered rx
expression will be ill-formed (unlike normal regex syntax, which is often syntactically valid at several intermediate points).
Well, to answer the question for merely interactive searching: just make a wrapper for re-search-forward
that prompts for an expression, evaluates it and searches for the resulting regexp:
(defun eval-re-search-forward (re)
(interactive "Xrx: ")
(re-search-forward re))
(The first "X" in the argument to interactive specifies that when used interactively the function should prompt for a Lisp expression, and evaluate it before binding it to the argument re
.)
The prompt says "rx: ", because that's the intended usage, but you can enter any Lisp expression that evaluates to a string.
Also, @Drew gave you a reasonable suggestion, but I want to offer a very minor variation: instead of recursive minibuffers, write a function that replaces an s-expression by its value (without the quotes if said value is a string). I use this function all the time:
(defun eval-replace-last-sexp ()
(interactive)
(let ((sexp (preceding-sexp)))
(backward-kill-sexp)
(let ((result (eval sexp)))
(when result
(insert (format "%s" result))))))
(By choosing "%s"
as the format, if result is a string it gets inserted without the quotes. I find this useful because I typically use this function to generate little parts of my files. Also, I test the result so if it's nil I don't wind up inserting the word "nil" in the buffer, as that's almost never what I want.)
With this function bound to a key of your choosing, you can run M-x re-search-forward
, type the rx
expression and hit the key you choose for eval-replace-last-sexp
. If you want to further edit the rx
form, you can undo the eval-replace-last-sexp
. If you really want to use this incrementally, i.e., with isearch
, just use M-e
first, as @Drew said.
If you use Isearch+ then you can just use M-:
to do what you request. It prompts you for a Lisp sexp, evaluates it, and appends the value to the search string. You can use an rx
expression as the sexp.
For example:
C-M-s M-: (rx (and line-start (1+ (in "("))))
searches using the result of that ‘rx’ sexp, which is ^(+
.
With Isearch+ you can also use C-u M-:
after M-e
, to insert the sexp value into the minibuffer, where you are editing the search string.
(M-e
lets you edit the search string, and with Isearch+ it lets you use a recursive minibuffer, so you can use M-:
, which itself reads from a minibuffer. In the minibuffer, M-:
is bound to its usual command, and for that, C-u
inserts the value returned at point - which in this case is at point in the parent minibuffer, from M-e
.)
So use M-e
followed by C-u M-:
when you do not want to simply append the sexp value to the search string, but instead you want to do some editing of it or the rest of the search string.