使用vim中的正则表达式匹配带括号的块

时间:2011-02-28 07:37:25

标签: regex vim

我正在尝试匹配vim在使用动作(时找到的某个)及其匹配的%之间的内容。

更具体地说,我正在寻找一个看起来像这个假设的/someKeyword (\{pair}\(.*\))\{pair}/的正则表达式,如果有\{pair}这样的修饰符,当应用于正则表达式中的两个正好两个字符时,使第二个一个匹配,如果它是第一个匹配括号(% - 明智)。

我正在寻找的模式应该匹配someKeyword之后的第一个括号的内部内容(n.b.它应该处理的代码总是正确括号),如下例所示:

对于someKeyword ("aaa"),子匹配将匹配"aaa"。 同样,someKeyword ("aaa)")将匹配"aaa)"someKeyword(("double-nested stuff"))将匹配("double-nested stuff")

但也适用于:

(
  someKeyword("xyz"))

它应与"xyz"匹配。

有没有办法在正则表达式中使用vim的匹配括号功能?如果没有,还有什么其他解决方案可以实现这一目标?

编辑1:匹配的内容可能跨越几行。

4 个答案:

答案 0 :(得分:5)

这对于vim正则表达式是不可能的(因为允许这种嵌套结构的语言不是常规的),但是可以使用perl提供的“常规”表达式(以及其他语言,我不知道足够确定) )和perl可以从vim内部使用。我不喜欢vim-perl绑定(因为它非常有限),但如果你知道所有应该工作的情况,那么你可以使用perl正则表达式的递归功能(需要更新的perl,我有5.12 *):

perl VIM::Msg($+{"outer"}) if $curbuf->Get(3) =~ /someKeyword\((?'outer'(?'inner'"(?:\\.|[^"])*"|'(?:[^']|'')*'|[^()]*|\((?P>inner)*\))*)\)/

注意,如果可以避免这样的正则表达式,你应该这样做(因为你依赖于重编译器太多),所以我建议直接使用vim动作:

let s:reply=""
function! SetReplyToKeywordArgs(...)
    let [sline, scol]=getpos("'[")[1:2]
    let [eline, ecol]=getpos("']")[1:2]
    let lchar=len(matchstr(getline(eline), '\%'.ecol.'c.'))
    if lchar>1
        let ecol+=lchar-1
    endif
    let text=[]
    let ellcol=col([eline, '$'])
    let slinestr=getline(sline)
    if sline==eline
        if ecol>=ellcol
            call extend(text, [slinestr[(scol-1):], ""])
        else
            call add(text, slinestr[(scol-1):(ecol-1)])
        endif
    else
        call add(text, slinestr[(scol-1):])
        let elinestr=getline(eline)
        if (eline-sline)>1
            call extend(text, getline(sline+1, eline-1))
        endif
        if ecol<ellcol
            call add(text, elinestr[:(ecol-1)])
        else
            call extend(text, [elinestr, ""])
        endif
    endif
    let s:reply=join(text, "\n")
endfunction
function! GetKeywordArgs()
    let winview=winsaveview()
    keepjumps call search('someKeyword', 'e')
    setlocal operatorfunc=SetReplyToKeywordArgs
    keepjumps normal! f(g@i(
    call winrestview(winview)
    return s:reply
endfunction

您可以使用类似

的内容
let savedureg=@"
let saved0reg=@0
keepjumps normal! f(yi(
let s:reply=@"
let @"=savedureg
let @0=saved0reg

而不是operatorfunc来保存和恢复寄存器,但上面的代码使所有寄存器和标记保持不变,我无法保证保存的*东西。它还保证,如果删除join()周围的text,您将保存有关NULL位置的信息(当然,如果您关心它们)。寄存器变体是不可能的。

答案 1 :(得分:3)

大多数正则表达方言不能这样做。有可能使用.NET实现(参见here),但非常非常难看,不应该在我看来使用。

答案 2 :(得分:1)

正如Jens所说,用Vim正则表达式无法做到这一点。

但是,如果您想进行一些特殊处理,可以执行以下操作:

  • 找到您感兴趣的左括号,然后运行一个宏,该宏将在开始括号后面和使用^@绑定的结束括号前插入一些字符(比如%)。
  • 然后相应地修改正则表达式。

答案 3 :(得分:1)

你可以用宏很容易地做到这一点。您将使用正则表达式搜索关键字,然后搜索文本的开头,然后搜索结束,然后您可以执行您想要的任何操作。

E.g。抽取所需的文字:

qa/somekeyword\s*
/(
lvh%hyq

现在每当你使用@a的宏时,你都会抽出你感兴趣的下一段文字。