; hlatex.el -- extensions to latex-mode ; ; Harri Ojanen ; ojanen@math.rutgers.edu ; ; To install this file, ; either: append it to your ~/.emacs file ; or: put a command such as (load-file "hlatex.el") in your ~/.emacs ; (if the file hlatex.el is not in your home directory, make sure ; you use a full path to, e.g. (load-file "~/emacs/hlatex.el") ) ; ; Sep 10, 1998 ;;;;;;;; LaTeX commands ;;;; MISC (defun hlatex-braces () "Inserts braces and places the cursor between them." (interactive "*") (insert "{}") (backward-char)) (defun hlatex-brackets () "Inserts brackets and places the cursor between them." (interactive "*") (insert "[]") (backward-char)) ;;;; EQUATIONS (defun hlatex-math-mode () "Inserts $$ and places the cursor between them." (interactive "*") (insert "$$") (backward-char)) (defun hlatex-equations (opt c) "Asks for an equation environment to insert at the current position. With optional argument (C-u) the current region is wrapped inside the environment. " (interactive "*P\nca)align, e)equation, g)gather, m)multline, s)split, [ for \\[ \\] (caps: no*)? ") (let (env-list) (setq env-list (hlatex-eqn-env c)) (if env-list (let ((begin (nth 0 env-list)) (end (nth 1 env-list)) (labelp (nth 2 env-list)) (right (point)) left) (if opt (save-excursion (let ((indent-column (current-column)) (begin-pt (min (point) (mark))) (end-pt (max (point) (mark)))) (goto-char end-pt) (insert (concat end "\n")) (goto-char begin-pt) (insert (concat begin "\n")) (indent-rigidly (+ begin-pt (length begin)) ;(- (+ end-pt (length begin)) indent-column) (+ end-pt (length begin) 2) indent-column)) ) (beginning-of-line) (setq left (point)) (end-of-line) (insert (concat begin "\n")) (insert-buffer-substring (current-buffer) left right) (insert " \n") (insert-buffer-substring (current-buffer) left right) (insert end) (if labelp (progn (previous-line 2) (end-of-line) (backward-char)) (progn (previous-line 1) (end-of-line)))))))) (defun hlatex-eqn-env (c) "Returns a three element list: strings for begin and end environment, and t/nil if the begin line includes a label command." (if (or (equal c ?\[) (equal c ?\])) (list "\\[" "\\]" nil) (let ((name-and-props (hlatex-eqn-env-name (downcase c)))) (if name-and-props (progn (let* ((starp (nth 2 name-and-props)) (name (concat (nth 0 name-and-props) (if (and starp (>= c ?a)) "*" ""))) (labelp (and (< c ?a) (nth 1 name-and-props)))) (list (concat "\\begin{" name "}" (if labelp (hlatex-label-string) "")) (concat "\\end{" name "}") labelp))))))) (defun hlatex-eqn-env-name (c) "Returns a list with three elements: the first one is the name of the environment; second one is t if automatic labels make sense for this environment, nil otherwise; third one is t if there is a star form of this environment." (cond ((equal c ?e) '("equation" t t)) ((equal c ?s) '("split" nil nil)) ((equal c ?g) '("gather" nil t)) ((equal c ?m) '("multline" t t)) ((equal c ?a) '("align" nil t)) ((equal c ?f) '("flalign" nil t)) (t (message "Unknown letter.") nil))) ;(defun hlatex-equation* () ; "Inserts LaTeX commands for a display math environment and puts cursor between the brackets." ; (interactive "*") ; (let ((right (point)) ; left) ; (beginning-of-line) ; (setq left (point)) ; (end-of-line) ; (insert "\\[\n") ; (insert-buffer-substring (current-buffer) left right) ; (insert " \n") ; (insert-buffer-substring (current-buffer) left right) ; (insert "\\]") ; (previous-line 1) ; (end-of-line))) ;;;; LABELS (defun hlatex-label-string () "Returns the string representing a label or mylabel command." (if hlatex-mylabels '"\\mylabel{}" '"\\label{}")) (defun hlatex-insert-label-string () "Inserts the string representing a label or mylabel command." (interactive) (insert (hlatex-label-string)) (backward-char)) (defvar hlatex-mylabels nil "Value t is for mylabel command, nil for label") (defun hlatex-toggle-label-string () "Switches between labels and mylabels." (interactive) (setq hlatex-mylabels (not hlatex-mylabels)) (message (concat "Using " (hlatex-label-string)))) ;;;; ENVIRONMENTS (defun hlatex-begin-end (regionp) "Make an environment, whose name is the word on the current line. With optional argument wraps the current region inside the environment." (interactive "*P") (end-of-line) (let (new-point) (save-excursion (let ((line-end (point)) word-start line-start indent-column word-end env-name begin-txt end-txt begin-pt end-pt args) (beginning-of-line) (setq line-start (point)) ; find first word on line (if (re-search-forward "[^ ]" line-end t) (progn (backward-char) (setq word-start (point)) (setq indent-column (current-column)) ; find end of word, this gives the name of the environment (re-search-forward "\\([a-zA-Z*@]*\\)" line-end t) (setq env-name (match-string 1)) (setq word-end (point)) ; rest of the line is arguments to the environment (setq args (buffer-substring word-end line-end)) ; begin and end commands (setq begin-txt (concat "\\begin{" env-name "}" args)) (setq end-txt (concat "\\end{" env-name "}")) (setq indent-txt (buffer-substring line-start word-start)) (delete-region line-start line-end) (if (< (point) (point-max)) (delete-char 1)) (if regionp (progn ; check point is set (setq begin-pt (min (point) (mark))) (setq end-pt (max (point) (mark))) (goto-char end-pt) (insert (concat indent-txt end-txt "\n")) (goto-char begin-pt) (insert (concat indent-txt begin-txt "\n")) (indent-rigidly (+ begin-pt (length begin-txt)) (+ end-pt (length begin-txt) indent-column) indent-column)) ; not a region, just put the strings there (beginning-of-line) (insert (concat indent-txt begin-txt "\n" indent-txt "\n" indent-txt end-txt "\n")) (next-line -2) (end-of-line) (setq new-point (point))))))) (if new-point (goto-char new-point)))) ;;;; MOVING AROUND (defun hlatex-find-command-start () "Put point at the beginning of current LaTeX command. Point may be at the command name or argument. Returns nil if the beginning was not found, value of (point) there if found. If not found, does not move point." (let (current line-end line-start new-point) (save-excursion ;;; search limits at the beginning and end of current line (setq current (point)) (end-of-line) (setq line-end (point)) (beginning-of-line) (setq line-start (point)) (goto-char current) ;;; find spaces, {}, or goto end of line (or (re-search-forward "[ {}\t]" line-end t) (end-of-line)) ;;; find command (setq new-point (or ;;; first expect we were after the command: search backward (re-search-backward "\\\\[a-zA-Z@*]+" line-start t) ;;; if not, maybe there is something after the point (if (re-search-forward "\\\\[a-zA-Z@*]+" line-end t) (re-search-backward "\\\\[a-zA-Z@*]+" line-start t))))) (if new-point (goto-char new-point)))) (defun hlatex-find-begin-end () "Finds matching \\begin{...} or \\end{...}. Sets mark where started. Bugs: doesn't understand comments." (interactive) (let (new-point name begin-or-end) (save-excursion (if (and (hlatex-find-begin-end-start) (setq name (hlatex-curr-env-name)) (setq begin-or-end (hlatex-is-it-begin-or-end)) (if (= begin-or-end 1) ; if it's \begin, skip (forward-word 1) t) (hlatex-find-begin-end-go name begin-or-end)) (setq new-point (point)) (message "Can't find the other part (or it's not an environment)."))) (if new-point (progn (push-mark) (goto-char new-point))))) (defun hlatex-find-begin-end-start () "Puts point at the beginning of current LaTeX environment command. Point may be originally at the command name or argument. Returns nil if the command was not found, value of (point) there if found. If not found, does not move point." (let (current line-end line-start new-point) (save-excursion ;;; search limits at the beginning and end of current line (setq current (point)) (end-of-line) (setq line-end (point)) (beginning-of-line) (setq line-start (point)) (goto-char current) ;;; find spaces, {}, or goto end of line (or (re-search-forward "[ {}\t]" line-end t) (end-of-line)) ;;; find command (setq new-point (or ;;; first expect we were after the command: search backward (re-search-backward "\\\\begin\\|\\\\end" line-start t) ;;; if not, maybe there is something after the point (if (re-search-forward "\\\\begin\\|\\\\end" line-end t) (re-search-backward "\\\\begin\\|\\\\end" line-start t))))) (if new-point (goto-char new-point)))) (defun hlatex-find-begin-end-go (env-name direction) "Finds the string \\begin{env-name} if direction = -1, or \\end... if direction = +1. Leaves point at the first letter of the environment name." (let ((level direction) (search-string (concat "\\\\\\(begin\\|end\\)[ \t]*{[ \t]*" (regexp-quote env-name) "[ \t]*}"))) (while (and (/= level 0) (if (= direction 1) (re-search-forward search-string (point-max) t) (re-search-backward search-string (point-min) t))) (if (equal (match-string 1) "begin") (setq level (+ level 1)) (setq level (- level 1)))) (if (/= level 0) nil (if (= direction +1) (forward-word -1) (forward-word 2) (forward-word -1))))) (defun hlatex-is-it-begin-or-end () "If the text at the point is \\begin, returns +1; if \\end, returns -1; nil otherwise" (save-excursion (cond ((looking-at "\\\\begin") +1) ((looking-at "\\\\end") -1) (t nil)))) (defun hlatex-curr-env-name () "Returns a string containing the name of the current environment. Point should be before or at the left brace. Returns nil if cannot figure out the name. Does not change point." (save-excursion (let (current line-end name-start) (setq current (point)) (end-of-line) (setq line-end (point)) (goto-char current) (if (re-search-forward "{[ ]*" line-end t) (progn (setq name-start (point)) (if (re-search-forward "[a-zA-Z*@]+" line-end t) (buffer-substring name-start (point)) nil)))))) ;;;; REPLACE (defun hlatex-replace-env () "Replaces the name of an environment with another one (in both \\begin{...} and \\end{...}). Can also replace \\ [ ... \\ ] with a begin-end style environment. Bugs: doesn't understand comments or nested \\ [ ... \\ ] environments." (interactive "*") (save-excursion (if (hlatex-find-begin-end-start) (if (and (hlatex-find-begin-end) (hlatex-find-begin-end)) (progn (forward-word -1) (let* ((old-name (hlatex-curr-env-name)) (new-name (hlatex-replace-env-ask old-name))) (if (not (equal new-name "")) (progn (search-forward old-name nil t) (replace-match new-name t) (exchange-point-and-mark) (search-forward old-name nil t) (replace-match new-name t)))))) ;;; no begin or end on this line, maybe there is \[ or \] (hlatex-replace-eqn)))) (defun hlatex-replace-env-ask (old) (completing-read (concat "Replace " old " with: ") '(("equation" 1) ("multline" 2) ("align" 3) ("gather" 4) ("flalign" 5) ("split" 6)))) (defun hlatex-replace-eqn () "Replaces \\ [ ... \\ ] with begin-end stuff. Moves point, caller needs to do save-excursion. Bugs: doesn't understand comments or nested \\ [ \\ ] environments." (let (line-end line-start current direction) (setq current (point)) (end-of-line) (setq line-end (point)) (beginning-of-line) (setq line-start (point)) (goto-char current) (if (< current (point-max)) (forward-char)) ;;; find where either \[ or \] begins, set found to be that point (setq found (re-search-backward "\\(\\\\\\[\\|\\\\\\]\\)" line-start t)) (if (not found) (progn (goto-char current) (if (re-search-forward "\\(\\\\\\[\\|\\\\\\]\\)" line-end t) (progn (forward-char -2) (setq found (point)))))) (if found (progn (let ((new-name (hlatex-replace-env-ask "\\[ ... \\]"))) (if (not (equal new-name "")) (progn (if (equal (match-string 1) "\\[") (setq direction +1) (setq direction -1)) (if (= direction 1) (progn (replace-match (concat "\\\\begin{" new-name "}")) (search-forward "\\]" nil t) (replace-match (concat "\\\\end{" new-name "}"))) (replace-match (concat "\\\\end{" new-name "}") t) (search-backward "\\[" nil t) (replace-match (concat "\\\\begin{" new-name "}") t)))))) (message "Not on an environment command.")))) ;;;; BINDINGS (defun hlatex-set-bindings () (global-set-key "\M-[" 'hlatex-braces) (global-set-key "\M-]" 'hlatex-brackets) (global-set-key "\M-4" 'hlatex-math-mode) (global-set-key "\C-cb" 'hlatex-begin-end) (global-set-key "\C-ce" 'hlatex-equations) (global-set-key "\C-cl" 'hlatex-insert-label-string) (global-set-key "\C-cL" 'hlatex-toggle-label-string) (global-set-key "\C-cr" 'hlatex-replace-env) (global-set-key "\C-cm" 'hlatex-find-begin-end)) (add-hook 'tex-mode-hook 'hlatex-set-bindings) ;;;;;;;; This is for all those who are using an ancient version of ;;;;;;;; Emacs (that means Rutgers) (if (not (fboundp 'match-string)) ;; Introduced in Emacs 19.29. (defun match-string (num &optional string) "Return string of text matched by last search. Added by HO. NUM specifies which parenthesized expression in the last regexp. Value is nil if NUMth pair didn't match, or there were less than NUM pairs. Zero means the entire text matched by the whole regexp or whole string. STRING should be given if the last search was by `string-match' on STRING." (if (match-beginning num) (if string (substring string (match-beginning num) (match-end num)) (buffer-substring (match-beginning num) (match-end num))))))