ocaml.vim (9287B)
1 " Vim indent file 2 " Language: OCaml 3 " Maintainers: Jean-Francois Yuen <jfyuen@happycoders.org> 4 " Mike Leary <leary@nwlink.com> 5 " Markus Mottl <markus.mottl@gmail.com> 6 " URL: https://github.com/ocaml/vim-ocaml 7 " Last Change: 2017 Jun 13 8 " 2005 Jun 25 - Fixed multiple bugs due to 'else\nreturn ind' working 9 " 2005 May 09 - Added an option to not indent OCaml-indents specially (MM) 10 " 2013 June - commented textwidth (Marc Weber) 11 " 12 " Marc Weber's comment: This file may contain a lot of (very custom) stuff 13 " which eventually should be moved somewhere else .. 14 15 " Only load this indent file when no other was loaded. 16 if exists("b:did_indent") 17 finish 18 endif 19 let b:did_indent = 1 20 21 setlocal expandtab 22 setlocal indentexpr=GetOCamlIndent() 23 setlocal indentkeys+=0=and,0=class,0=constraint,0=done,0=else,0=end,0=exception,0=external,0=if,0=in,0=include,0=inherit,0=initializer,0=let,0=method,0=open,0=then,0=type,0=val,0=with,0;;,0>\],0\|\],0>},0\|,0},0\],0) 24 setlocal nolisp 25 setlocal nosmartindent 26 27 let b:undo_indent = "setl et< inde< indk< lisp< si<" 28 29 " At least Marc Weber and Markus Mottl do not like this: 30 " setlocal textwidth=80 31 32 " Comment formatting 33 if !exists("no_ocaml_comments") 34 if (has("comments")) 35 setlocal comments=sr:(*\ ,mb:\ ,ex:*) 36 setlocal comments^=sr:(**,mb:\ \ ,ex:*) 37 setlocal fo=cqort 38 let b:undo_indent .= " | setl com< fo<" 39 endif 40 endif 41 42 " Only define the function once. 43 if exists("*GetOCamlIndent") 44 finish 45 endif 46 47 " Define some patterns: 48 let s:beflet = '^\s*\(initializer\|method\|try\)\|\(\<\(begin\|do\|else\|in\|then\|try\)\|->\|<-\|=\|;\|(\)\s*$' 49 let s:letpat = '^\s*\(let\|type\|module\|class\|open\|exception\|val\|include\|external\)\>' 50 let s:letlim = '\(\<\(sig\|struct\)\|;;\)\s*$' 51 let s:lim = '^\s*\(exception\|external\|include\|let\|module\|open\|type\|val\)\>' 52 let s:module = '\<\%(begin\|sig\|struct\|object\)\>' 53 let s:obj = '^\s*\(constraint\|inherit\|initializer\|method\|val\)\>\|\<\(object\|object\s*(.*)\)\s*$' 54 let s:type = '^\s*\%(class\|let\|type\)\>.*=' 55 56 " Skipping pattern, for comments 57 function! s:GetLineWithoutFullComment(lnum) 58 let lnum = prevnonblank(a:lnum - 1) 59 let lline = substitute(getline(lnum), '(\*.*\*)\s*$', '', '') 60 while lline =~ '^\s*$' && lnum > 0 61 let lnum = prevnonblank(lnum - 1) 62 let lline = substitute(getline(lnum), '(\*.*\*)\s*$', '', '') 63 endwhile 64 return lnum 65 endfunction 66 67 " Indent for ';;' to match multiple 'let' 68 function! s:GetInd(lnum, pat, lim) 69 let llet = search(a:pat, 'bW') 70 let old = indent(a:lnum) 71 while llet > 0 72 let old = indent(llet) 73 let nb = s:GetLineWithoutFullComment(llet) 74 if getline(nb) =~ a:lim 75 return old 76 endif 77 let llet = search(a:pat, 'bW') 78 endwhile 79 return old 80 endfunction 81 82 " Indent pairs 83 function! s:FindPair(pstart, pmid, pend) 84 call search(a:pend, 'bW') 85 return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"')) 86 endfunction 87 88 " Indent 'let' 89 function! s:FindLet(pstart, pmid, pend) 90 call search(a:pend, 'bW') 91 return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment" || getline(".") =~ "^\\s*let\\>.*=.*\\<in\\s*$" || getline(prevnonblank(".") - 1) =~ s:beflet')) 92 endfunction 93 94 function! GetOCamlIndent() 95 " Find a non-commented line above the current line. 96 let lnum = s:GetLineWithoutFullComment(v:lnum) 97 98 " At the start of the file use zero indent. 99 if lnum == 0 100 return 0 101 endif 102 103 let ind = indent(lnum) 104 let lline = substitute(getline(lnum), '(\*.*\*)\s*$', '', '') 105 106 " Return double 'shiftwidth' after lines matching: 107 if lline =~ '^\s*|.*->\s*$' 108 return ind + 2 * shiftwidth() 109 endif 110 111 let line = getline(v:lnum) 112 113 " Indent if current line begins with 'end': 114 if line =~ '^\s*end\>' 115 return s:FindPair(s:module, '','\<end\>') 116 117 " Indent if current line begins with 'done' for 'do': 118 elseif line =~ '^\s*done\>' 119 return s:FindPair('\<do\>', '','\<done\>') 120 121 " Indent if current line begins with '}' or '>}': 122 elseif line =~ '^\s*\(\|>\)}' 123 return s:FindPair('{', '','}') 124 125 " Indent if current line begins with ']', '|]' or '>]': 126 elseif line =~ '^\s*\(\||\|>\)\]' 127 return s:FindPair('\[', '','\]') 128 129 " Indent if current line begins with ')': 130 elseif line =~ '^\s*)' 131 return s:FindPair('(', '',')') 132 133 " Indent if current line begins with 'let': 134 elseif line =~ '^\s*let\>' 135 if lline !~ s:lim . '\|' . s:letlim . '\|' . s:beflet 136 return s:FindLet(s:type, '','\<let\s*$') 137 endif 138 139 " Indent if current line begins with 'class' or 'type': 140 elseif line =~ '^\s*\(class\|type\)\>' 141 if lline !~ s:lim . '\|\<and\s*$\|' . s:letlim 142 return s:FindLet(s:type, '','\<\(class\|type\)\s*$') 143 endif 144 145 " Indent for pattern matching: 146 elseif line =~ '^\s*|' 147 if lline !~ '^\s*\(|[^\]]\|\(match\|type\|with\)\>\)\|\<\(function\|parser\|private\|with\)\s*$' 148 call search('|', 'bW') 149 return indent(searchpair('^\s*\(match\|type\)\>\|\<\(function\|parser\|private\|with\)\s*$', '', '^\s*|', 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment" || getline(".") !~ "^\\s*|.*->"')) 150 endif 151 152 " Indent if current line begins with ';;': 153 elseif line =~ '^\s*;;' 154 if lline !~ ';;\s*$' 155 return s:GetInd(v:lnum, s:letpat, s:letlim) 156 endif 157 158 " Indent if current line begins with 'in': 159 elseif line =~ '^\s*in\>' 160 if lline !~ '^\s*\(let\|and\)\>' 161 return s:FindPair('\<let\>', '', '\<in\>') 162 endif 163 164 " Indent if current line begins with 'else': 165 elseif line =~ '^\s*else\>' 166 if lline !~ '^\s*\(if\|then\)\>' 167 return s:FindPair('\<if\>', '', '\<else\>') 168 endif 169 170 " Indent if current line begins with 'then': 171 elseif line =~ '^\s*then\>' 172 if lline !~ '^\s*\(if\|else\)\>' 173 return s:FindPair('\<if\>', '', '\<then\>') 174 endif 175 176 " Indent if current line begins with 'and': 177 elseif line =~ '^\s*and\>' 178 if lline !~ '^\s*\(and\|let\|type\)\>\|\<end\s*$' 179 return ind - shiftwidth() 180 endif 181 182 " Indent if current line begins with 'with': 183 elseif line =~ '^\s*with\>' 184 if lline !~ '^\s*\(match\|try\)\>' 185 return s:FindPair('\<\%(match\|try\)\>', '','\<with\>') 186 endif 187 188 " Indent if current line begins with 'exception', 'external', 'include' or 189 " 'open': 190 elseif line =~ '^\s*\(exception\|external\|include\|open\)\>' 191 if lline !~ s:lim . '\|' . s:letlim 192 call search(line) 193 return indent(search('^\s*\(\(exception\|external\|include\|open\|type\)\>\|val\>.*:\)', 'bW')) 194 endif 195 196 " Indent if current line begins with 'val': 197 elseif line =~ '^\s*val\>' 198 if lline !~ '^\s*\(exception\|external\|include\|open\)\>\|' . s:obj . '\|' . s:letlim 199 return indent(search('^\s*\(\(exception\|include\|initializer\|method\|open\|type\|val\)\>\|external\>.*:\)', 'bW')) 200 endif 201 202 " Indent if current line begins with 'constraint', 'inherit', 'initializer' 203 " or 'method': 204 elseif line =~ '^\s*\(constraint\|inherit\|initializer\|method\)\>' 205 if lline !~ s:obj 206 return indent(search('\<\(object\|object\s*(.*)\)\s*$', 'bW')) + shiftwidth() 207 endif 208 209 endif 210 211 " Add a 'shiftwidth' after lines ending with: 212 if lline =~ '\(:\|=\|->\|<-\|(\|\[\|{\|{<\|\[|\|\[<\|\<\(begin\|do\|else\|fun\|function\|functor\|if\|initializer\|object\|parser\|private\|sig\|struct\|then\|try\)\|\<object\s*(.*)\)\s*$' 213 let ind = ind + shiftwidth() 214 215 " Back to normal indent after lines ending with ';;': 216 elseif lline =~ ';;\s*$' && lline !~ '^\s*;;' 217 let ind = s:GetInd(v:lnum, s:letpat, s:letlim) 218 219 " Back to normal indent after lines ending with 'end': 220 elseif lline =~ '\<end\s*$' 221 let ind = s:FindPair(s:module, '','\<end\>') 222 223 " Back to normal indent after lines ending with 'in': 224 elseif lline =~ '\<in\s*$' && lline !~ '^\s*in\>' 225 let ind = s:FindPair('\<let\>', '', '\<in\>') 226 227 " Back to normal indent after lines ending with 'done': 228 elseif lline =~ '\<done\s*$' 229 let ind = s:FindPair('\<do\>', '','\<done\>') 230 231 " Back to normal indent after lines ending with '}' or '>}': 232 elseif lline =~ '\(\|>\)}\s*$' 233 let ind = s:FindPair('{', '','}') 234 235 " Back to normal indent after lines ending with ']', '|]' or '>]': 236 elseif lline =~ '\(\||\|>\)\]\s*$' 237 let ind = s:FindPair('\[', '','\]') 238 239 " Back to normal indent after comments: 240 elseif lline =~ '\*)\s*$' 241 call search('\*)', 'bW') 242 let ind = indent(searchpair('(\*', '', '\*)', 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"')) 243 244 " Back to normal indent after lines ending with ')': 245 elseif lline =~ ')\s*$' 246 let ind = s:FindPair('(', '',')') 247 248 " If this is a multiline comment then align '*': 249 elseif lline =~ '^\s*(\*' && line =~ '^\s*\*' 250 let ind = ind + 1 251 252 else 253 " Don't change indentation of this line 254 " for new lines (indent==0) use indentation of previous line 255 256 " This is for preventing removing indentation of these args: 257 " let f x = 258 " let y = x + 1 in 259 " Printf.printf 260 " "o" << here 261 " "oeuth" << don't touch indentation 262 263 let i = indent(v:lnum) 264 return i == 0 ? ind : i 265 266 endif 267 268 " Subtract a 'shiftwidth' after lines matching 'match ... with parser': 269 if lline =~ '\<match\>.*\<with\>\s*\<parser\s*$' 270 let ind = ind - shiftwidth() 271 endif 272 273 return ind 274 275 endfunction 276 277 " vim:sw=2