diff lisp/energize/energize-windows.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/energize/energize-windows.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,404 @@
+;;; -*- Mode:Emacs-Lisp -*-
+;;; Copyright © 1992 by Lucid, Inc.  All Rights Reserved.
+
+;;; Displaying buffers.  Why is this so hard?
+
+
+;;; This crud is damage control, because sometimes things get confused, and
+;;; the server asks us to display a buffer that has been killed.  
+
+(defun energize-request-kill-buffer-if-dead (buffer)
+  (cond ((not (bufferp buffer)) t)
+        ((null (buffer-name buffer))
+         (if (energize-buffer-p buffer)
+             (energize-request-kill-buffer buffer))
+         t)
+        (t nil)))
+
+(defun energize-prune-killed-buffers-from-list (buffer-extent-list)
+  (let ((rest buffer-extent-list)
+        (buffer-count 0)
+        (deleted-count 0))
+    (while rest
+      (let* ((buffer (car rest))
+             (extent (car (cdr rest))))
+        (setq rest (cdr (cdr rest)))
+        (setq buffer-count (1+ buffer-count))
+        (if (energize-request-kill-buffer-if-dead buffer)
+            (progn
+              (setq deleted-count (1+ deleted-count))
+              (setq buffer-extent-list (delq buffer buffer-extent-list))
+              (setq buffer-extent-list (delq extent buffer-extent-list))))))
+    (if (> deleted-count 0)
+        (progn
+          (message 
+            (format "Oops, confused about %s selected %s -- please try again."
+                    (if (> deleted-count 1) 
+                        (format "%d of the" deleted-count)
+                        (if (> buffer-count 1)
+                            "one of the"
+                            "the"))
+                    (if (> buffer-count 1)
+                        "buffers"
+                        "buffer")))
+          (ding t)))
+    buffer-extent-list))
+
+
+(defvar energize-auto-scroll-p t	;#### this should be nil, t is LOSING
+  "*If t, energize will scroll your debugger and error log buffers
+to the bottom whenever output appears with reckless abandon.  If nil,
+it will behave just like normal shell and gdb-mode buffers.")
+
+(defvar energize-error-log-context-lines 0
+  "*Number of lines to skip above the current error in the Energize error log")
+
+;;; called by energize-show-all-buffers
+;;; If the extent is specified:
+;;;   - scrolls the window so that point is at at the beginning of the extent.
+;;;   - If the buffer is "Error Log", the extent is moved to top-of-window.
+;;;   - if `only-one' and the buffer is a source buffer, then... what?
+;;; If the buffer is "*Debugger*" or "Error Log", point is moved to eof,
+;;;   IF and ONLY if it was at EOF already.
+;;;
+(defun energize-scroll-window-at-extent (window extent only-one)
+  (let* ((buffer (window-buffer window))
+	 (type (energize-buffer-type buffer)))
+    (if (and extent (null (extent-start-position extent)))
+	;; it has been detached somehow.
+	(setq extent nil))
+    (if extent
+	(let ((pos (extent-start-position extent)))
+	  (if (not (eq pos 0))
+	      (progn
+		(set-window-point window pos)
+		(cond ((eq type 'energize-error-log-buffer)
+		       ;; scroll the Error Log buffer so that the first error
+		       ;; is at the top of the window.
+		       (set-window-start window
+					 (save-excursion
+					   (set-buffer buffer)
+					   (goto-char pos)
+					   (forward-line
+					    (-
+					     energize-error-log-context-lines))
+					   (beginning-of-line)
+					   (point))))
+		      ((and only-one (eq type 'energize-source-buffer))
+		       ;; if only one buffer is requested to be visible and it
+		       ;; is a source buffer then scroll point close to the top
+		       (set-window-start window
+					 (save-excursion
+					   (set-buffer buffer)
+					   (goto-char pos)
+					   (beginning-of-line)
+					   (if (> (window-height window)
+						  next-screen-context-lines)
+					       (vertical-motion
+						(- next-screen-context-lines)
+						window)
+					     (vertical-motion -1 window))
+					   (point)))))))))
+
+    (cond ((and (memq type '(energize-error-log-buffer
+			     energize-debugger-buffer))
+		; don't move point if it's before the last line
+		(or energize-auto-scroll-p
+		    (>= (window-point window)
+			(save-excursion
+			  (set-buffer (window-buffer window))
+			  ;;(comint-mark)
+			  (energize-user-input-buffer-mark)
+			  )))
+		)
+	   ;; Debugger and Error Log buffers generally get scrolled to
+	   ;; the bottom when displayed.
+	   (set-window-point window
+			     (save-excursion (set-buffer buffer)
+					     (+ 1 (buffer-size))))
+	   ;; Careful to deactivate the selection when automatically moving
+	   ;; the user to the end of the buffer.  This is suboptimal, but
+	   ;; otherwise we have bad interactions with the debugger-panel
+	   ;; Print button.  (Double-click on a value (point is now at the
+	   ;; end of that word); hit Print; point is now at point-max, but
+	   ;; the original word is still highlighted, which is incorrect -
+	   ;; we're now in a state where the selection highlighting and the
+	   ;; region between point and mark is out of sync.  I'm not entirely
+	   ;; sure how to fix this short of using a point-motion hook of some
+	   ;; kind, so we'll punt, and just deactivate the region instead.)
+	   (zmacs-deactivate-region)
+	   ))))
+
+
+;;; called by energize-make-buffers-visible
+;;; For each of the contents of an plist of buffers and extents:
+;;;   - If the buffer is visible in a window
+;;;     - dedicate the window
+;;;     - energize-scroll-window-at-extent
+;;; If we dedicated any windows, select the last one dedicated.
+;;; For each of the buffers and extents:
+;;;   - pop-to-buffer
+;;;   - remember the first window selected in this way
+;;;   - dedicate the window
+;;;   - energize-scroll-window-at-extent; only-one arg is true if there
+;;;     is only one buffer/extent pair in the list
+;;;   - if energize-edit-buffer-externally-p make it read-only
+;;; Un-dedicate the windows
+;;; Select the remembered window (the first one we popped-to-buffer on)
+;;; Maybe raise its frame
+;;;
+(defun energize-show-all-buffers (buffer-extent-list)
+  (let ((pop-up-windows t)
+	(dedicated-windows ())
+	(buffer-extent-current)
+	(window-to-select ())
+	(only-one (null (cdr (cdr buffer-extent-list)))))
+    (setq buffer-extent-current buffer-extent-list)
+    (while buffer-extent-current
+      (let* ((buffer (car buffer-extent-current))
+	     (extent (car (cdr buffer-extent-current)))
+	     (window (get-buffer-window buffer (selected-frame))))
+	(if window
+	    (progn
+	      (set-window-buffer-dedicated window buffer)
+	      (setq dedicated-windows (cons window dedicated-windows))
+	      (energize-scroll-window-at-extent window extent only-one)))
+	(setq buffer-extent-current (cdr (cdr buffer-extent-current)))))
+    (if dedicated-windows
+	(select-window (car dedicated-windows)))
+    (setq buffer-extent-current buffer-extent-list)
+    (while buffer-extent-current
+      (let* ((buffer (car buffer-extent-current))
+	     (extent (car (cdr buffer-extent-current))))
+;; ## what was this intended to do? a frame is being passed as the
+;; ## argument which means "always select a different window even if
+;; ## it's visible in the selected window.
+;;	(pop-to-buffer buffer nil (selected-frame))
+	(pop-to-buffer buffer)
+	(if (energize-edit-buffer-externally-p buffer)
+	    (setq buffer-read-only t))
+	(let ((window (selected-window)))
+	  (if (null window-to-select)
+	      (setq window-to-select window))
+	  (set-window-buffer-dedicated window buffer)
+	  (setq dedicated-windows (cons window dedicated-windows))
+	  (energize-scroll-window-at-extent window extent only-one))
+	(setq buffer-extent-current (cdr (cdr buffer-extent-current)))))
+    (while dedicated-windows
+      (set-window-buffer-dedicated (car dedicated-windows) ())
+      (setq dedicated-windows (cdr dedicated-windows)))
+
+    (select-window window-to-select)
+    ;; now we may have to pop the frame up
+    (let ((frame (selected-frame)))
+      (if (and energize-auto-raise-screen
+	       (or (not (frame-visible-p frame))
+		   (not (frame-totally-visible-p frame))))
+	  (progn
+	    (sit-for 0)
+	    (make-frame-visible frame))))))
+
+;;; called by energize-make-buffers-visible
+(defun energize-main-buffer-of-list (list)
+  ;; Given an alternating list of buffers and extents, pick out the
+  ;; "interesting" buffer.  If one of the buffers is in debugger-mode,
+  ;; or in breakpoint-mode, then that's the interesting one; otherwise,
+  ;; the last buffer in the list is the interesting one.
+  (let (buffer mode result)
+    (while list
+      (setq buffer (car list))
+      (or (memq mode '(energize-debugger-mode energize-breakpoint-mode))
+	  (setq result buffer
+		mode (save-excursion (set-buffer buffer) major-mode)))
+      (setq list (cdr (cdr list))))
+    result))
+
+;;; called by energize-make-many-buffers-visible-function
+;;; If there is only one buffer/extent pair, and it's a source buffer, then
+;;;  edit it in vi if that's the kind of kinkiness we're into.
+;;; Get the "main" buffer, and select a frame for it.
+;;; Then call energize-show-all-buffers.
+;;;
+(defun energize-make-buffers-visible (buffer-extent-list)
+  (let ((main-buffer (energize-main-buffer-of-list buffer-extent-list))
+	window)
+    (if (and (null (cdr (cdr buffer-extent-list)))
+	     (energize-edit-buffer-externally-p main-buffer))
+	(energize-edit-buffer-externally-1 main-buffer
+					   (car (cdr buffer-extent-list)))
+      ;; This may create and/or select a frame as a side-effect.
+      ;; I'm not sure it's necessary to call this, as display-buffer
+      ;; calls it too.  But it can't hurt to select the appropriate
+      ;; frame early...
+      (let ((hacked-frame nil))
+	(cond ((null energize-split-screens-p)
+	       nil)
+	      ((get-frame-name-for-buffer main-buffer)
+	       (setq hacked-frame t)
+	       (if pre-display-buffer-function
+		   (funcall pre-display-buffer-function main-buffer nil nil))
+	       )
+	      ((setq window (get-buffer-window main-buffer t))
+	       (cond (window
+		      (setq hacked-frame t)
+		      (select-frame (window-frame window))))))
+	(let ((pre-display-buffer-function
+	       (if hacked-frame nil pre-display-buffer-function)))
+	  (energize-show-all-buffers buffer-extent-list))
+;;	;; kludge!!  Select the debugger frame, not the sources frame.
+;;	(if (and (null energize-split-screens-p)
+;;		 pre-display-buffer-function)
+;;	    (funcall pre-display-buffer-function main-buffer nil nil))
+	))))
+
+;;; this is the guts of energize-make-many-buffers-visible
+;;; `arg' is really two args: `buffer-extent-list' and `go-there'.
+;;; go-there is specified by 
+;;; Given a list of buffer/extent pairs, make them all visible at once
+;;;  (presumably in the same frame?)
+;;; If `go-there'
+;;;  - call energize-make-buffers-visible
+;;; else
+;;;  - dedicate the selected window
+;;;  - call energize-make-buffers-visible
+;;;  - re-select and undedicate the original selected window
+;;;
+(defun energize-make-many-buffers-visible-function (arg)
+  (let ((buffer-extent-list (car arg))
+	(go-there (cdr arg)))
+    ;; enqueue an history record if we're going to move
+    (if go-there
+	(energize-history-enqueue))
+    (setq buffer-extent-list 
+	  (energize-prune-killed-buffers-from-list buffer-extent-list))
+    (if buffer-extent-list
+	(if go-there
+	    (energize-make-buffers-visible buffer-extent-list)
+	  (let ((window (selected-window)))
+	    (set-window-buffer-dedicated window (window-buffer window))
+	    (unwind-protect 
+		(energize-make-buffers-visible buffer-extent-list)
+	      (set-window-buffer-dedicated window ())
+	      (select-window window)))))))
+
+(defvar energize-make-many-buffers-visible-should-enqueue-event t
+  "Special variable bound by energize-execute-command to allow the
+buffers to be selected while the command is executed")
+
+;;; called by by editorside.c:MakeBufferAndExtentVisible().
+;;; should-enqueue is bound by `energize-execute-command'
+;;;
+(defun energize-make-many-buffers-visible (buffer-extent-list go-there)
+  "First arg is a list of buffers and extents. All those should be
+made visible at the same time.  If the second argument is T then point
+should be moved to the first character of the extent of the first
+buffer, or to the buffer if no extent is specified for this buffer.  
+If second argument is NIL point should not change."
+  (if energize-make-many-buffers-visible-should-enqueue-event
+      ;; don't do it from process filters, but wait until we come back to
+      ;; top-level.  Using go-there should still be done sparingly, as we can
+      ;; surprise the user and grab their keystrokes into another buffer.
+      (enqueue-eval-event 'energize-make-many-buffers-visible-function
+			  (cons buffer-extent-list go-there))
+    ;; go-there is always true when called from energize-execute-command,
+    ;; I guess under the assumption that it's always ok to select a buffer
+    ;; when we're doing something in direct response to a menu selection.
+    (energize-make-many-buffers-visible-function
+     (cons buffer-extent-list t))))
+
+
+;;; This deales with the energize history
+(defvar energize-navigation-history '(nil)
+  "List of places where Energize took you to.
+It is a list of (file-name/buffer-name . position)")
+
+(defvar energize-history-maximum-length 20
+  "Maximum number of locations kept in the energize history")
+
+(defvar energize-navigation-current ()
+  "Current pointer into the energize-navigation-history")
+
+(defvar energize-navigation-current-length 0)
+
+(defun energize-history-enqueue ()
+  "Memorize the current place in the history.
+Trim the history if need be."
+  (let ((new-item
+	 (cons (or buffer-file-truename (current-buffer))
+	       (1+ (count-lines 1 (point))))))
+    (if (not (equal new-item (car energize-navigation-history)))
+	(progn
+	  (setq energize-navigation-history
+		(cons new-item energize-navigation-history))
+	  (setq energize-navigation-current-length
+		(1+ energize-navigation-current-length))
+	  (if (> energize-navigation-current-length
+		 (* 2 energize-history-maximum-length))
+	      (let ((tail (nthcdr energize-history-maximum-length
+				  energize-navigation-history)))
+		(rplacd tail nil)
+		(setq energize-navigation-current-length
+		      energize-history-maximum-length)))))))
+
+(defun energize-history-dequeue ()
+  "Forget the current place in the history"
+  (setq energize-navigation-history (cdr energize-navigation-history)))
+
+(defun energize-history-go-back (item)
+  "Go back to the place memorized by item"
+  (let ((buffer-or-file (car item))
+	(position (cdr item))
+	(buffer ()))
+    (cond ((bufferp buffer-or-file)
+	   (setq buffer buffer-or-file))
+	  ((stringp buffer-or-file)
+	   (setq buffer (or (get-file-buffer buffer-or-file)
+			    (find-file-noselect buffer-or-file)))))
+    (if (null (buffer-name buffer))
+	()
+      (pop-to-buffer buffer)
+      (goto-line position)
+      t)))
+
+(defun energize-history-previous ()
+  "Go back in the history.
+If the last command was the same go back more"
+  (interactive)
+  (if (not (eq last-command 'energize-history-previous))
+      (setq energize-navigation-current energize-navigation-history))
+  (energize-history-enqueue)
+  (while (and (car energize-navigation-current)
+	      (not
+	       (energize-history-go-back (car energize-navigation-current))))
+    (rplaca energize-navigation-current
+	    (car (cdr energize-navigation-current)))
+    (rplacd energize-navigation-current
+	    (cdr (cdr energize-navigation-current))))
+  (if (null (car energize-navigation-current))
+      (progn
+	(energize-history-dequeue)
+	(setq last-command 'beep)
+	(error "You reached the beginning of the Energize history"))
+    (setq energize-navigation-current
+	  (cdr energize-navigation-current))))
+
+(define-key global-map '(shift f14) 'energize-history-previous)
+
+(defun energize-history ()
+  "Show the energize history in the energize history buffer"
+  (interactive)
+  (pop-to-buffer "*Energize History*")
+  (erase-buffer)
+  (mapcar (function (lambda (item)
+		      (if item
+			  (progn
+			    (insert (format "%s" (car item)))
+			    (indent-to-column 32 1)
+			    (insert (format "%s\n" (cdr item)))))))
+	  energize-navigation-history)
+  (goto-char (point-min))
+  (energize-history-mode))
+
+(defun energize-history-mode ()
+  "Turn on energize history mode"
+  )