Skip to content

Instantly share code, notes, and snippets.

@hail2u

hail2u/lang-vim.js

Created Jan 5, 2012
Embed
What would you like to do?
Vim script language handler for google code prettify
/**
* @preserve Copyright (C) 2012 Kyo Nagashima
*
* LICENSE: http://hail2u.mit-license.org/2012
*/
/**
* @fileoverview
* Registers a language handler for Vim script.
*
*
* To use, include prettify.js and this file in your HTML page.
* Then put your code in an HTML tag like
* <pre class="prettyprint lang-vim"></pre>
*
*
* @author kyo@haiil2u.net
*/
PR['registerLangHandler'](
PR['createSimpleLexer'](
[
// Whitespace
[PR['PR_PLAIN'], /^[\t\n\r \xA0\u2028\u2029]+/, null, '\t\n\r \xA0\u2028\u2029']
],
[
// Double quoted string
[PR['PR_STRING'], /^\"[^\"\r\n]*?\"/],
// Single quoted string
[PR['PR_STRING'], /^\'[^\'\r\n]*?\'/],
// Line comment
[PR['PR_COMMENT'], /^[\"\u2018\u2019][^\r\n\u2028\u2029]*/],
// Keywords
[PR['PR_KEYWORD'], /^(?:function|endfunction|delfunction|return|call|let|unlet|lockvar|unlockvar|if|endif|else|elseif|while|endwhile|for|in|endfor|continue|break|try|endtry|catch|finally|throw|echo|ehon|echohl|echomsg|echoerr|execute|set|autocmd|augroup|[nvxsoilc]?(?:nore)?map|command)\b/i],
// Literal number
[PR['PR_LITERAL'], /^(?:\d+)/i],
// Identifier
[PR['PR_PLAIN'], /^(?:(?:[a-z]|_\w)[\w\:]*)/i],
// Punctuation
[PR['PR_PUNCTUATION'], /^[^\s\w\'\"]+/]
]
),
['vim']
);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test page for lang-vim.js</title>
<link rel="stylesheet" href="http://hail2u.github.com/natural.css">
<link rel="stylesheet" href="http://google-code-prettify.googlecode.com/svn/trunk/styles/desert.css">
<script src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script>
<script src="lang-vim.js"></script>
</head>
<body onload="prettyPrint()">
<header>
<h1>Test page for lang-vim.js</h1>
</header>
<p>Download: <a href="https://gist.github.com/1566304">lang-vim.js</a></p>
<pre class="prettyprint lang-vim">" jptemplate.vim:
"
" A simple yet powerful interactive templating system for VIM.
"
" Version 1.5 (released 2008-07-08).
"
" Copyright (c) 2008 Jannis Pohlmann &lt;jannis@xfce.org&gt;.
"
" 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; either version 2 of the License, or (at
" your option) any later version.
"
" 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 for more details.
"
" You should have received a copy of the GNU General Public License
" along with this program; if not, write to the Free Software
" Foundation, Inc., 59 Temple Place, Suite 330, Boston,
" MA 02111-1307 USA
" Reserved variable names
let s:reservedVariables = ['date','shell','interactive_shell']
" Variable value history
let s:rememberedValues = {}
" Characters to be escaped before the substitute() call
let s:escapeCharacters = '&amp;~\'
function! jp:Initialize()
" List for default configuration
let defaults = []
" Default configuration values
call add(defaults, ['g:jpTemplateDir', $HOME . '/.vim/jptemplate'])
call add(defaults, ['g:jpTemplateKey', '&lt;C-Tab&gt;'])
call add(defaults, ['g:jpTemplateDateFormat', '%Y-%m-%d'])
call add(defaults, ['g:jpTemplateDefaults', {}])
call add(defaults, ['g:jpTemplateVerbose', 0])
" Set default configuration for non-existent variables
for var in filter(defaults, '!exists(v:val[0])')
exec 'let ' . var[0] . ' = ' . string(var[1])
endfor
endfunction
function! jp:GetTemplateInfo()
" Prepare info dictionary
let info = {}
" Get part of the line before the cursor
let part = getline('.')[0 : getpos('.')[2]-1]
" Get start and end position of the template name
let info['start'] = match(part, '\(\w*\)$')
let info['end'] = matchend(part, '\(\w*\)$')
" Get template name
let info['name'] = part[info['start'] : info['end']]
" Throw exception if no template name could be found
if empty(info['name'])
throw 'No template name found at cursor'
endif
" Determine directory to load the template from (skip empty directories;
" each directory may override the ones before)
for dir in filter([ 'general', &amp;ft ], '!empty(v:val)')
let filename = g:jpTemplateDir .'/'. dir .'/'. info['name']
if filereadable(filename)
let info['filename'] = filename
endif
endfor
" Throw exception if the template file does not exist in any of these
" directories (or is not readable)
if !has_key(info, 'filename')
throw 'Template file not found'
endif
" Determine indentation
let info['indent'] = matchstr(part, '^\s\+')
" Return template name information
return info
endfunction
function! jp:ReadTemplate(name, filename)
" Try to read the template file and throw exception if that fails
try
return readfile(a:filename)
catch
throw 'Template "' . a:name . '" could not be found.'
endtry
endfunction
function! jp:UpdateCursorPosition(lines, endColumn)
" Define cursorPosition as the column to which the cursor is moved
let cursorPosition = -1
for cnt in range(0, a:lines)
" Search for ${cursor} in the current line
let str = getline(line('.') + cnt)
let start = match(str, '${cursor}')
let end = matchend(str, '${cursor}')
let before = strpart(str, 0, start)
let after = strpart(str, end)
if start &gt;= 0
" Remove ${cursor} and move the cursor to the desired position
call setline(line('.') + cnt, before . after)
call cursor(line('.') + cnt, start+1)
let cursorPosition = start
" We're done
break
endif
endfor
" Update cursor position in case no ${cursor} was found in the template
" and the resulting template was not empty
if cursorPosition == -1 &amp;&amp; a:lines &gt;= 0
if a:lines == 0
call cursor(line('.'), a:endColumn + 1)
else
call cursor(line('.') + a:lines, a:endColumn + 1)
endif
endif
" Return to insert mode (distinguish between 'in the middle of the line' and
" 'at the end of the line')
if col('.') == len(getline('.'))
startinsert!
else
startinsert
endif
endfunction
function! jp:ParseExpression(expr)
" Determine position of the separator between name and value
let valuepos = match(a:expr, ':')
" Extract name and value strings
let name = valuepos &gt;= 0 ? strpart(a:expr, 0, valuepos) : a:expr
let value = valuepos &gt;= 0 ? strpart(a:expr, valuepos + 1) : ''
" Return list with both strings
return [name, value]
endfunction
function! jp:EvaluateReservedVariable(name, value, variables)
let result = ''
if a:name == 'date'
let result = strftime(empty(a:value) ? g:jpTemplateDateFormat : a:value)
elseif a:name == 'shell'
if !empty(a:value)
let result = system(a:value)
endif
elseif a:name == 'interactive_shell'
let command = input('interactive_shell: ', a:value)
if !empty(command)
let result = system(command)
endif
endif
return result
endfunction
function! jp:ExpandTemplate(info, template)
" Backup content before and after the template name
let before = strpart(getline('.'), 0, a:info['start'])
let after = strpart(getline('.'), a:info['end'])
" Merge lines of the template and then split them up again.
" This makes multi-line variable values possible
let mergedTemplate = split(join(a:template, "\n"), "\n")
" Define cnt as the number of inserted lines
let cnt = 0
" Remove template string if the resulting template is empty
if empty(mergedTemplate)
call setline(line('.'), before . after)
call cursor(line('.'), len(before) + 1)
let cnt = -1
else
" Insert template between before and after
for cnt in range(0, len(mergedTemplate) - 1)
if cnt == 0
call setline(line('.'), before . mergedTemplate[cnt])
else
call append(line('.') + cnt - 1, a:info['indent'] . mergedTemplate[cnt])
endif
if cnt == len(mergedTemplate) - 1
call setline(line('.') + cnt, getline(line('.') + cnt) . after)
endif
endfor
endif
" Define start and end columns of the template
let startColumn = len(before)
if empty(mergedTemplate)
let endColumn = startColumn
else
if cnt == 0
let endColumn = startColumn + len(mergedTemplate[0])
else
let endColumn = len(a:info['indent'] . mergedTemplate[len(mergedTemplate) - 1])
endif
endif
" Return number of inserted lines, start and end columns
return [cnt, startColumn, endColumn]
endfunction
function! jp:ProcessTemplate(info, template)
let matchpos = 0
let expressions = []
let variables = {}
let reserved = {}
" Make a string out of the template lines
let s:str = join(a:template, ' ')
" Detect all variable names of the template
while 1
" Find next variable start and end position
let start = match(s:str, '${[^{}]\+}', matchpos)
let end = matchend(s:str, '${[^{}]\+}', matchpos)
if start &lt; 0
" Stop search if there is no variable left
break
else
" Extract variable expression (remove '${' and '}')
let expr = s:str[start+2 : end-2]
" Extract variable name and default value */
let [name, value] = jp:ParseExpression(expr)
if name == 'cursor'
" Skip the ${cursor} variable
elseif count(s:reservedVariables, name) &gt; 0
let reserved[expr] = ''
else
" Only insert variables on their first appearance
if !has_key(variables, name)
" Add expression to the expression list
call add(expressions, expr)
" Set variable value to ''
let variables[name] = ''
endif
" Check whether local default value is defined or not
if empty(value)
" If not, check if variable value is empty
if empty(variables[name])
" If so, either set it to the last remembered value or the global
" default if it exists
if has_key(s:rememberedValues, name)
let variables[name] = s:rememberedValues[name]
elseif has_key(g:jpTemplateDefaults, name)
let variables[name] = g:jpTemplateDefaults[name]
endif
endif
else
" Use local default(first occurence in the template only)
let variables[name] = value
endif
endif
" Start next search at the end position of this expression
let matchpos = end
endif
endwhile
" Ask the user to enter values for all variables
for expr in expressions
let [name, value] = jp:ParseExpression(expr)
let variables[name] = input(name . ': ', variables[name])
let s:rememberedValues[name] = variables[name]
endfor
" Evaluate reserved variables
for expr in keys(reserved)
let [name, value] = jp:ParseExpression(expr)
let replacement = jp:EvaluateReservedVariable(name, value, variables)
let reserved[expr] = replacement
endfor
" Expand all variables (custom and reserved)
for index in range(len(a:template))
for expr in expressions
let [name, value] = jp:ParseExpression(expr)
let expr = '${' . name . '\(:[^{}]\+\)\?}'
let value = escape(variables[name], s:escapeCharacters)
let a:template[index] = substitute(a:template[index], expr, value, 'g')
endfor
for [expr, value] in items(reserved)
let expr = '${' . expr . '}'
let value = escape(value, s:escapeCharacters)
let a:template[index] = substitute(a:template[index], expr, value, 'g')
endfor
endfor
" Insert template into the code line by line
let [insertedLines, startColumn, endColumn] = jp:ExpandTemplate(a:info, a:template)
" Update the cursor position and return to insert mode
call jp:UpdateCursorPosition(insertedLines, endColumn)
endfunction
function! jp:InsertTemplate()
try
" Detect bounds of the template name as well as the name itself
let info = jp:GetTemplateInfo()
" Load the template file
let template = jp:ReadTemplate(info['name'], info['filename'])
" Do the hard work: Process the template
call jp:ProcessTemplate(info, template)
catch
" Inform the user about errors
echo g:jpTemplateVerbose ? v:exception . " (in " . v:throwpoint . ")" : v:exception
endtry
endfunction
" Initialize jptemplate configuration
call jp:Initialize()
" Map keyboard shortcut to the template system
exec 'imap ' . g:jpTemplateKey . ' &lt;Esc&gt;:call jp:InsertTemplate()&lt;CR&gt;'
</pre>
<p>This test Vim script code is <a href="https://github.com/Jannis/jptemplate">jptemplate</a>.</p>
</body>
</html>
@dciccale

This comment has been minimized.

Copy link

@dciccale dciccale commented Oct 7, 2013

cool thx!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.