;; @(#) mathem-mode.el -- A major mode for editing Mathematica files ;; Copyright (C) 1998, Robert Harlander. All rights reserved. ;; This file is intended to be used with GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, 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 GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. (defvar mathem-mode-version-string "2.3.6") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; mathem-mode ;; ;; ;; ;; (C) by Robert Harlander ;; ;; ;; ;; comments, bug reports, suggestions, etc. ;; ;; to robert.harlander@cern.ch ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; It consists of two parts: the first one deals with submission of ;; ;; MATHEMATICA commands directly from the source file to buffer ;; ;; *math*, the second part is mainly concerned with pretty (well...) ;; ;; indentation of the MATHEMATICA code and is essentially a copy of ;; ;; perl-mode from the EMACS distribution. ;; ;; There is still a lot of code that is not used at all. This is ;; ;; because I only modified huge parts of perl-mode, but didn't really ;; ;; remove things that are not needed. So this is one of the things ;; ;; to do. ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; INSTALLATION: ;; ;; ;; ;; This program requires math.el. ;; ;; ;; ;; Put this file as mathem-mode.el into your load-path and add the ;; ;; following lines to .emacs: ;; ;; ;; ;; (autoload 'mathem-mode "mathem-mode" ;; ;; "Major mode for editing MATHEMATICA-files" t) ;; ;; (setq auto-mode-alist (cons '("\\.m\$" . mathem-mode) ;; ;; auto-mode-alist)) ;; ;; ;; ;; You may repeat the (setq ...) bracket with different entries for ;; ;; "\\.m\$ (e.g., "\\.mat\$), depending on your favorite extensions ;; ;; for MATHEMATICA files. ;; ;; ;; ;; mathem-mode should be faster when byte-compiled: ;; ;; M-x byte-compile-file ;; ;; ;; ;; If you don't like the indentation, put also ;; ;; (setq mathem-mode-auto-indent nil) ;; ;; to your .emacs-file. ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; FUNCTIONS: KEY: ;; ;; ;; ;; math-buffer C-M-return ;; ;; math-eval-active-region C-c C-c ;; ;; math-eval-block-or-line M-return ;; ;; math-eval-line ;; ;; math-eval-expression C-return ;; ;; math-eval-undo C-c C-u ;; ;; math-set-dir ;; ;; math-recenter-output-buffer S-select ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; ;; ;; last modified: ;; ;; ;; ;; Aug 24 2003 (rh) (v2.3.6): ;; ;; - bug fix in math-eval-expression: ;; ;; math-eval-count -> (number-to-string math-eval-count) ;; ;; otherwise problems in Linux 2.4.20 ;; ;; - comment-start modified and comment-padding introduced ;; ;; ;; ;; Oct 08 2001 (rh) (v2.3.6): ;; ;; - made the comma equivalent to the semi-colon as far as indentation ;; ;; is concerned. Let's hope that now long lists don't get messed up ;; ;; any longer. ;; ;; ;; ;; Sep 13 2001 (rh) (v2.3.6): ;; ;; - replaced mathem-mode-syntax-table by the one from math.el. ;; ;; Now comments are properly recognized for font-locking. ;; ;; ;; ;; Jul 19 2000 (rh) (v2.3.5): ;; ;; - math-eval-region and math-buffer: clear the input cell before ;; ;; copying the command to the *math* buffer ;; ;; ;; ;; Jun 24 1999 (rh) (v2.3.4): ;; ;; - mathem-beginning-of-function: regexp changed in order to avoid ;; ;; indentation troubles with commented lines within blocks. ;; ;; ;; ;; Feb 3 1999 (rh) (v2.3.3): ;; ;; - removed special indentation for Perl's loop labels of the form ;; ;; LOOP: foreach (@list) {
} ;; ;; ;; ;; Nov 19 1998 (rh) (v2.3.2): ;; ;; - math-eval-block changed: if block is shorter than one line, ;; ;; evaluate line instead ;; ;; ;; ;; Nov 6 1998 (rh): ;; ;; - bug fixed in math-recenter-output-buffer: ;; ;; if -offset was larger than (window-height), recenter didn't ;; ;; work properly ;; ;; ;; ;; Nov 5 1998 (rh): ;; ;; - load math.el to do syntax-check BEFORE submitting to math ;; ;; - this required to introduce the auxiliary buffer *mathem* ;; ;; - bug fixes ;; ;; ;; ;; Nov 2 1998 (rh): ;; ;; - removed problem with ^G's that occured with multiple statments. ;; ;; The simple solution: math-eval-region surrounds the region by ;; ;; brackets now ;; ;; ;; ;; Nov 1 1998 (rh): ;; ;; - math-eval-block introduced ;; ;; - math-looking-at-function introduced ;; ;; - math-eval-block-or-line modified: ;; ;; now evaluates block if ;; ;; (a) cursor is looking at a function ;; ;; (b) cursor is at the end of a block ;; ;; - math-eval-expression modified: ;; ;; now evaluates also terms like exp[4,5,j] etc. ;; ;; ;; ;; Sep 23 1998 (rh): ;; ;; - math-eval-block-or-line added and bound to M-return ;; ;; instead of math-eval-line ;; ;; - math-eval-line code modified (functionality unchanged) ;; ;; - math-eval-undo modified ;; ;; ;; ;; Aug 26 1998 (rh): ;; ;; - bug fixes ;; ;; ;; ;; Apr 1 1998 (rh): ;; ;; - recenter after sending inputs ;; ;; ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (require 'math) (defvar mathem-mode-map () "Keymap used in mathem mode.") (defvar mathem-mode-auto-indent t "If t, auto-indentation is done for Mathematica files.") (defvar math-eval-count 0) (defvar math-buffer-name) (defvar line-number) (defvar math-tmp-buffer-name) (defvar mathem-mode-syntax-table nil "Syntax table in use in mathem-mode buffers.") (defun mathem-mode-version () "Report on version of mathem-mode." (interactive) (message mathem-mode-version-string) ) (defun mathem-math () "If no Mathematica process is running, start one." (if (not (get-process "math")) (progn (load "math") (math) ))) (defun math-buffer () "Load the current buffer into Mathematica." (interactive) (mathem-math) (math-recenter-output-buffer nil -4) (setq math-buffer-name buffer-file-name) (set-buffer "*math*") (goto-char (point-max)) (search-backward ":=") (forward-char 3) (delete-region (point) (point-max)) (insert (concat "<<" math-buffer-name)) (math-send-input) ) (defun math-eval-active-region () "Send active region to Mathematica." (interactive) (math-eval-region (region-beginning) (region-end) nil) ) (defun math-eval-region (beg end mathem-colon) "Send region defined by 'beg' and 'end' to Mathematica." (copy-region-as-kill beg end) (mathem-math) (save-excursion (set-buffer (get-buffer-create "*mathem*")) (setq math-eval-count (+ 1 math-eval-count)) (insert (concat "\n\n$" (number-to-string math-eval-count) ">> ")) (setq pmin (point)) (insert "(") (yank) (insert ")") (if mathem-colon (insert ";")) (setq pmax (point)) (check-math-syntax pmin pmax) (copy-region-as-kill pmin pmax) ) (math-recenter-output-buffer nil (- -2 (abs (count-lines beg end)))) (set-buffer "*math*") (goto-char (point-max)) (search-backward ":=") (forward-char 3) (delete-region (point) (point-max)) (yank) ;; go to beginning of cell and delete comment lines: ;; (if (not (looking-at "In\[[0-9]*\]:=")) (search-backward-regexp "In\[[0-9]*\]:=")) (while (re-search-forward "\(\\*[^(\\*\))]*\\*\)" nil t) (replace-match "" nil nil)) ;; go to beginning of cell and delete blank lines: ;; (if (not (looking-at "In\[[0-9]*\]:=")) (search-backward-regexp "In\[[0-9]*\]:=")) (delete-matching-lines "^ *$") (math-send-input)) (defun math-eval-line () "Send line to MATHEMATICA." (interactive) (save-excursion (beginning-of-line 1) (let ((beg (point))) (end-of-line) (math-eval-region beg (point) nil)) )) (defun math-eval-expression () "Evaluate expression at point." (interactive) (math-recenter-output-buffer nil -6) (save-excursion (forward-word 1) (backward-word 1) (let ((beg (point))) (forward-word 1) (if (looking-at "\\[") (forward-sexp 1)) ; (copy-region-as-kill beg (point)) (math-eval-region beg (point) nil) ) ; (set-buffer "*math*") ; (goto-char (point-max)) ; (yank) ; (math-send-input) ) ) (defun math-eval-block-or-line (mathem-mode) "If cursor is on the left hand side of a function definition, evaluate whole definition. If cursor is behind ')' or ']', searches for matching parenthesis and evaluates the whole block, including the left hand side of '=', if present. If block is shorter than one line, evaluates line instead." (interactive "p") (save-excursion (if (= (preceding-char) ?\;) (setq mathem-colon t) (setq mathem-colon nil)) (if (looking-at "\)\\|\\]") (forward-char 1)) (skip-chars-backward ";\t ") (if (memq (preceding-char) '(?\) ?\])) (math-eval-block mathem-colon) (if (math-looking-at-function) (progn (goto-char (math-looking-at-function)) (math-eval-block mathem-colon) ) (math-eval-line)) ))) (defun math-eval-block (mathem-colon) "If cursor is behind ')' or ']', evaluate block that is defined through matching bracket, including left hand side of '=', if present." (interactive "p") (let ((opoint (point)) (apoint nil)) ; (forward-char 1) (backward-sexp 1) (if (looking-at "\\[") (backward-word 1)) (setq apoint (point)) (skip-chars-backward "\t \n") (if (= (preceding-char) ?=) (progn (skip-chars-backward "\t :=") (if (= (preceding-char) ?\]) (backward-sexp 1)) (backward-word 1) ) (goto-char apoint)) (if (= (line-number (point)) (line-number opoint)) (math-eval-line) (math-eval-region (point) opoint mathem-colon))) ) (defun math-looking-at-function () "t if expression is the left hand side of a function definition." (interactive) (save-excursion (if (not (looking-at "\[\]; \]*$")) (forward-word 1)) (if (looking-at "\\[") (forward-sexp 1)) (if (not (looking-at "\[\]; \]*$")) (skip-chars-forward "\t ")) (if (or (looking-at ":") (looking-at "=")) (progn (skip-chars-forward "\t :=") (cond ((looking-at "[A-Za-z\n]") (forward-word 1) (if (looking-at "\\[") (progn (forward-sexp 1) (if (looking-at "\[ \t\n\]*;") (progn (skip-chars-forward " \t\n") (forward-char 1) )) (point) ) )) ((looking-at "(") (progn (forward-sexp 1) (if (looking-at "\[ \t\n\]*;") (progn (skip-chars-forward " \t\n") (forward-char 1) )) (point) )) (t (= 1 0))) ) (= 1 0)))) (defun math-eval-undo () "Delete current cell." (interactive) (set-buffer "*math*") (goto-char (point-max)) (if (not (looking-at "In\[[0-9]*\]:=")) (search-backward-regexp "In\[[0-9]*\]:=")) (kill-math-cell (point) nil) (math-send-input) (previous-line 1) (delete-line) (delete-line) (goto-char (point-max)) ) (defun math-set-dir (dir) "Set directory for buffer *math*." (interactive "DSetDirectory: ") (set-buffer "*math*") (setq default-directory dir) (goto-char (point-max)) (insert (concat "SetDirectory[\"" dir "\"]")) (math-send-input) ) (defun math-recenter-output-buffer (linenum offset) "Redisplay buffer of TeX job output so that most recent output can be seen. The last line of the buffer is displayed on line LINE of the window, or centered if LINE is nil. (math-recenter-output-buffer LINE)" ;; (interactive "P") (let ((math (get-buffer "*math*")) (old-buffer (current-buffer))) (if (null math) (message "No MATHEMATICA output buffer") (pop-to-buffer math) (bury-buffer math) (goto-char (point-max)) (setq pos (+ (if linenum (prefix-numeric-value linenum) (/ (window-height) 2)) (if offset (prefix-numeric-value offset) 0))) (if (< pos 0) (setq pos 0)) (recenter pos) (pop-to-buffer old-buffer)))) (defvar mathem-mode-abbrev-table nil "Abbrev table in use in mathem-mode buffers.") (define-abbrev-table 'mathem-mode-abbrev-table ()) (defvar mathem-mode-map () "Keymap used in Mathem mode.") (if mathem-mode-map () (setq mathem-mode-map (make-sparse-keymap)) (define-key mathem-mode-map "\t" 'tab-to-tab-stop) (define-key mathem-mode-map [M-down] 'mathem-goto-math-window) (define-key mathem-mode-map "\M-\C-m" 'math-eval-block-or-line) (define-key mathem-mode-map [C-return] 'math-eval-expression) (define-key mathem-mode-map [C-M-return] 'math-buffer) (define-key mathem-mode-map "\C-c\C-c" 'math-eval-active-region) (define-key mathem-mode-map "\C-c\C-u" 'math-eval-undo) (define-key mathem-mode-map [S-select] 'math-recenter-output-buffer) (define-key mathem-mode-map [S-select] 'recenter-command) ) (defun mathem-goto-math-window () "Go to buffer *math*." (interactive) (if (window-live-p (get-buffer-window "*math*")) (progn (math-recenter-output-buffer nil -2) (select-window (get-buffer-window "*math*")) ) (other-window 1) ) ) (defun recenter-command () "" (interactive) (math-recenter-output-buffer nil -2) ) (defun line-number (pos) "Determines line number." (interactive "P") (if (not pos) (setq pos (point))) (save-restriction (widen) (save-excursion (beginning-of-line) (setq line-number (1+ (count-lines 1 pos))))) ) ;;;###autoload (defun mathem-mode () "Major mode for editing Mathem code. Expression and list commands understand all Mathem brackets. Tab indents for Mathem code. Comments are delimited with # ... \\n. Paragraphs are separated by blank lines only. Delete converts tabs to spaces as it moves back. \\{mathem-mode-map} Variables controlling indentation style: mathem-tab-always-indent Non-nil means TAB in Mathem mode should always indent the current line, regardless of where in the line point is when the TAB command is used. mathem-tab-to-comment Non-nil means that for lines which don't need indenting, TAB will either delete an empty comment, indent an existing comment, move to end-of-line, or if at end-of-line already, create a new comment. mathem-nochange Lines starting with this regular expression are not auto-indented. mathem-indent-level Indentation of Mathem statements within surrounding block. The surrounding block's indentation is the indentation of the line on which the open-brace appears. mathem-continued-statement-offset Extra indentation given to a substatement, such as the then-clause of an if or body of a while. mathem-continued-brace-offset Extra indentation given to a brace that starts a substatement. This is in addition to `mathem-continued-statement-offset'. mathem-brace-offset Extra indentation for line if it starts with an open brace. mathem-brace-imaginary-offset An open brace following other text is treated as if it were this far to the right of the start of its line. mathem-label-offset Extra indentation for line that is a label. Various indentation styles: K&R BSD BLK GNU LW mathem-indent-level 5 8 0 2 4 mathem-continued-statement-offset 5 8 4 2 4 mathem-continued-brace-offset 0 0 0 0 -4 mathem-brace-offset -5 -8 0 0 0 mathem-brace-imaginary-offset 0 0 4 0 0 mathem-label-offset -5 -8 -2 -2 -2 Turning on Mathem mode runs the normal hook `mathem-mode-hook'." (interactive) (kill-all-local-variables) (use-local-map mathem-mode-map) (setq major-mode 'mathem-mode) (setq mode-name "MATHEM") (make-local-variable 'comment-start) (setq comment-start "(*") (make-local-variable 'comment-padding) (setq comment-padding 1) (make-local-variable 'comment-end) (setq comment-end "*)") (make-local-variable 'comment-column) (setq comment-column 32) (make-local-variable 'comment-start-skip) ; (setq comment-start-skip "\\(^\\|\\s-\\);?\(\\*+ *") (setq comment-start-skip "(\\*+ *") (make-local-variable 'parse-sexp-ignore-comments) (setq parse-sexp-ignore-comments t) (if mathem-mode-auto-indent (progn (setq local-abbrev-table mathem-mode-abbrev-table) (set-syntax-table mathem-mode-syntax-table) (make-local-variable 'paragraph-start) (setq paragraph-start (concat "$\\|" page-delimiter)) (make-local-variable 'paragraph-separate) (setq paragraph-separate paragraph-start) (make-local-variable 'paragraph-ignore-fill-prefix) (setq paragraph-ignore-fill-prefix t) (make-local-variable 'indent-line-function) (setq indent-line-function 'mathem-indent-line) (make-local-variable 'require-final-newline) (setq require-final-newline t) (make-local-variable 'comment-indent-function) (define-key mathem-mode-map "\e\C-q" 'indent-mathem-exp) (define-key mathem-mode-map "\177" 'backward-delete-char-untabify) (define-key mathem-mode-map "\t" 'mathem-indent-command) (define-key mathem-mode-map "{" 'electric-mathem-terminator) (define-key mathem-mode-map "}" 'electric-mathem-terminator) (define-key mathem-mode-map "[" 'electric-mathem-terminator) (define-key mathem-mode-map "]" 'electric-mathem-terminator) (define-key mathem-mode-map ";" 'electric-mathem-terminator) (define-key mathem-mode-map "," 'electric-mathem-terminator) (define-key mathem-mode-map "\e\C-a" 'mathem-beginning-of-function) (define-key mathem-mode-map "\e\C-e" 'mathem-end-of-function) (define-key mathem-mode-map "\e\C-h" 'mark-mathem-function) (setq comment-indent-function 'mathem-comment-indent) (make-local-variable 'parse-sexp-ignore-comments) (setq parse-sexp-ignore-comments t) ;; Tell font-lock.el how to handle Mathem. (make-local-variable 'font-lock-defaults) ; (setq font-lock-defaults '((mathem-font-lock-keywords ; mathem-font-lock-keywords-1 ; mathem-font-lock-keywords-2) ; nil nil ((?\_ . "w")))) (setq font-lock-defaults '((mathem-font-lock-keywords mathem-font-lock-keywords-1 mathem-font-lock-keywords-2) nil nil ((?\_ . "w")) beginning-of-defun (font-lock-comment-start-regexp . "^([*]\\|[ \t]([*]"))) ;; Tell imenu how to handle Mathem. (make-local-variable 'imenu-generic-expression) (setq imenu-generic-expression mathem-imenu-generic-expression) )) (run-hooks 'mathem-mode-hook) ) ;;---------------------------------------------------------------------- ;; ;; The following is taken from PERL mode and was slightly adjusted ;; by rh. ;; ;;---------------------------------------------------------------------- (defvar mathem-mode-syntax-table nil "Syntax table used while in mathem-mode.") (if mathem-mode-syntax-table () (setq mathem-mode-syntax-table (make-syntax-table)) (modify-syntax-entry ?% "." mathem-mode-syntax-table) (modify-syntax-entry ?& "." mathem-mode-syntax-table) (modify-syntax-entry ?* ". 23" mathem-mode-syntax-table) ;allow for (* comment *) (modify-syntax-entry ?+ "." mathem-mode-syntax-table) (modify-syntax-entry ?- "." mathem-mode-syntax-table) (modify-syntax-entry ?/ "." mathem-mode-syntax-table) (modify-syntax-entry ?< "." mathem-mode-syntax-table) (modify-syntax-entry ?= "." mathem-mode-syntax-table) (modify-syntax-entry ?> "." mathem-mode-syntax-table) (modify-syntax-entry ?_ "." mathem-mode-syntax-table) (modify-syntax-entry ?\| "." mathem-mode-syntax-table) (modify-syntax-entry ?\` "_" mathem-mode-syntax-table) ; Mathematica context symbol (modify-syntax-entry ?\( "()1" mathem-mode-syntax-table) ;allow for (* comment *) (modify-syntax-entry ?\) ")(4" mathem-mode-syntax-table)) ;allow for (* comment *) (defvar mathem-imenu-generic-expression '( ; ;; Functions ; (nil "^sub\\s-+\\([-A-Za-z0-9+_:]+\\)\\(\\s-\\|\n\\)*{" 1 ) ; ;;Variables ; ("Variables" "^\\([$@%][-A-Za-z0-9+_:]+\\)\\s-*=" 1 ) ; ("Packages" "^package\\s-+\\([-A-Za-z0-9+_:]+\\);" 1 ) ) "Imenu generic expression for Mathem mode. See `imenu-generic-expression'.") ;; Regexps updated with help from Tom Tromey