| 1 | ;;; -*- Log: hemlock.log; Package: Hemlock -*-
|
|---|
| 2 | ;;;
|
|---|
| 3 | ;;; **********************************************************************
|
|---|
| 4 | ;;; This code was written as part of the CMU Common Lisp project at
|
|---|
| 5 | ;;; Carnegie Mellon University, and has been placed in the public domain.
|
|---|
| 6 | ;;;
|
|---|
| 7 | #+CMU (ext:file-comment
|
|---|
| 8 | "$Header$")
|
|---|
| 9 | ;;;
|
|---|
| 10 | ;;; **********************************************************************
|
|---|
| 11 | ;;;
|
|---|
| 12 | ;;; Highlighting paren and some other good stuff.
|
|---|
| 13 | ;;;
|
|---|
| 14 | ;;; Written by Bill Chiles and Jim Healy.
|
|---|
| 15 | ;;;
|
|---|
| 16 |
|
|---|
| 17 | (in-package :hemlock)
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 | |
|---|
| 21 |
|
|---|
| 22 | ;;;; Open parens.
|
|---|
| 23 |
|
|---|
| 24 | (defhvar "Highlight Open Parens"
|
|---|
| 25 | "When non-nil, causes open parens to be displayed in a different font when
|
|---|
| 26 | the cursor is directly to the right of the corresponding close paren."
|
|---|
| 27 | :value nil)
|
|---|
| 28 |
|
|---|
| 29 | (defhvar "Open Paren Finder Function"
|
|---|
| 30 | "Should be a function that takes a mark for input and returns either NIL
|
|---|
| 31 | if the mark is not after a close paren, or two (temporary) marks
|
|---|
| 32 | surrounding the corresponding open paren."
|
|---|
| 33 | :value 'lisp-open-paren-finder-function)
|
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 | (defvar *open-paren-font-marks* nil
|
|---|
| 37 | "The pair of font-marks surrounding the currently highlighted open-
|
|---|
| 38 | paren or nil if there isn't one.")
|
|---|
| 39 |
|
|---|
| 40 | (defvar *open-paren-highlight-font* 2
|
|---|
| 41 | "The index into the font-map for the open paren highlighting font.")
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 | ;;; MAYBE-HIGHLIGHT-OPEN-PARENS is a redisplay hook that matches parens by
|
|---|
| 45 | ;;; highlighting the corresponding open-paren after a close-paren is
|
|---|
| 46 | ;;; typed.
|
|---|
| 47 | ;;;
|
|---|
| 48 | (defun maybe-highlight-open-parens (window)
|
|---|
| 49 | (declare (ignore window))
|
|---|
| 50 | (when (value highlight-open-parens)
|
|---|
| 51 | (if (and (value highlight-active-region) (region-active-p))
|
|---|
| 52 | (kill-open-paren-font-marks)
|
|---|
| 53 | (multiple-value-bind
|
|---|
| 54 | (start end)
|
|---|
| 55 | (funcall (value open-paren-finder-function)
|
|---|
| 56 | (current-point))
|
|---|
| 57 | (if (and start end)
|
|---|
| 58 | (set-open-paren-font-marks start end)
|
|---|
| 59 | (kill-open-paren-font-marks))))))
|
|---|
| 60 | ;;;
|
|---|
| 61 | (add-hook redisplay-hook 'maybe-highlight-open-parens)
|
|---|
| 62 |
|
|---|
| 63 | (defun set-open-paren-font-marks (start end)
|
|---|
| 64 | (if *open-paren-font-marks*
|
|---|
| 65 | (flet ((maybe-move (dst src)
|
|---|
| 66 | (unless (mark= dst src)
|
|---|
| 67 | (move-font-mark dst src))))
|
|---|
| 68 | (declare (inline maybe-move))
|
|---|
| 69 | (maybe-move (region-start *open-paren-font-marks*) start)
|
|---|
| 70 | (maybe-move (region-end *open-paren-font-marks*) end))
|
|---|
| 71 | (let ((line (mark-line start)))
|
|---|
| 72 | (setf *open-paren-font-marks*
|
|---|
| 73 | (region
|
|---|
| 74 | (font-mark line (mark-charpos start)
|
|---|
| 75 | *open-paren-highlight-font*)
|
|---|
| 76 | (font-mark line (mark-charpos end) 0))))))
|
|---|
| 77 |
|
|---|
| 78 | (defun kill-open-paren-font-marks ()
|
|---|
| 79 | (when *open-paren-font-marks*
|
|---|
| 80 | (delete-font-mark (region-start *open-paren-font-marks*))
|
|---|
| 81 | (delete-font-mark (region-end *open-paren-font-marks*))
|
|---|
| 82 | (setf *open-paren-font-marks* nil)))
|
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 |
|
|---|
| 86 | |
|---|
| 87 |
|
|---|
| 88 | ;;;; Active regions.
|
|---|
| 89 |
|
|---|
| 90 | (defvar *active-region-font-marks* nil)
|
|---|
| 91 | (defvar *active-region-highlight-font* 3
|
|---|
| 92 | "The index into the font-map for the active region highlighting font.")
|
|---|
| 93 |
|
|---|
| 94 |
|
|---|
| 95 | ;;; HIGHLIGHT-ACTIVE-REGION is a redisplay hook for active regions.
|
|---|
| 96 | ;;; Since it is too hard to know how the region may have changed when it is
|
|---|
| 97 | ;;; active and already highlighted, if it does not check out to being exactly
|
|---|
| 98 | ;;; the same, we just delete all the font marks and make new ones. When
|
|---|
| 99 | ;;; the current window is the echo area window, just pretend everything is
|
|---|
| 100 | ;;; okay; this keeps the region highlighted while we're in there.
|
|---|
| 101 | ;;;
|
|---|
| 102 | (defun highlight-active-region (window)
|
|---|
| 103 | (unless (eq window *echo-area-window*)
|
|---|
| 104 | (when (value highlight-active-region)
|
|---|
| 105 | (cond ((region-active-p)
|
|---|
| 106 | (cond ((not *active-region-font-marks*)
|
|---|
| 107 | (set-active-region-font-marks))
|
|---|
| 108 | ((check-active-region-font-marks))
|
|---|
| 109 | (t (kill-active-region-font-marks)
|
|---|
| 110 | (set-active-region-font-marks))))
|
|---|
| 111 | (*active-region-font-marks*
|
|---|
| 112 | (kill-active-region-font-marks))))))
|
|---|
| 113 | ;;;
|
|---|
| 114 | (add-hook redisplay-hook 'highlight-active-region)
|
|---|
| 115 |
|
|---|
| 116 | (defun set-active-region-font-marks ()
|
|---|
| 117 | (flet ((stash-a-mark (m &optional (font *active-region-highlight-font*))
|
|---|
| 118 | (push (font-mark (mark-line m) (mark-charpos m) font)
|
|---|
| 119 | *active-region-font-marks*)))
|
|---|
| 120 | (let* ((region (current-region nil nil))
|
|---|
| 121 | (start (region-start region))
|
|---|
| 122 | (end (region-end region)))
|
|---|
| 123 | (with-mark ((mark start))
|
|---|
| 124 | (unless (mark= mark end)
|
|---|
| 125 | (loop
|
|---|
| 126 | (stash-a-mark mark)
|
|---|
| 127 | (unless (line-offset mark 1 0) (return))
|
|---|
| 128 | (when (mark>= mark end) (return)))
|
|---|
| 129 | (unless (start-line-p end) (stash-a-mark end 0))))))
|
|---|
| 130 | (setf *active-region-font-marks* (nreverse *active-region-font-marks*)))
|
|---|
| 131 |
|
|---|
| 132 | (defun kill-active-region-font-marks ()
|
|---|
| 133 | (dolist (m *active-region-font-marks*)
|
|---|
| 134 | (delete-font-mark m))
|
|---|
| 135 | (setf *active-region-font-marks* nil))
|
|---|
| 136 |
|
|---|
| 137 | ;;; CHECK-ACTIVE-REGION-FONT-MARKS returns t if the current region is the same
|
|---|
| 138 | ;;; as that what is highlighted on the screen. This assumes
|
|---|
| 139 | ;;; *active-region-font-marks* is non-nil. At the very beginning, our start
|
|---|
| 140 | ;;; mark must not be at the end; it must be at the first font mark; and the
|
|---|
| 141 | ;;; font marks must be in the current buffer. We don't make font marks if the
|
|---|
| 142 | ;;; start is at the end, so if this is the case, then they just moved together.
|
|---|
| 143 | ;;; We return nil in this case to kill all the font marks and make new ones, but
|
|---|
| 144 | ;;; no new ones will be made.
|
|---|
| 145 | ;;;
|
|---|
| 146 | ;;; Sometimes we hack the font marks list and return t because we can easily
|
|---|
| 147 | ;;; adjust the highlighting to be correct. This keeps all the font marks from
|
|---|
| 148 | ;;; being killed and re-established. In the loop, if there are no more font
|
|---|
| 149 | ;;; marks, we either ended a region already highlighted on the next line down,
|
|---|
| 150 | ;;; or we have to revamp the font marks. Before returning here, we see if the
|
|---|
| 151 | ;;; region ends one more line down at the beginning of the line. If this is
|
|---|
| 152 | ;;; true, then the user is simply doing "Next Line" at the beginning of the
|
|---|
| 153 | ;;; line.
|
|---|
| 154 | ;;;
|
|---|
| 155 | ;;; Each time through the loop we look at the top font mark, move our roving
|
|---|
| 156 | ;;; mark down one line, and see if they compare. If they are not equal, the
|
|---|
| 157 | ;;; region may still be the same as that highlighted on the screen. If this
|
|---|
| 158 | ;;; is the last font mark, not at the beginning of the line, and it is at the
|
|---|
| 159 | ;;; region's end, then this last font mark is in the middle of a line somewhere
|
|---|
| 160 | ;;; changing the font from the highlighting font to the default font. Return
|
|---|
| 161 | ;;; t.
|
|---|
| 162 | ;;;
|
|---|
| 163 | ;;; If our roving mark is not at the current font mark, but it is at or after
|
|---|
| 164 | ;;; the end of the active region, then the end of the active region has moved
|
|---|
| 165 | ;;; before its previous location.
|
|---|
| 166 | ;;;
|
|---|
| 167 | ;;; Otherwise, move on to the next font mark.
|
|---|
| 168 | ;;;
|
|---|
| 169 | ;;; If our roving mark never moved onto a next line, then the buffer ends on the
|
|---|
| 170 | ;;; previous line, and the last font mark changes from the highlighting font to
|
|---|
| 171 | ;;; the default font.
|
|---|
| 172 | ;;;
|
|---|
| 173 | (defun check-active-region-font-marks ()
|
|---|
| 174 | (let* ((region (current-region nil nil))
|
|---|
| 175 | (end (region-end region)))
|
|---|
| 176 | (with-mark ((mark (region-start region)))
|
|---|
| 177 | (let ((first-active-mark (car *active-region-font-marks*))
|
|---|
| 178 | (last-active-mark (last *active-region-font-marks*)))
|
|---|
| 179 | (if (and (mark/= mark end)
|
|---|
| 180 | (eq (current-buffer)
|
|---|
| 181 | (line-buffer (mark-line first-active-mark)))
|
|---|
| 182 | (mark= first-active-mark mark))
|
|---|
| 183 | (let ((marks (cdr *active-region-font-marks*)))
|
|---|
| 184 | (loop
|
|---|
| 185 | (unless marks
|
|---|
| 186 | (let ((res (and (line-offset mark 1 0)
|
|---|
| 187 | (mark= mark end))))
|
|---|
| 188 | (when (and (not res)
|
|---|
| 189 | (line-offset mark 1 0)
|
|---|
| 190 | (mark= mark end)
|
|---|
| 191 | (start-line-p (car last-active-mark)))
|
|---|
| 192 | (setf (cdr last-active-mark)
|
|---|
| 193 | (list (font-mark (line-previous (mark-line mark))
|
|---|
| 194 | 0
|
|---|
| 195 | *active-region-highlight-font*)))
|
|---|
| 196 | (return t))
|
|---|
| 197 | (return res)))
|
|---|
| 198 | (let ((fmark (car marks)))
|
|---|
| 199 | (if (line-offset mark 1 0)
|
|---|
| 200 | (cond ((mark/= mark fmark)
|
|---|
| 201 | (return (and (not (cdr marks))
|
|---|
| 202 | (not (start-line-p fmark))
|
|---|
| 203 | (mark= fmark end))))
|
|---|
| 204 | ((mark>= mark end)
|
|---|
| 205 | (return nil))
|
|---|
| 206 | (t (setf marks (cdr marks))))
|
|---|
| 207 |
|
|---|
| 208 | (return (and (not (cdr marks))
|
|---|
| 209 | (not (start-line-p fmark))
|
|---|
| 210 | (mark= fmark end))))))))))))
|
|---|
| 211 |
|
|---|