views:

107

answers:

3

Background task

To eliminate X-Y problems I'll say what I'm doing: I'm trying to use :perldo in VIM 7.2 to complete two tasks:

  • Clear all trailing whitespace, including (clearing not deleting) lines that only have whitespace
    • s/\s+$//;
  • Remove non-tab whitespace that exists before the first-non space character
    • s/^ (\s*) (?=\S) / s#[^\t]##g;$_ /xe;

I'd like to do this all with one pass. Currently, using :perldo, I can get this working with two passes. (by using :perldo twice)

The command should look like this:

:perldo s/\s+$//; s/^ (\s*) (?=\S) / s#[^\t]##g;$_ /xe;

Perl background

In order to understand this problem you must know a little bit about Perl s/// automagically binds to the default variable $_ which the regex is free to modify. Most core functions operate on $_ by default.

perl -e'$_="foo"; s/foo/bar/; s/bar/baz/; print' # will print baz

The assumption is that you can chain expressions using :perldo in VIM and that it will work logically.

VIM not being nice

Now my VIM problem is better demonstrated with code -- I've reduced it to a simple test. Open a new buffer place the following text into it:

aa bb
aa
bb

Now run this :perldo s/a/z/; s/b/z/; The buffer now has:

za zb
aa
zb

Why was the first regex unsuccessful on the second row, and yet the second regex was successful by itself, and on the first row?

A: 

Seems to me like only the last command is run on all lines in [range].

graywh
+5  A: 

Don't know what :perldo is doing exactly, but if you run something like

:perldo s/a/z/+s/b/z/

then you get something more like you'd expect.

mobrule
Thanks!!! I'm not sure how that does it, but it does.
Evan Carroll
I still don't know how it works, but I suspected it would behave better with a single expression.
mobrule
should probably be added to `:he`
Evan Carroll
@mobrule he was adding these in scalar context. So the amount of matches in the first `s///` plus the amount of matches in the second `s///` is the return code. The second `s` failing can not negate the return code of the first `s///`
Evan Carroll
+3  A: 

It appears the whole Perl expression you pass to :perldo must return a true / defined value, or the results are discarded, per-line.

Try this, nothing happens on any line:

:perldo s/a/z/; s/b/z/; 0

Try this, it works on all 3 lines as expected:

:perldo s/a/z/; s/b/z; 1

An example in the :perldo documentation hints at this:

:perldo $_ = reverse($_);1

but unfortunately it doesn't say explicitly what's going on.

Brian Carper
Yep, that'd explain the OP's results: The last statement in the `perldo` must return a true value, or the entire result of `perldo` is discarded.
Adam Bellaire
I've switched this to my answer, because it makes sense. I imagine the other answer was exploiting that `0 + 0 = 0 ( no replacement )`, and 1+0 or 0+1 would result in a replacement. This is a pretty silly thing to not have documented.
Evan Carroll