;;; zmat-mode.el --- A major mode for editing Z matrixes in GNU Emacs ;; Copyright (C) 1996 Toomas Tamm, Quantum Theory Project, ;; University of Florida, USA ;; You may use this software without charge. You may NOT remove this ;; copyright notice, and you must include this copyright notice into ;; any derivative work. Please read the NO WARRANTY section below. ;; Author: Toomas Tamm ;; Version: 1.0 ;; Keywords: Z-matrix, quantum chemistry, mopac ;;; Commentary: ;; In this mode, deleting or adding a line may (and should, in fact) ;; modify all lines below the one added. It is very specifically designed ;; for editing Z-matrixes for the MOPAC and AMPAC programs, and most likely ;; has no other utility value. ;; ;; You may want to re-bind the keys to something which better suits your ;; working style. You can do this by modifying the (define-key) lines ;; which almost immediately follow the comments section. ;;; Bugs: ;; Unpredictable things may happen if you edit anything other than a ;; MOPAC or AMPAC Z-matrix. It is assumed, that the matrix starts at ;; line 4. If a line is found to be of different format, the mode assumes ;; that Z-matrix has ended, and will not attempt to modify lines below ;; the first suspicious line. ;; ;; Changes are not necessarily reversible; you may use the Emacs undo ;; function to get back what you have lost. ;; ;; Anything beyond the three columns representing connectivity will be ;; removed. Sometimes this may even be a desirable feature, but sometimes not. ;; ;; If you have other problems, you may e-mail me, ;; but I will not assume any responsibility for correcting the problems ;; you may encounter. ;;; NO WARRANTY ;; This code comes without any warranty or guarantee of any kind, including ;; the implied warranty of fitness for a particular purpose. The author and/or ;; his employer(s) will not take any responsibility for lost time, data, ;; results, hardware, or anything else. Use this software at your own risk. ;;; Usage: ;; * It is assumed that you use Emacs version 19. Others have not been tested. ;; * Place this file either into the site-lisp directory (inquire the system ;; manager if needed), or into any directory in your home space. ;; * If you are familiar with byte-compilation, you may want to byte-compile ;; the file. This may result in noticable speedup of the mode. ;; * Add the following line [excluding the comment signs] into ;; your .emacs file: ;; (load "zmat-mode") ;; * You may need to specify a full pathname and a .el or .elc extension if ;; you placed the file in your home space. ;; ;; * (Re)start emacs. ;; * Find (load) the file you wish to edit. ;; * Type M-x zmat-mode RET. ;; * The major mode should change to (Z-matrix). ;; * Use C-k to kill a line. ;; * Use C-n to add an empty line between existing atoms. ;; * If you do not like these key bindings, change the (define-key) lines ;; below to suit your taste and try again. ;; * Enjoy! ;;; Code: (defvar zmat-mode-map nil "") (if zmat-mode-map () (setq zmat-mode-map (make-keymap)) (define-key zmat-mode-map "\C-k" 'zmat-kill-line) (define-key zmat-mode-map "\C-n" 'zmat-insert-line) ) (defun zmat-mode () "Major mode for editing Mopac-style Z matrixes. Only two keys are redefined so far: C-k and C-n. Those kill line and create new line, respectively, renumbering the references (three rightmost columns) along the way. " (interactive) (kill-all-local-variables) (use-local-map zmat-mode-map) (setq mode-name "Z-matrix") (setq major-mode 'zmat-mode) (run-hooks 'zmat-mode-hook) ) (defun zmat-current-line () "Return the vertical position of point..." (+ (count-lines (point-min) (point)) (if (= (current-column) 0) 1 0) -1)) (defun zmat-renumber-line (delta lowest-lineno invalid-value) "Renumbers the connectivity indexes on a line of Z-matrix delta is the numeric value for change introduced (-1 or 1, typically) lowest-lineno is the lower limit on atom sequence numbers that need changing invalid-value is a value to be inserted for sequence numbers that would become invalid otherwise. zmat-renumber-line returns t if the line was non-conforming to standard zmatrix form. " (let (a b c eol save) (save-excursion (catch 'zrl-error-exit (end-of-line) (setq eol (point)) (beginning-of-line) ; * matches 0+ times, + matches 1+ . (if (not (re-search-forward " *[A-z]+ +" eol t 1)) ; Skip the element and space after it (progn (message "no chemical element") (throw 'zrl-error-exit t) ) ) (if (not (re-search-forward "[-.0-9]+ +" eol t 6)) (progn (message "non-conforming line") (throw 'zrl-error-exit t) ) ) (setq save (point)) ; We shall kill from here onwards soon (setq a (read (current-buffer))) ; Read the first number (forward-word 1) (forward-word -1); Re-position to next line if at EOL (if (< (point) eol) ; Read the second number unless at new line (setq b (read (current-buffer))) (setq b 0)) (forward-word 1) (forward-word -1); Repeat for third number (if (< (point) eol) (setq c (read (current-buffer))) (setq c 0)) (if (and (integerp a) (integerp b) (integerp c)) (progn ; OK. If we are here, we got three integers (setq a (if (> a lowest-lineno) (+ a delta) (if (= a lowest-lineno) invalid-value a ) )) (setq b (if (> b lowest-lineno) (+ b delta) (if (= b lowest-lineno) invalid-value b ) )) (setq c (if (> c lowest-lineno) (+ c delta) (if (= c lowest-lineno) invalid-value c ) )) (goto-char save) ; Go back (forward-word -1) ; Position for kill (forward-word 1) (if (not (eolp)) (kill-line)) ; Do it (princ (format " %3d %3d %3d" a b c) (current-buffer)) ; Print the new ) ; else (progn (message "Non-conforming line") (throw 'zrl-error-exit t) ) ) () ; return a nil if everything went OK ) ; this closes the catch form ))) (defun zmat-renumber-loop (delta invalid-value start-line) "Renumbers from current line downwards until a non-conforming line is encountered, or a clear end of z-matrix is found. " (let (previous-line-result) (save-excursion (beginning-of-line) (forward-char) (setq previous-line-result nil) (while (and (looking-at " *[A-z]+ +[-.0-9]+ +[-.0-9]+") (not previous-line-result)) (beginning-of-line) (setq previous-line-result (zmat-renumber-line delta start-line invalid-value)) (forward-line) ) ))) (defun zmat-insert-line () "Insert a blank line into Z matrix" (interactive) (let (x) (beginning-of-line) (setq x (- (zmat-current-line) 2)) ; For some unclear reason this needs to (newline) ; be done outside of zmat-renumber-loop (zmat-renumber-loop 1 (- (zmat-current-line) 2) x) ) ) (defun zmat-kill-line () "Remove a line from Z matrix" (interactive) (let (x) (beginning-of-line) (setq x (- (zmat-current-line) 2)) (kill-line) (zmat-renumber-loop -1 999 x) ) ) (provide 'zmat)