3

I'm currently using

imap <silent> <C-c> </<C-X><C-O><C-X><Esc>F<i

as per this post and this page to close <></> tags of any kind with <C-c>, which works a treat.

However, I am stuck coming up with a way to enter a new line indented and have the closing tag on the line below automatically placed.

As follows (| being the cursor):

<body>|</body>

... hit <CR>...

<body>
    |
</body>

I notice this happens with just <body><CR> (no closing tag) but I cannot get it to behave as required.

Any assistance would be appreciated.

2

6 Answers 6

2

Plugin xml.vim by Rene de Zwart (github, #1397)

This plugin defines two relevant insert mode mappings > and ;;. They work as following:

  1. When you have already <body>|</body>, type >.

  2. When you have body, type ;;.

For both you should end up with:

<body>
    |
</body>

Similar insert mode behavior are provided by the plugins

  1. xmledit by Devin Weaver (github, #301):

    When in insert mode and you finish a tag (pressing >) the tag will be completed. If you press > twice, it will complete the tag and place the cursor in the middle of the tags on it's own line (helps with nested tags).

  2. Closetag by alvan (github) (Based on xml.vim)

    <table|
    

    Now you press >, the content will be:

    <table>|</table>
    

    And now if you press > again, the content will be:

    <table>
        |
    </table>
    

    The following tags will not be closed:

    <area>, <base>, <br>, <col>, <command>, <embed>, <hr>, <img>, 
    <input>, <keygen>, <link>, <meta>, <param>, <source>, <track>, <wbr>
    
  3. HTML AutoCloseTag by Michael Sanders (#2591) with remapping of <CR>:

    ino <buffer> <silent> < <><left>
    ino <buffer> <silent> > <c-r>=<SID>CloseTag()<cr>
    ino <buffer> <expr> <cr> <SID>Return()
    

    Return() remaps <cr> to <cr><cr>\<up> which does no autoindentation. Following patch changes this:

    --- orig_html_autoclosetag.vim  2018-12-03 10:57:54.000000000 +0100
    +++ html_autoclosetag.vim   2018-12-03 10:57:10.000000000 +0100
    @@ -24,7 +24,7 @@
     fun s:Return()
        let tag = s:GetCurrentTag()
        return tag != '' && match(getline('.'), '</'.tag.'>') > -1 ?
    -               \ "\<cr>\<cr>\<up>" : "\<cr>"
    +               \ "\<cr>\<esc>O" : "\<cr>"
     endf
    
     fun s:InComment()
    

    Now you would enter <body><cr> to get the desired result.

1
  • How did I miss that one, works a treat, thanks
    – BodneyC
    Dec 1, 2018 at 13:04
2

Plugin splitjoin.vim by Andrew Radev (github, #3613) for Normal Mode

If you want to change the style afterwards in normal mode, the plugin splitjoin.vim makes this possible with the mapping gS:

The idea of this plugin is to introduce a single key binding (default: gS) for transforming a line like this:

<div id="foo">bar</div>

Into this:

<div id="foo">
  bar
</div>

The other direction is also possible with gJ when the cursor is on the opening or closing tag.

2
  • This looks like a great plugin in general, nice!
    – ZeroKnight
    Dec 1, 2018 at 13:22
  • Yes, I agree this plugin is quite useful in many circumstances.
    – Hotschke
    Dec 1, 2018 at 13:26
2

Plugin vim-ragtag by Tim Pope (github, #1896)

This plugin defines following insert mode mappings which are useful after you have only typed body:

<C-X><Space>  <foo>^</foo>                              *ragtag-CTRL-X_<Space>*
<C-X><CR>     <foo>\n^\n</foo>                          *ragtag-CTRL-X_<CR>*
<C-X>/        Last HTML tag closed                      *ragtag-CTRL-X_/*

However, it does not provide a mapping to change <body>|</body> to <body>\n|\n</body>. On the other side, the mappings can be considered less intrusive.

1

Plugin UltiSnips by Holger Rapp (github, #2715) and Honza's Snippet Repository

You find in honza's snippet repository most html tags as a trigger word. This means following works for those:

body<tab>

expands to a predefined style (inline or multi-line):

<body>
    |
</body>

If you do not agree, you can easily customize the default style. If you need both styles you need two different trigger words.

Furthermore, two general purpose trigger words are defined t and ti:

  1. Block style:

    t<tab>body<tab> 
    
  2. Inline style:

    ti<tab>body<tab>
    
1

Plugin Lexima Rules for HTML & Templates

The plugin lexima.vim provides smart behaviour when entering an opening expression, pressing <backspace> or <CR>.

By default rules e.g. for (,[,{ exist. However, someone else has written a few rules for html/xml and template languages such as jinja and has published them in a separate github repository under the title lexima-template-rules:

https://github.com/deathlyfrantic/lexima-template-rules

The relevant rules are described as following:

Before                      Input                   After
----------------------------------------------------------------------------
|                           <                       <|>
----------------------------------------------------------------------------
<|> or <|/>                 <Backspace>             |
----------------------------------------------------------------------------
<tag>| or <tag/>|           <Backspace>             <tag|> or <tag|/>
----------------------------------------------------------------------------
<tag foo="bar"|>            /                       <tag foo="bar"/>|
----------------------------------------------------------------------------
<tag|/>                     /                       <tag/>|
----------------------------------------------------------------------------
<tag>|</tag>                <Enter>                 <tag>
                                                        |
                                                    </tag>
----------------------------------------------------------------------------

Together with the mapping already used by OP using inoremap

❯ cat ~/.vim/vimrc
source $VIMRUNTIME/defaults.vim
inoremap <silent> <C-c> </<C-X><C-O><C-X><Esc>F<i

You would type <body><C-c><CR>.

1

Plugin emmet-vim by Yasuhiro Matsumoto (github, #2981)

This plugin is quite powerful. Unfortunately, there is only a global switch to insert newlines for all childless tags:

let g:user_emmet_settings = {
\ 'html' : {
\     'block_all_childless' : 1,
\   }
\ }

Then you can type

body<C-y>,

to get the desired result.

Related issues on github are #378 and #270.

You can merge multi-line tags into a single line with

<C-y>m

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.