Using shorter textwidth in comments and docstrings
Asked Answered
P

2

32

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?

Podiatry answered 26/10, 2010 at 19:22 Comment(1)
Very similar question: #3475572Oxfordshire
P
18

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
Podiatry answered 26/10, 2010 at 19:22 Comment(6)
Is there any noticeable effect on performance from using the CursorMoved* groups? I use a similar method, but chose to use the 'InsertEnter' au group rather than CursorMoved*. CursorMoved is definitely more fine-grained, but 'InsertEnter' was fine-grained enough for me and gets called far less often. Just wanted to mention it as an option, and also to check if you notice any performance issue with 'CursorMoved'.Trafficator
@Herbert Sitz I haven't noticed any performance issues using CursorMoved. It makes this script more natural for (e.g.) reformatting existing docstrings, or if, like me, you're new to Vim and still move around a lot while in insert mode. But using InsertEnter will definitely catch the common uses, and use less resources.Podiatry
This is awesome; thanks for putting it together. I noticed that in my vim setup, the synIDattr call results in "Constant", not "String", when I'm in a string literal. I had to compare against that as an option in my vimrc.Demona
Love it. Any motion towards making this a Vim plugin?Westerfield
I have just proposed to add it to the python-mode plugin: github.com/klen/python-mode/issues/461Drain
the issue was closed due to inactivity twice :( ... is there anything us people interested can do to support this getting added to the python-mode plugin?Trictrac
S
6

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.

Sadden answered 12/2, 2015 at 17:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.