Vim: How to handle newlines when storing multiple commands in registers? - vim

I have a file where I store snippets of vim commands. When I need a snippet, I yank it and then execute it with #". The snippets are stored as a script, one line per command, like this:
:s/foo/bar/g
:echo "hello"
:s/1/2/g
Edit: I removed normal mode commands from the example, as they were not part of the problem.
Now this procedure doesn't work anymore: when executing the snippet, it just stops at the first line as if waiting for a newline.
Is there an option somewhere affecting how # is executed? I'm pretty sure it was working some time ago...
Substituting the newline with a ^M character works but makes the file more difficult to handle.
Additional information:
Here's another symptom: when I yank a snippet, if I execute it with #" it stops at the first line as I just explained. But if I execute it with :# it works. But the help file doesn't seem to imply any difference in how the two commands treat the register's content...

I don't think the problem is ^M vs. ^J. Vim macros will treat either one as a valid end-of-line character for recorded macros. I think the problem is extra newlines.
In your example, there's at least one spurious newline after 2j, and unless you're particularly careful when copying the snippet, there's probably another one after 10k as well. These extra newlines are like pressing <Enter> in Normal mode -- they move the cursor down one line.
Here's what I think you want the snippet to look like:
:s/foo/bar/g
2j:s/1/2/g
10k
(Even that's a little misleading -- you'd still have to be careful not to copy the newline after the 10k.)
Why do these extra newlines make such a big difference? Well, for one thing, they cause you to be at least one line away from where you expect to be, which throws off anything you want to do on a particular line (like execute the :s// command).
More importantly, however -- and this is what I think is happening in your example -- is that Vim stops macro playback if the macro attempts to use <Enter> on the last line of a buffer. (I'm guessing Vim considers it an error, and any error causes a macro to stop running.)
Here's an example. Suppose you've got this snippet stored in register x:
4j
:echo "Done"
(Notice the newline after 4j.)
Furthermore, suppose you have the following five lines (and only these five lines) in a buffer:
line 1
line 2
line 3
line 4
line 5
If you now press #x on line 1, the :echo "Done" never executes. Vim moves the cursor down 4 lines to line 5, then attempts to move down one more line because of the extra newline, but it can't. The macro stops executing at that point, before the :echo command gets a chance to run.
However, it works if you change the x register to this:
4j:echo "Done"
So to return to your original example, I'll bet what's happening is that the extra newline after 2j is attempting to move your cursor somewhere it can't go, and that causes the macro to stop. The bottom line of the screen contains the last command executed (:s/foo/bar/g), which makes it look like Vim is waiting for you to press Return.
Finally, I'd strongly recommend using another method to store and execute Vim command sequences. The technique you're using is tolerable for simple cases, but it's fragile and doesn't scale well. Vim has a full scripting language that includes functions and custom commands, and it can be used to do all the things you're doing now, but in a much more robust fashion. Vim scripting is a big topic, but I'd start here:
:help script
Be sure to read about the :normal command, which lets you execute Normal-mode commands (like 2j and 10k) within scripts.
Good luck!

I finally found the culprit. Somehow I had a command mapping on <C-J> in my .vimrc file. When read with the default cpoptions, this turned into a mapping on <NL>.
How I found out: I noticed that when starting vim with -u ~/.vimrc, it would indeed execute yanked snippets. I generated a session file with and without that commandline option and compared them. This way I found out that a different set of cpoptions where used to read the same .vimrc file, so that in one case the mapping was indeed on <C-J>, in the other it was converted into a mapping on <NL>!
If someone has a similar problem, I suggest to look carefully at the currently set command mappings, with :cmap.

Related

Vim discards input characters at startup

I have been driven crazy for years with Vim's behavior of throwing away input characters. I start vim like this:
$ vim file.c
and then immediately begin typing commands. However, Vim discards some of those characters, causing the wrong action to take place.
Is there something we can put in the .vimrc to solve this issue?
Vim should be able to change the TTY to raw mode without flushing buffered input.
Update: the issue is more precisely characterized, thanks to the following investigation method. I created a script called delayvim which contains:
#!/bin/sh
sleep 2.0
vim "$#"
Now during this two second pause I can type something like iabc<ESC> and then when Vim comes up, everything is cool: the command is processed, abc is inserted and Vim pops back into command mode, with the cursor backed up over the c. Thus, it is not simply flushing the TTY input buffer.
However, if I keep typing during this delay, for instance iabcdefghijk..., it will sometimes lose a letter or two of the alphabet that is typed right around the time when the sleep terminates and the editor launches. For instance, here is the result of one trial I just performed:
abcdefghilmn_
~
~
Where are jk, oops? I am sure I typed them. I didn't type very fast; my cadence was around 4-5 strokes per second, yet two consecutive events disappeared.
Basically, it might be trying to interrogate the terminal, and in the process discarding the input that is mixed up in the response. Or it could be a combination of reading some of the prior input, then flushing the input buffer and losing the rest.
2 points that might help:
1) are you being sure to hit i first, to enter input mode, so that all characters you type afterward should go ahead and be seen in your buffer (on the screen?). Otherwise the letters you type will be processed as commands, which will often do nothing, especially if you're starting an empty file.
Note that a and o are other common commands for telling vim that you are about to begin inserting text starting with the next keystroke.
2) In case the reason on your system has to do with speed, you can look for options to change what happens at startup. For example, if you put this in your .vimrc file
autocmd VimEnter * :sleep 5
Then after processing other startup files, vim or gvim will literally do nothing for 5 seconds before showing your file on the screen. On my system, I was able to type iabcdef during those 5 seconds and when the time was up, I did see abcdef entered into my text file.
If your file was not empty, beware, as (depending on your settings) your vim installation might be kindly returning you to the last place you were editing inside the file, and you will have inserted the text there, instead of at the start.
If this doesn't work, you could try to find other things vim can do (on the web, or using :help from within vim) and program it using autocmd to happen at startup to see if it helps.

How to use ":g/regex/d_" without losing current cursor position

In Vim, if you run
:g/some_word/d_
all lines containing "some_word" are removed and the cursor automatically jumps to the place of last deletion. I want the cursor to stay where it was before the operation. How do I accomplish this?
You can't make the cursor stay but you can make it go back to where it was:
:g/some_word/d_|norm ''
The answer by romainl is a good one, and it should do the trick without any additional fuss.
If this is a common problem, and you'd rather not add the additional norm! '' at the end (or type the key sequence when you're done), you could encapsulate it in a command:
command! -nargs=* G call s:G(<q-args>)
function! s:G(args)
let saved_position = winsaveview()
exe 'g'.a:args
call winrestview(saved_position)
endfunction
The :G command will invoke the s:G function with all of its arguments, which saves the position, runs the normal g with these exact arguments, and then restores it. So you'd do :G/some_word/d_ and it would run the command and restore the cursor in the line/column where it started.
Obviously, this only makes sense if you use it often enough and you don't often work on bare Vims (like on remote servers and such). Otherwise, it might be a better idea to try romainl's suggestion, or get used to typing in ''. Your choice.
Also, if the current position happens to be after some of these lines, the cursor might end up in an unexpected place. For example, if your cursor is on line 7 and the command deletes line 3, then you'll be back at line 7, but all the text will have shifted up one line, so you'll be in the "wrong" place. You could probably play around with this function and compensate for the change, but it'll get pretty complicated pretty fast, so I wouldn't recommend it :)
The anwolib plugin provides a handy :KeepView command; you can apply this to any Ex command, so it's even more generic than the :G command suggested by #AndrewRadev:
:KeepView g/some_word/d_

How to call a function when text is wrapped in vim?

In vim I want to visually make transparent the space I have to write a text in markdown. I use hard wrapping with textwidth=79. I know by some calculation that I'll have 20 lines for a chapter for example. So, what I do is inserting 20 empty lines to get a visual feeling for what I can write. After writing some lines, I manually delete the number of lines already written from the empty lines, so that the visual impression still is correct.
What I want to do, is to automate this deletion process. That means I want vim to automatically remove one line below the last written line if this line is empty and after vim automatically started a new line because I reached 79 characters in the line before. How can I do this?
I know that there are autocommands in vim but I haven't found an <event> that fits to the action: after vim automatically hard wraps a line / reached new line in insert (or however you would like to describe it)
I don't think there's an event for that particular action but there's a buffer-local option called formatexpr that gq & co will use, if set. So you can write a function that inspects any placeholder whitespace, if existing. That function can call the text format command gqq to maintain original feel (+ the cursor movement to the new, empty line).

vim Jump to the next non-identical line

I am looking at files that may have several consecutive identical lines.
Is there a easy way of jumping to the next non-identical line?
Alternatively I would like to be able to fold all the lines that are equal to the initial one showing just the number of linees that are folded.
You could define your own fold-expr:
first set fdm:
:set fdm=expr
then
:set foldexpr=getline(v:lnum)==#getline(v:lnum-1)?1:0
now you can test by typing zM, to close all fold, if you are lucky ^_^ all duplicated lines are folded.
you could type zR to open all folds.
if it works and you open those kind of file very often, you could put the above lines in your .vimrc.(au with ft) if only one time job, you can write mode line into that file.
Try this:
:nmap <F2> "1y$<CR>/^\(<C-R>1$\)\#!<CR>
It maps F2 to:
copy the current line into register 1
search for (and move to) the first line that does not match the contents of register 1
This seems to work well, unless the text of your copied line has escaped characters that will confuse the search regexp. This is because register 1 is just dropped into the search expression without escaping. This would be tricky to fix reliably, but for normal log files, it shouldn't be much of a problem.
Also: if you're not married to vim and just need to read the non-consecutively-duplicated lines of a file, the canonical UNIX way is:
uniq filename
If you want to be in vim but won't need to make changes to the file, try:
:%!uniq
(If you try the latter, be sure to exit without saving)

How to repeat a command with substitution in Vim?

In Unix the ^ allows you to repeat a command with some text substituted for new text. For example:
csh% grep "stuff" file1 >> Results
grep "stuff" file1
csh% ^file1^file2^
grep "stuff" file2
csh%
Is there a Vim equivalent? There are a lot of times I find myself editing minor things on the command line over and over again.
Specifically for subsitutions: use & to repeat your last substitution on the current line from normal mode.
To repeat for all lines, type :%&
q: to enter the command-line window (:help cmdwin).
You can edit and reuse previously entered ex-style commands in this window.
Once you hit :, you can type a couple characters and up-arrow, and it will character-match what you typed. e.g. type :set and it will climb back through your "sets". This also works for search - just type / and up-arrow. And /abc up-arrow will feed you matching search strings counterchronologically.
There are 2 ways.
You simply hit the . key to perform an exact replay of the very last command (other than movement). For example, I type cw then hello to change a word to "hello". After moving my cursor to a different word, I hit . to do it again.
For more advanced commands like a replace, after you have performed the substition, simply hit the : key then the ↑ up arrow key, and it fills your command line with the same command.
To repeat the previous substition on all lines with all of the same flags you can use the mapping g&.
If you have made a substitution in either normal mode :s/A/B/g (the current line) or visual mode :'<,>'s/A/B/g (lines included in the current selection) and you want to repeat that last substitution, you can:
Move to another line (normal mode) and simply press &, or if you like, :-&-<CR> (looks like :&), to affect the current line without highlighting, or
Highlight a range (visual mode) and press :-&-<CR> (looks like :'<,'>&) to affect the range of lines in the selection.
With my limited knowledge of Vim, this solves several problems. For one, the last visual substitution :'<,'>s/A/B/g is available as the last command (:-<UP>) from both normal and visual mode, but always produces an error from normal mode. (It still refers to the last selection from visual mode - not to the empty selection at the cursor like I assumed - and my example substitution exhausts every match in one pass.) Meanwhile, the last normal mode substitution starts with :s, not :'<,'>s, so you would need to modify it to use in visual mode. Finally, & is available directly from normal mode and so it accepts repetitions and other alternatives to selections, like 2& for the next two lines, and as user ruohola said, g& for the entire file.
In both versions, pressing : then & works as if you had pressed : and then retyped s/A/B/, so the mode you were in last time is irrelevant and only the current cursor line or selection determines the line(s) to be affected. (Note that the trailing flags like g are cleared too, but come next in this syntax too, as in :&g/: '<,'>&g. This is a mixed blessing in my opinion, as you can/must re-specify flags here, and standalone & doesn't seem to take flags at all. I must be missing something.)
I welcome suggestions and corrections. Most of this comes from experimentation just now so I'm sure there's a lot more to it, but hopefully it helps anyway.
Take a look at this: http://vim.wikia.com/wiki/Using_command-line_history for explanation.

Resources