Monday, June 6, 2011

Re: syntax highlighting: 'good' syntax item becomes 'bad' after unrelated change in file

On 3 Jun 2011, at 00:26, Ben Schmidt wrote:

>> I'm experiencing a problem where a valid piece of syntax (a variable
>> declaration) sudden;y changes state (becomes an (invalid) procedure
>> call) following the insertion elsewhere in the file of an indexed
>> assignment.
> [...]
>> Here is a simple test file that shows the problem, which shows that
>> the problem exhibits itself even when the assignment that provokes the
>> error is inside a comment.
>
> This indeed seems very strange. With sync fromstart, I can't imagine why
> this would happen, particularly in the case where it's in a comment, as
> it's a completely irrelevant syntax item.
>
> Maybe it's a bug in Vim's syntax engine. We seem to have been finding a
> few of those recently.
>
>> 1. sclProcedureCall is declared earlier in the syntax file than
>> sclStringDeclaration.
>
> Does that matter? Did you try swapping them? If it doesn't matter,
> probably not worth mentioning!
>
>> 2. I use 'foldmethod=syntax' and use folding aggressively to detect
>> bad syntax.
>
> I don't think this is relevant either. With foldmethod=manual the
> problem still occurs.
>
>> 3. sclStringDeclaration is a region with 'start=/\ze\<string\>/
>> skip=/\(\<string\|)\},\)\_s*/ end=/,/ end=/$/'
>
> Seems a bit different to that in the actual syntax file. The one in the
> syntax file seems a bit more sensible, though.
>
>> 4. The reason for the \ze in the 'start' pattern is to make the folds
>> work for declarations and assignments.
>
> OK. That is helpful to know.
>
>> 5. An indexed assignment is modelled as a procedure call - it has the
>> same semantics as a procedure call returning a reference has.
>
> OK.
>
>> Can anyone suggest what might be causing this problem?
>
> Not yet. Your minimal test data was wonderful, though I've managed to
> minimise it even more. This is enough to show the problem:
>
> string ( 80 ) message := ""
> @ )=
>
> In fact, the problem is easier to see there, because Vim seems to update
> more quickly as you edit. Modifying the comment causes the highlighting
> to change 'in realtime'.
>
> I'm still a bit baffled.
>
> Perhaps work on this even smaller example and see if you can minimise
> the syntax file. This example doesn't require a procedure body or
> anything like that, so should be able to work with a really small syntax
> file, and that should help us pinpoint either the problem with the
> syntax file or the bug in Vim.

Here's the smallest sample syntax file that I've so far managed to exhibit both behaviours (correct and incorrect), minus the highlight groups.

========================================================================
syntax case ignore

syntax sync fromstart

syntax match sclError /\S\+/
syntax match sclError /\k\+/

syntax match sclIdentifier contained /\%(\<string\>\)\@!\<\a\k*\>/

syntax match sclNumber /\<\d\+\>/ contained

syntax match sclStringEsc1 contained
\ /""/
syntax match sclStringEsc2 contained
\ /''/

syntax match sclStringConst
\ /""/

syntax match sclParens contained /[([{})\]]/

syntax match sclComma contained /,/

syntax match sclAssignOp contained /:\==/
"
" Now start building syntactic constructs from them
"
syntax region sclParen fold keepend extend contains=@sclExpr,@sclAlwaysValid
\ matchgroup=sclParens
\ start=/[[({]/ end=/[])}]/

syntax region sclProcedureCall keepend extend contains=sclIdentifier,sclActualParameters,@sclAlwaysValid
\ start=/\<\a\k*\>\ze\s*[[({]/
\ end=/[])}]\zs/

syntax match sclType /\<string\>/

syntax region sclStringAssignment fold keepend extend
\ contains=sclProcedureCall,sclAssignOp,@sclStringExpr,
\ @sclAlwaysValid
\ start=/\<\a\k*\>\s*[[({]\_.\{-}[])}]\s*:\==\_s*/
\ skip=/\(:\==\|+\|-\|\<and\>\|\<or\>\|\<after\|\<before\>\)\_s*/
\ end=/\ze,/ end=/\ze;/ end=/$/
syntax region sclStringAssignment fold keepend extend contains=sclIdentifier,
\ sclAssignOp,@sclStringExpr,@sclAlwaysValid
\ start=/\<\a\k*\>\s*\<is\>\_s*/
\ skip=/\(\<is\>\|+\|-\|\<and\>\|\<or\>\|\<after\|\<before\>\)\_s*/
\ end=/\ze,/ end=/\ze;/ end=/$/
syntax region sclStringAssignment fold keepend extend contains=sclIdentifier,
\ sclAssignOp,@sclStringExpr,@sclAlwaysValid
\ start=/\<\a\k*\>\s*:\==\_s*/
\ skip=/\(:\==\|+\|-\|\<and\>\|\<or\>\|\<after\|\<before\>\)\_s*/
\ end=/\ze,/ end=/\ze;/ end=/$/

syntax region sclStringAdvisoryLength keepend extend contains=@sclIntExpr,
\ @sclAlwaysValid
\ matchgroup=sclParens
\ start=/(/
\ matchgroup=sclParens
\ end=/)\_s*/

syntax region sclActualParameters fold keepend contains=sclComment,@sclExpr
\ matchgroup=sclParens
\ start=/[[({]/
\ matchgroup=sclParens
\ end=/[])}]/

syntax region sclStringDeclaration fold keepend extend contains=sclType,
\ sclStringAdvisoryLength,sclStringAssignment,sclIdentifier,
\ sclComma,@sclAlwaysValid
\ start=/\<string\>\_s*\%((\_.\{-})\_s*\)\=/
\ skip=/\(\<string\>\|)\|,\)\_s*/
\ matchgroup=sclSemiColon
\ end=/;/ end=/$/

syntax region sclComment
\ extend
\ start=/@/ end=/\n/ end=/@/
syntax region sclComment contained
\ extend
\ start=/@/ end=/\n/ end=/@/
"
" These clusters group useful types together
"
syntax cluster sclIntOperand contains=sclLocalName,sclIdentifier,sclNumber,
\ sclProcedureCall,sclArithOp,sclParen

syntax cluster sclStringOperand contains=sclIdentifier,sclStringConst,
\ sclProcedureCall,sclStringOp,sclParen

syntax cluster sclIntExpr contains=@sclIntOperand,sclRelOp,sclArithOp

syntax cluster sclStringExpr contains=@sclStringOperand,sclRelOp,sclStringOp

syntax cluster sclExpr contains=@sclStringExpr,@sclSStringExpr,@sclIntExpr,
\ @sclBoolExpr,sclAlwaysValid

syntax cluster sclAlwaysValid contains=sclComment,sclError
========================================================================

If I delete the 'syntax region sclStringAssignment' that contains the sclProcedureCall, then it stops misbehaving. This is, in fact, probably a more realistic definition, too, as I was probably trying to overload the clause by one straw too many - while you can assign a reference a value, you can't do it as part of a string declaration; I can probably live wigh leaving that as an sclAnonymousAssignment.

So, while I believe that I have found a bug in the syntax engine, I now have something that looks, from a distance, like a work-around. Despite that, I'm prepared to assist in someone else's debugging of this, if I can.

Regards, Andy

--
Andrew Long
andrew dot long at mac dot com

No comments:

Post a Comment