diff --git a/autoload/vimlparser.vim b/autoload/vimlparser.vim index 4f6094b3..9e7f697f 100644 --- a/autoload/vimlparser.vim +++ b/autoload/vimlparser.vim @@ -2122,21 +2122,94 @@ endfunction " FIXME: validate argument function! s:VimLParser.parse_cmd_syntax() abort - let end = self.reader.getpos() + let subcmd = self.reader.read_alpha() + + " Read name + if subcmd ==# 'region' || subcmd ==# 'match' + call self.reader.read_white() + call self.reader.read_alpha() + endif + + let match_pattern = subcmd !=# 'match' + while s:TRUE let end = self.reader.getpos() + let pattern = s:FALSE + + " Read non-alpha let c = self.reader.peek() - if c ==# '/' || c ==# "'" || c ==# '"' + if self.ends_excmds(c) + if !match_pattern && (c ==# '|' || c ==# '"') + let pattern = s:TRUE + let match_pattern = s:TRUE + else + break + endif + elseif !s:isalpha(c) + if !match_pattern && !s:iswhite(c) + let pattern = s:TRUE + let match_pattern = s:TRUE + else + call self.reader.getn(1) + continue + endif + endif + + " Read arg + if !pattern + let arg_pos = self.reader.tell() + let arg = self.reader.read_alpha() + + if !match_pattern + \ && arg !=# 'keepend' + \ && arg !=# 'conceal' + \ && arg !=# 'cchar' + \ && arg !=# 'contained' + \ && arg !=# 'containedin' + \ && arg !=# 'nextgroup' + \ && arg !=# 'transparent' + \ && arg !=# 'skipwhite' + \ && arg !=# 'skipnl' + \ && arg !=# 'skipempty' + let match_pattern = s:TRUE + let pattern = s:TRUE + call self.reader.seek_set(arg_pos) + elseif subcmd ==# 'region' && (arg ==# 'start' || arg ==# 'skip' || arg ==# 'end') + if self.reader.peek() !=# '=' + continue + endif + call self.reader.getn(1) + let pattern = s:TRUE + elseif self.reader.peek() ==# '=' + " Read arg value + call self.reader.getn(1) + if arg ==# 'cchar' + call self.reader.getn(1) + else + while s:TRUE + call self.reader.read_alpha() + let c = self.reader.peek() + if c ==# ',' || c ==# '@' + call self.reader.getn(1) + else + break + endif + endwhile + endif + endif + endif + + " Read pattern + if pattern + let c = self.reader.peek() + if self.ends_excmds(c) && c !=# '|' && c !=# '"' + continue + endif call self.reader.getn(1) call self.parse_pattern(c) - elseif c ==# '=' - call self.reader.getn(1) - call self.parse_pattern(' ') - elseif self.ends_excmds(c) - break endif - call self.reader.getn(1) endwhile + let node = s:Node(s:NODE_EXCMD) let node.pos = self.ea.cmdpos let node.ea = self.ea diff --git a/js/vimlparser.js b/js/vimlparser.js index 1649caa1..d738a68e 100644 --- a/js/vimlparser.js +++ b/js/vimlparser.js @@ -2431,22 +2431,82 @@ VimLParser.prototype.parse_wincmd = function() { // FIXME: validate argument VimLParser.prototype.parse_cmd_syntax = function() { - var end = this.reader.getpos(); + var subcmd = this.reader.read_alpha(); + // Read name + if (subcmd == "region" || subcmd == "match") { + this.reader.read_white(); + this.reader.read_alpha(); + } + var match_pattern = subcmd != "match"; while (TRUE) { var end = this.reader.getpos(); + var pattern = FALSE; + // Read non-alpha var c = this.reader.peek(); - if (c == "/" || c == "'" || c == "\"") { - this.reader.getn(1); - this.parse_pattern(c); + if (this.ends_excmds(c)) { + if (!match_pattern && (c == "|" || c == "\"")) { + var pattern = TRUE; + var match_pattern = TRUE; + } + else { + break; + } } - else if (c == "=") { - this.reader.getn(1); - this.parse_pattern(" "); + else if (!isalpha(c)) { + if (!match_pattern && !iswhite(c)) { + var pattern = TRUE; + var match_pattern = TRUE; + } + else { + this.reader.getn(1); + continue; + } } - else if (this.ends_excmds(c)) { - break; + // Read arg + if (!pattern) { + var arg_pos = this.reader.tell(); + var arg = this.reader.read_alpha(); + if (!match_pattern && arg != "keepend" && arg != "conceal" && arg != "cchar" && arg != "contained" && arg != "containedin" && arg != "nextgroup" && arg != "transparent" && arg != "skipwhite" && arg != "skipnl" && arg != "skipempty") { + var match_pattern = TRUE; + var pattern = TRUE; + this.reader.seek_set(arg_pos); + } + else if (subcmd == "region" && (arg == "start" || arg == "skip" || arg == "end")) { + if (this.reader.peek() != "=") { + continue; + } + this.reader.getn(1); + var pattern = TRUE; + } + else if (this.reader.peek() == "=") { + // Read arg value + this.reader.getn(1); + if (arg == "cchar") { + this.reader.getn(1); + } + else { + while (TRUE) { + this.reader.read_alpha(); + var c = this.reader.peek(); + if (c == "," || c == "@") { + this.reader.getn(1); + } + else { + break; + } + } + } + } + } + // Read pattern + if (pattern) { + var c = this.reader.peek(); + if (this.ends_excmds(c) && c != "|" && c != "\"") { + continue; + } + this.reader.getn(1); + this.parse_pattern(c); } - this.reader.getn(1); } var node = Node(NODE_EXCMD); node.pos = this.ea.cmdpos; diff --git a/py/vimlparser.py b/py/vimlparser.py index 76e2d508..13e4ed43 100644 --- a/py/vimlparser.py +++ b/py/vimlparser.py @@ -1993,19 +1993,63 @@ def parse_wincmd(self): # FIXME: validate argument def parse_cmd_syntax(self): - end = self.reader.getpos() + subcmd = self.reader.read_alpha() + # Read name + if subcmd == "region" or subcmd == "match": + self.reader.read_white() + self.reader.read_alpha() + match_pattern = subcmd != "match" while TRUE: end = self.reader.getpos() + pattern = FALSE + # Read non-alpha c = self.reader.peek() - if c == "/" or c == "'" or c == "\"": + if self.ends_excmds(c): + if not match_pattern and (c == "|" or c == "\""): + pattern = TRUE + match_pattern = TRUE + else: + break + elif not isalpha(c): + if not match_pattern and not iswhite(c): + pattern = TRUE + match_pattern = TRUE + else: + self.reader.getn(1) + continue + # Read arg + if not pattern: + arg_pos = self.reader.tell() + arg = self.reader.read_alpha() + if not match_pattern and arg != "keepend" and arg != "conceal" and arg != "cchar" and arg != "contained" and arg != "containedin" and arg != "nextgroup" and arg != "transparent" and arg != "skipwhite" and arg != "skipnl" and arg != "skipempty": + match_pattern = TRUE + pattern = TRUE + self.reader.seek_set(arg_pos) + elif subcmd == "region" and (arg == "start" or arg == "skip" or arg == "end"): + if self.reader.peek() != "=": + continue + self.reader.getn(1) + pattern = TRUE + elif self.reader.peek() == "=": + # Read arg value + self.reader.getn(1) + if arg == "cchar": + self.reader.getn(1) + else: + while TRUE: + self.reader.read_alpha() + c = self.reader.peek() + if c == "," or c == "@": + self.reader.getn(1) + else: + break + # Read pattern + if pattern: + c = self.reader.peek() + if self.ends_excmds(c) and c != "|" and c != "\"": + continue self.reader.getn(1) self.parse_pattern(c) - elif c == "=": - self.reader.getn(1) - self.parse_pattern(" ") - elif self.ends_excmds(c): - break - self.reader.getn(1) node = Node(NODE_EXCMD) node.pos = self.ea.cmdpos node.ea = self.ea diff --git a/test/test_syncmd.ok b/test/test_syncmd.ok index f56098d7..4ba221d0 100644 --- a/test/test_syncmd.ok +++ b/test/test_syncmd.ok @@ -6,3 +6,10 @@ (excmd "syn match pythonError \"[&|]\\{2,}\" display") (excmd "syntax match qfFileName /^\\zs\\S[^|]\\+\\/\\ze[^|\\/]\\+\\/[^|\\/]\\+|/ conceal cchar=+") (excmd "syntax region jsString start=+\"+ skip=+\\\\\\(\"\\|$\\)+ end=+\"\\|$+ contains=jsSpecial,@Spell extend") +(excmd "syntax match testCchar conceal cchar=/ /pattern/") +(excmd "syntax match testArgValue contained containedin=parentGroup /pattern/") +(excmd "syntax match testArgsAfterPattern /pattern/ contained containedin=parentGroup") +(excmd "syntax match testPatternDelim contained +pattern+") +(excmd "syntax match testAlphaPatternDelim contained ApatternA") +(excmd "syntax match testPipePatternDelim contained |pattern|") +(excmd "syntax match testQuotePatternDelim contained \"pattern\"") diff --git a/test/test_syncmd.vim b/test/test_syncmd.vim index d25dea04..0e45dea0 100644 --- a/test/test_syncmd.vim +++ b/test/test_syncmd.vim @@ -5,3 +5,10 @@ syntax list GroupName syn match pythonError "[&|]\{2,}" display syntax match qfFileName /^\zs\S[^|]\+\/\ze[^|\/]\+\/[^|\/]\+|/ conceal cchar=+ syntax region jsString start=+"+ skip=+\\\("\|$\)+ end=+"\|$+ contains=jsSpecial,@Spell extend +syntax match testCchar conceal cchar=/ /pattern/ +syntax match testArgValue contained containedin=parentGroup /pattern/ +syntax match testArgsAfterPattern /pattern/ contained containedin=parentGroup +syntax match testPatternDelim contained +pattern+ +syntax match testAlphaPatternDelim contained ApatternA +syntax match testPipePatternDelim contained |pattern| +syntax match testQuotePatternDelim contained "pattern"