how do I define an emacs replace-regex shortcut command? - regex

I don't understand how to re-use an interactive command in a command I'm writing myself.
I want to make a command that always uses the same arguments to replace-regexp. It's a shortcut, really.
So I tried to mimic in a function what I'd done interactively on a selected region, namely:
M-x replace-regexp RET ^\(\s *\)\(.*\)\s *$ RET \1 + '\2'
I mimicked it by writing this function:
(defun myH2js ()
"Converts html to an (incomplete) JavaScript String concatenation."
(interactive)
(let (p1 p2)
(setq p1 "^\(\s *\)\(.*\)\s *$" )
(setq p2 "\1 + '\2'" )
(replace-regexp p1 p2 )
)
)
But my function "replaces zero occurrences" of the selected region whereas my interaction rewrites everything exactly as I want.
What am I doing wrong?

You need to double the backslashes in the strings, because backslash is both the string and regular expression escape character:
(defun myH2js (start end)
"Converts html to an (incomplete) JavaScript String concatenation."
(interactive "r")
(let ((p1 "^\\(\\s *\\)\\(.*\\)\\s *$")
(p2 "\\1 + '\\2'"))
(replace-regexp p1 p2 nil start end)
)
)
Note that replace-regexp is not recommended for use inside programs; the online documentation says:
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.

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

Empty string regular expression in emacs lisp

I have this code to find empty strings in a region.
(defun replace-in-region (start end)
(interactive "r")
(let ((region-text (buffer-substring start end))
(temp nil))
(delete-region start end)
(setq temp (replace-regexp-in-string "\\_>" "X" region-text))
(insert temp)))
When I use it on a region it wipes it out, no matter the content of said region, and gives the error "Args out of range: 4, 4".
When I use query-replace-regexp in a region containing:
abcd abcd
abcd 11.11
Been the regexp \_> (note that there is only one backslash) and rep X the resulting region after 4 occurences are replaced is:
abcdX abcdX
abcdX 11.11X
What am I missing here?
It looks like a bug in replace-regexp-in-string.
It first match the regexp in the original string. For example, it finds the end of "abcd". It then picks out the substring that match and, for some reason unknown to me, redo the match on the substring. In this case, the match fails (as it no longer follows a word), but the code that follows it assumes that it succeeded and that the match data has been updated.
Please report this as a bug using M-x report-emacs-bug.
I would suggest that you replace the call to replace-regexp-in-string with a simple loop. In fact, I would recommend that you don't cut out the string and do something like the following:
(defun my-replace-in-region (start end)
(interactive "r")
(save-excursion
(goto-char start)
(setq end (copy-marker end))
(while (re-search-forward "\\_>" end t)
(insert "X")
;; Ensure that the regexp doesn't match the newly inserted
;; character.
(forward-char))))

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 search and replace with regular expressions

With emacs, how can I search and replace regular expressions within a buffer? How can I do this programmatically by evaluating elisp in addition to interactively? For example, replace one or more spaces with tabs, where I need to match ' +' (one or more spaces) with something like C-q-TAB. Is this possible?
Programmatically you can do it as follows:
(defun region-replace-multiple-spaces-with-single-space(beg end)
(interactive "*r")
(save-restriction
(narrow-to-region beg end)
(save-excursion
(goto-char (point-min))
(while (search-forward-regexp " +" nil t)
(replace-match " " nil nil)))))
I believe the answer is M-x replace-regexp
I've got a multi-purpose replace-regexp:
if region is active, replace regexp in region
with numeric argument, call query-replace-regexp
otherwise, replace regexp on current line (actually very useful)
Here's the code:
(global-set-key (kbd "C-/") 'replace-regexp-dwim)
(defun replace-regexp-dwim (arg)
(interactive "P")
(destructuring-bind (from to &rest)
(query-replace-read-args "Replace regexp" nil)
(if arg
(query-replace-regexp from to)
(let ((st (if (region-active-p)
(region-beginning)
(line-beginning-position)))
(en (if (region-active-p)
(region-end)
(line-end-position))))
(goto-char st)
(while (re-search-forward from en t)
(replace-match to nil t))))))