How to limit syntax rule match to the outside of a region?

27 views
Skip to first unread message

Vadim Zeitlin

unread,
Feb 29, 2012, 11:12:14 AM2/29/12
to vim...@googlegroups.com
Hello,

I'm trying to write a syntax file for a language that looks like this:

var1 = foo;
block name {
var2 = bar;
var3 = true;
}

The problem I have is that some variables can occur only at the global
scope, i.e. outside of any block, while others can only occur inside a
block. Ideally I'd like to highlight occurrences of the variables of the
first kind inside a block and that of the second kind at global scope as
errors but I'd also settle for not highlighting them at all. Unfortunately
I don't see how to do it. Currently I have (simplified):

syn keyword GlobalVar var1;
syn keyword BlockVar var2 contained;
syn region Block start="{" end="}" contains=ALLBUT,GlobalVar transparent

which more or less works for "var2" but "var1" is highlighted even if it
occurs inside the block. I'd like to understand why does this happen and
how to avoid it -- as well as implementing highlighting of the variables in
the wrong places as errors. Does anybody know how could this be done?


Also, on a similar topic, what is the best way to highlight the values of
the variables? I.e. for some of them only a limited set of values is valid,
for example there are boolean variables which can only be "true" or "false".
Currently I do

syn keyword BoolValue false true contained
syn match BlockVar "var3 *=.*$"he=s+4 contains BoolValue

but this is quite inconvenient because I need to compute the highlighting
index (4 above) for each variable and just doesn't look right. I'm sure
there must be a better way of doing what I want but what could it be?

Thanks in advance for your help!
VZ

Ben Fritz

unread,
Mar 1, 2012, 11:01:57 AM3/1/12
to vim_use


On Feb 29, 10:12 am, Vadim Zeitlin <vz-...@zeitlins.org> wrote:
>  Hello,
>
>  I'm trying to write a syntax file for a language that looks like this:
>
>         var1 = foo;
>         block name {
>                 var2 = bar;
>                 var3 = true;
>         }
>
> The problem I have is that some variables can occur only at the global
> scope, i.e. outside of any block, while others can only occur inside a
> block. Ideally I'd like to highlight occurrences of the variables of the
> first kind inside a block and that of the second kind at global scope as
> errors but I'd also settle for not highlighting them at all. Unfortunately
> I don't see how to do it. Currently I have (simplified):
>
>         syn keyword GlobalVar   var1;
>         syn keyword BlockVar    var2 contained;
>         syn region  Block       start="{" end="}" contains=ALLBUT,GlobalVar transparent
>

This code works fine for me, if I remove the trailing ';' from the
keyword definitions. Vim commands just go to the end of the line, you
don't need to (and can't) terminate them with ';'.

> which more or less works for "var2" but "var1" is highlighted even if it
> occurs inside the block. I'd like to understand why does this happen and
> how to avoid it -- as well as implementing highlighting of the variables in
> the wrong places as errors. Does anybody know how could this be done?
>

To highlight in the wrong places as errors, I adapted your script as
follows:

syn keyword VarError var1 var2
syn keyword GlobalVar var1
syn keyword BlockVar var2 contained
syn region Block start="{" end="}"
contains=ALLBUT,GlobalVar transparent

hi GlobalVar guibg=blue
hi BlockVar guibg=yellow
hi VarError guibg=red

Note that rules defined later take precedence over rules defined
earlier. Thus VarError always matches, but GlobarVar or BlockVar
overrides it if the variable is in the correct place. Another way to
do it would be to exactly define things with a Var1Error and Var2Error
which only match in the *wrong* spot, but the way I've done it lets
you be lazier.

Note, var3 is not highlighted. Did you want a syn match instead of a
syn keyword, to allow multiple var names to match with one rule? If
you use :syn match instead, do be aware that :syn keyword always takes
precedence over :syn match or :syn region.

>  Also, on a similar topic, what is the best way to highlight the values of
> the variables? I.e. for some of them only a limited set of values is valid,
> for example there are boolean variables which can only be "true" or "false".
> Currently I do
>
>         syn keyword BoolValue   false true contained
>         syn match   BlockVar    "var3 *=.*$"he=s+4 contains BoolValue
>
> but this is quite inconvenient because I need to compute the highlighting
> index (4 above) for each variable and just doesn't look right. I'm sure
> there must be a better way of doing what I want but what could it be?
>

Do you want to highlight var3 only if it is being assigned a true/
false value? Use a zero-width look-ahead, or end the match explicitly
with \ze. See :help /\ze and :help /\@= and :help /zero-width.

Do you want to highlight the "true" or "false"? If this is what you
want, you probably want to use a "nextgroup" definition.

You can probably use both of these methods together.

Vadim Zeitlin

unread,
Mar 10, 2012, 8:36:29 AM3/10/12
to vim_use
Hello Ben and thanks for your very helpful reply -- and also sorry
for the
delay with my own one, I've unfortunately got sidetracked by other
stuff
and only could back to this now.

On Mar 1, 5:01pm Ben Fritz wrote:
> On Feb 29, 10:12 am, Vadim Zeitlin <vz-...@zeitlins.org> wrote:
> > Hello,
> >
> > I'm trying to write a syntax file for a language that looks like this:
> >
> > var1 = foo;
> > block name {
> > var2 = bar;
> > var3 = true;
> > }
> >
> > The problem I have is that some variables can occur only at the global
> > scope, i.e. outside of any block, while others can only occur inside a
> > block. Ideally I'd like to highlight occurrences of the variables of the
> > first kind inside a block and that of the second kind at global scope as
> > errors but I'd also settle for not highlighting them at all. Unfortunately
> > I don't see how to do it. Currently I have (simplified):
> >
> > syn keyword GlobalVar var1;
> > syn keyword BlockVar var2 contained;
> > syn region Block start="{" end="}" contains=ALLBUT,GlobalVar
transparent
> >
>
> This code works fine for me, if I remove the trailing ';' from the
> keyword definitions. Vim commands just go to the end of the line, you
> don't need to (and can't) terminate them with ';'.

Yes, this was a mistake subconsciously (too many years of C/C++
programming taking its toll, I guess...) introduced when I was typing
in
the simplified version, sorry about this as well. The real file
version
(at https://github.com/vslavik/bakefile/blob/master/extras/vim/bkl.vim)
doesn't have them. But the worst is not this but the fact that it now
does
work for me as well, even although I didn't really change anything. I
don't
understand what happened but my guess would be that I should have
restarted
my Vim after modifying the syntax file as perhaps some previously
defined
syntax matches hadn't been cleared. So this one was my mistake as
well...


> To highlight in the wrong places as errors, I adapted your script as
> follows:
>
> syn keyword VarError var1 var2
> syn keyword GlobalVar var1
> syn keyword BlockVar var2 contained
> syn region Block start="{" end="}" contains=ALLBUT,GlobalVar
transparent
>
> hi GlobalVar guibg=blue
> hi BlockVar guibg=yellow
> hi VarError guibg=red
>
> Note that rules defined later take precedence over rules defined
> earlier. Thus VarError always matches, but GlobarVar or BlockVar
> overrides it if the variable is in the correct place. Another way to
> do it would be to exactly define things with a Var1Error and Var2Error
> which only match in the *wrong* spot, but the way I've done it lets
> you be lazier.

Thanks a lot, that's an excellent advice and I especially like it
because
it's so simple. The need to list all the names twice is a bit annoying
but
using Var1Error and Var2Error wouldn't really help that much as each
variable would still appear twice in the file and now you'd have to
think
where to put it, too. Instead, I'm thinking about automatically
generating
(part) of the syntax file using the list of variable names which can
be
output by the program using these files itself.

Anyhow, thanks again for the idea!


> > Also, on a similar topic, what is the best way to highlight the values of
> > the variables? I.e. for some of them only a limited set of values is valid,
> > for example there are boolean variables which can only be "true" or "false".
> > Currently I do
> >
> > syn keyword BoolValue false true contained
> > syn match BlockVar "var3 *=.*$"he=s+4 contains BoolValue
> >
> > but this is quite inconvenient because I need to compute the highlighting
> > index (4 above) for each variable and just doesn't look right. I'm sure
> > there must be a better way of doing what I want but what could it be?
>
> Do you want to highlight var3 only if it is being assigned a true/
> false value? Use a zero-width look-ahead, or end the match explicitly
> with \ze. See :help /\ze and :help /\@= and :help /zero-width.
>
> Do you want to highlight the "true" or "false"? If this is what you
> want, you probably want to use a "nextgroup" definition.
>
> You can probably use both of these methods together.

I actually wanted to do the latter but your pointer to \ze was still
very
helpful too as I could get rid of the "he=s+N" construction thanks to
it,
i.e. now I simply match "var3\ze *=" instead which is much simpler,
thanks!

But the most useful hint you gave me was about "nextgroup", I've
somehow
managed to completely overlook it while reading the syntax chapter of
Vim
manual. This is exactly what I needed, i.e. a way to "chain" 2 syntax
items
together.

So to achieve my main goal, i.e. to highlight "true" or "false" as
possible correct values for this variable I now simply do this:

syn keyword BoolValue false true contained
syn region BoolValueRgn matchgroup=Normal start="= *" end=";" \
contains=BoolValue contained
syn match BoolVar "var3\ze *=" nextgroup=BoolValueRgn skipwhite

which seems to work well (but please let me know if you notice any
problems
with it).

Thanks again for your help!
VZ
Reply all
Reply to author
Forward
0 new messages