diff options
| -rw-r--r-- | autoload/erlang_complete.vim | 219 | ||||
| -rwxr-xr-x | build.sh | 1 | ||||
| -rw-r--r-- | compiler/erlang.vim | 111 | ||||
| -rw-r--r-- | ftplugin/erlang.vim | 85 | ||||
| -rw-r--r-- | indent/erlang.vim | 58 | ||||
| -rw-r--r-- | syntax/erlang.vim | 126 | 
6 files changed, 600 insertions, 0 deletions
| diff --git a/autoload/erlang_complete.vim b/autoload/erlang_complete.vim new file mode 100644 index 00000000..9d108fbc --- /dev/null +++ b/autoload/erlang_complete.vim @@ -0,0 +1,219 @@ +" Vim omni completion file +" Language:     Erlang +" Author:       Oscar Hellström <oscar@oscarh.net> +" Contributors: kTT (http://github.com/kTT) +"               Ricardo Catalinas Jiménez <jimenezrick@gmail.com> +"               Eduardo Lopez (http://github.com/tapichu) +"               Zhihui Jiao (http://github.com/onlychoice) +" License:      Vim license +" Version:      2012/11/26 + +if !exists('g:erlang_completion_cache') +	let g:erlang_completion_cache = 1 +endif + +" Completion program path +let s:erlang_complete_file = expand('<sfile>:p:h') . '/erlang_complete.erl' + +" Modules cache used to speed up the completion +let s:modules_cache = {} + +" File cache for persistence between Vim sessions +if filewritable(expand('<sfile>:p:h')) == 2 +	let s:file_cache = expand('<sfile>:p:h') . '/vimerl_cache' +else +	let s:file_cache = '/tmp/vimerl_cache' +endif + +" Patterns for completions +let s:erlang_local_func_beg    = '\(\<[0-9A-Za-z_-]*\|\s*\)$' +let s:erlang_external_func_beg = '\<[0-9A-Za-z_-]\+:[0-9A-Za-z_-]*$' +let s:erlang_blank_line        = '^\s*\(%.*\)\?$' + +" Main function for completion +function erlang_complete#Complete(findstart, base) +	let lnum = line('.') +	let column = col('.') +	let line = strpart(getline('.'), 0, column - 1) + +	" 1) Check if the char to the left of us are part of a function call +	" +	" Nothing interesting is written at the char just before the cursor +	" This means _anything_ could be started here +	" In this case, keyword completion should probably be used, +	" for now we'll only try and complete local functions. +	" +	" TODO: Examine if we can stare Identifiers end complete on them +	" Is this worth it? Is /completion/ of a "blank" wanted? Can we consider +	" `(' interesting and check if we are in a function call etc.? +	if line[column - 2] !~ '[0-9A-Za-z:_-]' +		if a:findstart +			return column +		else +			return s:ErlangFindLocalFunc(a:base) +		endif +	endif +	 +	" 2) Function in external module +	if line =~ s:erlang_external_func_beg +		let delimiter = match(line, ':[0-9A-Za-z_-]*$') + 1 +		if a:findstart +			return delimiter +		else +			let module = matchstr(line[:-2], '\<\k*\>$') +			return s:ErlangFindExternalFunc(module, a:base) +		endif +	endif + +	" 3) Local function +	if line =~ s:erlang_local_func_beg +		let funcstart = match(line, ':\@<![0-9A-Za-z_-]*$') +		if a:findstart +			return funcstart +		else +			return s:ErlangFindLocalFunc(a:base) +		endif +	endif + +	" 4) Unhandled situation +	if a:findstart +		return -1 +	else +		return [] +	endif +endfunction + +" Find the next non-blank line +function s:ErlangFindNextNonBlank(lnum) +	let lnum = nextnonblank(a:lnum + 1) +	let line = getline(lnum) + +	while line =~ s:erlang_blank_line && 0 != lnum +		let lnum = nextnonblank(lnum + 1) +		let line = getline(lnum) +	endwhile + +	return lnum +endfunction + +" Find external function names +function s:ErlangFindExternalFunc(module, base) +	" If the module is cached, load its functions +	if has_key(s:modules_cache, a:module) +		for field_cache in get(s:modules_cache, a:module) +			if match(field_cache.word, a:base) == 0 +				call complete_add(field_cache) +			endif +		endfor + +		return [] +	endif + +	let functions = system(s:erlang_complete_file . ' ' . a:module) +	for function_spec in split(functions, '\n') +		if match(function_spec, a:base) == 0 +			let function_name = matchstr(function_spec, a:base . '\w*') +			let field = {'word': function_name . '(', 'abbr': function_spec, +				  \  'kind': 'f', 'dup': 1} +			call complete_add(field) + +			" Populate the cache only when iterating over all the +			" module functions (i.e. no prefix for the completion) +			if g:erlang_completion_cache && a:base == '' +				if !has_key(s:modules_cache, a:module) +					let s:modules_cache[a:module] = [field] +				else +					let fields_cache = get(s:modules_cache, a:module) +					let s:modules_cache[a:module] = add(fields_cache, field) +				endif +			endif + +			" The user entered some text, so stop the completion +			if complete_check() +				" The module couldn't be entirely cached +				if has_key(s:modules_cache, a:module) +					call remove(s:modules_cache, a:module) +				endif +				break +			endif +		endif +	endfor + +	call s:ErlangWriteCache(a:module) + +	return [] +endfunction + +" Find local function names +function s:ErlangFindLocalFunc(base) +	" Begin at line 1 +	let lnum = s:ErlangFindNextNonBlank(1) + +	if "" == a:base +		let base = '\w' " Used to match against word symbol +	else +		let base = a:base +	endif + +	while 0 != lnum && !complete_check() +		let line = getline(lnum) +		let function_name = matchstr(line, '^' . base . '[0-9A-Za-z_-]\+(\@=') +		if function_name != "" +			call complete_add({'word': function_name, 'kind': 'f'}) +		endif +		let lnum = s:ErlangFindNextNonBlank(lnum) +	endwhile + +	return [] +endfunction + +function s:ErlangLoadCache() +	if filereadable(s:file_cache) +		for line in readfile(s:file_cache) +			let cache_entry = eval(line) +			" cache_entry is a dict with just one key with the +			" module name and the function list we are going to +			" add to the memory cache as the value of this key +			for mod_name in keys(cache_entry) +				let func_list = get(cache_entry, mod_name) +				let s:modules_cache[mod_name] = func_list +			endfor +		endfor +	endif +endfunction + +function s:ErlangWriteCache(module) +	" Write all the module functions to the cache file +	if has_key(s:modules_cache, a:module) +		let func_list = get(s:modules_cache, a:module) +		if len(func_list) > 0 +			let cache_entry = {a:module : func_list} +			execute 'redir >>' . s:file_cache +			silent echon cache_entry +			silent echon "\n" +			redir END +		endif +	endif +endfunction + +function s:ErlangPurgeCache(...) +	for mod_name in a:000 +		if has_key(s:modules_cache, mod_name) +			call remove(s:modules_cache, mod_name) +		endif +	endfor + +	" Delete the old cache file +	call delete(s:file_cache) + +	" Write a new one +	for mod_name in keys(s:modules_cache) +		call s:ErlangWriteCache(mod_name) +	endfor +endfunction + +" Load the file cache when this script is autoloaded +call s:ErlangLoadCache() + +" Command for removing modules from the cache +command -nargs=+ ErlangPurgeCache silent call s:ErlangPurgeCache(<f-args>) @@ -66,6 +66,7 @@ syntax 'sudar/vim-arduino-syntax' &  syntax 'guns/vim-clojure-static' &  syntax 'chrisbra/csv.vim' &  syntax 'elixir-lang/vim-elixir' & +syntax 'jimenezrick/vimerl' &  wait diff --git a/compiler/erlang.vim b/compiler/erlang.vim new file mode 100644 index 00000000..da88b856 --- /dev/null +++ b/compiler/erlang.vim @@ -0,0 +1,111 @@ +" Vim compiler file +" Language:     Erlang +" Author:       Pawel 'kTT' Salata <rockplayer.pl@gmail.com> +" Contributors: Ricardo Catalinas Jiménez <jimenezrick@gmail.com> +" License:      Vim license +" Version:      2013/03/06 + +if exists("current_compiler") || v:version < 703 +	finish +else +	let current_compiler = "erlang" +endif + +let b:error_list     = {} +let b:is_showing_msg = 0 +let b:next_sign_id   = 1 + +if exists(":CompilerSet") != 2 +	command -nargs=* CompilerSet setlocal <args> +endif + +CompilerSet makeprg=make +CompilerSet errorformat=%f:%l:\ %tarning:\ %m,%f:%l:\ %m + +" Only define functions and script scope variables once +if exists("*s:ShowErrors") +	finish +endif + +if !exists("g:erlang_show_errors") +	let g:erlang_show_errors = 1 +endif + +let s:erlang_check_file = expand("<sfile>:p:h") . "/erlang_check.erl" +let s:autocmds_defined  = 0 + +sign define ErlangError   text=>> texthl=Error +sign define ErlangWarning text=>> texthl=Todo + +command ErlangDisableShowErrors silent call s:DisableShowErrors() +command ErlangEnableShowErrors  silent call s:EnableShowErrors() + +function s:ShowErrors() +	setlocal shellpipe=> +	if match(getline(1), "#!.*escript") != -1 +		setlocal makeprg=escript\ -s\ % +	else +		execute "setlocal makeprg=" . s:erlang_check_file . "\\ \%" +	endif +	silent make! +	for error in getqflist() +		let item         = {} +		let item["lnum"] = error.lnum +		let item["text"] = error.text +		let b:error_list[error.lnum] = item +		let type = error.type == "W" ? "ErlangWarning" : "ErlangError" +		execute "sign place" b:next_sign_id "line=" . item.lnum "name=" . type "file=" . expand("%:p") +		let b:next_sign_id += 1 +	endfor +	setlocal shellpipe& +	setlocal makeprg=make +endfunction + +function s:ShowErrorMsg() +	let pos = getpos(".") +	if has_key(b:error_list, pos[1]) +		let item = get(b:error_list, pos[1]) +		echo item.text +		let b:is_showing_msg = 1 +	else +		if b:is_showing_msg +			echo +			let b:is_showing_msg = 0 +		endif +	endif +endf + +function s:ClearErrors() +	sign unplace * +	let b:error_list   = {} +	let b:next_sign_id = 1 +	if b:is_showing_msg +		echo +		let b:is_showing_msg = 0 +	endif +endfunction + +function s:EnableShowErrors() +	if !s:autocmds_defined +		augroup vimerl +			autocmd! +			autocmd BufWritePre  *.erl call s:ClearErrors() +			autocmd BufWritePost *.erl call s:ShowErrors() +			autocmd CursorHold   *.erl call s:ShowErrorMsg() +			autocmd CursorMoved  *.erl call s:ShowErrorMsg() +		augroup END +		let s:autocmds_defined = 1 +	endif +endfunction + +function s:DisableShowErrors() +	sign unplace * +	augroup vimerl +		autocmd! +	augroup END +	let s:autocmds_defined = 0 +endfunction + +if g:erlang_show_errors +	call s:EnableShowErrors() +endif diff --git a/ftplugin/erlang.vim b/ftplugin/erlang.vim new file mode 100644 index 00000000..49b64ebb --- /dev/null +++ b/ftplugin/erlang.vim @@ -0,0 +1,85 @@ +" Vim ftplugin file +" Language:     Erlang +" Author:       Oscar Hellström <oscar@oscarh.net> +" Contributors: Ricardo Catalinas Jiménez <jimenezrick@gmail.com> +"               Eduardo Lopez (http://github.com/tapichu) +" License:      Vim license +" Version:      2012/11/25 + +if exists('b:did_ftplugin') +	finish +else +	let b:did_ftplugin = 1 +endif + +if exists('s:did_function_definitions') +	call s:SetErlangOptions() +	finish +else +	let s:did_function_definitions = 1 +endif + +if !exists('g:erlang_keywordprg') +	let g:erlang_keywordprg = 'erl -man' +endif + +if !exists('g:erlang_folding') +	let g:erlang_folding = 0 +endif + +let s:erlang_fun_begin = '^\(\a\w*\|[''][^'']*['']\)(.*$' +let s:erlang_fun_end   = '^[^%]*\.\s*\(%.*\)\?$' + +function s:SetErlangOptions() +	compiler erlang +	if version >= 700 +		setlocal omnifunc=erlang_complete#Complete +	endif + +	if g:erlang_folding +		setlocal foldmethod=expr +		setlocal foldexpr=GetErlangFold(v:lnum) +		setlocal foldtext=ErlangFoldText() +	endif + +	setlocal comments=:%%%,:%%,:% +	setlocal commentstring=%%s +	setlocal formatoptions+=ro +	setlocal suffixesadd=.erl +	let libs = substitute(system('which erl'), '/bin/erl', '/lib/erlang/lib/**/src/', '') +	execute 'setlocal path+=' . libs +	let &l:keywordprg = g:erlang_keywordprg +endfunction + +function GetErlangFold(lnum) +	let lnum = a:lnum +	let line = getline(lnum) + +	if line =~ s:erlang_fun_end +		return '<1' +	endif + +	if line =~ s:erlang_fun_begin && foldlevel(lnum - 1) == 1 +		return '1' +	endif + +	if line =~ s:erlang_fun_begin +		return '>1' +	endif + +	return '=' +endfunction + +function ErlangFoldText() +	let line    = getline(v:foldstart) +	let foldlen = v:foldend - v:foldstart + 1 +	let lines   = ' ' . foldlen . ' lines: ' . substitute(line, "[ \t]*", '', '') +	if foldlen < 10 +		let lines = ' ' . lines +	endif +	let retval = '+' . v:folddashes . lines + +	return retval +endfunction + +call s:SetErlangOptions() diff --git a/indent/erlang.vim b/indent/erlang.vim new file mode 100644 index 00000000..370a8810 --- /dev/null +++ b/indent/erlang.vim @@ -0,0 +1,58 @@ +" Vim indent file +" Language: Erlang +" Author:   Ricardo Catalinas Jiménez <jimenezrick@gmail.com> +" License:  Vim license +" Version:  2013/09/11 + +if !exists('g:erlang_force_use_vimerl_indent') +	let g:erlang_force_use_vimerl_indent = 0 +endif + +if exists('b:did_indent') || (v:version >= 704 && !g:erlang_force_use_vimerl_indent) +	finish +else +	let b:did_indent = 1 +endif + +setlocal indentexpr=ErlangIndent() +setlocal indentkeys=!^F,o,O,=),=},=],=>>,=of,=catch,=after,=end + +if exists('*ErlangIndent') +	finish +endif + +let s:erlang_indent_file = expand('<sfile>:p:h') . '/erlang_indent.erl' +if filewritable(expand('<sfile>:p:h')) == 2 +	let s:in_fifo  = expand('<sfile>:p:h') . '/vimerl_in_fifo.' . getpid() +	let s:out_fifo = expand('<sfile>:p:h') . '/vimerl_out_fifo.' . getpid() +else +	let s:in_fifo  = '/tmp/vimerl_in_fifo.' . getpid() +	let s:out_fifo = '/tmp/vimerl_out_fifo.' . getpid() +endif + +execute 'silent !mkfifo' s:in_fifo +execute 'silent !mkfifo' s:out_fifo +execute 'silent !' . s:erlang_indent_file s:out_fifo s:in_fifo '&' + +autocmd VimLeave * call <SID>StopIndenter() + +function s:StopIndenter() +	call writefile([], s:out_fifo) +	call delete(s:in_fifo) +	call delete(s:out_fifo) +endfunction + +function ErlangIndent() +	if v:lnum == 1 +		return 0 +	else +		call writefile([v:lnum] + getline(1, v:lnum), s:out_fifo) +		let indent = split(readfile(s:in_fifo)[0]) + +		if len(indent) == 1 || !&expandtab +			return indent[0] * &shiftwidth +		else +			return indent[1] +		endif +	endif +endfunction diff --git a/syntax/erlang.vim b/syntax/erlang.vim new file mode 100644 index 00000000..4b683edc --- /dev/null +++ b/syntax/erlang.vim @@ -0,0 +1,126 @@ +" Vim syntax file +" Language:     Erlang +" Author:       Oscar Hellström <oscar@oscarh.net> (http://oscar.hellstrom.st) +" Contributors: Ricardo Catalinas Jiménez <jimenezrick@gmail.com> +" License:      Vim license +" Version:      2012/05/07 + +if exists("b:current_syntax") +	finish +else +	let b:current_syntax = "erlang" +endif + +if !exists("g:erlang_highlight_bif") +	let g:erlang_highlight_bif = 1 +endif + +" Erlang is case sensitive +syn case match + +" Match groups +syn match erlangStringModifier               /\\./ contained +syn match erlangStringModifier               /\~\%(-\?[0-9*]\+\)\?\%(\.[0-9*]*\%(\..\?t\?\)\?\)\?\%(\~\|c\|f\|e\|g\|s\|w\|p\|W\|P\|B\|X\|#\|b\|x\|+\|n\|i\)/ contained +syn match erlangModifier                     /\$\\\?./ + +syn match erlangInteger                      /\<\%([0-9]\+#[0-9a-fA-F]\+\|[0-9]\+\)\>/ +syn match erlangFloat                        /\<[0-9]\+\.[0-9]\+\%(e-\?[0-9]\+\)\?\>/ + +syn keyword erlangTodo                       TODO FIXME XXX contained +syn match   erlangComment                    /%.*$/ contains=@Spell,erlangTodo,erlangAnnotation +syn match   erlangAnnotation                 /\%(%\s\)\@<=@\%(author\|clear\|copyright\|deprecated\|doc\|docfile\|end\|equiv\|headerfile\|hidden\|private\|reference\|see\|since\|spec\|throws\|title\|todo\|TODO\|type\|version\)/ contained +syn match   erlangAnnotation                 /`[^']\+'/ contained + +syn keyword erlangKeyword                    band bor bnot bsl bsr bxor div rem xor +syn keyword erlangKeyword                    try catch begin receive after cond fun let query + +syn keyword erlangConditional                case if of end +syn keyword erlangConditional                not and or andalso orelse +syn keyword erlangConditional                when + +syn keyword erlangBoolean                    true false + +syn keyword erlangGuard                      is_list is_alive is_atom is_binary is_bitstring is_boolean is_tuple is_number is_integer is_float is_function is_constant is_pid is_port is_reference is_record is_process_alive + +syn match   erlangOperator                   /\/\|*\|+\|-\|++\|--/ +syn match   erlangOperator                   /->\|<-\|||\||\|!\|=/ +syn match   erlangOperator                   /=:=\|==\|\/=\|=\/=\|<\|>\|=<\|>=/ +syn keyword erlangOperator                   div rem + +syn region erlangString                      start=/"/ end=/"/ skip=/\\/ contains=@Spell,erlangStringModifier + +syn match erlangVariable                     /\<[A-Z_]\w*\>/ +syn match erlangAtom                         /\%(\%(^-\)\|#\)\@<!\<[a-z][A-Za-z0-9_]*\>\%(\s*[(:]\)\@!/ +syn match erlangAtom                         /\\\@<!'[^']*\\\@<!'/ + +syn match erlangRecord                       /#\w\+/ + +syn match erlangTuple                        /{\|}/ +syn match erlangList                         /\[\|\]/ + +syn match erlangAttribute                    /^-\%(vsn\|author\|copyright\|compile\|deprecated\|module\|export\|import\|behaviour\|behavior\|export_type\|ignore_xref\|on_load\|mode\)\s*(\@=/ +syn match erlangInclude                      /^-include\%(_lib\)\?\s*(\@=/ +syn match erlangRecordDef                    /^-record\s*(\@=/ +syn match erlangDefine                       /^-\%(define\|undef\)\s*(\@=/ +syn match erlangPreCondit                    /^-\%(ifdef\|ifndef\|else\|endif\)\%(\s*(\@=\)\?/ + +syn match erlangType                         /^-\%(spec\|type\|opaque\|callback\)[( ]\@=/ + +syn match erlangMacro                        /\%(-define(\)\@<=\w\+/ +syn match erlangMacro                        /?\??\w\+/ + +syn match erlangBitType                      /\%(\/\|-\)\@<=\%(bits\|bitstring\|binary\|integer\|float\|unit\)\>/ +syn match erlangBitSize                      /:\@<=[0-9]\+/ + +syn match erlangBinary                       /<<\|>>/ + +" BIFs +syn match erlangBIF                          /\%([^:0-9A-Za-z_]\|\<erlang:\|^\)\@<=\%(abs\|apply\|atom_to_binary\|atom_to_list\|binary_part\|binary_to_atom\|binary_to_existing_atom\|binary_to_list\|binary_to_term\|bit_size\|bitstring_to_list\|byte_size\|check_process_code\|date\|delete_module\|demonitor\|disconnect_node\|element\|erase\|error\|exit\|float\|float_to_list\|garbage_collect\|get\|get_keys\|group_leader\|halt\|hd\|integer_to_list\|iolist_size\|iolist_to_binary\|is_alive\|is_atom\|is_binary\|is_bitstring\|is_boolean\|is_float\|is_function\|is_integer\|is_list\|is_number\|is_pid\|is_port\|is_process_alive\|is_record\|is_reference\|is_tuple\|length\|link\|list_to_atom\|list_to_binary\|list_to_bitstring\|list_to_existing_atom\|list_to_float\|list_to_integer\|list_to_pid\|list_to_tuple\|load_module\|make_ref\|max\|min\|module_loaded\|monitor\|monitor_node\|node\|nodes\|now\|open_port\|pid_to_list\|port_close\|port_command\|port_connect\|port_control\|pre_loaded\|processes\|process_flag\|process_info\|purge_module\|put\|register\|registered\|round\|self\|setelement\|size\|spawn\|spawn_link\|spawn_monitor\|spawn_opt\|split_binary\|statistics\|term_to_binary\|throw\|time\|tl\|trunc\|tuple_size\|tuple_to_list\|unlink\|unregister\|whereis\)\%((\|\/[0-9]\)\@=/ +syn match erlangBIF                          /\%(\<erlang:\)\@<=\%(append_element\|bump_reductions\|cancel_timer\|decode_packet\|display\|function_exported\|fun_info\|fun_to_list\|get_cookie\|get_stacktrace\|hash\|is_builtin\|loaded\|load_nif\|localtime\|localtime_to_universaltime\|make_tuple\|memory\|monitor_node\|phash\|port_call\|port_info\|ports\|port_to_list\|process_display\|read_timer\|ref_to_list\|resume_process\|send\|send_after\|send_nosuspend\|set_cookie\|start_timer\|suspend_process\|system_flag\|system_info\|system_monitor\|system_profile\|trace\|trace_delivered\|trace_info\|trace_pattern\|universaltime\|universaltime_to_localtime\|yield\)(\@=/ +syn match erlangGBIF                         /\<erlang\%(:\w\)\@=/ + +" Link Erlang stuff to Vim groups +hi def link erlangTodo           Todo +hi def link erlangString         String +hi def link erlangNoSpellString  String +hi def link erlangModifier       SpecialChar +hi def link erlangStringModifier SpecialChar +hi def link erlangComment        Comment +hi def link erlangAnnotation     Special +hi def link erlangVariable       Identifier +hi def link erlangInclude        Include +hi def link erlangRecordDef      Keyword +hi def link erlangAttribute      Keyword +hi def link erlangKeyword        Keyword +hi def link erlangMacro          Macro +hi def link erlangDefine         Define +hi def link erlangPreCondit      PreCondit +hi def link erlangPreProc        PreProc +hi def link erlangDelimiter      Delimiter +hi def link erlangBitDelimiter   Normal +hi def link erlangOperator       Operator +hi def link erlangConditional    Conditional +hi def link erlangGuard          Conditional +hi def link erlangBoolean        Boolean +hi def link erlangAtom           Constant +hi def link erlangRecord         Structure +hi def link erlangInteger        Number +hi def link erlangFloat          Number +hi def link erlangFloat          Number +hi def link erlangFloat          Number +hi def link erlangFloat          Number +hi def link erlangHex            Number +hi def link erlangFun            Keyword +hi def link erlangList           Delimiter +hi def link erlangTuple          Delimiter +hi def link erlangBinary         Keyword +hi def link erlangBitVariable    Identifier +hi def link erlangBitType        Type +hi def link erlangType           Type +hi def link erlangBitSize        Number + +" Optional highlighting +if g:erlang_highlight_bif +	hi def link erlangBIF    Keyword +	hi def link erlangGBIF   Keyword +endif | 
