I really like
rx but I can't use it interactively.
Has someone already tried to extend isearch to support
Emacs Stack Exchange is a question and answer site for those using, extending or developing Emacs. It only takes a minute to sign up.Sign up to join this community
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)))
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
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))))))
"%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.
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. ;-))
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:
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
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
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.
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.
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 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.