summaryrefslogtreecommitdiffstats
path: root/indent/haskell.vim
diff options
context:
space:
mode:
authorAdam Stankiewicz <sheerun@sher.pl>2015-07-18 23:22:55 +0200
committerAdam Stankiewicz <sheerun@sher.pl>2015-07-18 23:22:55 +0200
commitcf1e53bc39c96b9f5586a68efa118a13c615da13 (patch)
treeeb6c96a71098df7fc2a38df4606f32a3bfcc925e /indent/haskell.vim
parent92ab75408df8bff49bb29e113b3cc159d1ac3105 (diff)
downloadvim-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.vim348
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