Skip to content

Improve :syntax command parsing #183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 81 additions & 8 deletions autoload/vimlparser.vim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
80 changes: 70 additions & 10 deletions js/vimlparser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
60 changes: 52 additions & 8 deletions py/vimlparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions test/test_syncmd.ok
Original file line number Diff line number Diff line change
Expand Up @@ -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\"")
7 changes: 7 additions & 0 deletions test/test_syncmd.vim
Original file line number Diff line number Diff line change
Expand Up @@ -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"