10

I use gvim for writing emails and use tw=72 to break long lines. Now I also edit basic mail headers in the same document, e.g. I am adding recipients to my mail. Now with tw=72 it also breaks every time after 72 characters.

How can I set tw=0 on the first 4 lines (that's the header part), and tw=72 for the rest of the document?

My formatoptions look like this, if that's relevant:

set fo+=tcqn
3
  • textwidth is applied to the whole buffer.
    – romainl
    Aug 12, 2016 at 10:24
  • 3
    Also see: Prevent Vim from breaking up links mid-tag in markdown. You could use a similar solution for emails, either by checking if line('.') < 5, or by checking if the line starts with an email header (e.g. From: , To: , etc.). Aug 12, 2016 at 11:13
  • @Carpetsmoker Thanks a lot, this was the key to the solution. I added my full answer with the necessary modifications.
    – mxmehl
    Aug 12, 2016 at 11:43

4 Answers 4

6

Thanks to @Carpetsmoker's comment on my question, I was able to solve this exactly like I wanted it to have.

With the following function in .vimrc (or in my case in a special file that it only loaded when editing my emails), tw is set to 500 if the line number is smaller than 5, otherwise it's 72. With au CursorMovedI this is checked everytime the cursor moves.

" Set tw to 500 if in the first 4 lines, else 500
au CursorMovedI * call ModifyTextWidth()   " execute when cursor has moved, use for all files
function! ModifyTextWidth()
    if line('.') < 5               " if line number smaller than 5
        setlocal textwidth=500     " use high tw setting
    else
        setlocal textwidth=72      " Otherwise use normal textwidth
    endif
endfunction

If someone plans to implement this, consider replacing the wildcard in au CursorMovedI * with a fixed path or file suffix.

4

A more robust alternative to mxmehl's answer.

If you use filetype=mail Vim will highlight headers for you:

enter image description here

We can use the syntax information to determine if a line is a header, and set the textwidth accordingly.

The syntax name of the headers are mailHeaderKey, mailSubject, mailHeaderEmail, and mailHeader. I found this out by looking at /usr/share/vim/vim74/syntax/mail.vim.

The reason I prefer this solution is that it doesn't depend on arbitrary line numbers, but that it works equally correct for emails with one header and twenty headers.

augroup filetypes
    autocmd!

    autocmd FileType mail call s:mail()
augroup end

" Set up ft=mail
fun! s:mail()
    augroup ft_mail
        autocmd!
        autocmd CursorMoved,CursorMovedI *
            \  if index(["mailHeaderKey", "mailSubject", "mailHeaderEmail", "mailHeader"], synIDattr(synID(line('.'), col('.'), 1), 'name')) >= 0
            \|     setlocal textwidth=500
            \| else
            \|     setlocal textwidth=72
            \| endif
    augroup end
endfun
2

You could achieve this using autocmds. You could include them in your vimrc, but it is usually better to include them in a filetype, as explained here.

Then you could use the following:

augroup headerTw
   au!
   autocmd InsertEnter <buffer>
            \ if line('.') < 5 |
            \    setl tw=0 |
            \ endif
   autocmd InsertLeave <buffer>
            \ if line('.') < 5 |
            \    setl tw=72 |
            \ endif
augroup END

If you prefer to use it without a filetype plugin you will need to replace the <buffer> with a file pattern.

The idea is to change the 'textwidth' when you enter insert mode, and restore it when you leave it.

If you usually navigate in the text while in insert mode (e.g.: using the arrow keys) you could enhance it based on the link mentioned by @Carpetsmoke: Prevent Vim from breaking up links mid-tag in markdown.

1

I don't believe this is possible. However, there are a couple workarounds.

  1. You could write your header and then :set tw=72. Since tw doesn't apply retroactively to the text already in your buffer,

  2. You could type the entire thing with :set tw=0 and then once you are finished, move to line 4, :set tw=72, and gqG. Or, if you don't feel like doing that manually, you could macro it or map it. For example, both of these would work:

    let @r=":set tw=72<cr>4GgqG:set tw=0<cr>"
    nnoremap <silent> <leader>r :set tw=72<cr>4GgqG:set tw=0<cr>
    

    ("r" for "reformat")

    You could even set this up to happen automatically when you close vim, assuming that you want it to reformat when you close vim, and that there is a filetype that you use just for emails. For example, if all of your emails had the .email extension, you could do:

    autocommand VimLeavePre *.email set tw=72 | exe "normal! 4GgqG" | w
    

    Of course, you'd have to change *.email to whatever file extension you actually use. If you use something like .txt, I would not recommend this, since it will mess with every .txt file you have.

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.