From: Erik Naggum

Subject: Re: xemacs lisp listener buffer max size

Date: 1997-10-7 18:27

| 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.)

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.  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.

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)
	  (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.

| 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 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.

| 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.

#\Erik