"Snappy" Indenting in Vim? - vim

I'm just starting out in Vim. One feature I like is >> and << for indenting / unindenting a line without needing to place the cursor at the beginning of the line. I use a shiftwidth of 3, expanded to spaces.
My question is, when using >> or << is there a way to make that line "snap" to the nearest "perfect" indent? i.e. for me that would be the fourth column, seventh, tenth, and so on. This would be very handy to me at deeply nested indentation levels.
Alternatively (though slightly unsatisfying / less optimal): a way to remove all whitespace from the beginning of a line, then I could just use >> multiple times.

The shiftround option will round to the nearest shiftwidth amount of space.

Vim has automatic indentation modes. This is a somewhat big topic for one answer, but :help autoindent may be a good start.
Additionally, the == command will indent the current line according to the previous lines and the behavior of the current indent mode. (This applies the = filter to the current line.)
I usually apply == to a line first to "calibrate" it to the reference indentation, and from there fiddle with >> and <<.
Or, over multiple lines: visually select, then =.

Related

autoindent in vim for selected text

1) Is there anyway to autoindent/retab on a set of lines, rather than the entire file?
set shiftwidth=4
set expandtab
set tabstop=4
has been defined in my .vimrc.
What I want is to select a bunch of lines and apply indentation only on them. This is because the file is large and I just want to clean my line of codes. By doing :retab, I would have to force the whole file to be reindented.
For example select the following code and reindent automatically
def hello(self):
line1
line2
to
def hello(self)
line1
line2
2) Is there any way that I can reverse tab? Something like Shift-Tab in Eclipse. It goes back 4 spaces.
You can try this one...
Shift + V to enter in Visual mode
Then press j until you reach the text you want to be ident.
Finally press
=, it will ident automatically your selected code.
If you want to ident the whole code just type gg=G.
What I want is to select a bunch of lines and apply indentation only on them
You can apply indentation to certain lines in Visual mode. To do this follow these steps:
Position your cursor in the first line to be indented
Enter visual mode by typing shift + v
Move down, typing j until you reach the last line to be indented (alternately you can type line # + G if you know the line number of the last line to be indented).
Type >
Another solution is to do this with a regex in command-line mode:
2,5s/\v(.*)/\t\1/g
Here the line range is specified in the first two numbers of the regex (in this case from line 2 to 5).
To answer both of your questions at the same time, you can use the > and < operators. Since they are operators, they behave just like y, d, c, and all the other operators do, so you must supply them with a motion. For example, >> will indent the current line (with 4 spaces), and >j will indent the current line and the line below. >G will indent everything to the end of the buffer, etc.
Similarly, < will unindent whatever you specify.
In your specific example, there are two different approaches I would recommend.
Use normal mode. This one kinda depends on how large your function is. If it's just the two lines, you could put your cursor on line1, and type <j>,. (Of course, if your function has more than 2 lines, you'll need to adjust). Unfortunately you need to unindent before reindenting because otherwise you'll end up with 5 spaces, which I assume you don't want.
Use visual mode. This one is slightly less convenient unless you modify your .vimrc because calling > or < in visual mode will drop your visual selection. That's why I have the following in my .vimrc:
"Make it easier to indent a visual selection several times.
xnoremap > >gv
xnoremap < <gv
With this setup, you can visually select the lines you would like to reindent, and then do <>. IMO, this is the best solution, and I frequently use this kind of workflow.
Most (if not all) Ex commands take a range so you could just visually select the lines and do :'<,'>retab.
Or :12,16retab.
Or :.,+9retab.
And so on.
See :help :retab and :help :range.

In vim, how can I modify all existing indents to be 2?

In vim, I set shiftwidth=2, but all my previous indents are still at the default 8. How can I change the previous indents from 8 to 2?
You can reindent the whole file with gg=G. gg goes to the first line, = indents (taking a movement), G goes to the last line.
If you're using set expandtab (like you should), you can modify the indentation in a file with
:%s/^ */ /
The settings affect how changes are made, but do not themselves make changes to the file.
If your original indents were achieved using hard tabs stops, then one trick you can do is this. Set the hard tab stop to 2:
:set ts=2
Now you have the two-space indentation (but achieved with hard tabs).
Now, do
:retab 8
This means, roughly, change the hard tab size to 8 (as if by :set ts=8) but at the same time edit all the tabbing in the buffer so that the indentation's appearance does not change.
So now the buffer is still indented to two spaces, but now :ts is back to 8.
If you have :expandtab set, then the indentation is now all spaces, otherwise it is a combination of 8-space tabs and spaces.
Even if this doesn't apply to your situation, retab is good to know because it's handy for dealing with sources that use hard tabs and that you'd like to convert to use spaces and a different indentation level at the same time.

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

Display some special character as a linebreak in Vim

I would like to display some (arbitrary) special character as linebreak <CR> in vim.
So far I tried misusing (certainly extreme misuse:) the non-breakable space typing
:set list listchars=nbsp:<CR>
which does not work, seemingly because the command does not accept the <CR>.
Is there anything which I can use here for <CR>? \r didn't work either.
Note that I don't want to edit the text file. The goal is to have blocks of lines (some related code) treated as a single line with respect to vim actions but displayed as multiple lines. The special character (to be defined) would be used only to save this block structure in the file replacing the linebreak \r in these cases.
Given the wider context of the problem that you have provided in a
later comment, I would suggest the following solution. Group dependent
lines of code in folds by indentation, language’s syntax, or markers.
All of these three methods are automatic and do not require manual
creation of folds. Which one to choose depends on the language you
use. See :help foldmethod, and feel free to comment this answer if
you need any help with folding.
Unless the syntax of the language you use has extensive support in
Vim, the most convenient methods would be using fold markers or
defining a custom expression to calculate fold level of each line.
The former method implies surrounding every group of lines to fold
with special text markers (which could be enclosed in a comment not
to break the syntax rules of the language). By default, those markers
are {{{ and }}}; see :help fold-marker and :help foldmarker
to find out how to change them. Use
:set foldmethod=marker
to enable this mode of folding.
Defining an expression to calculate fold level for every line is an
even more flexible method. It allows to use any logic (that can be
expressed in Vimscript) to determine the fold level. For example, to
fold groups of lines that start with a single space use the following
settings:
:set foldmethod=expr
:set foldexpr=getline(v:lnum)[0]=='\ '
See :help fold-expr for further details on customizing the fold
expression.
When the lines that depend on each other are grouped into folds, you
can easily pass the contents of any particular fold to a filter
program. Move the cursor to a line inside a target fold, then type
[zV]z to select the entire fold, followed by !, and enter the
command to run. To save typing, you can define the mapping
:nnoremap <leader>z [zV]z!
If the command is always the same, you can include it in the mapping:
:nnoremap <leader>z [zV]z!cat -n<cr>
Substitute the cat -n portion above—my example command—with the
appropriate command in your case.
I think you might want check out this vimcasts episode. May not be exactly what you want, but could get you there. Good luck!
My solution, in the end, was to insert non-breaking spaces and linebreaks in the respective places in the file. :set list listchars=nbsp:$ can then be used to display these 'special linebreaks'. Their presence allows interpreting code to identify the blocks of lines separated by this combination as related lines.
Of course this doesn't answer the question. The answer, according to my best knowledge now, is that neither :set list nor :wrap can be used to achieve this.

Smart Wrap in Vim

I have been wondering if Vim has the capability to smart wrap lines of code, so that it keeps the same indentation as the line that it is indenting. I have noticed it on some other text editor, such as e-text editor, and found that it helped me to comprehend what I'm looking at easier.
For example rather than
<p>
<a href="http://www.example.com">
This is a bogus link, used to demonstrate
an example
</a>
</p>
it would appear as
<p>
<a href="somelink">
This is a bogus link, used to demonstrate
an example
</a>
</p>
This feature has been implemented on June 25, 2014 as patch 7.4.338. There followed a few patches refining the feature, last one being 7.4.354, so that's the version you'll want.
:help breakindent
:help breakindentopt
Excerpts from vim help below:
'breakindent' 'bri' boolean (default off)
local to window
{not in Vi}
{not available when compiled without the |+linebreak|
feature}
Every wrapped line will continue visually indented (same amount of
space as the beginning of that line), thus preserving horizontal blocks
of text.
'breakindentopt' 'briopt' string (default empty)
local to window
{not in Vi}
{not available when compiled without the |+linebreak|
feature}
Settings for 'breakindent'. It can consist of the following optional
items and must be seperated by a comma:
min:{n} Minimum text width that will be kept after
applying 'breakindent', even if the resulting
text should normally be narrower. This prevents
text indented almost to the right window border
occupying lot of vertical space when broken.
shift:{n} After applying 'breakindent', wrapped line
beginning will be shift by given number of
characters. It permits dynamic French paragraph
indentation (negative) or emphasizing the line
continuation (positive).
sbr Display the 'showbreak' value before applying the
additional indent.
The default value for min is 20 and shift is 0.
Also relevant to this is the showbreak setting, this will suffix your shift amount with character(s) you specify.
Example configuration
" enable indentation
set breakindent
" ident by an additional 2 characters on wrapped lines, when line >= 40 characters, put 'showbreak' at start of line
set breakindentopt=shift:2,min:40,sbr
" append '>>' to indent
set showbreak=>>
Note on behaviour
If you don't specify the sbr option, any showbreak any characters put appended to the indentation. Removing sbr from the above example causes an effective indent of 4 characters; with that setting, if you just want to use showbreak without additional indentation, specify shift:0.
You can also give a negative shift, which would have the effect of dragging showbreak characters, and wrapped text, back into any available indent space.
When specifying a min value, the shifted amount will be squashed if you terminal width is narrower, but showbreak characters are always preserved.
There is a patch for this, but it's been lingering for years and last time I checked did not apply cleanly. See the "Correctly indent wrapped lines" entry in http://groups.google.com/group/vim_dev/web/vim-patches -- I really wish this would get in the mainline.
Update: that link seems to have bitrotted. Here is a more up to date version of the patch.
Update 2: it has been merged upstream (as of 7.4.345), so now you only have to :set breakindent.
I don't think it's possible to have exactly the same indentation, but you can still get a better view by setting the 'showbreak' option.
:set showbreak=>>>
Example:
<p>
<a href="http://www.example.com">
This is a bogus link, used to demonstrate
>>>an example
</a>
</p>
The real thing looks better than the example code above, because Vim uses a different colour for '>>>'.
UPDATE: In June 2014, a patch to support a breakindent option was merged into Vim (version 7.4.346 or later for best support).
You might also try :set nowrap which will allow vim to display long lines by scrolling to the right. This may be useful for examining the overall structure of a document, but can be less convenient for actually editing.
Other options close to what you're looking for are linebreak and showbreak. With showbreak, you can modify what is displayed at the left margin of lines that are wrapped, but unfortunately it doesn't allow a variable indent depending on the current context.
The only way I know of that you could do this would be to use a return character (as mentioned by Cfreak) and combine the textwidth option with the various indentation options. If your indent is configured correctly (as it is by default with the html syntax I believe, but otherwise see the autoindent and smartindent options), you can:
:set formatoptions = tcqw
:set textwidth = 50
gggqG
If you have any customisation of the formatoptions setting, it may be better to simply do:
:set fo += w
:set tw = 50
gggqG
What this does:
:set fo+=w " Add the 'w' flag to the formatoptions so
" that reformatting is only done when lines
" end in spaces or are too long (so your <p>
" isn't moved onto the same line as your <a...).
:set tw=50 " Set the textwidth up to wrap at column 50
gg " Go to the start of the file
gq{motion} " Reformat the lines that {motion} moves over.
G " Motion that goes to the end of the file.
Note that this is not the same as a soft wrap: it will wrap the lines in the source file as well as on the screen (unless you don't save it of course!). There are other settings that can be added to formatoptions that will auto-format as you type: details in :help fo-table.
For more information, see:
:help 'formatoptions'
:help fo-table
:help 'textwidth'
:help gq
:help gg
:help G
:help 'autoindent'
:help 'smartindent'
:set smartindent
:set autoindent
I think you still have to use a return though
If your HTML is sufficiently well formed, running it through xmllint might help:
:%!xmllint --html --format
A Macro Solution:
Edit:
The operate gq{motion} auto-formats to whatever the variable "textwidth" is set to. This is easier/better than using the 80lBi^M I have for my macro.
If you have autoindent enabled
:set autoindent
Then entering a return at the end of a line will indent the next line the same amount. You can use this to hard enter in linewraps if you'd like. The following macro takes advantage of this to automatically indent your text:
set register z to:
gg/\v^.{80,}$^M#x (change 80 to whatever length you want your text to be)
and set register x to:
80lBi^M^[n#x (change 80 to whatever length you want your text to be)
Then do
#x
to activate the macros. After a few seconds you're text will all be in properly indented lines of 80 characters or less.
Explanation:
Here's a dissection of the macros:
Part 1 (macro z):
gg/\v^.{80,}$^M#x
gg - start at the top of the file (this avoids some formatting issues)
/ - begin search
\v - switch search mode to use a more generic regex input style - no weird vim 'magic'
^.{80,}$ - regex for lines that contain 80 or more characters
^M - enter - do the search (don't type this, you can enter it with ctrl+v then enter)
#x - do macro x
Part 2 (macro x):
80lBi^M^[n#x
80l - move right 80 characters
B - move back one WORD (WORDS include characters like "[];:" etc.)
i^M - enter insert mode and then add a return (again don't type this, use ctrl+v)
^[ - escape out of insert mode (enter this with ctrl+v then escape)
#x - repeat the macro (macro will run until there are no more lines of 80 characters or more)
Caveats:
This macro will break if the there's a WORD that is 80 characters or longer.
This macro will not do smart things like indent lines past tags.
Use the lazyredraw setting (:set lazyredraw) to speed this up

Resources