1. Paul Hervot
  2. configuration

Commits

Paul Hervot  committed 58f11b9

vim: omlet for ocaml

  • Participants
  • Parent commits 7910f1a
  • Branches master

Comments (0)

Files changed (4)

File .vim/ftdetect/omlet.vim

View file
+au BufRead,BufNewFile *.ml,*.mli set ft=omlet
+" setf omlet would result in having omlet not loaded cause system's 
+" filetype.vim defines ocaml.vim for .ml files.
+" This is quite ugly, but we have to do this until ocaml.vim becomes omlet...

File .vim/ftplugin/omlet.vim

View file
+" Language:    OCaml
+" Maintainer:  David Baelde        <firstname.name@ens-lyon.org>
+"              Mike Leary          <leary@nwlink.com>
+"              Markus Mottl        <markus.mottl@gmail.com>
+"              Stefano Zacchiroli  <zack@bononia.it>
+" URL:         http://www.ocaml.info/vim/ftplugin/ocaml.vim
+" Last Change: 2005 March 25
+" Changelog:
+"         0.12 - Modeline support
+"              - Folding now works also when indent_struct != 2
+"              - Changed folding customization variable, default is off
+"              - Made folding settings local to the buffer
+"              - Included the official ftplugin ocaml.vim, except
+"                annotations stuff, and parenthesis around assert false
+"                abbreviation
+"              - Corrected toplevel let folding
+"              - Made the file reloading correctly
+"
+" omlet.vim plugins -- utilities for working on OCaml files with VIm
+" Copyright (C) 2005 D. Baelde, M. Leary, M. Mottl, S. Zacchiroli
+"
+" This program is free software; you can redistribute it and/or modify
+" it under the terms of the GNU General Public License as published by
+" the Free Software Foundation; either version 2 of the License, or
+" (at your option) any later version.
+"
+" This program is distributed in the hope that it will be useful,
+" but WITHOUT ANY WARRANTY; without even the implied warranty of
+" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+" GNU General Public License for more details.
+"
+" You should have received a copy of the GNU General Public License
+" along with this program; if not, write to the Free Software
+" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+" Folding is activated if ocaml_folding is set
+" Do these settings once per buffer
+
+" if exists("b:did_ftplugin")
+"   finish
+" endif
+let b:did_ftplugin=1
+
+" Error handling -- helps moving where the compiler wants you to go
+set cpo-=C
+setlocal efm=
+      \%EFile\ \"%f\"\\,\ line\ %l\\,\ characters\ %c-%*\\d:,
+      \%EFile\ \"%f\"\\,\ line\ %l\\,\ character\ %c:%m,
+      \%+EReference\ to\ unbound\ regexp\ name\ %m,
+      \%Eocamlyacc:\ e\ -\ line\ %l\ of\ \"%f\"\\,\ %m,
+      \%Wocamlyacc:\ w\ -\ %m,
+      \%-Zmake%.%#,
+      \%C%m
+
+" Add mappings, unless the user didn't want this.
+if !exists("no_plugin_maps") && !exists("no_ocaml_maps")
+  " (un)commenting
+  if !hasmapto('<Plug>Comment')
+    nmap <buffer> <LocalLeader>c <Plug>LUncomOn
+    vmap <buffer> <LocalLeader>c <Plug>BUncomOn
+    nmap <buffer> <LocalLeader>C <Plug>LUncomOff
+    vmap <buffer> <LocalLeader>C <Plug>BUncomOff
+  endif
+
+  nnoremap <buffer> <Plug>LUncomOn mz0i(* <ESC>$A *)<ESC>`z
+  nnoremap <buffer> <Plug>LUncomOff <ESC>:s/^(\* \(.*\) \*)/\1/<CR>
+  vnoremap <buffer> <Plug>BUncomOn <ESC>:'<,'><CR>`<O<ESC>0i(*<ESC>`>o<ESC>0i*)<ESC>`<
+  vnoremap <buffer> <Plug>BUncomOff <ESC>:'<,'><CR>`<dd`>dd`<
+
+  if !hasmapto('<Plug>Abbrev')
+    iabbrev <buffer> ASS (assert false)
+  endif
+endif
+
+" Let % jump between structure elements (due to Issac Trotts)
+let b:mw='\<let\>:\<and\>:\(\<in\>\|;;\),'
+let b:mw=b:mw . '\<if\>:\<then\>:\<else\>,\<do\>:\<done\>,'
+let b:mw=b:mw . '\<\(object\|sig\|struct\|begin\)\>:\<end\>'
+let b:match_words=b:mw
+
+" switching between interfaces (.mli) and implementations (.ml)
+if !exists("g:did_ocaml_switch")
+  let g:did_ocaml_switch = 1
+  map <LocalLeader>s :call OCaml_switch(0)<CR>
+  map <LocalLeader>S :call OCaml_switch(1)<CR>
+  fun OCaml_switch(newwin)
+    if (match(bufname(""), "\\.mli$") >= 0)
+      let fname = substitute(bufname(""), "\\.mli$", ".ml", "")
+      if (a:newwin == 1)
+        exec "new " . fname
+      else
+        exec "arge " . fname
+      endif
+    elseif (match(bufname(""), "\\.ml$") >= 0)
+      let fname = bufname("") . "i"
+      if (a:newwin == 1)
+        exec "new " . fname
+      else
+        exec "arge " . fname
+      endif
+    endif
+  endfun
+endif
+
+" Folding support
+
+" Get the modeline because folding depends on indentation
+let s:s = line2byte(line('.'))+col('.')-1
+if search('^\s*(\*:o\?caml:')
+  let s:modeline = getline(".")
+else
+  let s:modeline = ""
+endif
+if s:s > 0
+  exe 'goto' s:s
+endif
+
+" Get the indentation params
+let s:m = matchstr(s:modeline,'default\s*=\s*\d\+')
+if s:m != ""
+  let s:idef = matchstr(s:m,'\d\+')
+elseif exists("g:omlet_indent")
+  let s:idef = g:omlet_indent
+else
+  let s:idef = 2
+endif
+let s:m = matchstr(s:modeline,'struct\s*=\s*\d\+')
+if s:m != ""
+  let s:i = matchstr(s:m,'\d\+')
+elseif exists("g:omlet_indent_struct")
+  let s:i = g:omlet_indent_struct
+else
+  let s:i = s:idef
+endif
+
+" Set the folding method
+if exists("g:ocaml_folding")
+  setlocal foldmethod=expr
+  setlocal foldexpr=OMLetFoldLevel(v:lnum)
+endif
+
+" - Only definitions below, executed once -------------------------------------
+
+if exists("*OMLetFoldLevel")
+  finish
+endif
+
+function s:topindent(lnum)
+  let l = a:lnum
+  while l > 0
+    if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'
+      return indent(l)
+    endif
+    let l = l-1
+  endwhile
+  return -s:i
+endfunction
+
+function OMLetFoldLevel(l)
+
+  " This is for not merging blank lines around folds to them
+  if getline(a:l) !~ '\S'
+    return -1
+  endif
+
+  " We start folds for modules, classes, and every toplevel definition
+  if getline(a:l) =~ '^\s*\%(\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'
+    exe 'return ">' (indent(a:l)/s:i)+1 '"'
+  endif
+
+  " Toplevel let are detected thanks to the indentation
+  if getline(a:l) =~ '^\s*let\>' && indent(a:l) == s:i+s:topindent(a:l)
+    exe 'return ">' (indent(a:l)/s:i)+1 '"'
+  endif
+
+  " We close fold on end which are associated to struct, sig or object.
+  " We use syntax information to do that.
+  if getline(a:l) =~ '^\s*end\>' && synIDattr(synID(a:l, indent(a:l)+1, 0), "name") != "ocamlKeyword"
+    return (indent(a:l)/s:i)+1
+  endif
+
+  " Folds end on ;;
+  if getline(a:l) =~ '^\s*;;'
+    exe 'return "<' (indent(a:l)/s:i)+1 '"'
+  endif
+
+  " Comments around folds aren't merged to them.
+  if synIDattr(synID(a:l, indent(a:l)+1, 0), "name") == "ocamlComment"
+    return -1
+  endif
+
+  return '='
+endfunction
+
+" Vim support for OCaml .annot files (requires Vim with python support)
+"
+" Executing OCamlPrintType(<mode>) function will display in the Vim bottom
+" line(s) the type of an ocaml value getting it from the corresponding .annot
+" file (if any).  If Vim is in visual mode, <mode> should be "visual" and the
+" selected ocaml value correspond to the highlighted text, otherwise (<mode>
+" can be anything else) it corresponds to the literal found at the current
+" cursor position.
+"
+" .annot files are parsed lazily the first time OCamlPrintType is invoked; is
+" also possible to force the parsing using the OCamlParseAnnot() function.
+"
+" Typing ',3' will cause OCamlPrintType function to be invoked with
+" the right argument depending on the current mode (visual or not).
+"
+" Copyright (C) <2003-2004> Stefano Zacchiroli <zack@bononia.it>
+"
+" Created:        Wed, 01 Oct 2003 18:16:22 +0200 zack
+" LastModified:   Wed, 25 Aug 2004 18:28:39 +0200 zack
+"
+"
+" This program is free software; you can redistribute it and/or modify
+" it under the terms of the GNU General Public License as published by
+" the Free Software Foundation; either version 2 of the License, or
+" (at your option) any later version.
+"
+" This program is distributed in the hope that it will be useful,
+" but WITHOUT ANY WARRANTY; without even the implied warranty of
+" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+" GNU General Public License for more details.
+"
+" You should have received a copy of the GNU General Public License
+" along with this program; if not, write to the Free Software
+" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+if !has("python")
+  finish
+endif
+
+python << EOF
+
+import re
+import os
+import string
+import time
+import vim
+
+debug = False
+
+class AnnExc(Exception):
+    def __init__(self, reason):
+        self.reason = reason
+
+no_annotations = AnnExc("No type annotations (.annot) file found")
+annotation_not_found = AnnExc("No type annotation found for the given text")
+def malformed_annotations(lineno):
+    return AnnExc("Malformed .annot file (line = %d)" % lineno)
+
+class Annotations:
+    """
+      .annot ocaml file representation
+
+      File format (copied verbatim from caml-types.el)
+
+      file ::= block *
+      block ::= position <SP> position <LF> annotation *
+      position ::= filename <SP> num <SP> num <SP> num
+      annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren
+
+      <SP> is a space character (ASCII 0x20)
+      <LF> is a line-feed character (ASCII 0x0A)
+      num is a sequence of decimal digits
+      filename is a string with the lexical conventions of O'Caml
+      open-paren is an open parenthesis (ASCII 0x28)
+      close-paren is a closed parenthesis (ASCII 0x29)
+      data is any sequence of characters where <LF> is always followed by
+           at least two space characters.
+
+      - in each block, the two positions are respectively the start and the
+        end of the range described by the block.
+      - in a position, the filename is the name of the file, the first num
+        is the line number, the second num is the offset of the beginning
+        of the line, the third num is the offset of the position itself.
+      - the char number within the line is the difference between the third
+        and second nums.
+
+      For the moment, the only possible keyword is \"type\"."
+    """
+
+    def __init__(self):
+        self.__filename = None  # last .annot parsed file
+        self.__ml_filename = None # as above but s/.annot/.ml/
+        self.__timestamp = None # last parse action timestamp
+        self.__annot = {}
+        self.__re = re.compile(
+          '^"[^"]*"\s+(\d+)\s+(\d+)\s+(\d+)\s+"[^"]*"\s+(\d+)\s+(\d+)\s+(\d+)$')
+
+    def __parse(self, fname):
+        try:
+            f = open(fname)
+            line = f.readline() # position line
+            lineno = 1
+            while (line != ""):
+                m = self.__re.search(line)
+                if (not m):
+                    raise malformed_annotations(lineno)
+                line1 = int(m.group(1))
+                col1 = int(m.group(3)) - int(m.group(2))
+                line2 = int(m.group(4))
+                col2 = int(m.group(6)) - int(m.group(5))
+                line = f.readline() # "type(" string
+                lineno += 1
+                if (line == ""): raise malformed_annotations(lineno)
+                type = []
+                line = f.readline() # type description
+                lineno += 1
+                if (line == ""): raise malformed_annotations(lineno)
+                while line != ")\n":
+                    type.append(string.strip(line))
+                    line = f.readline()
+                    lineno += 1
+                    if (line == ""): raise malformed_annotations(lineno)
+                type = string.join(type, "\n")
+                key = ((line1, col1), (line2, col2))
+                if not self.__annot.has_key(key):
+                    self.__annot[key] = type
+                line = f.readline() # position line
+            f.close()
+            self.__filename = fname
+            self.__ml_filename = re.sub("\.annot$", ".ml", fname)
+            self.__timestamp = int(time.time())
+        except IOError:
+            raise no_annotations
+
+    def parse(self):
+        annot_file = re.sub("\.ml$", ".annot", vim.current.buffer.name)
+        self.__parse(annot_file)
+
+    def get_type(self, (line1, col1), (line2, col2)):
+        if debug:
+            print line1, col1, line2, col2
+        if vim.current.buffer.name == None:
+            raise no_annotations
+        if vim.current.buffer.name != self.__ml_filename or  \
+          os.stat(self.__filename).st_mtime > self.__timestamp:
+            self.parse()
+        try:
+            return self.__annot[(line1, col1), (line2, col2)]
+        except KeyError:
+            raise annotation_not_found
+
+word_char_RE = re.compile("^[\w.]$")
+
+  # TODO this function should recognize ocaml literals, actually it's just an
+  # hack that recognize continuous sequences of word_char_RE above
+def findBoundaries(line, col):
+    """ given a cursor position (as returned by vim.current.window.cursor)
+    return two integers identify the beggining and end column of the word at
+    cursor position, if any. If no word is at the cursor position return the
+    column cursor position twice """
+    left, right = col, col
+    line = line - 1 # mismatch vim/python line indexes
+    (begin_col, end_col) = (0, len(vim.current.buffer[line]) - 1)
+    try:
+        while word_char_RE.search(vim.current.buffer[line][left - 1]):
+            left = left - 1
+    except IndexError:
+        pass
+    try:
+        while word_char_RE.search(vim.current.buffer[line][right + 1]):
+            right = right + 1
+    except IndexError:
+        pass
+    return (left, right)
+
+annot = Annotations() # global annotation object
+
+def printOCamlType(mode):
+    try:
+        if mode == "visual":  # visual mode: lookup highlighted text
+            (line1, col1) = vim.current.buffer.mark("<")
+            (line2, col2) = vim.current.buffer.mark(">")
+        else: # any other mode: lookup word at cursor position
+            (line, col) = vim.current.window.cursor
+            (col1, col2) = findBoundaries(line, col)
+            (line1, line2) = (line, line)
+        begin_mark = (line1, col1)
+        end_mark = (line2, col2 + 1)
+        print annot.get_type(begin_mark, end_mark)
+    except AnnExc, exc:
+        print exc.reason
+
+def parseOCamlAnnot():
+    try:
+        annot.parse()
+    except AnnExc, exc:
+        print exc.reason
+
+EOF
+
+fun! OCamlPrintType(current_mode)
+  if (a:current_mode == "visual")
+    python printOCamlType("visual")
+  else
+    python printOCamlType("normal")
+  endif
+endfun
+
+fun! OCamlParseAnnot()
+  python parseOCamlAnnot()
+endfun
+
+map ,t :call OCamlPrintType("normal")<RETURN>
+vmap ,t :call OCamlPrintType("visual")<RETURN>

File .vim/indent/omlet.vim

View file
+" Vim indent file
+" Language:    OCaml
+" Maintainer:  David Baelde <firstname.name@ens-lyon.org>
+" URL:         http://ocaml.info/vim/indent/omlet.vim
+" Last Change: 2005 Apr 08
+" Changelog:
+"         0.13 - Comments are now always aligned on the next block
+"              - Corrected a bug related to comment un-skipping
+"              - Added 'c', "=" and [|...|] handling. Sorry for the delay :)
+"         0.12 - Added OCaml modeline support
+"              - Prevented a few abusive autoindentation, when atoms begin
+"                like keywords ("incr"...)
+"              - Multiple comments are now aligned on the next real block
+"              - New option omlet_middle_comment, still has pb with autoindent
+"              - Corrected the parsing return value -- indentation of "and"
+"              - Indentation code for ";" handles "bla ; bla ;\nbla" better
+"              - Added "mod"
+"         0.11 - Enhanced a lot the expression backward parsing, including
+"                many infix operators, which are now well considered regarding
+"                their binding power
+"              - s:indent() was enhanced a lot
+
+" omlet.vim -- a set of files for working on OCaml code with VIm
+" Copyright (C) 2005 David Baelde
+"
+" This program is free software; you can redistribute it and/or modify
+" it under the terms of the GNU General Public License as published by
+" the Free Software Foundation; either version 2 of the License, or
+" (at your option) any later version.
+"
+" This program is distributed in the hope that it will be useful,
+" but WITHOUT ANY WARRANTY; without even the implied warranty of
+" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+" GNU General Public License for more details.
+"
+" You should have received a copy of the GNU General Public License
+" along with this program; if not, write to the Free Software
+" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+" TODO cannot re-indent when || is typed at begining of line
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+  finish
+endif
+let b:did_indent = 1
+
+setlocal expandtab
+setlocal fo=croq
+syntax enable
+setlocal indentexpr=GetOMLetIndent(v:lnum)
+setlocal indentkeys=0{,0},!^F,o,O,0=let\ ,0=and,0=in,0=end,0),0],0=\|],0=do,0=done,0=then,0=else,0=with,0\|,0=->,0=;;,0=module,0=struct,0=sig,0=class,0=object,0=val,0=method,0=initializer,0=inherit,0=open,0=include,0=exception,0=external,0=type,0=&&,0^,0*,0\,,0=::,0@,0+,0/,0-
+
+" Get the modeline
+let s:s = line2byte(line('.'))+col('.')-1
+if search('^\s*(\*:o\?caml:')
+  let s:modeline = getline(".")
+else
+  let s:modeline = ""
+endif
+if s:s > 0
+  exe 'goto' s:s
+endif
+
+" P is a modeline param name, S a global variable name, V the default value
+function! s:default(p,s,v)
+  let m = matchstr(s:modeline,a:p.'\s*=\s*\d\+')
+  if m != ""
+    return matchstr(m,'\d\+')
+  elseif exists(a:s)
+    exe "return" a:s
+  else
+    return a:v
+  endif
+endfunction
+
+let b:i = s:default("default","g:omlet_indent",2)
+let b:i_struct = s:default("struct","g:omlet_indent_struct",b:i)
+let b:i_match = s:default("match","g:omlet_indent_match",b:i)
+let b:i_function = s:default("fun","g:omlet_indent_function",b:i)
+let b:i_let = s:default("let","g:omlet_indent_let",b:i)
+let b:mb = s:default("xxx","g:omlet_middle_comment",1)
+
+if b:mb != 0
+  let b:mb = 1
+endif
+
+if b:mb == 1
+  setlocal comments=s1l:(*,mb:*,ex:*)
+else
+  setlocal comments=s1l:(*,ex:*)
+endif
+
+" Do not define our functions twice
+if exists("*GetOMLetIndent")
+  finish
+endif
+let b:did_indent = 1
+
+" {{{ A few utils
+
+function s:save()
+  return line2byte(line('.'))+col('.')-1
+endfunction
+
+function s:restore(v)
+  execute 'goto ' a:v
+endfunction
+
+" Same as searchpair() but skips comments and strings
+function s:searchpair(beg,mid,end,flags)
+  return searchpair(a:beg,a:mid,a:end,a:flags,'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"')
+endfunction
+
+" Same as search(_,'bW') but skips comments
+function s:search(re)
+  let p = s:save()
+  while search('\*)\_s*\%#','bW')
+    call searchpair('(\*','','\*)','bW')
+  endwhile
+  if search(a:re,'bW')
+    return 1
+  else
+    call s:restore(p)
+  endif
+endfunction
+
+" Goes back to the beginning of an "end", whatever its opening keyword is
+let s:begend = '\%(\<begin\>\|\<object\>\|\<struct\>\|\<sig\>\)'
+function OMLetBegendBackward()
+  return s:searchpair(s:begend,'','\<end\>','bW')
+endfunction
+
+" }}}
+
+" {{{ Jumping
+" We separated expressions in three classes:
+"  A: The applications
+"  E: A + closure by infix operators which bind tighter than ";"
+"        We ignore the aggregating "let" and the special "then" operator
+"  Block: Full expressions, it's a mess...
+" "then", "match", "patterns"...
+
+" Goes to the beginning of the previous (exclusive) block.
+" It is stopped by any non-trivial syntax.
+" The block moves are really the heart of omlet!
+
+let s:blockstop = '\(\%^\|(\|{\|\[\|\[|\|\<begin\>\|;\|,\|&&\|||\|\<try\>\|\<match\>\|\<with\>\||\|->\|\<when\>\|\<of\>\|\<fun\>\|\<function\>\|=\|\<let\>\|\<in\>\|\<for\>\|\<to\>\|\<do\>\|\<while\>\|\<if\>\|\<then\>\|\<else\>\|\<sig\>\|\<struct\>\|\<object\>\)\_s*\%#'
+
+let s:binop_core = '\%(\<lor\>\|\<land\>\|\<lsr\>\|\<lsl\>\|\<asr\>\|\<asl\>\|\<mod\>\|,\|::\|@\|\^\|||\|&&\|\.\|<-\|:=\|+\|\*\|/\|-\)'
+" Thanks to ".", floating point arith operators are included...
+let s:binop = s:binop_core.'\_s*\%#'
+
+function OMLetAtomBackward()
+  let s = s:save()
+
+  if search('\*)\_s*\%#','bW')
+    call searchpair('(\*','','\*)','bW')
+    return OMLetAtomBackward()
+
+  elseif search(')\_s*\%#','bW')
+    return s:searchpair('(','',')','bW')
+
+  elseif search('\]\_s*\%#','bW')
+    return s:searchpair('\[','','\]','bW')
+
+  elseif search('}\_s*\%#','bW')
+    return s:searchpair('{','','}','bW')
+
+  elseif search("'\\_s*\\%#",'bW')
+    while search("\\_.'",'bW')
+      if synIDattr(synID(line("."), col("."), 0), "name") != "ocamlCharacter"
+        call search("'")
+        return 1
+      endif
+    endwhile
+    throw "Couldn't find beginning of character"
+
+  elseif search('"\_s*\%#','bW')
+    while search('\_."','bW')
+      if synIDattr(synID(line("."), col("."), 0), "name") != "ocamlString"
+        call search('"')
+        return 1
+      endif
+    endwhile
+    throw "Couldn't find beginning of string"
+
+  elseif search('\<done\>\_s*\%#','bW')
+    call s:searchpair('\<do\>','','\<done\>','bW')
+    return s:searchpair('\%(\<while\>\|\<for\>\)','','\<do\>','bW')
+
+  elseif search('\<end\>\_s*\%#','bW')
+    if synIDattr(synID(line("."), col("."), 0), "name") == "ocamlKeyword"
+      call s:searchpair('\<begin\>','','\<end\>','bW')
+      return 1
+    else
+      call s:restore(s)
+    endif
+
+  elseif search('\%(!\|`\|#\|\.\)\_s*\%#','bW')
+    " Ignore some atom prefixes
+    return OMLetAtomBackward()
+
+  elseif search(s:binop,'bW') || search(s:blockstop,'bW')
+    " Stop moving in front of those block delimiters
+    call s:restore(s)
+    return 0
+
+  else
+    " Otherwise, move backward, skipping a variable name or constant...
+    return search('\<','bW')
+  endif
+endfunction
+
+function OMLetABackward()
+  if OMLetAtomBackward()
+    while OMLetAtomBackward()
+    endwhile
+    return 1
+  else
+    return 0
+  endif
+endfunction
+
+function OMLetEBackward()
+  if OMLetABackward()
+    call OMLetEBackward()
+    return 1
+  endif
+
+  if s:search(s:binop)
+    call OMLetEBackward()
+    return 1
+  endif
+
+  let s = s:save()
+  if search('=\_s*\%#','bW')
+    if search('\%(\<let\>\|\<val\>\|\<method\>\|\<type\>\)[^=]\+\%#','bW')
+      call s:restore(s)
+      return 0
+    else
+      call OMLetEBackward()
+      return 1
+    endif
+  endif
+endfunction
+
+" }}}
+
+" {{{ Complex jumping
+" We still have problems with "match", and "else",
+" which have no closing keyword.
+
+function OMLetMatchHeadBackward()
+  if s:search('\<with\>\_s*\%#')
+    call s:searchpair('\%(\<try\>\|\<match\>\)','','\<with\>','bW')
+    return 1
+  elseif s:search('\<fun\>\_s*\%#')
+    return 1
+  elseif s:search('\<function\>\_s*\%#')
+    return 1
+  elseif s:search('\<type\>\_[^=]\+=\_s*\%#')
+    return 1
+  else
+    return 0
+  endif
+endfunction
+
+function OMLetIfHeadBackward()
+  if search('\<then\_s*\%#','bW')
+    call s:searchpair('\<if\>','','\<then\>','bW')
+  else
+    throw "Indentation failed!"
+  endif
+endfunction
+
+function OMLetBlockBackward(lf,gbg)
+  if search('\<else\_s*\%#','bW')
+    call OMLetBlockBackward('then',a:gbg)
+    call OMLetIfHeadBackward()
+    call OMLetBlockBackward(a:lf,a:gbg)
+    return 1
+
+  elseif OMLetEBackward()
+    call OMLetBlockBackward(a:lf,a:gbg)
+    while searchpair('(\*','','\*)')
+      " Undo some abusive comment jumping...
+      " Go to the real end of comment, then to the next meaningful point
+      call search(')')
+      call search('\S')
+    endwhile
+    return 1
+
+  elseif a:lf == 'then'
+    return 0
+
+  elseif search('\<then\_s*\%#','bW')
+    call s:searchpair('\<if\>','','\<then\>','bW')
+    call OMLetBlockBackward(a:lf,a:gbg)
+    return 1
+
+  elseif a:lf == ';'
+    return 0
+
+  elseif search('\<in\>\_s*\%#','bW')
+    call s:searchpair('\<let\>','','\<in\>','bW')
+    call OMLetBlockBackward(a:lf, 0) " [let ... in] eats the garbage
+    return 1
+
+  elseif search(';\_s*\%#','bW')
+    call OMLetBlockBackward(a:lf,1) " sequence is the garbage
+    return 1
+
+  elseif search('\<when\_s*\%#','bW')
+    call OMLetBlockBackward(a:lf,0)
+    return 1
+
+  elseif search('\%(->\|\<of\>\)\_s*\%#','bW')
+    call OMLetPatternBackward()
+    call OMLetBlockBackward('match',0)
+    if a:lf != 'match'
+      call OMLetMatchHeadBackward()
+      call OMLetBlockBackward(a:lf,0) " match has eaten the garbage
+    endif
+    return 1
+
+  endif
+  return 0
+endfunction
+
+function OMLetPatternBackward()
+  call OMLetBlockBackward('',0)
+  call search('|\_s*\%#','bW') " allow failure
+endfunction
+
+" }}}
+
+" {{{ Indentation function
+" The goal is to return a 'correct' indentation,
+" assuming that the previous lines are well indented.
+
+function s:indent(...)
+  " The optionnal argument avoids ignoring leading "| "
+
+  " If there's a hard delimiter like "{","(","[", its position is
+  " used as the reference for the indentation
+  let p = s:save()
+  let l = line(".")
+  if !search('\%#[{(\[]') && s:searchpair('[\[{(]','','[)}\]]','bW') && line(".") == l
+    call search('\S')
+    return col('.')-1
+  else
+    call s:restore(p)
+  endif
+
+  if a:0==0 && search('^\s*\%(|.*\%#\|\%#|\)','bW')
+    " We consider leading "|" as whitespace
+    call search('|')
+    call search('\S')
+    return col('.')-1
+  endif
+
+  return indent('.')
+endfunction
+
+function s:topindent(lnum)
+  let l = a:lnum
+  while l > 0
+    if getline(l) =~ '\s*\%(\<struct\>\|\<sig\>\|\<object\>\)'
+      return b:i_struct+indent(l)
+    endif
+    let l = l-1
+  endwhile
+  return 0
+endfunction
+
+function GetOMLetIndent(l)
+
+  " Go to the first non-blank char on the line to be indented.
+  exe a:l
+
+  " {{{ Comments
+
+  " Inside comments -- needs the comment to be closed!
+  if synIDattr(synID(line("."), col("."), 0), "name") == 'ocamlComment'
+    " TODO the first line inside the comment isn't autoindented correctly
+    if searchpair('(\*','','\*)','bW')
+      " We were strictly inside the comment, and we are now at its beginning
+      " Let's jump to the last *
+      call search('\*\_[^\*]')
+      if b:mb == 0 && getline(a:l) !~ '^\s*\*)'
+        return col('.')+1
+      else
+        return col('.')-1
+      endif
+    endif
+    " No need to restore, there was no move
+  endif
+
+  " Comments with a blank line before them are indented as the next block
+  if getline(a:l) =~ '^\s*(\*'
+    let ok = 1
+    while ok == 1
+      call searchpair('(\*','','\*)')
+      " TODO this would be better : /\%#\*)\_s*(/e
+      if getline(line('.')+1) =~ '^\s*(\*'
+        exe line('.')+1
+      else
+        let ok = 0
+      endtry
+    endwhile
+
+    let new = nextnonblank(line('.')+1)
+    if new == 0 || new == a:l
+      return -1
+    else
+      return GetOMLetIndent(new)
+    endif
+  endif
+
+  " }}}
+
+  " {{{ Keyword alignments
+  " How to indent a line starting with a keyword
+
+  " Parenthesis-like closing
+
+  if getline(a:l) =~ '^\s*\<end\>' && s:searchpair(s:begend,'','\<end\>','bW')
+    return s:indent()
+  endif
+
+  if getline(a:l) =~ '^\s*}'
+    call s:searchpair('{','','}','bW')
+    return s:indent()
+  endif
+
+  if getline(a:l) =~ '^\s*)'
+    call s:searchpair('(','',')','bW')
+    return s:indent()
+  endif
+
+  if getline(a:l) =~ '^\s*\]'
+    call s:searchpair('\[','','\]','bW')
+    return s:indent()
+  endif
+
+  if getline(a:l) =~ '^\s*|\]'
+    call s:searchpair('\[|','','|\]','bW')
+    return s:indent()
+  endif
+
+  " WHILE and FOR
+
+  if getline(a:l) =~ '^\s*\<done\>' && s:searchpair('\<do\>','','\<done\>','bW')
+    call s:searchpair('\%(\<while\>\|\<for\>\)','','\<do\>','bW')
+    return s:indent()
+  endif
+
+  if getline(a:l) =~ '^\s*\<do\>' && s:searchpair('\<\(while\|for\)\>','','\<do\>','bW')
+    return s:indent()
+  endif
+
+  " PATTERNS
+
+  if getline(a:l) =~ '^\s*\<with\>' && s:searchpair('\%(\<try\>\|\<match\>\)','','\<with\>','bW')
+    return s:indent()
+  endif
+
+  if getline(a:l) =~ '^\s*->'
+    call OMLetPatternBackward()
+    if search('\%#|')
+      return s:indent(1)+2
+    else
+      return s:indent()
+    endif
+  endif
+
+  if getline(a:l) =~ '^\s*|'
+    call OMLetBlockBackward('match',0)
+    if s:search('|\_s*\%#')
+      " We are stuck on a 0-ary constructor
+      return s:indent(1)
+    elseif s:search('\[\_s*\%#')
+      " Polymorphic variant
+      return col(".")-1
+    elseif s:search('function\_s*\%#')
+      return s:indent()+b:i_function
+    else
+      call OMLetMatchHeadBackward()
+      return s:indent()+b:i_match
+    endif
+  endif
+
+  " INFIX "&&", "||", "^", ...
+  if getline(a:l) =~ '^\s*'.s:binop_core
+    call OMLetEBackward()
+    return s:indent()
+  endif
+
+  " IF/THEN/ELSE
+
+  if getline(a:l) =~ '^\s*\<then\>' && s:searchpair('\<if\>','','\<then\>','bW')
+    return s:indent()
+  endif
+
+  if getline(a:l) =~ '^\s*\<else\>'
+    call OMLetBlockBackward('then',0)
+    if s:search('\<then\_s*\%#')
+      call s:searchpair('\<if\>','','\<then\>','bW')
+    endif
+    return s:indent()
+  endif
+
+  " ;; alone is indented as toplevel defs
+  if getline(a:l) =~ '^\s*;;'
+    if OMLetBegendBackward()
+      return s:indent()+b:i_struct
+    else
+      return 0
+    endif
+  endif
+
+  " { and [ may need to be reindented in type definitions, where they don't
+  " really deserve the default +4 indentation
+  " if getline(a:l) =~ '^\s*{'
+  "   return s:indent(a:l-1)+2 " But that's weak !
+  " endif
+
+  " Easy toplevel definitions
+  if getline(a:l) =~ '^\s*\%(\<open\>\|\<include\>\|\<struct\>\|\<object\>\|\<sig\>\|\<val\>\|\<module\>\|\<class\>\|\<type\>\|\<method\>\|\<initializer\>\|\<inherit\>\|\<exception\>\|\<external\>\)'
+    if s:searchpair(s:begend,'','\<end\>','bW')
+      return s:indent()+b:i_struct
+    else
+      return 0
+    endif
+  endif
+
+  " The next three tests are for indenting toplevel let
+
+  " let after a high-level end (not matching a begin)
+  if getline(a:l) =~ '^\s*let\>' && s:search('\<end\>\_s*\%#') && synIDattr(synID(line('.'),col('.'),0),'name') != 'ocamlKeyword'
+    call OMLetBegendBackward()
+    return s:indent()
+  else
+    exe a:l
+  endif
+
+  " let at the beginning of a structure
+  if getline(a:l) =~ '^\s*let\>' && s:search('\<struct\>\_s*\%#')
+    return s:indent()+b:i_struct
+  endif
+
+  " let after another value-level construct
+  if getline(a:l) =~ '^\s*let\>' && (OMLetAtomBackward() || search(';;\_s*\%#'))
+    if OMLetBegendBackward()
+      return s:indent()+b:i_struct
+    else
+      return 0
+    endif
+  else
+    exe a:l
+    " That was undoing the AtomBackward
+  endif
+
+  " let/and
+  if getline(a:l) =~ '^\s*and\>' && OMLetBlockBackward('',0)
+    if s:search('\%(\<let\>\|\<and\>\)\_[^=]\+=\_s*\%#')
+      return s:indent()
+    else
+      exe a:l
+    endif
+  endif
+
+  " Now we deal with let/in
+  if getline(a:l) =~ '^\s*in\>' && s:searchpair('\<let\>','','\<in\>','bW')
+    " Check that we don't have a toplevel let
+    if s:indent() == s:topindent('.')
+      exe a:l
+    else
+      return s:indent()
+    endif
+  endif
+
+  " let after let isn't indented
+  if getline(a:l) =~ '^\s*\<let\>' && s:search('\<in\>\_s*\%#')
+    call s:searchpair('\<let\>','','\<in\>','bW')
+    return s:indent()
+  endif
+
+  " }}}
+
+  " {{{ Beginning of blocks
+  " Howto indent a line after some keyword
+
+  " Basic case
+  if s:search('\(\<struct\>\|\<sig\>\|\<class\>\)\_s*\%#')
+    return s:indent()+b:i_struct
+  endif
+  if s:search('\(\<while\>\|\<for\>\|\<if\>\|\<begin\>\|\<match\>\|\<try\>\|(\|{\|\[\|\[|\|\<initializer\>\)\_s*\%#')
+    return s:indent()+b:i
+  endif
+  if s:search('\%(\<let\>\|\<and\>\|\<module\>\|\<val\>\|\<method\>\)\_[^=]\+=\_s*\%#')
+    return s:indent()+b:i
+  endif
+
+  " PATTERNS
+  if s:search('\<function\>\_s*\%#')
+    return s:indent()+2+b:i_function
+  endif
+  if s:search('\<with\>\_s*\%#')
+    call s:searchpair('\%(\<match\>\|\<try\>\)','','\<with\>','bW')
+    return s:indent()+2+b:i_match
+  endif
+  if s:search('\<type\>\_[^=]\+=\_s*\%#')
+    return s:indent()+2+b:i
+  endif
+  if s:search('\<of\>\_s*\%#')
+    return s:indent()+b:i
+  endif
+
+  " Sometimes you increment according to a master keyword
+
+  " IF
+  if s:search('\<then\>\_s*\%#')
+    call s:searchpair('\<if\>','','\<then\>','bW')
+    return s:indent()+b:i
+  endif
+  if s:search('\<else\>\_s*\%#')
+    call OMLetBlockBackward('then',0)
+    call OMLetIfHeadBackward()
+    return s:indent()+b:i
+  endif
+
+  " MATCH
+  if s:search('\<when\>\_s*\%#')
+    call OMLetPatternBackward()
+    if search('\%#|')
+      return s:indent(1)+2+b:i
+    else
+      " First pattern can have no |
+      return s:indent()+b:i
+    endif
+  endif
+  if s:search('->\_s*\%#')
+    call OMLetPatternBackward()
+    if s:search('fun\>\s*\%#')
+      return s:indent()+b:i
+    elseif search('\%#|')
+      return s:indent(1)+2+b:i
+    else
+      " First pattern can have no |
+      return s:indent()+b:i
+    endif
+  endif
+
+  " WHILE/DO
+  if s:search('\<do\>\_s*\%#')
+    call s:searchpair('\%(\<while\>\|\<for\>\)','','\<do\>','bW')
+    return s:indent()+b:i
+  endif
+
+  " LET
+  if s:search('\<in\>\_s*\%#')
+    call s:searchpair('\<let\>','','\<in\>','bW')
+    return s:indent()+b:i_let
+  endif
+
+  " }}}
+
+  " Sequence: find previous instruction's base indentation
+
+  if s:search(';;\_s*\%#')
+    if OMLetBegendBackward()
+      return s:indent()+b:i_struct
+    else
+      return 0
+    endif
+  endif
+
+  if s:search(';\_s*\%#')
+    " Flexible indentation using s:indent() would be nice,
+    " but I would actually need to return the identation
+    " that *would have had* the previous expr in the sequence
+    " if it has been alone on a line.
+    " s/col(".")-1/s:indent()/ leads to bad things like:
+    " > let x = bla ;
+    " > bla
+    call OMLetBlockBackward(';',0)
+    while s:search(';\s*\%#')
+      call OMLetBlockBackward(';',0)
+    endwhile
+    return col(".")-1
+  endif
+
+  if s:search(s:binop)
+    call OMLetEBackward()
+    return col(".")-1
+  endif
+
+  " = is a special binop
+  if s:search('=\_s*\%#')
+    call OMLetEBackward()
+    return s:indent()+b:i
+  endif
+
+  " Application: indentation between a function and its arguments
+
+  if OMLetAtomBackward()
+    " I think s:indent() is ugly here...
+    call OMLetABackward()
+    return col('.')-1+b:i
+  endif
+
+  " This shouldn't happen
+  return 0
+
+endfunction
+
+" }}}

File .vim/syntax/omlet.vim

View file
+" Vim syntax file
+" Language:     OCaml
+" Filenames:    *.ml *.mli *.mll *.mly
+" Maintainers:  Markus Mottl      <markus.mottl@gmail.com>
+"               Karl-Heinz Sylla  <Karl-Heinz.Sylla@gmd.de>
+"               Issac Trotts      <ijtrotts@ucdavis.edu>
+" URL:          http://www.ocaml.info/vim/syntax/ocaml.vim
+" Last Change:  2004 Aug 13 - Added new type keywords (MM)
+"               2004 Jul 30 - Added script keyword "thread" (MM)
+"               2004 May 15 - Added keyword "format4" (MM)
+"               2003 Jan 19 - Added script keyword "require" (MM)
+
+" A minor patch was applied to the official version so that object/end
+" can be distinguished from begin/end, which is used for indentation,
+" and folding. (David Baelde)
+
+" For version 5.x: Clear all syntax items
+" For version 6.x: Quit when a syntax file was already loaded
+if version < 600
+  syntax clear
+elseif exists("b:current_syntax") && b:current_syntax != "ocaml"
+  finish
+endif
+
+" OCaml is case sensitive.
+syn case match
+
+" Script headers highlighted like comments
+syn match    ocamlComment   "^#!.*"
+
+" Scripting directives
+syn match    ocamlScript "^#\<\(quit\|labels\|warnings\|directory\|cd\|load\|use\|install_printer\|remove_printer\|require\|thread\|trace\|untrace\|untrace_all\|print_depth\|print_length\)\>"
+
+" lowercase identifier - the standard way to match
+syn match    ocamlLCIdentifier /\<\(\l\|_\)\(\w\|'\)*\>/
+
+syn match    ocamlKeyChar    "|"
+
+" Errors
+syn match    ocamlBraceErr   "}"
+syn match    ocamlBrackErr   "\]"
+syn match    ocamlParenErr   ")"
+syn match    ocamlArrErr     "|]"
+
+syn match    ocamlCommentErr "\*)"
+
+syn match    ocamlCountErr   "\<downto\>"
+syn match    ocamlCountErr   "\<to\>"
+
+if !exists("ocaml_revised")
+  syn match    ocamlDoErr      "\<do\>"
+endif
+
+syn match    ocamlDoneErr    "\<done\>"
+syn match    ocamlThenErr    "\<then\>"
+
+" Error-highlighting of "end" without synchronization:
+" as keyword or as error (default)
+if exists("ocaml_noend_error")
+  syn match    ocamlKeyword    "\<end\>"
+else
+  syn match    ocamlEndErr     "\<end\>"
+endif
+
+" Some convenient clusters
+syn cluster  ocamlAllErrs contains=ocamlBraceErr,ocamlBrackErr,ocamlParenErr,ocamlCommentErr,ocamlCountErr,ocamlDoErr,ocamlDoneErr,ocamlEndErr,ocamlThenErr
+
+syn cluster  ocamlAENoParen contains=ocamlBraceErr,ocamlBrackErr,ocamlCommentErr,ocamlCountErr,ocamlDoErr,ocamlDoneErr,ocamlEndErr,ocamlThenErr
+
+syn cluster  ocamlContained contains=ocamlTodo,ocamlPreDef,ocamlModParam,ocamlModParam1,ocamlPreMPRestr,ocamlMPRestr,ocamlMPRestr1,ocamlMPRestr2,ocamlMPRestr3,ocamlModRHS,ocamlFuncWith,ocamlFuncStruct,ocamlModTypeRestr,ocamlModTRWith,ocamlWith,ocamlWithRest,ocamlModType,ocamlFullMod
+
+
+" Enclosing delimiters
+syn region   ocamlEncl transparent matchgroup=ocamlKeyword start="(" matchgroup=ocamlKeyword end=")" contains=ALLBUT,@ocamlContained,ocamlParenErr
+syn region   ocamlEncl transparent matchgroup=ocamlKeyword start="{" matchgroup=ocamlKeyword end="}"  contains=ALLBUT,@ocamlContained,ocamlBraceErr
+syn region   ocamlEncl transparent matchgroup=ocamlKeyword start="\[" matchgroup=ocamlKeyword end="\]" contains=ALLBUT,@ocamlContained,ocamlBrackErr
+syn region   ocamlEncl transparent matchgroup=ocamlKeyword start="\[|" matchgroup=ocamlKeyword end="|\]" contains=ALLBUT,@ocamlContained,ocamlArrErr
+
+
+" Comments
+syn region   ocamlComment start="(\*" end="\*)" contains=ocamlComment,ocamlTodo
+syn keyword  ocamlTodo contained TODO FIXME XXX
+
+
+" Objects
+syn region   ocamlEnd matchgroup=ocamlObject start="\<object\>" matchgroup=ocamlObject end="\<end\>" contains=ALLBUT,@ocamlContained,ocamlEndErr
+
+
+" Blocks
+if !exists("ocaml_revised")
+  syn region   ocamlEnd matchgroup=ocamlKeyword start="\<begin\>" matchgroup=ocamlKeyword end="\<end\>" contains=ALLBUT,@ocamlContained,ocamlEndErr
+endif
+
+
+" "for"
+syn region   ocamlNone matchgroup=ocamlKeyword start="\<for\>" matchgroup=ocamlKeyword end="\<\(to\|downto\)\>" contains=ALLBUT,@ocamlContained,ocamlCountErr
+
+
+" "do"
+if !exists("ocaml_revised")
+  syn region   ocamlDo matchgroup=ocamlKeyword start="\<do\>" matchgroup=ocamlKeyword end="\<done\>" contains=ALLBUT,@ocamlContained,ocamlDoneErr
+endif
+
+" "if"
+syn region   ocamlNone matchgroup=ocamlKeyword start="\<if\>" matchgroup=ocamlKeyword end="\<then\>" contains=ALLBUT,@ocamlContained,ocamlThenErr
+
+
+"" Modules
+
+" "struct"
+syn region   ocamlStruct matchgroup=ocamlModule start="\<struct\>" matchgroup=ocamlModule end="\<end\>" contains=ALLBUT,@ocamlContained,ocamlEndErr
+
+" "sig"
+syn region   ocamlSig matchgroup=ocamlModule start="\<sig\>" matchgroup=ocamlModule end="\<end\>" contains=ALLBUT,@ocamlContained,ocamlEndErr,ocamlModule
+syn region   ocamlModSpec matchgroup=ocamlKeyword start="\<module\>" matchgroup=ocamlModule end="\<\u\(\w\|'\)*\>" contained contains=@ocamlAllErrs,ocamlComment skipwhite skipempty nextgroup=ocamlModTRWith,ocamlMPRestr
+
+" "open"
+syn region   ocamlNone matchgroup=ocamlKeyword start="\<open\>" matchgroup=ocamlModule end="\<\u\(\w\|'\)*\(\.\u\(\w\|'\)*\)*\>" contains=@ocamlAllErrs,ocamlComment
+
+" "include"
+syn match    ocamlKeyword "\<include\>" contained skipwhite skipempty nextgroup=ocamlModParam,ocamlFullMod
+
+" "module" - somewhat complicated stuff ;-)
+syn region   ocamlModule matchgroup=ocamlKeyword start="\<module\>" matchgroup=ocamlModule end="\<\u\(\w\|'\)*\>" contains=@ocamlAllErrs,ocamlComment skipwhite skipempty nextgroup=ocamlPreDef
+syn region   ocamlPreDef start="."me=e-1 matchgroup=ocamlKeyword end="\l\|="me=e-1 contained contains=@ocamlAllErrs,ocamlComment,ocamlModParam,ocamlModTypeRestr,ocamlModTRWith nextgroup=ocamlModPreRHS
+syn region   ocamlModParam start="([^*]" end=")" contained contains=@ocamlAENoParen,ocamlModParam1
+syn match    ocamlModParam1 "\<\u\(\w\|'\)*\>" contained skipwhite skipempty nextgroup=ocamlPreMPRestr
+
+syn region   ocamlPreMPRestr start="."me=e-1 end=")"me=e-1 contained contains=@ocamlAllErrs,ocamlComment,ocamlMPRestr,ocamlModTypeRestr
+
+syn region   ocamlMPRestr start=":" end="."me=e-1 contained contains=@ocamlComment skipwhite skipempty nextgroup=ocamlMPRestr1,ocamlMPRestr2,ocamlMPRestr3
+syn region   ocamlMPRestr1 matchgroup=ocamlModule start="\ssig\s\=" matchgroup=ocamlModule end="\<end\>" contained contains=ALLBUT,@ocamlContained,ocamlEndErr,ocamlModule
+syn region   ocamlMPRestr2 start="\sfunctor\(\s\|(\)\="me=e-1 matchgroup=ocamlKeyword end="->" contained contains=@ocamlAllErrs,ocamlComment,ocamlModParam skipwhite skipempty nextgroup=ocamlFuncWith,ocamlMPRestr2
+syn match    ocamlMPRestr3 "\w\(\w\|'\)*\(\.\w\(\w\|'\)*\)*" contained
+syn match    ocamlModPreRHS "=" contained skipwhite skipempty nextgroup=ocamlModParam,ocamlFullMod
+syn region   ocamlModRHS start="." end=".\w\|([^*]"me=e-2 contained contains=ocamlComment skipwhite skipempty nextgroup=ocamlModParam,ocamlFullMod
+syn match    ocamlFullMod "\<\u\(\w\|'\)*\(\.\u\(\w\|'\)*\)*" contained skipwhite skipempty nextgroup=ocamlFuncWith
+
+syn region   ocamlFuncWith start="([^*]"me=e-1 end=")" contained contains=ocamlComment,ocamlWith,ocamlFuncStruct skipwhite skipempty nextgroup=ocamlFuncWith
+syn region   ocamlFuncStruct matchgroup=ocamlModule start="[^a-zA-Z]struct\>"hs=s+1 matchgroup=ocamlModule end="\<end\>" contains=ALLBUT,@ocamlContained,ocamlEndErr
+
+syn match    ocamlModTypeRestr "\<\w\(\w\|'\)*\(\.\w\(\w\|'\)*\)*\>" contained
+syn region   ocamlModTRWith start=":\s*("hs=s+1 end=")" contained contains=@ocamlAENoParen,ocamlWith
+syn match    ocamlWith "\<\(\u\(\w\|'\)*\.\)*\w\(\w\|'\)*\>" contained skipwhite skipempty nextgroup=ocamlWithRest
+syn region   ocamlWithRest start="[^)]" end=")"me=e-1 contained contains=ALLBUT,@ocamlContained
+
+" "module type"
+syn region   ocamlKeyword start="\<module\>\s*\<type\>" matchgroup=ocamlModule end="\<\w\(\w\|'\)*\>" contains=ocamlComment skipwhite skipempty nextgroup=ocamlMTDef
+syn match    ocamlMTDef "=\s*\w\(\w\|'\)*\>"hs=s+1,me=s
+
+syn keyword  ocamlKeyword  and as assert class
+syn keyword  ocamlKeyword  constraint else
+syn keyword  ocamlKeyword  exception external fun
+
+syn keyword  ocamlKeyword  in inherit initializer
+syn keyword  ocamlKeyword  land lazy let match
+syn keyword  ocamlKeyword  method mutable new of
+syn keyword  ocamlKeyword  parser private raise rec
+syn keyword  ocamlKeyword  try type
+syn keyword  ocamlKeyword  val virtual when while with
+
+if exists("ocaml_revised")
+  syn keyword  ocamlKeyword  do value
+  syn keyword  ocamlBoolean  True False
+else
+  syn keyword  ocamlKeyword  function
+  syn keyword  ocamlBoolean  true false
+  syn match    ocamlKeyChar  "!"
+endif
+
+syn keyword  ocamlType     array bool char exn float format format4
+syn keyword  ocamlType     int int32 int64 lazy_t list nativeint option
+syn keyword  ocamlType     string unit
+
+syn keyword  ocamlOperator asr lor lsl lsr lxor mod not
+
+syn match    ocamlConstructor  "(\s*)"
+syn match    ocamlConstructor  "\[\s*\]"
+syn match    ocamlConstructor  "\[|\s*>|]"
+syn match    ocamlConstructor  "\[<\s*>\]"
+syn match    ocamlConstructor  "\u\(\w\|'\)*\>"
+
+" Polymorphic variants
+syn match    ocamlConstructor  "`\w\(\w\|'\)*\>"
+
+" Module prefix
+syn match    ocamlModPath      "\u\(\w\|'\)*\."he=e-1
+
+syn match    ocamlCharacter    "'\\\d\d\d'\|'\\[\'ntbr]'\|'.'"
+syn match    ocamlCharErr      "'\\\d\d'\|'\\\d'"
+syn match    ocamlCharErr      "'\\[^\'ntbr]'"
+syn region   ocamlString       start=+"+ skip=+\\\\\|\\"+ end=+"+
+
+syn match    ocamlFunDef       "->"
+syn match    ocamlRefAssign    ":="
+syn match    ocamlTopStop      ";;"
+syn match    ocamlOperator     "\^"
+syn match    ocamlOperator     "::"
+
+syn match    ocamlOperator     "&&"
+syn match    ocamlOperator     "<"
+syn match    ocamlOperator     ">"
+syn match    ocamlAnyVar       "\<_\>"
+syn match    ocamlKeyChar      "|[^\]]"me=e-1
+syn match    ocamlKeyChar      ";"
+syn match    ocamlKeyChar      "\~"
+syn match    ocamlKeyChar      "?"
+syn match    ocamlKeyChar      "\*"
+syn match    ocamlKeyChar      "="
+
+if exists("ocaml_revised")
+  syn match    ocamlErr        "<-"
+else
+  syn match    ocamlOperator   "<-"
+endif
+
+syn match    ocamlNumber        "\<-\=\d\+\>"
+syn match    ocamlNumber        "\<-\=0[x|X]\x\+\>"
+syn match    ocamlNumber        "\<-\=0[o|O]\o\+\>"
+syn match    ocamlNumber        "\<-\=0[b|B][01]\+\>"
+syn match    ocamlFloat         "\<-\=\d\+\.\d*\([eE][-+]\=\d\+\)\=[fl]\=\>"
+
+" Labels
+syn match    ocamlLabel        "\~\(\l\|_\)\(\w\|'\)*"lc=1
+syn match    ocamlLabel        "?\(\l\|_\)\(\w\|'\)*"lc=1
+syn region   ocamlLabel transparent matchgroup=ocamlLabel start="?(\(\l\|_\)\(\w\|'\)*"lc=2 end=")"me=e-1 contains=ALLBUT,@ocamlContained,ocamlParenErr
+
+
+" Synchronization
+syn sync minlines=50
+syn sync maxlines=500
+
+if !exists("ocaml_revised")
+  syn sync match ocamlDoSync      grouphere  ocamlDo      "\<do\>"
+  syn sync match ocamlDoSync      groupthere ocamlDo      "\<done\>"
+endif
+
+if exists("ocaml_revised")
+  syn sync match ocamlEndSync     grouphere  ocamlEnd     "\<\(object\)\>"
+else
+  syn sync match ocamlEndSync     grouphere  ocamlEnd     "\<\(begin\|object\)\>"
+endif
+
+syn sync match ocamlEndSync     groupthere ocamlEnd     "\<end\>"
+syn sync match ocamlStructSync  grouphere  ocamlStruct  "\<struct\>"
+syn sync match ocamlStructSync  groupthere ocamlStruct  "\<end\>"
+syn sync match ocamlSigSync     grouphere  ocamlSig     "\<sig\>"
+syn sync match ocamlSigSync     groupthere ocamlSig     "\<end\>"
+
+" Define the default highlighting.
+" For version 5.7 and earlier: only when not done already
+" For version 5.8 and later: only when an item doesn't have highlighting yet
+if version >= 508 || !exists("did_ocaml_syntax_inits")
+  if version < 508
+    let did_ocaml_syntax_inits = 1
+    command -nargs=+ HiLink hi link <args>
+  else
+    command -nargs=+ HiLink hi def link <args>
+  endif
+
+  HiLink ocamlBraceErr     Error
+  HiLink ocamlBrackErr     Error
+  HiLink ocamlParenErr     Error
+  HiLink ocamlArrErr       Error
+
+  HiLink ocamlCommentErr   Error
+
+  HiLink ocamlCountErr     Error
+  HiLink ocamlDoErr        Error
+  HiLink ocamlDoneErr      Error
+  HiLink ocamlEndErr       Error
+  HiLink ocamlThenErr      Error
+
+  HiLink ocamlCharErr      Error
+
+  HiLink ocamlErr          Error
+
+  HiLink ocamlComment      Comment
+
+  HiLink ocamlModPath      Include
+  HiLink ocamlObject	   Include
+  HiLink ocamlModule       Include
+  HiLink ocamlModParam1    Include
+  HiLink ocamlModType      Include
+  HiLink ocamlMPRestr3     Include
+  HiLink ocamlFullMod      Include
+  HiLink ocamlModTypeRestr Include
+  HiLink ocamlWith         Include
+  HiLink ocamlMTDef        Include
+
+  HiLink ocamlScript       Include
+
+  HiLink ocamlConstructor  Constant
+
+  HiLink ocamlModPreRHS    Keyword
+  HiLink ocamlMPRestr2     Keyword
+  HiLink ocamlKeyword      Keyword
+  HiLink ocamlFunDef       Keyword
+  HiLink ocamlRefAssign    Keyword
+  HiLink ocamlKeyChar      Keyword
+  HiLink ocamlAnyVar       Keyword
+  HiLink ocamlTopStop      Keyword
+  HiLink ocamlOperator     Keyword
+
+  HiLink ocamlBoolean      Boolean
+  HiLink ocamlCharacter    Character
+  HiLink ocamlNumber       Number
+  HiLink ocamlFloat        Float
+  HiLink ocamlString       String
+
+  HiLink ocamlLabel        Identifier
+
+  HiLink ocamlType         Type
+
+  HiLink ocamlTodo         Todo
+
+  HiLink ocamlEncl         Keyword
+
+  delcommand HiLink
+endif
+
+let b:current_syntax = "ocaml"
+
+" vim: ts=8