vim-polyglot/indent/haskell.vim

508 lines
11 KiB
VimL
Raw Normal View History

if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'haskell') == -1
" indentation for haskell
"
" author: raichoo (raichoo@googlemail.com)
"
" Modify g:haskell_indent_if and g:haskell_indent_case to
" change indentation for `if'(default 3) and `case'(default 5).
" Example (in .vimrc):
" > let g:haskell_indent_if = 2
if exists('b:did_indent')
finish
endif
2017-05-17 05:07:28 -04:00
if get(g:, 'haskell_indent_disable', 0)
2017-03-23 06:28:19 -04:00
finish
endif
let b:did_indent = 1
if !exists('g:haskell_indent_if')
2016-05-02 04:42:37 -04:00
" if x
" >>>then ...
" >>>else ...
let g:haskell_indent_if = 3
endif
if !exists('g:haskell_indent_case')
" case xs of
" >>[] -> ...
" >>(y:ys) -> ...
let g:haskell_indent_case = 2
endif
if !exists('g:haskell_indent_let')
" let x = 0 in
" >>>>x
2017-09-27 13:57:29 -04:00
"
" let x = 0
" y = 1
let g:haskell_indent_let = 4
endif
2017-09-27 13:57:29 -04:00
if !exists('g:haskell_indent_let_no_in')
" let x = 0
" x
let g:haskell_indent_let_no_in = 4
endif
if !exists('g:haskell_indent_where')
" where f :: Int -> Int
" >>>>>>f x = x
let g:haskell_indent_where = 6
endif
if !exists('g:haskell_indent_do')
" do x <- a
" >>>y <- b
let g:haskell_indent_do = 3
endif
if !exists('g:haskell_indent_in')
" let x = 1
" >in x
let g:haskell_indent_in = 1
endif
2016-05-02 04:42:37 -04:00
if !exists('g:haskell_indent_guard')
" f x y
" >>|
let g:haskell_indent_guard = 2
endif
2017-03-23 06:28:19 -04:00
setlocal indentexpr=GetHaskellIndent()
2017-05-17 05:07:28 -04:00
setlocal indentkeys=0},0),0],!^F,o,O,0=where,0=let,0=deriving,<space>
2016-05-02 04:42:37 -04:00
function! s:isInBlock(hlstack)
2017-03-23 06:28:19 -04:00
return index(a:hlstack, 'haskellDelimiter') > -1 || index(a:hlstack, 'haskellParens') > -1 || index(a:hlstack, 'haskellBrackets') > -1 || index(a:hlstack, 'haskellBlock') > -1 || index(a:hlstack, 'haskellBlockComment') > -1 || index(a:hlstack, 'haskellPragma') > -1
2016-09-11 07:24:17 -04:00
endfunction
2017-03-23 06:28:19 -04:00
function! s:stripComment(line)
if a:line =~ '^\s*--\(-*\s\+\|$\)'
return ''
2016-09-11 07:24:17 -04:00
else
let l:stripped = split(a:line, '-- ')
if len(l:stripped) > 1
return substitute(l:stripped[0], '\s*$', '', '')
else
return a:line
endif
endif
endfunction
function! s:isSYN(grp, line, col)
return index(s:getHLStack(a:line, a:col), a:grp) != -1
2016-05-02 04:42:37 -04:00
endfunction
2016-05-02 04:42:37 -04:00
function! s:getNesting(hlstack)
2016-09-11 07:24:17 -04:00
return filter(a:hlstack, 'v:val == "haskellBlock" || v:val == "haskellBrackets" || v:val == "haskellParens" || v:val == "haskellBlockComment" || v:val == "haskellPragma" ')
2016-05-02 04:42:37 -04:00
endfunction
2016-09-11 07:24:17 -04:00
function! s:getHLStack(line, col)
return map(synstack(a:line, a:col), 'synIDattr(v:val, "name")')
2016-05-02 04:42:37 -04:00
endfunction
2016-05-02 04:42:37 -04:00
" indent matching character
function! s:indentMatching(char)
normal! 0
call search(a:char, 'cW')
normal! %
return col('.') - 1
endfunction
2016-05-02 04:42:37 -04:00
" backtrack to find guard clause
function! s:indentGuard(pos, prevline)
let l:l = a:prevline
let l:c = 1
while v:lnum != l:c
" empty line, stop looking
if l:l =~ '^$'
return a:pos
" guard found
elseif l:l =~ '^\s*|\s\+'
return match(l:l, '|')
" found less deeper indentation (not starting with `,` or `=`)
" stop looking
else
let l:m = match(l:l, '\S')
if l:l !~ '^\s*[=,]' && l:m <= a:pos
return l:m + g:haskell_indent_guard
endif
endif
2016-05-02 04:42:37 -04:00
let l:c += 1
let l:l = getline(v:lnum - l:c)
endwhile
2016-05-02 04:42:37 -04:00
return -1
endfunction
2016-05-02 04:42:37 -04:00
function! GetHaskellIndent()
2016-09-11 07:24:17 -04:00
let l:hlstack = s:getHLStack(line('.'), col('.'))
2016-05-02 04:42:37 -04:00
" do not indent in strings and quasiquotes
2016-05-30 19:53:12 -04:00
if index(l:hlstack, 'haskellQuasiQuote') > -1 || index(l:hlstack, 'haskellBlockComment') > -1
2016-05-02 04:42:37 -04:00
return -1
endif
2017-03-23 06:28:19 -04:00
let l:prevline = s:stripComment(getline(v:lnum - 1))
2016-05-02 04:42:37 -04:00
let l:line = getline(v:lnum)
2016-05-30 19:53:12 -04:00
" indent multiline strings
if index(l:hlstack, 'haskellString') > -1
if l:line =~ '^\s*\\'
return match(l:prevline, '["\\]')
else
return - 1
endif
endif
2016-05-02 04:42:37 -04:00
" reset
if l:prevline =~ '^\s*$' && l:line !~ '^\s*\S'
return 0
endif
2016-07-30 07:18:40 -04:00
" { foo :: Int
" >>,
"
" |
" ...
" >>,
if l:line =~ '^\s*,'
2017-03-23 06:28:19 -04:00
if s:isInBlock(s:getHLStack(line('.'), col('.')))
2016-07-30 07:18:40 -04:00
normal! 0
call search(',', 'cW')
2016-09-11 07:24:17 -04:00
let l:n = s:getNesting(s:getHLStack(line('.'), col('.')))
2016-07-30 07:18:40 -04:00
call search('[([{]', 'bW')
2016-09-11 07:24:17 -04:00
let l:cl = line('.')
let l:cc = col('.')
2016-07-30 07:18:40 -04:00
2016-09-11 07:24:17 -04:00
while l:n != s:getNesting(s:getHLStack(l:cl, l:cc)) || s:isSYN('haskellString', l:cl, l:cc) || s:isSYN('haskellChar', l:cl, l:cc)
2016-07-30 07:18:40 -04:00
call search('[([{]', 'bW')
2016-09-11 07:24:17 -04:00
let l:cl = line('.')
let l:cc = col('.')
2016-07-30 07:18:40 -04:00
endwhile
2016-09-11 07:24:17 -04:00
return l:cc - 1
2016-07-30 07:18:40 -04:00
else
let l:s = s:indentGuard(match(l:line, ','), l:prevline)
if l:s > -1
return l:s
end
endif
endif
2016-05-02 04:42:37 -04:00
" operator at end of previous line
if l:prevline =~ '[!#$%&*+./<>?@\\^|~-]\s*$'
return match(l:prevline, '\S') + &shiftwidth
endif
2016-05-02 04:42:37 -04:00
" let foo =
" >>>>>>bar
if l:prevline =~ '\C\<let\>\s\+[^=]\+=\s*$'
return match(l:prevline, '\C\<let\>') + g:haskell_indent_let + &shiftwidth
endif
2016-05-02 04:42:37 -04:00
" let x = 1 in
" >>>>x
if l:prevline =~ '\C\<let\>\s\+.\+\<in\>\?$' && l:line !~ '\C^\s*\<in\>'
return match(l:prevline, '\C\<let\>') + g:haskell_indent_let
endif
2016-05-02 04:42:37 -04:00
" let x = 1
" let y = 2
"
" let x = 1
" >in x
"
" let x = 1
" >>>>y = 2
2017-09-27 13:57:29 -04:00
"
" let x = 1
" y 2
2016-05-02 04:42:37 -04:00
if l:prevline =~ '\C\<let\>\s\+.\+$'
if l:line =~ '\C^\s*\<let\>'
2016-09-11 07:24:17 -04:00
let l:s = match(l:prevline, '\C\<let\>')
if s:isSYN('haskellLet', v:lnum - 1, l:s + 1)
return l:s
endif
2016-05-02 04:42:37 -04:00
elseif l:line =~ '\C^\s*\<in\>'
2016-09-11 07:24:17 -04:00
let l:s = match(l:prevline, '\C\<let\>')
if s:isSYN('haskellLet', v:lnum - 1, l:s + 1)
return l:s + g:haskell_indent_in
endif
2017-09-27 13:57:29 -04:00
elseif l:line =~ '\s=\s'
2016-09-11 07:24:17 -04:00
let l:s = match(l:prevline, '\C\<let\>')
if s:isSYN('haskellLet', v:lnum - 1, l:s + 1)
return l:s + g:haskell_indent_let
endif
2017-09-27 13:57:29 -04:00
else
let l:s = match(l:prevline, '\C\<let\>')
if s:isSYN('haskellLet', v:lnum - 1, l:s + 1)
return l:s + g:haskell_indent_let_no_in
endif
2016-05-02 04:42:37 -04:00
endif
endif
" if handling
if l:prevline !~ '\C\<else\>'
let l:s = match(l:prevline, '\C\<if\>.*\&.*\zs\<then\>')
if l:s > 0
return l:s
endif
let l:s = match(l:prevline, '\C\<if\>')
if l:s > 0
return l:s + g:haskell_indent_if
endif
endif
2016-05-02 04:42:37 -04:00
" where
" >>foo
"
2017-05-17 05:07:28 -04:00
if l:prevline =~ '\C\<where\>\s*$'
return match(l:prevline, '\S') + get(g:, 'haskell_indent_after_bare_where', &shiftwidth)
endif
2016-05-02 04:42:37 -04:00
" do
" >>foo
"
" foo =
" >>bar
2017-05-17 05:07:28 -04:00
if l:prevline =~ '\C\(\<do\>\|=\)\s*$'
return match(l:prevline, '\S') + &shiftwidth
endif
2016-05-02 04:42:37 -04:00
" do foo
" >>>bar
if l:prevline =~ '\C\<do\>\s\+\S\+.*$'
2016-09-11 07:24:17 -04:00
let l:s = match(l:prevline, '\C\<do\>')
if s:isSYN('haskellKeyword', v:lnum - 1, l:s + 1)
return l:s + g:haskell_indent_do
endif
endif
2016-05-02 04:42:37 -04:00
" case foo of
" >>bar -> quux
if l:prevline =~ '\C\<case\>.\+\<of\>\s*$'
2017-05-17 05:07:28 -04:00
if get(g:,'haskell_indent_case_alternative', 0)
2016-12-20 14:57:20 -05:00
return match(l:prevline, '\S') + &shiftwidth
else
return match(l:prevline, '\C\<case\>') + g:haskell_indent_case
endif
2016-05-02 04:42:37 -04:00
endif
2016-09-11 07:24:17 -04:00
"" where foo
"" >>>>>>bar
if l:prevline =~ '\C\<where\>\s\+\S\+.*$'
if l:line =~ '^\s*[=-]>\s' && l:prevline =~ ' :: '
return match(l:prevline, ':: ')
else
let l:s = match(l:prevline, '\C\<where\>')
if s:isSYN('haskellWhere', v:lnum - 1, l:s + 1)
return l:s + g:haskell_indent_where
endif
endif
endif
2016-05-02 04:42:37 -04:00
" newtype Foo = Foo
" >>deriving
if l:prevline =~ '\C\s*\<\(newtype\|data\)\>[^{]\+' && l:line =~ '\C^\s*\<deriving\>'
return match(l:prevline, '\S') + &shiftwidth
endif
" foo :: Int
" >>>>-> Int
"
" foo
" :: Int
" foo
if l:prevline =~ '\s::\s'
if l:line =~ '^\s*[-=]>'
return match(l:prevline, '::\s')
elseif match(l:prevline, '^\s\+::') > -1
return match(l:prevline, '::\s') - &shiftwidth
endif
endif
2017-05-17 05:07:28 -04:00
" foo :: Int
" -> Int
" >>>>-> Int
"
" foo :: Monad m
" => Functor f
" >>>>=> Int
"
2016-05-02 04:42:37 -04:00
" foo :: Int
" -> Int
" foo x
"
" foo
" :: Int
" -> Int
" foo x
2017-05-17 05:07:28 -04:00
if l:prevline =~ '^\s*[-=]>'
if l:line =~ '^\s*[-=]>'
return match(l:prevline, '[-=]')
2016-05-02 04:42:37 -04:00
else
2017-05-17 05:07:28 -04:00
if s:isInBlock(l:hlstack)
return match(l:prevline, '[^-=]')
else
let l:m = matchstr(l:line, '^\s*\zs\<\S\+\>\ze')
let l:l = l:prevline
let l:c = 1
while v:lnum != l:c
" fun decl
if l:l =~ ('^\s*' . l:m . '\(\s*::\|\n\s\+::\)')
let l:s = match(l:l, l:m)
if match(l:l, '\C^\s*\<default\>') > -1
return l:s - 8
else
return l:s
endif
" empty line, stop looking
elseif l:l =~ '^$'
return 0
2016-05-02 04:42:37 -04:00
endif
2017-05-17 05:07:28 -04:00
let l:c += 1
let l:l = getline(v:lnum - l:c)
endwhile
2016-05-02 04:42:37 -04:00
2017-05-17 05:07:28 -04:00
return 0
endif
2016-05-02 04:42:37 -04:00
endif
endif
" | otherwise = ...
" foo
"
" | foo
" >>, bar
"
" | foo
" >>= bar
"
" | Foo
" >>deriving
if l:prevline =~ '^\s\+|' && !s:isInBlock(l:hlstack)
if l:line =~ '\s*[,=]'
return match(l:prevline, '|')
elseif l:line =~ '\C^\s*\<deriving\>'
return match(l:prevline, '|')
elseif l:line !~ '^\s*|'
return match(l:prevline, '|') - g:haskell_indent_guard
endif
endif
" foo :: ( Monad m
" , Functor f
" )
">>>>>=> Int
if l:prevline =~ '^\s*)' && l:line =~ '^\s*=>'
let l:s = match(l:prevline, ')')
return l:s - (&shiftwidth + 1)
endif
" module Foo
" >>( bar
if l:prevline =~ '\C^\<module\>'
return &shiftwidth
endif
" foo
" >>{
2017-03-23 06:28:19 -04:00
if l:line =~ '^\s*{ '
2016-05-13 09:54:46 -04:00
let l:s = match(l:prevline, '\S')
if l:s >= 0
return l:s + &shiftwidth
endif
2016-05-02 04:42:37 -04:00
endif
" in foo
" where bar
2017-05-17 05:07:28 -04:00
"
" or
"
" foo
" >>where
2016-05-02 04:42:37 -04:00
if l:line =~ '\C^\s*\<where\>'
if match(l:prevline, '\C^\s\+in\s\+') == 0
return match(l:prevline, 'in') - g:haskell_indent_in
endif
2017-05-17 05:07:28 -04:00
return match(l:prevline, '\S') + get(g:, 'haskell_indent_before_where', &shiftwidth)
2016-05-02 04:42:37 -04:00
endif
" let x = 1
" y = 2
" >in x + 1
if l:line =~ '\C^\s*\<in\>'
return match(l:prevline, '\S') - (4 - g:haskell_indent_in)
endif
" data Foo
" >>= Bar
"
" |
" ...
" >>=
"
" foo
" >>=
if l:line =~ '^\s*='
if l:prevline =~ '\C^\<data\>\s\+[^=]\+\s*$'
return match(l:prevline, '\C\<data\>') + &shiftwidth
else
let l:s = s:indentGuard(match(l:line, '='), l:prevline)
if l:s > 0
return l:s
else
return &shiftwidth
endif
endif
endif
" |
" ...
" >>|
"
" data Foo = Bar
" >>>>>>>>>|
if l:line =~ '^\s*|\s'
if l:prevline =~ '\C^\s*\<data\>.\+=.\+$'
return match(l:prevline, '=')
2016-05-02 04:42:37 -04:00
else
let l:s = s:indentGuard(match(l:line, '|'), l:prevline)
if l:s > -1
return l:s
endif
endif
endif
2016-05-02 04:42:37 -04:00
" foo
" >>:: Int
if l:line =~ '^\s*::\s'
return match(l:prevline, '\S') + &shiftwidth
endif
" indent closing brace, paren or bracket
if l:line =~ '^\s*}'
return s:indentMatching('}')
endif
if l:line =~ '^\s*)'
return s:indentMatching(')')
endif
if l:line =~ '^\s*]'
return s:indentMatching(']')
endif
2017-03-23 06:28:19 -04:00
return -1
endfunction
endif