|
From: Jay B. <bel...@us...> - 2001-11-08 22:26:03
|
Update of /cvsroot/maxima/maxima-pre59/emacs In directory usw-pr-cvs1:/tmp/cvs-serv12014 Added Files: maximadoc.el Log Message: Initial commit of maximadoc mode for Emacs. --- NEW FILE: maximadoc.el --- ;; maximadoc.el Mode for interaction with Maxima from TeX buffer ;; Written 2/12/1991 by Dan Dill da...@ch... ;; Modified for Maxima by Jay Belanger ;; Copyright (C) 1991, 1993 Dan Dill (da...@ch...) ;; 1999-2001 Jay Belanger (bel...@tr...) ;; Author: Dan Dill ;; Jay Belanger ;; Maintainer: Jay Belanger <bel...@tr...> ;; $Name: $ ;; $Revision: 1.1 $ ;; $Date: 2001/11/08 22:26:00 $ ;; Keywords: maxima, maximadoc ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation; either version 2 of ;; the License, or (at your option) any later version. ;; ;; This program is distributed in the hope that it will be ;; useful, but WITHOUT ANY WARRANTY; without even the implied ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ;; PURPOSE. See the GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public ;; License along with this program; if not, write to the Free ;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, ;; MA 02111-1307 USA ;; ;; ;; Please send suggestions and bug reports to <bel...@tr...>. ;; The latest version of this package should be available at ;; ftp://vh213601.truman.edu/pub/Maxima ;; You will need, in addition to this file, ;; maxima.el, maxima-font-lock.el, maxima-symbols.el, and maximadoc.sty ;;; Commentary: ;; ;; To use this mode, you need maximadoc.el, maxima.el (and ;; maxima-font-lock.el and maxima-symbols.el for maxima.el) as well ;; as maximadoc.sty in order to typeset the resulting document. ;; The elisp files need to be put in the emacs load path, and ;; maximadoc.sty needs to be put in the TeX input path. ;; To typeset the resulting document using LaTeX, put ;; \usepackage{maximadoc} ;; in the preamble. ;; MaximaDoc mode: ;; This is a mode intended to allow the user to write documents that ;; include Maxima code. The file can be LaTeXed to produce nice ;; looking sessions (although that isn't necessary, of course), and so the ;; mode is an extension of tex-mode (AucTeX). ;; The units of Maxima code that are worked with are "cells", which are ;; delimited by "\maximadoc" and "\endmaximadoc". The cells can be ;; evaluated individually, as a group, and the results can optionally be ;; returned. Evaluating a cell and returning the session is called ;; "updating" the cell. ;; If the openining cell delimiter is followed by "[* Hide *]", and so it ;; opens with "\maximadoc[* Hide *]", then updating the cell will send ;; the contents to Maxima, but nothing is returned to the document buffer, ;; and nothing shows when the document is processed by LaTeX. ;; The commands for working with cells are: ;; C-c C-o create a cell ;; C-c C-q toggles whether or not the cell is a "hidden" cell ;; C-c C-u a update all the cells ;; C-c + go to the next cell ;; C-c - go to the previous cell ;; (With a prefix, C-u C-c C-u a will update the cells without prompting) ;; Single lines can be evaluated: ;; C-c C-u l replace the current line with Maxima session ;; C-c C-u L replace the current line with Maxima session in TeX form. ;; Within a cell, the following commands are available: ;; C-c C-d delete the cell's session ;; C-c C-u c update the cell ;; C-c C-q toggle hidden cells ;; The Maxima process can be killed with the command C-M-k. ;; Finally, the command M-x maximadoc-mark-file-as-maximadoc will insert a ;; %-*-MaximaDoc-*- at the beginning of the file (if there isn't one there ;; already) so the file will begin in maximadoc-mode next time it's opened. ;; ;;; Change Log: ;; $Log: maximadoc.el,v $ ;; Revision 1.1 2001/11/08 22:26:00 belanger ;; Initial commit of maximadoc mode for Emacs. ;; ;; Revision 1.5 2001/11/06 22:51:05 jay ;; I added some brief installation instructions. ;; ;; Revision 1.4 2001/11/06 19:27:56 jay ;; I update the internal documentation. ;; ;; Revision 1.3 2001/11/06 17:40:31 jay ;; I fixed a problem with the output display. ;; ;; Revision 1.2 2001/11/05 13:40:07 jaycvs ;; I fixed the update functions, and made a few other minor fixes. ;; ;; Revision 1.1 2001/11/04 21:04:55 jaycvs ;; Initial checkin of maximadoc.el. ;; (require 'maxima) (provide 'maximadoc) ;;;; The variables that the user may wish to change (defgroup maximadoc nil "Maxima mode" :prefix "maximadoc-" :tag "MaximaDoc") (defcustom maximadoc-use-tex 'auctex "Possible modes to use within MaximaDoc. Possible choices are 'auctex, 'tex or nil" :group 'maximadoc :type '(choice :menu-tag "TeX style" :tag "TeX style" (const auctex) (const tex) (const nil))) ;;; Other variables and constants (defconst maximadoc-session-marker "\\session" "Contents of line separating input and session portion of cell.") (defconst maximadoc-tex-string "tex(%);" "A string to send to Maxima to set session to TeX form") (defun maximadoc-mark-file-as-maximadoc () "Mark the file as an MaximaDoc buffer. The next time the file is loaded, it will then be in MaximaDoc mode" (interactive) (save-excursion (goto-line 1) (beginning-of-line) (if (looking-at ".*-\\*-MaximaDoc-\\*-") () (open-line 1) (insert "%-*-MaximaDoc-*-")))) (defun maximadoc-toggle-hide () "Toggle hidden marker of cell containing point." (interactive) (save-excursion (re-search-backward "^\\\\maximadoc") (goto-char (match-end 0)) (if (looking-at "\\[\\* Hide \\*\\]") (delete-region (match-beginning 0) (match-end 0)) (insert "[* Hide *]")))) (defun maximadoc-create-cell () "Insert cell in buffer." (interactive) (if (maximadoc-cell-p) (error "Cell already exists") (if (not (bolp)) (progn (open-line 1) (forward-line 1))) (insert "\\maximadoc\n\n\\endmaximadoc") (unless (looking-at " *$") (insert "\n") (forward-line -1)) (beginning-of-line) (previous-line 1))) (defun maximadoc-forward-cell () "Move to next cell." (interactive) (let ((cur-pos (point)) (cell-pos (point-max)) new-pos) (setq new-pos (maximadoc-next-cell-start)) (if (not (equal new-pos cur-pos)) (if (> new-pos cell-pos) nil (setq cell-pos new-pos))) (if (equal cell-pos (point-max)) nil; No more cells (goto-char cell-pos)))) (defun maximadoc-backward-cell () "Move to previous cell." (interactive) (let ((cur-pos (point)) (cell-pos (point-min)) new-pos) (setq new-pos (maximadoc-previous-cell-start)) (if (not (equal new-pos cur-pos)) (if (< new-pos cell-pos) nil (setq cell-pos new-pos))) (if (equal cell-pos (point-min)) nil ; No more cells (goto-char cell-pos)))) (defun maximadoc-update (&optional noask) "Optionally update all cells. If NOASK is non-nil, then update without confirmation at each cell." (interactive "P") (if (not noask) (setq noask (not (y-or-n-p "Interactive update? ")))) (let (bypass display-start display-end cur-pos) (save-excursion (goto-char (point-min)) (while (maximadoc-forward-cell) (forward-line -1) (setq display-start (point)) (goto-char (maximadoc-cell-end)) (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 (narrow-to-region display-start display-end) (goto-char (point-min)) (recenter 1) ; force display, just in case... (forward-line 1) (if (and (not noask) (not (y-or-n-p "Update this cell? "))) t (maximadoc-send-or-update-cell))) (widen) ; If user aborts evaluation at prompt ) ; unwind-protect ) ; while still types to check (widen) (sit-for 1) ) ; save-excursion (message (concat "Update of cells finished.")))) (defun maximadoc-cell-start () "Return position of start of cell containing point." (let ((begin-re "^\\\\maximadoc")) (save-excursion (if (not (looking-at begin-re)) (re-search-backward begin-re)) (forward-line 1) (point)))) (defun maximadoc-cell-end () "Return position of end of cell containing point." (let ((end-re "^\\\\endmaximadoc")) (save-excursion (re-search-forward end-re) (forward-line -1) (end-of-line) (point)))) (defun maximadoc-previous-cell-start () "Get start of preceding cell. If none, return current position." (let ((cur-pos (point)) (start nil) (begin-re "^\\\\maximadoc") (end-re "^\\\\endmaximadoc")) (save-excursion (if (not (re-search-backward end-re (point-min) t)) cur-pos (if (maximadoc-cell-p) (progn (re-search-backward begin-re) (forward-line 1) (point)) cur-pos))))) (defun maximadoc-next-cell-start () "Get start of next cell. If none, return current position." (let ((cur-pos (point)) (start nil) (begin-re "^\\\\maximadoc") (end-re "^\\\\endmaximadoc")) (save-excursion (if (re-search-forward begin-re (point-max) t) (progn (if (not (maximadoc-cell-p)) cur-pos) (forward-line 1) (point)) cur-pos)))) (defun maximadoc-cell-p () "Returns t if point is in a MaximaDoc cell, else returns nil." (let ((begin-re "^\\\\maximadoc") (end-re "^\\\\endmaximadoc") (found nil)) (save-excursion (if (re-search-backward begin-re (point-min) t) ; \maxima (setq found (point)))) (save-excursion (if (and found (re-search-backward end-re found t)) ; Intervening \endmaxima (setq found nil))) (save-excursion (if (and found (re-search-forward end-re (point-max) t)) ;\endmaxima (setq found (point)))) (save-excursion (if (and found (re-search-forward begin-re found t)) ; Intervening \maxima (setq found nil))) (if found t nil))) (defun maximadoc-delete-session () "Delete current session (if any). Assumes point in cell. Session assumed to follow input, separated by a maximadoc-session-marker line. Input *may* contain blank lines." (interactive) (let ((out-start (maximadoc-session-p))) (if out-start (delete-region out-start (maximadoc-cell-end)) t))) (defun maximadoc-session-p () "Return start of session text if present, else return nil. Assumes point in cell. Session assumed to follow input, separated by a \\session." (save-excursion (goto-char (maximadoc-cell-start)) (if (re-search-forward "^\\\\session" (maximadoc-cell-end) t) (progn (forward-line -1) (end-of-line) (point)) nil))) ;;; @@ MaximaDoc functions for "maxima" cells (defun maximadoc-get-cell-contents () "Return the cell contents as a string." (if (not (maximadoc-cell-p)) (message "Not in Maxima cell")) (let ((home-buffer (current-buffer)) (start) (end)) (save-excursion (goto-char (maximadoc-cell-start)) ;; Now I want to skip over any blank lines at the beginning of the cell (beginning-of-line) (while (looking-at "^ *$") (forward-line 1)) (setq start (point)) ;; as well as at the end of the cell (if (not (setq end (maximadoc-session-p))) (progn (goto-char (maximadoc-cell-end)) (while (looking-at "^ *$") (forward-line -1)) (end-of-line) (setq end (point))) (progn (goto-char end) (while (looking-at "^ *$") (forward-line -1)) (end-of-line) (setq end (point))))) (buffer-substring-no-properties start end))) (defun maximadoc-send-cell () "Send the current cell's contents to Maxima." (interactive) (if (not (maximadoc-cell-p)) (message "Not in cell.") (maximadoc-delete-session) (maxima-region-nodisplay (maximadoc-cell-start) (maximadoc-cell-end)))) (defun maximadoc-update-cell () "Send the current cell's contents to Maxima, and return the results." (interactive) (save-excursion (maximadoc-delete-session) (let ((end) (cell (maximadoc-get-cell-contents))) (goto-char (maximadoc-cell-end)) (forward-line 1) (insert "\\session\n") (while (setq end (string-match "[$;]" cell)) (maxima-string-nodisplay (substring cell 0 (1+ end))) (insert (maxima-last-input-prompt)) (while (or (string= "\n" (substring cell 0 1)) (string= " " (substring cell 0 1))) (setq cell (substring cell 1)) (setq end (- end 1))) (insert (substring cell 0 (1+ end))) (unless (string= "\n" (substring cell end (1+ end))) (insert "\n")) (insert "\n") (insert (maxima-last-output)) (setq cell (substring cell (1+ end))))))) (defun maximadoc-send-or-update-cell () "Send input to Maxima and replace session with result. Point must be in cell. Session assumed to follow input, separated by a maximadoc-session-marker line." (interactive) (if (not (maximadoc-cell-p)) (message "Not in MaximaDoc cell")) (save-excursion (re-search-backward "\\maximadoc") (goto-char (match-end 0)) (if (looking-at "\\[\\* Hide \\*\\]") (maximadoc-send-cell) (maximadoc-update-cell)))) (defun maximadoc-replace-line-with-tex () "Sends the current line to Maxima, and then replaces it with the Maxima session in TeX form." (interactive) (maxima-send-line-nodisplay) (maxima-wait) (maxima-string-nodisplay maximadoc-tex-string) (beginning-of-line) (insert "% ") (end-of-line) (newline) (insert (maxima-last-session-tex-noprompt))) (defun maximadoc-replace-line () "Sends the current line to Maxima, and then replaces it with the Maxima session." (interactive) (maxima-send-line-nodisplay) (maxima-wait) (beginning-of-line) (insert "% ") (end-of-line) (newline) (insert (maxima-last-session-noprompt))) ;;; @@ The mode ;;; First of all, I want to be able to change the keymap depending ;;; on whether the point is in a cell or not. ;;; So I need one keymap for when in a cell, and one for when ;;; not in a cell. ;;; Changing the keymaps doesn't seem to work, so I'll have to do ;;; it on a key by key basis. ;;; maxima-add-keys will add the maxima keys. ;; First, find out what kind of TeX mode is being used. (cond ((eq maximadoc-use-tex 'auctex) (require 'tex-site) ;; I don't think this is the best thing to do... (load "latex") (setq texmode-map LaTeX-mode-map) (defun texmode () (latex-mode))) ((eq maximadoc-use-tex 'tex) (require 'tex-mode) (setq texmode-map tex-mode-map) (defun texmode () (tex-mode))) (t (autoload 'text-mode "text-mode") (setq texmode-map text-mode-map) (defun texmode () (text-mode)))) ;;; Now, define the keymap (defvar maximadoc-mode-map nil "The keymap for maximadoc-mode") (if maximadoc-mode-map nil (let ((map (copy-keymap texmode-map))) (define-key map "\C-c\C-u" nil) (define-key map "\C-c+" 'maximadoc-forward-cell) (define-key map "\C-c-" 'maximadoc-backward-cell) (define-key map "\C-c\C-ua" 'maximadoc-update) (define-key map "\C-c\C-o" 'maximadoc-create-cell) (define-key map "\C-c\C-ul" 'maximadoc-replace-line) (define-key map "\C-c\C-uL" 'maximadoc-replace-line-with-tex) (define-key map "\M-\C-k" 'maxima-stop) ;; And some maximadoc keys that make sense in cells (define-key map "\C-c\C-s" 'maximadoc-send-cell) (define-key map "\C-c\C-uc" 'maximadoc-send-or-update-cell) (define-key map "\C-c\C-d" 'maximadoc-delete-session) (define-key map "\C-c\C-q" 'maximadoc-toggle-hide) (define-key map "\C-c\C-h" 'maxima-help) (define-key map "\C-c\C-i" 'maxima-info) (setq maximadoc-mode-map map))) (define-derived-mode maximadoc-mode texmode "MaximaDoc" "This is a mode intended to allow the user to write documents that include Maxima code. The file can be LaTeXed to produce nice looking sessions (although that isn't necessary, of course), and so the mode is an extension of tex-mode (AucTeX). The units of Maxima code that are worked with are \"cells\", which are delimited by \"\\maximadoc\" and \"\\endmaximadoc\". The cells can be evaluated individually, as a group, and the results can optionally be returned. Evaluating a cell and returning the session is called \"updating\" the cell. If the openining cell delimiter is followed by \"[* Hide *]\", and so it opens with \"\\maximadoc[* Hide *]\", then updating the cell will send the contents to Maxima, but nothing is returned to the document buffer, and nothing shows when the document is processed by LaTeX. The commands for working with cells are: \\[maximadoc-create-cell] create a cell \\[maximadoc-toggle-hide] toggles whether or not the cell is a \"hidden\" cell \\[maximadoc-update] update all the cells \\[maximadoc-forward-cell] go to the next cell \\[maximadoc-backward-cell] go to the previous cell (With a prefix, C-u \\[maximadoc-update] will update the cells without prompting) Single lines can be evaluated: \\[maximadoc-replace-line] replace the current line with Maxima session \\[maximadoc-replace-line-with-tex] replace the current line with Maxima session in TeX form. Within a cell, the following commands are available: \\[maximadoc-delete-session] delete the cell's session \\[maximadoc-send-or-update-cell] update the cell \\[maximadoc-toggle-hide] toggle hidden cells The Maxima process can be killed with the command \\[maxima-stop]. Finally, the command \\[maximadoc-mark-file-as-maximadoc] will insert a %-*-MaximaDoc-*- at the beginning of the file (if there isn't one there already) so the file will begin in maximadoc-mode next time it's opened. \\{maximadoc-mode-map} " (when (or (eq maximadoc-use-tex 'auctex) (eq maximadoc-use-tex 'tex)) (make-local-variable 'ispell-parser) (setq ispell-parser 'tex) (make-local-variable 'ispell-tex-p) (setq ispell-tex-p t)) (if (eq maximadoc-use-tex 'auctex) (progn (require 'font-latex) (add-hook 'maximadoc-mode-hook 'font-latex-setup))) (run-hooks 'maximadoc-mode-hook)) (if (eq maximadoc-use-tex 'auctex) (put 'latex-mode 'font-lock-defaults 'maximadoc-mode)) ;; Now, some more font-locking ;; Some more fontlocking ;; First, fontify the \maxima and \endmaxima (if (fboundp 'font-lock-add-keywords) (progn (defun maximadoc-font-lock-doc-cell (limit) "Used to fontify whatever's between \\maximadoc and \\endmaximadoc." (when (re-search-forward "\\\\maximadoc" limit t) (let ((beg (match-end 0)) end) (if (search-forward "\\\\endmaximadoc" limit 'move) (setq end (match-beginning 0)) (setq end (point))) (store-match-data (list beg end)) t))) (defun maximadoc-font-lock-nodoc-cell (limit) "Used to fontify whatever's between \\maximanodoc and \\endmaximanodoc." (when (re-search-forward "\\\\maximanodoc" limit t) (let ((beg (match-end 0)) end) (if (search-forward "\\\\endmaximanodoc" limit 'move) (setq end (match-beginning 0)) (setq end (point))) (store-match-data (list beg end)) t))) (font-lock-add-keywords 'maximadoc-mode '((maximadoc-font-lock-doc-cell (0 font-lock-function-name-face append t)) (maximadoc-font-lock-nodoc-cell (0 font-lock-function-name-face append t)))) (font-lock-add-keywords 'maximadoc-mode '(("\\(\\\\\\(endmaxima\\(?:\\(?:no\\)?doc\\)\\|maxima\\(?:\\(?:no\\)?doc\\)\\|session\\)\\)" . font-lock-keyword-face))))) ;;; Now, the menu. (easy-menu-define maximadoc-menu maximadoc-mode-map "MaximaDoc mode menu" '("MaximaDoc" ("Cells" ["Create cell" maximadoc-create-cell (not (maximadoc-cell-p))] ["Send cell" maximadoc-send-cell (maximadoc-cell-p)] ["Update cell" maximadoc-send-or-update-cell (maximadoc-cell-p)] ["Delete session" maximadoc-delete-session (maximadoc-cell-p)] ["Toggle hidden" maximadoc-toggle-hide (maximadoc-cell-p)] ["Forward cell" maximadoc-forward-cell] ["Backwards cell" maximadoc-backward-cell]) ("Update" ["Update line" maximadoc-replace-line (not (maximadoc-cell-p))] ["TeX update line" maximadoc-replace-line-with-tex (not (maximadoc-cell-p))] ["Update all cells" maximadoc-update]) ("Process" ["Kill Maxima process" maxima-stop (processp maxima-process)]) ("Misc" ["Mark file as MaximaDoc" maximadoc-mark-file-as-maximadoc]) ("Help" ["Info" maxima-info] ["Help" maxima-help]))) ;;; The next line is necessary for XEmacs (easy-menu-add maximadoc-menu maximadoc-mode-map) ;;; maximadoc.el ends here |