Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vim cursor position after expanding html tag

I most IDEs and modern text editors (Sublime Text 3) the cursor is correctly indented after inserting a newline in between an html tag (aka 'expanding" the tag):

Before:

<div>|</div>

After pressing CR:

<div>
    |
</div>

But in Vim, this is what I get:

<div>
|</div>

How can I get the same behaviour in Vim like in most other editors (see above)?

like image 297
Daniel Lang Avatar asked Aug 05 '13 11:08

Daniel Lang


3 Answers

The only correct behavior of <CR> in insert mode is to break the line at the cursor.

What you want is an enhanced behavior and you need to add something to your config to get it: a mapping, a short function or a full fledged plugin.

When I started to use vim, that behavior was actually one of the first things I added to my vimrc. I've changed it many times in the past but this mapping has been quite stable for a while:

inoremap <leader><CR> <CR><C-o>==<C-o>O

I've used <leader><CR> to keep the normal behavior of <CR>.


Here is a small function that seems to do what you want:

function! Expander()
  let line   = getline(".")
  let col    = col(".")
  let first  = line[col-2]
  let second = line[col-1]
  let third  = line[col]

  if first ==# ">"
    if second ==# "<" && third ==# "/"
      return "\<CR>\<C-o>==\<C-o>O"

    else
      return "\<CR>"

    endif

  else
    return "\<CR>"

  endif

endfunction

inoremap <expr> <CR> Expander()
like image 53
romainl Avatar answered Nov 16 '22 08:11

romainl


This little snippet will remap Enter in insert mode to test whether or not the cursor is between > and < and act accordingly if it is. Depending on your indent settings the \<Tab> may need to be removed.

It will not play nice with other plugins that might be also be mapping the Enter key so be aware that there is probably more work to do if you want that compatibility.

function EnterOrIndentTag()
  let line = getline(".")
  let col = getpos(".")[2]
  let before = line[col-2]
  let after = line[col-1]

  if before == ">" && after == "<"
    return "\<Enter>\<C-o>O\<Tab>"
  endif
   return "\<Enter>"
endfunction

inoremap <expr> <Enter> EnterOrIndentTag()

I have only tested the simple cases (beginning of the line, end of the line, inside and outside of ><), there are probably edge cases that this would not catch.

like image 41
Randy Morris Avatar answered Nov 16 '22 10:11

Randy Morris


@RandyMorris and @romainl have posted good solutions for your exact problem.

There are some other possibilities you might be interested in if you are typing out these tags yourself: there's the ragtag.vim plugin for HTML/XML editing.

With ragtag.vim you type this to create your "before" situation (in insert mode):

div<C-X><Space>

To create your "after" situation you would instead type:

div<C-X><Enter>

So if you know beforehand that you are going to "expand" the tag, typing just the element name and the combo CtrlX followed by Enter is enough.

There are also other more advanced plugins to save keystrokes when editing HTML, such as ZenCoding.vim and Sparkup.

like image 2
glts Avatar answered Nov 16 '22 10:11

glts