John Paul Wallington <jpw(a)shootybangbang.com> writes:
Maybe he was toying with reusing the same string-output buffer,
He almost certainly was. Creating a new buffer is not exactly a cheap
operation.
but that would have insane effects whenever a
`with-output-to-string' call occured within another.
Agreed. Perhaps the macro could be smarter and reuse the "top-level"
buffer, and create new ones for recursive calls. But none of the
other temp-buffer users implement this optimization, so there's no
particular reason to add it here.
Placing BODY outside the `with-current-buffer' seems correct as
well
as neat, but I wonder whether any callers accidentally use `insert'
instead of `princ' and haven't been bitten yet.
If someone uses insert instead of princ, he's doing the wrong thing
and should be fixing his code. It is perfectly valid to (insert ...)
into the current buffer and to (princ ...) to standard-output, and
only the latter to be collected by with-output-to-string.
As for `with-current-buffer', the GNU version uses it to get the
buffer contents, but we can do that with (buffer-string buffer). One
should keep in mind that changing the current buffer is a fairly
expensive operation; it requires, among other things, rebinding all
buffer-local variables to their new values. If the result is
available without changing the current buffer, the alternative method
should be used.
I propose this version, which is similar to FSF's, but with additional
safety of using unwind-protect to guard against temp buffer leakage.
(defmacro with-output-to-string (&rest body)
"Execute BODY, return the text it sent to `standard-output', as a
string."
`(let ((standard-output
(get-buffer-create (generate-new-buffer-name " *string-output*"))))
(unwind-protect
(progn
;; LET guards the value of standard-output should BODY
;; change it.
(let ((standard-output standard-output))
,@body)
(buffer-string standard-output))
(kill-buffer standard-output))))