summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--autoload/erlangcomplete.vim161
-rwxr-xr-xbuild2
-rw-r--r--compiler/erlang.vim80
-rw-r--r--ftplugin/erlang.vim154
-rw-r--r--ftplugin/erlang_refactor.vim295
-rw-r--r--indent/erlang.vim1612
-rw-r--r--syntax/erlang.vim376
8 files changed, 1700 insertions, 982 deletions
diff --git a/README.md b/README.md
index 5a129029..08509ee3 100644
--- a/README.md
+++ b/README.md
@@ -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
-
diff --git a/build b/build
index ba798d5d..40927cb7 100755
--- a/build
+++ b/build
@@ -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