From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: Paul Pogonyshev Newsgroups: gmane.emacs.devel Subject: Re: simple patch for `etags.el' Date: Fri, 24 Sep 2004 01:21:45 -0200 Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Message-ID: <200409240121.45514.pogonyshev@gmx.net> References: <200409201650.26315.pogonyshev@gmx.net> <200409222230.55996.pogonyshev@gmx.net> NNTP-Posting-Host: deer.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Trace: sea.gmane.org 1095978474 11456 80.91.229.6 (23 Sep 2004 22:27:54 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Thu, 23 Sep 2004 22:27:54 +0000 (UTC) Cc: "Kim F. Storm" , emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri Sep 24 00:27:32 2004 Return-path: Original-Received: from lists.gnu.org ([199.232.76.165]) by deer.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 1CAc3n-0001KX-00 for ; Fri, 24 Sep 2004 00:27:32 +0200 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1CAc9p-0002BF-OS for ged-emacs-devel@m.gmane.org; Thu, 23 Sep 2004 18:33:45 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.33) id 1CAc9f-0002A8-6Y for emacs-devel@gnu.org; Thu, 23 Sep 2004 18:33:35 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.33) id 1CAc9c-000291-9p for emacs-devel@gnu.org; Thu, 23 Sep 2004 18:33:34 -0400 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1CAc9b-00028r-Uy for emacs-devel@gnu.org; Thu, 23 Sep 2004 18:33:31 -0400 Original-Received: from [213.165.64.20] (helo=mail.gmx.net) by monty-python.gnu.org with smtp (Exim 4.34) id 1CAc3W-0000A1-83 for emacs-devel@gnu.org; Thu, 23 Sep 2004 18:27:14 -0400 Original-Received: (qmail 22357 invoked by uid 65534); 23 Sep 2004 22:20:27 -0000 Original-Received: from unknown (EHLO localhost.localdomain) (195.50.12.116) by mail.gmx.net (mp008) with SMTP; 24 Sep 2004 00:20:27 +0200 X-Authenticated: #16844820 Original-To: rms@gnu.org User-Agent: KMail/1.4.3 In-Reply-To: X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: main.gmane.org gmane.emacs.devel:27518 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:27518 RMS wrote: > It looks good. > > To install it, we need the text for etc/NEWS and for the manual. Here is the patch rediffed together with new manual node. I also tweaked comments in `subr.el' as I have suddenly discovered that minibuffer is not the same as echo area. My problems with CVS are still present, so the patch is made ``by hands.'' A news entry could look like this (to go to ``Lisp Changes''): +++ ** New functions `make-progress-reporter', `progress-reporter-update', `progress-reporter-force-update' and `progress-reporter-done' provide a simple and efficient way of printing progress messages to the user. Change log entry for `lisp/ChangeLog': 2004-09-24 Paul Pogonyshev =09* tar-mode.el (tar-summarize-buffer): Use progress reporter. =09* progmodes/etags.el (etags-tags-completion-table): Use progress =09reporter. =09(etags-tags-apropos): Likewise. =09* subr.el (make-progress-reporter, progress-reporter-update) =09(progress-reporter-force-update, progress-reporter-do-update) =09(progress-reporter-done): New functions. Change log entry for `lispref/ChangeLog': 2004-09-24 Paul Pogonyshev =09* display.texi (Progress): New node. --- lispref/display.texi.~1.124.~=092004-07-17 12:40:22.000000000 -0200 +++ lispref/display.texi=092004-09-24 00:58:48.000000000 -0200 @@ -16,6 +16,7 @@ that Emacs presents to the user. * Truncation:: Folding or wrapping long text lines. * The Echo Area:: Where messages are displayed. * Warnings:: Displaying warning messages for the user. +* Progress:: Informing user about progress of a long operatio= n. * Invisible Text:: Hiding part of the buffer text. * Selective Display:: Hiding part of the buffer text (the old way). * Overlay Arrow:: Display of an arrow to indicate position. @@ -530,7 +531,105 @@ symbols. If it matches the first few el that warning is not logged. @end defopt =20 +@node Progress +@section Operation Progress +@cindex progress + +When an operation can take a while to finish, you should inform the +user about the progress it makes. This way the user can estimate +remaining time and clearly see that Emacs is busy working, not hung. + +Functions listed in this section provide simple and efficient way of +reporting operation progress. Here is a working example that does +nothing useful: + +@example +(let ((progress-reporter + (make-progress-reporter "Collecting some mana for Emacs..." + 0 500))) + (dotimes (k 500) + (sit-for 0.01) + (progress-reporter-update progress-reporter k)) + (progress-reporter-done progress-reporter)) +@end example + +@defun make-progress-reporter message min-value max-value &optional curr= ent-value min-change min-time +This function creates a progress reporter---the object you will use as +an argument for all other functions listed here. The idea is to +precompute as much data as possible to make progress reporting very +fast. + +The @var{message} will be displayed in the echo area, followed by +progress percentage. @var{message} is treated as a simple string. If +you need it to depend on a filename, for instance, use @code{format} +before calling this function. + +@var{min-value} and @var{max-value} arguments stand for starting and +final states of your operation. For instance, if you scan a buffer, +they should be the results of @code{point-min} and @code{point-max} +correspondingly. It is required that @var{max-value} is greater than +@var{min-value}. If you create progress reporter when some part of +the operation has already been completed, then specify +@var{current-value} argument. But normally you should omit it or set +it to @code{nil}---it will default to @var{min-value} then. + +Remaining arguments control the rate of echo area updates. Progress +reporter will wait for at least @var{min-change} more percents of the +operation to be completed before printing next message. +@var{min-time} specifies the minimum time in seconds to pass between +successive prints. It can be fractional. Depending on Emacs and +system capabilities, progress reporter may or may not respect this +last argument or do it with varying precision. Default value for +@var{min-change} is 1 (one percent), for @var{min-time}---0.2 +(seconds.) + +This function calls @code{progress-reporter-update}, so the first +message is printed immediately. +@end defun + +@defun progress-reporter-update reporter value +This function does the main work of reporting progress of your +operation. It print the message of @var{reporter} followed by +progress percentage determined by @var{value}. If percentage is zero, +then it is not printed at all. + +@var{reporter} must be the result of a call to +@code{make-progress-reporter}. @var{value} specifies the current +state of your operation and must be between @var{min-value} and +@var{max-value} (inclusive) as passed to +@code{make-progress-reporter}. For instance, if you scan a buffer, +then @var{value} should be the result of a call to @code{point}. + +This function respects @var{min-change} and @var{min-time} as passed +to @code{make-progress-reporter} and so does not output new messages +on every invocation. It is thus very fast and normally you should not +try to reduce the number of calls to it: resulting overhead will most +likely negate your effort. +@end defun + +@defun progress-reporter-force-update reporter value &optional new-messa= ge +This function is similar to @code{progress-reporter-update} except +that it prints a message in the echo area unconditionally. + +First two arguments has the same meaning as for +@code{progress-reporter-update}. Optional @var{new-message} allows +you to change the message of the @var{reporter}. Since this functions +always updates the echo area, such a change will be immediately +presented to the user. +@end defun + +@defun progress-reporter-done reporter +This function should be called when the operation is finished. It +prints the message of @var{reporter} followed by word ``done'' in the +echo area. + +You should always call this function and not hope for +@code{progress-reporter-update} to print ``100%.'' Firstly, it may +never print it, there are many good reasons for this not to happen. +Secondly, ``done'' is more explicit. +@end defun + @node Invisible Text @section Invisible Text =20 --- lisp/subr.el.~1.407.~=092004-09-08 10:24:29.000000000 -0200 +++ lisp/subr.el=092004-09-24 00:45:07.000000000 -0200 @@ -2644,5 +2644,133 @@ The properties used on SYMBOL are `compo (put symbol 'abortfunc (or abortfunc 'kill-buffer)) (put symbol 'hookvar (or hookvar 'mail-send-hook))) =20 +=20 +;; Standardized progress reporting + +;; Progress reporter has the following structure: +;; +;;=09(NEXT-UPDATE-VALUE . [NEXT-UPDATE-TIME +;;=09=09=09 MIN-VALUE +;;=09=09=09 MAX-VALUE +;;=09=09=09 MESSAGE +;;=09=09=09 MIN-CHANGE +;;=09=09=09 MIN-TIME]) +;; +;; This weirdeness is for optimization reasons: we want +;; `progress-reporter-update' to be as fast as possible, so +;; `(car reporter)' is better than `(aref reporter 0)'. +;; +;; NEXT-UPDATE-TIME is a float. While `float-time' loses a couple +;; digits of precision, it doesn't really matter here. On the other +;; hand, it greatly simplifies the code. + +(defun make-progress-reporter (message min-value max-value +=09=09=09=09 &optional current-value +=09=09=09=09 min-change min-time) + "Return an object suitable for reporting operation progress with `prog= ress-reporter-update'. + +MESSAGE is shown in the echo area. When at least 1% of operation +is complete, the exact percentage will be appended to the +MESSAGE. When you call `progress-reporter-done', word \"done\" +is printed after the MESSAGE. You can change MESSAGE of an +existing progress reporter with `progress-reporter-force-update'. + +MIN-VALUE and MAX-VALUE designate starting (0% complete) and +final (100% complete) states of operation. The latter should be +larger; if this is not the case, then simply negate all values. +Optional CURRENT-VALUE specifies the progress by the moment you +call this function. You should omit it or set it to nil in most +cases since it defaults to MIN-VALUE. + +Optional MIN-CHANGE determines the minimal change in percents to +report (default is 1%.) Optional MIN-TIME specifies the minimal +time before echo area updates (default is 0.2 seconds.) If +`float-time' function is not present, then time is not tracked +at all. If OS is not capable of measuring fractions of seconds, +then this parameter is effectively rounded up." + + (unless min-time + (setq min-time 0.2)) + (let ((reporter +=09 (cons min-value ;; Force a call to `message' now +=09 (vector (if (and (fboundp 'float-time) +=09=09=09=09(>=3D min-time 0.02)) +=09=09=09 (float-time) nil) +=09=09 min-value +=09=09 max-value +=09=09 message +=09=09 (if min-change (max (min min-change 50) 1) 1) +=09=09 min-time)))) + (progress-reporter-update reporter (or current-value min-value)) + reporter)) + +(defsubst progress-reporter-update (reporter value) + "Report progress of an operation in the echo area. +However, if the change since last echo area update is too small +or not enough time has passed, then do nothing (see +`make-progress-reporter' for details). + +First parameter, REPORTER, should be the result of a call to +`make-progress-reporter'. Second, VALUE, determines the actual +progress of operation; it must be between MIN-VALUE and MAX-VALUE +as passed to `make-progress-reporter'. + +This function is very inexpensive, you may not bother how often +you call it." + (when (>=3D value (car reporter)) + (progress-reporter-do-update reporter value))) + +(defun progress-reporter-force-update (reporter value &optional new-mess= age) + "Report progress of an operation in the echo area unconditionally. + +First two parameters are the same as for +`progress-reporter-update'. Optional NEW-MESSAGE allows you to +change the displayed message." + (let ((parameters (cdr reporter))) + (when new-message + (aset parameters 3 new-message)) + (when (aref parameters 0) + (aset parameters 0 (float-time))) + (progress-reporter-do-update reporter value))) + +(defun progress-reporter-do-update (reporter value) + (let* ((parameters (cdr reporter)) +=09 (min-value (aref parameters 1)) +=09 (max-value (aref parameters 2)) +=09 (one-percent (/ (- max-value min-value) 100.0)) +=09 (percentage (truncate (/ (- value min-value) one-percent))) +=09 (update-time (aref parameters 0)) +=09 (current-time (float-time)) +=09 (enough-time-passed +=09 ;; See if enough time has passed since the last update. +=09 (or (not update-time) +=09 (when (>=3D current-time update-time) +=09=09;; Calculate time for the next update +=09=09(aset parameters 0 (+ update-time (aref parameters 5))))))) + ;; + ;; Calculate NEXT-UPDATE-VALUE. If we are not going to print + ;; message this time because not enough time has passed, then use + ;; 1 instead of MIN-CHANGE. This makes delays between echo area + ;; updates closer to MIN-TIME. + (setcar reporter +=09 (min (+ min-value (* (+ percentage +=09=09=09=09 (if enough-time-passed +=09=09=09=09=09(aref parameters 4) ;; MIN-CHANGE +=09=09=09=09 1)) +=09=09=09=09 one-percent)) +=09=09 max-value)) + (when (integerp value) + (setcar reporter (ceiling (car reporter)))) + ;; + ;; Only print message if enough time has passed + (when enough-time-passed + (if (> percentage 0) +=09 (message "%s%d%%" (aref parameters 3) percentage) +=09(message "%s" (aref parameters 3)))))) + +(defun progress-reporter-done (reporter) + "Print reporter's message followed by word \"done\" in echo area." + (message "%s%d%%" (aref (cdr reporter) 3) 20)) + ;;; arch-tag: f7e0e6e5-70aa-4897-ae72-7a3511ec40bc ;;; subr.el ends here --- lisp/tar-mode.el.~1.95.~=092004-02-09 03:47:32.000000000 -0200 +++ lisp/tar-mode.el=092004-09-22 21:42:29.000000000 -0200 @@ -404,11 +404,10 @@ Place a dired-like listing on the front; then narrow to it, so that only that listing is visible (and the real data of the buffer is hidden)." (set-buffer-multibyte nil) - (message "Parsing tar file...") (let* ((result '()) =09 (pos (point-min)) -=09 (bs (max 1 (- (buffer-size) 1024))) ; always 2+ empty blocks at end. -=09 (bs100 (max 1 (/ bs 100))) +=09 (progress-reporter "Parsing tar file..." +=09=09=09 (point-min) (max 1 (- (buffer-size) 1024))) =09 tokens) (while (and (<=3D (+ pos 512) (point-max)) =09=09(not (eq 'empty-tar-block @@ -416,10 +415,7 @@ is visible (and the real data of the buf =09=09=09 (tar-header-block-tokenize =09=09=09=09(buffer-substring pos (+ pos 512))))))) (setq pos (+ pos 512)) - (message "Parsing tar file...%d%%" -=09 ;(/ (* pos 100) bs) ; this gets round-off lossage -=09 (/ pos bs100) ; this doesn't -=09 ) + (progress-reporter-update progress-reporter pos) (if (eq (tar-header-link-type tokens) 20) =09 ;; Foo. There's an extra empty block after these. =09 (setq pos (+ pos 512))) @@ -446,7 +442,7 @@ is visible (and the real data of the buf ;; A tar file should end with a block or two of nulls, ;; but let's not get a fatal error if it doesn't. (if (eq tokens 'empty-tar-block) -=09(message "Parsing tar file...done") +=09(progress-reporter-done progress-reporter) (message "Warning: premature EOF parsing tar file"))) (save-excursion (goto-char (point-min)) --- lisp/progmodes/etags.el.~1.181.~=092004-08-28 13:30:31.000000000 -020= 0 +++ lisp/progmodes/etags.el=092004-09-22 21:30:55.000000000 -0200 @@ -1229,10 +1229,10 @@ where they were found." =20 (defun etags-tags-completion-table () (let ((table (make-vector 511 0)) -=09(point-max (/ (float (point-max)) 100.0)) -=09(msg-fmt (format=20 -=09=09 "Making tags completion table for %s...%%d%%%%" -=09=09 buffer-file-name))) +=09(progress-reporter +=09 (make-progress-reporter +=09 (format "Making tags completion table for %s..." buffer-file-name) +=09 (point-min) (point-max)))) (save-excursion (goto-char (point-min)) ;; This monster regexp matches an etags tag line. @@ -1253,7 +1253,7 @@ where they were found." =09=09=09 (buffer-substring (match-beginning 5) (match-end 5)) =09=09=09 ;; No explicit tag name. Best guess. =09=09=09 (buffer-substring (match-beginning 3) (match-end 3))) -=09=09 (message msg-fmt (/ (point) point-max))) +=09=09 (progress-reporter-update progress-reporter (point))) =09=09table))) table)) =20 @@ -1433,11 +1433,12 @@ where they were found." (tags-with-face 'highlight (princ buffer-file-name)) (princ "':\n\n")) (goto-char (point-min)) - (let ((point-max (/ (float (point-max)) 100.0))) + (let ((progress-reporter (make-progress-reporter +=09=09=09 (format "Making tags apropos buffer for `%s'..." +=09=09=09=09 string) +=09=09=09 (point-min) (point-max)))) (while (re-search-forward string nil t) - (message "Making tags apropos buffer for `%s'...%d%%" -=09 string -=09 (/ (point) point-max)) + (progress-reporter-update progress-reporter (point)) (beginning-of-line) =20 (let* ( ;; Get the local value in the tags table