diff options
author | Stefan Monnier <monnier@iro.umontreal.ca> | 2012-10-03 23:38:45 -0400 |
---|---|---|
committer | Stefan Monnier <monnier@iro.umontreal.ca> | 2012-10-03 23:38:45 -0400 |
commit | 3b36fd37f2e5bcba5973af60599d3bcee40a4dc2 (patch) | |
tree | d9d30432f1517f45120fc5cac9319ddb7b046e2a | |
parent | 16268c2858dde29363eebfef613ad46ccc96e135 (diff) | |
download | emacs-3b36fd37f2e5bcba5973af60599d3bcee40a4dc2.tar.gz |
Start preparing for the move to ELPA.
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | sml-mode.el | 639 | ||||
-rw-r--r-- | sml-proc.el | 338 |
3 files changed, 387 insertions, 599 deletions
@@ -1,6 +1,6 @@ # Makefile for emacs-lisp package -# Copyright (C) 1998, 1999, 2004, 2007, 2010 Stefan Monnier <monnier@gnu.org> +# Copyright (C) 1998,1999,2004,2007,2010-2012 Stefan Monnier <monnier@gnu.org> # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the @@ -159,14 +159,15 @@ $(PACKAGE)-startup.el: $(ELFILES) #TAG = $(shell echo v$(VERSION) | tr '.' '_') URL=$(shell sed -n -e '5p' .svn/entries) -TAG=$(shell dirname "$(URL)")/releases/$(PACKAGE)-$(VERSION) +#TAG=$(shell dirname "$(URL)")/releases/$(PACKAGE)-$(VERSION) +TAG="v$(VERSION)" ftpdir=/u/monnier/html/elisp/ cvsmodule=$(shell cat CVS/Repository) cvsroot=$(shell cat CVS/Root) dist: - svn cp . "$(TAG)" &&\ - svn export "$(TAG)" "$(TMP)/$(PACKAGE)-$(VERSION)" &&\ + echo bzr tag "$(TAG)" &&\ + bzr export "$(TMP)/$(PACKAGE)-$(VERSION)" &&\ cd "$(TMP)/$(PACKAGE)-$(VERSION)" &&\ $(MAKE) info $(PACKAGE)-startup.el &&\ cd .. &&\ diff --git a/sml-mode.el b/sml-mode.el index 3daeb880be8..f32478aeebb 100644 --- a/sml-mode.el +++ b/sml-mode.el @@ -1,4 +1,4 @@ -;;; sml-mode.el --- Major mode for editing (Standard) ML +;;; sml-mode.el --- Major mode for editing (Standard) ML -*- lexical-binding: t; coding: utf-8 -*- ;; Copyright (C) 1999,2000,2004,2007,2010-2012 Stefan Monnier ;; Copyright (C) 1994-1997 Matthew J. Morley @@ -76,35 +76,13 @@ "Editing SML code." :group 'languages) -;;; VARIABLES CONTROLLING INDENTATION - (defcustom sml-indent-level 4 - "Indentation of blocks in ML (see also `sml-indent-rule')." - :type '(integer)) + "Basic indentation step for SML code." + :type 'integer) (defcustom sml-indent-args sml-indent-level "Indentation of args placed on a separate line." - :type '(integer)) - -;; (defvar sml-indent-align-args t -;; "*Whether the arguments should be aligned.") - -;; (defvar sml-case-indent nil -;; "*How to indent case-of expressions. -;; If t: case expr If nil: case expr of -;; of exp1 => ... exp1 => ... -;; | exp2 => ... | exp2 => ... - -;; The first seems to be the standard in SML/NJ, but the second -;; seems nicer...") - -(defcustom sml-electric-semi-mode nil - "If non-nil, `\;' will self insert, reindent the line, and do a newline. -If nil, just insert a `\;'. (To insert while t, do: \\[quoted-insert] \;)." - :type 'boolean) -(when (fboundp 'electric-layout-mode) - (make-obsolete-variable 'sml-electric-semi-mode - 'electric-layout-mode "Emacs-24")) + :type 'integer) (defcustom sml-rightalign-and t "If non-nil, right-align `and' with its leader. @@ -113,39 +91,12 @@ If nil: If t: and b = B and b = B" :type 'boolean) -;;; OTHER GENERIC MODE VARIABLES - -(defvar sml-mode-info "sml-mode" - "*Where to find Info file for `sml-mode'. -The default assumes the info file \"sml-mode.info\" is on Emacs' info -directory path. If it is not, either put the file on the standard path -or set the variable `sml-mode-info' to the exact location of this file - - (setq sml-mode-info \"/usr/me/lib/info/sml-mode\") - -in your .emacs file. You can always set it interactively with the -set-variable command.") - (defvar sml-mode-hook nil - "*Run upon entering `sml-mode'. + "Run upon entering `sml-mode'. This is a good place to put your preferred key bindings.") -;;; CODE FOR SML-MODE - -(defun sml-mode-info () - "Command to access the TeXinfo documentation for `sml-mode'. -See doc for the variable `sml-mode-info'." - (interactive) - (require 'info) - (condition-case nil - (info sml-mode-info) - (error (progn - (describe-variable 'sml-mode-info) - (message "Can't find it... set this variable first!"))))) - - ;;; Autoload functions -- no-doc is another idea cribbed from AucTeX! - +;; FIXME-copyright: probably include sml-proc.el in sml-mode.el. (let ((sml-no-doc "This function is part of sml-proc, and has not yet been loaded. Full documentation will be available after autoloading the function.")) @@ -160,7 +111,7 @@ Full documentation will be available after autoloading the function.")) (defvar sml-outline-regexp ;; `st' and `si' are to match structure and signature. - "\\|s[ti]\\|[ \t]*\\(let[ \t]+\\)?\\(fun\\|and\\)\\>" + "\\|s[ti]\\|[ \t]*\\(let[ \t]+\\)?\\(fun\\|and\\)\\_>" "Regexp matching a major heading. This actually can't work without extending `outline-minor-mode' with the notion of \"the end of an outline\".") @@ -171,15 +122,10 @@ notion of \"the end of an outline\".") (defvar sml-mode-map (let ((map (make-sparse-keymap))) - ;; Smarter cursor movement. - ;; (define-key map [remap forward-sexp] 'sml-user-forward-sexp) - ;; (define-key map [remap backward-sexp] 'sml-user-backward-sexp) ;; Text-formatting commands: (define-key map "\C-c\C-m" 'sml-insert-form) - (define-key map "\C-c\C-i" 'sml-mode-info) (define-key map "\M-|" 'sml-electric-pipe) (define-key map "\M-\ " 'sml-electric-space) - (define-key map "\;" 'sml-electric-semi) (define-key map [backtab] 'sml-back-to-outer-indent) ;; Process commands added to sml-mode-map -- these should autoload. (define-key map "\C-c\C-l" 'sml-load-file) @@ -190,18 +136,9 @@ notion of \"the end of an outline\".") map) "The keymap used in `sml-mode'.") -(defconst sml-builtin-nested-comments-flag - (ignore-errors - (not (equal (let ((st (make-syntax-table))) - (modify-syntax-entry ?\* ". 23n" st) st) - (let ((st (make-syntax-table))) - (modify-syntax-entry ?\* ". 23" st) st)))) - "Non-nil means this Emacs understands the `n' in syntax entries.") - (defvar sml-mode-syntax-table (let ((st (make-syntax-table))) - (modify-syntax-entry ?\* (if sml-builtin-nested-comments-flag - ". 23n" ". 23") st) + (modify-syntax-entry ?\* ". 23n" st) (modify-syntax-entry ?\( "()1" st) (modify-syntax-entry ?\) ")(4" st) (mapc (lambda (c) (modify-syntax-entry c "_" st)) "._'") @@ -215,7 +152,7 @@ notion of \"the end of an outline\".") (easy-menu-define sml-mode-menu sml-mode-map "Menu used in `sml-mode'." '("SML" - ("Process" + ("Process" ;FIXME-copyright. ["Start default ML compiler" run-sml t] ["-" nil nil] ["run CM.make" sml-compile t] @@ -224,18 +161,14 @@ notion of \"the end of an outline\".") ["--" nil nil] ["send buffer contents" sml-send-buffer t] ["send region" sml-send-region t] - ["send paragraph" sml-send-function t] + ["send function" sml-send-function t] ["goto next error" next-error (featurep 'sml-proc)] ["---" nil nil] - ;; ["Standard ML of New Jersey" sml-smlnj (fboundp 'sml-smlnj)] - ;; ["Poly/ML" sml-poly-ml (fboundp 'sml-poly-ml)] - ;; ["Moscow ML" sml-mosml (fboundp 'sml-mosml)] ["Help for Inferior ML" (describe-function 'inferior-sml-mode) :active (featurep 'sml-proc)]) - ["electric pipe" sml-electric-pipe t] - ["insert SML form" sml-insert-form t] + ["insert SML form" sml-insert-form t] ;FIXME-copyright. ("Forms" :filter sml-forms-menu) - ("Format/Mode Variables" + ("Format/Mode Variables" ;FIXME-copyright. ["indent region" indent-region t] ["outdent" sml-back-to-outer-indent t] ;; ["-" nil nil] @@ -244,34 +177,16 @@ notion of \"the end of an outline\".") ;; ["--" nil nil] ;; ["toggle type-of-indent" sml-type-of-indent t] ;; ["toggle nested-if-indent" sml-nested-if-indent t] - ;; ["toggle electric-semi-mode" sml-electric-semi-mode t] ) ["-----" nil nil] - ["SML mode help (brief)" describe-mode t] - ["SML mode *info*" sml-mode-info t] - ["Remove overlay" (sml-error-overlay 'undo) - :visible (or (and (boundp 'sml-error-overlay) - sml-error-overlay) - (not (fboundp 'compilation-fake-loc))) - :active (and (boundp 'sml-error-overlay) - (overlayp sml-error-overlay) - (overlay-start sml-error-overlay)) - ])) - -;; Make's sure they appear in the menu bar when sml-mode-map is active. -;; On the hook for XEmacs only -- see easy-menu-add in auc-menu.el. -;; (defun sml-mode-menu-bar () -;; "Make sure menus appear in the menu bar as well as under mouse 3." -;; (and (eq major-mode 'sml-mode) -;; (easy-menu-add sml-mode-menu sml-mode-map))) -;; (add-hook 'sml-mode-hook 'sml-mode-menu-bar) + ["SML mode help (brief)" describe-mode t])) ;FIXME-copyright. ;; -;; regexps +;; Regexps ;; (defun sml-syms-re (syms) - (concat "\\<" (regexp-opt syms t) "\\>")) + (concat "\\_<" (regexp-opt syms t) "\\_>")) ;; @@ -307,81 +222,52 @@ notion of \"the end of an outline\".") (sml-syms-re '("abstraction" "abstype" "and" "andalso" "as" "before" "case" "datatype" "else" "end" "eqtype" "exception" "do" "fn" "fun" "functor" "handle" "if" "in" "include" "infix" - "infixr" "let" "local" "nonfix" "of" "op" "open" "orelse" + "infixr" "let" "local" "nonfix" "o" "of" "op" "open" "orelse" "overload" "raise" "rec" "sharing" "sig" "signature" "struct" "structure" "then" "type" "val" "where" "while" - "with" "withtype" "o")) + "with" "withtype")) "A regexp that matches any and all keywords of SML.") +(eval-and-compile + (defconst sml-id-re "\\sw\\(?:\\sw\\|\\s_\\)*")) + (defconst sml-tyvarseq-re - "\\(\\('+\\(\\sw\\|\\s_\\)+\\|(\\([,']\\|\\sw\\|\\s_\\|\\s-\\)+)\\)\\s-+\\)?") + (concat "\\(\\('+" sml-id-re "\\|(\\([,']\\|" sml-id-re + "\\|\\s-\\)+)\\)\\s-+\\)?")) ;;; Font-lock settings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defcustom sml-font-lock-symbols nil "Display \\ and -> and such using symbols in fonts. This may sound like a neat trick, but be extra careful: it changes the -alignment and can thus lead to nasty surprises w.r.t layout. -If t, try to use whichever font is available. Otherwise you can -set it to a particular font of your preference among `japanese-jisx0208' -and `unicode'." - :type '(choice (const nil) - (const t) - (const unicode) - (const japanese-jisx0208))) +alignment and can thus lead to nasty surprises w.r.t layout." + :type 'boolean) (defconst sml-font-lock-symbols-alist - (append - ;; The symbols can come from a JIS0208 font. - (and (fboundp 'make-char) (charsetp 'japanese-jisx0208) - (memq sml-font-lock-symbols '(t japanese-jisx0208)) - (list (cons "fn" (make-char 'japanese-jisx0208 38 75)) - (cons "andalso" (make-char 'japanese-jisx0208 34 74)) - (cons "orelse" (make-char 'japanese-jisx0208 34 75)) - ;; (cons "as" (make-char 'japanese-jisx0208 34 97)) - (cons "not" (make-char 'japanese-jisx0208 34 76)) - (cons "div" (make-char 'japanese-jisx0208 33 96)) - ;; (cons "*" (make-char 'japanese-jisx0208 33 95)) - (cons "->" (make-char 'japanese-jisx0208 34 42)) - (cons "=>" (make-char 'japanese-jisx0208 34 77)) - (cons "<-" (make-char 'japanese-jisx0208 34 43)) - (cons "<>" (make-char 'japanese-jisx0208 33 98)) - (cons ">=" (make-char 'japanese-jisx0208 33 102)) - (cons "<=" (make-char 'japanese-jisx0208 33 101)) - (cons "..." (make-char 'japanese-jisx0208 33 68)) - ;; Some greek letters for type parameters. - (cons "'a" (make-char 'japanese-jisx0208 38 65)) - (cons "'b" (make-char 'japanese-jisx0208 38 66)) - (cons "'c" (make-char 'japanese-jisx0208 38 67)) - (cons "'d" (make-char 'japanese-jisx0208 38 68)) - )) - ;; Or a unicode font. - (and (fboundp 'decode-char) - (memq sml-font-lock-symbols '(t unicode)) - (list (cons "fn" (decode-char 'ucs 955)) - (cons "andalso" (decode-char 'ucs 8896)) - (cons "orelse" (decode-char 'ucs 8897)) - ;; (cons "as" (decode-char 'ucs 8801)) - (cons "not" (decode-char 'ucs 172)) - (cons "div" (decode-char 'ucs 247)) - (cons "*" (decode-char 'ucs 215)) - (cons "o" (decode-char 'ucs 9675)) - (cons "->" (decode-char 'ucs 8594)) - (cons "=>" (decode-char 'ucs 8658)) - (cons "<-" (decode-char 'ucs 8592)) - (cons "<>" (decode-char 'ucs 8800)) - (cons ">=" (decode-char 'ucs 8805)) - (cons "<=" (decode-char 'ucs 8804)) - (cons "..." (decode-char 'ucs 8943)) - ;; (cons "::" (decode-char 'ucs 8759)) - ;; Some greek letters for type parameters. - (cons "'a" (decode-char 'ucs 945)) - (cons "'b" (decode-char 'ucs 946)) - (cons "'c" (decode-char 'ucs 947)) - (cons "'d" (decode-char 'ucs 948)) - )))) - -(defun sml-font-lock-compose-symbol (alist) + '(("fn" . ?λ) + ("andalso" . ?∧) ;; ?⋀ + ("orelse" . ?∨) ;; ?⋁ + ;; ("as" . ?≡) + ("not" . ?¬) + ("div" . ?÷) + ("*" . ?×) + ("o" . ?○) + ("->" . ?→) + ("=>" . ?⇒) + ("<-" . ?←) + ("<>" . ?≠) + (">=" . ?≥) + ("<=" . ?≤) + ("..." . ?⋯) + ;; ("::" . ?∷) + ;; Some greek letters for type parameters. + ("'a" . ?α) + ("'b" . ?β) + ("'c" . ?γ) + ("'d" . ?δ) + )))) + +(defun sml-font-lock-compose-symbol () "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. @@ -398,41 +284,38 @@ Regexp match data 0 points to the chars." ;; we may have added earlier and which is now incorrect. (remove-text-properties start end '(composition)) ;; That's a symbol alright, so add the composition. - (compose-region start end (cdr (assoc (match-string 0) alist))))) + (compose-region start end (cdr (assoc (match-string 0) + sml-font-lock-symbols-alist))))) ;; Return nil because we're not adding any face property. nil) (defun sml-font-lock-symbols-keywords () - (when (fboundp 'compose-region) - (let ((alist nil)) - (dolist (x sml-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 (sml-font-lock-compose-symbol ',alist)))))))) + (when sml-font-lock-symbols + `((,(regexp-opt (mapcar 'car sml-font-lock-symbols-alist) t) + (0 (sml-font-lock-compose-symbol)))))) ;; The font lock regular expressions. (defconst sml-font-lock-keywords `(;;(sml-font-comments-and-strings) - (,(concat "\\<\\(fun\\|and\\)\\s-+" sml-tyvarseq-re "\\(\\sw+\\)\\s-+[^ \t\n=]") + (,(concat "\\_<\\(fun\\|and\\)\\s-+" sml-tyvarseq-re + "\\(" sml-re-id "\\)\\s-+[^ \t\n=]") (1 font-lock-keyword-face) (6 font-lock-function-name-face)) - (,(concat "\\<\\(\\(data\\|abs\\|with\\|eq\\)?type\\)\\s-+" sml-tyvarseq-re "\\(\\sw+\\)") + (,(concat "\\_<\\(\\(data\\|abs\\|with\\|eq\\)?type\\)\\s-+" + sml-tyvarseq-re "\\(" sml-id-re "\\)") (1 font-lock-keyword-face) (7 font-lock-type-def-face)) - ("\\<\\(val\\)\\s-+\\(\\sw+\\>\\s-*\\)?\\(\\sw+\\)\\s-*[=:]" + (,(concat "\\_<\\(val\\)\\s-+\\(" sml-id-re "\\_>\\s-*\\)?\\(" + sml-id-re "\\)\\s-*[=:]") (1 font-lock-keyword-face) ;;(6 font-lock-variable-def-face nil t) (3 font-lock-variable-name-face)) - ("\\<\\(structure\\|functor\\|abstraction\\)\\s-+\\(\\sw+\\)" + (,(concat "\\_<\\(structure\\|functor\\|abstraction\\)\\s-+\\(" + sml-re-id "\\)") (1 font-lock-keyword-face) (2 font-lock-module-def-face)) - ("\\<\\(signature\\)\\s-+\\(\\sw+\\)" + (,(concat "\\_<\\(signature\\)\\s-+\\(" sml-id-re "\\)") (1 font-lock-keyword-face) (2 font-lock-interface-def-face)) @@ -472,134 +355,110 @@ Regexp match data 0 points to the chars." st) "Syntax table for text-properties") -;; For Emacsen that have no built-in support for nested comments -(defun sml-get-depth-st () - (save-excursion - (let* ((disp (if (eq (char-before) ?\)) (progn (backward-char) -1) nil)) - (_ (backward-char)) - (disp (if (eq (char-before) ?\() (progn (backward-char) 0) disp)) - (pt (point))) - (when disp - (let* ((depth - (save-match-data - (if (re-search-backward "\\*)\\|(\\*" nil t) - (+ (or (get-char-property (point) 'comment-depth) 0) - (case (char-after) (?\( 1) (?* 0)) - disp) - 0))) - (depth (if (> depth 0) depth))) - (put-text-property pt (1+ pt) 'comment-depth depth) - (when depth sml-syntax-prop-table)))))) - (defconst sml-font-lock-syntactic-keywords - `(("^\\s-*\\(\\\\\\)" (1 ',sml-syntax-prop-table)) - ,@(unless sml-builtin-nested-comments-flag - '(("(?\\(\\*\\))?" (1 (sml-get-depth-st))))))) + `(("^\\s-*\\(\\\\\\)" (1 ',sml-syntax-prop-table)))) (defconst sml-font-lock-defaults - '(sml-font-lock-keywords nil nil ((?_ . "w") (?' . "w")) nil + '(sml-font-lock-keywords nil nil nil nil (font-lock-syntactic-keywords . sml-font-lock-syntactic-keywords))) ;;; Indentation with SMIE -(defvar sml-use-smie t) - (defconst sml-smie-grammar - (when (fboundp 'smie-prec2->grammar) - ;; We have several problem areas where SML's syntax can't be handled by an - ;; operator precedence grammar: - ;; - ;; "= A before B" is "= A) before B" if this is the - ;; `boolean-=' but it is "= (A before B)" if it's the `definitional-='. - ;; We can work around the problem by tweaking the lexer to return two - ;; different tokens for the two different kinds of `='. - ;; "of A | B" in a "case" we want "of (A | B, but in a `datatype' - ;; we want "of A) | B". - ;; "= A | B" can be "= A ) | B" if the = is from a "fun" definition, - ;; but it is "= (A | B" if it is a `datatype' definition (of course, if - ;; the previous token introducing the = is `and', deciding whether - ;; it's a datatype or a function requires looking even further back). - ;; "functor foo (...) where type a = b = ..." the first `=' looks very much - ;; like a `definitional-=' even tho it's just an equality constraint. - ;; Currently I don't even try to handle `where' at all. - (smie-prec2->grammar - (smie-merge-prec2s - (smie-bnf->prec2 - '((exp ("if" exp "then" exp "else" exp) - ("case" exp "of" branches) - ("let" decls "in" cmds "end") - ("struct" decls "end") - ("sig" decls "end") - (sexp) - (sexp "handle" branches) - ("fn" sexp "=>" exp)) - ;; "simple exp"s are the ones that can appear to the left of `handle'. - (sexp (sexp ":" type) ("(" exps ")") - (sexp "orelse" sexp) - (marg ":>" type) - (sexp "andalso" sexp)) - (cmds (cmds ";" cmds) (exp)) - (exps (exps "," exps) (exp)) ; (exps ";" exps) - (branches (sexp "=>" exp) (branches "|" branches)) - ;; Operator precedence grammars handle separators much better then - ;; starters/terminators, so let's pretend that let/fun are separators. - (decls (sexp "d=" exp) - (sexp "d=" databranches) - (funbranches "|" funbranches) - (sexp "=of" type) ;After "exception". - ;; FIXME: Just like PROCEDURE in Pascal and Modula-2, this - ;; interacts poorly with the other constructs since I - ;; can't make "local" a separator like fun/val/type/... - ("local" decls "in" decls "end") - ;; (decls "local" decls "in" decls "end") - (decls "functor" decls) - (decls "signature" decls) - (decls "structure" decls) - (decls "type" decls) - (decls "open" decls) - (decls "and" decls) - (decls "infix" decls) - (decls "infixr" decls) - (decls "nonfix" decls) - (decls "abstype" decls) - (decls "datatype" decls) - (decls "exception" decls) - (decls "fun" decls) - (decls "val" decls)) - (type (type "->" type) - (type "*" type)) - (funbranches (sexp "d=" exp)) - (databranches (sexp "=of" type) (databranches "d|" databranches)) - ;; Module language. - ;; (mexp ("functor" marg "d=" mexp) - ;; ("structure" marg "d=" mexp) - ;; ("signature" marg "d=" mexp)) - (marg (marg ":" type) (marg ":>" type)) - (toplevel (decls) (exp) (toplevel ";" toplevel))) - ;; '(("local" . opener)) - ;; '((nonassoc "else") (right "handle")) - '((nonassoc "of") (assoc "|")) ; "case a of b => case c of d => e | f" - '((nonassoc "handle") (assoc "|")) ; Idem for "handle". - '((assoc "->") (assoc "*")) - '((assoc "val" "fun" "type" "datatype" "abstype" "open" "infix" "infixr" - "nonfix" "functor" "signature" "structure" "exception" - ;; "local" - ) - (assoc "and")) - '((assoc "orelse") (assoc "andalso") (nonassoc ":")) - '((assoc ";")) '((assoc ",")) '((assoc "d|"))) - - (smie-precs->prec2 - '((nonassoc "andalso") ;To anchor the prec-table. - (assoc "before") ;0 - (assoc ":=" "o") ;3 - (nonassoc ">" ">=" "<>" "<" "<=" "=") ;4 - (assoc "::" "@") ;5 - (assoc "+" "-" "^") ;6 - (assoc "/" "*" "quot" "rem" "div" "mod") ;7 - (nonassoc " -dummy- "))) ;Bogus anchor at the end. - )))) + ;; We have several problem areas where SML's syntax can't be handled by an + ;; operator precedence grammar: + ;; + ;; "= A before B" is "= A) before B" if this is the + ;; `boolean-=' but it is "= (A before B)" if it's the `definitional-='. + ;; We can work around the problem by tweaking the lexer to return two + ;; different tokens for the two different kinds of `='. + ;; "of A | B" in a "case" we want "of (A | B, but in a `datatype' + ;; we want "of A) | B". + ;; "= A | B" can be "= A ) | B" if the = is from a "fun" definition, + ;; but it is "= (A | B" if it is a `datatype' definition (of course, if + ;; the previous token introducing the = is `and', deciding whether + ;; it's a datatype or a function requires looking even further back). + ;; "functor foo (...) where type a = b = ..." the first `=' looks very much + ;; like a `definitional-=' even tho it's just an equality constraint. + ;; Currently I don't even try to handle `where' at all. + (smie-prec2->grammar + (smie-merge-prec2s + (smie-bnf->prec2 + '((exp ("if" exp "then" exp "else" exp) + ("case" exp "of" branches) + ("let" decls "in" cmds "end") + ("struct" decls "end") + ("sig" decls "end") + (sexp) + (sexp "handle" branches) + ("fn" sexp "=>" exp)) + ;; "simple exp"s are the ones that can appear to the left of `handle'. + (sexp (sexp ":" type) ("(" exps ")") + (sexp "orelse" sexp) + (marg ":>" type) + (sexp "andalso" sexp)) + (cmds (cmds ";" cmds) (exp)) + (exps (exps "," exps) (exp)) ; (exps ";" exps) + (branches (sexp "=>" exp) (branches "|" branches)) + ;; Operator precedence grammars handle separators much better then + ;; starters/terminators, so let's pretend that let/fun are separators. + (decls (sexp "d=" exp) + (sexp "d=" databranches) + (funbranches "|" funbranches) + (sexp "=of" type) ;After "exception". + ;; FIXME: Just like PROCEDURE in Pascal and Modula-2, this + ;; interacts poorly with the other constructs since I + ;; can't make "local" a separator like fun/val/type/... + ("local" decls "in" decls "end") + ;; (decls "local" decls "in" decls "end") + (decls "functor" decls) + (decls "signature" decls) + (decls "structure" decls) + (decls "type" decls) + (decls "open" decls) + (decls "and" decls) + (decls "infix" decls) + (decls "infixr" decls) + (decls "nonfix" decls) + (decls "abstype" decls) + (decls "datatype" decls) + (decls "exception" decls) + (decls "fun" decls) + (decls "val" decls)) + (type (type "->" type) + (type "*" type)) + (funbranches (sexp "d=" exp)) + (databranches (sexp "=of" type) (databranches "d|" databranches)) + ;; Module language. + ;; (mexp ("functor" marg "d=" mexp) + ;; ("structure" marg "d=" mexp) + ;; ("signature" marg "d=" mexp)) + (marg (marg ":" type) (marg ":>" type)) + (toplevel (decls) (exp) (toplevel ";" toplevel))) + ;; '(("local" . opener)) + ;; '((nonassoc "else") (right "handle")) + '((nonassoc "of") (assoc "|")) ; "case a of b => case c of d => e | f" + '((nonassoc "handle") (assoc "|")) ; Idem for "handle". + '((assoc "->") (assoc "*")) + '((assoc "val" "fun" "type" "datatype" "abstype" "open" "infix" "infixr" + "nonfix" "functor" "signature" "structure" "exception" + ;; "local" + ) + (assoc "and")) + '((assoc "orelse") (assoc "andalso") (nonassoc ":")) + '((assoc ";")) '((assoc ",")) '((assoc "d|"))) + + (smie-precs->prec2 + '((nonassoc "andalso") ;To anchor the prec-table. + (assoc "before") ;0 + (assoc ":=" "o") ;3 + (nonassoc ">" ">=" "<>" "<" "<=" "=") ;4 + (assoc "::" "@") ;5 + (assoc "+" "-" "^") ;6 + (assoc "/" "*" "quot" "rem" "div" "mod") ;7 + (nonassoc " -dummy- "))) ;Bogus anchor at the end. + ))) (defvar sml-indent-separator-outdent 2) @@ -712,7 +571,7 @@ Assumes point is right before the = sign." Assumes point is right before the \"of\" symbol." (save-excursion (and (re-search-backward (concat "\\(" sml-non-nested-of-starter-re - "\\)\\|\\<case\\>") nil t) + "\\)\\|\\_<case\\_>") nil t) (match-beginning 1)))) (defun sml-smie-datatype-|-p () @@ -778,7 +637,7 @@ Assumes point is right before the | symbol." (concat "^[ \t]*\\(let[ \t]+\\)?" (regexp-opt (append sml-module-head-syms '("and" "fun" "datatype" "abstype" "type")) t) - "\\>")) + "\\_>")) (defun sml-imenu-create-index () (let (alist) @@ -808,15 +667,15 @@ Assumes point is right before the | symbol." ;;;###autoload (add-to-list 'auto-mode-alist '("\\.s\\(ml\\|ig\\)\\'" . sml-mode)) -(unless (fboundp 'prog-mode) (defalias 'prog-mode 'fundamental-mode)) (defvar comment-quote-nested) (defvar electric-indent-chars) (defvar electric-layout-rules) ;;;###autoload (define-derived-mode sml-mode prog-mode "SML" - "\\<sml-mode-map>Major mode for editing ML code. + "\\<sml-mode-map>Major mode for editing Standard ML code. This mode runs `sml-mode-hook' just before exiting. +See also (info \"(sml-mode)Top\"). \\{sml-mode-map}" (set (make-local-variable 'font-lock-defaults) sml-font-lock-defaults) (set (make-local-variable 'outline-regexp) sml-outline-regexp) @@ -839,23 +698,15 @@ This mode runs `sml-mode-hook' just before exiting. (progn (skip-chars-forward " \t;") (eolp))) 'after)))))) - ;; For XEmacs - (easy-menu-add sml-mode-menu) - ;; Compatibility. FIXME: we should use `-' in Emacs-CVS. - (unless (boundp 'skeleton-positions) (set (make-local-variable '@) nil)) (sml-mode-variables)) (defun sml-mode-variables () (set-syntax-table sml-mode-syntax-table) (setq local-abbrev-table sml-mode-abbrev-table) ;; Setup indentation and sexp-navigation. - (when (fboundp 'smie-setup) - (smie-setup sml-smie-grammar #'sml-smie-rules - :backward-token #'sml-smie-backward-token - :forward-token #'sml-smie-forward-token)) - (unless (and sml-use-smie (fboundp 'smie-setup)) - (set (make-local-variable 'forward-sexp-function) 'sml-user-forward-sexp) - (set (make-local-variable 'indent-line-function) 'sml-indent-line)) + (smie-setup sml-smie-grammar #'sml-smie-rules + :backward-token #'sml-smie-backward-token + :forward-token #'sml-smie-forward-token) (set (make-local-variable 'parse-sexp-ignore-comments) t) (set (make-local-variable 'comment-start) "(* ") (set (make-local-variable 'comment-end) " *)") @@ -880,7 +731,7 @@ Point has to be right after the `and' symbol and is not preserved." (not (looking-at re))) (or (ignore-errors (forward-sexp 1) t) (forward-char 1)))) -(defun sml-electric-pipe () +(defun sml-electric-pipe () ;FIXME: Use post-self-insert-hook? "Insert a \"|\". Depending on the context insert the name of function, a \"=>\" etc." ;; FIXME: Make it a skeleton. @@ -921,39 +772,28 @@ Depending on the context insert the name of function, a \"=>\" etc." (indent-according-to-mode) (beginning-of-line) (skip-chars-forward "\t |") - (skip-syntax-forward "w") + (skip-syntax-forward "w_") (skip-chars-forward "\t ") (when (eq ?= (char-after)) (backward-char)))) -(defun sml-electric-semi () - "Insert a \;. -If variable `sml-electric-semi-mode' is t, indent the current line, insert -a newline, and indent." - (interactive) - (self-insert-command 1) - (if sml-electric-semi-mode - (reindent-then-newline-and-indent))) - ;;; Misc (defun sml-mark-function () "Mark the surrounding function. Or try to at least." (interactive) - (if (not (fboundp 'smie-setup)) - (mark-paragraph) - ;; FIXME: Provide beginning-of-defun-function so mark-defun "just works". - (let ((start (point))) - (sml-beginning-of-defun) - (let ((beg (point))) - (smie-forward-sexp 'halfsexp) - (if (or (< start beg) (> start (point))) - (progn - (goto-char start) - (mark-paragraph)) - (push-mark nil t t) - (goto-char beg)))))) - -(defun sml-back-to-outer-indent () + ;; FIXME: Provide beginning-of-defun-function so mark-defun "just works". + (let ((start (point))) + (sml-beginning-of-defun) + (let ((beg (point))) + (smie-forward-sexp 'halfsexp) + (if (or (< start beg) (> start (point))) + (progn + (goto-char start) + (mark-paragraph)) + (push-mark nil t t) + (goto-char beg))))) + +(defun sml-back-to-outer-indent () ;FIXME-copyright. "Unindents to the next outer level of indentation." (interactive) (save-excursion @@ -965,12 +805,12 @@ a newline, and indent." (progn (save-excursion (while (>= indent start-column) - (if (re-search-backward "^[^\n]" nil t) - (setq indent (current-indentation)) - (setq indent 0)))) + (setq indent (if (re-search-backward "^[^\n]" nil t) + (current-indentation) + 0)))) (backward-delete-char-untabify (- start-column indent))))))) -(defun sml-smie-find-matching-starter (syms) +(defun sml-find-matching-starter (syms) (let ((halfsexp nil) tok) ;;(sml-smie-forward-token) @@ -984,14 +824,7 @@ a newline, and indent." (if (nth 2 tok) (goto-char (cadr tok))) (nth 2 tok))) -(defun sml-find-matching-starter (syms) - (cond - ((and sml-use-smie (fboundp 'smie-backward-sexp)) - (sml-smie-find-matching-starter syms)) - ((fboundp 'sml-old-find-matching-starter) - (sml-old-find-matching-starter syms)))) - -(defun sml-smie-skip-siblings () +(defun sml-skip-siblings () (let (tok) (while (and (not (bobp)) (progn (setq tok (smie-backward-sexp 'half)) @@ -1002,14 +835,6 @@ a newline, and indent." (if (nth 2 tok) (goto-char (cadr tok))) (nth 2 tok))) -(defun sml-skip-siblings () - (cond - ((and sml-use-smie (fboundp 'smie-backward-sexp)) - (sml-smie-skip-siblings)) - ((fboundp 'sml-old-skip-siblings) - (sml-old-skip-siblings)) - (t (up-list -1)))) - (defun sml-beginning-of-defun () (let ((sym (sml-find-matching-starter sml-starters-syms))) (if (member sym '("fun" "and" "functor" "signature" "structure" @@ -1043,7 +868,7 @@ a newline, and indent." ;;; INSERTING PROFORMAS (COMMON SML-FORMS) (defvar sml-forms-alist nil - "*Alist of code templates. + "Alist of code templates. You can extend this alist to your heart's content. For each additional template NAME in the list, declare a keyboard macro or function (or interactive command) called 'sml-form-NAME'. @@ -1056,26 +881,23 @@ and `sml-addto-forms-alist'. signature, structure, and functor by default.") (defmacro sml-def-skeleton (name interactor &rest elements) - (when (fboundp 'define-skeleton) - (let ((fsym (intern (concat "sml-form-" name)))) - ;; TODO: don't do the expansion in comments and strings. - `(progn - (add-to-list 'sml-forms-alist ',(cons name fsym)) - (condition-case err - ;; Try to use the new `system' flag. - (define-abbrev sml-mode-abbrev-table ,name "" ',fsym nil 'system) - (wrong-number-of-arguments - (define-abbrev sml-mode-abbrev-table ,name "" ',fsym))) - (when (fboundp 'abbrev-put) - (let ((abbrev (abbrev-symbol ,name sml-mode-abbrev-table))) - (abbrev-put abbrev :case-fixed t) - (abbrev-put abbrev :enable-function - (lambda () (not (nth 8 (syntax-ppss))))))) - (define-skeleton ,fsym - ,(format "SML-mode skeleton for `%s..' expressions" name) - ,interactor - ,(concat name " ") > - ,@elements))))) + (let ((fsym (intern (concat "sml-form-" name)))) + `(progn + (add-to-list 'sml-forms-alist ',(cons name fsym)) + (condition-case err + ;; Try to use the new `system' flag. + (define-abbrev sml-mode-abbrev-table ,name "" ',fsym nil 'system) + (wrong-number-of-arguments + (define-abbrev sml-mode-abbrev-table ,name "" ',fsym))) + (let ((abbrev (abbrev-symbol ,name sml-mode-abbrev-table))) + (abbrev-put abbrev :case-fixed t) + (abbrev-put abbrev :enable-function + (lambda () (not (nth 8 (syntax-ppss)))))) + (define-skeleton ,fsym + ,(format "SML-mode skeleton for `%s..' expressions" name) + ,interactor + ,(concat name " ") > + ,@elements)))) (put 'sml-def-skeleton 'lisp-indent-function 2) (sml-def-skeleton "let" nil @@ -1141,7 +963,7 @@ the corresponding form is inserted." (this-command 'self-insert-command)) (call-interactively 'self-insert-command))) -(defun sml-insert-form (name newline) +(defun sml-insert-form (name newline) ;FIXME-copyright. "Interactive short-cut to insert the NAME common ML form. If a prefix argument is given insert a NEWLINE and indent first, or just move to the proper indentation if the line is blank\; otherwise @@ -1152,24 +974,23 @@ The default form to insert is 'whatever you inserted last time' completion from `sml-forms-alist'." (interactive (list (completing-read - (format "Form to insert: (default %s) " sml-last-form) - sml-forms-alist nil t nil) + (format "Form to insert (default %s): " sml-last-form) + sml-forms-alist nil t nil nil sml-forms-alist) current-prefix-arg)) - ;; default is whatever the last insert was... - (if (string= name "") (setq name sml-last-form) (setq sml-last-form name)) + (setq sml-last-form name) (unless (or (not newline) (save-excursion (beginning-of-line) (looking-at "\\s-*$"))) (insert "\n")) - (unless (/= ?w (char-syntax (preceding-char))) (insert " ")) + (when (memq (char-syntax (preceding-char)) '(?_ ?w)) (insert " ")) (let ((f (cdr (assoc name sml-forms-alist)))) (cond ((commandp f) (command-execute f)) (f (funcall f)) - (t (error "Undefined form: %s" name))))) + (t (error "Undefined SML form: %s" name))))) ;; See also macros.el in emacs lisp dir. -(defun sml-addto-forms-alist (name) +(defun sml-addto-forms-alist (name) ;FIXME-copyright. "Assign a name to the last keyboard macro defined. Argument NAME is transmogrified to sml-form-NAME which is the symbol actually defined. @@ -1204,7 +1025,7 @@ See also `edit-kbd-macro' which is bound to \\[edit-kbd-macro]." `(("^\\(?:Error\\|\\(Warning\\)\\): \\(.+\\) \\([0-9]+\\)\\.\\([0-9]+\\)\\.$" 2 3 4 ;; If subgroup 1 matched, then it's a warning, otherwise it's an error. - ,@(if (fboundp 'compilation-fake-loc) '((1)))))) + (1)))) (defvar compilation-error-regexp-alist) (eval-after-load "compile" @@ -1307,9 +1128,9 @@ See also `edit-kbd-macro' which is bound to \\[edit-kbd-macro]." (defvar sml-cm-mode-syntax-table sml-mode-syntax-table) (defvar sml-cm-font-lock-keywords - `(,(concat "\\<" (regexp-opt '("library" "group" "is" "structure" + `(,(concat "\\_<" (regexp-opt '("library" "group" "is" "structure" "functor" "signature" "funsig") t) - "\\>"))) + "\\_>"))) ;;;###autoload (add-to-list 'completion-ignored-extensions ".cm/") ;; This was used with the old compilation manager. @@ -1329,7 +1150,7 @@ See also `edit-kbd-macro' which is bound to \\[edit-kbd-macro]." (defvar sml-lex-font-lock-keywords (append - '(("^%\\sw+" . font-lock-builtin-face) + `((,(concat "^%" sml-id-re) . font-lock-builtin-face) ("^%%" . font-lock-module-def-face)) sml-font-lock-keywords)) (defconst sml-lex-font-lock-defaults @@ -1364,16 +1185,18 @@ If nil, align it with previous cases." :type 'integer) (defvar sml-yacc-font-lock-keywords - (cons '("^\\(\\sw+\\s-*:\\|\\s-*|\\)\\(\\s-*\\sw+\\)*\\s-*\\(\\(%\\sw+\\)\\s-+\\sw+\\|\\)" - (0 (save-excursion - (save-match-data - (goto-char (match-beginning 0)) - (unless (or (re-search-forward "\\<of\\>" (match-end 0) 'move) - (progn (forward-comment (point-max)) - (not (looking-at "(")))) - sml-yacc-bnf-face)))) - (4 font-lock-builtin-face t t)) - sml-lex-font-lock-keywords)) + (cons `((concat "^\\(" sml-id-re "\\s-*:\\|\\s-*|\\)\\(\\s-*" sml-id-re + "\\)*\\s-*\\(\\(%" sml-id-re "\\)\\s-+" sml-id-re "\\|\\)") + (0 (save-excursion + (save-match-data + (goto-char (match-beginning 0)) + (unless (or (re-search-forward "\\_<of\\_>" + (match-end 0) 'move) + (progn (forward-comment (point-max)) + (not (looking-at "(")))) + sml-yacc-bnf-face)))) + (4 font-lock-builtin-face t t)) + sml-lex-font-lock-keywords)) (defconst sml-yacc-font-lock-defaults (cons 'sml-yacc-font-lock-keywords (cdr sml-font-lock-defaults))) @@ -1388,7 +1211,9 @@ If nil, align it with previous cases." (defun sml-yacc-indentation () (save-excursion (back-to-indentation) - (or (and (looking-at "%\\|\\(\\sw\\|\\s_\\)+\\s-*:") 0) + (or (and (looking-at (eval-when-compile + (concat "%\\|" sml-id-re "\\s-*:"))) + 0) (when (save-excursion (condition-case nil (progn (up-list -1) nil) (scan-error t))) ;; We're outside an action. @@ -1415,10 +1240,7 @@ If nil, align it with previous cases." (skip-syntax-forward " ") (- (current-column) 2)))))) ;; default to SML rules - (cond - ((and sml-use-smie (fboundp 'smie-indent-calculate)) - (smie-indent-calculate)) - ((fboundp 'sml-calculate-indentation) (sml-calculate-indentation)))))) + (smie-indent-calculate)))) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.grm\\'" . sml-yacc-mode)) @@ -1431,7 +1253,4 @@ If nil, align it with previous cases." (provide 'sml-mode) -(unless (and sml-use-smie (fboundp 'smie-setup)) - (require 'sml-oldindent)) - ;;; sml-mode.el ends here diff --git a/sml-proc.el b/sml-proc.el index 84e5e65670a..08d88085800 100644 --- a/sml-proc.el +++ b/sml-proc.el @@ -1,4 +1,4 @@ -;;; sml-proc.el --- Comint based interaction mode for Standard ML. +;;; sml-proc.el --- Comint based interaction mode for Standard ML. -*- lexical-binding: t; coding: utf-8 -*- ;; Copyright (C) 1999,2000,2003,2004,2005,2007,2012 Stefan Monnier ;; Copyright (C) 1994-1997 Matthew J. Morley @@ -26,12 +26,10 @@ ;; ==================================================================== -;; [MJM 10/94] Separating this from sml-mode means sml-mode will run -;; under 18.59 (or anywhere without comint, if there are such places). -;; See sml-mode.el for further information. - ;;; Commentary: +;; FIXME-copyright. + ;; Inferior-sml-mode is for interacting with an ML process run under ;; emacs. This uses the comint package so you get history, expansion, ;; backup and all the other benefits of comint. Interaction is @@ -82,7 +80,6 @@ ;; Todo: ;; - Keep improving `sml-compile'. -;; - ignore warnings (if requested) for next-error ;;; Code: @@ -126,18 +123,11 @@ It is perfectly OK to associate several files with a command or several commands with the same file.") (defvar inferior-sml-mode-hook nil - "*This hook is run when the inferior ML process is started. + "Hook is run when the inferior ML process is started. All buffer local customisations for the interaction buffers go here.") -(defvar sml-error-overlay nil - "*Non-nil means use an overlay to highlight errorful code in the buffer. -The actual value is the name of a face to use for the overlay. -Instead of setting this variable to 'region, you can also simply keep -it NIL and use (transient-mark-mode) which will provide similar -benefits (but with several side effects).") - (defvar sml-buffer nil - "*The current ML process buffer. + "The current ML process buffer. MULTIPLE PROCESS SUPPORT (Whoever wants multi-process support anyway?) ===================================================================== @@ -182,12 +172,12 @@ use the command \\[sml-buffer] in the interaction buffer of choice.") ;;; ALL STUFF THAT DEFAULTS TO THE SML/NJ COMPILER (0.93) (defvar sml-use-command "use \"%s\"" - "*Template for loading a file into the inferior ML process. + "Template for loading a file into the inferior ML process. Set to \"use \\\"%s\\\"\" for SML/NJ or Edinburgh ML; set to \"PolyML.use \\\"%s\\\"\" for Poly/ML, etc.") (defvar sml-cd-command "OS.FileSys.chDir \"%s\"" - "*Command template for changing working directories under ML. + "Command template for changing working directories under ML. Set this to nil if your compiler can't change directories. The format specifier \"%s\" will be converted into the directory name @@ -205,14 +195,10 @@ specified when running the command \\[sml-cd].") ;; SML/NJ: the file-pattern is anchored to avoid ;; pathological behavior with very long lines. ("^[-= ]*\\(.*[^\n)]\\)\\( (.*)\\)?:\\([0-9]+\\)\\.\\([0-9]+\\)\\(-\\([0-9]+\\)\\.\\([0-9]+\\)\\)? \\(Error\\|Warnin\\(g\\)\\): .*" 1 - ,@(if (fboundp 'compilation-fake-loc) ;New compile.el. - '((3 . 6) (4 . 7) (9)) - '(sml-make-error 3 4 6 7))) + (3 . 6) (4 . 7) (9)) ;; SML/NJ's exceptions: see above. ("^ +\\(raised at: \\)?\\(.+\\):\\([0-9]+\\)\\.\\([0-9]+\\)\\(-\\([0-9]+\\)\\.\\([0-9]+\\)\\)" 2 - ,@(if (fboundp 'compilation-fake-loc) ;New compile.el. - '((3 . 6) (4 . 7)) - '(sml-make-error 3 4 6 7)))) + (3 . 6) (4 . 7))) "Alist that specifies how to match errors in compiler output. See `compilation-error-regexp-alist' for a description of the format.") @@ -226,11 +212,7 @@ See `compilation-error-regexp-alist' for a description of the format.") ;; CM's messages ("^\\[\\(.*GC #.*\n\\)*.*\\]" . font-lock-comment-face) ;; SML/NJ's irritating GC messages - ("^GC #.*" . font-lock-comment-face) - ;; error messages - ,@(unless (fboundp 'compilation-fake-loc) - (mapcar (lambda (ra) (cons (car ra) 'font-lock-warning-face)) - sml-error-regexp-alist))) + ("^GC #.*" . font-lock-comment-face)) "Font-locking specification for inferior SML mode.") (defface font-lock-prompt-face @@ -258,17 +240,13 @@ See `compilation-error-regexp-alist' for a description of the format.") (set-keymap-parent map comint-mode-map) (define-key map "\C-c\C-s" 'run-sml) (define-key map "\C-c\C-l" 'sml-load-file) - (define-key map "\t" - (if (fboundp 'completion-at-point) - 'completion-at-point 'comint-dynamic-complete)) + (define-key map "\t" 'completion-at-point) map) "Keymap for inferior-sml mode") ;; buffer-local (defvar sml-temp-file nil) -;;(defvar sml-error-file nil) ; file from which the last error came -(defvar sml-error-cursor nil) ; ditto (defun sml-proc-buffer () "Return the current ML process buffer. @@ -310,7 +288,7 @@ If prefix argument ECHO is set, then it only reports on the current state." ;; but this doesn't catch the case when the user types commands directly ;; at the prompt. (compilation-forget-errors) ;Has to run before compilation-fake-loc. - (if (and (fboundp 'compilation-fake-loc) sml-temp-file) + (if sml-temp-file (compilation-fake-loc (cdr sml-temp-file) (car sml-temp-file))) str) @@ -346,22 +324,20 @@ If prefix argument ECHO is set, then it only reports on the current state." ;; The position of `point' is not guaranteed :-( (looking-at (concat ".*\\[tycon mismatch\\]\n" " \\(operator domain\\|expression\\|rule domain\\): +"))) - (ignore-errors (require 'smerge-mode)) - (if (not (fboundp 'smerge-refine-subst)) - (remove-hook 'next-error-hook 'inferior-sml-next-error-hook) - (save-excursion - (let ((b1 (match-end 0)) - e1 b2 e2) - (when (re-search-forward "\n in \\(expression\\|declaration\\):\n" - nil t) - (setq e2 (match-beginning 0)) - (when (re-search-backward - "\n \\(operand\\|result type\\|object\\): +" - b1 t) - (setq e1 (match-beginning 0)) - (setq b2 (match-end 0)) - (smerge-refine-subst b1 e1 b2 e2 - '((face . smerge-refined-change))))))))))) + (require 'smerge-mode) + (save-excursion + (let ((b1 (match-end 0)) + e1 b2 e2) + (when (re-search-forward "\n in \\(expression\\|declaration\\):\n" + nil t) + (setq e2 (match-beginning 0)) + (when (re-search-backward + "\n \\(operand\\|result type\\|object\\): +" + b1 t) + (setq e1 (match-beginning 0)) + (setq b2 (match-end 0)) + (smerge-refine-subst b1 e1 b2 e2 + '((face . smerge-refined-change)))))))))) (define-derived-mode inferior-sml-mode comint-mode "Inferior-SML" "Major mode for interacting with an inferior ML process. @@ -420,35 +396,28 @@ TAB file name completion, as in shell-mode, etc.." (set (make-local-variable 'font-lock-defaults) inferior-sml-font-lock-defaults) - ;; For sequencing through error messages: - (set (make-local-variable 'sml-error-cursor) (point-max-marker)) - (set-marker-insertion-type sml-error-cursor nil) ;; Compilation support (used for `next-error'). ;; The keymap of compilation-minor-mode is too unbearable, so we ;; just can't use the minor-mode if we can't override the map. - (when (boundp 'minor-mode-overriding-map-alist) - (set (make-local-variable 'compilation-error-regexp-alist) - sml-error-regexp-alist) - (compilation-minor-mode 1) - ;; Eliminate compilation-minor-mode's map. - (let ((map (make-sparse-keymap))) - (dolist (keys '([menu-bar] [follow-link])) - ;; Preserve some of the bindings. - (define-key map keys (lookup-key compilation-minor-mode-map keys))) - (add-to-list 'minor-mode-overriding-map-alist - (cons 'compilation-minor-mode map))) - ;; I'm sure people might kill me for that - (setq compilation-error-screen-columns nil) - (make-local-variable 'sml-endof-error-alist)) - ;;(make-local-variable 'sml-error-overlay) + (set (make-local-variable 'compilation-error-regexp-alist) + sml-error-regexp-alist) + (compilation-minor-mode 1) + ;; Eliminate compilation-minor-mode's map. + (let ((map (make-sparse-keymap))) + (dolist (keys '([menu-bar] [follow-link])) + ;; Preserve some of the bindings. + (define-key map keys (lookup-key compilation-minor-mode-map keys))) + (add-to-list 'minor-mode-overriding-map-alist + (cons 'compilation-minor-mode map))) + ;; I'm sure people might kill me for that. ;FIXME: move it to sml-mode? + (set (make-local-variable 'compilation-error-screen-columns) nil) (setq mode-line-process '(": %s"))) ;;; FOR RUNNING ML FROM EMACS -;;;###autoload -(autoload 'run-sml "sml-proc" nil t) +;;;###autoload (autoload 'run-sml "sml-proc" nil t) (defalias 'run-sml 'sml-run) (defun sml-run (cmd arg &optional host) "Run the program CMD with given arguments ARG. @@ -456,14 +425,10 @@ The command is run in buffer *CMD* using mode `inferior-sml-mode'. If the buffer already exists and has a running process, then just go to this buffer. -This updates `sml-buffer' to the new buffer. -You can have several inferior M(or L process running, but only one (> s -current one -- given by `sml-buffer' (qv). - If a prefix argument is used, the user is also prompted for a HOST on which to run CMD using `remote-shell-program'. -\(Type \\[describe-mode] in the process buffer for a list of commands.)" +\(Type \\[describe-mode] in the process's buffer for a list of commands.)" (interactive (list (read-string "ML command: " sml-program-name) @@ -474,7 +439,7 @@ on which to run CMD using `remote-shell-program'. (read-string "On host: " sml-host-name) sml-host-name))) (let* ((pname (file-name-nondirectory cmd)) - (args (if (equal arg "") () (split-string arg))) + (args (split-string arg)) (file (when (and sml-config-file (file-exists-p sml-config-file)) sml-config-file))) ;; and this -- to keep these as defaults even if @@ -487,7 +452,8 @@ on which to run CMD using `remote-shell-program'. (setq args (list* host "cd" default-directory ";" cmd args)) (setq cmd remote-shell-program)) ;; go for it - (let ((exec-path (if (file-name-directory cmd) + (let ((exec-path (if (and (file-name-directory cmd) + (not (file-name-absolute-p cmd))) ;; If the command has slashes, make sure we ;; first look relative to the current directory. ;; Emacs-21 does it for us, but not Emacs-20. @@ -495,7 +461,6 @@ on which to run CMD using `remote-shell-program'. (setq sml-buffer (apply 'make-comint pname cmd file args))) (pop-to-buffer sml-buffer) - ;;(message (format "Starting \"%s\" in background." pname)) (inferior-sml-mode) (goto-char (point-max)) sml-buffer)) @@ -509,11 +474,9 @@ Move point to the end of buffer unless prefix argument EOBP is set." (push-mark (point) t) (goto-char (point-max)))) -;; Fakes it with a "use <temp-file>;" if necessary. - (defun sml-send-region (start end &optional and-go) "Send current region START..END to the inferior ML process. -Prefix AND-GO argument means switch-to-sml afterwards. +Prefix AND-GO argument means `switch-to-sml' afterwards. The region is written out to a temporary file and a \"use <temp-file>\" command is sent to the compiler. @@ -547,7 +510,7 @@ With a prefix argument AND-GO switch to the sml buffer as well (if and-go (switch-to-sml nil))) (defvar sml-source-modes '(sml-mode) - "*Used to determine if a buffer contains ML source code. + "Used to determine if a buffer contains ML source code. If it's loaded into a buffer that is in one of these major modes, it's considered an ML source file by `sml-load-file'. Used by these commands to determine defaults.") @@ -557,8 +520,7 @@ to determine defaults.") With a prefix argument AND-GO switch to the sml buffer as well \(cf. `sml-send-region'\)." (interactive "P") - (if (memq major-mode sml-source-modes) - (sml-send-region (point-min) (point-max) and-go))) + (sml-send-region (point-min) (point-max) and-go)) ;; Since sml-send-function/region take an optional prefix arg, these ;; commands are redundant. But they are kept around for the user to @@ -579,7 +541,7 @@ With a prefix argument AND-GO switch to the sml buffer as well (defvar sml-prev-dir/file nil "Cache for (DIRECTORY . FILE) pair last. Set in `sml-load-file' and `sml-cd' commands. -Used to determine the default in the next `ml-load-file'.") +Used to determine the default in the next `sml-load-file'.") (defun sml-load-file (&optional and-go) "Load an ML file into the current inferior ML process. @@ -592,7 +554,7 @@ automatically." (let ((file (car (comint-get-source "Load ML file: " sml-prev-dir/file sml-source-modes t)))) (with-current-buffer (sml-proc-buffer) - ;; Check if buffer needs saved. Should (save-some-buffers) instead? + ;; Check if buffer needs saving. Should (save-some-buffers) instead? (comint-check-source file) (setq sml-prev-dir/file (cons (file-name-directory file) (file-name-nondirectory file))) @@ -608,7 +570,7 @@ be executed to change the compiler's working directory\; a trailing (let ((dir (expand-file-name dir))) (with-current-buffer (sml-proc-buffer) (sml-send-string (format sml-cd-command dir) t) - (setq default-directory dir)) + (setq default-directory (file-name-as-directory dir))) (setq sml-prev-dir/file (cons dir nil)))) (defun sml-send-string (str &optional print and-go) @@ -618,7 +580,6 @@ be executed to change the compiler's working directory\; a trailing (when win (select-window win)) (goto-char (point-max)) (when print (insert str)) - (sml-update-cursor) (set-marker (process-mark proc) (point-max)) (setq compilation-last-buffer (current-buffer)) (comint-send-string proc str) @@ -637,7 +598,7 @@ Prefix arg AND-GO also means to `switch-to-sml' afterwards." (interactive (let* ((dir default-directory) (cmd "cd \".")) - ;; look for files to determine the default command + ;; Look for files to determine the default command. (while (and (stringp dir) (dolist (cf sml-compile-commands-alist 1) (when (file-exists-p (expand-file-name (cdr cf) dir)) @@ -682,103 +643,110 @@ Prefix arg AND-GO also means to `switch-to-sml' afterwards." (sml-send-string (concat (format sml-cd-command dir) "; " command) t and-go)))) -;;; PARSING ERROR MESSAGES - -;; This should need no modification to support other compilers. - -;; Update the buffer-local error-cursor in proc-buffer to be its -;; current proc mark. - -(defvar sml-endof-error-alist nil) - -(defun sml-update-cursor () - ;; Update buffer local variable. - (set-marker sml-error-cursor (1- (process-mark (sml-proc)))) - (setq sml-endof-error-alist nil) - ;; This is now done in comint-input-filter-functions. - ;; (compilation-forget-errors) ;Has to run before compilation-fake-loc. - ;; (if (and (fboundp 'compilation-fake-loc) sml-temp-file) - ;; (compilation-fake-loc (cdr sml-temp-file) (car sml-temp-file))) - (if (markerp compilation-parsing-end) - (set-marker compilation-parsing-end sml-error-cursor) - (setq compilation-parsing-end sml-error-cursor))) - -(defun sml-make-error (f c) - (let ((err (point-marker)) - (linenum (string-to-number c)) - (filename (list (first f) (second f))) - (column (string-to-number (match-string (third f))))) - ;; record the end of error, if any - (when (fourth f) - (let ((endlinestr (match-string (fourth f)))) - (when endlinestr - (let* ((endline (string-to-number endlinestr)) - (endcol (string-to-number - (or (match-string (fifth f)) "0"))) - (linediff (- endline linenum))) - (push (list err linediff (if (= 0 linediff) (- endcol column) endcol)) - sml-endof-error-alist))))) - ;; build the error descriptor - (if (string= (car sml-temp-file) (first f)) - ;; special case for code sent via sml-send-region - (let ((marker (cdr sml-temp-file))) - (with-current-buffer (marker-buffer marker) - (goto-char marker) - (forward-line (1- linenum)) - (forward-char (1- column)) - ;; A pair of markers is the right thing to return, but some - ;; code in compile.el doesn't like it (when we reach the end - ;; of the errors). So we could try to avoid it, but we don't - ;; because that doesn't work correctly if the current buffer - ;; has unsaved modifications. And it's fixed in Emacs-21. - ;; (if buffer-file-name - ;; (list err buffer-file-name - ;; (count-lines (point-min) (point)) (current-column)) - (cons err (point-marker)))) ;; ) - ;; taken from compile.el - (list err filename linenum column)))) - -(unless (fboundp 'compilation-fake-loc) -(defadvice compilation-goto-locus (after sml-endof-error activate) - (let* ((next-error (ad-get-arg 0)) - (err (car next-error)) - (pos (cdr next-error)) - (endof (with-current-buffer (marker-buffer err) - (assq err sml-endof-error-alist)))) - (if (not endof) (sml-error-overlay 'undo) - (with-current-buffer (marker-buffer pos) - (goto-char pos) - (let ((linediff (second endof)) - (coldiff (third endof))) - (when (> 0 linediff) (forward-line linediff)) - (forward-char coldiff)) - (sml-error-overlay nil pos (point)) - (push-mark nil t (not sml-error-overlay)) - (goto-char pos)))))) - -(defun sml-error-overlay (undo &optional beg end) - "Move `sml-error-overlay' to the text region in the current buffer. -If the buffer-local variable `sml-error-overlay' is -non-nil it should be an overlay \(or extent, in XEmacs speak\)\; this -function moves the overlay over the current region. If the optional -BUFFER argument is given, move the overlay in that buffer instead of -the current buffer. - -Called interactively, the optional prefix argument UNDO indicates that -the overlay should simply be removed: \\[universal-argument] \ -\\[sml-error-overlay]." - (interactive "P") - (when sml-error-overlay - (unless (overlayp sml-error-overlay) - (let ((ol sml-error-overlay)) - (setq sml-error-overlay (make-overlay (point) (point))) - (overlay-put sml-error-overlay 'face (if (symbolp ol) ol 'region)))) - (if undo (delete-overlay sml-error-overlay) - ;; If active regions, signals mark not active if no region set. - (move-overlay sml-error-overlay - (or beg (region-beginning)) (or end (region-end)) - (current-buffer))))) (provide 'sml-proc) +;;; Prog-Proc: Interacting with an inferior process from a source buffer. + +;; Prog-Proc is a package designed to complement Comint: while Comint was +;; designed originally to handle the needs of inferior process buffers, such +;; as a buffer running a Scheme repl, Comint does not actually provide any +;; functionality that links this process buffer with some source code. +;; +;; That's where Prog-Proc comes into play: it provides the usual commands and +;; key-bindings that lets the user send his code to the underlying repl. + +(defvar sml-prog-proc-mode-map + (let ((map (make-sparse-keymap))) + (define-key [?\C-c ?\C-l] 'sml-prog-proc-load-file) + (define-key [?\C-c ?\C-c] 'sml-prog-proc-compile) + (define-key [?\C-c ?\C-z] 'sml-prog-proc-switch-to) + (define-key [?\C-c ?\C-r] 'sml-prog-proc-send-region) + (define-key [?\C-c ?\C-b] 'sml-prog-proc-send-buffer) + map) + "Keymap for `sml-prog-proc-mode'.") + +(defvar sml-prog-proc-buffer nil + "The inferior-process buffer to which to send code.") +(make-variable-buffer-local 'sml-prog-proc-buffer) + +(defstruct (sml-prog-proc-functions + (:predicate nil) + (:copy nil)) + (run :read-only t) + (load-cmd :read-only t)) + +(defvar sml-prog-proc-functions nil + "Struct containing the various functions to create a new process, ...") + +(defmacro sml-prog-proc--call (method &rest args) + `(sml-prog-proc--apply + #',(intern (format "sml-prog-proc-functions-%s" method)) + args)) +(defun sml-prog-proc--apply (selector &rest args) + (if (not sml-prog-proc-functions) + (error "Not an `sml-prog-proc' buffer") + (apply (funcall selector sml-prog-proc-functions) args))) + +(defun sml-prog-proc-proc () + "Return the inferior process for the code in current buffer." + (or (and (buffer-live-p sml-prog-proc-buffer) + (get-buffer-process sml-prog-proc-buffer)) + (sml-prog-proc--call run))) + +(defun sml-prog-proc-switch-to () + "Switch to the buffer running the read-eval-print process." + (let ((proc (sml-prog-proc-proc))) + (pop-to-buffer (process-buffer proc)))) + +(defun sml-prog-proc-send-string (proc str) + (with-current-buffer (process-buffer proc) + (comint-send-string proc str))) + +(defun sml-prog-proc-load-file (file &optional and-go) + "Load FILE into the read-eval-print process. +FILE is the file visited by the current buffer. +If prefix argument AND-GO is used, then we additionally switch +to the buffer where the process is running." + (interactive + (list (or buffer-file-name + (read-file-name "File to load: " nil nil t)) + current-prefix-arg)) + (comint-check-source file) + (let ((proc (sml-prog-proc-proc))) + (sml-prog-proc-send-string proc (sml-prog-proc--call load-cmd file)) + (when and-go (pop-to-buffer (process-buffer proc))))) + +(defvar sml-prog-proc-tmp-file nil) + +(defun sml-prog-proc-send-region (start end &optional and-go) + "Send the content of the region to the read-eval-print process. +START..END delimit the region; AND-GO if non-nil indicate to additionally +switch to the process's buffer." + (interactive "r\nP") + (if (> start end) (let ((tmp end)) (setq end start) (setq start tmp)) + (if (= start end) (error "Nothing to send: the region is empty"))) + (let ((proc (sml-prog-proc-proc)) + (tmp (make-temp-file "sml-prog-proc"))) + (write-region start end tmp nil 'silently) + (when sml-prog-proc-tmp-file + (ignore-errors (delete-file (car sml-prog-proc-tmp-file))) + (set-marker (cdr sml-prog-proc-tmp-file) nil)) + (setq sml-prog-proc-tmp-file (cons tmp (copy-marker start))) + (sml-prog-proc-send-string proc (sml-prog-proc--call load-cmd tmp)) + (when and-go (pop-to-buffer (process-buffer proc))))) + +(defun sml-prog-proc-send-buffer (&optional and-go) + "Send the content of the current buffer to the read-eval-print process. +AND-GO if non-nil indicate to additionally switch to the process's buffer." + (interactive "P") + (sml-prog-proc-send-region (point-min) (point-max) and-go)) + +;; FIXME: How 'bout a menu? Now, that's trickier because keymap inheritance +;; doesn't play nicely with menus! + +(define-derived-mode sml-prog-proc-mode prog-mode "Prog-Proc" + "Major mode for editing source code and interact with an interactive loop." + ) + ;;; sml-proc.el ends here |