Vim 中的智能包装

我一直想知道 Vim 是否能够智能地包装代码行,这样它就可以保持与正在缩进的行相同的缩进。我在其他一些文本编辑器上注意到了它,比如电子文本编辑器,并发现它帮助我更容易地理解我所看到的内容。

比如说

<p>
<a href="http://www.example.com">
This is a bogus link, used to demonstrate
an example
</a>
</p>

看起来就像

<p>
<a href="somelink">
This is a bogus link, used to demonstrate
an example
</a>
</p>
29335 次浏览

If your HTML is sufficiently well formed, running it through xmllint might help:

:%!xmllint --html --format
:set smartindent
:set autoindent

I think you still have to use a return though

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.

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 '>>>'.

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'

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.

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

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.