cakemate / branches / master / vendors / vim / plugins / cakephp.vim

history
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" cakephp.vim
" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Authors:      Andy Dawson <andydawson76 AT yahoo DOT co DOT uk>
" Version:      2
" Licence:      http://www.opensource.org/licenses/mit-license.php
"               The MIT License
" URL:          http://thechaw.com/cakemate
"
"-----------------------------------------------------------------------------
"
" Section: Documentation
"
" Command Documentation
" By Default, the following commands are defined:
" 	:M - Open the model file
" 	:V - Open the view file
" 	:C - Open the controller file
" 	:D - Add doc blocks to the current buffer
" 	:F - Clean the current buffer (Apply code standards)
" 	:R - Regenerate all docs for the current file
" 	:L - Show the log

" Mapping Documentation
" By Default, the following mappings are defined (partial):
" 	<Ctrl+P> will document the current line
" 	<F9> will document the current buffer
" 	<F12> will document all open buffers
"

" Section: Plugin header
"
" loaded_cakephp is set to 1 when initialization begins, and 2 when it
" completes.

if (exists('debugger_running'))
	finish
endif
if exists('loaded_cakephp')
	call s:SetupBuffer()
	norm! zx
	finish
endif
let loaded_cakephp=1

" Section: Event group setup

augroup CakeCommand
augroup END

" Section: Script variables

let s:Projects = {}
let b:Root = ''
let s:Cake = ''
let s:Root = ''
let s:ConsoleLog = []
let s:CommandLog = []
let s:ConfigOpenCmd = 'tabe' "'sp'
let s:ConfigCleanOnWrite = 1
let s:RunAutotest = 0
let s:LogActivity = '~/activity/'

" Section: Utility functions
" These functions are not/should not be directly accessible

" Function: Console()
" Call the cake console editor shell with the passed args pass the results through s:Return
function s:Console(cmd, ...)
	let i = 1
	let l:command = ["cake editor", a:cmd]
	while i <= a:0
		if a:{i} != ""
			call add(l:command, shellescape(a:{i}))
		endif
		let i = i + 1
	endwhile
	let l:return = call('s:DirectConsole', l:command)
	return call('s:Return', l:return)
endfunction

" Function: DirectConsole()
" Directly make a system call, returning the response as a list of [line1, line2, ...]
" write the call to the log too
function s:DirectConsole(...)
	let cmd = join(a:000, ' ') . ' -q 1 -app ' . shellescape(b:Root)
	let s:ConsoleLog = s:ConsoleLog + [cmd]
	return split(substitute(system(cmd), s:junk, '', 'g'), '\n')
endfunction

" Function: LogActivity()
" Log doing something
function s:LogActivity(action)
	if s:LogActivity == ''
		return
	endif
	let l:cmd = '!echo "' . strftime('%F %T') 
	let l:cmd = l:cmd . ' ' . a:action
	let l:cmd = l:cmd . ' ' . substitute(b:Root, '.*/', '', 'g')
	let l:cmd = l:cmd . ' ' . substitute(expand("%"), b:Root, '', 'g')
	let l:cmd = l:cmd . ' ' . expand("%:p") . '"'
	:silent exe l:cmd . ' >> ' . s:LogActivity . strftime('%y%m%d') . '.log'
endfunction

" Function: Return()
" Parse the response and if necessary prompt the user to make a selection
" If only a single result is found, that is the selection.
" Expects EITHER to be called with
" 	(string, string, string, ...)
" OR
" 	('Display<tab>path', 'Display<tab>path', 'Display<tab>path', ...)
function s:Return(first, ...)
	if a:0 == 0
		if a:first =~ '\t'
			let [l:key, l:value] = split(a:first, "\t")
			return l:value
		else
			return a:first
		endif
	endif
	let l:list = ["Which one: ?"]
	let l:options = [a:first] + a:000
	let l:returns = ['Spacer']
	let i = 1
	for item in l:options
		if item =~ '.*\t.*'
			let [l:key, l:value; rest] = split(item, "\t")
			let l:value = substitute (l:value, b:Root . '/', '', "g")
			let item = l:key . ' (' . l:value . ')'
			call add(l:returns, l:value)
		else
			call add(l:returns, l:item)
		endif
		call add(l:list, i . ' ' . item)
		let i = i + 1
	endfor
	let l:selection = inputlist(l:list)
	if l:selection > 0
		return l:returns[l:selection]
	endif
	return ''
endfunction

" Function: Open()
" Open the file, or goto an already-open buffer
function s:Open(file)
	if strlen(a:file) > 0
		if bufnr(a:file) > 0
			let cmd = 'b ' . bufnr(a:file)
		else
			let cmd = s:ConfigOpenCmd . ' ' . a:file
		endif
		let s:CommandLog = s:CommandLog + [cmd]
		exe cmd
	endif
endfunction

" Function: DocPaste(start)
" Disable any other enhancements before generating doc blocks, and reset the
" paste function thereafter.
" Prevents (for example) ever increasing comment indentation
function s:DocPaste(start)
	if a:start == 1
		let &g:paste = 1
	else
		let &g:paste = 0
	endif
endfunc

" Function: BufferWritePre()
" EOL markers are not desired, change to binary mode before saving so one isn't added
function s:BufferWritePre()
	"remove trailing whitespace
	:silent %s/\s\s*$//e
	"remove trailing windoze
	:silent %s/
$//e

	" set to binary and remove eol
	let b:save_bin = &bin
	let &l:bin = 1
	let &l:eol = 0
endfunction
" Function: BufferWritePost()
" Back to not-binary-mode

function s:BufferWritePost()
	let &l:bin = b:save_bin
	if (&ft == 'php')
		if (s:RunAutotest && !has_key(s:Projects, b:Root))
			let cmd = '!cd ' . shellescape(b:Root) . ' && nice -n20 cake autopilot -q -noclear >auto.log 2>auto.err &'
			let s:ConsoleLog = s:ConsoleLog + [cmd]
			:silent exe cmd
			let s:Projects[b:Root] = system('$!')
		endif
		" :call DocTags(expand("%:p")) "disabled it's too slow
	endif
	call s:LogActivity('write')
endfunction

" Function: SetupBuffer()
" For each file that is opened, setup the buffer auto commands
function s:SetupBuffer()
	if (exists('b:CakeBufferSetup') && b:CakeBufferSetup)
		return
	endif
	if (expand("%:t") == 'acl.ini.php')
		return
	endif
	set makeprg=php\ -l\ %
	set errorformat=%m\ in\ %f\ on\ line\ %l
	compiler php

	if !exists("b:Root") || len(b:Root) == 0
		if !exists("s:junk") || len(s:junk) == 0
			let cmd = 'cake editor junk -q'
			let s:ConsoleLog = s:ConsoleLog + [cmd]
			let s:junk = system(cmd)

			let cmd = 'cake editor base CAKE -q'
			let s:ConsoleLog = s:ConsoleLog + [cmd]
			let l:Cake = split(substitute(system(cmd), s:junk, '', 'g'), '\n')
			silent! let [s:Cake; rest] = l:Cake
		endif

		let cmd = 'cake editor base -q 1 ' . shellescape(expand("%:p"))
		let s:ConsoleLog = s:ConsoleLog + [cmd]
		let l:Root = split(substitute(system(cmd), s:junk, '', 'g'), '\n')
		silent! let [b:Root; rest] = l:Root
		silent! :exe "set tags=" . b:Root . "/tags," . s:Cake . "/tags"
	endif
	"autocmd BufWinEnter * :let w:m1=matchadd('Search', '\%<101v.\%>97v', -1)
	"autocmd BufWinEnter * :let w:m2=matchadd('ErrorMsg', '\%>100v.\+', -1)
	autocmd BufNewFile *.{php,ctp,css,js} call s:SetupBuffer()
	autocmd BufReadPost *.{php,ctp,css,js} call s:SetupBuffer()
	autocmd BufWritePre *.{php,ctp,css,js} call s:BufferWritePre()
	autocmd BufWritePost *.{php,ctp,css,js} call s:BufferWritePost()
	call s:SetupCommands()
	call s:SetupMappings()
	call s:SetupMenu()
	norm! zx
	let b:CakeBufferSetup=1
	call s:LogActivity('open')
endfunction

" Section: Public functions

" Function: Cake()
function Cake(...)
	let i = 1
	let l:command = ["cake"]
	while i <= a:0
		if a:{i} != ""
			call add(l:command, shellescape(a:{i}))
		endif
		let i = i + 1
	endwhile
	let l:return = call('s:DirectConsole', l:command)
	for item in l:return
		echom item
	endfor
endfunction

" Function: ExplodePhp()
" For code that's not wrapped correctly, explode it (put each new array element on a new line)
" and then indent it. It's a lot easier to read/cleanup code that's been too-exploded
" than code that isn't wrapped/formatted/indented
function ExplodePhp() range
	let l:line = line('.')
	:delmarks yz
	:silent exe 'norm ' . a:firstline . 'G'
	:ky
	:silent exe 'norm ' . a:lastline . 'G'
	:kz

	"explode on any comma, or ()
	:silent 'y,'zs/\([,()]\)\( \?[^, ]\)/\1 \r\2/ge
	"for any closing bracket that isn't on its own line explode that too
	:silent 'y,'zs/\([\S]\))\(,?\)$/\1\r)\2/ge
	"For a closing bracket, with a  semicolon on the following line - clean up
	:silent 'y,'zs/)\_s\+;/\r\t);/e
	
	" indent	
	exe "norm ='y'z"
	:silent exe 'norm ' . l:line . 'G'
endfunction

" Function: FormatPhp()
" (Attempt to) autoformat php code according to various conventions
function FormatPhp() range
	let l:line = line('.')
	:delmarks yz
	:silent exe 'norm ' . a:firstline . 'G'
	:ky
	:silent exe 'norm ' . a:lastline . 'G'
	:kz

	""replace any double spaces with a single space
	":silent '<,'>s/ \{2,}/ /ge
	"remove trailing whitespace
	:silent 'y,'zs/\s\s*$//e
	"remove trailing windoze
	:silent 'y,'zs/
$//e

	" Lower case booleans please
	:silent 'y,'zs/TRUE/true/ge
	:silent 'y,'zs/FALSE/false/ge

	" auto correct lines which start with a { so the { is on the end of the
	" previous line
	:silent 'y,'zs/\n\s*{/ {/e
	" auto correct lines which start with 'else' prepending the else to the
	" previous line
	:silent 'y,'zs/}\s*\n\s*else/} else/e
	:silent 'y,'zs/\S\s*\zs} else/\r\t} else/e
	:silent 'y,'zs/\n\s*else/} else/e
	" add parenthesese to a trailing else or if
	:silent 'y,'zs/\(else|if\)\s*\n/\1 {\n/e
	" add parenthesese to what looks like an else with no parenthesese
	:silent 'y,'zs/^\(\s*\)\zselse\s*\([^{]\)/else {\n\t\1\2/e
	" auto correct lines with a semicolon on their own
	:silent 'y,'zs/)\_s\+;/\r\t);/e

	" correct whitespace around comas (parameters)
	:silent 'y,'zs/,\(\S\)\@=/, /ge
	" correct whitespace around assignments/comparisons x=y becomes x = y
	:silent 'y,'zs/\([^\.!=<>&+\-? ]\)\([\.!<]\?==\?[=>&]\?\|&&\|||\)\([^=<>& ]\)/\1 \2 \3/ge
	" correct whitespace around assignments/comparisons x= y becomes x = y
	:silent 'y,'zs/\([^\.!=<>&+\-? ]\)\([\.!<]\?==\?[=>&]\?\)/\1 \2/ge
	" correct whitespace around assignments/comparisons x =y becomes x = y
	:silent 'y,'zs/\([\.!<]\?==\?[=>&]\?\)\([^=<>& ]\)/\1 \2/ge

	" put a blank line before comment blocks
	:silent 'y,'zs/\([\{\}\/;]\)\n\zs\/\*/\r\/\*/e
	" correct doc block headers
	:silent 'y,'zs/^\s*\/\*\*/\/\*\*/e
	" correct doc block contents/tail that is indented ( * or  */)
	:silent 'y,'zs/^\s*\*/ \*/e

	" auto correct shorttags
	:silent 'y,'zs/<?$/<?php/e
	:silent 'y,'zs/<? \$/<?php $/e
	:silent 'y,'zs/<?\(=\| echo\)/<?php echo/e

	" auto correct deprecated methods
	:silent 'y,'zs/\W\zsam(/array_merge(/e

	" spaces to tabs - there'll be a better way of doing this
	:silent 'y,'zs/^ \{40,}/\t\t\t\t\t\t\t\t\t\t/e
	:silent 'y,'zs/^ \{36,}/\t\t\t\t\t\t\t\t\t/e
	:silent 'y,'zs/^ \{32,}/\t\t\t\t\t\t\t\t/e
	:silent 'y,'zs/^ \{28,}/\t\t\t\t\t\t\t/e
	:silent 'y,'zs/^ \{24,}/\t\t\t\t\t\t/e
	:silent 'y,'zs/^ \{20,}/\t\t\t\t\t/e
	:silent 'y,'zs/^ \{16,}/\t\t\t\t/e
	:silent 'y,'zs/^ \{12,}/\t\t\t/e
	:silent 'y,'zs/^ \{8,}/\t\t/e
	:silent 'y,'zs/^ \{2,}/\t/e

	'y,'z:call s:FormatComments()
	
	"" replace any double spaces with a single space
	":silent 'y,'zs/ \{2,}/ /ge
	"remove trailing whitespace
	:silent 'y,'zs/\s\s*$//e
	" prune empty lines
	:silent 'y,'zv/./,/./-j

	" indent	
	exe "norm ='y'z"

	:silent exe 'norm ' . l:line . 'G'
endfunction

" Function: ImplodePhp()
" combine lines of php code
" and then indent it. It's a lot easier to read/cleanup code that's been too-exploded
" than code that isn't wrapped/formatted/indented
function ImplodePhp() range
	let l:line = line('.')
	:delmarks yz
	:silent exe 'norm ' . a:firstline . 'G'
	:ky
	:silent exe 'norm ' . a:lastline . 'G'
	:kz

	"explode on any comma, or ()
	:silent 'y,'zs/\([,()]\)\( \?[^, ]\)/\1 \r\2/ge
	"for any closing bracket that isn't on its own line explode that too
	:silent 'y,'zs/\([\S]\))\(,?\)$/\1\r)\2/ge
	"For a closing bracket, with a  semicolon on the following line - clean up
	:silent 'y,'zs/)\_s\+;/\r\t);/e

	" indent	
	exe "norm ='y'z"

	:silent exe 'norm ' . l:line . 'G'
endfunction


" Function: Controller()
" Open the controller file that is associated with whatever is currently being edited
" If the function is known - search for and jump to it
function Controller()
	call s:Open(s:Console('path controller', expand("%:p")))
endfunction

" Function: Model()
" Open the model file that is associated with whatever is currently being edited.
function Model()
	call s:Open(s:Console('path model', expand("%:p")))
endfunction

" Function: View()
" Open the view file that is associated with whatever is currently being edited.
function View()
	call s:Open(s:Console('path view', expand("%:p")))
endfunction

" Function: DocSingle()
" Document a single line of code (does not check if doc block already exists)
" Enters paste mode before starting to ensure indentation is correct
" Skips doing anything if it doesn't look like a class, property or function declaration
function DocSingle(...)
	let l:default = ' -default '
	if a:0 == 1 && a:1 == 1
		let l:default = ''
	endif

	if (l:default == '' && line('.') > 1 && getline('.') !~ '^\s*\(final\|abstract\|static\|class\|function\|var\|public\|protected\|private\)')
		return
	endif
	let l:line = substitute(getline("."), '^\s', '', 'g')
	" Console balks on empty parameters
	if (l:line == '')
		let l:line = ' '
	endif
	call s:DocPaste(1)
	let l:doc = s:DirectConsole('cake editor doc ' . expand("%:p") . ' ' . shellescape(l:line) . ' ' . line(".") . l:default)
	if len(l:doc) == 0
		return
	endif
	call s:DocPaste(1)
	let l:first = 1
	for item in l:doc
		if l:first == 1
			if line('.') == 1
				let cmd = "norm! 0R" . item
			else
				let cmd = "norm! O" . item
			endif
		else
			let cmd = "norm! o" . item
		endif
		let s:CommandLog = s:CommandLog + [cmd]
		exe cmd
		let l:first = 0
	endfor
	call s:DocPaste(0)
endfunc

" Function: DocRange()
" Documents a whole range of code lines (does not add defualt doc block to unknown types of lines).
" For each line in the requested range, if it's not a doc block line already and is not preceeded by
" a doc blcok call DocSingle()
" If running for the whole file, check by starting from line 2 (to exclude the php tag line) if a file
" doc block (by looking for an @filesource tag and a class doc block (any line which includes class)
" exist before the first none-comment line. Generate missing doc blocks as appropriate
function DocRange() range
	call s:DocPaste(1)
	norm zR
	let l:line = a:firstline
	if l:line == 1
		let l:line = 2
	endif
	let l:endLine = a:lastline
	while l:line < (l:endLine)
		if (getline(l:line) !~ '^\s*$' && getline(l:line) !~ '^\s*/\?\*' && getline(l:line - 1) !~ '^\s*/\?\*' && getline(l:line - 2) !~ '^\s*/\?\*')
			exe "norm! " . l:line . "G$"
			call DocSingle(1)
			if line('.') != l:line
				" A doc block has been inserted
				" Adjust the line to run to (maintain the same effective endline)
				let l:diff = line('.') - l:line + 1
				let l:endLine += l:diff
			endif
			let l:line = line('.') + 1
		elseif (l:line > 2 && getline(l:line) =~ '^/\*' && getline(l:line - 1) !~ '^\s*$')
			exe "norm! " . l:line . "G$"
			exe "norm! O"
			let l:endLine += 1
			let l:line = line('.') + 1
		else
			let l:line = l:line + 1
		endif
	endwhile
	if a:firstline == 1
		let l:line = 2
		let l:headDocFound = 0
		let l:classDocFound = 0
		while l:line <= l:endLine
			if getline(l:line) !~ '^\s*$' && getline(l:line) !~ '^\s*/\?\*'
				break
			endif
			if getline(l:line) =~ '^.*@filesource'
				let l:headDocFound = 1
			elseif getline(l:line) =~ 'class'
				let l:classDocFound = 1
			endif
			let l:line = l:line + 1
		endwhile
		if l:headDocFound == 0
			norm gg
			call DocSingle(1)
		elseif l:classDocFound == 0
			exe "norm! " . l:line . "G$"
			call DocSingle(1)
		endif
	endif
	call s:DocPaste(0)
	" back to the first line
	exe "norm! " . a:firstline . 'G'
endfunc

" Function: DocDebug()
" Convenience function for dumping vars and seeing the console log
function DocDebug(...)
	if a:0 == 1 && type(a:1) == type([])
		return call('DocDebug', a:1)
	endif
	echom 'file ' . expand("%:p")
	echom 's:Projects '
	for [key, value] in items(s:Projects)
		echo key . ': ' . value
	endfor
	echom 'b:Root ' . b:Root
	echom 's:Cake ' . s:Cake
	echom 's:junk ' . s:junk
	echom 'Console Log:'
	for item in  s:ConsoleLog
		echom "\t" . item
	endfor
	echom 'Command Log:'
	for item in  s:CommandLog
		echom "\t" . item
	endfor
	if a:0
		echom ''
	endif
	for item in  a:000
		echom item
	endfor
endfunction

" Function: DocTags()
" Include the core (the 1), update the tags file
function DocTags(...)
	if a:0 == 0
		"call s:DirectConsole('cake editor tags', '"*"')
		let cmd = '!cake editor tags "*" -q 1 -app ' . shellescape(b:Root)
		let s:ConsoleLog = s:ConsoleLog + [cmd]
		:exe cmd
	else
		"call s:DirectConsole('cake editor tags', a:1)
		let cmd = '!cake editor tags ' . a:1 . ' -q 1 -app ' . shellescape(b:Root) . ' > /dev/null'
		let s:ConsoleLog = s:ConsoleLog + [cmd]
		:silent exe cmd
	endif
endfunction

" Function: SetupCommands()
" Setup default commands for each of the public methods
function s:SetupCommands()
	if !exists(":M")
		command -bar -narg=0 M call Model()
	endif
	if !exists(":V")
		command -bar -narg=0 V call View()
	endif
	if !exists(":C")
		command -bar -narg=0 C call Controller()
	endif
	if !exists(":D")
		command -nargs=0 D %call DocRange()
	endif
	if !exists(":R")
		command -nargs=0 R %call FormatPhp()
	endif
	if !exists(":L")
		command -bar -narg=0 L call DocDebug()
	endif
	if !exists(":Cake")
		command -bar -narg=0 Cake call Cake()
	endif
endfunction

" Function: SetupMappings()
" Setup mappings for each of the public methods
function s:SetupMappings()
	if !hasmapto('<Plug>Model')
		map <buffer> <unique> <Leader>m Model
	endif
	if !hasmapto('<Plug>View')
		map <buffer> <unique> <Leader>v View
	endif
	if !hasmapto('<Plug>Controller')
		map <buffer> <unique> <Leader>c Controller
	endif
	if !hasmapto('<Plug>DocSingle')
		map <buffer> <unique> <Leader>s DocSingle
	endif
	"if !hasmapto('<Plug>DocRange')
	"	map <buffer> <unique> <Leader>r <Plug>DocRange
	"	map <buffer> <unique> <Leader>a %<Plug>DocRange
	"endif
	nnoremap <buffer> <C-F> :%call FormatPhp()<CR>
	vnoremap <buffer> <C-F> :call FormatPhp()<CR>
	nnoremap <buffer> <C-X> :%call ExplodePhp()<CR>
	vnoremap <buffer> <C-X> :call ExplodePhp()<CR>
	inoremap <buffer> <C-P> <Esc>:call DocSingle()<CR>i
	nnoremap <buffer> <C-P> :call DocSingle()<CR>
	vnoremap <buffer> <C-P> :call DocRange()<CR>
	nnoremap <buffer> <F4> :call DocTags()<CR>
	nnoremap <buffer> <F9> :%call DocRange()<CR>
	nnoremap <buffer> <F12> :bufdo! :%call DocRange()<CR>
endfunction

" function SetupMenu()
" Setup standard menu items
" TODO Needs updating
function s:SetupMenu()
	noremenu <script> &Plugin.&CakePHP.Switch\ To\ &Model<Tab>:M :call Model()<CR>
	noremenu <script> &Plugin.&CakePHP.Switch\ To\ &View<Tab>:V :call View()<CR>
	noremenu <script> &Plugin.&CakePHP.Switch\ To\ &Controller<Tab>:C :call Controller()<CR>

	inoremenu <script> &Plugin.&CakePHP.Add\ &Doc\ Block<Tab>:D <Esc>:call DocSingle()<CR>i
	nnoremenu <script> &Plugin.&CakePHP.Add\ &Doc\ Block<Tab>:D :call DocSingle()<CR>
	nnoremenu <script> &Plugin.&CakePHP.Check\ Doc\ &Blocks<Tab>F9 :%call DocRange()<CR>
	vnoremenu <script> &Plugin.&CakePHP.Doc\ &Range<Tab><C-P> :call DocRange()<CR>
	nnoremenu <script> &Plugin.&CakePHP.Doc\ All\ &Tabs<Tab><F12> :bufdo! :%call DocRange()<CR>
endfunction

" Section: Plugin completion
call s:SetupBuffer()
let loaded_cakephp=2

" Section: Limbo
" Functions/code waiting to be either deleted, fixed or rewrittendif

" Function: ReprocessComments()
" Delete all existing comments, and rerun the DocRange function to regenerate them.
function s:ReprocessComments()
	:silent g@^\(/\| \)\*@de
	%:call DocRange()
endfunction

" Function: FormatComments()
" Correct whitespace around doc blocks
function s:FormatComments() range
	let l:line = line('.')
	:delmarks yz
	:silent exe 'norm ' . a:firstline . 'G'
	:ky
	:silent exe 'norm ' . a:lastline . 'G'
	:kz

  	   'y,'zs/\*\s*@copyright\s*/\* @copyright     /ge
	        'y,'zs/\*\s*@link\s*/\* @link          /ge
	     'y,'zs/\*\s*@package\s*/\* @package       /ge
	  'y,'zs/\*\s*@subpackage\s*/\* @subpackage    /ge
	       'y,'zs/\*\s*@since\s*/\* @since         /ge
	     'y,'zs/\*\s*@version\s*/\* @version       /ge
	  'y,'zs/\*\s*@modifiedBy\s*/\* @modifiedby    /ge
	  'y,'zs/\*\s*@modifiedby\s*/\* @modifiedby    /ge
	'y,'zs/\*\s*@lastModified\s*/\* @lastmodified  /ge
	'y,'zs/\*\s*@lastmodified\s*/\* @lastmodified  /ge
	     'y,'zs/\*\s*@license\s*/\* @license       /ge
 	        'y,'zs/\*\s*@uses\s*/\* @uses          /ge
 	      'y,'zs/\*\s*@author\s*/\* @author        /ge
	:silent exe 'norm ' . l:line . 'G'
endfunction

" Function: DeleteBlankLines()
" TODO doesn't work as a function call atm.
" Exactly what it says on the tin
function s:DeleteBlankLines()
	" :g/^\s\*$/d
	exe "norm :g/^\s\*$/d<CR>"
endfunction