3 " A comfortable, interactive templating system for VIM.
5 " Copyright (c) 2008 Jannis Pohlmann <jannis@xfce.org>.
7 " This program is free software; you can redistribute it and/or modify
8 " it under the terms of the GNU General Public License as published by
9 " the Free Software Foundation; either version 2 of the License, or (at
10 " your option) any later version.
12 " This program is distributed in the hope that it will be useful, but
13 " WITHOUT ANY WARRANTY; without even the implied warranty of
14 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 " General Public License for more details.
17 " You should have received a copy of the GNU General Public License
18 " along with this program; if not, write to the Free Software
19 " Foundation, Inc., 59 Temple Place, Suite 330, Boston,
27 " Default template dir used if g:jpTemplateDir is not set
28 let s:defaultTemplateDir = $HOME . '/.vim/jptemplate'
34 function! jp:GetTemplateInfo ()
36 " Prepare info dictionary
39 " Get part of the line before the cursor
40 let part = getline ('.')[0 : getpos ('.')[2]-1]
42 " Get start and end position of the template name
43 let info['start'] = match (part, '\(\w*\)$')
44 let info['end'] = matchend (part, '\(\w*\)$')
47 let info['name'] = part[info['start'] : info['end']]
49 " Throw exception if no template name could be found
51 throw 'No template name found at cursor'
54 " Calculate line indentation
55 let info['indent'] = max ([0, matchend (part, '^\s\+')])
57 " Return template name information
63 function! jp:ReadTemplate (name, filename)
65 " Try to read the template file and throw exception if that fails
67 return readfile (a:filename)
69 throw 'Template "' . a:name . '" could not be found.'
75 function! jp:SetCursorPosition (lines)
77 for cnt in range (0, a:lines)
78 " Search for ${cursor} in the current line
79 let str = getline (line ('.') + cnt)
80 let start = match (str, '${cursor}')
81 let end = matchend (str, '${cursor}')
82 let before = strpart (str, 0, start)
83 let after = strpart (str, end)
86 " Remove ${cursor} and move the cursor to the desired position
87 call setline (line ('.') + cnt, before . after)
88 call cursor (line ('.') + cnt, start+1)
98 function! jp:ProcessTemplate (info, template)
103 let str = join (a:template, ' ')
105 " Detect all variable names of the template
107 " Find next variable start and end position
108 let start = match (str, '${\(\w\|\s\)\+}', matchpos)
109 let end = matchend (str, '${\(\w\|\s\)\+}', matchpos)
112 " Stop search if there is no variable left
115 if str[start+2 : end-2] == 'cursor'
118 " Add variable name (without ${}) to the dictionary
119 let variables[str[start+2 : end-2]] = 0
121 " Start next search at the end position of this variable
127 " Ask the user to enter values for all variables
128 for name in keys (variables)
129 let variables[name] = input (name . ": ")
132 " Expand all variables
134 while index < len (a:template)
135 for var in items (variables)
136 let expr = '${' . var[0] . '}'
137 let a:template[index] = substitute (a:template[index], expr, var[1], 'g')
139 let index = index + 1
142 " Backup characters before and after the template name
143 let before = strpart (getline ('.'), 0, a:info['start'])
144 let after = strpart (getline ('.'), a:info['end'])
146 " Generate indentation
147 let indent = repeat (' ', a:info['indent'])
149 " Insert template into the code line by line
150 for cnt in range (0, len (a:template)-1)
152 call setline (line ('.'), before . a:template[cnt])
154 call append (line ('.') + cnt - 1, indent . a:template[cnt])
156 if cnt == len (a:template)-1
157 call setline (line ('.') + cnt, getline (line ('.') + cnt) . after)
159 " Move cursor to the end of the inserted template. ${cursor} may
161 call cursor(line ('.'), len (getline (line ('.') + cnt)))
165 " Set the cursor position
166 call jp:SetCursorPosition (cnt)
168 " Return to insert mode
174 function! jp:InsertTemplate ()
176 " Determine the template directory
177 let templateDir = exists ('g:jpTemplateDir') ? g:jpTemplateDir : s:defaultTemplateDir
179 " Determine the filetype subdirectory
180 let ftDir = &ft == '' ? 'general' : &ft
183 " Detect bounds of the template name as well as the name itself
184 let info = jp:GetTemplateInfo ()
186 " Generate the full template filename
187 let templateFile = templateDir .'/'. ftDir . '/' . info['name']
189 " Load the template file
190 let template = jp:ReadTemplate (info['name'], templateFile)
192 " Do the hard work: Process the template
193 call jp:ProcessTemplate (info, template)
195 " Inform the user about errors
196 echo s:debug ? v:exception . " (in " . v:throwpoint . ")" : v:exception
202 " Map <Ctrl>+<Tab> to the template system
203 imap <Esc><Space> <Esc>:call jp:InsertTemplate()<CR>