allegro-cl archives 1997-10-17 | home index prev H thread prev K thread-next J next L |
From: Steve Haflich Subject: Re: xemacs lisp listener buffer max size Date: 1997-10-17 1:16 I was backlogged and didn't get a chance to respond to this promptly. Perhaps it still isn't too late for these remarks to be of use. Date: 07 Oct 1997 22:25:05 UT From: Erik Naggum <naggum.no at erik> | This proposal is simple, provided one is willing to do a little elisp | programming, but consider this thought experiment: | | (defun tailify-buffer (lines) | "Truncate the current buffer to the last n LINES." | (interactive "nNumber of lines: ") | (let (beg end) | (save-excursion | (end-of-buffer) | (forward-line (- lines)) | (setq end (point)) | (beginning-of-buffer) | (setq beg (point))) | (delete-region beg end))) as an Emacs maintainer, it pains me to see this code. Hans Chalupsky's advice (pun intended) is much to be preferred, but see below. both `end-of-buffer' and `beginning-of-buffer' have a strong warning in their documentation not to use them in Lisp programs. ("Don't use this command in Lisp programs!") instead, use (goto-char (point-min)) and (goto-char (point-max)), respectively. (but this is also unnecessary in this case.) Right -- very sloppy of me. I don't write elisp code every day. we need to be careful about a few things when deleting text from the start of a buffer. first, when deleting text, Emacs moves the "gap" to the deletion point and then it is moved again when the next insertion arrives at the end. the "gap" is just that, a gap between two halves of a buffer where all editing operations are performed. moving it or expanding it unnecessarily is the major source of CPU usage in badly written Emacs Lisp code. (not that anybody gives any guidance to Emacs Lisp programmers about it, however.) it's therefore prudent to do something like Hans Chalupsky's 90%-full solution. there is no guarantee that the buffer will actually shrink from this exercise, either, and many small deletions may yet cause Emacs to grow without bound over time. second, the undo list is a user feature, and should not have stuff like this added to it. ideally, the undo list should not affect process output to a buffer, but this is on our to-do list. This seems like an unsolvable algorithm to me. until an Emacs-wide solution is made available, programmers of functions that insert text into buffers from inferior processes must take care to bind `buffer-undo-list' to t dynamically over their insertion. This doesn't seem right. See below. third, a user may have narrowed the buffer by the time these functions are called. (this problem also applies to Hans Chalupsky's code.) this code, replacing the meat of Hans' code, takes care of these problems (save-excursion (set-buffer buffer) (if (< max-acl-buffer-size (buffer-size)) (save-restriction (widen) Corrrection noted, again. (let ((buffer-undo-list t)) (delete-region (point-min) (- (point-max) (truncate max-acl-buffer-size 1.1111111))))))) | Emacs is very reticent to throw anything away. well. `undo-limit' (20000) and `undo-strong-limit' (30000) are the upper limits to how much undo information is kept, in bytes. see their documentation. I tried a quick test, and was surprised by the results. I wrote a couple megabytes into a cl lisp listener buffer, then used delete-region to remove most of it. I then made about ten small additional interactions with the lisp listener. The theory was that the huge delete-region would have been flushed by the size limit on buffer-undo memory. I then used the undo command repeatedly to undo back through the huge delete-region. To my surprise Emacs 19.34.2 was quite willing to reinstantiate all the deleted text, quite in excess of undo-strong-limit. I didn't investigate further (i.e. look at the Emacs source code) and I don't know whether the undo information would eventually have been flushed, perhaps the next time Emacs needed to allocate more memory. You should be able to duplicate this experiment if you are so inclined. (If you're really interested I'll try to repeart it and record the steps.) Meanwhile, it behooves anyone who runs processes lasting several months and which will depend upon Emacs not growing without bounds to investigate and verify that the actual behavior will be reasonable. | You can control this, assuming you don't really need undo | to be available in this buffer. See buffer-disable-undo. hm. the FI package should not introduce process output to the undo list to begin with. continuous output to the Lisp process buffer will be made into one, giant undo item. once past `undo-strong-limit' in length, it will be nuked during garbage collection. in any case, (buffer-disable-undo) is exactly like (setq buffer-undo-list t). process filters have a nasty habit of changing this value if not written super-correctly, so it wouldn't hurt to "reinforce" the binding. I'm unsure whether you are actually suggesting that undo not be supported at all in a listener buffer. I think this would be unfortunate, since I often think and revise while typing at a listener. It might be reasonable to flush the undo information each time immediately after the listener prints to the buffer, so undo information is repeatedly bounded each time lisp prints a prompt. This idea would pose some difficulties for those of us who like to type ahead while the previous top-level form is still executing, but of course the current sublisp mode also misbehaves in that regard. | I believe to buffer-disable-undo appearently flushes all undo information | (but check -- undo informtion is kept in a buffer-local variable -- see | buffer-local-variables). It need be called just once, but is probably | harmless to call repeatedly. the undo information is kept in `buffer-undo-list'. undo information is added to the head of the list in this variable, unless it is t, in which case undo information is discarded. | See the emacs-lisp source file fi-subproc.el. There is an undocumented | hook variable fi::subprocess-filter-insert-output-hook which I believe is | unused except by the "presenting listener" in Composer. It could | probably be used in the initial lisp listener to call tailify-buffer, | something like this, after the ACL has started in the buffer: this is cool. this is even better than Hans Chalupsky's advice. | (save-excursion | (set-buffer "*common-lisp*") | (buffer-disable-undo) | ;; This will be called each time CL output is inserted into the buffer, | ;; just before the output is inserted. | (setq fi::subprocess-filter-insert-output-hook 'tailify-1000)) but _please_ don't nuke hooks. hooks are magic, and should be added to with `add-hook' and removed from with `remove-hook', which keeps magic working. Right. | None of this is checked, and anyone who uses it would have to take the | responsibility for debugging it, supporting it, and keeping up with | future versions. new releases of "comint" in the standard Emacs distribution obey a variable `comint-buffer-maximum-size' if `comint-truncate-buffer' is on the filter functions list. I would suggest that the FI package adopt the newer "comint" version and only add to it where absolutely necessary. Alas, I suspect this is impractical without significant work. Comint is nice, but the initial version of the acl emacs-lisp interface was coded in 1987 and would be hard to glue together the subsequent decade of independent developments. I don't think anyone is prepared to do a full rewrite right now... |