diff options
| author | Adam Stankiewicz <sheerun@sher.pl> | 2017-09-27 20:52:13 +0200 | 
|---|---|---|
| committer | Adam Stankiewicz <sheerun@sher.pl> | 2017-09-27 20:52:13 +0200 | 
| commit | 7673a61990d4062adebbe49f71067b0aad90382a (patch) | |
| tree | 06f6a5e83257abb59930153e15e0644b504ac94b | |
| parent | 6a12aa87f41b02a68cd8e6b494e5400367c2b028 (diff) | |
| download | vim-polyglot-7673a61990d4062adebbe49f71067b0aad90382a.tar.gz vim-polyglot-7673a61990d4062adebbe49f71067b0aad90382a.zip | |
Change elm provider, closes #224
Diffstat (limited to '')
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | autoload/elm.vim | 382 | ||||
| -rw-r--r-- | autoload/elm/io.vim | 12 | ||||
| -rw-r--r-- | autoload/elm/util.vim | 178 | ||||
| -rwxr-xr-x | build | 2 | ||||
| -rw-r--r-- | ftdetect/polyglot.vim | 6 | ||||
| -rw-r--r-- | ftplugin/elm.vim | 137 | ||||
| -rw-r--r-- | ftplugin/elm/tagbar.vim | 24 | ||||
| -rw-r--r-- | indent/elm.vim | 212 | ||||
| -rw-r--r-- | syntax/elm.vim | 110 | 
10 files changed, 816 insertions, 249 deletions
| @@ -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 @@ -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 | 
