Related
I am confused about doing a really simple task. I need to add lines to the current buffer with a VimL script.
As it is known, there are two functions for this: append() and setline(). Both of them work approximately similarly — the first argument is the number of the line to perform insertion at (after or before, respectively), the second one defines the line(s) to add.
But the problem is that I cannot determine the number properly without inventing weird tricks. The only standard method I have found is to ask the line('$') function. So, to add a line to the end of the current buffer it can be append(line('$'), ['example']) or setline(line('$')+1, 'example'). But this approach gives unpredictable results depending on the content of the current buffer.
Let’s consider append(line('$'), ['example']) first. If the buffer is not empty, I get what I expect:
first existing line
example
But if the buffer is empty, I will get the first line blank:
example
Now, let’s take a look at setline(line('$')+1, 'example'). It works conversely. If the buffer is empty, everything is fine:
example
But if the buffer is not empty, trying to add “example” after the last line, I get a mess of text lines:
first existing line
example
second existing line
Well, I could test the last line whether it is blank or not. But what if the last line is empty due to the content?
So, here are my questions. Should my code analyze the content of the current buffer to decide which of the functions it must choose, considering whether the existing last line is empty because this is how the function line('$') works or it empty due to the desired content? Or should I use another approach that I have missed?
Perhaps, I should utilize something like:
write! buffer.tmp
exe '! printf '.join(g:my_lines_to_add, "\n")."\n >>buffer.tmp"
edit! buffer.tmp
rm -f buffer.tmp
Is there a trivial standard way to solve this trivial task? My text may sound brute, but I love Vim and count myself as its experienced user. Thank you in advance for your help.
You have a few conflicting questions in here.
As you've already noticed, you use append() (or, to borrow a phrase from this answer, "appendline()") or setline(). Both of these take a line number and a string or an array, which is then handled appropriately by the function.
the first argument is the number of the line to perform insertion at (after or before, respectively).
Strictly speaking, no. Go to any file, and go to the first line. Run :echo line('.'). It'll return 1. This is the part I hate most about Vim/Vimscript: line() and getline() are 1-indexed. But if you use the array returned by getline(), you get a 0-indexed array. Consequently:
append() adds a number of lines after the first argument. It doesn't affect the line at idx. setline() changes the line at idx and len(text) out. This means that the functions perform their operation, respectively, after or at, not before.
Should my code analyze the content of the current buffer to decide which of the functions it must choose, considering whether the existing last line is empty because this is how the function line('$') works or it empty due to the desired content?
I can't answer this with a definite yes or no, because it's a matter of what you want to do. There's different approaches based on what end result you're looking for, and how much scripting you're fine with.
You may some times need to do comparisons, but you often don't need to make it that complicated. In your particular case, as I'll demonstrate in a bit, you can get away with checking the line count, and then making sure the only line isn't empty. It's a bit messy to write, but it does the trick. You can also do various comparisons.
Consider append(line('$') - 1, ["example"]): in an empty buffer, this gives
example
... and note the blank line at the end of the file. Line 1 is still preserved, because append doesn't overwrite lines. Sadly, the blank line is considered a line, so there's no particularly easy way to do it with built-in functions, because there aren't any that do thisAFAIK.
So, let's look at your options:
Single-line setline()
The shortest single-line option is:
call setline(1, add(line('$') == 1 && col('$') == 1 ? [] : getline(0, '$'), 'example'))
There's a lot going on here, so let's break it down:
setline( We use setline(), because it can override existing lines.
1, Start at line 1.
add( add() appends to an array.
Then we have a condition using ternary.
line('$') == 1 If the last line is line 1...
&& col('$') == 1 and the last column is 1...
? [] then we use an empty array.
: getline(0, '$'), Otherwise, we get the contents of the buffer
'example') and add 'example' to the array
)
Note that you need to use extend() if you want to add several items, NOT insert! See :h insert() and :h extend().
Single-line append()
If you're fine with a blank line at the end of the file for empty files, you can also use append:
call append(line('$') == 1 && col('$') == 1 ? 0 : line('$'), ["example"])
Again, append() CANNOT override existing lines, so you're stuck with a blank line somewhere. Which leads us to...
If approach
This can be reduced into a single statement, which I'll include for copy-pasta purposes, but this is much better used if you're writing a script, and not just using it as a command:
if line('$') == 1 && col('$') == 1 | call setline(1, 'example') | else | call append(line('$'), "example") | endif
Or, in its expanded, script-friendly form:
if line('$') == 1 && col('$') == 1
call setline(1, 'example')
else
call append(line('$'), "example")
endif
... which is indirectly what you asked about.
In some ways, this is an easier to read variant of the first option, at least for script use. If you have a script, you can actually get away with just append.
A note on hacks
By using the append approach, you can move the condition that determines the line into a variable, and use that in combination with normal! "Gdd" (Goto last line, ddelete line) to get rid of the blank line. You can use an equivalent approach if you detect an empty file.
TL;DR:
But the problem is that I cannot determine the number properly without inventing weird tricks.
It's not particularly weird with this particular use-case, at least when you know exactly what it means. It's definitely a hack none the less, but not much to do about that in this particular case.
Should my code analyze the content of the current buffer to decide which of the functions it must choose,
in this case, line('$') == 1 && col('$') == 1 is enough. (AFAIK, we don't have a better approach, but this does avoid string operations).
considering whether the existing last line is empty because this is how the function line('$') works
line('$') is a victim here. Blank lines are still considered lines, and that holds for every other function as well. Consequently, append() doesn't want to override it. Whether the line is blank or not, line('$') == 1 if there's only one line in the buffer. Annoyingly enough, line('.') == 1, but line('^') == 0. I can't explain that one, but this happens in single-line buffers regardless of the contents of said line.
or it empty due to the desired content?
Now mildly out of context thanks to creative quoting, but the blank line that appears from a few of these combinations is an unavoidable fact based on how the functions work. You need a varying index to deal with these cases, but it really depends on what you want to do in the first place. There may be a better and/or shorter alternative, but that requires me to make assumptions about your code.
Or should I use another approach that I have missed?
There are a few alternatives in this answer already, that can be expanded if necessary. There's a lot of ways to Rome with this particular question, so there probably is. I've probably missed a few approaches as well though, for what little that's worth.
You are making things sound harder than they are.
You can check if the current buffer is empty with:
wordcount().bytes == 0
and you can forget about append() as setline() does the job perfectly as long as you give it the right line:
if wordcount().bytes == 0
" buffer is empty so our target is the last line itself
call setline('$', ['foo', 'bar'])
else
" buffer is not empty so our target is the line after the last line:
call setline(line('$') + 1, ['foo', 'bar'])
endif
From there, you can fine tune the way you handle the "last line is blank or empty" scenario by adding more checks to the second branch of the conditional…
if a given line is empty:
getline(<line spec>) =~ '^$'
if a given line is non-empty and made only of whitespace:
getline(<line spec>) !~ '^$' && getline(<line spec>) !~ '\S'
etc.
Looking for a fancy one-liner?
let foo =<< END
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
END
:call execute(["$append"] + foo)
Thanks for all, especially to Olivia and romainl!
Files may or may not have zero lines, but a Vim buffer always has at least one line, even it is empty actually. So, it is impossible to determine the line number the new content is adding after without understanding the existing content of the buffer.
We can use two main approaches:
not to analyze the content and not to worry about a redundant blank line;
to analyze the content to avoid an unwanted blank line;
In the first case, we must always use append(line('$'), ...), not as I said append(line('$')-1, ...) in my answer to Olivia. I was wrong because if the content of the buffer had an empty line at the end (for example, to separate the paragraphs), using line('$')-1 would lead to a loss of the text structure. But using append(line('$'), ... leads to the first line is always blank.
The second approach is more interesting as it implies content analysis. It is not so hard as it can be thought. romainl suggests using the wordcount() function, Olivia advises testing the line and column numbers.
I did not profile the code. But, I am sure, on huge texts, Olivia’s suggestion is much more effective because knowing the number of lines and counting the bytes in the text are jobs of significantly different complexity.
So, the solution is turning out the following. (The code has not been tested yet, this is a concept).
function Append(lines)
" Add the given lines to the end of the current buffer.
"
" Arguments:
" ‛lines’
" a list of text lines to add to the buffer.
"
let index = (line('$') == 1) && (col('$') == 1) ? 1 : line('$') + 1
call setline(index, a:lines)
endfunction
Thanks for all one more time! The question is closed.
I Googled this but was unable to find any clear explanation.
A lot of people praise Vim for sort of being its own internally consistent language but I am failing to see the consistency.
Since hjkl are left down up right:
dh is like "delete left" and it deletes the character to the left of the cursor, makes sense so far.
dl is like "delete right" but it deletes... the current character?
dj is like "delete down" and I'd assume this just deletes the line below, but it actually deletes the current line as well!
'dk' is like "delete up" and similarly I'd expect it just clears the line above, but instead it clears the current line AND the one above it.
Can anyone explain what the logic is here? I am okay with the idea of delete + left and right handling characters on the same line, and delete + up and down handling lines, but why is the behavior so inconsistent? What's the logic I am missing here?
Or is it really more of a "there is no consistent logic here really, just memorize it" thing?
This is consistent. Think about it as a textcursor, not as a block cursor so for example:
abc
^
You are here with the cursor. For the delete command it is actually like this:
a|bc
Vim always works with ranges. If you do not specify a range, only a target, the first point of the range is always the cursor position: So dh is delete from here to h, with h being the following position:
|abc
So you tell vim to delete from a|bc to |abc hence everything between the cursor positions is deleted.
dl goes to the other direction from a|bc to ab|c -> a|b|c so only the b will be deleted.
In j and k movements, it is line-based, but basically the same.
So if you press dk on the following situation:
abc
abc
^
You tell vim to delete every line until you reach the following position:
abc
^
abc
This is consistent with all other commands in vim, and is a part of what makes vim so powerful.
Think about the search / command for example:
abc
a|bc
abc
dfe
dfe
dfe
If you press d/dfe<CR> it will not just delete dfe, it will delete until dfe. So you will have following two cursor positions:
abc
a|bc
abc
|dfe
dfe
dfe
Everything in between will be deleted, you will end up with:
abc
a
dfe
dfe
dfe
This is the case for every other movement.
You shouldn't "assume" or "expect" anything. The behavior of all those commands is explained precisely in Vim's online documentation.
hjkl are motions and d is an operator that works on the text covered by a motion.
dj works on the text covered by the j motion. Since j is a linewise motion that moves the cursor to the line below, dj is "cut this line and the one below".
dk works on the text covered by the k motion. Since k is a linewise motion that moves the cursor to the line above, dk is "cut this line and the one above".
For horizontal motions, you must understand that the terminal's cursor is actually between two characters:
sample text: lorem
the "current character": lorem
^
the "cursor": lo|rem
the text covered by "h": l|orem
←
the text covered by "l": lor|em
→
dh works on the text covered by the h motion so it cuts the character before the "cursor" which happens to also be the character before the "current character".
dl works on the text covered by the l motion so it cuts the character after the "cursor" which happens to be the "current character".
I'm switching my TextEditor from Sublime Text to Vim with iTerm2 recently.
I'm looking for a plugin to paste text in Multiple Position of Multiple Line on Vim.
(Similar to the multicursor on Sublime Text which I can select cursor and use CMD+Click to select in other position then paste.)
I've seen vim-multiple-cursors plugin but it will only allow me to select the same word of next occurrence.
e.g - to place cursor at different location on different line as below.,
Line 1: Lorem ipsum dolor sit amet, [CURSOR HERE] consectetuer adipiscing elit. Aenean
Line 2: commodo ligula [CURSOR HERE] eget dolor. Aenean massa. Cum sociis natoque
Line 3: penatibus et magnis dis parturient montes,[CURSOR HERE] nascetur ridiculus mus.
Line 4: Donec [CURSOR HERE] quam felis, ultricies nec, pellentesque eu,pretium quis, sem.
Your help would be greatly appreciated and Many Thanks in advance.
VIM Version
VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Apr 21 2014 14:54:22)
MacOS X (unix) version
Warning
I recommend against trying to port your previous editing habits unchanged to Vim; after all, Vim's mode-based editing and command-based syntax are its distingishing features and make editing highly efficient.
Vim way
The "Vim way" would be more like using a regular expression to specify the places, moving to the first match, pasting, and then repeating with n and .
Emulation
You can sort-of emulate your intended workflow by manually inserting a marker char (e.g. §) at each location, and then replacing it via:
:%s/§/\=##/g
This uses :help sub-replace-expr to replace with the default register's contents.
Vim has a different buffer that stores what you have copied. To paste you just have to use "P" and the content is pasted.
Or I would recommend you to use "." [dot] - in Vim it is used for last action. So, once you have written/copied and pasted it in one line, you can just use the "dot" everywhere you want the same text to be pasted (i.e. last action to be taken).
The canonical Vim way to do that, the "dot formula" as coined by Drew Neil, is the reverse of the Sublime Text way.
In Sublime, you mark each match of a pattern, before doing the change :
mark Cmd+D
mark Cmd+D
mark Cmd+D
mark Cmd+D
change foo
In Vim, you "mark" a pattern, do the change once and repeat it on further instances of that pattern:
mark *N
change ciwfoo<Esc>
repeat n.
repeat n.
repeat n.
But, since you have 7.4, you can use the "new" gn normal mode command/motion:
nnoremap <key> *``cgn
and shorten the process above to:
change <key>foo<Esc>
repeat .
repeat .
repeat .
repeat .
Which is not that bad, I think.
In vim, I often find myself deleting (or copying) large blocks of text. One can count the lines of text and say (for example) 50dd to delete 50 lines.
But how would one delete this large block of text without having to know how many lines to delete?
Go to the starting line and type ma (mark "a"). Then go to the last line and enter d'a (delete to mark "a").
That will delete all lines from the current to the marked one (inclusive). It's also compatible with vi as well as vim, on the off chance that your environment is not blessed with the latter.
I'm no vim guru, but what I use in this circumstance is "visual mode". In command mode, type V (capital). Then move up/down to highlight the block you want deleted (all the usual movement commands work). Then remove it with x or d.
You can use the visual mode also (some commands are usable with the delete option also)
vip vap to select paragraph, v2ap to select two paragraphs
dap works, d2ap also. You can delete within blocks of [ ] like da[
For reference: the types of objects.
From vim documentation : section 4. http://vimdoc.sourceforge.net/htmldoc/visual.html
4. Operating on the Visual area *visual-operators*
...
The objects that can be used are:
aw a word (with white space)
iw inner word
aW a WORD (with white space)
iW inner WORD
as a sentence (with white space)
is inner sentence
ap a paragraph (with white space)
ip inner paragraph
ab a () block (with parenthesis)
ib inner () block
aB a {} block (with braces)
iB inner {} block
a< a <> block (with <>)
i< inner <> block
a[ a [] block (with [])
i[ inner [] block
There are many better answers here, but for completeness I will mention the method I used to use before reading some of the great answers mentioned above.
Suppose you want to delete from lines 24-39. You can use the ex command
:24,39d
You can also yank lines using
:24,39y
And find and replace just over lines 24-39 using
:24,39s/find/replace/g
It sort of depends on what that large block is. Maybe you just mean to delete a paragraph in which case a dip would do.
If you turn on line numbers via set number you can simply dNNG which will delete to line NN from the current position. So you can navigate to the start of the line you wish to delete and simply d50G assuming that is the last line you wish to delete.
There are several possibilities, what's best depends on the text you work on.
Two possibilities come to mind:
switch to visual mode (V, S-V,
...), select the text with cursor
movement and press d
delete a whole paragraph with: dap
If the entire block is visible on the screen, you can use relativenumber setting. See :help relativenumber. Available in 7.3
Counting lines is too tedious for me, but counting 'paragraphs' isn't so bad. '{' and '}' move the cursor to the first empty line before and after the cursor, respectively. Cursor moving operations can be combined with deletion, and several other answers used a similar approach (dd for a line, dG for the end of the document, etc.)
For example:
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. */
Lorem *ipsum(void) {
return dolor(sit, amet);
}
If your cursor starts above the comment block, 'd}' deletes the comment block, and 'd2}' deletes both the comment block and the code block. If your cursor starts below the code block, 'd{' deletes the code, and 'd2{' deletes both. Of course, you can skip over one block by moving the cursor first: '{d{' or '}d}'.
If you're consistent with your whitespace, or you can count the paragraphs at a glance, this should work. The Vim help file has more cursor tricks if you're interested.
You could place your cursor at the beginning or end of the block and enter visual mode (shift-v). Then simply move up or down until the desired block is highlighted. Finally, copy the text by pressing y or cut the text by pressing d.
Alongside with other motions that are already mentioned here, there is also /{pattern}<CR> motion, so if you know that you want to delete to line that contains foo, you could do dV/foo<CR>. V is here to force motion be line-wise because by default / is characterwise.
You can also enter a very large number, and then press dd if you wish to delete all the lines below the cursor.
Deleting a block of text
Assuming your cursor sits at the beginning of the block:
V/^$<CR>d (where <CR> is the enter/return key)
Explanation
Enter "linewise-visual" mode: V
Highlight until the next empty line: /^$<CR>
Delete: d
Key binding
A more robust solution:
:set nowrapscan
:nnoremap D V/^\s*$\\|\%$<CR>d
Explanation
Disable search wrap: :set nowrapscan
Remap the D key (to the following commands): :nnoremap D
Enter "linewise-visual" mode: V
Highlight until the next empty/whitespace line or EOF: /^\s*$\\|\%$<CR>
Delete: d
Lets say that i have this text:
$test = 'lorem';
$test2= 'ipsum';
and I want to copy lorem and paste into ipsum.
I tried to do yi' on lorem and then went on ipsum and did ci' but that replaced my pastebin with ipsum. and my previous copy was lost.
yi' on lorem, move to i of ipsum, vep?
Easy:
"kyi' on lorem, move to i of ipsum, ve"kp
This yanks lorem into register k, pastes it over ipsum from register k, and keeps it in register k ready to paste again anywhere else you might want to put it. ipsum still ends up in the default register, but that's no longer a problem and could be useful too.
If you've already got something in register k, you can use a different register instead (just use a different key).
I usually go to the sed command.
:%s/ipsum/lorem/g
% means do this for every line
s means sed, or search and replace
g at the end means replace every ipsum with lorem; if you omit this, it only replaces the first.
Go to the l of lorem, ye (yank to end of word).
Go to the i of ipsum, "_de (delete to end of word, putting the deleted text in the black hole register.
P (paste register before cursor).
Altogether: yej"_deP
Why don't you yank into a named buffer, using "ayi', then delete and paste with d'i"aP?
I'm not sure what do you want exactly.
You have this on two lines:
$test = 'lorem';
$test2= 'ipsum';
If you go to l and yw, it yanks lorem,
then go to i of ipsum, and cw, and p and it will replace ipsum with lorem.
You will still have both lorem and ipsum in the registers.
You can get the contents of those registers with :reg
If you want to paste something from them then "*p, or ":p, or "0p (you'll see it when you type :reg)
vi'y on lorem
vi'p on ipsum
gvy to copy back lorem to register for possible macro with vi'p
(qa - gvy - j - vi'p - q - #a - ## - ## ...)
Words here to stop Markdown treating the code that follows as plain text.
/lorem
"adw
/ipsum
"aP"bdw
``
"bp
The first text searches for 'lorem'; the next deletes the word into the buffer named 'a', leaving a pair of empty quotes behind in the text. The next search finds 'ipsum'; the "aP pulls the buffer named 'a' in before the word ipsum; the "bdw deletes the word into the buffer named 'b', leaving 'lorem' behind. The double back-tick goes back to the place the last search came from - the empty quotes; and "bp pulls the named buffer after the first quote.
You can also omit the "a and "b, but this way, the values are in the named buffers "a and "b and can be copied again, and again, and again until you don't need the values any more.
After you do ci' on impsum your lorem is in register "0. So, you can do ci'^R0 (^R means Ctrl+r) and paste your lorem in place of ipsum.
See :help quote_number for more info on numbered registers.
You want to use y to copy a selection and p to paste.
Here is a good list to keep handy.