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 <jannis@xfce.org>. | |
" | |
" 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 = '&~\' | |
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', '<C-Tab>']) | |
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', &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 >= 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 && a:lines >= 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 >= 0 ? strpart(a:expr, 0, valuepos) : a:expr | |
let value = valuepos >= 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 < 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) > 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 . ' <Esc>:call jp:InsertTemplate()<CR>' | |
</pre> | |
<p>This test Vim script code is <a href="https://github.com/Jannis/jptemplate">jptemplate</a>.</p> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
cool thx!