Emacs lisp escaping regexp - regex

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)))))

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)))

emacs isearch with automatic application of a regex between characters

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.

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 () )

Regexp in Emacs progn

I have the following Emacs macro, which evaluates the AucTex command TeX-insert-macro on selected text:
(eval-after-load "latex"
'(progn
(define-key LaTeX-mode-map (kbd "C-c (")
(lambda ()
(interactive)
(TeX-insert-macro "command")))))
However, how would I perform a regexp on the selected text and pass that to TeX-insert-macro?
See this question for more details.
For example:
Suppose I have the following text:
…as shown in figure (24).
Now, suppose I select "(24)." Then, I want a command to convert it to:
…as shown in figure \command{eq:24}.
Viz., how would I define a new Emacs command that I could place in ~/.emacs that would run C-C RET command RET on a regexp'ed version of the selected text?
Why doesn't the following with regexp work?
(eval-after-load "latex"
'(progn
(define-key LaTeX-mode-map (kbd "C-c )")
(lambda ()
(interactive)
(query-replace-regexp "\(([0-9]+)\)" "\1")
(TeX-insert-macro "eq")
))))
Minor improvement:
This works well if I select, e.g., "(45)" from the left to the right and then run the code by typing C-c )
(eval-after-load "latex"
'(progn
(define-key LaTeX-mode-map (kbd "C-c )")
(lambda ()
(interactive)
(exchange-point-and-mark)
(replace-regexp "[\(\)]" "")
(TeX-insert-macro "eq")
))))
However, if I select "(45)" from the right to the left, it doesn't work because the point and mark are flipped. Is there a way to make sure the point and mark aren't flipped, such as by running exchange-point-and-mark when they are flipped?
Also, there are other issue with the regexp, e.g., if there are other parentheses nearby it gets greedy.
thanks
I don't have the requisite libraries installed, or know how the TeX functions work, but something like this might do the trick.
(eval-after-load "latex"
'(progn
(defun my-insert-latex-command-macro (beginning end)
(interactive "r")
(goto-char beginning)
(when (not (looking-at "(\\(.+?\\))"))
(error "Region does not match expected pattern."))
(push-mark)
(replace-match "eq:\\1")
(exchange-point-and-mark)
(TeX-insert-macro "command")
(goto-char (mark))
(pop-mark))
(define-key LaTeX-mode-map (kbd "C-c (") 'my-insert-latex-command-macro)))
Using the "r" argument for interactive conveniently gives you the region boundaries in ascending order, regardless of which direction you marked it in.
To avoid greedy matching in a regexp, use the non-greedy operators (*?, +?, ??).
Note that you do not have sufficient backslash escapes in your regexps. In order to get a backslash into a regexp string in the first place, you first need to account for string escaping. Basically, double the backslashes. e.g.: This:
(query-replace-regexp "\(([0-9]+)\)" "\1")
needs to be this:
(query-replace-regexp "\\(([0-9]+)\\)" "\\1")

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.