0

I would like to make if-fi statement in vim, where the iabbrev will "autocomplete" the if-fi clause:

 autocmd Filetype sh iabbrev if if<esc>ma<cr>ifi<esc>`ak

which upon if generates:

if
fi

That's correct - I do not have to type fi at the end of the clause. So I can continue. For example:

if pwd; then
    echo "bla bla"

fi

Now below the echo, I would like to nest another statement, but this time, If I type if it generates:

if pwd; then
    echo "bla bla"

    if
fi
fi

The second (nested) if-fi broke the indentation. The fi ending should be this:

if pwd; then
    echo "bla bla"

    if
    fi
fi

So the second if-fi clause is withing the first one. But I have no idea how to achieve that. I have

:set autoindent
:set smartindent

So vim remember the level of indentation upon <cr>, so I can get the right indentation in the nested if-fi with one backspace. But I have no idea what key-mapping should I make to achieve that. So what key-mapping should I add to .vimrc as iabbrev in order to generate the right level of indentation in vim?

1

1 Answer 1

1

I don't reproduce the exact issue you're having, with filetype=sh and the indent plug-in enabled it seems to be reinfecting the fi to match the if for me.

But I can see a problem in your abbreviation in that it's using the <CR> in normal mode to move to the next line and then using an i to insert the fi part. For one thing, it's counting on this next line being empty already (otherwise it will just prepend the fi to whatever is already there.) It might also be causing your issue, since inserting on a new line typically doesn't trigger the automatic indentation as inserting a new line (with a <CR> in insert mode, or with a normal mode o command) would.

My suggestion is to keep in insert mode and use <C-o> to execute a single normal mode command to set the mark and move to it. (See :help i_CTRL-O.)

This abbreviation should work well:

iabbrev if if<C-o>ma<CR>fi<C-o>`a

Note that the <CR> is happening in insert mode, so it's actually inserting a new line and when a new line is inserted autoindent is typically honored.

If you're going to set abbreviations per filetype, you should typically make them buffer-local, so if you have other buffers editing different types of files, they won't apply there. You can do so by adding a <buffer> modifier to the command:

iabbrev <buffer> if if<C-o>ma<cr>fi<C-o>`a

You can then add this to an autocmd FileTye sh if you like. If you use an autocmd, you'll usually want to keep it inside an augroup, so if you source your vimrc file again, it will not duplicate the auto-command and run it multiple times every time the file type is matched.

My usual preference is to let the Vim ftplugin mechanism handle that for me. To do that, just create a file named ~/.vim/after/ftplugin/sh.vim and add your iabbrev <buffer> ... command directly to it. (Don't add the autocmd part.) Vim will handle these automatically, whenever the file type is detected to be sh, it will automatically source all these scripts. This makes it simpler to source many rules for a specific file type. You also don't pollute your vimrc with all the filetype-specific rules. Finally, you don't need to worry about escaping those long lines with auto-commands and mappings.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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