(in-package :ccl) #| ###################################################################### Miscellaneous FRED hacks Copyright © 1994-97 Michael Travers Permission is given to use and modify this code as long as the copyright notice is preserved. Send questions, comments, and fixes to mt@media.mit.edu. ------------------------------------------------------------------------- Some of this material is copied or modified from examples;assorted-fred-commands.lisp. Contents: - Key commands for font style changes - Improved M-( - window changing commands (C-X O C-X N, C-X P) - fill screen with window (C-X 1) - eliminate ellipses in displayed pathnames - auto-fill and fill-paragraph (improved from apple-supplied versions) - interface to compare - date insertion commands (C-X T) - count lines command (C-X =) See also: kbd-macros.lisp History: 5/2/95 00:14: Spiffed up for release, redid date/time insertion commands 5/29/95 11:45: Fixed font commands for CCL3 7/2/95 16:10: ed-font-change-plain will also set color to black. 7/2/95 20:31 fix c-x c-i to use last returned value if necessary 7/17/95 14:55 make one-window work again 8/22/95 23:31 for CCL2 -- no :color option in font-change-plain 6/3/96 19:54: added count-lines on C-X = 7/5/97 19:54: fix c-x n, c-x p, prepare for release ###################################################################### |# ;;; font style commands (defmacro make-font-command (char attribute &optional (symbol (intern (concatenate 'string "ED-FONT-CHANGE-" (string attribute))))) `(progn (defmethod ,symbol ((v fred-mixin)) (#-CCL-3 set-view-font #+CCL-3 ed-set-view-font v ,attribute)) (comtab-set-key *comtab* ',char ',symbol))) ;;; These must be accessed with actual control key, not Command (make-font-command (:control :meta :shift #\B) :bold) (make-font-command (:control :meta :shift #\I) :italic) (make-font-command (:control :meta :shift #\P) `(:plain #+CCL-3 (:color ,*black-color*)) ed-font-change-plain) ;;; M-( command and friends ;;; The built in M-) bites bigtime, this is somewhat more reasonable. (defmethod ed-move-over-or-insert-close ((w fred-mixin)) (let* ((buf (fred-buffer w)) (pos (buffer-position buf))) (let ((kill (ed-kill-selection w))) (if (and (not kill) (not (eq pos (buffer-size buf))) (eq (buffer-char buf) #\))) (ed-forward-char w) (ed-insert-char w #\)))))) ;;; Ditto for this (but the version in MCL3 is better) #-CCL-3 (defmethod ed-insert-parens ((w fred-mixin) &aux (buf (fred-buffer w))) (multiple-value-bind (start end) (selection-range w) (if (= start end) (progn (ed-insert-with-undo w "()") (move-mark buf -1)) (ed-delimit-selection w "(" ")")))) ;(comtab-set-key *comtab* #\[ 'ed-insert-parens) ;(comtab-set-key *comtab* #\] 'ed-move-over-close-and-reindent) ;(comtab-set-key *comtab* #\( 'ed-insert-[) ;(comtab-set-key *comtab* #\) 'ed-insert-]) (comtab-set-key *comtab* #\( 'ed-insert-parens) ;(comtab-set-key *comtab* #\) 'ed-move-over-close-and-reindent) (comtab-set-key *comtab* #\) 'ed-move-over-or-insert-close) ;;; Window changing commands ;;; From assorted-fred-commands, modified to restrict operations to fred-windows. ; C-X O moves to the next window that is not a listener. ; C-X N moves the top window to the bottom. ; C-X P moves the bottom window to the top. (defmethod ed-other-window ((w fred-mixin)) (setq w (view-window w)) (if *modal-dialog-on-top* (ed-beep) (let ((other-window #'(lambda (window) (unless (or (eq w window) (typep window 'listener) (not (typep window 'fred-window))) (window-select window) (return-from ed-other-window window))))) (declare (dynamic-extent other-window)) (map-windows other-window)))) (defmethod ed-next-window ((w fred-mixin)) (set-window-layer (view-window w) 10000) (window-select (car (windows :class 'fred-window)))) (defmethod ed-previous-window ((w fred-mixin)) (let ((last (car (last (windows :class 'fred-window))))) (when last (window-select last)))) (comtab-set-key *control-x-comtab* '(#\o) 'ed-other-window) (comtab-set-key *control-x-comtab* '(#\n) 'ed-next-window) (comtab-set-key *control-x-comtab* '(#\p) 'ed-previous-window) ;;; Miscellaneous commands ; C-M-W toggles line wrapping. (defun ed-toggle-wrap-p (w) (setf (fred-wrap-p w) (not (fred-wrap-p w)))) (comtab-set-key *comtab* '(:control :meta #\w) 'ed-toggle-wrap-p) (defconstant *wsp&cr* #.(let ((str (make-string 7))) (set-schar str 0 #\Space) (set-schar str 1 #\^I) (set-schar str 2 #\^L) (set-schar str 3 #\^@) (set-schar str 4 #\^J) (set-schar str 5 (%code-char #xCA)) (set-schar str 6 #\newline) str)) (defmethod paragraph-bounds ((w fred-mixin)) (multiple-value-bind (b e) (selection-range w) (when (eq b e) (let* ((buf (fred-buffer w))) (setq b (buffer-backward-find-char buf #\newline)) (if b (loop (if (<= b 0) (return)) (let ((b2 (buffer-backward-find-char buf #\newline b 0))) (unless b2 (return (setq b 0))) (unless (buffer-forward-find-not-char buf *wsp&cr* b2 b) (return (incf b))) (setq b b2))) (setq b 0)) (setq e (buffer-forward-find-char buf #\newline)) (if e (loop (if (>= e (buffer-size buf)) (return)) (let ((e2 (buffer-forward-find-char buf #\newline e))) (unless e2 (return (setq e (buffer-size buf)))) (unless (buffer-forward-find-not-char buf *wsp&cr* e e2) (return (decf e))) (setq e e2))) (setq e (buffer-size buf))))) (values b e))) (defmethod ed-fill-paragraph ((w fred-mixin)) (multiple-value-bind (b e) (paragraph-bounds w) (unless (eq b e) (let* ((buf (fred-buffer w)) (margin (or (view-get w 'fill-margin) (- (point-h (view-size w)) 72))) (prefix (view-get w 'fill-prefix)) (bmark (make-mark buf b t)) (emark (make-mark buf e)) (string (buffer-substring buf b e)) (style (buffer-get-style buf b e)) p last-word-end wsp-end done?) (unwind-protect (progn (setq e (make-mark buf e)) (setq p b) (loop (setq p (buffer-forward-find-char buf *wsp&cr* p e)) (if p (progn (setq wsp-end (buffer-forward-find-not-char buf *wsp&cr* p e) p (1- p) wsp-end (if wsp-end (1- wsp-end) e)) (buffer-delete buf p wsp-end) (buffer-insert buf " " p)) (setq p (buffer-position e) wsp-end p done? t)) (if (> (buffer-string-width buf b p) margin) (progn (unless last-word-end (if done? (return)) (setq last-word-end p)) (buffer-delete buf last-word-end (1+ last-word-end)) (buffer-insert buf #\newline last-word-end) (setq b (1+ last-word-end) p b) (if (>= p (buffer-position e)) (return)) (when prefix (buffer-insert-with-style buf (car prefix) (cdr prefix) b) (incf p (length (car prefix)))) (setq last-word-end nil)) (progn (setq last-word-end p) (incf p))) (if done? (return)))) (setup-undo w #'(lambda () (buffer-delete bmark (buffer-position bmark) (buffer-position emark)) (buffer-insert-with-style bmark string style) (fred-update w)))))))) (comtab-set-key *comtab* '(:meta #\q) 'ed-fill-paragraph) ;;; A better auto fill than apple provides (but adapted from it). ;;; Deals with variable width fonts. ;;; Missing (because I don't care much): fill prefix, filling to other than window size (defun auto-fill-or-insert (w) (let* ((buf (fred-buffer w)) (margin (or (view-get w 'fill-margin) (point-h (view-size w)))) (beg (or (ccl::buffer-backward-find-char buf #\Newline) 0)) (here (buffer-position buf))) (when (> (ccl::buffer-string-width buf beg here) margin) (do ((break-pos here (buffer-char-pos buf ccl::*wsp&cr* :start 0 :end break-pos :from-end t))) ((or (null break-pos) (<= (ccl::buffer-string-width buf beg break-pos) margin)) (when break-pos (buffer-char-replace buf #\return break-pos) (set-fred-hscroll w 0) ; (ed-indent-for-lisp w (1+ break-pos)) )))) (if (eql *current-character* #\space) (interactive-arglist w) (ed-self-insert w)))) ; (comtab-set-key *comtab* #\Space 'auto-fill-or-insert) ;;; keyboard macros have their own file now. (defmethod one-window ((v fred-mixin)) (let* ((w (view-window v)) (zoomed? (view-get w :zoomed))) (if zoomed? (progn (set-view-size w (car zoomed?)) (set-view-position w (cdr zoomed?)) (view-put w :zoomed nil)) (progn (view-put w :zoomed (cons (view-size w) (view-position w))) (window-zoom-event-handler w 8))))) (comtab-set-key *control-x-comtab* #\1 'one-window "Fill the screen with this window") ;;; Don't use annoying ellipses that elide the disk name ;;; from ccl:l1;l1-edwin (defun pathname-to-window-title (pathname) (when pathname ; ?? (let* ((name (file-namestring pathname)) (directory (directory-namestring pathname)) (host (pathname-host pathname)) ;(name-len (length name)) ;(dir-len (length directory)) ;(total-len (+ name-len dir-len)) ) (when (and host (neq host :unspecific)) (setq directory (%str-cat host ":" directory))) (%str-cat name " {" directory "}")))) ;;; do truename in compare, so that aliased files will work right. (defun pathname-to-window (pathname &aux wpath) (setq pathname (or (probe-file pathname) (probe-file (merge-pathnames pathname *.lisp-pathname*)) ;Default type to :unspecific, since that's what ;a window-filename would have. (let ((fpath (full-pathname pathname))) (if (null fpath) (file-namestring pathname) (merge-pathnames fpath #1P""))))) (dolist (w1 (windows :include-invisibles t)) (if (and (setq wpath (window-filename w1)) (or (equalp pathname wpath) (equalp pathname (ignore-errors (truename (full-pathname wpath)))))) ; mt -- added truename (return w1)))) ;;; from fred-additions.lisp ;;; if no sexp is available, use last returned value #+CCL-3 (defmethod ed-info-current-sexp ((w fred-mixin)) (multiple-value-bind (b e) (ed-current-sexp-bounds w) (if (eq b e) (inspect (symbol-value-in-process '* (window-process (view-window w)))) (inspect (ed-current-sexp w e))))) ;;; Interface to compare (comtab-set-key *control-x-comtab* #\D #'(lambda (w) (require :compare) (eval-enqueue `(compare-buffer-to-file ,w))) "Compare top *fred-window* to selected file") (comtab-set-key *control-x-comtab* #\d #'(lambda (w) (require :compare) (eval-enqueue `(compare-buffer-to-other-buffer ,w))) "Compare top *fred-window* to selected other buffer") ;;; based on AlanR code (defmethod ed-insert-date/time ((f fred-mixin)) (insert-string f (mt:date-time-string (get-universal-time)))) (defmethod ed-insert-short-date/time ((f fred-mixin)) (insert-string f (mt:short-date-time-string (get-universal-time)))) (defmethod insert-string ((f fred-mixin) string) (let* ((buf (fred-buffer f)) (start (buffer-position buf)) end) (buffer-insert buf string) (setf end (buffer-position buf)) (when (fred-prefix-argument f) (buffer-set-font-spec buf :bold start end) (buffer-set-font-spec buf :plain)))) (comtab-set-key *control-x-comtab* (keystroke-code '(:control #\t)) 'ed-insert-date/time) (comtab-set-key *control-x-comtab* (keystroke-code #\t) 'ed-insert-short-date/time) ; +++ misplaced: nothing to do with FRED #-CCL-3 (defun report-time (form thunk) (let* ((initial-real-time (get-internal-real-time)) (initial-run-time (get-internal-run-time)) (initial-gc-time (gctime)) (initial-consed (total-bytes-allocated))) (multiple-value-prog1 (funcall thunk) (let* ((s *trace-output*) (bytes-consed (- (total-bytes-allocated) initial-consed)) (elapsed-real-time (- (get-internal-real-time) initial-real-time)) (elapsed-run-time (- (get-internal-run-time) initial-run-time)) (elapsed-gc-time (- (gctime) initial-gc-time)) (elapsed-mf-time (- elapsed-real-time elapsed-run-time))) (format s "~&~S took ~D milliseconds (~,3F seconds) to run." form elapsed-real-time (/ elapsed-real-time internal-time-units-per-second)) (format s "~& (~D msec actual runtime)" elapsed-run-time) (when (> elapsed-mf-time 0) (format s "~%Of that, ~D milliseconds (~,3F seconds) were spent in The Cooperative Multitasking Experience." elapsed-mf-time (/ elapsed-mf-time internal-time-units-per-second))) (unless (eql elapsed-gc-time 0) (format s "~%") (unless (> elapsed-mf-time 0) (format *trace-output* "Of that, ")) (format s "~D milliseconds (~,3F seconds) was spent in GC." elapsed-gc-time (/ elapsed-gc-time internal-time-units-per-second))) (unless (eql 0 bytes-consed) (format s "~% ~D bytes of memory allocated." bytes-consed)) (format s "~&"))))) (defmethod ed-count-lines ((f fred-mixin)) (clear-mini-buffer-maybe f) (set-mini-buffer f "~D lines" (lines-in-buffer (fred-buffer f)))) (comtab-set-key *control-x-comtab* (keystroke-code #\=) 'ed-count-lines)