0

I am a Python programmer using Vim. When writing a long function call, it often exceeds the width of my document:

a_long_python_function(with_some, additional, arguments, kwarg1=kwarg1, kwarg2=kwarg2, kwarg3=kwarg3)

In the end, I would like to achieve a formatting like the following

a_long_python_function(
    with_some, additional, arguments,
    kwarg1=kwarg1, kwarg2=kwarg2, kwarg3=kwarg3
)

where all the arguments get shifted exactly once more than the line with the function call and the closing bracket is at the same indentation level as the call. For the arguments in between, I would like to have a line break between keyword and non-keyword arguments, though this is optional. I use tabs rather than spaces. Function calls that do not exceed textwidth should not be changed.

The function that currently does Python indentation for me was delivered directly with Vim. I read it and thought that I would be able to change it to my needs but it seems I am still not experienced enough in Vim script (I don't give the code here because it is lengthy. Interested readers may check $VIMRUNTIME/autoload/python.vim. That's where I found it). Ideally, I would like the re-formatting to take place while I type and as soon as the number of characters in a line exceeds textwidth, but it would be also okay if I was able to auto-format things properly later, e.g. by using gq.

1

3 Answers 3

1

I would advise you against doing the formatting work with vim's builtin indent system, or via some vimscript magic.

Code formatting (rather than just indenting according to braces) is best done by an external formatter, like black, yapf or autopep8, as they know better about formatting rules and language specific details.

You can invoke these formatters via vim. Actually gq tries to invoke a user-defined formatter before fallbacking to vim's internal format rules. All you have to do is set formatprg (see :h formatprg) appropriately.

For example to format with black, just add the following line to your vimrc.

:let &formatprg='black -q -'

(we have -q - here because formatprg is expected to read unformatted code from stdin and print formatted code to stdout, which is not the default for black)

One subtlety is that most formatters like black only work with entire files; they dont work well for fragments of code like just a long function call. So if you still just want to format long calls, you'll have to write your own formatter (which should be easy, I guess a 20-liner script will do).

1
  • 1
    I think you can do let &l:formatprg = 'black -q -' and skip the wrapper script.
    – D. Ben Knoble
    Mar 21, 2023 at 13:21
0

Thanks @Hoblovski for the help. I now managed to write my own formatter, cause yapf and co didn't perform quite as desired for me. Here's the code:

#!/usr/local/bin/zsh

sed -E 's/([[:space:]]*)([^(]*\()(.*)(\)[^)]*)/\1\2\n\1 \3\n\1\4/' | sed -E 's/([[:space:]]*)([^=]*)[[:space:]]([a-z_]*=[a-z_].*)/\1\2\n\1\3/' | gfmt -s -w 78

This should work for macOS users after installing gfmt via brew install coreutils. For Linux users

  • Replace the path to the shell in the first line with your shell
  • Check whether you have fmt installed. In this case, you may want to replace gfmt with fmt

Also, if you don't like tabs, you can replace the literal tab character in the first sed command with 4 (or 8) spaces. Once you have the script, it can be added as described by @Hoblovski by running

set formatprg='path/to/script'
0

Hoblovski provided some great, simple suggestions in his answer. Here's how I have my VIM set up to automatically run black to format my Python code ("fixing"), as well as provide other formatting or code-quality suggestions (known as "linting").

The former will automatically provide formatting like you asked for to format long functions, and the latter will go further in providing additional suggestions on quality. You can mix and match and choose one or the other in configuring your vim environment.

Here's steps you can take to set up your vim to provide these features automatically as you edit your code:

  1. Use a plugin manager, which will let you then install a few plugins to help format your code automatically. I use vundle, which is installed using your .vimrc and a few commands inside vim to complete the installation. There's a helpful quickstart guide here.

  2. Install ALE (Asynchronous Lint Engine) using your plugin manager. Here's where you can get it.

  3. Install the linters or formatters you want. I recommend as a linter either Flake8 or Pylint, and as a formatter Black.

  4. Add the needed configuration to your .vimrc file. ALE has all the details of this, but here's some basic configuration that should get pylint and black working for you once you've completed the installation steps above:

let g:ale_lint_on_enter = 1
let g:ale_lint_on_save = 1
let g:ale_lint_on_text_changed = 'always'
let g:ale_set_highlights = 1
let g:ale_set_signs = 1
let g:ale_fix_on_save = 1
let g:ale_fixers = {
   'python': ['black',],
}
let g:ale_linters = {
   'python': ['pylint',],
}

Congrats on choosing VIM as your editor, it's a great tool, infinitely customizable to meet your needs!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.