emacs search and replace with regular expressions - regex

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

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

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

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.

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

What is the simple way to detect emacs buffer has the regexp matched string, then execute a function?

I want run a function org-toggle-inline-images each time when I open a new buffer which contains a link like this: file:folder/file.jpg.
How to do this ?
Try this:
(add-hook 'org-mode-hook 'my-org-mode-hook)
(defun my-org-mode-hook ()
(save-excursion
(save-restriction
(goto-char (point-min))
(when (re-search-forward "file:folder/file\\.jpg" nil :noerror)
(org-toggle-inline-images)))))
It's not clear to me whether you want a regexp match or not. Are you looking for a specific filename, or a pattern?
If the former, use search-forward instead of re-search-forward, and then you don't need the regexp syntax.
If the latter, you'll need to sort out the regexp, in accordance with the org-mode 'link' syntax.
For the double-square-bracket link syntax, you could try this:
(add-hook 'org-mode-hook 'my-org-mode-hook)
(defun my-org-mode-hook ()
;; Enable inline images if there are jpeg images in the file.
(save-excursion
(save-restriction
(goto-char (point-min))
(catch 'done
(while (re-search-forward org-bracket-link-regexp nil :noerror)
(when (string-match "^file:.+\\.jpg" (match-string-no-properties 1))
(org-toggle-inline-images)
(throw 'done t)))))))
But I agree with lawlist's comment -- org-startup-with-inline-images seems to have you covered.