diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/emacs-modes/swiftex |
Initial commit
Diffstat (limited to 'support/emacs-modes/swiftex')
-rw-r--r-- | support/emacs-modes/swiftex/hilit-swiftex.el | 314 | ||||
-rw-r--r-- | support/emacs-modes/swiftex/swiftex-doc.txt | 548 | ||||
-rw-r--r-- | support/emacs-modes/swiftex/swiftex.el | 2170 |
3 files changed, 3032 insertions, 0 deletions
diff --git a/support/emacs-modes/swiftex/hilit-swiftex.el b/support/emacs-modes/swiftex/hilit-swiftex.el new file mode 100644 index 0000000000..cc0ae5979e --- /dev/null +++ b/support/emacs-modes/swiftex/hilit-swiftex.el @@ -0,0 +1,314 @@ +;;; hilit-swiftex.el -- enhancements for highlighting swiftex and doctex +;;; buffers using hilit19.el or hl319.el. +;;; +;;; This file was based on hilit-LaTeX.el v1.05 by Peter Galbraith +;;; (rhogee@bathybius.meteo.mcgill.ca). + +;;; to do: we need to reset the patterns explicitly whenever we +;;; change modes, since we need to eval variables; or can we use +;;; functions? + +;;; need to distinguish doctex from swiftex. + +(require 'hilit19) +(provide 'hilit-swiftex) + +(hilit-set-mode-patterns + '(doctex-mode) + '( + ;; outline heading + ("^\\^\\^A\\*+.*$" nil comment) + ;; comment + ("^\\^\\^A.*$" nil comment) + ;; label + ("\\\\item\\(\\[[^]]*\\]\\)?" nil label) + ;; formula + ("\\\\(" "\\\\)" formula) ; \( \) + ("[^\\\\]\\\\\\[" "\\\\\\]" formula) ; \[ \] but not \\[len] + ("[^$\\]\\($\\($[^$]*\\$\\|[^$]*\\)\\$\\)" 1 formula) ; '$...$' or '$$...$$' + ;; ^^ but not \$102.54 is expensive for a car wash. + ;; crossref + ("|[^|\n]*|" nil crossref) + (hilit-bracket-region + ; (\\(no\\)?cite -> changed to any cite commands that users make up. + "\\\\\\([^ ]*cite\\|\\(page\\)?ref\\|eqref\\|label\\|index\\|glossary\\){" + crossref) + ;; bold + (hilit-bracket-wysiwyg "{\\\\bfseries" bold) + (hilit-bracket-region "\\\\textbf{" bold) + ;; italic + (hilit-bracket-wysiwyg "{\\\\\\(em\\|itshape\\|slshape\\)" italic) + (hilit-bracket-region "\\\\emph{" italic) + (hilit-bracket-region "\\\\text\\(it\\|sl\\){" italic) + ;; include + (hilit-bracket-region "\\\\\\(include\\*?\\|input\\|bibliography\\){" include) + ;; defun + ("\\\\ReserveCS{?\\\\[a-zA-Z-@]+" defun) + ;; FIX, many things don't necessarily have {'s after them. + ;; FIX: global and long;; see doctex + (hilit-bracket-region "\\\\new\\(let\\|tokens\\|length\\|theorem\\|counter\\){" defun) + ;; FIX want to have matching braces... + (hilit-bracket-region "\\\\NewNameDef\\*?{" defun) +;; causes error! + (hilit-bracket-region "\\(\\\\long\\|\\\\global\\)+\\\\def[^}]*{" defun) + ;; keyword + (hilit-bracket-region "\\\\\\(v\\|h\\)space\\(\*\\)?{" keyword) + (hilit-bracket-region "\\\\text\\(tt\\|sf\\|rm\\){" keyword) + (hilit-bracket-region "\\\\footnote\\(mark\\|text\\)?{" keyword) + (hilit-bracket-region + "\\\\\\(sub\\)*\\(paragraph\\|section\\)\\(\*\\|\\[.*\\]\\)?{" keyword) + (hilit-bracket-region + "\\\\\\(chapter\\|part\\)\\(\*\\|\\[.*\\]\\)?{" keyword) + ("\\\\[a-z]+box" nil keyword) + ;; define + ("\\\\\\(setlength\\|settowidth\\|addtolength\\|setcounter\\|addtocounter\\)" nil define) + (hilit-bracket-region "\\\\\\(title\\|author\\|date\\|thanks\\){" define) + ;; decl + ("\\\\\\(scshape\\|bfseries\\|em\\|itshape\\|rmfamily\\|sffamily\\|slshape\\|ttfamily\\)\\b" nil decl) + ("``" "''" string) + ("\\\\\\(raggedright\\|makeindex\\|makeglossary\\|maketitle\\)\\b" nil + decl) + (hilit-bracket-region + "\\\\\\(pagestyle\\|thispagestyle\\|pagenumbering\\|numberwithin\\){" decl) + (hilit-bracket-region "\\\\documentclass\\(\\[.*\\]\\)?{" decl) + (hilit-bracket-region "\\\\\\(begin\\|end\\|nofiles\\|includeonly\\){" + decl) + ("\\\\\\(normalsize\\|small\\|footnotesize\\|scriptsize\\|tiny\\|large\\|Large\\|LARGE\\|huge\\|Huge\\)\\b" nil decl) + ("\\\\\\(smaller\\|larger\\)\\(\\[[^]]*\\]\\)?" nil decl) + (hilit-bracket-region "\\\\relsize{" decl) + ("\\\\\\(appendix\\|tableofcontents\\|listoffigures\\|listoftables\\)\\b" + nil decl) + ;; label + (hilit-bracket-wysiwyg "{\\\\scshape" label) + (hilit-bracket-region "\\\\caption\\(\\[[^]]*\\]\\)?{" label) +)) + +;; avoid message while byte-compiling +(eval-when-compile (defvar hilit-on-the-fly-in-use)) +(hilit-set-mode-patterns + '(swiftex-mode) + '( + ;; outline heading + ("^%\\*+.*$" nil comment) + ("\\(^\\|[^\\]\\)\\(%.*\\)$" 2 comment) ; comments + ;; label + ("\\\\item\\(\\[[^]]*\\]\\)?" nil label) + ;; formula + ("\\\\(" "\\\\)" formula) ; \( \) + ("[^\\\\]\\\\\\[" "\\\\\\]" formula) ; \[ \] but not \\[len] + ("[^$\\]\\($\\($[^$]*\\$\\|[^$]*\\)\\$\\)" 1 formula) ; '$...$' or '$$...$$' + ;; ^^ but not \$102.54 is expensive for a car wash. + ;; crossref + ("|[^|\n]*|" nil crossref) + (hilit-bracket-region + ; (\\(no\\)?cite -> changed to any cite commands that users make up. + "\\\\\\([^ ]*cite\\|\\(page\\)?ref\\|eqref\\|label\\|index\\|glossary\\){" + crossref) + ;; bold + (hilit-bracket-wysiwyg "{\\\\bfseries" bold) + (hilit-bracket-region "\\\\textbf{" bold) + ;; italic + (hilit-bracket-wysiwyg "{\\\\\\(em\\|itshape\\|slshape\\)" italic) + (hilit-bracket-region "\\\\emph{" italic) + (hilit-bracket-region "\\\\text\\(it\\|sl\\){" italic) + ;; include + (hilit-bracket-region "\\\\\\(include\\*?\\|input\\|bibliography\\){" include) + ;; defun + ("\\\\ReserveCS{?\\\\[a-zA-Z-@]+" defun) + ;; FIX, many things don't necessarily have {'s after them. + ;; FIX: global and long ; ak, all messed up. + (hilit-bracket-region + "\\\\\\(re\\)?new\\(environment\\|command\\|let\\)\\\\[.\n]*{" defun) + (hilit-bracket-region "\\\\new\\(let\\|tokens\\|length\\|theorem\\|counter\\){" defun) + ;; FIX want to have matching braces... + (hilit-bracket-region "\\\\NewNameDef\\*?{" defun) +;; changed; see \\long in doctex... + ;; keyword + (hilit-bracket-region "\\\\\\(v\\|h\\)space\\(\*\\)?{" keyword) + (hilit-bracket-region "\\\\text\\(tt\\|sf\\|rm\\){" keyword) + (hilit-bracket-region "\\\\footnote\\(mark\\|text\\)?{" keyword) + (hilit-bracket-region + "\\\\\\(sub\\)*\\(paragraph\\|section\\)\\(\*\\|\\[.*\\]\\)?{" keyword) + (hilit-bracket-region + "\\\\\\(chapter\\|part\\)\\(\*\\|\\[.*\\]\\)?{" keyword) + ("\\\\[a-z]+box" nil keyword) + ;; define + ("\\\\\\(setlength\\|settowidth\\|addtolength\\|setcounter\\|addtocounter\\)" nil define) + (hilit-bracket-region "\\\\\\(title\\|author\\|date\\|thanks\\){" define) + ;; decl + ("\\\\\\(scshape\\|bfseries\\|em\\|itshape\\|rmfamily\\|sffamily\\|slshape\\|ttfamily\\)\\b" nil decl) + ("``" "''" string) + ("\\\\\\(raggedright\\|makeindex\\|makeglossary\\|maketitle\\)\\b" nil + decl) + (hilit-bracket-region + "\\\\\\(pagestyle\\|thispagestyle\\|pagenumbering\\|numberwithin\\){" decl) + (hilit-bracket-region "\\\\documentclass\\(\\[.*\\]\\)?{" decl) + (hilit-bracket-region "\\\\\\(begin\\|end\\|nofiles\\|includeonly\\){" + decl) + ("\\\\\\(normalsize\\|small\\|footnotesize\\|scriptsize\\|tiny\\|large\\|Large\\|LARGE\\|huge\\|Huge\\)\\b" nil decl) + ("\\\\\\(smaller\\|larger\\)\\(\\[[^]]*\\]\\)?" nil decl) + (hilit-bracket-region "\\\\relsize{" decl) + ("\\\\\\(appendix\\|tableofcontents\\|listoffigures\\|listoftables\\)\\b" + nil decl) + ;; label + (hilit-bracket-wysiwyg "{\\\\scshape" label) + (hilit-bracket-region "\\\\caption\\(\\[[^]]*\\]\\)?{" label) +)) + +;; define decl label keyword bold italic comment string include + +;; from the hilit-src + ;; and anotherone for LaTeX +;; (crossref DarkGoldenrod Goldenrod underline) +;; (formula Goldenrod DarkGoldenrod underline) +;; + +; ;; comments +; ("[^\\]%.*$" nil comment) + +; ;; the following two match \foo[xx]{xx} or \foo*{xx} or \foo{xx} +; ("\\\\\\(sub\\)*\\(paragraph\\|section\\)\\(\*\\|\\[.*\\]\\)?{" "}" +; keyword) +; ("\\\\\\(chapter\\|part\\)\\(\*\\|\\[.*\\]\\)?{" "}" keyword) +; ("\\\\footnote\\(mark\\|text\\)?{" "}" keyword) +; ("\\\\[a-z]+box" nil keyword) +; ("\\\\\\(v\\|h\\)space\\(\*\\)?{" "}" keyword) + +; ;; (re-)define new commands/environments/counters +; ("\\\\\\(re\\)?new\\(environment\\|command\\){" "}" defun) +; ("\\\\new\\(length\\|theorem\\|counter\\){" "}" defun) + +; ;; various declarations/definitions +; ("\\\\\\(setlength\\|settowidth\\|addtolength\\|setcounter\\|addtocounter\\)" nil define) +; ("\\\\\\(title\\|author\\|date\\|thanks\\){" "}" define) + +; ("\\\\documentstyle\\(\\[.*\\]\\)?{" "}" decl) +; ("\\\\\\(begin\\|end\\|nofiles\\|includeonly\\){" "}" decl) +; ("\\\\\\(raggedright\\|makeindex\\|makeglossary\\|maketitle\\)\\b" nil +; decl) +; ("\\\\\\(pagestyle\\|thispagestyle\\|pagenumbering\\){" "}" decl) +; ("\\\\\\(normalsize\\|small\\|footnotesize\\|scriptsize\\|tiny\\|large\\|Large\\|LARGE\\|huge\\|Huge\\)\\b" nil decl) +; ("\\\\\\(appendix\\|tableofcontents\\|listoffigures\\|listoftables\\)\\b" +; nil decl) +; ("\\\\\\(bf\\|em\\|it\\|rm\\|sf\\|sl\\|ss\\|tt\\)\\b" nil decl) + +; ;; label-like things +; ("\\\\item\\(\\[[^]]*\\]\\)?" nil label) +; ("\\\\caption\\(\\[[^]]*\\]\\)?{" "}" label) + +; ;; formulas +; ("[^\\]\\\\(" "\\\\)" formula) ; \( \) +; ("[^\\]\\\\\\[" "\\\\\\]" formula) ; \[ \] +; ("[^\\$]\\($\\($[^$]*\\$\\|[^$]*\\)\\$\\)" 1 formula) ; '$...$' or '$$...$$' + +; ;; things that bring in external files +; ("\\\\\\(include\\|input\\|bibliography\\){" "}" include) + +; ;; "wysiwyg" emphasis -- these don't work with nested expressions +; ;; ("{\\\\\\(em\\|it\\|sl\\)" "}" italic) +; ;; ("{\\\\bf" "}" bold) + +; ("``" "''" string) + +; ;; things that do some sort of cross-reference +; ("\\\\\\(\\(no\\)?cite\\|\\(page\\)?ref\\|label\\|index\\|glossary\\){" "}" crossref) +; )) + + (defun hilit-bracket-region (open) + "Find region within curly brackets for hilit pattern. +ARG is pattern for beginning of pattern and ends with {. +It cannot be white space. +Patterns ends simply at the matching closing bracket. + +Used for patterns like: + \\chapter{stuff with matching {} ending with }" + (if (re-search-forward open nil t) + (let ((here (point)) + (the-start (match-beginning 0))) + (backward-char 1) ; point is on bracket + (if hilit-on-the-fly-in-use + ;; if hl319's on-the-fly hilighting is in use then we can't use + ;; forward-list because it uses the built-in scan-lists and will + ;; return an error if there is no matching bracket yet. + (re-search-forward "}" + (progn (save-excursion (end-of-line)(point))) + 1) + (forward-list 1)) + (cons the-start (point))))) + +(defun hilit-inside-bracket-region (open) + "Find region within curly brackets for hilit pattern. +ARG is pattern for beginning of pattern and ends with {. +It cannot be white space. +Patterns ends simply at the matching closing bracket. + +Used for patterns like: + \\textbf{only stuff within bracket is highlited}" + (if (re-search-forward open nil t) + (let ((the-start (point))) + (backward-char 1) ; point is on bracket + (if hilit-on-the-fly-in-use + (re-search-forward "}" + (progn (save-excursion (end-of-line)(point))) 1) + (forward-list 1)) + (backward-char 1) + (cons the-start (point))))) + +(defun hilit-inside-environment (open) + "Find region within curly brackets for hilit pattern. +ARG is pattern for \\begin{something}." + (if (re-search-forward open nil t) + (let ((the-start (progn (forward-line 1)(point))) ;Start on next line + (the-end)) + (re-search-backward "\\\\begin{\\([^}]+\\)}" nil t) + (setq the-end (buffer-substring (match-beginning 1)(match-end 1))) + (if hilit-on-the-fly-in-use ;Search no more than 10 lines away + (if (search-forward (concat "\\end{" the-end "}") + (save-excursion (forward-line 10)(point)) t) + (cons the-start (progn (forward-line -1)(point))) + (cons the-start the-start)) + (if (search-forward (concat "\\end{" the-end "}") nil t) + (cons the-start (match-beginning 0)) + (end-of-line) ;Move off the \begin{} + (cons the-start the-start)))))) + +(defun hilit-bracket-wysiwyg (open) + "Find region within curly brackets for hilit pattern. +ARG is pattern for beginning of pattern and starts with {. +Patterns ends simply at the matching closing bracket. + +Used for patterns like: + {\em stuff with matching {} ending with }" + (if (re-search-forward open nil t) + (let ((the-start (match-beginning 0))) + (goto-char the-start) + (if hilit-on-the-fly-in-use + (re-search-forward "}" + (progn (save-excursion (end-of-line)(point))) 1) + (forward-list 1)) + (cons the-start (point))))) + + +(defvar hilit-AmSLaTeX-commands nil + "*Set to t if you want to hightlight AmSLaTeX commands.") + +(defvar hilit-multilingual-strings nil + "*Set to t if you want to hightlight multilingual quoted strings. +Highlights: \"`german\"', \"< french \">, << french >> and + 8-bit french ;.") + +(defvar hilit-LaTeX-commands t + "*Set to nil if you don't want to highlight unknown LaTeX commands") + +(defvar hilit-on-the-fly-in-use nil + "Used by hilit-LaTeX to determine if on-the-fly hilighting is in use") + +;; I need to modify hl319.el's on-the-fly highlighter so that it +;; tells me on-the-fly highlighting is in use. +(defun hilit-rehighlight-changed-lines (st en len) + "Quietly rehighlight just this line. +Useful as an after change hook in VM/gnus summary buffers and dired buffers." + (save-match-data + (let ((hilit-on-the-fly-in-use t)) + ;; (> (- en st) hilit-max-change-rehighlight) + (hilit-rehighlight-region st en 'quietly)))) + diff --git a/support/emacs-modes/swiftex/swiftex-doc.txt b/support/emacs-modes/swiftex/swiftex-doc.txt new file mode 100644 index 0000000000..30f73281a8 --- /dev/null +++ b/support/emacs-modes/swiftex/swiftex-doc.txt @@ -0,0 +1,548 @@ + swiftex.el documentation + +I mean to turn this into .info documentation in the future, but for now, it is +a plain text file in outline mode. + + +* introduction + +This file defines major modes swifTeX and docTeX. + +swifTeX mode is for editing buffers containing normal LaTeX files, and +provides an alternative to the LaTeX modes in the standard Emacs +distribution and the AUC TeX package. + +docTeX mode is for editing buffers containing self-documenting LaTeX code +that uses the package "doc.sty", including the document class "ltxdoc". +For these buffers, docTeX mode is significantly more useful than the +alternatives provided by standard Emacs and AUC TeX. + +These modes are derived from the LaTeX mode in "tex-mode.el" (distributed with +GNU Emacs) using the autoloaded function `define-derived-mode' in "derived.el" +(also part of the standard distribution). FIX: untested-by-me option to derive +from AUC-TeX's latex-mode. + +The principal additions allow convenient, precise control over filling and +outlining. There are also a few improvements to help save typing. In +docTeX mode, functions are provided to help manage LaTeX file versions, +dates, and CheckSums. These major modes are fully documented and +well-tested. + +* installation + +Swiftex.el should work with GNU Emacs version 20.x. If it doesn't, tell me +about it! I don't have the resources to ensure it works on other versions of +Emacs. There used to be a byte-compilation problem on versions through 19.28. +I no longer include information on it because I don't think it works on 19.x +any more anyway. + +Put swiftex.el where your Emacs can find it, that is, somewhere in the +directories in `load-path'. Byte-compile it with `M-x byte-compile-file'. + +You will then probably want to make some additions to your Emacs startup code +(i.e., your .emacs file): + +(1) You should either load "swiftex.el" immediately or cause it to be loaded as + necessary. Otherwise you will have to load it manually before using it. To + load it immediately: + + (require 'swiftex) + + To load it whenever `doctex-mode' or `swiftex-mode' is called: + + (autoload 'doctex-mode "swiftex" "Major mode for LaTeX doc documents.") + (autoload 'swiftex-mode "swiftex" "Major mode for LaTeX documents.") + +(2) There are two ways to have Emacs use swifTeX mode or docTeX mode + automatically when you work on certain documents. + + (A) On a per-file basis with Emacs file variables. FIX: link to emacs.info + node. + + Example: at the end of your file, put: + + % Local Variables: + % mode: doctex + % End: + + (B) By file extension by setting the Emacs variable `auto-mode-alist'. You + may set this variable any way you like, but a particularly convenient + way is to use the function `stx-merge-list' defined in "swiftex.el". + Here is an example using `stx-merge-list'. Each entry is independent + and can be omitted according to your preferences. + + ;; These will only replace entries in `auto-mode-alist' with identical + ;; first parts; watch out for ending up with two entries that match the + ;; same file names, such as "\\.tex$" and "\\.tex\\'". + (stx-merge-list auto-mode-alist '( + ("\\.bbl\\'" . swiftex-mode) ("\\.aux\\'" . swiftex-mode) + ("\\.tex\\'" . swiftex-mode) ("\\.ins\\'" . swiftex-mode) + ("\\.ltx\\'" . swiftex-mode) ("\\.bbl\\'" . swiftex-mode) + ("\\.cls\\'" . doctex-mode) ("\\.sty\\'" . doctex-mode) + ("\\.dtx\\'" . doctex-mode) ("\\.fdd\\'" . doctex-mode) + ("\\.sto\\'" . doctex-mode) ("\\.clo\\'" . doctex-mode) + ("\\.cfg\\'" . doctex-mode) + )) + + You can't use `stx-merge-list' until it is defined, so you may just + want to copy the definition into your .emacs file before you set + `auto-mode-alist': + + (defun stx-merge-list (old new &optional warn) + "Modify a list OLD to include all NEW's elements not in OLD. + Compare elements with `equal'. New elements are added at the end of + OLD. NEW and OLD can be both lists or both alists. Returns OLD. + + For alists, add NEW's keys to end of OLD if OLD does not have them. + If OLD does have a matching key, change its value to NEW's value for + that key. The key is the car of each element, the value is the cdr. + + Optional WARN non-nil means print a message whenever the value of one + of OLD's keys is being replaced." + (let ((p (listp (car old)))) + ;; check by checking first element that args are either both lists or both + ;; alists + (if (not (eq p (listp (car new)))) + (error "Args to `merge-list' must be both lists or both alists!") + (if p + ;; `old' and `new' are alists + (while new + (let* ((new-cons (car new)) + (old-cons (assoc (car new-cons) old))) + (if old-cons + (progn + (if warn + (message "merge-list: changing value of key %s from %s to %s" + (car old-cons) (cdr old-cons) (cdr new-cons))) + (setcdr old-cons (cdr new-cons))) + (nconc old (list new-cons))) + (setq new (cdr new)))) + ;; `old' and `new' are regular lists + (while new + (let ((x (car new))) + (or (member x old) + (nconc old (list x))) + (setq new (cdr new))))) + old))) + +(3) You may want to modify some key bindings. By convention, a major mode does + not bind `C-c <letter>' by default, since these are reserved for users. + But you are a user, so bind away. Here are the bindings I like to use: + + (eval-after-load "swiftex" '(swiftex-startup)) + (defun swiftex-startup () + "Onetime customization to be executed after loading \"swiftex.el\"." + (define-key swiftex-mode-map "\C-cm" 'stx-emphasize) + (define-key swiftex-mode-map "\C-ce" 'stx-close-block-from-inside) + (define-key swiftex-mode-map "\C-cB" 'stx-insert-block) + (define-key swiftex-mode-map "\C-cb" 'stx-begin-block) + ;; These next three are the way I like to use braces, but this is best + ;; left as a personal choice. + (define-key swiftex-mode-map "\M-{" 'self-insert-command) + (define-key swiftex-mode-map "\M-}" 'self-insert-command) + (define-key swiftex-mode-map "{" 'tex-insert-braces)) + +* use with AUC-TeX + +Having swiftex and auctex both loaded causes problems for me, though you might +want to try setting some variable (I forget which) in swiftex that will derive +from auctex. + +I do the following to run AUC-TeX and swiftex in different Emacs processes. I +put the following into my .emacs: + + ;; Code in dotemacs will do the right thing with these variables. + (defvar dte-have-swiftex-flag t + "*Non-nil means swiftex.el available on system.") + + (defvar dte-have-auctex-flag t + "*Non-nil means AUC-TeX available on system.") + + ;; `dte-use-swiftex-flag' is not defined non-nil in this file. It might be + ;; defined on the command line, however. + (defvar dte-use-swiftex-flag nil + "Non-nil means Emacs should use swiftex.") + + (defun dte-load-swiftex () + "Load \"swiftex\"." + (require 'swiftex) + (merge-list auto-mode-alist '( + ("\\.cls\\'" . doctex-mode) + ("\\.sty\\'" . doctex-mode) + ("\\.sto\\'" . doctex-mode) + ("\\.clo\\'" . doctex-mode) + ("\\.cfg\\'" . doctex-mode) + ("\\.dtx\\'" . doctex-mode) + ("\\.fdd\\'" . doctex-mode) + ("\\.hlx\\'" . swiftex-mode) + ("\\.bbl\\'" . swiftex-mode) + ("\\.tex\\'" . swiftex-mode) + ("\\.ltx\\'" . swiftex-mode) + ("\\.ins\\'" . swiftex-mode) + ("\\.aux\\'" . swiftex-mode) + ("\\.cfq\\'" . swiftex-mode) + ("\\.stq\\'" . swiftex-mode) + ("\\.clq\\'" . swiftex-mode) + ("\\.soq\\'" . swiftex-mode) + ("\\.coq\\'" . swiftex-mode) + ("\\.tmpl\\'" . swiftex-mode) + ("\\.notes\\'" . swiftex-mode) + ) t)) + + (unless (boundp 'dte-no-server-flag) + (gnuserv-start)) + + (if dte-have-swiftex-flag + (if dte-have-auctex-flag + (if dte-use-swiftex-flag + ;; have both, use swiftex + (dte-load-swiftex) + ;; have both, don't use swiftex + (require 'tex-site)) + ;; have swiftex not auctex + (dte-load-swiftex)) + (when dte-have-auctex-flag + ;; have auctex not swiftex + (require 'tex-site))) + + +Then I run my Emacs processes that use swiftex with a shell wrapper. Pared down: + + #!/bin/sh + + # Run emacs with special arguments. + + # $1 is a flag. + + # If this is called from a fvwm menu item, then we don't have + # .bashrc-interactive in the environment. We want to load .bash-dirs whether + # we do or not, since some of the values depend on the setting of + # SHELL_FOR_EMACS. + SHELL_FOR_EMACS=true + if [ -e ~/.bash-dirs ]; then + set -a + source ~/.bash-dirs + set +a + fi + unset SHELL_FOR_EMACS + + # We remove . from PATH if it's there. + export PATH=$(echo $PATH | sed -e 's/^.://' -e 's/:.:/:/') + + #debug=true + + function runemacs () { + dinit= + if [ $debug ]; then + dinit=--debug-init + echo emacs-wrap args: + echo $dinit + let i=1 + for x; do + echo $i: $x + let i+=1 + done + fi + exec emacs $dinit "$@" + } + + # Quoting is a nightmare: + + # eval runemacs -q $profile_A '"$@"';; + + # to get the quotes within profile_A to be understood, we need the eval, and it + # won't work (God knows why) if you write $(eval echo $profile_A) or + # $(echo $profile_A). But if you eval the "$@" (which needs the double quotes + # or it will expand as one word) you will screw up any quotes given on the + # command line, so you need to protect it from the eval with the single quotes. + + # you can leave out the no-desktop flag if you like, it doesn't matter. i + # didn't include the code that looks at it above. + + case "$1" in + [Ss]wiftex) + shift + # cd somewhere if you like, esp. if you use the desktop features + eval runemacs \ + -q \ + '--title="Emacs with swiftex"' \ + --eval "'(progn + (setq dte-no-server-flag t + dte-no-desktop-flag t + dte-use-swiftex-flag t) + (load \"~/.emacs\"))'" \ + '"$@"';; + + *) shift + eval runemacs \ + -q \ + --eval "'(progn + (setq dte-no-desktop-flag t + dte-no-server-flag t) + (load \"~/.emacs\"))'" \ + '"$@"';; + esac + + + +* user documentation + +The user functions are described below. Next to each one is the initial +key binding, or a suggested one in parentheses, or both. A major mode +should by convention not bind `C-c <letter>'; where this is my preferred +binding, I have put the binding in the comments in the installation +section above, and left the function unbound, or in one case bound to a +similar key (these are given in parentheses below). + +** SWIFTEX MODE + +swifTeX mode provides all the functionality of the standard LaTeX mode, +plus the following extensions. In the case of tab and C-c C-u, the +standard functions have been replaced by improved ones that act similarly. + +*** swiftex-mode + +Select swifTeX major mode. See the installation section for useful ways to +have swifTeX mode come up automatically. + +*** stx-tab: TAB + +If you have tabs in a LaTeX buffer, you will be in for some surprises when +you use a verbatim environment, so we define a tabbing command that only +inserts spaces. I can't imagine that the savings of file-size is at all +significant using tabs instead of spaces. If you decide to reinstate +normal tabbing for swifTeX mode, I strongly recommend you keep the new +space-tabbing for docTeX mode because every macrocode environment is a +verbatim environment. + +*** comment-region: C-c ; + +Make the region into a comment. Prefix arg means add +that many %'s, default 1. + +*** stx-next-braces: M-TAB and C-c TAB + +Move point out of the current set of braces and just into the next. + +*** stx-up-block: C-c C-] + +Move point out of the current \begin{}-\end{} environment. + +*** stx-begin-block: (C-c b) + +Insert \begin{*} and leave point where the * is. + +*** OTHERS + + stx-insert-block: (C-c B) + +Alias for standard `tex-latex-block'. Create a matching pair of +\begin{}-\end{} lines and leave point on a blank line between them. + + stx-close-block-from-inside: (C-c e) + +Let * be the point. +Initial buffer contents: + + \begin{text*} + +Final buffer contents: + + \begin{text} + * + \end{text} + + stx-close-block: C-c C-e + +Close the last unclosed \begin{}. Duplicate any legal prefix to the +\begin{}. + + stx-goto-prev-unended-begin: C-c C-u + stx-goto-first-unended-begin: C-c C-u + +Move point to the previous or first unended \begin{} above. + + stx-goto-next-unbegun-end: C-c C-n + stx-goto-last-unbegun-end: C-c M-n + +Move point to the next or last unbegun \end{} below. + + stx-verify-blocks: (C-c v) + +Move point to a mismatched \\begin .. \\end block. + + stx-emphasize: (C-c m) + +Insert \emph{*} and leave point at *. + + stx-emphasize-word: C-c C-m + +Surround word at point with "\emph{" and "}". + + dtx-insert-change: C-c C-d g - + add-change-log-entry: C-c C-d l - + dtx-get-fileinfo: C-c C-d v - + dtx-update-minor-version: C-c C-d u - + dtx-update-major-version: C-c C-d U - + dtx-update-documentation-date: C-c C-d D - + dtx-update-checksum: C-c C-d s - + +All the docTeX mode commands that make sense to use in a swifTeX buffer are +available on the same keys as in docTeX mode. See below for what these +functions do. + +** DOCTEX MODE + +docTeX mode is intended to behave as you would want swifTeX mode to behave +in doc.sty buffers. The argument to \CheckSum is automatically updated +every time you save the buffer. + +*** doctex-mode + +Select docTeX major mode. See the installation section for useful ways to +have docTeX mode come up automatically. + + dtx-update-minor-version: C-c C-d u + dtx-update-major-version: C-c C-d U + dtx-update-documentation-date: C-c C-d d + dtx-update-checksum: C-c C-d s + +A <version> is any number of integers separated by dots with an optional +prefix "v" and an optional suffix of a lowercase letter. When the version +is updated, a "v" will always be added. + +The major and minor version strings will be the last two elements of the +<version>, counting the possible suffix as a separate element if it exists. + +Updates are done by looking for the first line that defines \fileversion in +the buffer (with \def or \newcommand) as a <version>. If no such line +exists, then the line with \ProvidesPackage or \ProvidesClass is updated. + +When the major or minor version is updated, the date is updated. Once +again, this is either the line defining \filedate, or the right part of the +\ProvidesPackage (or Class) optional argument. + +The documentation date is similarly found on the line defining \docdate. + +The checksum is automatically updated whenever any of the above are +updated, and it can be updated on its own with `dtx-update-checksum'. + +When the `minor-version' is a letter, the minor-version is treated a little +differently. + +When the `minor-version' is a letter, doctex treats it as an indication +consistent with the idea of a trivial revision that does not affect +function (e.g., correcting typos in the documentation). + +If the minor version is a letter, updating minor-version increments the +letter; updating major-version increments the number before it and sets the +minor-version to null. This is fine and good. + +After a major update, however, there will be no letter at all, so the next +minor update will increment the final number (i.e. the number the last +major update incremented), and the next major update will increment the +number BEFORE that. Examples: + + VER UPDATE-TYPE NEW-VER +====================================== + 1.3a minor 1.3b + 1.3b major 1.4 + 1.4 minor 1.5 + 1.5 major 2 + 2 major 3 + 3 minor 3<prompt user> + +If your minor version is a letter, you should think of minor updates as +incrementing the trivial revision and major updates as incrementing the +minor version. If you want to increment the major version and your minor +version is currently a letter, you need to do a major update TWICE: + + 1.3a -> 1.4 -> 2.0 + +In case you're wondering, if we DIDN'T do things this way, then there would +be inconsistent behavior between the case where you do an update having +already executed one previously in the same buffer in the same session of +Emacs, and the case where you are performing the first update of the +editing session on the buffer. Really I should build in a separate concept +and update command for "trivial revision", but for the moment this is how +things work, so I'm documenting it. + +*** dtx-get-fileinfo: C-c C-d v + +Display current file version, date, and documentation date in the +minibuffer. + +*** OTHERS + + dtx-begin-FOO + dtx-insert-FOO + + FOO begin insert +======================================= +macrocode: C-c C-d c C-c C-d C +macro: C-c C-d m C-c C-d M +environment: C-c C-d e C-c C-d E +bibfunction: C-c C-d f C-c C-d F + +The `insert' commands (on uppercase keys) begin and end the relevant +environment, and leave point in between\; `begin' commands (on lowercase +keys) just begin the environment. All prompt for the name of the thing in +question. + + dtx-interrupt-macrocode: C-c C-d i + +Interrupt a macrocode environment to add some commentary. + + add-change-log-entry: C-c C-d l + +Make a ChangeLog entry. + + dtx-insert-change: C-c C-d g + +Insert a LaTeX changes entry, prompting for the description of the change. + + +** SYMBOL NAMES + +The prefixes `stx' or `dtx' begin all new symbol names, the latter being +for symbols unique to docTeX mode. + +There are some exceptions that begin with `swiftex', `doctex', or `rx'. +The actual mode functions, the mode maps, and variables holding the prefix +keys are: + `swiftex-mode' `doctex-mode' + `swiftex-mode-map' `doctex-mode-map' + `swiftex-dtx-mode-map' `doctex-dtx-mode-map' + `swiftex-dtx-prefix-key' `doctex-dtx-prefix-key'. + +A group of constants have names beginning with `rx', short for "regexp". + +* history + +These major modes are the result of my own hacking over the years, adding +to the standard LaTeX major mode. + +Once I got used to my developments, switching to AUC TeX seemed like too +much of a pain in the neck. Perhaps someone will add this functionality to +AUC TeX's one day. I don't expect many people besides me will want to use +swifTeX mode except in its extension of docTeX mode. Do let me know if you +like swifTeX mode. + +DocTeX mode began its life on 14 November 1994 when David Carlisle of the +LaTeX 3 team gave me some Emacs lisp code written by fellow LaTeX 3 team +members Johanes Brahms and Frank Mittelbach. That file contained functions +for handling version control, inserting changes, and inserting the basic +doc.sty environments. I used these functions to build a new major mode in +parallel with swifTeX mode. + +The original authors have given me permission to incorporate their code in +mine, but they do not otherwise endorse or support swiftex.el. I, Matt +Swift <swift@alum.mit.edu>, am the sole maintainer. + +* future + +* Emacs File Variables + +Local Variables: +mode: outline +End: diff --git a/support/emacs-modes/swiftex/swiftex.el b/support/emacs-modes/swiftex/swiftex.el new file mode 100644 index 0000000000..8b529f2ef2 --- /dev/null +++ b/support/emacs-modes/swiftex/swiftex.el @@ -0,0 +1,2170 @@ +;;; swiftex.el --- major modes for LaTeX and LaTeX doc.sty documents + +;;;; COPYRIGHT NOTICE +;;; +;;; Copyright (C) 1995-1999 Matt Swift <swift@alum.mit.edu> +;;; +;; Author: Matt Swift <swift@alum.mit.edu> +;; Johanes Braams +;; Frank Mittelbach +;; Maintainer: Matt Swift <swift@alum.mit.edu> +;; Version: $Revision: 1.35 $ +;; Keywords: tex, latex, doc, doc.sty, ltxdoc, dtx +;; LCD Archive Entry: +;; swifTeX|Matt Swift|swift@alum.mit.edu| +;; Major modes for LaTeX and LaTeX doc.sty documents| +;; $Date: 2001/09/08 04:09:20 $|$Revision: 1.35 $|~/modes/swiftex.el.gz| + +;;; FIX last field above will be wrong when I have a tarball + +;;; +;;; 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 your Emacs program (without which this file is not useful); if not, +;;; write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA +;;; 02139, USA. +;;; + +;;; Commentary: +;;; +;;; This file is distributed by its author with +;;; documentation in Info format that gives an overview of +;;; this package and describes how to install and use and +;;; use it. It should always be available on CTAN FIX. + +;;;; IMPLEMENTATION +;;; CODE: + +;;;;; CUSTOMIZATION GROUPS + +;;; :prefix "swiftex-" +;;; :prefix "doctex-" +;;; :prefix "rx-" +;;; :prefix "stx-" +;;; :prefix "dtx-" +(defgroup swiftex nil + "Major mode for LaTeX files." + :tag "SwifTeX" + :group 'tex) +(defgroup doctex nil + "Major mode for documented LaTeX source (.dtx) files." + :tag "docTeX" + :group 'tex + :group 'swiftex) + +;;; (defcustom +;;; :type +;;; :options +;;; :version +;;; :set +;;; :get +;;; :initialize +;;; ;; customize common options +;;; :tag +;;; :group +;;; :link +;;; :load +;;; :require + +;;;;; VERSION INFO +(defconst stx-version "$Revision: 1.35 $" + "RCS version of Matt's master swiftex.el.") + +;;;;; GENERAL ACTIONS +(provide 'swiftex) + +;;; Patch below from Karl Eichwalder <ke@gnu.franken.de>. I think I want to do +;;; it differently eventually. +;;; FIX Don't customize for now, since it may disappear. +(defvar stx-tex-derivation t + "Non-nil means use Emacs standard `tex-mode' for derivation. +Nil means use `LaTeX-mode' from the AUC TeX package.") +(if stx-tex-derivation + (require 'tex-mode) + (require 'tex-site)) + +;;; `concat-chars' was renamed to `string' in 20.3 +;;; thanks to Peter Møller Neergaard for noticing this +(if (not (fboundp 'string)) + (if (fboundp 'concat-chars) + (defalias 'string 'concat-chars) + (error + "I'm confused: neither `string' nor `concat-chars' are defined as functions"))) + +;;; FIX perhaps a variable to let the user choose between old and new outlines? +;;; do i depend on the old one? yes probably that is all +;;; the weird boln stuff +(require 'outline) + +;;;;; REGEXP and STRING CONSTANTS + +;;; Using the following constants makes regular expressions more readable in +;;; Lisp code. + +(defconst rx-whitespace "\\s-*" + "Matches optional characters with whitespace syntax.") +;;; Notice that this expression has parentheses. + +;;; FIX: As defined, it will match two newlines in a row, which is a +;;; construction that would break TeX in the places where I use it. I would +;;; like to improve the matching to avoid matching this case, to be consistent +;;; with TeX, but I don't see that it will cause a problem under normal +;;; circumstances. +(defconst rx-whitespace-newlines "\\(\\s-\\|\n\\)*" + "Matches optional newlines or characters with whitespace syntax.") +(defconst rx-normal-boln "^" + "Matches unhidden beginning of line.") +(defconst rx-hidden-boln "[\^M]" + "Matches beginning of line when hidden in outline mode.") +(defconst rx-eoln "$") +(defconst rx-normal-whiteln (concat rx-normal-boln rx-whitespace rx-eoln) + "Matches empty line.") +(defconst rx-dollar "\\$" + "Matches a dollar sign.") +(defconst rx-backslash "\\\\" + "Matches a backslash.") +(defconst rx-white-back (concat rx-whitespace rx-backslash) + "Matches optional whitespace plus backslash." ) +(defconst rx-or "\\|" + "Regexp string for logical OR." ) +;;; FIX: why do I need to avoid the \n? It's only working for top-level! +;;; don't I mean innermost level? +(defconst rx-brace-pair "{[^}\n]*}" + "Matches one balanced set of braces on a single line.") +(defconst rx-tex-grop "{" + "Matches TeX group open character -- a left brace. +Also (ab)used as a regular string for insertions.") +(defconst rx-tex-grcl "}" + "Matches TeX group close character -- a right brace. +Also (ab)used as a regular string for insertions.") +(defconst stx-str-backslash "\\") + +;;;;; FONT LOCK / HILIT STUFF [FIX] + +;;; FIX: what do I mean 'loads'? +;;; FIX: :require? +(defvar stx-hilit nil + "Non-nil means highlight swifTeX buffers. +Loads the library \"hilit-swiftex\".") + +;;; FIX: what do I mean 'loads'? +;;; FIX: :require? +;;; FIX: link to font-latex customization? +(defvar stx-fontlock t + "Non-nil means font lock swifTeX buffers. +Loads the library \"font-latex\" until I write something +better.") + +;;; FIX what does load mean? :require? +(defvar dtx-hilit t + "Non-nil means highlight docTeX buffers. +Loads the library \"hilit-doctex\".") + +;;; FIX what does load mean? +;;; FIX :require? +;;; FIX :link to font-latex customization? +(defvar dtx-fontlock nil + "Non-nil means font-lock docTeX buffers. +Loads library \"font-latex\" for now until I write something +better.") + + +;;;;; SWIFTEX MODE + +;;; Much of what is defined below takes docTeX mode into account. Only the +;;; code forms unique to docTeX mode appear in its section below. + +;;;;;; USER VARIABLES + +;;; FIX: use :set? +;;; FIX: link? +(defcustom stx-sectioning-offset 1 + "Outline level offset for all LaTeX sectioning commands in a swifTeX buffer. +See the documentation for the variable +`stx-sectioning-commands'." + :tag "Sectioning Commands Outline Offset" + :type 'integer + :group 'swiftex) + +;;; FIX; :set? +(defcustom stx-sectioning-commands '( + ("documentclass" . -2) + ;; leave off final + ;; closebrace, since the + ;; \> in the regexp needs + ;; to match the end of the + ;; strings here. + ("begin{document" . -2) + ("end{document" . -2) + ("endinput" . -2) + ("part" . -2) + ("frontmatter" . -1) + ("mainmatter" . -1) + ("backmatter" . -1) + ("chapter" . -1) + ("appendix" . -1) + ("section". 0) + ("subsection" . 1) + ("subsubsection" . 2) + ("paragraph" . 3) + ("subparagraph" . 4) + ) + "An alist of LaTeX sectioning commands with level offsets for swifTeX mode. +List elements have the form (NAME . NUMBER) where NAME is a +string containing a LaTeX sectioning command with no +backslash, and NUMBER is an integer which will be added to +`stx-sectioning-offset' to get the actual outline level. + +The normal top level outline heading of `%*' is level 1. +The default offset for \"\\section\" is 0, so its default +level is 1. For documents with chapters, you might want to +set `stx-sectioning-offset' to 2, so that \"\\chapter\" and +\"%*\" are the same level. + +An \"endinput\" entry, if it exists, is treated specially in +docTeX mode. See `stx-set-outlinevars'. + +Changes to `stx-sectioning-offset' and +`stx-sectioning-commands' will take effect the next time +`stx-set-vars' is called. This is normally called only by +`swiftex-mode' and `doctex-mode'." + :tag "Sectioning Commands and Levels" + :type '(repeat (cons :format "%v" + (string :tag "Command") + (integer :tag "Level"))) + :group 'swiftex) +;;; FIX I don't understand :format, I copied this from +;;; somewhere. + +;;; FIX: no nil +;;; FIX: use :set? +(defcustom stx-command-parseps '( + ;;; below ought to match first section of + ;;; `stx-command-parstarts' exactly: + "begin" "end" + "par" + "opening" "closing" + "\\[" "caption" "label" "usepackage" + "documentclass" "date" "author" "title" + "tableofcontents" "maketitle" + "listoffigures" "listoftables" + "bibliographystyle" "bibliography" + "include" "input" "includeonly" + "frontmatter" "mainmatter" "backmatter" + ) + "A list of LaTeX commands which separate Emacs paragraphs. + +When these regexp strings occur after optional whitespace +and a backslash, they separate Emacs paragraphs, and resist +being filled. + +More specifically, `stx-set-vars' combines these strings +with logical OR (`rx-or') and tacks them on to +`paragraph-separate' with a logical OR. + +Sectioning commands included in `stx-sectioning-commands' +don't need to be given here because they will get into +`paragraph-separate' via `outline-regexp'. + +Do not make this list nil. + +Do not add strings to this variable without also adding them +to `stx-command-parstarts', or otherwise ensuring they make +it into `paragraph-start'." + :tag "Paragraph Separating Commands" + :type '(repeat regexp) + :group 'swiftex + :group 'doctex) +(make-variable-buffer-local 'stx-command-parseps) + +;;; FIX: no nil +;;; FIX: use :set? +(defcustom stx-command-parstarts '( + ;;; below do not receive fill text: + "begin" "end" + "par" + "opening" "closing" + "\\[" "caption" "label" "usepackage" + "documentclass" "date" "author" "title" + "lastchange" "copystyle" "usepackage" + "tableofcontents" "maketitle" + "listoffigures" "listoftables" + "bibliographystyle" "bibliography" + "include" "input" "includeonly" + "frontmatter" "mainmatter" "backmatter" + ;;; below receive fill text: + "indent" "noindent" + "bibitem" "item" + "footnote" + ) + + "A list of LaTeX commands which start or separate Emacs paragraphs. + +When these regexps occur after a backslash at the beginning +of a line, those that are not in `paragraph-separate' start +Emacs paragraphs, and receive text on their line during +fills. + +Sectioning commands included in `stx-sectioning-commands' +don't need to be given here because they will get into +`paragraph-separate' via `outline-regexp'. + +Do not make this list nil. + +See also `stx-command-parseps'." + :tag "Paragraph Starting or Separating Commands" + :type '(repeat regexp) + :group 'swiftex + :group 'doctex) +(make-variable-buffer-local 'stx-command-parstarts) + +;;; FIX: need key-specification :type +;;; FIX :set? +(defcustom swiftex-dtx-prefix-key '"\C-c\C-d" + "Prefix key for certain docTeX mode commands available in swifTeX mode. +A change in this variable takes effect the next time +`swiftex-mode' is called." + :tag "Prefix for Certain docTeX Commands" + :type 'string + :group 'swiftex) + +;;;;;; FILE VARIABLES + +(defvar stx-local-command-parstarts nil + "Partial list of LaTeX commands that start or separate Emacs paragraphs. +This variable exists so that it can be set as a file +variable. After the line that sets it, include a line with +`eval: stx-set-vars' + +When these regexp strings occur after optional whitespace +and a backslash, those not in `paragraph-separate' start +Emacs paragraphs, and receive text on their line during +fills. + +More specifically, `stx-set-vars' combines these strings +with logical OR (`rx-or'), precedes them by optional +whitespace and a backslash, and tacks them on to +`paragraph-separate' with a logical OR. + +Sectioning commands included in `stx-sectioning-commands' +don't need to be given here because they will get into +`paragraph-separate' via `outline-regexp'. + +See `stx-local-parstarts' for a way to add starters that are +not LaTeX commands. + +See also `stx-local-parseps'.") +(make-variable-buffer-local 'stx-local-command-parstarts) + +(defvar stx-local-command-parseps nil + "Partial list of LaTeX commands that separate Emacs paragraphs. +This variable exists so that it can be set as a file +variable. After the line that sets it, include a line with +`eval: stx-set-vars'. + +When these regexp strings occur after optional whitespace +and a backslash, they separate Emacs paragraphs, and resist +being filled. + +More specifically, `stx-set-vars' combines these strings +with logical OR (`rx-or'), precedes them by optional +whitespace and a backslash, and tacks them on to +`paragraph-separate' with a logical OR. + +Sectioning commands included in `stx-sectioning-commands' +don't need to be given here because they will get into +`paragraph-separate' via `outline-regexp'. + +Do not add strings to this list without also adding them to +`stx-local-command-parstarts', or otherwise ensuring they +make it into `paragraph-start'. + +See `stx-local-parseps' for a way to add separators that are +not LaTeX commands.") +(make-variable-buffer-local 'stx-local-command-parseps) + +(defvar stx-local-parstarts nil + "A part of the variable `paragraph-start' in swifTeX and docTeX modes. +This variable exists so that it can be set as a file +variable. After the line that sets it, include a line with +`eval: stx-set-vars'. + +The regexp strings in this list that are not in +`paragraph-separate' start Emacs paragraphs, and receive +text on their line during a fill. + +More specifically, `stx-set-vars' combines these strings +with logical OR (`rx-or') and tacks them on to +`paragraph-start' with a logical OR. + +Sectioning commands included in `stx-sectioning-commands' +don't need to be given here because they get into +`paragraph-separate' via `outline-regexp'. + +See `stx-local-command-parstarts' for an easy way to add +starters that are LaTeX commands. + +See also `stx-local-parseps'.") +(make-variable-buffer-local 'stx-local-parstarts) + +(defvar stx-local-parseps nil + "A part of the variable `parargraph-separate' in swifTeX and docTeX modes. +This variable exists so that it can be set as a file +variable. After the line that sets it, include a line with +`eval: stx-set-vars'. + +The regexp strings in this list separate Emacs paragraphs, +and resist being filled. + +More specifically, `stx-set-vars' combines these strings +with logical OR (`rx-or') and tacks them on to +`paragraph-separate' with a logical OR. + +Sectioning commands included in `stx-sectioning-commands' +don't need to be given here because they will get into +`paragraph-separate' via `outline-regexp'. + +Do not add strings to this list without also adding them to +`stx-local-parstarts', or otherwise ensuring they make it +into `paragraph-start'. + +See `stx-local-command-parseps' for an easy way to add +separators that are LaTeX commands.") +(make-variable-buffer-local 'stx-local-parseps) +;;;;;; HOOK VARIABLES +;;; FIX see what define-derived will do as docstring for this +;;; (defvar swiftex-mode-hook nil +;;; "Hook run as the last thing after entering swifTeX mode. +;;; When entering docTeX mode, the last thing done is to run this hook then +;;; `doctex-mode-hook.") +;;;;;; INTERNAL VARIABLES + +(defvar stx-comment-start "%" + "A regexp that matches the start of a comment. +It is \"%\" in swifTeX mode and \"\\\\^\\\\^A\" in docTeX mode.") +(make-variable-buffer-local 'stx-comment-start) + +(defvar stx-line-start "" + "A regexp for the beginning of all significant lines. +It is the empty string in swifTeX mode and \"%*\" in docTeX mode.") +(make-variable-buffer-local 'stx-line-start) + +;;;;;; INLINE FUNCTION-VARIABLES + +;;; Without these abbreviations, other code would be very hard to read. + +(defsubst stx-boln () +"Return regexp for the beginning of a line in swifTeX or docTeX modes. +The regexp returned matches lines hidden in an outline, and +depends on the value of `stx-line-start'." + (concat "\\(" rx-normal-boln rx-or rx-hidden-boln "\\)" + stx-line-start)) + +(defsubst stx-bwb () +"Return a regexp boln + w-space + b-slash that is possibily hidden in an outline." + (concat (stx-boln) rx-whitespace rx-backslash)) + +(defsubst stx-whiteln () +"Return a regexp for a blank line that is possibly hidden in an outline." + (concat (stx-boln) rx-whitespace rx-eoln)) +;;;;;; MODE INITIALIZATION FUNCTIONS + +(defun stx-set-outlinevars (sectioning-commands sectioning-offset doctex) + "Set `outline-regexp' and define `outline-function' for LaTeX buffers. + +Headings of the form `stx-comment-start' followed by at +least one star (\"*\"), and elements of SECTIONING-COMMANDS +are included. The variable SECTIONING-OFFSET controls the +relative depths of these two kinds of headings. DOCTEX +non-nil means the buffer is a docTeX buffer. + +In default use, the first two arguments will always be the +variables of the same name with either `stx-' or `dtx-' +prefixed, e.g., `dtx-sectioning-offset'. + +If an entry for \"endinput\" exists in SECTIONING-COMMANDS +it is treated specially. In swifTeX mode (i.e., DOCTEX +non-nil), it is treated just like the other sectioning +commands. In docTeX mode, \"\\endinput\" will be an +outline heading only if it begins in the first column of the +buffer. This is because 1) there is no reason for it to +occur as an executed macro behind a comment in the +documentation text, and 2) when it occurs with preceding +whitespace in a macrocode environment, doc.sty considers it +to be part of a macro being defined, and does not obey it, +so neither will docTeX." + (let (;; modifications to `sectioning-commands' below are local + (sectioning-commands (copy-sequence sectioning-commands)) + ;; `endinput-entry' will be nil if \endinput is not there, i.e., not + ;; supposed to be an outline heading; or will be the cons cell if there + (endinput-entry (assoc "endinput" sectioning-commands))) + ;; STEP ONE -- set `outline-regexp' + (make-local-variable 'outline-regexp) + (setq outline-regexp + ;;`outline-regexp' is prefixed by "^" when it is used by the outline + ;;commands, and enclosed in regexp parentheses, so we don't need to + ;;use "^" or parentheses here. + (rx-catenate-or + nil + (concat stx-line-start stx-comment-start "[*]+") + (if (and doctex endinput-entry) + (progn + ;; Remove the endinput entry from `stx-sectioning-commands' so + ;; it doesn't get included below in the wrong way. + (delete endinput-entry sectioning-commands) + ;; include it in the right way here + (concat rx-backslash "endinput"))) + (concat stx-line-start + rx-white-back + "\\(" + ;; These are the actual sectioning commands. + (rx-catenate-or (mapcar 'car sectioning-commands)) + "\\)" + "\\>"))) + ;; STEP TWO -- define `stx-outline-level' or `dtx-outline-level'and + ;; `byte-compile' it. We want to make `(s|d)tx-outline-level' as fast as + ;; possible, so we avoid evaluating conditionals and variable references + ;; which are going to be the same throughout a buffer's life. The tradeoff + ;; is requiring the byte compiling code to be in memory. + (byte-compile + (eval + (` + (defun (, (if doctex + 'dtx-outline-level + 'stx-outline-level)) () + (, (concat "Return the outline level for heading at point. + +For a sectioning command, return the value of +`" (if doctex + "d" + "s") "tx-sectioning-offset' plus the number associated with the +particular heading in `" (if doctex + "d" + "s") "tx-sectioning-commands'. + +For a normal outline heading `stx-comment-start' followed by +at least one star (\"*\"), return the number of stars. + +Like the standard function `outline-level', this function +must be called at the beginning of a header line.")) + ;; First check if we're on a normal outline heading. + (if (looking-at (concat stx-line-start ; "" or "%*" + stx-comment-start ; "%" or "\\^\\^A" + "[*]+")) + ;; Return the number of stars. The formula is different in docTeX + ;; mode and swifTeX mode. + (, (if doctex + ;; length of match minus length of "%^^A" + '(- (- (match-end 0) (match-beginning 0)) 4) + ;; length of match minus length of "%" + '(1- (- (match-end 0) (match-beginning 0))))) + ;; Still don't know where we are. + ;; Check if we're on a sectioning command. + (let ((count 0) + (max (, (length sectioning-commands)))) + (while (and (< count max) + (not (looking-at + (concat stx-line-start + rx-white-back + (car (elt '(, sectioning-commands) + count)) + "\\>")))) + (setq count (1+ count))) + (if (equal count max) + ;; Still haven't figured where we are. + ;; If docTeX mode and \endinput is a heading, check for it. + (, (if (and doctex endinput-entry) + (` (if (looking-at (concat rx-backslash + "endinput")) + (, (cdr endinput-entry)) + ;; We really don't know where we are. (In docTeX + ;; mode and looking for \endinput.) + (error + ;; FIX: s-or-d tx-outline-level + "`stx-outline-level' falling through! (a)"))) + ;; We really don't know where we are. (In swifTeX mode, + ;; or docTeX mode and not looking for \endinput.) + ;; FIX: s-or-d tx-outline-level + '(error "`stx-outline-level' falling through! (b)"))) + (+ (, sectioning-offset) + (cdr (elt '(, sectioning-commands) count)))))))))))) + +(defun stx-set-parvars () + "Set `paragraph-' variables for swifTeX or docTeX mode. + +Set `paragraph-start' to a string containing each of the +following strings separated by logical OR (`rx-or'): + + BOLN + whitespace + EOLN + BOLN + whitespace + `$$' + `$$' + whitespace + EOLN + `\\\\' + whitespace + EOLN + BOLN + `outline-regexp' + BOLN + significant text + single comment + BOLN + whitespace + double comment + `stx-local-parstarts' + BOLN + whitespace + `\\' + `command' + `\\>' + where `command' is an element of either + `stx-command-parstarts' or + `stx-local-command-parstarts'. + +Set `paragaph-separate' to the same string, with the +substitution of `parseps' for `parstarts' in the obvious +places. + +The variable `rx-whitespace' determines what whitespace is\; +the variable `rx-eoln' determines EOLN\; the function +`stx-boln' determines BOLN. Significant text is one or more +non-newlines followed by an even number of backslashes +\(including zero backslashes). + +The regexps are actually defined in a more efficient but +logically equivalent way. + +The function `stx-merge-list' provides a convenient way to +add items to list variables." + (let ((common + (rx-catenate-or nil + (stx-whiteln) + (concat (stx-boln) rx-whitespace rx-dollar rx-dollar) + (concat "\\(" + rx-backslash rx-backslash rx-or + rx-dollar rx-dollar + "\\)" + rx-whitespace + rx-eoln) + (concat (stx-boln) outline-regexp) + (concat (stx-boln) + ".*[^\n\\]" + "\\(" rx-backslash rx-backslash "\\)*" + comment-start) + (concat (stx-boln) + rx-whitespace + stx-comment-start + "\\(" stx-comment-start "\\)+")))) + (setq paragraph-start + (rx-catenate-or nil + common + (concat (stx-boln) + rx-white-back + "\\(" + (rx-catenate-or stx-command-parstarts) + (rx-catenate-or stx-local-command-parstarts) + "\\)" + "\\>" + (rx-catenate-or stx-local-parstarts)))) + (setq paragraph-separate + (rx-catenate-or nil + common + (concat (stx-boln) + rx-white-back + "\\(" + (rx-catenate-or stx-command-parseps) + (rx-catenate-or stx-local-command-parseps) + "\\)" + "\\>" + (rx-catenate-or stx-local-parseps)))))) + +;;;;;; DEFINE MODE +(define-derived-mode swiftex-mode latex-mode "swifTeX" + "Major mode for LaTeX documents derived from the standard LaTeX mode. + +The variables `paragraph-separate' and `paragraph-start' are +carefully chosen to make filling and movement convenient. +See `stx-set-parvars'. + +One consequence of their values is that single-commented +paragraphs fill normally, and double-or-more commented lines +resist filling (they separate Emacs paragraphs). Comments +with non-whitespace before a \"%\" also resist filling. + +Outline headings are either a single \"%\" in the first +column plus at least one star (\"*\"), or the standard LaTeX +sectioning commands. See `stx-set-outlinevars'. + +The TAB key seems to behave as usual but inserts spaces only +\(that is, no `\\t' characters). It is confusing when `\\t' +characters occur inside verbatim environments (including +macrocode environments) because the typeset appearance will +not match the appearance of the source file, and +code-indenting conventions will often get mangled. + +A number of commands facilitate movement and save typing. + +\\[stx-up-block] moves point past the next \"\\end{name}\". +\\[stx-next-braces] moves point out of enclosing braces and just into the next set. +\\[stx-begin-block] inserts \"\\begin{}\" and prompts for the argument. +\\[stx-insert-block] inserts a \"\\begin{} \\end{}\" pair and prompts for the argument. +\\[stx-close-block] creates an \"\\end{...}\" to match the last unclosed \"\\begin{...}\". +\\[stx-close-block-from-inside] closes a LaTeX block after the user has typed the argument to \"\\begin\". +\\[stx-emphasize] inserts \"\\emph{}\" and leaves point between braces. +\\[stx-emphasize-word] surrounds word at point with \"\\emph{\" and \"}\". + +Certain docTeX mode commands are available with the prefix +`swiftex-dtx-prefix-key': + +The following commands maintain the version information in +the LaTeX buffer. They act on lines that define +\\fileversion, \\filedate, or \\docdate, or the equivalent +components of the \\ProvidesPackage or \\ProvidesClass +declarations if the first are absent. + +\\[dtx-get-fileinfo] displays the buffer's version in the minibuffer. +\\[dtx-update-minor-version] updates the minor version. +\\[dtx-update-major-version] updates the major version. + +The last two also update the file date to the current date. + +\\[dtx-update-documentation-date] makes the documentation date current. +\\[dtx-insert-change] inserts a LaTeX \\changes entry. +\\[add-change-log-entry] adds a ChangeLog entry. + +Some convenient ways to customize behavior in individual +files are provided by the `stx-local-' variables. + +\\{swiftex-mode-map}" + (make-local-variable 'adaptive-fill-regexp) + (setq adaptive-fill-regexp + ;; whitespace + optional stx-comment-start + whitespace + ;; Notice that a line with more than one "%" is a paragraph separator + ;; and therefore never part of a paragraph anyway. + (concat rx-whitespace + stx-comment-start "?" + rx-whitespace)) + (make-local-variable 'paragraph-ignore-fill-prefix) + (setq paragraph-ignore-fill-prefix t) + (outline-minor-mode 1) + (make-local-variable 'outline-level) + (setq outline-level (function stx-outline-level)) + (stx-set-vars) + (if stx-fontlock + (progn + (require 'font-latex) + (font-latex-setup)) + (if stx-hilit + (require 'hilit-swiftex)))) + +;;;;;; DEFINE KEYMAP +(defvar swiftex-dtx-mode-map (make-sparse-keymap) + "Keymap for docTeX mode commands in swifTeX mode.") + +;; avoid message while byte-compiling +(defvar swiftex-mode-map) +(define-key swiftex-mode-map swiftex-dtx-prefix-key swiftex-dtx-mode-map) +(define-key swiftex-dtx-mode-map "g" 'dtx-insert-change) +(define-key swiftex-dtx-mode-map "l" 'add-change-log-entry) +(define-key swiftex-dtx-mode-map "v" 'dtx-get-fileinfo) +(define-key swiftex-dtx-mode-map "u" 'dtx-update-minor-version) +(define-key swiftex-dtx-mode-map "U" 'dtx-update-major-version) +(define-key swiftex-dtx-mode-map "D" 'dtx-update-documentation-date) +(define-key swiftex-mode-map "\t" 'stx-tab) +(define-key swiftex-mode-map "\C-c;" 'comment-region) +(define-key swiftex-mode-map "\M-\t" 'stx-next-braces) +(define-key swiftex-mode-map "\C-c\t" 'stx-next-braces) +(define-key swiftex-mode-map "\C-c\C-]" 'stx-up-block) +(define-key swiftex-mode-map "\C-]\C-]" 'up-list) +(define-key swiftex-mode-map "\C-cl" 'tex-bibtex-file) +(define-key swiftex-mode-map "\C-cp" 'stx-pdflatex-file) +(define-key swiftex-mode-map "\C-cg" 'stx-pdfview-file) +(define-key swiftex-mode-map "\"" 'stx-insert-quote) +(define-key swiftex-mode-map "\C-cv" 'stx-verify-blocks) +(define-key swiftex-mode-map "\C-c\M-u" 'stx-goto-first-unended-begin) +(define-key swiftex-mode-map "\C-c\M-n" 'stx-goto-last-unbegun-end) +(define-key swiftex-mode-map "\C-c\C-u" 'stx-goto-prev-unended-begin) +(define-key swiftex-mode-map "\C-c\C-n" 'stx-goto-next-unbegun-end) +(define-key swiftex-mode-map "\C-c\C-e" 'stx-close-block) +(define-key swiftex-mode-map "\C-c\C-m" 'stx-emphasize-word) + +;;;;;; FUNCTIONS AND VARIABLES TAKEN FROM AUC TEX +;;; The following are taken from AUC TeX. +;;; +;;; I have mirrored these variables with my own variables. This way, if a +;;; user's .emacs sets things up for AUC TeX, this mode will do the right +;;; thing. And a properly-named version exists, for someone who is looking for +;;; things by completion. I have changed documentation strings conform to +;;; proper convention. + +(defvar TeX-open-quote "``" + "String inserted by typing \\[stx-insert-quote] to open a quotation.") +(defvar rx-tex-open-quote TeX-open-quote + "String inserted by typing \\[stx-insert-quote] to open a quotation.") +(defvar TeX-close-quote "''" + "String inserted by typing \\[stx-insert-quote] to close a quotation.") +(defvar rx-tex-close-quote TeX-close-quote + "String inserted by typing \\[stx-insert-quote] to close a quotation.") +(defvar TeX-quote-after-quote nil + "See documentation for `stx-insert-quote'.") +(defvar stx-quote-after-quote TeX-quote-after-quote + "See documentation for `stx-insert-quote'.") + +(defun stx-insert-quote (arg) + "Insert the appropriate quote marks for TeX. +Inserts the value of `rx-tex-open-quote' (normally ``) or +`rx-tex-close-quote' (normally '') depending on the +context. + +`stx-quote-after-quote' non-nil means this insertion works +only after \". A prefix argument ARG means always insert \" +characters." + (interactive "*P") + (if arg + (self-insert-command (prefix-numeric-value arg)) + (if stx-quote-after-quote + (insert (cond ((bobp) + ?\") + ((not (= (preceding-char) ?\")) + ?\") + ((save-excursion + (forward-char -1) + (bobp)) + (delete-backward-char 1) + rx-tex-open-quote) + ((save-excursion + (forward-char -2) ;;; at -1 there is double quote + (looking-at "[ \t\n]\\|\\s(")) + (delete-backward-char 1) + rx-tex-open-quote) + (t + (delete-backward-char 1) + rx-tex-close-quote))) + (insert (cond ((bobp) + rx-tex-open-quote) + ((= (preceding-char) (string-to-char stx-str-backslash)) + ?\") + ((= (preceding-char) ?\") + ?\") + ((save-excursion + (forward-char (- (length rx-tex-open-quote))) + (looking-at (regexp-quote rx-tex-open-quote))) + (delete-backward-char (length rx-tex-open-quote)) + ?\") + ((save-excursion + (forward-char (- (length rx-tex-close-quote))) + (looking-at (regexp-quote rx-tex-close-quote))) + (delete-backward-char (length rx-tex-close-quote)) + ?\") + ((save-excursion + (forward-char -1) + (looking-at "[ \t\n]\\|\\s(")) + rx-tex-open-quote) + (t + rx-tex-close-quote)))))) + +;;;;;; INTERACTIVE FUNCTIONS + +;;; If you have tabs in a LaTeX buffer, you will be in for some surprises when +;;; you use a verbatim environment. We define a version of `tab-to-tab-stop' +;;; that only will insert spaces. I can't imagine that the savings of +;;; file-size is at all significant using tabs instead of spaces, but you can +;;; always debind this from tab if you want usual Emacs tabbing. I strongly +;;; recommend you keep the new space-tabbing for `doctex-mode' because every +;;; macrocode environment is a verbatim environment. + +;; This function's code is based on the standard `tab-to-tab-stop' in +;; the standard "indent.el". +(defun stx-tab () + "Insert spaces (no tabs) to next defined tab-stop column. +The variable `tab-stop-list' is a list of columns at which +there are tab stops. Use \\[edit-tab-stops] to edit them +interactively." + (interactive "*") + (if abbrev-mode (expand-abbrev)) + (let ((tabs tab-stop-list)) + (while (and tabs (>= (current-column) (car tabs))) + (setq tabs (cdr tabs))) + (if tabs + (insert (make-string (- (car tabs) (current-column)) ?\ )) + (insert ?\ )))) +(defun stx-up-block () + "Move point past the next \"\\end{name}\". +Let * = point before calling this function, +** = point afterward, and \\n = a new line: + +* [possible text here] + [more possible text] +\\end{name} +\\n +**" + (interactive) + (goto-char (re-search-forward (concat rx-backslash "end" rx-brace-pair))) + (forward-line 1) + (newline)) +(defun stx-next-braces () + "Move point out of enclosing braces and just into the next set. +That is, call `up-list' and then go to position of next +\"{\" plus one." + (interactive) + (up-list 1) + (goto-char (search-forward rx-tex-grop))) + +;;; Ugh. FIX. Horribly inefficient conditional on 'doctex-mode. +(defun stx-next-beginorend () + "Leave point at next \\begin or \\end in swifTeX or docTeX mode buffer. +Signal error if not found." + (re-search-forward + (concat "\\(" rx-normal-boln rx-or rx-hidden-boln "\\)" + (if (equal major-mode 'doctex-mode) "%+" "") + rx-whitespace rx-backslash "\\(begin" rx-or "end\\)" + rx-whitespace rx-tex-grop))) + +;;; FIX where is point left? what does match-data have? +;;; FIX: why is this a defsubst and not the above? +;;; Ugh. FIX. Horribly inefficient conditional on 'doctex-mode. +(defsubst stx-prev-beginorend () + "Leave point at previous \\begin or \\end in swifTeX or docTeX mode buffer. +Signal error if not found." + (re-search-backward + (concat "\\(" rx-normal-boln rx-or rx-hidden-boln "\\)" + (if (equal major-mode 'doctex-mode) "%+" "") + rx-whitespace rx-backslash "\\(begin" rx-or "end\\)" + rx-whitespace rx-tex-grop))) + +;;; Ugh. FIX. Horribly inefficient conditional on 'doctex-mode. +(defun stx-next-is-end () + "Return beginning if true, nil if false, or error if no prev." + (and (stx-next-beginorend) + ;; FIX can't i goto-char match-beginning or something? + (stx-prev-beginorend) + (let ((p (match-beginning 0))) + (if (looking-at + (concat "\\(" rx-normal-boln rx-or rx-hidden-boln "\\)" + (if (equal major-mode 'doctex-mode) + "%+" "") + rx-whitespace rx-backslash "end" + rx-whitespace rx-tex-grop)) + p)))) + +;;; Ugh. FIX. Horribly inefficient conditional on 'doctex-mode. +(defun stx-prev-is-begin () + "Return beginning if true, nil if false, or error if no prev." + (if (stx-prev-beginorend) + (let ((p (match-beginning 0))) + (if (looking-at + (concat "\\(" rx-normal-boln rx-or rx-hidden-boln "\\)" + (if (equal major-mode 'doctex-mode) + "%+" "") + rx-whitespace rx-backslash + "begin" + rx-whitespace rx-tex-grop)) + p)))) +(defun stx-goto-next-unbegun-end () + "Move point to the next unbegun \\end after point. +Signal error if there isn't one. +Leave mark at original point if there is." + (interactive) + (stx-unbegun-end-internal) + (message "Point moved to next unbegun \\end. Mark is at previous point.")) +(defun stx-goto-last-unbegun-end () + "Move point to the last unbegun \\end in the buffer. +Signal error if there isn't one. +Leave mark at original point if there is." + (interactive) + (stx-unbegun-end-internal 'last) + (message "Point moved to first unbegun end. Mark is at previous point.")) +(defun stx-unbegun-end-internal (&optional last) + "Internal function to find an unbegun end. +LAST non-nil means start at `(point-min)'." + (interactive) + (let (case-fold-search + (b 0) + (e 0) + result) + (save-excursion + (if last (goto-char (point-min))) + (condition-case nil + (setq result (stx-fue-recursor)) + (error (if last + (error "There is no unbegun \\end") + (error "No unbegun \\end after point"))))) + (if (car result) + (error "Something's wrong with stx-goto-first-unbegun end") + (push-mark) + (if (interactive-p) + (message "Point moved to first unbegun end. Mark is at previous point.")) + (goto-char (cadr result))))) + +;; avoid message while byte-compiling +(defvar b) +(defvar e) +(defun stx-fue-recursor () + "Internal recursive function to find unbegun end." + (let ((next-is-end (stx-next-is-end))) + (if next-is-end + (setq e (1+ e)) + (setq b (1+ b))) + (if (> e b) + (list nil next-is-end) + (forward-line) + (stx-fue-recursor)))) +(defun stx-goto-first-unended-begin () + "Move point to the first unended \\begin in the buffer. +Signal error if there isn't one. +Leave mark at original point if there is." + (interactive) + (stx-unended-begin-internal 'first) + (message "Point moved to first unended \\begin. Mark is at previous point.")) +(defun stx-goto-prev-unended-begin (&optional silent) + "Move point to the first unended \\begin before point. +Signal error if there isn't one. +Leave mark at original point if there is. + +Optional argument SILENT non-nil means don't print a message." + (interactive) + (stx-unended-begin-internal) + (if (not silent) + (message "Point moved to previous unended \\begin. Mark is at previous point."))) +(defun stx-unended-begin-internal (&optional first) + "Internal function to find unended begin. +FIRST non-nil means start at \(point-max\)." + (let (case-fold-search + (b 0) + (e 0) + result) + (save-excursion + (if first (goto-char (point-max))) + (condition-case nil + (setq result (stx-fub-recursor)) + (error (if first + (error "There is no unended \\begin") + (error "No unended \\begin before point"))))) + (if (car result) + (error "Something's wrong with stx-goto-last-unended begin") + (push-mark) + (goto-char (cadr result))))) +(defun stx-fub-recursor () + "Internal recursive function to find unended begin." + (let ((prev-is-begin (stx-prev-is-begin))) + (if prev-is-begin + (setq b (1+ b)) + (setq e (1+ e))) + (if (> b e) + (list nil prev-is-begin) + (stx-fub-recursor)))) + +;; This is weird. Two errors mean everything's OK. +(defun stx-verify-blocks () + "Move point to a mismatched \\begin .. \\end block. +Leave point alone if there are none. Leave mark at old +point if we move point." + (interactive) + (condition-case nil + (stx-goto-first-unended-begin) + (error (condition-case nil + (stx-goto-last-unbegun-end) + (error (message "Your \\begin .. \\end blocks are OK.")))))) +(defun stx-close-block () + "Create an \"\\end{...}\" to match the last unclosed \"\\begin{...}\". +The \"\\begin{...}\" must follow `stx-bwb', which is +duplicated in front of the \"\\end{...}\". + +Return indentation string." + (interactive "*") + (let ((bolp (bolp)) + case-fold-search + text + indentation) + (save-excursion + (condition-case nil + (stx-goto-prev-unended-begin 'silent) + (error (error "Couldn't find unended \\begin above! (stx-close-block)"))) + (setq indentation (buffer-substring + (save-excursion + (re-search-forward (concat (stx-boln) rx-whitespace))) + (point))) + (re-search-forward (concat rx-backslash "begin" + "\\(" rx-whitespace rx-brace-pair + "\\)")) + (setq text (buffer-substring (match-beginning 1) (match-end 1)))) + (if (not bolp) + (let ((bol (save-excursion (beginning-of-line) (point)))) + (if (save-excursion + (re-search-backward + (concat (stx-boln) rx-whitespace + rx-backslash "end" + rx-whitespace rx-brace-pair) bol t)) + ;; if there is an \\end before point on the same line + (insert ?\n) + ;; if not + (beginning-of-line) + (setq bolp t)))) + (insert indentation "\\end" text) + (if bolp + (insert ?\n) + (forward-char)) + ;; return value + indentation)) +(defun stx-close-block-from-inside () + "Close LaTeX block after typing the argument to \"\\begin\". +Let * be point. +Initial buffer contents: + + \\begin{foo*} + +Final buffer contents: + + \\begin{foo} + * + \\end{foo}" + (interactive "*") + (forward-line 1) + (let ((indent (stx-close-block))) + (forward-line -1) + (open-line 1) + (insert indent))) +(defun stx-command (command &optional arg) + "Insert a one-argument LaTeX command \"\\COMMAND{}\". +ARG non-nil means place ARG between braces following COMMAND +and leave point (*) after both: + +\\COMMAND{ARG} * + +If ARG is nil, leave point between the braces: + \\COMMAND{*}" + (interactive "*") + (insert "\\" command "{") + (if arg + (insert arg "} ") + (insert "}") + (backward-char))) + +;; This code is based on `current-word' in the standard 19.28 simple.el. +;; +;; It seemed wisest to imitate its "strict" behavior. +;; +;; We explicitly add the original point to undo list, because we have to +;; explicitly move point during the normal execution of the function to keep it +;; on the same text on which it was called. +;; +;; FIX: consider `insert-before-markers' +(defun stx-enclose-word (before after) + "Insert string BEFORE before word at point and string AFTER after it. +Keep point over the same text as when the function is called." + (let* ((oldpoint (point)) + (start oldpoint) + (end oldpoint)) + ;; Since the following movement commands do not alter + ;; `buffer-undo-list', we add point manually. We could do this + ;; any time before the first command that modified it, which is + ;; the `insert' below. + (setq buffer-undo-list (cons oldpoint buffer-undo-list)) + (skip-syntax-backward "w") + (setq start (point)) + (goto-char oldpoint) + (skip-syntax-forward "w") + (setq end (point)) + (if (and (eq start oldpoint) + (eq end oldpoint)) + (error "Point is neither in nor adjacent to a word!") + ;; because the head of `buffer-undo-list' is non-nil, `insert' + ;; is going to add a boundary to it before adding its element. + (insert after) + ;; We remove the boundary now. + (setcdr buffer-undo-list (cdr (cdr buffer-undo-list))) + (goto-char start) + (insert before) + ;; We remove the boundary as before. + (setcdr buffer-undo-list (cdr (cdr buffer-undo-list))) + (goto-char (+ oldpoint (length before)))))) +(defun stx-emphasize-word () + "Surround word at point with \"\\emph{\" and \"}\"." + (interactive "*") + (stx-enclose-word "\\emph{" "}")) +(defun stx-emphasize () + "Call `(stx-command \"emph\")'." + (interactive "*") + (stx-command "emph")) +(defun stx-begin-block () + "Call `(stx-command \"begin\")'." + (interactive "*") + (stx-command "begin")) +(defalias 'stx-insert-block 'tex-latex-block) +(defun stx-block-comment () + "Call `(stx-insert-block \"comment\")'." + (interactive "*") + (stx-insert-block "comment")) +(defun stx-block-quotation () + "Call `(stx-insert-block \"quotation\")'." + (interactive "*") + (stx-insert-block "quotation")) +;; avoid message while byte-compiling +(eval-when-compile (require 'tex-mode)) +(defun stx-pdflatex-file () + "Prompt to save all buffers and run PDFLaTeX on current buffer's file. +This function is a wrapper for `tex-file'." + (interactive) + (let ((tex-command "pdflatex")) + (tex-file))) + +;; avoid message while byte-compiling +(defvar tex-print-file) +(defvar tex-last-buffer-texed) +(defun stx-pdfview-file () + "Preview with gv the last `.pdf' file made by running PDFLaTeX under Emacs. +This means, made using \\[tex-region], \\[tex-buffer] or +\\[tex-file]." + (interactive) + (let ((print-file-name-pdf (tex-append tex-print-file ".pdf")) + test-name) + (if (and (not (equal (current-buffer) tex-last-buffer-texed)) + (buffer-file-name) + ;; Check that this buffer's printed file is up to date. + (file-newer-than-file-p + (setq test-name (tex-append (buffer-file-name) ".pdf")) + (buffer-file-name))) + (setq print-file-name-pdf test-name)) + (if (not (file-exists-p print-file-name-pdf)) + (error "No appropriate `.pdf' file could be found") + (if (tex-shell-running) + (tex-kill-job) + (tex-start-shell)) + (tex-send-command "gv" print-file-name-pdf t)))) + + +;;;;; DOCTEX MODE + +;;; The code forms below are unique to docTeX mode, but rely on many of the +;;; forms in the swifTeX mode section above. + +;;;;;; USER VARIABLES + +;;; FIX :link? +(defcustom dtx-sectioning-offset 1 + "Outline level offset for all LaTeX sectioning commands in a docTeX buffer. +See the documentation for the variable +`stx-sectioning-commands'." + :tag "Sectioning Commands Outline Offset" + :type 'integer + :group 'doctex) + +;;; FIX :link? +(defcustom dtx-sectioning-commands '( + ("documentclass" . -2) + ;; leave off final + ;; closebrace, since the + ;; \> in the regexp needs + ;; to match the end of the + ;; strings here. + ("begin{document" . -2) + ("end{document" . -2) + ("endinput" . -2) + ("StopEventually" . -2) + ("Finale" . -2) + ("part" . -2) + ("appendix" . -1) + ("chapter" . -1) + ("section". 0) + ("subsection" . 1) + ("subsubsection" . 2) + ("paragraph" . 3) + ("subparagraph" . 4) + ) + "Equivalent of `stx-sectioning-commands' for docTeX buffers. +See documentation for that variable." + :tag "Sectioning Commands and Levels" + :type '(repeat (cons :format "%v" + (string :tag "Command") + (integer :tag "Level"))) + :group 'doctex) + +;;; FIX prefix key type +;;; FIX :set? +(defcustom doctex-dtx-prefix-key '"\C-c\C-d" + "Prefix key for all commands unique to docTeX mode. +A change in this variable takes effect the next time `doctex-mode' is +called." + :tag "Prefix key for all docTeX Commands" + :type 'string + :group 'doctex) + +(defcustom rx-tex-def-cmd + (concat "\\(" +;;; FIX Frankenstein-specific stuff here; move to +;;; FIX should have file-variable for new defining commands + (regexp-opt '("def" "long\\\\def" + "gdef" "global\\\\def" + "edef" "long\\\\edef" + "xdef" "global\\\\edef" + "ReserveCS" "ReserveCS\\*" + "InitCS" "InitCS\\*" + "newcommand" "newcommand\\*" + "renewcommand" "renewcommand\\*" + "defcommand" "defcommand\\*" + "requirecommand" "requirecommand\\*" + "CheckCommand" "CheckCommand\\*" + "CheckName" "CheckName\\*" + "NewUserInfo" "NewUserInfo\\*" + "CheckName" "CheckName\\*" + "RequireNewName" "RequireNewName\\*" + "DeclareRobustCommand" + "DeclareRobustCommand\\*" + "NewNameDef" "NewNameDef\\*" + "let" "global\\\\let" + "EElet" "global\\\\EElet" + "newlet" "global\\\\newlet")) + "\\)") + "Matches a (La)TeX defining command without its argument." + :tag "Defining Command Regexp" + :type 'regexp + :group 'doctex) +(defcustom rx-dtx-version "\\(v?\\)\\([0-9.]*\\)\\([a-z]?\\)" + "Matches a LaTeX version string." + :tag "LaTeX Version Regexp" + :type 'regexp + :group 'doctex) +(defcustom rx-dtx-date "[0-9]+/[0-9]+/[0-9]+" + "Matches a LaTeX date string." + :tag "LaTeX Date Regexp" + :type 'regexp + :group 'doctex) + +;;;;;; FILE VARIABLES +(defvar dtx-bibstyle-mode nil + "Non-nil means assume the buffer contains a bibstyle source. + +FIX: this should be detected automatically. But for now, set this +in the file variables. Right now this only has an effect on the functions +which update the file version and date and documentation +date.") + +;;;;;; HOOK VARIABLES +;;; FIX see what define-derived-mode gives for its dox +;;; (defvar doctex-mode-hook nil +;;; "Hook run as the last thing after entering docTeX mode. +;;; When entering docTeX mode, the last thing done is to run `swiftex-mode-hook' +;;; then this hook.") + +;;;;;; INTERNAL VARIABLES + +(defvar dtx-file-version nil + "The version string of the file in the current buffer.") +(make-variable-buffer-local 'dtx-file-version) + +(defvar dtx-prefix-version nil + "The leading part of the version string up until the major version. + +This is the optional \"v\" plus any following elements that +will still leave two elements to the right, which are the +major and minor versions.") +(make-variable-buffer-local 'dtx-prefix-version) + +(defvar dtx-major-version nil + "The major version part of the version string.") +(make-variable-buffer-local 'dtx-major-version) + +(defvar dtx-minor-version nil + "The minor version part of the version string.") +(make-variable-buffer-local 'dtx-minor-version) + +;;;;;; INLINE FUNCTION-VARIABLES + +;;; Without these abbreviations, other code would be very hard to read. + +;;; The version control commands are going to be behind double %%'s, this is +;;; just a fact of how bibstyle sources have to get generated. You can't have +;;; the regular \TeX{} commands in the bibstyle. +(defsubst dtx-bwcb () + "Return a regexp boln + w-space + \"%%\" + w-space + b-slash. +Do so even if hidden in an outline." + (concat (stx-boln) rx-whitespace "%%" rx-whitespace rx-backslash)) +(defsubst dtx-tex-defn (name) + "Return regexp string matching a line that defines NAME. +The match begins at the beginning of the line, and goes +through the \"{\" that begins the definition. + +The definitions searched for are expected to be in a doctex +file, but not in the documentation, in a macrocode +environment. + +The string returned will depend on the value of +`dtx-bibstyle-mode'." + (concat (if dtx-bibstyle-mode (dtx-bwcb) (stx-bwb)) + rx-tex-def-cmd + rx-tex-grop "?" + rx-backslash + name + rx-tex-grcl "?" + rx-whitespace + rx-tex-grop)) + +;;;;;; DEFINE MODE +;; avoid message while byte-compiling +(defvar doctex-mode-syntax-table) +(define-derived-mode doctex-mode swiftex-mode "docTeX" + "Major mode for LaTeX doc documents derived from swifTeX mode. + +DocTeX mode preserves the filling and outlining behavior of +swifTeX mode behind the wall of of \"%\" characters in the +first column of a LaTeX doc.sty buffer. + +You won't want to fill inside a \"macrocode\" environment. + +The TAB key seems to behave as usual but inserts spaces only +\(that is, no `\\t' characters). It is confusing when `\\t' +characters occur inside verbatim environments \(including +macrocode environments) because the typeset appearance will +not match the appearance of the source file, and +code-indenting conventions will often get mangled. + +Outline headings all begin with a \"%\" in the first column. +LaTeX sectioning commands are outline headings, and so is +\"^^A\" followed by at least one star \(\"*\"). (The ltxdoc +document class defines the \"^^A\" character as a LaTeX +comment.) See `stx-set-outlinevars'. + +The following commands maintain the version information in +the LaTeX buffer. They act on lines that define +\\fileversion, \\filedate, or \\docdate, or the equivalent +components of the \\ProvidesPackage or \\ProvidesClass +declarations if the first are absent. + +\\[dtx-get-fileinfo] displays the buffer's version in the minibuffer. +\\[dtx-update-minor-version] updates the minor version. +\\[dtx-update-major-version] updates the major version. + +The last two also update the file date to the current date. + +\\[dtx-update-documentation-date] makes the documentation date current. +\\[dtx-insert-change] inserts a LaTeX \\changes entry. +\\[add-change-log-entry] adds a ChangeLog entry. + +A number of commands save typing. `insert' commands (on +uppercase keys) begin and end the relevant environment and +leave point in between\; `begin' commands \(on lowercase +keys) just begin the environment. All prompt for the name +of the thing in question. + +\\[dtx-interrupt-macrocode] interrupts a \"macrocode\" environment for more documentation. + +Some convenient ways to customize behavior in individual +files are provided by the `stx-local-' variables. + +Some function and variable names begin with `stx-' and some +with `dtx-'. The latter are unique to docTeX mode\; the +former are in common with swifTeX mode. The difference is +unimportant if you just want to use docTeX mode. + +\\{doctex-mode-map}" + ;; Change the syntax of % to punctuation. It's not a + ;; comment in docTeX mode, and leaving it as a comment + ;; char causes problems. I'm not sure what syntax class + ;; it should have, however. + (modify-syntax-entry ?% "." doctex-mode-syntax-table) + ;; Bug in Emacs: you have to declare this a buffer-local + ;; hook. FIX: version? + (make-local-hook 'write-contents-hooks) + (add-hook 'write-contents-hooks 'dtx-update-checksum nil t) + (setq stx-comment-start "\\^\\^A") + (setq stx-line-start "%*") + ;; FIX: merge? + ;; FIX: should have customizable dtx-extra-command-parstarts + (setq stx-command-parstarts + (append stx-command-parstarts '("DescribeMacro" "DescribeEnv"))) + (setq stx-command-parseps + (append stx-command-parseps '("DescribeMacro" "DescribeEnv"))) + (setq outline-level (function dtx-outline-level)) + (stx-set-vars) + ;; FIX I think this is not so great, these variables aren't independent from + ;; stx-fontlock + (if dtx-fontlock + (progn + (require 'font-latex) + (font-latex-setup)) + (if dtx-hilit + (require 'hilit-swiftex)))) + +;;;;;; DEFINE KEYMAP + +(defvar doctex-dtx-mode-map (make-sparse-keymap) + "Keymap for all new commands in docTeX mode.") +;; avoid message while byte-compiling +(defvar doctex-mode-map) +(define-key doctex-mode-map doctex-dtx-prefix-key doctex-dtx-mode-map) +(define-key doctex-dtx-mode-map "g" 'dtx-insert-change) +(define-key doctex-dtx-mode-map "l" 'add-change-log-entry) +(define-key doctex-dtx-mode-map "e" 'dtx-begin-environment) +(define-key doctex-dtx-mode-map "E" 'dtx-insert-environment) +(define-key doctex-dtx-mode-map "f" 'dtx-begin-bibfunction) +(define-key doctex-dtx-mode-map "F" 'dtx-insert-bibfunction) +(define-key doctex-dtx-mode-map "m" 'dtx-begin-macro) +(define-key doctex-dtx-mode-map "M" 'dtx-insert-macro) +(define-key doctex-dtx-mode-map "i" 'dtx-interrupt-macrocode) +(define-key doctex-dtx-mode-map "v" 'dtx-get-fileinfo) +(define-key doctex-dtx-mode-map "s" 'dtx-update-checksum) +(define-key doctex-dtx-mode-map "c" 'dtx-begin-macrocode) +(define-key doctex-dtx-mode-map "C" 'dtx-insert-macrocode) +(define-key doctex-dtx-mode-map "u" 'dtx-update-minor-version) +(define-key doctex-dtx-mode-map "U" 'dtx-update-major-version) +(define-key doctex-dtx-mode-map "d" 'dtx-update-documentation-date) + +;;;;;; FUNCTIONS TO SAVE TYPING + +(defun dtx-begin-macrocode () + "Begin a \"macrocode\" environment." + (interactive "*") + (beginning-of-line) + (insert "% \\begin{macrocode}")) +(defun dtx-insert-macro () + "Insert a \"macro\" environment and matching \"macrocode\" environment." + (interactive "*") + (end-of-line) + (insert-string " +% \\begin{macro} +% +% \\begin{macrocode} +% \\end{macrocode} +% \\end{macro} +%") + (forward-line -5) + (end-of-line) + (insert rx-tex-grop (read-string "Macro: " stx-str-backslash) rx-tex-grcl) + (forward-line 1) + (end-of-line)) +(defun dtx-begin-macro () + "Begin a \"macro\" environment." + (interactive "*") + (beginning-of-line) + (insert-string "% \\begin{macro} +") + (forward-line -1) + (end-of-line) + (insert rx-tex-grop (read-string "Macro: " stx-str-backslash) rx-tex-grcl) + (forward-line 1) + (end-of-line)) +(defun dtx-insert-environment () + "Insert an \"environment\" environment." + (interactive "*") + (end-of-line) + (insert-string " +% \\begin{environment} +% +% \\begin{macrocode} +% \\end{macrocode} +% \\end{environment} +%") + (forward-line -5) + (end-of-line) + (insert rx-tex-grop (read-string "Environment: ") rx-tex-grcl) + (forward-line 1) + (end-of-line)) +(defun dtx-begin-environment () + "Begin an \"environment\" environment." + (interactive "*") + (beginning-of-line) + (insert-string "% \\begin{environment} +") + (forward-line -1) + (end-of-line) + (insert rx-tex-grop (read-string "Environment: ") rx-tex-grcl) + (forward-line 1) + (end-of-line)) +(defun dtx-insert-bibfunction () + "Insert a \"bibfunction\" environment." + (interactive "*") + (end-of-line) + (insert-string " +% \\begin{bibfunction} +% +% \\begin{macrocode} +% \\end{macrocode} +% \\end{bibfunction} +%") + (forward-line -5) + (end-of-line) + (insert rx-tex-grop (read-string "FUNCTION: ") rx-tex-grcl) + (forward-line 1) + (end-of-line)) +(defun dtx-begin-bibfunction () + "Begin a \"bibfunction\" environment." + (interactive "*") + (beginning-of-line) + (insert-string "% \\begin{bibfunction} +") + (forward-line -1) + (end-of-line) + (insert rx-tex-grop (read-string "FUNCTION: ") rx-tex-grcl) + (forward-line 1) + (end-of-line)) +(defun dtx-insert-change () + "Insert a LaTeX change log entry with automatic date." + (interactive "*") + (if (null dtx-file-version) + (dtx-get-fileinfo)) + (end-of-line) + (insert-string " +% \\changes") + (insert rx-tex-grop dtx-file-version rx-tex-grcl) + (insert rx-tex-grop (dtx-current-date) rx-tex-grcl) + (insert rx-tex-grop (read-string "Change: ") rx-tex-grcl)) +(defun dtx-insert-macrocode () + "Insert a \"macrocode\" environment." + (interactive "*") + (insert-string "% \\begin{macrocode} + +% \\end{macrocode} +") + (forward-line -2)) +(defun dtx-interrupt-macrocode () + "Interrupt a \"macrocode\" environment for more documentation." + (interactive "*") + (backward-char 1) + (end-of-line) + (insert-string " +% \\end{macrocode} +% +% \\begin{macrocode}") + (forward-line -1) + (end-of-line)) + +;;;;;; FUNCTIONS TO HANDLE VERSIONS, DATES, CHECKSUMS +(defun dtx-get-fileinfo (&optional noecho) + "Set `dtx-' v.c. var.s according to lines in the buffer, and echo them. + +That is, set `dtx-file-version', `dtx-prefix-version', +`dtx-major-version', and `dtx-minor-version', and echo them +to the minibuffer. + +Optional NOECHO non-nil means don't echo information. + +To set them, look for a line that defines +\"\\fileversion{<version>}\" (see `dtx-tex-defn'). +<version> is any number of integers separated by dots with +an optional prefix \"v\" and an optional suffix of a +lowercase letter. If no such line exists, then use the line +with \"\\ProvidesPackage\" or \"\\ProvidesClass\". + +The major and minor version strings will be the last two +elements of the <version>, counting the possible suffix as a +separate element if it exists." + (interactive) + (let (case-fold-search + fileversion filedate docdate) + (save-excursion + (goto-char (point-min)) + (if (or + ;; Look for definition of \fileversion. + (and (re-search-forward + (dtx-tex-defn "fileversion") + nil t) + ;; we search twice because we want to remain independent + ;; of how many \\(...\\) pairs are in `dtx-tex-defn'. + ;; FIX: we can not acceptably skip anything here -- what can we + ;; skip? What if we skip more? + (re-search-forward + (concat rx-whitespace rx-dtx-version + rx-whitespace rx-tex-grcl) + nil t)) + ;; If the first search failed, point is still at point-min. + ;; Look for an optional argument to \ProvidesFoo. + (and (re-search-forward + (concat (stx-bwb) "Provides" + "\\(Package" rx-or "Class\\)" + ;; This used to be: rx-tex-grop "\\w+" rx-tex-grcl + ;; which is more restrictive than the rx-brace-pair I + ;; used elsewhere. I see no reason to use the more + ;; restrictive regexp. + ;; thanks to Peter Møller Neergaard for noticing this + rx-whitespace rx-brace-pair) + nil t) + ;; FIX: the only thing we can acceptably skip here is + ;; rx-whitespace-newlines -- what if we skip more? + (re-search-forward + (concat "\\[" + rx-whitespace rx-dtx-date + rx-whitespace rx-dtx-version ".*\\]") + nil t))) + ;; We've found what we need. + (let* ((ver-match-data (match-data)) + (the-list (dtx-massage-version + ;; NOTE: byte-compiling turns the arithmetic into constants + (buffer-substring (elt ver-match-data (* 2 2)) + (elt ver-match-data (1+ (* 2 3)))))) + (prefix (buffer-substring (elt ver-match-data (* 2 1)) + (elt ver-match-data (1+ (* 2 1))))) + (n (length the-list)) + (k 0)) + (cond + ((= n 0) + (setq dtx-major-version "" + dtx-minor-version "")) + ((= n 1) + (setq dtx-major-version (elt the-list 0) + dtx-minor-version "")) + (t + (setq dtx-major-version (elt the-list (- n 2)) + dtx-minor-version (elt the-list (- n 1))))) + (setq dtx-prefix-version prefix) + ;; now add onto the prefix whatever is right + (while (< k (- n 2)) + (setq dtx-prefix-version + (concat dtx-prefix-version + (if (or (string-equal dtx-prefix-version "") + (string-equal dtx-prefix-version "v")) + "" ".") + (elt the-list k)) + k (1+ k))) + (setq fileversion (dtx-set-version 'noecho))) + ;; We have not found what we need. + (error "Can't find file info! (dtx-get-fileinfo)")) + (if (null noecho) + (progn + (goto-char (point-min)) + (if (and (re-search-forward (dtx-tex-defn "docdate") nil t) + (re-search-forward rx-dtx-date nil t)) + (setq docdate (match-string 0)) + (error + "Can't find documentation date! (dtx-get-fileinfo)")) + (goto-char (point-min)) + (if (and (re-search-forward (dtx-tex-defn "filedate") nil t) + (re-search-forward rx-dtx-date nil t)) + (setq filedate (match-string 0)) + (if (and (re-search-forward + (concat (stx-bwb) + "Provides\\(Package" rx-or + "Class\\)" + rx-whitespace rx-brace-pair + rx-whitespace-newlines "\\[") + nil t) + (re-search-forward rx-dtx-date nil t)) + (setq filedate (match-string 0)) + (error "Can't find file date! (dtx-get-fileinfo)"))) + (message "Version: %s Date: %s Docdate: %s" + fileversion filedate docdate)))))) +(defun dtx-update-documentation-date () + "Update the \"\\docdate\" to the current date and update the checksum." + (interactive "*") + ;; FIX why update the checksum? +;;; (dtx-update-checksum 'noecho) + (save-excursion + (goto-char (point-min)) + (let (case-fold-search) + (if (and (re-search-forward (dtx-tex-defn "docdate") nil t) + (re-search-forward rx-dtx-date nil t)) + (replace-match (dtx-current-date)) + (error + "Can't find documentation date! (dtx-update-documentation-date)"))))) +(defun dtx-update-fileinfo () + "Update the \fileversion and \filedate. + +First assemble `dtx-file-version' by calling +`dtx-set-version'. Then find and update the version and +date in the line that defines them (see `dtx-tex-defn') +using the current values of `dtx-file-version' and +`(dtx-current-date)', respectively. If no such defining +line exists, use a \"\\ProvidesPackage\" or +\"\\ProvidesClass\" line." + (dtx-set-version) + (let (case-fold-search) + (save-excursion + (goto-char (point-min)) + (if (re-search-forward (dtx-tex-defn "fileversion") nil t) + (progn + ;; The argument can contain matching braces. + (backward-char) + (delete-region (point) (forward-list)) + (insert rx-tex-grop dtx-file-version rx-tex-grcl)) + ;; If first search failed, point is still point-min. + (if (and + (re-search-forward + (concat (stx-bwb) "Provides\\(Package" rx-or "Class\\)" + rx-whitespace rx-brace-pair + rx-whitespace-newlines "\\[" + rx-whitespace rx-dtx-date rx-whitespace) + nil t) + (re-search-forward rx-dtx-version nil t)) + (replace-match dtx-file-version) + (error "Can't find file version! (dtx-update-fileinfo)"))) + ;; The date is only changed if a change above succeeds. + (goto-char (point-min)) + (if (re-search-forward (dtx-tex-defn "filedate") nil t) + (progn + (backward-char) + (delete-region (point) (forward-list)) + (insert rx-tex-grop (dtx-current-date) rx-tex-grcl)) + (if (and (re-search-forward + (concat (stx-bwb) + "Provides\\(Package" rx-or + "Class\\)" + rx-whitespace rx-brace-pair + rx-whitespace-newlines "\\[") + nil t) + (re-search-forward rx-dtx-date nil t)) + (replace-match (dtx-current-date)) + (error "Can't find file date! (dtx-update-fileinfo)")))))) +(defun dtx-update-minor-version () + "Increment the minor version and update the checksum." + (interactive "*") + (if (null dtx-file-version) + (dtx-get-fileinfo)) + (setq dtx-minor-version + (cond ((string-equal "" dtx-minor-version) + (read-string "Minor version: ")) + ((string-match "[a-z]" dtx-minor-version) + (string (1+ (string-to-char dtx-minor-version)))) + (t + (number-to-string (1+ (string-to-number dtx-minor-version)))))) + ;; FIX: why bother to update the checksum? +;;; (dtx-update-checksum 'noecho) + (dtx-update-fileinfo)) + +;;; FIX: check behavior +;;; 1.3b major 1.4 +;;; 1.5 major 2 +;;; 2 major 3 +(defun dtx-update-major-version () + "Increment the major version and update the checksum." + (interactive "*") + (if (null dtx-file-version) + (dtx-get-fileinfo)) + (setq dtx-minor-version "" + dtx-major-version (number-to-string + (1+ (string-to-number dtx-major-version)))) + ;; FIX: why bother to update the checksum? +;;; (dtx-update-checksum 'noecho) + (dtx-update-fileinfo)) +(defun dtx-set-version (&optional noecho) + "Assemble `dtx-file-version' from its constituent parts. + +NOECHO non-nil means don't echo the version to the +minibuffer. Returns `dtx-file-version'." + (let (case-fold-search) + (setq dtx-file-version + (concat dtx-prefix-version + (if (or (string-equal dtx-prefix-version "") + (string-equal dtx-prefix-version "v")) + "" ".") + dtx-major-version + (if (not (string-equal dtx-minor-version "")) + (concat (if (or (string-equal dtx-major-version "") + (string-match "[a-z]" dtx-minor-version)) + "" ".") + dtx-minor-version)))) + (or noecho + (message "The version of this file is now <%s>" + dtx-file-version)) + dtx-file-version)) +(defun dtx-massage-version (s) + "Massage a version string into a list of strings. +Given a string S matching a series of unsigned integers +separated by dots, return a list of those integers as +strings. + +If a lowercase letter ends S, it will be tacked on to the +returned list." + (let (case-fold-search + (k 0) + (n (length s)) + element + number-list + kth-char-as-string) + (while (< k n) + (setq kth-char-as-string (string (elt s k))) + (cond + ((string-match "[0-9]" kth-char-as-string) + (setq element (concat element kth-char-as-string))) + ((string-match "\\." kth-char-as-string) + (setq number-list (append number-list (list element)) + element nil)) + ((string-match "[a-zA-Z]" kth-char-as-string) + (if (or (not (equal k (1- n))) + (string-match "[A-Z]" kth-char-as-string)) + (error (concat "Bad letter \"%s\" in version string \"%s\"! " + "The last character is permitted to be a LOWERCASE letter. " + "(dtx-massage-version)") kth-char-as-string s) + (setq number-list (append number-list (list element)) + element kth-char-as-string))) + (t + (error "Can't parse version string \"%s\"! (dtx-massage-version)" s))) + (setq k (1+ k))) + (setq number-list (append number-list (list element))) + (if (equal number-list (list nil)) + (setq number-list nil)) + number-list)) + +;;; Thanks to Karl Eichwalder <ke@gnu.franken.de> for his improvement of this +;;; function. +(defun dtx-current-date () + "Return the current date as a string in the form \"1991/01/23\"." + (format-time-string "%Y/%m/%d")) +(defun dtx-report-checksum-error (function checksum) +"Report a checksum error to the user. +Use FUNCTION, which takes a string argument, to report the +error CHECKSUM. Returns nil so it won't halt +`write-contents-hooks'." + (cond + ((eq checksum 'checksum) + (funcall function "Can't find CheckSum declaration!")) + ((eq checksum 'macrocode) + (funcall function "Mismatched macrocode environments!")) + ((eq checksum 'finale) + (funcall function "Couldn't find Finale!")) + ((eq checksum 'stop) + (funcall function "Couldn't find StopEventually or AddToCheckSum!"))) + nil) +(defun dtx-update-checksum (&optional noecho) + "Update the argument of the \\CheckSum or \\AddToCheckSum command. +If a error occurs while computing the checksum, signal an +error in the interactive case or issue a warning message in +the non-interactive case. + +NOECHO non-nil means don't echo the CheckSum to the minibuffer." + (interactive "*") + (let ((opoint (point)) + (checksum (dtx-compute-checksum)) + case-fold-search) + (if (stringp checksum) + ;; The checksum was successfully computed. + (save-excursion + (goto-char (point-min)) + ;; \CheckSum and \AddToCheckSum should be behind at least one comment. + ;; If there is a \CheckSum in the file, there will not be a + ;; \AddToCheckSum and vice versa. + (if (or + ;; We find \CheckSum + ;; FIX: what if more than one comment? + (and (re-search-forward (concat (stx-boln) "%" rx-whitespace + rx-backslash "CheckSum{") + ;; Leave point at beginning if we don't find + ;; it. + nil t) + (progn + (backward-char) + (delete-region (point) (forward-list)) + t)) + ;; We find \AddToCheckSum. + (and (re-search-forward (concat (stx-boln) "%" rx-whitespace + rx-backslash "AddToCheckSum{") + nil 'move) + (progn + (backward-char) + (delete-region (point) (forward-list)) + t)) + ;; We query the user if we're interactive. + (and (if (interactive-p) + t + (dtx-report-checksum-error 'message 'checksum) + ;; don't proceed + nil) + (goto-char opoint) + (y-or-n-p + "I can't find a `\\CheckSum{}' declaration. Should I insert one at point? ") + (progn + (insert "\\CheckSum") + t))) + (progn + (insert rx-tex-grop checksum rx-tex-grcl) + (if (null noecho) (message "Checksum updated to %s." checksum)) + ;; Return nil so that we can put this function in + ;; `write-contents-hooks'. + nil))) + ;; Checksum was not successfully computed. + (if (interactive-p) + (dtx-report-checksum-error 'error checksum) + (dtx-report-checksum-error 'message checksum))))) +(defun dtx-compute-checksum () +"Return the `doctex-mode' checksum or an error code. +The checksum is the number of backslashes in the macrocode +environments between \\StopEventually and \\Finale if those +declarations are found, and all macrocode environments if an +\\AddToCheckSum declaration is found. The error codes are +`macrocode' for unbalanced macrocode environments, `finale' +for a missing \\Finale, and `stop' for a missing +\\StopEventually. When a `macrocode' error occurs, the +function leaves mark at point and skips to the unclosed +\\begin. + +I think `'move' is a more efficient final argument to `re-search-*' +than t; when it doesn't matter which, I've used `'move'." + (let* (checksum-error + (opoint (point)) + ;; WARNING: see above notes on comment syntax. + (parse-sexp-ignore-comments nil) + (leave-me-here opoint) + case-fold-search + ;; The number of `%' characters must be exact, so we don't use `stx-boln'. + (begin-macrocode (concat "\\(" rx-normal-boln rx-or rx-hidden-boln "\\)% " + rx-backslash "begin{macrocode\*?}")) + (end-macrocode (concat "\\(" rx-normal-boln rx-or rx-hidden-boln "\\)% " + rx-backslash "end{macrocode\*?}")) + (stop-eventually (save-excursion + (goto-char (point-min)) + ;; The argument to \StopEventually might contain + ;; some backslashes that we don't want to count, so + ;; we skip over it with `forward-list'. + ;; WARNING: see above notes on comment syntax. + (and + (re-search-forward (concat (stx-boln) "%" + rx-whitespace rx-backslash + "StopEventually") + nil 'move) + (forward-list)))) + (addtochecksum (save-excursion + (goto-char (point-min)) + (re-search-forward (concat (stx-boln) "%" + rx-whitespace rx-backslash + "AddToCheckSum{") + nil t)))) + (unwind-protect + (if (or stop-eventually addtochecksum) + (save-excursion + ;; Goto `stop-eventually' if it's there, else beginning. + (goto-char (or stop-eventually (point-min))) + (let ((backslashes 0) + (codeblocks 0) + ;; We stop at either \Finale or the end. + (finale (if (not stop-eventually) + (point-max) + (save-excursion + ;; \Finale must be on a line by itself + (re-search-forward (concat (stx-boln) "%" + rx-whitespace rx-backslash + "Finale") nil 'move))))) + (if finale + (while (not (or checksum-error + (equal (point) finale))) + (let ((begin-code (re-search-forward begin-macrocode finale 'move)) + (end-code (save-excursion + (re-search-forward end-macrocode finale 'move)))) + (if begin-code + (if end-code + ;; we have a well-defined macrocode region + (progn + (setq codeblocks (1+ codeblocks)) + (while (and (not (equal (point) end-code)) + (search-forward "\\" end-code 'move)) + (setq backslashes (1+ backslashes)))) + ;; if `end-code' is nil but `begin-code' is non-nil we have a + ;; problem + (setq leave-me-here begin-code) + (setq checksum-error 'macrocode)) + ;; if both `begin-code' and `end-code' are nil, the + ;; checksum is zero, but that's not an error. + ))) + ;; if `finale' is nil we have a problem + (setq checksum-error 'finale)) + ;; The count is too high because it counted the \ in each "\end{macrocode}". + ;; This is the checksum as a string. + (int-to-string (- backslashes codeblocks)))) + ;; If `stop-eventually' and `addtochecksum' are nil we have a problem. + (setq checksum-error 'stop)) + (if checksum-error + (progn + (push-mark opoint) + (goto-char leave-me-here) + checksum-error) + (goto-char leave-me-here))))) + +;;;;;; API FUNCTIONS + +;;; FIX: are set-outlinevars and set-parvars API? +;;; if not, then should give abstract description of what +;;; this func does. +(defun stx-set-vars () + "Call `stx-set-outlinevars' with appropriate args, then `stx-set-parvars'." + (interactive) + (if (equal major-mode 'doctex-mode) + (stx-set-outlinevars dtx-sectioning-commands dtx-sectioning-offset t) + (stx-set-outlinevars stx-sectioning-commands stx-sectioning-offset nil)) + (stx-set-parvars)) +(defmacro stx-merge-list (old new &optional warn) + "Modify a list OLD to include all NEW's elements not in OLD. +Compare elements with `equal'. New elements are added at the end of OLD. NEW +and OLD can be both lists or both alists. NEW or OLD or both can be null. +Duplicate elements in NEW will not be duplicated in OLD. Returns OLD. NEW is +not altered. + +For alists, add NEW's keys to end of OLD if OLD does not have them. +If OLD does have a matching key, change its value to NEW's value for +that key. The key is the car of each element, the value is the cdr. + +Optional WARN non-nil means print a message whenever the value of one +of OLD's keys is being replaced. + +In the case that OLD is nil, this macro evaluates OLD twice, so do not call +`merge-list' with a computed argument that has a side effect that shouldn't be +done twice. The only alternative I see is to work like `add-to-list' and +require a constant symbol name as an argument, effectively requiring you to +compute things once before you call the function. NEW and WARN are evaluated +exactly once." + `(let ((n ,new) + (o ,old) + (w ,warn)) + (unless o + ;; Move the car of `n' to `o'. Then we proceed as if we started in + ;; this state. This filters duplicates from `new' as advertised, + ;; whereas setting `old' to 'new' would not. If `old' is the *constant* + ;; nil then we get an error from `set', which is what we want. + (setq o (set ',old (list (pop n))))) + (when n + ;; Assume that if the car of `o' is a list, then `o' is an alist, + ;; otherwise it is a list. Same for `n'. + (let ((old-is-alist (listp (car o))) + n-car) + (when (not (eq old-is-alist (listp (car n)))) + (error "Arguments to `merge-list' must be both lists or both alists")) + (if old-is-alist + ;; `o' and `n' are alists and non-null + (while (setq n-car (pop n)) + (let ((o-cons (assoc (car n-car) o))) + (if (null o-cons) + (nconc o (list n-car)) + (when w + (message "merge-list: changing value of key %s from %s to %s" + (car o-cons) (cdr o-cons) (cdr n-car))) + (setcdr o-cons (cdr n-car))))) + ;; `old' and `new' are regular lists and non-null + (while (setq n-car (pop n)) + (unless (member n-car o) + (nconc o (list n-car))))))) + o)) + +;;; FIX: emacs-19 only, right? +;; This function can cause screwups if byte-compiled, and if calling it is part +;; of a hook that gets called by `get-buffer-create', for example if it's part +;; of the hook belonging to your `default-major-mode'. +(defun rx-catenate-or (strlist &rest strings) + "Catenate strings with `rx-or' between them. + +Return catenation with `rx-or' of the elements of STRLIST +and optional STRINGS which follow. Ignore STRINGS that are +nil and elements of STRLIST that are nil. Return nil if +passed no strings at all. + +Empty strings will get catenated, and should probably not be +passed to this function. + +STRINGS and the elements of STRLIST are expected to be +strings or nil, but can be other types. See the source for +full details. + +CAUTION: if you are experiencing strange bugs with this +function, see the warning in \"swiftex.el\"." + (if (not (listp strlist)) + (error "First arg %S to `rx-catenate-or' must be a list!" strlist)) + (let* ((strlist (mapconcat (function identity) (delq nil strlist) rx-or)) + (strings (mapconcat (function identity) (delq nil strings) rx-or)) + (islist (not (string-equal "" strlist))) + (arestrings (not (string-equal "" strings)))) + (if (and arestrings islist) + (concat strlist rx-or strings) + (if arestrings + strings + (if islist + strlist))))) + +;;;; EMACS FILE VARIABLES +;;; +;;; Local Variables: +;;; mode: checkdoc-minor +;;; fill-column: 60 +;;; End: +;;; +;;; swiftex.el ends here |