diff options
| author | Adam Stankiewicz <sheerun@sher.pl> | 2014-12-11 22:58:14 +0100 | 
|---|---|---|
| committer | Adam Stankiewicz <sheerun@sher.pl> | 2014-12-11 22:58:14 +0100 | 
| commit | 7f2e3663341d725b073857fcd3b2f5868ce85248 (patch) | |
| tree | 34c4063119045da0824d46b6c1f8fc8fc0b9c2b4 | |
| parent | 04f868b94aad9ef31d3cd1adc526d720808f7837 (diff) | |
| download | vim-polyglot-7f2e3663341d725b073857fcd3b2f5868ce85248.tar.gz vim-polyglot-7f2e3663341d725b073857fcd3b2f5868ce85248.zip | |
Change erlang provider to hcs42/vim-erlang-runtime
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | autoload/erlangcomplete.vim | 161 | ||||
| -rwxr-xr-x | build | 2 | ||||
| -rw-r--r-- | compiler/erlang.vim | 80 | ||||
| -rw-r--r-- | ftplugin/erlang.vim | 154 | ||||
| -rw-r--r-- | ftplugin/erlang_refactor.vim | 295 | ||||
| -rw-r--r-- | indent/erlang.vim | 1612 | ||||
| -rw-r--r-- | syntax/erlang.vim | 376 | 
8 files changed, 1700 insertions, 982 deletions
| @@ -39,7 +39,7 @@ Optionally download one of the [releases](https://github.com/sheerun/vim-polyglo  - [elixir](https://github.com/elixir-lang/vim-elixir) (syntax, indent, compiler, ftplugin, ftdetect)  - [emberscript](https://github.com/heartsentwined/vim-ember-script) (syntax, indent, ftplugin, ftdetect)  - [emblem](https://github.com/heartsentwined/vim-emblem) (syntax, indent, ftplugin, ftdetect) -- [erlang](https://github.com/oscarh/vimerl) (syntax, indent, compiler, autoload, ftplugin) +- [erlang](https://github.com/hcs42/vim-erlang-runtime) (syntax, indent)  - [git](https://github.com/tpope/vim-git) (syntax, indent, ftplugin, ftdetect)  - [go](https://github.com/fatih/vim-go) (syntax, indent, ftdetect)  - [haml](https://github.com/tpope/vim-haml) (syntax, indent, compiler, ftplugin, ftdetect) diff --git a/autoload/erlangcomplete.vim b/autoload/erlangcomplete.vim deleted file mode 100644 index 3e4208e8..00000000 --- a/autoload/erlangcomplete.vim +++ /dev/null @@ -1,161 +0,0 @@ -" ------------------------------------------------------------------------------ -" Vim omni-completion script -" Author: Oscar Hellström -" Email: oscar@oscarh.net -" Version: 2010-08-10 -" Contributors: kTT (http://github.com/kTT) -" 		Ricardo Catalinas Jiménez <jimenezrick@gmail.com> -" ------------------------------------------------------------------------------ - -" Patterns for completions {{{1 -let s:erlangLocalFuncBeg    = '\(\<[0-9A-Za-z_-]*\|\s*\)$' -let s:erlangExternalFuncBeg = '\<[0-9A-Za-z_-]\+:[0-9A-Za-z_-]*$' -let s:ErlangBlankLine       = '^\s*\(%.*\)\?$' -let s:erlangCompletionPath  = expand('<sfile>:p:h') . '/erlang_completion.erl' - -if !exists('g:erlangCompletionGrep') -	let g:erlangCompletionGrep = 'grep' -endif - -if !exists('g:erlangManSuffix') -	let g:erlangManSuffix = '' -endif - -if !exists('g:erlangManPath') -	let g:erlangManPath = '/usr/lib/erlang/man' -endif - -if !exists('g:erlangCompletionDisplayDoc') -	let g:erlangCompletionDisplayDoc = 1 -endif - -" Main function for completion {{{1 -function! erlangcomplete#Complete(findstart, base) -	" 0) Init {{{2 -	let lnum = line('.') -	let column = col('.')  -	let line = strpart(getline('.'), 0, column - 1) - -	" 1) First, check if completion is impossible {{{2 -	if line =~ '[^~\\]%' -		return -1 -	endif - -	"echo "line[col - 1]:" . line[column - 1] . " line[col - 2]:" . line[column - 2] .  "\n" . line . "\n" - -	" 2) Check if the char to the left of us are part of a function call {{{2 -	" -	" 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 -	 - -	" 3) Function in external module {{{2 -	if line =~ s:erlangExternalFuncBeg -		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 - -	" 4) Local function {{{2 -	if line =~ s:erlangLocalFuncBeg -		let funcstart = match(line, ':\@<![0-9A-Za-z_-]*$') -		if a:findstart -			return funcstart -		else -			return s:erlangFindLocalFunc(a:base) -		endif -	endif - -	" 5) Unhandled situation {{{2 -	if a:findstart -		return -1 -	else -		return [] -	endif -endfunction - -" Auxiliary functions for completion {{{1  -" Find the next non-blank line {{{2 -function s:erlangFindNextNonBlank(lnum) -	let lnum = nextnonblank(a:lnum + 1) -	let line = getline(lnum) -	while line =~ s:ErlangBlankLine && 0 != lnum -		let lnum = nextnonblank(lnum + 1) -		let line = getline(lnum) -   endwhile -   return lnum -endfunction -			 -" vim: foldmethod=marker: -" Find external function names {{{2 -function s:erlangFindExternalFunc(module, base) -        " If it's a local module, try to compile it -        if filereadable(a:module . '.erl') && !filereadable(a:module . '.beam') -            silent execute '!erlc' a:module . '.erl' '>/dev/null' '2>/dev/null' -            redraw! -        endif -        let functions = system(s:erlangCompletionPath . ' ' . a:module) -        for element in sort(split(functions, '\n')) -            if match(element, a:base) == 0 -                let function_name = matchstr(element, a:base . '\w\+') -                let number_of_args = matchstr(element, '\d\+', len(function_name)) -                let number_of_comma = max([number_of_args - 1, 0]) -                let file_path = g:erlangManPath . '/man?/' . a:module . '\.?' . g:erlangManSuffix -                " [:-2] cutting some weird characters at the end -                " becouse grep doesn't support multilines, we have to filter -                " first by .B and next by looking via function name -                " if someone have better idea, please change it -                let description = '' -                " Don't look man pages if the module is present in the current directory -                if g:erlangCompletionDisplayDoc != 0 && !filereadable(a:module . '.erl') -                    let system_command = g:erlangCompletionGrep . ' -A 1 "\.B" ' . file_path . ' | grep -EZo "\<' . -\                           function_name . '\>\((\w+, ){' . number_of_comma . '}[^),]*\) -> .*"' -                    let description = system(system_command) -                    let description = description[:-2] -                endif -                if description == '' -                    let description = element " if function doesn't have description e.g. lists:rmerge, put rmerge/2 instead -                endif -                let field = {'word': function_name . '(', 'abbr': description, 'kind': 'f', 'dup': 1} " always duplicate functions -                call complete_add(field) -            endif -        endfor -        return [] -endfunction - -" Find local function names {{{2 -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(function_name) -		endif -		let lnum = s:erlangFindNextNonBlank(lnum) -	endwhile -	return [] -endfunction - @@ -82,7 +82,7 @@ PACKS="    elixir:elixir-lang/vim-elixir    emberscript:heartsentwined/vim-ember-script    emblem:heartsentwined/vim-emblem -  erlang:oscarh/vimerl +  erlang:hcs42/vim-erlang-runtime    git:tpope/vim-git    go:fatih/vim-go:_BASIC    haml:tpope/vim-haml diff --git a/compiler/erlang.vim b/compiler/erlang.vim deleted file mode 100644 index 6ab7526f..00000000 --- a/compiler/erlang.vim +++ /dev/null @@ -1,80 +0,0 @@ -" Erlang compiler file -" Language:   Erlang -" Maintainer: Pawel 'kTT' Salata <rockplayer.pl@gmail.com> -" URL:        http://ktototaki.info - -if exists("current_compiler") -    finish -endif -let current_compiler = "erlang" - -if exists(":CompilerSet") != 2 -    command -nargs=* CompilerSet setlocal <args> -endif - -if !exists('g:erlangCheckFile') -    let g:erlangCheckFile = "~/.vim/compiler/erlang_check_file.erl" -endif - -if !exists('g:erlangHighlightErrors') -    let g:erlangHighlightErrors = 0 -endif - -let b:error_list = {} -let b:is_showing_msg = 0 - -function! HighlightErlangErrors() -    if match(getline(1), "#!.*escript") != -1 -        setlocal makeprg=escript\ -s\ % -    else -        execute "setlocal makeprg=" . g:erlangCheckFile . "\\ \%" -    endif -    silent make! -    call s:clear_matches() -    for error in getqflist() -        let item = {} -        let item['lnum'] = error.lnum -        let item['msg'] = error.text -        let b:error_list[error.lnum] = item -        call matchadd('SpellBad', "\\%" . error.lnum . "l") -    endfor -    if len(getqflist()) -        redraw! -    endif -    call s:show_msg() -    setlocal makeprg=erlc\ % -endfunction - -function! s:show_msg() -    let pos = getpos(".") -    if has_key(b:error_list, pos[1]) -        let item = get(b:error_list, pos[1]) -        echo item.msg -        let b:is_showing_msg = 1 -    else -        if exists("b:is_showing_msg") && b:is_showing_msg == 1 -            echo -            let b:is_showing_msg = 0 -        endif -    endif -endf - -function! s:clear_matches() -    call clearmatches() -    let b:error_list = {} -    if exists("b:is_showing_msg") && b:is_showing_msg == 1 -        echo -        let b:is_showing_msg = 0 -    endif -endfunction - -CompilerSet makeprg=erlc\ % -CompilerSet errorformat=%f:%l:\ %tarning:\ %m,%E%f:%l:\ %m - -if g:erlangHighlightErrors -    autocmd BufLeave *.erl call s:clear_matches() -    autocmd BufEnter *.erl call s:clear_matches() -    autocmd BufWritePost *.erl call HighlightErlangErrors() -    autocmd CursorHold *.erl call s:show_msg() -    autocmd CursorMoved *.erl call s:show_msg() -endif diff --git a/ftplugin/erlang.vim b/ftplugin/erlang.vim deleted file mode 100644 index 4f1acfe8..00000000 --- a/ftplugin/erlang.vim +++ /dev/null @@ -1,154 +0,0 @@ -" Vim ftplugin file -" Language:    Erlang -" Maintainer:  Oscar Hellström <oscar@oscarh.net> -" URL:         http://personal.oscarh.net -" Contributor: Ricardo Catalinas Jiménez <jimenezrick@gmail.com> -" Version:     2010-09-03 -" ------------------------------------------------------------------------------ -" Usage: -" -" To enable folding put in your vimrc: -" set foldenable -" -" Folding will make only one fold for a complete function, even though it has -" more than one function head and body. -" -" To change this behaviour put in your vimrc file: -" let g:erlangFoldSplitFunction=1 -" -" ------------------------------------------------------------------------------ -" Plugin init -if exists("b:did_ftplugin") -	finish -endif - -" Don't load any other -let b:did_ftplugin=1 - -if exists('s:doneFunctionDefinitions') -	call s:SetErlangOptions() -	finish -endif - -let s:doneFunctionDefinitions=1 - -" Local settings -function s:SetErlangOptions() -	compiler erlang -	if version >= 700 -		setlocal omnifunc=erlangcomplete#Complete -	endif - -	setlocal comments=:%%%,:%%,:% -	setlocal commentstring=%%s - -	setlocal foldmethod=expr -	setlocal foldexpr=GetErlangFold(v:lnum) -	setlocal foldtext=ErlangFoldText() -endfunction - -" Define folding functions -if !exists("*GetErlangFold") -	" Folding params -	let s:ErlangFunBegin    = '^\a\w*(.*$' -	let s:ErlangFunEnd      = '^[^%]*\.\s*\(%.*\)\?$' -	let s:ErlangBlankLine   = '^\s*\(%.*\)\?$' - -	" Auxiliary fold functions -	function s:GetNextNonBlank(lnum) -		let lnum = nextnonblank(a:lnum + 1) -		let line = getline(lnum) -		while line =~ s:ErlangBlankLine && 0 != lnum -			let lnum = nextnonblank(lnum + 1) -			let line = getline(lnum) -		endwhile -		return lnum -	endfunction - -	function s:GetFunName(str) -		return matchstr(a:str, '^\a\w*(\@=') -	endfunction - -	function s:GetFunArgs(str, lnum) -		let str = a:str -		let lnum = a:lnum -		while str !~ '->\s*\(%.*\)\?$' -			let lnum = s:GetNextNonBlank(lnum) -			if 0 == lnum " EOF -				return "" -			endif -			let str .= getline(lnum) -		endwhile -		return matchstr(str,  -			\ '\(^(\s*\)\@<=.*\(\s*)\(\s\+when\s\+.*\)\?\s\+->\s*\(%.*\)\?$\)\@=') -	endfunction - -	function s:CountFunArgs(arguments) -		let pos = 0 -		let ac = 0 " arg count -		let arguments = a:arguments -		 -		" Change list / tuples into just one A(rgument) -		let erlangTuple = '{\([A-Za-z_,|=\-\[\]]\|\s\)*}' -		let erlangList  = '\[\([A-Za-z_,|=\-{}]\|\s\)*\]' - -		" FIXME: Use searchpair? -		while arguments =~ erlangTuple -			let arguments = substitute(arguments, erlangTuple, "A", "g") -		endwhile -		" FIXME: Use searchpair? -		while arguments =~ erlangList -			let arguments = substitute(arguments, erlangList, "A", "g") -		endwhile -		 -		let len = strlen(arguments) -		while pos < len && pos > -1 -			let ac += 1 -			let pos = matchend(arguments, ',\s*', pos) -		endwhile -		return ac -	endfunction - -	" Main fold function -	function GetErlangFold(lnum) -		let lnum = a:lnum -		let line = getline(lnum) - -		if line =~ s:ErlangFunEnd -			return '<1' -		endif - -		if line =~ s:ErlangFunBegin && foldlevel(lnum - 1) == 1 -			if exists("g:erlangFoldSplitFunction") && g:erlangFoldSplitFunction -				return '>1' -			else -				return '1' -			endif -		endif - -		if line =~ s:ErlangFunBegin -			return '>1' -		endif - -		return '=' -	endfunction - -	" Erlang fold description (foldtext function) -	function ErlangFoldText() -		let foldlen = v:foldend - v:foldstart -		if 1 < foldlen -			let lines = "lines" -		else -			let lines = "line" -		endif -		let line = getline(v:foldstart) -		let name = s:GetFunName(line) -		let arguments = s:GetFunArgs(strpart(line, strlen(name)), v:foldstart) -		let argcount = s:CountFunArgs(arguments) -		let retval = "+" . v:folddashes . " " . name . "/" . argcount -		let retval .= " (" . foldlen . " " . lines . ")" -		return retval -	endfunction -endif - -call s:SetErlangOptions() diff --git a/ftplugin/erlang_refactor.vim b/ftplugin/erlang_refactor.vim deleted file mode 100644 index f809db9b..00000000 --- a/ftplugin/erlang_refactor.vim +++ /dev/null @@ -1,295 +0,0 @@ -" Erlang refactor file -" Language:   Erlang -" Maintainer: Pawel 'kTT' Salata <rockplayer.pl@gmail.com> -" URL:        http://ktototaki.info - -if exists("b:did_ftplugin_erlang") -    finish -endif - -" Don't load any other -let b:did_ftplugin_erlang=1 - -if !exists('g:erlangRefactoring') || g:erlangRefactoring == 0 -    finish -endif - -if !exists('g:erlangWranglerPath') -    let g:erlangWranglerPath = '/usr/share/wrangler/' -endif - -if glob(g:erlangWranglerPath) == "" -    call confirm("Wrong path to wrangler dir") -    finish -endif - -autocmd VimLeavePre * call StopWranglerServer() - -let s:erlangServerName = "wrangler_vim" - -" Starting background erlang session with wrangler on -function! StartWranglerServer() -    let wranglerEbinDir = g:erlangWranglerPath . "/ebin" -    let command = "erl_call -s -sname " . s:erlangServerName . " -x 'erl -pa " . wranglerEbinDir . "'" -    call system(command) -    call s:send_rpc('application', 'start', '[wrangler_app]') -endfunction - -" Stopping erlang session -function! StopWranglerServer() -    echo s:send_rpc('erlang', 'halt', '') -endfunction - -" Sending rpc call to erlang session -function! s:send_rpc(module, fun, args) -    let command = "erl_call -sname " . s:erlangServerName . " -a '" . a:module . " " . a:fun . " " . a:args . "'" -    let result = system(command) -    if match(result, 'erl_call: failed to connect to node .*') != -1 -        call StartWranglerServer() -        return system(command) -    endif -    return result -endfunction - -function! ErlangUndo() -    echo s:send_rpc("wrangler_undo_server", "undo", "[]") -    :e! -endfunction - -function! s:trim(text) -    return substitute(a:text, "^\\s\\+\\|\\s\\+$", "", "g") -endfunction - -function! s:get_msg(result, tuple_start) -    let msg_begin = '{' . a:tuple_start . ',' -    let matching_start =  match(a:result, msg_begin) -    if matching_start != -1 -        return s:trim(matchstr(a:result, '[^}]*', matching_start + strlen(msg_begin))) -    endif -    return "" -endfunction - -" Check if there is an error in result -function! s:check_for_error(result) -    let msg = s:get_msg(a:result, 'ok') -    if msg != "" -        return [0, msg] -    endif -    let msg = s:get_msg(a:result, 'warning') -    if msg != "" -        return [1, msg] -    endif -    let msg = s:get_msg(a:result, 'error') -    if msg != "" -        return [2, msg] -    endif -    return [-1, ""] -endfunction - -" Sending apply changes to file -function! s:send_confirm() -    let choice = confirm("What do you want?", "&Preview\n&Confirm\nCa&ncel", 0) -    if choice == 1 -        echo "TODO: Display preview :)" -    elseif choice == 2 -        let module = 'wrangler_preview_server' -        let fun = 'commit' -        let args = '[]' -        return s:send_rpc(module, fun, args) -    else -        let module = 'wrangler_preview_server' -        let fun = 'abort' -        let args = '[]' -        return s:send_rpc(module, fun, args) -        echo "Canceled" -    endif -endfunction - -" Manually send confirm, for testing purpose only -function! SendConfirm() -    echo s:send_confirm() -endfunction - -" Format and send function extracton call -function! s:call_extract(start_line, start_col, end_line, end_col, name) -    let file = expand("%:p") -    let module = 'wrangler' -    let fun = 'fun_extraction' -    let args = '["' . file . '", {' . a:start_line . ', ' . a:start_col . '}, {' . a:end_line . ', ' . a:end_col . '}, "' . a:name . '", ' . &sw . ']' -    let result = s:send_rpc(module, fun, args) -    let [error_code, msg] = s:check_for_error(result) -    if error_code != 0 -        call confirm(msg) -        return 0 -    endif -    echo "This files will be changed: " . matchstr(msg, "[^]]*", 1) -    echo s:send_confirm() -    return 1 -endfunction - -function! ErlangExtractFunction(mode) range -    silent w! -    let name = inputdialog("New function name: ") -    if name != "" -        if a:mode == "v" -            let start_pos = getpos("'<") -            let start_line = start_pos[1] -            let start_col = start_pos[2] - -            let end_pos = getpos("'>") -            let end_line = end_pos[1] -            let end_col = end_pos[2] -        elseif a:mode == "n" -            let pos = getpos(".") -            let start_line = pos[1] -            let start_col = pos[2] -            let end_line = pos[1] -            let end_col = pos[2] -        else -            echo "Mode not supported." -            return -        endif -        if s:call_extract(start_line, start_col, end_line, end_col, name) -            let temp = &autoread -            set autoread -            :e -            if temp == 0 -                set noautoread -            endif -        endif -    else -        echo "Empty function name. Ignoring." -    endif -endfunction -nmap <A-r>e :call ErlangExtractFunction("n")<ENTER> -vmap <A-r>e :call ErlangExtractFunction("v")<ENTER> - -function! s:call_rename(mode, line, col, name, search_path) -    let file = expand("%:p") -    let module = 'wrangler' -    let fun = 'rename_' . a:mode -    let args = '["' . file .'", ' -    if a:mode != "mod" -         let args = args . a:line . ', ' . a:col . ', ' -    endif -    let args = args . '"' . a:name . '", ["' . a:search_path . '"], ' . &sw . ']' -    let result = s:send_rpc(module, fun, args) -    let [error_code, msg] = s:check_for_error(result) -    if error_code != 0 -        call confirm(msg) -        return 0 -    endif -    echo "This files will be changed: " . matchstr(msg, "[^]]*", 1) -    echo s:send_confirm() -    return 1 -endfunction - -function! ErlangRename(mode) -    silent w! -    if a:mode == "mod" -        let name = inputdialog('Rename module to: ') -    else -        let name = inputdialog('Rename "' . expand("<cword>") . '" to: ') -    endif -    if name != "" -        let search_path = expand("%:p:h") -        "let search_path = inputdialog('Search path: ', expand("%:p:h")) -        let pos = getpos(".") -        let line = pos[1] -        let col = pos[2] -        let current_filename = expand("%") -        let current_filepath = expand("%:p") -        let new_filename = name . '.erl' -        if s:call_rename(a:mode, line, col, name, search_path) -            if a:mode == "mod" -                execute ':bd ' . current_filename -                execute ':e ' . new_filename -                silent execute '!mv ' . current_filepath . ' ' . current_filepath . '.bak' -                redraw! -            else -                let temp = &autoread -                set autoread -                :e -                if temp == 0 -                    set noautoread -                endif -            endif -        endif -    else -        echo "Empty name. Ignoring." -    endif -endfunction - -function! ErlangRenameFunction() -    call ErlangRename("fun") -endfunction -map <A-r>f :call ErlangRenameFunction()<ENTER> - -function! ErlangRenameVariable() -    call ErlangRename("var") -endfunction -map <A-r>v :call ErlangRenameVariable()<ENTER> - -function! ErlangRenameModule() -    call ErlangRename("mod") -endfunction -map <A-r>m :call ErlangRenameModule()<ENTER> - -function! ErlangRenameProcess() -    call ErlangRename("process") -endfunction -map <A-r>p :call ErlangRenameProcess()<ENTER> - -function! s:call_tuple_fun_args(start_line, start_col, end_line, end_col, search_path) -    let file = expand("%:p") -    let module = 'wrangler' -    let fun = 'tuple_funpar' -    let args = '["' . file . '", {' . a:start_line . ', ' . a:start_col . '}, {' . a:end_line . ', ' . a:end_col . '}, ["' . a:search_path . '"], ' . &sw . ']' -    let result = s:send_rpc(module, fun, args) -    if s:check_for_error(result) -        return 0 -    endif -    call s:send_confirm() -    return 1 -endfunction - -function! ErlangTupleFunArgs(mode) -    silent w! -    let search_path = expand("%:p:h") -    "let search_path = inputdialog('Search path: ', expand("%:p:h")) -    if a:mode == "v" -        let start_pos = getpos("'<") -        let start_line = start_pos[1] -        let start_col = start_pos[2] - -        let end_pos = getpos("'>") -        let end_line = end_pos[1] -        let end_col = end_pos[2] -        if s:call_tuple_fun_args(start_line, start_col, end_line, end_col, search_path) -            let temp = &autoread -            set autoread -            :e -            if temp == 0 -                set noautoread -            endif -        endif -    elseif a:mode == "n" -        let pos = getpos(".") -        let line = pos[1] -        let col = pos[2] -        if s:call_tuple_fun_args(line, col, line, col, search_path) -            let temp = &autoread -            set autoread -            :e -            if temp == 0 -                set noautoread -            endif -        endif -    else -        echo "Mode not supported." -    endif -endfunction -nmap <A-r>t :call ErlangTupleFunArgs("n")<ENTER> -vmap <A-r>t :call ErlangTupleFunArgs("v")<ENTER> - -" vim: set foldmethod=marker: diff --git a/indent/erlang.vim b/indent/erlang.vim index 61833f74..6bcec851 100644 --- a/indent/erlang.vim +++ b/indent/erlang.vim @@ -1,207 +1,1481 @@  " Vim indent file -" Language:     Erlang -" Maintainer:   Csaba Hoch <csaba.hoch@gmail.com> -" Contributor:  Edwin Fine <efine145_nospam01 at usa dot net> -" Contributor:  Pawel 'kTT' Salata <rockplayer.pl@gmail.com> -" Last Change:  2010 Aug 30 - -" Only load this indent file when no other was loaded. -if exists("b:did_indent") +" Language:     Erlang (http://www.erlang.org) +" Author:       Csaba Hoch <csaba.hoch@gmail.com> +" Contributors: Edwin Fine <efine145_nospam01 at usa dot net> +"               Pawel 'kTT' Salata <rockplayer.pl@gmail.com> +"               Ricardo Catalinas Jiménez <jimenezrick@gmail.com> +" Last Update:  2013-Jul-21 +" License:      Vim license +" URL:          https://github.com/hcs42/vim-erlang + +" Note About Usage: +"   This indentation script works best with the Erlang syntax file created by +"   KreÄ…imir Marľić (Kresimir Marzic) and maintained by Csaba Hoch. + +" Notes About Implementation: +" +" - LTI = Line to indent. +" - The index of the first line is 1, but the index of the first column is 0. + + +" Initialization {{{1 +" ============== + +" Only load this indent file when no other was loaded +" Vim 7 or later is needed +if exists("b:did_indent") || version < 700    finish +else +  let b:did_indent = 1  endif -let b:did_indent = 1  setlocal indentexpr=ErlangIndent() -setlocal indentkeys+==after,=end,=catch,=),=],=} +setlocal indentkeys+=0=end,0=of,0=catch,0=after,0=when,0=),0=],0=},0=>> -" Only define the functions once. +" Only define the functions once  if exists("*ErlangIndent") -   finish +  finish  endif -" The function go through the whole line, analyses it and sets the indentation -" (ind variable). -" l: the number of the line to be examined. -function s:ErlangIndentAfterLine(l) -    let i = 0 " the index of the current character in the line -    let length = strlen(a:l) " the length of the line -    let ind = 0 " how much should be the difference between the indentation of -                " the current line and the indentation of the next line? -                " e.g. +1: the indentation of the next line should be equal to -                " the indentation of the current line plus one shiftwidth -    let lastFun = 0 " the last token was a 'fun' -    let lastReceive = 0 " the last token was a 'receive'; needed for 'after' -    let lastHashMark = 0 " the last token was a 'hashmark' - -    " ignore type annotation lines -    if a:l =~# '^\s*-type' -	return 0 -    endif - -    while 0<= i && i < length - -        " m: the next value of the i -        if a:l[i] == '%' -            break -        elseif a:l[i] == '"' -            let m = matchend(a:l,'"\%([^"\\]\|\\.\)*"',i) -            let lastReceive = 0 -        elseif a:l[i] == "'" -            let m = matchend(a:l,"'[^']*'",i) -            let lastReceive = 0 -        elseif a:l[i] =~# "[a-z]" -            let m = matchend(a:l,".[[:alnum:]_]*",i) -            if lastFun -                let ind = ind - 1 -                let lastFun = 0 -                let lastReceive = 0 -            elseif a:l[(i):(m-1)] =~# '^\%(case\|if\|try\)$' -                let ind = ind + 1 -            elseif a:l[(i):(m-1)] =~# '^receive$' -                let ind = ind + 1 -                let lastReceive = 1 -            elseif a:l[(i):(m-1)] =~# '^begin$' -                let ind = ind + 2 -                let lastReceive = 0 -            elseif a:l[(i):(m-1)] =~# '^end$' -                let ind = ind - 2 -                let lastReceive = 0 -            elseif a:l[(i):(m-1)] =~# '^after$' -                if lastReceive == 0 -                    let ind = ind - 1 -                else -                    let ind = ind + 0 -                endif -                let lastReceive = 0 -            elseif a:l[(i):(m-1)] =~# '^fun$' -                let ind = ind + 1 -                let lastFun = 1 -                let lastReceive = 0 -            endif -        elseif a:l[i] =~# "[A-Z_]" -            let m = matchend(a:l,".[[:alnum:]_]*",i) -            let lastReceive = 0 -        elseif a:l[i] == '$' -            let m = i+2 -            let lastReceive = 0 -        elseif a:l[i] == "." && (i+1>=length || a:l[i+1]!~ "[0-9]") -            let m = i+1 -            if lastHashMark -                let lastHashMark = 0 -            else -                let ind = ind - 1 -            endif -            let lastReceive = 0 -        elseif a:l[i] == '-' && (i+1<length && a:l[i+1]=='>') -            let m = i+2 -            let ind = ind + 1 -            let lastReceive = 0 -        elseif a:l[i] == ';' && a:l[(i):(length)] !~# '.*->.*' -            let m = i+1 -            let ind = ind - 1 -            let lastReceive = 0 -        elseif a:l[i] == '#' -            let m = i+1 -            let lastHashMark = 1 -        elseif a:l[i] =~# '[({[]' -            let m = i+1 -            let ind = ind + 1 -            let lastFun = 0 -            let lastReceive = 0 -            let lastHashMark = 0 -        elseif a:l[i] =~# '[)}\]]' -            let m = i+1 -            let ind = ind - 1 -            let lastReceive = 0 -        else -            let m = i+1 -        endif +let s:cpo_save = &cpo +set cpo&vim + +" Logging library {{{1 +" =============== + +" Purpose: +"   Logs the given string using the ErlangIndentLog function if it exists. +" Parameters: +"   s: string +function! s:Log(s) +  if exists("*ErlangIndentLog") +    call ErlangIndentLog(a:s) +  endif +endfunction + +" Line tokenizer library {{{1 +" ====================== + +" Indtokens are "indentation tokens". See their exact format in the +" documentaiton of the s:GetTokensFromLine function. + +" Purpose: +"   Calculate the new virtual column after the given segment of a line. +" Parameters: +"   line: string +"   first_index: integer -- the index of the first character of the segment +"   last_index: integer -- the index of the last character of the segment +"   vcol: integer -- the virtual column of the first character of the token +"   tabstop: integer -- the value of the 'tabstop' option to be used +" Returns: +"   vcol: integer +" Example: +"   " index:    0 12 34567 +"   " vcol:     0 45 89 +"   s:CalcVCol("\t'\tx', b", 1, 4, 4)  -> 10 +function! s:CalcVCol(line, first_index, last_index, vcol, tabstop) + +  " We copy the relevent segment of the line, otherwise if the line were +  " e.g. `"\t", term` then the else branch below would consume the `", term` +  " part at once. +  let line = a:line[a:first_index : a:last_index] + +  let i = 0 +  let last_index = a:last_index - a:first_index +  let vcol = a:vcol + +  while 0 <= i && i <= last_index + +    if line[i] ==# "\t" +      " Example (when tabstop == 4): +      " +      " vcol + tab -> next_vcol +      " 0 + tab -> 4 +      " 1 + tab -> 4 +      " 2 + tab -> 4 +      " 3 + tab -> 4 +      " 4 + tab -> 8 +      " +      " next_i - i == the number of tabs +      let next_i = matchend(line, '\t*', i + 1) +      let vcol = (vcol / a:tabstop + (next_i - i)) * a:tabstop +      call s:Log('new vcol after tab: '. vcol) +    else +      let next_i = matchend(line, '[^\t]*', i + 1) +      let vcol += next_i - i +      call s:Log('new vcol after other: '. vcol) +    endif +    let i = next_i +  endwhile + +  return vcol +endfunction + +" Purpose: +"   Go through the whole line and return the tokens in the line. +" Parameters: +"   line: string -- the line to be examined +"   string_continuation: bool +"   atom_continuation: bool +" Returns: +"   indtokens = [indtoken] +"   indtoken = [token, vcol, col] +"   token = string (examples: 'begin', '<quoted_atom>', '}') +"   vcol = integer (the virtual column of the first character of the token; +"                   counting starts from 0) +"   col = integer (counting starts from 0) +function! s:GetTokensFromLine(line, string_continuation, atom_continuation, +                             \tabstop) + +  let linelen = strlen(a:line) " The length of the line +  let i = 0 " The index of the current character in the line +  let vcol = 0 " The virtual column of the current character +  let indtokens = [] + +  if a:string_continuation +    let i = matchend(a:line, '^\%([^"\\]\|\\.\)*"', 0) +    if i ==# -1 +      call s:Log('    Whole line is string continuation -> ignore') +      return [] +    else +      let vcol = s:CalcVCol(a:line, 0, i - 1, 0, a:tabstop) +      call add(indtokens, ['<string_end>', vcol, i]) +    endif +  elseif a:atom_continuation +    let i = matchend(a:line, "^\\%([^'\\\\]\\|\\\\.\\)*'", 0) +    if i ==# -1 +      call s:Log('    Whole line is quoted atom continuation -> ignore') +      return [] +    else +      let vcol = s:CalcVCol(a:line, 0, i - 1, 0, a:tabstop) +      call add(indtokens, ['<quoted_atom_end>', vcol, i]) +    endif +  endif + +  while 0 <= i && i < linelen + +    let next_vcol = '' -        let i = m +    " Spaces +    if a:line[i] ==# ' ' +      let next_i = matchend(a:line, ' *', i + 1) -    endwhile +    " Tabs +    elseif a:line[i] ==# "\t" +      let next_i = matchend(a:line, '\t*', i + 1) -    return ind +      " See example in s:CalcVCol +      let next_vcol = (vcol / a:tabstop + (next_i - i)) * a:tabstop + +    " Comment +    elseif a:line[i] ==# '%' +      let next_i = linelen + +    " String token: "..." +    elseif a:line[i] ==# '"' +      let next_i = matchend(a:line, '\%([^"\\]\|\\.\)*"', i + 1) +      if next_i ==# -1 +        call add(indtokens, ['<string_start>', vcol, i]) +      else +        let next_vcol = s:CalcVCol(a:line, i, next_i - 1, vcol, a:tabstop) +        call add(indtokens, ['<string>', vcol, i]) +      endif + +    " Quoted atom token: '...' +    elseif a:line[i] ==# "'" +      let next_i = matchend(a:line, "\\%([^'\\\\]\\|\\\\.\\)*'", i + 1) +      if next_i ==# -1 +        call add(indtokens, ['<quoted_atom_start>', vcol, i]) +      else +        let next_vcol = s:CalcVCol(a:line, i, next_i - 1, vcol, a:tabstop) +        call add(indtokens, ['<quoted_atom>', vcol, i]) +      endif + +    " Keyword or atom or variable token or number +    elseif a:line[i] =~# '[a-zA-Z_@0-9]' +      let next_i = matchend(a:line, +                           \'[[:alnum:]_@:]*\%(\s*#\s*[[:alnum:]_@:]*\)\=', +                           \i + 1) +      call add(indtokens, [a:line[(i):(next_i - 1)], vcol, i]) + +    " Character token: $<char> (as in: $a) +    elseif a:line[i] ==# '$' +      call add(indtokens, ['$.', vcol, i]) +      let next_i = i + 2 + +    " Dot token: . +    elseif a:line[i] ==# '.' + +      let next_i = i + 1 + +      if i + 1 ==# linelen || a:line[i + 1] =~# '[[:blank:]%]' +        " End of clause token: . (as in: f() -> ok.) +        call add(indtokens, ['<end_of_clause>', vcol, i]) + +      else +        " Possibilities: +        " - Dot token in float: . (as in: 3.14) +        " - Dot token in record: . (as in: #myrec.myfield) +        call add(indtokens, ['.', vcol, i]) +      endif + +    " Equal sign +    elseif a:line[i] ==# '=' +      " This is handled separately so that "=<<" will be parsed as +      " ['=', '<<'] instead of ['=<', '<']. Although Erlang parses it +      " currently in the latter way, that may be fixed some day. +      call add(indtokens, [a:line[i], vcol, i]) +      let next_i = i + 1 + +    " Three-character tokens +    elseif i + 1 < linelen && +         \ index(['=:=', '=/='], a:line[i : i + 1]) != -1 +      call add(indtokens, [a:line[i : i + 1], vcol, i]) +      let next_i = i + 2 + +    " Two-character tokens +    elseif i + 1 < linelen && +         \ index(['->', '<<', '>>', '||', '==', '/=', '=<', '>=', '++', '--', +         \        '::'], +         \       a:line[i : i + 1]) != -1 +      call add(indtokens, [a:line[i : i + 1], vcol, i]) +      let next_i = i + 2 + +    " Other character: , ; < > ( ) [ ] { } # + - * / : ? = ! | +    else +      call add(indtokens, [a:line[i], vcol, i]) +      let next_i = i + 1 + +    endif + +    if next_vcol ==# '' +      let vcol += next_i - i +    else +      let vcol = next_vcol +    endif + +    let i = next_i + +  endwhile + +  return indtokens  endfunction -function s:FindPrevNonBlankNonComment(lnum) +" TODO: doc, handle "not found" case +function! s:GetIndtokenAtCol(indtokens, col) +  let i = 0 +  while i < len(a:indtokens) +    if a:indtokens[i][2] ==# a:col +      return [1, i] +    elseif a:indtokens[i][2] > a:col +      return [0, s:IndentError('No token at col ' . a:col . ', ' . +                              \'indtokens = ' . string(a:indtokens), +                              \'', '')] +    endif +    let i += 1 +  endwhile +  return [0, s:IndentError('No token at col ' . a:col . ', ' . +                           \'indtokens = ' . string(a:indtokens), +                           \'', '')] +endfunction + +" Stack library {{{1 +" ============= + +" Purpose: +"   Push a token onto the parser's stack. +" Parameters: +"   stack: [token] +"   token: string +function! s:Push(stack, token) +  call s:Log('    Stack Push: "' . a:token . '" into ' . string(a:stack)) +  call insert(a:stack, a:token) +endfunction + +" Purpose: +"   Pop a token from the parser's stack. +" Parameters: +"   stack: [token] +"   token: string +" Returns: +"   token: string -- the removed element +function! s:Pop(stack) +  let head = remove(a:stack, 0) +  call s:Log('    Stack Pop: "' . head . '" from ' . string(a:stack)) +  return head +endfunction + +" Library for accessing and storing tokenized lines {{{1 +" ================================================= + +" The Erlang token cache: an `lnum -> indtokens` dictionary that stores the +" tokenized lines. +let s:all_tokens = {} +let s:file_name = '' +let s:last_changedtick = -1 + +" Purpose: +"   Clear the Erlang token cache if we have a different file or the file has +"   been changed since the last indentation. +function! s:ClearTokenCacheIfNeeded() +  let file_name = expand('%:p') +  if file_name != s:file_name || +   \ b:changedtick != s:last_changedtick +    let s:file_name = file_name +    let s:last_changedtick = b:changedtick +    let s:all_tokens = {} +  endif +endfunction + +" Purpose: +"   Return the tokens of line `lnum`, if that line is not empty. If it is +"   empty, find the first non-empty line in the given `direction` and return +"   the tokens of that line. +" Parameters: +"   lnum: integer +"   direction: 'up' | 'down' +" Returns: +"   result: [] -- the result is an empty list if we hit the beginning or end +"                  of the file +"           | [lnum, indtokens] +"   lnum: integer -- the index of the non-empty line that was found and +"                    tokenized +"   indtokens: [indtoken] -- the tokens of line `lnum` +function! s:TokenizeLine(lnum, direction) + +  call s:Log('Tokenizing starts from line ' . a:lnum) +  if a:direction ==# 'up'      let lnum = prevnonblank(a:lnum) +  else " a:direction ==# 'down' +    let lnum = nextnonblank(a:lnum) +  endif + +  " We hit the beginning or end of the file +  if lnum ==# 0 +    let indtokens = [] +    call s:Log('  We hit the beginning or end of the file.') + +    " The line has already been parsed +  elseif has_key(s:all_tokens, lnum) +    let indtokens = s:all_tokens[lnum] +    call s:Log('Cached line ' . lnum . ': ' . getline(lnum)) +    call s:Log("  Tokens in the line:\n    - " . join(indtokens, "\n    - ")) + +    " The line should be parsed now +  else + +    " Parse the line      let line = getline(lnum) -    " continue to search above if the current line begins with a '%' -    while line =~# '^\s*%.*$' -        let lnum = prevnonblank(lnum - 1) -        if 0 == lnum -            return 0 -        endif -        let line = getline(lnum) -    endwhile -    return lnum +    let string_continuation = s:IsLineStringContinuation(lnum) +    let atom_continuation = s:IsLineAtomContinuation(lnum) +    let indtokens = s:GetTokensFromLine(line, string_continuation, +                                       \atom_continuation, &tabstop) +    let s:all_tokens[lnum] = indtokens +    call s:Log('Tokenizing line ' . lnum . ': ' . line) +    call s:Log("  Tokens in the line:\n    - " . join(indtokens, "\n    - ")) + +  endif + +  return [lnum, indtokens]  endfunction -function ErlangIndent() +" Purpose: +"   As a helper function for PrevIndToken and NextIndToken, the FindIndToken +"   function finds the first line with at least one token in the given +"   direction. +" Parameters: +"   lnum: integer +"   direction: 'up' | 'down' +" Returns: +"   result: [[], 0, 0] +"             -- the result is an empty list if we hit the beginning or end of +"             the file +"           | [indtoken, lnum, i] +"             -- the content, lnum and token index of the next (or previous) +"             indtoken +function! s:FindIndToken(lnum, dir) +  let lnum = a:lnum +  while 1 +    let lnum += (a:dir ==# 'up' ? -1 : 1) +    let [lnum, indtokens] = s:TokenizeLine(lnum, a:dir) +    if lnum ==# 0 +      " We hit the beginning or end of the file +      return [[], 0, 0] +    elseif !empty(indtokens) +      " We found a non-empty line. If we were moving up, we return the last +      " token of this line. Otherwise we return the first token if this line. +      let i = (a:dir ==# 'up' ? len(indtokens) - 1 : 0) +      return [indtokens[i], lnum, i] +    endif +  endwhile +endfunction -    " Find a non-blank line above the current line. -    let lnum = prevnonblank(v:lnum - 1) +" Purpose: +"   Find the token that directly precedes the given token. +" Parameters: +"   lnum: integer -- the line of the given token +"   i: the index of the given token within line `lnum` +" Returns: +"   result = [] -- the result is an empty list if the given token is the first +"                  token of the file +"          | indtoken +function! s:PrevIndToken(lnum, i) +  call s:Log('    PrevIndToken called: lnum=' . a:lnum . ', i =' . a:i) + +  " If the current line has a previous token, return that +  if a:i > 0 +    return [s:all_tokens[a:lnum][a:i - 1], a:lnum, a:i - 1] +  else +    return s:FindIndToken(a:lnum, 'up') +  endif +endfunction + +" Purpose: +"   Find the token that directly succeeds the given token. +" Parameters: +"   lnum: integer -- the line of the given token +"   i: the index of the given token within line `lnum` +" Returns: +"   result = [] -- the result is an empty list if the given token is the last +"                  token of the file +"          | indtoken +function! s:NextIndToken(lnum, i) +  call s:Log('    NextIndToken called: lnum=' . a:lnum . ', i =' . a:i) + +  " If the current line has a next token, return that +  if len(s:all_tokens[a:lnum]) > a:i + 1 +    return [s:all_tokens[a:lnum][a:i + 1], a:lnum, a:i + 1] +  else +    return s:FindIndToken(a:lnum, 'down') +  endif +endfunction + +" ErlangCalcIndent helper functions {{{1 +" ================================= + +" Purpose: +"   This function is called when the parser encounters a syntax error. +" +"   If we encounter a syntax error, we return +"   g:erlang_unexpected_token_indent, which is -1 by default. This means that +"   the indentation of the LTI will not be changed. +" Parameter: +"   msg: string +"   token: string +"   stack: [token] +" Returns: +"   indent: integer +function! s:IndentError(msg, token, stack) +  call s:Log('Indent error: ' . a:msg . ' -> return') +  call s:Log('  Token = ' . a:token . ', ' . +            \'  stack = ' . string(a:stack)) +  return g:erlang_unexpected_token_indent +endfunction + +" Purpose: +"   This function is called when the parser encounters an unexpected token, +"   and the parser will return the number given back by UnexpectedToken. +" +"   If we encounter an unexpected token, we return +"   g:erlang_unexpected_token_indent, which is -1 by default. This means that +"   the indentation of the LTI will not be changed. +" Parameter: +"   token: string +"   stack: [token] +" Returns: +"   indent: integer +function! s:UnexpectedToken(token, stack) +  call s:Log('    Unexpected token ' . a:token . ', stack = ' . +            \string(a:stack) . ' -> return') +  return g:erlang_unexpected_token_indent +endfunction + +if !exists('g:erlang_unexpected_token_indent') +  let g:erlang_unexpected_token_indent = -1 +endif + +" Purpose: +"   Return whether the given line starts with a string continuation. +" Parameter: +"   lnum: integer +" Returns: +"   result: bool +" Example: +"   f() ->           % IsLineStringContinuation = false +"       "This is a   % IsLineStringContinuation = false +"       multiline    % IsLineStringContinuation = true +"       string".     % IsLineStringContinuation = true +function! s:IsLineStringContinuation(lnum) +  if has('syntax_items') +    return synIDattr(synID(a:lnum, 1, 0), 'name') =~# '^erlangString' +  else +    return 0 +  endif +endfunction + +" Purpose: +"   Return whether the given line starts with an atom continuation. +" Parameter: +"   lnum: integer +" Returns: +"   result: bool +" Example: +"   'function with   % IsLineAtomContinuation = true, but should be false +"   weird name'() -> % IsLineAtomContinuation = true +"       ok.          % IsLineAtomContinuation = false +function! s:IsLineAtomContinuation(lnum) +  if has('syntax_items') +    return synIDattr(synID(a:lnum, 1, 0), 'name') =~# '^erlangQuotedAtom' +  else +    return 0 +  endif +endfunction + +" Purpose: +"   Return whether the 'catch' token (which should be the `i`th token in line +"   `lnum`) is standalone or part of a try-catch block, based on the preceding +"   token. +" Parameters: +"   lnum: integer +"   i: integer +" Return: +"   is_standalone: bool +function! s:IsCatchStandalone(lnum, i) +  call s:Log('    IsCatchStandalone called: lnum=' . a:lnum . ', i=' . a:i) +  let [prev_indtoken, _, _] = s:PrevIndToken(a:lnum, a:i) -    " Hit the start of the file, use zero indent. -    if lnum == 0 -        return 0 +  " If we hit the beginning of the file, it is not a catch in a try block +  if prev_indtoken == [] +    return 1 +  endif + +  let prev_token = prev_indtoken[0] + +  if prev_token =~# '^[A-Z_@0-9]' +    let is_standalone = 0 +  elseif prev_token =~# '[a-z]' +    if index(['after', 'and', 'andalso', 'band', 'begin', 'bnot', 'bor', 'bsl', +            \ 'bsr', 'bxor', 'case', 'catch', 'div', 'not', 'or', 'orelse', +            \ 'rem', 'try', 'xor'], prev_token) != -1 +      " If catch is after these keywords, it is standalone +      let is_standalone = 1 +    else +      " If catch is after another keyword (e.g. 'end') or an atom, it is +      " part of try-catch. +      " +      " Keywords: +      " - may precede 'catch': end +      " - may not precede 'catch': fun if of receive when +      " - unused: cond let query +      let is_standalone = 0      endif +  elseif index([')', ']', '}', '<string>', '<string_end>', '<quoted_atom>', +              \ '<quoted_atom_end>', '$.'], prev_token) != -1 +    let is_standalone = 0 +  else +    " This 'else' branch includes the following tokens: +    "   -> == /= =< < >= > =:= =/= + - * / ++ -- :: < > ; ( [ { ? = ! . | +    let is_standalone = 1 +  endif -    let prevline = getline(lnum) -    let currline = getline(v:lnum) +  call s:Log('   "catch" preceded by "' . prev_token  . '" -> catch ' . +            \(is_standalone ? 'is standalone' : 'belongs to try-catch')) +  return is_standalone -    let ind = indent(lnum) + &sw * s:ErlangIndentAfterLine(prevline) +endfunction -    " special cases: -    if prevline =~# '^\s*\%(after\|end\)\>' -        let ind = ind + 2*&sw +" Purpose: +"   This function is called when a begin-type element ('begin', 'case', +"   '[', '<<', etc.) is found. It asks the caller to return if the stack +" Parameters: +"   stack: [token] +"   token: string +"   curr_vcol: integer +"   stored_vcol: integer +"   sw: integer -- number of spaces to be used after the begin element as +"                  indentation +" Returns: +"   result: [should_return, indent] +"   should_return: bool -- if true, the caller should return `indent` to Vim +"   indent -- integer +function! s:BeginElementFoundIfEmpty(stack, token, curr_vcol, stored_vcol, sw) +  if empty(a:stack) +    if a:stored_vcol ==# -1 +      call s:Log('    "' . a:token . '" directly preceeds LTI -> return') +      return [1, a:curr_vcol + a:sw] +    else +      call s:Log('    "' . a:token . +                \'" token (whose expression includes LTI) found -> return') +      return [1, a:stored_vcol]      endif -    if currline =~# '^\s*end\>' -        let ind = ind - 2*&sw +  else +    return [0, 0] +  endif +endfunction + +" Purpose: +"   This function is called when a begin-type element ('begin', 'case', '[', +"   '<<', etc.) is found, and in some cases when 'after' and 'when' is found. +"   It asks the caller to return if the stack is already empty. +" Parameters: +"   stack: [token] +"   token: string +"   curr_vcol: integer +"   stored_vcol: integer +"   end_token: end token that belongs to the begin element found (e.g. if the +"              begin element is 'begin', the end token is 'end') +"   sw: integer -- number of spaces to be used after the begin element as +"                  indentation +" Returns: +"   result: [should_return, indent] +"   should_return: bool -- if true, the caller should return `indent` to Vim +"   indent -- integer +function! s:BeginElementFound(stack, token, curr_vcol, stored_vcol, end_token, sw) + +  " Return 'return' if the stack is empty +  let [ret, res] = s:BeginElementFoundIfEmpty(a:stack, a:token, a:curr_vcol, +                                             \a:stored_vcol, a:sw) +  if ret | return [ret, res] | endif + +  if a:stack[0] ==# a:end_token +    call s:Log('    "' . a:token . '" pops "' . a:end_token . '"') +    call s:Pop(a:stack) +    if !empty(a:stack) && a:stack[0] ==# 'align_to_begin_element' +      call s:Pop(a:stack) +      if empty(a:stack) +        return [1, a:curr_vcol] +      else +        return [1, s:UnexpectedToken(a:token, a:stack)] +      endif +    else +      return [0, 0]      endif -    if currline =~# '^\s*after\>' -        let plnum = s:FindPrevNonBlankNonComment(v:lnum-1) -        if getline(plnum) =~# '^[^%]*\<receive\>\s*\%(%.*\)\=$' -            let ind = ind - 1*&sw -            " If the 'receive' is not in the same line as the 'after' -        else -            let ind = ind - 2*&sw +  else +    return [1, s:UnexpectedToken(a:token, a:stack)] +  endif +endfunction + +" Purpose: +"   This function is called when we hit the beginning of a file or an +"   end-of-clause token -- i.e. when we found the beginning of the current +"   clause. +" +"   If the stack contains an '->' or 'when', this means that we can return +"   now, since we were looking for the beginning of the clause. +" Parameters: +"   stack: [token] +"   token: string +"   stored_vcol: integer +"   lnum: the line number of the "end of clause" mark (or 0 if we hit the +"         beginning of the file) +"   i: the index of the "end of clause" token within its own line +" Returns: +"   result: [should_return, indent] +"   should_return: bool -- if true, the caller should return `indent` to Vim +"   indent -- integer +function! s:BeginningOfClauseFound(stack, token, stored_vcol, lnum, i) +  if !empty(a:stack) && a:stack[0] ==# 'when' +    call s:Log('    BeginningOfClauseFound: "when" found in stack') +    call s:Pop(a:stack) +    if empty(a:stack) +      call s:Log('    Stack is ["when"], so LTI is in a guard -> return') +      return [1, a:stored_vcol + &sw + 2] +    else +      return [1, s:UnexpectedToken(a:token, a:stack)] +    endif +  elseif !empty(a:stack) && a:stack[0] ==# '->' +    call s:Log('    BeginningOfClauseFound: "->" found in stack') +    call s:Pop(a:stack) +    if empty(a:stack) +      call s:Log('    Stack is ["->"], so LTI is in function body -> return') +      return [1, a:stored_vcol + &sw] +    elseif a:stack[0] ==# ';' +      call s:Pop(a:stack) + +      if !empty(a:stack) +        return [1, s:UnexpectedToken(a:token, a:stack)] +      endif + +      if a:lnum ==# 0 +        " Set lnum and i to be NextIndToken-friendly +        let lnum = 1 +        let i = -1  +      else +        let lnum = a:lnum +        let i = a:i +      endif + +      " Are we after a "-spec func() ...;" clause? +      let [next1_indtoken, next1_lnum, next1_i] = s:NextIndToken(lnum, i) +      if !empty(next1_indtoken) && next1_indtoken[0] =~# '-' +        let [next2_indtoken, next2_lnum, next2_i] = +           \s:NextIndToken(next1_lnum, next1_i) +        if !empty(next2_indtoken) && next2_indtoken[0] =~# 'spec' +          let [next3_indtoken, next3_lnum, next3_i] = +             \s:NextIndToken(next2_lnum, next2_i) +          if !empty(next3_indtoken) +            let [next4_indtoken, next4_lnum, next4_i] = +               \s:NextIndToken(next3_lnum, next3_i) +            if !empty(next4_indtoken) +              " Yes, we are. +              call s:Log('    Stack is ["->", ";"], so LTI is in a "-spec" ' . +                        \'attribute -> return') +              return [1, next4_indtoken[1]] +            endif +          endif          endif +      endif + +      call s:Log('    Stack is ["->", ";"], so LTI is in a function head ' . +                \'-> return') +      return [1, a:stored_vcol] + +    else +      return [1, s:UnexpectedToken(a:token, a:stack)]      endif -    if prevline =~# '^\s*[)}\]]' -        let ind = ind + 1*&sw +  else +    return [0, 0] +  endif +endfunction + +let g:erlang_indent_searchpair_timeout = 2000 + +" TODO +function! s:SearchPair(lnum, curr_col, start, middle, end) +  call cursor(a:lnum, a:curr_col + 1) +  let [lnum_new, col1_new] =  +      \searchpairpos(a:start, a:middle, a:end, 'bW', +                    \'synIDattr(synID(line("."), col("."), 0), "name") ' . +                    \'=~? "string\\|quotedatom\\|todo\\|comment\\|' .  +                    \'erlangmodifier"', +                    \0, g:erlang_indent_searchpair_timeout) +  return [lnum_new, col1_new - 1] +endfunction + +function! s:SearchEndPair(lnum, curr_col) +  return s:SearchPair( +         \ a:lnum, a:curr_col, +         \ '\C\<\%(case\|try\|begin\|receive\|if\)\>\|' . +         \ '\<fun\>\%(\s\|\n\|%.*$\|[A-Z_@][a-zA-Z_@]*\)*(', +         \ '', +         \ '\<end\>') +endfunction + +" ErlangCalcIndent {{{1 +" ================ + +" Purpose: +"   Calculate the indentation of the given line. +" Parameters: +"   lnum: integer -- index of the line for which the indentation should be +"                    calculated +"   stack: [token] -- initial stack +" Return: +"   indent: integer -- if -1, that means "don't change the indentation"; +"                      otherwise it means "indent the line with `indent` +"                      number of spaces or equivalent tabs" +function! s:ErlangCalcIndent(lnum, stack) +  let res = s:ErlangCalcIndent2(a:lnum, a:stack) +  call s:Log("ErlangCalcIndent returned: " . res) +  return res +endfunction + +function! s:ErlangCalcIndent2(lnum, stack) + +  let lnum = a:lnum +  let stored_vcol = -1 " Virtual column of the first character of the token that +                   " we currently think we might align to. +  let mode = 'normal' +  let stack = a:stack +  let semicolon_abscol = '' + +  " Walk through the lines of the buffer backwards (starting from the +  " previous line) until we can decide how to indent the current line. +  while 1 + +    let [lnum, indtokens] = s:TokenizeLine(lnum, 'up') + +    " Hit the start of the file +    if lnum ==# 0 +      let [ret, res] = s:BeginningOfClauseFound(stack, 'beginning_of_file', +                                               \stored_vcol, 0, 0) +      if ret | return res | endif + +      return 0      endif -    if currline =~# '^\s*[)}\]]' -        let ind = ind - 1*&sw + +    let i = len(indtokens) - 1 +    let last_token_of_line = 1 + +    while i >= 0 + +      let [token, curr_vcol, curr_col] = indtokens[i] +      call s:Log('  Analyzing the following token: ' . string(indtokens[i])) + +      if len(stack) > 256 " TODO: magic number +        return s:IndentError('Stack too long', token, stack) +      endif + +      if token ==# '<end_of_clause>' +        let [ret, res] = s:BeginningOfClauseFound(stack, token, stored_vcol, +                                                 \lnum, i) +        if ret | return res | endif + +        if stored_vcol ==# -1 +          call s:Log('    End of clause directly preceeds LTI -> return') +          return 0 +        else +          call s:Log('    End of clause (but not end of line) -> return') +          return stored_vcol +        endif + +      elseif stack == ['prev_term_plus'] +        if token =~# '[a-zA-Z_@]' || +         \ token ==# '<string>' || token ==# '<string_start>' || +         \ token ==# '<quoted_atom>' || token ==# '<quoted_atom_start>' +          call s:Log('    previous token found: curr_vcol + plus = ' . +                    \curr_vcol . " + " . plus) +          return curr_vcol + plus +        endif + +      elseif token ==# 'begin' +        let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, +                                            \stored_vcol, 'end', &sw) +        if ret | return res | endif + +      " case EXPR of BRANCHES end +      " try EXPR catch BRANCHES end +      " try EXPR after BODY end +      " try EXPR catch BRANCHES after BODY end +      " try EXPR of BRANCHES catch BRANCHES end +      " try EXPR of BRANCHES after BODY end +      " try EXPR of BRANCHES catch BRANCHES after BODY end +      " receive BRANCHES end +      " receive BRANCHES after BRANCHES end + +      " This branch is not Emacs-compatible +      elseif (index(['of', 'receive', 'after', 'if'], token) != -1 || +           \  (token ==# 'catch' && !s:IsCatchStandalone(lnum, i))) && +           \ !last_token_of_line && +           \ (empty(stack) || stack ==# ['when'] || stack ==# ['->'] || +           \  stack ==# ['->', ';']) + +        " If we are after of/receive, but these are not the last +        " tokens of the line, we want to indent like this: +        " +        "   % stack == [] +        "   receive stored_vcol, +        "           LTI +        " +        "   % stack == ['->', ';'] +        "   receive stored_vcol -> +        "               B; +        "           LTI +        " +        "   % stack == ['->'] +        "   receive stored_vcol -> +        "               LTI +        " +        "   % stack == ['when'] +        "   receive stored_vcol when +        "               LTI + +        " stack = []  =>  LTI is a condition +        " stack = ['->']  =>  LTI is a branch +        " stack = ['->', ';']  =>  LTI is a condition +        " stack = ['when']  =>  LTI is a guard +        if empty(stack) || stack == ['->', ';'] +          call s:Log('    LTI is in a condition after ' . +                    \'"of/receive/after/if/catch" -> return') +          return stored_vcol +        elseif stack == ['->'] +          call s:Log('    LTI is in a branch after ' . +                    \'"of/receive/after/if/catch" -> return') +          return stored_vcol + &sw +        elseif stack == ['when'] +          call s:Log('    LTI is in a guard after ' . +                    \'"of/receive/after/if/catch" -> return') +          return stored_vcol + &sw +        else +          return s:UnexpectedToken(token, stack) +        endif + +      elseif index(['case', 'if', 'try', 'receive'], token) != -1 + +        " stack = []  =>  LTI is a condition +        " stack = ['->']  =>  LTI is a branch +        " stack = ['->', ';']  =>  LTI is a condition +        " stack = ['when']  =>  LTI is in a guard +        if empty(stack) +          " pass +        elseif (token ==# 'case' && stack[0] ==# 'of') || +             \ (token ==# 'if') || +             \ (token ==# 'try' && (stack[0] ==# 'of' || +             \                     stack[0] ==# 'catch' || +             \                     stack[0] ==# 'after')) || +             \ (token ==# 'receive') + +          " From the indentation point of view, the keyword +          " (of/catch/after/end) before the LTI is what counts, so +          " when we reached these tokens, and the stack already had +          " a catch/after/end, we didn't modify it. +          " +          " This way when we reach case/try/receive (i.e. now), +          " there is at most one of/catch/after/end token in the +          " stack. +          if token ==# 'case' || token ==# 'try' || +           \ (token ==# 'receive' && stack[0] ==# 'after') +            call s:Pop(stack) +          endif + +          if empty(stack) +            call s:Log('    LTI is in a condition; matching ' . +                      \'"case/if/try/receive" found') +            let stored_vcol = curr_vcol + &sw +          elseif stack[0] ==# 'align_to_begin_element' +            call s:Pop(stack) +            let stored_vcol = curr_vcol +          elseif len(stack) > 1 && stack[0] ==# '->' && stack[1] ==# ';' +            call s:Log('    LTI is in a condition; matching ' . +                      \'"case/if/try/receive" found') +            call s:Pop(stack) +            call s:Pop(stack) +            let stored_vcol = curr_vcol + &sw +          elseif stack[0] ==# '->' +            call s:Log('    LTI is in a branch; matching ' . +                      \'"case/if/try/receive" found') +            call s:Pop(stack) +            let stored_vcol = curr_vcol + 2 * &sw +          elseif stack[0] ==# 'when' +            call s:Log('    LTI is in a guard; matching ' . +                      \'"case/if/try/receive" found') +            call s:Pop(stack) +            let stored_vcol = curr_vcol + 2 * &sw + 2 +          endif + +        endif + +        let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, +                                            \stored_vcol, 'end', &sw) +        if ret | return res | endif + +      elseif token ==# 'fun' +        let [next_indtoken, next_lnum, next_i] = s:NextIndToken(lnum, i) +        call s:Log('    Next indtoken = ' . string(next_indtoken)) + +        if !empty(next_indtoken) && next_indtoken[0] =~# '^[A-Z_@]' +          " The "fun" is followed by a variable, so we might have a named fun: +          " "fun Fun() -> ok end". Thus we take the next token to decide +          " whether this is a function definition ("fun()") or just a function +          " reference ("fun Mod:Fun"). +          let [next_indtoken, _, _] = s:NextIndToken(next_lnum, next_i) +          call s:Log('    Next indtoken = ' . string(next_indtoken)) +        endif + +        if !empty(next_indtoken) && next_indtoken[0] ==# '(' +          " We have an anonymous function definition +          " (e.g. "fun () -> ok end") + +          " stack = []  =>  LTI is a condition +          " stack = ['->']  =>  LTI is a branch +          " stack = ['->', ';']  =>  LTI is a condition +          " stack = ['when']  =>  LTI is in a guard +          if empty(stack) +            call s:Log('    LTI is in a condition; matching "fun" found') +            let stored_vcol = curr_vcol + &sw +          elseif len(stack) > 1 && stack[0] ==# '->' && stack[1] ==# ';' +            call s:Log('    LTI is in a condition; matching "fun" found') +            call s:Pop(stack) +            call s:Pop(stack) +          elseif stack[0] ==# '->' +            call s:Log('    LTI is in a branch; matching "fun" found') +            call s:Pop(stack) +            let stored_vcol = curr_vcol + 2 * &sw +          elseif stack[0] ==# 'when' +            call s:Log('    LTI is in a guard; matching "fun" found') +            call s:Pop(stack) +            let stored_vcol = curr_vcol + 2 * &sw + 2 +          endif + +          let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, +                                              \stored_vcol, 'end', &sw) +          if ret | return res | endif +        else +          " Pass: we have a function reference (e.g. "fun f/0") +        endif + +      elseif token ==# '[' +        " Emacs compatibility +        let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, +                                            \stored_vcol, ']', 1) +        if ret | return res | endif + +      elseif token ==# '<<' +        " Emacs compatibility +        let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, +                                            \stored_vcol, '>>', 2) +        if ret | return res | endif + +      elseif token ==# '(' || token ==# '{' + +        let end_token = (token ==# '(' ? ')' : +                        \token ==# '{' ? '}' : 'error') + +        if empty(stack) +          " We found the opening paren whose block contains the LTI. +          let mode = 'inside' +        elseif stack[0] ==# end_token +          call s:Log('    "' . token . '" pops "' . end_token . '"') +          call s:Pop(stack) + +          if !empty(stack) && stack[0] ==# 'align_to_begin_element' +            " We found the opening paren whose closing paren +            " starts LTI +            let mode = 'align_to_begin_element' +          else +            " We found the opening pair for a closing paren that +            " was already in the stack. +            let mode = 'outside' +          endif +        else +          return s:UnexpectedToken(token, stack) +        endif + +        if mode ==# 'inside' || mode ==# 'align_to_begin_element' + +          if last_token_of_line && i != 0 +            " Examples: {{{ +            " +            " mode == 'inside': +            " +            "     my_func( +            "       LTI +            " +            "     [Variable, { +            "        LTI +            " +            " mode == 'align_to_begin_element': +            " +            "     my_func( +            "       Params +            "      ) % LTI +            " +            "     [Variable, { +            "        Terms +            "       } % LTI +            " }}} +            let stack = ['prev_term_plus'] +            let plus = (mode ==# 'inside' ? 2 : 1) +            call s:Log('    "' . token . +                      \'" token found at end of line -> find previous token') +          elseif mode ==# 'align_to_begin_element' +            " Examples: {{{ +            " +            " mode == 'align_to_begin_element' && !last_token_of_line +            " +            "     my_func(stored_vcol +            "            ) % LTI +            " +            "     [Variable, {stored_vcol +            "                } % LTI +            " +            " mode == 'align_to_begin_element' && i == 0 +            " +            "     ( +            "       stored_vcol +            "     ) % LTI +            " +            "     { +            "       stored_vcol +            "     } % LTI +            " }}} +            call s:Log('    "' . token . '" token (whose closing token ' . +                      \'starts LTI) found -> return') +            return curr_vcol +          elseif stored_vcol ==# -1 +            " Examples: {{{ +            " +            " mode == 'inside' && stored_vcol == -1 && !last_token_of_line +            " +            "     my_func( +            "             LTI +            "     [Variable, { +            "                 LTI +            " +            " mode == 'inside' && stored_vcol == -1 && i == 0 +            " +            "     ( +            "      LTI +            " +            "     { +            "      LTI +            " }}} +            call s:Log('    "' . token . +                      \'" token (which directly precedes LTI) found -> return') +            return curr_vcol + 1 +          else +            " Examples: {{{ +            " +            " mode == 'inside' && stored_vcol != -1 && !last_token_of_line +            " +            "     my_func(stored_vcol, +            "             LTI +            " +            "     [Variable, {stored_vcol, +            "                 LTI +            " +            " mode == 'inside' && stored_vcol != -1 && i == 0 +            " +            "     (stored_vcol, +            "      LTI +            " +            "     {stored_vcol, +            "      LTI +            " }}} +            call s:Log('    "' . token . +                      \'" token (whose block contains LTI) found -> return') +            return stored_vcol +          endif +        endif + +      elseif index(['end', ')', ']', '}', '>>'], token) != -1 + +        " If we can be sure that there is synchronization in the Erlang +        " syntax, we use searchpair to make the script quicker. Otherwise we +        " just push the token onto the stack and keep parsing. +     +        " No synchronization -> no searchpair optimization +        if !exists('b:erlang_syntax_synced') +          call s:Push(stack, token) + +        " We don't have searchpair optimization for '>>' +        elseif token ==# '>>' +          call s:Push(stack, token) + +        elseif token ==# 'end' +          let [lnum_new, col_new] = s:SearchEndPair(lnum, curr_col) + +          if lnum_new ==# 0 +            return s:IndentError('Matching token for "end" not found', +                                \token, stack) +          else +            if lnum_new != lnum +              call s:Log('    Tokenize for "end" <<<<') +              let [lnum, indtokens] = s:TokenizeLine(lnum_new, 'up') +              call s:Log('    >>>> Tokenize for "end"') +            endif + +            let [success, i] = s:GetIndtokenAtCol(indtokens, col_new) +            if !success | return i | endif +            let [token, curr_vcol, curr_col] = indtokens[i] +            call s:Log('    Match for "end" in line ' . lnum_new . ': ' . +                      \string(indtokens[i])) +          endif + +        else " token is one of the following: ')', ']', '}' + +          call s:Push(stack, token) + +          " We have to escape '[', because this string will be interpreted as a +          " regexp +          let open_paren = (token ==# ')' ? '(' : +                           \token ==# ']' ? '\[' : +                           \               '{') + +          let [lnum_new, col_new] = s:SearchPair(lnum, curr_col, +                                                \open_paren, '', token) + +          if lnum_new ==# 0 +            return s:IndentError('Matching token not found', +                                \token, stack) +          else +            if lnum_new != lnum +              call s:Log('    Tokenize the opening paren <<<<') +              let [lnum, indtokens] = s:TokenizeLine(lnum_new, 'up') +              call s:Log('    >>>>') +            endif + +            let [success, i] = s:GetIndtokenAtCol(indtokens, col_new) +            if !success | return i | endif +            let [token, curr_vcol, curr_col] = indtokens[i] +            call s:Log('    Match in line ' . lnum_new . ': ' . +                      \string(indtokens[i])) + +            " Go back to the beginning of the loop and handle the opening paren +            continue +          endif +        endif + +      elseif token ==# ';' + +        if empty(stack) +          call s:Push(stack, ';') +        elseif index([';', '->', 'when', 'end', 'after', 'catch'], +                    \stack[0]) != -1 +          " Pass: +          " +          " - If the stack top is another ';', then one ';' is +          "   enough. +          " - If the stack top is an '->' or a 'when', then we +          "   should keep that, because they signify the type of the +          "   LTI (branch, condition or guard). +          " - From the indentation point of view, the keyword +          "   (of/catch/after/end) before the LTI is what counts, so +          "   if the stack already has a catch/after/end, we don't +          "   modify it. This way when we reach case/try/receive, +          "   there will be at most one of/catch/after/end token in +          "   the stack. +        else +          return s:UnexpectedToken(token, stack) +        endif + +      elseif token ==# '->' + +        if empty(stack) && !last_token_of_line +          call s:Log('    LTI is in expression after arrow -> return') +          return stored_vcol +        elseif empty(stack) || stack[0] ==# ';' || stack[0] ==# 'end' +          " stack = [';']  -> LTI is either a branch or in a guard +          " stack = ['->']  ->  LTI is a condition +          " stack = ['->', ';']  -> LTI is a branch +          call s:Push(stack, '->') +        elseif index(['->', 'when', 'end', 'after', 'catch'], stack[0]) != -1 +          " Pass: +          " +          " - If the stack top is another '->', then one '->' is +          "   enough. +          " - If the stack top is a 'when', then we should keep +          "   that, because this signifies that LTI is a in a guard. +          " - From the indentation point of view, the keyword +          "   (of/catch/after/end) before the LTI is what counts, so +          "   if the stack already has a catch/after/end, we don't +          "   modify it. This way when we reach case/try/receive, +          "   there will be at most one of/catch/after/end token in +          "   the stack. +        else +          return s:UnexpectedToken(token, stack) +        endif + +      elseif token ==# 'when' + +        " Pop all ';' from the top of the stack +        while !empty(stack) && stack[0] ==# ';' +          call s:Pop(stack) +        endwhile + +        if empty(stack) +          if semicolon_abscol != '' +            let stored_vcol = semicolon_abscol +          endif +          if !last_token_of_line +            " Example: +            "   when A, +            "        LTI +            let [ret, res] = s:BeginElementFoundIfEmpty(stack, token, curr_vcol, +                                                       \stored_vcol, &sw) +            if ret | return res | endif +          else +            " Example: +            "   when +            "       LTI +            call s:Push(stack, token) +          endif +        elseif index(['->', 'when', 'end', 'after', 'catch'], stack[0]) != -1 +          " Pass: +          " - If the stack top is another 'when', then one 'when' is +          "   enough. +          " - If the stack top is an '->' or a 'when', then we +          "   should keep that, because they signify the type of the +          "   LTI (branch, condition or guard). +          " - From the indentation point of view, the keyword +          "   (of/catch/after/end) before the LTI is what counts, so +          "   if the stack already has a catch/after/end, we don't +          "   modify it. This way when we reach case/try/receive, +          "   there will be at most one of/catch/after/end token in +          "   the stack. +        else +          return s:UnexpectedToken(token, stack) +        endif + +      elseif token ==# 'of' || token ==# 'after' || +           \ (token ==# 'catch' && !s:IsCatchStandalone(lnum, i)) + +        if token ==# 'after' +          " If LTI is between an 'after' and the corresponding +          " 'end', then let's return +          let [ret, res] = s:BeginElementFoundIfEmpty(stack, token, curr_vcol, +                                                     \stored_vcol, &sw) +          if ret | return res | endif +        endif + +        if empty(stack) || stack[0] ==# '->' || stack[0] ==# 'when' +          call s:Push(stack, token) +        elseif stack[0] ==# 'catch' || stack[0] ==# 'after' || stack[0] ==# 'end' +          " Pass: From the indentation point of view, the keyword +          " (of/catch/after/end) before the LTI is what counts, so +          " if the stack already has a catch/after/end, we don't +          " modify it. This way when we reach case/try/receive, +          " there will be at most one of/catch/after/end token in +          " the stack. +        else +          return s:UnexpectedToken(token, stack) +        endif + +      elseif token ==# '||' && empty(stack) && !last_token_of_line + +        call s:Log('    LTI is in expression after "||" -> return') +        return stored_vcol + +      else +        call s:Log('    Misc token, stack unchanged = ' . string(stack)) + +      endif + +      if empty(stack) || stack[0] ==# '->' || stack[0] ==# 'when' +        let stored_vcol = curr_vcol +        let semicolon_abscol = '' +        call s:Log('    Misc token when the stack is empty or has "->" ' . +                  \'-> setting stored_vcol to ' . stored_vcol) +      elseif stack[0] ==# ';' +        let semicolon_abscol = curr_vcol +        call s:Log('    Setting semicolon-stored_vcol to ' . stored_vcol) +      endif + +      let i -= 1 +      call s:Log('    Token processed. stored_vcol=' . stored_vcol) + +      let last_token_of_line = 0 + +    endwhile " iteration on tokens in a line + +    call s:Log('  Line analyzed. stored_vcol=' . stored_vcol) + +    if empty(stack) && stored_vcol != -1 && +     \ (!empty(indtokens) && indtokens[0][0] != '<string_end>' && +     \                       indtokens[0][0] != '<quoted_atom_end>') +      call s:Log('    Empty stack at the beginning of the line -> return') +      return stored_vcol      endif -    if prevline =~# '^\s*\%(catch\)\s*\%(%\|$\)' -        let ind = ind + 1*&sw + +    let lnum -= 1 + +  endwhile " iteration on lines + +endfunction + +" ErlangIndent function {{{1 +" ===================== + +function! ErlangIndent() + +  call s:ClearTokenCacheIfNeeded() + +  let currline = getline(v:lnum) +  call s:Log('Indenting line ' . v:lnum . ': ' . currline) + +  if s:IsLineStringContinuation(v:lnum) || s:IsLineAtomContinuation(v:lnum) +    call s:Log('String or atom continuation found -> ' . +              \'leaving indentation unchanged') +    return -1 +  endif + +  " If the line starts with the comment, and so is the previous non-blank line +  if currline =~# '^\s*%' +    let lnum = prevnonblank(v:lnum - 1) +    if lnum ==# 0 +      call s:Log('First non-empty line of the file -> return 0.') +      return 0 +    else +      let ml = matchlist(getline(lnum), '^\(\s*\)%') +      " If the previous line also starts with a comment, then return the same +      " indentation that line has. Otherwise exit from this special "if" and +      " don't care that the current line is a comment. +      if !empty(ml) +        let new_col = s:CalcVCol(ml[1], 0, len(ml[1]) - 1, 0, &tabstop) +        call s:Log('Comment line after another comment line -> ' . +                  \'use same indent: ' . new_col) +        return new_col +      endif      endif -    if currline =~# '^\s*\%(catch\)\s*\%(%\|$\)' -        let ind = ind - 1*&sw +  endif + +  let ml = matchlist(currline, +                    \'^\(\s*\)\(\%(end\|of\|catch\|after\)\>\|[)\]}]\|>>\)') + +  " If the line has a special beginning, but not a standalone catch +  if !empty(ml) && !(ml[2] ==# 'catch' && s:IsCatchStandalone(v:lnum, 0)) + +    let curr_col = len(ml[1]) + +    " If we can be sure that there is synchronization in the Erlang +    " syntax, we use searchpair to make the script quicker. +    if ml[2] ==# 'end' && exists('b:erlang_syntax_synced') + +      let [lnum, col] = s:SearchEndPair(v:lnum, curr_col) + +      if lnum ==# 0 +        return s:IndentError('Matching token for "end" not found', +                            \'end', []) +      else +        call s:Log('    Tokenize for "end" <<<<') +        let [lnum, indtokens] = s:TokenizeLine(lnum, 'up') +        call s:Log('    >>>> Tokenize for "end"') + +        let [success, i] = s:GetIndtokenAtCol(indtokens, col) +        if !success | return i | endif +        let [token, curr_vcol, curr_col] = indtokens[i] +        call s:Log('    Match for "end" in line ' . lnum . ': ' . +                   \string(indtokens[i])) +        return curr_vcol +      endif + +    else + +      call s:Log("  Line type = 'end'") +      let new_col = s:ErlangCalcIndent(v:lnum - 1, +                                      \[ml[2], 'align_to_begin_element'])      endif +  else +    call s:Log("  Line type = 'normal'") -    if ind<0 -        let ind = 0 +    let new_col = s:ErlangCalcIndent(v:lnum - 1, []) +    if currline =~# '^\s*when\>' +      let new_col += 2      endif -    return ind +  endif + +  if new_col < -1 +    call s:Log('WARNING: returning new_col == ' . new_col) +    return g:erlang_unexpected_token_indent +  endif + +  return new_col  endfunction -" TODO: -"  -" f() -> -"     x("foo -"         bar") -"         , -"         bad_indent. -" -" fun -"     init/0, -"     bad_indent -" -"     #rec -"     .field, -" bad_indent +" ErlangShowTokensInLine functions {{{1 +" ================================ + +" These functions are useful during development. + +function! ErlangShowTokensInLine(line) +  echo "Line: " . a:line +  let indtokens = s:GetTokensFromLine(a:line, 0, 0, &tabstop) +  echo "Tokens:" +  for it in indtokens +    echo it +  endfor +endfunction + +function! ErlangShowTokensInCurrentLine() +  return ErlangShowTokensInLine(getline('.')) +endfunction + +" Cleanup {{{1 +" ======= + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: sw=2 et fdm=marker diff --git a/syntax/erlang.vim b/syntax/erlang.vim index 8b6ad624..7f883559 100644 --- a/syntax/erlang.vim +++ b/syntax/erlang.vim @@ -1,137 +1,271 @@  " Vim syntax file -" Language:   Erlang -" Maintainer: Oscar Hellström <oscar@oscarh.net> -" URL:        http://oscar.hellstrom.st -" Version:    2010-08-09 -" ------------------------------------------------------------------------------ -" {{{1 -" Options: +" Language:     Erlang (http://www.erlang.org) +" Maintainer:   Csaba Hoch <csaba.hoch@gmail.com> +" Contributor:  Adam Rutkowski <hq@mtod.org> +" Last Update:  2013-Nov-23 +" License:      Vim license +" URL:          https://github.com/hcs42/vim-erlang + +" Acknowledgements: This script was originally created by Kresimir Marzic [1]. +" The script was then revamped by Csaba Hoch [2]. During the revamp, the new +" highlighting style and some code was taken from the Erlang syntax script +" that is part of vimerl [3], created by Oscar Hellström [4] and improved by +" Ricardo Catalinas Jiménez [5]. + +" [1]: KreÄ…imir Marľić (Kresimir Marzic) <kmarzic@fly.srk.fer.hr> +" [2]: Csaba Hoch <csaba.hoch@gmail.com> +" [3]: https://github.com/jimenezrick/vimerl +" [4]: Oscar Hellström <oscar@oscarh.net> (http://oscar.hellstrom.st) +" [5]: Ricardo Catalinas Jiménez <jimenezrick@gmail.com> + +" Customization: +" +" To use the old highlighting style, add this to your .vimrc: +" +"     let g:erlang_old_style_highlight = 1  " -" Erlang BIFs -" g:erlangHighlightBif - highlight erlang built in functions (default: off) +" To highlight further module attributes, add them to +" ~/.vim/after/syntax/erlang.vim:  " -" }}} -" ----------------------------------------------------------------------------- +"     syn keyword erlangAttribute myattr1 myattr2 contained -" Setup {{{1  " For version 5.x: Clear all syntax items  " For version 6.x: Quit when a syntax file was already loaded  if version < 600 -  syntax clear +    syntax clear  elseif exists("b:current_syntax") -  finish +    finish  endif -" Erlang is case sensitive +let s:cpo_save = &cpo +set cpo&vim + +" Case sensitive  syn case match -" Match groups {{{1 -syn match erlangStringModifier               /\\./ contained -syn match erlangStringModifier               /\~\%(-\?[0-9*]\+\)\?\%(\.[0-9*]\+\..\?\)\?\%(c\|f\|e\|g\|s\|w\|p\|W\|P\|B\|X\|#\|b\|+\|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 - -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\|export_type\|ignore_xref\) *(\@=/ -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\)[( ]\@=/ - -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_list\|binary_part\|binary_to_list\|binary_to_term\|binary_to_atom\|binary_to_existing_atom\|bitstring_to_list\|check_process_code\|concat_binary\|date\|delete_module\|disconnect_node\|element\|erase\|error\|exit\|float\|float_to_list\|garbage_collect\|get\|get_keys\|group_leader\|halt\|hd\|integer_to_list\|iolist_to_binary\|iolist_size\|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\|monitor_node\|node\|nodes\|now\|open_port\|pid_to_list\|port_close\|port_command\|port_connect\|port_control\|pre_loaded\|process_flag\|process_info\|processes\|purge_module\|put\|register\|registered\|round\|self\|setelement\|size\|bit_size\|byte_size\|spawn\|spawn_link\|spawn_opt\|split_binary\|statistics\|term_to_binary\|throw\|time\|tl\|trunc\|tuple_to_list\|unlink\|unregister\|whereis\)\((\|\/[0-9]\)\@=/ -syn match erlangBIF                          /\<\%(erlang:\)\@<=\%(append_element\|bump_reductions\|cancel_timer\|decode_packet\|demonitor\|display\|fault\|fun_info\|fun_to_list\|function_exported\|get_cookie\|get_stacktrace\|hash\|hibernate\|info\|is_builtin\|loaded\|localtime\|localtime_to_universaltime\|localtime_to_universaltime\|make_tuple\|md5\|md5_init\|md5_update\|memory\|monitor\|monitor_node\|phash\|phash2\|port_call\|port_info\|port_to_list\|ports\|process_display\|raise\|read_timer\|ref_to_list\|resume_process\|send\|send_after\|send_nosuspend\|set_cookie\|spawn_monitor\|start_timer\|suspend_process\|system_flag\|system_info\|system_monitor\|trace\|trace_delivered\|trace_info\|trace_pattern\|universaltime\|universaltime_to_localtime\|yield\)(\@=/ -syn match erlangGBIF                         /erlang\(:\w\)\@=/ -" }}} - -" Link Erlang stuff to Vim groups {{{1 -hi link erlangTodo           Todo -hi link erlangString         String -hi link erlangNoSpellString  String  -hi link erlangModifier       SpecialChar -hi link erlangStringModifier SpecialChar -hi link erlangComment        Comment -hi link erlangVariable       Identifier -hi link erlangInclude        Include -hi link erlangRecordDef      Keyword -hi link erlangAttribute      Keyword -hi link erlangKeyword        Keyword -hi link erlangMacro          Macro -hi link erlangDefine         Define -hi link erlangPreCondit      PreCondit -hi link erlangPreProc        PreProc -hi link erlangDelimiter      Delimiter -hi link erlangBitDelimiter   Normal -hi link erlangOperator       Operator -hi link erlangConditional    Conditional -hi link erlangGuard          Conditional -hi link erlangBoolean        Boolean -hi link erlangAtom           Constant -hi link erlangRecord         Structure -hi link erlangInteger        Number -hi link erlangFloat          Number -hi link erlangFloat          Number -hi link erlangFloat          Number -hi link erlangFloat          Number -hi link erlangHex            Number -hi link erlangBIF            Keyword -hi link erlangFun            Keyword -hi link erlangList           Delimiter -hi link erlangTuple          Delimiter -hi link erlangBinary         Keyword -hi link erlangBitVariable    Identifier -hi link erlangBitType        Type -hi link erlangType           Type -hi link erlangBitSize        Number -" }}} - -" Optional linkings {{{1 -if exists("g:erlangHighlightBif") && g:erlangHighlightBif -	hi link erlangGBIF           Keyword +if version >= 600 +  setlocal iskeyword+=$,@-@ +endif + +" Comments +syn match erlangComment           '%.*$' contains=erlangCommentAnnotation,erlangTodo +syn match erlangCommentAnnotation ' \@<=@\%(clear\|docfile\|end\|headerfile\|todo\|TODO\|type\|author\|copyright\|doc\|reference\|see\|since\|title\|version\|deprecated\|hidden\|private\|equiv\|spec\|throws\)' contained +syn match erlangCommentAnnotation /`[^']*'/ contained +syn keyword erlangTodo            TODO FIXME XXX contained + +" Numbers (minimum base is 2, maximum is 36.) +syn match erlangNumberInteger '\<\d\+\>' +syn match erlangNumberInteger '\<\%([2-9]\|[12]\d\|3[0-6]\)\+#[[:alnum:]]\+\>' +syn match erlangNumberFloat   '\<\d\+\.\d\+\%([eE][+-]\=\d\+\)\=\>' + +" Strings, atoms, characters +syn region erlangString            start=/"/ end=/"/ contains=erlangStringModifier +syn region erlangQuotedAtom        start=/'/ end=/'/ contains=erlangQuotedAtomModifier +syn match erlangStringModifier     '\\\%(\o\{1,3}\|x\x\x\|x{\x\+}\|\^.\|.\)\|\~\%([ni~]\|\%(-\=\d\+\|\*\)\=\.\=\%(\*\|\d\+\)\=\%(\..\)\=[tl]*[cfegswpWPBX#bx+]\)' contained +syn match erlangQuotedAtomModifier '\\\%(\o\{1,3}\|x\x\x\|x{\x\+}\|\^.\|.\)' contained +syn match erlangModifier           '\$\%([^\\]\|\\\%(\o\{1,3}\|x\x\x\|x{\x\+}\|\^.\|.\)\)' + +" Operators, separators +syn match erlangOperator   '==\|=:=\|/=\|=/=\|<\|=<\|>\|>=\|=>\|:=\|++\|--\|=\|!\|<-\|+\|-\|\*\|\/' +syn keyword erlangOperator div rem or xor bor bxor bsl bsr and band not bnot andalso orelse +syn match erlangBracket    '{\|}\|\[\|]\||\|||' +syn match erlangPipe       '|' +syn match erlangRightArrow '->' + +" Atoms, function calls (order is important) +syn match erlangAtom           '\<\l[[:alnum:]_@]*' contains=erlangBoolean +syn keyword erlangBoolean      true false contained +syn match erlangLocalFuncCall  '\<\a[[:alnum:]_@]*\>\%(\%(\s\|\n\|%.*\n\)*(\)\@=' contains=erlangBIF +syn match erlangLocalFuncRef   '\<\a[[:alnum:]_@]*\>\%(\%(\s\|\n\|%.*\n\)*/\)\@=' +syn match erlangGlobalFuncCall '\<\%(\a[[:alnum:]_@]*\%(\s\|\n\|%.*\n\)*\.\%(\s\|\n\|%.*\n\)*\)*\a[[:alnum:]_@]*\%(\s\|\n\|%.*\n\)*:\%(\s\|\n\|%.*\n\)*\a[[:alnum:]_@]*\>\%(\%(\s\|\n\|%.*\n\)*(\)\@=' contains=erlangComment +syn match erlangGlobalFuncRef  '\<\%(\a[[:alnum:]_@]*\%(\s\|\n\|%.*\n\)*\.\%(\s\|\n\|%.*\n\)*\)*\a[[:alnum:]_@]*\%(\s\|\n\|%.*\n\)*:\%(\s\|\n\|%.*\n\)*\a[[:alnum:]_@]*\>\%(\%(\s\|\n\|%.*\n\)*/\)\@=' contains=erlangComment + +" Variables, macros, records, maps +syn match erlangVariable '\<[A-Z_][[:alnum:]_@]*' +syn match erlangMacro    '??\=[[:alnum:]_@]\+' +syn match erlangMacro    '\%(-define(\)\@<=[[:alnum:]_@]\+' +syn match erlangMap      '#' +syn match erlangRecord   '#\s*\l[[:alnum:]_@]*' + +" Shebang (this line has to be after the ErlangMap) +syn match erlangShebang  '^#!.*' + +" Bitstrings +syn match erlangBitType '\%(\/\%(\s\|\n\|%.*\n\)*\)\@<=\%(integer\|float\|binary\|bytes\|bitstring\|bits\|binary\|utf8\|utf16\|utf32\|signed\|unsigned\|big\|little\|native\|unit\)\%(\%(\s\|\n\|%.*\n\)*-\%(\s\|\n\|%.*\n\)*\%(integer\|float\|binary\|bytes\|bitstring\|bits\|binary\|utf8\|utf16\|utf32\|signed\|unsigned\|big\|little\|native\|unit\)\)*' contains=erlangComment + +" Constants and Directives +syn match erlangUnknownAttribute '^\s*-\%(\s\|\n\|%.*\n\)*\l[[:alnum:]_@]*' contains=erlangComment +syn match erlangAttribute '^\s*-\%(\s\|\n\|%.*\n\)*\%(behaviou\=r\|compile\|export\(_type\)\=\|file\|import\|module\|author\|copyright\|doc\|vsn\|on_load\)\>' contains=erlangComment +syn match erlangInclude   '^\s*-\%(\s\|\n\|%.*\n\)*\%(include\|include_lib\)\>' contains=erlangComment +syn match erlangRecordDef '^\s*-\%(\s\|\n\|%.*\n\)*record\>' contains=erlangComment +syn match erlangDefine    '^\s*-\%(\s\|\n\|%.*\n\)*\%(define\|undef\)\>' contains=erlangComment +syn match erlangPreCondit '^\s*-\%(\s\|\n\|%.*\n\)*\%(ifdef\|ifndef\|else\|endif\)\>' contains=erlangComment +syn match erlangType      '^\s*-\%(\s\|\n\|%.*\n\)*\%(spec\|type\|opaque\|callback\)\>' contains=erlangComment + +" Keywords +syn keyword erlangKeyword after begin case catch cond end fun if let of +syn keyword erlangKeyword receive when try + +" Build-in-functions (BIFs) +syn keyword erlangBIF abs alive apply atom_to_binary atom_to_list contained +syn keyword erlangBIF binary_part binary_to_atom contained +syn keyword erlangBIF binary_to_existing_atom binary_to_float contained +syn keyword erlangBIF binary_to_integer bitstring_to_list contained +syn keyword erlangBIF binary_to_list binary_to_term bit_size contained +syn keyword erlangBIF byte_size check_old_code check_process_code contained +syn keyword erlangBIF concat_binary date delete_module demonitor contained +syn keyword erlangBIF disconnect_node element erase error exit contained +syn keyword erlangBIF float float_to_binary float_to_list contained +syn keyword erlangBIF garbage_collect get get_keys group_leader contained +syn keyword erlangBIF halt hd integer_to_binary integer_to_list contained +syn keyword erlangBIF iolist_to_binary iolist_size is_alive contained +syn keyword erlangBIF is_atom is_binary is_bitstring is_boolean contained +syn keyword erlangBIF is_float is_function is_integer is_list contained +syn keyword erlangBIF is_number is_pid is_port is_process_alive contained +syn keyword erlangBIF is_record is_reference is_tuple length link contained +syn keyword erlangBIF list_to_atom list_to_binary contained +syn keyword erlangBIF list_to_bitstring list_to_existing_atom contained +syn keyword erlangBIF list_to_float list_to_integer list_to_pid contained +syn keyword erlangBIF list_to_tuple load_module make_ref max min contained +syn keyword erlangBIF module_loaded monitor monitor_node node contained +syn keyword erlangBIF nodes now open_port pid_to_list port_close contained +syn keyword erlangBIF port_command port_connect pre_loaded contained +syn keyword erlangBIF process_flag process_flag process_info contained +syn keyword erlangBIF process purge_module put register registered contained +syn keyword erlangBIF round self setelement size spawn spawn_link contained +syn keyword erlangBIF spawn_monitor spawn_opt split_binary contained +syn keyword erlangBIF statistics term_to_binary throw time tl contained +syn keyword erlangBIF trunc tuple_size tuple_to_list unlink contained +syn keyword erlangBIF unregister whereis contained + +" Sync at the beginning of functions: if this is not used, multiline string +" are not always recognized, and the indentation script cannot use the +" "searchpair" (because it would not always skip strings and comments when +" looking for keywords and opening parens/brackets). +syn sync match erlangSync grouphere NONE "^[a-z]\s*(" +let b:erlang_syntax_synced = 1 + +" Define the default highlighting. See ":help group-name" for the groups and +" their colors. + +let s:old_style = (exists("g:erlang_old_style_highlight") && +                  \g:erlang_old_style_highlight == 1) + +" For version 5.7 and earlier: only when not done already +" For version 5.8 and later: only when an item doesn't have highlighting yet +if version >= 508 || !exists("did_erlang_inits") +  if version < 508 +    let did_erlang_inits = 1 +    command -nargs=+ HiLink hi link <args> +  else +    command -nargs=+ HiLink hi def link <args> +  endif + +  " Comments +  HiLink erlangComment Comment +  HiLink erlangCommentAnnotation Special +  HiLink erlangTodo Todo +  HiLink erlangShebang Comment + +  " Numbers +  HiLink erlangNumberInteger Number +  HiLink erlangNumberFloat Float + +  " Strings, atoms, characters +  HiLink erlangString String + +  if s:old_style +    HiLink erlangQuotedAtom Type +  else +    HiLink erlangQuotedAtom String +  endif + +  HiLink erlangStringModifier Special +  HiLink erlangQuotedAtomModifier Special +  HiLink erlangModifier Special + +  " Operators, separators +  HiLink erlangOperator Operator +  HiLink erlangRightArrow Operator +  if s:old_style +    HiLink erlangBracket Normal +    HiLink erlangPipe Normal +  else +    HiLink erlangBracket Delimiter +    HiLink erlangPipe Delimiter +  endif + +  " Atoms, functions, variables, macros +  if s:old_style +    HiLink erlangAtom Normal +    HiLink erlangLocalFuncCall Normal +    HiLink erlangLocalFuncRef Normal +    HiLink erlangGlobalFuncCall Function +    HiLink erlangGlobalFuncRef Function +    HiLink erlangVariable Normal +    HiLink erlangMacro Normal +    HiLink erlangRecord Normal +    HiLink erlangMap Normal +  else +    HiLink erlangAtom String +    HiLink erlangLocalFuncCall Normal +    HiLink erlangLocalFuncRef Normal +    HiLink erlangGlobalFuncCall Normal +    HiLink erlangGlobalFuncRef Normal +    HiLink erlangVariable Identifier +    HiLink erlangMacro Macro +    HiLink erlangRecord Structure +    HiLink erlangMap Structure +  endif + +  " Bitstrings +  if !s:old_style +    HiLink erlangBitType Type +  endif + +  " Constants and Directives +  if s:old_style +    HiLink erlangAttribute Type +    HiLink erlangMacroDef Type +    HiLink erlangUnknownAttribute Normal +    HiLink erlangInclude Type +    HiLink erlangRecordDef Type +    HiLink erlangDefine Type +    HiLink erlangPreCondit Type +    HiLink erlangType Type +  else +    HiLink erlangAttribute Keyword +    HiLink erlangMacroDef Macro +    HiLink erlangUnknownAttribute Normal +    HiLink erlangInclude Include +    HiLink erlangRecordDef Keyword +    HiLink erlangDefine Define +    HiLink erlangPreCondit PreCondit +    HiLink erlangType Type +  endif + +  " Keywords +  HiLink erlangKeyword Keyword + +  " Build-in-functions (BIFs) +  HiLink erlangBIF Function + +  if s:old_style +    HiLink erlangBoolean Statement +    HiLink erlangExtra Statement +    HiLink erlangSignal Statement +  else +    HiLink erlangBoolean Boolean +    HiLink erlangExtra Statement +    HiLink erlangSignal Statement +  endif + +  delcommand HiLink  endif -" }}}  let b:current_syntax = "erlang" -" vim: set foldmethod=marker: +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: sw=2 et | 
