dot_files/vim-plugins/bundle/xptemplate/autoload/xpt/parser.vim

583 lines
18 KiB
VimL
Raw Permalink Normal View History

2017-07-09 00:26:06 +03:00
exec xpt#once#init
let s:oldcpo = &cpo
set cpo-=< cpo+=B
let s:log = xpt#debug#Logger( 'debug' )
exe XPT#importConst
let s:KEYTYPE_MAP = { '.':'onfocus', '.def':'onfocus', '.ontype':'live', '.onchange':'live', }
let s:KEYTYPE_TO_DICT = { 'pre':'preValues', 'live':'liveFilters', 'onfocus':'onfocusFilters', }
runtime plugin/xptemplate.vim
fun s:CompileSnippetFile(fn)
if a:fn =~ '\V.xpt.vimc\$' || !filereadable( a:fn )
return
endif
let lines = readfile(a:fn)
let lines = xpt#parser#Compact(lines)
let lines = xpt#parser#CompileCompacted(lines)
call writefile( lines, a:fn . 'c' )
endfunction
fun! xpt#parser#Compile(fn)
let compiledFn = a:fn . 'c'
let ctime = getftime(a:fn)
if !filereadable(compiledFn) || getftime(compiledFn) < ctime || g:xptemplate_always_compile
call s:CompileSnippetFile(a:fn)
else
endif
endfunction
fun! xpt#parser#Compact(lines)
let compacted = []
let iSnipPart = match( a:lines, '\V\^XPT\s' )
if iSnipPart < 0
let iSnipPart = len(a:lines)
endif
let [i,len] = [0 - 1,iSnipPart - 1]
while i < len | let i += 1
let l = a:lines[i]
if l != '' && l !~ '\v^"[^"]*$'
call add(compacted,l)
endif
endwhile
let [s,e,lastNonblank] = [-1,-1,100000]
let [i,len] = [iSnipPart - 1,len(a:lines) - 1]
while i < len | let i += 1
let l = a:lines[i]
if l == '' || l =~ '\v^"[^"]*$'
let lastNonblank = min([lastNonblank,i - 1])
continue
endif
if l =~# '\V\^..XPT\>'
if s == -1
let [s,e,lastNonblank] = [-1,-1,100000]
continue
else
let compacted += a:lines[s : i - 1]
let [s,e,lastNonblank] = [-1,-1,100000]
endif
elseif l =~# '\V\^XPT\>'
if s == -1
let [s,lastNonblank] = [i,i]
else
let e = min([i - 1,lastNonblank])
let compacted += a:lines[s : e]
let [s,e,lastNonblank] = [i,-1,100000]
endif
else
let lastNonblank = i
endif
endwhile
if s != -1
let compacted += a:lines[s : min([lastNonblank,i])]
endif
return compacted
endfunction
fun! xpt#parser#CompileCompacted(lines)
let rst = []
let lines = a:lines
let iSnipPart = match( lines, '\V\^XPT\s' )
if iSnipPart < 0
return lines
endif
if iSnipPart > 0
let rst += lines[: iSnipPart - 1]
let lines = lines[iSnipPart :]
endif
let [i,len] = [0,len(lines)]
call xpt#indent#IndentToTab(lines)
let s = i
while i < len-1 | let i += 1
let v = lines[i]
if v =~# '\V\^XPT\>'
let ll = xpt#parser#CompileSnippet(lines[s : i - 1])
let rst += [ll]
let s = i
elseif v =~# '\V\^\\XPT'
let lines[i] = v[1 :]
endif
endwhile
if i >= s
let ll = xpt#parser#CompileSnippet(lines[s : i])
let rst += [ll]
endif
return rst
endfunction
fun! xpt#parser#CompileSnippet(lines)
let lines = a:lines
let snippetLines = []
let setting = xpt#st#New()
let l0 = lines[0]
let pos = match( l0, '\VXPT\s\+\S\+\.\{-}\zs\s' . s:nonEscaped . '"' )
if pos >= 0
let [setting.rawHint, lines[0]] = [ matchstr( l0[ pos + 1 + 1 : ], '\v\S.*' ), l0[ : pos ] ]
endif
let [ x, snippetName; snippetParameters ] = split(lines[0], '\V'.s:nonEscaped.'\s\+')
for pair in snippetParameters
let name = matchstr(pair, '\V\^\[^=]\*')
let value = pair[len(name) :]
let value = value[0:0] == '=' ? xpt#util#UnescapeChar(value[1:], ' ') : 1
let setting[name] = value
endfor
let start = 1
let len = len(lines)
while start < len
let command = matchstr( lines[ start ], '\V\^XSETm\?\ze\s' )
if command != ''
let [key,val,start] = s:GetXSETkeyAndValue(lines,start)
if key == ''
let start += 1
continue
endif
let [keyname,keytype] = xpt#parser#GetKeyType(key)
call s:HandleXSETcommand(setting,command,[keyname,keytype,val])
elseif lines[start] =~# '^\\XSET' " escaped XSET or XSETm
let snippetLines += [lines[start][1:]]
else
call add(snippetLines,lines[start])
endif
let start += 1
endwhile
call xpt#st#Simplify(setting)
if has_key( setting, 'alias' )
return printf( 'call XPTemplateAlias(%s,%s,%s)', string(snippetName),string(setting.alias),string(setting))
else
return printf( 'call XPTdefineSnippet(%s,%s,%s)', string(snippetName),string(setting),string(snippetLines))
endif
endfunction
fun! xpt#parser#Include(...)
call xpt#parser#Load(a:000,1)
endfunction
fun! xpt#parser#Embed(...)
call xpt#parser#Load(a:000,0)
endfunction
fun! xpt#parser#Load(fns,inherit)
let scope = b:xptemplateData.snipFileScope
let ftscope = b:xptemplateData.filetypes[scope.filetype]
let saved_inherit = scope.inheritFT
let scope.inheritFT = a:inherit
let fns = xpt#util#Flatten(a:fns)
for f in fns
if a:inherit && xpt#ftscope#IsSnippetLoaded(ftscope,f)
continue
endif
call xpt#snipfile#Push()
exe 'runtime! ftplugin/' . f . '.xpt.vim'
call xpt#snipfile#Pop()
endfor
let scope.inheritFT = saved_inherit
endfunction
fun! xpt#parser#SetVar(nameSpaceValue)
let x = b:xptemplateData
let ftScope = x.filetypes[x.snipFileScope.filetype]
let name = matchstr(a:nameSpaceValue, '^\S\+\ze')
if name == ''
return
endif
let val = matchstr(a:nameSpaceValue, '\s\+\zs.*')
if val =~ '^''.*''$'
let val = val[1:-2]
else
let val = substitute( val, '\\ ', " ", 'g' )
endif
let val = substitute( val, '\\n', "\n", 'g' )
let priority = x.snipFileScope.priority
if !has_key(ftScope.varPriority,name) || priority <= ftScope.varPriority[name]
let [ftScope.funcs[name],ftScope.varPriority[name]] = [val,priority]
endif
endfunction
fun! xpt#parser#SnipSet(dictNameValue)
let x = b:xptemplateData
let snipScope = x.snipFileScope
let [ dict, nameValue ] = split( a:dictNameValue, '\V.', 1 )
let name = matchstr( nameValue, '^.\{-}\ze=' )
let value = nameValue[len(name) + 1 :]
let snipScope[dict][name] = value
endfunction
fun! xpt#parser#loadSpecialFiletype(ft)
let x = b:xptemplateData
let ft = a:ft
if has_key(x.filetypes,ft)
return
endif
if ft == 'unknown'
call xpt#parser#loadSnippetFile( 'unknown/unknown' )
else
call xpt#parser#InitSnippetFile( '~~/xpt/pseudo/ftplugin/' . ft . '/' . ft . '.xpt.vim' )
call XPTinclude( '_common/common' )
call XPTfiletypeInit()
endif
call XPTparseSnippets()
endfunction
fun! xpt#parser#loadSnippetFile(rel_snip)
exe 'runtime! ftplugin/' . a:rel_snip . '.xpt.vim'
call XPTfiletypeInit()
endfunction
fun! s:AssignSnipFT(filename)
let x = b:xptemplateData
let filename = substitute( a:filename, '\\', '/', 'g' )
if filename =~ 'unknown.xpt.vim$'
return 'unknown'
endif
let ftFolder = matchstr( filename, '\V/ftplugin/\zs\[^\\]\+\ze/' )
if empty(x.snipFileScopeStack)
if filename =~ '\V\<pseudo\>/'
return ftFolder
endif
if &filetype =~ '\<' . ftFolder . '\>' " sub type like 'xpt.vim'
let ft = &filetype
else
let ft = 'not allowed'
endif
else
if x.snipFileScopeStack[-1].inheritFT || ftFolder =~ '\V\^_'
if !has_key( x.snipFileScopeStack[ -1 ], 'filetype' )
throw 'parent may has no XPTemplate command called :' . a:filename
endif
let ft = x.snipFileScopeStack[-1].filetype
else
let ft = ftFolder
endif
endif
return ft
endfunction
fun! xpt#parser#InitSnippetFile(filename,...)
if ! xpt#option#lib_filter#Match(a:filename)
return 'finish'
endif
if !exists("b:xptemplateData")
call XPTemplateInit()
endif
let x = b:xptemplateData
let filetypes = x.filetypes
let snipScope = xpt#snipfile#New(a:filename)
let snipScope.filetype = s:AssignSnipFT(a:filename)
let x.snipFileScope = snipScope
let ft = snipScope.filetype
if ft == 'not allowed'
call s:log.Info( "not allowed:" . a:filename )
return 'finish'
endif
if ! has_key(filetypes,ft)
let filetypes[ft] = xpt#ftscope#New()
endif
let ftScope = filetypes[ft]
if xpt#ftscope#CheckAndSetSnippetLoaded(ftScope,a:filename)
return 'finish'
endif
for pair in a:000
let kv = split( pair, '=', 1 )
let key = kv[0]
let val = join( kv[ 1 : ], '=' )
if key =~ 'prio\%[rity]'
call XPTemplatePriority(val)
elseif key =~ 'mark'
call XPTemplateMark(val[0 : 0],val[1 : 1])
elseif key =~ 'key\%[word]'
call XPTemplateKeyword(val)
endif
endfor
return 'doit'
endfunction
fun! xpt#parser#SnippetFileInit_for_compiled(filename,...)
if !filereadable(a:filename)
return call( function( 'xpt#parser#InitSnippetFile' ), [ a:filename ] + a:000 )
endif
if a:filename =~ '\V.xpt.vim\$'
call xpt#parser#Compile(a:filename)
exe 'so' a:filename . 'c'
return 'finish'
else
return call( function( 'xpt#parser#InitSnippetFile' ), [ a:filename ] + a:000 )
endif
endfunction
fun! xpt#parser#LoadSnippets()
let fts = split( &filetype, '\V.', 1 )
call filter( fts, 'v:val!=""' )
for ft in fts
call xpt#parser#LoadFTSnippets(ft)
endfor
endfunction
fun! s:RTP()
let rtps = split( &runtimepath, ',' )
call filter( rtps, 'v:val!=""' )
let rtps += [ g:XPT_PATH . '/xptsnippets', g:XPT_PATH . '/personal' ]
let rtpath = join( rtps, ',' )
return rtpath
endfunction
fun! xpt#parser#LoadFtDetectors(ft)
let namePattern = a:ft =~ '/' ? a:ft : a:ft . '/*'
let rtpath = s:RTP()
let ftdetectfiles = split( globpath( rtpath, 'ftplugin/' . namePattern . '.ftdetect.vim' ), "\n" )
for fn in ftdetectfiles
exe 'so' fn
endfor
endfunction
fun! xpt#parser#LoadFTSnippets(ft)
let namePattern = a:ft =~ '/' ? a:ft : a:ft . '/*'
let rtpath = s:RTP()
let ftdetectfiles = split( globpath( rtpath, 'ftplugin/' . namePattern . '.ftdetect.vim' ), "\n" )
for fn in ftdetectfiles
exe 'so' fn
endfor
let snipfiles = split( globpath( rtpath, 'ftplugin/' . namePattern . '.xpt.vim' ), "\n" )
for fn in snipfiles
let compiled = fn . 'c'
if !filereadable(compiled) || getftime(compiled) < getftime(fn) || g:xptemplate_always_compile
call xpt#parser#Compile(fn)
exe 'so' compiled
endif
endfor
endfunction
fun! xpt#parser#GetKeyType(rawKey)
let keytype = matchstr(a:rawKey, '\V'.s:nonEscaped.'|\zs\.\{-}\$')
if keytype == ""
let keytype = matchstr(a:rawKey, '\V'.s:nonEscaped.'.\zs\.\{-}\$')
endif
let keyname = keytype == "" ? a:rawKey : a:rawKey[ 0 : - len(keytype) - 2 ]
let keyname = substitute(keyname, '\V\\\(\[.|\\]\)', '\1', 'g')
return [keyname,keytype]
endfunction
let s:KEY_NAME = 0
let s:KEY_TYPE = 1
let s:VALUE = 2
let s:stHandler = {}
fun! s:stHandler.ComeFirst(setting,cmdArgs)
let a:setting.comeFirst = xpt#util#SplitWith( a:cmdArgs[ s:VALUE ], ' ' )
endfunction
fun! s:stHandler.ComeLast(setting,cmdArgs)
let a:setting.comeLast = xpt#util#SplitWith( a:cmdArgs[ s:VALUE ], ' ' )
endfunction
fun! s:stHandler.postQuoter(setting,cmdArgs)
let pq = split( a:cmdArgs[ s:VALUE ], ',' )
let a:setting.postQuoter = { 'start' : pq[0], 'end' : pq[1] }
endfunction
let s:stHandler.PostQuoter = s:stHandler.postQuoter
let s:keytypeHandler = {}
fun! s:keytypeHandler.repl(setting,cmdArgs)
let a:setting.replacements[a:cmdArgs[s:KEY_NAME]] = a:cmdArgs[s:VALUE]
endfunction
fun! s:keytypeHandler.map(setting,cmdArgs)
let [kn,kt,val] = a:cmdArgs
let mp = a:setting.mappings
if !has_key(mp,kn)
let mp[ kn ] = { 'saver' : xpt#msvr#New( 1 ), 'keys' : {} }
endif
let key = matchstr( val, '\V\^\S\+\ze\s' )
let mapping = matchstr( val, '\V\s\+\zs\.\*' )
call xpt#msvr#Add( mp[ kn ].saver, 'i', key )
let mp[kn].keys[key] = xpt#flt#NewSimple(0,mapping)
endfunction
fun! s:keytypeHandler.post(setting,cmdArgs)
let [kn,kt,val] = a:cmdArgs
let val = xpt#ph#AlterFilterByPHName(kn,val)
let a:setting.postFilters[kn] = xpt#flt#NewSimple(0,val)
endfunction
fun! s:HandleXSETcommand(setting,command,cmdArgs)
let [kn,kt,val] = a:cmdArgs
let kt = get( s:KEYTYPE_MAP, '.' . kt, kt )
let fcon = {}
if has_key(s:stHandler,kn)
let fcon.f = s:stHandler[kn]
call fcon.f(a:setting,[kn,kt,val])
elseif has_key(s:KEYTYPE_TO_DICT,kt)
let dicName = s:KEYTYPE_TO_DICT[kt]
let a:setting[dicName][kn] = xpt#flt#NewSimple(0,val)
elseif kn =~ '\V\^$'
let a:setting.variables[kn] = val
elseif has_key(s:keytypeHandler,kt)
let fcon.f = s:keytypeHandler[kt]
call fcon.f(a:setting,[kn,kt,val])
else
throw "unknown key name or type:" . kn . ' ' . kt
endif
endfunction
fun! xpt#parser#LoadSnippetToParseList(fn)
let lines = readfile(a:fn)
let i = match( lines, '\V\^XPTemplateDef' )
if i == -1
let i = match( lines, '\V\^XPT\s' ) - 1
endif
if i < 0
return
endif
let lines = lines[i :]
let x = b:xptemplateData
let x.snippetToParse += [ { 'snipFileScope' : x.snipFileScope, 'lines' : lines } ]
endfunction
fun! xpt#parser#ParseSnippet(p)
call xpt#snipfile#Push()
let x = b:xptemplateData
let x.snipFileScope = a:p.snipFileScope
let lines = a:p.lines
let [i,len] = [0,len(lines)]
let [s,e,blk] = [-1,-1,10000]
while i < len-1 | let i += 1
let v = lines[i]
if v =~ '^\s*$' || v =~ '^"[^"]*$'
let blk = min([blk,i - 1])
continue
endif
if v =~# '^\.\.XPT'
let e = i - 1
call s:XPTemplateParseSnippet(lines[s : e])
let [s,e,blk] = [-1,-1,10000]
elseif v =~# '^XPT\>'
if s != -1
let e = min([i - 1,blk])
call s:XPTemplateParseSnippet(lines[s : e])
let [s,e,blk] = [i,-1,10000]
else
let s = i
let blk = i
endif
elseif v =~# '^\\XPT'
let lines[i] = v[1 :]
else
let blk = i
endif
endwhile
if s != -1
call s:XPTemplateParseSnippet(lines[s : min([blk,i])])
endif
call xpt#snipfile#Pop()
endfunction
fun! s:XPTemplateParseSnippet(lines)
let lines = a:lines
let snipScope = XPTsnipScope()
let snipScope.loadedSnip = get( snipScope, 'loadedSnip', {} )
let snippetLines = []
let setting = deepcopy(g:XPTemplateSettingPrototype)
let [hint,lines[0]] = s:GetSnipCommentHint(lines[0])
if hint != ''
let setting.rawHint = hint
endif
let snippetParameters = split(lines[0], '\V'.s:nonEscaped.'\s\+')
let snippetName = snippetParameters[1]
let snippetParameters = snippetParameters[2:]
for pair in snippetParameters
let name = matchstr(pair, '\V\^\[^=]\*')
let value = pair[len(name) :]
let value = value[0:0] == '=' ? xpt#util#UnescapeChar(value[1:], ' ') : 1
let setting[name] = value
endfor
let start = 1
let len = len(lines)
while start < len
let command = matchstr( lines[ start ], '\V\^XSETm\?\ze\s' )
if command != ''
let [key,val,start] = s:GetXSETkeyAndValue(lines,start)
if key == ''
let start += 1
continue
endif
let [keyname,keytype] = xpt#parser#GetKeyType(key)
call s:HandleXSETcommandOld(setting,command,keyname,keytype,val)
elseif lines[start] =~# '^\\XSET' " escaped XSET or XSETm
let snippetLines += [lines[start][1:]]
else
let snippetLines += [lines[start]]
endif
let start += 1
endwhile
let setting.fromXPT = 1
if has_key( setting, 'alias' )
call XPTemplateAlias(snippetName,setting.alias,setting)
else
call XPTdefineSnippet(snippetName,setting,snippetLines)
endif
if has_key(snipScope.loadedSnip,snippetName)
call XPT#info( "XPT: warn : duplicate snippet:" . snippetName . ' in file:' . snipScope.filename )
endif
let snipScope.loadedSnip[snippetName] = 1
if has_key( setting, 'synonym' )
let synonyms = split( setting.synonym, '|' )
for synonym in synonyms
call XPTemplateAlias(synonym,snippetName,{})
if has_key(snipScope.loadedSnip,synonym)
call XPT#warn( "XPT: warn : duplicate synonym:" . synonym . ' in file:' . snipScope.filename )
endif
let snipScope.loadedSnip[synonym] = 1
endfor
endif
endfunction
fun! s:GetSnipCommentHint(str)
let pos = match(a:str, '\V' . s:nonEscaped . '\shint=')
if pos != -1
return [a:str[pos + 6 :],a:str[: pos - 1]]
endif
let pos = match( a:str, '\VXPT\s\+\S\+\.\{-}\zs\s' . s:nonEscaped . '"' )
if pos == -1
return [ '', a:str ]
else
return [ matchstr( a:str[ pos + 1 + 1 : ], '\S.*' ), a:str[ : pos ] ]
endif
endfunction
fun! s:GetXSETkeyAndValue(lines,start)
let start = a:start
let XSETparam = matchstr(a:lines[start], '\V\^XSET\%[m]\s\+\zs\.\*')
let isMultiLine = a:lines[ start ] =~# '\V\^XSETm'
if isMultiLine
let key = XSETparam
let [start,val] = s:ParseMultiLineValues(a:lines,start)
else
let key = matchstr(XSETparam, '\V\[^=]\*\ze=')
if key == ''
return [ '', '', start + 1 ]
endif
let val = matchstr(XSETparam, '\V=\s\*\zs\.\*')
let val = substitute(val, '\\n', "\n", 'g')
endif
return [key,val,start]
endfunction
fun! s:ParseMultiLineValues(lines,start)
let lines = a:lines
let start = a:start
let endPattern = '\V\^XSETm\s\+END\$'
let start += 1
let multiLineValues = []
while start < len(lines)
let line = lines[start]
if line =~# endPattern
break
endif
if line =~# '^\V\\\+XSET\%[m]'
let slashes = matchstr( line, '^\\\+' )
let nrSlashes = len(slashes + 1) / 2
let line = line[nrSlashes :]
endif
let multiLineValues += [line]
let start += 1
endwhile
let val = join(multiLineValues, "\n")
return [start,val]
endfunction
fun! s:HandleXSETcommandOld(setting,command,keyname,keytype,value)
if a:keyname ==# 'ComeFirst'
let a:setting.comeFirst = xpt#util#SplitWith( a:value, ' ' )
elseif a:keyname ==# 'ComeLast'
let a:setting.comeLast = xpt#util#SplitWith( a:value, ' ' )
elseif a:keyname ==# 'postQuoter'
let a:setting.postQuoter = a:value
elseif a:keyname =~ '\V\^$'
let a:setting.variables[a:keyname] = a:value
elseif a:keytype == "" || a:keytype ==# 'def'
let a:setting.defaultValues[a:keyname] = xpt#flt#New(0,a:value)
elseif a:keytype ==# 'map'
let a:setting.mappings[a:keyname] = get( a:setting.mappings, a:keyname, { 'saver' : xpt#msvr#New(1), 'keys' : {} } )
let key = matchstr( a:value, '\V\^\S\+\ze\s' )
let mapping = matchstr( a:value, '\V\s\+\zs\.\*' )
call xpt#msvr#Add( a:setting.mappings[ a:keyname ].saver, 'i', key )
let a:setting.mappings[a:keyname].keys[key] = xpt#flt#New(0,mapping)
elseif a:keytype ==# 'pre'
let a:setting.preValues[a:keyname] = xpt#flt#New(0,a:value)
elseif a:keytype ==# 'ontype'
let a:setting.ontypeFilters[a:keyname] = xpt#flt#New(0,a:value)
elseif a:keytype ==# 'post'
if a:keyname =~ '\V...'
let a:setting.postFilters[a:keyname] = xpt#flt#New( 0, 'BuildIfNoChange(' . string(a:value) . ')' )
else
let a:setting.postFilters[a:keyname] = xpt#flt#New(0,a:value)
endif
else
throw "unknown key name or type:" . a:keyname . ' ' . a:keytype
endif
endfunction
let &cpo = s:oldcpo