; Last updated from parent file: 2006 Jul 25 ; This wants to be a self contained version of eltex ; Lines modified from { bibliotek.eltex.el } are indicated by { ;! } ; USAGE Byte-compile this file in a full environment. The resulting .elc file ; should be portable without need of all the macro files etc that went into its ; compilation. That is, anyone with emacs (and the `cl' package that comes ; with it) should be able to use it. ; For more information the user should load eltex.standalone.elc and read the ; documentation for `eltex-do' etc. using the command `describe-function' ; Time-stamp:<2006-Jul-25 01:15:17 17605.43237 at umoja.phy.syr.edu> ;: Terminology ; ; In something like (E::abs-val b) ; we call "abs-val" the TAG ; we call "b" the CODA ; we call "(E::" the OPENING ; we call "abs-val b" the NAME (yuk) ; we call the whole thing "(E::abs-val b)" the EQUATION-CONSTRUCT ;: Some variables used ; ; eltex-ref-open "[R::" ; token which opens citations ; eltex-eqn-open "(E::" ; token which opens equation reference ; eltex-ref-offset 1 ; begins numbering references with this ; eltex-eqn-offset 1 ; begins numbering equations with this ; eltex-footnote-indicators ; ; *ref-dictionary* ; *eqn-dictionary* ; *eltex-draftmode* ; will be used to turn on/off draftmode ;: Functions defined herein ((C) means a user command) ; ; del-blanks-fwd ; del-blanks-bwd ; stably-remove-duplicates ; ; within-equation-p ; within-comment-p ; to-next-open ; ; eltex-do (C) ; eltex-do-draftmode (C) ; ; ref (C) ; eqn (C) ; footnote (C) ; ; eltex-number-refs ; eltex-number-eqs ; ; eltex-replace-tags ; eltex-provide-footnote-indicators ; ; eltex-show-refs/eltex-list-refs (C) ; eltex-show-eqs/eltex-list-eqs (C) ; eltex-show-dictionary ;: Preparations ;; (require 'preparations "~/lisp/preparations.el") ;! sa (require 'cl) ;! [comment IN for sa] (provide 'eltex) ;: Global variables (defvar eltex-ref-open "[R::" "* token that opens reference names") (defvar eltex-eqn-open "(E::" "* token that opens equation names") (defvar eltex-footnote-indicators '("$^\\star$" "$^\\dagger$" "$^\\flat$") "* a list of the symbols to be used cyclically to indicate footnotes") ; (setqq eltex-footnote-indicators ("$^\\star$" "$^\\dagger$" "$^\\flat$")) (defvar *ref-dictionary* nil "\ After a call to `eltex-number-refs' this variable holds an ``alist'' mapping each reference-tag to its number. To see this dictionary in readable form call `eltex-list-refs' ") (defvar *eqn-dictionary* nil "\ After a call to `eltex-number-eqs' this variable holds an ``alist'' mapping each equation tag to its number. To see the dictionary in readable form say `eltex-list-eqs' ") (defvar *eltex-draftmode* nil) ;: The functions (defun stably-remove-duplicates (L) " Remove repititions from a list scanning from the left" (remove-duplicates L :test 'equal :from-end t)) (defun del-blanks-fwd (max) " Delete up to max blanks, newlines or carriage returns forward from point" (loop repeat max while (member (following-char) (list ?\ ?\n ?\r)) do (delete-char 1))) (defun del-blanks-bwd (max) " Delete up to max blanks backward from point" (loop repeat max while (equal (preceding-char) ?\ ) do (delete-char -1))) (deff within-comment-p (&optional override) "\ Is point within a (``non-escaped'') comment? True iff a `%' strictly precedes point on the same line, and the line does not begin with the `override' escape string. \ " ;-------------------------------------------- ;/mark beginning of line and current location ;-------------------------------------------- (varbind stop (point)) (beginning-of-line) (varbind start (point)) (setf (point) stop) ;--------------------------------------- ;/look for `%' in that portion of buffer ;--------------------------------------- (and (find ?% (buffer-substring start stop)) (not (equal override (buffer-substring start (+ start (length override))))))) (deff within-equation-p () "\ True iff point is within a displayed equation (ie is preceded by an odd number of $$'s, ignoring $$ within comments). " (interactive) (varbind end (point)) (save-excursion (setf (point) (point-min)) (oddp (loop while (search-forward "$$" end t) unless (within-comment-p) sum 1)))) (deff to-next-open (opening) "\ Go to next construct-opening and leave point just after it. Pass over constructs within comments unless they are escaped by `% eltex:' at the beginning of the line. (The word construct means equation number or reference etc) \ " (loop while (search-forward opening nil t) unless (within-comment-p "% eltex:") return (point))) ; ; the args to `search-forward' mean: "no bound" and "return nil if not found" (defun eltex-show-dictionary (dict) " used to display dictionary of references or equations" (with-output-to-temp-buffer (princ (format "*Eltex Dictionary*")) (mapcar 'princ (image of (format "%s\n" $) on (sort* (copy-seq dict) '< :key 'cdr))))) ;! alt for sa ;; on (Sort (copy-seq dict) '< :key 'cdr))))) (defun eltex-list-refs () " See the documentation for `eltex-do'" (interactive) (eltex-show-dictionary *ref-dictionary*)) (defalias 'eltex-show-refs 'eltex-list-refs) (defun eltex-list-eqs () " See the documentation for `eltex-do'" (interactive) (eltex-show-dictionary *eqn-dictionary*)) (defalias 'eltex-show-eqs 'eltex-list-eqs) (deff eltex-replace-tags (dictionary open) "\ Replaces partially processed tags by their numbers, removing up to 16 trailing blanks, as well as part of the opening. The net effect can be illustrated by [R::{piombino}] --> [17] Notice here that rather than remove the entire opening string \"[R::\", we have retained its first character in order to preserve the initial bracket. We do the same for an initial parenthesis, but not for any other characters. See the function itself for the klugey details. " ;------------------------------------- ;/decide how much of `open' to replace ;------------------------------------- (varbind head (if (member (elt open 0) (list (elt "[" 0) (elt "(" 0))) (subseq open 1) open)) ;-------------------- ;/do the replacements ;-------------------- (loop for x in dictionary do (save-excursion (while (search-forward (concat head "{" (car x) "}" ) nil t) (replace-match (format "%s" (cdr x)) t t) (del-blanks-fwd 16))))) ; ; In the `search-forward' form, the nil means not to bound the search, ; and the t means to return nil (rather than error) if no match is found. ; ; The args t t to replace-match are telling it not to modify the ; replacement string (they should not be needed, since the latter is just a ; number). ; ; Still needs improvement, see ~/ms/eltex/developing/replace.el (deff eltex-number-refs () "\ Processes references from point forward. A citation should have the form [R::] or [R:: ] where should be either a legal name for an elisp symbol or an integer. We also allow to be be surrounded by a few blanks. (Currently must also be a ``sexp'' for tex mode. Thus, can contain the usual letters [A-z] and digits [0-9] as well as certain special characters including the hyphen [-] and, by special dispensation, the colon [:]. Various other special characters can be ``protected'' by including {} in the tag. For example, `{Minkowski^17:1903_or_1907}' would be valid as a single tag.) This input will turn into [n] where n is a sequential number beginning with *`eltex-ref-offset', which for now is an internal variable set to 1. EXAMPLE [R:: kuku b page 7] --> [6b page 7] A set of tags can be declared equivalent using the syntax % (ref-equivalence:: ...) where the entire equivalence class must go into a single declaration (ie you should NOT declare ovelapping equivalences). EXAMPLE % (ref-equivalence:: logan VisT unimod3) The dictionary used to convert tags into reference numbers is returned and also put into `*ref-dictionary*'. Reference constructs within comments are ignored *unless* the line begins with the string \"% eltex:\". You can use this to override the numbering you would have got otherwise. ADVERTENCIA A totally blank will cause an error \ " (varbind eltex-ref-offset 1 ; number to give to first reference ref-open eltex-ref-open ; token which opens citations equiv-open "(ref-equivalence::" ; token which opens equiv declarations equiv-close ")" ; token which closes them return-dictionary '*ref-dictionary*) ; what to call the resulting dictionary (&bind-too tag-start ; location in buffer where tag starts tag-end ; location in buffer where tag ends tag ; a string identifying a particular reference tags ; list of all the tags dictionary ; maps each tag to its assigned number msg ; used in case of error start ; start of an equivalence declaration stop ; end of an equivalence declaration equivalences ; a list of equivalence classes of tags n) ;; (debug "ref numberer") ;------------------------------------------------------------------ ;/define auxiliary fcns (see below for description of what they do) ;------------------------------------------------------------------ ;--------------------------- ;/the function `gather-tags' ;--------------------------- (fbind gather-tags () ; this version is for REFERENCES (save-excursion (loop ;------------------------------------------------------ ;/Find beginning of next tag and remove initial blanks ;------------------------------------------------------ while (to-next-open ref-open) do (del-blanks-fwd 16) ; remove up to 16 blanks (setq tag-start (point)) ;---------------------------------------------------------------------- ;/Find end of this tag (not of the whole reference-construct) ; Error handler is to take care of blank tag. ; (We temporarily change colon syntax to make it a legal part of a tag) ;---------------------------------------------------------------------- (setq tag-end (condition-case msg ; (progn (vbind colon ?: save-syntax (o string char-syntax colon)) (modify-syntax-entry colon "_") (forward-sexp) ;[here is where it matters that tag be a sexp!] (modify-syntax-entry colon save-syntax) (point)) ; (error ; name of specific error condition (with-output-to-temp-buffer (princ (format " *error in reference numberer*")) (princ (format (concat "Likely syntax error in tex file." "\nProblem is near character %d in buffer %s." "\n(Error message was: %s.)" "\nHere is a glimpse of the offending text:" "\n=======================================\n" "%s" "\n=======================================") (point) (buffer-name) (cadr msg) (buffer-substring (- (point) 64) (+ (point) 48))))) (error "Numbering cannot continue")))) ;---------------------------------------------------------------- ; Extract the tag, and also surround it with braces in the buffer ;---------------------------------------------------------------- (setf tag (buffer-substring tag-start tag-end) (buffer-substring tag-start tag-end) (concat "{"tag"}")) collect tag))) ;----------------------------------- ;/the function `gather-equivalences' ;----------------------------------- (fbind gather-equivalences () (setq equivalences (save-excursion (setf (point) (point-min)) ; go to beginning of buffer (loop with open = equiv-open with close = equiv-close while (search-forward open nil t) ;-------------------------------------------------------- ; Do the following for each equivalence declaration found ;-------------------------------------------------------- do (setq start (point)) (setq stop (search-forward close)) collect (image on (Read-from-string (concat "( "(buffer-substring start stop)" )" )) of (cond ((symbolp $) (symbol-name $)) ((integerp $) (format "%s" $)) (t (error "invalid tag used for a reference")))))))) ; ; Here is where it matters that a tag be a lisp symbol or an ; integer. Obviously it could be changed. ;----------------------------- ;/the function `canonize-tags' ;----------------------------- (fbind canonize-tags () (loop with c-tags = (copy-seq tags) ; protect `tags' by copying for fiber in equivalences do (loop with x = (car fiber) for y in (cdr fiber) do (nsubstitute x y c-tags :test 'equal)) finally return c-tags)) ;------------------------------- ;/the function `make-dictionary' ;------------------------------- (fbind make-dictionary (tags) (loop for j from 0 below (length tags) collect (cons (nth j tags) (+ eltex-ref-offset j)))) ;----------------------------------- ;/the function `complete-dictionary' ;----------------------------------- (fbind complete-dictionary () (setq dictionary (loop for fiber in equivalences do (setq n (cdr (assoc (car fiber) dictionary))) (setq dictionary (append (image on (cdr fiber) of (cons $ n)) dictionary)) finally return dictionary))) ;------------------------------------- ;/Begin actual program ;------------------------------------- (save-excursion (setq tags (stably-remove-duplicates (gather-tags))) (gather-equivalences) (setq dictionary (make-dictionary (stably-remove-duplicates (canonize-tags)))) (complete-dictionary) ; add the non-canonical tags to the dictionary (eltex-replace-tags dictionary ref-open) (set return-dictionary dictionary))) ; ; Notes on `eltex-number-refs' ; ; 1. Buffer should be in tex(t) mode for `forward-sexp' to work right. ; Currently `eltex-do' makes sure that it is in tex mode ; ; 2. Reason for surrounding tag with braces { } is to avoid confusion when one ; tag is a substring of another, as in `causet-2' and `causet-23' ; ; 3. What the auxiliary functions do ; ; `gather-tags' ; Gather the tags sequentially into a list. As each one is ; encountered, remove (up to 16) preceding blanks, and surround ; the tag itself with { }. Return the list of tags. ; (We ignore tags within comments) ; ; `gather-equivalences' ; returns a list of "fibers", where a fiber is a set of equivalent tags. ; Each fiber comes from a SINGLE equivalence declaration. ; Such a declaration is a substring of the buffer, We put () around it ; to make it look like a list, we "read" it, and then we convert ; each elt of the resulting list to a string. ; It's in this fcn that it matters that a tag be *both* a valid lisp symbol ; and a sexp in the mode we are using (so far tex). ; ; `canonize-tags' ; Returns a list of tags in which each tag belonging to a equivalence-fiber ; is replaced by a canonical member of that fiber ; ; `make-dictionary' ; "Creates a dictionary from the set of tags" ; ; ` complete-dictionary' ; "Completes the dictionary, by consing on the non-canonical pairs taken ; from fibers" (deff eltex-number-eqs () "\ Processes equations from point forward. An equation number should have the form (E:: ) where should be either a legal name for an elisp symbol or an integer, and should ALSO be a sexp in tex mode. (It can be surrounded by a few blanks.) This input will turn into (n) where n is a sequential number beginning with *`eltex-eqn-offset', which for now is an internal variable set to 1. Example: (E::kuku b) --> (6b) WARNINGS 1. ``Forward references'' to equations may fail if `$$' are embedded within comments. 2. Using ``(E::??)'' as an eq. number will cause an error (``??'' is not a sexp in tex mode!) 3. A totally blank tag will cause an error 4. Only tags that actually number equations get processed. A stray equation number in the text will not be processed if it corresponds to no actual equation 5. Commented out equations once confused it, but are supposed to be ok now. The dictionary that governs the conversion of tags into equation numbers is returned and also put into `*eqn-dictionary*'. \ " (varbind eltex-eqn-offset 1 ; number to give to first equation eqn-open eltex-eqn-open ; token which opens equation reference return-dictionary '*eqn-dictionary*) (&bind-too tag-start ; location in buffer where tag starts tag-end ; location in buffer where tag ends tag ; string identifying a particular eqn tags ; list of all the tags dictionary ; maps each tag to its assigned number msg ; used in case of error ) ;------------------------------------------------------------------ ;/define auxiliary fcns (seee for description of what they do) ;------------------------------------------------------------------ ;--------------------------- ;/the function `gather-tags' ;--------------------------- (fbind gather-tags () ; this version is for EQUATIONS (save-excursion (loop ;------------------------------------------------------ ;/Find beginning of next tag and remove initial blanks ;------------------------------------------------------ while (to-next-open eqn-open) do (del-blanks-fwd 16) ; remove up to 16 blanks (setq tag-start (point)) ;------------------------------------------------------------ ;/Find end of this tag (not of the whole equation reference) ;/Error handler is to take care of blank tag. ;------------------------------------------------------------ (setq tag-end (condition-case msg ; (progn (forward-sexp) ; here is where it matters that tag be a sexp! (point)) ; (error ; name of specific error condition (with-output-to-temp-buffer (princ (format " *error in equation numberer*")) (princ (format (concat "Likely syntax error in tex file." "\nProblem is near character %d in buffer %s." "\n(Error message was: %s.)" "\nHere is a glimpse of the offending text:" "\n=======================================\n" "%s" "\n=======================================") (point) (buffer-name) (cadr msg) (buffer-substring (- (point) 64) (+ (point) 48))))) (error "Numbering cannot continue")))) ;;; Here should position the cursor to show where error was if possible ;---------------------------------------------------------------- ;/Extract the tag, and also surround it with braces in the buffer ;---------------------------------------------------------------- (setf tag (buffer-substring tag-start tag-end) (buffer-substring tag-start tag-end) (concat "{" tag "}")) ;------------------------------------------------------------------------ ;/collect the tag iff it is within an equation ; (double-checking it's not within a comment, which it should never be) ;------------------------------------------------------------------------ when (and (within-equation-p) (not(within-comment-p))) collect tag))) ;------------------------------- ;/the function `make-dictionary' ;------------------------------- (fbind make-dictionary (tags) (loop for tag in tags for j from eltex-eqn-offset collect (cons tag j))) ;---------------------------------------- ;/Begin actual program ;---------------------------------------- (save-excursion (setq tags (stably-remove-duplicates (gather-tags))) (setq dictionary (make-dictionary tags)) (eltex-replace-tags dictionary eqn-open) (set return-dictionary dictionary))) ; ; Note: In order to make forward references work properly, we only collect ; tags which are within eqs. ; ; What some of the internal functions do ; ; `gather-tags' ; Gather the tags sequentially into a list. As each one is ; encountered, remove initial blanks (up to 16), and surround the tag ; itself with braces { }. Collect it if it's within an equation. ; Ignore all tags that are within comments (deff eltex-provide-footnote-indicators () "\ Provides footnote labels for footnotes that begin: \\footnote{}, choosing them cyclically from the list `eltex-footnote-indicators', which normally is (asterisk dagger flat). If the footnote begins any other way than \\footnote{}, it will be ignored. This can be convenient if you want to label certain footnotes by hand, or for latex files. If you need an extra indicator try \\ddagger or \\otimes. \ " (save-excursion (loop with inds = eltex-footnote-indicators ;------------------------ ;/Find the next footnote ;------------------------ while (search-forward "\\footnote{}" nil t) do (backward-char 1) (insert (car inds)) (setq inds (append (cdr inds) (o list car inds)))))) (deff eltex-do (&optional no-suspend) "\ Numbers the equations and references in a TeX buffer, also provides symbols for the footnotes. Then writes the processed text out to the file `tmp.tex'. Equation and reference numbers should have the respective forms (E:: ) and [R:: ], and a footnote should begin as: \\footnote{} if you want eltex to process it. For further details, see the documentation for the functions eltex-number-eqs, eltex-number-refs, and eltex-provide-footnote-indicators. Unless dissuaded by a prefix argument, we also suspend emacs so you can tex the file tmp.tex from shell level. (NOT WORKING FOR NOW: Your directory is also changed after the suspension to that where tmp.tex resides.) To see the respective dictionaries use `eltex-list-refs' or `eltex-list-eqs'. (See the documentation for the global variables *ref-dictionary* and *eqn-dictionary*.) The interactive commands `eqn' and `ref' aid in writing the TeX file (and they do name completion). Normally the keystrokes ^C^C will invoke `eltex-do' \ " (interactive "P") (save-excursion ;---------------------------------------- ;/bind ^C^C to `eltex-do' for convenience ;---------------------------------------- (local-set-key [?\C-c ?\C-c] 'eltex-do) ;---------------- ;/prepare buffers ;---------------- (varbind in-buff (current-buffer) temp-buff (generate-new-buffer "tmp.tex")) (setf (current-buffer) temp-buff) ; makes "tmp.tex" be the current buffer (insert-buffer in-buff) ;---------------------------------- ;/put the temp buffer into tex mode ;---------------------------------- (tex-mode) ;---------------------------------------------------------------------- ;/the next 3 lines are to undo the effect of outline hiding on the text ;---------------------------------------------------------------------- (outline-minor-mode 1) (show-all) (outline-minor-mode 0) ;-------------------------------------------------- ;/process the references, equations, and footnotes ;-------------------------------------------------- (message "Starting on references") (eltex-number-refs) (message "Starting on equations") (eltex-number-eqs) (message "Starting on footnotes") (eltex-provide-footnote-indicators) ;----------------------------------------- ;/write out the buffer to a file "tmp.tex" ;----------------------------------------- (write-file "tmp.tex") (kill-buffer temp-buff)) ;-------------------------------- ;/suspend emacs unless overridden ;-------------------------------- (unless no-suspend (suspend-emacs (concat "cd " default-directory)))) ; ; FOR SOME REASON THE SECOND ARG SUDEENLY SEEMS TO BE IGNORED: ; GNU Emacs 20.7.1 (alpha-redhat-linux-gnu, X toolkit) of Tue Oct 10 2000 on ; george.devel.redhat.com (deff eltex-do-draftmode (&optional no-suspend) (interactive "P") (let ((*eltex-draftmode* nil)) (eltex-do no-suspend)) (setq *eltex-draftmode* nil)) ; ; So far draft mode does nothing (defined for future reference)! ; This is completely untested. (deff ref () "\ Takes from user and inserts ``[R::]'' into the text. Will do completion on . \ " (interactive) (varbind tag (completing-read "reference name? " *ref-dictionary*)) (insert (concat "[R::" tag "]")) (eltex-do 'no-suspend) (message "%s" (assoc tag *ref-dictionary*))) ; ; We run `eltex-do' so that it will know the new ref immediately. ; Probably only really need a subset of `eltex-number-refs' for this. (deff eqn () "\ Takes from user and inserts ``(E::)'' into the text Will do completion on . \ " (interactive) (varbind tag (completing-read "equation name? " *eqn-dictionary*)) (insert (concat "(E::" tag ")")) (eltex-do 'no-suspend) (message "%s" (assoc tag *eqn-dictionary*))) ; ; We run `eltex-do' so that it will know the new equation immediately. ; (Only really need a subset of `eltex-number-eqs' for this purpose.) (deff footnote () " Insert template for an eltex sytle footnote" (interactive) (insert "\\footnote{}\n%\n{\n}\n%") (previous-line 2)) ;: e n d