Mercurial > hg > xemacs-beta
diff lisp/ilisp/ilisp-out.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | b82b59fe008d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/ilisp/ilisp-out.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,518 @@ +;;; -*- Mode: Emacs-Lisp -*- + +;;; ilisp-out.el -- + +;;; This file is part of ILISP. +;;; Version: 5.7 +;;; +;;; Copyright (C) 1990, 1991, 1992, 1993 Chris McConnell +;;; 1993, 1994 Ivan Vasquez +;;; 1994, 1995 Marco Antoniotti and Rick Busdiecker +;;; +;;; Other authors' names for which this Copyright notice also holds +;;; may appear later in this file. +;;; +;;; Send mail to 'ilisp-request@lehman.com' to be included in the +;;; ILISP mailing list. 'ilisp@lehman.com' is the general ILISP +;;; mailing list were bugs and improvements are discussed. +;;; +;;; ILISP is freely redistributable under the terms found in the file +;;; COPYING. + + + +;;; +;;; ILISP output, including a popper replacement. +;;; + +(defvar ilisp-output-buffer " *Output*") +(defvar ilisp-output-buffer-major-mode 'lisp-mode + "*The major mode for the ilisp typeout window.") +(defvar ilisp-output-min-height 2 + "*The minimum height of the typeout window used to display ilisp output.") +(defvar ilisp-output-max-height 25 + "*The maximum height of the typeout window used to display ilisp output.") +(defvar ilisp-display-output-function 'ilisp-display-output-default + "The name of a function to display all ilisp output. The function gets a + single argument, a string.") + + +;; Minor mode (just to get a pretty mode line). +(defvar ilisp-output-mode-line nil) +(defvar ilisp-output-mode nil "If T, then we are in the ilisp-output minor mode.") +(make-variable-buffer-local 'ilisp-output-mode) + +(or (assq 'ilisp-output-mode minor-mode-alist) + (setq minor-mode-alist + (cons '(ilisp-output-mode ilisp-output-mode-line) minor-mode-alist))) + + +(defun ilisp-output-buffer (&optional create-p) + (let ((buffer (if create-p + (get-buffer-create ilisp-output-buffer) + (get-buffer ilisp-output-buffer)))) + (or ilisp-output-mode-line + (setq ilisp-output-mode-line + (list (format + " %s bury, %s scroll" + (ilisp-where-is 'ilisp-bury-output) + (ilisp-where-is 'ilisp-scroll-output))))) + buffer)) + +(defun ilisp-output-window () + (let ((buffer (get-buffer ilisp-output-buffer))) + (if buffer + (get-buffer-window buffer)))) + + +(defun lisp-display-output (output) + "Display OUTPUT in the appropriate place. + This calls the function given by the value of ilisp-display-output-function in + order to do the real work." + (cond ((null output)) + (t + ;; Bugcheck + (if (not (stringp output)) + (error "bug: not a string in lisp-display-output")) + + (if (ilisp-value 'comint-errorp t) + (setq output (funcall (ilisp-value 'ilisp-error-filter) + output))) + (funcall ilisp-display-output-function output)))) + + + +;;; Popper replacement + + +(defun ilisp-bury-output () + "Delete the typeout window, if any" + (interactive) + (let* ((buffer (ilisp-output-buffer)) + (window (and buffer (get-buffer-window buffer)))) + (if buffer + (bury-buffer buffer)) + (if window + (ilisp-delete-window window)))) + + +(defun ilisp-show-output (&optional buffer) + "Make typeout visible, if it is not already." + (interactive) + (let ((buffer (or buffer (ilisp-output-buffer)))) + (if buffer + (ilisp-display-buffer-in-typeout-window buffer)))) + + +(defun ilisp-delete-window (window) + "Delete a window with minimal redisplay." + (let ((height (window-height window)) + (lower-window (ilisp-find-lower-window window))) + (delete-window window) + (if (and lower-window + (not (eq lower-window window))) + (let ((old-window (selected-window))) + (save-excursion + (select-window lower-window) + (set-buffer (window-buffer)) + (goto-char (window-start)) + (vertical-motion (- height)) + (set-window-start lower-window (point))) + (select-window old-window))))) + + +(defun ilisp-scroll-output (&optional lines) + "Scroll the typeout-window, if any." + (interactive "P") + (let* ((buffer (ilisp-output-buffer)) + (window (and buffer (get-buffer-window buffer))) + (old-window (selected-window))) + (if window + (unwind-protect + (progn + (select-window window) + (set-buffer buffer) + (scroll-up lines)) + (select-window old-window))))) + + +(defun ilisp-grow-output (&optional n) + "Grow the typeout window by ARG (default 1) lines." + (interactive "p") + (let* ((buffer (ilisp-output-buffer)) + (window (and buffer (get-buffer-window buffer))) + (old-window (selected-window))) + (if window + (unwind-protect + (progn + (select-window window) + (enlarge-window n)) + (if (ilisp-window-live-p old-window) + (select-window old-window)))))) + + +(defun ilisp-trim-blank-lines () + ;; Delete leading blank lines + (goto-char (point-min)) + (if (looking-at "\n+") + (replace-match "")) + ;; Delete trailing blank lines + (goto-char (point-max)) + (skip-chars-backward "\n") + (if (< (point) (point-max)) + (delete-region (1+ (point)) (point-max)))) + + +(defun ilisp-write-string-to-buffer (buffer string) + (save-excursion + (set-buffer buffer) + ;; Maybe an option to keep the old output? + (erase-buffer) + ;; New: select mode for the output buffer. + (if (not (eq major-mode ilisp-output-buffer-major-mode)) + (funcall ilisp-output-buffer-major-mode)) + (setq ilisp-output-mode t) + (princ string buffer) + (ilisp-trim-blank-lines) + (goto-char (point-min)))) + + +(defun ilisp-desired-height (buffer-or-window) + (let ((height + (cond ((bufferp buffer-or-window) + (ilisp-needed-buffer-height buffer-or-window)) + ((windowp buffer-or-window) + (ilisp-needed-window-height buffer-or-window))))) + (max window-min-height + (min ilisp-output-max-height + (max ilisp-output-min-height + height))))) + + +;; A first guess at the height needed to display this buffer. +(defun ilisp-needed-buffer-height (buffer) + (save-excursion + (set-buffer buffer) + (1+ (count-lines (point-min) (point-max))))) + + +;; The height this window must be to display its entire buffer. +(defun ilisp-needed-window-height (window) + (save-window-excursion + (select-window window) + (save-excursion + (set-buffer (window-buffer)) + (+ 2 (save-excursion + (goto-char (point-min)) + ;; Any upper bound on the height of an emacs window will + ;; do here. How about 1000. + (vertical-motion 1000)))))) + + +(defun ilisp-shrink-wrap-window (window) + (let ((previously-selected-window (selected-window)) + (buffer (window-buffer window))) + + (select-window window) + (let* ((current-height (window-height window)) + (desired-height (ilisp-desired-height window)) + (delta (- desired-height current-height))) + (enlarge-window delta) + (set-buffer buffer) + (goto-char (point-min)) + + ;; Now repair damage to the window below us, if it still exists. + (let ((lower-window (ilisp-find-lower-window window))) + (if lower-window + (progn + (select-window lower-window) + (let ((old-point (point))) + (goto-char (window-start)) + (vertical-motion delta) + (set-window-start lower-window (point)) + (goto-char old-point) + (if (not (pos-visible-in-window-p old-point)) + (recenter 0)))))) + ;; If there was no lower window, then we ought to preserve + ;; the start of the window above us, if any. + + (if (ilisp-window-live-p previously-selected-window) + (select-window previously-selected-window))))) + + + +(defun ilisp-window-live-p (window) + (let* ((initial-window (selected-window)) + (win initial-window) + (found nil)) + (while win + (cond ((eq window win) + (setq found t + win nil)) + (t + (setq win (next-window win 'no)) + (if (eq win initial-window) + (setq win nil))))) + found)) + +;; XEmacs change -- window-edges is gone in 19.12+ so use +;; next-vertical-window instead. +(defun ilisp-find-lower-window (window) + "Find the window directly below us, if any. This is probably the + window from which enlarge-window would steal lines." + (if (< emacs-minor-version 12) + (let* ((bottom (nth 3 (window-edges window))) + (window* nil) + (win window)) + (while (not (eq (setq win (next-window win 'no)) + window)) + (if (and (= (nth 1 (window-edges win)) + bottom) + (null window*)) + (setq window* win))) + window*) + (next-vertical-window window))) + +;; XEmacs change -- There is now a primitive to do this. +(defun ilisp-find-top-left-most-window () + "Return the leftmost topmost window on the current screen." + (if (< emacs-minor-version 12) + (let* ((window* (selected-window)) + (edges* (window-edges window*)) + (win nil) + (edges nil) + (start-window window*)) + (while (not (eq (setq win (next-window win 'no)) + start-window)) + (setq edges (window-edges win)) + (if (or (< (car (cdr edges)) (car (cdr edges*))) ; top + (and (= (car (cdr edges)) (car (cdr edges*))) + (< (car edges) (car edges*)))) ; left + (setq window* win + edges* edges))) + window*) + (frame-highest-window (selected-frame) 0))) + + +;; This causes the typeout window to be created by splitting or using the +;; top-left-most window on the current screen. That is different behavior +;; from the popper, which always split the current window. +(defun ilisp-window-to-use-for-typeout () + (ilisp-find-top-left-most-window)) + + +(defun ilisp-display-buffer-in-typeout-window (buffer) + "Display buffer in a window at the top of the screen." + (let ((window (get-buffer-window buffer))) + + ;; If buffer already has a window, keep it. + (if (null window) + ;; Otherwise, find a window to split. + (let* ((top-window (ilisp-window-to-use-for-typeout)) + (new-window nil) + (previously-selected-window (selected-window)) + (desired-height (ilisp-desired-height buffer))) + + ;; The new window is always the lower one. + (select-window top-window) + + ;; Always minimize redisplay (except in emacs 18). + (let ((split-window-keep-point nil)) + ;; If the top window is not big enough to split, commandeer it + ;; entirely. + (cond ((> desired-height (- (window-height) window-min-height)) + (setq new-window top-window)) + (t + (setq new-window (split-window-vertically desired-height))))) + + (set-window-buffer top-window buffer) + ;; The height is already correct, unless there was line wrapping. + ;; Account for that here. + (ilisp-shrink-wrap-window top-window) + + ;; Restore selected window. + (if (eq previously-selected-window top-window) + (select-window new-window) + (select-window previously-selected-window))) + + ;; Simply shrink-wrap an existing window. + (ilisp-shrink-wrap-window window)))) + + + + + + + + +;;; Various functions to which to bind ilisp-display-output-function. + +;; This function does what ilisp used to do, except that we use the +;; new "popper". + +(defun ilisp-display-output-default (output) + "Dispatch on the value of lisp-no-popper: + lisp-no-popper = nil: display output in a typeout window. + lisp-no-popper = t: display output in the ilisp buffer + otherwise: display one-line output in the echo area, multiline output in the ilisp buffer." + (cond ((null lisp-no-popper) + (ilisp-display-output-in-typeout-window output)) + ((eq lisp-no-popper t) + (ilisp-display-output-in-lisp-listener output)) + (t + (ilisp-display-output-adaptively output)))) + + +;; This is the display function I like to use. + +;; Another trick which might be useful is to dispatch on the value +;; this-command here, to make output from different ilisp commands +;; go to different places. + +(defun ilisp-display-output-adaptively (output) + "Display one-liners in the echo area, others in the typeout window" + (cond ((or (string-match "\n" output) + (> (length output) (window-width (minibuffer-window)))) + (message "See above.") + (ilisp-display-output-in-typeout-window output)) + (t + (ilisp-display-output-in-echo-area output)))) + + +(defun ilisp-display-output-in-typeout-window (output) + "Display output in a shrink-wrapped window at the top of the screen." + (let ((buffer (ilisp-output-buffer t))) + (ilisp-write-string-to-buffer buffer output) + (ilisp-display-buffer-in-typeout-window buffer))) + + +(defun ilisp-display-output-in-echo-area (output) + "Display output as a message in the echo area." + ;; First clear any existing typeout so as to not confuse the user. + (or (eq (selected-window) (ilisp-output-window)) + (ilisp-bury-output)) + ;; Patch suggested by hunter@work.nlm.nih.gov (Larry Hunter) + ;; If output contains '%', 'message' loses. + ;; (message (ilisp-quote-%s output)) + ;; An alternative here could be '(princ output)', as suggested by + ;; Christopher Hoover <ch@lks.csi.com> + (princ output) + ) + + +;;; ilisp-quote-%s -- +;;; Patch suggested by hunter@work.nlm.nih.gov (Larry Hunter) + +(defun ilisp-quote-%s (string) + "Quote all the occurences of ?% in STRING in an ELisp fashion." + (mapconcat '(lambda (char) + (if (char-equal char ?%) + "%%" + (char-to-string char))) + string "")) + + +(defun ilisp-display-output-in-temp-buffer (output) + (with-output-to-temp-buffer ilisp-output-buffer + (princ output))) + + +(defun ilisp-display-output-in-lisp-listener (output) + "Display output in the ilisp buffer" + (let ((buffer (current-buffer)) + (window (selected-window))) + (unwind-protect + (progn + (lisp-pop-to-buffer (ilisp-buffer)) + (if (not (eq (current-buffer) buffer)) + (setq ilisp-last-buffer buffer)) + (comint-insert + (concat + (if ilisp-last-message + (concat ";;; " ilisp-last-message "\n")) + (comint-remove-whitespace output) + "\n" + ilisp-last-prompt)) + (setq ilisp-last-message nil)) + (if (window-point window) + (progn (select-window window) + (set-buffer buffer)))))) + + + +;;; Changed according to suggestions by Robert P. Goldman +(defun lisp-pop-to-buffer (buffer) + "Like pop-to-buffer, but select a screen that buffer was shown in." + (let ((ilisp-window (if ilisp-epoch-running + (epoch::get-buffer-window buffer) + (get-buffer-window buffer)))) + (if ilisp-window + (select-window ilisp-window) + ;; It is not currently displayed, so find some place to display + ;; it. + (progn + (cond (ilisp-epoch-running + ;; Select a screen that the buffer has been displayed in before + ;; or the current screen otherwise. + (epoch::select-screen + ;; allowed-screens in epoch 3.2, was called screens before that + (or (car (save-excursion + (set-buffer buffer) + (symbol-value 'allowed-screens))) + (epoch::current-screen)))) + + ;; Next clauses patterned after a suggestion by R. P. Goldman. + ((eq +ilisp-emacs-version-id+ 'fsf-19) + (let* ((window (get-buffer-window buffer t)) + (frame (if window (window-frame window)))) + (if (eq 'x (framep frame)) + (progn + (raise-frame frame) + (select-frame frame))))) + (t nil)) ; fsf-18, but also lucid and + ; xemacs. + ; I do not know how to make + ; them work + ; Marco Antoniotti, Jan 4th 1995 + (ilisp-bury-output) + (pop-to-buffer buffer)))) + (set-buffer buffer)) + +;(defun lisp-pop-to-buffer (buffer) +; "Like pop-to-buffer, but select a screen that buffer was shown in. +; Also, first bury any typeout-window." +; (let ((ilisp-window (if ilisp-epoch-running +; (epoch::get-buffer-window buffer) +; (get-buffer-window buffer)))) +; (if ilisp-window +; (select-window ilisp-window) +; ;; It is not currently displayed, so find some place to display it. +; (if ilisp-epoch-running +; ;; Select a screen that the buffer has been displayed in before +; ;; or the current screen otherwise. +; (epoch::select-screen +; ;; allowed-screens in epoch 3.2, was called screens before that +; (or (car (save-excursion +; (set-buffer buffer) +; (symbol-value 'allowed-screens))) +; (epoch::current-screen)))) +; ;; Do not pop to the output buffer. +; (ilisp-bury-output) +; (pop-to-buffer buffer))) +; (set-buffer buffer)) + + +;;; +(defun switch-to-lisp (eob-p &optional ilisp-only) + "If in an ILISP buffer, switch to the buffer that last switched to +an ILISP otherwise, switch to the current ILISP buffer. With +argument, positions cursor at end of buffer. If you don't want to +split windows, set pop-up-windows to NIL." + (interactive "P") + (if (and (not ilisp-only) ilisp-last-buffer + (memq major-mode ilisp-modes)) + (lisp-pop-to-buffer ilisp-last-buffer) + (if (not (memq major-mode ilisp-modes)) + (setq ilisp-last-buffer (current-buffer))) + (lisp-pop-to-buffer (ilisp-buffer)) + (cond (eob-p (goto-char (point-max))))))