From 5bc380150aee647d26a5a538ed855e9e82dcc7f7 Mon Sep 17 00:00:00 2001 From: Adam Stankiewicz Date: Thu, 12 Sep 2013 17:32:53 +0200 Subject: [PATCH] Add erlang support --- autoload/erlang_complete.vim | 219 +++++++++++++++++++++++++++++++++++ build.sh | 1 + compiler/erlang.vim | 111 ++++++++++++++++++ ftplugin/erlang.vim | 85 ++++++++++++++ indent/erlang.vim | 58 ++++++++++ syntax/erlang.vim | 126 ++++++++++++++++++++ 6 files changed, 600 insertions(+) create mode 100644 autoload/erlang_complete.vim create mode 100644 compiler/erlang.vim create mode 100644 ftplugin/erlang.vim create mode 100644 indent/erlang.vim create mode 100644 syntax/erlang.vim diff --git a/autoload/erlang_complete.vim b/autoload/erlang_complete.vim new file mode 100644 index 0000000..9d108fb --- /dev/null +++ b/autoload/erlang_complete.vim @@ -0,0 +1,219 @@ +" Vim omni completion file +" Language: Erlang +" Author: Oscar Hellström +" Contributors: kTT (http://github.com/kTT) +" Ricardo Catalinas Jiménez +" 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(':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(':p:h')) == 2 + let s:file_cache = expand(':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 + 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() diff --git a/build.sh b/build.sh index dd06af0..2065597 100755 --- a/build.sh +++ b/build.sh @@ -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 0000000..da88b85 --- /dev/null +++ b/compiler/erlang.vim @@ -0,0 +1,111 @@ +" Vim compiler file +" Language: Erlang +" Author: Pawel 'kTT' Salata +" Contributors: Ricardo Catalinas Jiménez +" 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 +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(":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 0000000..49b64eb --- /dev/null +++ b/ftplugin/erlang.vim @@ -0,0 +1,85 @@ +" Vim ftplugin file +" Language: Erlang +" Author: Oscar Hellström +" Contributors: Ricardo Catalinas Jiménez +" 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 0000000..370a881 --- /dev/null +++ b/indent/erlang.vim @@ -0,0 +1,58 @@ +" Vim indent file +" Language: Erlang +" Author: Ricardo Catalinas Jiménez +" 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(':p:h') . '/erlang_indent.erl' +if filewritable(expand(':p:h')) == 2 + let s:in_fifo = expand(':p:h') . '/vimerl_in_fifo.' . getpid() + let s:out_fifo = expand(':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 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 0000000..4b683ed --- /dev/null +++ b/syntax/erlang.vim @@ -0,0 +1,126 @@ +" Vim syntax file +" Language: Erlang +" Author: Oscar Hellström (http://oscar.hellstrom.st) +" Contributors: Ricardo Catalinas Jiménez +" 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 /\%(\%(^-\)\|#\)\@\%(\s*[(:]\)\@!/ +syn match erlangAtom /\\\@/ +syn match erlangBitSize /:\@<=[0-9]\+/ + +syn match erlangBinary /<<\|>>/ + +" BIFs +syn match erlangBIF /\%([^:0-9A-Za-z_]\|\