"=============================================================================== " " File: mmtemplates#core.vim " " Description: Template engine: Core. " " Maps & Menus - Template Engine " " VIM Version: 7.0+ " Author: Wolfgang Mehner, wolfgang-mehner@web.de " Organization: " Version: 0.9 " Created: 30.08.2011 " Revision: 04.02.2012 " License: Copyright (c) 2012, Wolfgang Mehner " This program is free software; you can redistribute it and/or " modify it under the terms of the GNU General Public License as " published by the Free Software Foundation, version 2 of the " License. " This program is distributed in the hope that it will be " useful, but WITHOUT ANY WARRANTY; without even the implied " warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR " PURPOSE. " See the GNU General Public License version 2 for more details. "=============================================================================== " "------------------------------------------------------------------------------- " TODO , TODO (win) , TODO (FM) " some todos can be found in the internal documentation "------------------------------------------------------------------------------- " "------------------------------------------------------------------------------- " Basic checks. {{{1 "------------------------------------------------------------------------------- " " need at least 7.0 if v:version < 700 echohl WarningMsg echo 'The plugin templates.vim needs Vim version >= 7.' echohl None finish endif " " prevent duplicate loading " need compatible if &cp || ( exists('g:Templates_Version') && ! exists('g:Templates_DevelopmentOverwrite') ) finish endif let g:Templates_Version= '0.9' " version number of this script; do not change " if ! exists ( 'g:Templates_MapInUseWarn' ) let g:Templates_MapInUseWarn = 1 endif " "---------------------------------------------------------------------- " === Modul setup. === {{{1 "---------------------------------------------------------------------- " let s:DebugGlobalOverwrite = 0 let s:DebugLevel = s:DebugGlobalOverwrite " let s:StateStackStyleTop = -2 let s:StateStackFile = -1 " let s:StateStackLength = 2 " let s:Flagactions = { \ ':i' : '', \ ':l' : ' (-> lowercase)', \ ':u' : ' (-> uppercase)', \ ':c' : ' (-> capitalize)', \ ':L' : ' (-> legalize name)', \ } " let s:MsgInsertionNotAvail = "insertion not available for a fold" " "---------------------------------------------------------------------- " s:StandardMacros : The standard macros. {{{2 "---------------------------------------------------------------------- " let s:StandardMacros = { \ 'BASENAME' : '', \ 'DATE' : '%x', \ 'FILENAME' : '', \ 'PATH' : '', \ 'SUFFIX' : '', \ 'TIME' : '%X', \ 'YEAR' : '%Y', \ } " "---------------------------------------------------------------------- " s:FileReadNameSpace : The set of functions a template file can call. {{{2 "---------------------------------------------------------------------- " let s:FileReadNameSpace = { \ 'IncludeFile' : 'ss\?', \ 'SetFormat' : 'ss', \ 'SetMacro' : 'ss', \ 'SetPath' : 'ss', \ 'SetStyle' : 's', \ \ 'MenuShortcut' : 'ss', \ 'SetMap' : 'ss', \ 'SetProperty' : 'ss', \ 'SetShortcut' : 'ss', \ } " "---------------------------------------------------------------------- " s:TypeNames : Name of types as characters. {{{2 "---------------------------------------------------------------------- " let s:TypeNames = [ ' ', ' ', ' ', ' ', ' ', ' ' ] " let s:TypeNames[ type(0) ] = 'i' " integer let s:TypeNames[ type("") ] = 's' " string let s:TypeNames[ type([]) ] = 'l' " list let s:TypeNames[ type({}) ] = 'd' " dict "let s:TypeNames[ type(0.0) ] = 'n' " number " TODO: why does float not work in some cases? " not important right now. " "---------------------------------------------------------------------- " === Regular Expressions. === {{{1 "---------------------------------------------------------------------- " let s:RegexSettings = { \ 'MacroName' : '[a-zA-Z_][a-zA-Z0-9_]*', \ 'MacroList' : '\%([a-zA-Z_]\|[a-zA-Z_][a-zA-Z0-9_ \t,]*[a-zA-Z0-9_]\)', \ 'TemplateName' : '[a-zA-Z_][a-zA-Z0-9_+\-\., ]*[a-zA-Z0-9_+\-\.,]', \ 'TextOpt' : '[a-zA-Z_][a-zA-Z0-9_+\-: \t,]*[a-zA-Z0-9_+\-]', \ 'Mapping' : '[a-zA-Z0-9+\-]\+', \ \ 'CommentStart' : '\$', \ 'BlockDelimiter' : '==', \ \ 'CommentHint' : '$', \ 'CommandHint' : '[A-Z]', \ 'DelimHint' : '=', \ 'MacroHint' : '|', \ \ 'MacroStart' : '|', \ 'MacroEnd' : '|', \ 'EditTagStart' : '<', \ 'EditTagEnd' : '>', \ 'JumpTag1Start' : '{', \ 'JumpTag1End' : '}', \ 'JumpTag2Start' : '<', \ 'JumpTag2End' : '>', \ } " "---------------------------------------------------------------------- " s:UpdateFileReadRegex : Update the regular expressions. {{{2 "---------------------------------------------------------------------- " function! s:UpdateFileReadRegex ( regex, settings ) " let quote = '\(["'']\?\)' " " Basics let a:regex.MacroName = a:settings.MacroName let a:regex.MacroNameC = '\('.a:settings.MacroName.'\)' let a:regex.TemplateNameC = '\('.a:settings.TemplateName.'\)' let a:regex.Mapping = a:settings.Mapping let a:regex.AbsolutePath = '^[\~/]' " TODO: Is that right and/or complete? " " Syntax Categories let a:regex.EmptyLine = '^\s*$' let a:regex.CommentLine = '^'.a:settings.CommentStart let a:regex.FunctionCall = '^\s*'.a:regex.MacroNameC.'\s*(\(.*\))\s*$' let a:regex.MacroAssign = '^\s*'.a:settings.MacroStart.a:regex.MacroNameC.a:settings.MacroEnd \ .'\s*=\s*'.quote.'\(.\{-}\)'.'\2'.'\s*$' " deprecated " " Blocks let delim = a:settings.BlockDelimiter let a:regex.Styles1Start = '^'.delim.'\s*IF\s\+|STYLE|\s\+IS\s\+'.a:regex.MacroNameC.'\s*'.delim let a:regex.Styles1End = '^'.delim.'\s*ENDIF\s*'.delim let a:regex.Styles2Start = '^'.delim.'\s*USE\s\+STYLES\s*:' \ .'\s*\('.a:settings.MacroList.'\)'.'\s*'.delim let a:regex.Styles2End = '^'.delim.'\s*ENDSTYLES\s*'.delim " " Texts let a:regex.TemplateStart = '^'.delim.'\s*\%(TEMPLATE:\)\?\s*'.a:regex.TemplateNameC.'\s*'.delim \ .'\s*\%(\('.a:settings.TextOpt.'\)\s*'.delim.'\)\?' let a:regex.TemplateEnd = '^'.delim.'\s*ENDTEMPLATE\s*'.delim " let a:regex.ListStart = '^'.delim.'\s*LIST:\s*'.a:regex.MacroNameC.'\s*'.delim \ .'\s*\%(\('.a:settings.TextOpt.'\)\s*'.delim.'\)\?' let a:regex.ListEnd = '^'.delim.'\s*ENDLIST\s*'.delim " let a:regex.HelpStart = '^'.delim.'\s*HELP:\s*'.a:regex.TemplateNameC.'\s*'.delim \ .'\s*\%(\('.a:settings.TextOpt.'\)\s*'.delim.'\)\?' let a:regex.HelpEnd = '^'.delim.'\s*ENDHELP\s*'.delim " " Special Hints let a:regex.CommentHint = a:settings.CommentHint let a:regex.CommandHint = a:settings.CommandHint let a:regex.DelimHint = a:settings.DelimHint let a:regex.MacroHint = a:settings.MacroHint " endfunction " ---------- end of function s:UpdateFileReadRegex ---------- " "---------------------------------------------------------------------- " s:UpdateTemplateRegex : Update the regular expressions. {{{2 "---------------------------------------------------------------------- " function! s:UpdateTemplateRegex ( regex, settings ) " let quote = '["'']' " " Function Arguments let a:regex.RemoveQuote = '^\s*'.quote.'\zs.*\ze'.quote.'\s*$' " " Basics let a:regex.MacroStart = a:settings.MacroStart let a:regex.MacroEnd = a:settings.MacroEnd let a:regex.MacroName = a:settings.MacroName let a:regex.MacroNameC = '\('.a:settings.MacroName.'\)' let a:regex.MacroMatch = '^'.a:settings.MacroStart.a:settings.MacroName.a:settings.MacroEnd.'$' let a:regex.MacroSimple = a:settings.MacroStart.a:settings.MacroName.a:settings.MacroEnd " " Syntax Categories let a:regex.FunctionLine = '^'.a:settings.MacroStart.'\('.a:regex.MacroNameC.'(\(.*\))\)'.a:settings.MacroEnd.'\s*\n' let a:regex.FunctionChecked = '^'.a:regex.MacroNameC.'(\(.*\))$' let a:regex.FunctionList = '^LIST(\(.\{-}\))$' let a:regex.FunctionComment = a:settings.MacroStart.'\(C\|Comment\)'.'(\(.\{-}\))'.a:settings.MacroEnd let a:regex.FunctionInsert = a:settings.MacroStart.'\(Insert\|InsertLine\)'.'(\(.\{-}\))'.a:settings.MacroEnd let a:regex.MacroRequest = a:settings.MacroStart.'?'.a:regex.MacroNameC.'\%(:\(\a\)\)\?'.a:settings.MacroEnd let a:regex.MacroInsert = a:settings.MacroStart.''.a:regex.MacroNameC.'\%(:\(\a\)\)\?'.a:settings.MacroEnd let a:regex.ListItem = a:settings.MacroStart.''.a:regex.MacroNameC.':ENTRY_*'.a:settings.MacroEnd " let a:regex.TextBlockFunctions = '^\%(C\|Comment\|Insert\|InsertLine\)$' " " Jump Tags let a:regex.JumpTagBoth = '<-\w*->\|{-\w*-}\|<+\w*+>\|{+\w*+}' let a:regex.JumpTagType2 = '<-\w*->\|{-\w*-}' " endfunction " ---------- end of function s:UpdateTemplateRegex ---------- " }}}2 " "---------------------------------------------------------------------- " === Script: Auxiliary functions. === {{{1 "---------------------------------------------------------------------- " "---------------------------------------------------------------------- " s:ParameterTypes : Get the types of the arguments. {{{2 " " Returns a string with one character per argument, denoting the type. " Uses the codebook 's:TypeNames'. " " Examples: " - s:ParameterTypes ( 1, "string", [] ) -> "isl" " - s:ParameterTypes ( 1, 'string', {} ) -> "isd" " - s:ParameterTypes ( 1, 1.0 ) -> "in" "---------------------------------------------------------------------- " function! s:ParameterTypes ( ... ) return join( map( copy( a:000 ), 's:TypeNames[ type ( v:val ) ]' ), '' ) endfunction " ---------- end of function s:ParameterTypes ---------- " "---------------------------------------------------------------------- " s:FunctionCheck : Check the syntax, name and parameter types. {{{2 " " Throw a 'Template:Check:*' exception whenever: " - The syntax of the call "name( params )" is wrong. " - The function name 'name' is not a key in 'namespace'. " - The parameter string (as produced by s:ParameterTypes) does not match " the regular expression found in "namespace[name]". "---------------------------------------------------------------------- " function! s:FunctionCheck ( name, param, namespace ) " " check the syntax and get the parameter string try exe 'let param_s = s:ParameterTypes( '.a:param.' )' catch /^Vim(let):E\d\+:/ throw 'Template:Check:function call "'.a:name.'('.a:param.')": '.matchstr ( v:exception, '^Vim(let):E\d\+:\zs.*' ) endtry " " check the function and the parameters if ! has_key ( a:namespace, a:name ) throw 'Template:Check:unknown function: "'.a:name.'"' elseif param_s !~ '^'.a:namespace[ a:name ].'$' throw 'Template:Check:wrong parameter types: "'.a:name.'"' endif " endfunction " ---------- end of function s:FunctionCheck ---------- " "---------------------------------------------------------------------- " s:LiteralReplacement : Substitute without using regular expressions. {{{2 "---------------------------------------------------------------------- " function! s:LiteralReplacement ( text, remove, insert, flag ) return substitute( a:text, \ '\V'.escape( a:remove, '\' ), \ escape( a:insert, '\&~' ), a:flag ) " \ '\='.string( a:insert ), a:flag ) endfunction " ---------- end of function s:LiteralReplacement ---------- " "---------------------------------------------------------------------- " s:ConcatNormalizedFilename : Concatenate and normalize a filename. {{{2 "---------------------------------------------------------------------- " function! s:ConcatNormalizedFilename ( ... ) if a:0 == 1 let filename = ( a:1 ) elseif a:0 == 2 let filename = ( a:1 ).'/'.( a:2 ) endif return fnamemodify( filename, ':p' ) endfunction " ---------- end of function s:ConcatNormalizedFilename ---------- " "---------------------------------------------------------------------- " s:GetNormalizedPath : Split and normalize a path. {{{2 "---------------------------------------------------------------------- " function! s:GetNormalizedPath ( filename ) return fnamemodify( a:filename, ':p:h' ) endfunction " ---------- end of function s:GetNormalizedPath ---------- " ""---------------------------------------------------------------------- " s:UserInput : Input after a highlighted prompt. {{{2 " " 3. argument : optional completion " 4. argument : optional list, if the 3. argument is 'customlist' " " Throws an exception 'Template:UserInputAborted' if the obtained input is empty, " so use it like this: " try " let style = s:UserInput( 'prompt', '', ... ) " catch /Template:UserInputAborted/ " return " endtry "---------------------------------------------------------------------- " function! s:UserInput ( prompt, text, ... ) " echohl Search " highlight prompt call inputsave() " preserve typeahead if a:0 == 0 || a:1 == '' let retval = input( a:prompt, a:text ) elseif a:1 == 'customlist' let s:UserInputList = a:2 let retval = input( a:prompt, a:text, 'customlist,mmtemplates#core#UserInputEx' ) let s:UserInputList = [] else let retval = input( a:prompt, a:text, a:1 ) endif call inputrestore() " restore typeahead echohl None " reset highlighting " if empty( retval ) throw 'Template:UserInputAborted' endif " let retval = substitute( retval, '^\s\+', "", "" ) " remove leading whitespaces let retval = substitute( retval, '\s\+$', "", "" ) " remove trailing whitespaces " return retval " endfunction " ---------- end of function s:UserInput ---------- " "---------------------------------------------------------------------- " mmtemplates#core#UserInputEx : ex-command for s:UserInput. {{{3 "---------------------------------------------------------------------- " function! mmtemplates#core#UserInputEx ( ArgLead, CmdLine, CursorPos ) return filter( copy( s:UserInputList ), 'v:val =~ "\\V\\<'.escape(a:ArgLead,'\').'\\w\\*"' ) endfunction " ---------- end of function mmtemplates#core#UserInputEx ---------- " }}}3 " let s:UserInputList = [] " "---------------------------------------------------------------------- " s:ErrorMsg : Print an error message. {{{2 "---------------------------------------------------------------------- " function! s:ErrorMsg ( ... ) echohl WarningMsg for line in a:000 echomsg line endfor echohl None endfunction " ---------- end of function s:ErrorMsg ---------- " "---------------------------------------------------------------------- " s:DebugMsg : Print debug information. {{{2 "---------------------------------------------------------------------- " function! s:DebugMsg ( msg, ... ) if s:DebugLevel if a:0 == 0 || ( a:1 <= s:DebugLevel ) echo a:msg endif endif endfunction " ---------- end of function s:DebugMsg ---------- " "---------------------------------------------------------------------- " mmtemplates#core#NewLibrary : Create a new template library. {{{1 "---------------------------------------------------------------------- " function! mmtemplates#core#NewLibrary ( ... ) " " ================================================== " data " ================================================== " " library let library = { \ 'macros' : {}, \ 'resources' : {}, \ 'templates' : {}, \ \ 'temp_list' : [], \ \ 'styles' : [ 'default' ], \ 'current_style' : 'default', \ \ 'menu_shortcuts' : {}, \ 'menu_existing' : {}, \ \ 'regex_settings' : ( copy ( s:RegexSettings ) ), \ 'regex_file' : {}, \ 'regex_template' : {}, \ \ 'library_files' : [], \ } " used by maps: 'map_commands' " call extend ( library.macros, s:StandardMacros, 'keep' ) " call s:UpdateFileReadRegex ( library.regex_file, library.regex_settings ) call s:UpdateTemplateRegex ( library.regex_template, library.regex_settings ) " " ================================================== " parameters " ================================================== " let i = 1 while i <= a:0 " " if a:[i] == 'debug' && i+1 <= a:0 && ! s:DebugGlobalOverwrite " let s:DebugLevel = a:[i+1] " let i += 2 " else if type ( a:[i] ) == type ( '' ) | call s:ErrorMsg ( 'Unknown option: "'.a:[i].'"' ) else | call s:ErrorMsg ( 'Unknown option at position '.i.'.' ) | endif let i += 1 " endif " endwhile " " ================================================== " done " ================================================== " return library " return the new library " endfunction " ---------- end of function mmtemplates#core#NewLibrary ---------- " "---------------------------------------------------------------------- " === Read Templates: Auxiliary functions. === {{{1 "---------------------------------------------------------------------- " "---------------------------------------------------------------------- " s:SetFormat : Set the format of |DATE|, ... (template function). {{{2 "---------------------------------------------------------------------- " function! s:SetFormat ( name, replacement ) " " check for valid name if a:name !~ 'TIME\|DATE\|YEAR' call s:ErrorMsg ( 'Can not set the format of: '.a:name ) return endif " let s:library.macros[ a:name ] = a:replacement " endfunction " ---------- end of function s:SetFormat ---------- " "---------------------------------------------------------------------- " s:SetMacro : Set a replacement (template function). {{{2 "---------------------------------------------------------------------- " function! s:SetMacro ( name, replacement ) " " check for valid name if a:name !~ s:library.regex_file.MacroName call s:ErrorMsg ( 'Macro name must be a valid identifier: '.a:name ) return elseif has_key ( s:StandardMacros, a:name ) call s:ErrorMsg ( 'The special macro "'.a:name.'" can not be replaced via SetMacro.' ) return endif " let s:library.macros[ a:name ] = a:replacement " endfunction " ---------- end of function s:SetMacro ---------- " "---------------------------------------------------------------------- " s:SetStyle : Set the current style (template function). {{{2 "---------------------------------------------------------------------- " function! s:SetStyle ( name ) " " check for valid name if a:name !~ s:library.regex_file.MacroName call s:ErrorMsg ( 'Style name must be a valid identifier: '.a:name ) return endif " let s:library.current_style = a:name " endfunction " ---------- end of function s:SetStyle ---------- " "---------------------------------------------------------------------- " s:SetPath : Set a path-resource (template function). {{{2 "---------------------------------------------------------------------- " function! s:SetPath ( name, value ) " " check for valid name if a:name !~ s:library.regex_file.MacroName call s:ErrorMsg ( 'Path name must be a valid identifier: '.a:name ) return endif " let s:library.resources[ 'path!'.a:name ] = a:value " endfunction " ---------- end of function s:SetPath ---------- " "---------------------------------------------------------------------- " s:MenuShortcut : Set a shortcut for a sub-menu (template function). {{{2 "---------------------------------------------------------------------- " function! s:MenuShortcut ( name, shortcut ) " " check for valid shortcut if len ( a:shortcut ) > 1 call s:ErrorMsg ( 'The shortcut for "'.a:name.'" must be a single character.' ) return endif " let name = substitute( a:name, '\.$', '', '' ) " let s:library.menu_shortcuts[ name ] = a:shortcut " endfunction " ---------- end of function s:MenuShortcut ---------- " "---------------------------------------------------------------------- " s:SetMap : TODO (template function). {{{2 "---------------------------------------------------------------------- " function! s:SetMap ( name, map ) " echo 'SetMap: TO BE IMPLEMENTED' " endfunction " ---------- end of function s:SetMap ---------- " "---------------------------------------------------------------------- " s:SetProperty : TODO (template function). {{{2 "---------------------------------------------------------------------- " function! s:SetProperty ( name, shortcut ) " echo 'SetProperty: TO BE IMPLEMENTED' " endfunction " ---------- end of function s:SetProperty ---------- " "---------------------------------------------------------------------- " s:SetShortcut : TODO (template function). {{{2 "---------------------------------------------------------------------- " function! s:SetShortcut ( name, shortcut ) " " check for valid shortcut if len ( a:shortcut ) > 1 call s:ErrorMsg ( 'The shortcut for "'.a:name.'" must be a single character.' ) return endif " echo 'SetShortcut: TO BE IMPLEMENTED' " endfunction " ---------- end of function s:SetShortcut ---------- " "---------------------------------------------------------------------- " s:AddStyles : Add styles to the list. {{{2 "---------------------------------------------------------------------- " function! s:AddStyles ( styles ) " " TODO: check for valid name " add the styles to the list for s in a:styles if -1 == index ( s:library.styles, s ) call add ( s:library.styles, s ) endif endfor " endfunction " ---------- end of function s:AddStyles ---------- " "---------------------------------------------------------------------- " s:UseStyles : Set the styles. {{{2 "---------------------------------------------------------------------- " function! s:UseStyles ( styles ) " " 'use_styles' empty? -> we may have new styles " otherwise -> must be a subset, so no new styles if empty ( s:t_runtime.use_styles ) " add the styles to the list call s:AddStyles ( a:styles ) else " are the styles a sub-set of the currently used styles? for s in a:styles if -1 == index ( s:t_runtime.use_styles, s ) call s:ErrorMsg ( 'Style "'.s.'" currently not in use.' ) return endif endfor endif " " push the new style and use it as the current style call add ( s:t_runtime.styles_stack, a:styles ) let s:t_runtime.use_styles = a:styles " endfunction " ---------- end of function s:UseStyles ---------- " "---------------------------------------------------------------------- " s:RevertStyles : Revert the styles. {{{2 "---------------------------------------------------------------------- " function! s:RevertStyles ( times ) " " get the current top, and check whether any more styles can be removed let state_lim = s:t_runtime.state_stack[ s:StateStackStyleTop ] let state_top = len( s:t_runtime.styles_stack ) " if state_lim > ( state_top - a:times ) call s:ErrorMsg ( 'Can not close any more style sections.' ) return endif " " remove the top call remove ( s:t_runtime.styles_stack, -1 * a:times, -1 ) " " reset the current style if state_top > a:times let s:t_runtime.use_styles = s:t_runtime.styles_stack[ -1 ] else let s:t_runtime.use_styles = [] endif " endfunction " ---------- end of function s:RevertStyles ---------- " "---------------------------------------------------------------------- " s:AddText : Add a text. {{{1 "---------------------------------------------------------------------- " function! s:AddText ( type, name, settings, lines ) " if a:type == 'help' call s:AddTemplate ( 'help', a:name, a:settings, a:lines ) elseif a:type == 'list' call s:AddList ( 'list', a:name, a:settings, a:lines ) elseif a:type == 'template' call s:AddTemplate ( 't', a:name, a:settings, a:lines ) endif " endfunction " ---------- end of function s:AddText ---------- " "---------------------------------------------------------------------- " s:AddList : Add a list. {{{1 "---------------------------------------------------------------------- " function! s:AddList ( type, name, settings, lines ) " " ================================================== " checks " ================================================== " " Error: empty name if empty ( a:name ) call s:ErrorMsg ( 'List name can not be empty.' ) return endif " " Warning: empty template if empty ( a:lines ) call s:ErrorMsg ( 'Warning: Empty list: "'.a:name.'"' ) endif " " Warning: already existing if s:t_runtime.overwrite_warning && has_key ( s:library.resources, 'list!'.a:name ) call s:ErrorMsg ( 'Warning: Overwriting list "'.a:name.'"' ) endif " " ================================================== " settings " ================================================== " let type = 'list' let bare = 0 " for s in a:settings " if s == 'list' let type = 'list' elseif s == 'hash' || s == 'dict' || s == 'dictionary' let type = 'dict' elseif s == 'bare' let bare = 1 else call s:ErrorMsg ( 'Warning: Unknown setting in list "'.a:name.'": "'.s.'"' ) endif " endfor " if type == 'list' if bare let lines = escape( a:lines, '"' ) let lines = substitute( lines, '^\s*', '"', '' ) let lines = substitute( lines, '\s*\n$', '"', '' ) let lines = substitute( lines, '\s*\n\s*', '", "', 'g' ) exe 'let list = [ '.lines.' ]' else exe 'let list = [ '.substitute( a:lines, '\n', ' ', 'g' ).' ]' end call sort ( list ) elseif type == 'dict' if bare s:ErrorMsg ( 'bare hash: to be implemented' ) else exe 'let list = { '.substitute( a:lines, '\n', ' ', 'g' ).' }' end endif " let s:library.resources[ 'list!'.a:name ] = list " endfunction " ---------- end of function s:AddList ---------- " "---------------------------------------------------------------------- " s:AddTemplate : Add a template. {{{1 "---------------------------------------------------------------------- " function! s:AddTemplate ( type, name, settings, lines ) " let name = a:name " " ================================================== " checks " ================================================== " " Error: empty name if empty ( name ) call s:ErrorMsg ( 'Template name can not be empty.' ) return endif " " Warning: empty template if empty ( a:lines ) call s:ErrorMsg ( 'Warning: Empty template: "'.name.'"' ) endif " " ================================================== " new template " ================================================== " if ! has_key ( s:library.templates, name.'!!type' ) " " -------------------------------------------------- " new template " -------------------------------------------------- " let type = a:type let placement = 'below' let indentation = '1' " let entry = 1 let sc = '' let mp = '' let visual = -1 != stridx ( a:lines, '' ) " " -------------------------------------------------- " settings " -------------------------------------------------- for s in a:settings " if s == 'start' || s == 'above' || s == 'below' || s == 'append' || s == 'insert' let placement = s " entry and hot keys: elseif s == 'nomenu' let entry = 0 elseif s == 'expandmenu' let entry = 2 elseif s =~ '^sc\s*:' || s =~ '^shortcut\s*:' let sc = matchstr ( s, '^\w\+\s*:\s*\zs'.s:library.regex_file.Mapping ) elseif s =~ '^map\s*:' let mp = matchstr ( s, '^map\s*:\s*\zs'.s:library.regex_file.Mapping ) " special insertion in visual mode: elseif s == 'visual' let visual = 1 elseif s == 'novisual' let visual = 0 " indentation elseif s == 'indent' let indentation = '1' elseif s == 'noindent' let indentation = '0' " " picker: " elseif s == 'pick-file' " let type = 'pick-file' " elseif s == 'pick-list' " let type = 'pick-list' " " lists: " elseif s == 'single-line-list' " let type = 'single-line-list' " elseif s == 'multi-line-list' " let type = 'multi-line-list' else call s:ErrorMsg ( 'Warning: Unknown setting in template "'.name.'": "'.s.'"' ) endif " endfor " " TODO: review this if a:type == 'help' let placement = 'help' endif " " -------------------------------------------------- " new template " -------------------------------------------------- let s:library.templates[ name.'!!type' ] = type.','.placement.','.indentation let s:library.templates[ name.'!!menu' ] = entry.",".visual.",'".sc."','".mp."'" " call add ( s:library.temp_list, name ) " endif " " ================================================== " text " ================================================== " " the styles if a:type == 'help' " Warning: overwriting a style if s:t_runtime.overwrite_warning && has_key ( s:library.templates, name.'!default' ) call s:ErrorMsg ( 'Warning: Help is overwriting a template: "'.name.'"' ) endif let s:library.templates[ name.'!default' ] = a:lines return elseif empty ( s:t_runtime.use_styles ) let styles = [ 'default' ] else let styles = s:t_runtime.use_styles endif " " save the lines for s in styles " " Warning: overwriting a style if s:t_runtime.overwrite_warning && has_key ( s:library.templates, name.'!'.s ) call s:ErrorMsg ( 'Warning: Overwriting style in template "'.name.'": "'.s.'"' ) endif " let s:library.templates[ name.'!'.s ] = a:lines " endfor " endfunction " ---------- end of function s:AddTemplate ---------- " "---------------------------------------------------------------------- " s:IncludeFile : Read a template file (IncludeFile). {{{1 "---------------------------------------------------------------------- " function! s:IncludeFile ( templatefile, ... ) " let regex = s:library.regex_file " let read_abs = 0 if a:0 >= 1 && a:1 == 'abs' let read_abs = 1 endif " " ================================================== " checks " ================================================== " " Expand ~, $HOME, ... and check for absolute path let templatefile = expand( a:templatefile ) " " if templatefile =~ regex.AbsolutePath " let templatefile = s:ConcatNormalizedFilename ( templatefile ) " else " let templatefile = s:ConcatNormalizedFilename ( s:t_runtime.state_stack[ s:StateStackFile ], templatefile ) " endif if read_abs let templatefile = s:ConcatNormalizedFilename ( templatefile ) else let templatefile = s:ConcatNormalizedFilename ( s:t_runtime.state_stack[ s:StateStackFile ], templatefile ) endif " " file does not exists or was already visited? if !filereadable( templatefile ) throw 'Template:Check:file "'.templatefile.'" does not exist or is not readable' elseif has_key ( s:t_runtime.files_visited, templatefile ) throw 'Template:Check:file "'.templatefile.'" already read' endif " " ================================================== " setup " ================================================== " " add to the state stack call add ( s:t_runtime.state_stack, len( s:t_runtime.styles_stack ) ) " length of styles_stack call add ( s:t_runtime.state_stack, s:GetNormalizedPath ( templatefile ) ) " current path " " mark file as read let s:t_runtime.files_visited[templatefile] = 1 " " debug: call s:DebugMsg ( 'Reading '.templatefile.' ...', 2 ) " " old format? let format = 'new' " TODO: review this " let state = 'command' let t_start = 0 let last_styles = '' " " ================================================== " go trough the file " ================================================== " let filelines = readfile( templatefile ) " for line in filelines " let firstchar = line[0] " " which state if state == 'command' " ================================================== " state: command " ================================================== " " empty line? if empty ( line ) continue endif " " comment? if firstchar == regex.CommentHint if line =~ regex.CommentLine continue endif endif " " macro line? --- |MACRO| = something if firstchar == regex.MacroHint " let mlist = matchlist ( line, regex.MacroAssign ) if ! empty ( mlist ) " STYLE, includefile or general macro if mlist[1] == 'STYLE' call s:SetStyle ( mlist[3] ) elseif mlist[1] == 'includefile' try call s:IncludeFile ( mlist[3], 'old' ) catch /Template:Check:.*/ let msg = v:exception[ len( 'Template:Check:') : -1 ] call s:ErrorMsg ( 'While loading "'.templatefile.'":', msg ) endtry else call s:SetMacro ( mlist[1], mlist[3] ) endif continue endif " endif " " function call? --- Function( param_list ) if firstchar =~ regex.CommandHint " let mlist = matchlist ( line, regex.FunctionCall ) if ! empty ( mlist ) let [ name, param ] = mlist[ 1 : 2 ] " try " check the call call s:FunctionCheck ( name, param, s:FileReadNameSpace ) " try to call exe 'call s:'.name.' ( '.param.' ) ' catch /Template:Check:.*/ let msg = v:exception[ len( 'Template:Check:') : -1 ] call s:ErrorMsg ( 'While loading "'.templatefile.'":', msg ) catch // call s:ErrorMsg ( 'While calling "'.name.'" in "'.templatefile.'":', v:exception ) endtry " continue endif " endif " " section or text? if firstchar == regex.DelimHint " " switch styles? let mlist = matchlist ( line, regex.Styles1Start ) if ! empty ( mlist ) call s:UseStyles ( [ mlist[1] ] ) let last_styles = mlist[0] continue endif " " switch styles? if line =~ regex.Styles1End call s:RevertStyles ( 1 ) continue endif " " switch styles? let mlist = matchlist ( line, regex.Styles2Start ) if ! empty ( mlist ) call s:UseStyles ( split( mlist[1], '\s*,\s*' ) ) let last_styles = mlist[0] continue endif " " switch styles? if line =~ regex.Styles2End call s:RevertStyles ( 1 ) continue endif " " start of text? let mlist_template = matchlist ( line, regex.TemplateStart ) let mlist_list = matchlist ( line, regex.ListStart ) let mlist_help = matchlist ( line, regex.HelpStart ) if ! empty ( mlist_template ) let state = 'text' let t_type = 'template' let t_start = 1 elseif ! empty ( mlist_list ) let state = 'text' let t_type = 'list' let t_start = 1 elseif ! empty ( mlist_help ) let state = 'text' let t_type = 'help' let t_start = 1 endif " endif " " empty line? if line =~ regex.EmptyLine continue endif " elseif state == 'text' " ================================================== " state: text " ================================================== " if firstchar == regex.CommentHint || firstchar == regex.DelimHint "if firstchar =~ '[=\$]' " " comment or end of template? if line =~ regex.CommentLine \ || ( line =~ regex.TemplateEnd && format == 'new' ) \ || ( line =~ regex.ListEnd && format == 'new' ) let state = 'command' call s:AddText ( t_type, t_name, t_settings, t_lines ) continue endif " " start of new template? let mlist_template = matchlist ( line, regex.TemplateStart ) let mlist_list = matchlist ( line, regex.ListStart ) let mlist_help = matchlist ( line, regex.HelpStart ) if ! empty ( mlist_template ) call s:AddText ( t_type, t_name, t_settings, t_lines ) let t_type = 'template' let t_start = 1 elseif ! empty ( mlist_list ) call s:AddText ( t_type, t_name, t_settings, t_lines ) let t_type = 'list' let t_start = 1 elseif ! empty ( mlist_help ) call s:AddText ( t_type, t_name, t_settings, t_lines ) let t_type = 'help' let t_start = 1 else let t_lines .= line."\n" " read the line continue endif " else let t_lines .= line."\n" " read the line continue endif " endif " " start of template? if t_start if t_type == 'template' let t_name = mlist_template[1] let t_settings = split( mlist_template[2], '\s*,\s*' ) elseif t_type == 'list' let t_name = mlist_list[1] let t_settings = split( mlist_list[2], '\s*,\s*' ) elseif t_type == 'help' let t_name = mlist_help[1] let t_settings = split( mlist_help[2], '\s*,\s*' ) endif let t_lines = '' let t_start = 0 continue endif " call s:ErrorMsg ( 'Failed to read line: '.line ) " endfor " " ================================================== " wrap up " ================================================== " if state == 'text' call s:AddText ( t_type, t_name, t_settings, t_lines ) endif " " all style sections closed? let state_lim = s:t_runtime.state_stack[ s:StateStackStyleTop ] let state_top = len( s:t_runtime.styles_stack ) if state_lim < state_top call s:RevertStyles ( state_top - state_lim ) call s:ErrorMsg ( 'Styles section has not been closed: '.last_styles ) endif " " debug: call s:DebugMsg ( '... '.templatefile.' done.', 2 ) " " restore the previous state call remove ( s:t_runtime.state_stack, -1 * s:StateStackLength, -1 ) " endfunction " ---------- end of function s:IncludeFile ---------- " "---------------------------------------------------------------------- " mmtemplates#core#ReadTemplates : Read a template file. {{{1 "---------------------------------------------------------------------- " " TODO: what to do with library.temp_list during reloading? " function! mmtemplates#core#ReadTemplates ( library, ... ) " " ================================================== " checks " ================================================== " " check the arguments if type( a:library ) == type( '' ) exe 'let t_lib = '.a:library elseif type( a:library ) == type( {} ) let t_lib = a:library else return s:ErrorMsg ( 'Argument "library" must be given as a dict or string.' ) endif " let mode = '' let file = '' " " ================================================== " setup " ================================================== " " library let s:library = t_lib let s:t_runtime = { \ 'state_stack' : [], \ 'use_styles' : [], \ 'styles_stack' : [], \ 'files_visited' : {}, \ \ 'overwrite_warning' : 0, \ } " " ================================================== " parameters " ================================================== " let i = 1 while i <= a:0 " if a:[i] == 'load' && i+1 <= a:0 let mode = 'load' let file = a:[i+1] let i += 2 elseif a:[i] == 'reload' && i+1 <= a:0 let mode = 'reload' let file = a:[i+1] let i += 2 elseif a:[i] == 'overwrite_warning' let s:t_runtime.overwrite_warning = 1 let i += 1 elseif a:[i] == 'debug' && i+1 <= a:0 && ! s:DebugGlobalOverwrite let s:DebugLevel = a:[i+1] let i += 2 else if type ( a:[i] ) == type ( '' ) | call s:ErrorMsg ( 'Unknown option: "'.a:[i].'"' ) else | call s:ErrorMsg ( 'Unknown option at position '.i.'.' ) | endif let i += 1 endif " endwhile " " ================================================== " files " ================================================== " let templatefiles = [] " if mode == 'load' " " check the type if type( file ) != type( '' ) return s:ErrorMsg ( 'Argument "filename" must be given as a string.' ) endif " " expand ~, $HOME, ... and normalize let file = expand ( file ) call add ( templatefiles, s:ConcatNormalizedFilename ( file ) ) " " add to library call add ( t_lib.library_files, s:ConcatNormalizedFilename ( file ) ) " elseif mode == 'reload' " if type( file ) == type( 0 ) call add ( templatefiles, t_lib.library_files[ file ] ) elseif type( file ) == type( '' ) " load all or a specific file if file == 'all' call extend ( templatefiles, t_lib.library_files ) else " " check and add the file let file = expand ( file ) let file = s:ConcatNormalizedFilename ( file ) " if ! filereadable ( file ) return s:ErrorMsg ( 'The file "'.file.'" does not exist.' ) elseif index ( t_lib.library_files, file ) == -1 return s:ErrorMsg ( 'The file "'.file.'" is not part of the template library.' ) endif " call add ( templatefiles, file ) " endif else return s:ErrorMsg ( 'Argument "fileid" must be given as an integer or string.' ) endif " " remove old maps if has_key ( t_lib, 'map_commands' ) call remove ( t_lib, 'map_commands' ) endif " endif " " ================================================== " read the library " ================================================== " " debug: if s:DebugLevel > 0 let time_start = reltime() endif " for f in templatefiles " " file exists? if !filereadable ( f ) call s:ErrorMsg ( 'Template library "'.f.'" does not exist or is not readable.' ) continue endif " " runtime information: " - set up the state stack: length of styles_stack + current path " - reset the current styles let s:t_runtime.state_stack = [ 0, s:GetNormalizedPath ( f ) ] let s:t_runtime.use_styles = [] let s:t_runtime.styles_stack = [] " " read the top-level file call s:IncludeFile ( f, 'abs' ) " endfor " call sort ( s:library.styles ) " sort the styles " " debug: if s:DebugLevel > 0 echo 'Loading library: '.reltimestr( reltime( time_start ) ) endif " " ================================================== " wrap up " ================================================== " unlet s:library " remove script variables unlet s:t_runtime " ... " let s:DebugLevel = s:DebugGlobalOverwrite " reset debug " if mode == 'reload' echo 'Reloaded the template library.' endif endfunction " ---------- end of function mmtemplates#core#ReadTemplates ---------- " "---------------------------------------------------------------------- " === Insert Templates: Auxiliary functions. === {{{1 "---------------------------------------------------------------------- " "---------------------------------------------------------------------- " s:ApplyFlag : Modify a text according to 'flag'. {{{2 "---------------------------------------------------------------------- " function! s:ApplyFlag ( text, flag ) " if a:flag == '' || a:flag == 'i' " i : identity return a:text elseif a:flag == 'l' " l : lowercase return tolower(a:text) elseif a:flag == 'u' " u : uppercase return toupper(a:text) elseif a:flag == 'c' " c : capitalize return toupper(a:text[0]).a:text[1:] elseif a:flag == 'L' " L : legalized name let text = substitute( a:text, '\s\+', '_', 'g' ) " multiple whitespaces let text = substitute( text, '\W\+', '_', 'g' ) " multiple non-word characters let text = substitute( text, '_\+', '_', 'g' ) " multiple underscores return text else " flag not valid return a:text endif " endfunction " ---------- end of function s:ApplyFlag ---------- " "---------------------------------------------------------------------- " s:OpenFold : Open fold and go to the first or last line of this fold. {{{2 "---------------------------------------------------------------------- " function! s:OpenFold ( mode ) if foldclosed(".") < 0 return endif " we are on a closed fold: " get end position, open fold, " jump to the last line of the previously closed fold let foldstart = foldclosed(".") let foldend = foldclosedend(".") normal zv if a:mode == 'below' exe ":".foldend elseif a:mode == 'start' exe ":".foldstart endif endfunction " ---------- end of function s:OpenFold ---------- " "---------------------------------------------------------------------- " s:ReplaceMacros : Replace all the macros in a text. {{{1 "---------------------------------------------------------------------- " function! s:ReplaceMacros ( text, m_local ) " let text1 = '' let text2 = a:text " let regex = '\(\_.\{-}\)'.s:library.regex_template.MacroInsert.'\(\_.*\)' " while 1 " let mlist = matchlist ( text2, regex ) " " no more macros? if empty ( mlist ) break endif " " check for recursion if -1 != index ( s:t_runtime.macro_stack, mlist[2] ) let m_text = '' call add ( s:t_runtime.macro_stack, mlist[2] ) throw 'Template:MacroRecursion' elseif has_key ( a:m_local, mlist[2] ) let m_text = get ( a:m_local, mlist[2] ) else let m_text = get ( s:library.macros, mlist[2], '' ) end " if m_text =~ s:library.regex_template.MacroSimple " call add ( s:t_runtime.macro_stack, mlist[2] ) " let m_text = s:ReplaceMacros ( m_text, a:m_local ) " call remove ( s:t_runtime.macro_stack, -1 ) " endif " " apply flag? if ! empty ( mlist[3] ) let m_text = s:ApplyFlag ( m_text, mlist[3] ) endif " let text1 .= mlist[1].m_text let text2 = mlist[4] " endwhile " return text1.text2 " endfunction " ---------- end of function s:ReplaceMacros ---------- " " "---------------------------------------------------------------------- " " s:ReplaceMacrosOld : Replace all the macros in a text. {{{1 " "---------------------------------------------------------------------- " " " function! s:ReplaceMacrosOld ( text, m_local, m_global ) " " " " TODO: prevent recursion of macros " " TODO: should be rewritten, so that this works as expected: " " |DDD| = test " " |EEE| = |DDD| " " Expanding |EEE:u| will result in: " " test " " " let text = a:text " " " while 1 " " " let mlist = matchlist ( text, s:library.regex_template.MacroInsert ) " " " " no more macros? " if empty ( mlist ) " break " endif " " " if has_key ( a:m_local, mlist[1] ) " let m_text = get ( a:m_local, mlist[1] ) " else " let m_text = get ( a:m_global, mlist[1], '' ) " end " " " " apply flag? " if ! empty ( mlist[2] ) " let m_text = s:ApplyFlag ( m_text, mlist[2] ) " endif " " " " insert the replacement " let text = s:LiteralReplacement ( text, mlist[0], m_text, 'g' ) " " " endwhile " " " return text " " " endfunction " ---------- end of function s:ReplaceMacrosOld ---------- " "---------------------------------------------------------------------- " === Templates === {{{1 "---------------------------------------------------------------------- " "---------------------------------------------------------------------- " s:CheckHelp : Check a template (pick-file). {{{2 "---------------------------------------------------------------------- " let s:NamespaceHelp = { \ 'Word' : 's', \ 'Pattern' : 's', 'Default' : 's', \ 'Substitute' : 'sss', 'LiteralSub' : 'sss', \ 'System' : 's', 'Vim' : 's', \ } " function! s:CheckHelp ( cmds, text, calls ) return [ a:cmds, a:text ] endfunction " ---------- end of function s:CheckHelp ---------- " " "---------------------------------------------------------------------- " " s:CheckPickFile : Check a template (pick-file). {{{2 " "---------------------------------------------------------------------- " " " let s:NamespacePickFile = { " \ 'Prompt' : 's', " \ 'Path' : 's', 'GetPath' : 's', " \ } " " " function! s:CheckPickFile ( cmds, text, calls ) " return [ a:cmds, a:text ] " endfunction " ---------- end of function s:CheckPickFile ---------- " " " "---------------------------------------------------------------------- " " s:CheckPickList : Check a template (pick-list). {{{2 " "---------------------------------------------------------------------- " " " let s:NamespacePickList = { " \ 'Prompt' : 's', " \ 'List' : '[ld]', 'GetList' : 's', " \ } " " " function! s:CheckPickList ( cmds, text, calls ) " return [ a:cmds, a:text ] " endfunction " ---------- end of function s:CheckPickList ---------- " "---------------------------------------------------------------------- " s:CheckStdTempl : Check a template (standard). {{{2 "---------------------------------------------------------------------- " let s:NamespaceStdTempl = { \ 'DefaultMacro' : 's[sl]', \ 'PickFile' : 'ss', \ 'PickList' : 's[sld]', \ 'Prompt' : 'ss', \ 'SurroundWith' : 's[sl]*', \ } let s:NamespaceStdTemplInsert = { \ 'Comment' : 's\?', \ 'Insert' : 's[sl]*', \ 'InsertLine' : 's[sl]*', \ } " function! s:CheckStdTempl ( cmds, text, calls ) " let regex = s:library.regex_template let ms = regex.MacroStart let me = regex.MacroEnd " let cmds = a:cmds let text = a:text " let prompted = {} " " -------------------------------------------------- " replacements " -------------------------------------------------- while 1 " let mlist = matchlist ( text, regex.MacroRequest ) " " no more macros? if empty ( mlist ) break endif " let m_name = mlist[1] let m_flag = mlist[2] " " not a special macro and not already done? if has_key ( s:StandardMacros, m_name ) call s:ErrorMsg ( 'The special macro "'.m_name.'" can not be replaced via |?'.m_name.'|.' ) elseif ! has_key ( prompted, m_name ) let cmds .= "Prompt(".string(m_name).",".string(m_flag).")\n" let prompted[ m_name ] = 1 endif " if ! empty ( m_flag ) | let m_flag = ':'.m_flag | endif " " insert a normal macro let text = s:LiteralReplacement ( text, \ mlist[0], ms.m_name.m_flag.me, 'g' ) " endwhile " " -------------------------------------------------- " lists " -------------------------------------------------- let list_items = [ 'EMPTY', 'SINGLE', 'FIRST', 'LAST' ] " + 'ENTRY' " while 1 " let mlist = matchlist ( text, regex.ListItem ) " " no more macros? if empty ( mlist ) break endif " let l_name = mlist[1] " let mlist = matchlist ( text, \ '\([^'."\n".']*\)'.ms.l_name.':ENTRY_*'.me.'\([^'."\n".']*\)\n' ) " let cmds .= "LIST(".string(l_name)."," \ .string(mlist[1]).",".string(mlist[2]).")\n" let text = s:LiteralReplacement ( text, \ mlist[0], ms.l_name.':LIST'.me."\n", '' ) " for item in list_items " let mlist = matchlist ( text, \ '\([^'."\n".']*\)'.ms.l_name.':'.item.'_*'.me.'\([^'."\n".']*\)\n' ) " if empty ( mlist ) let cmds .= "\n" continue endif " let cmds .= "[".string(mlist[1]).",".string(mlist[2])."]\n" let text = s:LiteralReplacement ( text, mlist[0], '', '' ) endfor " endwhile " " -------------------------------------------------- " comments " -------------------------------------------------- while 1 " let mlist = matchlist ( text, regex.FunctionComment ) " " no more comments? if empty ( mlist ) break endif " let [ f_name, f_param ] = mlist[ 1 : 2 ] " " check the call call s:FunctionCheck ( 'Comment', f_param, s:NamespaceStdTemplInsert ) " exe 'let flist = ['.f_param.']' " if empty ( flist ) | let flag = 'eol' else | let flag = flist[0] | endif " let mlist = matchlist ( text, regex.FunctionComment.'\s*\([^'."\n".']*\)' ) " let text = s:LiteralReplacement ( text, mlist[0], \ ms.'InsertLine("Comments.end-of-line","|CONTENT|",'.string( mlist[3] ).')'.me, '' ) " endwhile " return [ cmds, text ] " endfunction " ---------- end of function s:CheckStdTempl ---------- " "---------------------------------------------------------------------- " s:CheckTemplate : Check a template. {{{2 " " Get the command and text block. "---------------------------------------------------------------------- " function! s:CheckTemplate ( template, type ) " let regex = s:library.regex_template " let cmds = '' let text = '' let calls = [] " " the known functions if a:type == 't' let namespace = s:NamespaceStdTempl elseif a:type == 'pick-file' let namespace = s:NamespacePickFile elseif a:type == 'pick-list' let namespace = s:NamespacePickList elseif a:type == 'help' let namespace = s:NamespaceHelp endif " " go trough the lines let idx = 0 while idx < len ( a:template ) " let idx_n = stridx ( a:template, "\n", idx ) let mlist = matchlist ( a:template[ idx : idx_n ], regex.FunctionLine ) " " no match or 'Comment' or 'Insert' function? if empty ( mlist ) || mlist[ 2 ] =~ regex.TextBlockFunctions break endif " let [ f_name, f_param ] = mlist[ 2 : 3 ] " " check the call call s:FunctionCheck ( f_name, f_param, namespace ) " call add ( calls, [ f_name, f_param ] ) " let cmds .= mlist[1]."\n" let idx += len ( mlist[0] ) " endwhile " let text = a:template[ idx : -1 ] " " checks depending on the type if a:type == 't' return s:CheckStdTempl( cmds, text, calls ) elseif a:type == 'pick-file' return s:CheckPickFile( cmds, text, calls ) elseif a:type == 'pick-list' return s:CheckPickList( cmds, text, calls ) elseif a:type == 'help' return s:CheckHelp( cmds, text, calls ) endif " endfunction " ---------- end of function s:CheckTemplate ---------- " "---------------------------------------------------------------------- " s:GetTemplate : Get a template. {{{2 "---------------------------------------------------------------------- " function! s:GetTemplate ( name, style ) " let name = a:name let style = a:style " " check the template if has_key ( s:library.templates, name.'!!type' ) let info = s:library.templates[ a:name.'!!type' ] let [ type, placement, indentation ] = split ( info, ',' ) else throw 'Template:Prepare:template does not exist' endif " if style == '!any' for s in s:library.styles if has_key ( s:library.templates, name.'!'.s ) let template = get ( s:library.templates, name.'!'.s ) let style = s endif endfor else " check the style if has_key ( s:library.templates, name.'!'.style ) let template = get ( s:library.templates, name.'!'.style ) elseif has_key ( s:library.templates, name.'!default' ) let template = get ( s:library.templates, name.'!default' ) let style = 'default' elseif style == 'default' throw 'Template:Check:template does not have the default style'. else throw 'Template:Check:template has neither the style "'.style.'" nor the default style' endif endif " " check the text let head = template[ 0 : 5 ] " if head == "|P()|\n" " plain text " TODO: special type for plain let cmds = '' let text = template[ 6 : -1 ] elseif head == "|T()|\n" " only text (contains only macros without '?') " TODO: special type for text " TODO: no need to call 's:PrepareStdTempl', " once every other special forces an entry in the command block let cmds = '' let text = template[ 6 : -1 ] elseif head == "|C()|\n" " command and text block let splt = stridx ( template, "|T()|\n" ) - 1 let cmds = template[ 6 : splt ] let text = template[ splt+7 : -1 ] else " " do checks let [ cmds, text ] = s:CheckTemplate ( template, type ) " " save the result if empty ( cmds ) let template = "|T()|\n".text else let template = "|C()|\n".cmds."|T()|\n".text end let s:library.templates[ a:name.'!'.style ] = template " " echo cmds."<" " echo text."<" end " return [ cmds, text, type, placement, indentation ] endfunction " ---------- end of function s:GetTemplate ---------- " "---------------------------------------------------------------------- " s:GetPickList : Get the list (pick-list). {{{2 "---------------------------------------------------------------------- " function! s:GetPickList ( name ) " let regex = s:library.regex_template " " get the template let [ cmds, text, type, placement, indentation ] = s:GetTemplate ( a:name, '!any' ) " if type == 't' " for line in split( cmds, "\n" ) " the line will match and it will be a valid function let [ f_name, f_param ] = matchlist ( line, regex.FunctionChecked )[ 1 : 2 ] " if f_name == 'PickList' " exe 'let [ _, listarg ] = [ '.f_param.' ]' " let entry = '' " if type ( listarg ) == type ( '' ) if ! has_key ( s:library.resources, 'list!'.listarg ) call s:ErrorMsg ( 'List "'.listarg.'" does not exist.' ) return [] endif let list = s:library.resources[ 'list!'.listarg ] else let list = listarg endif " endif endfor " elseif type == 'pick-list' " for line in split( cmds, "\n" ) " the line will match and it will be a valid function let [ f_name, f_param ] = matchlist ( line, regex.FunctionChecked )[ 1 : 2 ] " if f_name == 'List' exe 'let list = '.f_param elseif f_name == 'GetList' " let listname = matchstr ( f_param, regex.RemoveQuote ) if ! has_key ( s:library.resources, 'list!'.listname ) call s:ErrorMsg ( 'List "'.listname.'" does not exist.' ) return [] endif let list = s:library.resources[ 'list!'.listname ] " endif endfor " else call s:ErrorMsg ( 'Template "'.a:name.'" is not a list picker.' ) return [] endif " if type ( list ) == type ( [] ) return list else return sort ( keys ( list ) ) endif " endfunction " ---------- end of function s:GetPickList ---------- " "---------------------------------------------------------------------- " s:PrepareHelp : Prepare a template (pick-file). {{{2 "---------------------------------------------------------------------- " function! s:PrepareHelp ( cmds, text ) " let regex = s:library.regex_template " let pick = '' let default = '' let method = '' let call = '' " let buf_line = getline('.') let buf_pos = col('.') - 1 " " ================================================== " command block " ================================================== " for line in split( a:cmds, "\n" ) " " the line will match and it will be a valid function let [ f_name, f_param ] = matchlist ( line, regex.FunctionChecked )[ 1 : 2 ] " if f_name == 'C' " ignore elseif f_name == 'Word' exe 'let switch = '.f_param | " TODO: works differently than 'Pattern': picks up word behind the cursor, too if switch == 'W' | let pick = expand('') else | let pick = expand('') | endif elseif f_name == 'Pattern' exe 'let pattern = '.f_param let cnt = 1 while 1 let m_end = matchend ( buf_line, pattern, 0, cnt ) - 1 if m_end < 0 let pick = '' break elseif m_end >= buf_pos let m_start = match ( buf_line, pattern, 0, cnt ) if m_start <= buf_pos | let pick = buf_line[ m_start : m_end ] else | let pick = '' | endif break endif let cnt += 1 endwhile elseif f_name == 'Default' exe 'let default = '.f_param elseif f_name == 'LiteralSub' exe 'let [ p, r, f ] = ['.f_param.']' let pick = s:LiteralReplacement ( pick, p, r, f ) elseif f_name == 'Substitute' exe 'let [ p, r, f ] = ['.f_param.']' let pick = substitute ( pick, p, r, f ) elseif f_name == 'System' || f_name == 'Vim' let method = f_name exe 'let call = '.f_param endif " endfor " " ================================================== " call for help " ================================================== " if empty ( pick ) && empty ( default ) \ || empty ( method ) return '' endif " let m_local = copy ( s:t_runtime.macros ) " if ! empty ( pick ) let m_local.PICK = pick let call = s:ReplaceMacros ( call, m_local ) else let call = s:ReplaceMacros ( default, m_local ) endif " if method == 'System' echo 'call system ( '.string ( call ).' )' | " debug exe 'call system ( '.string ( call ).' )' elseif method == 'Vim' echo call | " debug exe call endif " return '' " endfunction " ---------- end of function s:PrepareHelp ---------- " " "---------------------------------------------------------------------- " " s:PreparePickFile : Prepare a template (pick-file). {{{2 " "---------------------------------------------------------------------- " " " function! s:PreparePickFile ( cmds, text ) " " " let regex = s:library.regex_template " " " let msg = 'File' " let path = '' " let text = a:text " " " " ================================================== " " command block " " ================================================== " " " for line in split( a:cmds, "\n" ) " " " " the line will match and it will be a valid function " let [ f_name, f_param ] = matchlist ( line, regex.FunctionChecked )[ 1 : 2 ] " " " if f_name == 'C' " " ignore " elseif f_name == 'Prompt' " let msg = matchstr ( f_param, regex.RemoveQuote ) " elseif f_name == 'Path' " let path = matchstr ( f_param, regex.RemoveQuote ) " elseif f_name == 'GetPath' " " " let path = matchstr ( f_param, regex.RemoveQuote ) " if ! has_key ( s:library.resources, 'path!'.path ) " throw 'Template:Prepare:the resources "'.path.'" does not exist' " endif " let path = s:library.resources[ 'path!'.path ] " endif " " " endfor " " " " ================================================== " " get the path " " ================================================== " " " let path = expand ( path ) " let file = s:UserInput ( msg.' : ', path, 'file' ) " " " let m_local = copy ( s:t_runtime.macros ) " " " let m_local.PICK_COMPL = file " let m_local.PATH_COMPL = fnamemodify ( file, ':h' ) " " " let file = substitute ( file, '\V\^'.path, '', '' ) " " " let m_local.PICK = file " let m_local.PATH = fnamemodify ( file, ':h' ) " let m_local.FILENAME = fnamemodify ( file, ':t' ) " let m_local.BASENAME = fnamemodify ( file, ':t:r' ) " let m_local.SUFFIX = fnamemodify ( file, ':e' ) " " " let text = s:ReplaceMacros ( text, m_local, s:library.macros ) " " " return text " " " endfunction " ---------- end of function s:PreparePickFile ---------- " " " "---------------------------------------------------------------------- " " s:PreparePickList : Prepare a template (pick-list). {{{2 " "---------------------------------------------------------------------- " " " function! s:PreparePickList ( cmds, text ) " " " let regex = s:library.regex_template " " " let msg = 'Choose' " let text = a:text " let entry = '' " " " " ================================================== " " command block " " ================================================== " " " for line in split( a:cmds, "\n" ) " " " " the line will match and it will be a valid function " let [ f_name, f_param ] = matchlist ( line, regex.FunctionChecked )[ 1 : 2 ] " " " if f_name == 'C' " " ignore " elseif f_name == 'Prompt' " let msg = matchstr ( f_param, regex.RemoveQuote ) " elseif f_name == 'List' " exe 'let list = '.f_param " elseif f_name == 'GetList' " " " let listname = matchstr ( f_param, regex.RemoveQuote ) " if ! has_key ( s:library.resources, 'list!'.listname ) " throw 'Template:Prepare:the resources "'.listname.'" does not exist' " endif " let list = s:library.resources[ 'list!'.listname ] " " " elseif f_name == 'Pick' " let entry = matchstr ( f_param, regex.RemoveQuote ) " endif " " " endfor " " " " ================================================== " " get the entry " " ================================================== " " " if ! exists ( 'list' ) " throw 'Template:Check:no list specified' " endif " " " if type ( list ) == type ( [] ) " let type = 'list' " let input_list = list " else " let type = 'dict' " let input_list = sort ( keys ( list ) ) " endif " " " if empty ( entry ) " let entry = s:UserInput ( msg.' : ', '', 'customlist', input_list ) " endif " " " let m_local = copy ( s:t_runtime.macros ) " let m_local.KEY = entry " " " " TODO: entry might not exist " if type == 'dict' " let entry = list[ entry ] " endif " " " let m_local.VALUE = entry " let m_local.PICK = entry " " " let text = s:ReplaceMacros ( text, m_local, s:library.macros ) " " " return text " " " endfunction " ---------- end of function s:PreparePickList ---------- " "---------------------------------------------------------------------- " s:PrepareStdTempl : Prepare a template (standard). {{{2 "---------------------------------------------------------------------- " function! s:PrepareStdTempl ( cmds, text ) " " TODO: revert must work like a stack, first set, last reverted " TODO: revert in case of PickList and PickFile " let regex = s:library.regex_template let ms = regex.MacroStart let me = regex.MacroEnd " let m_local = s:t_runtime.macros let m_global = s:library.macros let prompted = s:t_runtime.prompted " let text = a:text let surround = '' let revert = '' " " " ================================================== " command block " ================================================== " let cmds = split( a:cmds, "\n" ) let i_cmds = 0 let n_cmds = len( cmds ) " while i_cmds < n_cmds " " the line will match and it will be a valid function let [ f_name, f_param ] = matchlist ( cmds[ i_cmds ], regex.FunctionChecked )[ 1 : 2 ] " if f_name == 'C' " ignore elseif f_name == 'SurroundWith' let surround = f_param elseif f_name == 'DefaultMacro' " let [ m_name, m_text ] = eval ( '[ '.f_param.' ]' ) " if ! has_key ( m_local, m_name ) let revert = 'call remove ( m_local, "'.m_name.'" ) | '.revert let m_local[ m_name ] = m_text endif " elseif f_name == 'PickFile' " let [ p_prompt, p_path ] = eval ( '[ '.f_param.' ]' ) " if p_path =~ regex.MacroName if ! has_key ( s:library.resources, 'path!'.p_path ) throw 'Template:Prepare:the resources "'.p_path.'" does not exist' endif let p_path = s:library.resources[ 'path!'.p_path ] endif " let p_path = expand ( p_path ) let file = s:UserInput ( p_prompt.' : ', p_path, 'file' ) " let m_local.PICK_COMPL = file let m_local.PATH_COMPL = fnamemodify ( file, ':h' ) " let file = substitute ( file, '\V\^'.p_path, '', '' ) " let m_local.PICK = file let m_local.PATH = fnamemodify ( file, ':h' ) let m_local.FILENAME = fnamemodify ( file, ':t' ) let m_local.BASENAME = fnamemodify ( file, ':t:r' ) let m_local.SUFFIX = fnamemodify ( file, ':e' ) " elseif f_name == 'PickEntry' " let [ p_which, p_entry ] = eval ( '[ '.f_param.' ]' ) " let l:pick_entry = p_entry " elseif f_name == 'PickList' " let [ p_prompt, p_list ] = eval ( '[ '.f_param.' ]' ) " if type ( p_list ) == type ( '' ) if ! has_key ( s:library.resources, 'list!'.p_list ) throw 'Template:Prepare:the resources "'.p_list.'" does not exist' endif let list = s:library.resources[ 'list!'.p_list ] else let list = p_list end " if type ( list ) == type ( [] ) let type = 'list' let input_list = list else let type = 'dict' let input_list = sort ( keys ( list ) ) endif " if exists ( 'l:pick_entry' ) let entry = l:pick_entry else let entry = s:UserInput ( p_prompt.' : ', '', 'customlist', input_list ) endif " let m_local.KEY = entry " if type == 'dict' if ! has_key ( list, entry ) throw 'Template:Prepare:the entry "'.entry.'" does not exist' endif let entry = list[ entry ] endif " let m_local.VALUE = entry let m_local.PICK = entry " elseif f_name == 'Prompt' " let [ m_name, m_flag ] = eval ( '[ '.f_param.' ]' ) " " not already done and not a local macro? if ! has_key ( prompted, m_name ) \ && ! has_key ( m_local, m_name ) let m_text = get ( m_global, m_name, '' ) " " prompt user for replacement let flagaction = get ( s:Flagactions, m_flag, '' ) " notify flag action, if any let m_text = s:UserInput ( m_name.flagaction.' : ', m_text ) let m_text = s:ApplyFlag ( m_text, m_flag ) " " save the result let m_global[ m_name ] = m_text let prompted[ m_name ] = 1 endif else break endif " let i_cmds += 1 endwhile " " -------------------------------------------------- " lists " -------------------------------------------------- " while i_cmds < n_cmds " let mlist = matchlist ( cmds[ i_cmds ], regex.FunctionList ) " if empty ( mlist ) break endif "echo '--'.mlist[1].'--' " exe 'let [ l_name, head_def, tail_def ] = ['.mlist[1].']' let l_text = '' if ! has_key ( m_local, l_name ) let l_len = 0 elseif type ( m_local[ l_name ] ) == type ( '' ) let l_list = [ m_local[ l_name ] ] let l_len = 1 else let l_list = m_local[ l_name ] let l_len = len ( l_list ) endif " if l_len == 0 if ! empty ( cmds[ i_cmds+1 ] ) exe 'let [ head, tail ] = '.cmds[ i_cmds+1 ] let l_text = head.tail."\n" endif elseif l_len == 1 if ! empty ( cmds[ i_cmds+2 ] ) exe 'let [ head, tail ] = '.cmds[ i_cmds+2 ] let l_text = head.l_list[0].tail."\n" elseif ! empty ( cmds[ i_cmds+3 ] ) exe 'let [ head, tail ] = '.cmds[ i_cmds+3 ] let l_text = head.l_list[0].tail."\n" else let l_text = head_def.l_list[0].tail_def."\n" end else " l_len >= 2 " if ! empty ( cmds[ i_cmds+3 ] ) exe 'let [ head, tail ] = '.cmds[ i_cmds+3 ] let l_text .= head.l_list[0].tail."\n" else let l_text .= head_def.l_list[0].tail_def."\n" endif " for idx in range ( 1, l_len-2 ) let l_text .= head_def.l_list[idx].tail_def."\n" endfor " if ! empty ( cmds[ i_cmds+4 ] ) exe 'let [ head, tail ] = '.cmds[ i_cmds+4 ] let l_text .= head.l_list[-1].tail."\n" else let l_text .= head_def.l_list[-1].tail_def."\n" endif endif " let text = s:LiteralReplacement ( text, ms.l_name.':LIST'.me."\n", l_text, '' ) " let i_cmds += 5 endwhile " " ================================================== " text block: macros and templates " ================================================== " " insert other templates while 1 " let mlist = matchlist ( text, regex.FunctionInsert ) " " no more inserts? if empty ( mlist ) break endif " let [ f_name, f_param ] = mlist[ 1 : 2 ] " " check the call call s:FunctionCheck ( f_name, f_param, s:NamespaceStdTemplInsert ) " if f_name == 'InsertLine' " get the replacement exe 'let m_text = s:PrepareTemplate ( '.f_param.' )[0]' let m_text = m_text[ 0 : -2 ] " check if m_text =~ "\n" throw 'Template:Prepare:inserts more than a single line: "'.mlist[0].'"' endif elseif f_name == 'Insert' " get the replacement exe 'let m_text = s:PrepareTemplate ( '.f_param.' )[0]' let m_text = m_text[ 0 : -2 ] " prepare let mlist = matchlist ( text, '\([^'."\n".']*\)'.regex.FunctionInsert.'\([^'."\n".']*\)' ) let head = mlist[1] let tail = mlist[4] let m_text = head.substitute( m_text, "\n", tail."\n".head, 'g' ).tail else throw 'Template:Check:the function "'.f_name.'" does not exist' endif " " insert let text = s:LiteralReplacement ( text, mlist[0], m_text, '' ) " endwhile " " insert the replacements let text = s:ReplaceMacros ( text, m_local ) " " ================================================== " surround the template " ================================================== " if ! empty ( surround ) " get the replacement exe 'let [ s_text, s_place ] = s:PrepareTemplate ( '.surround.', "do_surround" )' " if s_place == 'CONTENT' if -1 == match( s_text, '' ) throw 'Template:Check:surround template: missing' endif " let mcontext = matchlist ( s_text, '\([^'."\n".']*\)'.''.'\([^'."\n".']*\)' ) let head = mcontext[1] let tail = mcontext[2] " insert let text = text[ 0: -2 ] " remove trailing '\n' let text = head.substitute( text, "\n", tail."\n".head, 'g' ).tail let text = s:LiteralReplacement ( s_text, mcontext[0], text, '' ) elseif s_place == 'SPLIT' if -1 == match( s_text, '' ) throw 'Template:Check:surround template: missing' endif " if match( s_text, '\s*\n' ) >= 0 let part = split ( s_text, '\s*\s*\n', 1 ) else let part = split ( s_text, '', 1 ) endif let text = part[0].text.part[1] endif endif " exe revert " return text " endfunction " ---------- end of function s:PrepareStdTempl ---------- " "---------------------------------------------------------------------- " s:PrepareTemplate : Prepare a template for insertion. {{{2 "---------------------------------------------------------------------- " function! s:PrepareTemplate ( name, ... ) " let regex = s:library.regex_template " " ================================================== " setup and checks " ================================================== " " check for recursion if -1 != index ( s:t_runtime.obj_stack, a:name ) call add ( s:t_runtime.obj_stack, a:name ) throw 'Template:Recursion' endif " call add ( s:t_runtime.obj_stack, a:name ) " " current style let style = s:library.current_style " " get the template let [ cmds, text, type, placement, indentation ] = s:GetTemplate ( a:name, style ) " " current macros let m_local = s:t_runtime.macros let prompted = s:t_runtime.prompted " let remove_cursor = 1 let remove_split = 1 let use_surround = 0 let use_split = 0 " let revert = '' " " ================================================== " parameters " ================================================== " let i = 1 while i <= a:0 " if a:[i] =~ regex.MacroMatch && i+1 <= a:0 let m_name = matchlist ( a:[i], regex.MacroNameC )[1] if has_key ( m_local, m_name ) let revert = 'let m_local["'.m_name.'"] = '.string( m_local[ m_name ] ).' | '.revert else let revert = 'call remove ( m_local, "'.m_name.'" ) | '.revert endif let m_local[ m_name ] = a:[i+1] let i += 2 elseif a:[i] == '' let remove_cursor = 0 let i += 1 elseif a:[i] == '' let remove_split = 0 let i += 1 elseif a:[i] == 'do_surround' let use_surround = 1 let i += 1 elseif a:[i] == 'use_split' let use_split = 1 let remove_split = 0 let i += 1 elseif a:[i] == 'pick' && i+1 <= a:0 let cmds = "PickEntry( '', ".string(a:[i+1])." )\n".cmds let i += 2 else if type ( a:[i] ) == type ( '' ) | call s:ErrorMsg ( 'Unknown option: "'.a:[i].'"' ) else | call s:ErrorMsg ( 'Unknown option at position '.i.'.' ) | endif let i += 1 endif " endwhile " " ================================================== " prepare " ================================================== " if type == 't' let text = s:PrepareStdTempl( cmds, text ) elseif type == 'pick-file' let text = s:PreparePickFile( cmds, text ) elseif type == 'pick-list' let text = s:PreparePickList( cmds, text ) elseif type == 'help' let text = s:PrepareHelp( cmds, text ) endif " if remove_cursor let text = s:LiteralReplacement( text, '', '', 'g' ) endif if remove_split let text = s:LiteralReplacement( text, '', '', 'g' ) endif if ! use_surround || use_split let text = s:LiteralReplacement( text, '', '', 'g' ) endif " " ================================================== " wrap up " ================================================== " exe revert " call remove ( s:t_runtime.obj_stack, -1 ) " if use_split return [ text, 'SPLIT' ] elseif use_surround return [ text, 'CONTENT' ] else return [ text, placement, indentation ] endif " endfunction " ---------- end of function s:PrepareTemplate ---------- " "---------------------------------------------------------------------- " s:InsertIntoBuffer : Insert a text into the buffer. {{{1 " (thanks to Fritz Mehner) "---------------------------------------------------------------------- " function! s:InsertIntoBuffer ( text, placement, indentation, flag_visual_mode ) " " TODO: syntax let regex = s:library.regex_template " let placement = a:placement let indentation = a:indentation == '1' " if a:flag_visual_mode == 0 " -------------------------------------------------- " command and insert mode " -------------------------------------------------- " " remove the split point let text = substitute( a:text, '\V'.'', '', 'g' ) " if placement == 'below' " exe ':'.s:t_runtime.range[1] call s:OpenFold('below') let pos1 = line(".")+1 put = text let pos2 = line(".") " elseif placement == 'above' " exe ':'.s:t_runtime.range[0] let pos1 = line(".") put! = text let pos2 = line(".") " elseif placement == 'start' " exe ':1' call s:OpenFold('start') let pos1 = 1 put! = text let pos2 = line(".") " elseif placement == 'append' || placement == 'insert' " if &foldenable && foldclosed(".") >= 0 echohl WarningMsg | echomsg s:MsgInsertionNotAvail | echohl None return elseif placement == 'append' let pos1 = line(".") put = text let pos2 = line(".")-1 exe ":".pos1 :join! let indentation = 0 elseif placement == 'insert' let text = text[ 0: -2 ] " remove trailing '\n' let currentline = getline( "." ) let pos1 = line(".") let pos2 = pos1 + count( split(text,'\zs'), "\n" ) "" assign to the unnamed register " : "let @"=text "normal p exe 'normal! a'.text " reformat only multi-line inserts and previously empty lines if pos1 == pos2 && currentline != '' let indentation = 0 endif endif " endif " elseif a:flag_visual_mode == 1 " -------------------------------------------------- " visual mode " -------------------------------------------------- " " remove the jump targets (2nd type) let text = substitute( a:text, regex.JumpTagType2, '', 'g' ) " " TODO: Is the behaviour well-defined? " Suggestion: The line might include a cursor and a split and nothing else. if match( text, '' ) >= 0 if match( text, '\s*\n' ) >= 0 let part = split ( text, '\s*\s*\n', 1 ) else let part = split ( text, '', 1 ) endif let part[1] = part[1][ 0: -2 ] " remove trailing '\n' else let part = [ "", text[ 0: -2 ] ] " remove trailing '\n' echomsg 'SPLIT missing in template.' endif " " 'visual' and placement 'insert': " " part0 and part1 can consist of several lines " " 'visual' and placement 'below': " " " " part0 and part1 can consist of several lines " if placement == 'insert' " windows: register @* does not work " solution: recover area of the visual mode and yank, " puts the selected area into the buffer @" normal gvy let pos1 = line(".") let pos2 = pos1 let str = escape ( @", '\' ) let repl = escape ( part[0].@".part[1], '\&~' ) " TODO: substitute is not the best choice, " problematic when '\V'.@* matches before the actual position exe ':s/\V'.str.'/'.repl.'/' let indentation = 0 elseif placement == 'below' :'put = part[1] let pos1 = line("'<") - len(split(part[0], '\n' )) let pos2 = line("'>") + len(split(part[1], '\n' )) endif " endif " " proper indenting if indentation exe ":".pos1 exe "normal ".( pos2-pos1+1 )."==" endif " return [ pos1, pos2 ] " endfunction " ---------- end of function s:InsertIntoBuffer ---------- " "---------------------------------------------------------------------- " s:PositionCursor : Position the cursor. {{{1 " (thanks to Fritz Mehner) "---------------------------------------------------------------------- " function! s:PositionCursor ( placement, flag_visual_mode, pos1, pos2 ) " " TODO: syntax " exe ":".a:pos1 let mtch = search( '\|{CURSOR}', 'c', a:pos2 ) if mtch != 0 " CURSOR found: let line = getline(mtch) if line =~ '$\|{CURSOR}$' call setline( mtch, substitute( line, '\|{CURSOR}', '', '' ) ) if a:flag_visual_mode == 1 && getline(".") =~ '^\s*$' normal J else startinsert! endif else call setline( mtch, substitute( line, '\|{CURSOR}', '', '' ) ) :startinsert endif else " no CURSOR found if a:placement == 'below' exe ":".a:pos2 | " to the end of the block; needed for repeated inserts endif endif " endfunction " ---------- end of function s:PositionCursor ---------- " "---------------------------------------------------------------------- " s:HighlightJumpTargets : Highlight the jump targets. {{{1 "---------------------------------------------------------------------- " function! s:HighlightJumpTargets ( regex ) exe 'match Search /'.a:regex.'/' endfunction " ---------- end of function s:HighlightJumpTargets ---------- " "---------------------------------------------------------------------- " mmtemplates#core#InsertTemplate : Insert a template. {{{1 "---------------------------------------------------------------------- " function! mmtemplates#core#InsertTemplate ( library, t_name, ... ) range " " TODO: check arguments, 'library' as a string " TODO: t_name == '!pick' " TODO: syntax for CURSOR, SPLIT, jump tags " " ================================================== " setup " ================================================== " " library and runtime information let s:library = a:library let s:t_runtime = { \ 'obj_stack' : [], \ 'macro_stack' : [], \ 'macros' : {}, \ 'prompted' : {}, \ \ 'placement' : '', \ 'range' : [ a:firstline, a:lastline ], \ } let regex = s:library.regex_template " " renew the predefined macros let s:t_runtime.macros[ 'BASENAME' ] = expand( '%:t:r' ) let s:t_runtime.macros[ 'FILENAME' ] = expand( '%:t' ) let s:t_runtime.macros[ 'PATH' ] = expand( '%:p:h' ) let s:t_runtime.macros[ 'SUFFIX' ] = expand( '%:e' ) " let s:t_runtime.macros[ 'DATE' ] = strftime( s:library.macros[ 'DATE' ] ) let s:t_runtime.macros[ 'TIME' ] = strftime( s:library.macros[ 'TIME' ] ) let s:t_runtime.macros[ 'YEAR' ] = strftime( s:library.macros[ 'YEAR' ] ) " " handle folds internally (and save the state) if &foldenable let foldmethod_save = &foldmethod set foldmethod=manual endif " use internal formatting to avoid conflicts when using == below " (and save the state) let equalprg_save = &equalprg set equalprg= " let flag_visual_mode = 0 let options = [] " " ================================================== " parameters " ================================================== " let i = 1 while i <= a:0 " if a:[i] == 'v' || a:[i] == 'visual' let flag_visual_mode = 1 let i += 1 elseif a:[i] == 'placement' && i+1 <= a:0 let s:t_runtime.placement = a:[i+1] let i += 2 elseif a:[i] == 'range' && i+2 <= a:0 let s:t_runtime.range[0] = a:[i+1] let s:t_runtime.range[1] = a:[i+2] let i += 3 elseif a:[i] =~ regex.MacroMatch && i+1 <= a:0 let name = matchlist ( a:[i], regex.MacroNameC )[1] let s:t_runtime.macros[ name ] = a:[i+1] let i += 2 elseif a:[i] == 'pick' && i+1 <= a:0 call add ( options, 'pick' ) call add ( options, a:[i+1] ) let i += 2 else if type ( a:[i] ) == type ( '' ) | call s:ErrorMsg ( 'Unknown option: "'.a:[i].'"' ) else | call s:ErrorMsg ( 'Unknown option at position '.i.'.' ) | endif let i += 1 endif " endwhile " " ================================================== " do the job " ================================================== " try " " prepare the template for insertion if empty ( options ) let [ text, placement, indentation ] = s:PrepareTemplate ( a:t_name, '', '' ) else let [ text, placement, indentation ] = call ( 's:PrepareTemplate', [ a:t_name, '', '' ] + options ) endif " if placement == 'help' " job already done, TODO: review this else " if ! empty ( s:t_runtime.placement ) let placement = s:t_runtime.placement endif " " insert the text into the buffer let [ pos1, pos2 ] = s:InsertIntoBuffer ( text, placement, indentation, flag_visual_mode ) " " position the cursor call s:PositionCursor ( placement, flag_visual_mode, pos1, pos2 ) " " highlight jump targets call s:HighlightJumpTargets ( regex.JumpTagBoth ) endif " catch /Template:UserInputAborted/ " noop catch /Template:Check:.*/ " let templ = s:t_runtime.obj_stack[ -1 ] let msg = v:exception[ len( 'Template:Check:') : -1 ] call s:ErrorMsg ( 'Checking "'.templ.'":', msg ) " catch /Template:Prepare:.*/ " let templ = s:t_runtime.obj_stack[ -1 ] let incld = len ( s:t_runtime.obj_stack ) == 1 ? '' : '(included by: "'.s:t_runtime.obj_stack[ -2 ].'")' let msg = v:exception[ len( 'Template:Prepare:') : -1 ] call s:ErrorMsg ( 'Preparing "'.templ.'":', incld, msg ) " catch /Template:Recursion/ " let templ = s:t_runtime.obj_stack[ -1 ] let idx1 = index ( s:t_runtime.obj_stack, templ ) let cont = idx1 == 0 ? [] : [ '...' ] call call ( 's:ErrorMsg', [ 'Recursion detected while including the template/s:' ] + cont + \ s:t_runtime.obj_stack[ idx1 : -1 ] ) " catch /Template:MacroRecursion/ " let macro = s:t_runtime.macro_stack[ -1 ] let idx1 = index ( s:t_runtime.macro_stack, macro ) let cont = idx1 == 0 ? [] : [ '...' ] call call ( 's:ErrorMsg', [ 'Recursion detected while replacing the macro/s:' ] + cont + \ s:t_runtime.macro_stack[ idx1 : -1 ] ) " " catch /Template:Insert:.*/ catch /Template:.*/ " let msg = v:exception[ len( 'Template:') : -1 ] call s:ErrorMsg ( msg ) " finally " " ================================================== " wrap up " ================================================== " " restore the state: folding and formatter program if &foldenable exe "set foldmethod=".foldmethod_save normal zv endif let &equalprg = equalprg_save " " remove script variables unlet s:library unlet s:t_runtime " endtry " endfunction " ---------- end of function mmtemplates#core#InsertTemplate ---------- " "---------------------------------------------------------------------- " mmtemplates#core#CreateMaps : Create maps for a template library. {{{1 "---------------------------------------------------------------------- " function! mmtemplates#core#CreateMaps ( library, localleader, ... ) " " check the arguments if type( a:library ) == type( '' ) exe 'let t_lib = '.a:library else call s:ErrorMsg ( 'Argument "library" must be given as a string.' ) return endif " if type( a:localleader ) != type( '' ) call s:ErrorMsg ( 'Argument "localleader" must be given as a string.' ) return elseif ! empty ( a:localleader ) let maplocalleader = a:localleader endif " " the commands have been generated before if has_key ( t_lib, 'map_commands' ) "let TimeStart = reltime() exe t_lib.map_commands "echo 'Executing maps: '.reltimestr( reltime( TimeStart ) ) return endif " " options let options = ' ' let leader = '' let sep = "\n" " let do_jump_map = 0 let do_special_maps = 0 " let cmd = '' " " parameters let i = 1 while i <= a:0 " if a:[i] == 'do_jump_map' let do_jump_map = 1 let i += 1 elseif a:[i] == 'do_special_maps' let do_special_maps = 1 let i += 1 else if type ( a:[i] ) == type ( '' ) | call s:ErrorMsg ( 'Unknown option: "'.a:[i].'"' ) else | call s:ErrorMsg ( 'Unknown option at position '.i.'.' ) | endif let i += 1 endif " endwhile " "let TimeStart = reltime() " " go through all the templates for t_name in t_lib.temp_list " exe 'let [ entry, visual, shortcut, mp ] = ['.t_lib.templates[ t_name.'!!menu' ].']' " " no map? if empty ( mp ) continue endif " for mode in [ 'n', 'v', 'i' ] " " map already existing? if ! empty ( maparg( leader.mp, mode ) ) if g:Templates_MapInUseWarn != 0 call s:ErrorMsg ( 'Mapping already in use: "'.leader.mp.'", mode "'.mode.'"' ) endif continue endif " " visual and insert mode: insert '' if mode == 'n' | let esc = '' else | let esc = '' | endif " " visual mode: template contains a split tag, or the mode is forced if mode == 'v' && visual == 1 | let v = ',"v"' else | let v = '' | endif " " assemble the command to create the maps let cmd .= mode.'noremap '.options.' '.leader.mp.' '.esc.':call mmtemplates#core#InsertTemplate('.a:library.',"'.t_name.'"'.v.')'.sep endfor " endfor " " jump map if do_jump_map let jump_key = '' " TODO: configurable if ! empty ( maparg( jump_key ) ) call s:ErrorMsg ( 'Mapping already in use: "'.jump_key.'"' ) else let jump_regex = string ( escape ( t_lib.regex_template.JumpTagBoth, '|' ) ) let cmd .= 'nnoremap '.options.' '.jump_key.' i=mmtemplates#core#JumpToTag('.jump_regex.')'.sep let cmd .= 'inoremap '.options.' '.jump_key.' =mmtemplates#core#JumpToTag('.jump_regex.')'.sep endif endif " " special maps " TODO: configuration of maps " TODO: edit template if do_special_maps let special_maps = { \ 're' : ':call mmtemplates#core#EditTemplateFiles('.a:library.',-1)', \ 'rr' : ':call mmtemplates#core#ReadTemplates('.a:library.',"reload","all")', \ 'rs' : ':call mmtemplates#core#ChooseStyle('.a:library.',"!pick")', \ } "\ 'rt' : ':call mmtemplates#core#InsertTemplate('.a:library.',"!pick")', " for [ mp, action ] in items ( special_maps ) if ! empty ( maparg( leader.mp ) ) call s:ErrorMsg ( 'Mapping already in use: "'.leader.mp.'"' ) else let cmd .= ' noremap '.options.' '.leader.mp.' '.action.sep let cmd .= 'inoremap '.options.' '.leader.mp.' '.action.sep endif endfor endif " let t_lib.map_commands = cmd exe cmd " "echo 'Generating maps: '.reltimestr( reltime( TimeStart ) ) " endfunction " ---------- end of function mmtemplates#core#CreateMaps ---------- " "---------------------------------------------------------------------- " === Create Menus: Auxiliary functions. === {{{1 "---------------------------------------------------------------------- " "---------------------------------------------------------------------- " s:CreateSubmenu : Create sub-menus, given they do not already exists. {{{2 " " The menu 'menu' can contain '&' and a trailing '.'. Both are ignored. "---------------------------------------------------------------------- function! s:CreateSubmenu ( t_lib, root_menu, global_name, menu ) " " go through the menu, clean up and check for new menus let submenu = '' for part in split( a:menu, '\.' ) let clean = substitute( part, '&', '', 'g' ) if ! has_key ( a:t_lib.menu_existing, submenu.clean ) " a new menu! let a:t_lib.menu_existing[ submenu.clean ] = 1 " " shortcut and menu entry if has_key ( a:t_lib.menu_shortcuts, submenu.clean ) let shortcut = a:t_lib.menu_shortcuts[ submenu.clean ] if stridx ( tolower( clean ), tolower( shortcut ) ) == -1 let assemble = submenu.clean.' (&'.shortcut.')' else let assemble = submenu.substitute( clean, '\c'.shortcut, '\&&', '' ) endif else let assemble = submenu.part endif " let assemble .= '.' " if -1 != stridx ( clean, '' ) exe 'amenu '.a:root_menu.escape( assemble.clean, ' ' ).' :echo "This is a menu header."' else exe 'amenu '.a:root_menu.escape( assemble.clean, ' ' ).''.escape( a:global_name, ' .' ).' :echo "This is a menu header."' endif exe 'amenu '.a:root_menu.escape( assemble, ' ' ).'-Sep00- ' endif let submenu .= clean.'.' endfor " endfunction " ---------- end of function s:CreateSubmenu ---------- " "---------------------------------------------------------------------- " s:CreateTemplateMenus : Create menus for the templates. {{{2 "---------------------------------------------------------------------- " function! s:CreateTemplateMenus ( t_lib, root_menu, global_name, t_lib_name ) " " go through all the templates for t_name in a:t_lib.temp_list " exe 'let [ entry, visual, shortcut, mp ] = ['.a:t_lib.templates[ t_name.'!!menu' ].']' " " no menu entry? if entry == 0 continue endif " " get the sub-menu and the entry let [ t_menu, t_last ] = matchlist ( t_name, '^\(.*\.\)\?\([^\.]\+\)$' )[1:2] " " menu does not exist? if ! empty ( t_menu ) && ! has_key ( a:t_lib.menu_existing, t_menu[ 0 : -2 ] ) call s:CreateSubmenu ( a:t_lib, a:root_menu, a:global_name, t_menu[ 0 : -2 ] ) endif " " shortcut and menu entry if ! empty ( shortcut ) if stridx ( tolower( t_last ), tolower( shortcut ) ) == -1 let t_last .= ' (&'.shortcut.')' else let t_last = substitute( t_last, '\c'.shortcut, '\&&', '' ) endif endif " " assemble the entry, including the map, TODO: escape the map let compl_entry = escape( t_menu.t_last, ' ' ) if empty ( mp ) let map_entry = '' else let map_entry = '\\'.mp end " if entry == 1 " prevents problems in insert mode exe 'amenu '.a:root_menu.compl_entry.map_entry.' :call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'")' if visual == 1 exe 'vmenu '.a:root_menu.compl_entry.map_entry.' :call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'","v")' endif elseif entry == 2 call s:CreateSubmenu ( a:t_lib, a:root_menu, a:global_name, t_menu.t_last.map_entry ) " for item in s:GetPickList ( t_name ) let item_entry = compl_entry.'.'.substitute ( substitute ( escape ( item, ' .' ), '&', '\&\&', 'g' ), '\w', '\&&', '' ) exe 'amenu '.a:root_menu.item_entry.' :call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'","pick",'.string(item).')' if visual == 1 exe 'vmenu '.a:root_menu.item_entry.' :call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'","v","pick",'.string(item).')' endif endfor " " exe 'amenu '.a:root_menu.compl_entry.'.-\ choose\ -'.map_entry.' :call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'")' " if visual == 1 " exe 'vmenu '.a:root_menu.compl_entry.'.-\ choose\ -'.map_entry.' :call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'","v")' " endif endif " endfor " endfunction " ---------- end of function s:CreateTemplateMenus ---------- " "---------------------------------------------------------------------- " s:CreateSpecialsMenus : Create menus for a template library. {{{2 "---------------------------------------------------------------------- " function! s:CreateSpecialsMenus ( t_lib, root_menu, global_name, t_lib_name, specials_menu, styles_only ) " " TODO: expansion of the 'styles'-menu should be configurable " let specials_menu = a:specials_menu let specials_menu = substitute( specials_menu, '\.$', '', '' ) " " new menu? if ! has_key ( a:t_lib.menu_existing, substitute ( specials_menu, '&', '', 'g' ) ) call s:CreateSubmenu ( a:t_lib, a:root_menu, a:global_name, specials_menu ) endif " if ! a:styles_only " create edit, reread and choose templates exe 'amenu '.a:root_menu.specials_menu.'.&edit\ templates' \ .' :call mmtemplates#core#EditTemplateFiles('.a:t_lib_name.',-1)' exe 'amenu '.a:root_menu.specials_menu.'.&reread\ templates' \ .' :call mmtemplates#core#ReadTemplates('.a:t_lib_name.',"reload","all")' " exe 'amenu '.a:root_menu.specials_menu.'.choose\ &template' " \ .' :call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"!pick")' endif " " create a menu for all the styles for s in a:t_lib.styles exe 'amenu '.a:root_menu.specials_menu.'.choose\ &style.'.escape( s, ' ' ) \ .' :call mmtemplates#core#ChooseStyle('.a:t_lib_name.','.string(s).')' endfor " endfunction " ---------- end of function s:CreateSpecialsMenus ---------- " "---------------------------------------------------------------------- " mmtemplates#core#CreateMenus : Create menus for a template library. {{{1 "---------------------------------------------------------------------- " function! mmtemplates#core#CreateMenus ( library, root_menu, ... ) " " TODO: correct escapes " TODO: separate special entries " TODO: mapleader configurable " " check for feature if ! has ( 'menu' ) return endif " " check the arguments if type( a:library ) == type( '' ) exe 'let t_lib = '.a:library let s:library = t_lib else call s:ErrorMsg ( 'Argument "library" must be given as a string.' ) return endif " if type( a:root_menu ) != type( '' ) call s:ErrorMsg ( 'Argument "root_menu" must be given as a string.' ) return endif " " prepare let root_menu = substitute( a:root_menu, '&', '', 'g' ) let global_name = substitute( root_menu, '\.$', '', '' ) let root_menu = global_name.'.' let specials_menu = '&Run' " let do_reset = 0 let do_templates = 0 let do_specials = 0 " no specials let existing = [] let submenus = [] " " parameters let i = 1 while i <= a:0 " if a:[i] == 'global_name' && i+1 <= a:0 let global_name = a:[i+1] let i += 2 elseif a:[i] == 'existing_menu' && i+1 <= a:0 if type ( a:[i+1] ) == type ( '' ) | call add ( existing, a:[i+1] ) else | call extend ( existing, a:[i+1] ) | endif let i += 2 elseif a:[i] == 'sub_menu' && i+1 <= a:0 if type ( a:[i+1] ) == type ( '' ) | call add ( submenus, a:[i+1] ) else | call extend ( submenus, a:[i+1] ) | endif let i += 2 elseif a:[i] == 'specials_menu' && i+1 <= a:0 let specials_menu = a:[i+1] let i += 2 elseif a:[i] == 'do_all' let do_reset = 1 let do_templates = 1 let do_specials = 1 let i += 1 elseif a:[i] == 'do_reset' let do_reset = 1 let i += 1 elseif a:[i] == 'do_templates' let do_templates = 1 let i += 1 elseif a:[i] == 'do_specials' let do_specials = 1 let i += 1 elseif a:[i] == 'do_styles' let do_specials = 2 let i += 1 else if type ( a:[i] ) == type ( '' ) | call s:ErrorMsg ( 'Unknown option: "'.a:[i].'"' ) else | call s:ErrorMsg ( 'Unknown option at position '.i.'.' ) | endif let i += 1 endif " endwhile " " reset if do_reset call filter ( t_lib.menu_existing, 0 ) endif " " existing menus for name in existing let name = substitute( name, '&', '', 'g' ) let name = substitute( name, '\.$', '', '' ) let t_lib.menu_existing[ name ] = 1 endfor " " sub-menus for name in submenus call s:CreateSubmenu ( t_lib, root_menu, global_name, name ) endfor " " templates if do_templates call s:CreateTemplateMenus ( t_lib, root_menu, global_name, a:library ) endif " " specials if do_specials == 1 " all specials call s:CreateSpecialsMenus ( t_lib, root_menu, global_name, a:library, specials_menu, 0 ) elseif do_specials == 2 " styles only call s:CreateSpecialsMenus ( t_lib, root_menu, global_name, a:library, specials_menu, 1 ) endif " " wrap up unlet s:library " endfunction " ---------- end of function mmtemplates#core#CreateMenus ---------- " "---------------------------------------------------------------------- " mmtemplates#core#ChooseStyle : Choose a style. {{{1 "---------------------------------------------------------------------- " function! mmtemplates#core#ChooseStyle ( library, style ) " " check the arguments if type( a:library ) == type( '' ) exe 'let t_lib = '.a:library elseif type( a:library ) == type( {} ) let t_lib = a:library else call s:ErrorMsg ( 'Argument "library" must be given as a dict or string.' ) return endif " if type( a:style ) != type( '' ) call s:ErrorMsg ( 'Argument "style" must be given as a string.' ) return endif " " pick and change the style if a:style == '!pick' try let style = s:UserInput( 'Style (currently '.t_lib.current_style.') : ', '', \ 'customlist', t_lib.styles ) catch /Template:UserInputAborted/ return endtry else let style = a:style endif " " check the new style if style == '' " noop elseif -1 != index ( t_lib.styles, style ) if t_lib.current_style != style let t_lib.current_style = style echo 'Changed style to "'.style.'".' endif else call s:ErrorMsg ( 'Style was not changed. Style "'.style.'" is not available.' ) end " endfunction " ---------- end of function mmtemplates#core#ChooseStyle ---------- " "---------------------------------------------------------------------- " mmtemplates#core#Resource : Access to various resources. {{{1 "---------------------------------------------------------------------- function! mmtemplates#core#Resource ( library, mode, ... ) " " TODO mode 'special' for |DATE|, |TIME| and |year| " " check the arguments if type( a:library ) == type( '' ) exe 'let t_lib = '.a:library elseif type( a:library ) == type( {} ) let t_lib = a:library else return [ '', 'Argument "library" must be given as a dict or string.' ] endif " if type( a:mode ) != type( '' ) return [ '', 'Argument "mode" must be given as a string.' ] endif " " some specials? if a:mode == 'get' || a:mode == 'set' " continue below elseif a:mode == 'style' return [ t_lib.current_style, '' ] elseif a:mode == 'jumptag' return [ t_lib.regex_template.JumpTagBoth, '' ] else return [ '', 'Mode "'.a:mode.'" is unknown.' ] endif " " type of 'resource' let types = { 'macro' : 's', 'path' : 's', 'list' : '[ld]' } " " additional arguments if a:mode == 'get' && a:0 < 2 return [ '', 'Mode "get" requires two additional arguments.' ] elseif a:mode == 'set' && a:0 < 3 return [ '', 'Mode "set" requires three additional arguments.' ] elseif type( a:1 ) != type( '' ) return [ '', 'Argument "resource" must be given as a string.' ] elseif type( a:2 ) != type( '' ) return [ '', 'Argument "key" must be given as a string.' ] elseif ! has_key ( types, a:1 ) return [ '', 'Resource "'.a:1.'" does not exist.' ] endif " let resource = a:1 let key = a:2 " " operation: 'get', 'set' or special? if a:mode == 'get' " " get if resource == 'macro' return [ get( t_lib.macros, key ), '' ] elseif resource == 'path' return [ get( t_lib.resources, 'path!'.key ), '' ] elseif resource == 'list' return [ get( t_lib.resources, 'list!'.key ), '' ] endif " elseif a:mode == 'set' " let value = a:3 " " check type and set if s:TypeNames[ type( value ) ] !~ types[ resource ] return [ '', 'Argument "value" has the wrong type.' ] elseif resource == 'macro' let t_lib.macros[ key ] = value elseif resource == 'path' let t_lib.resources[ 'path!'.key ] = value elseif resource == 'list' let t_lib.resources[ 'list!'.key ] = value endif " return [ 0, '' ] endif " endfunction " ---------- end of function mmtemplates#core#Resource ---------- " "---------------------------------------------------------------------- " mmtemplates#core#ExpandText : Expand the macros in a text. {{{1 "---------------------------------------------------------------------- function! mmtemplates#core#ExpandText ( library, text ) " " check the arguments if type( a:library ) == type( '' ) exe 'let t_lib = '.a:library elseif type( a:library ) == type( {} ) let t_lib = a:library else return s:ErrorMsg ( 'Argument "library" must be given as a dict or string.' ) endif " if type( a:text ) != type( '' ) return s:ErrorMsg ( 'Argument "text" must be given as a string.' ) endif " " runtime environment let s:library = t_lib let s:t_runtime = { \ 'macro_stack' : [], \ } " " renew the predefined macros let m_local = {} let m_local[ 'BASENAME' ] = expand( '%:t:r' ) let m_local[ 'FILENAME' ] = expand( '%:t' ) let m_local[ 'PATH' ] = expand( '%:p:h' ) let m_local[ 'SUFFIX' ] = expand( '%:e' ) " let m_local[ 'DATE' ] = strftime( t_lib.macros[ 'DATE' ] ) let m_local[ 'TIME' ] = strftime( t_lib.macros[ 'TIME' ] ) let m_local[ 'YEAR' ] = strftime( t_lib.macros[ 'YEAR' ] ) " let res = '' " try let res = s:ReplaceMacros ( a:text, m_local ) catch /Template:MacroRecursion/ " let macro = s:t_runtime.macro_stack[ -1 ] let idx1 = index ( s:t_runtime.macro_stack, macro ) let cont = idx1 == 0 ? [] : [ '...' ] call call ( 's:ErrorMsg', [ 'Recursion detected while replacing the macro/s:' ] + cont + \ s:t_runtime.macro_stack[ idx1 : -1 ] ) " catch /Template:.*/ " let msg = v:exception[ len( 'Template:') : -1 ] call s:ErrorMsg ( msg ) " finally unlet s:library unlet s:t_runtime endtry " return res " endfunction " ---------- end of function mmtemplates#core#ExpandText ---------- " "---------------------------------------------------------------------- " mmtemplates#core#JumpToTag : Access to various resources. {{{1 "---------------------------------------------------------------------- function! mmtemplates#core#JumpToTag ( regex ) " let match = search( a:regex, 'c' ) if match > 0 " remove the target call setline( match, substitute( getline('.'), a:regex, '', '' ) ) endif " return '' endfunction " ---------- end of function mmtemplates#core#JumpToTag ---------- " "------------------------------------------------------------------------------- " mmtemplates#core#EditTemplateFiles : Choose and edit a template file. {{{1 "------------------------------------------------------------------------------- function! mmtemplates#core#EditTemplateFiles ( library, file ) " " check the arguments if type( a:library ) == type( '' ) exe 'let t_lib = '.a:library elseif type( a:library ) == type( {} ) let t_lib = a:library else return s:ErrorMsg ( 'Argument "library" must be given as a dict or string.' ) endif " if type( a:file ) == type( 0 ) let file = t_lib.library_files[ a:file ] elseif type( a:file ) == type( '' ) " let file = expand ( a:file ) let file = s:ConcatNormalizedFilename ( file ) " if ! filereadable ( file ) return s:ErrorMsg ( 'The file "'.file.'" does not exist.' ) elseif index ( t_lib.library_files, file ) == -1 return s:ErrorMsg ( 'The file "'.file.'" is not part of the template library.' ) endif " else return s:ErrorMsg ( 'Argument "file" must be given as an integer or string.' ) endif " " get the directory let dir = fnamemodify ( file, ':h' ) " " TODO: method configurable let method = 'explore' let templatefile = '' " if ! filereadable ( file ) return s:ErrorMsg ( 'The directory "'.dir.'" does not exist.' ) elseif method == 'explore' " open a file explorer if ! exists ( 'g:loaded_netrwPlugin' ) | return s:ErrorMsg ( 'The plugin "netrw" is not available.' ) | endif exe 'update! | split | Explore '.dir elseif method == 'browse' " open a file browser if ! has ( 'browse' ) | return s:ErrorMsg ( 'The command "browse" is not available.' ) | endif let templatefile = browse ( 0, 'edit a template file', dir, '' ) " returns an empty string if "Cancel" is pressed endif " " open a buffer and start editing if ! empty ( templatefile ) exe 'update! | split | edit '.templatefile endif " endfunction " ---------- end of function mmtemplates#core#EditTemplateFiles ---------- " "------------------------------------------------------------------------------- " mmtemplates#core#ChangeSyntax : Change the syntax of the templates. {{{1 "------------------------------------------------------------------------------- function! mmtemplates#core#ChangeSyntax ( library, category, ... ) " " check the arguments if type( a:library ) == type( '' ) exe 'let t_lib = '.a:library elseif type( a:library ) == type( {} ) let t_lib = a:library else return s:ErrorMsg ( 'Argument "library" must be given as a dict or string.' ) endif " if type( a:category ) != type( '' ) return s:ErrorMsg ( 'Argument "category" must be given as an integer or string.' ) endif " if a:category == 'comment' " if a:0 < 1 return s:ErrorMsg ( 'Not enough arguments for '.a:category.'.' ) elseif a:0 == 1 let t_lib.regex_settings.CommentStart = a:1 let t_lib.regex_settings.CommentHint = a:1[0] elseif a:0 == 2 let t_lib.regex_settings.CommentStart = a:1 let t_lib.regex_settings.CommentHint = a:2[0] endif " call s:UpdateFileReadRegex ( t_lib.regex_file, t_lib.regex_settings ) " else return s:ErrorMsg ( 'Unknown category: '.a:category ) endif " endfunction " ---------- end of function mmtemplates#core#ChangeSyntax ---------- " }}}1 " " ===================================================================================== " vim: foldmethod=marker