vim-polyglot/indent/purescript.vim
Adam Stankiewicz 8b3418cab8
Update
2017-09-27 19:57:29 +02:00

281 lines
7.2 KiB
VimL

if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'purescript') == -1
" indentation for purescript
"
" Based on idris indentation
"
" author: raichoo (raichoo@googlemail.com)
"
" Modify g:purescript_indent_if and g:purescript_indent_case to
" change indentation for `if'(default 3) and `case'(default 5).
" Example (in .vimrc):
" > let g:purescript_indent_if = 2
if exists('b:did_indent')
finish
endif
let b:did_indent = 1
if !exists('g:purescript_indent_if')
" if bool
" >>>then ...
" >>>else ...
let g:purescript_indent_if = 3
endif
if !exists('g:purescript_indent_case')
" case xs of
" >>>>>[] -> ...
" >>>>>(y:ys) -> ...
let g:purescript_indent_case = 5
endif
if !exists('g:purescript_indent_let')
" let x = 0 in
" >>>>x
let g:purescript_indent_let = 4
endif
if !exists('g:purescript_indent_in')
" let x = 0
" >in
let g:purescript_indent_in = 1
endif
if !exists('g:purescript_indent_where')
" where
" >>f :: Int -> Int
" >>f x = x
let g:purescript_indent_where = 6
endif
if !exists('g:purescript_indent_do')
" do x <- a
" >>>y <- b
let g:purescript_indent_do = 3
endif
if !exists('g:purescript_indent_dot')
" f
" :: forall a
" >. String
" -> String
let g:purescript_indent_dot = 1
endif
setlocal indentexpr=GetPurescriptIndent()
setlocal indentkeys=!^F,o,O,},=where,=in,=::,=->,=,==>,=
function! s:GetSynStack(lnum, col)
return map(synstack(a:lnum, a:col), { key, val -> synIDattr(val, "name") })
endfunction
function! GetPurescriptIndent()
let ppline = getline(v:lnum - 2)
let prevline = getline(v:lnum - 1)
let line = getline(v:lnum)
if line =~ '^\s*\<where\>'
let s = indent(v:lnum - 1)
return max([s, &l:shiftwidth])
endif
if line =~ '^\s*\<in\>'
let n = v:lnum
let s = 0
while s <= 0 && n > 0
let n = n - 1
let s = match(getline(n), '\<let\>')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') != -1
let s = -1
endif
endwhile
return s + g:purescript_indent_in
endif
let s = match(prevline, '^\s*\zs\(--\|import\)')
if s >= 0
" comments
" imports
return s
endif
if prevline =~ '^\S.*::' && line !~ '^\s*\(\.\|->\|→\|=>\|⇒\)' && !~ '^instance'
" f :: String
" -> String
return 0
endif
let s = match(prevline, '[[:alnum:][:blank:]]\@<=|[[:alnum:][:blank:]$]')
if s >= 0 && prevline !~ '^class\>' && index(s:GetSynStack(v:lnum - 1, s), "purescriptFunctionDecl") == -1
" ident pattern guards but not if we are in a type declaration
" what we detect using syntax groups
if prevline =~ '|\s*otherwise\>'
return indent(search('^\s*\k', 'bnW'))
" somehow this pattern does not work :/
" return indent(search('^\(\s*|\)\@!', 'bnW'))
else
return s
endif
endif
let s = match(line, '\%(\\.\{-}\)\@<=->')
if s >= 0
" inline lambda
return indent(v:lnum)
endif
" indent rules for -> (lambdas and case expressions)
let s = match(line, '->')
let p = match(prevline, '\\')
" protect that we are not in a type signature
" and not in a case expression
if s >= 0 && index(s:GetSynStack(s == 0 ? v:lnum - 1 : v:lnum, max([1, s])), "purescriptFunctionDecl") == -1
\ && p >= 0 && index(s:GetSynStack(v:lnum - 1, p), "purescriptString") == -1
return p
endif
if prevline =~ '^\S'
" start typing signature, function body, data & newtype on next line
return &l:shiftwidth
endif
if ppline =~ '^\S' && prevline =~ '^\s*$'
return 0
endif
if line =~ '^\s*\%(::\|∷\)'
return match(prevline, '\S') + &l:shiftwidth
endif
if prevline =~ '^\s*\(::\|∷\)\s*forall'
return match(prevline, '\S') + g:purescript_indent_dot
endif
let s = match(prevline, '^\s*\zs\%(::\|∷\|=>\|⇒\|->\|→\)')
let r = match(prevline, '^\s*\zs\.')
if s >= 0 || r >= 0
if s >= 0
if line !~ '^\s*\%(::\|∷\|=>\|⇒\|->\|→\)' && line !~ '^\s*$'
return s - 2
else
return s
endif
elseif r >= 0
if line !~ '^\s\%(::\|∷\|=>\|⇒\|->\|→\)'
return r - g:purescript_indent_dot
else
return r
endif
endif
endif
if prevline =~ '[!#$%&*+./<>?@\\^~-]\s*$'
let s = match(prevline, '=')
if s > 0
return s + &l:shiftwidth
endif
let s = match(prevline, '\<:\>')
if s > 0
return s + &l:shiftwidth
else
return match(prevline, '\S') + &l:shiftwidth
endif
endif
if prevline =~ '[{([][^})\]]\+$'
echom "return 1"
return match(prevline, '[{([]')
endif
let s = match(prevline, '\<let\>\s\+\zs\S')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return s
endif
let s = match(prevline, '\<let\>\s*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return s + g:purescript_indent_let
endif
let s = match(prevline, '\<let\>\s\+.\+\(\<in\>\)\?\s*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return match(prevline, '\<let\>') + g:purescript_indent_let
endif
let s = searchpairpos('\%(--.\{-}\)\@<!\<if\>', '\<then\>', '\<else\>.*\zs$', 'bnrc')[0]
if s > 0
" this rule ensures that using `=` in visual mode will correctly indent
" `if then else`, but it does not handle lines after `then` and `else`
if line =~ '\<\%(then\|else\)\>'
return match(getline(s), '\<if\>') + &l:shiftwidth
endif
endif
let p = match(prevline, '\<if\>\%(.\{-}\<then\>.\{-}\<else\>\)\@!')
if p > 0
return p + &l:shiftwidth
endif
let s = match(prevline, '=\s*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return match(prevline, '\S') + &l:shiftwidth
endif
let s = match(prevline, '[{([]\s*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return match(prevline, '\S') + (line !~ '^\s*[})]]' ? 0 : &l:shiftwidth)
endif
if prevline =~ '^class'
return &l:shiftwidth
endif
let s = match(prevline, '\<where\>\s*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return match(prevline, '\S') + g:purescript_indent_where
endif
let s = match(prevline, '\<where\>\s\+\zs\S\+.*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return s
endif
let s = match(prevline, '\<do\>\s*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return match(prevline, '\S') + g:purescript_indent_do
endif
let s = match(prevline, '\<do\>\s\+\zs\S\+.*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return s
endif
let s = match(prevline, '^\s*\<data\>\s\+[^=]\+\s\+=\s\+\S\+.*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return match(prevline, '=')
endif
let s = match(prevline, '\<case\>\s\+.\+\<of\>\s*$')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return match(prevline, '\<case\>') + g:purescript_indent_case
endif
if prevline =~ '^\s*\<\data\>\s\+\S\+\s*$'
return match(prevline, '\<data\>') + &l:shiftwidth
endif
let s = match(prevline, '^\s*[}\]]')
if s >= 0 && index(s:GetSynStack(v:lnum - 1, s), 'purescriptString') == -1
return match(prevline, '\S') - &l:shiftwidth
endif
return match(prevline, '\S')
endfunction
endif