;;; woman.el --- browse UN*X manual pages `wo (without) man'
-;; Copyright (C) 2000, 2002, 2004, 2005 Free Software Foundation, Inc.
+;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005,
+;; 2006 Free Software Foundation, Inc.
;; Author: Francis J. Wright <F.J.Wright@qmul.ac.uk>
-;; Maintainer: Francis J. Wright <F.J.Wright@qmul.ac.uk>
+;; Maintainer: FSF
;; Keywords: help, unix
;; Adapted-By: Eli Zaretskii <eliz@gnu.org>
;; Version: see `woman-version'
;; 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, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; man man_page_name
-;; Using the `word at point' as a topic suggestion
-;; ===============================================
+;; Using the word at point as the default topic
+;; ============================================
-;; By default, the `woman' command uses the word nearest to point in
-;; the current buffer as a suggestion for the topic to look up. The
-;; topic must be confirmed or edited in the minibuffer. This
-;; suggestion can be turned off, or `woman' can use the suggested
-;; topic without confirmation* if possible, by setting the user-option
-;; `woman-topic-at-point' to nil or t respectively. (Its default
-;; value is neither nil nor t, meaning ask for confirmation.)
+;; The `woman' command uses the word nearest to point in the current
+;; buffer as the default topic to look up if it matches the name of a
+;; manual page installed on the system. The default topic can also be
+;; used without confirmation by setting the user-option
+;; `woman-use-topic-at-point' to t; thanks to Benjamin Riefenstahl for
+;; suggesting this functionality.
-;; [* Thanks to Benjamin Riefenstahl for suggesting this
-;; functionality.]
-
-;; The variable `woman-topic-at-point' can be rebound locally, which
-;; may be useful to provide special private key bindings, e.g.
+;; The variable `woman-use-topic-at-point' can be rebound locally,
+;; which may be useful to provide special private key bindings, e.g.
;; (global-set-key "\C-cw"
;; (lambda ()
;; (interactive)
-;; (let ((woman-topic-at-point t))
+;; (let ((woman-use-topic-at-point t))
;; (woman)))))
;; code fragments, general interest, etc.:
;; Jari Aalto <jari.aalto@cs.tpu.fi>
;; Dean Andrews <dean@dra.com>
-;; Juanma Barranquero <barranquero@laley-actualidad.es>
+;; Juanma Barranquero <lekktu@gmail.com>
;; Karl Berry <kb@cs.umb.edu>
;; Jim Chapman <jchapman@netcomuk.co.uk>
;; Kin Cho <kin@neoscale.com>
;; Paul A. Thompson <pat@po.cwru.edu>
;; Arrigo Triulzi <arrigo@maths.qmw.ac.uk>
;; Geoff Voelker <voelker@cs.washington.edu>
-;; Eli Zaretskii <eliz@is.elta.co.il>
-
-;;; History:
-;; For recent change log see end of file.
+;; Eli Zaretskii <eliz@gnu.org>
\f
;;; Code:
(defvar woman-version "0.551 (beta)" "WoMan version information.")
(require 'man)
+(require 'button)
+(define-button-type 'WoMan-xref-man-page
+ :supertype 'Man-abstract-xref-man-page
+ 'func (lambda (arg)
+ (woman
+ ;; `woman' cannot deal with arguments that contain a
+ ;; section name, like close(2), so strip the section name.
+ (if (string-match Man-reference-regexp arg)
+ (substring arg 0 (match-end 1))
+ arg))))
+
(eval-when-compile ; to avoid compiler warnings
(require 'dired)
+ (require 'cl)
(require 'apropos))
(defun woman-mapcan (fn x)
:type 'string
:group 'woman-interface)
-(defcustom woman-topic-at-point-default 'confirm
- ;; `woman-topic-at-point' may be let-bound when woman is loaded, in
- ;; which case its global value does not get defined.
+(defcustom woman-use-topic-at-point-default nil
+ ;; `woman-use-topic-at-point' may be let-bound when woman is loaded,
+ ;; in which case its global value does not get defined.
;; `woman-file-name' sets it to this value if it is unbound.
- "*Default value for `woman-topic-at-point'."
+ "*Default value for `woman-use-topic-at-point'."
:type '(choice (const :tag "Yes" t)
- (const :tag "No" nil)
- (other :tag "Confirm" confirm))
+ (const :tag "No" nil))
:group 'woman-interface)
-(defcustom woman-topic-at-point woman-topic-at-point-default
- "*Controls use by `woman' of `word at point' as a topic suggestion.
-If non-nil then the `woman' command uses the word at point as an
-initial topic suggestion when it reads a topic from the minibuffer; if
-t then the `woman' command uses the word at point WITHOUT
-INTERACTIVE CONFIRMATION if it exists as a topic. The default value
-is `confirm', meaning suggest a topic and ask for confirmation."
+(defcustom woman-use-topic-at-point woman-use-topic-at-point-default
+ "*Control use of the word at point as the default topic.
+If non-nil the `woman' command uses the word at point automatically,
+without interactive confirmation, if it exists as a topic."
:type '(choice (const :tag "Yes" t)
- (const :tag "No" nil)
- (other :tag "Confirm" confirm))
+ (const :tag "No" nil))
:group 'woman-interface)
(defvar woman-file-regexp nil
:group 'woman-formatting)
(defcustom woman-ignore t
- "*If non-nil then unrecognised requests etc. are ignored. Default is t.
+ "*If non-nil then unrecognized requests etc. are ignored. Default is t.
This gives the standard ?roff behavior. If nil then they are left in
the buffer, which may aid debugging."
:type 'boolean
:group 'woman-faces)
(defcustom woman-use-symbol-font nil
- "*If non-nil then may use the symbol font. It is off by default,
-mainly because it may change the line spacing (in NTEmacs 20.5)."
+ "*If non-nil then may use the symbol font.
+It is off by default, mainly because it may change the line spacing
+\(in NTEmacs 20.5)."
:type 'boolean
:group 'woman-faces)
(kill-buffer standard-output)
))))
-(defvar woman-topic-history nil "Topic read history.")
+(defvaralias 'woman-topic-history 'Man-topic-history)
(defvar woman-file-history nil "File-name read history.")
(defun woman-file-name (topic &optional re-cache)
"Get the name of the UN*X man-page file describing a chosen TOPIC.
-When `woman' is called interactively, the word at point may be used as
-the topic or initial topic suggestion, subject to the value of the
-user option `woman-topic-at-point'. Return nil if no file can be found.
-Optional argument RE-CACHE, if non-nil, forces the cache to be re-read."
+When `woman' is called interactively, the word at point may be
+automatically used as the topic, if the value of the user option
+`woman-use-topic-at-point' is non-nil. Return nil if no file can
+be found. Optional argument RE-CACHE, if non-nil, forces the
+cache to be re-read."
;; Handle the caching of the directory and topic lists:
(if (and (not re-cache)
(or
;; completions, but to return only a case-sensitive match. This
;; does not seem to work properly by default, so I re-do the
;; completion if necessary.
- (let (files)
+ (let (files
+ (default (current-word)))
(or (stringp topic)
- (and (eq t
- (if (boundp 'woman-topic-at-point)
- woman-topic-at-point
- ;; Was let-bound when file loaded, so ...
- (setq woman-topic-at-point woman-topic-at-point-default)))
- (setq topic
- (or (current-word t) "")) ; only within or adjacent to word
- (assoc topic woman-topic-all-completions))
+ (and (if (boundp 'woman-use-topic-at-point)
+ woman-use-topic-at-point
+ ;; Was let-bound when file loaded, so ...
+ (setq woman-use-topic-at-point woman-use-topic-at-point-default))
+ (setq topic (or (current-word t) "")) ; only within or adjacent to word
+ (test-completion topic woman-topic-all-completions))
(setq topic
- (completing-read
- "Manual entry: "
- woman-topic-all-completions nil 1
- ;; Initial input suggestion (was nil), with
- ;; cursor at left ready to kill suggestion!:
- (and woman-topic-at-point
- (cons (or (current-word) "") 0)) ; nearest word
- 'woman-topic-history)))
+ (let* ((word-at-point (current-word))
+ (default
+ (when (and word-at-point
+ (test-completion
+ word-at-point woman-topic-all-completions))
+ word-at-point)))
+ (completing-read
+ (if default
+ (format "Manual entry (default %s): " default)
+ "Manual entry: ")
+ woman-topic-all-completions nil 1
+ nil
+ 'woman-topic-history
+ default))))
;; Note that completing-read always returns a string.
(if (= (length topic) 0)
nil ; no topic, so no file!
;; Unread the command event (TAB = ?\t = 9) that runs the command
;; `minibuffer-complete' in order to automatically complete the
;; minibuffer contents as far as possible.
- (setq unread-command-events '(9)) ; and delete any type-ahead!
+ (setq unread-command-events '(9)) ; and delete any type-ahead!
(completing-read "Manual file: " files nil 1
- (try-completion "" files) 'woman-file-history)))
- )))
+ (try-completion "" files) 'woman-file-history))))))
(defun woman-select (predicate list)
"Select unique elements for which PREDICATE is true in LIST.
(define-key woman-mode-map "w" 'woman)
(define-key woman-mode-map "\en" 'WoMan-next-manpage)
(define-key woman-mode-map "\ep" 'WoMan-previous-manpage)
- (define-key woman-mode-map [M-mouse-2] 'woman-follow-word))
+ (define-key woman-mode-map [M-mouse-2] 'woman-follow-word)
+
+ ;; We don't need to call `man' when we are in `woman-mode'.
+ (define-key woman-mode-map [remap man] 'woman)
+ (define-key woman-mode-map [remap man-follow] 'woman-follow))
+
+(defun woman-follow (topic)
+ "Get a Un*x manual page of the item under point and put it in a buffer."
+ (interactive (list (Man-default-man-entry)))
+ (if (or (not topic)
+ (string= topic ""))
+ (error "No item under point")
+ (woman (if (string-match Man-reference-regexp topic)
+ (substring topic 0 (match-end 1))
+ topic))))
(defun woman-follow-word (event)
"Run WoMan with word under mouse as topic.
(setq woman-imenu-done nil)
(if woman-imenu (woman-imenu))
(let (buffer-read-only)
- (Man-highlight-references))
+ (Man-highlight-references 'WoMan-xref-man-page))
(set-buffer-modified-p nil)
(run-mode-hooks 'woman-mode-hook))
;; Output the result:
(and (apropos-print t nil)
message
- (message message))))
+ (message "%s" message))))
(defun WoMan-getpage-in-background (topic)
(defvar WoMan-Man-start-time nil
"Used to record formatting time used by the `man' command.")
-(defadvice Man-getpage-in-background
- (around Man-getpage-in-background-advice (topic) activate)
- "Use WoMan unless invoked outside a WoMan buffer or invoked explicitly.
-Otherwise use Man and record start of formatting time."
- (if (and (eq major-mode 'woman-mode)
- (not (eq (caar command-history) 'man)))
- (WoMan-getpage-in-background topic)
- ;; Initiates man processing
- (setq WoMan-Man-start-time (current-time))
- ad-do-it))
-
-(defadvice Man-bgproc-sentinel
- (after Man-bgproc-sentinel-advice activate)
- ;; Terminates man processing
- "Report formatting time."
- (let* ((time (current-time))
- (time (+ (* (- (car time) (car WoMan-Man-start-time)) 65536)
- (- (cadr time) (cadr WoMan-Man-start-time)))))
- (message "Man formatting done in %d seconds" time)))
+;; Both advices are disabled because "a file in Emacs should not put
+;; advice on a function in Emacs" (see Info node "(elisp)Advising
+;; Functions"). Counting the formatting time is useful for
+;; developping, but less applicable for daily use. The advice for
+;; `Man-getpage-in-background' can be discarded, because the
+;; key-binding in `woman-mode-map' has been remapped to call `woman'
+;; but `man'. Michael Albinus <michael.albinus@gmx.de>
+
+;; (defadvice Man-getpage-in-background
+;; (around Man-getpage-in-background-advice (topic) activate)
+;; "Use WoMan unless invoked outside a WoMan buffer or invoked explicitly.
+;; Otherwise use Man and record start of formatting time."
+;; (if (and (eq major-mode 'woman-mode)
+;; (not (eq (caar command-history) 'man)))
+;; (WoMan-getpage-in-background topic)
+;; ;; Initiates man processing
+;; (setq WoMan-Man-start-time (current-time))
+;; ad-do-it))
+
+;; (defadvice Man-bgproc-sentinel
+;; (after Man-bgproc-sentinel-advice activate)
+;; ;; Terminates man processing
+;; "Report formatting time."
+;; (let* ((time (current-time))
+;; (time (+ (* (- (car time) (car WoMan-Man-start-time)) 65536)
+;; (- (cadr time) (cadr WoMan-Man-start-time)))))
+;; (message "Man formatting done in %d seconds" time)))
\f
;;; Buffer handling:
(interactive) ; mainly for testing
(WoMan-log-begin)
(run-hooks 'woman-pre-format-hook)
+
+ ;; look for macro sets that woman cannot handle:
+ (goto-char (point-min))
+ (let ((case-fold-search nil))
+ (unless (and (re-search-forward "^\\.SH[ \n]" (point-max) t)
+ (progn (goto-char (point-min))
+ (re-search-forward "^\\.TH[ \n]" (point-max) t))
+ (progn (goto-char (point-min))
+ (not (re-search-forward "^\\.\\([pnil]p\\|sh\\)[ \n]"
+ (point-max) t))))
+ (error "WoMan can only format man pages written with the usual `-man' macros")))
+
(and (boundp 'font-lock-mode) font-lock-mode (font-lock-mode -1))
;; (fundamental-mode)
(let ((start-time (current-time)) ; (HIGH LOW MICROSEC)
(woman0-search-regex
(concat woman0-search-regex-start woman0-search-regex-end))
woman0-rename-alist)
+ (set-marker-insertion-type woman0-if-to t)
(while (re-search-forward woman0-search-regex nil t)
(setq request (match-string 1))
(cond ((string= request "ig") (woman0-ig))
(setq c (memq (following-char) woman-if-conditions-true)))
;; Unrecognised letter so reject:
((looking-at "[A-Za-z]") (setq c nil)
- (WoMan-warn "%s %s -- unrecognised condition name rejected!"
+ (WoMan-warn "%s %s -- unrecognized condition name rejected!"
request (match-string 0)))
;; Accept strings if identical:
((save-restriction
;; String delimiter can be any non-numeric character,
;; including a special character escape:
(looking-at "\\(\\\\(..\\|[^0-9]\\)\\(.*\\)\\1\\(.*\\)\\1\\'"))
- (let ((end1 (copy-marker (match-end 2)))) ; end of first string
+ (let ((end1 (copy-marker (match-end 2) t))) ; End of first string.
;; Delete 2nd and 3rd delimiters to avoid processing them:
(delete-region (match-end 3) woman0-if-to)
(delete-region (match-end 2) (match-beginning 3))
(error "File `%s' not found" name))
(beginning-of-line)
(woman-delete-line 1)
- (let ((from (point))
- (to (make-marker))
- (length (woman-insert-file-contents filename 0)))
- (set-marker to (+ from length))
+ (let* ((from (point))
+ (length (woman-insert-file-contents filename 0))
+ (to (copy-marker (+ from length) t)))
(woman-pre-process-region from to)
(set-marker to nil)
(goto-char from)
(defsubst woman2-process-escapes-to-eol (&optional numeric)
"Process remaining escape sequences up to eol.
Handle numeric arguments specially if optional argument NUMERIC is non-nil."
- (woman2-process-escapes
- (save-excursion (end-of-line) (point-marker))
- numeric))
+ (woman2-process-escapes (copy-marker (line-end-position) t) numeric))
(defun woman2-nr (to)
".nr R +/-N M -- Assign +/-N (wrt to previous value, if any) to register R.
(woman-registers woman-registers)
fn request translations
tab-stop-list)
+ (set-marker-insertion-type to t)
;; ?roff does not squeeze multiple spaces, but does fill, so...
(fset 'canonically-space-region 'ignore)
;; Try to avoid spaces inheriting underlines from preceding text!
;; Call the appropriate function:
(funcall fn to)))
(if (not (eobp)) ; This should not happen, but ...
- (woman2-format-paragraphs (point-max-marker) woman-left-margin))
+ (woman2-format-paragraphs (copy-marker (point-max) t)
+ woman-left-margin))
(fset 'canonically-space-region canonically-space-region)
(fset 'set-text-properties set-text-properties)
(fset 'insert-and-inherit insert-and-inherit)
(defun woman2-process-escapes (to &optional numeric)
"Process remaining escape sequences up to marker TO, preserving point.
Optional argument NUMERIC, if non-nil, means the argument is numeric."
+ (assert (and (markerp to) (marker-insertion-type to)))
;; The first two cases below could be merged (maybe)!
(let ((from (point)))
;; Discard zero width filler character used to hide leading dots
(delete-char -1)
(delete-char 1)
(looking-at "\\(.\\)\\(.*\\)\\1")
- (let ((to (make-marker)) from N c)
- (set-marker to (match-end 2))
- (delete-char 1)
- (setq from (point)
- N (woman-parse-numeric-arg))
- (setq c (if (< (point) to) (following-char) ?_))
+ (forward-char 1)
+ (let* ((to (match-end 2))
+ (from (match-beginning 0))
+ (N (woman-parse-numeric-arg))
+ (c (if (< (point) to) (following-char) ?_)))
(delete-region from to)
(delete-char 1)
- (set-marker to nil)
(insert (make-string N c))
))
(eolp)
(skip-syntax-forward " ")
(setq woman-leave-blank-lines 1))
- (beginning-of-line)
+ ;; This shouldn't happen, but in case it does (e.g. for
+ ;; badly-formatted manfiles with no terminating newline),
+ ;; avoid an infinite loop.
+ (unless (and (eolp) (eobp))
+ (beginning-of-line))
;; If a single short line then just leave it.
;; This is necessary to preserve some table layouts.
;; PROBABLY NOT NECESSARY WITH SQUEEZE MODIFICATION !!!!!