开发者

Vimscript: regex works in :s but not in substitute()

开发者 https://www.devze.com 2023-04-11 12:03 出处:网络
If I paste the output of :ls into a buffer, the command :%s/.*\\(\\\".*\\\"\\).*/\\1/ reduces that output to just the file paths. Wanting to achieve that result in a variable, I did

If I paste the output of :ls into a buffer, the command

:%s/.*\(\".*\"\).*/\1/

reduces that output to just the file paths. Wanting to achieve that result in a variable, I did

:redir => x|silent :ls|redir END
:let y = substitute(x, ".*\(\".*\"\).*", "\1", "g")

which accomplished absolutely nothing, y is identical to x. I've tried umpteen variations on that substitute command, getting only the same result, or a bunch of error messages. How s开发者_运维技巧hould I be specifying it?


You need to escape backslashes in quotes. Use \\( and \\\" with substitute().


You have two errors: first was already mentioned by @Radu, second is arises from the fact that in substitute() newline matches ., while in :s it is not. That is why «only the final match is delivered». I can post a correct solution using :redir, :ls and substitute(), but instead suggest the following:

let buflist=map(filter(range(1, bufnr('$')), 'buflisted(v:val)'), 'bufname(v:val)')

If you want output almost identical to what :ls has, try replacing bufname(v:val) with BufName(v:val):

function BufName(bnr)
    let bn=bufname(a:bnr)
    let bt=getbufvar(a:bnr, '&buftype')
    if empty(bn)
        return (empty(bt)?('[No Name]'):('[Scratch]'))
    elseif empty(bt)
        " This won't produce names like ../file, while :ls will "
        return fnamemodify(bn, ':~:.')
    elseif bt is# 'help'
        return fnamemodify(bn, ':t')
    else
        return bn
    endif
endfunction

The above command leaves you with a list of strings. If you want to have a newline-separated «list» instead, use

let bufliststr=join(buflist, "\n")

If you still want to use substitute(), see :h /[].


The variable x contains

1 #    "~/Session.vim"                line 5
2      "~/.vimrc"                     line 34
3      ".vim/vscripts/makesess.vim"   line 4
4      "~/Documents/vimcht"           line 62
5      "~/.fluxbox/startup"           line 5
6      "~/Documents/Notes"            line 2604
7      "~/Documents/bashcht"          line 21
8 %a   "junk"                         line 10

By my reading of :h /[], particularly "...Without the "_" or "\n" the collection does not match an end-of-line...", then

:let y = ""
:let y = substitute(x, '[.]*\("[.]*"\)[.]*', '\1', "g")

should have done the job. But it delivered

"junk"

just as it did without the []. I then tried

:let y = ""
:let y = substitute(x, '[^\n]*\("[^\n]*"\)[^\n]*', '\1', "g")

and got exactly the same result.

Clearly vim does not use the actual newline character internally, neither for variables nor for registers. So it treats a variable as a single line. Which led me to this solution

:let y = substitute(x, '[^"]*\("[^"]\+"\)[^"]*', ':tabe \1\n', 'g')

yielding y containing

:tabe "~/Session.vim"
:tabe "~/.vimrc"
:tabe ".vim/vscripts/makesess.vim"
:tabe "~/Documents/vimcht"
:tabe "~/.fluxbox/startup"
:tabe "~/Documents/Notes"
:tabe "~/Documents/bashcht"
:tabe "junk"

as desired to form my Session.vim file

0

精彩评论

暂无评论...
验证码 换一张
取 消