;; File tex-mma.el
;; Major modes for interaction with Mathematica from a TeX buffer
;; Written 2/12/1991 by Dan Dill dan@chem.bu.edu

(defconst tex-mma-version-string
  "TeX/Mathematica Version 1.3 <Wed 8 April 1991> Copyright 1991 Dan Dill"
  "String describing this version of TeX/Mathematica.")

(defconst tex-mma-herald
  "This is TeX/Mathematica 1.3 Copyright 1991 Dan Dill.  "
  "Startup message.")

;; Copyright (C) 1991 Dan Dill
;; This is part of TeX/Mathematica
;;
;; TeX/Mathematica is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility to
;; anyone for the consequences of using it or for whether it serves any
;; particular purpose or works at all, unless he says so in writing.
;;
;; Everyone is granted permission to copy, modify and redistribute this
;; tex-mma package, provided:
;;  1.  All copies contain this copyright notice.
;;  2.  All modified copies shall carry a prominant notice stating who
;;      made the last modification and the date of such modification.
;;  3.  No charge is made for this software or works derived from it.  
;;      This clause shall not be construed as constraining other software
;;      distributed on the same medium as this software, nor is a
;;      distribution fee considered a charge.
;;
;; Portions of this package were adapted from GNU Emacs.
;;
;; Copyright (C) 1985, 1986, 1987, 1988 Free Software Foundation, Inc. 
;; 
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.
;;
;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.


;; Developed with GNU Emacs version 18.55

;; 2/17/1991
;;   Version 1 -> 1.1:  Replace shell-mma-mode with David Jacobson's math-mode

;; 2/20/1991
;;   Fix syntax table misassignment in tex-mma-send-cell, caught by
;;   Weiqi Gao gao@ucrmath.ucr.edu

;; 2/21/1991
;;   Change (defconst ...) to (defvar ...) so user (setq ...) preserved,
;;   Dave Gillespie daveg@csvax.cs.caltech.edu

;; 2/28/1991 Version 1.1 -> 1.2:
;;   TeX-Mathematica-help -> Texinfo tex-mma.info
;;   mma -> mathematica for user-visible functions
;;   Fix logic in tex-mma-eval-init so bogus cells bypassed

;; 3/5/1991
;;   Add latex-mathematica and plain-tex-mathematica

;; 3/6/1991
;;   Add tex-mma-eval-all-ask/noask (C-c a/C-u C-c a)

;; 4/9/1991 Version 1.2 -> 1.3
;;   Don't start Mathematica until needed.
;;   Bind tex-mma-recenter to C-c l.
;;   Adapt/add code for generic TeX/Mathematica interface.
;;   Add tex-mma-assemble-package (C-c m) to assemble packages from cells;
;;     related changes.
;;   Renamed many functions; general cleanup

;; 4/26/1991
;;   Rework package interface
;;   Rename tex-mma-assemble-cell to tex-mma-create-cell (C-c c)
;;   Add tex-mma-assemble-cell (C-u C-c m) to assemble a cell
;;   tex-mma-eval-all/init bypass cells containing cell references

;; 5/8/1991
;;   Change tex-mma-math-send-cell to use double unwind-protect, to work
;;   with revised math.el.
;;   Make tex-mma-new-types user setable.
;;   Fix (let...) bug in tex-mma-dispatch

;;;; Environment

(provide 'tex-mma)
(require 'math)
(require 'tex-mode)


;; Constants

(defvar tex-mma-process-string
  "math"
  "String to pass to Unix exec function to start Mathematica.")

(defvar tex-mma-process-name
  "math"
  "The name of the inferior Mathematica process.
make-shell names the buffer the process name surrounded by `*'s.")

(defvar tex-mma-process-buffer
  "*math*"
  "The name of the shell buffer running Mathematica with tex-mma-mode")
 
(defvar tex-mma-info-file
  "/usr8/dan/emacs/info/tex-mma.info"
  "Fully specified location of the tex-mma.info file.")


;; Help

(autoload 'Info-goto-node "info")
(defun tex-mma-info ()
  (interactive)
  (Info-goto-node (concat "(" tex-mma-info-file ")" "Top")))

;;;; Startup

(defun tex-mathematica ()
  "Set up tex-mma-mode and the generice interface."
  (interactive)
  (tex-mma-setup)
  (tex-mma-startup 'tex-mma-mode))

(defun latex-mathematica ()
  "Set up latex-mma-mode and the generic interface."
  (interactive)
  (tex-mma-setup)
  (tex-mma-startup 'latex-mma-mode))

(defun plain-tex-mathematica ()
  "Set up plain-tex-mma-mode and the generic interface."
  (interactive)
  (tex-mma-setup)
  (tex-mma-startup 'plain-tex-mma-mode))

(defun tex-mma-startup (mode)
  "Set up tex-mma-mode."
  (funcall mode)
  ; We don't enter math-mode in TeX buffer so we must make this variable here
  (make-local-variable 'doing-math-complete-symbol)
  (message (substitute-command-keys
	    (concat tex-mma-herald "Use \\[tex-mma-info] for help."))))

;;;; Data structures

; Not defvar, since the code depends on the values defined here.
(defconst tex-mma-cell-alist-default
  '(("mathematica" .
     (
      ("buffer-go" . tex-mma-math-buffer-go)
      ("recenter" . tex-mma-math-recenter)
      ("replace" . tex-mma-math-replace)
      ("send" . tex-mma-math-send)
      ("send-cell" . tex-mma-math-send-cell)
      ("show" . tex-mma-math-show)
      ("update" . tex-mma-math-update)
      )))
  "Alist of functions for `mathematica' cells.  Used by tex-mma-get-cell-alist to
initialize tex-mma-cell-alist so we can be sure to start fresh.")

(defvar tex-mma-cell-alist ()
  "Alist of defined cells.  The entry for each cell is the alist of command names
and corresponding functions.  This is filled in by tex-mma-get-cell-alist from
variables `type'-tex-mma-cell-alist.")

(defvar tex-mma-defined-cells ()
  "List of defined cells.
This is filled in by tex-mma-get-defined-cells.")

(defconst tex-mma-cell-default "mathematica"
  "Default cell-type for generic operations.")

(defvar tex-mma-dereference-path nil
  "List of buffers referenced in cell assembly.
Used by `tex-mma-dereference-buffer' to detect self-reference.")

(defvar tex-mma-error-point nil
  "Buffer position where error detected.")


;;;; Initialization

(defun tex-mma-version ()
  "Display string indentifying each component of this TeX/Mathematica."
  (interactive)
  (with-output-to-temp-buffer "*Help*" (print-help-return-message))
  (let ((home-buffer (current-buffer))
	list
	type)
    (pop-to-buffer "*Help*")
    (insert tex-mma-version-string)
    (insert "\n")
    (setq list tex-mma-defined-cells)
    (while (setq type (car list))
      (setq list (cdr list))
      (if (equal type "mathematica")
	  nil ; We have already displayed this
	(insert (eval (intern (concat type "-tex-mma-version-string")))))
      (insert "\n"))
    (pop-to-buffer home-buffer))
  (bury-buffer "*Help*"))

(defvar tex-mma-new-types ()
  "*List of cell types to be added to TeX/Mathematica.")

(defun tex-mma-setup ()
  "Run hooks for new cell types and setup TeX/Mathematica data structures."
  (tex-mma-run-hooks)
  (tex-mma-get-cell-alist)
  (tex-mma-get-defined-cells)
  )

(defun tex-mma-run-hooks ()
  "Run hooks for new cell types."
  (let ((types tex-mma-new-types)
	type)
    (while (setq type (car types))
      (setq types (cdr types))
      (funcall (intern (concat type "-tex-mma-hook"))) ; Force functions to load
      )))

(defun tex-mma-replace-assoc (alist key val)
  "Replace ALIST KEY VALUE, if KEY present, else add KEY VALUE.
Return modified alist."
  (if (assoc key alist)
      (setcdr (assoc key alist) val)
    (setcdr alist (cons (cons key val) (cdr alist))))
  alist)

(defun tex-mma-get-cell-alist ()
  "Create tex-mma-cell-alist."
  (setq tex-mma-cell-alist tex-mma-cell-alist-default)
  (if tex-mma-new-types
      (let ((types tex-mma-new-types)
	    type)
	(while (setq type (car types))
	  (setq types (cdr types))
	  (if (equal "mathematica" type)
	      (error "You can't add cell type `%s' to TeX/Mathematica" type)
            ; Add functions for a new type
	    (message "Adding cell type `%s' ..." type)
	    (sleep-for 1)
	    (tex-mma-replace-assoc
	     tex-mma-cell-alist
	     type
	     (eval (intern (concat type "-tex-mma-cell-alist")))))))))
      
(defun tex-mma-get-defined-cells ()
  "Fill in list of defined cells."
  (setq tex-mma-defined-cells (mapcar 'car tex-mma-cell-alist)))

;;;; TeX/Mathematica functions for generic cells

(defun tex-mma-dispatch (cell command)
  "Run function for CELL specified by COMMAND."
  (let ((name-function (assoc command (cdr (assoc cell tex-mma-cell-alist)))))
    (if (not name-function)
	(error "Command `%s' not defined for cell type `%s'" command cell)
      (funcall (cdr name-function)))))

(defun tex-mma-cell-type ()
  "Returns cell type if contained in tex-mma-defined-cells,
else returns nil."
  (let ((list tex-mma-defined-cells)
	type)
    (catch 'done
      (while (setq type (car list))
	(if (tex-mma-cell-p type)
	    (throw 'done t))
	(setq list (cdr list))))
    type))

(defun tex-mma-buffer-go ()
  "Go to process buffer.
Process is that of cell containing point, else prompt for process."
  (interactive)
  (if (not tex-mma-new-types)
      (tex-mma-math-buffer-go)
    (let (cell)
      (if (setq cell (tex-mma-cell-type))
	  t
	(setq cell (completing-read
		    (concat "Buffer (default " tex-mma-cell-default "): ")
	 tex-mma-cell-alist nil t nil))
	(if (string-equal cell "")
	    (setq cell tex-mma-cell-default)))
      (setq tex-mma-cell-default cell)
      (tex-mma-dispatch cell "buffer-go"))))

(defun tex-mma-send ()
  "Send statements constaining point to process.
Statements are delimited by blank lines."
  (interactive)
  (if (not tex-mma-new-types)
      (tex-mma-math-send)
    (let ((cell (completing-read
		 (concat "Buffer (default " tex-mma-cell-default "): ")
		 tex-mma-cell-alist nil t nil)))
      (if (string-equal cell "")
	  (setq cell tex-mma-cell-default))
      (setq tex-mma-cell-default cell)
      (tex-mma-dispatch cell "send"))))

(defun tex-mma-send-cell ()
  "Send input to process.
Point must be in a cell."
  (interactive)
  (if (not tex-mma-new-types)
      (tex-mma-math-send-cell)
    (let ((cell (tex-mma-cell-type)))
      (if cell
	  (tex-mma-dispatch cell "send-cell")
	(error "Not in a cell")))))

(defun tex-mma-replace ()
  "Replace output (if any) with last process result.
Point must be in process cell.
Output (if any) assumed to follow input, separated by blank line."
  (interactive)
  (if (not tex-mma-new-types)
      (tex-mma-math-replace)
    (let ((cell (tex-mma-cell-type)))
      (if cell
	  (tex-mma-dispatch cell "replace")
	(error "Not in a cell")))))

(defun tex-mma-update ()
  "Send input to process and optionally replace output with result.
Point must be in process cell.
Output (if any) assumed to follow input, separated by blank line." 
  (interactive)
  (if (not tex-mma-new-types)
      (tex-mma-math-update)
    (let ((cell (tex-mma-cell-type)))
      (if cell
	  (tex-mma-dispatch cell "update")
	(error "Not in a cell")))))

(defun tex-mma-recenter ()
  "Place process buffer input prompt at top of screen.
Process is that of cell containing point, else prompt for process."
  (interactive)
  (if (not tex-mma-new-types)
      (tex-mma-math-recenter)
    (let (cell)
      (if (setq cell (tex-mma-cell-type))
	  t
	(setq cell (completing-read
                    (concat "Buffer (default " tex-mma-cell-default "): ")
		    tex-mma-cell-alist nil t nil))
	(if (string-equal cell "")
            (setq cell tex-mma-cell-default)))
      (setq tex-mma-cell-default cell)
      (tex-mma-dispatch cell "recenter"))))

(defun tex-mma-show ()
  "Make last process output visible.
Process is that of cell containing point, else prompt for process."
  (interactive)
  (if (not tex-mma-new-types)
      (tex-mma-math-show)
    (let (cell)
      (if (setq cell (tex-mma-cell-type))
	  t
        (setq cell (completing-read
                    (concat "Buffer (default " tex-mma-cell-default "): ")
		    tex-mma-cell-alist nil t nil))
        (if (string-equal cell "")
            (setq cell tex-mma-cell-default)))
      (setq tex-mma-cell-default cell)
      (tex-mma-dispatch cell "show"))))

(defun tex-mma-toggle-init ()
  "Toggle the initialization mark of cell."
  (interactive)
  (let ((type (tex-mma-cell-type)))
    (if type
	(tex-mma-toggle-init-type type)
      (error "Not in a cell"))))

(defun tex-mma-toggle-init-type (type)
  "Toggle initialization marker of TYPE cell containing point."
  (save-excursion
    (re-search-backward (concat "^\\\\begin\{" type "\}"))
    (goto-char (match-end 0))
    (if (looking-at "\\[\\* Initialization Cell \\*\\]")
	(delete-region (match-beginning 0) (match-end 0))
      (insert "[* Initialization Cell *]")
      )))

(defun tex-mma-eval-all-ask ()
  "Optionally evaluate all cells."
  (interactive)
  (tex-mma-eval nil t))

(defun tex-mma-eval-all-noask ()
  "Evaluate all cells."
  (interactive)
  (tex-mma-eval nil nil))

(defun tex-mma-eval-init-ask ()
  "Optionally evaluate all initialization cells."
  (interactive)
  (tex-mma-eval "\\[\\* Initialization Cell \\*\\]" t))

(defun tex-mma-eval-init-noask ()
  "Evaluate all initialization cells."
  (interactive)
  (tex-mma-eval "\\[\\* Initialization Cell \\*\\]" nil))

(defun tex-mma-create-cell ()
  "Insert a cell."
  (interactive)
  (let (type)
    (if (not tex-mma-new-types)
	(setq type "mathematica")
      (setq type (completing-read
		  (concat "Cell type (default " tex-mma-cell-default "): ")
		  tex-mma-cell-alist nil t nil))
      (if (string-equal type "")
	  (setq type tex-mma-cell-default)))
    (setq tex-mma-cell-default type)
    (tex-mma-create-cell-type type)))

(defun tex-mma-create-cell-type (type)
  "Insert TYPE cell in buffer."
  (if (tex-mma-cell-p type)
      (error "Cell already exists")
    (if (not (bolp))
	(progn
	  (open-line 1)
	  (forward-line 1)))
    (insert (concat "\\begin{" type "}\n\n\\end{" type "}\n"))
    (beginning-of-line)
    (previous-line 2)))

(defun tex-mma-forward-cell ()
  "Move to next cell.
Return type of cell, else return nil."
  (interactive)
    (let ((cur-pos (point))
	  (list tex-mma-defined-cells)
	  (cell-pos (point-max))
	  cell-type
	  type)
      (while (setq type (car list))
	(setq list (cdr list))
	(setq new-pos (tex-mma-next-cell-start type))
	(if (not (equal new-pos cur-pos))
	    (if (> new-pos cell-pos)
		nil
	      (setq cell-pos new-pos)
	      (setq cell-type type))))
      (if (equal cell-pos (point-max))
	  nil; No more cells
	(goto-char cell-pos)
	cell-type)))

(defun tex-mma-backward-cell ()
  "Move to previous cell.
Return type of cell found, else return nil."
  (interactive)
    (let ((cur-pos (point))
	  (list tex-mma-defined-cells)
	  (cell-pos (point-min))
	  cell-type
	  type)
      (while (setq type (car list))
	(setq list (cdr list))
	(setq new-pos (tex-mma-previous-cell-start type))
	(if (not (equal new-pos cur-pos))
	    (if (< new-pos cell-pos)
		nil
	      (setq cell-pos new-pos)
	      (setq cell-type type))))
      (if (equal cell-pos (point-min))
	  nil ; No more cells
	(goto-char cell-pos)
	cell-type)))

(defun tex-mma-eval (kind ask)
  "Optionally evaluate all KIND cells.
If ASK is non-nil, then ask whether each KIND cell is to be evaluated,
else evaluate each KIND cell.  If KIND is nil, evaluate all cells."
  (let (type bypass display-start display-end cur-pos)
    (save-excursion
      (goto-char (point-min))
      (while (setq type (tex-mma-forward-cell))
	(if (tex-mma-reference-p type)
	    (setq bypass t) ; Skip cells that refer to other cells
	  (forward-line -1)
	  (if (not (looking-at (concat "^\\\\begin\{" type "\}" kind)))
	      (progn
		(forward-line 1) ; Don't want the same cell next time
		nil) ; Wrong kind of cell
					; We have a cell of the right kind
	    (setq display-start (point))
	    (goto-char (tex-mma-cell-end type))
	    (forward-line 1) ; We need to include cell trailer in narrowed region
	    (end-of-line)    ; ..
	    (setq display-end (point))
	    (forward-line 0)
	    (unwind-protect
		(progn
		  (tex-mma-recenter)
		  (narrow-to-region display-start display-end)
		  (goto-char (point-min))
		  (recenter 1) ; force display, just in case...
		  (forward-line 1)
		  (if (and ask (not (y-or-n-p "Evaluate this cell? ")))
		      t
		    (tex-mma-update)
		    (tex-mma-show)))
	      (widen) ; If user aborts evaluation at prompt
	      ) ; unwind-protect
	    ) ; if in a valid cell
	  ) ; if in a cell without references
	) ; while still types to check

      ) ; save-excursion
    (widen)
    (beep)
    (if bypass
	(message
    "Evaluation of cells finished (cells containing cell references were bypassed)")
      (message "Evaluation of cells finished"))
    ) ; let
  )

(defun tex-mma-do-completion ()
  (interactive)
  (let ((type (tex-mma-cell-type)))
    (if (and type (tex-mma-insert-complete-name type))
	t ; Cell filename or partname completion
      ; Mathematica completion
      (tex-mma-math-start-process)
      (math-complete-symbol))))

(defun tex-mma-cell-start (type)
  "Return position of start of cell of TYPE containing point."
  (let ((begin-re (concat "^\\\\begin\{" type "\}")))
  (save-excursion
    (if (not (looking-at begin-re))
	(re-search-backward begin-re))
    (forward-line 1)
    (point))))

(defun tex-mma-cell-end (type)
  "Return position of end of cell of TYPE containing point."
  (let ((end-re (concat "^\\\\end\{" type "\}")))
    (save-excursion
      (re-search-forward end-re)
      (forward-line -1)
      (end-of-line)
      (point))))

(defun tex-mma-previous-cell-start (type)
  "Get start of preceding cell of TYPE.  If none, return current position."
  (let ((cur-pos (point))
	(start nil)
	(begin-re (concat "^\\\\begin\{" type "\}"))
	(end-re (concat "^\\\\end\{" type "\}")))
    (save-excursion
      (if (not (re-search-backward end-re (point-min) t))
	  cur-pos
	(if (tex-mma-cell-p type)
	    (progn
	      (re-search-backward begin-re)
	      (forward-line 1)
	      (point))
	  cur-pos)))))
	      
(defun tex-mma-next-cell-start (type)
  "Get start of next cell of TYPE.  If none, return current position."
  (let ((cur-pos (point))
	(start nil)
        (begin-re (concat "^\\\\begin\{" type "\}"))
        (end-re (concat "^\\\\end\{" type "\}")))
    (save-excursion
      (if (re-search-forward begin-re (point-max) t)
	  (progn
	    (if (not (tex-mma-cell-p type))
		cur-pos)
	    (forward-line 1)
	    (point))
	cur-pos))))

(defun tex-mma-cell-p (type)
  "Returns t if point is in a TeX/Mathematica cell of type, else returns nil."
  (let ((begin-re (concat "^\\\\begin\{" type "\}"))
	(end-re (concat "^\\\\end\{" type "\}"))
	found)
    (catch 'done
      (save-excursion
	(if (re-search-backward begin-re (point-min) t)
	    (setq found (point))
	  (throw 'done nil))) ; No \begin{...}
      (save-excursion
	(if (re-search-backward end-re found t)
	    (throw 'done nil))) ; Intervening \end{...}
      (save-excursion
	(if (re-search-forward end-re (point-max) t)
	    (setq found (point))
	  (throw 'done nil))) ; No \end{...}
      (save-excursion
	(if (re-search-forward begin-re found t)
	    (throw 'done nil) ; Intervening \begin{...}
	  (throw 'done t)))))) ; In a cell
	
(defun tex-mma-delete-output (type)
  "Delete current TYPE output (if any).  Assumes point in TYPE cell.
Output assumed to follow input, separated by a blank line.
Input may not contain blank lines."
  (let ((out-start (tex-mma-output-p type)))
    (if out-start
	(delete-region out-start (tex-mma-cell-end type))
      t)))

(defun tex-mma-output-p (type)
  "Return start of TYPE output text if present, else return nil.
Assumes point in TYPE cell.
Output assumed to follow input, separated by a blank line.
Input may not contain blank lines."
  (save-excursion
    (goto-char (tex-mma-cell-start type))
    (if (re-search-forward "^\n" (tex-mma-cell-end type) t)
	(progn
	  (forward-line -2)
	  (end-of-line)
	  (point))
      nil)))

;;;; TeX/Mathematica functions for package assembly

(defun tex-mma-assemble-cell ()
  "Assemble references in cell."
  (interactive)
  (let ((type (tex-mma-cell-type))
	files parts file part)
    (if (not type) (error "Not in a cell"))
    (if (not (tex-mma-reference-p type)) (error "Cell contains no references"))
    (save-excursion
      (goto-char (tex-mma-cell-start type))
      (forward-line -1)
      (if (not (looking-at (concat "^\\\\begin{" type "}.*<.*:.*>")))
	  (error "Cell is not marked"))
      (setq files (tex-mma-get-filenames type))
      (setq file (tex-mma-get-filename files))
      (if (not file) (error "Ambiguous filename"))
      (setq parts (tex-mma-get-partnames type file files))
      (setq part (tex-mma-get-partname parts))
      (if  (not part) (error "Ambiguous partname"))
      ) ; save-excursion
    (setq cell-file file)
    (if (not (equal part "")) (setq cell-file (concat cell-file ":" part)))
    (if (bufferp (get-buffer file))
	(progn
	  (setq prompt
		(concat
		 "Cell buffer `"
		 cell-file
		 "' exists. Overwrite it? "))
	  (if (not (y-or-n-p prompt))
	      (error "Cell assembly cancelled"))))
    (message "Assembling cell `%s' ..." cell-file) ; (sleep-for 3)
    (tex-mma-assemble-package-type type file files parts part)
    ) ; let
  )

(defun tex-mma-assemble-package (&optional this-type this-file overwrite)
  "Assemble text into a package.
Optional arguments (useful for batch processing)
THIS-TYPE cells;
THIS-FILE package filename;
OVERWRITE, if not nil package filename buffer will be overwritten without asking."
  (interactive)
  (let (type file files parts prompt)
    (if this-type
	(setq type this-type)
      (if (not tex-mma-new-types)
	  (setq type "mathematica")
	(setq type (completing-read
		    (concat "Package type (default " tex-mma-cell-default ": ")
		    tex-mma-cell-alist nil t nil))
	(if (string-equal type "")
	    (setq type tex-mma-cell-default))))
    (setq tex-mma-cell-default type)
    (setq files (tex-mma-get-filenames type))
    (if (not files) (error "No complete package filenames found"))
    (if this-file
	(progn
	  (if (assoc this-file files)
	      (setq file this-file)
	    (error "Package filename not found: `%s'" this-file)))
      (if (equal 1 (length files))
	  (setq file (car (car files)))
	(setq file
	      (completing-read
	       "Package filename (<space> to see filenames): "
	       files nil t nil))))
    (if (equal "" file)
	(error "No package filename specified"))
    (setq parts (tex-mma-get-partnames type file files))
    (if overwrite
	t ; Overwrite file if it necessary
      (if (bufferp (get-buffer file))
	  (progn
	    (setq prompt (concat
			  "Package buffer `"
			  file
			  "' exists. Overwrite it ? "))
	    (if (not (y-or-n-p prompt))
	      (error "Package assembly cancelled")))))
    (message "Assembling package filename `%s' ..." file) ;(sleep-for 1)
    (tex-mma-assemble-package-type type file files parts)
    ))

(defun tex-mma-assemble-package-type (type file files parts &optional part)
  "Assemble TYPE cells marked with filename FILE in buffer FILE,
which is overwritten if it exists. Alists FILES and PARTS are
used for name completion (see `tex-mma-get-filename' and `tex-mma-get-partname').
If optional string PART is non-nil, then assembles cells FILE:PART in buffer
FILE:PART instead."
  (let ((home-buffer (current-buffer))
	cell-file cell-found
	this-part this-file part-file part-files cell-type problem)
    (setq tex-mma-error-point (point)) ; Go here if no error
    (if (not part) (setq part ""))
    (if (equal part "")
	(setq cell-file file)
      (setq cell-file (concat file ":" part)))
    (if (get-buffer cell-file) ; Start fresh
	(save-excursion
	  (set-buffer cell-file)
	  (erase-buffer)
	  (set-buffer home-buffer)))
    (unwind-protect ; So part files will be deleted even if errors or abort
	(progn
	  (save-excursion
	    (goto-char (point-min))
	    (while (setq cell-type (tex-mma-forward-cell))
	      (if (not (equal type cell-type))
		  nil ;
		(forward-line -1) ; Move to \begin{...
		(if (not (looking-at (concat "^\\\\begin{" type "}.*<.*:.*>")))
		    (forward-line 1) ; So we go to next cell next time through
		  ; We have a marked cell
		  (setq this-file (tex-mma-get-filename files))
		  (cond
		   ((not this-file)
		    (setq tex-mma-error-point (point))
		    (error "Ambiguous filename"))
		   ((not (equal file this-file))
		    (forward-line 1)) ; So we go to next cell next time through
		   (t ; We have a cell of the right package filename
		    (setq this-part (tex-mma-get-partname parts))
		    (cond
		     ((not this-part)
		      (setq tex-mma-error-point (point))
		      (error "Ambiguous partname"))
		     (t
		      (forward-line 1) ; Move into cell
		      (if (equal this-part part)
			  (progn
			    (setq part-file cell-file)
			    (setq cell-found t))
			(setq part-file (concat file ":" this-part))
			(if (not (tex-mma-string-mem part-file part-files))
			    (setq part-files (cons part-file part-files))))
		      ) ; proper partname
		     ) ; cond
		    (tex-mma-append-cell-to-buffer type part-file)
		    ) ; if cell marked with requested filename
		   ) ; cond
		  ) ; if a marked cell
		) ; if a TYPE cell
	      ) ; while still cells to process
	    ) ; save excursion
	  (if (not cell-found)
	      (error "No `%s' package cell `%s:%s' found" type file part)
	    (while (tex-mma-dereference-buffer cell-file files parts))
	    ) ; if parts found
	  ; Package assembled.  Make it visible and move to its start
	  (switch-to-buffer-other-window cell-file)
	  (tex-mathematica)
	  (setq tex-mma-error-point (point-min))
	  (message "`%s' assembled" cell-file)
	  ) ; progn
      ; unwind-protect forms:  Delete part this-files
      (while (setq part-file (car part-files))
	(setq part-files (cdr part-files))
	(condition-case nil ; In case a buffer was not actually created
	    (kill-buffer part-file)
	  (error nil)))
      (goto-char tex-mma-error-point)
      ) ; unwind-protect
    ) ; let
  ) ; done

(defun tex-mma-append-cell-to-buffer (type buffer)
  "Append text of TYPE cell containing point to BUFFER."
  (if (not (tex-mma-cell-p type))
      (error "Not in a cell.")
    (let ((home-buffer (current-buffer))
	  (start (tex-mma-cell-start type))
	  (end (tex-mma-cell-end type)))
      (save-excursion
	(set-buffer (get-buffer-create buffer))
	(goto-char (point-max))
	(insert-buffer-substring home-buffer start end)
	(insert "\n")
	))))

(defun tex-mma-dereference-buffer (buffer files parts &optional buffers)
  "Resolve all references in BUFFER using FILES and PARTS for name completion.
If optional BUFFERS is nil, initialize global variable `tex-mma-dereference-path'
with BUFFER. If BUFFERS is non-nil, add BUFFER to `tex-mma-dereference-path'.
Use `tex-mma-dereference-path' to check for self-reference and report error
if detected,"
  (let ((ref-found nil)
	(home-buffer (current-buffer))
	path-to-here
	ref-indent ref-buf)
    (set-buffer buffer)
    (goto-char (point-min))
    (if buffers
	t
      (setq buffers t)
      (setq tex-mma-dereference-path (list buffer))
      )
    (setq path-to-here tex-mma-dereference-path)
    (while (re-search-forward "^ *\t*<[^:].*:[^>].*>$" (point-max) t)
      (setq re-found 1)
      (beginning-of-line)
      (setq ref-indent (tex-mma-get-reference-indentation))
      (setq ref-buf
	    (concat
	     (tex-mma-get-filename files)
	     ":"
	     (tex-mma-get-partname parts)))
      (if (tex-mma-string-mem ref-buf path-to-here)
	  (tex-mma-dereference-error (cons ref-buf path-to-here)))
      (setq tex-mma-dereference-path (cons ref-buf path-to-here))
      (while (tex-mma-dereference-buffer ref-buf files parts buffers))
      (kill-line 1) ; Remove reference line
      (insert-buffer ref-buf)
      (if ref-indent (indent-rigidly (point) (mark) ref-indent))
      )
    (setq tex-mma-dereference-path path-to-here)
    (set-buffer home-buffer)
    ref-found))

(defun tex-mma-dereference-error (path)
  "Report package self-reference error, in PATH"
  (let ((cell (car path))
	(home-buffer (current-buffer))
	to-cell from-cell)
    (setq to-cell cell)
    (with-output-to-temp-buffer "*Help*" (message ""))
    (pop-to-buffer "*Help*")
    (insert "Self-reference detected assembling TeX/Mathematica cell\n\n")
    (insert (concat "\t\t" to-cell "\n\n"))
    (insert "Here is how the self-reference happened:\n\n")
    (setq path (reverse path))
    (setq from-cell (car path))
    (insert (concat "\t" from-cell "\n"))
    (while (setq path (cdr path))
      (setq to-cell (car path))
      (if (equal cell to-cell)
	  (insert (concat " !!! ->\t   -->\t" to-cell "\n"))
	(insert (concat "\t   -->\t" to-cell "\n")))
      (setq from-cell to-cell)
      )
    (pop-to-buffer home-buffer)
    (error "Self-reference detected")
    ))

(defun tex-mma-get-reference-indentation ()
  "Return indentation of reference on current line.
Line assumed tabified."
  (let (start end)
    (save-excursion
      (beginning-of-line)
      (setq start (point))
      (search-forward "<")
      (untabify start (point))
      (setq end (point))
      (beginning-of-line)
      (tabify (point) end)
      (- end start 1) ; -1 since search places point after `>'
      )))

(defun tex-mma-insert-complete-name (type)
  "Insert complete name in buffer, for cell of type TYPE.
Return t if successful, else nil."
  (interactive)
  (let ((here (point))
	start end name text files parts
	)
    (save-excursion
      (beginning-of-line)
      (cond
       ((and ; partname
	 (or
	  (re-search-forward (concat "^\\\\begin{" type "}<.*:[^\t]*") here t)
	  (re-search-forward (concat "^[ \t]*<.*:[^\t]*") here t))
	 (equal here (point)))
	(if (not (setq files (tex-mma-get-filenames type)))
            (error "No package filenames in document"))
	(search-backward "<")
        (forward-char 1)
        (setq start (point))
	(search-forward ":")
	(forward-char -1)
        (setq text (buffer-substring start (point)))
	(if (not (setq name (tex-mma-complete-name text files)))
            (error "No matching package filename found"))
	(setq parts (tex-mma-get-partnames type name files))
	(forward-char 1)
        (setq start (point)) ; New start, for partname deletion
	(setq text (buffer-substring (point) here))
	(if (not (setq name (tex-mma-complete-name
			     (concat text "...")
			     parts)))
            (error "No matching package partname found"))
        (cond
         ((equal t name) ; Text is complete
          (setq name text)
          )
         ((equal t (try-completion name parts)) ; Completion is exact
          )
         (t ; Else, get completion
          (setq name
                (completing-read
                 "Partname (<space> to see partnames): "
                 parts nil t name))
          )
         ) ; cond: what kind of partname completion was done
        (delete-region start here)
        (insert (concat name ">"))
	) ; End of partname completion
       ((and ; filename
	 (or (re-search-forward (concat "^\\\\begin{" type "}<[^ \t]*") here t)
	     (re-search-forward "^[ \t]*<[^ \t]*" here t))
	 (equal here (point)))
	(if (not (setq files (tex-mma-get-filenames type)))
	    (error "No package filenames in document"))
	(re-search-backward "<")
	(forward-char 1)
	(setq start (point))
	(setq text (buffer-substring start here))
	(if (not (setq name (tex-mma-complete-name
			     (concat text "...") ; completion form
			     files)))
	    (error "No matching package filename found"))
	(cond
	 ((equal t name) ; Text is complete
	  (setq name text)
	  )
	 ((equal t (try-completion name files)) ; Completion is exact
	  )
	 (t ; Else, get completion
	  (setq name
		(completing-read
		 "Filename (<space> to see filenames): "
		 files nil t name))
	  (if (equal "" name) (error "")) ; No response means no completion
	  )
	 ) ; cond: what kind of filename completion was done
	(delete-region start here)
	(insert (concat name ":"))
	) ; End of filename completion
       (t
	(error "Nothing to complete")
	)
       ) ; cond: what kind of completion to do
      ) ; save-excursion
    (if (not name)
	nil
      (goto-char (+ (point) (length name) 1))
      t)))

(defun tex-mma-get-filenames (type)
  "Return alist of package filenames for cells of type TYPE."
  (let (cell-type file files)
    (save-excursion
      (goto-char (point-min))
      (while (setq cell-type (tex-mma-forward-cell))
	(if (not (equal type cell-type))
	    nil
	  (forward-line -1)
	  (if (not (looking-at (concat "^\\\\begin{" type "}.*<.*>")))
	      (forward-line 1) ; Cell not marked.  Get set for next one
	    (if (setq file (tex-mma-get-filename)) ; Only unabbreviated names
		(if files
		    (if (assoc file files)
			nil ; already only
		      (setq files (cons (list file) files))) ; Add to alist
		  (setq files (list (list file))))) ; Start alist
	    (forward-line 1)
	    ) ; if a marked cell
	  ) ; if cell of type TYPE
	) ; while cell to look at
      ) ; save-excursion
    files
    )) ; let and done

(defun tex-mma-complete-name (text alist &optional exact)
  "Get full name corresponding to TEXT.
If text is a string ending in `...',
then the substring preceding the `...' is used with try-completion on ALIST.
An exact match is required if optional EXACT is t.
If text is just `...' and alist is length 1, then the car of its single element
is returned.
Oherwise nil is returned."
  (let (name try-name)
    (if (not (string-match "\\(\\.\\.\\.$\\)" text))
	(setq name text) ; don't do completion on full names
      (if (and
	   (eq 0 (match-beginning 1)) ; just "..."
	   (eq 1 (length alist))) ; a single package filename
	  (setq name (car (car alist)))
	(setq try-name (substring text 0 (match-beginning 1)))
	(setq name (try-completion try-name alist)))
      (cond
       ((equal t name)
	(setq name try-name))
       ((and
	 exact
	 (not (equal t (try-completion name alist))))
	(setq name nil)))) ; Not an exact match, so error
    name))

(defun tex-mma-get-partnames (type file files)
  "Return alist of partnames for TYPE package FILE, using FILES for
filename completion."
  (let (cell-end cell-type cell-file part parts)
    (setq tex-mma-error-point (point))
    (unwind-protect
	(save-excursion
	  (goto-char (point-min))
	  (while (setq cell-type (tex-mma-forward-cell))
	    (if (not (equal type cell-type))
		nil ; Wrong type
	      (setq cell-end (tex-mma-cell-end type))
	      (forward-line -1)
	      (if (not (looking-at
                       "^\\\\begin{.+}.*<[^:].*:.*>"))
		  (forward-line 1) ; Not a marked cell
		(setq cell-file (tex-mma-get-filename files))
		(if (not (equal file cell-file))
		    (forward-line 1) ; Wrong file
		  (while (and
			  (<= (point) cell-end)
			  (or
			   (re-search-forward
			    "^\\\\begin{.+}.*<[^:].*:.*>" cell-end t)
			   (re-search-forward
			    "^ *\t*<[^:].*:.*>" cell-end t)))
		    (beginning-of-line) ; We have a filename-partname reference
		    (if (not (setq file (tex-mma-get-filename files)))
 			(progn
			  (setq tex-mma-error-point (point))
			  (error "Ambiguous filename")))
		    (if (not (equal cell-file file))
			(progn
			  (setq tex-mma-error-point (point))
			  (error "Reference must match cell filename: `%s'"
				 cell-file)))
		    (setq part (tex-mma-get-partname))
		    (if (not part)
			nil ; Need full (unabbreviated) parts only, for alist
		      (if parts ; Update alist
			  (if (or
			       (equal part "")
			       (tex-mma-string-mem part parts))
			      nil; already on list
			    (setq parts (append (list part) parts))) ; Add to alist
			(if (not (equal part ""))
			    (setq parts (list part)))) ; Create alist
		      ) ; if an unabbreviated part		      
		    (forward-line 1)
		    ) ; while references to process in this cell
		  ) ; if a marked cell of this FILE
		) ; if a marked cell
	      ) ; if a TYPE cell
	    ) ; while cells to process
	  ); save-excursion
      (goto-char tex-mma-error-point) ; unwind-protect form
      ) ; unwind-protect
    (setq parts (mapcar 'list parts)) ; Make list into an alist
    parts
    ) ; let
  ) ; done

(defun tex-mma-get-filename (&optional alist)
  "Get filename in package reference on current line.
If optional ALIST is supplied, use it for name completion.
Return nil if no name or error in name."
  (let ((match-re "\\(<\\)[^:]*\\(:\\)")
	(abbrev-re "\\.\\.\\.")
	beg text)
    (save-excursion
      (beginning-of-line)
      (setq beg (point))
      (end-of-line)
      (setq text (buffer-substring beg (point)))
      (string-match match-re text)
      (setq text (substring text (+ 1 (match-beginning 1)) (+ -1 (match-end 2))))
      ) ; save excursion
    (if alist
	(tex-mma-complete-name text alist t)
      ; if no alist, then only full filenames allowed
      (if (string-match abbrev-re text)
	  nil
	text))))

(defun tex-mma-get-partname (&optional alist)
  "Get partname in package reference on current line.
If optional ALIST is supplied, use it for name completion.
Return nil if no name or error in name."
  (let ((match-re "\\(:\\)\\([^>]*\\)")
	(abbrev-re "\\.\\.\\.")
	beg text)
    (save-excursion
      (beginning-of-line)
      (setq beg (point))
      (end-of-line)
      (setq text (buffer-substring beg (point)))
      (string-match match-re text)
      (setq text (substring text (+ 1 (match-beginning 1)) (match-end 2)))
      ) ; save excursion
    (if alist
	(tex-mma-complete-name text alist t)
      ; if no alist, then only full filenames allowed
      (if (string-match abbrev-re text)
	  nil
	text))))

(defun tex-mma-string-mem (element list) ; memq doesn't work for strings
  "Returns t if string ELEMENT is in LIST of strings, else returns nil."
  (let (try)
    (catch 'done
      (while (setq try (car list))
	(setq list (cdr list))
	(if (equal element try)
	    (throw 'done t)))
      nil)))

(defun tex-mma-reference-p (type)
  "Return t if TYPE cell contains a cell reference, else retrun nil."
  (save-excursion
    (goto-char (tex-mma-cell-start type))
    (if (re-search-forward "^ *\t*<[^:].*:[^>].*>$" (tex-mma-cell-end type) t)
	t
      nil)))

;;;; TeX/Mathematica functions for "mathematica" cells

(defun tex-mma-math-start-process ()
  "Start up Mathematica in math mode, if necessary."
  (if (tex-mma-math-start-process-p)
      nil
    (let ((home-buffer (current-buffer)))
      (math)
      ; Wait until Mathematica startup is done
      (while (not (looking-at "^In\\[[0-9]+\\]:=\\s *$"))
	(sit-for 1)
	(goto-char (point-max))
	(beginning-of-line))
      (goto-char (point-max))
      (pop-to-buffer home-buffer)))
  (message ""))

(defun tex-mma-math-start-process-p ()
  (if (get-buffer-process tex-mma-process-buffer)
      t
    nil))

(defun tex-mma-math-buffer-go ()
  "Go to Mathematica buffer."
  (interactive)
  (tex-mma-math-start-process)
  (tex-mma-math-pop-to-buffer))

(defun tex-mma-math-send ()
  "Send Mathematica statements containing point to Mathematica subshell.
Statements are delimited by blank lines."
  (interactive)
  (tex-mma-math-start-process)
  (let ((start (tex-mma-math-start))
	(end (tex-mma-math-end))
	(home-buffer (current-buffer)))
    (tex-mma-math-pop-to-buffer)
    (goto-char (point-max))
    (insert-buffer-substring home-buffer start end)))

(defun tex-mma-math-send-cell ()
  "Send input to Mathematica.  Point must be in a cell.
Input is scaned for syntax errors, using check-math-syntax."
  (interactive)
  (if (not (tex-mma-cell-p "mathematica"))
      (error "Not in Mathematica cell"))
  (if (tex-mma-reference-p "mathematica")
      (error (concat
	      "References in cell.  Assemble with "
	      (substitute-command-keys "\\[tex-mma-assemble-cell]"))))
  (tex-mma-math-start-process)
  (let ((home-buffer (current-buffer))
	start end)
    (save-excursion
      (goto-char (tex-mma-cell-start "mathematica"))
      (setq start (point))
      (if (not (setq end (tex-mma-output-p "mathematica")))
	  (setq end (tex-mma-cell-end "mathematica"))))
    (set-syntax-table math-mode-syntax-table) ; Run math-mode syntax checks
    (check-math-syntax start end)             ; ..
    (set-syntax-table TeX-mma-mode-syntax-table)  ; ..
    (tex-mma-math-pop-to-buffer)
    (goto-char (point-max))
    (insert-buffer-substring home-buffer start end)
    (setq math-send-filter-active t)
    (unwind-protect
	(unwind-protect
	    (progn
	      (math-send-input)
	      (while math-send-filter-active
		(sit-for 1) ; Let Mathematica process filter work
		))
	  ;; math-send-input unwind-protect tail
	  (save-excursion
	    (if (not (memq math-send-filter-status '(normal blank-line-added)))
		(error "Problem in math-send..."))))
      ;; unwind-protect tail
      (if (memq math-send-filter-status '(normal blank-line-added premature-output))
	  (pop-to-buffer home-buffer) ; Return to TeX buffer
	)                             ; .. else stay in Mma buffer to fix error
      )))

(defun tex-mma-math-replace ()
  "Replace output (if any) with last Mathematica result. Point must be in a cell.
Output assumed to follow input, separated by a blank line."
  (interactive)
  (if (not (tex-mma-cell-p "mathematica"))
      (error "Not in Mathematica cell"))
  (tex-mma-math-start-process)
  (save-excursion
    (tex-mma-delete-output "mathematica")
    (tex-mma-math-get-output)
    ))

(defun tex-mma-math-update ()
  "Send input to Mathematica and optionally replace output with result.
Point must be in cell.  Output assumed to follow input, separated by a blank line."
  (interactive)
  (if (not (tex-mma-cell-p "mathematica"))
      (error "Not in Mathematica cell"))
  (tex-mma-math-send-cell)
  ; We need to wait for Mathematica's output anyhow, so we might as well ask
  (if (y-or-n-p "Replace output? ")
      (tex-mma-math-replace)
    t))

(defun tex-mma-math-show ()
  "Make last Mathematica output visible."
  (interactive)
  (tex-mma-math-start-process)
  (let ((home-buffer (current-buffer)))
    (tex-mma-math-pop-to-buffer)
    (goto-char (point-max))
    (recenter)
    (pop-to-buffer home-buffer)))

(defun tex-mma-math-find-error ()
  "Go to error in .m file reported in Mathematica buffer."
  (interactive)
  (tex-mma-math-buffer-go)
  (find-math-error))

(defun tex-mma-math-recenter ()
  "Place shell-mma-buffer input prompt at top of screen."
  (interactive)
  (tex-mma-math-start-process)
  (let ((home-buffer (current-buffer)))
    (tex-mma-math-pop-to-buffer)
    (goto-char (point-max))
    (recenter 0)
    (pop-to-buffer home-buffer)))

(defun tex-mma-math-pop-to-buffer ()
  (pop-to-buffer (process-buffer (get-process tex-mma-process-name))))

(defun tex-mma-math-get-output ()
  "Insert last output from Mathematica.
Assumes point in cell.  Output inserted at end of cell."
  (tex-mma-math-start-process)
  (let ((tex-mma-process (get-process tex-mma-process-name)))
    (let ((tex-mma-buffer (process-buffer tex-mma-process))
	  (home-buffer (current-buffer))
	  out-start out-end)
      (pop-to-buffer tex-mma-buffer)
      (goto-char math-last-input-end) ; First line of Mathematica output
      (beginning-of-line)	 ; ..
      (forward-line 1)		 ; ..
      (setq out-start (point))	 ; ..
      (goto-char (point-max))	 ; Last line
      (beginning-of-line)	 ; .. exclude next In[..]:= prompt
      (setq out-end (point))
      (goto-char (point-max))	 ; Leave point at next In[..]:= prompt
      (pop-to-buffer home-buffer)
                                 ; Insert output before \end{mathematica}
      (goto-char (tex-mma-cell-end "mathematica"))
      (forward-line 1)		 ; Insert blank line before output
      (open-line 2)		 ; ..
      (forward-line 1)		 ; ..
      (insert-buffer-substring tex-mma-buffer out-start out-end)
      (beginning-of-line)	 ; Delete blank lines at end of output
      (previous-line 1)		 ; ..
      (kill-line 2)		 ; ..
      )))

(defun tex-mma-math-start ()
  "Return position of start of text containing point.
Statement is delimited by blank lines (or start/end of buffer)."
  (save-excursion
    (beginning-of-line)
    (while (and
	    (not (looking-at "\n"))
	    (not (equal (point-min) (point))))
      (forward-line -1))
    (if (looking-at "\n")
	(forward-line 1)
      t)
    (point)))

(defun tex-mma-math-end ()
  "Return position of end of text containing point.
Statement is delimited by blank lines (or start/end of buffer)."
  (save-excursion
    (beginning-of-line)
    (while (and
	    (not (looking-at "\n"))
	    (not (equal (point-max) (point))))
      (forward-line 1))
    (if (looking-at "\n")
	(forward-line -1)
      (progn
	(forward-char -1)
	(if (looking-at "\n")
	    t
	  (forward-char 1))))
    (end-of-line)
    (point)))

;;;; tex-mma-mode

;; This is a modified interface to tex-mode

(defvar TeX-mma-default-mode 'plain-TeX-mma-mode
  "*Mode to enter for a new file when it can't be determined whether
the file is plain TeX or LaTeX or what.")

(defvar TeX-mma-mode-syntax-table nil
  "Syntax table used while in TeX mode.")

(defvar TeX-mma-mode-map nil "Keymap for TeX-mma mode")

(if TeX-mma-mode-map 
    nil
  (setq TeX-mma-mode-map (make-sparse-keymap))
  (TeX-define-common-keys TeX-mma-mode-map)
  (define-key TeX-mma-mode-map "\"" 'TeX-insert-quote)
  (define-key TeX-mma-mode-map "\C-c\C-[" 'tex-mma-backward-cell)
  (define-key TeX-mma-mode-map "\C-c\C-]" 'tex-mma-forward-cell)
  (define-key TeX-mma-mode-map "\C-c\C-b" 'TeX-buffer)
  (define-key TeX-mma-mode-map "\C-c\C-e" 'tex-mma-math-find-error)
  (define-key TeX-mma-mode-map "\C-c\C-f" 'TeX-close-LaTeX-block)
  (define-key TeX-mma-mode-map "\C-c\C-r" 'TeX-region)
  (define-key TeX-mma-mode-map "\C-c\r" 'tex-mma-send)
  (define-key TeX-mma-mode-map "\C-c\t" 'tex-mma-toggle-init)
  (define-key TeX-mma-mode-map "\C-ca" 'tex-mma-eval-all-ask)
  (define-key TeX-mma-mode-map "\C-cc" 'tex-mma-create-cell)
  (define-key TeX-mma-mode-map "\C-ch" 'tex-mma-info)
  (define-key TeX-mma-mode-map "\C-ci" 'tex-mma-eval-init-ask)
  (define-key TeX-mma-mode-map "\C-cl" 'tex-mma-recenter)
  (define-key TeX-mma-mode-map "\C-cm" 'tex-mma-assemble-package)
  (define-key TeX-mma-mode-map "\C-cr" 'tex-mma-replace)
  (define-key TeX-mma-mode-map "\C-cs" 'tex-mma-show)
  (define-key TeX-mma-mode-map "\C-cu" 'tex-mma-update)
  (define-key TeX-mma-mode-map "\C-cv" 'tex-mma-version)
  (define-key TeX-mma-mode-map "\C-hE" 'math-extra-help)
  (define-key TeX-mma-mode-map "\C-he" 'math-help)
  (define-key TeX-mma-mode-map "\C-u\C-ca" 'tex-mma-eval-all-noask)
  (define-key TeX-mma-mode-map "\C-u\C-ci" 'tex-mma-eval-init-noask)
  (define-key TeX-mma-mode-map "\C-u\C-cm" 'tex-mma-assemble-cell)
  (define-key TeX-mma-mode-map "\e\r" 'tex-mma-send-cell)
  (define-key TeX-mma-mode-map "\e\t" 'tex-mma-do-completion)
  (define-key TeX-mma-mode-map "\eo" 'tex-mma-buffer-go)
  (define-key TeX-mma-mode-map "\e{" 'TeX-insert-braces)
  (define-key TeX-mma-mode-map "\e}" 'up-list)
  (define-key TeX-mma-mode-map "\n" 'TeX-terminate-paragraph)
  )

(fset 'TeX-mma-mode 'tex-mma-mode)
(fset 'plain-TeX-mma-mode 'plain-tex-mma-mode)
(fset 'LaTeX-mma-mode 'latex-mma-mode)

;;; This would be a lot simpler if we just used a regexp search,
;;; but then it would be too slow.
(defun tex-mma-mode ()
  "Major mode for editing TeX/Mathematica files of input for TeX or LaTeX.
Trys to intuit whether this file is for plain TeX or LaTeX and
calls plain-tex-mma-mode or latex-mma-mode.  If it cannot be determined
\(e.g., there are no commands in the file), the value of
TeX-mma-default-mode is used."
  (interactive)
  (let (mode slash comment)
    (save-excursion
      (goto-char (point-min))
      (while (and (setq slash (search-forward "\\" nil t))
		  (setq comment (let ((search-end (point)))
				  (save-excursion
				    (beginning-of-line)
				    (search-forward "%" search-end t))))))
      (if (and slash (not comment))
	  (setq mode (if (looking-at "documentstyle")
			 'latex-mma-mode
		       'plain-tex-mma-mode))))
    (if mode (funcall mode)
      (funcall TeX-mma-default-mode))))

(defun plain-tex-mma-mode ()
  "Major mode for editing TeX/Mathematica files of input for plain TeX.

Use \\[tex-mma-info] for help on TeX/Mathematica tools.

Makes $ and } display the characters they match.
Makes \" insert `` when it seems to be the beginning of a quotation,
and '' when it appears to be the end; it inserts \" only after a \\.

Use \\[TeX-region] to run TeX on the current region, plus a \"header\"
copied from the top of the file (containing macro definitions, etc.),
running TeX under a special subshell.  \\[TeX-buffer] does the whole buffer.
\\[TeX-print] prints the .dvi file made by either of these.

Use \\[validate-TeX-buffer] to check buffer for paragraphs containing
mismatched $'s or braces.

Special commands:
\\{TeX-mma-mode-map}

Mode variables:
TeX-directory
	Directory in which to create temporary files for TeX jobs
	run by \\[TeX-region] or \\[TeX-buffer].
TeX-dvi-print-command
	Command string used by \\[TeX-print] to print a .dvi file.
TeX-show-queue-command
	Command string used by \\[TeX-show-print-queue] to show the print
	queue that \\[TeX-print] put your job on.

Entering plain-TeX-mma mode calls the value of text-mode-hook,
then the value of TeX-mma-mode-hook, and then the value
of plain-TeX-mma-mode-hook."
  (interactive)
  (TeX-mma-common-initialization)
  (setq mode-name "TeX/Mathematica")
  (setq major-mode 'plain-tex-mma-mode)
  (setq TeX-command "tex")
  (setq TeX-start-of-header "%**start of header")
  (setq TeX-end-of-header "%**end of header")
  (setq TeX-trailer "\\bye\n")
  (run-hooks 'text-mode-hook 'TeX-mma-mode-hook 'plain-TeX-mma-mode-hook))

(defun latex-mma-mode ()
  "Major mode for editing LaTeX/Mathematica files of input for LaTeX.

Use \\[tex-mma-info] for help on TeX/Mathematica tools.

Makes $ and } display the characters they match.
Makes \" insert `` when it seems to be the beginning of a quotation,
and '' when it appears to be the end; it inserts \" only after a \\.

Use \\[TeX-region] to run LaTeX on the current region, plus the preamble
copied from the top of the file (containing \\documentstyle, etc.),
running LaTeX under a special subshell.  \\[TeX-buffer] does the whole buffer.
\\[TeX-print] prints the .dvi file made by either of these.

Use \\[validate-TeX-buffer] to check buffer for paragraphs containing
mismatched $'s or braces.

Special commands:
\\{TeX-mma-mode-map}

Mode variables:
TeX-directory
	Directory in which to create temporary files for TeX jobs
	run by \\[TeX-region] or \\[TeX-buffer].
TeX-dvi-print-command
	Command string used by \\[TeX-print] to print a .dvi file.
TeX-show-queue-command
	Command string used by \\[TeX-show-print-queue] to show the print
	queue that \\[TeX-print] put your job on.

Entering LaTeX-mma mode calls the value of text-mode-hook,
then the value of TeX-mma-mode-hook, and then the value
of LaTeX-mma-mode-hook."
  (interactive)
  (TeX-mma-common-initialization)
  (setq mode-name "LaTeX/Mathematica")
  (setq major-mode 'LaTeX-mma-mode)
  (setq TeX-command "latex")
  (setq TeX-start-of-header "\\documentstyle")
  (setq TeX-end-of-header "\\begin{document}")
  (setq TeX-trailer "\\end{document}\n")
  (run-hooks 'text-mode-hook 'TeX-mma-mode-hook 'LaTeX-mma-mode-hook))

(defun TeX-mma-common-initialization ()
  (kill-all-local-variables)
  (use-local-map TeX-mma-mode-map)
  (setq local-abbrev-table text-mode-abbrev-table)
  (if (null TeX-mma-mode-syntax-table)
      (progn
	(setq TeX-mma-mode-syntax-table (make-syntax-table))
	(set-syntax-table TeX-mma-mode-syntax-table)
	(modify-syntax-entry ?\\ ".")
	(modify-syntax-entry ?\f ">")
	(modify-syntax-entry ?\n ">")
	(modify-syntax-entry ?$ "$$")
	(modify-syntax-entry ?% "<")
	(modify-syntax-entry ?\" ".")
	(modify-syntax-entry ?& ".")
	(modify-syntax-entry ?_ ".")
	(modify-syntax-entry ?@ "_")
	(modify-syntax-entry ?~ " ")
	(modify-syntax-entry ?' "w"))
    (set-syntax-table TeX-mma-mode-syntax-table))
  (make-local-variable 'paragraph-start)
  (setq paragraph-start "^[ \t]*$\\|^[\f\\\\%]")
  (make-local-variable 'paragraph-separate)
  (setq paragraph-separate paragraph-start)
  (make-local-variable 'comment-start)
  (setq comment-start "%")
  (make-local-variable 'comment-start-skip)
  (setq comment-start-skip "\\(\\(^\\|[^\\]\\)\\(\\\\\\\\\\)*\\)\\(%+ *\\)")
  (make-local-variable 'comment-indent-hook)
  (setq comment-indent-hook 'TeX-comment-indent)
  (make-local-variable 'TeX-command)
  (make-local-variable 'TeX-start-of-header)
  (make-local-variable 'TeX-end-of-header)
  (make-local-variable 'TeX-trailer)
)
