Question

From the mighty PEP 8:

[P]lease limit all lines to a maximum of 79 characters. For flowing long blocks of text (docstrings or comments), limiting the length to 72 characters is recommended.

When editing Python code in Vim, I set my textwidth to 79, and Vim automatically wraps long lines of Python code for me when I hit the character limit. But in comments and docstrings, I need to wrap text at 72 characters instead.

Is there any way to make Vim automatically set textwidth to 72 when I'm in a comment or docstring, and set it back when I'm done?

Was it helpful?

Solution

So, I've never done any Vim scripting before, but based on this question about doing something similar in C and this tip for checking if you're currently in a comment, I've hacked together a solution.

By default, this uses the PEP8-suggested widths of 79 characters for normal lines and 72 characters for comments, but you can override them by letting g:python_normal_text_width or g:python_comment_text_width variables, respectively. (Personally, I wrap normal lines at 78 characters.)

Drop this baby in your .vimrc and you should be good to go. I may package this up as a plugin later.

function! GetPythonTextWidth()
    if !exists('g:python_normal_text_width')
        let normal_text_width = 79
    else
        let normal_text_width = g:python_normal_text_width
    endif

    if !exists('g:python_comment_text_width')
        let comment_text_width = 72
    else
        let comment_text_width = g:python_comment_text_width
    endif

    let cur_syntax = synIDattr(synIDtrans(synID(line("."), col("."), 0)), "name")
    if cur_syntax == "Comment"
        return comment_text_width
    elseif cur_syntax == "String"
        " Check to see if we're in a docstring
        let lnum = line(".")
        while lnum >= 1 && (synIDattr(synIDtrans(synID(lnum, col([lnum, "$"]) - 1, 0)), "name") == "String" || match(getline(lnum), '\v^\s*$') > -1)
            if match(getline(lnum), "\\('''\\|\"\"\"\\)") > -1
                " Assume that any longstring is a docstring
                return comment_text_width
            endif
            let lnum -= 1
        endwhile
    endif

    return normal_text_width
endfunction

augroup pep8
    au!
    autocmd CursorMoved,CursorMovedI * :if &ft == 'python' | :exe 'setlocal textwidth='.GetPythonTextWidth() | :endif
augroup END

OTHER TIPS

The accepted answer is great! It does not, however, support the habit I have for formatting/editing comments: I make my edits and then use the gqj command, which is essentially, "reformat the current line combined with the next". Then I hit '.' to repeat that for each line (the command itself advances the cursor to the next line). I don't know the vim scripting language very well, so someone may be able to add support for this to the accepted answer. In the meantime, what I have done is map a function key (F6) to change the textwidth to 72, format the line and then change the textwidth back to 79.

nmap <F6> :set textwidth=72<CR>gqj:set textwidth=79<CR>

Now, when I'm in a docstring, I just make the edit, (ESC) and then hit F6 repeatedly until all the lines are properly formatted.

I added my map command and the accepted answer script to my .vim/after/ftplugin/python.vim.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top