summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--README.md2
-rw-r--r--autoload/elm.vim382
-rw-r--r--autoload/elm/io.vim12
-rw-r--r--autoload/elm/util.vim178
-rwxr-xr-xbuild2
-rw-r--r--ftdetect/polyglot.vim6
-rw-r--r--ftplugin/elm.vim137
-rw-r--r--ftplugin/elm/tagbar.vim24
-rw-r--r--indent/elm.vim212
-rw-r--r--syntax/elm.vim110
10 files changed, 816 insertions, 249 deletions
diff --git a/README.md b/README.md
index bb1239ee..3ccfb00c 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ Most importantly, vim-polyglot contains all runtime syntax files from upstream [
- [dart](https://github.com/dart-lang/dart-vim-plugin) (syntax, indent, autoload, ftplugin)
- [dockerfile](https://github.com/docker/docker) (syntax)
- [elixir](https://github.com/elixir-lang/vim-elixir) (syntax, indent, compiler, autoload, ftplugin)
-- [elm](https://github.com/lambdatoast/elm.vim) (syntax, indent, autoload, ftplugin)
+- [elm](https://github.com/ElmCast/elm-vim) (syntax, indent, autoload, ftplugin)
- [emberscript](https://github.com/yalesov/vim-ember-script) (syntax, indent, ftplugin)
- [emblem](https://github.com/yalesov/vim-emblem) (syntax, indent, ftplugin)
- [erlang](https://github.com/vim-erlang/vim-erlang-runtime) (syntax, indent)
diff --git a/autoload/elm.vim b/autoload/elm.vim
new file mode 100644
index 00000000..85a1a0b2
--- /dev/null
+++ b/autoload/elm.vim
@@ -0,0 +1,382 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elm') == -1
+
+let s:errors = []
+
+function! s:elmOracle(...) abort
+ let l:project = finddir('elm-stuff/..', '.;')
+ if len(l:project) == 0
+ echoerr '`elm-stuff` not found! run `elm-package install` for autocomplete.'
+ return []
+ endif
+
+ let l:filename = expand('%:p')
+
+ if a:0 == 0
+ let l:oldiskeyword = &iskeyword
+ " Some non obvious values used in 'iskeyword':
+ " @ = all alpha
+ " 48-57 = numbers 0 to 9
+ " @-@ = character @
+ " 124 = |
+ setlocal iskeyword=@,48-57,@-@,_,-,~,!,#,$,%,&,*,+,=,<,>,/,?,.,\\,124,^
+ let l:word = expand('<cword>')
+ let &iskeyword = l:oldiskeyword
+ else
+ let l:word = a:1
+ endif
+
+ let l:infos = elm#Oracle(l:filename, l:word)
+ if v:shell_error != 0
+ call elm#util#EchoError("elm-oracle failed:\n\n", l:infos)
+ return []
+ endif
+
+ let l:d = split(l:infos, '\n')
+ if len(l:d) > 0
+ return elm#util#DecodeJSON(l:d[0])
+ endif
+
+ return []
+endf
+
+" Vim command to format Elm files with elm-format
+function! elm#Format() abort
+ " check for elm-format
+ if elm#util#CheckBin('elm-format', 'https://github.com/avh4/elm-format') ==# ''
+ return
+ endif
+
+ " save cursor position, folds and many other things
+ let l:curw = {}
+ try
+ mkview!
+ catch
+ let l:curw = winsaveview()
+ endtry
+
+ " save our undo file to be restored after we are done.
+ let l:tmpundofile = tempname()
+ exe 'wundo! ' . l:tmpundofile
+
+ " write current unsaved buffer to a temporary file
+ let l:tmpname = tempname() . '.elm'
+ call writefile(getline(1, '$'), l:tmpname)
+
+ " call elm-format on the temporary file
+ let l:out = system('elm-format ' . l:tmpname . ' --output ' . l:tmpname)
+
+ " if there is no error
+ if v:shell_error == 0
+ try | silent undojoin | catch | endtry
+
+ " replace current file with temp file, then reload buffer
+ let l:old_fileformat = &fileformat
+ call rename(l:tmpname, expand('%'))
+ silent edit!
+ let &fileformat = l:old_fileformat
+ let &syntax = &syntax
+ elseif g:elm_format_fail_silently == 0
+ call elm#util#EchoLater('EchoError', 'elm-format:', l:out)
+ endif
+
+ " save our undo history
+ silent! exe 'rundo ' . l:tmpundofile
+ call delete(l:tmpundofile)
+
+ " restore our cursor/windows positions, folds, etc..
+ if empty(l:curw)
+ silent! loadview
+ else
+ call winrestview(l:curw)
+ endif
+endf
+
+" Query elm-oracle and echo the type and docs for the word under the cursor.
+function! elm#ShowDocs() abort
+ " check for the elm-oracle binary
+ if elm#util#CheckBin('elm-oracle', 'https://github.com/elmcast/elm-oracle') ==# ''
+ return
+ endif
+
+ let l:response = s:elmOracle()
+
+ if len(l:response) > 0
+ let l:info = l:response[0]
+ redraws! | echohl Identifier | echon l:info.fullName | echohl None | echon ' : ' | echohl Function | echon l:info.signature | echohl None | echon "\n\n" . l:info.comment
+ else
+ call elm#util#Echo('elm-oracle:', '...no match found')
+ endif
+endf
+
+" Query elm-oracle and open the docs for the word under the cursor.
+function! elm#BrowseDocs() abort
+ " check for the elm-oracle binary
+ if elm#util#CheckBin('elm-oracle', 'https://github.com/elmcast/elm-oracle') ==# ''
+ return
+ endif
+
+ let l:response = s:elmOracle()
+
+ if len(l:response) > 0
+ let l:info = l:response[0]
+ call elm#util#OpenBrowser(l:info.href)
+ else
+ call elm#util#Echo('elm-oracle:', '...no match found')
+ endif
+endf
+
+
+function! elm#Syntastic(input) abort
+ let l:fixes = []
+
+ let l:bin = 'elm-make'
+ let l:format = '--report=json'
+ let l:input = shellescape(a:input)
+ let l:output = '--output=' . shellescape(syntastic#util#DevNull())
+ let l:command = l:bin . ' ' . l:format . ' ' . l:input . ' ' . l:output
+ let l:reports = s:ExecuteInRoot(l:command)
+
+ for l:report in split(l:reports, '\n')
+ if l:report[0] ==# '['
+ for l:error in elm#util#DecodeJSON(l:report)
+ if g:elm_syntastic_show_warnings == 0 && l:error.type ==? 'warning'
+ else
+ if a:input == l:error.file
+ call add(s:errors, l:error)
+ call add(l:fixes, {'filename': l:error.file,
+ \'valid': 1,
+ \'bufnr': bufnr('%'),
+ \'type': (l:error.type ==? 'error') ? 'E' : 'W',
+ \'lnum': l:error.region.start.line,
+ \'col': l:error.region.start.column,
+ \'text': l:error.overview})
+ endif
+ endif
+ endfor
+ endif
+ endfor
+
+ return l:fixes
+endf
+
+function! elm#Build(input, output, show_warnings) abort
+ let s:errors = []
+ let l:fixes = []
+ let l:rawlines = []
+
+ let l:bin = 'elm-make'
+ let l:format = '--report=json'
+ let l:input = shellescape(a:input)
+ let l:output = '--output=' . shellescape(a:output)
+ let l:command = l:bin . ' ' . l:format . ' ' . l:input . ' ' . l:output
+ let l:reports = s:ExecuteInRoot(l:command)
+
+ for l:report in split(l:reports, '\n')
+ if l:report[0] ==# '['
+ for l:error in elm#util#DecodeJSON(l:report)
+ if a:show_warnings == 0 && l:error.type ==? 'warning'
+ else
+ call add(s:errors, l:error)
+ call add(l:fixes, {'filename': l:error.file,
+ \'valid': 1,
+ \'type': (l:error.type ==? 'error') ? 'E' : 'W',
+ \'lnum': l:error.region.start.line,
+ \'col': l:error.region.start.column,
+ \'text': l:error.overview})
+ endif
+ endfor
+ else
+ call add(l:rawlines, l:report)
+ endif
+ endfor
+
+ let l:details = join(l:rawlines, "\n")
+ let l:lines = split(l:details, "\n")
+ if !empty(l:lines)
+ let l:overview = l:lines[0]
+ else
+ let l:overview = ''
+ endif
+
+ if l:details ==# '' || l:details =~? '^Successfully.*'
+ else
+ call add(s:errors, {'overview': l:details, 'details': l:details})
+ call add(l:fixes, {'filename': expand('%', 1),
+ \'valid': 1,
+ \'type': 'E',
+ \'lnum': 0,
+ \'col': 0,
+ \'text': l:overview})
+ endif
+
+ return l:fixes
+endf
+
+" Make the given file, or the current file if none is given.
+function! elm#Make(...) abort
+ if elm#util#CheckBin('elm-make', 'http://elm-lang.org/install') ==# ''
+ return
+ endif
+
+ call elm#util#Echo('elm-make:', 'building...')
+
+ let l:input = (a:0 == 0) ? expand('%:p') : a:1
+ let l:fixes = elm#Build(l:input, g:elm_make_output_file, g:elm_make_show_warnings)
+
+ if len(l:fixes) > 0
+ call elm#util#EchoWarning('', 'found ' . len(l:fixes) . ' errors')
+
+ call setqflist(l:fixes, 'r')
+ cwindow
+
+ if get(g:, 'elm_jump_to_error', 1)
+ ll 1
+ endif
+ else
+ call elm#util#EchoSuccess('', 'Sucessfully compiled')
+
+ call setqflist([])
+ cwindow
+ endif
+endf
+
+" Show the detail of the current error in the quickfix window.
+function! elm#ErrorDetail() abort
+ if !empty(filter(tabpagebuflist(), 'getbufvar(v:val, "&buftype") ==? "quickfix"'))
+ exec ':copen'
+ let l:linenr = line('.')
+ exec ':wincmd p'
+ if len(s:errors) > 0
+ let l:detail = s:errors[l:linenr-1].details
+ if l:detail ==# ''
+ let l:detail = s:errors[l:linenr-1].overview
+ endif
+ echo l:detail
+ endif
+ endif
+endf
+
+" Open the elm repl in a subprocess.
+function! elm#Repl() abort
+ " check for the elm-repl binary
+ if elm#util#CheckBin('elm-repl', 'http://elm-lang.org/install') ==# ''
+ return
+ endif
+
+ if has('nvim')
+ term('elm-repl')
+ else
+ !elm-repl
+ endif
+endf
+
+function! elm#Oracle(filepath, word) abort
+ let l:bin = 'elm-oracle'
+ let l:filepath = shellescape(a:filepath)
+ let l:word = shellescape(a:word)
+ let l:command = l:bin . ' ' . l:filepath . ' ' . l:word
+ return s:ExecuteInRoot(l:command)
+endfunction
+
+let s:fullComplete = ''
+
+" Complete the current token using elm-oracle
+function! elm#Complete(findstart, base) abort
+" a:base is unused, but the callback function for completion expects 2 arguments
+ if a:findstart
+ let l:line = getline('.')
+
+ let l:idx = col('.') - 1
+ let l:start = 0
+ while l:idx > 0 && l:line[l:idx - 1] =~# '[a-zA-Z0-9_\.]'
+ if l:line[l:idx - 1] ==# '.' && l:start == 0
+ let l:start = l:idx
+ endif
+ let l:idx -= 1
+ endwhile
+
+ if l:start == 0
+ let l:start = l:idx
+ endif
+
+ let s:fullComplete = l:line[l:idx : col('.')-2]
+
+ return l:start
+ else
+ " check for the elm-oracle binary
+ if elm#util#CheckBin('elm-oracle', 'https://github.com/elmcast/elm-oracle') ==# ''
+ return []
+ endif
+
+ let l:res = []
+ let l:response = s:elmOracle(s:fullComplete)
+
+ let l:detailed = get(g:, 'elm_detailed_complete', 0)
+
+ for l:r in l:response
+ let l:menu = ''
+ if l:detailed
+ let l:menu = ': ' . l:r.signature
+ endif
+ call add(l:res, {'word': l:r.name, 'menu': l:menu})
+ endfor
+
+ return l:res
+ endif
+endf
+
+" If the current buffer contains a consoleRunner, run elm-test with it.
+" Otherwise run elm-test in the root of your project which deafults to
+" running 'elm-test tests/TestRunner'.
+function! elm#Test() abort
+ if elm#util#CheckBin('elm-test', 'https://github.com/rtfeldman/node-elm-test') ==# ''
+ return
+ endif
+
+ if match(getline(1, '$'), 'consoleRunner') < 0
+ let l:out = s:ExecuteInRoot('elm-test')
+ call elm#util#EchoSuccess('elm-test', l:out)
+ else
+ let l:filepath = shellescape(expand('%:p'))
+ let l:out = s:ExecuteInRoot('elm-test ' . l:filepath)
+ call elm#util#EchoSuccess('elm-test', l:out)
+ endif
+endf
+
+" Returns the closest parent with an elm-package.json file.
+function! elm#FindRootDirectory() abort
+ let l:elm_root = getbufvar('%', 'elmRoot')
+ if empty(l:elm_root)
+ let l:current_file = expand('%:p')
+ let l:dir_current_file = fnameescape(fnamemodify(l:current_file, ':h'))
+ let l:match = findfile('elm-package.json', l:dir_current_file . ';')
+ if empty(l:match)
+ let l:elm_root = ''
+ else
+ let l:elm_root = fnamemodify(l:match, ':p:h')
+ endif
+
+ if !empty(l:elm_root)
+ call setbufvar('%', 'elmRoot', l:elm_root)
+ endif
+ endif
+ return l:elm_root
+endfunction
+
+" Executes a command in the project directory.
+function! s:ExecuteInRoot(cmd) abort
+ let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
+ let l:current_dir = getcwd()
+ let l:root_dir = elm#FindRootDirectory()
+
+ try
+ execute l:cd . fnameescape(l:root_dir)
+ let l:out = system(a:cmd)
+ finally
+ execute l:cd . fnameescape(l:current_dir)
+ endtry
+
+ return l:out
+endfunction
+
+endif
diff --git a/autoload/elm/io.vim b/autoload/elm/io.vim
deleted file mode 100644
index 509a8f6a..00000000
--- a/autoload/elm/io.vim
+++ /dev/null
@@ -1,12 +0,0 @@
-if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elm') == -1
-
-" System IO
-
-" Craft a system command and run it, returning the output.
-function! elm#io#system(program, args)
- let cmd ="which " . a:program . " && " . a:program . " " . a:args
- return system(cmd)
-endfunction
-
-
-endif
diff --git a/autoload/elm/util.vim b/autoload/elm/util.vim
new file mode 100644
index 00000000..b276394c
--- /dev/null
+++ b/autoload/elm/util.vim
@@ -0,0 +1,178 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elm') == -1
+
+" IsWin returns 1 if current OS is Windows or 0 otherwise
+fun! elm#util#IsWin() abort
+ let l:win = ['win16', 'win32', 'win32unix', 'win64', 'win95']
+ for l:w in l:win
+ if (has(l:w))
+ return 1
+ endif
+ endfor
+
+ return 0
+endf
+
+fun! elm#util#CheckBin(bin, url) abort
+ let l:binpath = substitute(a:bin, '^\s*\(.\{-}\)\s*$', '\1', '')
+
+ if executable(l:binpath)
+ return l:binpath
+ endif
+
+ call elm#util#EchoWarning('elm-vim:', 'could not find ' . l:binpath . ' [' . a:url . ']')
+
+ return ''
+endf
+
+" Determines the browser command to use
+fun! s:get_browser_command() abort
+ let l:elm_browser_command = get(g:, 'elm_browser_command', '')
+ if l:elm_browser_command ==? ''
+ if elm#util#IsWin()
+ let l:elm_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%'
+ elseif has('mac') || has('macunix') || has('gui_macvim') || system('uname') =~? '^darwin'
+ let l:elm_browser_command = 'open %URL%'
+ elseif executable('xdg-open')
+ let l:elm_browser_command = 'xdg-open %URL%'
+ elseif executable('firefox')
+ let l:elm_browser_command = 'firefox %URL% &'
+ else
+ let l:elm_browser_command = ''
+ endif
+ endif
+ return l:elm_browser_command
+endf
+
+" OpenBrowser opens a url in the default browser
+fun! elm#util#OpenBrowser(url) abort
+ let l:cmd = s:get_browser_command()
+ if len(l:cmd) == 0
+ redraw
+ echohl WarningMsg
+ echo "It seems that you don't have general web browser. Open URL below."
+ echohl None
+ echo a:url
+ return
+ endif
+ if l:cmd =~? '^!'
+ let l:cmd = substitute(l:cmd, '%URL%', '\=shellescape(a:url)', 'g')
+ silent! exec l:cmd
+ elseif l:cmd =~# '^:[A-Z]'
+ let l:cmd = substitute(l:cmd, '%URL%', '\=a:url', 'g')
+ exec l:cmd
+ else
+ let l:cmd = substitute(l:cmd, '%URL%', '\=shellescape(a:url)', 'g')
+ call system(l:cmd)
+ endif
+endf
+
+" DecodeJSON decodes a string of json into a viml object
+fun! elm#util#DecodeJSON(s) abort
+ let l:true = 1
+ let l:false = 0
+ let l:null = 0
+ return eval(a:s)
+endf
+
+" Remove ANSI escape characters used for highlighting purposes
+fun! s:strip_color(msg) abort
+ return substitute(a:msg, '\e\[[0-9;]\+[mK]', '', 'g')
+endf
+
+" Print functions
+fun! elm#util#Echo(title, msg) abort
+ redraws! | echon a:title . ' ' | echohl Identifier | echon s:strip_color(a:msg) | echohl None
+endf
+
+fun! elm#util#EchoSuccess(title, msg) abort
+ redraws! | echon a:title . ' ' | echohl Function | echon s:strip_color(a:msg) | echohl None
+endf
+
+fun! elm#util#EchoWarning(title, msg) abort
+ redraws! | echon a:title . ' ' | echohl WarningMsg | echon s:strip_color(a:msg) | echohl None
+endf
+
+fun! elm#util#EchoError(title, msg) abort
+ redraws! | echon a:title . ' ' | echohl ErrorMsg | echon s:strip_color(a:msg) | echohl None
+endf
+
+fun! elm#util#EchoLater(func_name, title, msg) abort
+ let s:echo_func_name = a:func_name
+ let s:echo_title = a:title
+ let s:echo_msg = a:msg
+endf
+
+fun! elm#util#EchoStored() abort
+ if exists('s:echo_func_name') && exists('s:echo_title') && exists('s:echo_msg')
+ call elm#util#{s:echo_func_name}(s:echo_title, s:echo_msg)
+ unlet s:echo_func_name
+ unlet s:echo_title
+ unlet s:echo_msg
+ endif
+endf
+
+function! elm#util#GoToModule(name)
+ if empty(a:name) | return | endif
+ if empty(matchstr(a:name, '^Native\.'))
+ let l:extension = '.elm'
+ else
+ let l:extension = '.js'
+ endif
+ let l:rel_path = substitute(a:name, '\.', '/', 'g') . l:extension
+ let l:root = elm#FindRootDirectory()
+
+ let l:module_file = s:findLocalModule(l:rel_path, l:root)
+ if !filereadable(l:module_file)
+ let l:module_file = s:findDependencyModule(l:rel_path, l:root)
+ endif
+
+ if filereadable(l:module_file)
+ exec 'edit ' . fnameescape(l:module_file)
+ else
+ return s:error("Can't find module \"" . a:name . "\"")
+ endif
+endfunction
+
+function! s:findLocalModule(rel_path, root)
+ let l:package_json = a:root . '/elm-package.json'
+ if exists('*json_decode')
+ let l:package = json_decode(readfile(l:package_json))
+ let l:source_roots = l:package['source-directories']
+ else
+ " This is a fallback for vim's which do not support json_decode.
+ " It simply only looks in the 'src' subdirectory and fails otherwise.
+ let l:source_roots = ['src']
+ end
+ for l:source_root in l:source_roots
+ let l:file_path = a:root . '/' . l:source_root . '/' . a:rel_path
+ if !filereadable(l:file_path)
+ continue
+ endif
+ return l:file_path
+ endfor
+endfunction
+
+function! s:findDependencyModule(rel_path, root)
+ " If we are a dependency ourselves, we need to check our siblings.
+ " This is because elm package doesn't install dependencies recursively.
+ let l:root = substitute(a:root, '\/elm-stuff/packages.\+$', '', '')
+
+ " We naively craws the dependencies dir for any fitting module name.
+ " If it exists, we'll find it. If multiple filenames match,
+ " there's a chance we return the wrong one.
+ let l:module_paths = glob(l:root . '/elm-stuff/packages/**/' . a:rel_path, 0, 1)
+ if len(l:module_paths) > 0
+ return l:module_paths[0]
+ endif
+endfunction
+
+" Using the built-in :echoerr prints a stacktrace, which isn't that nice.
+" From: https://github.com/moll/vim-node/blob/master/autoload/node.vim
+function! s:error(msg)
+ echohl ErrorMsg
+ echomsg a:msg
+ echohl NONE
+ let v:errmsg = a:msg
+endfunction
+
+endif
diff --git a/build b/build
index 376c194a..81d0ddde 100755
--- a/build
+++ b/build
@@ -129,7 +129,7 @@ PACKS="
dart:dart-lang/dart-vim-plugin
dockerfile:docker/docker::/contrib/syntax/vim/
elixir:elixir-lang/vim-elixir
- elm:lambdatoast/elm.vim
+ elm:ElmCast/elm-vim
emberscript:yalesov/vim-ember-script
emblem:yalesov/vim-emblem
erlang:vim-erlang/vim-erlang-runtime
diff --git a/ftdetect/polyglot.vim b/ftdetect/polyglot.vim
index 6c8553e2..ba1a2d85 100644
--- a/ftdetect/polyglot.vim
+++ b/ftdetect/polyglot.vim
@@ -187,8 +187,10 @@ endfunction
augroup END
augroup filetypedetect
-" elm:lambdatoast/elm.vim
-au BufNewFile,BufRead *.elm set filetype=elm
+" elm:ElmCast/elm-vim
+" detection for Elm (http://elm-lang.org/)
+
+au BufRead,BufNewFile *.elm set filetype=elm
augroup END
augroup filetypedetect
diff --git a/ftplugin/elm.vim b/ftplugin/elm.vim
index 7f121520..180e719c 100644
--- a/ftplugin/elm.vim
+++ b/ftplugin/elm.vim
@@ -1,86 +1,101 @@
if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elm') == -1
-" elm.vim - Plugin for the Elm programming language
-" Maintainer: Alexander Noriega <http://lambdatoast.com/>
-" Version: 0.4.3
+" plugin for Elm (http://elm-lang.org/)
-" Plugin setup stuff
-
-if exists("b:did_ftplugin")
+if exists('b:did_ftplugin')
finish
endif
let b:did_ftplugin = 1
-" Compilation
+" Settings
+if !exists('g:elm_jump_to_error')
+ let g:elm_jump_to_error = 0
+endif
-function! ElmMake(file)
- let args = a:file
- return elm#io#system("elm-make", args)
-endfunction
+if !exists('g:elm_make_output_file')
+ let g:elm_make_output_file = 'elm.js'
+endif
-function! ElmMakeCurrentFile()
- echo ElmMake(expand("%"))
-endfunction
+if !exists('g:elm_make_show_warnings')
+ let g:elm_make_show_warnings = 0
+endif
-function! ElmMakeMain()
- echo ElmMake("Main.elm")
-endfunction
+if !exists('g:elm_syntastic_show_warnings')
+ let g:elm_syntastic_show_warnings = 0
+endif
-function! ElmMakeFile(file)
- echo ElmMake(a:file)
-endfunction
+if !exists('g:elm_format_autosave')
+ let g:elm_format_autosave = 1
+endif
-" REPL
+if !exists('g:elm_format_fail_silently')
+ let g:elm_format_fail_silently = 0
+endif
-function! ElmRepl()
- !elm-repl
-endfunction
+if !exists('g:elm_setup_keybindings')
+ let g:elm_setup_keybindings = 1
+endif
-" Evaluation
+setlocal omnifunc=elm#Complete
-function! ElmEvalLine()
- return ElmEval(getline("."))
-endfunction
+setlocal comments=:--
+setlocal commentstring=--\ %s
+
+" Commands
+command -buffer -nargs=? -complete=file ElmMake call elm#Make(<f-args>)
+command -buffer ElmMakeMain call elm#Make("Main.elm")
+command -buffer -nargs=? -complete=file ElmTest call elm#Test(<f-args>)
+command -buffer ElmRepl call elm#Repl()
+command -buffer ElmErrorDetail call elm#ErrorDetail()
+command -buffer ElmShowDocs call elm#ShowDocs()
+command -buffer ElmBrowseDocs call elm#BrowseDocs()
+command -buffer ElmFormat call elm#Format()
+
+if get(g:, 'elm_setup_keybindings', 1)
+ nmap <buffer> <LocalLeader>m <Plug>(elm-make)
+ nmap <buffer> <LocalLeader>b <Plug>(elm-make-main)
+ nmap <buffer> <LocalLeader>t <Plug>(elm-test)
+ nmap <buffer> <LocalLeader>r <Plug>(elm-repl)
+ nmap <buffer> <LocalLeader>e <Plug>(elm-error-detail)
+ nmap <buffer> <LocalLeader>d <Plug>(elm-show-docs)
+ nmap <buffer> <LocalLeader>w <Plug>(elm-browse-docs)
+endif
-function! ElmEvalSelection()
- let savedReg = @z
- normal! `<v`>"zy
- let res = ElmEval(substitute(getreg("z"), "\n", "\\\n", "g"))
- let @z = savedReg
- normal! gv
-endfunction
+" Better gf command
+nmap <buffer> gf :call elm#util#GoToModule(expand('<cfile>'))<CR>
-function! ElmEval(sourceCode)
- let currentLine = a:sourceCode
- let args = "echo '" . currentLine . "' | elm-repl"
- let result = elm#io#system("echo", args)
- let cleanResult = "-- " . join(s:Filtered(function("s:IsUsefulReplOutput"), split(result, "\n")), "")
- put =cleanResult
-endfunction
+" Elm code formatting on save
+if get(g:, 'elm_format_autosave', 1)
+ augroup elmFormat
+ autocmd!
+ autocmd BufWritePre *.elm call elm#Format()
+ autocmd BufWritePost *.elm call elm#util#EchoStored()
+ augroup END
+endif
+if has('win32')
+ set viewdir=$HOME/vimfiles/views/
+endif
-function! s:IsUsefulReplOutput(str)
- return a:str !~ "^Elm REPL" && a:str !~ "Type :help" && a:str !~ ">\\s*$"
-endfunction
+" Enable go to file under cursor from module name
+" Based on: https://github.com/elixir-lang/vim-elixir/blob/bd66ed134319d1e390f3331e8c4d525109f762e8/ftplugin/elixir.vim#L22-L56
+function! GetElmFilename(word)
+ let l:word = a:word
-" List processing
+ " replace module dots with slash
+ let l:word = substitute(l:word,'\.','/','g')
-function! s:Filtered(fn, l)
- let new_list = deepcopy(a:l)
- call filter(new_list, string(a:fn) . '(v:val)')
- return new_list
+ return l:word
endfunction
-command -buffer ElmEvalLine call ElmEvalLine()
-command -buffer ElmEvalSelection call ElmEvalSelection()
-command -buffer ElmMakeMain call ElmMakeMain()
-command -buffer -nargs=1 ElmMakeFile call ElmMakeFile <args>
-command -buffer ElmMakeCurrentFile call ElmMakeCurrentFile()
-command -buffer ElmRepl call ElmRepl()
-
-" Define comment convention
-
-setlocal comments=:--
-setlocal commentstring=--%s
+let &l:path =
+ \ join([
+ \ elm#FindRootDirectory().'/src',
+ \ elm#FindRootDirectory().'/elm-stuff/packages/**/src',
+ \ &g:path
+ \ ], ',')
+setlocal includeexpr=GetElmFilename(v:fname)
+setlocal include=^\\s*import\\s\\+
+setlocal suffixesadd=.elm
endif
diff --git a/ftplugin/elm/tagbar.vim b/ftplugin/elm/tagbar.vim
new file mode 100644
index 00000000..1ad3a48b
--- /dev/null
+++ b/ftplugin/elm/tagbar.vim
@@ -0,0 +1,24 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elm') == -1
+
+if !executable('ctags')
+ finish
+elseif globpath(&runtimepath, 'plugin/tagbar.vim') ==? ''
+ finish
+endif
+
+function! s:SetTagbar()
+ if !exists('g:tagbar_type_elm')
+ let g:tagbar_type_elm = {
+ \ 'ctagstype' : 'elm',
+ \ 'kinds' : [
+ \ 'c:constants',
+ \ 'f:functions',
+ \ 'p:ports'
+ \ ]
+ \ }
+ endif
+endfunction
+
+call s:SetTagbar()
+
+endif
diff --git a/indent/elm.vim b/indent/elm.vim
index 48e96d7e..b8635314 100644
--- a/indent/elm.vim
+++ b/indent/elm.vim
@@ -1,129 +1,115 @@
if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elm') == -1
-" Vim indent file
-" Language: Haskell
-" Maintainer: lilydjwg <lilydjwg@gmail.com>
-" Version: 1.0
-" References: http://en.wikibooks.org/wiki/Haskell/Indentation
-" http://book.realworldhaskell.org/read/
-" See Also: The Align plugin http://www.vim.org/scripts/script.php?script_id=294
+" indentation for Elm (http://elm-lang.org/)
" Only load this indent file when no other was loaded.
-if exists("b:did_indent")
- finish
+if exists('b:did_indent')
+ finish
endif
let b:did_indent = 1
-setlocal indentexpr=HaskellIndent()
-for i in split('0{,:,0#,e', ',')
- exec "setlocal indentkeys-=" . i
-endfor
-setlocal indentkeys+=0=else,0=in,0=where,0),0<bar>
-setlocal tabstop=8
+" Local defaults
setlocal expandtab
+setlocal indentexpr=GetElmIndent()
+setlocal indentkeys+=0=else,0=if,0=of,0=import,0=then,0=type,0\|,0},0\],0),=-},0=in
+setlocal nolisp
+setlocal nosmartindent
-if !exists('g:Haskell_no_mapping')
- inoremap <silent> <BS> <C-R>=<SID>HaskellDedent(1)<CR>
- inoremap <silent> <C-D> <C-R>=<SID>HaskellDedent(0)<CR>
-endif
+" Comment formatting
+setlocal comments=s1fl:{-,mb:\ ,ex:-},:--
-" Only define the functions once.
-if exists("*HaskellIndent")
- finish
+" Only define the function once.
+if exists('*GetElmIndent')
+ finish
endif
-let s:align_map = {
- \ 'in': '\<let\>',
- \ '\<else\>': '\<then\>',
- \ ',': '\v%(\s|\w|^)@<=[[{]%(\s|\w|"|$)@='
- \ }
-let s:indent_self = ['=']
-let s:indent_next = ['let', 'in', 'where', 'do', 'if']
-let s:indent_if_final = ['=', 'do', '->', 'of', 'where']
-
-function HaskellIndent()
- let lnum = v:lnum - 1
-
- " Hit the start of the file, use zero indent.
- if lnum == 0
- return 0
- endif
-
- let ind = indent(lnum)
- let prevline = getline(lnum)
- let curline = getline(v:lnum)
- let curwords = split(curline)
- if len(curwords) > 0
- if has_key(s:align_map, curwords[0])
- let word = s:align_map[curwords[0]]
- let m = -1
- let line = v:lnum
- while m == -1
- let line -= 1
- if line <= 0
- return -1
- endif
- let m = match(getline(line), word)
- endwhile
- return m
- elseif index(s:indent_self, curwords[0]) != -1
- return ind + &sw
- elseif curwords[0] == '|'
- return match(prevline, '\v%(\s|\w|^)@<=[|=]%(\s|\w)@=')
- elseif index([')', '}'], curwords[0]) != -1
- return ind - &sw
- elseif curwords[0] == 'where'
- if prevline =~ '\v^\s+\|%(\s|\w)@='
- return ind - 1
- endif
- endif
- endif
-
- let prevwords = split(prevline)
- if len(prevwords) == 0
- return 0
- endif
-
- if prevwords[-1] == 'where' && prevwords[0] == 'module'
- return 0
- elseif index(s:indent_if_final, prevwords[-1]) != -1
- return ind + &sw
- elseif prevwords[-1] =~ '\v%(\s|\w|^)@<=[[{(]$'
- return ind + &sw
- else
- for word in reverse(prevwords)
- if index(s:indent_next, word) != -1
- return match(prevline, '\<'.word.'\>') + len(word) + 1
- endif
- endfor
- endif
-
- if len(curwords) > 0 && curwords[0] == 'where'
- return ind + &sw
- endif
-
- return ind
+" Indent pairs
+function! s:FindPair(pstart, pmid, pend)
+ "call search(a:pend, 'bW')
+ return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"'))
endfunction
-function s:HaskellDedent(isbs)
- if a:isbs && strpart(getline('.'), 0, col('.')-1) !~ '^\s\+$'
- return "\<BS>"
- endif
-
- let curind = indent('.')
- let line = line('.') - 1
- while curind > 0 && line > 0
- let ind = indent(line)
- if ind >= curind
- let line -= 1
- else
- echomsg curind ind
- call setline('.', repeat(' ', ind) .
- \ substitute(getline('.'), '^\s\+', '', ''))
- return ''
- endif
- endwhile
- return a:isbs ? "\<BS>" : ''
-endfunction
+function! GetElmIndent()
+ let l:lnum = v:lnum - 1
+
+ " Ident 0 if the first line of the file:
+ if l:lnum == 0
+ return 0
+ endif
+
+ let l:ind = indent(l:lnum)
+ let l:lline = getline(l:lnum)
+ let l:line = getline(v:lnum)
+
+ " Indent if current line begins with '}':
+ if l:line =~? '^\s*}'
+ return s:FindPair('{', '', '}')
+
+ " Indent if current line begins with 'else':
+ elseif l:line =~# '^\s*else\>'
+ if l:lline !~# '^\s*\(if\|then\)\>'
+ return s:FindPair('\<if\>', '', '\<else\>')
+ endif
+
+ " Indent if current line begins with 'then':
+ elseif l:line =~# '^\s*then\>'
+ if l:lline !~# '^\s*\(if\|else\)\>'
+ return s:FindPair('\<if\>', '', '\<then\>')
+ endif
+
+ " HACK: Indent lines in case with nearest case clause:
+ elseif l:line =~# '->' && l:line !~# ':' && l:line !~# '\\'
+ return indent(search('^\s*case', 'bWn')) + &shiftwidth
+
+ " HACK: Don't change the indentation if the last line is a comment.
+ elseif l:lline =~# '^\s*--'
+ return l:ind
+
+ " Align the end of block comments with the start
+ elseif l:line =~# '^\s*-}'
+ return indent(search('{-', 'bWn'))
+
+ " Indent double shift after let with an empty rhs
+ elseif l:lline =~# '\<let\>.*\s=$'
+ return l:ind + 4 + &shiftwidth
+
+ " Align 'in' with the parent let.
+ elseif l:line =~# '^\s*in\>'
+ return indent(search('^\s*let', 'bWn'))
+
+ " Align bindings with the parent let.
+ elseif l:lline =~# '\<let\>'
+ return l:ind + 4
+
+ " Align bindings with the parent in.
+ elseif l:lline =~# '^\s*in\>'
+ return l:ind + 4
+
+ endif
+
+ " Add a 'shiftwidth' after lines ending with:
+ if l:lline =~# '\(|\|=\|->\|<-\|(\|\[\|{\|\<\(of\|else\|if\|then\)\)\s*$'
+ let l:ind = l:ind + &shiftwidth
+
+ " Add a 'shiftwidth' after lines starting with type ending with '=':
+ elseif l:lline =~# '^\s*type' && l:line =~# '^\s*='
+ let l:ind = l:ind + &shiftwidth
+
+ " Back to normal indent after comments:
+ elseif l:lline =~# '-}\s*$'
+ call search('-}', 'bW')
+ let l:ind = indent(searchpair('{-', '', '-}', 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"'))
+
+ " Ident some operators if there aren't any starting the last line.
+ elseif l:line =~# '^\s*\(!\|&\|(\|`\|+\||\|{\|[\|,\)=' && l:lline !~# '^\s*\(!\|&\|(\|`\|+\||\|{\|[\|,\)=' && l:lline !~# '^\s*$'
+ let l:ind = l:ind + &shiftwidth
+
+ elseif l:lline ==# '' && getline(l:lnum - 1) !=# ''
+ let l:ind = indent(search('^\s*\S+', 'bWn'))
+
+ endif
+
+ return l:ind
+endfunc
endif
diff --git a/syntax/elm.vim b/syntax/elm.vim
index 5825e228..fb3874c0 100644
--- a/syntax/elm.vim
+++ b/syntax/elm.vim
@@ -1,81 +1,73 @@
if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elm') == -1
-" Vim syntax file
-" Language: Elm (http://elm-lang.org/)
-" Maintainer: Alexander Noriega
-" Latest Revision: 19 April 2015
+" syntax highlighting for Elm (http://elm-lang.org/)
-if exists("b:current_syntax")
+if exists('b:current_syntax')
finish
endif
" Keywords
-syn keyword elmKeyword alias as case else exposing if import in let module of port then type where
+syn keyword elmConditional case else if of then
+syn keyword elmAlias alias
+syn keyword elmTypedef type port let in
+syn keyword elmImport exposing as import module where
-" Builtin operators
-syn match elmBuiltinOp "\~"
-syn match elmBuiltinOp "||"
-syn match elmBuiltinOp "|>"
-syn match elmBuiltinOp "|"
-syn match elmBuiltinOp "`"
-syn match elmBuiltinOp "\^"
-syn match elmBuiltinOp "\\"
-syn match elmBuiltinOp ">>"
-syn match elmBuiltinOp ">="
-syn match elmBuiltinOp ">"
-syn match elmBuiltinOp "=="
-syn match elmBuiltinOp "="
-syn match elmBuiltinOp "<\~"
-syn match elmBuiltinOp "<|"
-syn match elmBuiltinOp "<="
-syn match elmBuiltinOp "<<"
-syn match elmBuiltinOp "<-"
-syn match elmBuiltinOp "<"
-syn match elmBuiltinOp "::"
-syn match elmBuiltinOp ":"
-syn match elmBuiltinOp "/="
-syn match elmBuiltinOp "//"
-syn match elmBuiltinOp "/"
-syn match elmBuiltinOp "\.\."
-syn match elmBuiltinOp "\."
-syn match elmBuiltinOp "->"
-syn match elmBuiltinOp "-"
-syn match elmBuiltinOp "++"
-syn match elmBuiltinOp "+"
-syn match elmBuiltinOp "*"
-syn match elmBuiltinOp "&&"
-syn match elmBuiltinOp "%"
+" Operators
+syn match elmOperator "\([-!#$%`&\*\+./<=>\?@\\^|~:]\|\<_\>\)"
-" Special names
-syntax match specialName "^main "
+" Types
+syn match elmType "\<[A-Z][0-9A-Za-z_'-]*"
+syn keyword elmNumberType number
+
+" Delimiters
+syn match elmDelimiter "[,;]"
+syn match elmBraces "[()[\]{}]"
+
+" Functions
+syn match elmTupleFunction "\((,\+)\)"
" Comments
-syn match elmTodo "[tT][oO][dD][oO]\|FIXME\|XXX" contained
+syn keyword elmTodo TODO FIXME XXX contained
syn match elmLineComment "--.*" contains=elmTodo,@spell
syn region elmComment matchgroup=elmComment start="{-|\=" end="-}" contains=elmTodo,elmComment,@spell
-" String literals
-syn region elmString start="\"" skip="\\\"" end="\"" contains=elmStringEscape
+" Strings
syn match elmStringEscape "\\u[0-9a-fA-F]\{4}" contained
syn match elmStringEscape "\\[nrfvbt\\\"]" contained
+syn region elmString start="\"" skip="\\\"" end="\"" contains=elmStringEscape,@spell
+syn region elmTripleString start="\"\"\"" skip="\\\"" end="\"\"\"" contains=elmStringEscape,@spell
+syn match elmChar "'[^'\\]'\|'\\.'\|'\\u[0-9a-fA-F]\{4}'"
-" Number literals
-syn match elmNumber "\(\<\d\+\>\)"
-syn match elmNumber "\(\<\d\+\.\d\+\>\)"
+" Numbers
+syn match elmInt "-\?\<\d\+\>\|0[xX][0-9a-fA-F]\+\>"
+syn match elmFloat "\(\<\d\+\.\d\+\>\)"
-" Types
-syn match elmType "\<[A-Z][0-9A-Za-z_'-]*"
+" Identifiers
+syn match elmTopLevelDecl "^\s*[a-zA-Z][a-zA-z0-9_]*\('\)*\s\+:\s\+" contains=elmOperator
+
+hi def link elmTopLevelDecl Function
+hi def link elmTupleFunction Normal
+hi def link elmTodo Todo
+hi def link elmComment Comment
+hi def link elmLineComment Comment
+hi def link elmString String
+hi def link elmTripleString String
+hi def link elmChar String
+hi def link elmStringEscape Special
+hi def link elmInt Number
+hi def link elmFloat Float
+hi def link elmDelimiter Delimiter
+hi def link elmBraces Delimiter
+hi def link elmTypedef TypeDef
+hi def link elmImport Include
+hi def link elmConditional Conditional
+hi def link elmAlias Delimiter
+hi def link elmOperator Operator
+hi def link elmType Identifier
+hi def link elmNumberType Identifier
-let b:current_syntax = "elm"
+syn sync minlines=500
-hi def link elmKeyword Keyword
-hi def link elmBuiltinOp Special
-hi def link elmType Type
-hi def link elmTodo Todo
-hi def link elmLineComment Comment
-hi def link elmComment Comment
-hi def link elmString String
-hi def link elmNumber Number
-hi def link specialName Special
+let b:current_syntax = 'elm'
endif