[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#12098: How to trap errors in man?

From: Juri Linkov
Subject: bug#12098: How to trap errors in man?
Date: Wed, 01 Aug 2012 11:23:30 +0300
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.1.50 (x86_64-pc-linux-gnu)

> There are plenty of things that freeze the session for much longer
> than the fraction of a second it takes to format most man pages! Even
> bash(1) only takes about a second.

You are right, the largest man page `man bash' takes just 2 sec to format,
so perhaps it makes no sense to run the man command asynchronously nowadays.

The patch below introduces a new variable `Man-async' whose value
could be set to nil to run `man' synchronously.

> Are you sure about that bug number? I looked and it seems to be about
> window layout.

That's right, currently the async mode of man has its peculiarities:
it arranges window layouts after formatting is done, so formatting
can't fit into window layout.

So perhaps we should have two asynchronous modes:
1. delay changes in window configuration
   until the process is finished and man page is formatted.
2. prepare window layout before formatting;

I'm still not sure whether we need the former for backward compatibility,
so `Man-async' could have a special value for it.

> I can't see how to use this to communicate back to a particular caller
> that man failed; help?

I don't know if it's possible with lack of multi-threading
to yield to the command loop while waiting for the process output.
But with the following patch you can run `man' synchronously
by using just `(let ((Man-async nil)) (man "bash"))' when its
default value is not nil.

=== modified file 'lisp/man.el'
--- lisp/man.el 2012-07-11 23:13:41 +0000
+++ lisp/man.el 2012-08-01 08:23:10 +0000
@@ -144,6 +144,20 @@ (defcustom Man-reverse-face 'highlight
   :type 'face
   :group 'man)
+(defcustom Man-async nil
+  "Synchronicity of the manpage command.
+If nil, run the manpage command synchronously.
+If t, run the manpage command asynchronously
+preparing output windows before the process is started.
+If the value is `delayed', run the manpage command
+asynchronously but delay changes in window configuration
+until the process is finished and man page is formatted."
+  :type '(choice (const :tag "Synchronous" nil)
+                 (const :tag "Asynchronous" t)
+                 (const :tag "Delayed" delayed))
+  :group 'man
+  :version "24.2")
 ;; Use the value of the obsolete user option Man-notify, if set.
 (defcustom Man-notify-method (if (boundp 'Man-notify) Man-notify 'friendly)
   "Selects the behavior when manpage is ready.
@@ -904,16 +920,37 @@ (defun Man-getpage-in-background (topic)
 Return the buffer in which the manpage will appear."
   (let* ((man-args topic)
         (bufname (concat "*Man " man-args "*"))
-        (buffer  (get-buffer bufname)))
+        (buffer  (get-buffer bufname))
+        (procbufname (concat " " bufname))
+        procbuffer)
     (if buffer
        (Man-notify-when-ready buffer)
       (require 'env)
-      (message "Invoking %s %s in the background" manual-program man-args)
-      (setq buffer (generate-new-buffer bufname))
-      (with-current-buffer buffer
-       (setq buffer-undo-list t)
-       (setq Man-original-frame (selected-frame))
-       (setq Man-arguments man-args))
+      (cond
+       ((eq Man-async 'delayed)
+       (message "Invoking %s %s in the background" manual-program man-args)
+       (setq buffer (generate-new-buffer bufname))
+       (with-current-buffer buffer
+         (setq buffer-undo-list t)
+         (setq Man-original-frame (selected-frame))
+         (setq Man-arguments man-args)))
+       (t
+       (setq buffer (generate-new-buffer bufname))
+       (setq procbuffer (generate-new-buffer procbufname))
+       ;; Display empty output buffer.
+       (unless (memq Man-notify-method '(polite quiet meek))
+         (Man-notify-when-ready buffer))
+       (with-current-buffer buffer
+         (insert (format "Invoking %s %s in the background\n"
+                         manual-program man-args))
+         (setq buffer-undo-list t)
+         (setq Man-original-frame (selected-frame))
+         (setq Man-arguments man-args))
+       (with-current-buffer procbuffer
+         (setq buffer-undo-list t)
+         (setq Man-original-frame (selected-frame))
+         (setq Man-arguments man-args))))
       (let ((process-environment (copy-sequence process-environment))
            ;; The following is so Awk script gets \n intact
            ;; But don't prevent decoding of the outside.
@@ -952,16 +989,26 @@ (defun Man-getpage-in-background (topic)
                              ((and (integerp Man-width) (> Man-width 0))
-                             (Man-width (frame-width))
-                             ((window-width))))))
+                             (Man-width
+                              (if (eq Man-async 'delayed)
+                                  (frame-width)
+                                (with-selected-window (get-buffer-window
+                                                       buffer t)
+                                  (frame-width))))
+                             (t
+                              (if (eq Man-async 'delayed)
+                                  (window-width)
+                                (with-selected-window (get-buffer-window
+                                                       buffer t)
+                                  (window-width))))))))
        (setenv "GROFF_NO_SGR" "1")
        ;; Since man-db 2.4.3-1, man writes plain text with no escape
        ;; sequences when stdout is not a tty.  In 2.5.0, the following
        ;; env-var was added to allow control of this (see Debian Bug#340673).
        (setenv "MAN_KEEP_FORMATTING" "1")
-       (if (fboundp 'start-process)
+       (if (and Man-async (fboundp 'start-process))
-            (start-process manual-program buffer
+            (start-process manual-program (if (eq Man-async 'delayed) buffer 
                            (if (memq system-type '(cygwin windows-nt))
@@ -969,7 +1016,7 @@ (defun Man-getpage-in-background (topic)
                            (format (Man-build-man-command) man-args))
          (let ((exit-status
-                (call-process shell-file-name nil (list buffer nil) nil
+                (call-process shell-file-name nil (list procbuffer nil) nil
                               (format (Man-build-man-command) man-args)))
                (msg ""))
@@ -980,7 +1027,7 @@ (defun Man-getpage-in-background (topic)
                           (format "exited abnormally with code %d"
                (setq msg exit-status))
-           (Man-bgproc-sentinel bufname msg)))))
+           (Man-bgproc-sentinel procbufname msg)))))
 (defun Man-notify-when-ready (man-buffer)
@@ -1216,16 +1263,18 @@ (defun Man-bgproc-sentinel (process msg)
 synchronously, PROCESS is the name of the buffer where the manpage
 command is run.  Second argument MSG is the exit message of the
 manpage command."
-  (let ((Man-buffer (if (stringp process) (get-buffer process)
-                     (process-buffer process)))
-       (delete-buff nil)
-       (err-mess nil))
+  (let* ((Man-procbuffer (if (stringp process) (get-buffer process)
+                          (process-buffer process)))
+        (Man-buffer (get-buffer (replace-regexp-in-string
+                                 "\\` " "" (buffer-name Man-procbuffer))))
+        (delete-buff nil)
+        (err-mess nil))
     (if (null (buffer-name Man-buffer)) ;; deleted buffer
        (or (stringp process)
            (set-process-buffer process nil))
-      (with-current-buffer Man-buffer
+      (with-current-buffer Man-procbuffer
        (let ((case-fold-search nil))
          (goto-char (point-min))
          (cond ((or (looking-at "No \\(manual \\)*entry for")
@@ -1261,11 +1310,17 @@ (defun Man-bgproc-sentinel (process msg)
+         (unless (eq Man-async 'delayed)
+           (copy-to-buffer Man-buffer (point-min) (point-max))))))
+      (unless delete-buff
+       (with-current-buffer (if (eq Man-async 'delayed) Man-procbuffer 
           (run-hooks 'Man-cooked-hook)
@@ -1279,11 +1342,13 @@ (defun Man-bgproc-sentinel (process msg)
        ;; Man-notify-when-ready because it may switch buffers.
        (if (not delete-buff)
-           (Man-notify-when-ready Man-buffer))
+           (when (or (eq Man-async 'delayed)
+                     (memq Man-notify-method '(polite quiet meek)))
+             (Man-notify-when-ready Man-buffer)))
        (if err-mess
            (error "%s" err-mess))
-       ))))
+       )))
 (defun Man-page-from-arguments (args)
   ;; Skip arguments and only print the page name.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]