2

Consider the text

A
B
# C
# D

Is there a text object or motion that will select lines A and B if the cursor is on either, but not select lines C and D? ap or ip with the cursor on any of those four lines will select all four, since there is not a blank line between C and D.

Use case: git commit messages. For example:

Added glob tests; bugfix in fnmatch

Run `CTEST_OUTPUT_ON_FAILURE=1 ctest .` to see the inputs for glob tests
that fail.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.

If I put the cursor on the Run... line and wrap it with gqap, all of the #... comment lines get wrapped in:

Run `CTEST_OUTPUT_ON_FAILURE=1 ctest .` to see the inputs for glob tests
that fail.  # Please enter the commit message for your changes. Lines
starting # with '#' will be ignored, and an empty message aborts the

I would like gq<something> to select only the non-# lines and wrap them. The only workaround I can think of so far is v/^#<CR>jgq, which seems rather tedious.

Another use case:

Being able to distinguish comment lines from non-comment lines would also help me select Doxygen comments before functions:

/// Comment here                      A
/// More comment here                 B
void do_something_interesting() {     C
}                                     D

Same deal: I'd like to be able to select A+B without C+D or vice versa.

0

1 Answer 1

3

Define syntax match gitcommitBody for the custom text object syntax

Install vim-textobj-user and vim-textobj-syntax. The default syntax file for gitcommit does not define gitcommitBody. Place following line

syn match   gitcommitBody   "\%>2l[^#].*" contains=@Spell

into the file ~/.vim/after/ftplugin/gitcommit.vim.

This defines for all lines except the first two and comment lines of a gitcommit buffer the syntax match gitcommitBody.

You could also add it to your vimrc with

augroup Mysyntax
  autocmd!
  autocmd FileType gitcommit syn match   gitcommitBody  "\%>2l[^#].*" contains=@Spell
augroup END

However, if you have many autocommands, filetype is tested for each autocommand, which is not efficient. Furthermore, you replicate something that Vim already does automatically anyway (see also the reddit post where_to_put_filetype_specific_stuff by romainl).

For a typical gitcommit buffer, the syntax groups are now :

enter image description here

Without the additional syntax definition the body would belong to no syntax group at all and could not be adressed by the custom text object vim-textobj-syntax.

Now you can press

gqiy

to reflow the gitcommitBody to

enter image description here

5
  • It would be better not to use an autocommand but to define the syntax group in a ftplugin. To do so, simply put the command syn match gitcommitBody "\%>2l[^#].*" contains=@Spell in a file ~/.vim/after/ftplugin/gitcommit.vim. See :h ftplugin
    – statox
    Oct 11, 2018 at 7:49
  • Thanks for your suggestion. Some people prefer to keep their config in a single file (I am not one of them). What is the reason to do this here?
    – Hotschke
    Oct 11, 2018 at 8:01
  • @statox: do you know how one could improve it to support several paragraphs separated by blank lines? I tried syn region gitcommitBody start="\%3l" end='^#'he=e-1 contains=@Spell but it does not work as wished.
    – Hotschke
    Oct 11, 2018 at 8:15
  • 1
    this page gives a pretty good list of pros and cons of using autocommands vs ftplugins. About the syntax I don't have a computer right now but I'll try to see later if I can come up with something
    – statox
    Oct 11, 2018 at 9:15
  • 1
    Thanks for the good link! I have found the list of downsides naming three reasons. I'd say the first one is not an issue here. The second one is an issue for people with many autocommands. The third is more minor. I think I mention both possibilities but recommend to put everyting into ~/.vim/after/ftplugin/gitcommit.vim and repeat the 2nd and 3rd reason of the post on reddit.
    – Hotschke
    Oct 11, 2018 at 11:30

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.