diff options
| author | Kathy Gray | 2014-09-05 23:33:37 +0100 |
|---|---|---|
| committer | Kathy Gray | 2014-09-05 23:33:37 +0100 |
| commit | 9760b76fde4184b1ff376b347e09fc936dc9bf59 (patch) | |
| tree | 14b3aa2f2bef455765024495096e9eaddb4d9c1e | |
| parent | 6a8bbee35c7bd6ce9775184433ce3c0ef151578b (diff) | |
Add a sail mode for emacs, based heavily on tuareg
It doesn't get everything right, but it's better than the ocaml mode or c mode colorings
| -rw-r--r-- | editors/sail.el | 2077 |
1 files changed, 2077 insertions, 0 deletions
diff --git a/editors/sail.el b/editors/sail.el new file mode 100644 index 00000000..a9963558 --- /dev/null +++ b/editors/sail.el @@ -0,0 +1,2077 @@ +;;; sail.el --- Sail mode for Emacs. +;;; based on tuareg.el which was + ;; 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 + ;; URL: http://forge.ocamlcore.org/projects/tuareg/ + ;; EmacsWiki: TuaregMode + +(eval-when-compile (require 'cl)) +(require 'easymenu) + +(defalias 'sail-match-string + (if (fboundp 'match-string-no-properties) + 'match-string-no-properties + 'match-string)) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; User customizable variables + +;; Use the standard `customize' interface or `sail-mode-hook' to +;; Configure these variables + +(require 'custom) + +(defgroup sail nil + "Support for the Sail language." + :group 'languages) + +;; Comments + +(defcustom sail-indent-leading-comments t + "*If true, indent leading comment lines (starting with `(*') like others." + :group 'sail :type 'boolean) + +(defcustom sail-indent-comments t + "*If true, automatically align multi-line comments." + :group 'sail :type 'boolean) + +(defcustom sail-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 `sail-comment-end-extra-indent' = 1." + :group 'sail + :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 sail-support-leading-star-comments t + "*Enable automatic intentation of comments of the form + (* + * ... + *) +Documentation comments (** *) are not concerned by this variable +unless `sail-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 `sail-comment-end-extra-indent' to 1." + :group 'sail :type 'boolean) + +(defcustom sail-leading-star-in-doc nil + "*Enable automatic intentation of documentation comments of the form + (** + * ... + *)" + :group 'sail :type 'boolean) + +;; Indentation defaults + +(defcustom sail-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 'sail :type 'integer) + +(defcustom sail-let-always-indent t + "*If true, enforce indentation is at least `sail-let-indent' after a `let'. + +As an example, set it to false when you have `sail-with-indent' set to 0, +and you want `let x = match ... with' and `match ... with' indent the +same way." + :group 'sail :type 'boolean) + +(defcustom sail-case-extra-unindent sail-default-indent + "*Extra backward indent for Sail lines starting with the `case' operator. + +It is NOT the variable controlling the indentation of the `case' itself: +this value is automatically added to `switch' to leave enough space for `case' backward +indentation." + + :group 'sail :type 'integer) + +(defcustom sail-enum-indent sail-default-indent + "*How many spaces to indent from an `enumerate' keyword." + :group 'sail :type 'integer) + +(defcustom sail-struct-struct-indent sail-default-indent + "*How many spaces to indent from a `struct' keyword." + :group 'sail :type 'integer) + +(defcustom sail-foreach-indent sail-default-indent + "*How many spaces to indent from a `foreach' keyword." + :group 'sail :type 'integer) + +(defcustom sail-function-indent sail-default-indent + "*How many spaces to indent from a `function' keyword." + :group 'sail :type 'integer) + +(defcustom sail-if-then-else-indent sail-default-indent + "*How many spaces to indent from an `if', `then' or `else' keyword." + :group 'sail :type 'integer) + +(defcustom sail-let-indent sail-default-indent + "*How many spaces to indent from a `let' keyword." + :group 'sail :type 'integer) + +(defcustom sail-in-indent sail-default-indent + "*How many spaces to indent from a `in' keyword." + :group 'sail :type 'integer) + +(defcustom sail-switch-indent sail-default-indent + "*How many spaces to indent from a `switch' keyword." + :group 'sail :type 'integer) + +(defcustom sail-with-indent sail-default-indent + "*How many spaces to indent from a `with' keyword." + :group 'sail :type 'integer) + +(defcustom sail-typedef-indent sail-default-indent + "*How many spaces to indent from a `typedef' keyword." + :group 'sail :type 'integer) + +(defcustom sail-val-indent sail-default-indent + "*How many spaces to indent from a `val' keyword." + :group 'sail :type 'integer) + +;; Automatic indentation +;; Using abbrev-mode and electric keys + +(defcustom sail-use-abbrev-mode t + "*Non-nil means electrically indent lines starting with leading keywords. +It makes use of `abbrev-mode'. + +Many people find eletric keywords irritating, so you can disable them by +setting this variable to nil." + :group 'sail :type 'boolean + :set '(lambda (var val) + (setq sail-use-abbrev-mode val) + (abbrev-mode val))) + +(defcustom sail-electric-indent t + "*Non-nil means electrically indent lines starting with `|]', '||]', '>', `)', `]' or `}'. + +Many people find eletric keys irritating, so you can disable them in +setting this variable to nil." + :group 'sail :type 'boolean) + +(defcustom sail-electric-close-list t + "*Non-nil means electrically insert `||' before a list-closing `]'. + +Many people find eletric keys irritating, so you can disable them in +setting this variable to nil. You should probably have this on, +though, if you also have `sail-electric-indent' on." + :group 'sail :type 'boolean) + + +(defvar sail-options-list + '(("Automatic indentation of leading keywords" . 'sail-use-abbrev-mode) + ("Automatic indentation of ), ], |], ||], >, and }" . 'sail-electric-indent) + ("Automatic matching of [||" . 'sail-electric-close-list) + "---" + ("Indent body of comments" . 'sail-indent-comments) + ("Indent first line of comments" . 'sail-indent-leading-comments) + ("Leading-`*' comment style" . 'sail-support-leading-star-comments)) + "*List of menu-configurable Sail options.") + +(eval-and-compile + (defconst sail-use-syntax-ppss (fboundp 'syntax-ppss) + "*If nil, use our own parsing and caching.")) + +(defgroup sail-faces nil + "Special faces for the Sail mode." + :group 'sail) + +(defconst sail-faces-inherit-p + (and (boundp 'face-attribute-name-alist) + (assq :inherit face-attribute-name-alist))) + +(defface sail-font-lock-governing-face + '((((background light)) (:foreground "blue" :bold t)) + (t (:foreground "orange" :bold t))) + "Face description for governing/leading keywords." + :group 'sail-faces) +(defvar sail-font-lock-governing-face + 'sail-font-lock-governing-face) + +(defface sail-font-lock-operator-face + '((((background light)) (:foreground "brown")) + (t (:foreground "khaki"))) + "Face description for all operators." + :group 'sail-faces) +(defvar sail-font-lock-operator-face + 'sail-font-lock-operator-face) + +(defface sail-font-lock-error-face + '((t (:foreground "yellow" :background "red" :bold t))) + "Face description for all errors reported to the source." + :group 'sail-faces) +(defvar sail-font-lock-error-face + 'sail-font-lock-error-face) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Support definitions + +(defun sail-leading-star-p () + (and sail-support-leading-star-comments + (save-excursion ; this function does not make sense outside of a comment + (sail-beginning-of-literal-or-comment) + (and (or sail-leading-star-in-doc + (not (looking-at "(\\*[Tt][Ee][Xx]\\|(\\*\\*"))) + (progn + (forward-line 1) + (back-to-indentation) + (looking-at "\\*[^)]")))))) + +(defun sail-auto-fill-insert-leading-star (&optional leading-star) + (let ((point-leading-comment (looking-at "(\\*")) (return-leading nil)) + (save-excursion + (back-to-indentation) + (when sail-electric-indent + (when (and (sail-in-comment-p) + (or leading-star + (sail-leading-star-p))) + (unless (looking-at "(?\\*") + (insert-before-markers "* ")) + (setq return-leading t)) + (unless point-leading-comment + ;; Use optional argument to break recursion + (sail-indent-command t)))) + return-leading)) + +(defun sail-auto-fill-function () + (unless (sail-in-literal-p) + (let ((leading-star + (and (not (char-equal ?\n last-command-event)) + (sail-auto-fill-insert-leading-star)))) + (do-auto-fill) + (unless (char-equal ?\n last-command-event) + (sail-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 sail-forward-char (&optional step) + (if step (goto-char (+ (point) step)) + (goto-char (1+ (point))))) + +(defun sail-backward-char (&optional step) + (if step (goto-char (- (point) step)) + (goto-char (1- (point))))) + +(defun sail-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 sail-cache-stop (point-min)) +(make-variable-buffer-local 'sail-cache-stop) +(defvar sail-cache nil) +(make-variable-buffer-local 'sail-cache) +(defvar sail-cache-local nil) +(make-variable-buffer-local 'sail-cache-local) +(defvar sail-cache-last-local nil) +(make-variable-buffer-local 'sail-cache-last-local) +(defvar sail-last-loc (cons nil nil)) + +;; PPSS definitions +(defun sail-ppss-in-literal-or-comment () (error "sail uses PPSS")) +(defun sail-ppss-fontify (beg end) (error "sail uses PPSS")) +(defun sail-ppss-in-literal-p () + "Returns non-nil if point is inside a Sail literal." + (nth 3 (syntax-ppss))) +(defun sail-ppss-in-comment-p () + "Returns non-nil if point is inside or right before a Sail comment." + (or (nth 4 (syntax-ppss)) + (looking-at "[ \t]*(\\*"))) +(defun sail-ppss-in-literal-or-comment-p () + "Returns non-nil if point is inside a Sail literal or comment." + (nth 8 (syntax-ppss))) +(defun sail-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 sail-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 'sail-backward-up-list 'backward-up-list) + +;; non-PPSS definitions +(defun sail-!ppss-in-literal-p () + "Return non-nil if point is inside a Sail literal." + (car (sail-in-literal-or-comment))) +(defun sail-!ppss-in-comment-p () + "Return non-nil if point is inside a Sail comment." + (cdr (sail-in-literal-or-comment))) +(defun sail-!ppss-in-literal-or-comment-p () + "Return non-nil if point is inside a Sail literal or comment." + (sail-in-literal-or-comment) + (or (car sail-last-loc) (cdr sail-last-loc))) +(defun sail-!ppss-in-literal-or-comment () + "Return the pair `((sail-in-literal-p) . (sail-in-comment-p))'." + (if (and (<= (point) sail-cache-stop) sail-cache) + (progn + (if (or (not sail-cache-local) (not sail-cache-last-local) + (and (>= (point) (caar sail-cache-last-local)))) + (setq sail-cache-local sail-cache)) + (while (and sail-cache-local (< (point) (caar sail-cache-local))) + (setq sail-cache-last-local sail-cache-local + sail-cache-local (cdr sail-cache-local))) + (setq sail-last-loc + (if sail-cache-local + (cons (eq (cadar sail-cache-local) 'b) + (> (cddar sail-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 sail-cache (<= sail-cache-stop (caar sail-cache))) + (setq sail-cache (cdr sail-cache))) + (if sail-cache + (if (eq (cadar sail-cache) 'b) + (progn + (setq sail-cache-stop (1- (caar sail-cache))) + (goto-char sail-cache-stop) + (setq balance (cddar sail-cache)) + (setq sail-cache (cdr sail-cache))) + (setq balance (cddar sail-cache)) + (setq sail-cache-stop (caar sail-cache)) + (goto-char sail-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 "\\\\") + (sail-forward-char 2)) + ((looking-at "'\\([^\n\\']\\|\\\\[^ \t\n][^ \t\n]?[^ \t\n]?\\)'") + (setq sail-cache (cons (cons (1+ (point)) (cons 'b balance)) + sail-cache)) + (goto-char (match-end 0)) + (setq sail-cache (cons (cons (point) (cons 'e balance)) + sail-cache))) + ((looking-at "\"") + (sail-forward-char) + (setq sail-cache (cons (cons (point) (cons 'b balance)) + sail-cache)) + (skip-chars-forward "^\\\\\"") + (while (looking-at "\\\\") + (sail-forward-char 2) (skip-chars-forward "^\\\\\"")) + (sail-forward-char) + (setq sail-cache (cons (cons (point) (cons 'e balance)) + sail-cache))) + ((looking-at "(\\*") + (setq balance (1+ balance)) + (setq sail-cache (cons (cons (point) (cons nil balance)) + sail-cache)) + (sail-forward-char 2)) + ((looking-at "\\*)") + (sail-forward-char 2) + (if (> balance 1) + (progn + (setq balance (1- balance)) + (setq sail-cache (cons (cons (point) (cons nil balance)) + sail-cache))) + (setq end-of-comment t) + (setq sail-cache (cons (cons (point) (cons nil 0)) + sail-cache)))) + (t (sail-forward-char))) + (setq flag (<= (point) mp))) + (setq sail-cache-local sail-cache + sail-cache-stop (point)) + (goto-char op) + (if sail-cache (sail-in-literal-or-comment) + (setq sail-last-loc (cons nil nil)) + sail-last-loc)))) +(defun sail-!ppss-beginning-of-literal-or-comment () + "Skips to the beginning of the current literal or comment (or buffer)." + (interactive) + (when (sail-in-literal-or-comment-p) + (sail-beginning-of-literal-or-comment-fast))) + +(defun sail-!ppss-beginning-of-literal-or-comment-fast () + (while (and sail-cache-local + (or (eq 'b (cadar sail-cache-local)) + (> (cddar sail-cache-local) 0))) + (setq sail-cache-last-local sail-cache-local + sail-cache-local (cdr sail-cache-local))) + (if sail-cache-last-local + (goto-char (caar sail-cache-last-local)) + (goto-char (point-min))) + (when (eq 'b (cadar sail-cache-last-local)) (sail-backward-char))) + +(defun sail-!ppss-backward-up-list () + "Safe up-list regarding comments, literals and errors." + (let ((balance 1) (op (point)) (oc nil)) + (sail-in-literal-or-comment) + (while (and (> (point) (point-min)) (> balance 0)) + (setq oc (if sail-cache-local (caar sail-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 "^[]{}()<>") (sail-backward-char) + (cond ((sail-in-literal-or-comment-p) + (sail-beginning-of-literal-or-comment-fast)) + ((looking-at "[[{(<]") + (setq balance (1- balance))) + ((looking-at "[]})>]") + (setq balance (1+ balance))))) + (setq op (point))))) + +(defalias 'sail-in-literal-or-comment + (eval-and-compile (if sail-use-syntax-ppss + 'sail-ppss-in-literal-or-comment + 'sail-!ppss-in-literal-or-comment))) +(defalias 'sail-fontify + (eval-and-compile (if sail-use-syntax-ppss + 'sail-ppss-fontify + 'sail-!ppss-fontify))) +(defalias 'sail-in-literal-p + (eval-and-compile (if sail-use-syntax-ppss + 'sail-ppss-in-literal-p + 'sail-!ppss-in-literal-p))) +(defalias 'sail-in-comment-p + (eval-and-compile (if sail-use-syntax-ppss + 'sail-ppss-in-comment-p + 'sail-!ppss-in-comment-p))) +(defalias 'sail-in-literal-or-comment-p + (eval-and-compile (if sail-use-syntax-ppss + 'sail-ppss-in-literal-or-comment-p + 'sail-!ppss-in-literal-or-comment-p))) +(defalias 'sail-beginning-of-literal-or-comment + (eval-and-compile (if sail-use-syntax-ppss + 'sail-ppss-beginning-of-literal-or-comment + 'sail-!ppss-beginning-of-literal-or-comment))) +(defalias 'sail-beginning-of-literal-or-comment-fast + (eval-and-compile (if sail-use-syntax-ppss + 'sail-ppss-beginning-of-literal-or-comment-fast + 'sail-!ppss-beginning-of-literal-or-comment-fast))) +(defalias 'sail-backward-up-list + ;; FIXME: not clear if moving out of a string/comment counts as 1 or no. + (eval-and-compile (if sail-use-syntax-ppss + 'backward-up-list + 'sail-!ppss-backward-up-list))) + +(defun sail-false-=-p () + "Is the underlying `=' the first/second letter of an operator?" + (or (memq (preceding-char) '(?: ?> ?< ?=)) + (char-equal ?= (char-after (1+ (point)))))) + +(defun sail-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))))) + +(defvar sail-mode-syntax-table + (let ((st (make-syntax-table))) + (modify-syntax-entry ?_ "_" st) + (modify-syntax-entry ?? ". p" st) + (modify-syntax-entry ?~ ". p" st) + (modify-syntax-entry ?: "." st) + (modify-syntax-entry ?' "w" st) ; ' is part of words (for primes). + (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 Sail mode buffers.") + +(defmacro sail-with-internal-syntax (&rest body) + `(progn + ;; Switch to a modified internal syntax. + (modify-syntax-entry ?. "w" sail-mode-syntax-table) + (modify-syntax-entry ?_ "w" sail-mode-syntax-table) + (unwind-protect (progn ,@body) + ;; Switch back to the interactive syntax. + (modify-syntax-entry ?. "." sail-mode-syntax-table) + (modify-syntax-entry ?_ "_" sail-mode-syntax-table)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Font-Lock + +(defvar sail-doc-face 'font-lock-doc-face) + +(unless sail-use-syntax-ppss + + (defun sail-fontify-buffer () + (font-lock-default-fontify-buffer) + (sail-fontify (point-min) (point-max))) + + (defun sail-fontify-region (begin end &optional verbose) + (font-lock-default-fontify-region begin end verbose) + (sail-fontify begin end)) + + (defun sail-fontify (begin end) + (when (eq major-mode 'sail-mode) + (save-excursion + (sail-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)) + (sail-in-literal-or-comment) + (cond + ((cdr sail-last-loc) + (sail-beginning-of-literal-or-comment) + (put-text-property (max begin (point)) end 'face + (if (looking-at + "(\\*[Tt][Ee][Xx]\\|(\\*\\*[^*]") + sail-doc-face + 'font-lock-comment-face)) + (setq end (1- (point)))) + ((car sail-last-loc) + (sail-beginning-of-literal-or-comment) + (put-text-property (max begin (point)) end 'face + 'font-lock-string-face) + (setq end (point))) + (t (while (and sail-cache-local + (or (> (caar sail-cache-local) end) + (eq 'b (cadar sail-cache-local)))) + (setq sail-cache-local (cdr sail-cache-local))) + (setq end (if sail-cache-local + (caar sail-cache-local) begin))))) + (unless modified (set-buffer-modified-p nil))) + )))) + ) ;; end sail-use-syntax-ppss + +(defconst sail-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. + '(("\\<\\('\\)\\([^'\\\n]\\|\\\\.[^\\'\n \")]*\\)\\('\\)" + (1 '(7)) (3 '(7))))) + +(defun sail-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 + sail-doc-face + font-lock-comment-face)))) + +;; Initially empty, set in `sail-install-font-lock' +(defvar sail-font-lock-keywords () + "Font-Lock patterns for Sail mode.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Keymap + +(defvar sail-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "|]" 'sail-electric-piperb) + (define-key map ")" 'sail-electric-rp) + (define-key map "}" 'sail-electric-rc) + (define-key map "]" 'sail-electric-rb) + (define-key map ">" 'sail-electric-lt) + (define-key map "\M-q" 'sail-indent-phrase) + (define-key map "\C-c\C-q" 'sail-indent-phrase) + (define-key map "\M-\C-\\" 'indent-region) + (define-key map "\C-c\C-a" 'sail-find-alternate-file) + (define-key map "\C-c\C-c" 'compile) + (define-key map "\C-xnd" 'sail-narrow-to-phrase) + (define-key map "\C-c\C-n" 'sail-next-phrase) + (define-key map "\C-c\C-p" 'sail-previous-phrase) + (define-key map [(backspace)] 'backward-delete-char-untabify) + (define-key map [(control c) (control down)] 'sail-next-phrase) + (define-key map [(control c) (control up)] 'sail-previous-phrase) + (define-key map [(meta control down)] 'sail-next-phrase) + (define-key map [(meta control up)] 'sail-previous-phrase) + (define-key map [(meta control n)] 'sail-next-phrase) + (define-key map [(meta control p)] 'sail-previous-phrase) + (define-key map [(meta control h)] 'sail-mark-phrase) + map) + "Keymap used in Sail mode.") + +(defconst sail-font-lock-syntax + `((?_ . "w") (?` . ".") + ,@(unless sail-use-syntax-ppss + '((?\" . ".") (?\( . ".") (?\) . ".") (?* . ".")))) + "Syntax changes for Font-Lock.") + +(defvar sail-mode-abbrev-table () + "Abbrev table used for Sail mode buffers.") +(defun sail-define-abbrev (keyword) + (define-abbrev sail-mode-abbrev-table keyword keyword 'sail-abbrev-hook)) +(if sail-mode-abbrev-table () + (setq sail-mode-abbrev-table (make-abbrev-table)) + (mapc 'sail-define-abbrev + '("scattered" "function" "typedef" "let" "default" "val" "register" + "alias" "union" "member" "clause" "extern" "effect" + "rec" "and" "switch" "case" "exit" "foreach" "from" "else" + "to" "end" "downto" "in" "then" "with" "if" "nondet" "as" + "undefined" "const" "struct" "IN" "deinfix")) + (setq abbrevs-changed nil)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The major mode + +;;;###autoload (add-to-list 'auto-mode-alist '("\\.sail?" . sail-mode)) + +;;;###autoload +(defun sail-mode () + "Major mode for editing Sail code. + +Based on Tuareg mode. See Tuareg mode for usage" + (interactive) + (kill-all-local-variables) + (setq major-mode 'sail-mode) + (setq mode-name "Sail") + (use-local-map sail-mode-map) + (set-syntax-table sail-mode-syntax-table) + (setq local-abbrev-sail sail-mode-abbrev-table) + + ;; Initialize the Sail menu + (sail-build-menu) + + ;; Initialize indentation regexps + (sail-make-indentation-regexps) + + (make-local-variable 'paragraph-start) + (setq paragraph-start (concat "^[ \t]*$\\|\\*)$\\|" page-delimiter)) + (make-local-variable 'paragraph-separate) + (setq paragraph-separate paragraph-start) + (make-local-variable 'require-final-newline) + (setq require-final-newline t) + (make-local-variable 'comment-start) + (setq comment-start "(* ") + (make-local-variable 'comment-end) + (setq comment-end " *)") + (make-local-variable 'comment-column) + (setq comment-column 40) + (make-local-variable 'comment-start-skip) + (setq comment-start-skip "(\\*+[ \t]*") + (make-local-variable 'comment-multi-line) + (setq comment-multi-line t) + (make-local-variable 'parse-sexp-ignore-comments) + (setq parse-sexp-ignore-comments nil) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'sail-indent-command) + (unless sail-use-syntax-ppss + (add-hook 'before-change-functions 'sail-before-change-function nil t)) + (make-local-variable 'normal-auto-fill-function) + (setq normal-auto-fill-function 'sail-auto-fill-function) + + (when (featurep 'imenu) + (setq imenu-prev-index-position-function 'sail-imenu-prev-index-position + imenu-extract-index-name-function 'sail-imenu-extract-index-name)) + + ;; Hooks for sail-mode, use them for sail-mode configuration + (sail-install-font-lock) + (run-hooks 'sail-mode-hook) + (when sail-use-abbrev-mode (abbrev-mode 1)) + (message nil)) + +(defun sail-install-font-lock () + (setq + sail-font-lock-keywords + `(("\\<\\(extern\\|function\\|scattered\\|clause\\|effect\\|default\\|struct\\|const\\|union\\|val\\|typedef\\|in\\|let\\|rec\\|and\\|end\\|register\\|alias\\|member\\|enumerate\\)\\>" + 0 sail-font-lock-governing-face nil nil) + ("\\<\\(false\\|true\\)\\>" 0 font-lock-constant-face nil nil) + ("\\<\\(as\\|downto\\|else\\|foreach\\|if\\|t\\(hen\\|o\\)\\|when\\|switch\\|with\\|case\\|exit\\|nondet\\|from\\|by\\)\\>" + 0 font-lock-keyword-face nil nil) + ("\\<\\(clause\\)\\>[ \t\n]*\\(\\(\\w\\|[_ \t()*,]\\)+\\)" + 2 font-lock-variable-name-face keep nil) + ("\\<\\(typedef\\|union\\)\\>[ \t\n]*\\(\\(\\w\\|[_ \t()*,]\\)+\\)" + 2 font-lock-type-face keep nil) + ("\\<\\(Type\\|Nat\\|Order\\|Effect\\|inc\\|dec\\|rreg\\|wreg\\|rmem\\|wmem\\|barr\\|undef\\|unspec\\|nondet\\|pure\\|effect\\|IN\\|forall\\)\\>" + 0 font-lock-type-face keep nil) + ("\\<\\(val\\|extern\\|clause\\|and\\||let\\|rec\\>[ \t\n]*\\(\\(\\w\\|[_,?~.]\\)*\\)" + 2 font-lock-variable-name-face keep nil) + ("\\<\\(val\\|and\\|let\\>[ \t\n]*\\(\\(\\w\\|[_,?~.]\\)*\\)\\>\\(\\(\\w\\|[->_ \t,?~.]\\|(\\(\\w\\|[--->_ \t,?~.=]\\)*)\\)*\\)" + 6 font-lock-variable-name-face keep nil) + ("\\<\\([?~]?[_[:alpha:]]\\w*\\)[ \t\n]*:[^:>=]" + 1 font-lock-variable-name-face keep nil) + ("^#\\w+\\>" 0 font-lock-preprocessor-face t nil) + )) + (setq font-lock-defaults + (list* + 'sail-font-lock-keywords (not sail-use-syntax-ppss) nil + sail-font-lock-syntax nil + '(font-lock-syntactic-keywords + . sail-font-lock-syntactic-keywords) + '(parse-sexp-lookup-properties + . t) + '(font-lock-syntactic-face-function + . sail-font-lock-syntactic-face-function) + (unless sail-use-syntax-ppss + '((font-lock-fontify-region-function + . sail-fontify-region))))) + (when (and (boundp 'font-lock-fontify-region-function) + (not sail-use-syntax-ppss)) + (make-local-variable 'font-lock-fontify-region-function) + (setq font-lock-fontify-region-function 'sail-fontify-region))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Indentation stuff + +(eval-and-compile + (defconst sail-no-more-code-this-line-regexp "[ \t]*\\((\\*\\|$\\)" + "Regexp matching lines which have no more code: + blanks + (maybe) comment start.")) + +(defmacro sail-no-code-after (rex) + `(eval-when-compile (concat ,rex sail-no-more-code-this-line-regexp))) + +(defconst sail-no-code-this-line-regexp + (concat "^" sail-no-more-code-this-line-regexp)) + +(defun sail-ro (&rest words) (concat "\\<" (regexp-opt words t) "\\>")) + +(defconst sail-extra-unindent-regexp + (concat "\\(" (sail-ro "function") + "\\|\\[" sail-no-more-code-this-line-regexp "\\)") + "Regexp for keywords needing extra indentation to compensate for case matches.") + +(defun sail-give-extra-unindent-regexp () + sail-extra-unindent-regexp) + +(defconst sail-keyword-regexp + (concat (sail-ro "scattered" "function" "typedef" "let" "default" "val" "register" + "alias" "union" "member" "clause" "extern" "effect" + "rec" "and" "switch" "case" "exit" "foreach" "from" "else" + "to" "end" "downto" "in" "then" "with" "if" "nondet" "as" + "undefined" "const" "struct" "IN" "deinfix") + "\\|->\\|[;,|]") + "Regexp for all recognized keywords.") + +(defun sail-give-keyword-regexp () sail-keyword-regexp) + +(defconst sail-operator-regexp "[---+*/=<>@^&|]\\|:>\\|::\\|\\<\\(or\\|l\\(and\\|x?or\\|s[lr]\\)\\|as[lr]\\|mod\\)\\>" + "Regexp for all operators.") + +(defconst sail-matching-keyword-regexp + (sail-ro "and" "then" "else" "in") + "Regexp matching Sail keywords which act as end block delimiters.") + +(defun sail-give-matching-keyword-regexp () sail-matching-keyword-regexp) + +(defconst sail-matching-kwop-regexp + (concat sail-matching-keyword-regexp + "\\|\\<with\\>\\|[|>]?\\]\\|>?}\\|[|)]\\|;;") + "Regexp matching Sail keywords or operators which act as end block +delimiters.") + +(defun sail-give-matching-kwop-regexp () sail-matching-kwop-regexp) + +(defconst sail-block-regexp + (concat (sail-ro "foreach" "nondet" "if" "clause" "switch" "case") + "\\|[][(){}]\\|\\*)")) + +(defconst sail-find-kwop-regexp + (concat sail-matching-keyword-regexp "\\|" sail-block-regexp)) + +(defun sail-give-find-kwop-regexp () sail-find-kwop-regexp) + +(defconst sail-governing-phrase-regexp + (sail-ro "val" "typedef" "function" "scattered" "default" "union" "member" + "end" "extern" "let") + "Regexp matching Sail phrase delimitors.") + +(defconst sail-keyword-alist + '(("end" . sail-default-indent) + ("foreach" . sail-foreach-indent) + ("val" . sail-val-indent) + ("function" . sail-fun-indent) + ("if" . sail-if-then-else-indent) + ("then" . sail-if-then-else-indent) + ("else" . sail-if-then-else-indent) + ("let" . sail-let-indent) + ("switch" . sail-match-indent) + + ;; Case match keywords + ("case" . sail-case-indent) + + ;; Assume default indentation for other keywords and operators + ) + "Association list of indentation values based on governing keywords.") + +(defconst sail-leading-kwop-alist + '(("}" . sail-find-match) + (">" . sail-find-match) + (")" . sail-find-match) + ("]" . sail-find-match) + ("|]" . sail-find-match) + ("||]" . sail-find-match) + ("in" . sail-find-in-match) + ("else" . sail-find-else-match) + ("then" . sail-find-then-match) + ("to" . sail-find-match) + ("downto" . sail-find-match) + ("by" . sail-find-match) + ("and" . sail-find-and-match)) + "Association list used in Sail mode for skipping back over nested blocks.") + +(defun sail-find-leading-kwop-match (kwop) + (funcall (cdr (assoc kwop sail-leading-kwop-alist)))) + +(defconst sail-binding-regexp "\\(\\<and\\>\\|(*\\<let\\>\\)") + +(defun sail-assoc-indent (kwop &optional look-for-let-or-and) + "Return relative indentation of the keyword given in argument." + (let ((ind (or (symbol-value (cdr (assoc kwop sail-keyword-alist))) + sail-default-indent)) + (looking-let-or-and (and look-for-let-or-and + (looking-at sail-binding-regexp)))) + (if (string-match (sail-give-extra-unindent-regexp) kwop) + (if (and sail-let-always-indent + looking-let-or-and (< ind sail-let-indent)) + sail-let-indent ind) + ind))) + +(defconst sail-meaningful-word-regexp + "[^ \t\n_[:alnum:]]\\|\\<\\(\\w\\|_\\)+\\>\\|\\*)") +(defun sail-find-meaningful-word () + "Look back for a word, skipping comments and blanks. +Returns the actual text of the word, if found." + (let ((found nil) (kwop nil) (pt (point))) + (while (and (not found) + (re-search-backward sail-meaningful-word-regexp + (point-min) t)) + (setq kwop (sail-match-string 0)) + (cond ((or (string= kwop "=") (string= kwop ">")) + (backward-char 2) + (setq kwop (concat ">>" kwop))) + ((and (string= kwop ">") (char-equal ?- (char-before))) + (backward-char) + (setq kwop "->"))) + (when (= pt (point)) + (error "sail-find-meaningful-word: inf loop at %d, kwop=%s" pt kwop)) + (setq pt (point)) + (if kwop + (if (sail-in-comment-p) + (sail-beginning-of-literal-or-comment-fast) + (setq found t)) + (setq found t))) + (if found kwop (goto-char (point-min)) nil))) + +(defun sail-make-find-kwop-regexp (kwop-regexp) + "Make a custom indentation regexp." + (concat (sail-give-find-kwop-regexp) "\\|" kwop-regexp)) + +;; Static regexps +(defconst sail-find-and-match-regexp + (concat (sail-ro "else" "in" "then" "downto" "by" "from" + "foreach" "if" "function" + "let" "in" "typedef" "val" "end") + "\\|[][(){}]\\|\\*)")) +(defconst sail-find-phrase-beginning-regexp + (concat (sail-ro "end" "typedef" "let") + "\\|^#[ \t]*[a-z][_a-z]*\\>\\|;;")) +(defconst sail-find-phrase-beginning-and-regexp + (concat "\\<\\(and\\)\\>\\|" sail-find-phrase-beginning-regexp)) +(defconst sail-back-to-paren-or-indentation-regexp + "[][(){}]\\|\\.<\\|>\\.\\|\\*)\\|^[ \t]*\\(.\\|\n\\)") + +(defun sail-make-indentation-regexps () + "Initialisation of specific indentation regexp. +Gathered here for memoization and dynamic reconfiguration purposes." + (setq + sail-find-comma-match-regexp + (sail-make-find-kwop-regexp + (concat (sail-ro "and" "switch" "else" "then" "function" "let" "foreach") + "\\|->\\|[[{(]")) + sail-find-with-match-regexp + (sail-make-find-kwop-regexp + (concat (sail-ro "switch" "case" "typedef") + "\\|[[{(]")) + sail-find-in-match-regexp + (sail-make-find-kwop-regexp (sail-ro "let" "function")) + sail-find-else-match-regexp + (sail-make-find-kwop-regexp ";") + sail-find-do-match-regexp + (sail-make-find-kwop-regexp "->") + sail-find-=-match-regexp + (sail-make-find-kwop-regexp + (concat (sail-ro "val" "let" "function" "typedef" "if" "in") + "\\|=")) + sail-find-arrow-match-regexp + (sail-make-find-kwop-regexp + (concat (sail-ro "extern" "typedef" "val" "function" "let") + "\\|[|;]")) + sail-find-semicolon-match-regexp + (sail-make-find-kwop-regexp + (concat ";" sail-no-more-code-this-line-regexp "\\|->\\|" + (sail-ro "let"))) + sail-find-phrase-indentation-regexp + (sail-make-find-kwop-regexp + (concat sail-governing-phrase-regexp "\\|" (sail-ro "and"))) + sail-find-phrase-indentation-break-regexp + (concat sail-find-phrase-indentation-regexp "\\|;") + sail-compute-argument-indent-regexp + (sail-make-find-kwop-regexp + (concat (sail-give-keyword-regexp) "\\|=")) + sail-compute-normal-indent-regexp + (concat sail-compute-argument-indent-regexp "\\|^.[ \t]*") + sail-find-monadic-match-regexp + (concat sail-block-regexp "\\|\\([;=]\\)\\|\\(->\\)\\|" + (sail-ro "val" "let" "function" "typedef" "if" "in" "end")))) + +(defun sail-strip-trailing-whitespace (string) + (if (string-match "[ \t]*\\'" string) + (substring string 0 (match-beginning 0)) + string)) + +(defun sail-find-kwop-pos (kr do-not-skip-regexp may-terminate-early) + "Look back for a keyword or operator matching KR (short for kwop regexp). +Skips blocks etc... + +Ignore occurences inside literals and comments. +If found, return the actual text of the keyword or operator." + (let ((found nil) + (kwop nil) pos + (kwop-regexp kr)) + (while (and (not found) + (setq pos (re-search-backward kwop-regexp (point-min) t)) + (setq kwop (sail-strip-trailing-whitespace + ;; for trailing blanks after a semicolon + (sail-match-string 0)))) + (cond + ((sail-in-literal-or-comment-p) + (sail-beginning-of-literal-or-comment-fast)) + ((looking-at "[>]})]") + (sail-backward-up-list)) + ((sail-at-phrase-break-p) + (setq found t)) + ((and do-not-skip-regexp (looking-at do-not-skip-regexp)) + (if (and (string= kwop "|") (char-equal ?| (preceding-char))) + (backward-char) + (setq found t))) + ((looking-at (sail-give-matching-keyword-regexp)) + (let ((mkwop (sail-find-leading-kwop-match (sail-match-string 0)))) + (when (and may-terminate-early (string-match kwop-regexp mkwop)) + (setq found t)))) + (t + (setq found t)))) + (if found (list kwop pos) (goto-char (point-min)) nil))) + +(defun sail-find-kwop (kr &optional do-not-skip-regexp) + (car (sail-find-kwop-pos kr do-not-skip-regexp nil))) + +(defun sail-find-match () + (let ((kwop (sail-find-kwop (sail-give-find-kwop-regexp)))) + (when (string= kwop "then") + (sail-find-then-match) + (sail-find-match)) + kwop)) + +(defun sail-find-comma-match () + (car (sail-find-kwop-pos sail-find-comma-match-regexp nil t))) + +(defun sail-find-in-match () + (let ((kwop (sail-find-kwop sail-find-in-match-regexp "\\<and\\>"))) + (cond + ((string= kwop "and") + (sail-find-in-match)) + (t + kwop)))) + +(defconst sail-find-then-match-regexp + (sail-make-find-kwop-regexp "\\(->\\)")) +(defun sail-find-then-kwop () + (sail-find-kwop sail-find-then-match-regexp "\\(->\\)")) +(defun sail-find-then-match () + (let ((kwop (sail-find-then-kwop))) + (cond ((string= kwop "if") + (let ((back (point))) + (sail-back-to-paren-or-indentation) + (if (looking-at "else[ \t]*\\((\\*.*\\*)\\)*[ \t]*if") + "else if" + (goto-char back) + kwop))) + (t kwop)))) + +(defun sail-find-then-else-match () + (let ((kwop (sail-find-then-kwop))) + (cond + ((string= kwop "if") + (let ((pos (point))) + (if (and (not (sail-in-indentation-p)) + (string= "else" (sail-find-meaningful-word))) + "else" + (goto-char pos) + kwop))) + (t + kwop)))) + +(defun sail-find-else-match () + (let ((kwop (sail-find-kwop sail-find-else-match-regexp + "\\<then\\>"))) + (cond + ((string= kwop "then") + (sail-find-then-else-match)) + ((string= kwop ";") + (sail-find-semicolon-match) + (sail-find-else-match))))) + + +(defconst sail-done-match-stop-regexp (sail-ro "and")) +(defun sail-find-done-match () + (let ((kwop (sail-find-kwop (sail-give-find-kwop-regexp) + sail-done-match-stop-regexp))) + (cond + ((string= kwop "and") + (sail-find-and-match)) + (t + kwop)))) + +(defun sail-find-and-match () + (let* ((kwop (sail-find-kwop + sail-find-and-match-regexp + (sail-give-and-stop-regexp))) + (old-point (point))) + (cond + ((string= kwop "typedef") + (let ((kwop2 (sail-find-meaningful-word))) + (cond ((string= kwop2 "and") + (sail-find-and-match)) + ((and (string= kwop "function") + (string= kwop2 "let")) + kwop2) + (t (goto-char old-point) kwop)))) + (t kwop)))) + +(defconst sail-=-stop-regexp (concat (sail-ro "and" "in") "\\|=")) +(defun sail-give-=-stop-regexp () sail-=-stop-regexp) + +(defun sail-find-=-match () + (let ((kwop (sail-find-kwop + sail-find-=-match-regexp + (sail-give-=-stop-regexp)))) + (cond + ((string= kwop "and") + (sail-find-and-match)) + ((and (string= kwop "=") + (not (sail-false-=-p))) + (while (and (string= kwop "=") + (not (sail-false-=-p))) + (setq kwop (sail-find-=-match))) + kwop) + (t kwop)))) + +(defconst sail-captive-regexp + (sail-ro "let" "if" "typedef")) +(defun sail-captive-= () + (save-excursion + (sail-find-=-match) + (looking-at sail-captive-regexp))) + + +(defun sail-find-arrow-match () + (let ((kwop (sail-find-kwop (sail-give-find-arrow-match-regexp) + "\\<with\\>"))) + (cond + ((string= kwop "function") + (let ((pos (point))) + (progn (goto-char pos) kwop))) + ((not (string= kwop ":")) + kwop) + ;; If we get this far, we know we're looking at a colon. + ((or (char-equal (char-before) ?:) + (char-equal (char-after (1+ (point))) ?:) + (char-equal (char-after (1+ (point))) ?>)) + (sail-find-arrow-match)) + ;; Patch by T. Freeman + (t + (let ((oldpoint (point)) + (match (sail-find-arrow-match))) + (if (looking-at ":") + match + (progn + ;; Go back to where we were before the recursive call. + (goto-char oldpoint) + kwop))))))) + +(defconst sail-semicolon-match-stop-regexp + (sail-ro "and" "end" "in")) +(defconst sail-no-code-after-paren-regexp + (sail-no-code-after "[[{(][|<]?")) +(defun sail-semicolon-indent-kwop-point (&optional leading-semi-colon) + ;; return (kwop kwop-point indentation) + (let ((kwop (sail-find-kwop sail-find-semicolon-match-regexp + sail-semicolon-match-stop-regexp)) + (point (point))) + ;; We don't need to find the keyword matching `and' since we know it's `let'! + (list + (cond + ((string= kwop ";") + (forward-line 1) + (while (or (sail-in-comment-p) + (looking-at sail-no-code-this-line-regexp)) + (forward-line 1)) + (back-to-indentation) + (current-column)) + ((and leading-semi-colon + (looking-at "\\((\\|\\[[<|]?\\|{<?\\)[ \t]*[^ \t\n]") + (not (looking-at sail-no-code-after-paren-regexp))) + (current-column)) + ;; ((looking-at (tuareg-no-code-after "\\((\\|\\[[<|]?\\|{<?\\)")) + ;; (+ (current-column) tuareg-default-indent)) + ((looking-at "\\(\\.<\\|(\\|\\[[<|]?\\|{<?\\)") ; paren with subsequent text + (sail-search-forward-paren) + (current-column)) + ((string= kwop "->") + (if (save-excursion + (sail-find-arrow-match) + (or (looking-at "\\<function\\>\\||") + (looking-at (sail-give-extra-unindent-regexp)))) + (sail-paren-or-indentation-indent) + (sail-find-semicolon-match))) + ((string= kwop "in") + (sail-find-in-match) + (+ (current-column) sail-in-indent)) + ((string= kwop "let") + (+ (current-column) sail-let-indent)) + (t (sail-paren-or-indentation-indent))) + kwop point))) + +(defun sail-find-semicolon-match (&optional leading-semi-colon) + (car (sail-semicolon-indent-kwop-point leading-semi-colon))) + +(defmacro sail-reset-and-kwop (kwop) + `(when (and ,kwop (string= ,kwop "and")) + (setq ,kwop (sail-find-and-match)))) + +(defconst sail-phrase-regexp-1 (sail-ro "typedef" "end")) +(defconst sail-phrase-regexp-2 (sail-ro "and" "let" "function" "case")) +(defconst sail-phrase-regexp-3 + (sail-ro "and" "in")) +(defun sail-find-phrase-indentation (&optional phrase-break) + (if (and (looking-at sail-phrase-regexp-1) (> (point) (point-min)) + (save-excursion + (sail-find-meaningful-word) + (looking-at sail-phrase-regexp-2))) + (progn + (sail-find-meaningful-word) + (+ (current-column) sail-default-indent)) + (let ((looking-at-and (looking-at "\\<and\\>")) + (kwop (sail-find-kwop + (if phrase-break + sail-find-phrase-indentation-break-regexp + sail-find-phrase-indentation-regexp) + sail-phrase-regexp-3)) + (tmpkwop nil) (curr nil)) + (sail-reset-and-kwop kwop) + (cond ((not kwop) (current-column)) + ((and (string= kwop "in") + (not (save-excursion + (setq tmpkwop (sail-find-in-match)) + (sail-reset-and-kwop tmpkwop) + (setq curr (point)) + (and (string= tmpkwop "let") + (not (sail-looking-at-internal-let)))))) + (goto-char curr) + (sail-find-phrase-indentation phrase-break)) + ((sail-at-phrase-break-p) + (end-of-line) + (sail-skip-blank-and-comments) + (current-column)) + ((string= kwop "let") + (if (sail-looking-at-internal-let) + (sail-find-phrase-indentation phrase-break) + (current-column))) + ((string= kwop "in") + (sail-find-in-match) + (current-column)) + ((looking-at "\\(\\.<\\|(\\|\\[[<|]?\\|{<?\\)[ \t]*[^ \t\n]") + (sail-search-forward-paren) + (current-column)) + (t (current-column)))))) + +(defconst sail-paren-or-indentation-stop-regexp + (sail-ro "and" "in")) +(defun sail-back-to-paren-or-indentation () + "Search backwards for the first open paren in line, or skip to indentation. +Returns t iff skipped to indentation." + (if (or (bolp) (sail-in-indentation-p)) + (progn (back-to-indentation) t) + (let ((kwop (sail-find-kwop + sail-back-to-paren-or-indentation-regexp + sail-paren-or-indentation-stop-regexp)) + (retval)) + (setq retval + (cond + ((string= kwop "in") + (sail-in-indentation-p)) +; ((looking-at "[[{(]") (tuareg-search-forward-paren) nil) +; ((looking-at "\\.<") +; (if tuareg-support-metaocaml +; (progn +; (tuareg-search-forward-paren) nil) +; (tuareg-back-to-paren-or-indentation))) + (t (back-to-indentation) t))) + (cond + ; ((looking-at "|[^|]") + ; (re-search-forward "|[^|][ \t]*") nil) + ((string= kwop "in") + (sail-find-in-match) + (sail-back-to-paren-or-indentation) + (if (looking-at "\\<\\(let\\|and\\)\\>") + (forward-char sail-in-indent)) nil) + (t retval))))) + +(defun sail-paren-or-indentation-column () + (sail-back-to-paren-or-indentation) + (current-column)) + +(defun sail-paren-or-indentation-indent () + (+ (sail-paren-or-indentation-column) sail-default-indent)) + +(defun sail-search-forward-paren () + (re-search-forward "\\(\\.<\\|(\\|\\[[<|]?\\|{|<\\)[ \t]*")) + +(defun sail-add-default-indent (leading-operator) + (if leading-operator 0 sail-default-indent)) + +(defconst sail-internal-let-regexp + (concat "[[({;=]\\|" + (sail-ro "if" "in" "then" "else" "switch"))) +(defun sail-looking-at-internal-let () + (save-excursion + (sail-find-meaningful-word) + (and (not (sail-at-phrase-break-p)) + (or (looking-at sail-internal-let-regexp) + (looking-at sail-operator-regexp))))) + +(defun sail-looking-at-in-let () + (save-excursion + (string= (sail-find-meaningful-word) "in"))) + +(defun sail-indent-from-previous-kwop () + (let* ((start-pos (point)) + (kwop (sail-find-argument-kwop-non-blank t)) + (captive= (and (string= kwop "=") (sail-captive-=))) + (kwop-pos (point))) + (forward-char (length kwop)) + (sail-skip-blank-and-comments) + (cond ((or (not captive=) + (/= (point) start-pos)) ; code between paren and kwop + (goto-char start-pos) + (sail-paren-or-indentation-indent)) + (t + (goto-char kwop-pos) + (when (string= kwop "=") + (setq kwop (sail-find-=-match))) + (+ sail-default-indent + (if (assoc kwop sail-leading-kwop-alist) + (sail-compute-kwop-indent kwop) + (current-column))))))) + +(defun sail-indent-from-paren (leading-operator start-pos) + (cond + ((looking-at (sail-no-code-after "\\(\\(\\.<\\|(\\|\\[[<|]?\\|{\\|<\\)\\)")) + (cond ((sail-in-indentation-p) + (+ sail-default-indent + (current-column))) + (t (sail-indent-from-previous-kwop)))) + ((looking-at "([ \t]*\\(\\w\\)") + (goto-char (match-beginning 1)) + (current-column)) + (t + (+ (sail-add-default-indent leading-operator) + (current-column))))) + +(defun sail-skip-to-next-form (old-point) + (while (and (not (looking-at sail-no-more-code-this-line-regexp)) + (< (point) old-point)) ; do not go beyond old-point + (forward-sexp 1)) + (sail-skip-blank-and-comments) + (sail-back-to-paren-or-indentation)) + +(defun sail-find-argument-kwop (leading-operator) + (sail-find-kwop (if leading-operator + sail-compute-argument-indent-regexp + sail-compute-normal-indent-regexp) + (sail-give-keyword-regexp))) + +(defun sail-find-argument-kwop-clean (leading-operator) + (let (kwop) + (while (or (progn (setq kwop (sail-find-argument-kwop leading-operator)) + (sail-reset-and-kwop kwop) + nil) + (and (string= kwop "=") (sail-false-=-p)) + (and (looking-at sail-no-code-this-line-regexp) + (not (= (point) (point-min)))))) + kwop)) + +(defun sail-find-argument-kwop-non-blank (leading-operator) + (let ((kwop "") (point (1+ (point)))) + (while (and (> point (point)) (string= "" kwop)) + (setq point (point) + kwop (sail-find-argument-kwop-clean leading-operator))) + kwop)) + +(defun sail-compute-argument-indent (leading-operator) + (let* ((old-point (line-beginning-position)) + (kwop (sail-find-argument-kwop-non-blank leading-operator)) + (match-end-point (+ (point) (length kwop)))) ; match-end is invalid! + (cond + ((let (matching-kwop matching-pos) + (save-excursion + (setq matching-kwop (sail-find-arrow-match)) + (setq matching-pos (point))) + (cond + ((or (string= matching-kwop "val") (string= matching-kwop "let") (string= matching-kwop "function")) + (+ (current-column) sail-val-indent)) + (t + (+ (sail-paren-or-indentation-column) + (sail-add-default-indent leading-operator)))))) + ((string= kwop "function") + (+ (sail-paren-or-indentation-column) + (sail-add-default-indent leading-operator) + (sail-assoc-indent kwop))) + ((<= old-point (point)) + (+ (sail-add-default-indent leading-operator) + (current-column))) + (t + (goto-char match-end-point) ; skip kwop == (forward-char (length kwop)) + (sail-skip-to-next-form old-point) + (+ (sail-add-default-indent + (if (save-excursion (goto-char match-end-point) + (looking-at sail-no-more-code-this-line-regexp)) + (or leading-operator (string= kwop "{") + (looking-at (sail-no-code-after "[[:upper:]].*\\."))) + (not (looking-at sail-operator-regexp)))) + (current-column)))))) + +(defun sail-compute-arrow-indent (start-pos) + (let (kwop pos) + (save-excursion (setq kwop (sail-find-arrow-match) pos (point))) + (cond ((or (string= kwop "val") + (string= kwop "let")) + (goto-char pos) + (+ (current-column) sail-val-indent)) + ((string= kwop "typedef") + (goto-char pos) + (+ (current-column) sail-type-indent + sail-default-indent)) + ((string= kwop "(") + (goto-char pos) + (sail-indent-after-next-char)) + ((or (string= kwop "{") + (string= kwop ";")) + (if (and (looking-at "->") + (search-backward ":" pos t)) + (sail-indent-after-next-char) + (sail-back-to-paren-or-indentation) + (current-column))) + (t (sail-paren-or-indentation-indent))))) + +(defun sail-compute-keyword-indent (kwop leading-operator start-pos) + (cond ((string= kwop ";") + (if (looking-at (sail-no-code-after ";")) + (let* ((pos (point)) (indent (sail-find-semicolon-match))) + (if (looking-at sail-phrase-regexp-1) + (progn + (goto-char start-pos) + (if (search-backward ":" pos t) + (sail-indent-after-next-char) + indent)) + indent)) + (sail-paren-or-indentation-indent))) + ((string= kwop ",") + (if (looking-at (sail-no-code-after ",")) + (let ((mkwop (sail-find-comma-match))) + (cond ((or (string= mkwop "[") + (string= mkwop "{") + (string= mkwop "(") + (string= mkwop "<") + (string- mkwop "[|")) + (forward-char 1) (skip-syntax-forward " ") + (current-column)) + ((looking-at "[[{(]\\|\\<") + (sail-indent-from-paren t start-pos)) + ((or (and (looking-at "[<|]") + (char-equal ?\[ (preceding-char))) + (and (looking-at "<") + (char-equal ?\{ (preceding-char)))) + (sail-backward-char) + (sail-indent-from-paren t start-pos)) + ((and (looking-at "\\<let\\>") (string= mkwop "in")) + (+ (current-column) sail-in-indent)) + (t (+ (sail-paren-or-indentation-column) + (sail-assoc-indent mkwop))))) + (sail-paren-or-indentation-indent))) + ((or (string= kwop "function") (string= kwop "and")) + (sail-back-to-paren-or-indentation) + (+ (sail-paren-or-indentation-indent) + (sail-assoc-indent kwop t))) + ((string-match "\\<\\(function\\)\\>" kwop) + (+ (sail-paren-or-indentation-column) + (sail-add-default-indent leading-operator) + (sail-assoc-indent kwop t))) + ((string-match (sail-give-extra-unindent-regexp) kwop) + (+ (sail-paren-or-indentation-column) + (sail-assoc-indent kwop t))) + ((string= kwop "in") + (when (looking-at (sail-no-code-after "\\<in\\>")) + (sail-find-in-match)) + (+ (current-column) + sail-in-indent)) + ((string-match (sail-give-matching-kwop-regexp) kwop) + (sail-find-leading-kwop-match kwop) + (if (sail-in-indentation-p) + (+ (current-column) + (sail-assoc-indent kwop t)) + (sail-back-to-paren-or-indentation) + (+ (sail-paren-or-indentation-indent) + (sail-assoc-indent kwop t)))) + (t (+ (if (sail-in-indentation-p) + (current-column) + (sail-paren-or-indentation-indent)) + (sail-assoc-indent kwop t))))) + +(defconst sail-=-indent-regexp-1 + (sail-ro "val" "let" "function" "scattered" "foreach" "if")) + +(defun sail-compute-=-indent (start-pos) + (let ((current-column-module-type nil) (kwop1 (sail-find-=-match)) + (next-pos (point))) + (+ (save-excursion + (sail-reset-and-kwop kwop1) + (cond ((string= kwop1 "typedef") + (sail-find-meaningful-word) + (cond (t (goto-char start-pos) + (beginning-of-line) + (+ (sail-add-default-indent + (looking-at "[ \t]*[\[|]")) + sail-type-indent)))) + ((looking-at sail-=-indent-regexp-1) + (let ((matched-string (sail-match-string 0))) + (setq current-column-module-type (current-column)) + (sail-assoc-indent matched-string))) + ((looking-at sail-no-code-after-paren-regexp) + (setq current-column-module-type + (sail-indent-from-paren nil next-pos)) + sail-default-indent) + (t (setq current-column-module-type + (sail-paren-or-indentation-indent)) + sail-default-indent))) + (or current-column-module-type + (current-column))))) + +(defun sail-indent-after-next-char () + (forward-char 1) + (sail-skip-blank-and-comments) + (current-column)) + +(defconst sail-definitions-regexp + (sail-ro "and" "val" "typedef" "scattered" "function" "default" "register" "let") + "Regexp matching definition phrases.") + +(defun sail-compute-normal-indent () + (let ((leading-operator (looking-at sail-operator-regexp))) + (beginning-of-line) + (save-excursion + (let ((start-pos (point)) + (kwop (sail-find-argument-kwop-clean leading-operator))) + (cond + ((not kwop) (current-column)) + ((sail-at-phrase-break-p) + (sail-find-phrase-indentation t)) + ((or (looking-at "[[{(<]") + (and (looking-at "[<|]") + (char-equal ?\[ (preceding-char)) + (progn (sail-backward-char) t)) + (and (looking-at "<") + (char-equal ?\{ (preceding-char)) + (progn (sail-backward-char) t))) + (cond ((looking-at "{ *[A-Z]") + (forward-char 1) (skip-syntax-forward " ") + (current-column)) + ((looking-at (sail-no-code-after "[[{(][<|]?")) + (sail-indent-from-paren leading-operator start-pos)) + ((and leading-operator (string= kwop "(")) + (sail-indent-after-next-char)) + (t (+ sail-default-indent + (sail-indent-from-paren leading-operator start-pos))))) + ((looking-at (sail-give-keyword-regexp)) + (sail-compute-keyword-indent kwop leading-operator start-pos)) + ((and (string= kwop "=") (not (sail-false-=-p)) + (or (null leading-operator) + ;; defining "=", not testing for equality + (string-match sail-definitions-regexp + (save-excursion + (sail-find-argument-kwop-clean t))))) + (sail-compute-=-indent start-pos)) + (nil 0) + (t (sail-compute-argument-indent leading-operator))))))) + +(defun sail-compute-paren-indent (paren-match-p old-point) + (unless paren-match-p + (sail-search-forward-paren)) + (let ((looking-at-paren (char-equal ?\( (char-after))) (start-pos (point))) + (when (or looking-at-paren + (looking-at (sail-no-code-after "\\(\{\\(.*with[ \t]*\\([[:upper:]].*\\.\\)?\\)?\\|\\[\\)"))) + (if (sail-in-indentation-p) + (sail-back-to-paren-or-indentation) + (sail-indent-from-previous-kwop)) + (when looking-at-paren + (skip-chars-forward "( \t" start-pos)) + (while (and (looking-at "[([{]") + (> (scan-sexps (point) 1) + (save-excursion (goto-char old-point) + (line-end-position)))) + (forward-char 1) + (skip-syntax-forward " ")))) + (current-column)) + +(defun sail-compute-kwop-indent-general (kwop matching-kwop) + (let* ((looking-at-matching (looking-at matching-kwop)) + (extra-unindent ; non-paren code before matching-kwop + (unless (save-excursion + (skip-chars-backward "( \t" (line-beginning-position)) + (bolp)) + (sail-back-to-paren-or-indentation) + t))) + (+ (current-column) + (sail-add-default-indent + (or (not (string= kwop "then")) + looking-at-matching))))) + +(defun ail-compute-kwop-indent (kwop) + (when (string= kwop "rec") + (setq kwop "and")) + (let* ((old-point (point)) + (paren-match-p (looking-at "[|>]?[]})]\\|>\\.")) + (real-pipe (looking-at "|\\([^|]\\|$\\)")) + (matching-kwop (sail-find-leading-kwop-match kwop))) + (cond ((looking-at "[[{(][<|]?\\|\\.<") + (sail-compute-paren-indent paren-match-p old-point)) + ((string= kwop "and") + (if (sail-in-indentation-p) + (current-column) + (sail-paren-or-indentation-column))) + ((string= kwop "in") + (+ (current-column) + (sail-add-default-indent (string= matching-kwop "let")))) + ((not (string= kwop "and")) ; pretty general case + (sail-compute-kwop-indent-general kwop matching-kwop)) + (t (sail-paren-or-indentation-column))))) + +(defun sail-indent-to-code (beg-pos match) + (unless (and (string= match "(") + (search-forward "->" beg-pos t)) + (forward-char (length match))) + (sail-skip-blank-and-comments) + (current-column)) + +(defun sail-indent-command (&optional from-leading-star) + "Indent the current line in Sail mode. + +Compute new indentation based on Sail syntax." + (interactive "*") + (unless from-leading-star + (sail-auto-fill-insert-leading-star)) + (let ((case-fold-search nil)) + (sail-with-internal-syntax + (save-excursion + (back-to-indentation) + (indent-line-to (max 0 (sail-compute-indent)))) + (when (sail-in-indentation-p) (back-to-indentation))))) + +(defun sail-compute-indent () + (save-excursion + (cond + ((sail-in-comment-p) + (cond + ((looking-at "(\\*") + (if sail-indent-leading-comments + (save-excursion + (sail-skip-blank-and-comments) + (back-to-indentation) + (current-column)) + (current-column))) + ((looking-at "\\*\\**)") + (sail-beginning-of-literal-or-comment-fast) + (if (sail-leading-star-p) + (+ (current-column) + (if (save-excursion + (forward-line 1) + (back-to-indentation) + (looking-at "*")) 1 + sail-comment-end-extra-indent)) + (+ (current-column) sail-comment-end-extra-indent))) + (sail-indent-comments + (let ((star (and (sail-leading-star-p) + (looking-at "\\*")))) + (sail-beginning-of-literal-or-comment-fast) + (if star (re-search-forward "(") (re-search-forward "(\\*+[ \t]*")) + (current-column))) + (t (current-column)))) + ((sail-in-literal-p) + (current-column)) + ((looking-at "\\<let\\>") + (if (sail-looking-at-internal-let) + (if (sail-looking-at-in-let) + (progn + (sail-find-meaningful-word) + (sail-find-in-match) + (current-column)) + (sail-compute-normal-indent)) + (sail-find-phrase-indentation))) + ((or (looking-at sail-governing-phrase-regexp) + (looking-at ";")) + (sail-find-phrase-indentation)) + ((looking-at ";") + (sail-find-semicolon-match t)) + ((or (looking-at (sail-give-matching-kwop-regexp)) + (looking-at "\\<rec\\>")) + (sail-compute-kwop-indent (sail-match-string 0))) + (t (sail-compute-normal-indent))))) + +(defun sail-split-string () + "Called whenever a line is broken inside an Sail string literal." + (insert-before-markers "\\ ") + (sail-backward-char)) + +(defadvice newline-and-indent (around + sail-newline-and-indent + activate) + "Handle multi-line strings in Sail mode." + (let ((hooked (and (eq major-mode 'sail-mode) (sail-in-literal-p))) + (split-mark)) + (when hooked + (setq split-mark (set-marker (make-marker) (point))) + (sail-split-string)) + ad-do-it + (when hooked + (goto-char split-mark) + (set-marker split-mark nil)))) + +(defun sail-electric-rp () + "If inserting a ) operator or a comment-end at beginning of line, +reindent the line." + (interactive "*") + (let ((electric (and sail-electric-indent + (or (sail-in-indentation-p) + (char-equal ?* (preceding-char))) + (not (sail-in-literal-p)) + (or (not (sail-in-comment-p)) + (save-excursion + (back-to-indentation) + (looking-at "\\*")))))) + (self-insert-command 1) + (and electric + (indent-according-to-mode)))) + +(defun sail-electric-rc () + "If inserting a } operator at beginning of line, reindent the line." + (interactive "*") + (let* ((prec (preceding-char)) + (look-bra (and sail-electric-close-vector + (not (sail-in-literal-or-comment-p)) + (not (char-equal ?> prec)))) + (electric (and sail-electric-indent + (or (sail-in-indentation-p) + (and (char-equal ?> prec) + (save-excursion (sail-backward-char) + (sail-in-indentation-p)))) + (not (sail-in-literal-or-comment-p))))) + (self-insert-command 1) + (when look-bra + (save-excursion + (let ((inserted-char + (save-excursion + (sail-backward-char) + (sail-backward-up-list) + (cond ((looking-at "{<") ">") + (t ""))))) + (sail-backward-char) + (insert inserted-char)))) + (when electric (indent-according-to-mode)))) + +(defun sail-electric-rb () + "If inserting a ] operator at beginning of line, reindent the line. + +Reindent also if ] is inserted after a | operator at beginning of line. +Also, if the matching [ is followed by a | and this ] is not preceded +by |, insert one |." + (interactive "*") + (let* ((prec (preceding-char)) + (look-pipe-or-bra (and sail-electric-close-list + (not (sail-in-literal-or-comment-p)) + (not (and (char-equal ?| prec) + (not (char-equal + (save-excursion + (sail-backward-char) + (preceding-char)) ?\[)))))) + (electric (and sail-electric-indent + (or (sail-in-indentation-p) + (and (char-equal ?| prec) + (save-excursion (sail-backward-char) + (sail-in-indentation-p)))) + (not (sail-in-literal-or-comment-p))))) + (self-insert-command 1) + (when look-pipe-or-bra + (save-excursion + (let ((inserted-char + (save-excursion + (sail-backward-char) + (sail-backward-up-list) + (cond ((looking-at "\\[|") "|") + (t ""))))) + (sail-backward-char) + (insert inserted-char)))) + (when electric (indent-according-to-mode)))) + +(defun sail-abbrev-hook () + "If inserting a leading keyword at beginning of line, reindent the line." + (unless (sail-in-literal-or-comment-p) + (let* ((bol (line-beginning-position)) + (kw (save-excursion + (and (re-search-backward "^[ \t]*\\(\\w\\|_\\)+\\=" bol t) + (sail-match-string 1))))) + (when kw + (insert " ") + (indent-according-to-mode) + (backward-delete-char-untabify 1))))) + +(defun sail-skip-to-end-of-phrase () + (let ((old-point (point))) + (when (and (string= (sail-find-meaningful-word) ";") + (char-equal (preceding-char) ?\;)) + (setq old-point (1- (point)))) + (goto-char old-point) + (let ((kwop (sail-find-meaningful-word))) + (goto-char (+ (point) (length kwop)))))) + +(defun sail-skip-blank-and-comments () + (skip-syntax-forward " ") + (while (and (not (eobp)) (sail-in-comment-p) + (search-forward "*)" nil t)) + (skip-syntax-forward " "))) + +(defun sail-skip-back-blank-and-comments () + (skip-syntax-backward " ") + (while (save-excursion (sail-backward-char) + (and (> (point) (point-min)) (sail-in-comment-p))) + (sail-backward-char) + (sail-beginning-of-literal-or-comment) (skip-syntax-backward " "))) + +(defun sail-find-phrase-beginning (&optional stop-at-and) + "Find `real' phrase beginning and return point." + (beginning-of-line) + (sail-skip-blank-and-comments) + (end-of-line) + (sail-skip-to-end-of-phrase) + (let ((old-point (point)) (pt (point))) + (if stop-at-and + (sail-find-kwop sail-find-phrase-beginning-and-regexp "and") + (sail-find-kwop sail-find-phrase-beginning-regexp)) + (while (and (> (point) (point-min)) (< (point) old-point) + (or (not (looking-at sail-find-phrase-beginning-and-regexp)) + (and (looking-at "\\<let\\>") + (sail-looking-at-internal-let)) + (and (looking-at "\\<and\\>") + (save-excursion + (sail-find-and-match) + (sail-looking-at-internal-let))) + ) + (when (= pt (point)) + (error "sail-find-phrase-beginning: inf loop at %d" pt)) + (setq pt (point)) + (if (looking-at "\\<end\\>") + (sail-find-match) + (unless (bolp) (sail-backward-char)) + (setq old-point (point)) + (if stop-at-and + (sail-find-kwop sail-find-phrase-beginning-and-regexp "and") + (sail-find-kwop sail-find-phrase-beginning-regexp)))) + (when (sail-at-phrase-break-p) + (end-of-line) (sail-skip-blank-and-comments)) + (back-to-indentation) + (point)))) + +(defun sail-imenu-prev-index-position () + "The default value for `imenu-prev-index-position-function'." + (let ((pos (point)) ret) + (while (and (<= 0 pos) + (<= pos (setq ret (sail-find-phrase-beginning t)))) + (setq pos (goto-char (1- pos)))) + (and (<= 0 pos) ret))) + +(defun sail-imenu-extract-index-name () + "The default value for `imenu-extract-index-name-function'." + (forward-sexp 1) + (skip-syntax-forward " ") + (buffer-substring-no-properties (point) (scan-sexps (point) 1))) + +(defun sail-mark-phrase () + "Put mark at end of this Sail phrase, point at beginning. +The Sail phrase is the phrase just before the point." + (interactive) + (let ((pair (sail-discover-phrase))) + (goto-char (nth 1 pair)) (push-mark (nth 0 pair) t t))) + +(defun sail-next-phrase (&optional quiet stop-at-and) + "Skip to the beginning of the next phrase." + (interactive "i") + (goto-char (save-excursion + (nth 2 (sail-discover-phrase quiet stop-at-and)))) + (cond + ((looking-at "}") + (forward-char 1) + (sail-skip-blank-and-comments)) + ((looking-at ")") + (forward-char 1) + (sail-skip-blank-and-comments)) + ((looking-at ";") + (forward-char 1) + (sail-skip-blank-and-comments)))) + +(defun sail-previous-phrase () + "Skip to the beginning of the previous phrase." + (interactive) + (beginning-of-line) + (sail-skip-to-end-of-phrase) + (sail-discover-phrase)) + +(defun sail-indent-phrase () + "Depending of the context: justify and indent a comment, +or indent all lines in the current phrase." + (interactive) + (save-excursion + (back-to-indentation) + (if (sail-in-comment-p) + (let* ((cobpoint (save-excursion + (sail-beginning-of-literal-or-comment) + (point))) + (begpoint (save-excursion + (while (and (> (point) cobpoint) + (sail-in-comment-p) + (not (looking-at "^[ \t]*$"))) + (forward-line -1)) + (max cobpoint (point)))) + (coepoint (save-excursion + (while (sail-in-comment-p) + (re-search-forward "\\*)" nil 'end)) + (point))) + (endpoint (save-excursion + (re-search-forward "^[ \t]*$" coepoint 'end) + (line-beginning-position 2))) + (leading-star (sail-leading-star-p))) + (goto-char begpoint) + (while (and leading-star + (< (point) endpoint) + (not (looking-at "^[ \t]*$"))) + (forward-line 1) + (back-to-indentation) + (when (looking-at "\\*\\**\\([^)]\\|$\\)") + (delete-char 1) + (setq endpoint (1- endpoint)))) + (goto-char (min (point) endpoint)) + (fill-region begpoint endpoint) + (re-search-forward "\\*)" nil 'end) + (setq endpoint (point)) + (when leading-star + (goto-char begpoint) + (forward-line 1) + (if (< (point) endpoint) + (sail-auto-fill-insert-leading-star t))) + (indent-region begpoint endpoint nil)) + (let ((pair (sail-discover-phrase))) + (indent-region (nth 0 pair) (nth 1 pair) nil))))) + +(defun sail-complete (arg) + "Completes qualified Sail identifiers." + (interactive "p") + (modify-syntax-entry ?_ "w" sail-mode-syntax-table) + (caml-complete arg) + (modify-syntax-entry ?_ "_" sail-mode-syntax-table)) + +(defun sail-ensure-space () + (let ((prec (preceding-char))) + (when (and prec (not (char-equal ?\ (char-syntax prec)))) + (insert " ")))) + +(defun sail-insert-if-form () + "Insert a nicely formatted if-then-else form, leaving a mark after else." + (interactive "*") + (sail-ensure-space) + (let ((old (point))) + (insert "if\n\nthen\n\nelse\n") + (end-of-line) + (indent-region old (point) nil) + (indent-according-to-mode) + (push-mark) + (forward-line -2) + (indent-according-to-mode) + (forward-line -2) + (indent-according-to-mode))) + +(defun sail-insert-let-form () + "Insert a nicely formatted let-in form, leaving a mark after in." + (interactive "*") + (sail-ensure-space) + (let ((old (point))) + (insert "let in\n") + (end-of-line) + (indent-region old (point) nil) + (indent-according-to-mode) + (push-mark) + (beginning-of-line) + (backward-char 4) + (indent-according-to-mode))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Menu support + +(defun sail-about () + (interactive) + (describe-variable 'sail-mode-version)) + +(defun sail-short-cuts () + "Short cuts for the sail mode: +\\{sail-mode-map} + +" + (interactive) + (describe-function 'sail-short-cuts)) + +(defun sail-help () + (interactive) + (describe-function 'sail-mode)) + +(defvar sail-definitions-menu (list ["Scan..." sail-list-definitions t]) + "Initial content of the definitions menu.") +(make-variable-buffer-local 'sail-definitions-menu) + +(defvar sail-definitions-menu-last-buffer nil) +(defvar sail-definitions-keymaps nil) + +(defun sail-build-menu () + (easy-menu-define + sail-mode-menu (list sail-mode-map) + "Sail Mode Menu." + '("Sail" + ("Sail Forms" + ["let .. in .." sail-insert-let-form t] + ["if .. then .. else .." sail-insert-if-form t]) + "---" + ["Customize Sail Mode..." (customize-group 'sail) t] + ("Sail Options" ["Dummy" nil t]) + "---" + ["About" sail-about t] + ["Short Cuts" sail-short-cuts] + ["Help" sail-help t])) + (easy-menu-add sail-mode-menu) + (sail-update-options-menu) + ;; Save and update definitions menu + (when (functionp 'easy-menu-create-menu) + ;; Patch for Emacs + (add-hook 'menu-bar-update-hook + 'sail-with-emacs-update-definitions-menu) + (make-local-variable 'sail-definitions-keymaps) + (setq sail-definitions-keymaps + (cdr (easy-menu-create-menu + "Definitions" sail-definitions-menu))) + (setq sail-definitions-menu-last-buffer nil))) + +(defun sail-update-definitions-menu () + (when (eq major-mode 'sail-mode) + (easy-menu-change + '("Sail") "Definitions" + sail-definitions-menu))) + +(defun sail-with-emacs-update-definitions-menu () + (when (current-local-map) + (let ((keymap + (lookup-key (current-local-map) [menu-bar Sail Definitions]))) + (if (and + (keymapp keymap) + (not (eq sail-definitions-menu-last-buffer (current-buffer)))) + (setcdr keymap sail-definitions-keymaps) + (setq sail-definitions-menu-last-buffer (current-buffer)))))) + +(defun sail-toggle-option (symbol) + (interactive) + (set symbol (not (symbol-value symbol))) + (when (eq 'sail-use-abbrev-mode symbol) + (abbrev-mode sail-use-abbrev-mode)) ; toggle abbrev minor mode + (sail-update-options-menu)) + +(defun sail-update-options-menu () + (easy-menu-change + '("Sail") "Sail Options" + (mapcar (lambda (pair) + (if (consp pair) + (vector (car pair) + (list 'sail-toggle-option (cdr pair)) + ':style 'toggle + ':selected (nth 1 (cdr pair)) + ':active t) + pair)) sail-options-list)) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Definitions List + +;; Designed from original code by M. Quercia + +(defconst sail--id-regexp "[[:alpha:]][_'[:alnum:]]*") + +(defconst sail-definitions-bind-skip-regexp + (concat (sail-ro "rec" "typedef" "function") "\\|'" + sail--id-regexp "\\|('.*)") + "Regexp matching stuff to ignore after a binding keyword.") + +(defconst sail-identifier-regexp (concat "\\<" sail--id-regexp "\\>")) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Hooks and Exit + +(eval-when-compile + (autoload 'speedbar-add-supported-extension "speedbar")) +(when (require 'speedbar nil t) + (speedbar-add-supported-extension + '(".sail"))) + +(defvar sail-load-hook nil + "This hook is run when Sail is loaded in. It is a good place to put +key-bindings or hack Font-Lock keywords...") + +(run-hooks 'sail-load-hook) + +(provide 'sail_mode) + +;;; sail.el ends here |
