I have the following Python code

gvu.set_ticks(ax, xticks=[45, 50],
              yticks=[-25, -20, -15])

After this substitution, in visual mode

:s/\([^=]\)\ *\(=\)\ *\([^=]\)/\1 \2 \3/g

the code becomes

z = xyz%10
gvu.set_ticks(ax, xticks = [45, 50],
              yticks = [-25, -20, -15])

But I don't want the substitution to apply to the equal signs inside parentheses. What I want is

z = xyz%10
gvu.set_ticks(ax, xticks=[45, 50],
              yticks=[-25, -20, -15])
  • Try this: sil *s/\n/\="\x01"/ | sil s/\%(^\%(([^()]*)\|[^()]\)*\)\@<==/ = /g | sil s/\%x01/\r/g.
    – user938271
    Jun 14 '20 at 14:45
  • 1
    As a general piece of advice, to format Python code (or that of most other languages) I'd recommend using an external formatting tool primarily, such as black, pyformat or yapf (in Python's case.) It will take care of all style recommendations (not just that one) and it's easy to integrate it into Vim.
    – filbranden
    Jun 14 '20 at 18:40
  • 1
    @filbranden Thanks for introducing me to these formatting tools. I am using yapf now, it is very convenient. I still want to have a vim solution (the one that is proposed above does not work, and add extra spaces when not necessary). I have a similar vim-regex problem in Latex but for excluding a pattern between begin and end environment instead of parentheses.
    – sfx
    Jun 14 '20 at 21:36
  • @user938271 The first * should be a %. This looks pretty interesting, joining all lines so you can match it in a single line. You should consider posting it as an answer.
    – filbranden
    Jun 14 '20 at 21:43

Try to visually select the lines on which you want to perform the substitution, then run this:

sil *s/\n/\="\x01"/ | sil s/\%(^\%(([^()]*)\|[^()]\)*\)\@<==/ = /g | sil s/\%x01/\r/g

If you want to operate on all the lines in the buffer, replace the range * with %.

Broken down:

" join all lines in the visual selection into a single one
sil *s/\n/\="\x01"/

" add spaces around equal signs outside parentheses
sil s/\%(^\%(([^()]*)\|[^()]\)*\)\@<==/ = /g

" split back the lines
sil s/\%x01/\r/g
  • I only get it to work in normal mode, with * or %. I changed it slightly to exclude == and not to add extra space, sil *s/\n/\="\x01"/ | sil s/\%(^\%(([^()]*)\|[^()]\)*\)\@<=\([^=]\)\s*=\s*\([^=]\)/\1 = \3/g | sil s/\%x01/\r/g
    – sfx
    Jun 15 '20 at 20:07

I came up with:

g/^[^(]*=[^)]*$/s/[^ ]\zs=\ze[^ ]/\ =\ /g

The global search for a = that has no ( before and no ) after.

The substitute command changes each = not surrounded by spaces with a _=_ (with spaces). zs and ze (start and end) are delimiters: only the = will be replaced here, not the whole pattern.

There must be numbers of edge cases I did not check.

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.