seanmcl avatar seanmcl committed 000cd6c

ocp/tuareg indent comparisons

Comments (0)

Files changed (7)

 .deps
 
 ocaml/omake/OMakefile
+ocp-indent/ocp.ml
+ocp-indent/tuareg.ml
+Indentations differ on avltree.ml
+Indentations differ on avltree.mli
+Indentations differ on bigbuffer.ml
+Indentations differ on bigstring.ml
+Indentations differ on bigstring.mli
+Indentations differ on bigstring_marshal.ml
+Indentations differ on bigstring_marshal.mli
+Indentations differ on binable.mli
+Indentations differ on binary_packing.ml
+Indentations differ on blang.ml
+Indentations differ on bounded_int_table.ml
+Indentations differ on bounded_int_table.mli
+Indentations differ on bucket.ml
+Indentations differ on byte_units.ml
+Indentations differ on byte_units.mli
+Indentations differ on common.ml
+Indentations differ on comparable.ml
+Indentations differ on comparator.mli
+Indentations differ on container.ml
+Indentations differ on core_arg.ml
+Indentations differ on core_arg.mli
+Indentations differ on core_array.ml
+Indentations differ on core_array.mli
+Indentations differ on core_bin_prot.ml
+Indentations differ on core_filename.ml
+Indentations differ on core_gc.ml
+Indentations differ on core_gc.mli
+Indentations differ on core_hashtbl.ml
+Indentations differ on core_hashtbl.mli
+Indentations differ on core_hashtbl_intf.ml
+Indentations differ on core_list.ml
+Indentations differ on core_list.mli
+Indentations differ on core_map.ml
+Indentations differ on core_map.mli
+Indentations differ on core_map_intf.ml
+Indentations differ on core_map_unit_tests.ml
+Indentations differ on core_queue.ml
+Indentations differ on core_random.ml
+Indentations differ on core_set.ml
+Indentations differ on core_set.mli
+Indentations differ on core_set_intf.ml
+Indentations differ on core_stack.mli
+Indentations differ on core_string.ml
+Indentations differ on core_string.mli
+Indentations differ on core_sys.ml
+Indentations differ on core_thread.ml
+Indentations differ on core_unix.ml
+Indentations differ on core_unix.mli
+Indentations differ on daemon.ml
+Indentations differ on daemon.mli
+Indentations differ on date.ml
+Indentations differ on dequeue.ml
+Indentations differ on doubly_linked.ml
+Indentations differ on error.ml
+Indentations differ on exn.ml
+Indentations differ on float.ml
+Indentations differ on float_intf.ml
+Indentations differ on floatable.ml
+Indentations differ on fn.ml
+Indentations differ on force_once.ml
+Indentations differ on hash_heap.ml
+Indentations differ on hash_queue.ml
+Indentations differ on hash_set.ml
+Indentations differ on hash_set.mli
+Indentations differ on hash_set_intf.ml
+Indentations differ on heap.ml
+Indentations differ on identifiable.ml
+Indentations differ on in_channel.ml
+Indentations differ on int_conversions.ml
+Indentations differ on int_set.ml
+Indentations differ on interval.ml
+Indentations differ on interval_intf.ml
+Indentations differ on linux_ext.ml
+Indentations differ on linux_ext.mli
+Indentations differ on lock_file.ml
+Indentations differ on lock_file.mli
+Indentations differ on make_substring.ml
+Indentations differ on memo.ml
+Indentations differ on monad.ml
+Indentations differ on month.ml
+Indentations differ on month.mli
+Indentations differ on mutex0.ml
+Indentations differ on nano_mutex.ml
+Indentations differ on no_polymorphic_compare.ml
+Indentations differ on no_polymorphic_compare.mli
+Indentations differ on ofday.ml
+Indentations differ on or_error.ml
+Indentations differ on ordered_collection_common.mli
+Indentations differ on piecewise_linear.ml
+Indentations differ on quickcheck.ml
+Indentations differ on result.ml
+Indentations differ on result.mli
+Indentations differ on sexpable.ml
+Indentations differ on signal.ml
+Indentations differ on signal.mli
+Indentations differ on span.ml
+Indentations differ on span.mli
+Indentations differ on squeue.ml
+Indentations differ on std_internal.ml
+Indentations differ on string_id.ml
+Indentations differ on thread_safe_queue.ml
+Indentations differ on time.ml
+Indentations differ on time.mli
+Indentations differ on timer.ml
+Indentations differ on timer.mli
+Indentations differ on tuple.ml
+Indentations differ on union_find.ml
+Indentations differ on unpack_buffer.ml
+Indentations differ on unpack_buffer.mli
+Indentations differ on weekday.ml
+Indentations differ on word_size.ml
+Indentations differ on zone.ml
+#!/bin/bash
+
+rm -f ocp.ml tuareg.ml
+ocp-indent model.ml > ocp.ml
+cp model.ml tuareg.ml
+emacs --batch --eval '(load-file "doit.el")'
+

ocp-indent/doit.el

+
+(load-file "tuareg.el")
+(find-file "tuareg.ml")
+(tuareg-mode)
+
+(setq tuareg-function-indent 0
+      tuareg-in-indent 0
+      tuareg-indent-comments nil
+      tuareg-indent-leading-comments nil
+      tuareg-leading-star-in-doc nil
+      tuareg-let-always-indent nil
+      tuareg-sig-struct-align nil
+      tuareg-support-leading-star-comments nil
+      tuareg-type-indent 0
+      tuareg-use-abbrev-mode nil
+      tuareg-with-indent 0)
+
+(indent-region (point-min) (point-max))
+(untabify (point-min) (point-max))
+(save-buffer)

ocp-indent/model.ml

+
+val f
+  :  int
+  -> int
+
+type t =
+| A
+| B
+
+let height = function
+| A -> 0
+| B -> 1
+
+let _ =
+  if x then begin
+    y
+  end else if x then
+      y
+  else z
+
+let _ = match x with
+  1 |
+  2 |
+  3
+
+let _ =
+  if x then y else
+  if x then y else
+  x
+
+type t
+  =  int
+  -> int

ocp-indent/test-ocp-indent

+#!/bin/bash
+
+set -e -u -o pipefail
+
+if [[ $# -ne 1 ]]; then
+    echo "Usage: $(basename $0) FILE"
+    echo $#
+    exit 1
+fi
+
+file=$1; shift
+indent=ocp-indent
+tmp_dir=/tmp/ocp-indent
+tmp_file=$tmp_dir/$file
+diff_file=$tmp_file.diff
+
+mkdir -p $tmp_dir
+rm -f $tmp_file $diff_file
+
+ocp-indent $file > $tmp_file
+diff $file $tmp_file > $diff_file || true
+if [[ -s $diff_file ]]; then
+    #echo "----------------------------"
+    echo "Indentations differ on $file"
+    echo
+    cat $diff_file
+fi

ocp-indent/tuareg.el

+;;; tuareg.el --- OCaml mode for Emacs.  -*- coding: utf-8 -*-
+
+;; Copyright (C) 1997-2006 Albert Cohen, all rights reserved.
+;; Copyright (C) 2009-2010 Jane Street Holding, LLC.
+;; Licensed under the GNU General Public License.
+
+;; Author: Albert Cohen <Albert.Cohen@inria.fr>
+;;	Sam Steingold <sds@gnu.org>
+;;	Christophe Troestler <Christophe.Troestler@umons.ac.be>
+;;	Till Varoquaux <till@pps.jussieu.fr>
+;;	Sean McLaughlin <seanmcl@gmail.com>
+;; Created: 8 Jan 1997
+;; Version: 2.0.5
+;; Package-Requires: ((caml "3.12.0.1"))
+;; Keywords: ocaml languages
+;; URL: http://forge.ocamlcore.org/projects/tuareg/
+;; EmacsWiki: TuaregMode
+
+;;; Commentary:
+;; Description:
+;; Tuareg helps editing OCaml code, to highlight important parts of
+;; the code, to run an OCaml toplevel, and to run the OCaml debugger
+;; within Emacs.
+
+;; Installation:
+;; If you have permissions to the local `site-lisp' directory, you
+;; only have to copy `tuareg.el' and `ocamldebug.el' Otherwise, copy
+;; `tuareg.el' and `ocamldebug.el' to a local directory and add the
+;; following line to your `.emacs'
+;;
+;; (add-to-list 'load-path "DIR")
+
+
+;;; Usage:
+;; Tuareg allows you to run batch OCaml compilations from Emacs (using
+;; M-x compile) and browse the errors (C-x `). Typing C-x ` sets the
+;; point at the beginning of the erroneous program fragment, and the
+;; mark at the end.  Under Emacs, the program fragment is temporarily
+;; hilighted.
+;;
+;; M-x tuareg-run-ocaml starts an OCaml toplevel with input and output in
+;; an Emacs buffer named `*ocaml-toplevel*. This gives you the full
+;; power of Emacs to edit the input to the OCaml toplevel. This mode is
+;; based on comint so you get all the usual comint features, including
+;; command history. A hook named `tuareg-interactive-mode-hook' may be
+;; used for customization.
+;;
+;; Typing C-c C-e in a buffer in tuareg mode sends the current phrase
+;; (containing the point) to the OCaml toplevel, and evaluates it.  If
+;; you type one of these commands before M-x tuareg-run-ocaml, the
+;; toplevel will be started automatically.
+;;
+;; M-x ocamldebug FILE starts the OCaml debugger ocamldebug on the
+;; executable FILE, with input and output in an Emacs buffer named
+;; *ocamldebug-FILE*.  It is similar to April 1996 version, with minor
+;; changes to support XEmacs, Tuareg and OCaml. Furthermore, package
+;; `thingatpt' is not required any more.
+
+;; This file is *NOT* part of GNU Emacs.
+
+;; 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.
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+(require 'easymenu)
+
+(defconst tuareg-mode-revision
+  (eval-when-compile
+    (with-temp-buffer
+      (cond ((file-directory-p ".hg")
+	     (call-process "hg" nil t nil "id" "-i" "--debug"))
+	    ((file-directory-p ".svn")
+             (let ((process-environment
+                    (cons "LANG=C" process-environment)))
+               (shell-command "svn info | grep Revision: | sed 's/Revision: //'" t)))
+	    ((file-directory-p ".bzr")
+	     (shell-command "bzr log -l -1 | grep revno:" t)))
+      (unless (zerop (buffer-size))
+        (buffer-substring-no-properties
+         (point-min) (1- (point-max))))))
+  "Tuareg revision from the control system used.")
+
+(defconst tuareg-mode-version
+  (let ((version "Tuareg Version 2.0.4"))
+    (if (null tuareg-mode-revision)
+	version
+      (concat version " (" tuareg-mode-revision ")")
+      ))
+  "         Copyright (C) 1997-2006 Albert Cohen, all rights reserved.
+         Copyright (C) 2009-2010 Jane Street Holding, LLC.
+         Copying is covered by the GNU General Public License.
+
+    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.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                      Compatibility functions
+
+(defun tuareg-editing-ls3 ()
+  "Tell whether we are editing Lucid Synchrone syntax."
+  (string-match "\\.ls\\'" (or buffer-file-name (buffer-name))))
+
+(defun tuareg-editing-ocamllex ()
+  "Tell whether we are editing OCamlLex syntax."
+  (string-match "\\.mll\\'" (or buffer-file-name (buffer-name))))
+
+(defalias 'tuareg-match-string
+  (if (fboundp 'match-string-no-properties)
+      'match-string-no-properties
+    'match-string))
+
+(or (fboundp 'read-shell-command)
+    (defun read-shell-command  (prompt &optional initial-input history)
+      "Read a string from the minibuffer, using `shell-command-history'."
+      (read-from-minibuffer prompt initial-input nil nil
+                            (or history 'shell-command-history))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                             Import types and help features
+
+(defvar tuareg-with-caml-mode-p
+  (and (require 'caml-types nil t) (require 'caml-help nil t)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                       User customizable variables
+
+;; Use the standard `customize' interface or `tuareg-mode-hook' to
+;; Configure these variables
+
+(require 'custom)
+
+(defgroup tuareg nil
+  "Support for the OCaml language."
+  :group 'languages)
+
+;; Comments
+
+(defcustom tuareg-indent-leading-comments t
+  "*If true, indent leading comment lines (starting with `(*') like others."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-indent-comments t
+  "*If true, automatically align multi-line comments."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-comment-end-extra-indent 0
+  "*How many spaces to indent a leading comment end `*)'.
+If you expect comments to be indented like
+        (*
+          ...
+         *)
+even without leading `*', use `tuareg-comment-end-extra-indent' = 1."
+  :group 'tuareg
+  :type '(radio :extra-offset 8
+                :format "%{Comment End Extra Indent%}:
+   Comment alignment:\n%v"
+                (const :tag "align with `(' in comment opening" 0)
+                (const :tag "align with `*' in comment opening" 1)
+                (integer :tag "custom alignment" 0)))
+
+(defcustom tuareg-support-leading-star-comments t
+  "*Enable automatic intentation of comments of the form
+        (*
+         * ...
+         *)
+Documentation comments (** *) are not concerned by this variable
+unless `tuareg-leading-star-in-doc' is also set.
+
+If you do not set this variable and still expect comments to be
+indented like
+        (*
+          ...
+         *)
+\(without leading `*'), set `tuareg-comment-end-extra-indent' to 1."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-leading-star-in-doc nil
+  "*Enable automatic intentation of documentation comments of the form
+        (**
+         * ...
+         *)"
+  :group 'tuareg :type 'boolean)
+
+;; Indentation defaults
+
+(defcustom tuareg-default-indent 2
+  "*Default indentation.
+
+Global indentation variable (large values may lead to indentation overflows).
+When no governing keyword is found, this value is used to indent the line
+if it has to."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-support-camllight nil
+  "*If true, handle Caml Light character syntax (incompatible with labels)."
+  :group 'tuareg :type 'boolean
+  :set (lambda (var val)
+         (set-default var val)
+         (when (boundp 'tuareg-mode-syntax-table)
+           (modify-syntax-entry ?` (if val "\"" ".")
+                                tuareg-mode-syntax-table))))
+
+(defcustom tuareg-support-metaocaml nil
+  "*If true, handle MetaOCaml syntax."
+  :group 'tuareg :type 'boolean
+  :set (lambda (var val)
+         (set-default var val)
+         (ignore-errors
+           (tuareg-make-indentation-regexps)
+           (dolist (buf (buffer-list))
+             (with-current-buffer buf
+               (when (derived-mode-p 'tuareg-mode 'tuareg-interactive-mode)
+                 (tuareg-install-font-lock)))))))
+
+(defcustom tuareg-let-always-indent t
+  "*If true, enforce indentation is at least `tuareg-let-indent' after a `let'.
+
+As an example, set it to nil when you have `tuareg-with-indent' set to 0,
+and you want `let x = match ... with' and `match ... with' indent the
+same way."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-pipe-extra-unindent tuareg-default-indent
+  "*Extra backward indent for OCaml lines starting with the `|' operator.
+
+It is NOT the variable controlling the indentation of the `|' itself:
+this value is automatically added to `function', `with', `parse' and
+some cases of `type' keywords to leave enough space for `|' backward
+indentation.
+
+For example, setting this variable to 0 leads to the following indentation:
+  match ... with
+    X -> ...
+    | Y -> ...
+    | Z -> ...
+
+To modify the indentation of lines lead by `|' you need to modify the
+indentation variables for `with', `function', and possibly
+for `type' as well. For example, setting them to 0 (and leaving
+`tuareg-pipe-extra-unindent' to its default value) yields:
+  match ... with
+    X -> ...
+  | Y -> ...
+  | Z -> ..."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-class-indent tuareg-default-indent
+  "*How many spaces to indent from a `class' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-sig-struct-align t
+  "*Align `sig' and `struct' keywords with `module'."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-sig-struct-indent tuareg-default-indent
+  "*How many spaces to indent from a `sig' or `struct' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-method-indent tuareg-default-indent
+  "*How many spaces to indent from a `method' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-begin-indent tuareg-default-indent
+  "*How many spaces to indent from a `begin' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-for-while-indent tuareg-default-indent
+  "*How many spaces to indent from a `for' or `while' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-do-indent tuareg-default-indent
+  "*How many spaces to indent from a `do' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-fun-indent tuareg-default-indent
+  "*How many spaces to indent from a `fun' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-function-indent 0 ;tuareg-default-indent
+  "*How many spaces to indent from a `function' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-if-then-else-indent tuareg-default-indent
+  "*How many spaces to indent from an `if', `then' or `else' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-let-indent tuareg-default-indent
+  "*How many spaces to indent from a `let' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-in-indent 0 ; tuareg-default-indent
+  "*How many spaces to indent from a `in' keyword.
+Upstream <http://caml.inria.fr/resources/doc/guides/guidelines.en.html>
+recommends 0, and this is what we default to since 2.0.1
+instead of the historical `tuareg-default-indent'."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-match-indent tuareg-default-indent
+  "*How many spaces to indent from a `match' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-try-indent tuareg-default-indent
+  "*How many spaces to indent from a `try' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-with-indent 0 ;tuareg-default-indent
+  "*How many spaces to indent from a `with' keyword.
+The examples at <http://caml.inria.fr/resources/doc/guides/guidelines.en.html>
+show the '|' is aligned with 'match', thus 0 is the default value."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-type-indent 0 ;tuareg-default-indent
+  "*How many spaces to indent from a `type' keyword."
+  :group 'tuareg :type 'integer)
+
+(defcustom tuareg-val-indent tuareg-default-indent
+  "*How many spaces to indent from a `val' keyword."
+  :group 'tuareg :type 'integer)
+
+;; Automatic indentation
+;; Using abbrev-mode and electric keys
+
+(defcustom tuareg-use-abbrev-mode t
+  "*Non-nil means electrically indent lines starting with leading keywords.
+Leading keywords are such as `end', `done', `else' etc.
+It makes use of `abbrev-mode'.
+
+Many people find electric keywords irritating, so you can disable them by
+setting this variable to nil."
+  :group 'tuareg :type 'boolean
+  :set (lambda (var val)
+         (set-default var val)
+         (dolist (buf (buffer-list))
+           (with-current-buffer buf
+             (when (derived-mode-p 'tuareg-mode)
+               (abbrev-mode (if val 1 -1)))))))
+
+(defcustom tuareg-electric-indent t
+  "*Non-nil means electrically indent lines starting with `|', `)', `]' or `}'.
+
+Many people find electric keys irritating, so you can disable them by
+setting this variable to nil."
+  :group 'tuareg :type 'boolean)
+(when (fboundp 'electric-indent-mode)
+  (make-obsolete-variable 'tuareg-electric-indent
+                          'electric-indent-mode "Emacs-24.1"))
+
+(defcustom tuareg-electric-close-vector t
+  "*Non-nil means electrically insert `|' before a vector-closing `]' or
+`>' before an object-closing `}'.
+
+Many people find electric keys irritating, so you can disable them by
+setting this variable to nil. You should probably have this on,
+though, if you also have `tuareg-electric-indent' on."
+  :group 'tuareg :type 'boolean)
+
+;; Tuareg-Interactive
+;; Configure via `tuareg-mode-hook'
+
+(defcustom tuareg-interactive-scroll-to-bottom-on-output nil
+  "*Controls when to scroll to the bottom of the interactive buffer
+upon evaluating an expression.
+
+See `comint-scroll-to-bottom-on-output' for details."
+  :group 'tuareg :type 'boolean
+  :set (lambda (var val)
+         (set-default var val)
+         (when (boundp 'comint-scroll-to-bottom-on-output)
+           (dolist (buf (buffer-list))
+             (with-current-buffer buf
+               (when (derived-mode-p 'tuareg-interactive-mode)
+                 (set (make-local-variable 'comint-scroll-to-bottom-on-output)
+                      val)))))))
+
+(defcustom tuareg-skip-after-eval-phrase t
+  "*Non-nil means skip to the end of the phrase after evaluation in the
+Caml toplevel."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-interactive-read-only-input nil
+  "*Non-nil means input sent to the OCaml toplevel is read-only."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-interactive-echo-phrase t
+  "*Non-nil means echo phrases in the toplevel buffer when sending
+them to the OCaml toplevel."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-interactive-input-font-lock t
+  "*Non nil means Font-Lock for toplevel input phrases."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-interactive-output-font-lock t
+  "*Non nil means Font-Lock for toplevel output messages."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-interactive-error-font-lock t
+  "*Non nil means Font-Lock for toplevel error messages."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-display-buffer-on-eval t
+  "*Non nil means pop up the OCaml toplevel when evaluating code."
+  :group 'tuareg :type 'boolean)
+
+(defcustom tuareg-manual-url
+  "http://pauillac.inria.fr/ocaml/htmlman/index.html"
+  "*URL to the OCaml reference manual."
+  :group 'tuareg :type 'string)
+
+(defcustom tuareg-browser 'browse-url
+  "*Name of function that displays the OCaml reference manual.
+Valid names are `browse-url', `browse-url-firefox', etc."
+  :group 'tuareg)
+
+(defcustom tuareg-library-path "/usr/local/lib/ocaml/"
+  "*Path to the OCaml library."
+  :group 'tuareg :type 'string)
+
+(defcustom tuareg-definitions-max-items 30
+  "*Maximum number of items a definitions menu can contain."
+  :group 'tuareg :type 'integer)
+
+(defvar tuareg-options-list
+  '(("Automatic indentation of leading keywords" . 'tuareg-use-abbrev-mode)
+    ("Automatic indentation of ), ] and }" . 'tuareg-electric-indent)
+    ("Automatic matching of [| and {<" . 'tuareg-electric-close-vector)
+    "---"
+    ("Indent body of comments" . 'tuareg-indent-comments)
+    ("Indent first line of comments" . 'tuareg-indent-leading-comments)
+    ("Leading-`*' comment style" . 'tuareg-support-leading-star-comments))
+  "*List of menu-configurable Tuareg options.")
+
+(defvar tuareg-interactive-options-list
+  '(("Skip phrase after evaluation" . 'tuareg-skip-after-eval-phrase)
+    ("Echo phrase in interactive buffer" . 'tuareg-interactive-echo-phrase)
+    "---"
+    ("Font-lock interactive input" . 'tuareg-interactive-input-font-lock)
+    ("Font-lock interactive output" . 'tuareg-interactive-output-font-lock)
+    ("Font-lock interactive error" . 'tuareg-interactive-error-font-lock)
+    "---"
+    ("Read only input" . 'tuareg-interactive-read-only-input))
+  "*List of menu-configurable Tuareg options.")
+
+(defvar tuareg-interactive-program "ocaml"
+  "*Default program name for invoking an OCaml toplevel from Emacs.")
+;; Could be interesting to have this variable buffer-local
+;;   (e.g., ocaml vs. metaocaml buffers)
+;; (make-variable-buffer-local 'tuareg-interactive-program)
+
+(eval-and-compile
+  (defconst tuareg-use-syntax-ppss (fboundp 'syntax-ppss)
+    "*If nil, use our own parsing and caching."))
+
+(defgroup tuareg-faces nil
+  "Special faces for the Tuareg mode."
+  :group 'tuareg)
+
+(defconst tuareg-faces-inherit-p
+  (and (boundp 'face-attribute-name-alist)
+       (assq :inherit face-attribute-name-alist)))
+
+(defface tuareg-font-lock-governing-face
+  '((((background light)) (:foreground "blue" :bold t))
+    (t (:foreground "orange" :bold t)))
+  "Face description for governing/leading keywords."
+  :group 'tuareg-faces)
+(defvar tuareg-font-lock-governing-face
+  'tuareg-font-lock-governing-face)
+
+(defface tuareg-font-lock-multistage-face
+  '((((background light))
+     (:foreground "darkblue" :background "lightgray" :bold t))
+    (t (:foreground "steelblue" :background "darkgray" :bold t)))
+  "Face description for MetaOCaml staging operators."
+  :group 'tuareg-faces)
+(defvar tuareg-font-lock-multistage-face
+  'tuareg-font-lock-multistage-face)
+
+(defface tuareg-font-lock-operator-face
+  '((((background light)) (:foreground "brown"))
+    (t (:foreground "khaki")))
+  "Face description for all operators."
+  :group 'tuareg-faces)
+(defvar tuareg-font-lock-operator-face
+  'tuareg-font-lock-operator-face)
+
+(defface tuareg-font-lock-error-face
+  '((t (:foreground "yellow" :background "red" :bold t)))
+  "Face description for all errors reported to the source."
+  :group 'tuareg-faces)
+(defvar tuareg-font-lock-error-face
+  'tuareg-font-lock-error-face)
+
+(defface tuareg-font-lock-interactive-output-face
+  '((((background light))
+     (:foreground "blue4"))
+    (t (:foreground "cyan")))
+  "Face description for all toplevel outputs."
+  :group 'tuareg-faces)
+(defvar tuareg-font-lock-interactive-output-face
+  'tuareg-font-lock-interactive-output-face)
+
+(defface tuareg-font-lock-interactive-error-face
+  (if tuareg-faces-inherit-p
+      '((t :inherit font-lock-warning-face))
+    '((((background light)) (:foreground "red3"))
+      (t (:foreground "red2"))))
+  "Face description for all toplevel errors."
+  :group 'tuareg-faces)
+(defvar tuareg-font-lock-interactive-error-face
+  'tuareg-font-lock-interactive-error-face)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                            Support definitions
+
+(defun tuareg-leading-star-p ()
+  (and tuareg-support-leading-star-comments
+       (save-excursion ; this function does not make sense outside of a comment
+         (tuareg-beginning-of-literal-or-comment)
+         (and (or tuareg-leading-star-in-doc
+                  (not (looking-at "(\\*[Tt][Ee][Xx]\\|(\\*\\*")))
+              (progn
+                (forward-line 1)
+                (back-to-indentation)
+                (looking-at "\\*[^)]"))))))
+
+(defun tuareg-auto-fill-insert-leading-star (&optional leading-star)
+  (let ((point-leading-comment (looking-at "(\\*")) (return-leading nil))
+    (save-excursion
+      (back-to-indentation)
+      (when tuareg-electric-indent
+        (when (and (tuareg-in-comment-p)
+                   (or leading-star
+                       (tuareg-leading-star-p)))
+          (unless (looking-at "(?\\*")
+            (insert-before-markers "* "))
+          (setq return-leading t))
+        (unless point-leading-comment
+          ;; Use optional argument to break recursion
+          (tuareg-indent-command t))))
+    return-leading))
+
+(unless (fboundp 'comment-normalize-vars)
+  (defun tuareg-auto-fill-function ()
+    (unless (tuareg-in-literal-p)
+      (let ((leading-star
+             (and (not (char-equal ?\n last-command-event))
+                  (tuareg-auto-fill-insert-leading-star))))
+        (do-auto-fill)
+        (unless (char-equal ?\n last-command-event)
+          (tuareg-auto-fill-insert-leading-star leading-star))))))
+
+;; these two functions are different from the standard
+;; in that they do NOT signal errors beginning-of-buffer and end-of-buffer
+(defun tuareg-forward-char (&optional step)
+  (if step (goto-char (+ (point) step))
+    (goto-char (1+ (point)))))
+
+(defun tuareg-backward-char (&optional step)
+  (if step (goto-char (- (point) step))
+    (goto-char (1- (point)))))
+
+(defun tuareg-in-indentation-p ()
+  "Return non-nil if all chars between beginning of line and point are blanks."
+  (save-excursion
+    (skip-chars-backward " \t")
+    (bolp)))
+
+(defvar tuareg-cache-stop (point-min))
+(make-variable-buffer-local 'tuareg-cache-stop)
+(defvar tuareg-cache nil)
+(make-variable-buffer-local 'tuareg-cache)
+(defvar tuareg-cache-local nil)
+(make-variable-buffer-local 'tuareg-cache-local)
+(defvar tuareg-cache-last-local nil)
+(make-variable-buffer-local 'tuareg-cache-last-local)
+(defvar tuareg-last-loc (cons nil nil))
+
+;; PPSS definitions
+(defun tuareg-ppss-in-literal-or-comment () (error "tuareg uses PPSS"))
+(defun tuareg-ppss-fontify (beg end) (error "tuareg uses PPSS"))
+(defun tuareg-ppss-in-literal-p ()
+  "Return non-nil if point is inside an OCaml literal."
+  (nth 3 (syntax-ppss)))
+(defun tuareg-ppss-in-comment-p ()
+  "Return non-nil if point is inside or right before an OCaml comment."
+  (or (nth 4 (syntax-ppss))
+      (looking-at "[ \t]*(\\*")))
+(defun tuareg-ppss-in-literal-or-comment-p ()
+  "Return non-nil if point is inside an OCaml literal or comment."
+  (nth 8 (syntax-ppss)))
+(defun tuareg-ppss-beginning-of-literal-or-comment ()
+  "Skips to the beginning of the current literal or comment (or buffer)."
+  (interactive)
+  (goto-char (or (nth 8 (syntax-ppss)) (point))))
+(defun tuareg-ppss-beginning-of-literal-or-comment-fast ()
+  (goto-char (or (nth 8 (syntax-ppss)) (point-min))))
+;; FIXME: not clear if moving out of a string/comment counts as 1 or no.
+(defalias 'tuareg-backward-up-list 'backward-up-list)
+
+;; non-PPSS definitions
+(defun tuareg-!ppss-in-literal-p ()
+  "Return non-nil if point is inside an OCaml literal."
+  (car (tuareg-in-literal-or-comment)))
+(defun tuareg-!ppss-in-comment-p ()
+  "Return non-nil if point is inside an OCaml comment."
+  (cdr (tuareg-in-literal-or-comment)))
+(defun tuareg-!ppss-in-literal-or-comment-p ()
+  "Return non-nil if point is inside an OCaml literal or comment."
+  (tuareg-in-literal-or-comment)
+  (or (car tuareg-last-loc) (cdr tuareg-last-loc)))
+(defun tuareg-!ppss-in-literal-or-comment ()
+  "Return the pair `((tuareg-in-literal-p) . (tuareg-in-comment-p))'."
+  (if (and (<= (point) tuareg-cache-stop) tuareg-cache)
+      (progn
+        (if (or (not tuareg-cache-local) (not tuareg-cache-last-local)
+                (and (>= (point) (caar tuareg-cache-last-local))))
+            (setq tuareg-cache-local tuareg-cache))
+        (while (and tuareg-cache-local (< (point) (caar tuareg-cache-local)))
+          (setq tuareg-cache-last-local tuareg-cache-local
+                tuareg-cache-local (cdr tuareg-cache-local)))
+        (setq tuareg-last-loc
+              (if tuareg-cache-local
+                  (cons (eq (cadar tuareg-cache-local) 'b)
+                        (> (cddar tuareg-cache-local) 0))
+                  (cons nil nil))))
+    (let ((flag t) (op (point)) (mp (min (point) (1- (point-max))))
+          (balance 0) (end-of-comment nil))
+      (while (and tuareg-cache (<= tuareg-cache-stop (caar tuareg-cache)))
+        (setq tuareg-cache (cdr tuareg-cache)))
+      (if tuareg-cache
+          (if (eq (cadar tuareg-cache) 'b)
+              (progn
+                (setq tuareg-cache-stop (1- (caar tuareg-cache)))
+                (goto-char tuareg-cache-stop)
+                (setq balance (cddar tuareg-cache))
+                (setq tuareg-cache (cdr tuareg-cache)))
+            (setq balance (cddar tuareg-cache))
+            (setq tuareg-cache-stop (caar tuareg-cache))
+            (goto-char tuareg-cache-stop)
+            (skip-chars-forward "("))
+          (goto-char (point-min)))
+      (skip-chars-backward "\\\\*")
+      (while flag
+        (if end-of-comment (setq balance 0 end-of-comment nil))
+        (skip-chars-forward "^\\\\'`\"(\\*")
+        (cond
+          ((looking-at "\\\\")
+           (tuareg-forward-char 2))
+          ((looking-at "'\\([^\n\\']\\|\\\\[^ \t\n][^ \t\n]?[^ \t\n]?\\)'")
+           (setq tuareg-cache (cons (cons (1+ (point)) (cons 'b balance))
+                                    tuareg-cache))
+           (goto-char (match-end 0))
+           (setq tuareg-cache (cons (cons (point) (cons 'e balance))
+                                    tuareg-cache)))
+          ((and
+            tuareg-support-camllight
+            (looking-at "`\\([^\n\\']\\|\\\\[^ \t\n][^ \t\n]?[^ \t\n]?\\)`"))
+           (setq tuareg-cache (cons (cons (1+ (point)) (cons 'b balance))
+                                    tuareg-cache))
+           (goto-char (match-end 0))
+           (setq tuareg-cache (cons (cons (point) (cons 'e balance))
+                                    tuareg-cache)))
+          ((looking-at "\"")
+           (tuareg-forward-char)
+           (setq tuareg-cache (cons (cons (point) (cons 'b balance))
+                                    tuareg-cache))
+           (skip-chars-forward "^\\\\\"")
+           (while (looking-at "\\\\")
+             (tuareg-forward-char 2) (skip-chars-forward "^\\\\\""))
+           (tuareg-forward-char)
+           (setq tuareg-cache (cons (cons (point) (cons 'e balance))
+                                    tuareg-cache)))
+          ((looking-at "(\\*")
+           (setq balance (1+ balance))
+           (setq tuareg-cache (cons (cons (point) (cons nil balance))
+                                    tuareg-cache))
+           (tuareg-forward-char 2))
+          ((looking-at "\\*)")
+           (tuareg-forward-char 2)
+           (if (> balance 1)
+               (progn
+                 (setq balance (1- balance))
+                 (setq tuareg-cache (cons (cons (point) (cons nil balance))
+                                          tuareg-cache)))
+               (setq end-of-comment t)
+               (setq tuareg-cache (cons (cons (point) (cons nil 0))
+                                        tuareg-cache))))
+          (t (tuareg-forward-char)))
+        (setq flag (<= (point) mp)))
+      (setq tuareg-cache-local tuareg-cache
+            tuareg-cache-stop (point))
+      (goto-char op)
+      (if tuareg-cache (tuareg-in-literal-or-comment)
+          (setq tuareg-last-loc (cons nil nil))
+          tuareg-last-loc))))
+(defun tuareg-!ppss-beginning-of-literal-or-comment ()
+  "Skips to the beginning of the current literal or comment (or buffer)."
+  (interactive)
+  (when (tuareg-in-literal-or-comment-p)
+    (tuareg-beginning-of-literal-or-comment-fast)))
+
+(defun tuareg-!ppss-beginning-of-literal-or-comment-fast ()
+  (while (and tuareg-cache-local
+              (or (eq 'b (cadar tuareg-cache-local))
+                  (> (cddar tuareg-cache-local) 0)))
+    (setq tuareg-cache-last-local tuareg-cache-local
+          tuareg-cache-local (cdr tuareg-cache-local)))
+  (if tuareg-cache-last-local
+      (goto-char (caar tuareg-cache-last-local))
+    (goto-char (point-min)))
+  (when (eq 'b (cadar tuareg-cache-last-local)) (tuareg-backward-char)))
+
+(defun tuareg-!ppss-backward-up-list ()
+  "Safe up-list regarding comments, literals and errors."
+  (let ((balance 1) (op (point)) (oc nil))
+    (tuareg-in-literal-or-comment)
+    (while (and (> (point) (point-min)) (> balance 0))
+      (setq oc (if tuareg-cache-local (caar tuareg-cache-local) (point-min)))
+      (condition-case nil (up-list -1) (error (goto-char (point-min))))
+      (if (>= (point) oc) (setq balance (1- balance))
+        (goto-char op)
+        (skip-chars-backward "^[]{}()") (tuareg-backward-char)
+        (cond ((tuareg-in-literal-or-comment-p)
+               (tuareg-beginning-of-literal-or-comment-fast))
+              ((looking-at "[[{(]")
+               (setq balance (1- balance)))
+              ((looking-at "[]})]")
+               (setq balance (1+ balance)))))
+      (setq op (point)))))
+
+(defalias 'tuareg-in-literal-or-comment
+  ;; FIXME: These eval-and-compile have no effect.  Maybe eval-when-compile
+  ;; was intended?
+  (eval-and-compile (if tuareg-use-syntax-ppss
+                        'tuareg-ppss-in-literal-or-comment
+                      'tuareg-!ppss-in-literal-or-comment)))
+(defalias 'tuareg-fontify
+    (eval-and-compile (if tuareg-use-syntax-ppss
+                          'tuareg-ppss-fontify
+                          'tuareg-!ppss-fontify)))
+(defalias 'tuareg-in-literal-p
+    (eval-and-compile (if tuareg-use-syntax-ppss
+                          'tuareg-ppss-in-literal-p
+                          'tuareg-!ppss-in-literal-p)))
+(defalias 'tuareg-in-comment-p
+    (eval-and-compile (if tuareg-use-syntax-ppss
+                          'tuareg-ppss-in-comment-p
+                          'tuareg-!ppss-in-comment-p)))
+(defalias 'tuareg-in-literal-or-comment-p
+    (eval-and-compile (if tuareg-use-syntax-ppss
+                          'tuareg-ppss-in-literal-or-comment-p
+                          'tuareg-!ppss-in-literal-or-comment-p)))
+(defalias 'tuareg-beginning-of-literal-or-comment
+    (eval-and-compile (if tuareg-use-syntax-ppss
+                          'tuareg-ppss-beginning-of-literal-or-comment
+                          'tuareg-!ppss-beginning-of-literal-or-comment)))
+(defalias 'tuareg-beginning-of-literal-or-comment-fast
+    (eval-and-compile (if tuareg-use-syntax-ppss
+                          'tuareg-ppss-beginning-of-literal-or-comment-fast
+                          'tuareg-!ppss-beginning-of-literal-or-comment-fast)))
+(defalias 'tuareg-backward-up-list
+    ;; FIXME: not clear if moving out of a string/comment counts as 1 or no.
+    (eval-and-compile (if tuareg-use-syntax-ppss
+                          'backward-up-list
+                          'tuareg-!ppss-backward-up-list)))
+
+(defun tuareg-false-=-p ()
+  "Is the underlying `=' the first/second letter of an operator?"
+  (or (memq (preceding-char) '(?: ?> ?< ?=))
+      (char-equal ?= (char-after (1+ (point))))))
+
+(defun tuareg-at-phrase-break-p ()
+  "Is the underlying `;' a phrase break?"
+  (and (char-equal ?\; (following-char))
+       (or (and (not (eobp))
+                (char-equal ?\; (char-after (1+ (point)))))
+           (char-equal ?\; (preceding-char)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                           Font-lock in Emacs
+
+;; Originally by Stefan Monnier
+
+(defcustom tuareg-font-lock-symbols nil
+  "*Display fun and -> and such using symbols in fonts.
+This may sound like a neat trick, but note that it can change the
+alignment and can thus lead to surprises."
+  :group 'tuareg :type 'boolean)
+
+(defvar tuareg-font-lock-symbols-alist
+  (cond ((fboundp 'decode-char) ;; or a unicode font.
+         `(("fun" . ,(decode-char 'ucs 955))
+           ("sqrt" . ,(decode-char 'ucs 8730))
+           ("not" . ,(decode-char 'ucs 172))
+           ("&&" . ,(decode-char 'ucs 8743)); 'LOGICAL AND' (U+2227)
+           ("or" . ,(decode-char 'ucs 8744)); 'LOGICAL OR' (U+2228)
+           ("||" . ,(decode-char 'ucs 8744))
+           ("[|" . ,(decode-char 'ucs 12314)) ;; 〚
+           ("|]" . ,(decode-char 'ucs 12315)) ;; 〛
+           ("*." . ,(decode-char 'ucs 215))
+           ("/." . ,(decode-char 'ucs 247))
+           ("->" . ,(decode-char 'ucs 8594))
+           ("<-" . ,(decode-char 'ucs 8592))
+           ("<=" . ,(decode-char 'ucs 8804))
+           (">=" . ,(decode-char 'ucs 8805))
+           ("<>" . ,(decode-char 'ucs 8800))
+           ("==" . ,(decode-char 'ucs 8801))
+           ("!=" . ,(decode-char 'ucs 8802))
+           ("<=>" . ,(decode-char 'ucs 8660))
+           (":=" . ,(decode-char 'ucs 8656))
+           ("infinity" . ,(decode-char 'ucs 8734))
+           ;; Some greek letters for type parameters.
+           ("'a" . ,(decode-char 'ucs 945))
+           ("'b" . ,(decode-char 'ucs 946))
+           ("'c" . ,(decode-char 'ucs 947))
+           ("'d" . ,(decode-char 'ucs 948))
+           ("'e" . ,(decode-char 'ucs 949))
+           ("'f" . ,(decode-char 'ucs 966))
+           ("'i" . ,(decode-char 'ucs 953))
+           ("'k" . ,(decode-char 'ucs 954))
+           ("'m" . ,(decode-char 'ucs 956))
+           ("'n" . ,(decode-char 'ucs 957))
+           ("'o" . ,(decode-char 'ucs 969))
+           ("'p" . ,(decode-char 'ucs 960))
+           ("'r" . ,(decode-char 'ucs 961))
+           ("'s" . ,(decode-char 'ucs 963))
+           ("'t" . ,(decode-char 'ucs 964))
+           ("'x" . ,(decode-char 'ucs 958))))
+	((and (fboundp 'make-char) (fboundp 'charsetp) (charsetp 'symbol))
+	 `(("fun" . ,(make-char 'symbol 108))
+	   ("sqrt" . ,(make-char 'symbol 214))
+           ("not" . ,(make-char 'symbol 216))
+           ("&&" . ,(make-char 'symbol 217))
+           ("or" . ,(make-char 'symbol 218))
+           ("||" . ,(make-char 'symbol 218))
+           ("*." . ,(make-char 'symbol 183))
+           ("/." . ,(make-char 'symbol 184))
+           ("<=" . ,(make-char 'symbol 163))
+           ("<-" . ,(make-char 'symbol 172))
+           ("->" . ,(make-char 'symbol 174))
+           (">=" . ,(make-char 'symbol 179))
+           ("<>" . ,(make-char 'symbol 185))
+           ("==" . ,(make-char 'symbol 186))
+           ("<=>" . ,(make-char 'symbol 219))
+           (":=" . ,(make-char 'symbol 220))
+           ("=>" . ,(make-char 'symbol 222))
+           ("infinity" . ,(make-char 'symbol 165))
+           ;; Some greek letters for type parameters.
+           ("'a" . ,(make-char 'symbol 97))
+           ("'b" . ,(make-char 'symbol 98))
+           ("'c" . ,(make-char 'symbol 103)) ; sic! 99 is chi, 103 is gamma
+           ("'d" . ,(make-char 'symbol 100))
+           ("'e" . ,(make-char 'symbol 101))
+           ("'f" . ,(make-char 'symbol 102))
+           ("'i" . ,(make-char 'symbol 105))
+           ("'k" . ,(make-char 'symbol 107))
+           ("'m" . ,(make-char 'symbol 109))
+           ("'n" . ,(make-char 'symbol 110))
+           ("'o" . ,(make-char 'symbol 111))
+           ("'p" . ,(make-char 'symbol 112))
+           ("'r" . ,(make-char 'symbol 114))
+           ("'s" . ,(make-char 'symbol 115))
+           ("'t" . ,(make-char 'symbol 116))
+           ("'x" . ,(make-char 'symbol 120))))))
+
+(defun tuareg-font-lock-compose-symbol (alist)
+  "Compose a sequence of ascii chars into a symbol.
+Regexp match data 0 points to the chars."
+  ;; Check that the chars should really be composed into a symbol.
+  (let* ((mbegin (match-beginning 0))
+         (mend (match-end 0))
+         (syntax (char-syntax (char-after mbegin))))
+    (if (or (eq (char-syntax (or (char-before mbegin) ?\ )) syntax)
+            (eq (char-syntax (or (char-after mend) ?\ )) syntax)
+            (memq (get-text-property mbegin 'face)
+                  '(tuareg-doc-face font-lock-string-face
+                    font-lock-comment-face)))
+        ;; No composition for you. Let's actually remove any composition
+        ;;   we may have added earlier and which is now incorrect.
+        (remove-text-properties mbegin mend '(composition))
+      ;; That's a symbol alright, so add the composition.
+      (compose-region mbegin mend (cdr (assoc (match-string 0) alist)))))
+  ;; Return nil because we're not adding any face property.
+  nil)
+
+(defun tuareg-font-lock-symbols-keywords ()
+  (when (fboundp 'compose-region)
+    (let ((alist nil))
+      (dolist (x tuareg-font-lock-symbols-alist)
+        (when (and (if (fboundp 'char-displayable-p)
+                       (char-displayable-p (cdr x))
+                     t)
+                   (not (assoc (car x) alist))) ; not yet in alist.
+          (push x alist)))
+      (when alist
+        `((,(regexp-opt (mapcar 'car alist) t)
+           (0 (tuareg-font-lock-compose-symbol ',alist))))))))
+
+(defvar tuareg-mode-syntax-table
+  (let ((st (make-syntax-table)))
+    (modify-syntax-entry ?_ "_" st)
+    (modify-syntax-entry ?. "_" st)     ;Make qualified names a single symbol.
+    (modify-syntax-entry ?? ". p" st)
+    (modify-syntax-entry ?~ ". p" st)
+    ;; See http://caml.inria.fr/pub/docs/manual-ocaml/lex.html.
+    (dolist (c '(?! ?$ ?% ?& ?+ ?- ?/ ?: ?< ?= ?> ?@ ?^ ?|))
+      (modify-syntax-entry c "." st))
+    (modify-syntax-entry ?' "_" st) ; ' is part of symbols (for primes).
+    (modify-syntax-entry
+     ;; ` is punctuation or character delimiter (Caml Light compatibility).
+     ?` (if tuareg-support-camllight "\"" ".") st)
+    (modify-syntax-entry ?\" "\"" st) ; " is a string delimiter
+    (modify-syntax-entry ?\\ "\\" st)
+    (modify-syntax-entry ?*  ". 23" st)
+    (condition-case nil
+        (progn
+          (modify-syntax-entry ?\( "()1n" st)
+          (modify-syntax-entry ?\) ")(4n" st))
+      (error               ;XEmacs signals an error instead of ignoring `n'.
+       (modify-syntax-entry ?\( "()1" st)
+       (modify-syntax-entry ?\) ")(4" st)))
+    st)
+  "Syntax table in use in Tuareg mode buffers.")
+
+(defmacro tuareg-with-internal-syntax (&rest body)
+  `(progn
+     ;; Switch to a modified internal syntax.
+     (modify-syntax-entry ?. "w" tuareg-mode-syntax-table)
+     (modify-syntax-entry ?' "w" tuareg-mode-syntax-table)
+     (modify-syntax-entry ?_ "w" tuareg-mode-syntax-table)
+     (unwind-protect (progn ,@body)
+       ;; Switch back to the interactive syntax.
+       (modify-syntax-entry ?. "_" tuareg-mode-syntax-table)
+       (modify-syntax-entry ?' "_" tuareg-mode-syntax-table)
+       (modify-syntax-entry ?_ "_" tuareg-mode-syntax-table))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                                  Font-Lock
+
+(defvar tuareg-doc-face 'font-lock-doc-face)
+
+(unless tuareg-use-syntax-ppss
+
+  (defun tuareg-fontify-buffer ()
+    (font-lock-default-fontify-buffer)
+    (tuareg-fontify (point-min) (point-max)))
+
+  (defun tuareg-fontify-region (begin end &optional verbose)
+    (font-lock-default-fontify-region begin end verbose)
+    (tuareg-fontify begin end))
+
+  (defun tuareg-fontify (begin end)
+    (when (eq major-mode 'tuareg-mode)
+      (save-excursion
+       (tuareg-with-internal-syntax
+
+        (let ((case-fold-search nil)
+              (modified (buffer-modified-p))) ; Emacs hack (see below)
+          (goto-char begin)
+          (setq begin (line-beginning-position))
+          (goto-char (1- end))
+          (end-of-line)
+          ;; Dirty hack to trick `font-lock-default-unfontify-region'
+          (forward-line 2)
+          (setq end (point))
+
+          (while (> end begin)
+            (goto-char (1- end))
+            (tuareg-in-literal-or-comment)
+            (cond
+              ((cdr tuareg-last-loc)
+               (tuareg-beginning-of-literal-or-comment)
+               (put-text-property (max begin (point)) end 'face
+                                  (if (looking-at
+                                       "(\\*[Tt][Ee][Xx]\\|(\\*\\*[^*]")
+                                      tuareg-doc-face
+                                      'font-lock-comment-face))
+               (setq end (1- (point))))
+              ((car tuareg-last-loc)
+               (tuareg-beginning-of-literal-or-comment)
+               (put-text-property (max begin (point)) end 'face
+                                  'font-lock-string-face)
+               (setq end (point)))
+              (t (while (and tuareg-cache-local
+                             (or (> (caar tuareg-cache-local) end)
+                                 (eq 'b (cadar tuareg-cache-local))))
+                   (setq tuareg-cache-local (cdr tuareg-cache-local)))
+                 (setq end (if tuareg-cache-local
+                               (caar tuareg-cache-local) begin)))))
+          (unless modified (set-buffer-modified-p nil)))
+        ))))
+  ) ;; end tuareg-use-syntax-ppss
+
+(defconst tuareg-font-lock-syntactic-keywords
+  ;; Char constants start with ' but ' can also appear in identifiers.
+  ;; Beware not to match things like '*)hel' or '"hel' since the first '
+  ;; might be inside a string or comment.
+  ;; Note: for compatibility with Emacs<23, we use "\\<" rather than "\\_<",
+  ;; which depends on tuareg-font-lock-syntax turning all "_" into "w".
+  '(("\\<\\('\\)\\([^'\\\n]\\|\\\\.[^\\'\n \")]*\\)\\('\\)"
+     (1 '(7)) (3 '(7)))))
+
+(defvar syntax-propertize-function)
+(defconst tuareg-syntax-propertize
+  (when (eval-when-compile (fboundp 'syntax-propertize-rules))
+    (syntax-propertize-rules
+     ;; When we see a '"', knowing whether it's a literal char (as opposed to
+     ;; the end of a string followed by the beginning of a literal char)
+     ;; requires checking syntax-ppss as in:
+     ;; ("\\_<\\('\"'\\)"
+     ;;  (1 (unless (nth 3 (save-excursion (syntax-ppss (match-beginning 0))))
+     ;;       (string-to-syntax "\""))))
+     ;; Not sure if it's worth the trouble since adding a space between the
+     ;; string and the literal char is easy enough and is the usual
+     ;; style anyway.
+     ;; For all other cases we don't need to check syntax-ppss because, if the
+     ;; first quote is within a string (or comment), the whole match is within
+     ;; the string (or comment), so the syntax-properties don't hurt.
+     ;;
+     ;; Note: we can't just use "\\<" here because syntax-propertize is also
+     ;; used outside of font-lock.
+     ("\\_<\\('\\)\\(?:[^'\\\n]\\|\\\\.[^\\'\n \")]*\\)\\('\\)"
+      (1 "\"") (2 "\"")))))
+
+(defun tuareg-font-lock-syntactic-face-function (state)
+  (if (nth 3 state) font-lock-string-face
+    (let ((start (nth 8 state)))
+      (if (and (> (point-max) (+ start 2))
+               (eq (char-after (+ start 2)) ?*)
+               (not (eq (char-after (+ start 3)) ?*)))
+          ;; This is a documentation comment
+          tuareg-doc-face
+        font-lock-comment-face))))
+
+;; Initially empty, set in `tuareg-install-font-lock'
+(defvar tuareg-font-lock-keywords ()
+  "Font-Lock patterns for Tuareg mode.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                                    Keymap
+
+(defvar tuareg-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "|" 'tuareg-electric-pipe)
+    (define-key map ")" 'tuareg-electric-rp)
+    (define-key map "}" 'tuareg-electric-rc)
+    (define-key map "]" 'tuareg-electric-rb)
+    (define-key map "\M-q" 'tuareg-indent-phrase)
+    (define-key map "\C-c\C-q" 'tuareg-indent-phrase)
+    ;; Don't bother: it's the global default anyway.
+    ;;(define-key map "\M-\C-\\" 'indent-region)
+    (define-key map "\C-c\C-a" 'tuareg-find-alternate-file)
+    (define-key map "\C-c\C-c" 'compile)
+    (define-key map "\C-xnd" 'tuareg-narrow-to-phrase)
+    (define-key map "\M-\C-x" 'tuareg-eval-phrase)
+    (define-key map [remap newline-and-indent] 'tuareg-newline-and-indent)
+    (define-key map "\C-x\C-e" 'tuareg-eval-phrase)
+    (define-key map "\C-c\C-e" 'tuareg-eval-phrase)
+    (define-key map "\C-c\C-r" 'tuareg-eval-region)
+    (define-key map "\C-c\C-b" 'tuareg-eval-buffer)
+    (define-key map "\C-c\C-s" 'tuareg-run-ocaml)
+    (define-key map "\C-c\C-i" 'tuareg-interrupt-ocaml)
+    (define-key map "\C-c\C-k" 'tuareg-kill-ocaml)
+    (define-key map "\C-c\C-n" 'tuareg-next-phrase)
+    (define-key map "\C-c\C-p" 'tuareg-previous-phrase)
+    (define-key map [(backspace)] 'backward-delete-char-untabify)
+    (define-key map [(control c) (home)]
+      'tuareg-move-inside-module-or-class-opening)
+    (define-key map [(control c) (control down)] 'tuareg-next-phrase)
+    (define-key map [(control c) (control up)] 'tuareg-previous-phrase)
+    (define-key map [(meta control down)]  'tuareg-next-phrase)
+    (define-key map [(meta control up)] 'tuareg-previous-phrase)
+    (define-key map [(meta control n)]  'tuareg-next-phrase)
+    (define-key map [(meta control p)] 'tuareg-previous-phrase)
+    (define-key map [(meta control h)] 'tuareg-mark-phrase)
+    (define-key map "\C-c`" 'tuareg-interactive-next-error-source)
+    (define-key map "\C-c?" 'tuareg-interactive-next-error-source)
+    (define-key map "\C-c.c" 'tuareg-insert-class-form)
+    (define-key map "\C-c.b" 'tuareg-insert-begin-form)
+    (define-key map "\C-c.f" 'tuareg-insert-for-form)
+    (define-key map "\C-c.w" 'tuareg-insert-while-form)
+    (define-key map "\C-c.i" 'tuareg-insert-if-form)
+    (define-key map "\C-c.l" 'tuareg-insert-let-form)
+    (define-key map "\C-c.m" 'tuareg-insert-match-form)
+    (define-key map "\C-c.t" 'tuareg-insert-try-form)
+    (when tuareg-with-caml-mode-p
+      ;; Trigger caml-types
+      (define-key map [?\C-c ?\C-t] 'caml-types-show-type)  ; "type"
+      (define-key map [?\C-c ?\C-f] 'caml-types-show-call)  ; "function"
+      (define-key map [?\C-c ?\C-l] 'caml-types-show-ident) ; "let"
+      ;; To prevent misbehavior in case of error during exploration.
+      (define-key map [?\C-c mouse-1] 'caml-types-mouse-ignore)
+      (define-key map [?\C-c down-mouse-1] 'caml-types-explore)
+      ;; Trigger caml-help
+      (define-key map [?\C-c ?i] 'ocaml-add-path)
+      (define-key map [?\C-c ?\[] 'ocaml-open-module)
+      (define-key map [?\C-c ?\]] 'ocaml-close-module)
+      (define-key map [?\C-c ?h] 'caml-help)
+      (define-key map [?\C-c ?\t] 'tuareg-complete))
+    map)
+  "Keymap used in Tuareg mode.")
+
+(defconst tuareg-font-lock-syntax
+  ;; Note: as a general rule, changing syntax-table during font-lock
+  ;; is a potential problem for syntax-ppss.
+  `((?_ . "w") (?' . "w")
+    ,@(unless tuareg-use-syntax-ppss
+        '((?` . ".") (?\" . ".") (?\( . ".") (?\) . ".") (?* . "."))))
+  "Syntax changes for Font-Lock.")
+
+(defvar tuareg-electric-indent-keywords
+  '("module" "class" "functor" "object" "type" "val" "inherit"
+    "include" "virtual" "constraint" "exception" "external" "open"
+    "method" "and" "initializer" "to" "downto" "do" "done" "else"
+    "begin" "end" "let" "in" "then" "with"))
+
+(defvar tuareg-mode-abbrev-table ()
+  "Abbrev table used for Tuareg mode buffers.")
+(if tuareg-mode-abbrev-table ()
+  (define-abbrev-table 'tuareg-mode-abbrev-table
+    (mapcar (lambda (keyword)
+              `(,keyword ,keyword tuareg-abbrev-hook nil t))
+            tuareg-electric-indent-keywords)))
+
+(defun tuareg--electric-indent-predicate (char)
+  "Check whether we should auto-indent.
+For use on `electric-indent-functions'."
+  (save-excursion
+    (forward-char -1) ;; Go before the inserted char.
+    (let ((syntax (char-syntax char)))
+      (if (tuareg-in-indentation-p)
+          (or (eq char ?|) (eq syntax ?\)))
+        (or (case char
+              (?\) (char-equal ?* (preceding-char)))
+              (?\} (and (char-equal ?> (preceding-char))
+                        (progn (tuareg-backward-char)
+                               (tuareg-in-indentation-p))))
+              (?\] (and (char-equal ?| (preceding-char))
+                        (progn (tuareg-backward-char)
+                               (tuareg-in-indentation-p)))))
+            (and tuareg-use-abbrev-mode  ;; Misnomer, eh?
+                 (not (eq syntax ?w))
+                 (let ((end (point)))
+                   (skip-syntax-backward "w_")
+                   (member (buffer-substring (point) end)
+                           tuareg-electric-indent-keywords))
+                                           (tuareg-in-indentation-p)))))))
+
+;;; SMIE
+
+;; TODO:
+;; - Obey tuareg-*-indent customization variables.
+;; - Fix use of tuareg-indent-command in tuareg-auto-fill-insert-leading-star.
+;; - Use it by default (when possible).
+;; - Move the old indentation code to a separate file.
+
+(defvar tuareg-use-smie nil)            ;Not used by default yet.
+
+(require 'smie nil 'noerror)
+
+(defconst tuareg-smie-grammar
+  ;; Problems:
+  ;; - "let D in E" expression vs "let D" declaration.  This is solved
+  ;;   by making the lexer return "d-let" for the second case.
+  ;; - FIXME: SMIE assumes that concatenation binds tighter than
+  ;;   everything else, whereas OCaml gives tighter precedence to ".".
+  ;; - "x : t1; (y : (t2 -> t3)); z : t4" but
+  ;;   "when (x1; x2) -> (z1; z2)".  We solve this by distinguishing
+  ;;   the two kinds of arrows, using "t->" for the type arrow.
+  ;; - The "with" in modules's "with type" has different precedence.
+  ;; - Big problem with "if...then": because of SMIE's transitivity of the
+  ;;   precedence relation, we can't properly parse both "if A then B; C" and
+  ;;   "if A then let x = E in B; C else D" (IOW I think a non-transitive OPG
+  ;;   could do it).  We could try and fix the problem in the lexer, but it's
+  ;;   far from obvious how (we'd probably end up having to pre-parse the text
+  ;;   in the lexer to decide which kind of "if" and "then" we're looking
+  ;;   at).  A good solution could be found maybe if SMIE let us disambiguate
+  ;;   lexemes late, i.e. at a time where we have access to the relevant parse
+  ;;   stack.  Or maybe by allowing smie-grammar to use a non-transitive
+  ;;   precedence relation.  But until that happens, we will live with an
+  ;;   incorrect parse, and instead we try to patch up the result with ad-hoc
+  ;;   hacks in tuareg-smie-rules.
+  ;; - The "<module-type> with <mod-constraints>" syntax introduces many
+  ;;   conflicts:
+  ;;      "... with module M = A with module B = C"
+  ;;   vs      "... module M = A with module B = C"
+  ;;   In the first, the second "with" should either have the first "with" as
+  ;;   sibling, or have some earlier construct as parent, whereas in the second
+  ;;   the "with" should have the first "=" (or maybe the first "module", tho
+  ;;   that would not correspond to the actual language syntax and would
+  ;;   probably break other cases) as parent.  Other problems in this
+  ;;   mod-constraints syntax: we need a precedence along the lines of
+  ;;   "with" < "and" < "module/type", whereas the rest of the syntax wants
+  ;;   "module/type" < "and" < "with", so basically all the keywords involved
+  ;;   in mod-constraints need to be handled specially in the lexer :-(
+  ;; - and then some...
+  (when (fboundp 'smie-prec2->grammar)
+    (let ((bnfprec2
+           (smie-bnf->prec2
+            '((decls (decls "type" decls) (decls "d-let" decls)
+                     (decls "and" decls) (decls ";;" decls)
+                     (decls "exception" decls)
+                     (decls "module" decls)
+                     (decls "class" decls)
+                     (decls "val" decls) (decls "external" decls)
+                     (decls "open" decls) (decls "include" decls)
+                     (exception)
+                     (def)
+                     ;; Hack: at the top-level, a "let D in E" can appear in
+                     ;; decls as well, but the lexer classifies it as "d-let",
+                     ;; so we need to make sure that "d-let D in E" doesn't
+                     ;; end up matching the "in" with some far away thingy.
+                     (def-in-exp))
+              (def-in-exp (defs "in" exp))
+              (def (var "d=" exp) (id "d=" datatype) (id "d=" module))
+              (var (id) ("m-type" var) ("rec" var) (id ":" type)
+                   ("l-module" var) ("l-class" var))
+              (exception (id "of" type))
+              (datatype ("{" typefields "}") (typebranches)
+                        (typebranches "with" id))
+              (typebranches (typebranches "|" typebranches) (id "of" type))
+              (typefields (typefields ";" typefields) (id ":" type))
+              (type (type "*…" type) (type "t->" type)
+                    ;; ("<" ... ">") ;; FIXME!
+                    (type "as" id))
+              (id)
+              (module ("struct" decls "end")
+                      ("sig" decls "end")
+                      ("functor" id "->" module)
+                      (module "m-with" mod-constraints))
+              (simpledef (id "c=" type))
+              (mod-constraints (mod-constraints "m-and" mod-constraints)
+                               ("w-type" simpledef)
+                               ("w-module" simpledef))
+              ;; http://caml.inria.fr/pub/docs/manual-ocaml/expr.html
+              ;; exp1 is "all exps except for `if exp then'".
+              (exp1 ("begin" exp "end")
+                    ("(" exp:type ")")
+                    ("[|" exp "|]")
+                    ("{" fields "}")
+                    ("if" exp "then" exp1 "else" exp1)
+                    ;; ("if" exp "then" exp)
+                    ("while" exp "do" exp "done")
+                    ("for" forbounds "do" exp "done")
+                    (exp1 ";" exp1)
+                    ("match" exp "with" branches)
+                    ("function" branches)
+                    ("fun" patterns "->" exp1)
+                    ("try" exp "with" branches)
+                    ("let" defs "in" exp1)
+                    ("object" class-body "end")
+                    ("(" exp:>type ")")
+                    ("{<" fields ">}"))
+              ;; Like `exp' but additionally allow if-then without else.
+              (exp (exp1) ("if" exp "then" exp))
+              (forbounds (iddef "to" exp) (iddef "downto" exp))
+              (defs (def) (defs "and" defs) ("l-open" id))
+              (exp:>type (exp:type ":>" type))
+              (exp:type (exp)) ;; (exp ":" type)
+              (fields (fields1) (exp "with" fields1))
+              (fields1 (fields1 ";" fields1) (iddef))
+              (iddef (id "f=" exp1))
+              (branches (branches "|" branches) (branch))
+              (branch (patterns "->" exp1))
+              (patterns (pattern) (pattern "when" exp1))
+              (pattern (id) (pattern "as" id) (pattern "," pattern))
+              (class-body (class-body "inherit" class-body)
+                          (class-body "method" class-body)
+                          (class-body "val" class-body)
+                          (class-body "constraint" class-body)
+                          (class-field))
+              (class-field (exp) ("mutable" class-field)
+                           ("virtual" class-field) ("private" class-field))
+              ;; We get cyclic dependencies between ; and | because things like
+              ;; "branches | branches" implies that "; > |" whereas "exp ; exp"
+              ;; implies "| > ;" and while those two do not directly conflict
+              ;; because they're constraints on precedences of different sides,
+              ;; they do introduce a cycle later on because those operators are
+              ;; declared associative which adds a constraint that both side
+              ;; are of equal precedence.  So we declare here a dummy rule to
+              ;; force a direct conflict, that we can later resolve with
+              ;; explicit precedence rules.
+              (foo1 (foo1 ";" foo1) (foo1 "|" foo1))
+              )
+            ;; Type precedence rules.
+            ;; http://caml.inria.fr/pub/docs/manual-ocaml/types.html
+            '((nonassoc "as") (assoc "t->") (assoc "*…"))
+            ;; Pattern precedence rules.
+            ;; http://caml.inria.fr/pub/docs/manual-ocaml/patterns.html
+            ;; Note that we don't include "|" because its precedence collides
+            ;; with the one of the | used between branches and resolving the
+            ;; conflict in the lexer is not worth the trouble.
+            '((nonassoc "as") (assoc ",") (assoc "::"))
+            ;; Resolve "{a=(1;b=2)}" vs "{(a=1);(b=2)}".
+            '((nonassoc ";") (nonassoc "f="))
+            ;; Resolve "(function a -> b) | c -> d".
+            '((nonassoc "function") (nonassoc "|"))
+            ;; Resolve "when (function a -> b) -> c".
+            '((nonassoc "function") (nonassoc "->"))
+            ;; Resolve ambiguity "(let d in e2); e3" vs "let d in (e2; e3)".
+            '((nonassoc "in" "match" "->" "with") (nonassoc ";"))
+            ;; Resolve "(if a then b else c);d" vs "if a then b else (c; d)".
+            '((nonassoc ";") (nonassoc "else")) ;; ("else" > ";")
+            ;; Resolve "match e1 with a → (match e2 with b → e3 | c → e4)"
+            ;;      vs "match e1 with a → (match e2 with b → e3) | c → e4"
+            '((nonassoc "with") (nonassoc "|"))
+            ;; Resolve "functor A -> (M with MC)".
+            '((nonassoc "->") (nonassoc "m-with"))
+            ;; Resolve the conflicts caused by "when" and by SMIE's assumption
+            ;; that all non-terminals can match the empty string.
+            '((nonassoc "with") (nonassoc "->")) ; "when (match a with) -> e"
+            '((nonassoc "|") (nonassoc "->")) ; "when (match a with a|b) -> e"
+            ;; Fix up conflict between (decls "and" decls) and (defs "in" exp).
+            '((nonassoc "in") (nonassoc "and"))
+            ;; Resolve the "artificial" conflict introduced by the `foo1' rule.
+            '((assoc "|") (assoc ";"))
+            ;; Fix up associative declaration keywords.
+            '((assoc "type" "d-let" "exception" "module" "val" "open"
+                     "external" "include" "class" ";;")
+              (assoc "and"))
+            '((assoc "val" "method" "inherit" "constraint"))
+            ;; Declare associativity of remaining sequence separators.
+            '((assoc ";")) '((assoc "|")) '((assoc "m-and")))))
+      ;; (dolist (pair '()) ;; ("then" . "|") ("|" . "then")
+      ;;   (display-warning 'prec2 (format "%s %s %s"
+      ;;                                   (car pair)
+      ;;                                   (gethash pair bnfprec2)
+      ;;                                   (cdr pair))))
+      ;; SMIE takes for granted that all non-terminals can match the empty
+      ;; string, which can lead to the addition of unnecessary constraints.
+      ;; Let's remove the ones that cause cycles without causing conflicts.
+      (progn
+        ;; This comes from "exp ; exp" and "function branches", where
+        ;; SMIE doesn't realize that `branches' has to have a -> before ;.
+        (assert (eq '> (gethash (cons "function" ";") bnfprec2)))
+        (remhash (cons "function" ";") bnfprec2))
+      (smie-prec2->grammar
+       (smie-merge-prec2s
+        bnfprec2
+        (smie-precs->prec2
+         ;; Precedence of operators.
+         ;; http://caml.inria.fr/pub/docs/manual-ocaml/expr.html
+         (reverse
+          '((nonassoc "." "#")
+            ;; function application, constructor application, assert, lazy
+            ;; - -. (prefix)	–
+            (right "**…" "lsl" "lsr" "asr")
+            (nonassoc "*…" "/…" "%…" "mod" "land" "lor" "lxor")
+            (left "+…" "-…")
+            (assoc "::")
+            (right "@…" "^…")
+            (left "=…" "<…" ">…" "|…" "&…" "$…")
+            (right "&" "&&")
+            (right "or" "||")
+            (assoc ",")
+            (right "<-" ":=")
+            (assoc ";")))))))))
+
+(defun tuareg-smie--search-backward (tokens)
+  (let (tok)
+    (while (progn
+             (setq tok (tuareg-smie--backward-token))
+             (if (not (zerop (length tok)))
+                 (not (member tok tokens))
+               (condition-case err
+                   (progn (backward-sexp) t)
+                 (scan-error
+                  (setq tok (buffer-substring (nth 3 err) (1+ (nth 3 err))))
+                  nil)))))
+    tok))
+
+(defconst tuareg-smie--type-label-leader
+  '("->" ":" "=" ""))
+(defconst tuareg-smie--exp-operator-leader
+  (delq nil (mapcar (lambda (x) (if (numberp (nth 2 x)) (car x)))
+                    tuareg-smie-grammar)))
+
+(defun tuareg-smie--forward-token ()
+  (forward-comment (point-max))
+  (buffer-substring-no-properties
+   (point)
+   (progn (if (zerop (skip-syntax-forward "."))
+              (skip-syntax-forward "w_'")
+            ;; The "." char is given symbol property so that "M.x" is
+            ;; considered as a single symbol, but in reality, it's part of the
+            ;; operator chars, since "+." and friends are operators.
+            (while (not (and (zerop (skip-chars-forward "."))
+                             (zerop (skip-syntax-forward "."))))))
+          (point))))
+
+(defun tuareg-smie--backward-token ()
+  (forward-comment (- (point)))
+  (buffer-substring-no-properties
+   (point)
+   (progn (if (and (zerop (skip-chars-backward "."))
+                   (zerop (skip-syntax-backward ".")))
+              (skip-syntax-backward "w_'")
+            (unless (memq (char-after) '(?\; ?,)) ; ".;" is not a token.
+              ;; The "." char is given symbol property so that "M.x" is
+              ;; considered as a single symbol, but in reality, it's part of
+              ;; the operator chars, since "+." and friends are operators.
+              (while (not (and (zerop (skip-chars-backward "."))
+                               (zerop (skip-syntax-backward ".")))))))
+          (point))))
+
+(defun tuareg-smie-forward-token ()
+  (let ((tok (tuareg-smie--forward-token)))
+    (cond
+     ((zerop (length tok))
+      (if (not (looking-at "{<\\|\\[|"))
+          tok
+        (goto-char (match-end 0))
+        (match-string 0)))
+     ((or (member tok '("let" "=" "->"
+                        "module" "class" "open" "type" "with" "and"))
+          ;; http://caml.inria.fr/pub/docs/manual-ocaml/expr.html lists
+          ;; the tokens whose precedence is based on their prefix.
+          (memq (aref tok 0) '(?* ?/ ?% ?+ ?- ?@ ?^ ?= ?< ?> ?| ?& ?$)))
+      ;; When indenting, the movement is mainly backward, so it's OK to make
+      ;; the forward tokenizer a bit slower.
+      (save-excursion (tuareg-smie-backward-token)))
+     ((and (member tok '("~" "?"))
+           (looking-at "[[:alpha:]_][[:alnum:]'_]*:"))
+      (goto-char (match-end 0))
+      "label:")
+     ((and (looking-at ":\\(?:[^:]\\|\\'\\)")
+           (string-match "\\`[[:alpha:]_]" tok)
+           (save-excursion
+             (tuareg-smie--backward-token) ;Go back.
+             (member (tuareg-smie--backward-token)
+                     tuareg-smie--type-label-leader)))
+      (forward-char 1)
+      "label:")
+     ((and (equal tok "|") (looking-at "\\]")) (forward-char 1) "|]")
+     ((and (equal tok ">") (looking-at "}")) (forward-char 1) ">}")
+     ((string-match "\\`[[:alpha:]_].*\\.\\'"  tok)
+      (forward-char -1) (substring tok 0 -1))
+     (t tok))))
+
+(defconst tuareg-smie--exp-leaders
+  ;; (let ((leaders ()))
+  ;;   (dolist (cat tuareg-smie-bnf)
+  ;;     (dolist (rule (cdr cat))
+  ;;       (setq rule (reverse rule))
+  ;;       (while (setq rule (cdr (memq 'exp rule)))
+  ;;         (push (car rule) leaders))))
+  ;;   leaders)
+  '("if" "then" "try" "match" "do" "while" "begin" "in" "when"
+    "downto" "to" "else"))
+
+(defun tuareg-smie--label-colon-p ()
+  (and (not (zerop (skip-chars-backward "[[:alnum:]]_")))
+       (or (not (zerop (skip-chars-backward "?~")))
+           (save-excursion
+             (member (tuareg-smie--backward-token)
+                     tuareg-smie--type-label-leader)))))
+
+(defun tuareg-smie-backward-token ()
+  (let ((tok (tuareg-smie--backward-token)))
+    (cond
+     ;; Distinguish a let expression from a let declaration.
+     ((equal tok "let")
+      (save-excursion
+        (let ((prev (tuareg-smie--backward-token)))
+          (if (or (member prev tuareg-smie--exp-leaders)
+                  (if (zerop (length prev))
+                      (and (not (bobp))
+                           (eq 4 (mod (car (syntax-after (1- (point)))) 256)))
+                    (and (eq ?. (char-syntax (aref prev 0)))
+                         (not (equal prev ";;")))))
+              tok
+            "d-let"))))
+     ;; Handle "let module" and friends.
+     ((member tok '("module" "class" "open"))
+      (let ((prev (save-excursion (tuareg-smie--backward-token))))
+        (cond
+         ((equal prev "let") (concat "l-" tok))
+         ((and (member prev '("with" "and")) (equal tok "module")) "w-module")
+         (t tok))))
+     ;; Distinguish a "type ->" from a "case ->".
+     ((equal tok "->")
+      (save-excursion
+        (let (nearest)
+          (while (progn
+                   (setq nearest (tuareg-smie--search-backward
+                                  '("with" "|" "fun" "type" ":" "of")))
+                   (and (equal nearest ":")
+                        (tuareg-smie--label-colon-p))))
+          (if (member nearest '("with" "|" "fun"))
+              tok "t->"))))
+     ;; Handle "module type" and mod-constraint's "with/and type".
+     ((equal tok "type")
+      (save-excursion
+        (let ((prev (tuareg-smie--backward-token)))
+          (cond ((equal prev "module") "m-type")
+                ((member prev '("and" "with")) "w-type")
+                (t tok)))))
+     ;; Disambiguate mod-constraint's "and" and "with".
+     ((member tok '("with" "and"))
+      (save-excursion
+        (tuareg-smie--forward-token)
+        (if (member (tuareg-smie--forward-token) '("type" "module"))
+            (concat "m-" tok) tok)))
+     ;; Distinguish a defining = from a comparison-=.
+     ((equal tok "=")
+      (save-excursion
+        (let ((pos (point))
+              (nearest (tuareg-smie--search-backward
+                        '("type" "let" "module" "class" "and"
+                          "=" "if" "then" "else" "->" ";"))))
+          (cond
+           ((and (member nearest '("{" ";"))
+                 (let ((field t))
+                   (while
+                       (let ((x (tuareg-smie--forward-token)))
+                         (and (< (point) pos)
+                              (cond
+                               ((zerop (length x)) (setq field nil))
+                               ((memq (char-syntax (aref x 0)) '(?w ?_)))
+                               ((member x '("." ";")))
+                               (t (setq field nil))))))
+                   field))
+            "f=")                       ;Field definition.
+           ((not (member nearest '("type" "let" "module" "class" "and"))) "=…")
+           ((and (member nearest '("type" "module"))
+                 (member (tuareg-smie--backward-token) '("with" "and"))) "c=")
+           (t "d=")))))
+     ((zerop (length tok))
+      (if (not (and (memq (char-before) '(?\} ?\]))
+                    (save-excursion (forward-char -2)
+                                    (looking-at ">}\\||\\]"))))
+          tok
+        (goto-char (match-beginning 0))
+        (match-string 0)))
+     ((and (equal tok "|") (eq (char-before) ?\[)) (forward-char -1) "[|")
+     ((and (equal tok "<") (eq (char-before) ?\{)) (forward-char -1) "{<")
+     ;; Some infix operators get a precedence based on their prefix, so we
+     ;; collapse them into a canonical representative.
+     ;; See http://caml.inria.fr/pub/docs/manual-ocaml/expr.html.
+     ((memq (aref tok 0) '(?* ?/ ?% ?+ ?- ?@ ?^ ?= ?< ?> ?| ?& ?$))
+      (cond
+       ((member tok '("|" "||" "&" "&&" "<-" "->")) tok)
+       ((and (eq (aref tok 0) ?*) (> (length tok) 1) (eq (aref tok 1) ?*))
+        "**…")
+       (t (string (aref tok 0) ?…))))
+     ((equal tok ":")
+      (let ((pos (point)))
+        (if (tuareg-smie--label-colon-p)
+            "label:"
+          (goto-char pos)
+          tok)))
+     ((string-match "\\`[[:alpha:]_].*\\.\\'"  tok)
+      (forward-char (1- (length tok))) ".")
+     (t tok))))
+
+(defun tuareg-smie-rules (kind token)
+  ;; FIXME: Handling of "= |", "with |", "function |", and "[ |" is
+  ;; problematic.
+  (cond
+   ;; Special indentation for module fields.
+   ((and (eq kind :after) (member token '("." ";"))
+         (smie-rule-parent-p "with")
+         (tuareg-smie--with-module-fields-rule)))
+   ;; Special indentation for monadic >>>, >>|, and >>= operators.
+   ((and (eq kind :before) (tuareg-smie--monadic-rule token)))
+   ((member token '(";" "|" "," "and" "m-and"))
+    (cond
+     ((and (eq kind :before) (member token '("|" ";"))
+           (smie-rule-parent-p "then")
+           ;; We have misparsed the code: TOKEN is not a child of `then' but
+           ;; should have closed the "if E1 then E2" instead!
+           (tuareg-smie--if-then-hack token)))
+     ;; FIXME: smie-rule-separator doesn't behave correctly when the separator
+     ;; is right after the parent (on another line).
+     ((smie-rule-prev-p "d=" "with" "[" "function")
+      (if (and (eq kind :before) (smie-rule-bolp)
+               (smie-rule-prev-p "[" "d=" "function"))
+          0 tuareg-with-indent))
+     (t (smie-rule-separator kind))))
+   (t
+    (case kind
+      (:elem (if (eq token 'basic) tuareg-default-indent))
+      (:list-intro (member token '("fun")))
+      (:before
+       (cond
+	((equal token "d=") (smie-rule-parent 2))
+	((member token '("fun" "match"))
+         (if (and (not (smie-rule-bolp)) (smie-rule-prev-p "d="))
+             (smie-rule-parent tuareg-default-indent)))
+	((equal token "then") (smie-rule-parent))
+	((equal token "if") (if (and (not (smie-rule-bolp))
+				     (smie-rule-prev-p "else"))
+				(smie-rule-parent)))
+	((and (equal token "with") (smie-rule-parent-p "{"))
+	 (smie-rule-parent))
+        ;; Align the "with" of "module type A = B \n with ..." w.r.t "module".
+	((and (equal token "m-with") (smie-rule-parent-p "d="))
+         (save-excursion
+           (smie-backward-sexp token)
+           (goto-char (nth 1 (smie-backward-sexp 'halfsexp)))
+           (cons 'column (+ 2 (current-column)))))
+        ;; Treat purely syntactic block-constructs as being part of their
+        ;; parent, when the opening statement is hanging.
+        ((member token '("let" "(" "[" "{" "struct"))
+         (when (and (smie-rule-hanging-p)
+                    (apply #'smie-rule-prev-p
+                           tuareg-smie--exp-operator-leader))
+           (if (let ((openers '("{" "(" "{<" "[" "[|")))
+                 (or (apply #'smie-rule-prev-p openers)
+                     (not (apply #'smie-rule-parent-p openers))))
+               (smie-rule-parent)
+             ;; In "{ a = (", "{" and "a =" are not part of the same
+             ;; syntax rule, so "(" is part of "a =" but not of the
+             ;; surrounding "{".
+             (save-excursion
+               (smie-backward-sexp 'halfsexp)
+               (cons 'column (smie-indent-virtual))))))))
+      (:after
+       (cond
+        ((equal token "d=")
+         (and (smie-rule-parent-p "type")
+              (not (smie-rule-next-p "["))
+              2))
+        ((equal token "->")
+         (if (and (smie-rule-parent-p "with")
+                  ;; Align with "with" but only if it's the only branch (often
+                  ;; the case in try..with), since otherwise subsequent
+                  ;; branches can't be both indented well and aligned.
+                  (save-excursion
+                    (and (not (equal "|" (nth 2 (smie-forward-sexp "|"))))
+                         ;; Since we may misparse "if..then.." we need to
+                         ;; double check that smie-forward-sexp indeed got us
+                         ;; to the right place.
+                         (equal (nth 2 (smie-backward-sexp "|")) "with"))))
+             (smie-rule-parent 2) 0))
+        ((equal token ":")
+         (if (smie-rule-parent-p "val") (smie-rule-parent 2) 2))
+	((equal token "in") tuareg-in-indent) ;;(if (smie-rule-hanging-p)
+	((equal token "with")
+	 (cond
+	  ;; ((smie-rule-next-p "|") 2)
+	  ((smie-rule-parent-p "{") nil)
+	  (t (+ 2 tuareg-with-indent))))
+        ((or (member token '("." "t->" "]"))
+             (consp (nth 2 (assoc token tuareg-smie-grammar)))) ;; Closer.
+         nil)
+        (t tuareg-default-indent)))))))
+
+(defun tuareg-smie--with-module-fields-rule ()
+  ;; Indentation of fields after "{ E with Module." where the "Module."
+  ;; syntactically only applies to the first field, but has
+  ;; semantically a higher position since it applies to all fields.
+  (save-excursion
+    (forward-char 1)
+    (let ((parent-data (smie-backward-sexp 'halfsexp)))
+      (when (looking-at "\\(?:\\sw\\|\\s_\\)+\\.[ \t]*$")
+        (smie-backward-sexp 'halfsexp)
+        (cons 'column (current-column))))))
+
+(defun tuareg-smie--monadic-rule (token)
+  ;; When trying to indent a >>=, try to look back to find any earlier
+  ;; >>= in a sequence of "monadic steps".
+  (or (and (equal token ">…") (looking-at ">>[>=|]")
+           (save-excursion
+             (tuareg-smie--forward-token)
+             (let ((indent nil))
+               (while
+                   (let ((parent-data (smie-backward-sexp 'halfsexp)))
+                     (cond
+                      ((car parent-data) (member (nth 2 parent-data) '("->")))
+                      ((member (nth 2 parent-data) '(";" "d=")) nil)
+                      ((equal (nth 2 parent-data) "fun")
+                       (let ((pos (point)))
+                         (if (member (tuareg-smie--backward-token)
+                                     '(">>|" ">>=" ">>>"))
+                             (progn
+                               (setq indent (cons 'column
+                                                  (smie-indent-virtual)))
+                               nil)
+                           t))))))
+               indent)))
+      ;; In "foo >>= fun x => bar" indent `bar' relative to `foo'.
+      (and (equal token "fun") (not (smie-rule-bolp))
+           (save-excursion
+             (let ((prev (tuareg-smie-backward-token)))
+               ;; FIXME: Should we use the same loop as above?
+               (and (equal prev ">…") (looking-at ">>[>=]")
+                    (progn (smie-backward-sexp prev)
+                           (cons 'column (current-column)))))))))
+
+(defun tuareg-smie--if-then-hack (token)
+  ;; Getting SMIE's parser to properly parse "if E1 then E2" is difficult, so
+  ;; instead we live with a confused parser and try to work around the mess
+  ;; here, although it clearly won't help other uses of the parser
+  ;; (e.g. navigation).
+  (save-excursion
+    (let (pd)
+      (while (equal (nth 2 (setq pd (smie-backward-sexp token))) "then")
+        (let ((pdi (smie-backward-sexp 'halfsexp)))
+          (assert (equal (nth 2 pdi) "if"))))
+      (cond
+       ((equal (nth 2 pd) token)
+        (goto-char (nth 1 pd))
+        (cons 'column (smie-indent-virtual)))
+       (t (cons 'column (current-column)))))))
+
+(defun tuareg-smie--inside-string ()
+  (when (nth 3 (syntax-ppss))
+    (goto-char (1+ (nth 8 (syntax-ppss))))
+    (current-column)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                              The major mode
+
+(defun tuareg--common-mode-setup ()
+  (setq local-abbrev-table tuareg-mode-abbrev-table)
+  (set (make-local-variable 'syntax-propertize-function)
+       tuareg-syntax-propertize)
+  (set (make-local-variable 'parse-sexp-ignore-comments)
+       ;; Tuareg used to set this to nil (for an unknown reason) but SMIE needs
+       ;; it to be set to t.
+       tuareg-use-smie)
+  (if (and tuareg-smie-grammar tuareg-use-smie)
+      (progn
+        (smie-setup tuareg-smie-grammar #'tuareg-smie-rules
+                    :forward-token #'tuareg-smie-forward-token
+                    :backward-token #'tuareg-smie-backward-token)
+        (add-hook 'smie-indent-functions #'tuareg-smie--inside-string nil t))
+    (set (make-local-variable 'indent-line-function) #'tuareg-indent-command))
+  (tuareg-install-font-lock)
+  (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil)
+
+  (add-hook 'completion-at-point-functions #'tuareg-completion-at-point nil t)
+
+  (when (fboundp 'electric-indent-mode)
+    (add-hook 'electric-indent-functions
+              #'tuareg--electric-indent-predicate nil t))
+  (when (boundp 'post-self-insert-hook)
+    (add-hook 'post-self-insert-hook #'tuareg--electric-close-vector nil t)))
+
+;;;###autoload(add-to-list 'auto-mode-alist '("\\.ml[iylp]?" . tuareg-mode))
+;;;###autoload(dolist (ext '(".cmo" ".cmx" ".cma" ".cmxa" ".cmi"))
+;;;###autoload  (add-to-list 'completion-ignored-extensions ext))
+
+(defalias 'tuareg--prog-mode
+  (if (fboundp 'prog-mode) #'prog-mode #'fundamental-mode))
+(defvar compilation-first-column)
+
+;;;###autoload
+(define-derived-mode tuareg-mode tuareg--prog-mode "Tuareg"
+  "Major mode for editing OCaml code.
+
+Dedicated to Emacs and XEmacs, version 21 and higher.  Provides
+automatic indentation and compilation interface. Performs font/color
+highlighting using Font-Lock.  It is designed for OCaml but handles
+Caml Light as well.
+
+The Font-Lock minor-mode is used according to your customization
+options.
+
+You have better byte-compile tuareg.el.
+
+For customization purposes, you should use `tuareg-mode-hook'
+\(run for every file) or `tuareg-load-hook' (run once) and not patch
+the mode itself. You should add to your configuration file something like:
+  (add-hook 'tuareg-mode-hook
+            (lambda ()
+               ... ; your customization code
+            ))
+For example you can change the indentation of some keywords, the
+`electric' flags, Font-Lock colors... Every customizable variable is
+documented, use `C-h-v' or look at the mode's source code.
+
+`dot-emacs.el' is a sample customization file for standard changes.
+You can append it to your `.emacs' or use it as a tutorial.
+
+`M-x ocamldebug' FILE starts the OCaml debugger ocamldebug on the executable
+FILE, with input and output in an Emacs buffer named *ocamldebug-FILE*.
+
+A Tuareg Interactive Mode to evaluate expressions in a toplevel is included.
+Type `M-x tuareg-run-ocaml' or see special-keys below.
+
+For the best indentation experience, some elementary rules must be followed.
+  - Because the `function' keyword has a special indentation (to handle
+    case matches) use the `fun' keyword when no case match is performed.
+  - In OCaml, `;;' is no longer necessary for correct indentation,
+    except before top level phrases not introduced by `type', `val', `let'
+    etc. (i.e., phrases used for their side-effects or to be executed
+    in a top level.)
+  - Long sequences of `and's may slow down indentation slightly, since
+    some computations (few) require to go back to the beginning of the
+    sequence. Some very long nested blocks may also lead to slow
+    processing of `end's, `else's, `done's...
+  - Multiline strings are handled properly, but you may prefer string
+    concatenation `^' to break long strings (the C-j keystroke can help).
+  - Comment indentation is often a matter of taste and context, yet
+    sophisticated heuristics provide reasonable indentation in most cases.
+    When inserting a comment right before the code it refers to, it is
+    generally expected that this comment will be aligned with the folowing
+    code; to enforce this, leave a blank line before the comment.
+
+Known bugs:
+  - When writing a line with mixed code and comments, avoid putting
+    comments at the beginning or middle of the text. More precisely,
+    writing comments immediately after `=' or parentheses then writing
+    some more code on the line leads to indentation errors. You may write
+    `let x (* blah *) = blah' but should avoid `let x = (* blah *) blah'.
+
+Short cuts for the Tuareg mode:
+\\{tuareg-mode-map}
+
+Short cuts for interactions with the toplevel:
+\\{tuareg-interactive-mode-map}"
+
+  ;; Initialize the Tuareg menu
+  (tuareg-build-menu)
+
+  ;; Initialize indentation regexps
+  (tuareg-make-indentation-regexps)
+
+  (set (make-local-variable 'paragraph-start)
+       (concat "^[ \t]*$\\|\\*)$\\|" page-delimiter))
+  (set (make-local-variable 'paragraph-separate) paragraph-start)
+  (set (make-local-variable 'require-final-newline) t)
+  (set (make-local-variable 'comment-start) "(* ")
+  (set (make-local-variable 'comment-end) " *)")
+  (set (make-local-variable 'comment-start-skip) "(\\*+[ \t]*")
+  (set (make-local-variable 'comment-column) 40)              ;FIXME: Why?
+  (set (make-local-variable 'comment-multi-line) t)           ;FIXME: Why?
+  ;; `ocamlc' counts columns from 0, contrary to other tools which start at 1.
+  (set (make-local-variable 'compilation-first-column) 0)
+  (tuareg--common-mode-setup)
+  (unless tuareg-use-syntax-ppss
+    (add-hook 'before-change-functions 'tuareg-before-change-function nil t))
+  (unless (fboundp 'comment-normalize-vars)
+    ;; Emacs-21's newcomment.el provides this functionality by default.
+    (set (make-local-variable 'normal-auto-fill-function)
+         #'tuareg-auto-fill-function))
+
+  (set (make-local-variable 'imenu-create-index-function)
+       #'tuareg-imenu-create-index)
+
+  (when (and tuareg-use-abbrev-mode
+             (not (and (boundp 'electric-indent-mode) electric-indent-mode)))
+    (abbrev-mode 1))
+  (message nil))
+
+(defun tuareg-install-font-lock ()
+  (setq
+   tuareg-font-lock-keywords
+   `(,@(and (tuareg-editing-ls3)
+            '(("\\<\\(let[ \t\n]+\\(clock\\|node\\|static\\)\\|present\\|automaton\\|where\\|match\\|with\\|do\\|done\\|unless\\|until\\|reset\\|every\\)\\>"
+               0 tuareg-font-lock-governing-face nil nil)))
+     ("\\<\\(external\\|open\\|include\\|s\\(ig\\|truct\\)\\|module\\|functor\\|with[ \t\n]+\\(type\\|module\\)\\|val\\|type\\|method\\|virtual\\|constraint\\|class\\|in\\|inherit\\|initializer\\|let\\|rec\\|object\\|and\\|begin\\|end\\)\\>"
+      0 tuareg-font-lock-governing-face nil nil)
+     ,@(and tuareg-support-metaocaml
+            '(("\\.<\\|>\\.\\|\\.~\\|\\.!"
+               0 tuareg-font-lock-multistage-face nil nil)))
+     ("\\<\\(false\\|true\\)\\>" 0 font-lock-constant-face nil nil)
+     ("\\<\\(as\\|do\\(ne\\|wnto\\)?\\|else\\|for\\|if\\|mutable\\|new\\|p\\(arser\\|rivate\\)\\|t\\(hen\\|o\\|ry\\)\\|wh\\(en\\|ile\\)\\|match\\|with\\|lazy\\|exception\\|raise\\|failwith[fs]?\\|exit\\|assert\\|fun\\(ction\\)?\\)\\>"
+      0 font-lock-keyword-face nil nil)
+     ,@(if (tuareg-editing-ls3)
+           '(("\\<\\(merge\\|when\\|emit\\|period\\)\\>"
+              0 font-lock-keyword-face nil nil)
+             ("[][;,()|{}]\\|[@^!:*=<>&/%+~?#---]\\.?\\|\\.\\.\\.*\\|\\<\\(asr\\|asl\\|lsr\\|lsl\\|l?or\\|l?and\\|lxor\\|l?not\\|mod\\|of\\|ref\\|fby\\|pre\\|last\\|at\\)\\>"
+              0 tuareg-font-lock-operator-face nil nil)
+             ("\\<\\(\\(method\\([ \t\n]+\\(private\\|virtual\\)\\)?\\)\\([ \t\n]+virtual\\)?\\|val\\([ \t\n]+mutable\\)?\\|external\\|and\\|class\\|let\\([ \t\n]+\\(rec\\|clock\\|node\\|static\\)\\)?\\)\\>[ \t\n]*\\(['_[:lower:]]\\(\\w\\|[._]\\)*\\)\\>[ \t\n]*\\(\\(\\w\\|[()_?~.'*:--->]\\)+\\|=[ \t\n]*fun\\(ction\\)?\\>\\)"
+              9 font-lock-function-name-face keep nil))
+           '(("[][;,()|{}]\\|[@^!:*=<>&/%+~?#---]\\.?\\|\\.\\.\\.*\\|\\<\\(asr\\|asl\\|lsr\\|lsl\\|l?or\\|l?and\\|lxor\\|l?not\\|mod\\|of\\|ref\\)\\>"
+              0 tuareg-font-lock-operator-face nil nil)
+             ("\\<\\(\\(method\\([ \t\n]+\\(private\\|virtual\\)\\)?\\)\\([ \t\n]+virtual\\)?\\|val\\([ \t\n]+mutable\\)?\\|external\\|and\\|class\\|let\\([ \t\n]+rec\\)?\\)\\>[ \t\n]*\\(['_[:lower:]]\\(\\w\\|[._]\\)*\\)\\>[ \t\n]*\\(\\(\\w\\|[()_?~.'*:--->]\\)+\\|=[ \t\n]*fun\\(ction\\)?\\>\\)"
+              8 font-lock-function-name-face keep nil)))
+     ("\\<method\\([ \t\n]+\\(private\\|virtual\\)\\)?\\>[ \t\n]*\\(\\(\\w\\|[_,?~.]\\)*\\)"
+      3 font-lock-function-name-face keep nil)
+     ("\\<\\(fun\\(ction\\)?\\)\\>[ \t\n]*\\(\\(\\w\\|[_ \t()*,]\\)+\\)"
+      3 font-lock-variable-name-face keep nil)
+     ,@(if (tuareg-editing-ls3)
+           '(("\\<\\(reset\\|do\\|val\\([ \t\n]+mutable\\)?\\|external\\|and\\|class\\|let\\([ \t\n]+rec\\)?\\)\\>[ \t\n]*\\(\\(\\w\\|[_,?~.]\\)*\\)"
+              4 font-lock-variable-name-face keep nil)
+             ("\\<\\(reset\\|do\\|val\\([ \t\n]+mutable\\)?\\|external\\|method\\|and\\|class\\|let\\([ \t\n]+\\(rec\\|clock\\|node\\|static\\)\\)?\\)\\>[ \t\n]*\\(\\(\\w\\|[_,?~.]\\)*\\)\\>\\(\\(\\w\\|[->_ \t,?~.]\\|(\\(\\w\\|[--->_ \t,?~.=]\\)*)\\)*\\)"
+              7 font-lock-variable-name-face keep nil))
+           '(("\\<\\(val\\([ \t\n]+mutable\\)?\\|external\\|and\\|class\\|let\\([ \t\n]+rec\\)?\\)\\>[ \t\n]*\\(\\(\\w\\|[_,?~.]\\)*\\)"
+              4 font-lock-variable-name-face keep nil)
+             ("\\<\\(val\\([ \t\n]+mutable\\)?\\|external\\|method\\|and\\|class\\|let\\([ \t\n]+rec\\)?\\)\\>[ \t\n]*\\(\\(\\w\\|[_,?~.]\\)*\\)\\>\\(\\(\\w\\|[->_ \t,?~.]\\|(\\(\\w\\|[--->_ \t,?~.=]\\)*)\\)*\\)"
+              6 font-lock-variable-name-face keep nil)))
+     ( "\\<\\(open\\|\\(class\\([ \t\n]+type\\)?\\)\\([ \t\n]+virtual\\)?\\|inherit\\|include\\|module\\([ \t\n]+\\(type\\|rec\\)\\)?\\|type\\)\\>[ \t\n]*\\(['~?]*\\([_--->.* \t]\\|\\w\\|(['~?]*\\([_--->.,* \t]\\|\\w\\)*)\\)*\\)"
+           7 font-lock-type-face keep nil)
+     ,@(and (tuareg-editing-ls3)
+            '(("\\<val\\>[ \t\n]*\\w*[ \t\n]*::[ \t\n]*\\(['~?]*\\([_--->.* \t]\\|\\w\\|(['~?]*\\([_--->.,* \t]\\|\\w\\)*)\\)*\\)"
+               1 font-lock-type-face keep nil)))
+     ("[^:>=]:[ \t\n]*\\(['~?]*\\([_--->.* \t]\\|\\w\\|(['~?]*\\([_--->.,* \t]\\|\\w\\)*)\\)*\\)"
+      1 font-lock-type-face keep nil)
+     ("\\<\\([A-Z]\\w*\\>\\)[ \t]*\\." 1 font-lock-type-face keep nil)
+     ("\\<\\([?~]?[_[:alpha:]]\\w*\\)[ \t\n]*:[^:>=]"
+      1 font-lock-variable-name-face keep nil)
+     ("\\<exception\\>[ \t\n]*\\(\\<[_[:alpha:]]\\w*\\>\\)"
+      1 font-lock-variable-name-face keep nil)
+     ("^#\\w+\\>" 0 font-lock-preprocessor-face t nil)
+     ,@(and tuareg-font-lock-symbols
+            (tuareg-font-lock-symbols-keywords))))
+  (setq font-lock-defaults
+        `(tuareg-font-lock-keywords
+          ,(not tuareg-use-syntax-ppss) nil
+          ,tuareg-font-lock-syntax nil
+          ,@(unless tuareg-syntax-propertize
+              '((font-lock-syntactic-keywords
+                 . tuareg-font-lock-syntactic-keywords)
+                (parse-sexp-lookup-properties . t)))
+          (font-lock-syntactic-face-function
+           . tuareg-font-lock-syntactic-face-function)
+          ,@(unless tuareg-use-syntax-ppss
+              '((font-lock-fontify-region-function
+                 . tuareg-fontify-region)))))
+  (when (and (boundp 'font-lock-fontify-region-function)
+             (not tuareg-use-syntax-ppss))
+    (set (make-local-variable 'font-lock-fontify-region-function)
+         'tuareg-fontify-region)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                               Error processing
+
+(require 'compile)
+
+;; In some versions of Emacs, the regexps in
+;; compilation-error-regexp-alist do not match the error messages when
+;; the language is not English.  Hence we add a regexp.
+;; FIXME: We should report those cases to bug-gnu-emacs@gnu.org.
+
+(defconst tuareg-error-regexp
+  (concat "^[^\0-@]+ \"\\([^\"\n]+\\)\", "                     ;File name.
+          "[^\0-@]+ \\([0-9]+\\)\\(?:\\(?:-\\([0-9]+\\)\\)?, " ;Lines.
+          "[^\0-@]+ \\([0-9]+\\)-\\([0-9]+\\):"                ;Columns.
+          "\\(\nWarning\\)?\\|[-,:]\\)")                       ;Warning/error.
+  "Regular expression matching the error messages produced by ocamlc.")
+
+(when (boundp 'compilation-error-regexp-alist)
+  (or (assoc tuareg-error-regexp
+             compilation-error-regexp-alist)
+      (setq compilation-error-regexp-alist
+            (cons (if (fboundp 'compilation-fake-loc)
+                      (list tuareg-error-regexp
+                            1 '(2 . 3) '(4 . 5) '(6))
+                    (list tuareg-error-regexp 1 2))
+                  compilation-error-regexp-alist))))
+
+;; A regexp to extract the range info.
+
+;; (defconst tuareg-error-chars-regexp
+;;   ".*, .*, [^\0-@]+ \\([0-9]+\\)-\\([0-9]+\\):"
+;;   "Regexp matching the char numbers in an error message produced by ocamlc.")
+
+;; Wrapper around next-error.
+
+;; itz 04-21-96 instead of defining a new function, use defadvice
+;; that way we get our effect even when we do \C-x` in compilation buffer
+
+;; smclaughlin 07-19-11 defadvice is to be avoided.  It makes debugging
+;; much more difficult.  If you really want this behavior, write your
+;; own next-error-function.  In particular, it breaks when omake is
+;; used.
+
+;; (defadvice next-error (after tuareg-next-error activate)
+;;  "Read the extra positional information provided by the OCaml compiler.
+
+;; Puts the point and the mark exactly around the erroneous program
+;; fragment. The erroneous fragment is also temporarily highlighted if
+;; possible."
+;;  (when (eq major-mode 'tuareg-mode)
+;;    (let ((beg nil) (end nil))
+;;      (with-current-buffer compilation-last-buffer
+;;        (save-excursion
+;;          (goto-char (window-point (get-buffer-window (current-buffer) t)))
+;;          (when (looking-at tuareg-error-chars-regexp)
+;;            (setq beg (string-to-number (tuareg-match-string 1))
+;;                  end (string-to-number (tuareg-match-string 2))))))
+;;      (beginning-of-line)
+;;      (when beg
+;;        (setq beg (+ (point) beg) end (+ (point) end))
+;;        (goto-char beg) (push-mark end t t)))))
+
+(defvar tuareg-interactive-error-regexp
+  (concat "\\(\\("
+          "Toplevel input:"
+          "\\|Entr.e interactive:"
+          "\\|Characters [0-9-]*:"
+          "\\|The global value [^ ]* is referenced before being defined."
+          "\\|La valeur globale [^ ]* est utilis.e avant d'.tre d.finie."