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)
Related
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.
I'm using the native cocoa emacs on OSX as well as on debian in a virtual machine.
When I run the aforementioned function in order to generate latex previews (on the same file) in the OSX or in the VM Emacs, I get huge performance differences: it takes at least 10 times as long to generate the previews in OSX as in the VM (running on the same machine, with the same document!!).
I imagine it is one of the programs in the stack of things that work to produce those previews? Or is calling a subprocess very expensive in OSX? (because it appears one is called for each preview)?
I would be happy to provide additional info, just don't know what...
Thanks in advance!
There are two approach to preview latex frags:
latex -> pdflatex-> dvi -> dvi2png -> png : fast
latex -> xelatex -> pdf -> imagemagick -> png: slow
may be you used the second one...
Eval this (test) function, and check message, you may find which step is slow.....
(defun test (&optional options buffer)
"This calls dvipng."
(require 'ox-latex)
(setq test-timestamp (float-time))
(message "test1: %s" (- (float-time) test-timestamp))
(let* ((tmpdir (if (featurep 'xemacs)
(temp-directory)
temporary-file-directory))
(texfilebase (make-temp-name
(expand-file-name "orgtex" tmpdir)))
(texfile (concat texfilebase ".tex"))
(dvifile (concat texfilebase ".dvi"))
(pngfile (concat texfilebase ".png"))
(fnh (if (featurep 'xemacs)
(font-height (face-font 'default))
(face-attribute 'default :height nil)))
(scale (or (plist-get options (if buffer :scale :html-scale)) 1.0))
(dpi (number-to-string (* scale (floor (* 0.9 (if buffer fnh 140.))))))
(fg (or (plist-get options (if buffer :foreground :html-foreground))
"Black"))
(bg (or (plist-get options (if buffer :background :html-background))
"Transparent")))
(message "test2: %s" (- (float-time) test-timestamp))
(if (eq fg 'default) (setq fg (org-dvipng-color :foreground))
(unless (string= fg "Transparent") (setq fg (org-dvipng-color-format fg))))
(if (eq bg 'default) (setq bg (org-dvipng-color :background))
(unless (string= bg "Transparent") (setq bg (org-dvipng-color-format bg))))
(message "test3: %s" (- (float-time) test-timestamp))
(let ((latex-header (org-create-formula--latex-header)))
(with-temp-file texfile
(insert latex-header)
(insert "\n\\begin{document}\n" "$y=ax+b+c+d$" "\n\\end{document}\n")))
(message "test4: %s" (- (float-time) test-timestamp))
(let ((dir default-directory))
(condition-case nil
(progn
(cd tmpdir)
(call-process "latex" nil nil nil texfile))
(error nil))
(cd dir))
(message "test5: %s" (- (float-time) test-timestamp))
(if (not (file-exists-p dvifile))
(progn (message "Failed to create dvi file from %s" texfile) nil)
(condition-case nil
(call-process "dvipng" nil nil nil
"-fg" fg "-bg" bg
"-D" dpi
;;"-x" scale "-y" scale
"-T" "tight"
"-o" pngfile
dvifile)
(error nil))
(message "test6: %s" (- (float-time) test-timestamp))
(if (not (file-exists-p pngfile))
(if org-format-latex-signal-error
(error "Failed to create png file from %s" texfile)
(message "Failed to create png file from %s" texfile)
nil)
;; Use the requested file name and clean up
(copy-file pngfile "~/testpic.png" 'replace)
(message "test7: %s" (- (float-time) test-timestamp))
pngfile))))
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)
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-/.
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."))))