2014-03-26 21:23:45 -04:00

3569 lines
107 KiB
VimL

"===============================================================================
"
" 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, '<SPLIT>' )
"
" --------------------------------------------------
" 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('<cWORD>')
else | let pick = expand('<cword>') | 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, '<CONTENT>' )
throw 'Template:Check:surround template: <CONTENT> missing'
endif
"
let mcontext = matchlist ( s_text, '\([^'."\n".']*\)'.'<CONTENT>'.'\([^'."\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, '<SPLIT>' )
throw 'Template:Check:surround template: <SPLIT> missing'
endif
"
if match( s_text, '<SPLIT>\s*\n' ) >= 0
let part = split ( s_text, '\s*<SPLIT>\s*\n', 1 )
else
let part = split ( s_text, '<SPLIT>', 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] == '<CURSOR>'
let remove_cursor = 0
let i += 1
elseif a:[i] == '<SPLIT>'
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, '<CURSOR>', '', 'g' )
endif
if remove_split
let text = s:LiteralReplacement( text, '<SPLIT>', '', 'g' )
endif
if ! use_surround || use_split
let text = s:LiteralReplacement( text, '<CONTENT>', '', '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'.'<SPLIT>', '', '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, '<SPLIT>' ) >= 0
if match( text, '<SPLIT>\s*\n' ) >= 0
let part = split ( text, '\s*<SPLIT>\s*\n', 1 )
else
let part = split ( text, '<SPLIT>', 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><marked area><part1>
" part0 and part1 can consist of several lines
"
" 'visual' and placement 'below':
" <part0>
" <marked area>
" <part1>
" 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[0]
:'>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>\|{CURSOR}', 'c', a:pos2 )
if mtch != 0 " CURSOR found:
let line = getline(mtch)
if line =~ '<CURSOR>$\|{CURSOR}$'
call setline( mtch, substitute( line, '<CURSOR>\|{CURSOR}', '', '' ) )
if a:flag_visual_mode == 1 && getline(".") =~ '^\s*$'
normal J
else
startinsert!
endif
else
call setline( mtch, substitute( line, '<CURSOR>\|{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, '<CURSOR>', '<SPLIT>' )
else
let [ text, placement, indentation ] = call ( 's:PrepareTemplate', [ a:t_name, '<CURSOR>', '<SPLIT>' ] + 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 = '<buffer> <silent>'
let leader = '<LocalLeader>'
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 '<Esc>'
if mode == 'n' | let esc = ''
else | let esc = '<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.')<CR>'.sep
endfor
"
endfor
"
" jump map
if do_jump_map
let jump_key = '<C-j>' " 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<C-R>=mmtemplates#core#JumpToTag('.jump_regex.')<CR>'.sep
let cmd .= 'inoremap '.options.' '.jump_key.' <C-R>=mmtemplates#core#JumpToTag('.jump_regex.')<CR>'.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)<CR>',
\ 'rr' : ':call mmtemplates#core#ReadTemplates('.a:library.',"reload","all")<CR>',
\ 'rs' : ':call mmtemplates#core#ChooseStyle('.a:library.',"!pick")<CR>',
\ }
"\ 'rt' : ':call mmtemplates#core#InsertTemplate('.a:library.',"!pick")<CR>',
"
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.' <Esc>'.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, '<TAB>' )
exe 'amenu '.a:root_menu.escape( assemble.clean, ' ' ).' :echo "This is a menu header."<CR>'
else
exe 'amenu '.a:root_menu.escape( assemble.clean, ' ' ).'<TAB>'.escape( a:global_name, ' .' ).' :echo "This is a menu header."<CR>'
endif
exe 'amenu '.a:root_menu.escape( assemble, ' ' ).'-Sep00- <Nop>'
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 = '<TAB>\\'.mp
end
"
if entry == 1
" <Esc><Esc> prevents problems in insert mode
exe 'amenu '.a:root_menu.compl_entry.map_entry.' <Esc><Esc>:call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'")<CR>'
if visual == 1
exe 'vmenu '.a:root_menu.compl_entry.map_entry.' <Esc><Esc>:call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'","v")<CR>'
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.' <Esc><Esc>:call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'","pick",'.string(item).')<CR>'
if visual == 1
exe 'vmenu '.a:root_menu.item_entry.' <Esc><Esc>:call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'","v","pick",'.string(item).')<CR>'
endif
endfor
"
" exe 'amenu '.a:root_menu.compl_entry.'.-\ choose\ -'.map_entry.' <Esc><Esc>:call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'")<CR>'
" if visual == 1
" exe 'vmenu '.a:root_menu.compl_entry.'.-\ choose\ -'.map_entry.' <Esc><Esc>:call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"'.t_name.'","v")<CR>'
" 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 <silent> '.a:root_menu.specials_menu.'.&edit\ templates'
\ .' :call mmtemplates#core#EditTemplateFiles('.a:t_lib_name.',-1)<CR>'
exe 'amenu <silent> '.a:root_menu.specials_menu.'.&reread\ templates'
\ .' :call mmtemplates#core#ReadTemplates('.a:t_lib_name.',"reload","all")<CR>'
" exe 'amenu '.a:root_menu.specials_menu.'.choose\ &template'
" \ .' :call mmtemplates#core#InsertTemplate('.a:t_lib_name.',"!pick")<CR>'
endif
"
" create a menu for all the styles
for s in a:t_lib.styles
exe 'amenu <silent> '.a:root_menu.specials_menu.'.choose\ &style.'.escape( s, ' ' )
\ .' :call mmtemplates#core#ChooseStyle('.a:t_lib_name.','.string(s).')<CR>'
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