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