emacs: search and replace on a region - search

So, I have this excellent function (that someone made for me) for doing multiple search and replaces on an entire buffer.
(defun accent-replace-whole-buffer ()
"Corrects macrons from badly scanned latin"
(interactive "*")
(dolist (ele (list ?â ?ä ?ê ?ë ?î ?ô ?ü ?ï))
(setq elt (char-to-string ele))
(goto-char (point-min))
(while (search-forward elt nil t 1)
(replace-match
(char-to-string
(pcase ele
(`?â ?ā)
(`?ä ?ā)
(`?ê ?ē)
(`?ë ?ē)
(`?î ?ī)
(`?ô ?ō)
(`?ü ?ū)
(`?ï ?ī)))))))
and I would like to make another function, which does this, only on the selected region.
How would I go about this? Is there a nice tutorial anywhere?

Use narrow-to-region, inside save-restriction:
(defun accent-replace-in-region (begin end)
"Corrects macrons in active region from badly scanned latin"
(interactive "*r")
(save-restriction
(narrow-to-region begin end)
(dolist (ele (list ?ā ?ā ?ē ?ē ?ī ?ō ?ū ?ī))
(setq elt (char-to-string ele))
(goto-char (point-min))
(while (search-forward elt nil t 1)
(replace-match
(char-to-string
(pcase ele
(`?â ?ā)
(`?ä ?ā)
(`?ê ?ē)
(`?ë ?ē)
(`?î ?ī)
(`?ô ?ō)
(`?ü ?ū)
(`?ï ?ī))))))))

Instead of the builtin interactive code "r", using that form:
(defun MYFUNTION (&optional beg end)
(interactive "*")
(let ((beg (cond (beg)
((use-region-p)
(region-beginning))
(t (point-min))))
(end (cond (end (point-marker end))
((use-region-p)
(point-marker (region-end)))
(t (point-marker (point-max))))))
(and beg end (narrow-to-region beg end))
(goto-char beg)
;; here some replace example revealing the problem mentioned
(while (re-search-forward "." end t 1)
(replace-match "+++++++++"))))
Basically two reasons for this: Make sure, the region is visible when acting upon. "r" doesn't care for transient-mark-mode. Unfortunately use-region-p also has some quirks. Second reason: end needs to be updated when a replacing will change the length of the region.

Related

How to pass current-prefix-arg to this-command

I want to reuse current-prefix-arg when calling this-command. I can imagine ways of capturing its value and passing it to modified functions. But, this doesn't seem like a great solution. How can I reuse it (eg. push it back onto a sort of call stack, or whatever emacs uses)?
The example is a simple function to increment/decrement numbers in region I was just writing where I want to temporarily bind i to increment or decrement if it was called with prefix. Can I do this without adding another argument to the function, but just using the original prefix?
(defun my-toggle-increment-numbers (start end)
"Simple function to increment numbers in region. Decrement with prefix."
(interactive "r")
(let (deactivate-mark)
(goto-char start)
(while (re-search-forward "\\([[:digit:]]+\\)" end 'move)
(replace-match (number-to-string
(+ (if current-prefix-arg -1 1)
(string-to-number (match-string 1))))
nil nil nil 1))
;; what is good way to reused the current-prefix-argument value when
;; calling this-command?
(set-transient-map
(let ((km (make-sparse-keymap)))
(define-key km "i" this-command)
km)
t)))
Try this:
(defun my-toggle-increment-numbers (start end &optional decrement)
"Simple function to increment numbers in region. Decrement with prefix."
(interactive "r\nP")
(let (deactivate-mark)
(setq start (copy-marker start)
end (copy-marker end))
(goto-char start)
(while (re-search-forward "\\([-]?[[:digit:]]+\\)" end 'move)
(replace-match (number-to-string (+ (if decrement -1 1)
(string-to-number (match-string 1))))
nil nil nil 1))
(set-transient-map
(let ((km (make-sparse-keymap)))
(define-key km "i" `(lambda ()
(interactive)
(my-toggle-increment-numbers ',start ',end ',decrement)))
km)
t)))
You forgot to include the minus sign preceding your [[:digit:]].
You need to either use lexical binding or construct a command that binds the current values of start, end and current-prefix-arg (or decrement - see next).
It's a bit simpler if you use an argument instead of current-prefix-arg.
(You ask if you can just use current-prefix-arg instead of adding an arg for it. Yes. In that case, let-bind current-prefix-arg in the constructed command that gets invoked for subsequent calls.)
Since replacement can change the number of digits in a numeral (e.g. from 9 to 10, you need to use a marker instead of the number value of end in subsequent calls.
I think the cleanest way is to create a command which calls this-command passing it the prefix arg:
;; This relies on -*- lexical-binding:t -*-
(defun my-toggle-increment-numbers (start end)
[...]
(set-transient-map
(let ((km (make-sparse-keymap))
(tc this-command)
(cpa current-prefix-arg))
(define-key km "i"
(lambda () (interactive)
(let ((current-prefix-arg cpa))
(call-interactively tc))))
km)
t)))
It's probably more verbose than what you were imagining, but I think it's the closest there is to what you're asking. A simpler way to get almost the same result could be:
(defun my-toggle-increment-numbers (start end)
[...]
(setq prefix-arg current-prefix-arg)
(set-transient-map
(let ((km (make-sparse-keymap)))
(define-key km "i" this-command)
km)
t)))

how to search string in other buffer in emacs?

I have llist of words in one buffer and bunch of text lines in another. I am looking for something like selected word in one buffer has to be highlighted in another buffer. Does emacs support it?
Try org-mode.
You can put a link [[file:yourFile::yourSearchExpression]] into the org-buffer.
If you click the link the buffer visiting yourFile is searched for yourSearchExpression (a regular expression).
I use the extension below. With this extension you can also write [[buffer:yourBuffer::yourSearchExpression]]. Here, yourBuffer must be a buffer (not a file).
But, you can open the file of interest beforehand.
The first character of yourSearchExpression can be an operator.
If it is a + then yourBuffer is searched forward starting at the current point position.
If it is a - then yourBuffer is searched backward starting at the current point position. If no operator is given then the buffer is searched forward starting at the beginning of the buffer. Other operators can easily be implemented.
The good thing is that you can easily pre-define your searches in the org-file. You have one file where you organize your searches for one subject. The stuff you search can be spread over several buffers.
That becomes handy if you need the search of this type again later on.
I have added the high-lighting quite recently. Therefore, if you encounter problems. Just give me a note.
;; stolen from isearch:
(defface search-highlight-face
'((((class color) (min-colors 88) (background light))
;; The background must not be too dark, for that means
;; the character is hard to see when the cursor is there.
(:background "magenta3" :foreground "lightskyblue1"))
(((class color) (min-colors 88) (background dark))
(:background "palevioletred2" :foreground "brown4"))
(((class color) (min-colors 16))
(:background "magenta4" :foreground "cyan1"))
(((class color) (min-colors 8))
(:background "magenta4" :foreground "cyan1"))
(t (:inverse-video t)))
"Face for highlighting search matches."
:group 'search-highlight)
(defface search-highlight-other-face
'((((class color) (min-colors 88) (background light))
(:background "paleturquoise"))
(((class color) (min-colors 88) (background dark))
(:background "paleturquoise4"))
(((class color) (min-colors 16))
(:background "turquoise3"))
(((class color) (min-colors 8))
(:background "turquoise3"))
(t (:underline t)))
"Face for lazy highlighting of matches other than the current one."
:group 'search-highlight)
(require 'cl) ;; for find
(defun search-highlight (se &rest opt)
"Like the group of `search-forward' commands with highlighting of the matches.
Note, that this function should only be used in commands since it is directly visible.
The window of the current buffer must be alive and should be visible.
Options:
:back non-nil: search backward instead of forward
:re non-nil: SE is regular expression
:noerror non-nil: issue error when not found
:bound bound of search
:count search that many times (defaults to 1)
:face use this face to highlight
:others non-nil: highlight also other matches within visible area of buffer
:recenter recenter point in window vertically (before highlighting others)
"
(interactive "sSearch expression:")
(search-highlight-cleanup) ;; If anything went wrong prevously.
(let* (ol
(regexp? (when (plist-get opt :re) "-regexp"))
(cmd (intern-soft (concat "search"
(if (plist-get opt :back) "-backward" "-forward")
regexp?
)))
e)
(when (funcall cmd se (plist-get opt :bound) (plist-get opt :noerror) (plist-get opt :count))
(setq ol (make-overlay (match-beginning 0) (match-end 0)))
(overlay-put ol 'face 'search-highlight-face))
(when (plist-get opt :recenter) (recenter))
(if (plist-get opt :others)
(save-excursion
(goto-char (window-start))
(setq e (window-end nil t))
(setq cmd (intern-soft (concat "search-forward" regexp?)))
(while (funcall cmd se e t)
(unless (and ol (= (match-beginning 0) (overlay-start ol)))
(overlay-put (make-overlay (match-beginning 0) (match-end 0))
'face 'search-highlight-other-face)))))
(add-hook 'pre-command-hook 'search-highlight-cleanup t t)))
(defun search-highlight-cleanup ()
"Remove highlights for search-highlight-mode."
(interactive)
(remove-hook 'pre-command-hook 'search-highlight-cleanup t)
(remove-overlays 0 (buffer-size)
'face 'search-highlight-face)
(remove-overlays 0 (buffer-size)
'face 'search-highlight-other-face))
(defun org-at-buffer ()
"Check whether point is at [[buffer:BUFFER::SEARCH]].
BUFFER is just the name of an existing buffer.
You can make sure that the buffer exists by [[file:...]].
::RE is an optional regular expression.
The first character of SEARCH may be an operator:
+ Start at current point and search forward.
- Start at current point and search backward.
If the operator is missing the buffer is searched for RE starting at the beginning of BUFFER.
"
(save-excursion
(let ((pt (point))
b e
name
re)
(when (search-backward "[[buffer:" (line-beginning-position) 'noErr)
(forward-char)
(setq b (+ (point) (length "[buffer:")))
(forward-sexp) ;; actual link
(setq e (1- (point)))
(when (looking-at "\\[") ;; optional description
(forward-sexp))
(when (>= (point) pt)
(goto-char b)
(if (search-forward "::" e 'noErr)
(setq
name (buffer-substring-no-properties b (match-beginning 0))
re (buffer-substring-no-properties (match-end 0) e)
)
(setq name (buffer-substring-no-properties b e)))
(switch-to-buffer-other-window name)
(when re
(if (and (> (length re) 0) (find (aref re 0) "+-"))
(let ((op (aref re 0)))
(setq re (substring re 1))
(cond
((= op ?+)
(when (= (point) (point-max))
(goto-char (point-min)))
(search-highlight re :re t :noerror t :others t :recenter t))
((= op ?-)
(when (= (point) (point-min))
(goto-char (point-max)))
(search-highlight re :back t :re t :noerror t :others t :recenter t))
(t
(error "Unexpected op."))))
(goto-char (point-min))
(search-highlight re :re t :noerror t :others t :recenter t)))
t)))))
(add-to-list 'org-open-at-point-functions 'org-at-buffer)

"1 of n" result for Emacs search

When I use incremental search in emacs I can't know where I am in whole matches. In Chrome browser it says location using "2 of 30". How can I do that in Emacs?
Here's my first attempt to implement this.
It uses the lazy highlighting that isearch implements, and forces the highlighting to cover the entire buffer (not just the visible portions) - which can slow down the search on large buffers. It then updates the display to include a current position (of total) relative to the highlighted search results.
This has the drawback that it is dependent on the entire buffer being searched and highlighted. And, sometimes when you C-s to the next match, the display changes to (0 of 1) even though the highlights are clearly still present.
But, it seems to be a reasonable first cut.
Prepare for big cut/paste:
(require 'isearch)
(defun lazy-highlight-cleanup (&optional force)
"Stop lazy highlighting and remove extra highlighting from current buffer.
FORCE non-nil means do it whether or not `lazy-highlight-cleanup'
is nil. This function is called when exiting an incremental search if
`lazy-highlight-cleanup' is non-nil."
(interactive '(t))
(if (or force lazy-highlight-cleanup)
(while isearch-lazy-highlight-overlays
(delete-overlay (car isearch-lazy-highlight-overlays))
(setq isearch-lazy-highlight-overlays
(cdr isearch-lazy-highlight-overlays))))
(when isearch-lazy-highlight-timer
(cancel-timer isearch-lazy-highlight-timer)
(setq isearch-message-suffix-add "")
(setq isearch-lazy-highlight-timer nil)))
(defun isearch-lazy-highlight-search ()
"Search ahead for the next or previous match, for lazy highlighting.
Attempt to do the search exactly the way the pending Isearch would."
(condition-case nil
(let ((case-fold-search isearch-lazy-highlight-case-fold-search)
(isearch-regexp isearch-lazy-highlight-regexp)
(search-spaces-regexp isearch-lazy-highlight-space-regexp)
(isearch-word isearch-lazy-highlight-word)
(search-invisible nil) ; don't match invisible text
(retry t)
(success nil)
(isearch-forward isearch-lazy-highlight-forward)
(bound (if isearch-lazy-highlight-forward
(min (or isearch-lazy-highlight-end-limit (point-max))
(if isearch-lazy-highlight-wrapped
isearch-lazy-highlight-start
(isearch-window-end)))
(max (or isearch-lazy-highlight-start-limit (point-min))
(if isearch-lazy-highlight-wrapped
isearch-lazy-highlight-end
(isearch-window-start))))))
;; Use a loop like in `isearch-search'.
(while retry
(setq success (isearch-search-string
isearch-lazy-highlight-last-string bound t))
;; Clear RETRY unless the search predicate says
;; to skip this search hit.
(if (or (not success)
(= (point) bound) ; like (bobp) (eobp) in `isearch-search'.
(= (match-beginning 0) (match-end 0))
(funcall isearch-filter-predicate
(match-beginning 0) (match-end 0)))
(setq retry nil)))
success)
(error nil)))
(defun isearch-find-current-overlay ()
(let ((total 0)
(count 1)
(olist isearch-lazy-highlight-overlays))
(while olist
(setq total (1+ total))
(if (< (overlay-end (car olist)) (point))
(setq count (1+ count)))
(setq olist
(cdr olist)))
(cons count total)))
(add-hook 'isearch-update-post-hook 'isearch-count-message)
(defun isearch-count-message ()
(let ((counts (isearch-find-current-overlay)))
(setq isearch-message-suffix-add (format " (%d of %d)" (car counts) (cdr counts)))))
(defun isearch-window-start ()
"force highlight entire buffer"
(point-min))
(defun isearch-window-end ()
"force highlight entire buffer"
(point-max))
(defun isearch-lazy-highlight-update ()
"Update highlighting of other matches for current search."
(let ((max lazy-highlight-max-at-a-time)
(looping t)
nomore)
(with-local-quit
(save-selected-window
(if (and (window-live-p isearch-lazy-highlight-window)
(not (eq (selected-window) isearch-lazy-highlight-window)))
(select-window isearch-lazy-highlight-window))
(save-excursion
(save-match-data
(goto-char (if isearch-lazy-highlight-forward
isearch-lazy-highlight-end
isearch-lazy-highlight-start))
(while looping
(let ((found (isearch-lazy-highlight-search)))
(when max
(setq max (1- max))
(if (<= max 0)
(setq looping nil)))
(if found
(let ((mb (match-beginning 0))
(me (match-end 0)))
(if (= mb me) ;zero-length match
(if isearch-lazy-highlight-forward
(if (= mb (if isearch-lazy-highlight-wrapped
isearch-lazy-highlight-start
(isearch-window-end)))
(setq found nil)
(forward-char 1))
(if (= mb (if isearch-lazy-highlight-wrapped
isearch-lazy-highlight-end
(isearch-window-start)))
(setq found nil)
(forward-char -1)))
;; non-zero-length match
(let ((ov (make-overlay mb me)))
(push ov isearch-lazy-highlight-overlays)
;; 1000 is higher than ediff's 100+,
;; but lower than isearch main overlay's 1001
(overlay-put ov 'priority 1000)
(overlay-put ov 'face lazy-highlight-face)
(overlay-put ov 'window (selected-window))))
(if isearch-lazy-highlight-forward
(setq isearch-lazy-highlight-end (point))
(setq isearch-lazy-highlight-start (point)))))
;; not found or zero-length match at the search bound
(if (not found)
(if isearch-lazy-highlight-wrapped
(setq looping nil
nomore t)
(setq isearch-lazy-highlight-wrapped t)
(if isearch-lazy-highlight-forward
(progn
(setq isearch-lazy-highlight-end (isearch-window-start))
(goto-char (max (or isearch-lazy-highlight-start-limit (point-min))
(isearch-window-start))))
(setq isearch-lazy-highlight-start (isearch-window-end))
(goto-char (min (or isearch-lazy-highlight-end-limit (point-max))
(isearch-window-end))))))))
(unless nomore
(setq isearch-lazy-highlight-timer
(run-at-time lazy-highlight-interval nil
'isearch-lazy-highlight-update)))))))))
I saw this the other day:
https://github.com/syohex/emacs-anzu
anzu.el is an Emacs port of anzu.vim. anzu.el provides a minor mode which displays current match and total matches information in the mode-line in various search modes.
The screenshot on the github page is a gif animation of its functionality.
Out of the box, you can use M-so while isearching to at least see the total number of matches (even if it unfortunately isn't smart enough to track the match that you're on in the original buffer).
Add the following to your emacs init file. Despite the use of count-matches, it works very fast on my laptop. I have not tried it with very large files.
(defun my-isearch-update-post-hook()
(let (suffix num-before num-after num-total)
(setq num-before (count-matches isearch-string (point-min) (point)))
(setq num-after (count-matches isearch-string (point) (point-max)))
(setq num-total (+ num-before num-after))
(setq suffix (if (= num-total 0)
""
(format " [%d of %d]" num-before num-total)))
(setq isearch-message-suffix-add suffix)
(isearch-message)))
(add-hook 'isearch-update-post-hook 'my-isearch-update-post-hook)

Emacs Lisp search-backward

Preamble
Using VTK library with C++, quite often I have to write something like this:
vtkInteractorStyleRubberBandZoom *isrbz = vtkInteractorStyleRubberBandZoom::New();
Furthermore, every time I need to use a new VTK class in my program, I have to go somewhere up the source file and add #include "vtkInteractorStyleRubberBandZoom.h"
How do I automate it, so I have to type each of the excruciatingly long class names one time instead of three?
I tried writing an Emacs minor mode for it. There are probably existing solutions out there already (YaSnippet?), but I thought that writing it myself would be a good excercise, too.
Code
;vtk-mode.el
;add to .emacs:
;(load "vtk-mode")
;(global-set-key [(control =)] 'expand-vtk)
(defun expand-vtk ()
(interactive)
(setq now (point))
(setq vtkstart (search-backward "vtk"))
(setq vtkend (- (search-forward " ") 1))
(setq vtkname (buffer-substring vtkstart vtkend))
;check for #include "vtkBlah.h"
(setq includename (format "#include \"%s.h\"\n" vtkname))
(search-backward includename nil (append-include-vtk includename))
(goto-char (+ now (length includename)))
(insert (format "= %s::New();" vtkname)))
(defun append-include-vtk (incname)
(goto-char 0)
(insert incname))
Problem
Basically, it works, except that searching for an include name always fails, e. g.:
vtkSomething *smth /*press C-= here, it looks backward for
#include "vtkSomething.h", can't find it and
calls append-include-vtk, adding it to the beginning
of the file, then comes back here and expands this line into: */
vtkSomething *smth = vtkSomething::New();
//and let's add another instance of vtkSomething...
vtkSomething *smth2 /*press C-= again, it looks backward for
#include "vtkSomething", and fails, despite the fact
that it was added by the previous command. So it adds it again."*/
What am I doing wrong here with search-backward?
(there's another (at least one) bug in the code, I shouldn't add the (length includename) if the search-backward for it was successful, but for now I am more interested in how to make it successful, in the first place)
OK, I got it. Somehow I got an idea that the third argument of search-backward (noerror) is a callback, which it isn't. Therefore it is evaluated every time, not just when the search fails. It should be something like this instead:
(defun expand-vtk ()
(interactive)
(setq now (point))
(setq vtkstart (search-backward "vtk"))
(setq vtkend (- (search-forward " ") 1))
(setq vtkname (buffer-substring vtkstart vtkend))
;check for #include "vtkBlah.h"
(setq includename (format "#include \"%s.h\"\n" vtkname))
(if (search-backward includename nil t)
(goto-char now)
(progn (append-include-vtk includename)
(goto-char (+ now (length includename)))))
(insert (format "= %s::New();" vtkname)))
(defun append-include-vtk (incname)
(goto-char 0)
(insert incname))
A command that's built into Emacs and will help you avoid typing excruciatingly long class names is dabbrev-expand (bound to M-/):
(dabbrev-expand ARG)
Expand previous word "dynamically".
Expands to the most recent, preceding word for which this is a prefix.
If no suitable preceding word is found, words following point are
considered. If still no suitable word is found, then look in the
buffers accepted by the function pointed out by variable
`dabbrev-friend-buffer-function'.
Having typed vtkInteractorStyleRubberBandZoom once, the next time you need it you just type vtkI M-/.

Searching with intelligent bracket counting (Elisp)

I have the following function that deletes the LaTeX command surrounding the current cursor position:
(defun remove-tex-cmd ()
(interactive)
(save-excursion
(let (cur-point beg-point end-point)
(setq cur-point (point))
(catch 'notexcmd
(if (not (re-search-backward "\\.*?{" nil t)) ; now the point is at the {
(throw 'notexcmd nil))
(search-backward "\\" nil t)
(setq beg-point (point))
(re-search-forward "}")
(setq end-point (point))
(if (> end-point cur-point)
(kill-region beg-point end-point))
(throw 'notexcmd nil)))
(if 'notexcmd
(message "no tex command at point"))))
It works well except for the following situation, because it simply matches the next closing }:
\test{a<cursor here>sdf ${bla}+1$}
results in
+1$}
I could, of course, count the opening and closing brackets. However, as this problem should occur frequently, I wonder whether there exists some more intelligent search function, or am I missing a totally different point?
Use list- or sexp- based operations:
(defun remove-tex-cmd ()
(interactive)
(backward-up-list 1)
(backward-sexp 1)
(kill-sexp 2))
To handle scan error when outside parentheses:
(defun remove-tex-cmd ()
(interactive)
(condition-case nil
(progn
(backward-up-list 1)
(backward-sexp 1)
(kill-sexp 2))
(scan-error (message "Outside parentheses."))))

Resources