This is just the following tarball expanded: https://sourceforge.net/projects/nuweb/files/nuweb-1.62.tar.gz/download
810 lines
28 KiB
EmacsLisp
810 lines
28 KiB
EmacsLisp
;;; 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 <ddewaleffe@gmail.com>
|
|
;; Maintainer: Dominique de Waleffe <ddewaleffe@gmail.com>
|
|
;; 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 <filename> 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(<num>) 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 <ravn@imada.ou.dk> 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
|