From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Daniel Colascione Newsgroups: gmane.emacs.devel Subject: Re: ielm changes: display standard-output in buffer Date: Wed, 02 Oct 2013 23:23:52 -0700 Message-ID: <524D0D78.9000106@dancol.org> References: <5244795A.2000009@dancol.org> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="3SPMfGQQ9xJo4si420WolnGgeuVMofOWc" X-Trace: ger.gmane.org 1380781527 14091 80.91.229.3 (3 Oct 2013 06:25:27 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 3 Oct 2013 06:25:27 +0000 (UTC) Cc: Emacs development discussions To: Stefan Monnier Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Oct 03 08:25:30 2013 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1VRcLo-0001N8-Vf for ged-emacs-devel@m.gmane.org; Thu, 03 Oct 2013 08:25:29 +0200 Original-Received: from localhost ([::1]:40043 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VRcLo-0005iF-4t for ged-emacs-devel@m.gmane.org; Thu, 03 Oct 2013 02:25:28 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:44480) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VRcLb-0005ht-T6 for emacs-devel@gnu.org; Thu, 03 Oct 2013 02:25:24 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VRcLT-00074t-CY for emacs-devel@gnu.org; Thu, 03 Oct 2013 02:25:15 -0400 Original-Received: from dancol.org ([2600:3c01::f03c:91ff:fedf:adf3]:41485) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VRcLT-0006r1-47 for emacs-devel@gnu.org; Thu, 03 Oct 2013 02:25:07 -0400 Original-Received: from c-76-22-66-162.hsd1.wa.comcast.net ([76.22.66.162] helo=[192.168.1.52]) by dancol.org with esmtpsa (TLS1.0:DHE_RSA_CAMELLIA_256_CBC_SHA1:256) (Exim 4.80) (envelope-from ) id 1VRcLM-00061H-83; Wed, 02 Oct 2013 23:25:00 -0700 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:17.0) Gecko/20130801 Thunderbird/17.0.8 In-Reply-To: X-Enigmail-Version: 1.5.2 X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2600:3c01::f03c:91ff:fedf:adf3 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 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 Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:163829 Archived-At: This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --3SPMfGQQ9xJo4si420WolnGgeuVMofOWc Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable On 9/27/13 5:46 PM, Stefan Monnier wrote:>> +(defun ielm-standard-output-= impl (char) >> + "`standard-output' while evaluating in ielm." >> + (push char ielm-output-buffer) >> + (when (eq char ?\n) >> + (comint-output-filter >> + ielm-active-process >> + (apply #'string (nreverse ielm-output-buffer))) >> + (setf ielm-output-buffer nil))) > > You could avoid the two global vars with: > > (defun ielm-standard-output-impl (proc) > "`standard-output' while evaluating in ielm." > (let ((buffer nil)) > (lambda (char) > (push char buffer) > (when (eq char ?\n) > (comint-output-filter proc (apply #'string (nreverse buffer)= )) > (setf buffer nil))))) Good idea. > >> + (ielm-active-process (ielm-process)) >> + (ielm-output-buffer nil) >> + (standard-output #'ielm-standard-output-impl) > > And here do (standard-output (ielm-standard-output-impl (ielm-process))= ) > > I do have one objection to the patch, tho: it resets standard-output > after each IELM command, so you can't use (setq standard-output ) = RET > and then (princ ) RET and expect to be sent to any mor= e. > > Maybe a buffer-local setting (instead of a let-binding) of > standard-output would solve this problem? I've changed the code so that we don't bind standard-output anymore, but instead explicitly set and reset it, skipping the reset if the value changed while we evaluated the form. I've also added a flush timer so that we don't need to wait for a while line of output to accumulate before displaying output. (Imagine a loop that calls (princ ".") occasionally to indicate progress.) I've also added an ielm-return-for-effect mode and bound C-M-RET to it. This function works like ielm-return, except that it doesn't print the result of evaluating the input expression. This functionality is useful when the expression evaluates to something voluminous and useless. =3D=3D=3D modified file 'lisp/ielm.el' --- lisp/ielm.el 2013-05-30 03:30:34 +0000 +++ lisp/ielm.el 2013-10-03 06:14:15 +0000 @@ -1,3 +1,4 @@ +;;; -*- lexical-binding: t -*- ;;; ielm.el --- interaction mode for Emacs Lisp ;; Copyright (C) 1994, 2001-2013 Free Software Foundation, Inc. @@ -169,6 +170,7 @@ (let ((map (make-sparse-keymap))) (define-key map "\t" 'completion-at-point) (define-key map "\C-m" 'ielm-return) + (define-key map "\e\C-m" 'ielm-return-for-effect) (define-key map "\C-j" 'ielm-send-input) (define-key map "\e\C-x" 'eval-defun) ; for consistency with= (define-key map "\e\t" 'completion-at-point) ; lisp-interaction-mod= e @@ -264,7 +266,7 @@ ;;; Other bindings -(defun ielm-return nil +(defun ielm-return (&optional for-effect) "Newline and indent, or evaluate the sexp before the prompt. Complete sexps are evaluated; for incomplete sexps inserts a newline and indents. If however `ielm-dynamic-return' is nil, this always @@ -277,7 +279,7 @@ (parse-partial-sexp (ielm-pm) (point))))) (if (and (< (car state) 1) (not (nth 3 state))) - (ielm-send-input) + (ielm-send-input for-effect) (when (and ielm-dynamic-multiline-inputs (save-excursion (beginning-of-line) @@ -288,6 +290,11 @@ (newline-and-indent))) (newline))) +(defun ielm-return-for-effect () + "Like `ielm-return', but do not print the result." + (interactive) + (ielm-return t)) + (defvar ielm-input) (defun ielm-input-sender (_proc input) @@ -295,12 +302,12 @@ ;; `ielm-send-input's call. (setq ielm-input input)) -(defun ielm-send-input nil +(defun ielm-send-input (&optional for-effect) "Evaluate the Emacs Lisp expression after the prompt." (interactive) (let (ielm-input) ; set by ielm-input-sender (comint-send-input) ; update history, markers etc. - (ielm-eval-input ielm-input))) + (ielm-eval-input ielm-input for-effect))) ;;; Utility functions @@ -311,16 +318,42 @@ ;;; Evaluation -(defvar ielm-string) -(defvar ielm-form) -(defvar ielm-pos) -(defvar ielm-result) -(defvar ielm-error-type) -(defvar ielm-output) -(defvar ielm-wbuf) -(defvar ielm-pmark) +(defun ielm-standard-output-impl (process) + "Return a function to use for `standard-output' while in ielm eval. +The returned function takes one character as input. Passing nil +to this function instead of a character flushes the output +buffer. Passing t appends a terminating newline if the buffer is +nonempty, then flushes the buffer." + ;; Use an intermediate output buffer because doing redisplay for + ;; each character we output is too expensive. Set up a flush timer + ;; so that users don't have to wait for whole lines to appear before + ;; seeing output. + (let* ((output-buffer nil) + (flush-timer nil) + (flush-buffer + (lambda () + (comint-output-filter + process + (apply #'string (nreverse output-buffer))) + (redisplay) + (setf output-buffer nil) + (when flush-timer + (cancel-timer flush-timer) + (setf flush-timer nil))))) + (lambda (char) + (let (flush-now) + (cond ((and (eq char t) output-buffer) + (push ?\n output-buffer) + (setf flush-now t)) + ((characterp char) + (push char output-buffer) + (setf flush-now (eq char ?\n)))) + (if flush-now + (funcall flush-buffer) + (unless flush-timer + (setf flush-timer (run-with-timer 0.1 nil flush-buffer))))))= ))) -(defun ielm-eval-input (input-string) +(defun ielm-eval-input (input-string &optional for-effect) "Evaluate the Lisp expression INPUT-STRING, and pretty-print the resul= t." ;; This is the function that actually `sends' the input to the ;; `inferior Lisp process'. All comint-send-input does is works out @@ -331,41 +364,41 @@ ;; this as in output filter that converted sexps in the output ;; stream to their evaluated value. But that would have involved ;; more process coordination than I was happy to deal with. - ;; - ;; NOTE: all temporary variables in this function will be in scope - ;; during the eval, and so need to have non-clashing names. - (let ((ielm-string input-string) ; input expression, as a string - ielm-form ; form to evaluate - ielm-pos ; End posn of parse in string - ielm-result ; Result, or error message - ielm-error-type ; string, nil if no error - (ielm-output "") ; result to display - (ielm-wbuf ielm-working-buffer) ; current buffer after evaluation - (ielm-pmark (ielm-pm))) - (unless (ielm-is-whitespace-or-comment ielm-string) + (let ((string input-string) ; input expression, as a string + form ; form to evaluate + pos ; End posn of parse in string + result ; Result, or error message + error-type ; string, nil if no error + (output "") ; result to display + (wbuf ielm-working-buffer) ; current buffer after evaluation + (pmark (ielm-pm))) + (unless (ielm-is-whitespace-or-comment string) (condition-case err - (let ((rout (read-from-string ielm-string))) - (setq ielm-form (car rout) - ielm-pos (cdr rout))) - (error (setq ielm-result (error-message-string err)) - (setq ielm-error-type "Read error"))) - (unless ielm-error-type + (let ((rout (read-from-string string))) + (setq form (car rout) + pos (cdr rout))) + (error (setq result (error-message-string err)) + (setq error-type "Read error"))) + (unless error-type ;; Make sure working buffer has not been killed (if (not (buffer-name ielm-working-buffer)) - (setq ielm-result "Working buffer has been killed" - ielm-error-type "IELM Error" - ielm-wbuf (current-buffer)) - (if (ielm-is-whitespace-or-comment (substring ielm-string ielm-pos)) + (setq result "Working buffer has been killed" + error-type "IELM Error" + wbuf (current-buffer)) + (if (ielm-is-whitespace-or-comment (substring string pos)) ;; To correctly handle the ielm-local variables *, ;; ** and ***, we need a temporary buffer to be ;; current at entry to the inner of the next two let ;; forms. We need another temporary buffer to exit ;; that same let. To avoid problems, neither of ;; these buffers should be alive during the - ;; evaluation of ielm-form. - (let ((*1 *) + ;; evaluation of form. + (let* ((*1 *) (*2 **) (*3 ***) + (active-process (ielm-process)) + (old-standard-output standard-output) + new-standard-output ielm-temp-buffer) (set-match-data ielm-match-data) (save-excursion @@ -377,7 +410,7 @@ ;; these default bindings are ;; identical to the ielm-local ;; bindings. Hence, during the - ;; evaluation of ielm-form, the + ;; evaluation of form, the ;; ielm-local values are going to be ;; used in all buffers except for ;; other ielm buffers, which override @@ -387,52 +420,63 @@ (let ((* *1) (** *2) (*** *3)) + (when (eq standard-output t) + (setf new-standard-output + (ielm-standard-output-impl + active-process)) + (setf standard-output new-standard-outpu= t)) (kill-buffer (current-buffer)) - (set-buffer ielm-wbuf) - (setq ielm-result - (eval ielm-form lexical-binding)) - (setq ielm-wbuf (current-buffer)) + (set-buffer wbuf) + (setq result + (eval form lexical-binding)) + (setq wbuf (current-buffer)) (setq ielm-temp-buffer (generate-new-buffer " *ielm-temp*")) (set-buffer ielm-temp-buffer)) (when ielm-temp-buffer - (kill-buffer ielm-temp-buffer))) - (error (setq ielm-result (error-message-string err)) - (setq ielm-error-type "Eval error")) - (quit (setq ielm-result "Quit during evaluation") - (setq ielm-error-type "Eval error"))))) + (kill-buffer ielm-temp-buffer)) + (when (eq new-standard-output standard-output)= + (ignore-errors + (funcall standard-output t)) + (setf standard-output old-standard-output)))= + (error (setq result (error-message-string err)) + (setq error-type "Eval error")) + (quit (setq result "Quit during evaluation") + (setq error-type "Eval error"))))) (setq ielm-match-data (match-data))) - (setq ielm-error-type "IELM error") - (setq ielm-result "More than one sexp in input")))) + (setq error-type "IELM error") + (setq result "More than one sexp in input")))) ;; If the eval changed the current buffer, mention it here - (unless (eq ielm-wbuf ielm-working-buffer) - (message "current buffer is now: %s" ielm-wbuf) - (setq ielm-working-buffer ielm-wbuf)) + (unless (eq wbuf ielm-working-buffer) + (message "current buffer is now: %s" wbuf) + (setq ielm-working-buffer wbuf)) - (goto-char ielm-pmark) - (unless ielm-error-type + (goto-char pmark) + (unless error-type (condition-case nil ;; Self-referential objects cause loops in the printer, so ;; trap quits here. May as well do errors, too - (setq ielm-output (concat ielm-output (pp-to-string ielm-result))) - (error (setq ielm-error-type "IELM Error") - (setq ielm-result "Error during pretty-printing (bug in pp)")) - (quit (setq ielm-error-type "IELM Error") - (setq ielm-result "Quit during pretty-printing")))) - (if ielm-error-type + (unless for-effect + (setq output (concat output (pp-to-string result)))) + (error (setq error-type "IELM Error") + (setq result "Error during pretty-printing (bug in pp)"= )) + (quit (setq error-type "IELM Error") + (setq result "Quit during pretty-printing")))) + (if error-type (progn (when ielm-noisy (ding)) - (setq ielm-output (concat ielm-output "*** " ielm-error-type " *** = ")) - (setq ielm-output (concat ielm-output ielm-result))) + (setq output (concat output "*** " error-type " *** ")) + (setq output (concat output result))) ;; There was no error, so shift the *** values (setq *** **) (setq ** *) - (setq * ielm-result)) - (setq ielm-output (concat ielm-output "\n"))) - (setq ielm-output (concat ielm-output ielm-prompt-internal)) - (comint-output-filter (ielm-process) ielm-output))) + (setq * result)) + (when (or (not for-effect) (not (equal output ""))) + (setq output (concat output "\n")))) + (setq output (concat output ielm-prompt-internal)) + (comint-output-filter (ielm-process) output))) ;;; Process and marker utilities @@ -462,6 +506,11 @@ Inputs longer than one line are moved to the line following the prompt (but see variable `ielm-dynamic-multiline-inputs'). +* \\[ielm-return-for-effect] works like `ielm-return', except + that it doesn't print the result of evaluating the input. This + functionality is useful when forms would generate voluminous + output. + * \\[completion-at-point] completes Lisp symbols (or filenames, within s= trings), or indents the line if there is nothing to complete. @@ -478,6 +527,13 @@ buffer, then the values in the working buffer are used. The variables `*1', `*2' and `*3', yield the process buffer values. +If, at the start of evaluation, `standard-output' is `t' (the +default), `standard-output' is set to a special function that +causes output to be directed to the ielm buffer. +`standard-output' is restored after evaluation unless explicitly +set to a different value during evaluation. You can use (princ +VALUE) or (pp VALUE) to write to the ielm buffer. + Expressions evaluated by IELM are not subject to `debug-on-quit' or `debug-on-error'. --3SPMfGQQ9xJo4si420WolnGgeuVMofOWc Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (Darwin) iQIcBAEBAgAGBQJSTQ16AAoJEMAaIROpHW7I57kP/2dxxwLyr+gugHT3eKH2Jq8S cKzGour35t6jfD3HvFNVhZUI7WkIDtHQKTgvkWv1cxtCWuQSFkhs6Y5v9Pkpmhlu dpSd2hAmUZtujx6qGJ4Xm44JU4qAE3ddEzUIJwGFRKQxiwPX0t4cc7+O2h8WBg5y apx3LF5GAZ7RH+kIKn5ZX5vMW27Da4b72/dQUlZFEgoQDgOX63aMCp7nHET8sWG3 d5tMT6hU6tYOoACnwVsfkI33/J11IaUI9PlxEC3KIyR4n3s9OlGSP/yYMEghWWdU CydhUuH7gQC+AdgRrtQ7O0ANhJRRSLs7OC3Vl6g/SpaAuH9zf0HLdgI3MyAfhgq8 KRJPkwPbERF61EL0r3IeO4wNX5d/ak6IW3kp7uD6+S8N1wdh0LsTjkjrS8BKjFlN gjH06DOb+RnqrPDAFfgfyGDvqXmQGnRs1vtHLecIUwLEeC0ndvCK37CjCb4BJJe9 1z/QtqAkXaVmPOmDkoPxhT/QX7rnSD4LEx0b2d8aDXPaLNTUZpDmBtqiqTBWjF0P ch8d8nLPCBxn5xAoh3/MvuMD0/NTlb+Q0uRY6d7ztkaxa9+QU1fuGGMrs9poqvJ/ Ho3colMvrfsgRowiaCvyF/+IhoqhlQFfLgrdgs7NNumvB+v6MBGXlDdPvBZjYwF9 Om/QiQ/18IaUTZXWl805 =ZDkS -----END PGP SIGNATURE----- --3SPMfGQQ9xJo4si420WolnGgeuVMofOWc--