在VIM中切换语法高亮的隐藏属性

3

我目前拥有一个语法文件,用于解析日志文件,类似于以下内容[这是针对syslog的]:

syn match   syslogText  /.*$/
syn match   syslogFacility  /.\{-1,}:/  nextgroup=syslogText skipwhite
syn match   syslogHost  /\S\+/  nextgroup=syslogFacility,syslogText skipwhite
syn match   syslogDate  /^.\{-}\d\d:\d\d:\d\d/  nextgroup=syslogHost skipwhite

我希望能够使用地图来切换一个特定的组(比如syslogHost)是否隐藏。是否有办法实现以下伪代码的功能:

map <leader>ch :syn match syslogHost conceal!

从而在以下两个选项之间切换syslogHost的定义:

syn match   syslogHost  /\S\+/  nextgroup=syslogFacility,syslogText skipwhite

并且

syn match   syslogHost  /\S\+/  nextgroup=syslogFacility,syslogText skipwhite conceal

感谢您的选择。
3个回答

4

我认为唯一的方法是使用一个函数:

map <leader>ch :call ToggleGroupConceal('sysLogHost')<CR>

function! ToggleGroupConceal(group)
    " Get the existing syntax definition
    redir => syntax_def
    exe 'silent syn list' a:group
    redir END
    " Split into multiple lines
    let lines = split(syntax_def, "\n")
    " Clear the existing syntax definitions
    exe 'syn clear' a:group
    for line in lines
            " Only parse the lines that mention the desired group
            " (so don't try to parse the "--- Syntax items ---" line)
        if line =~ a:group
                    " Get the required bits (the syntax type and the full definition)
            let matcher = a:group . '\s\+xxx\s\+\(\k\+\)\s\+\(.*\)'
            let type = substitute(line, matcher, '\1', '')
            let definition = substitute(line, matcher, '\2', '')
                    " Either add or remove 'conceal' from the definition
            if definition =~ 'conceal'
                let definition = substitute(definition, ' conceal\>', '', '')
                exe 'syn' type a:group definition
            else
                exe 'syn' type a:group definition 'conceal'
            endif
        endif
    endfor
endfunction

太完美了!非常感谢;这太好了!我唯一做的改变是添加一个<CR>到地图中,但那几乎不算什么(虽然这就是我的VIM脚本能力的范围... :)) - Mikeage
啊,是的!我测试了这个函数,然后在复制到stackoverflow时添加了映射。我会编辑我的答案。 - DrAl
尝试与 OP 大致相同的事情,但是在使用 vim 8.0 和我的自定义日志语法时,这个函数对我不起作用。我会进一步查看并报告,但到目前为止,看起来问题是解析“syn list”的输出。有人知道 vim 的“syn list”是否已更改吗? - timblaktu
区别在于我使用区域而不是匹配来定义我的组。然而,问题似乎是如何从 syn list <group> 的输出中提取语法组类型(例如匹配或区域)的任何正则表达式,因为这不包括文本“region”或“match”,只有“xxx”。@DrAl的正则表达式捕获了“xxx”后面空格之后的第一个关键字。 - timblaktu
syn list <group> 的输出通常不包括语法组类型('region'或'match')。对于'region'类型的组,除了args之外,'xxx'后面没有任何内容。因此,我猜想正确的一般解决方案需要分别处理match和region组。要识别区域类型,将需要执行类似捕获区域组类型所需的“start”和“end”args的操作。 - timblaktu

1
这是对@DrAl原始版本的修改,适用于匹配和区域类型的语法组。
" function to toggle conceal attribute for a syntax group
function! ToggleGroupConceal(group)
    " Get the existing syntax definition for specified group
    redir => syntax_def
    exe 'silent syn list' a:group
    redir END
    " Split into multiple lines
    let lines = split(syntax_def, "\n")
    " Clear the existing syntax definitions
    exe 'syn clear' a:group
    for line in lines
        " Only parse lines that contain the desired group's definition
        " (skip "--- Syntax items ---" line and 'links to' lines)
        if line =~ a:group
            "echo 'line = ' . line
            " need to handle match and region types separately since type
            " isn't specified for regions in the syn list output like it is
            " for matches.
            let type = substitute(line, '\v' . a:group . '\s+xxx\s+(\k+)\s+(.*)', '\1', '')
            if type == 'match'
                " get args as everything after 'xxx match'
                let args = substitute(line, '\v' . a:group . '\s+xxx\s+(\k+)\s+(.*)', '\2', '')
            else
                " get args as everything after 'xxx'
                let args = substitute(line, '\v' . a:group . '\s+xxx\s+(.*)', '\1', '')
                if args =~ 'start' && args =~ 'end'
                    let type = 'region'
                endif
            endif
            "echo '    type = ' . type
            "echo '    args = ' . args
            " Either add or remove 'conceal' from the definition
            if args =~ 'conceal'
                exe 'syn' type a:group substitute(args, ' conceal', '', '')
            else
                exe 'syn' type a:group args 'conceal'
            endif
        endif
    endfor
endfunction

0

您还可以更改文件的语法。对于我的json文件,我有一些隐藏规则,而对于我的javascript文件,我没有(例如,我隐藏引号和尾随逗号以使文件更易于阅读)。因此,当我想要查看整个文件时,我只需使用set syntax=javascript。您可能还可以进一步定义两种文件类型的规则集(例如,syslog.vim和syslog2.vim),其中1继承所有2并应用隐藏规则,然后您可以绑定一个键来在它们之间切换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接