emacs isearch with automatic application of a regex between characters - regex

I would like to have a hook into Emacs's isearch-forward function to make it automatically apply a regex between the input characters while searching a string. For example, I would like to set this regex to [-=<>]. If I now type foobar into isearch, it should match foo<bar, fo=ob=>ar, f-o-o-b-a-r, etc.
Is such a functionality already available? I looked into ELPA and MELPA without success. In case this is not available, and since my Elisp abilities are very limited: How could this be implemented?

OK, I found a solution by myself after inspecting hexl.el from Emacs.
Here's the code.
(defun my-isearch-function ()
"Make isearch skip characters -=<> while searching."
(if (not isearch-regexp)
(lambda (string &optional bound noerror count)
(funcall
(if isearch-forward
're-search-forward
're-search-backward)
(mapconcat (lambda (c) (regexp-quote (string c))) string
"\\(?:[-=<>]*\\)?")
bound
noerror
count))
(isearch-search-fun-default)))
(defun toggle-my-isearch ()
"Toggle my search mode.
If activated, incremental search skips characters -=<> while
searching.
For example, searching `foobar' matches `foo-bar' or `f-o-o=b<a>r'."
(interactive)
(if (eq isearch-search-fun-function 'isearch-search-fun-default)
(progn
(setq isearch-search-fun-function 'my-isearch-function)
(message "my isearch on"))
(setq isearch-search-fun-function 'isearch-search-fun-default)
(message "my isearch off")))
(global-set-key (kbd "s-s") 'toggle-my-isearch)

I wrote a package called flex-isearch, that basically inserts ".*" in between each character of the search string (it's a bit more complicated than that) and switches to regexp searching. It does this automatically when the isearch fails.

Related

Combine multiple replace-regexp to a program

I am working on cleaning files with multiple regex-replacement
<<.*>> -> ""
\([[:alpha:]]\)\* -> \1 ;; patters as pragram* to program
\*\([[:alpha:]]\) -> \1 ;; patters as *program to program
\*/\([[:alpha:]]\) -> \1
;;and so on
On every single file, I have to invoke replace-regexp various times.
How could combine these regex search?
To an extent, M-x whitespace-cleanup has similar requirements, that is, cleanup base on multiple conditions. It should be possible to use (emacs) Keyboard Macros, but I am not familiar with it. Once you have some knowledge in Emacs Lisp, you can solve the problem easily, for example, the following cleanups leading and trailing spaces, you can add your regexp and their replacement into my-cleanup-regexps:
(defvar my-cleanup-regexps
'(("^ +" "")
(" +$" ""))
"A list of (REGEXP TO-STRING).")
(defun my-cleanup-replace-regexp (regexp to-string)
"Replace REGEXP with TO-STRING in the whole buffer."
(goto-char (point-min))
(while (re-search-forward regexp nil t)
(replace-match to-string)))
(defun my-cleanup ()
"Cleanup the whole buffer according to `my-cleanup-regexps'."
(interactive)
(dolist (r my-cleanup-regexps)
(apply #'my-cleanup-replace-regexp r)))

AND operator for text search in Emacs

I am new to Emacs. I can search for text and show all lines in a separate buffer using "M-x occur". I can also search for multiple text items using OR operator as : one\|two , which will find lines with "one" or "two" (as explained on Emacs occur mode search for multiple strings). How can I search for lines with both "one" and "two"? I tried using \& and \&& but they do not work. Will I need to create a macro or function for this?
Edit:
I tried writing a function for above in Racket (a Scheme derivative). Following works:
#lang racket
(define text '("this is line number one"
"this line contains two keyword"
"this line has both one and two keywords"
"this line contains neither"
"another two & one words line"))
(define (srch . lst) ; takes variable number of arguments
(for ((i lst))
(set! text (filter (λ (x) (string-contains? x i)) text)))
text)
(srch "one" "two")
Ouput:
'("this line has both one and two keywords" "another two & one words line")
But how can I put this in Emacs Lisp?
Regex doesn't support "and" because it has very limited usefulness and weird semantics when you try to use it in any nontrivial regex. The usual fix is to just search for one.*two\|two.*one ... or in the case of *Occur* maybe just search for one and then M-x delete-non-matching-lines two.
(You have to mark the *Occur* buffer as writable before you can do this. read-only-mode is a toggle; the default keybinding is C-x C-q. At least in my Emacs, you have to move the cursor away from the first line or you'll get "Text is read-only".)
(defun occur2 (regex1 regex2)
"Search for lines matching both REGEX1 and REGEX2 by way of `occur'.
We first (occur regex1) and then do (delete-non-matching-lines regex2) in the
*Occur* buffer."
(interactive "sFirst term: \nsSecond term: ")
(occur regex1)
(save-excursion
(other-window 1)
(let ((buffer-read-only nil))
(forward-line 1)
(delete-non-matching-lines regex2))))
The save-excursion and other-window is a bit of a wart but it seemed easier than hardcoding the name of the *Occur* buffer (which won't always be true; you can have several occur buffers) or switching there just to fetch the buffer name, then Doing the Right Thing with set-buffer etc.

Regex search in Emacs for line not starting with semicolon

I am trying to search forward in the current buffer for the first elisp function definition that is not a comment. I tried this:
(re-search-forward "[^;] *\(defun ")
but it matches comments anyway. Like the following line:
;; (defun test ()
You can use:
(catch 'found
(let (match)
(while (setq match (search-forward "(defun "))
(when (null (syntax-ppss-context (syntax-ppss)))
(throw 'found match)))
nil))
It relies on the internal parser and the language syntax definition. It returns only the result of search-forward if point is not in a comment and not in a string.
If you do not like the error in the case of a search without hits you can add nil t to the arguments of the search-forward command. Search with re-search-forward is also fine.
This also works for cases like:
(defun test (args)
"This function is defined with (defun test (args) ...)"
)
The space in (defun test () actually matches [^;]. Since you have *, another space is not needed. You may want to use [^;] +. However, you can use a negative lookbehind via
\(?<!;;)
This seems to work nicely for me (tested in *scratch*):
(re-search-forward "^ *\(defun " nil t) ; hit C-x C-e after that
; closing parenthesis
;; (defun hi () )
(defun hola () )

Emacs lisp escaping regexp

As a first experience in defining a function for emacs, I would like to make write a function that take all occurences of argv[some number] and renumber them in order.
This is done inside emacs with replace-regexp, entering as search/replace strings
argv\[\([0-9]+\)\]
argv[\,(+ 1 \#)]
Now, I want to write this in my .emacs so I understand I need to escape also for Lisp special characters. So in my opinion it should write
(defun argv-order ()
(interactive)
(goto-char 1)
(replace-regexp "argv\\[[0-9]+\\]" "argv[\\,\(+ 1 \\#\)]")
)
The search string works fine but the replacement string gives me the error "invalid use of \ in replacement text. I've been trying around adding or removing some \ but with no success.
Any idea ?
Quoting the help from replace-regexp (the bold is mine):
In interactive calls, the replacement text may contain `\,'
You are not using it interactively in your defun, hence the error message. Another quote from the same help that helps solving your problem:
This function is usually the wrong thing to use in a Lisp program.
What you probably want is a loop like this:
(while (re-search-forward REGEXP nil t)
(replace-match TO-STRING nil nil))
which will run faster and will not set the mark or print anything.
And a solution based on that:
(defun argv-order ()
(interactive)
(let ((count 0))
(while (re-search-forward "argv\\[[0-9]+\\]" nil t)
(replace-match (format "argv[%d]" count) nil nil)
(setq count (1+ count)))))

Emacs: regular expression replacing to change case (in scripts)

This is related to
Emacs: regular expression replacing to change case
My additional problem is that I need to script the search-replace but the "\,()" solution works (for me) only when used interactively (emacs 24.2.1). Inside a script it gives the error: "Invalid use of \' in replacement text".
I usually write a "perform-replace" to some file to be loaded when needed. Something like:
(perform-replace "<\\([^>]+\\)>" "<\\,(downcase \1)>" t t nil 1 nil (point-min) (point-max))
It should be possible to call a function to generate the replacement (pg 741 of the emacs lisp manual), but I've tried many variations of the following with no luck:
(defun myfun ()
(downcase (match-string 0)))
(perform-replace "..." (myfun . ()) t t nil)
Can anyone help?
Constructs like \,() are only allowed in interactive calls to query-replace, which is why Emacs complains in your case.
The documentation of perform-replace mentions that you should not use it in elisp code and proposes a better alternative, upon which we can build the following code:
(while (re-search-forward "<\\([^>]+\\)>" nil t)
(replace-match (downcase (match-string 0)) t nil))
If you still want to interactively query the user about the replacements, using perform-replace like you did is probably the right thing to do. There were a few different problems in your code:
As stated in the elisp manual the replacement function must take two arguments (the data you provide in the cons cell and the number of replacements already made).
As stated in the documentation of query-replace-regexp (or the elisp manual), you need to ensure that case-fold-search or case-replace is set to nil so that the case pattern is not transferred to the replacement.
You need to quote the cons cell (myfun . nil), otherwise it will be interpreted as a function call and evaluated too early.
Here is a working version:
(let ((case-fold-search nil))
(perform-replace "<\\([^>]+\\)>"
`(,(lambda (data count)
(downcase (match-string 0))))
t t nil))
C-h f perform-replace says:
Don't use this in your own program unless you want to query and set the mark
just as `query-replace' does. Instead, write a simple loop like this:
(while (re-search-forward "foo[ \t]+bar" nil t)
(replace-match "foobar"))
Now the "<\\,(downcase \1)>" needs to be replaced by an Elisp expression that builds the proper string, such as (format "<%s>" (downcase (match-string 1))).
If you do need the query and stuff, then you might like to try: C-M-% f\(o\)o RET bar \,(downcase \1) baz RET and then C-x RET RET to see what arguments were constructed during the interactive call.
You'll see discover (even better if you click on replace.el in C-h f perform-replace to see the source code of the function), that the replacements argument can take the form (FUNCTION . ARGUMENT). More specifically, the code includes a comment giving some details:
;; REPLACEMENTS is either a string, a list of strings, or a cons cell
;; containing a function and its first argument. The function is
;; called to generate each replacement like this:
;; (funcall (car replacements) (cdr replacements) replace-count)
;; It must return a string.