Add erlang support

This commit is contained in:
Adam Stankiewicz 2013-09-12 17:32:53 +02:00
parent 6eb0c57e80
commit 5bc380150a
6 changed files with 600 additions and 0 deletions

View File

@ -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>)

View File

@ -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

111
compiler/erlang.vim Normal file
View File

@ -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

85
ftplugin/erlang.vim Normal file
View File

@ -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()

58
indent/erlang.vim Normal file
View File

@ -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

126
syntax/erlang.vim Normal file
View File

@ -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