How to select a rectangular area in visual block mode (Ctrl+v) in empty file (vim)? - vim

I can easily select a rectangular area in a file provided this area contains text or spaces (or anything). I do it with visual block mode and motion keys.
But when I try to create such area in a new file (or any file without text) I can't.
Is there a way to 'expand' this area by automatically filling it with spaces for example? Or am I going in wrong direction?
The reason I want this:
I create a new file with vim and then would like to create a comment block similar to this:
##############################################
# #
# My comment goes here #
# #
##############################################
I can do it over existing text using Ctrl+v+motion keys, then r# to create an area filled with pounds. Then similarly to cut out inner rectangle with spaces.
How do I use this technique on a new file?
Thanks.

Apart from the (very good) answer from Brian Rasmussen, the only way I know of to do almost exactly what you're asking is to use virtualedit mode. This won't let you edit on non-existent lines, but it will let you edit beyond the end of existing lines. Therefore, to turn the current line into a load of # symbols, you could do this:
:set virtualedit=all
v50lr#
To make a 50x5 block, you could create 4 new blank lines and then do the same:
:set virtualedit=all
4o<ESC>
<C-V>4k50lr#
(where <C-V> means press Ctrl+V and <ESC> means press Esc).
I believe there are some plugins for various file types that make it much easier to create comment blocks like this, but I'm not sure which is best.
You could just do something like:
50i#<ESC>yyo#<ESC>48a<SPACE><ESC>a#<ENTER>#<SPACE><SPACE>My comment goes here<ESC>:exe<SPACE>'normal'<SPACE>(49-getpos('.')[2]).'a<SPACE>'<ENTER>a#<ENTER>#<ESC>48a<SPACE><ESC>a#<ESC>p
But maybe that's just me being silly! I'll leave it as an exercise for the reader to figure out what's going on there if you're interested (:help is your friend).
How about this as a slightly more serious alternative: bung the following in your vimrc or in a file in the plugins directory of the vim runtime folder (e.g. ~/.vim/plugins on Unix)
nmap <F4> :InsertCommentBlock<CR>
command! InsertCommentBlock call InsertCommentBlock()
function! InsertCommentBlock()
let linelength = 50
let linelist = []
call add(linelist, repeat('#', linelength))
call add(linelist, '#' . repeat(' ', linelength-2) . '#')
let comment = input('Please enter a comment: ')
call add(linelist, '# ' . comment . repeat(' ', linelength - (4+len(comment))) . '#')
call add(linelist, '#' . repeat(' ', linelength-2) . '#')
call add(linelist, repeat('#', linelength))
call append(line('.'), linelist)
endfunction
See:
:help function
:help 'virtualedit'
:help command
:help nmap
:help repeat()
:help append()
:help add()
:help getpos()
:help :exe
etc...

If you want to create a block like that in a new file, you could do something like 50i#EscY5P
So it's 50 times insert # followed by yank current line and put it 5 times. This will give you a 50x5 block of #.
Of course you still have to do, whatever you do to get from the block of #s to the comment in your post.

The accepted answer mentions using
:set virtualedit=all
However, then you enable virtualedit in all modes, which might not what you want. There is actually also an option to enable it in just block editing mode:
:set virtualedit=block
This is what I put in my vimrc

I don't think there's any to select text that isn't there, but the easy solution would be to "seed" it with something like 76a yy4p ... so draw 76 spaces, then copy the line 4 extra times.

Related

Is it possible to have Ctrl-K cut/uncut functionality in Vim like there is in Nano and Pico?

In case you're not familiar with how it works, in Pico and Nano you can hit ctrl-k multiple times and it will add each line to the clipboard. Then you can ctrl-u to "uncut" this. It's a very useful command. Vim does something similar with the dd command, but it only works one line at a time. Thus, you have to use visual mode to properly accomplish the above.
I couldn't find a good answer online so I rolled my own solution. You can add this to your vimrc file:
imap <C-k> <Esc>:execute #a ? 'normal! "Bdd' : 'normal! "bdd'<cr>:let #a=1<cr>:echo ''<cr>i
imap <C-u> <Esc>"bPi
autocmd CursorMovedI * execute(':let #a=0')
The register #a is used to track whether or not the cut line should be appended. The register #b is used as the clipboard register. Whenever the cursor position changes, you stop being in "append" mode. Thus, you can hit ctrl-k over and over to keep appending lines, but as soon as you move the cursor you go back to normal. I'm pretty sure this is how Nano and Pico implement it under the hood.
Is anyone aware of a cleaner solution?
Intro to Registers
The Vim commands you are looking for are delete/cut, dd and put/paste, p. Each of these commands by default use the unnamed register, "". So dd will delete a line and put the freshly deleted line into the unnamed register. p will put the contents from the unnamed register into your current buffer.
Vim has more than just the unnamed register, it also has named registers. These registers are a-z.
To see what your registers are set to you can execute :registers.
To use a named register prefix your command with quote and a lowercase letter, e.g. "add
Upper case letters will append instead of replace the contents of a register, e.g "Add"
The Vim Way
"add the first line, then append the next line via "Add. For repeated deletions use .
Use a text object. e.g. dap for delete a paragraph. See :h text-objects
Delete text via some motion. e.g. d} is delete till end of paragraph
Delete to a regex patter. e.g. d/foo<cr>
Lastly would be using visual mode, V6jd. Nothing wrong with using visual mode
Additionally you want to stay out of insert mode unless you are inserting text. You only want to be in insert move for short burst at a time. Most of the time you should be in Normal mode, hence the name.
For a nice post on the Vi/Vim way see this StackOverflow post: Your problem with Vim is that you don't grok vi.
Alternatives
If none of the standard Vim tricks satisfy your needs you may want to look into plugins that support line exchanging like Unimpaired or LineJuggler.
However if you really do want something similar this nano/pico features you can use the following by putting it in your ~/.vimrc file:
nnoremap Q :<c-u>call <SID>DeleteAppend()<cr>
function! s:DeleteAppend()
let save = #a
let #a = ##
let reg = get(g:, 'append_tick', -1) == b:changedtick ? 'A' : 'a'
execute 'normal! "' . reg . 'dd'
let g:append_tick = b:changedtick
let ## = #a
let #a = save
endfunction
The Q normal command will now delete a line and append additional deleted lines until a another command is executed. Example usage: QQjjQQQp
For more help please see
:h d
:h p
:h "
:h registers
:h :reg
:h motion
:h text-objects
:h .
:h visual-mode
Yes, there are many cleaner solutions:
12dd{motion}p " cut 12 lines, move elsewhere, paste
d5j{motion}p " cut from here to 5 lines down, move elsewhere, paste
d/foo<CR>{motion}p " cut from here to next 'foo', move elsewhere, paste
:.,23d<CR>:12put<CR> " cut from here to line 23, paste after line 12
:.,+5d<CR>:0put<CR> " cut from here to fifth line below, paste at top of buffer
or the truly amazing:
:.,11m35<CR> " move the lines between this one and 11 to after line 35
You can even do:
Vjjjjjjjjjjd{motion}p
One of the big advantages of Vim over other editors is its expressive language: don't think in "nano", think in "Vim".

Special comment snippet for VIM

I want to add comments dependant on the column i'm writing for example if i'm adding a comment over a code which is indented 4 spaces the comment should look like
/* Comment -------------*/
as many dashes as needed to fill up the line till column 100. It should recognize where the position is and how long the comment is.
I can't figure it out with vimscript myself.
You can solve this with an expression mapping; see :help :map-expr:
:inoreabbrev <expr> comsep '/* Comment ' . repeat('-', 17 - indent('.')) . '*/'
This determines the width by subtracting the current indent (via indent()) from a constant. You could use &textwidth here, too.
Whenever you type comsep, it'll be expanded. Alternatively, you could also use an :inoremap <expr> <C-g> ... instead.
To insert the comment text, you could either use input(), or first just insert dashes and re-position the cursor by appending a number of "\<Left> keycodes.
If you use a snippet plugin like snipMate or Ultisnips, those may have functionality to dynamically modify the snippet, but the built-in methods should suffice.

Use vim code folding markers to generate index (contents)

In my .vimrc I have the lines
:set foldmethod=marker
:set foldmarker=SECTION:,ENDSECTION:
for custom code folding. In my files the comment character in the respecting language preceeds the code folding marker and they are followed by the title of the corresponding section. E.g.
# SECTION: First Section
some code
# SECTIION: Subsection
some more code
# ENDSECTION:
# ENDSECTION:
# SECTION: Second Section
some other code
# ENDSECTION:
This structure has all whats needed to generate a content for the file like
First Section
Subsection
Second Section
(ideally this index has markers similar to the vim help system so I can jump to the corresponding SECTION easily; I have no idea how to achieve this).
I can think of an easy perl script which generates this text, but I would prefere a solution based on a vim script which shows the index in a new window. Maybe there exists a solution already which does this?
Put this in your vimrc and run :MkIdx or <leader>z. You can also pass a range to the command, but the default is the whole buffer.
function! MkIdx() range
let items = filter(getline(a:firstline, a:lastline), 'v:val =~ ''\C^\s*#\s\+SECTION''')
new
call setline(1, map(items, 'substitute(v:val, ''^\(\s*\)[^:]\+:\(.\+\)'', ''\1\2'', '''')'))
" Mapping to jump to section:
nnore <silent><buffer><leader>x :<C-U>call Go2Section()<CR>
endfunction
function! Go2Section()
let section = matchstr(getline('.'), '^\s*\zs.*')
quit
call search('\C# SECTION:\s\+'.section, 'cw')
endfunction
command! -bar -range=% MkIdx <line1>,<line2>call MkIdx()
" Mapping to build the index:
nnore <silent><leader>z :<C-U>MkIdx<CR>
Edit: Put index on a new buffer.
Edit 2: Don't leave an empty line.
edit 3: Allow jumping back to sections with <leader>x.

Vim: Indent with one space (not shiftwidth spaces)

The default VIM indentation commands indent by shiftwidth spaces
e.g.
>> Indent line by shiftwidth spaces
<< De-indent line by shiftwidth spaces
Is there any way to indent with one or n (where n != shiftwidth) space(s)?
One way to do that is to vertically select a column in the block with Ctrl+V then, I to insert vertically and then type a space and <Esc>. But is there a better way?
I'm not sure that there is a better way. But, there are a few ways that you could do it (that I can think of anyway)...
Your Visual Block Solution
Like you said: press Ctl-V select the lines you want, press I to insert, and enter the number of spaces.
Search
Similar to the above but a little more flexible - you can use with with the 'select paragraph' vip command, or any range really: press v or vip or what have you to select the range, and the type :s/^/{n spaces} where {n spaces} is the number of spaces you want to insert.
Its a little more verbose, but works pretty well for pretty much any range. Heck, if you wanted to do the whole file you could do Ctl-A (OS dependent) and indent the whole file (or just skip the whole visual mode thing and just do it command mode...as in :1,$s/^/{n spaces}
Note that you don't have to include the third slash in s/// since you aren't putting any switches at the end of the search.
Global
Maybe you want to only indent lines that match some pattern. Say...all lines that contain foo. No problem: type :g/foo/s/^/{n spaces}
Global is especially handy if its multi-line sections with a similar pattern. You can just escape into normal mode land and select the lines you want and indent accordingly: :g/foo/norm Vjj:s/^/{n spaces}Ctl-V{Enter}. Little more complicated with that extra Ctl-V{Enter} at the end but useful under certain circumstances.
Use tabstop and shiftwidth
Yes, if your doing it a lot - I'd do :set ts=2 and :set et and :set sw=2 and use >> and << every which way...
Make a Function
Okay, so still not brief enough and for whatever reason you need to do this a lot and you can't abide messing with sw, et and ts settings. No problem, just write up a quick function and give it a localleader mapping:
function! AddSpace(num) range
let s:counter = 0
let s:spaces = ''
while s:counter < a:num
let s:spaces .= ' '
let s:counter = s:counter + 1
endwhile
execute a:firstline .','. a:lastline .'s/^/'. s:spaces
endfunction
:map <LocalLeader>i :call AddSpace(3)Ctl-V{enter}
Maybe just knowing more than one way to do this is better than only knowing one? After all, sometimes the best solution depends on the problem :)
Indent a block of code in vi by three spaces with Visual Block mode:
Select the block of code you want to indent. Do this using Ctrl+V in normal mode and arrowing down to select text. While it is selected, enter ":" to give a command to the block of selected text.
The following will appear in the command line: :'<,'>
To set indent to 3 spaces, type le 3 and press enter. This is what appears: :'<,'>le 3
The selected text is immediately indented to 3 spaces.
Indent a block of code in vi by three spaces with Visual Line mode:
Open your file in VI.
Put your cursor over some code
Be in normal mode press the following keys:
Vjjjj:le 3
Interpretation of what you did:
V means start selecting text.
jjjj arrows down 4 lines, highlighting 4 lines.
: tells vi you will enter an instruction for the highlighted text.
le 3 means indent highlighted text 3 lines.
To change the number of space characters inserted for indentation, use the shiftwidth option:
:set shiftwidth = <number>
Have a look here for more details.
You can also add that to your .vimrc file.
If I'm understanding correctly, you could use:
ctrl+V, jj then ':le n', where n is the number of spaces to indent.
http://vim.wikia.com/wiki/Shifting_blocks_visually
Place marks ('a' and 'b') between the code you want to indent:
<position cursor at top of block>
m a
<position cursor at bottom of block>
m b
Do a replace command such that each newline character between your marks is replaced with the number of spaces desired (in this example, 2 spaces):
:'a,'bs/^/ /g
If white space indentation already exists and you want to increase it further by one or more columns, then select a block of one or more white space columns using Ctrl-V, yank and paste it in the same place.
I had to dedent by a given number of spaces, amount, inside a vim script. This worked:
let sw_setting = &shiftwidth
set shiftwidth=1
exe "normal v" . amount . "<"
let &shiftwidth = sw_setting
A side-effect is that it resets the last visual mode selection. Instead, you may wish to edit the exe... line such that it executes "<<" repeated amount times in normal mode. That is: instead of normal v3<, make it normal <<<<<<, if amount is 3.
I like to use Space to indent a visual selection with a single space:
vnoremap <silent> <space> :s/^/ /<CR>:noh<CR>gv
And I couldn’t get Shift+Space to dedent, so I use z:
vnoremap <silent> z :s/^\s\=//<CR>:noh<CR>gv

How to emulate Emacs’ transpose-words in Vim?

Emacs has a useful transpose-words command which lets one exchange the word before the cursor with the word after the cursor, preserving punctuation.
For example, ‘stack |overflow’ + M-t = ‘overflow stack|’ (‘|’ is the cursor position).
<a>|<p> becomes <p><a|>.
Is it possible to emulate it in Vim? I know I can use dwwP, but it doesn’t work well with punctuation.
Update: No, dwwP is really not a solution. Imagine:
SOME_BOOST_PP_BLACK_MAGIC( (a)(b)(c) )
// with cursor here ^
Emacs’ M-t would have exchanged b and c, resulting in (a)(c)(b).
What works is /\w
yiwNviwpnviwgp. But it spoils "" and "/. Is there a cleaner solution?
Update²:
Solved
:nmap gn :s,\v(\w+)(\W*%#\W*)(\w+),\3\2\1\r,<CR>kgJ:nohl<CR>
Imperfect, but works.
Thanks Camflan for bringing the %# item to my attention. Of course, it’s all on the wiki, but I didn’t realize it could solve the problem of exact (Emacs got it completely right) duplication of the transpose-words feature.
These are from my .vimrc and work well for me.
" swap two words
:vnoremap <C-X> <Esc>`.``gvP``P
" Swap word with next word
nmap <silent> gw "_yiw:s/\(\%#\w\+\)\(\_W\+\)\(\w\+\)/\3\2\1/<cr><c-o><c-l> *N*
Depending on the situation, you can use the W or B commands, as in dWwP. The "capital" versions skip to the next/previous space, including punctuation. The f and t commands can help, as well, for specifying the end of the deleted range.
There's also a discussion on the Vim Tips Wiki about various swapping techniques.
In the middle of a line, go to the first letter of the first word, then do
dw wP
At the end of a line (ie the last two words of the line), go to the space between the words and do
2dw bhP
From the handy Equivalence of VIM & Emacs commands
You could add shortcut keys for those by adding something like the following to your vimrc file:
map L dwwP
map M 2dwbhP
In that case, SHIFT-L (in command-mode) would switch words in the middle of the line and SHIFT-M would do it at the end.
NB: This works best with space-separated words and doesn't handle the OP's specific case very well.
There's a tip on http://vim.wikia.com/wiki/VimTip10. But I choose to roll my own.
My snippet has two obvious advantages over the method mentioned in the tip: 1) it works when the cursor isn't in a word. 2) it won't high-light the entire screen.
It works almost like emacs 'transpose-words', except that when transposition is impossible, it does nothing. (emacs 'transpose-words' would blink and change cursor position to the beginning of current word.)
"transpose words (like emacs `transpose-words')
function! TransposeWords()
if search('\w\+\%#\w*\W\+\w\+')
elseif search('\w\+\W\+\%#\W*\w\+')
endif
let l:pos = getpos('.')
exec 'silent! :s/\(\%#\w\+\)\(\W\+\)\(\w\+\)/\3\2\1/'
call setpos('.', l:pos)
let l:_ = search('\(\%#\w\+\W\+\)\#<=\w\+')
normal el
endfunction
nmap <silent> <M-right> :call TransposeWords()<CR>
imap <silent> <M-right> <C-O>:call TransposeWords()<CR>
You can use dwwP or dWwP as Mark and CapnNefarious have said, but I have a few notes of my own:
If the cursor is on the first letter of the second word, as in the example you gave, you can use dwbP (or dWbP to handle punctuation);
If the cursor is in the middle of the word, you can use dawbP/daWbP.
There's a transpose-words script on vim.org that works beautifully.

Resources