" GetLatestVimScripts: 2611 1 :AutoInstall: xpt.tgz " VERSION: 0.4.9.161024-8f2c6c5 if exists( "g:__XPTEMPLATE_VIM__" ) && g:__XPTEMPLATE_VIM__ >= XPT#ver finish endif let g:__XPTEMPLATE_VIM__ = XPT#ver let s:oldcpo = &cpo set cpo-=< cpo+=B exe XPT#let_sid runtime plugin/xptemplate.conf.vim runtime plugin/xpreplace.vim runtime plugin/xpmark.vim runtime plugin/xpopup.vim exec XPT#importConst let s:log = xpt#debug#Logger( 'warn' ) let s:log = xpt#debug#Logger( 'debug' ) let s:close_pum = "\\\" let s:renderPhase = xpt#rctx#phase call XPRaddPreJob( 'XPMupdateCursorStat' ) call XPRaddPostJob( 'XPMupdateSpecificChangedRange' ) call XPMsetUpdateStrategy( 'normalMode' ) fun! XPTmarkCompare(o,markToAdd,existedMark) let renderContext = b:xptemplateData.renderContext if renderContext.phase == 'rendering' let [lm,rm] = [a:o.changeLikelyBetween.start,a:o.changeLikelyBetween.end] if a:existedMark ==# rm return -1 endif elseif renderContext.action == 'build' && has_key( renderContext, 'buildingMarkRange' ) && renderContext.buildingMarkRange.end == a:existedMark return -1 endif return 1 endfunction let s:repetitionPattern = '\w\*...\w\*' let s:expandablePattern = '\V\S\+...\$' let s:nullDict = {} let s:nullList = [] let s:nonEscaped = '\%(' . '\%(\[^\\]\|\^\)' . '\%(\\\\\)\*' . '\)' . '\@<=' let g:XPTemplateSettingPrototype = { 'hidden':0, 'variables':{}, 'preValues':{ 'cursor' : xpt#flt#New( 0, '$CURSOR_PH' ) }, 'defaultValues':{}, 'mappings':{}, 'ontypeFilters':{}, 'postFilters':{}, 'comeFirst':[], 'comeLast':[], } fun! g:XPTapplyTemplateSettingDefaultValue(setting) let s = a:setting let s.postQuoter = get( s, 'postQuoter', { 'start' : '{{', 'end' : '}}' } ) let s.preValues.cursor = get( s.preValues, 'cursor', '$CURSOR_PH' ) endfunction let g:XPT_RC = { 'ok':{}, 'canceled':{}, } let s:buildingSeqNr = 0 let s:anonymouseIndex = 0 let s:pumCB = {} fun! s:pumCB.onEmpty(sess) if g:xptemplate_fallback ==? '' call XPT#warn( "XPT: No snippet matches" ) return '' else let x = b:xptemplateData let x.fallbacks = [ [ "\XPTfallback", 'feed' ] ] + x.fallbacks return XPT#fallback(x.fallbacks) endif endfunction fun! s:pumCB.onOneMatch(sess) if a:sess.matched == '' call feedkeys( eval('"\' . g:xptemplate_key . '"' ), 'nt') return '' else return s:DoStart(a:sess) endif endfunction let s:ItemPumCB = {} fun! s:ItemPumCB.onOneMatch(sess) if 0 == s:XPTupdate() return s:ShiftForward( '' ) else return "" endif endfunction fun! s:FallbackKey() call feedkeys( "\XPTfallback", 'mt' ) return '' endfunction fun! XPTemplateKeyword(val) let x = b:xptemplateData let ftScope = x.filetypes[x.snipFileScope.filetype] let ftkeyword = ftScope.ftkeyword let val = substitute(a:val, '\w', '', 'g') let val = string(val)[1 : -2] let needEscape = '^\]-' let ftkeyword.list += split( val, '\v\s*' ) call sort(ftkeyword.list) let ftkeyword.list = split( substitute( join( ftkeyword.list, '' ), '\v(.)\1+', '\1', 'g' ), '\v\s*' ) let ftkeyword.regexp = '\[0-9A-Za-z_' . escape( join( ftkeyword.list, '' ), needEscape ) . ']' endfunction fun! XPTemplatePriority(...) let x = b:xptemplateData let p = get( a:000, 0, '' ) if p == '' let p = 'lang' endif let x.snipFileScope.priority = xpt#priority#Parse(p) endfunction fun! XPTemplateMark(sl,sr) let b:xptemplateData.snipFileScope.ptn = xpt#snipfile#GenPattern({'l':a:sl, 'r':a:sr}) endfunction fun! XPTmark() let renderContext = b:xptemplateData.renderContext let xp = renderContext.snipObject.ptn return [xp.l,xp.r] endfunction fun! g:XPTfuncs() return g:GetSnipFileFtScope().funcs endfunction fun! XPTemplateAlias(name,toWhich,setting) let name = a:name let xptObj = b:xptemplateData let xt = xptObj.filetypes[g:GetSnipFileFT()].allTemplates let toSnip = get(xt,a:toWhich) if toSnip is 0 return endif let setting = deepcopy(toSnip.setting) call xpt#util#DeepExtend(setting,a:setting) let prio = xptObj.snipFileScope.priority let existed = get( xt, a:name, { 'priority': xpt#priority#Get( 'lowest' ) } ) if existed.priority < prio return endif if has_key(xt,a:toWhich) let xt[a:name] = { 'name':a:name, 'parsed':0, 'ftScope':toSnip.ftScope, 'snipText':toSnip.snipText, 'priority':prio, 'setting':setting, 'ptn':deepcopy(toSnip.ptn), } call s:UpdateNamePrefixDict(toSnip.ftScope,a:name) call s:ParseTemplateSetting(xt[a:name]) if get( xt[ name ].setting, 'abbr', 0 ) call s:Abbr(name) endif endif endfunction fun! g:GetSnipFileFT() let x = b:xptemplateData return x.snipFileScope.filetype endfunction fun! g:GetSnipFileFtScope() let x = b:xptemplateData return x.filetypes[x.snipFileScope.filetype] endfunction fun! s:GetTempSnipScope(x,ft) if !has_key( a:x, '__tmp_snip_scope' ) let sc = xpt#snipfile#New('') let b:xptemplateData.snipFileScope = sc let sc.priority = 0 let a:x.__tmp_snip_scope = sc endif let a:x.__tmp_snip_scope.filetype = '' == a:ft ? 'unknown' : a:ft return a:x.__tmp_snip_scope endfunction fun! XPTemplate(name,str_or_ctx,...) let x = b:xptemplateData if a:0 == 0 let snip = a:str_or_ctx let setting = {} else let snip = a:1 let setting = a:str_or_ctx endif let ft = get( setting, 'filetype', &filetype ) let ft = '' == ft ? 'unknown' : ft call xpt#parser#loadSpecialFiletype(ft) call xpt#snipfile#Push() let x.snipFileScope = s:GetTempSnipScope(x,ft) call XPTdefineSnippet(a:name,setting,snip) call xpt#snipfile#Pop() endfunction fun! XPTdefineSnippet(name,setting,snip) let name = a:name let x = b:xptemplateData let ftScope = x.filetypes[x.snipFileScope.filetype] let templates = ftScope.allTemplates let xp = x.snipFileScope.ptn let templateSetting = deepcopy(g:XPTemplateSettingPrototype) call extend( templateSetting, a:setting, 'force' ) call g:XPTapplyTemplateSettingDefaultValue(templateSetting) let prio = x.snipFileScope.priority if has_key(templates,a:name) && templates[a:name].priority < prio return endif call s:UpdateNamePrefixDict(ftScope,a:name) if type(a:snip) == type([]) let snip = join(a:snip, "\n") else let snip = a:snip endif let templates[a:name] = { 'name':a:name, 'parsed':0, 'ftScope':ftScope, 'snipText':snip, 'priority':prio, 'setting':templateSetting, 'ptn':deepcopy(b:xptemplateData.snipFileScope.ptn), } call s:InitTemplateObject(x,templates[a:name]) if get( templates[ name ].setting, 'abbr', 0 ) call s:Abbr(name) endif endfunction fun! s:UpdateNamePrefixDict(ftScope,name) if !has_key( a:ftScope, 'namePrefix' ) let a:ftScope.namePrefix = {} endif let [n,pre] = [a:name,a:ftScope.namePrefix] while n != '' && !has_key( pre, n ) let pre[n] = 1 let n = n[: -2] endwhile endfunction fun! s:Abbr(name) let name = a:name try exe 'inoreabbr ' name '' . "\=XPTtgr(" . string( name ) . ",{'k':''})\" catch /.*/ let n = matchstr( name, '\v\w+$' ) let pre = name[: -len(n) - 1] let x.abbrPrefix[n] = get(x.abbrPrefix,n,{}) let x.abbrPrefix[n][pre] = 1 exe 'inoreabbr ' n printf( "\=XPTabbr(%s)\", string( n ) ) endtry endfunction fun! s:InitTemplateObject(xptObj,tmplObj) call s:ParseTemplateSetting(a:tmplObj) call s:AddCursorToComeLast(a:tmplObj.setting) call s:InitItemOrderList(a:tmplObj.setting) if !has_key( a:tmplObj.setting.defaultValues, 'cursor' ) let a:tmplObj.setting.defaultValues.cursor = xpt#flt#New( 0, 'Finish("")' ) endif if len(a:tmplObj.name) == 1 && 0 " diabled else let nonWordChar = substitute( a:tmplObj.name, '\w', '', 'g' ) if nonWordChar != '' if !(a:tmplObj.setting.wraponly || a:tmplObj.setting.hidden) call XPTemplateKeyword(nonWordChar) endif endif endif endfunction fun! s:ParseInclusion(tmplDict,tmplObject) if type( a:tmplObject.snipText ) == type( function( 'tr' ) ) return endif let xp = a:tmplObject.ptn let phPattern = '\V' . xp.lft . 'Include:\(\.\{-}\)' . xp.rt let linePattern = '\V' . '\n\(\s\*\)\.\{-}' . phPattern call s:DoInclude( a:tmplDict, a:tmplObject, { 'ph' : phPattern, 'line' : linePattern }, 1 ) let phPattern = '\V' . xp.lft . ':\(\.\{-}\):' . xp.rt let linePattern = '\V' . '\n\(\s\*\)\.\{-}' . phPattern call s:DoInclude( a:tmplDict, a:tmplObject, { 'ph' : phPattern, 'line' : linePattern }, 0 ) endfunction fun! s:DoInclude(tmplDict,tmplObject,pattern,keepCursor) let snip = "\n" . a:tmplObject.snipText let xp = a:tmplObject.ptn let included = {a:tmplObject.name : 1} let pos = 0 while 1 let pos = match(snip,a:pattern.line,pos) if -1 == pos break endif let [matching,indent,incName] = matchlist(snip,a:pattern.line,pos)[: 2] let indent = matchstr( split( matching, '\n' )[ -1 ], '^\s*' ) let [incName,params] = s:ParseInclusionStatement(a:tmplObject,incName) if has_key(a:tmplDict,incName) if has_key(included,incName) && included[incName] > 20 throw "XPT : include too many snippet:" . incName . ' in ' . a:tmplObject.name endif let included[incName] = get(included,incName,0) + 1 let ph = matchstr(matching,a:pattern.ph) let incTmplObject = a:tmplDict[incName] call s:ParseSnippet(incTmplObject,incTmplObject.ftScope) call s:MergeSetting(a:tmplObject.setting,incTmplObject.setting) let incSnip = s:ReplacePHInSubSnip(a:tmplObject,incTmplObject,params) let incSnip = substitute( incSnip, '\n', '&' . indent, 'g' ) if !a:keepCursor let incSnip = substitute( incSnip, xp.lft . 'cursor' . xp.rt, xp.l . xp.r, 'g' ) endif let leftEnd = pos + len(matching) - len(ph) let rightStart = pos + len(matching) let left = snip[: leftEnd - 1] let right = snip[rightStart :] let snip = left . incSnip . right else throw "XPT : include inexistent snippet:" . incName . ' in ' . a:tmplObject.name endif endwhile let a:tmplObject.snipText = snip[1:] endfunction fun! s:ReplacePHInSubSnip(snipObject,subSnipObject,params) let xp = a:snipObject.ptn let incSnip = a:subSnipObject.snipText let incSnipPieces = split( incSnip, '\V' . xp.rt, 1 ) for [k,v] in items(a:params) let [i,len] = [0 - 1,len(incSnipPieces) - 1] while i < len | let i += 1 let piece = incSnipPieces[i] if piece =~# '\V' . k let parts = split( piece, '\V' . xp.lft, 1 ) let iName = len(parts) == 4 ? 2 : len(parts) - 1 if parts[iName] ==# k let parts[iName] = v endif let incSnipPieces[i] = join(parts,xp.l) endif endwhile endfor let incSnip = join(incSnipPieces,xp.r) return incSnip endfunction fun! s:ParseInclusionStatement(snipObject,st) let xp = a:snipObject.ptn let ptn = '\V\^\[^(]\{-}(' let st = a:st if st =~ ptn && st[ -1 : -1 ] == ')' let name = matchstr(st,ptn)[: -2] let paramStr = st[len(name) + 1 : -2] let paramStr = xpt#util#UnescapeChar(paramStr,xp.l . xp.r) let params = {} try let params = eval(paramStr) catch /.*/ XPT#warn( 'XPT: Invalid parameter: ' . string( paramStr ) . ' Error=' . v:exception ) endtry return [name,params] else return [st,{}] endif endfunction fun! s:MergeSetting(toSettings,fromSettings) let a:toSettings.comeFirst += a:fromSettings.comeFirst let a:toSettings.comeLast = a:fromSettings.comeLast + a:toSettings.comeLast call s:InitItemOrderList(a:toSettings) call extend( a:toSettings.preValues, a:fromSettings.preValues, 'keep' ) call extend( a:toSettings.defaultValues, a:fromSettings.defaultValues, 'keep' ) call extend( a:toSettings.postFilters, a:fromSettings.postFilters, 'keep' ) call extend( a:toSettings.variables, a:fromSettings.variables, 'keep' ) for key in keys(a:fromSettings.mappings) if !has_key(a:toSettings.mappings,key) let a:toSettings.mappings[key] = { 'saver' : xpt#msvr#New(1), 'keys' : {} } endif for keystroke in keys(a:fromSettings.mappings[key].keys) let a:toSettings.mappings[key].keys[keystroke] = a:fromSettings.mappings[key].keys[keystroke] call xpt#msvr#Add( a:toSettings.mappings[ key ].saver, 'i', keystroke ) endfor endfor endfunction fun! s:ParseTemplateSetting(tmpl) let x = b:xptemplateData let setting = a:tmpl.setting if type( get( setting, 'wraponly', 0 ) ) == type( '' ) let setting.wrap = setting.wraponly let setting.wraponly = 1 endif let setting.iswrap = has_key( setting, 'wrap' ) let setting.wraponly = get( setting, 'wraponly', 0 ) if has_key( setting, 'wrap' ) && setting.wrap is 1 let setting.wrap = 'cursor' endif let x.renderContext.snipObject = a:tmpl if has_key(setting, 'rawHint') let hint = xpt#eval#Eval(setting.rawHint, [x.filetypes[x.snipFileScope.filetype].funcs, setting.variables, ]) if type(hint) == type({}) if get(hint, 'action', '') == 'pum' let pum = get(hint, 'pum', []) let setting.hint = join( pum[ : 3 ], ' ' ) . ' ..' else let setting.hint = get(hint, 'text', '') endif elseif type(hint) == type([]) let setting.hint = join( hint[ : 3 ], ' ' ) . ' ..' elseif type(hint) == type(1) let setting.hint = string(hint) elseif type(hint) == 2 let setting.hint = string(hint) else let setting.hint = hint endif endif call s:ParsePostQuoter(setting) endfunction fun! s:ParsePostQuoter(setting) if !has_key( a:setting, 'postQuoter' ) || type(a:setting.postQuoter) == type({}) return endif let quoters = split( a:setting.postQuoter, ',' ) if len(quoters) < 2 throw 'postQuoter must be separated with ","! :' . a:setting.postQuoter endif let a:setting.postQuoter = { 'start' : quoters[0], 'end' : quoters[1] } endfunction fun! s:AddCursorToComeLast(setting) if match( a:setting.comeLast, 'cursor' ) < 0 call add( a:setting.comeLast, 'cursor' ) endif endfunction fun! s:InitItemOrderList(setting) let a:setting.comeFirst = xpt#util#RemoveDuplicate(a:setting.comeFirst) let a:setting.comeLast = xpt#util#RemoveDuplicate(a:setting.comeLast) endfunction fun! XPTreload() try call s:Crash() catch /.*/ endtry try unlet b:xptemplateData catch /.*/ endtry e endfunction fun! XPTgetAllTemplates() call s:GetContextFTObj() " force initializing return copy(b:xptemplateData.filetypes[&filetype].allTemplates) endfunction fun! XPTemplatePreWrap(wrap) let x = b:xptemplateData let x.wrap = a:wrap let x.wrap = substitute( x.wrap, '\V\n\$', '', '' ) let x.wrap = xpt#indent#ToSpace(x.wrap) if ( g:xptemplate_strip_left || x.wrap =~ '\n' ) && visualmode() ==# 'V' let x.wrapStartPos = virtcol(".") let indent = matchstr( x.wrap, '^\s*' ) let indentNr = len(indent) let x.wrap = x.wrap[indentNr :] else let x.wrapStartPos = col(".") let indentNr = min( [ indent( line( "." ) ), virtcol('.') - 1 ] ) endif let maxIndent = indentNr let x.wrap = substitute( x.wrap, '\V\n \{0,' . maxIndent . '\}', "\n", 'g' ) let lines = split( x.wrap, '\V\\r\n\|\r\|\n', 1 ) let maxlen = 0 for l in lines let maxlen = maxlen < len(l) ? len(l) : maxlen endfor let indentNr -= maxIndent let x.wrap = { 'indent' : -indentNr, 'text':x.wrap, 'lines':lines, 'max':maxlen, 'curline':lines[ 0 ], } let leftSpaces = s:ConcreteSpace() if leftSpaces != '' let x.wrapStartPos = len(leftSpaces) + 1 endif let leftSpaces = substitute( leftSpaces, ' ', ' ', 'g' ) return leftSpaces . "\=XPTemplateDoWrap()\" endfunction fun! s:ConcreteSpace() if getline( line( '.' ) ) =~ '^\s*$' let pos = virtcol( '.' ) normal! d0 let leftSpaces = XPT#convertSpaceToTab( repeat( ' ', pos - 1 ) ) else let leftSpaces = '' endif return leftSpaces endfunction fun! XPTemplateDoWrap() call XPTparseSnippets() let x = b:xptemplateData let ppr = s:Popup("", x.wrapStartPos, {}) return ppr endfunction fun! XPTabbr(name) let x = b:xptemplateData let line = getline( "." ) let pre = matchstr( line, '\v\S+$' ) if pre == '' return printf( "\=XPTtgr(%s, {'k':''})\", string( a:name ) ) else if has_key(x.abbrPrefix,a:name) if has_key(x.abbrPrefix[a:name],pre) return repeat( "\", len( pre ) ) . printf( "\=XPTtgr(%s, {'k':''})\", string( pre . a:name ) ) else return printf( "\=XPTtgr(%s, {'k':''})\", string( a:name ) ) endif else return printf( "\=XPTtgr(%s, {'k':''})\", string( a:name ) ) endif endif endfunction fun! XPTtgr(snippetName,...) let opt = a:0 == 1 ? a:1 : {} if pumvisible() || XPPhasSession() return XPPend() . "\=XPTtgr(" . string( a:snippetName ) . ', ' . string(opt) . ")\" endif if opt != {} if get( opt, 'noliteral', 0 ) let opt.nosyn = '\V\cstring\|comment' elseif get( opt, 'literal', 0 ) let opt.syn = '\V\cstring\|comment' endif if has_key( opt, 'nopum' ) let opt.pum = !opt.nopum endif let syn = synIDattr( synID( line("."), col("."), 0 ), "name" ) if has_key( opt, 'nosyn' ) && syn =~ opt.nosyn || has_key( opt, 'syn' ) && syn !~ opt.syn return opt.k endif if has_key( opt, 'pum' ) if opt.pum && !pumvisible() || !opt.pum && pumvisible() return opt.k endif endif endif let action = XPTemplateStart( 0, { 'startPos' : [ line( "." ), col( "." ) ], 'tmplName' : a:snippetName } ) return action endfunction fun! XPTemplateTrigger(snippetName,...) let opt = a:0 == 1 ? a:1 : {} return XPTtgr(a:snippetName,opt) endfunction fun! XPTparseSnippets() let x = b:xptemplateData for p in x.snippetToParse call xpt#parser#ParseSnippet(p) endfor let x.snippetToParse = [] endfunction fun! XPTemplateStart(pos_unused_any_more,...) let action = '' call XPTparseSnippets() let x = b:xptemplateData let opt = a:0 == 1 ? a:1 : {} if has_key( opt, 'tmplName' ) let startColumn = opt.startPos[1] let templateName = opt.tmplName call cursor(opt.startPos) return action . s:DoStart({ 'line':opt.startPos[0], 'col':startColumn, 'matched':templateName, 'data':{ 'ftScope' : s:GetContextFTObj() } } ) endif if get( opt, 'concrete', 0 ) == 0 let opt.concrete = 1 let leftSpaces = s:ConcreteSpace() if leftSpaces != '' let leftSpaces = substitute( leftSpaces, ' ', ' ', 'g' ) return leftSpaces . "\=XPTemplateStart(0," . string( opt ) . ")\" endif endif let keypressed = get( opt, 'k', g:xptemplate_key ) let keypressed = substitute( keypressed, '\V++', '>', 'g' ) if pumvisible() if XPPhasSession() return XPPend() . "\=XPTemplateStart(0," . string( opt ) . ")\" else if x.fallbacks == [] if keypressed =~ g:xptemplate_fallback_condition let x.fallbacks = [ [ "\XPTfallback", 'feed' ] ] + x.fallbacks return XPT#fallback(x.fallbacks) else endif else if g:xptemplate_fallback =~? '\VXPTrawKey\|' || g:xptemplate_fallback ==? keypressed return XPT#fallback(x.fallbacks) else let x.fallbacks = [ [ "\XPTfallback", 'feed' ] ] + x.fallbacks return XPT#fallback(x.fallbacks) endif endif endif else if XPPhasSession() call XPPend() endif endif let forcePum = get( opt, 'forcePum', g:xptemplate_always_show_pum ) if x.renderContext.processing let miniPrefix = g:xptemplate_minimal_prefix_nested else let miniPrefix = g:xptemplate_minimal_prefix endif let isFullMaatching = miniPrefix is 'full' let cursorColumn = col(".") let startLineNr = line(".") let accEmp = 0 if g:xptemplate_key ==? '' let accEmp = 1 endif if has_key( opt, 'popupOnly' ) let startColumn = cursorColumn elseif x.wrapStartPos let startColumn = x.wrapStartPos else let ftScope = s:GetContextFTObj() let ftkeyword = ftScope.ftkeyword let columnBeforeCursor = col( "." ) - 2 if columnBeforeCursor >= 0 let lineToCursor = getline(startLineNr)[0 : columnBeforeCursor] else let lineToCursor = '' endif let pre = ftScope.namePrefix let n = split( lineToCursor, '\s', 1 )[ -1 ] let snpt_name_ptn = '\V\^\(' . ftkeyword.regexp . '\|\k\)\k\*' while n != '' && !has_key( pre, n ) let shorter = substitute( n, snpt_name_ptn, '', '' ) if shorter == n let n = n[1 :] else let n = shorter endif endwhile let matched = n if !has_key( opt, 'popupOnly' ) if !isFullMaatching && len(matched) < miniPrefix let x.fallbacks = [ [ "\XPTfallback", 'feed' ] ] + x.fallbacks return XPT#fallback(x.fallbacks) endif endif let startColumn = col( "." ) - len( matched ) if matched == '' let [startLineNr, startColumn] = [line("."), col(".")] endif endif let templateName = strpart(getline(startLineNr),startColumn - 1,cursorColumn - startColumn) let action = action . s:Popup(templateName,startColumn, { 'acceptEmpty' : accEmp, 'forcePum':forcePum, 'matchWholeName':get( opt, 'popupOnly', 0 ) ? 0 : isFullMaatching } ) return action endfunction fun! s:NewRenderContext(ftScope,tmplName) let x = b:xptemplateData if x.renderContext.processing call xpt#buf#Pushrctx() endif let renderContext = xpt#rctx#New(x) let x.renderContext = renderContext let renderContext.phase = 'inited' let renderContext.snipObject = s:GetContextFTObj().allTemplates[a:tmplName] let renderContext.ftScope = a:ftScope call s:ParseSnippet(renderContext.snipObject,renderContext.ftScope) let renderContext.snipSetting = copy(renderContext.snipObject.setting) let setting = renderContext.snipSetting for k in [ 'variables', 'preValues', 'defaultValues' , 'ontypeFilters', 'postFilters', 'comeFirst', 'comeLast' ] let setting[k] = copy(setting[k]) endfor return renderContext endfunction fun! s:ParseSnippet(snippet,ftScope) if !a:snippet.parsed let a:snippet.snipText = xpt#indent#IndentToTabStr(a:snippet.snipText) call s:ParseInclusion(a:ftScope.allTemplates,a:snippet) let a:snippet.snipText = s:ParseQuotedPostFilter(a:snippet) let a:snippet.snipText = s:ParseRepetition(a:snippet) let a:snippet.parsed = 1 endif endfunction fun! s:DoStart(sess) let x = b:xptemplateData if !has_key(s:GetContextFTObj().allTemplates,a:sess.matched) return '' endif let b:__xpt_snip_sess__ = a:sess return "\" . s:RenderSnippet() endfunction fun! s:RenderSnippet() let x = b:xptemplateData let sess = b:__xpt_snip_sess__ let x.savedReg = @" let [lineNr,column] = [sess.line,sess.col] let cursorColumn = col(".") let tmplname = sess.matched let ctx = s:NewRenderContext(sess.data.ftScope,tmplname) call s:BuildSnippet([lineNr,column],[lineNr,cursorColumn]) let ctx.phase = 'rendered' let ctx.processing = 1 call s:CallPlugin( 'render', 'after' ) if empty(x.stack) call s:SaveNavKey() call s:ApplyMap() endif let x.wrap = '' let x.wrapStartPos = 0 let action = s:GotoNextItem() call s:CallPlugin( 'start', 'after' ) return action endfunction fun! s:SaveNavKey() let x = b:xptemplateData let navKey = g:xptemplate_nav_next let mapInfo = xpt#msvr#MapInfo( navKey, 'i' ) if mapInfo.cont == '' let x.canNavFallback = 0 exe 'inoremap XPTnavFallback ' navKey else let x.canNavFallback = 1 let mapInfo.key = 'XPTnavFallback' exe xpt#msvr#MapCommand(mapInfo) endif endfunction fun! s:FinishRendering(...) let x = b:xptemplateData let renderContext = x.renderContext let xp = renderContext.snipObject.ptn let isCursor = get( renderContext.item, 'name', 0 ) is 'cursor' call XPMremoveMarkStartWith(renderContext.markNamePre) if empty(x.stack) let x.fallbacks = [] let renderContext.processing = 0 let renderContext.phase = 'finished' call s:ClearMap() call XPMflushWithHistory() let @" = x.savedReg call s:CallPlugin( 'finishAll', 'after' ) else call xpt#buf#Poprctx() call s:CallPlugin( 'finishSnippet', 'after' ) let renderContext = x.renderContext endif return '' endfunction fun! s:Popup(pref,coln,opt) let x = b:xptemplateData let renderContext = x.renderContext if renderContext.phase == 'finished' let renderContext.phase = 'popup' endif let cmpl=[] let cmpl2 = [] let ftScope = s:GetContextFTObj() if ftScope == {} return '' endif let forcePum = get( a:opt, 'forcePum', g:xptemplate_always_show_pum ) let snipDict = ftScope.allTemplates let synNames = s:SynNameStack(line("."), a:coln) if has_key(snipDict,a:pref) && !forcePum let snipObj = snipDict[a:pref] if s:IfSnippetShow(snipObj,synNames) return s:DoStart({ 'line':line( "." ), 'col':a:coln, 'matched':a:pref, 'data':{ 'ftScope' : s:GetContextFTObj() } } ) endif endif for [key,snipObj] in items(snipDict) if !s:IfSnippetShow(snipObj,synNames) continue endif let hint = get( snipObj.setting, 'hint', '' ) if key =~# '\V\^\[A-Z]' call add( cmpl2, {'word' : key, 'menu' : hint } ) else call add( cmpl, {'word' : key, 'menu' : hint } ) endif endfor call sort(cmpl) call sort(cmpl2) let cmpl = cmpl + cmpl2 let pumsess = XPPopupNew(s:pumCB, { 'ftScope' : ftScope }, cmpl) call pumsess.SetAcceptEmpty( get( a:opt, 'acceptEmpty', 0 ) ) call pumsess.SetMatchWholeName( get( a:opt, 'matchWholeName', 0 ) ) call pumsess.SetOption({ 'matchPrefix':! forcePum, 'tabNav':g:xptemplate_pum_tab_nav } ) return pumsess.popup(a:coln,{}) endfunction fun! s:IfSnippetShow(snipObj,synNames) let x = b:xptemplateData let snipObj = a:snipObj let synNames = a:synNames if snipObj.setting.wraponly && x.wrap is '' || !snipObj.setting.iswrap && x.wrap isnot '' return 0 endif if has_key(snipObj.setting, "syn") && snipObj.setting.syn != '' && match(synNames, '\c' . snipObj.setting.syn) == -1 return 0 endif if get( snipObj.setting, 'hidden', 0 ) == 1 return 0 endif return 1 endfunction fun! s:AddIndent(text,nIndent) let baseIndent = repeat( " ", a:nIndent ) return substitute(a:text, '\n', '&' . baseIndent, 'g') endfunction fun! s:ParseRepetition(snipObject) let tmplObj = a:snipObject let xp = a:snipObject.ptn let tmpl = a:snipObject.snipText let bef = "" let rest = "" let rp = xp.lft . s:repetitionPattern . xp.rt let repPtn = '\V\(' . rp . '\)\_.\{-}' . '\1' let repContPtn = '\V\(' . rp . '\)\zs\_.\{-}' . '\1' let stack = [] let from = 0 while 1 let startOfMatch = match(tmpl,repPtn,from) if startOfMatch == -1 break endif let stack += [startOfMatch] let from = startOfMatch + 1 endwhile while stack != [] let matchpos = stack[-1] unlet stack[-1] if matchpos == 0 let bef = '' else let bef = tmpl[: matchpos-1] endif let rest = tmpl[matchpos :] let indentNr = s:GetIndentBeforeEdge(tmplObj,bef) let repeatPart = matchstr(rest,repContPtn) let repeatPart = 'BuildIfNoChange(' . string( repeatPart ) . ')' let symbol = matchstr(rest,rp) let name = substitute( symbol, '\V' . xp.lft . '\|' . xp.rt, '', 'g' ) let tmplObj.setting.postFilters[name] = xpt#flt#New(-indentNr,repeatPart) let bef .= symbol let rest = substitute(rest, repPtn, '', '') let tmpl = bef . rest endwhile return tmpl endfunction fun! s:GetIndentBeforeEdge(tmplObj,textBeforeLeftMark) let xp = a:tmplObj.ptn if a:textBeforeLeftMark =~ '\V' . xp.lft . '\_[^' . xp.r . ']\*\%$' let tmpBef = substitute( a:textBeforeLeftMark, '\V' . xp.lft . '\_[^' . xp.r . ']\*\%$', '', '' ) let indentOfFirstLine = matchstr( tmpBef, '.*\n\zs\s*' ) else let indentOfFirstLine = matchstr( a:textBeforeLeftMark, '.*\n\zs\s*' ) endif return len(indentOfFirstLine) endfunction fun! s:ParseQuotedPostFilter(tmplObj) let xp = a:tmplObj.ptn let postFilters = a:tmplObj.setting.postFilters let quoter = a:tmplObj.setting.postQuoter let flagPattern = '\V\[!]\$' let startPattern = '\V\_.\{-}\zs' . xp.lft . '\_[^' . xp.r . ']\*' . quoter.start . xp.rt let endPattern = '\V' . xp.lft . quoter.end . xp.rt let snip = a:tmplObj.snipText let stack = [] let startPos = 0 while startPos != -1 let startPos = match(snip,startPattern,startPos) if startPos != -1 call add(stack,startPos) let startPos += len(matchstr(snip,startPattern,startPos)) endif endwhile while 1 if empty(stack) break endif let startPos = remove(stack,-1) let endPos = match(snip,endPattern,startPos + 1) if endPos == -1 break endif let startText = matchstr(snip,startPattern,startPos) let endText = matchstr(snip,endPattern,endPos) let name = startText[1 : -1 - len(quoter.start) - 1] let flag = matchstr(name,flagPattern) if flag != '' let name = name[: -1 - len(flag)] endif if name =~ xp.lft let name = matchstr( name, '\V' . xp.lft . '\zs\_.\*' ) if name =~ xp.lft let name = matchstr( name, '\V\_.\*\ze' . xp.lft ) endif endif let plainPostFilter = snip[startPos + len(startText) : endPos - 1] let firstLineIndentNr = s:GetIndentBeforeEdge(a:tmplObj,snip[: startPos - 1]) if flag == '!' let plainPostFilter = 'BuildIfChanged(' . string( plainPostFilter ) . ')' else let plainPostFilter = 'BuildIfNoChange(' . string( plainPostFilter ) . ')' endif let postFilters[name] = xpt#flt#New(-firstLineIndentNr,plainPostFilter) let snip = snip[: startPos + len(startText) - 1 - 1 - len(quoter.start) - len(flag)] . snip[endPos + len(endText) - 1 :] endwhile return snip endfunction fun! s:BuildSnippet(nameStartPosition,nameEndPosition) call getchar(0) let x = b:xptemplateData let ctx = b:xptemplateData.renderContext let xp = ctx.snipObject.ptn let curline = getline(a:nameStartPosition[0]) let nIndent = -1 if len( matchstr( curline, '\V\^\s\*' ) ) == a:nameStartPosition[ 1 ] - 1 if has_key(ctx.oriIndentkeys,ctx.snipObject.name) || has_key(ctx.leadingCharToReindent,ctx.snipObject.name) if a:nameStartPosition == a:nameEndPosition call XPreplace(a:nameStartPosition,a:nameEndPosition, ctx.snipObject.name, { 'doJobs' : 0 } ) endif let nIndent = XPT#getPreferedIndentNr(a:nameStartPosition[0]) if a:nameStartPosition == a:nameEndPosition call XPreplace(a:nameStartPosition,[a:nameEndPosition[0], a:nameEndPosition[1] + len(ctx.snipObject.name)], '', { 'doJobs' : 0 } ) endif endif endif let ctx.phase = 'rendering' if ctx.snipSetting.iswrap && x.wrap isnot '' let setting = ctx.snipSetting let setting.preValues[ setting.wrap ] = xpt#flt#New( 0, 'GetWrappedText()' ) let setting.defaultValues[ setting.wrap ] = xpt#flt#New( 0, "Next()", 1 ) call insert(setting.comeFirst,setting.wrap,0) endif if x.wrap isnot '' let ctx.wrap = copy(x.wrap) endif let snippetText = ctx.snipObject.snipText let currentNIndent = XPT#getIndentNr(a:nameStartPosition[0],a:nameStartPosition[1]) let nIndentToAdd = currentNIndent if nIndent >= 0 if nIndent > currentNIndent let snippetText = repeat( ' ', nIndent - currentNIndent ) . snippetText let nIndentToAdd = nIndent elseif nIndent < currentNIndent let snippetText = repeat( ' ', nIndent ) . snippetText let nIndentToAdd = nIndent let a:nameStartPosition[1] = 1 endif endif let snippetText = xpt#indent#ToActualIndentStr(snippetText,nIndentToAdd) call XPMupdate() call XPMadd( ctx.marks.tmpl.start, a:nameStartPosition, g:XPMpreferLeft, '\Ve\$' ) call XPMadd( ctx.marks.tmpl.end, a:nameEndPosition, g:XPMpreferRight, '\Ve\$' ) call xpt#settingswitch#Switch(b:xptemplateData.settingWrap) call XPMsetLikelyBetween(ctx.marks.tmpl.start,ctx.marks.tmpl.end) call XPreplace(a:nameStartPosition,a:nameEndPosition,snippetText) let ctx.firstList = [] let ctx.itemList = [] let ctx.lastList = [] if 0 > s:BuildPlaceHolders(ctx.marks.tmpl) return s:Crash() endif let ctx = empty(x.stack) ? x.renderContext : x.stack[0] let rg = XPMposList(ctx.marks.tmpl.start,ctx.marks.tmpl.end) exe 'silent! ' . rg[0][0] . ',' . rg[1][0] . 'foldopen!' endfunction fun! s:GetNameInfo(end) let x = b:xptemplateData let xp = x.renderContext.snipObject.ptn if getline(".")[col(".") - 1] != xp.l throw "cursor is not at item start position:".string(getpos(".")[1:2]) endif let endn = a:end[0] * 10000 + a:end[1] let l0 = getpos(".")[1:2] let r0 = searchpos(xp.rt, 'nW') let r0n = r0[0] * 10000 + r0[1] if r0 == [0,0] || r0n >= endn return [[0,0],[0,0],[0,0],[0,0]] endif let l1 = searchpos(xp.lft, 'W') let l2 = searchpos(xp.lft, 'W') let l1n = l1[0] * 10000 + l1[1] let l2n = l2[0] * 10000 + l2[1] if l1n > r0n || l1n >= endn let l1 = [0,0] endif if l2n > r0n || l1n >= endn let l2 = [0,0] endif if l1 != [0,0] && l2 != [0,0] return [l0,l1,l2,r0] elseif l1 == [0,0] && l2 == [0,0] return [l0,l0,r0,r0] else return [l0,l1,r0,r0] endif endfunction fun! s:GetValueInfo(end) let x = b:xptemplateData let xp = x.renderContext.snipObject.ptn if getline(".")[col(".") - 1] != xp.r throw "cursor is not at item end position:".string(getpos(".")[1:2]) endif let nEnd = a:end[0] * 10000 + a:end[1] let r0 = [ line( "." ), col( "." ) ] let l0 = searchpos(xp.lft, 'nW', a:end[0]) if l0 == [0,0] let l0n = nEnd else let l0n = min([l0[0] * 10000 + l0[1],nEnd]) endif let r1 = searchpos(xp.rt, 'W', a:end[0]) if r1 == [0,0] || r1[0] * 10000 + r1[1] >= l0n return [r0,copy(r0),copy(r0)] endif let r2 = searchpos(xp.rt, 'W', a:end[0]) if r2 == [0,0] || r2[0] * 10000 + r2[1] >= l0n return [r0,r1,copy(r1)] endif return [r0,r1,r2] endfunction fun! s:TextWithoutIndent(posRange) let [s,e] = a:posRange let text = xpt#util#TextBetween([s,e]) let text = xpt#indent#ToSpace(text) let nIndent = xpt#indent#IndentBefore(s) let text = xpt#indent#RemoveIndentStr(text,nIndent) return text endfunction fun! s:CreatePlaceHolder(ctx,nameInfo,valueInfo) let xp = a:ctx.snipObject.ptn let toescape = xp.l . xp.r let leftEdge = s:TextWithoutIndent(a:nameInfo[0 : 1]) let name = s:TextWithoutIndent(a:nameInfo[1 : 2]) let rightEdge = s:TextWithoutIndent(a:nameInfo[2 : 3]) let [leftEdge,name,rightEdge] = [leftEdge[1 :],name[1 :],rightEdge[1 :]] let leftEdge = xpt#util#UnescapeChar(leftEdge,toescape) let name = xpt#util#UnescapeChar(name,toescape) let rightEdge = xpt#util#UnescapeChar(rightEdge,toescape) let fullname = leftEdge . name . rightEdge let incPattern = '\V\^:\zs\.\*\ze:\$\|\^Include:\zs\.\*\$' if name =~ incPattern return { 'include' : matchstr( name, incPattern ) } endif if name =~ '\V' . xp.item_var . '\|' . xp.item_func return { 'value' : fullname, 'leftEdge':leftEdge, 'name':name, 'rightEdge':rightEdge, } endif let placeHolder = { 'name':name, 'isKey':(a:nameInfo[0] != a:nameInfo[1]), } if placeHolder.isKey call extend(placeHolder,{ 'leftEdge':leftEdge, 'rightEdge':rightEdge, 'fullname':fullname, }, 'force' ) endif if a:valueInfo[1] != a:valueInfo[0] let isPostFilter = a:valueInfo[1][0] == a:valueInfo[2][0] && a:valueInfo[1][1] + 1 == a:valueInfo[2][1] let val = xpt#util#TextBetween(a:valueInfo[0 : 1]) let val = val[1:] let val = xpt#util#UnescapeChar(val,xp.l . xp.r) let nIndent = indent(a:valueInfo[0][0]) if isPostFilter if name =~ s:expandablePattern let val = xpt#util#UnescapeChar( val, '{$( ' ) let val = 'BuildIfNoChange(' . string( val ) . ')' endif let placeHolder.postFilter = xpt#flt#New(-nIndent,val) else let placeHolder.ontimeFilter = xpt#flt#New(-nIndent,val) endif endif return placeHolder endfunction fun! s:BuildMarksOfPlaceHolder(item,placeHolder,nameInfo,valueInfo) let renderContext = b:xptemplateData.renderContext let [item,placeHolder,nameInfo,valueInfo] = [a:item,a:placeHolder,a:nameInfo,a:valueInfo] if item.name == '' let markName = '``' . s:anonymouseIndex let s:anonymouseIndex += 1 else let markName = item.name . s:buildingSeqNr . '`' . ( placeHolder.isKey ? 'k' : (len(item.placeHolders)-1) ) endif let markPre = renderContext.markNamePre . markName . '`' call extend(placeHolder,{ 'mark':{ 'start':markPre . 'os', 'end':markPre . 'oe', }, }, 'force' ) if placeHolder.isKey call extend(placeHolder,{ 'editMark':{ 'start':markPre . 'is', 'end':markPre . 'ie', }, }, 'force' ) let placeHolder.innerMarks = placeHolder.editMark else let placeHolder.innerMarks = placeHolder.mark endif let valueInfo[2][1] += 1 if placeHolder.isKey let shift = (nameInfo[0] != nameInfo[1] && nameInfo[0][0] == nameInfo[1][0]) let nameInfo[1][1] -= shift let shift = (nameInfo[1][0] == nameInfo[2][0]) * (shift + 1) let nameInfo[2][1] -= shift if nameInfo[2] != nameInfo[3] let shift = (nameInfo[2][0] == nameInfo[3][0]) * (shift + 1) let nameInfo[3][1] -= shift endif call XPreplaceInternal(nameInfo[0],valueInfo[2],placeHolder.fullname) else if nameInfo[0][0] == nameInfo[3][0] let nameInfo[3][1] -= 1 endif call XPreplaceInternal(nameInfo[0],valueInfo[2],placeHolder.name) endif call XPMadd( placeHolder.mark.start, nameInfo[0], 'l' ) if placeHolder.isKey call XPMadd( placeHolder.editMark.start, nameInfo[1], 'l' ) call XPMadd( placeHolder.editMark.end, nameInfo[2], 'r' ) endif call XPMadd( placeHolder.mark.end, nameInfo[3], 'r' ) endfunction fun! s:AddItemToRenderContext(ctx,item) let [ctx,item] = [a:ctx,a:item] let exist = has_key(ctx.itemDict,item.name) if item.name != '' let ctx.itemDict[item.name] = item endif if exist return endif if ctx.phase != 'rendering' if ! s:AddToOrderList(ctx.firstList,item) call add(ctx.firstList,item) endif call filter( ctx.itemList, 'v:val isnot item' ) return endif if item.name == '' call add(ctx.itemList,item) elseif s:AddToOrderList(ctx.firstList,item) || s:AddToOrderList(ctx.lastList,item) return else call add(ctx.itemList,item) endif endfunction fun! s:AddToOrderList(list,item) let i = index(a:list,a:item.name) if i != -1 let a:list[i] = a:item return 1 else return 0 endif endfunction fun! s:BuildPlaceHolders(markRange) let s:buildingSeqNr += 1 let rc = 0 let x = b:xptemplateData let renderContext = b:xptemplateData.renderContext let snipObj = renderContext.snipObject let setting = snipObj.setting let xp = renderContext.snipObject.ptn let renderContext.itemDict = {} let current = [renderContext.item,renderContext.leadingPlaceHolder] let renderContext.action = 'build' if renderContext.firstList == [] let renderContext.firstList = copy(renderContext.snipSetting.comeFirst) endif if renderContext.lastList == [] let renderContext.lastList = copy(renderContext.snipSetting.comeLast) endif let renderContext.buildingMarkRange = copy(a:markRange) call XPRstartSession() call XPMgoto(a:markRange.start) let i = 0 while i < 10000 let i += 1 let markPos = s:NextLeftMark(a:markRange) let end = XPMpos(a:markRange.end) let nEnd = end[0] * 10000 + end[1] if markPos == [0,0] || markPos[0] * 10000 + markPos[1] >= nEnd break endif let nameInfo = s:GetNameInfo(end) if nameInfo[0] == [0,0] break endif call cursor(nameInfo[3]) let valueInfo = s:GetValueInfo(end) if valueInfo[0] == [0,0] break endif let placeHolder = s:CreatePlaceHolder(renderContext,nameInfo,valueInfo) let rc = 1 if renderContext.wrap != {} && setting.iswrap && get( placeHolder, 'name', 0 ) is setting.wrap && get( placeHolder, 'isKey', 0 ) let n = len(renderContext.wrap.lines) - 1 let indent = repeat( ' ', virtcol( nameInfo[ 0 ] ) - 1 ) let line = "\n" . indent . xp.l . placeHolder.leftEdge . xp.l . 'GetWrappedText()' . xp.l . placeHolder.rightEdge . xp.r let lines = repeat(line,n) let pos = copy(valueInfo[-1]) let pos[1] += 1 call XPreplaceInternal(pos,pos,lines) endif if has_key( placeHolder, 'include' ) call s:ApplyBuildTimeInclusion(placeHolder,nameInfo,valueInfo) call cursor(nameInfo[0]) elseif has_key( placeHolder, 'value' ) let to_build = s:ApplyInstantValue(placeHolder,nameInfo,valueInfo) if to_build call cursor(nameInfo[0]) endif else let item = s:BuildItemForPlaceHolder(placeHolder) call s:BuildMarksOfPlaceHolder(item,placeHolder,nameInfo,valueInfo) let renderContext.item = item let renderContext.leadingPlaceHolder = item.keyPH == s:nullDict ? placeHolder : item.keyPH call s:EvaluateEdge(xp,item,placeHolder) call s:ApplyPreValues(placeHolder) call xpt#rctx#AddDefaultPHFilters(renderContext,placeHolder) call cursor(XPMpos(placeHolder.mark.end)) endif endwhile let renderContext.itemList = renderContext.firstList + renderContext.itemList + renderContext.lastList call filter( renderContext.itemList, 'type(v:val) != 1' ) let renderContext.firstList = [] let renderContext.lastList = [] let end = XPMpos(a:markRange.end) call cursor(end) let [renderContext.item,renderContext.leadingPlaceHolder] = current let renderContext.action = '' call XPRendSession() return rc endfunction fun! s:NextLeftMark(markRange) let x = b:xptemplateData let renderContext = x.renderContext let xp = renderContext.snipObject.ptn let curline = getline( line(".") ) let c = col(".") if len(curline) > 1 && curline[c - 1] == xp.l return [ line("."), c ] endif while 1 let end = XPMpos(a:markRange.end) let nEnd = end[0] * 10000 + end[1] let ptn = xpt#util#CharsPattern(xp.l . xp.r) let markPos = searchpos( '\V\\\*' . ptn, 'cW' ) if markPos == [0,0] || markPos[0] * 10000 + markPos[1] >= nEnd break endif let content = getline(markPos[0])[markPos[1] - 1 :] let char = matchstr( content, '\V' . ptn ) let content = matchstr( content, '^\\*' ) let newEsc = repeat( '\', len( content ) / 2 ) call XPreplaceInternal( markPos, [ markPos[0], markPos[1] + len( content ) ], newEsc, { 'doPostJob' : 1 } ) if len(content) % 2 == 0 && char == xp.l call cursor([markPos[0],markPos[1] + len(newEsc)]) break endif call cursor([markPos[0],markPos[1] + len(newEsc) + 1]) endwhile return markPos endfunction fun! s:EvaluateEdge(xp,item,ph) if !a:ph.isKey return endif let eval_ptn = '\V' . a:xp.item_var . '\|' . a:xp.item_func if a:ph.leftEdge =~ eval_ptn let a:ph.leftEdge = s:EvalAsFilter(a:ph.leftEdge, XPMpos(a:ph.mark.start)) call XPreplaceByMarkInternal(a:ph.mark.start,a:ph.editMark.start, a:ph.leftEdge) endif if a:ph.rightEdge =~ eval_ptn let a:ph.rightEdge = s:EvalAsFilter(a:ph.rightEdge, XPMpos(a:ph.editMark.end)) call XPreplaceByMarkInternal(a:ph.editMark.end,a:ph.mark.end, a:ph.rightEdge) endif let a:ph.fullname = a:ph.leftEdge . a:item.name . a:ph.rightEdge let a:item.fullname = a:ph.fullname endfunction fun! s:EvalAsFilter(raw,start_pos) let x = b:xptemplateData let rctx = x.renderContext let flt = xpt#flt#New(0,a:raw) let flt_rst = s:EvalFilter(flt,[ rctx.ftScope.funcs, rctx.snipSetting.variables, ]) return s:IndentFilterText(flt_rst,a:start_pos) endfunction fun! s:IndentFilterText(flt_rst,start) let lines = split( a:flt_rst.text, '\n', 1 ) if get(a:flt_rst, 'parseIndent', 1) call xpt#indent#IndentToTab(lines) endif let indent = s:IndentAt(a:start,a:flt_rst) call xpt#indent#ToActualIndent(lines,indent) return join(lines, "\n") endfunction fun! s:ApplyBuildTimeInclusion(placeHolder,nameInfo,valueInfo) let renderContext = b:xptemplateData.renderContext let tmplDict = renderContext.ftScope.allTemplates let placeHolder = a:placeHolder let nameInfo = a:nameInfo let valueInfo = a:valueInfo let [incName,params] = s:ParseInclusionStatement(renderContext.snipObject,placeHolder.include) if !has_key(tmplDict,incName) call XPT#warn( "unknown inclusion :" . incName ) return endif let incTmplObject = tmplDict[incName] call s:ParseSnippet(incTmplObject,renderContext.ftScope) call s:MergeSetting(renderContext.snipSetting,incTmplObject.setting) let incSnip = s:ReplacePHInSubSnip(renderContext.snipObject,incTmplObject,params) let incSnip = s:AddIndent(incSnip,nameInfo[0][1]-1) let valueInfo[-1][1] += 1 call XPreplaceInternal(nameInfo[0],valueInfo[-1],incSnip) endfunction fun! s:ApplyInstantValue(placeHolder,nameInfo,valueInfo) let rctx = b:xptemplateData.renderContext let ph = a:placeHolder let nameInfo = a:nameInfo let valueInfo = a:valueInfo let start = a:nameInfo[0] let combined_flt_rst = {} let text = '' let to_build = 0 for k in [ 'leftEdge', 'name', 'rightEdge' ] if ph[k] != '' let flt = xpt#flt#New(0,ph[k]) let flt_rst = s:EvalFilter(flt,[ rctx.ftScope.funcs, rctx.snipSetting.variables, ]) if flt_rst.rc == 0 continue endif let text .= get( flt_rst, 'text', '' ) if get(flt_rst, 'nIndent', 0) != 0 let combined_flt_rst.nIndent = flt_rst.nIndent endif if flt_rst.action == 'build' let to_build = 1 endif endif endfor let valueInfo[-1][1] += 1 let combined_flt_rst.text = text let text = s:IndentFilterText(combined_flt_rst,start) call XPreplaceInternal( nameInfo[0], valueInfo[-1], text, { 'doJobs' : 1 } ) return to_build endfunction fun! s:IndentAt(start,flt_rst) let filter_indent_offset = get(a:flt_rst, 'nIndent', 0) let indent = xpt#indent#IndentBefore(a:start) let indent += filter_indent_offset let indent = max([0,indent]) return indent endfunction fun! s:ApplyPreValues(placeHolder) let rctx = b:xptemplateData.renderContext let setting = rctx.snipSetting let name = a:placeHolder.name let preValue = name == '' ? g:EmptyFilter : get(setting.preValues,name,g:EmptyFilter) if preValue is g:EmptyFilter let preValue = get( a:placeHolder, 'ontimeFilter', get(setting.defaultValues,name,g:EmptyFilter)) endif if preValue is g:EmptyFilter return endif let flt_rst = s:EvalFilter(preValue,[ rctx.ftScope.funcs, rctx.snipSetting.variables, ]) if flt_rst.rc is 0 || ! has_key(flt_rst, 'text') return endif let mark_name = s:GetPHReplacingMarkName(flt_rst) let marks = a:placeHolder[mark_name] let s = XPMpos(marks.start) let text = s:IndentFilterText(flt_rst,s) try call XPreplaceByMarkInternal(marks.start,marks.end,text) catch /.*/ call s:Crash( v:exception . " while update preset text" ) endtry endfunction fun! s:BuildItemForPlaceHolder(placeHolder) let renderContext = b:xptemplateData.renderContext if has_key(renderContext.itemDict,a:placeHolder.name) let item = renderContext.itemDict[a:placeHolder.name] else let item = { 'name' : a:placeHolder.name, 'fullname':a:placeHolder.name, 'initValue':a:placeHolder.name, 'processed':0, 'placeHolders':[], 'keyPH':s:nullDict, 'behavior':{}, } endif let inPrevBuild = (index(renderContext.itemList,item) >= 0) call s:AddItemToRenderContext(renderContext,item) if a:placeHolder.isKey let item.keyPH = a:placeHolder let item.fullname = a:placeHolder.fullname else if renderContext.phase != 'rendering' && inPrevBuild call insert(item.placeHolders,a:placeHolder) else call add(item.placeHolders,a:placeHolder) endif endif return item endfunction fun! s:XPTvisual() if &selectmode =~ 'cmd' normal! v\ else normal! v endif endfunction fun! s:CleanupCurrentItem() let renderContext = b:xptemplateData.renderContext call s:ClearItemMapping(renderContext) endfunction fun! s:ShiftBackward() let renderContext = b:xptemplateData.renderContext if empty(renderContext.history) return '' endif call s:CleanupCurrentItem() let his = remove(renderContext.history,-1) call s:PushBackItem() let renderContext.item = his.item let renderContext.leadingPlaceHolder = his.leadingPlaceHolder let leader = renderContext.leadingPlaceHolder call XPMsetLikelyBetween(leader.mark.start,leader.mark.end) return s:SelectCurrent() endfunction fun! s:PushBackItem() let renderContext = b:xptemplateData.renderContext let item = renderContext.item if !renderContext.leadingPlaceHolder.isKey call insert(item.placeHolders,renderContext.leadingPlaceHolder,0) endif call insert(renderContext.itemList,item,0) let item.processed = 1 endfunction fun! s:ShiftForward(action) let x = b:xptemplateData let renderContext = x.renderContext if pumvisible() if XPPhasSession() return XPPend() . "\=" . s:sid . 'ShiftForward(' . string( a:action ) . ")\" else if g:xptemplate_move_even_with_pum return s:close_pum . "\" . '=XPTforceForward(' . string( a:action ) . ")\" else if x.canNavFallback let x.fallbacks = [ [ "\XPTnavFallback", 'feed' ], [ "\=XPTforceForward(" . string( a:action ) . ")\", 'expr' ], ] return XPT#fallback(x.fallbacks) else return XPPend() . "\=" . s:sid . 'ShiftForward(' . string( a:action ) . ")\" endif endif endif else if XPPhasSession() call XPPend() endif return s:close_pum . "\" . '=XPTforceForward(' . string( a:action ) . ")\" endif endfunction fun! XPTforceForward(action) call XPMupdate('force') if s:XPTupdate() < 0 return '' endif if s:FinishCurrent(a:action) < 0 return '' endif let postaction = s:GotoNextItem() return postaction endfunction fun! s:FinishCurrent(action) let renderContext = b:xptemplateData.renderContext let marks = renderContext.leadingPlaceHolder.mark call s:CleanupCurrentItem() let rc = s:XPTupdate() if rc == -1 return -1 endif let name = renderContext.item.name if a:action ==# 'clear' call XPreplace(XPMpos( marks.start ),XPMpos( marks.end ), '') endif let [post,built] = s:ApplyPostFilter() if name != '' let renderContext.namedStep[name] = post endif if built || a:action ==# 'clear' call s:RemoveCurrentMarks() else let renderContext.history += [{ 'item':renderContext.item, 'leadingPlaceHolder':renderContext.leadingPlaceHolder } ] endif return 0 endfunction fun! s:RemoveCurrentMarks() let renderContext = b:xptemplateData.renderContext let item = renderContext.item let leader = renderContext.leadingPlaceHolder call XPMremoveStartEnd(leader.mark) if has_key( leader, 'editMark' ) call XPMremoveStartEnd(leader.editMark) endif for ph in item.placeHolders call XPMremoveStartEnd(ph.mark) endfor endfunction fun! s:ApplyPostFilter() let rctx = b:xptemplateData.renderContext let rctx.activeLeaderMarks = 'mark' let posts = rctx.snipSetting.postFilters let name = rctx.item.name let leader = rctx.leadingPlaceHolder let marks = rctx.leadingPlaceHolder[rctx.activeLeaderMarks] let rctx.phase = 'post' let typed = xpt#util#TextBetween(XPMposStartEnd(marks)) if rctx.item.name != '' let rctx.namedStep[rctx.item.name] = typed endif let groupPostFilter = get(posts,name,g:EmptyFilter) let leaderPostFilter = get( leader, 'postFilter', g:EmptyFilter ) let filter = groupPostFilter is g:EmptyFilter ? leaderPostFilter : groupPostFilter let hadBuilt = 0 if filter isnot g:EmptyFilter let flt_rst = s:EvalPostFilter(filter,typed,leader) let mark_name = s:GetPHReplacingMarkName(flt_rst) let marks = rctx.leadingPlaceHolder[mark_name] let ori_flt_rst = copy(flt_rst) call XPMsetLikelyBetween(marks.start,marks.end) if flt_rst.rc != 0 if has_key( flt_rst, 'text' ) if flt_rst.text !=# typed let [start,end] = XPMposStartEnd(marks) if mark_name == 'mark' call s:RemoveEditMark(leader) endif call xpt#settingswitch#Switch(b:xptemplateData.settingWrap) let text = s:IndentFilterText(flt_rst,start) call XPreplace(start,end,text) endif endif if flt_rst.action == 'build' let rctx.firstList = [] let buildrc = s:BuildPlaceHolders(marks) if 0 > buildrc return [s:Crash(),1] endif let hadBuilt = 0 < buildrc let rctx.phase = 'post' endif endif endif if groupPostFilter is g:EmptyFilter call s:UpdateFollowingPlaceHoldersWith(typed,{}) return [typed,hadBuilt] else call s:UpdateFollowingPlaceHoldersWith( typed, { 'post' : ori_flt_rst } ) if hadBuilt return [typed,hadBuilt] else return [ get(flt_rst, 'text', typed), hadBuilt ] endif endif endfunction fun! s:RemoveEditMark(ph) if has_key( a:ph, 'editMark' ) call XPMremoveStartEnd(a:ph.editMark) let a:ph.innerMarks = a:ph.mark unlet a:ph.editMark endif endfunction fun! s:EvalPostFilter(filter,typed,leader) let renderContext = b:xptemplateData.renderContext let pos = XPMpos(a:leader.mark.start) let pos[1] = 1 let startMark = XPMmarkAfter(pos) let flt_rst = s:EvalFilter(a:filter,[ renderContext.ftScope.funcs, renderContext.snipSetting.variables, { '$UserInput' : a:typed } ] ) return flt_rst endfunction fun! s:GotoNextItem() let action = s:DoGotoNextItem() call xpt#settingswitch#Restore(b:xptemplateData.settingWrap) return action endfunction fun! s:DoGotoNextItem() let renderContext = b:xptemplateData.renderContext let placeHolder = s:ExtractOneItem() if placeHolder == s:nullDict call cursor(XPMpos(renderContext.marks.tmpl.end)) return s:FinishRendering(1) endif let phPos = XPMpos(placeHolder.mark.start) if phPos == [0,0] return s:Crash('failed to find position of mark:' . placeHolder.mark.start) endif let leader = renderContext.leadingPlaceHolder let leaderMark = leader.innerMarks call XPMsetLikelyBetween(leaderMark.start,leaderMark.end) if renderContext.item.processed let renderContext.phase = 'fillin' return s:SelectCurrent() endif let oldRenderContext = renderContext let postaction = s:InitItem() let renderContext = b:xptemplateData.renderContext let leader = renderContext.leadingPlaceHolder if renderContext.processing && empty(renderContext.itemList) && !has_key(renderContext.snipSetting.postFilters,renderContext.item.name) && !has_key( leader, 'postFilter' ) && empty(renderContext.item.placeHolders) && XPMpos(leader.mark.end) == XPMpos(renderContext.marks.tmpl.end) && postaction !~ '' let pp = s:FinishRendering() return postaction endif if !renderContext.processing return postaction endif try call XPMsetLikelyBetween(leader.mark.start,leader.mark.end) catch /.*/ return s:Crash() endtry if postaction == '' if oldRenderContext == renderContext || oldRenderContext.level < renderContext.level call cursor(XPMpos(renderContext.leadingPlaceHolder.innerMarks.end)) endif return '' else return postaction endif endfunction fun! s:ExtractOneItem() let renderContext = b:xptemplateData.renderContext let itemList = renderContext.itemList let [renderContext.item,renderContext.leadingPlaceHolder] = [{},{}] if empty(itemList) return s:nullDict endif let item = itemList[0] let renderContext.itemList = renderContext.itemList[1 :] let renderContext.item = item if empty(item.placeHolders) && item.keyPH == s:nullDict call XPT#warn( "item without placeholders!" ) return s:nullDict endif if item.keyPH == s:nullDict let renderContext.leadingPlaceHolder = item.placeHolders[0] let item.placeHolders = item.placeHolders[1:] else let renderContext.leadingPlaceHolder = item.keyPH endif return renderContext.leadingPlaceHolder endfunction fun! s:HandleDefaultValueAction(flt_rst) let x = b:xptemplateData let rctx = x.renderContext let leader = rctx.leadingPlaceHolder if a:flt_rst.action ==# 'expandTmpl' let marks = leader.mark call XPreplace(XPMpos( marks.start ), XPMpos( marks.end ), '') call XPMsetLikelyBetween(marks.start,marks.end) return XPTemplateStart(0, {'startPos' : getpos(".")[1:2], 'tmplName' : a:flt_rst.tmplName}) elseif a:flt_rst.action ==# 'pum' return s:DefaultValuePumHandler(rctx,a:flt_rst) elseif a:flt_rst.action ==# 'finishTemplate' return s:ActionFinish(rctx,a:flt_rst) elseif a:flt_rst.action ==# 'build' if s:FillinLeader(a:flt_rst) is s:BROKEN || s:BuildLeaderText(a:flt_rst) is s:BROKEN return s:BROKEN endif return s:GotoNextItem() elseif a:flt_rst.action ==# 'text' if s:FillinLeader(a:flt_rst) is s:BROKEN return s:BROKEN endif else if s:FillinLeader(a:flt_rst) is s:BROKEN return s:BROKEN endif endif if a:flt_rst.nav == 'next' if x.renderContext.processing let postaction = s:ShiftForward( '' ) return postaction else return '' endif endif return s:SelectCurrent() endfunction fun! s:GetLeaderOpPos(flt_rst) let marks = s:GetLeaderOpMarks(a:flt_rst) let [s,e] = XPMposStartEnd(marks) return [s,e] endfunction fun! s:GetLeaderOpMarks(flt_rst) let rctx = b:xptemplateData.renderContext let mark_name = s:GetPHReplacingMarkName(a:flt_rst) return rctx.leadingPlaceHolder[mark_name] endfunction fun! s:GetPHReplacingMarkName(flt_rst) let rctx = b:xptemplateData.renderContext let mark_name = get(a:flt_rst, 'marks') if mark_name is 0 let mark_name = xpt#rctx#DefaultMarks(rctx) endif return mark_name endfunction fun! s:ActionFinish(renderContext,flt_rst) let rctx = b:xptemplateData.renderContext let [start,end] = s:GetLeaderOpPos(a:flt_rst) if start[0] != 0 && end[0] != 0 if a:flt_rst.rc isnot 0 if has_key( a:flt_rst, 'text' ) let text = s:IndentFilterText(a:flt_rst,start) call XPreplace(start,end,text) endif endif endif if s:FinishCurrent( '' ) < 0 return '' endif call cursor(XPMpos(rctx.leadingPlaceHolder.mark.end)) let xptObj = b:xptemplateData if empty(xptObj.stack) || 1 return s:FinishRendering() else return '' endif endfunction fun! s:FillinLeader(flt_rst) let x = b:xptemplateData let rctx = x.renderContext let mark_name = s:GetPHReplacingMarkName(a:flt_rst) let marks = rctx.leadingPlaceHolder[mark_name] let [s,e] = XPMposStartEnd(marks) if s[0] == 0 || e[0] == 0 call s:Crash('leader marks not found:' . string(mark_name)) return s:BROKEN endif if a:flt_rst.rc is 0 return s:NONE endif if has_key( a:flt_rst, 'text' ) call xpt#settingswitch#Switch(b:xptemplateData.settingWrap) let text = s:IndentFilterText(a:flt_rst,s) call XPreplace(s,e,text) endif call s:XPTupdate() return s:DONE endfunction fun! s:BuildLeaderText(flt_rst) let rctx = b:xptemplateData.renderContext let mark_name = s:GetPHReplacingMarkName(a:flt_rst) let marks = rctx.leadingPlaceHolder[mark_name] if a:flt_rst.action == 'build' let build_rc = s:BuildPlaceHolders(marks) if build_rc is s:BROKEN call s:Crash('building place holder failed') elseif build_rc is s:BUILT call s:RemoveCurrentMarks() end return build_rc end return s:NONE endfunction fun! s:DefaultValuePumHandler(renderContext,flt_rst) let pumlen = len(a:flt_rst.pum) if pumlen > 1 return s:DefaultValueShowPum(a:renderContext,a:flt_rst) endif if pumlen == 0 let a:flt_rst.text = '' elseif pumlen == 1 let a:flt_rst.text = a:flt_rst.pum[0] endif if s:FillinLeader(a:flt_rst) is s:BROKEN return s:BROKEN endif return s:SelectCurrent() endfunction fun! s:DefaultValueShowPum(renderContext,flt_rst) let leader = a:renderContext.leadingPlaceHolder let [start,end] = XPMposStartEnd(leader.innerMarks) call XPreplace( start, end, '') call cursor(start) call s:CallPlugin( 'ph_pum', 'before' ) let pumsess = XPPopupNew(s:ItemPumCB,{},a:flt_rst.pum) call pumsess.SetAcceptEmpty( get( a:flt_rst, 'acceptEmpty', g:xptemplate_ph_pum_accept_empty ) ) call pumsess.SetOption({ 'tabNav':g:xptemplate_pum_tab_nav } ) return pumsess.popup( col("."), { 'doCallback' : 1, 'enlarge' : 0 } ) endfunction fun! s:InitItem() let renderContext = b:xptemplateData.renderContext let currentItem = renderContext.item let leaderMark = renderContext.leadingPlaceHolder.innerMarks let currentItem.initValue = xpt#util#TextBetween(XPMposStartEnd(leaderMark)) call xpt#rctx#SwitchPhase(renderContext,s:renderPhase.iteminit) let postaction = s:ApplyDefaultValue() let renderContext = b:xptemplateData.renderContext if renderContext.processing && currentItem == renderContext.item let renderContext.item.initValue = xpt#util#TextBetween(XPMposStartEnd(leaderMark)) endif if renderContext.phase == s:renderPhase.iteminit call s:InitItemMapping() call s:InitItemTempMapping() call xpt#rctx#SwitchPhase(renderContext,s:renderPhase.fillin) endif return postaction endfunction fun! s:ApplyDefaultValue() let rctx = b:xptemplateData.renderContext let leader = rctx.leadingPlaceHolder let defs = rctx.snipSetting.defaultValues if has_key(defs,leader.name) && defs[leader.name].force let filter = defs[leader.name] else let filter = get( leader, 'ontimeFilter', get(defs,leader.name, g:EmptyFilter)) endif if filter is g:EmptyFilter call s:XPTupdate() return s:SelectCurrent() endif let rctx.activeLeaderMarks = 'innerMarks' let typed = xpt#util#TextBetween(XPMposStartEnd(leader.innerMarks)) let flt_rst = s:EvalFilter(filter,[ rctx.ftScope.funcs, rctx.snipSetting.variables, { '$UserInput': typed } ] ) if flt_rst.rc is 0 return s:SelectCurrent() endif return s:HandleDefaultValueAction(flt_rst) endfunction fun! XPTmappingEval(str) if pumvisible() if XPPhasSession() return XPPend() . "\=XPTmappingEval(" . string(a:str) . ")\" else return "\\\\=XPTmappingEval(" . string(a:str) . ")\" endif endif let rc = s:XPTupdate() if rc != 0 return '' endif let x = b:xptemplateData let typed = xpt#util#TextBetween( XPMposStartEnd( x.renderContext.leadingPlaceHolder.mark)) let filter = xpt#flt#New(0,a:str) let flt_rst = s:EvalFilter(filter,[ x.renderContext.ftScope.funcs, x.renderContext.snipSetting.variables, { '$UserInput' : typed } ] ) if flt_rst.rc is 0 return '' endif let postAction = s:HandleMapAction(flt_rst) return postAction endfunction fun! s:InitItemMapping() let renderContext = b:xptemplateData.renderContext let mappings = renderContext.snipObject.setting.mappings let item = renderContext.item if has_key(mappings,item.name) call xpt#msvr#Save(mappings[item.name].saver) for [key,mapping] in items(mappings[item.name].keys) exe 'inoremap ' key '=XPTmappingEval(' string( mapping.text ) ')' endfor endif endfunction fun! s:InitItemTempMapping() let renderContext = b:xptemplateData.renderContext let mappings = renderContext.tmpmappings if !has_key( mappings, 'saver' ) return endif for keys in mappings.keys call xpt#msvr#Add( mappings.saver, 'i', keys[0] ) endfor call xpt#msvr#Save(mappings.saver) for keys in mappings.keys exe 'inoremap ' keys[0] '=XPTmappingEval(' string( keys[1] ) ')' endfor endfunction fun! XPTmapKey(left,right) let renderContext = b:xptemplateData.renderContext let mappings = renderContext.tmpmappings if renderContext.phase != s:renderPhase.iteminit call s:log.Warn( "Not in [iteminit] phase, mapping ingored" ) return endif if !has_key( mappings, 'saver' ) let mappings.saver = xpt#msvr#New(1) let mappings.keys = [] endif call add(mappings.keys,[a:left,a:right]) endfunction fun! s:ClearItemMapping(rctx) let renderContext = a:rctx let mappings = renderContext.tmpmappings if has_key( mappings, 'saver' ) call xpt#msvr#Restore(mappings.saver) endif let mappings = renderContext.snipObject.setting.mappings let item = renderContext.item if has_key(mappings,item.name) call xpt#msvr#Restore(mappings[item.name].saver) endif endfunction fun! s:SelectCurrent() let ph = b:xptemplateData.renderContext.leadingPlaceHolder let marks = ph.innerMarks let [ctl,cbr] = XPMposStartEnd(marks) if ctl == cbr call cursor(ctl) call XPMupdateStat() return '' else call cursor(ctl) call s:XPTvisual() if &l:selection == 'exclusive' call cursor(cbr) else if cbr[1] == 1 call cursor( cbr[0] - 1, col( [ cbr[0] - 1, '$' ] ) ) else call cursor(cbr[0],cbr[1] - 1) endif endif normal! v if &selectmode =~ 'cmd' call feedkeys( "\gv", 'nt' ) else call feedkeys( "\gv\", 'nt' ) endif call XPMupdateStat() return '' endif endfunction fun! s:EvalFilter(filter,closures) let rctx = b:xptemplateData.renderContext let snip = rctx.snipObject let r = xpt#flt#Eval(snip,a:filter,a:closures) call s:LoadFilterActionSnippet(r) return r endfunction fun! s:LoadFilterActionSnippet(flt_rst) let renderContext = b:xptemplateData.renderContext if has_key( a:flt_rst, 'snippet' ) let allsnip = renderContext.ftScope.allTemplates let snipname = a:flt_rst.snippet if has_key(allsnip,snipname) let snip = allsnip[snipname] call s:ParseSnippet(snip,renderContext.ftScope) call s:MergeSetting(renderContext.snipSetting, snip.setting) let a:flt_rst.text = snip.snipText else call XPT#warn( 'snippet "' . snipname . '" not found' ) end end endfunction fun! s:Goback() let renderContext = b:xptemplateData.renderContext return s:SelectCurrent() endfunction fun! s:XPTinitMapping() let literal_chars = '' . 'abcdefghijklmnopqrstuvwxyz' . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . '1234567890' . '!@#$%^&*()' . '`~-_=+[{]}\;:"'',<.>/?' let literalKeys = split( literal_chars, '\V\s\*' ) let literalKeys = map( literalKeys, '"s_".v:val' ) + [ 's_', 's_\|', ] let b:mapSaver = xpt#msvr#New(1) call xpt#msvr#AddList(b:mapSaver, 'i_' . g:xptemplate_nav_next, 's_' . g:xptemplate_nav_next, 'i_' . g:xptemplate_nav_prev, 's_' . g:xptemplate_nav_prev, 's_' . g:xptemplate_nav_cancel, 's_' . g:xptemplate_to_right, 'n_' . g:xptemplate_goback, 'i_' . g:xptemplate_goback, 'i_', 's_', 's_', ) if g:xptemplate_nav_next_2 != g:xptemplate_nav_next call xpt#msvr#AddList(b:mapSaver, 'i_' . g:xptemplate_nav_next_2, 's_' . g:xptemplate_nav_next_2, ) endif let b:mapLiteral = xpt#msvr#New(1) call xpt#msvr#AddList(b:mapLiteral,literalKeys) let b:xptemplateData.settingSwitch = xpt#settingswitch#New() call xpt#settingswitch#AddList(b:xptemplateData.settingSwitch, [ '&l:textwidth', '0' ], [ '&l:lazyredraw', '1' ], [ '&l:indentkeys', { 'exe' : 'setl indentkeys-=*' } ], [ '&l:cinkeys', { 'exe' : 'setl cinkeys-=*' } ], ) let b:xptemplateData.settingWrap = xpt#settingswitch#New() call xpt#settingswitch#Add(b:xptemplateData.settingWrap, '&l:wrap', '1') endfunction fun! s:XPTCR() let [ l, c ] = [ line( "." ), col( "." ) ] let textFollowing = getline(l)[c - 1 :] if textFollowing !~ '\V\^\s' || !&autoindent return "\" else let spaces = matchstr( textFollowing, '\V\^\s\+' ) return "\" . spaces . repeat( "\", len( spaces ) ) endif endfunction fun! s:ApplyMap() let x = b:xptemplateData let renderContext = x.renderContext call xpt#settingswitch#Switch(b:xptemplateData.settingSwitch) call xpt#msvr#Save(b:mapSaver) call xpt#msvr#Save(b:mapLiteral) call xpt#msvr#UnmapAll(b:mapSaver) call xpt#msvr#Literalize( b:mapLiteral, { 'insertAsSelect' : 1 } ) exe 'inoremap ' g:xptemplate_nav_prev '=ShiftBackward()' exe 'inoremap ' g:xptemplate_nav_next '=ShiftForward("")' exe 'snoremap ' g:xptemplate_nav_cancel 'i=ShiftForward("clear")' exe 'nnoremap ' g:xptemplate_goback 'i=Goback()' exe 'inoremap ' g:xptemplate_goback ' =Goback()' exe 'imap ' g:xptemplate_hook_before_cr . 'XPT_map_CR' snoremap i snoremap d if g:xptemplate_nav_next_2 != g:xptemplate_nav_next exe 'inoremap ' g:xptemplate_nav_next_2 '=ShiftForward("")' exe 'snoremap ' g:xptemplate_nav_next_2 '`>a=ShiftForward("")' endif if &selection == 'inclusive' exe 'snoremap ' g:xptemplate_nav_prev '`>a=ShiftBackward()' exe 'snoremap ' g:xptemplate_nav_next '`>a=ShiftForward("")' exe "snoremap ".g:xptemplate_to_right." `>a" else exe 'snoremap ' g:xptemplate_nav_prev '`>i=ShiftBackward()' exe 'snoremap ' g:xptemplate_nav_next '`>i=ShiftForward("")' exe "snoremap ".g:xptemplate_to_right." `>i" endif endfunction fun! s:ClearMap() call xpt#settingswitch#Restore(b:xptemplateData.settingSwitch) call xpt#msvr#Restore(b:mapLiteral) call xpt#msvr#Restore(b:mapSaver) endfunction fun! XPTbufData() if !exists( 'b:xptemplateData' ) call XPTemplateInit() endif return b:xptemplateData endfunction fun! XPTnewSnipScope(filename) let sf = xpt#snipfile#New(a:filename) let b:xptemplateData.snipFileScope = sf return sf endfunction fun! XPTsnipScope() return b:xptemplateData.snipFileScope endfunction fun! XPTemplateInit() if exists( 'b:xptemplateData' ) return endif call xpt#buf#New() call XPMsetBufSortFunction( function( 'XPTmarkCompare' ) ) call s:XPTinitMapping() endfunction fun! s:SynNameStack(l,c) if exists( '*synstack' ) let ids = synstack(a:l,a:c) if empty(ids) return [] endif let names = [] for id in ids let names = names + [synIDattr(id, "name")] endfor return names else return [synIDattr( synID( a:l, a:c, 0 ), "name" )] endif endfunction fun! s:UpdateFollowingPlaceHoldersWith(contentTyped,option) let renderContext = b:xptemplateData.renderContext let useGroupPost = renderContext.phase == 'post' && has_key( a:option, 'post' ) if useGroupPost let group_flt_rst = a:option.post endif call XPRstartSession() let phList = renderContext.item.placeHolders try for ph in phList let flt = renderContext.phase == 'post' ? get( ph, 'postFilter', get( ph, 'ontimeFilter', g:EmptyFilter ) ) : get( ph, 'ontimeFilter', g:EmptyFilter ) let phStartPos = XPMpos(ph.mark.start) let [phln,phcol] = phStartPos if flt isnot g:EmptyFilter let flt_rst = s:EvalFilter(flt,[ renderContext.ftScope.funcs, renderContext.snipSetting.variables, { '$UserInput' : a:contentTyped } ] ) elseif useGroupPost let flt_rst = copy(group_flt_rst) else let flt_rst = { 'nIndent': -XPT#getIndentNr( phln, phcol ), 'text':a:contentTyped } endif let flttext = s:IndentFilterText(flt_rst,phStartPos) let text = xpt#util#TextBetween(XPMposStartEnd(ph.mark)) if text !=# flttext call XPreplaceByMarkInternal(ph.mark.start,ph.mark.end,flttext) endif endfor catch /.*/ call XPT#error(v:exception) finally call XPRendSession() endtry endfunction fun! s:Crash(...) let msg = "XPTemplate session ends: " . join( a:000, "\n" ) call XPPend() let x = b:xptemplateData call s:ClearItemMapping(x.renderContext) while !empty(x.stack) let rctx = remove(x.stack,-1) call s:ClearItemMapping(rctx) endwhile call s:ClearMap() let x.stack = [] let x.renderContext = xpt#rctx#New(x) call XPMflushWithHistory() call XPT#warn(msg) call s:CallPlugin( 'finishAll', 'after' ) return '' endfunction fun! s:XPTupdateTyping() let rc = s:XPTupdate() if rc != 0 return rc endif let renderContext = b:xptemplateData.renderContext if 'fillin' != renderContext.phase return rc endif let leader = renderContext.leadingPlaceHolder let ontypeFilters = renderContext.snipSetting.ontypeFilters let flt = get(ontypeFilters,leader.name,g:EmptyFilter) if flt isnot g:EmptyFilter call s:HandleOntypeFilter(flt) endif return rc endfunction fun! s:HandleOntypeFilter(filter) let renderContext = b:xptemplateData.renderContext let leader = renderContext.leadingPlaceHolder let [start,end] = XPMposStartEnd(leader.mark) let contentTyped = xpt#util#TextBetween([start,end]) let flt_rst = s:EvalFilter(a:filter,[ renderContext.ftScope.funcs, renderContext.snipSetting.variables, { '$UserInput' : contentTyped } ] ) if 0 is flt_rst.rc return endif if has_key( flt_rst, 'action' ) call s:HandleOntypeAction(renderContext,flt_rst) endif endfunction fun! s:HandleOntypeAction(renderContext,flt_rst) let postaction = s:HandleAction(a:flt_rst) if '' != postaction call feedkeys( postaction, 'n' ) endif endfunction fun! s:HandleMapAction(flt_rst) let rctx = b:xptemplateData.renderContext if a:flt_rst.action == 'finishTemplate' let postaction = s:ActionFinish(rctx,a:flt_rst) return postaction elseif a:flt_rst.action == '' endif let postaction = get(a:flt_rst, 'text', '') if a:flt_rst.nav == 'next' if rctx.processing let postaction .= s:ShiftForward( '' ) return postaction else return '' endif endif return postaction endfunction fun! s:HandleAction(flt_rst) let rctx = b:xptemplateData.renderContext let [s,e] = s:GetLeaderOpPos(a:flt_rst) let postaction = '' if a:flt_rst.action == 'text' if has_key( a:flt_rst, 'text' ) call XPreplace(s,e,a:flt_rst.text) endif elseif a:flt_rst.action == 'finishTemplate' let postaction = s:ActionFinish(rctx,a:flt_rst) return postaction elseif a:flt_rst.action == '' endif if a:flt_rst.nav == 'next' if rctx.processing let postaction = s:ShiftForward( '' ) return postaction else return '' endif endif return postaction endfunction fun! s:IsUpdateCondition(renderContext) if a:renderContext.phase == 'uninit' call XPMflushWithHistory() return 0 endif if !a:renderContext.processing call XPMupdate() return 0 endif return 1 endfunction fun! s:UpdateMarksAccordingToLeaderChanges(renderContext) let leaderMark = a:renderContext.leadingPlaceHolder.mark let innerMarks = a:renderContext.leadingPlaceHolder.innerMarks let [start,end] = XPMposList(leaderMark.start,leaderMark.end) if start[0] == 0 || end[0] == 0 throw 'XPM:mark_lost:' . string( start[0] == 0 ? leaderMark.start : leaderMark.end ) endif if XPMhas(innerMarks.start,innerMarks.end) call XPMsetLikelyBetween(innerMarks.start,innerMarks.end) else call XPMsetLikelyBetween(leaderMark.start,leaderMark.end) endif let rc = XPMupdate() if a:renderContext.phase == 'fillin' if rc is g:XPM_RET.updated || (type(rc) == type([]) && (rc[0] != leaderMark.start && rc[0] != innerMarks.start || rc[1] != leaderMark.end && rc[1] != innerMarks.end)) if g:xptemplate_strict == 2 throw 'XPT:changes outside of place holder' elseif g:xptemplate_strict == 1 undo call XPMupdate() call XPT#warn( "editing OUTSIDE place holder is not allowed whne g:xptemplate_strict=1, use " . g:xptemplate_goback . " to go back" ) return g:XPT_RC.canceled else endif endif endif return rc endfunction fun! s:XPTupdate() call XPTemplateInit() let renderContext = b:xptemplateData.renderContext if !s:IsUpdateCondition(renderContext) return 0 endif try let rc = s:UpdateMarksAccordingToLeaderChanges(renderContext) if g:XPT_RC.canceled is rc return 0 endif call s:DoUpdate(renderContext,rc) return 0 catch /^XP.*/ call s:Crash(v:exception) return -1 finally call XPMupdateStat() endtry endfunction fun! s:DoUpdate(renderContext,changeType) let renderContext = a:renderContext let contentTyped = xpt#util#TextBetween(XPMposStartEnd(renderContext.leadingPlaceHolder.mark)) call s:CallPlugin("update", 'before') if type(a:changeType) == type([]) || a:changeType is g:XPM_RET.likely_matched || a:changeType is g:XPM_RET.no_updated_made let relPos = s:RecordRelativePosToMark( [ line( '.' ), col( '.' ) ], renderContext.leadingPlaceHolder.mark.start ) call s:UpdateFollowingPlaceHoldersWith(contentTyped,{}) call s:GotoRelativePosToMark(relPos,renderContext.leadingPlaceHolder.mark.start) else endif call s:CallPlugin('update', 'after') let renderContext.lastContent = contentTyped endfunction fun! s:DoBreakUndo() if pumvisible() return "\\" endif return "\u" endfunction inoremap XPTdoBreakUndo =DoBreakUndo() inoremap XPT_map_CR =XPTCR() fun! s:BreakUndo() if mode() != 'i' || pumvisible() return endif let x = b:xptemplateData if x.renderContext.processing call feedkeys( "\XPTdoBreakUndo", 'm' ) endif endfunction fun! s:RecordRelativePosToMark(pos,mark) let p = XPMpos(a:mark) if a:pos[0] == p[0] return [0,a:pos[1] - p[1]] else return [a:pos[0] - p[0],a:pos[1]] endif endfunction fun! s:GotoRelativePosToMark(rPos,mark) let p = XPMpos(a:mark) if a:rPos[0] == 0 call cursor(p[0],a:rPos[1] + p[1]) else call cursor(p[0] + a:rPos[0],a:rPos[1]) endif endfunction fun! s:XPTcheck() if !exists( 'b:xptemplateData' ) call XPTemplateInit() endif let x = b:xptemplateData if x.wrap isnot '' let x.wrapStartPos = 0 let x.wrap = '' endif call s:CallPlugin( 'insertenter', 'after' ) endfunction fun! s:GetContextFT() if exists( 'b:XPTfiletypeDetect' ) return b:XPTfiletypeDetect() elseif &filetype == '' return 'unknown' else return &filetype endif endfunction fun! s:GetContextFTObj() let x = b:xptemplateData let ft = s:GetContextFT() call xpt#parser#loadSpecialFiletype(ft) let ftScope = get(x.filetypes,ft,{}) return ftScope endfunction augroup XPT au! au BufEnter * call XPTemplateInit() au InsertEnter * call XPTcheck() au CursorMoved,CursorMovedI * call XPTupdateTyping() if g:xptemplate_strict == 1 au CursorMovedI * call BreakUndo() endif augroup END fun! g:XPTaddPlugin(event,when,func) if has_key(s:plugins,a:event) call add(s:plugins[a:event][a:when],a:func) else throw "XPT does NOT support event:".a:event endif endfunction let s:plugins = {} fun! s:CreatePluginContainer(...) for evt in a:000 let s:plugins[evt] = { 'before' : [], 'after' : []} endfor endfunction call s:CreatePluginContainer( 'start', 'render', 'build', 'finishSnippet', 'finishAll', 'preValue', 'defaultValue', 'ph_pum', 'postFilter', 'initItem', 'nextItem', 'prevItem', 'update', 'insertenter', ) delfunc s:CreatePluginContainer fun! s:CallPlugin(ev,when) let cnt = get(s:plugins,a:ev,{}) let evs = get(cnt,a:when,[]) if evs == [] return endif let x = b:xptemplateData for XPTplug in evs call XPTplug(x,x.renderContext) endfor endfunction com! XPTreload call XPTreload() com! XPTcrash call Crash() let &cpo = s:oldcpo