Emacs: Combine iseach-forward and recenter-top-bottom - search

Thank you very much in advance for helping.
In Emacs, I like to use iseach-forward (C-s) but I'd like it ever more if the highlighted fount words would be centered in the middle of the screen instead of at the very bottom.
I find myself doing this continuously:
C-s foo
C-s C-s C-s...
oh! that's the 'foo' I was looking for!
C-l
Is there a way to center the search results in the middle of the screen?
Cheers

Best approach would probably be to add the following hook:
(add-hook 'isearch-mode-end-hook 'recenter-top-bottom)
This will execute the recenter-top-bottom command at the completion of every successful incremental search.
Edit: I've investigated a bit, and the functions that are executed on repeated searches for the same string (i.e., with successive input of C-s or C-r during an active search) appear to be isearch-repeat-forward and/or isearch-repeat-backward. Hence, if you wish to recenter on every repeat as well, you need to advise said functions in addition to defining the above hook, like so:
(defadvice
isearch-repeat-forward
(after isearch-repeat-forward-recenter activate)
(recenter-top-bottom))
(defadvice
isearch-repeat-backward
(after isearch-repeat-backward-recenter activate)
(recenter-top-bottom))
(ad-activate 'isearch-repeat-forward)
(ad-activate 'isearch-repeat-backward)
Personally, I find the resulting behavior to be extremely annoying and disorienting, but de gustibus non est disputandum. Perhaps reserving recenter-top-bottom for use in the initial isearch-mode-end-hook and using recenter alone in the advice to the repeat functions would be less obnoxious.
Advising isearch-forward by itself is equivalent to adding the hook I originally suggested above and seemingly has no effect on repeat searches. Adding the hook is simpler and I think more idiomatic, so it should probably be preferred over advising the function.

Thank you Nicolas and Greg.
Your suggestions pointed me to the right direction.
This is the code to accomplish what I was asking for:
(defadvice
evil-search-forward
(after evil-search-forward-recenter activate)
(recenter))
(ad-activate 'evil-search-forward)
(defadvice
evil-search-next
(after evil-search-next-recenter activate)
(recenter))
(ad-activate 'evil-search-next)
(defadvice
evil-search-previous
(after evil-search-previous-recenter activate)
(recenter))
(ad-activate 'evil-search-previous)
Explanation:
I didn't want to get things more complicated adding that I use Evil (vim mode in Emacs), so I omitted that in my question.
I use defadvice to search forward, to move to the next searched item and to move to the previous searched item.
Something worth to mention is that I didn't use (recenter-top-bottom) function. The behaviour was crazy as it centered the first searched item in the middle of the screen, centered the second one to the top, and the third one to the bottom. I just used the function 'recenter' to center it always to the middle of the screen.
For not Evil users the code would look like this: (Not tested!)
(defadvice
isearch-forward
(after isearch-forward-recenter activate)
(recenter))
(ad-activate 'isearch-forward)
(defadvice
isearch-repeat-forward
(after isearch-repeat-forward-recenter activate)
(recenter))
(ad-activate 'isearch-repeat-forward)
(defadvice
isearch-repeat-backward
(after isearch-repeat-backward-recenter activate)
(recenter))
(ad-activate 'isearch-repeat-backward)
Cheers!

You could use defadvice to run recenter-top-bottom after isearch-forward.

For other folks like me who came here looking to configure their spacemacs, #RafaelGP's answer worked for me, with a slight modification: all of the evil functions have -ex- in them, like this:
(defadvice
evil-ex-search-forward
(after evil-search-forward-recenter activate)
(recenter))
(ad-activate 'evil-ex-search-forward)
(defadvice
evil-ex-search-next
(after evil-search-next-recenter activate)
(recenter))
(ad-activate 'evil-ex-search-next)
(defadvice
evil-ex-search-previous
(after evil-search-previous-recenter activate)
(recenter))
(ad-activate 'evil-ex-search-previous)

Setting scroll-margin to a huge value (so that it's greater than half the maximum possible window height), say 250, and maximum-scroll-margin to 0.5 achieves the desired effect. Both are found in customize group "Windows".
However, this obviously does not only affect isearch. So in practice, you might want to set more reasonable values of 10 and 0.25, for example. This approximates recentering, but is not too awkward for general scrolling.

Since some of the most voted answers are dated, here is another one. Advices should always be the last resort when there is no hook to add functions to. Even then, the newer way to add an advice is advice-add. However, in this case we do have a hook.
C-h f isearch-update:
[snip]
This is called after every isearch command to update the display.
The second last thing it does is to run ‘isearch-update-post-hook’.
The last thing is to trigger a new round of lazy highlighting.
[snip]
So simply adding recenter to isearch-update-post-hook should be enough. Here is my use-package configuration for it.
;; isearch
(use-package isearch
:custom (isearch-wrap-pause 'no)
:config
(defun post-isearch ()
(unless (isearch-fail-pos) (recenter)))
:hook (isearch-update-post . post-isearch)
:bind (:map isearch-mode-map ("C-v" . isearch-yank-kill)))

Related

Is there a better method for find and replace in Vim?

Edit: I moved this over to the Vi and Vim site: https://vi.stackexchange.com/questions/13689/how-to-find-and-replace-in-vim-without-having-to-type-the-original-word
I'd like to optimize my "find and replace" workflow in Vim. It's something I do often, as I'm sure most of you do too. Usually something along the lines of -- copy a block and change the name of a variable in a few places. I know, I know, that probably triggers your "why are you copying and pasting code" reflex, but let's not go down that road... There are plenty of valid use cases :)
I'm well aware of the search and replace commands: :s or :%s but I don't like them. It forces me to type out both the full variable name I'm searching for and what I'm changing it to. Maybe there is a better way fix the the amount of typing with :%s? I often use long descriptive variable names, so that is really a deal breaker for me. I also don't like how typing out a variable name from scratch is typo prone and can consume time and brainpower hunting down typos. I much prefer typing it once, and then copying and pasting to just avoid this entirely if possible.
My current workflow uses some combination of movement/yank/select/search/put to move around the file and replace one by one. It is not great but has the benefit of avoiding typing out full variable names. I might just need to type the first few letters with / or use another movement command (i.e. fx) depending on what's around and then hit ve to select the whole word. I also don't mind that I have to repeat for every instance. I never do a full find replace without confirming each change. But it would be much preferable if I could repeat the replacement action with a single keystroke (which I can't do with this method). each replacement is usually something like n then ve then p (or even worse "0p)
Is there a faster way?
My own workflow is similar to yours:
To start, get the cursor on one instance, possibly with / or by navigation.
Hit * to find the next instance of that word.
Change one instance with cw and then the new variable name.
Then it's fast: n/N to get to the next/previous instance, and . to repeat the last edit.
This workflow gives me the same advantage as yours, in that I can review each case before applying the change, but it's just two keystrokes for each additional change.
Hope this helps.
I like the "visual highlight then edit" approach.
shift + v to highlight the region that you want to modify.
then :s/old/new/r where old is what word you want to replace with new.
r changes the first instance of that word old.
Note* There are options other than r which modify its behavior how you want to replace the word.

Revisiting the top of the tagstack

I'm using tags in Vim, with tagstack enabled.
I use Ctrl-] to select a tag, Vim gives me three possibilities, I choose the second.
I then return to the original context with :pop.
I'd then like to select the same tag again, but the place I jumped to last time is not on the tagstack, because I only jumped to it, not from it. I know I can use Ctrl-] and select the second of three again, but I'd like the facility to simply go where I went before.
Is this possible?
You can use Control-o to jump to the last place you visited. It is not exactly what you described but effectively you reach the line you want faster.
Also, you can hit 2 Control-t if you want to visit the second option, which seems to skip the choosing part.
Otherwise, it doesn't seem to be possible as you actually lose the information about the tag when you :pop.

Undo ESC-keystroke in vim

I am using vim for quite some time now, but several times per day I accidentally encounter an inconvenience by hitting ESC too early.
Assume I am editing the following file:
I want to change 'house' in this line.
'house' should stay like this.
This 'house' should become 'home'
The other 'house' should also change.
I want to change house to home in all lines except the second one. (In this simple example it would easy to write a :s command and perform the task, but usually the task is more specific and manual replacements are quicker and less error prone.) I navigate to the first occurrence of house, press cw, type hone, and hit ESC. After hitting ESC I realize that by accident I typed the letter n instead of m.
I could navigate to the next occurrence of house and hit . to repeat the faulty replacement. In this case I have to fix all hones and replace the n by m afterwards.
I could fix the mistake immediately, but then I can not repeat the same house->home replacement, since . would repeat the n->m replacement.
All of this would be no problem, if I had spotted the mistake before hitting ESC. My question is, if there is a way to undo leaving the insert mode, such that . will repeat both actions? Or similarly, is there a way to tell . to repeat the last to operations?
(Of course this sounds like recording a macro, but since I end up in this situation by mistake, I have not started a macro recording.)
As far as I am concerned, there's no command history of vim in that you could repeat the last two operations.
However, there's a plugin that could help you accomplish that. It's called the RepeatLast.vim plugin to address this exact requirement. It provides a 2\. key binding. The cost for using that plugin and how the plugin actually works is that... it actually enables macro recording all the time. But if you could live with that, it should deal with this sort of situation pretty fine.

Emacs / vim quick copy paste

I'm trying to make a transition to emacs (using evil mode/vim keybindings) and I'm having a hard time feeling more efficient/productive than if I just used the mouse. Here is an example of a situation where I find myself really slow:
for i in range(self.allData.shape[0]):
self.correctSequence = self.displayNumbers(i, self.allData)
self.userSequence = self.numberEntry()
self.allData.set_value(i, 'userSequence', ''.join(self.userSequence))
if len(self.correctSequence) != len(self.userSequence):
self.allData.set_value(i, 'correct', 0)
else:
if list(reversed(self.correctSequence)) == self.userSequence:
self.allData.set_value(i, 'correct', 1)
else:
self.allData.set_value(i, 'correct', 0)
It would be very common for me to have to change the first 4 instances of self.allData to something else (self.testData, for example), leaving the last 2 untouched.
Normally this wouldnt be too bad with a mouse. For example, I could replace the first allData with testData, copy it, use the mouse to the next 3 occurences and just hit CTRL-V for each one. Or better yet, just use multiple cursors in sublime/atom and replace all 4 in one go
I use spacesmacs in emacs with vim keybindings. So, in emacs I find myself having to do something like the following:
SPC-SPC a (avy jump to words beginning with a)
cw testData
Repeat those 2 steps once for each word I want to replace
This seems really inefficient and I'm wondering: am I just using an inefficient method? Is there a faster way to do what I want?
It seems that even if I managed to complete those steps really fast (4 times), theres still A LOT more typing one would have to do, and I fail to see how this would be faster than just reaching for the mouse. Yes, one could make the argument that I'm losing time by constantly reaching for the mouse, but in my mind I'm saving typing time by reaching for the mouse because I can just hit CTRL-V a few times to achieve what I want. Where exactly are the vim speed gains in a situation like this?
If you just want to replace, you can use query-replace, and replace the word one by one.
You can use replace-string too, but remember to limit replacement to part of the buffer, activate the region around that part.
Anyway, these commands could prevent you from finding the word by your eyes, moving cursor by mouse and moving your hand back to keybaord. And they could avoid probable overlook too. At least I don't want to leave my hands from the keyboard when typing. :)
I'm not sure how "vim-like" Spacemacs is, but you could do it like this in Vim:
/all<CR>
cgntest<Esc>
.
.
.
or:
/all<CR>
cetestData<Esc>
n.
n.
n.
or:
:,$s/allD/testD/gc<CR>
Maybe one of these methods works in Spacemacs too?
In addition to the usual (and generally the best) answer, query-replace (see #songyuanyao's answer), you can use the secondary selection to advantage to selectively paste the same thing at various places. Unlike the region, the secondary selection is unrelated to the cursor position (aka point), so it is always available regardless of where the cursor is.
And unlike query-replacing, you can paste it on demand, instead of having to answer for each matching occurrence. If you use delete-selection mode then just select some text to replace and paste the secondary selection to replace it.
You can set and paste the secondary selection using the mouse - see Secondary Selection on the Emacs Wiki, and see the Emacs manual, node Secondary Selection.
If you use library second-sel.el then you can use the secondary selection from the keyboard as well, and get a lot more use out of it.

Which editors out of Emacs, Vim and JEdit support multiple simultaneous text insertion points?

Background: JEdit (and some other text editors as well) support a feature called Multiple simultaneous text insertion points. (at least that's what I'm calling it here).
To understand what this means, take a look at the link.
Out of all the features in use in modern text editors, initial research seems to indicate that this is one feature that both Emacs and Vim do not actually support. If correct, this would be pretty exceptional since it's quite difficult to find a text editor feature that has not made its way into at least one of these two old-school editors.
Question: Has anyone ever seen or implemented this feature in either Emacs, Vim, or both? If so, please point me to a link, script, reference or summary that explains the details.
If you know an alternate way to do the same (or similar) thing, please let me know.
The vim way to do this is the . command which repeats the last change. So, for instance, if I change a pointer to a reference and I have a bunch of
obj->func
that I want to change to
obj.func
then I search for obj->, do 2cw to change the obj-> to obj., then do n.n.n. until all the instances are changed.
Perhaps not a flexible as what you're talking about, but it works frequently and is very intuitive and fast when it does.
moccur-edit.el almost does what you want. All the locations matching the regexp are displayed, and the editing the matches makes changes in the corresponding source. However, the editing is done on a single instance of the occurrence.
I imagine it'd be straight forward to extend it to allow you to edit them all simultaneously (at least in the simple case).
There is a demo of it found here.
Turns out, the newest versions of moccur-edit don't apply changes in real-time - you must apply the changes. The changes are also now undoable (nice win).
In EMACS, you could/would do it with M-x find-grep and a macro. If you really insist that it be fully automatic, then you'd include the find-next in the macro.
But honestly, this strikes me as a sort of Microsoft-feature: yes, it adds to the feature list, but why bother? And would you remember it existed in six months, when you want to use it again?
For emacs, multiple-cursors does exactly that.
Have a look at emacsrocks episode 13, by the author of the module.
I don't think this feature has a direct analogue in either Emacs or Vim, which is not to say that everything achievable with this feature is not possible in some fashion with the two 'old-school' editors. And like most things Emacs and Vim, power-users would probably be able to achieve such a task exceedingly quickly, even if mere mortals like myself could spend five minutes figuring out the correct grep search and replace with appropriate back-references, for example.
YASnippet package for Emacs uses it. See 2:13 and 2:44 in the screencast.
Another slight similarity: In Emacs, the rectangle editing features provided by cua-selection-mode (or cua-mode) automatically gives you multiple insertion points down the left or right edge of the marked rectangle, so that you can type a common prefix or suffix to all of those lines.
e.g.:
M-x cua-selection-mode RET (enable the global minor mode, if you don't already use this or cua-mode)
C-RET down down down (marks a 1x3 character rectangle)
type prefix here
C-RET (unmark the rectangle to return to normal editing)
It should be something like this in vim:
%s/paint.\((.*),/\1.paint(/
Or something like that, I am really bad at "mock" regular expressions.
The idea is substitute the pattern:
/paint(object,/
with
/object.paint(/
So, yes, it is "supported"
It seemed simple to do a basic version of this in Emacs lisp. This is for when you just want two places to insert text in parallel:
(defun cjw-multi-insert (text)
"insert text at both point and mark"
(interactive "sText:")
(insert-before-markers text)
(save-excursion
(exchange-point-and-mark)
(insert-before-markers text)))
When you run it, it prompts for text and inserts it at both point (current position) and mark. You can set the mark with C-SPC. This could be easily extended for N different positions. A function like set-insert-point would record current position (stored as an Emacs marker) into a list and then when you run the multi-insert command, it just iterates through the list adding text at each.
I'm not sure about what would a simple way to handle a more general "multi-editing" feature.
Nope. This would be quite difficult to do with a primarily console-based UI.
That said, there is similar features in vim (and emacs, although I've not used it nearly as much) - search and replace, as people have said, and more similarly, column insert mode: http://pivotallabs.com/users/brian/blog/articles/350-column-edit-mode-in-vi

Resources