How get a list of substrings matching a pattern?

13 views
Skip to first unread message

BPJ

unread,
Jun 26, 2015, 11:34:00 AM6/26/15
to Vim Users
I have succeded in writing a function which splits the contents of
a visual selection on a pattern, reverses the resulting list,
joins it together on a separator and puts the result back into the
visual selection (Yes that's kind of basic, right?):

fun! RevList(...)
let reg_save=@@
let pattern=!exists('a:1') ? '\s*,\s*' : a:1
let sep=!exists('a:2') ? ', ' : a:2
normal gvy
let @@=join(reverse(split(@@,pattern)),sep)
normal gvp
let @@=reg_save
endfun

What I want to do is to make this smart enough to not split on
commas (or whatever) which are inside quotes, so how do I extract
all substrings which match a pattern rather than split on a
pattern, the equivalent of `my @list = $string =~ m/($pattern)/g`
in Perl? (Hopefully a pattern like '\v%(%(\s*\,\s*)@!.)+' will
match the non-quoted parts...)

Benji Fisher

unread,
Jun 26, 2015, 12:07:08 PM6/26/15
to vim...@googlegroups.com
On Fri, Jun 26, 2015 at 11:33 AM, BPJ <b...@melroch.se> wrote:
I have succeeded in writing a function which splits the contents of a visual selection on a pattern, reverses the resulting list, joins it together on a separator and puts the result back into the visual selection (Yes that's kind of basic, right?):

     I think you want to split on any comma that is not preceded by unbalanced quotation marks.   If you have to deal correctly with single and double quotation marks, then I am not sure it can be done with regular expressions.  As a starting point, see the notslash variable in matchit.vim:  https://github.com/benjifisher/matchit.zip/blob/master/plugin/matchit.vim#L85 .  What you want is a lot more complicated.  I assume that escaped quotes, like '\"', inside other quotes, do not count.

     Maybe you can work with something like this (after removing the spaces, assuming \v):

( [^,] | " ( [^"] | notslash \\")* " | ' ( [^'] | notslash \\')* ' ){-} \zs \s*,\s*

-- 
HTH
Benji Fisher


Nikolay Pavlov

unread,
Jun 26, 2015, 12:07:27 PM6/26/15
to vim...@googlegroups.com
You can use a hack with substitute(, , '\='):

let matches = []
call substitute(str, pattern, '\=add(matches, submatch(0))[-1]', 'g')

---

Some notes about your code:

1. !exists('a:n') may be written as a:0 < n. The whole expression can
be written using get(a:000, n, default).
2. You should not ever use :normal in scripts. You can’t know whether
`normal gvy` will restore the selection and yank because user may have
remapped something. You need to use :normal!.
3. Using :let and @reg does not allow you neither to correctly save
nor restore the register. You need to use

let reg_save = [getreg('"', 1, 1), getregtype('"')]

call setreg('"', reg_save[0], reg_save[1])

. In the current state your code spoils

1. Any NUL which happens to appear in the register. (This is why I
have second argument to getreg().)
2. Register restored with :let @ will *never* be blockwise.

4. Yank operation does alter registers besides the unnamed register:
at least, the zero register, possibly also system clipboard. If you
care about restoring registers it is better to avoid yank operation,
though code will be much more complex (I would be using code based on
`getpos()` (using positions of < and > marks) and `visualmode()`).
Warning: you cannot possibly restore system clipboard from Vim because
Vim can only put text there.

>
> --
> --
> You received this message from the "vim_use" maillist.
> Do not top-post! Type your reply below the text you are replying to.
> For more information, visit http://www.vim.org/maillist.php
>
> --- You received this message because you are subscribed to the Google
> Groups "vim_use" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to vim_use+u...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages