;;; nuweb.el --- major mode to edit nuweb files with AucTex or TeX. ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Original header ;;; ;;; $ Id: nuweb.el 184 2010-04-12 18:42:08Z ddw $ ;;; ;;; ;; Author: Dominique de Waleffe ;; Maintainer: Dominique de Waleffe ;; Version: $ Revision: 184 $ ;; Keywords: nuweb programming tex tools languages ;; ;; ;; DISCLAIMER: I do not guarantee anything about this ;; package, nor does my employer. You get it as is and not much else. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; This nuweb support package is free software, just as GNU Emacs; you ;; can redistribute it and/or modify it under the terms of the GNU ;; General Public License as published by ;the Free Software Foundation; ;; either version 2, or (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ;; ;;; ;;; Bug reports , suggestions are welcome at http:// ;;; DOCUMENTATION (short) there is no long version yet:-) ;;; To install: ;;; ; if your version of nuweb does not know about @% comments ;;; (setq nuweb-comment-leader "") ;;; ; if you want to activate the mode for .w files ;;; (push '( "\\.w" . nuweb-mode) auto-mode-alist) ;;; ; To load it ;;; (require 'nuweb) ;;; ;;; When called, nuweb-mode calls latex-mode and adds the following ;;; bindings: ;;; ;;; A) Help for editing scraps in language dependent mode ;;; ;;; C-c C-z nuweb-edit-this scrap ;;; Edit the scrap point is on in its own buffer *Source* ;;; put into the mode specified as follows: ;;; a) if the first line contains -*-mode-name-*- that is ;;; used, ;;; b) otherwise, for @o scraps, the mode is ;;; derived from the value of auto-mode-alist ;;; c) otherwise it is taken from the (buffer local) variable ;;; nuweb-source-mode (which defaults to "emacs-lisp") ;;; ;;; The *Source* buffer is then put into Nuweb minor mode ;;; which adds two bindings: ;;; C-c C-z nuweb-install-this-scrap ;;; Which takes the *Source* buffer contents and puts ;;; it back into the web buffer in place of the old ;;; version of the scrap. ;;; C-c M-k nuweb-kill-this-scrap ;;; Which restores the old scrap, ignoring changes ;;; made. ;;; The original buffer is put in read-only mode until you ;;; call one of the two above functions or kill the ;;; *Source* buffer. ;;; C-c @ nuweb-insert-scrap ;;; With no argument: inserts an empty scrap template at ;;; point. ;;; With an argument: prompt for scrap type (oDdD), scrap ;;; name and language mode. A new scrap is inserted and ;;; edited as if nuweb-edit-this-scrap had been called. ;;; ;;; B) Help for navigation on definitions and uses ;;; (not normally allowed by the guidelones for elisp modes, I ;;; could not think of any other other than C-c C-d [rpsn] but ;;; auctex now uses C-c C-d....) ;;; ;;; C-c d r nuweb-compute-d-u ;;; Recompute all defs and uses in current buffer. ;;; C-c d p ;;; Pop back to previous def/use ;;; C-c d s ;;; Find first definition for scrap name at point ;;; C-c d n ;;; Find next definition of same scrap name ;;; C-c u s ;;; Find first use of scrap name at point ;;; C-c u n ;;; Find next use of smae scrap name ;;; M-mouse3 ;;; Find first def or first use (if on a def -> use, if on a ;;; use -> def) ;;; C-M-S-mouse1 ;;; Make an imenu for symbols (@|) , scrap names (@d) and ;;; files (@o). (imenu.el comes with 19.25, and should be on archive ;;; sites). ;;; ;;; C) The commands to run nuweb and latex on the file are known as ;;; Tangle: generate output files only ;;; Weave: generate tex file then run latex on it ;;; Web: does it all. ;;; Provided your version of nuweb does output !name(f.w) and ;;; !offset() in the generated tex file Auctex will take you ;;; right back to Tex errors but at the correct stop in the web file. ;;; ;;; CUSTOMISATION: ;;; ;;; Change language mode for scraps ;;; (setq-default nuweb-source-mode "mode-name-function(without -mode)") ;;; (setq-default nuweb-source-mode "prolog") ;default for all buffers ;;; or (setq nuweb-source-mode "emacs-lisp") ; current one only ;;; ;;; Support for nuweb comments @% (I have patches to nuweb for that) ;;; (setq nuweb-comment-leader "@%") ;;; ;;; ;;; ;;; PROBLEMS: SOLUTION: ;;; ;;; -) Relies on FSF Emacs 19 Upgrade or make the package ;;; back-compatible ;;; -) Functions are not well I should have used nuweb for this ;;; documented ;;; -) Bindings may not suit Change as you like ;;; every one ;;; -) Does not yet support the ;;; standard TeX mode of Emacs ;;; WISH LIST: ;;; -) Menus active on scrap def/use point providing navigation ;;; functions (I'm thinking of looking at imenu.el for this ;;; (Experimental support for this at the end) Bound to C-M-mouse-1. ;;; -) Better support for standard tex mode (or that GNU adopts AucTeX..) ;;; ;;; CONTRIBUTIONS: ;;; Thorbj{\o}rn Andersen suggested the use of C-c C-z ;;; I used it to get into the *source* and as normal exit key. ;;; Also suggested the simple (no prompts) insertion of a scrap ;;; template, and other things. ;;; ;;; AVAILABILITY: ;;; ;;; elisp-archive directory: elisp-archive/modes/nuweb.el.Z ;;; from main site and mirrors ;;; or from by email from me ;;; (require 'cl) (if (locate-library "auctex") (progn (require 'tex-site) (require 'latex)) (require 'tex-mode) (setq TeX-command-list nil) (setq LaTeX-mode-map tex-mode-map) ) ;;; Extend the list of commands ;;; Web generates both tex and sources (push (list "Web" "sh -c \"nuweb %s && pdflatex '\\nonstopmode\\input{%s}'\"" 'TeX-run-LaTeX nil t) TeX-command-list) ;;; Weave generates Tex output then runs LaTeX (push (list "Weave" "sh -c \"nuweb -o %s && pdflatex '\\nonstopmode\\input{%s}'\"" 'TeX-run-LaTeX nil t) TeX-command-list) ;;; Tangle generates only the source file compile not done. (push (list "Tangle" "sh -c \"nuweb -t %s && echo 'Sources updated'\"" 'TeX-run-LaTeX nil t) TeX-command-list) ;;; allow .w as extension (if (boundp 'TeX-file-extensions) (push "w" TeX-file-extensions) (setq TeX-file-extensions '("tex" "sty" "w"))) (defvar nuweb-mode-map nil) (defvar nuweb-source-mode "emacs-lisp") (defvar nuweb-defs nil "List of defs found and where") (defvar nuweb-uses nil "List of uses found and where") (defvar nuweb-names nil "List of canonical names of scraps") (defvar *def-list* nil) (defvar *use-list* nil) (defvar nuweb-def-use-stack nil "Record locations we have visited so far") ; only one of those in effect.... (defvar *nuweb-last-scrap-pos* nil) (defvar *nuweb-last-scrap-begin* nil) (defvar *nuweb-last-scrap-end* nil) (defun ins-@()(interactive)(insert "@@") ) (defun ins-@<()(interactive)(insert "@< @>\n")(forward-char -4) ) (defun ins-@d()(interactive)(insert "@d @{@%\n@|@}\n") (forward-char -11)) (defun ins-@D()(interactive)(insert "@D @{@%\n@|@}\n")(forward-char -11) ) (defun ins-@o()(interactive)(insert "@o @{@%\n@|@}\n") (forward-char -11)) (defun ins-@O()(interactive)(insert "@O @{@%\n@|@}\n") (forward-char -11)) (defun ins-@|()(interactive)(insert "@|") ) (defun ins-@{()(interactive)(insert "@{") ) (defun ins-@}()(interactive)(insert "@}") ) (defun ins-@m()(interactive)(insert "@m") ) (defun ins-@u()(interactive)(insert "@u") ) (defun ins-@f()(interactive)(insert "@f") ) (defun ins-@%()(interactive)(insert "@%") ) (defun nuweb-mode () "Major mode to edit nuweb source files. Adds the following bindings to the normal LaTeX ones. Commands: \{nuweb-mode-map}" (interactive) (latex-mode) (setq mode-name "nuweb") ;; Make sure the nuweb map exist (cond ((or (not (boundp 'nuweb-mode-map)) (null nuweb-mode-map)) ;; this keymap inherit the current local bindings (setq nuweb-mode-map (cons 'keymap LaTeX-mode-map)) (define-key nuweb-mode-map "\C-c\C-z" 'nuweb-edit-this-scrap) (define-key nuweb-mode-map "\C-c@" 'nuweb-insert-scrap) (define-key nuweb-mode-map "@@" 'ins-@) (define-key nuweb-mode-map "@<" 'ins-@<) (define-key nuweb-mode-map "@|" 'ins-@|) (define-key nuweb-mode-map "@}" 'ins-@}) (define-key nuweb-mode-map "@{" 'ins-@{) (define-key nuweb-mode-map "@d" 'ins-@d) (define-key nuweb-mode-map "@D" 'ins-@D) (define-key nuweb-mode-map "@o" 'ins-@o) (define-key nuweb-mode-map "@O" 'ins-@O) (define-key nuweb-mode-map "@%" 'ins-@%) (define-key nuweb-mode-map "@m" 'ins-@m) (define-key nuweb-mode-map "@u" 'ins-@u) (define-key nuweb-mode-map "@f" 'ins-@f) (define-key nuweb-mode-map "\C-cds" 'nuweb-find-def) (define-key nuweb-mode-map "\C-cus" 'nuweb-find-use) (define-key nuweb-mode-map "\C-cdn" 'nuweb-find-next-def) (define-key nuweb-mode-map "\C-cun" 'nuweb-find-next-use) (define-key nuweb-mode-map "\C-cdr" 'nuweb-compute-d-u) (define-key nuweb-mode-map "\C-cdp" 'nuweb-pop-d-u) (define-key nuweb-mode-map [M-mouse-3] 'nuweb-find-def-or-use))) ;; make sure we have our own keymap ;; we use a copy for in buffer so that outline mode is ;; properly initialized (use-local-map (copy-keymap nuweb-mode-map)) (make-local-variable 'nuweb-source-mode) (make-local-variable 'nuweb-defs) (make-local-variable 'nuweb-uses) (make-local-variable 'nuweb-names) (make-local-variable '*def-list*) (make-local-variable '*use-list*) (make-local-variable 'nuweb-def-use-stack) (make-local-variable '*nuweb-last-scrap-pos*) (make-local-variable '*nuweb-last-scrap-begin*) (make-local-variable '*nuweb-last-scrap-end*) (setq TeX-default-extension "w") (setq TeX-auto-untabify nil) (setq TeX-command-default "Web") (run-hooks 'nuweb-mode-hook)) ;; set this to "" if you dont have comments in nuweb ;; (I have patches to support @%) (defvar nuweb-comment-leader "") (defvar nuweb-scrap-name-hist nil "History list for scrap names used") (defun nuweb-insert-scrap(arg) "Insert a scrap at current cursor location. With an argument, prompts for the type, name and editing mode then directly enter the *Source* buffer. If no argument given, simply inserts a template for a scrap" (interactive "P") (if arg (apply 'nuweb-insert-scrap-intern (list (concat (read-from-minibuffer "Type of scrap: " "d")" ") (concat (read-from-minibuffer "Scrap title:" (car nuweb-scrap-name-hist) nil ;no keymap nil ;dont use read (cons 'nuweb-scrap-name-hist 0)) " ") (read-from-minibuffer "Mode name:" nuweb-source-mode) ;; edit if interactive t)) (save-excursion (nuweb-insert-scrap-intern "" "\n" nuweb-source-mode nil)) (forward-char 1))) (defun nuweb-insert-scrap-intern(type title modename editp) (save-excursion (insert (format "@%s%s@\{%s%s\n@| @\}\n" type title nuweb-comment-leader (if (or (equal modename "") (equal modename nuweb-source-mode)) "" (concat " -*-" modename "-*-"))))) (cond ( editp (forward-line 1) (nuweb-edit-this-scrap)))) ;;; ;;; !!! The file re does not check for nuweb options that may follow (defun nuweb-edit-this-scrap() (interactive) (barf-if-buffer-read-only) (cond ((or (null *nuweb-last-scrap-pos*) (y-or-n-p "You did not finish editing the previous scrap. Continue")) (setq *nuweb-last-scrap-pos* (point-marker)) (let* ((s-begin (and (re-search-backward "@[dDoO]" nil t) ;; (search-forward "@\{" nil t) (point))) (file (if (looking-at "@[oO][ \t]*\\([^ \t]+\\)[ \t]*") (buffer-substring (match-beginning 1) (match-end 1)) nil)) (b-begin (and (re-search-forward (concat "@\{[ \t]*\\(" nuweb-comment-leader "\\)?[ \t]*\\(-\\*-[ \t]*\\([^ \t]+\\)[ \t]*-\\*-\\)?") nil t) (prog2 (if (and (match-beginning 1)(match-end 1)) (skip-chars-forward "[^\n]")) (point)))) (mode-spec (if (and b-begin (match-beginning 3) (match-end 3)) (buffer-substring (match-beginning 3) (match-end 3)) nil)) (offset (- (marker-position *nuweb-last-scrap-pos*) b-begin)) ;; fool nuweb-mode using concat (b-end (and (re-search-forward "@[|}]" nil t) (prog1 t (forward-char -2)) (>= (point) (marker-position *nuweb-last-scrap-pos*)) (point))) (text "") (nuweb-source-mode-orig nuweb-source-mode) (saved-return-location *nuweb-last-scrap-pos*) source-mode) (cond ( (and b-begin b-end) (setq *nuweb-last-scrap-begin* b-begin) (setq *nuweb-last-scrap-end* b-end) (setq text (buffer-substring b-begin b-end)) (setq buffer-read-only t) (setq *nuweb-win-config* (current-window-configuration)) (pop-to-buffer "*Source*") (erase-buffer) (insert text) (setq source-mode nil) (if (and (not source-mode) mode-spec) (setq source-mode (intern (concat (downcase mode-spec) "-mode")))) (if file (let* ((case-fold-search nil) (mode (cdr (find file auto-mode-alist :key 'car :test (function (lambda(a b) (string-match b a))))))) (if mode (setq source-mode mode)))) (if (not source-mode) (setq source-mode (if (stringp nuweb-source-mode-orig) (intern (concat (downcase nuweb-source-mode-orig) "-mode")) source-mode))) (funcall source-mode) ;; go to same relative position (goto-char (+ (point-min) (max offset 0))) ;; clean up when killing the *source* buffer (make-local-variable 'kill-buffer-hook) (make-local-variable 'nuweb-minor-mode) (make-local-variable 'nuweb-return-location) (setq nuweb-return-location saved-return-location) (add-hook 'kill-buffer-hook (function (lambda() (save-excursion (nuweb-kill-this-scrap))))) (nuweb-minor-mode 1) (message "C-c C-z to use source, C-c M-k to abort")) (t (goto-char (marker-position *nuweb-last-scrap-pos*)) (setq *nuweb-last-scrap-pos* nil) (error "Could not identify scrap"))) ) ) (t (message "Use C-x b and select buffer *Source* to finish")))) (defvar nuweb-minor-mode-map nil) (cond ((or (not (boundp 'nuweb-minor-mode-map)) (null nuweb-minor-mode-map)) (setq nuweb-minor-mode-map (make-sparse-keymap)) (define-key nuweb-minor-mode-map "\C-c\C-z" 'nuweb-install-this-scrap) (define-key nuweb-minor-mode-map "\C-c\M-k" 'nuweb-kill-this-scrap) )) (defvar nuweb-minor-mode nil) (make-variable-buffer-local 'nuweb-minor-mode) (or (assq 'nuweb-minor-mode minor-mode-alist) (setq minor-mode-alist (cons '(nuweb-minor-mode " Nuweb") minor-mode-alist))) (or (assq 'nuweb-minor-mode minor-mode-map-alist) (setq minor-mode-map-alist (cons (cons 'nuweb-minor-mode nuweb-minor-mode-map) minor-mode-map-alist))) ;;; The function is there but has nothing to do (thanks to Emacs 19 ;;; function for minor mode bindings ;;; It is here if anyone cares to make it Emacs 18 compatible. (defun nuweb-minor-mode (arg) "This minor mode provides two additional bindings to the current language mode. The bindings allow to install the contents of the *Source* buffer as current scrap body or to kill the buffer and re-install the old scrap body. The bindings installed by this minor mode are \\{nuweb-minor-mode-map}" (interactive "P") (setq nuweb-minor-mode (if (null arg) (not nuweb-minor-mode) (> (prefix-numeric-value arg) 0))) (cond (nuweb-minor-mode ;; turn it on ;; Nothin to do yet... t ) (t ;; turn it off ;; Nothin to do yet... nil ))) (defun nuweb-install-this-scrap() (interactive) (let ((offset (point))) (setq kill-buffer-hook nil) ;; use *Source* local variable to return to correct buffer (switch-to-buffer (marker-buffer nuweb-return-location)) (setq buffer-read-only nil) ;it was writable (set-window-configuration *nuweb-win-config*) ;; now we can use the last-scrap-pos variable (goto-char (marker-position *nuweb-last-scrap-pos* )) (set-marker *nuweb-last-scrap-pos* nil) (recenter) (goto-char *nuweb-last-scrap-begin*) (delete-region *nuweb-last-scrap-begin* *nuweb-last-scrap-end*) (insert-buffer "*Source*") (forward-char (- offset 1)) (setq *nuweb-last-scrap-pos* nil))) (defun nuweb-kill-this-scrap() (interactive) (nuweb-back-to-pos) (setq *nuweb-last-scrap-pos* nil)) (defun nuweb-back-to-pos() (setq kill-buffer-hook nil) (switch-to-buffer (marker-buffer nuweb-return-location)) (setq buffer-read-only nil) (delete-other-windows) (goto-char (marker-position *nuweb-last-scrap-pos*)) (recenter)) ;;; Below is code to support movements based on scrap uses and ;;; definitions. ;;; ;; structure to hold the scrap def descriptors ;; The name field ;; contains the name as found in buffer, then will hold the cononical ;; name (after second pass). While text will contain the whole text ;; matched when the def or use is found. (defstruct scrap-def name text type loc) ;; structure to hold the scrap def descriptors (see above) (defstruct scrap-use name text loc) (defvar nuweb-scrap-def-re "@\\([oOdD]\\)[ \t]*\\(.*[^ \t\n]\\)[ \t]*\n?@{" "Way to match a scrap definition") (defun nuweb-make-scrap-def-list() "Collect a list of all the scrap definitions" (save-excursion (goto-char (point-min)) (loop while (re-search-forward nuweb-scrap-def-re nil t) collect (make-scrap-def :name (buffer-substring (match-beginning 2) (match-end 2)) :type (buffer-substring (match-beginning 1) (match-end 1)) :text (buffer-substring (match-beginning 0) (match-end 0)) :loc (match-beginning 0))))) (defvar nuweb-scrap-use-re "@<[ \t]*\\(.*[^ \t\n]\\)[ \t]*@>" "How to recognize a usage") (defun nuweb-make-scrap-use-list() "Collect list of scrap usages" (save-excursion (goto-char (point-min)) (loop while (re-search-forward nuweb-scrap-use-re nil t) collect (make-scrap-use :name (buffer-substring (match-beginning 1) (match-end 1)) :text (buffer-substring (match-beginning 0) (match-end 0)) :loc (match-beginning 0))))) (defun nuweb-merge-names ( defs uses) "Builds a list of full names use as scrap names" (remove-duplicates (sort* (append (loop for x in defs when (not (string-match ".*\\.\\.\\." (scrap-def-name x))) collect (scrap-def-name x)) (loop for x in uses when (not (string-match ".*\\.\\.\\." (scrap-use-name x))) collect (scrap-use-name x))) 'string-lessp) :test 'equal)) (defun nuweb-canonic-name(name) "Returns the full name corresponding to the one given" (if (string-match "\\(.*\\)\\.\\.\\." name) (let ((prefix (substring name 0 -3))) ;; then finds it in name list (find (regexp-quote prefix) nuweb-names :test 'string-match)) name)) (defun nuweb-compute-d-u() "Recompute all the defs and uses point in the current file" (interactive) (message "Looking for defs") ; gets defs, (setq nuweb-defs (nuweb-make-scrap-def-list)) (message "Looking for uses") ; gets uses (setq nuweb-uses (nuweb-make-scrap-use-list)) ;; compute list of names (message "Fixing names") (setq nuweb-names (nuweb-merge-names nuweb-defs nuweb-uses)) ;; Now change all name fields back to their canonical names ;; in both lists (loop for def in nuweb-defs when (string-match "\\(.*\\)\\.\\.\\." (scrap-def-name def)) do (setf (scrap-def-name def) (nuweb-canonic-name (scrap-def-name def)))) (loop for use in nuweb-uses when (string-match "\\(.*\\)\\.\\.\\." (scrap-use-name use)) do (setf (scrap-use-name use) (nuweb-canonic-name (scrap-use-name use)))) (setq nuweb-def-use-stack nil) (message "Done.")) (defun nuweb-scrap-name-at-point() "Gathers the scrap name under the cursor. Returns a pair whose car is the scrap name found and whose cdr is either 'def or 'use" (interactive) (save-excursion ;; God this code is not the nicest I've written... ;; Comprenne qui pourras (let* ((here (point)) (beg (if (re-search-backward "@[oOdD<]" nil t) (match-end 0) nil)) (to-match (if beg (if (equal (char-after (+ (match-beginning 0) 1)) ?<) "@>" "@{") nil)) (context (if (equal to-match "@{") 'def 'use)) (end (if (search-forward to-match nil t) (match-beginning 0) nil))) (cond ((and beg end (<= beg here) (>= end here)) (cons (buffer-substring (progn (goto-char beg) (skip-chars-forward " \t\n") (point)) (progn (goto-char end) (skip-chars-backward" \t\n") (point))) context)) (t (error "Not on a possible scrap name")))))) (defun nuweb-find-next-use() (interactive) (nuweb-find-use t)) (defun nuweb-find-use(arg) "Find use of scrap name at point. With argument, find next use" (interactive "P") (if (not arg) (nuweb-find-use-internal (nuweb-canonic-name (car(nuweb-scrap-name-at-point))) t) (nuweb-find-use-internal *nuweb-prev-use* nil))) (defun nuweb-find-use-internal (name first) (if first (setq *use-list* (remove* name nuweb-uses :test-not 'equal :key 'scrap-use-name) ;; list of use points for scrap name at point *nuweb-prev-use* name)) (if (not *use-list*) (error "Nor more uses for of <%s>" name) (push (point) nuweb-def-use-stack) (nuweb-position-search (scrap-use-loc (car *use-list*)) (scrap-use-text (pop *use-list*))))) (defun nuweb-find-next-def() (interactive) (nuweb-find-def t)) (defun nuweb-find-def(arg) "Find def of scrap name at point. With argument, find next def" (interactive "P") (if (not arg) (nuweb-find-def-internal (nuweb-canonic-name (car (nuweb-scrap-name-at-point))) t) (nuweb-find-def-internal *nuweb-prev-def* nil))) (defun nuweb-find-def-internal (name first) (if first (setq *def-list* (remove* name nuweb-defs :test-not 'equal :key 'scrap-def-name) ;; list of def points for scrap name at point *nuweb-prev-def* name)) (if (not *def-list*) (error "Nor more defs for <%s>" name) (push (point) nuweb-def-use-stack) (nuweb-position-search (scrap-def-loc (car *def-list*)) (scrap-def-text (pop *def-list*))))) (defun nuweb-position-search( loc expected-text) (goto-char loc) (let ((offset 250) (found (looking-at expected-text)) up down) (while (and (not found) (not (equal up (point-min))) (not (equal down (point-max)))) (setq up (max (- loc offset) (point-min))) (setq down (min (+ loc offset) (point-max))) (setq offset (* 2 offset)) (goto-char up) (setq found (search-forward expected-text down t))) (if (not found) (error "Time to resynchronize defs and uses") (goto-char (+ 2 (match-beginning 0))) (recenter)))) (defun nuweb-find-def-or-use(arg) (interactive "e") (mouse-set-point arg) (let ((scrap (nuweb-scrap-name-at-point))) (message "Looking for: %S" scrap) (cond ((eq (cdr scrap) 'def) (nuweb-find-use-internal (nuweb-canonic-name (car scrap)) t)) ((eq (cdr scrap) 'use) (nuweb-find-def-internal (nuweb-canonic-name (car scrap)) t)) (t (error "nuweb mode: should never get here"))))) (defun nuweb-pop-d-u() (interactive) (goto-char (pop nuweb-def-use-stack))) ;;; ;;; Some support for use of imenu. Experimental. ;;; Provides a menu of scrap definitions (one for files, one for ;;; macros). (eval-when (compile ) (require 'imenu)) (defvar imenu-wanted t "Specifies whether imenu functions are useable in nuweb mode") (if imenu-wanted (progn (require 'imenu) (add-hook 'nuweb-mode-hook 'nuweb-imenu-setup))) (defun nuweb-imenu-setup () (make-local-variable 'imenu-create-index-function) (setq imenu-create-index-function 'nuweb-create-imenu-index) (local-set-key [C-M-S-mouse-1] 'imenu) (imenu-add-menubar-index)) (defvar imenu-nuweb-scrap-regexp "\\(@|[ \t]*\\([^@]*\\)@}\\)\\|\\(@[dDoO][ \t]*\\(.*[^ \t\n]\\)[ \t\n]*@{\\)" "How do we match a scrap def point or a index defining entry") (defun imenu-nuweb-name-and-position-scrap() (if (and (match-beginning 4) (match-end 4)) (cons (buffer-substring (match-beginning 4) (match-end 4)) (match-beginning 0)) nil)) ;;; The symbol information is not used as is... (defun imenu-nuweb-name-and-position-symbol() (if (and (match-beginning 2) (match-end 2)) (cons (buffer-substring (match-beginning 2) (match-end 2)) (match-beginning 0)) nil)) (defmacro imenu-create-submenu-name(name) name) (defun nuweb-create-imenu-index(&optional regexp) (let ((index-macros-alist '()) (index-files-alist '()) (index-symbols-alist '()) (char)) (goto-char (point-min)) ;; (imenu-progress-message 0) ;; Search for the function (save-match-data (while (re-search-forward (or regexp imenu-nuweb-scrap-regexp) nil t) ;; (imenu-progress-message) (let ((scrap-name-pos (imenu-nuweb-name-and-position-scrap)) (symbol-name-pos (imenu-nuweb-name-and-position-symbol))) (save-excursion (goto-char (match-beginning 0)) (cond ((looking-at "@[oO]") (push scrap-name-pos index-files-alist)) ( (looking-at "@|") (if (looking-at "@|[ \t]*@}") t ; do nothing (forward-char 2) (let ((pos (point)) (ref-pos (cdr symbol-name-pos))) (while (not (looking-at "[\t\n ]*@}")) (if (re-search-forward "[\t\n ]*\\([^@ \t\n]+\\)" nil t) (push (cons (buffer-substring (match-beginning 1) (match-end 1)) ref-pos) index-symbols-alist)))))) (t (push scrap-name-pos index-macros-alist))))))) ;; (imenu-progress-message 100) (list ;; the list of symbols is sorted as it is easier to search in an ;; alpha sorted list (cons (imenu-create-submenu-name "Symbols") (sort (nreverse index-symbols-alist) (function(lambda(pair1 pair2) (string< (car pair1) (car pair2)))))) ;; the scrap and file def point are left in their order of ;; appearance in the file. (cons (imenu-create-submenu-name "Macros") (nreverse index-macros-alist)) (cons (imenu-create-submenu-name "Files") (nreverse index-files-alist))))) (provide 'nuweb) ;;; nuweb.el ends here