-;;; A "macro-only" reimplementation of define-derived-mode.
-
-(defmacro easy-mmode-define-derived-mode (child parent name &optional docstring &rest body)
- "Create a new mode as a variant of an existing mode.
-
-The arguments to this command are as follow:
-
-CHILD: the name of the command for the derived mode.
-PARENT: the name of the command for the parent mode (e.g. `text-mode').
-NAME: a string which will appear in the status line (e.g. \"Hypertext\")
-DOCSTRING: an optional documentation string--if you do not supply one,
- the function will attempt to invent something useful.
-BODY: forms to execute just before running the
- hooks for the new mode.
-
-Here is how you could define LaTeX-Thesis mode as a variant of LaTeX mode:
-
- (define-derived-mode LaTeX-thesis-mode LaTeX-mode \"LaTeX-Thesis\")
-
-You could then make new key bindings for `LaTeX-thesis-mode-map'
-without changing regular LaTeX mode. In this example, BODY is empty,
-and DOCSTRING is generated by default.
-
-On a more complicated level, the following command uses `sgml-mode' as
-the parent, and then sets the variable `case-fold-search' to nil:
-
- (define-derived-mode article-mode sgml-mode \"Article\"
- \"Major mode for editing technical articles.\"
- (setq case-fold-search nil))
-
-Note that if the documentation string had been left out, it would have
-been generated automatically, with a reference to the keymap."
-
- ; Some trickiness, since what
- ; appears to be the docstring
- ; may really be the first
- ; element of the body.
- (if (and docstring (not (stringp docstring)))
- (progn (setq body (cons docstring body))
- (setq docstring nil)))
- (let* ((child-name (symbol-name child))
- (map (intern (concat child-name "-map")))
- (syntax (intern (concat child-name "-syntax-table")))
- (abbrev (intern (concat child-name "-abbrev-table")))
- (hook (intern (concat child-name "-hook"))))
-
- `(progn
- (defvar ,map (make-sparse-keymap))
- (defvar ,syntax (make-char-table 'syntax-table nil))
- (defvar ,abbrev (progn (define-abbrev-table ',abbrev nil) ,abbrev))
-
- (defun ,child ()
- ,(or docstring
- (format "Major mode derived from `%s' by `define-derived-mode'.
-Inherits all of the parent's attributes, but has its own keymap,
-abbrev table and syntax table:
-
- `%s', `%s' and `%s'
-
-which more-or-less shadow %s's corresponding tables.
-It also runs its own `%s' after its parent's.
-
-\\{%s}" parent map syntax abbrev parent hook map))
- (interactive)
- ; Run the parent.
- (,parent)
- ; Identify special modes.
- (put ',child 'special (get ',parent 'special))
- ; Identify the child mode.
- (setq major-mode ',child)
- (setq mode-name ,name)
- ; Set up maps and tables.
- (unless (keymap-parent ,map)
- (set-keymap-parent ,map (current-local-map)))
- (let ((parent (char-table-parent ,syntax)))
- (unless (and parent (not (eq parent (standard-syntax-table))))
- (set-char-table-parent ,syntax (syntax-table))))
- (when local-abbrev-table
- (mapatoms
- (lambda (symbol)
- (or (intern-soft (symbol-name symbol) ,abbrev)
- (define-abbrev ,abbrev (symbol-name symbol)
- (symbol-value symbol) (symbol-function symbol))))
- local-abbrev-table))
-
- (use-local-map ,map)
- (set-syntax-table ,syntax)
- (setq local-abbrev-table ,abbrev)
- ; Splice in the body (if any).
- ,@body
- ; Run the hooks, if any.
- (run-hooks ',hook)))))
+;;;
+;;; easy-mmode-define-navigation
+;;;
+
+(defmacro easy-mmode-define-navigation (base re &optional name endfun narrowfun)
+ "Define BASE-next and BASE-prev to navigate in the buffer.
+RE determines the places the commands should move point to.
+NAME should describe the entities matched by RE. It is used to build
+ the docstrings of the two functions.
+BASE-next also tries to make sure that the whole entry is visible by
+ searching for its end (by calling ENDFUN if provided or by looking for
+ the next entry) and recentering if necessary.
+ENDFUN should return the end position (with or without moving point).
+NARROWFUN non-nil means to check for narrowing before moving, and if
+found, do widen first and then call NARROWFUN with no args after moving."
+ (let* ((base-name (symbol-name base))
+ (prev-sym (intern (concat base-name "-prev")))
+ (next-sym (intern (concat base-name "-next")))
+ (check-narrow-maybe
+ (when narrowfun
+ '(setq was-narrowed
+ (prog1 (or (< (- (point-max) (point-min)) (buffer-size)))
+ (widen)))))
+ (re-narrow-maybe (when narrowfun
+ `(when was-narrowed (,narrowfun)))))
+ (unless name (setq name base-name))
+ `(progn
+ (add-to-list 'debug-ignored-errors
+ ,(concat "^No \\(previous\\|next\\) " (regexp-quote name)))
+ (defun ,next-sym (&optional count)
+ ,(format "Go to the next COUNT'th %s." name)
+ (interactive)
+ (unless count (setq count 1))
+ (if (< count 0) (,prev-sym (- count))
+ (if (looking-at ,re) (setq count (1+ count)))
+ (let (was-narrowed)
+ ,check-narrow-maybe
+ (if (not (re-search-forward ,re nil t count))
+ (if (looking-at ,re)
+ (goto-char (or ,(if endfun `(,endfun)) (point-max)))
+ (error "No next %s" ,name))
+ (goto-char (match-beginning 0))
+ (when (and (eq (current-buffer) (window-buffer (selected-window)))
+ (interactive-p))
+ (let ((endpt (or (save-excursion
+ ,(if endfun `(,endfun)
+ `(re-search-forward ,re nil t 2)))
+ (point-max))))
+ (unless (pos-visible-in-window-p endpt nil t)
+ (recenter '(0))))))
+ ,re-narrow-maybe)))
+ (defun ,prev-sym (&optional count)
+ ,(format "Go to the previous COUNT'th %s" (or name base-name))
+ (interactive)
+ (unless count (setq count 1))
+ (if (< count 0) (,next-sym (- count))
+ (let (was-narrowed)
+ ,check-narrow-maybe
+ (unless (re-search-backward ,re nil t count)
+ (error "No previous %s" ,name))
+ ,re-narrow-maybe))))))
+