我正在使用vim编辑乳胶文档。通常,我会想要将变量的名称更改为其他名称。但是,当我搜索变量名称时,必须经过不相关的匹配项有点烦人。我只想在tex语法文件中定义的“数学区域”中搜索。有没有一种简单的方法可以将搜索限制到特定的语法区域?

评论

我认为答案是否定的:没有简单的方法将搜索限制在特定的语法区域。

此映射使用synIDattr()获得光标下字符的语法组。您可以编写一个执行搜索(或执行正常n运算)的函数,然后使用synIDattr()决定是保留在该位置还是继续向前搜索。

@joeytwiddle在发布具有此类功能的答案后,我才看到您的评论。就像您的想法一样吗?

@jjaderberg是的。辛苦了!

只是为了完整性(我找到了一个很好的答案):stackoverflow.com/a/2696143/5000478

#1 楼

这是您可以尝试的功能。我只测试了一点,但似乎工作正常。

function! JJSyntaxSearch(pattern, syntaxitem)
  while search(a:pattern, 'W') > 0
    for id in synstack(line("."),col("."))
      if synIDattr(id,"name") =~? a:syntaxitem
        return line(".")
      endif
    endfor
  endwhile
  return 0
endfunc

command! -nargs=* JJSyntaxSearch call JJSyntaxSearch(<f-args>)


该函数循环调用Vim的search函数,直到找到“普通”匹配项为止这也是与特定语法项的“限定”匹配,或者直到它到达缓冲区的末尾。

它带有两个参数。第一个是搜索模式,它不变地传递给Vim的search()函数。有关该功能的工作原理,请参见:help search()。第二个参数是要在其上过滤这些匹配项的语法项的名称。这也可以是模式,并且可以作为不区分大小写的正则表达式进行比较。

要在数学区域中找到模式'alpha',可以执行

:JJSyntaxSearch alpha texMathZone.


最后的.是任何单个字符的模式原子,请参阅:help /.。这是因为存在许多数学区域语法项,分别称为texMathZoneAtexMathZoneB等。

search()函数将光标移至下一个“普通”匹配项。然后,我们的函数使用synstack()获取该新光标位置的所有语法项ID。遍历它们,它依次检索每个项目的名称,并根据syntaxitem参数对其进行测试。请参阅:help synstack():help synIDattr()。如果存在“限定”匹配,则返回行号。如果到达缓冲区末尾而没有找到任何“合格”匹配,则返回0。该函数的重点是使光标移至下一个“限定”匹配,因此您可以在那里进行一些操作,但是返回“行号或0”以指示再次调用该函数是否有意义是很有用的,例如例如,如果要从另一个函数或宏重复调用该函数。

我选择对W使用search()标志,因为它可以防止“包装”缓冲区的末尾。否则该函数可能会卡住,因为它会继续查找相同的“普通”匹配项,但找不到“合格”匹配项。

评论


谢谢你的帮助!我还没有尝试过此功能,但是看起来像我想要的。

– RobF。
15年8月18日在0:46

有关语法查找的vimtips Wiki :。

– joeytwiddle
15年8月18日在0:52

注意:Vim 8.2以后的版本支持search()函数的跳过表达式

–克里斯蒂安·布拉班特(Christian Brabandt)
20年8月26日在14:53

#2 楼

这也许应该是一条评论,但似乎我没有声誉。作为创建vim插件的一种练习,我基于jjaderberg的答案创建了一个插件,您可以在这里找到:https://github.com/AckslD/vim-sisr

评论


欢迎来到Vi和Vim!

–filbranden♦
20年8月26日在17:40

#3 楼

如果您的“数学区域”由某种语法模式(例如字符串“ math”)限定,那么您至少可以使用模式序列来查找具有匹配模式的行,如:

:/math//your pattern/


(注意:这两种模式可能在不同的行上!)

这可能不会将您的比赛限制在区域内-因为您需要确定终点区域-,但仅显示初始模式/ math /之后的匹配项。您可以使用ex-mode历史记录(: up-arrow enter)重复搜索下一次出现。


注意:ISTR在早期的vim / vi版本中我也可以使用该方法直接作为搜索命令(而不是ex-mode命令)作为/math//your pattern/,其优点是较少键入和使用nN命令,但是我无法确认当前的vim版本。

#4 楼

如果您要在foo$...$内搜索字符串$$...$$,则此方法应该起作用:

$.*\zsfoo


这会将匹配项固定到$字符,但仅选择字符串foo

这不是一个简单的解决方案,但是可以使用:global + :substitute查找“数学区域”并在其中搜索:

:global /\v\begin\{%(align|alignat|displaymath)\}/ +1,/\end/-1 s/foo/&/gc


假设:substitute是搜索命令,您可以使用n转到下一个出现的位置,然后使用q退出。如果您错误地确认替换,那应该没问题。

您必须将%( ... )中的列表设置为您所使用的数学环境的名称(完整列表为有点沉重:align|alignat|displaymath|eqnarray|equation|flalign|gather|math|multline|subequations|xalignat|xxalignat)。请注意,我在这里使用的是非常魔术的(\v)模式。

说明:

:global在模式匹配的每一行上执行一个Ex命令(:s)(:help :global/\v开始一个非常魔术的模式(:h /:h pattern:h magic\begin\{匹配一个文字\begin{ %(align|alignat|displaymath)匹配这些单词中的任何一个(:h /\%(:h /bar\}/匹配一个文字}并结束该模式。由下一行组成的范围(从+1,/\end/-1找到的那一行开始)直到搜索模式:global找到的那一行之前的那一行。在此范围内,

/\end在确认后立即将每次出现的文字s/foo/&/gc本身替换并替换(foo

评论


这当然是一个开始,但是我经常使用等式,对齐和IEEEeqnarray环境,并且我不想从搜索中排除它们。

– RobF。
15年7月28日在18:23

@RobF .:很可能在多种环境中进行匹配,但是除非编写脚本,否则这并不容易。但是,如果始终在变量名周围使用\ mathit,则查找它们会容易得多。

– Peter Lewerin
15年7月28日在18:33



#5 楼

从8.2.0915开始,search()支持第五个可选参数,在其他类型的表达式中,该参数可以是lambda。
假设您要查找文字字符串pat,则可以运行:
:call search('pat', '', 0, 0, {-> synstack('.', col('.'))->map({_, v -> synIDattr(v, 'name')})->match('\ctexmathzone') == -1})

可以将其转换为自定义命令:
com -nargs=1 SearchInMathZone call s:SearchInMathZone(<q-args>)
fu s:SearchInMathZone(pat) abort
    call search(a:pat, '', 0, 0, {->
        "\ get the stack of syntax items under the cursor
        \ synstack('.', col('.'))
        "\ translate the IDs into names
        \ ->map({_, v -> synIDattr(v, 'name')})
        "\ skip the match if no syntax item under the cursor contains "texmathzone" in its name
        \ ->match('\ctexmathzone') == -1})
endfu

Vim9脚本中的代码更具可读性,因为行连续性是隐式的,并且您可以摆脱前面的反斜杠: />
vim9
com -nargs=1 SearchInMathZone SearchInMathZone(<q-args>)
def SearchInMathZone(pat: string)
    search(pat, '', 0, 0, () =>
        # get the stack of syntax items under the cursor
        synstack('.', col('.'))
        # translate the IDs into names
        ->mapnew((_, v) => synIDattr(v, 'name'))
        # skip the match if no syntax item under the cursor contains "texmathzone" in its name
        ->match('\ctexmathzone') == -1)
enddef

它需要最新的Vim版本;它适用于8.2.2332。一旦编译了skip表达式,它也应该更快。

注意:只要函数需要行地址,只要该函数只能在当前缓冲区上工作(而不是其他缓冲区)。 line('.')就是其中之一:
synstack(line('.'), col('.'))
⇔
synstack('.', col('.'))


有关更多信息,请参见'.'。支持跳过表达式。

#6 楼

如果要在文件中搜索字符串'morning'
/morning,请输入并按n向前移动

评论


这个问题说我只想在“数学区域”中搜索,您的方法会搜索不限制研究区域的整个文件。

–statox♦
16年5月6日在15:51

#7 楼

除了为此写一个专用函数外,别无选择。
我写了一个函数搜索所需的字符串,并且只有在以下情况之一时才停止:


匹配项用尽
在数学环境中有一个匹配项(匹配项将停止在匹配项上)

您只需使用要搜索的字符串来调用它。

function! Finmath(nextstr)
    while 1
        if search(a:nextstr)==0
            break
        endif
        if searchpairpos('\(', '','\)','n')!=[0,0]
            break
        endif
        if searchpairpos('\[', '','\]','n')!=[0,0]
            break
        endif
        let a = getcurpos() 
        if count(strpart(getline('.'),0,a[2]),'$')%2==1
            break
        endif 
    endwhile 
endfunction


我打算为GrammerousCheck写一个类似的函数,该函数仅在数学之外的语法错误时停止。