diff options
author | Adam Stankiewicz <sheerun@sher.pl> | 2015-07-18 23:22:55 +0200 |
---|---|---|
committer | Adam Stankiewicz <sheerun@sher.pl> | 2015-07-18 23:22:55 +0200 |
commit | cf1e53bc39c96b9f5586a68efa118a13c615da13 (patch) | |
tree | eb6c96a71098df7fc2a38df4606f32a3bfcc925e /indent/haskell.vim | |
parent | 92ab75408df8bff49bb29e113b3cc159d1ac3105 (diff) | |
download | vim-polyglot-cf1e53bc39c96b9f5586a68efa118a13c615da13.tar.gz vim-polyglot-cf1e53bc39c96b9f5586a68efa118a13c615da13.zip |
Changed haskell provider to raichoo/haskell-vim, closes #63
Diffstat (limited to 'indent/haskell.vim')
-rw-r--r-- | indent/haskell.vim | 348 |
1 files changed, 114 insertions, 234 deletions
diff --git a/indent/haskell.vim b/indent/haskell.vim index 6a16e735..8af1aedd 100644 --- a/indent/haskell.vim +++ b/indent/haskell.vim @@ -1,8 +1,15 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'haskell') == -1 -" Vim indent file -" Language: Haskell -" Maintainer: Tristan Ravitch +" indentation for haskell +" +" Based on idris indentation +" +" 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 @@ -10,291 +17,164 @@ endif let b:did_indent = 1 -if !exists('g:hasksyn_indent_search_backward') - let g:hasksyn_indent_search_backward = 100 +if !exists('g:haskell_indent_if') + " if bool + " >>>then ... + " >>>else ... + let g:haskell_indent_if = 3 endif -if !exists('g:hasksyn_dedent_after_return') - let g:hasksyn_dedent_after_return = 1 +if !exists('g:haskell_indent_case') + " case xs of + " >>[] -> ... + " >>(y:ys) -> ... + let g:haskell_indent_case = 2 endif -if !exists('g:hasksyn_dedent_after_catchall_case') - let g:hasksyn_dedent_after_catchall_case = 1 +if !exists('g:haskell_indent_let') + " let x = 0 in + " >>>>x + let g:haskell_indent_let = 4 endif -setlocal noautoindent -setlocal indentexpr=HIndent(v:lnum) -setlocal indentkeys+=0=where -setlocal indentkeys+=0=-> -setlocal indentkeys+=0==> -setlocal indentkeys+=0=in -setlocal indentkeys+=0=class,0=instance,0=import -setlocal indentkeys+=<Bar> -setlocal indentkeys+=0\, - -if exists("*HIndent") - finish +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 -function! HIndent(lnum) - " Don't do anything boneheaded if we are inside of a block comment - if s:IsInBlockComment() - return -1 - endif +if !exists('g:haskell_indent_in') + " let x = 1 + " >in x + let g:haskell_indent_in = 1 +endif - let plnum = s:PrevNonCommentLineNum(a:lnum) - if plnum == 0 - return 0 - endif +setlocal indentexpr=GetHaskellIndent() +setlocal indentkeys=!^F,o,O,0\|,0=where,0=in,0=let,0=deriving,0=->,0=\=>,<CR>,0} - let prevl = s:GetAndStripTrailingComments(plnum) - let thisl = s:GetAndStripTrailingComments(a:lnum) - let previ = indent(plnum) - - " If this is a bare where clause, indent it one step. where as part of an - " instance should be unaffected unless you put it in an odd place. - " This is the wrong thing if you are deeply indented already and want to put - " a where clause on the top-level construct, but there isn't much that can - " be done about that case... - if thisl =~ '^\s*where\s*' - return previ + &sw - endif +function! GetHaskellIndent() + let l:prevline = getline(v:lnum - 1) - " If we start a new line for a type signature, see if we can line it up with - " the previous line. - if thisl =~ '^\s*\(->\|=>\)\s*' - let tokPos = s:BackwardPatternSearch(a:lnum, '\(::\|->\|=>\)') - if tokPos != -1 - return tokPos - endif + if l:prevline =~ '^\s*--' + return match(l:prevline, '\S') endif - if prevl =~ '\Wof\s*$' || prevl =~ '\Wm\=do\s*$' - return previ + &sw + if l:prevline =~ '^\s*$' + return 0 endif - " Now for commas. Commas will align pretty naturally for simple pattern - " guards, so don't worry about that for now. If we see the line is just a - " comma, search up for something to align it to. In the easy case, look - " for a [ or { (the last in their line). Also consider other commas that - " are preceeded only by whitespace. This isn't just a previous line check - " necessarily, though that would cover most cases. - if thisl =~ '^\s*,' - let cmatch = match(prevl, '\(^\s*\)\@<=,') - if cmatch != -1 - return cmatch - endif + let l:line = getline(v:lnum) - let bmatch = match(prevl, '\({\|\[\)') - if bmatch != -1 - return bmatch - endif + if l:line =~ '\C^\s*\<where\>' + let l:s = match(l:prevline, '\S') + return l:s + &shiftwidth endif - " Match an 'in' keyword with the corresponding let. Unfortunately, if the - " name of your next binding happens to start with 'in', this will muck with - " it. Not sure if there is a workaround because we can't force an - " auto-indent after 'in ' as far as I can see. - if thisl =~ '\s*in$' - let letStart = s:BackwardPatternSearch(a:lnum, '\(\W\)\@<=let\W') - if letStart != -1 - return letStart + if l:line =~ '\C^\s*\<deriving\>' + let l:s = match(l:prevline, '\C\<\(newtype\|data\)\>') + if l:s >= 0 + return l:s + &shiftwidth endif endif - " We don't send data or type to column zero because they can be indented - " inside of 'class' definitions for data/type families - if thisl =~ '^\s*\(class\|instance\|newtype\|import\)' - return 0 - endif - - " FIXME: Only do this if the previous line was not already indented for the - " same reason. Also be careful of -> in type signatures. Make sure we have - " an earlier rule to line those up properly. - if prevl =~ '[=>\$\.\^+\&`(-]\s*$' - return previ + &sw + if l:line =~ '\C^\s*\<let\>' + let l:s = match(l:prevline, '\C\<let\>') + if l:s != 0 + return l:s + endif endif - " We have a special case for dealing with trailing '*' operators. If the * - " is the end of a kind signature in a type family/associated type, we don't - " want to indent the next line. We do if it is just being a * operator in - " an expression, though. - if prevl =~ '\(\(type\|data\).*\)\@<!\*\s*$' - return previ + &sw + if l:line =~ '\C^\s*\<in\>' + let l:s = match(l:prevline, '\C\<let\>') + if l:s >= 0 + return l:s + g:haskell_indent_in + elseif match(l:prevline, '=') > 0 + let l:s = match(l:prevline, '\S') + return l:s - (4 - g:haskell_indent_in) + endif endif - " If the previous line ends in a where, indent us a step - if prevl =~ '\Wwhere\s*$' - return previ + &sw + if l:line =~ '^\s*|' + if match(l:prevline, '^\s*data') < 0 + if match(l:prevline, '^\s*|\s') >= 0 + return match(l:prevline, '|') + else + return &shiftwidth + endif + endif endif - " If we see a |, first try to line it up with the pipe on the previous line. - " Search backward on nearby lines, giving up if we hit a line with a \w at - " column 0. Otherwise, indent it relative to the previous line - " - " Here we can also handle the case of lining up data declarations. The - " backwards pipe search will fail for a data declaration (since data is at - " column 0), so we can have an extra check after the pipe search for - " data..=. - if thisl =~ '^\s*|$' - let nearestPipeIndex = s:BackwardPatternSearch(a:lnum, '\(^\s*\)\@<=|') - if nearestPipeIndex != -1 - return nearestPipeIndex + if l:line =~ '^\s*[=-]>' + let l:s = match(l:prevline, ' :: ') + if l:s >= 0 + return l:s + 1 endif + endif - let dataEquals = match(prevl, '\(data.*\)\@<==') - if dataEquals != -1 - return dataEquals + if l:prevline =~ '\s\+[!#$%&*+./<>?@\\^|~-]\+\s*$' + let l:s = match(l:prevline, '\S') + if l:s > 0 + return l:s + &shiftwidth endif - - return previ + &sw endif - " If the previous line has a let, line the cursor up with the start of the - " first binding name. Autoindent handles subsequent cases. - " - " This should come after the 'in' aligner so that 'in' is not treated as - " just something to be aligned to the previous binding. - let lbindStart = match(prevl, '\(\Wlet\s\+\)\@<=\w') - if lbindStart != -1 - return lbindStart + if l:prevline =~ '[{([][^})\]]\+$' + return match(l:prevline, '[{([]') endif - " If requested, dedent from a bare return (presumably in a do block). - " This comes after the trailing operator case - hopefully that will avoid - " returns on lines by themselves but not really in a do block. This is a - " heuristic. - if g:hasksyn_dedent_after_return && prevl =~ '^\s*return\W' - return previ - &sw + if l:prevline =~ '\C\<let\>\s\+[^=]\+=\s*$' + return match(l:prevline, '\C\<let\>') + g:haskell_indent_let + &shiftwidth endif - " Similar to the return dedent - after a catchall case _ -> ..., we can - " almost certainly dedent. Again, it comes after the line continuation - " heuristic so we don't dedent while someone is making an obviously - " multi-line construct - if g:hasksyn_dedent_after_catchall_case && prevl =~ '^\s*_\s*->\W' - return previ - &sw + if l:prevline =~ '\C\<let\>\s\+.\+\(\<in\>\)\?\s*$' + return match(l:prevline, '\C\<let\>') + g:haskell_indent_let endif - " On the other hand, if the previous line is a do or where with some bindings - " following it on the same line, accommodate and align with the first non-ws - " char after the where - if prevl =~ '\W\(do\|where\)\s\+\w' - let bindStart = match(prevl, '\(\W\(do\|where\)\s\+\)\@<=\w') - if bindStart != -1 - return bindStart + if l:prevline !~ '\C\<else\>' + let l:s = match(l:prevline, '\C\<if\>.*\&.*\zs\<then\>') + if l:s > 0 + return l:s endif - return previ + &sw + let l:s = match(l:prevline, '\C\<if\>') + if l:s > 0 + return l:s + g:haskell_indent_if + endif endif - return previ -endfunction - -" Search backwards for a token from the cursor position -function! s:FindTokenNotInCommentOrString(tok) - return search('\(--.*\|"\([^"]\|\\"\)*\)\@<!' . tok, 'bcnW') -endfunction - -" Should return -1 if the given line is inside of an unclosed block comment. -" This is meant to let us exit early from the indenter if we are in a comment. -" Look for the nearest -} and {- such that they are not between "" or in a -" line comment -" -" Note: we may need to restrict how far back this will search. On the other -" hand, the native vim 'search' function might be efficient enough to support -" entire buffers. -function! s:IsInBlockComment() - let openCommPos = s:FindTokenNotInCommentOrString('{-') - " If there is no open comment, then we don't have to look for a close - if openCommPos == 0 - return 0 + if l:prevline =~ '\C\(\<where\>\|\<do\>\|=\|[{([]\)\s*$' + return match(l:prevline, '\S') + &shiftwidth endif - " Or if there is a close comment marker that comes after the open marker, we - " are not in a comment. Note that we potentially need to check the position - " in the line if they are both on the same line. I'll fix it later. - let closeCommPos = s:FindTokenNotInCommentOrString('-}') - if closeCommPos >= openCommPos - return 0 + if l:prevline =~ '\C\<where\>\s\+\S\+.*$' + return match(l:prevline, '\C\<where\>') + g:haskell_indent_where endif - return 1 -endfunction - -" Get the previous line that is not a comment. Pass in the *current* line -" number. Also skips blank lines. -function! s:PrevNonCommentLineNum(lnum) - if a:lnum <= 1 - return 0 + if l:prevline =~ '\C\<do\>\s\+\S\+.*$' + return match(l:prevline, '\C\<do\>') + g:haskell_indent_do endif - let lnum = a:lnum - 1 - - while 1 - if lnum == 0 - return 0 + if l:prevline =~ '\C^\s*\<data\>\s\+[^=]\+\s\+=\s\+\S\+.*$' + if l:line =~ '^\s*|' + return match(l:prevline, '=') endif - - let aline = getline(lnum) - if aline =~ '^\s*--' - let lnum = lnum - 1 - else - return lnum - endif - endwhile -endfunction - -function! s:GetAndStripTrailingComments(lnum) - let aline = getline(a:lnum) - " We can't just remove the string literal since that leaves us with a - " trailing operator (=), so replace it with a fake identifier - let noStrings = substitute(aline, '"\([^"]\|\\"\)*"', '\=repeat("s", len(submatch(0)))', '') - let noLineCom = substitute(noStrings, '--.*$', '', '') - - " If there are no fancy block comments involved, skip some of this extra - " work - if noLineCom !~ '\({-\|-}\)' - return noLineCom endif - " We stripped line comments, now we need to strip out any relevant multiline - " comments. This includes comments starting much earlier but ending on this - " line or comments starting on this line and continuing to the next. This - " is probably easiest in two steps: {- to (-}|$) and then ^ to -}. - " Note we are using a non-greedy match here so that only the minimal {- -} - " pair is consumed. - let noBlock1 = substitute(noLineComm, '{-.\{-}-}', '', '') - let noBlock2 = substitute(noBlock1, '{-.\{-}$', '', '') - let noBlock3 = substitute(noBlock2, '^.\{-}-}', '', '') - return noBlock3 -endfunction + if l:prevline =~ '\C\<case\>\s\+.\+\<of\>\s*$' + return match(l:prevline, '\C\<case\>') + g:haskell_indent_case + endif -" Search backwards from lnum for pat, returning the starting index if found -" within the search range or -1 if not found. Stops searching at lines -" starting at column 0 with an identifier character. -function! s:BackwardPatternSearch(lnum, pat) - let lnum = s:PrevNonCommentLineNum(a:lnum) - while 1 - let aline = s:GetAndStripTrailingComments(lnum) - if a:lnum - lnum > g:hasksyn_indent_search_backward - return -1 - endif + if l:prevline =~ '\C^\s*\<\data\>\s\+\S\+\s*$' + return match(l:prevline, '\C\<data\>') + &shiftwidth + endif - let theMatch = match(aline, a:pat) - if theMatch != -1 - return theMatch - else - " We want to be able to consider lines starting in column 0, but we don't - " want to search back past them. - if aline =~ '^\w' - return -1 - endif - let lnum = s:PrevNonCommentLineNum(lnum) - endif - endwhile + return match(l:prevline, '\S') endfunction - endif |