I have a problem with breaking long line in markdown file. Given the following line

* "fo" : You can use the 'formatoptions' option to influence how Vim formats text. 'formatoptions' is a string that can contain any of the letters below. The default setting is "tcq".

runing gq$ will end up with

* "fo" : You can use the 'formatoptions' option to influence how Vim formats
* text. 'formatoptions' is a string that can contain any of the letters below.
* The default setting is "tcq". 

where the two new * below are definitely extra and erroneous. Ideally, I'd like to set my option or remap my gq$ to produce the following formatted text

* "fo" : You can use the 'formatoptions' option to influence how Vim formats
         text. 'formatoptions' is a string that can contain any of the letters
         below. The default setting is "tcq". 

If that is not easy to do, at least the two extra * should be gone and produces

* "fo" : You can use the 'formatoptions' option to influence how Vim formats
  text. 'formatoptions' is a string that can contain any of the letters below.
  The default setting is "tcq".

How can I remap to achieve the desired formatted text?

By the way, is there a quick way to gq pasted text?

  • 1
    What does :set comments? return for you? With fb:* as one of the entries, I get the result without the repeated *s... Do you have any of :set formatexpr? formatprg? set? (That might interfere with what this command does...) – filbranden May 17 '20 at 5:49
  • @filbranden The :set comments? gives me comments=b:>,b:*,b:+,b:-, while both formatexpr and formatprg are empty. I'm thinking of a map nnoremap gQ ^vyvr gq$<C-o>Plx but with some problems. The <C-o> part (and after) fails. – Kevin Powell May 17 '20 at 6:17
  • For :verbose set comments? I have comments=fb:*,fb:-,fb:+,n:> and Last set from $VIMRUNTIME/ftplugin/markdown.vim`. I'm on Vim 8.2.717. Maybe you need a newer vim-markdown? – filbranden May 17 '20 at 7:34
  • @filbranden Yes, my Vim version is a little old and I will try since what I got is similar comments=b:>,b:*,b:+,b:- and Last set from ~/.vim/plugged/vim-markdown/indent/markdown.vim line 15. I will try that later. Thanks! By the way, my desired format should be achieved by (at least everything is working correctly when typing the mapped keys literally) nnoremap <silent> gQ :exe "normal ^vt:yvt:r\<Space>i\<Space>\<Space>\<Esc>gq$g'.0vt:P"<Bar><CR> but the ` \<Esc>` key is not working here, is there a quick fix? – Kevin Powell May 17 '20 at 7:59

The behavior you're seeing is triggered by plug-in plasticboy/vim-markdown, which is installing an indent plug-in which sets:

setlocal comments+=b:*,b:+,b:-

This lacks the f flag on the * bullets, which means it will repeat them on every line of a formatted block.

It looks like you can control this formatting by setting the vim_markdown_auto_insert_bullets variable. Setting it to 0 will remove that part of the variable definition. So perhaps try this in your vimrc?

let g:vim_markdown_auto_insert_bullets = 0

But that means you won't get a * at all in 'comments', so that won't indent the lines after the first one when the first includes a *.

Vim does ship with default settings for markdown and those include fb:* in 'comments'. Looking at the source repo for it, it looks like it has done so for the last 10 years. So maybe consider dropping the extra plug-in and see if what you get by default from Vim isn't closer to what you actually wanted in first place?

It's possible that the plug-in does bring some behavior you wanted, in which case maybe open a issue with the plug-in's GitHub page about this behavior? That might potentially help get it addressed with the plug-in you would like to use.

  • 1
    That is exactly the reason! I also found it's because of the default setting of the plugin plasticboy/vim-markdown, and the official documentation recommends setting let g:vim_markdown_auto_insert_bullets = 0 and let g:vim_markdown_new_list_item_indent = 0. This indeed solves the problem. Again, thanks a lot! – Kevin Powell May 17 '20 at 13:10
  • @D.BenKnoble Fixed! Thank you for the reminder that it's time for coffee now 😁 – filbranden May 17 '20 at 14:09
  • ☕️☕️☕️no problem – D. Ben Knoble May 17 '20 at 14:16

Your 'formatoptions' include q, it allow gq to format comments.

Your 'comments' include b:* (b is format rule,* is literal pattern), your line starts with a space and *, vim see it as as comment.

vim use b rule of format-comments to format it:

  b Blank (<Space>, <Tab> or <EOL>) required after {string}.

That's why all your lines are prefixed with *.

Format your line as a numbered list

If you want to align indent after :, you can use n rule of 'formatoptions' to format it as numered list. vim use 'formatlistpat' to decide whether current line is a numbered list. It's default value for markdown in vim8.2.677 is:

setlocal formatlistpat?

There is space before your *, it doesn't qualified as a numbered list. To fix it, you can add this to you after/ftplugin/markdown.vim:

" disable comment format
setlocal formatoptions-=q

" enable numbered list format
setlocal formatoptions+=n

" add your special list pattern
let &l:formatlistpat .= '\|^ \{,3\}[-*+][^:]\+:\s*'

result of gq:

 * "fo" : You can use the 'formatoptions' option to influence how Vim formats
          text. 'formatoptions' is a string that can contain any of the letters
          below.  The default setting is "tcq". 

Format your line as comment

This won't work for your special List, this is default behavior, it's used for common List.

It might sounds weird, but vim format List as comment, 'comments' on my machine for markdown defaults to:

setlocal comments?

The f rule is the key:

  f Only the first line has the comment string.  Do not repeat comment on
    the next line, but preserve indentation (e.g., a bullet-list).

result of gq:

 * "fo" : You can use the 'formatoptions' option to influence how Vim formats
   text. 'formatoptions' is a string that can contain any of the letters below.
   The default setting is "tcq". 
  • 1
    Thanks a lot! As pointed out by filbranden, this problem is caused by the default behavior of the plasticboy/vim-markdown plugin. – Kevin Powell May 17 '20 at 13:01

After some workaround, the following mapping can give me the desired : aligned gq format

 inoremap jj <Esc>
 nnoremap <silent> gQ :exe "normal ^dmcmcvt:yvt:r\<Space>i\<Space>\<Space>jjgq$g'cdmc0vt:P"<Bar><CR>

in which I first use a mark c to remember the location of the * symbol, do the usual gq formatting and then go back to restore the content which has been cut from * to :. I have to use a mapping inoremap jj <Esc>, which is annoying, because replacing jj with \<Esc> or <Esc> does not work, at least for me!

For the normal expected result without extra *, as pointed by @filbranden, it is due to the default behavior of the plasticboy/vim-markdown. And it can be restored by setting let g:vim_markdown_auto_insert_bullets = 0 (setting let g:vim_markdown_new_list_item_indent = 0 at the same time is also recommended). My own tweak that does the same job is

nnoremap <silent> gq<CR> :exe "normal ^dmcmcvyvr\<Space>gq$g'cdmchhPlx"<Bar><CR>

In the same vein, mapping to auto format pasted text can be created.

After adopting several tips by @filbranden, the following maps works perfectly for me (gQ for : aligned, the other one for normal alignment but with visually-appealing amount of spaces as shown in my question post)

nnoremap <silent> gQ :exe "normal! ^vt:yvt:r\<Space>i\<Space>\<Space>\<lt>Esc>gq$g`[hvt:P"<CR>
nnoremap <silent> gq<CR> :exe "normal ^vyvr\<Space>gq$g`[Plx"<CR>
  • "replacing jj with \<Esc> or <Esc> does not work" => in a map expansion, when you want a literal < (which you do in this case), you need to use <lt> instead. So in this case you want \<lt>Esc> and then you can use normal!. – filbranden May 17 '20 at 15:35
  • I don't know what dmc is supposed to do, as far as I can tell it doesn't do anything... You don't need to set explicit marks, g'[ will take you back to the first line after formatting. You also don't need to yank then replace text, or even visual mode... Just use ct: \<lt>Esc> to replace the contents til the colon with three spaces, then later vt:"-P to put from the small deletion register "- where the replaced contents will have been stored. Also, <Bar><CR> I don't think you need both, just <CR> is enough, right? – filbranden May 17 '20 at 15:46
  • @filbranden I've been repeatedly told to use ` \<Esc>` to escape from insert mode in a " string. So the need to use <lt> is VERY new and also strange to me since \<Space> works! Any source for further read on this? – Kevin Powell May 18 '20 at 3:53
  • @filbranden dmc is necessary for me because my kshenoy/vim-signature plugin use mc to toggle marks. Otherwise, it is unnecessary. Sorry about that! Aside from replacing g'[ with manual mark c stuff, the rest complications are needed for desired formatting (refer to my question post). And I do not know how to make " containing "-P work in my map string starting with ". Replacing map string " with backtick cause other problems of special keys. – Kevin Powell May 18 '20 at 4:04
  • 1
    @filbranden It turns out there is a difference between the :exe "normal version and direction mapping. With direction mapping, the screen will flash several times in the process to reflect the sequence of operation. For :exe "normal, however, the screen is updated only once, suggesting it run all command in the background then use the final result to update the screen. This latter approach is therefore visually more appealing. – Kevin Powell May 18 '20 at 9:49

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.