Syntax-aware seach/replace (using line('.') within :s cmd)

9 views
Skip to first unread message

Ben Fritz

unread,
Jul 17, 2009, 9:33:09 AM7/17/09
to vim_use
StarWing asked this, probably intended as a rhetorical question, in
vim_dev:

http://groups.google.com/group/vim_dev/browse_thread/thread/e6caa2bb4099306b/ec1efe41296398a7

> can you write a pattern to replaced foo to bar in code, but not in
> string and comment? that's useful, but Vim pattern doesn't support
> that :-( maybe makes user can get current syntax state easily is
> better.

I came up with this:

%s/foo/\=(match(map(synstack(line('.'),col('.')), 'synIDattr(v:val,
"name")'), '\([Cc]omment\)\|\([sS]tring\)') >= 0 ? "foo" : "bar")/g

It's cheating (I wouldn't exactly call that a "pattern") but it
works...at least for me.

However, I'm a little curious about the use of line('.') and col('.')
within a sub-replace-expression. It certainly seems to be executing as
if the cursor is at the position of the match, but is this something I
can depend on always being the case? I can't find documentation of
this fact anywhere!

I had to learn about the "map()" function for this to work...Vim never
ceases to amaze.

David Fishburn

unread,
Jul 17, 2009, 9:40:04 AM7/17/09
to vim...@googlegroups.com
On Fri, Jul 17, 2009 at 9:33 AM, Ben Fritz<fritzo...@gmail.com> wrote:
...

>> can you write a pattern to replaced foo to bar in code, but not in
>> string and comment? that's useful, but Vim pattern doesn't support
>> that :-( maybe makes user can get current syntax state easily is
>> better.

I wonder if my plugin:

SrchRplcHiGrp.vim : Search and/or replace based on a syntax highlight
group http://www.vim.org/scripts/script.php?script_id=848

Could be used in this case.
If not, I would be willing to look into how it doesn't meet your needs
and make some changes.

I think it should do what you want though.

Dave

Charles Campbell

unread,
Jul 17, 2009, 9:42:43 AM7/17/09
to vim...@googlegroups.com
Ben Fritz wrote:
> StarWing asked this, probably intended as a rhetorical question, in
> vim_dev:
>
> http://groups.google.com/group/vim_dev/browse_thread/thread/e6caa2bb4099306b/ec1efe41296398a7
>
>
>> can you write a pattern to replaced foo to bar in code, but not in
>> string and comment? that's useful, but Vim pattern doesn't support
>> that :-( maybe makes user can get current syntax state easily is
>> better.
>>
>
> I came up with this:
>
> %s/foo/\=(match(map(synstack(line('.'),col('.')), 'synIDattr(v:val,
> "name")'), '\([Cc]omment\)\|\([sS]tring\)') >= 0 ? "foo" : "bar")/g
>
> It's cheating (I wouldn't exactly call that a "pattern") but it
> works...at least for me.
>

David Fishburn came up with a plugin to facilitate syntax-based replaces:

SrchRplcHiGrp.vim - Search and Replace based on a highlight group
http://www.vim.org/script.php?script_id=848


Regards,
Chip Campbell

Ben Fritz

unread,
Jul 17, 2009, 9:50:46 AM7/17/09
to vim_use


On Jul 17, 8:40 am, David Fishburn <dfishburn....@gmail.com> wrote:
>
> I wonder if my plugin:
>
> SrchRplcHiGrp.vim : Search and/or replace based on a syntax highlight
> grouphttp://www.vim.org/scripts/script.php?script_id=848
>
> Could be used in this case.
> If not, I would be willing to look into how it doesn't meet your needs
> and make some changes.
>
> I think it should do what you want though.
>

I'm actually just asking out of a desire to learn more about Vim...I
currently have no need for this functionality (though if I do I'll
take a look at the plugin). I don't currently have time to look in
detail at the script, but at a glance it looks like you move the
cursor around. Do you move the cursor to make sure the syntax group at
the cursor is the syntax group you're interested in, or for some other
reason (I saw a bunch of "restore the cursor" comments)?

I would really like to know whether line('.') and col('.') and
possibly other functions acting on "current cursor position" will work
in a sub-replace-expression by design, or whether I was just lucky.

Tony Mechelynck

unread,
Jul 17, 2009, 11:04:59 AM7/17/09
to vim...@googlegroups.com
On 17/07/09 15:33, Ben Fritz wrote:
>
> StarWing asked this, probably intended as a rhetorical question, in
> vim_dev:
>
> http://groups.google.com/group/vim_dev/browse_thread/thread/e6caa2bb4099306b/ec1efe41296398a7
>
>> can you write a pattern to replaced foo to bar in code, but not in
>> string and comment? that's useful, but Vim pattern doesn't support
>> that :-( maybe makes user can get current syntax state easily is
>> better.
>
> I came up with this:
>
> %s/foo/\=(match(map(synstack(line('.'),col('.')), 'synIDattr(v:val,
> "name")'), '\([Cc]omment\)\|\([sS]tring\)')>= 0 ? "foo" : "bar")/g
>
> It's cheating (I wouldn't exactly call that a "pattern") but it
> works...at least for me.
[...]

IIUC, technically the "pattern" here is just /foo/ -- or, rather, the
string 'foo' but there are many places where patterns are bounded by
slashes.

Everything between /foo/ and /g is the replacement string, or in this
case, '\=' plus the replace-expression.

I would call the whole line a substitute, or to be more precise, 'a
":substitute" command'. Calling it a "pattern" is in this case an abuse
of language, which may perhaps be condoned in informal text since the
context is clear.

(And, yes, I'm a retired teacher, and I can be quite a pedant when it
suits me.)


Best regards,
Tony.
--
All of the true things I am about to tell you are shameless lies.
-- The Book of Bokonon / Kurt Vonnegut Jr.

StarWing

unread,
Jul 17, 2009, 12:01:21 PM7/17/09
to vim_use


On Jul 17, 9:33 pm, Ben Fritz <fritzophre...@gmail.com> wrote:
> StarWing asked this, probably intended as a rhetorical question, in
> vim_dev:
>
> http://groups.google.com/group/vim_dev/browse_thread/thread/e6caa2bb4...
>
> > can you write a pattern to replaced foo to bar in code, but not in
> > string and comment? that's useful, but Vim pattern doesn't support
> > that :-( maybe makes user can get current syntax state easily is
> > better.
>
> I came up with this:
>
> %s/foo/\=(match(map(synstack(line('.'),col('.')), 'synIDattr(v:val,
> "name")'), '\([Cc]omment\)\|\([sS]tring\)') >= 0 ? "foo" : "bar")/g
>
> It's cheating (I wouldn't exactly call that a "pattern") but it
> works...at least for me.
>
> However, I'm a little curious about the use of line('.') and col('.')
> within a sub-replace-expression. It certainly seems to be executing as
> if the cursor is at the position of the match, but is this something I
> can depend on always being the case? I can't find documentation of
> this fact anywhere!
>
> I had to learn about the "map()" function for this to work...Vim never
> ceases to amaze.

thank you! but you feel comfortlessness isn't it? maybe we can do
something change it. and submatch(), I don't like it...

for submatch, I just want to write a patch to support v:1, v:2, etc. :
if str =~ '\(foo\)\(bar\)'
echo v:1 v:2 " should echo foo bar.
endif

that can work in anytime you used pattern. but I found to implement is
not easy...

\= is a good thing. BUT it can't recursion :-(

Andy Wokula

unread,
Jul 20, 2009, 3:19:08 AM7/20/09
to vim...@googlegroups.com
StarWing schrieb:

> for submatch, I just want to write a patch to support v:1, v:2, etc. :
> if str =~ '\(foo\)\(bar\)'
> echo v:1 v:2 " should echo foo bar.
> endif
>
> that can work in anytime you used pattern. but I found to implement is
> not easy...
>
> \= is a good thing. BUT it can't recursion :-(

:echo matchlist('foobar', '\(foo\)\(bar\)')[1:2]

--
Andy

Reply all
Reply to author
Forward
0 new messages