13

So a while ago I had decided to learn more about how vim's syntax files work, by creating my own. My goal was to create a simple syntax file that would split the file into 3 parts based on delimiters, and include pre-existing syntax files for two of those three parts.

This is an example file:

Some text, unstyled

==================== Log
(Output of git log)

==================== Diff
(Output of diff)

The goal was to apply git.vim to just the log, and diff.vim to just the diff, excluding the two headers.1 This is what my syntax file ended up looking like:

if exists("b:current_syntax")
    finish
endif

syntax include @gitlog $VIMRUNTIME/syntax/git.vim
syntax region GitLog
    \ start=/^===* Log/
    \ end=/^===* Diff/
    \ contains=@gitlog

syntax include @gitdiff $VIMRUNTIME/syntax/diff.vim
syntax region Diff
    \ start=/^===* Diff/
    \ end=/\n\n/
    \ contains=@gitdiff

let b:current_syntax = "logdiff"

Note how end on the first region is the same as start on the second region. This doesn't work; the second region is apparently not detected and syntax highlighting is not applied.

I can make it work by loosening the regex to end=/^===/ and start=/=== Diff/, so that the regions don't touch, but don't particularly like that - it's less obvious that one is supposed to start where the other ends.

Is there a way to allow two region definitions to touch the same characters (and/or make the matching edges exclusive instead of inclusive) or am I stuck with the looser regex?

1(Yes, I know git.vim includes diff.vim, but this was a learning exercise)

  • I think this should be possible either by using an "Region End" offset to make the edges exclusive (see :h :syn-pattern-offset), or by changing the GitLog region to extend to the end of the file, and the Diff region to be entirely inside the GitLog region. I couldn't get either of these methods to work, though. – Rich Feb 9 '15 at 15:43
  • Testing it was made somewhat harder by the fact that--as you note--git.vim already highlights diff output. Might be an idea to alter your example accordingly! – Rich Feb 9 '15 at 15:45
4

You can tell Vim regex engine where to start next match.

So far I know there are 2 ways:

  1. Use \ze to set the end of a match:

    syntax region GitLog
        \ start=/^===* Log/
        \ end=/\ze^===* Diff/
        \ contains=@gitlog
    
  2. Use positive lookahead, \@=, which is called zero-width matching in Vim:

    syntax region GitLog
        \ start=/^===* Log/
        \ end=/\(^===* Diff\)\@=/
        \ contains=@gitlog
    

So the end-of-match "pointer" kind of rewinds to the beginning of ^===* Diff line, such that region Diff's start will start its matching at ^===* Diff line again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.