source: release/1.5/source/cocoa-ide/hemlock/src/charprops.lisp

Last change on this file was 13711, checked in by R. Matthew Emerson, 15 years ago

merge r13710 from trunk

File size: 34.8 KB
RevLine 
[12268]1(in-package "HI")
2
3(defun make-empty-charprops-changes (&optional (size 2))
4 (make-array size :adjustable t :fill-pointer 0))
5
6(defun insert-charprops-change (changes index new)
7 "Insert a new change into the charprops changes vector at index. Objects
8 at and beyond INDEX are shifted right to make room."
9 (vector-push-extend nil changes)
10 (replace changes changes :start1 (1+ index) :start2 index)
11 (setf (aref changes index) new))
12
13(defun delete-charprops-change (changes index)
14 "Delete the change at index from the charprops changes vector. Objects
15 beyond INDEX are shifted left to fill the empty spot."
16 (unless (= (fill-pointer changes) index)
17 (replace changes changes :start1 index :start2 (1+ index)))
18 (decf (fill-pointer changes)))
19
20(defun push-charprops-change (change changes)
21 (vector-push-extend change changes))
22
23;;; Return the index of the charprops change that applies to
24;;; charpos, or else NIL if the charpos has (implicit)
25;;; default properties.
26(defun charprops-change-index-for-position (changes charpos)
27 (do ((i 0 (1+ i))
28 (i-1 nil i)
29 (change nil))
30 ((= i (length changes)) i-1)
31 (setq change (aref changes i))
32 (when (< charpos (charprops-change-index change))
33 (return i-1))))
34
35;;; Real programmers can write assembly in any language.
36(defun line-charprops-for-position (line charpos)
37 "Return, as multiple values, the plist, and start position and end
38 position over which the plist applies."
39 (unless (and (>= charpos 0)
40 (<= charpos (line-length line)))
41 (error "index ~d out of range" charpos))
42 (let* ((changes (line-charprops-changes line))
43 (change nil)
44 (prior-change nil)
45 (start-pos 0)
46 (end-pos 0))
47 (dotimes (i (length changes) (values (and change
48 (charprops-change-plist change))
[12536]49 start-pos (1+ (line-length line))))
[12268]50 (setq prior-change change)
51 (setq change (aref changes i))
52 (setq end-pos (charprops-change-index change))
53 (when (< charpos (charprops-change-index change))
54 (return (values (and prior-change (charprops-change-plist prior-change))
55 start-pos end-pos)))
56 (setq start-pos (charprops-change-index change)))))
57
58(defun squeeze-out-superseded-changes (changes idx)
59 (let* ((base-change (aref changes idx)))
60 (do* ((i (1+ idx) (1+ i))
61 (change nil)
62 (n 0))
63 ((= i (length changes))
64 ;;(format t "~&start1 = ~d start2 = ~d" (1+ idx) (+ n 1 idx))
65 (replace changes changes :start1 (1+ idx) :start2 (+ n 1 idx))
66 (decf (fill-pointer changes) n)
67 changes)
68 (setq change (aref changes i))
69 (when (<= (charprops-change-index change)
70 (charprops-change-index base-change))
71 (setf (charprops-change-plist base-change) (charprops-change-plist change))
72 (incf n)
73 (setf (aref changes i) nil)))))
74
75;;; Set the charprops of the specified LINE between positions START and
76;;; END. If END is NIL, that means the end of the line. Note that this
77;;; doesn't merge in properties: it replaces whatever is there.
78(defun set-line-charprops (line charprops &key (start 0) end &aux plist)
79 (setq plist (charprops-as-plist charprops))
80 (when (and end (= end (line-length line)))
81 (setq end nil))
82 (when (and (null plist) (= start 0) (null end))
83 (setf (line-charprops-changes line) nil)
84 (return-from set-line-charprops))
85 (let* ((changes (line-charprops-changes line))
86 (new-change (make-charprops-change start plist)))
87 (if (null changes)
88 (let ((new-changes (make-array 2 :adjustable t :fill-pointer 0)))
89 (vector-push new-change new-changes)
90 (when end
91 (vector-push (make-charprops-change end nil) new-changes))
92 (setf (line-charprops-changes line) new-changes)
93 (line-charprops-changes line))
94 ;; Put the new charprops change into the right place in the charprops
95 ;; changes vector, making note of its position.
96 (do* ((i 0 (1+ i))
97 (change nil)
98 (prior-change nil change))
99 ((= i (length changes))
100 (insert-charprops-change changes i new-change)
101 (when end
102 (let ((prior-plist (and prior-change
103 (charprops-change-plist prior-change))))
104 (insert-charprops-change changes (1+ i)
105 (make-charprops-change end prior-plist)))))
106 (setq change (aref changes i))
107 (when (<= start (charprops-change-index change))
108 (insert-charprops-change changes i new-change)
109 (incf i)
110 (if (null end)
111 (setf (fill-pointer changes) i)
112 (let ((prior-plist (and prior-change
113 (charprops-change-plist prior-change))))
114 (insert-charprops-change changes i (make-charprops-change end prior-plist))
115 (squeeze-out-superseded-changes changes i)))
116 (return))))))
117
[12536]118(defun add-line-charprop-value (line name value &key (start 0) end)
119 (let* ((changes (line-charprops-changes line))
120 (start-idx (charprops-change-index-for-position changes start))
121 (end-idx (charprops-change-index-for-position changes
122 (or end
123 (setq end (line-length line))))))
124 (cond ((or (null changes)
125 (and (null start-idx) (null end-idx)))
126 ;; Either the line has no existing charprops, or we're within the
127 ;; implicit run of default properties at the start of the line.
128 ;; Just set the charprops over the relevant range and return.
129 (set-line-charprops line (list name value) :start start :end end)
130 (return-from add-line-charprop-value changes))
131 ((null start-idx)
132 ;; The starting position is in the implicit run of default
133 ;; properties at the start of the line.
134 (let ((new-change (make-charprops-change start (list name value))))
135 (insert-charprops-change changes 0 new-change)
136 (setq start-idx 0)
137 (incf end-idx))
138 (let ((end-change (aref changes end-idx)))
139 (unless (= (charprops-change-index end-change) end)
140 (let ((new-change (copy-charprops-change end-change)))
141 (setf (charprops-change-index new-change) end)
142 (insert-charprops-change changes (1+ end-idx) new-change)
143 (incf end-idx)))))
144 ((and start-idx end-idx)
145 (let ((start-change (aref changes start-idx)))
146 (unless (= (charprops-change-index start-change) start)
147 (let ((new-change (copy-charprops-change start-change)))
148 (setf (charprops-change-index new-change) start)
149 (insert-charprops-change changes (1+ start-idx) new-change)
150 (incf start-idx)
151 (incf end-idx))))
152 (let ((end-change (aref changes end-idx))
153 (next-end-idx (charprops-change-index-for-position changes (1+ end))))
154 ;; If end-idx and next-end-idx differ, then the end
155 ;; position comes at the very end of a run, and we don't
156 ;; need to split. We also don't need to split if end is
157 ;; at the very end of the line.
158 (when (and (= end-idx next-end-idx)
159 (not (= end (line-length line))))
160 (let ((new-change (copy-charprops-change end-change)))
161 (setf (charprops-change-index new-change) end)
162 (insert-charprops-change changes (1+ end-idx) new-change)))))
163 (t (error "how did we get here?")))
164 (loop for i from start-idx to end-idx
165 as change = (aref changes i)
166 do (if (null value)
167 (remf (charprops-change-plist change) name)
168 (setf (getf (charprops-change-plist change) name) value)))))
169
170(defun set-region-charprops (region charprops)
171 (let* ((start (region-start region))
172 (end (region-end region))
173 (first-line (mark-line start))
174 (last-line (mark-line end)))
175 (cond ((eq first-line last-line)
176 (set-line-charprops first-line charprops :start (mark-charpos start)
177 :end (mark-charpos end))
178 (coalesce-line-charprops first-line))
179 (t
180 (set-line-charprops first-line charprops :start (mark-charpos start))
181 (do* ((line (line-next first-line) (line-next line)))
182 ((eq line last-line)
183 (set-line-charprops line charprops :end (mark-charpos end)))
184 (set-line-charprops line charprops))))))
185
[12268]186;;; Returns two values: fresh charprops change vectors for the line's characters
187;;; before and after charpos.
188(defun split-line-charprops (line charpos)
[13130]189 (let* ((changes (line-charprops-changes line))
190 (nchanges (length changes)))
191 (when (> nchanges 0)
[12268]192 (let ((left (make-array 2 :adjustable t :fill-pointer 0))
193 (right (make-array 2 :adjustable t :fill-pointer 0))
194 (pivot nil)
195 (prior-change nil))
196 (do* ((i 0 (1+ i))
197 (change nil))
198 ((or pivot
[13130]199 (= i nchanges))
[12268]200 (if (null pivot)
201 ;; The last change extends to the end of line, so that will be the
202 ;; charprops in effect at the beginning of the new line.
203 (if (null (charprops-change-plist change))
204 (setq right nil)
205 (let* ((c (copy-charprops-change change)))
206 (setf (charprops-change-index c) 0)
207 (push-charprops-change c right)))
208 ;; Some charprops changes remain. First, split the prior change
209 ;; if necessary, and then pick up the rest of the shifts.
210 (progn
211 (when (and prior-change
212 (> charpos (charprops-change-index prior-change)))
213 ;; need to split change
214 (let* ((c (copy-charprops-change prior-change)))
215 (setf (charprops-change-index c) 0)
216 (push-charprops-change c right)))
[13130]217 (loop for i from pivot below nchanges
[12268]218 as change = (aref changes i)
219 do (decf (charprops-change-index change) charpos)
220 (push-charprops-change (aref changes i) right))))
221 (values left right pivot))
222 (setq change (aref changes i))
223 (if (< (charprops-change-index change) charpos)
224 (progn
225 (push-charprops-change change left)
226 (setq prior-change change))
227 (setq pivot i)))))))
228
[12288]229(defun append-line-charprops (line changes)
230 (let* ((left (line-charprops-changes line))
231 (len (line-length line))
232 (right changes))
233 (cond ((and left right)
[12536]234 (loop for c across right
235 for new-change = (copy-charprops-change c)
236 do (incf (charprops-change-index new-change) len)
237 (push-charprops-change new-change left)))
[12288]238 ((and (null left) right)
[12536]239 (setq left (copy-charprops-changes right))
[12288]240 (adjust-charprops-change-indexes left len)
241 (setf (line-charprops-changes line) left))
242 ((and left (null right))
243 (push-charprops-change (make-charprops-change len nil) left)))
244 left))
245
[12268]246;;; Append the charprops-changes from line2 onto line1, modifying their
247;;; indexes appropriately.
248(defun join-line-charprops (line1 line2)
249 (let* ((left (line-charprops-changes line1))
250 (lidx (1- (length left)))
251 (right (line-charprops-changes line2))
252 (ridx 0)
253 (line1-len (line-length line1)))
254 (cond ((and left right)
255 ;; If the last change on line1 and the first change on line2
256 ;; represent the same properties, then omit line2's first
257 ;; change.
258 (let* ((lchange (aref left lidx))
259 (lprops (charprops-change-plist lchange))
260 (rchange (aref right ridx))
261 (rprops (charprops-change-plist rchange)))
262 (if (> 0 (charprops-change-index rchange))
263 ;; There is an implicit run of default charprops at the
264 ;; start of the line.
265 (unless (null lprops)
266 ;; The last change on line1 represents some non-default
267 ;; set of charprops, so insert an explicit change to the
268 ;; default set before copying over the rest.
269 (push-charprops-change (make-charprops-change (1+ line1-len) nil)
270 left))
271 (when (charprops-equal lprops rprops)
272 (incf ridx)))
273 (do* ((i ridx (1+ i))
274 (change nil))
275 ((= i (length right)))
276 (setq change (aref right i))
277 (incf (charprops-change-index change) (1+ line1-len))
278 (push-charprops-change change left))))
279 ((and (null left) right)
280 (adjust-charprops-change-indexes right line1-len)
281 (setf (line-charprops-changes line1) right))
282 ((and left (null right))
283 (let* ((lchange (aref left lidx)))
284 (unless (null (charprops-change-plist lchange))
285 (push-charprops-change (make-charprops-change (1+ line1-len) nil)
286 left))))
287 ;; otherwise both nil, so don't need to do anything.
288 )
289 left))
290
291(defun copy-line-charprops (line &key (start 0) end)
292 "Return a freshly-consed vector of charprops changes that applies to the
293 characters in the interval [start, end) on the specified line. If the
294 charprops in between start and end are the default charprops, return
295 NIL."
[12288]296 (let ((changes (line-charprops-changes line)))
297 ;; some early-out special cases
[12268]298 (cond ((null changes)
[12288]299 (return-from copy-line-charprops))
[13429]300 ((and (= start 0) (or (null end) (eql end (line-length line))))
[12701]301 (return-from copy-line-charprops (copy-charprops-changes changes))))
[12288]302 (unless end
303 (setq end (line-length line)))
[13711]304 (when (eql start end)
305 (return-from copy-line-charprops))
[12288]306 (let* ((new-changes (make-empty-charprops-changes))
307 (start-idx (charprops-change-index-for-position changes start))
308 (end-idx (charprops-change-index-for-position changes (1- end))))
309 (if (eql start-idx end-idx)
310 (if (null start-idx)
311 (setq new-changes nil)
312 (let* ((change (aref changes start-idx))
313 (plist (charprops-change-plist change)))
314 (if (null plist)
315 (setq new-changes nil)
316 (push-charprops-change (make-charprops-change start plist)
317 new-changes))))
318 (do ((i (or start-idx 0) (1+ i)))
319 ((> i end-idx))
320 (let* ((change (aref changes i))
321 (index (charprops-change-index change))
322 (plist (charprops-change-plist change)))
323 (push-charprops-change (make-charprops-change
324 (max 0 (- index start)) plist)
325 new-changes))))
326 new-changes)))
[12268]327
328(defun delete-line-charprops (line &key (start 0) end)
[12288]329 (let ((changes (line-charprops-changes line)))
330 ;; some early-out special cases
[12268]331 (cond ((null changes)
332 (return-from delete-line-charprops))
[12288]333 ((and (= start 0) (null end))
[12268]334 (setf (line-charprops-changes line) nil)
[12288]335 (return-from delete-line-charprops)))
336 (unless end
337 (setq end (line-length line)))
338 (assert (<= start end) (start end))
339 (let* ((start-idx (charprops-change-index-for-position changes start))
340 (end-idx (charprops-change-index-for-position changes (1- end))))
341 (cond ((null start-idx)
342 (if (null end-idx)
343 (adjust-charprops-change-indexes changes (- start end) :start 0)
344 (progn
345 ;; delete changes before end-idx
346 (replace changes changes :start1 0 :start2 end-idx)
347 (decf (fill-pointer changes) end-idx)
348 (setf (charprops-change-index (aref changes 0)) start)
349 ;; move back start of subsequent changes, if there are any
350 (when (> (length changes) 1)
351 (adjust-charprops-change-indexes changes (- start end)
352 :start 1)
353 ;; if the change is now zero-length, remove it
354 (when (= (charprops-change-index (aref changes 0))
355 (charprops-change-index (aref changes 1)))
356 (delete-charprops-change changes 0))))))
357 ((eql start-idx end-idx)
358 ;; The deletion takes place within the scope of a single
359 ;; charprops run.
360 ;; Move back start of subsequent changes, if there are any
361 (when (> (length changes) (1+ start-idx))
362 (adjust-charprops-change-indexes changes (- start end)
363 :start (1+ start-idx))
364 ;; if the change is now zero-length, remove it
365 (when (= (charprops-change-index (aref changes start-idx))
366 (charprops-change-index (aref changes (1+ start-idx))))
367 (delete-charprops-change changes start-idx))))
368 (t
369 ;; Remove changes between start-idx and and end-idx.
370 (replace changes changes :start1 (1+ start-idx)
371 :start2 end-idx)
372 (decf (fill-pointer changes) (- end-idx (1+ start-idx)))
373 (setf (charprops-change-index (aref changes (1+ start-idx))) start)
374 (when (> (length changes) (1+ start-idx))
375 (adjust-charprops-change-indexes changes (- start end)
376 :start (+ 2 start-idx))
377 ;; if first change is now zero-length, remove it
378 (when (= (charprops-change-index (aref changes start-idx))
379 (charprops-change-index (aref changes (1+ start-idx))))
380 (delete-charprops-change changes start-idx))))))
381 (coalesce-line-charprops line)))
[12268]382
[12288]383;;; Coalesce adjacent changes with CHARPROP-EQUAL plists.
384;;; Maybe make this remove zero-length changes, too?
385(defun coalesce-line-charprops (line)
386 (let ((changes (line-charprops-changes line)))
387 (do* ((i 0 (1+ i))
388 (change nil))
389 ((>= i (length changes)))
390 (setq change (aref changes i))
391 (loop with j = (1+ i)
392 while (and (< j (length changes))
393 (charprops-equal (charprops-change-plist change)
394 (charprops-change-plist (aref changes j))))
395 do (delete-charprops-change changes j)))
396 ;; Elide any changes with NIL plists at the start of the line.
397 (loop
398 while (and (> (length changes) 0)
399 (null (charprops-change-plist (aref changes 0))))
400 do (delete-charprops-change changes 0))
401 (when (zerop (length changes))
402 (setf (line-charprops-changes line) nil)))
403 (line-charprops-changes line))
404
[12268]405(defun adjust-charprops-change-indexes (changes delta &key (start 0))
406 (do* ((i start (1+ i))
407 (change nil))
408 ((>= i (length changes))
409 changes)
410 (setq change (aref changes i))
411 (incf (charprops-change-index change) delta)))
412
413;;; Add delta to the starting index of all charprops changes after the one
[12536]414;;; containing start.
415(defun adjust-line-charprops (line delta &key (start 0))
416 (let* ((changes (line-charprops-changes line))
417 (start-idx (charprops-change-index-for-position changes start)))
[12288]418 (adjust-charprops-change-indexes changes delta :start (if start-idx
419 (1+ start-idx)
420 0))))
[12268]421
[12536]422(defun apply-line-charprops (line changes start-pos end-pos)
423 (cond ((null changes)
424 (set-line-charprops line nil :start start-pos :end end-pos))
425 (t
426 (setq changes (copy-charprops-changes changes))
427 (do* ((i 0 (1+ i))
428 (change nil))
429 ((= i (length changes)))
430 (setq change (aref changes i))
431 (set-line-charprops line (charprops-change-plist change)
432 :start (+ (charprops-change-index change) start-pos)
433 :end end-pos))
434 (coalesce-line-charprops line)))
435 (line-charprops-changes line))
[12268]436
437(defvar *display-properties*
438 '(:font-name
439 :font-size
440 :font-weight
[12536]441 :font-width
[12268]442 :font-slant
443 :font-underline
444 :font-color
445 :background-color))
446
[12536]447;;; Setting and accessing charprops
[12268]448
449(defun next-charprop-value (mark name &key view)
450 (let ((props (next-charprops mark :view view)))
451 (getf props name)))
452
453(defun previous-charprop-value (mark name &key view)
454 (let ((props (previous-charprops mark :view view)))
455 (getf props name)))
456
457(defun set-charprop-value (mark name value &key (count 1 count-supplied-p) end view)
[12536]458 (declare (ignore view))
[12268]459 (when (and count-supplied-p end)
[12536]460 (error "Cannot specify both :COUNT and :END"))
461 (with-mark ((start-mark mark)
462 (end-mark mark))
463 (if end
464 (move-mark end-mark end)
465 (character-offset end-mark count))
466 (let* ((start-line (mark-line start-mark))
467 (start-charpos (mark-charpos start-mark))
468 (end-line (mark-line end-mark))
469 (end-charpos (mark-charpos end-mark)))
470 (cond ((eq start-line end-line)
471 (add-line-charprop-value start-line name value
472 :start start-charpos
473 :end end-charpos))
474 (t
475 (do* ((line start-line (line-next line))
476 (start start-charpos 0))
477 ((eq line end-line)
478 (add-line-charprop-value end-line name value
479 :start 0
480 :end end-charpos))
481 (add-line-charprop-value line name value :start start))))
482 (let ((n (count-characters (region start-mark end-mark)))
483 (buffer (line-%buffer start-line)))
[12859]484 (hemlock-ext:buffer-note-modification buffer mark n)))))
[12268]485
[12536]486(defun find-line-charprop-value (line name value &key (start 0) end)
487 (unless end
488 (setq end (line-length line)))
489 (let* ((changes (line-charprops-changes line))
490 (start-idx (or (charprops-change-index-for-position changes start) 0))
491 (end-idx (or (charprops-change-index-for-position changes end) 0)))
492 (when changes
493 (loop for i from start-idx to end-idx
494 as change = (aref changes i)
495 as plist = (charprops-change-plist change)
496 as found-value = (getf plist name)
497 do (when (and found-value
498 (charprop-equal found-value value))
499 (return (max start (charprops-change-index change))))))))
500
[12268]501(defun find-charprop-value (mark name value &key (count nil count-supplied-p)
[12536]502 end view from-end)
503 (declare (ignore from-end view))
504 (with-mark ((start-mark mark)
505 (end-mark mark))
506 (when (and count-supplied-p end)
507 (error "Cannot specify both :COUNT and :END"))
508 (let* ((buffer (line-buffer (mark-line mark))))
509 (unless (bufferp buffer)
510 (error "text must be in a buffer"))
511 (if count-supplied-p
512 (character-offset end-mark count)
513 (move-mark end-mark (buffer-end-mark buffer)))
514 (let* ((start-line (mark-line start-mark))
515 (start-charpos (mark-charpos start-mark))
516 (end-line (mark-line end-mark))
517 (end-charpos (mark-charpos end-mark)))
518 (do* ((line start-line (line-next line))
519 (charpos start-charpos 0))
520 ((eq line end-line)
521 (let ((pos (find-line-charprop-value end-line name value
522 :start charpos
523 :end end-charpos)))
524 (when pos
525 (move-to-position mark pos end-line)
526 mark)))
527 (let ((pos (find-line-charprop-value line name value :start charpos)))
528 (when pos
529 (move-to-position mark pos line)
530 (return mark))))))))
[12268]531
532(defun filter-match (filter name)
533 (cond ((functionp filter)
534 (funcall filter name))
535 ((eq filter :display)
536 (member name *display-properties* :test #'eq))
537 ((typep filter 'sequence)
538 (member name filter))
539 (t
540 name)))
541
542(defun filter-charprops (filter charprops)
543 (if (eq filter t)
544 charprops
545 (typecase charprops
546 ((satisfies ccl::plistp) (loop for (k v) on charprops by #'cddr
547 when (filter-match filter k)
548 collect k and collect v))
549 (hash-table (loop for k being the hash-keys of charprops using (hash-value v)
550 when (filter-match filter k)
551 collect k and collect v)))))
552
553(defun next-charprops (mark &key view (filter t))
554 "Return the properties of the character after MARK."
555 (declare (ignore view))
556 (when (next-character mark)
557 (let* ((props (line-charprops-for-position (mark-line mark) (mark-charpos mark))))
558 (filter-charprops filter props))))
559
560(defun previous-charprops (mark &key view (filter t))
561 "Return the properties of the character before MARK."
562 (with-mark ((m mark))
563 (when (mark-before m)
564 (next-charprops m :view view :filter filter))))
565
566(defun set-charprops (mark charprops &key (count 1 count-supplied-p)
[12804]567 (end nil end-supplied-p) (filter (charprops-names charprops)))
[12268]568 (when (and count-supplied-p end-supplied-p)
569 (error "Only one of count or end can be supplied."))
[12536]570 (setq charprops (charprops-as-plist charprops :filter filter))
571 (with-mark ((start-mark mark)
572 (end-mark mark))
573 (if end
574 (move-mark end-mark end)
575 (character-offset end-mark count))
[12804]576 ;; lame.
577 (loop for (k v) on charprops by #'cddr
578 do (set-charprop-value start-mark k v :end end-mark))))
[12268]579
580;;; Return a list of charprops-change vectors that correspond to the lines
[12536]581;;; of text in the region defined by the paramaters.
[12268]582(defun charprops-in-region (region-or-mark &key (count 1 count-supplied-p)
583 end filter)
584 (declare (ignore filter))
585 (when (and count-supplied-p end)
586 (error "Only one of count or end can be supplied."))
587 (let (region result)
588 (etypecase region-or-mark
589 (mark (with-mark ((m region-or-mark))
590 (when end
591 (setq count (- end (mark-absolute-position m))))
592 (character-offset m count)
593 (setq region (region region-or-mark m))))
[12536]594 (region (when (or count-supplied-p end)
595 (error "Can't specify count or end when passing in a region."))
596 (setq region region-or-mark)))
[12268]597 (let* ((start (region-start region))
598 (first-line (mark-line start))
[12536]599 (first-charpos (mark-charpos start))
[12268]600 (end (region-end region))
[12536]601 (last-line (mark-line end))
602 (last-charpos (mark-charpos end)))
603 (cond
604 ((eq first-line last-line)
605 (list (copy-line-charprops first-line :start first-charpos)))
606 (t
607 (push (copy-line-charprops first-line :start first-charpos) result)
608 (do* ((line (line-next first-line) (line-next line))
609 (m (copy-mark start) (line-start m line)))
610 ((eq line last-line)
611 (push (copy-line-charprops last-line :end last-charpos) result)
612 (nreverse result))
613 (push (copy-line-charprops line) result)))))))
[12268]614
615(defun apply-charprops (mark charprops-range &key filter from-end)
616 (declare (ignore from-end filter charprops-range mark)))
617
[12536]618#|
619 (let* ((start-line (mark-line mark))
620 (start-charpos (mark-charpos))
621 (nlines (length charprops-range))
622 (first-changes (pop charprops-range)))
623
624 ;; do possibly-partial first line
625 (let ((left (split-line-charprops start-line start-charpos)))
626 (setf (line-charprops start-line) left)
627 (append-line-charprops start-line first-changes))
628 ;; do some number of whole lines
629 (do* ((line (line-next start-line) (line-next line))
630 (previous-line start-line (line-next previous-line))
631 (cc-list charprops-range (cdr charprops-range))
632 (changes (car cc-list) (car cc-list)))
633 ((or (null line) (endp cc-list)))
634 (setf (line-charprops-changes line) (copy-charprops-changes changes)))
635 ;; I don't know what to do about a partial last line. There's no
636 ;; way that I can see to know whether the last charprops change vector
637 ;; in the charprops-range list is to apply to an entire line or to end
638 ;; at a particular charpos on that line. Maybe that information needs
639 ;; to be stored as part of the charprops-range list. For example, if the
640 ;; element of the charprops-range list is a non-null list, the list could
641 ;; be (charprops-change-vector start-charpos end-charpos).
642
643 (multiple-value-bind (left right)
644 (split-line-charprops last-line last-charpos)
645 (setf (line-charprops last-line) last-changes)
646 (append-line-charprops last-line right)))
647|#
648
[12268]649(defun find-charprops (mark charprops &key count end view filter from-end)
650 (declare (ignore from-end filter view end count charprops mark)))
651
652(defun find-charprops-change (mark &key count end view filter from-end)
653 (declare (ignore from-end filter view end count))
654 (let* ((line (mark-line mark))
655 (charpos (mark-charpos mark))
656 (changes (line-charprops-changes line))
657 (idx (charprops-change-index-for-position changes charpos)))
658 (loop
659 (incf idx)
660 (if (= idx (length changes))
661 (setf line (line-next line)
662 charpos 0
663 changes (line-charprops-changes line)
664 idx (charprops-change-index-for-position changes charpos))
665 (return (move-mark mark (charprops-change-index (aref changes idx))))))))
666
667(defun print-line-charprops (line &key (start 0) (end (hi:line-length line)))
668 (let* ((string (hi:line-string line))
669 (charprops-changes (hi::line-charprops-changes line)))
670 (let ((index start)
671 (plist nil)
672 (x 0))
673 (loop for change across charprops-changes
674 do (let* ((next-index (charprops-change-index change))
675 (next-plist (charprops-change-plist change))
676 (end (min end next-index)))
677 (when (and (>= index start)
678 (< index end))
679 (format t "~& ~d: [~d, ~d) ~s: ~s" x index end
680 (subseq string index end) plist))
681 (setq index next-index)
682 (setq plist next-plist)
683 (incf x)))
684 ;; final part of line
685 (format t "~& ~d: [~d, ~d) ~s: ~s" x index end
686 (subseq string index end) plist))))
687
688(defun copy-charprops (charprops)
689 (copy-list charprops))
690
691
692;;; Utility functions
693
694(defun charprop-equal (value1 value2)
695 (cond ((and (stringp value1) (stringp value2))
696 (string= value1 value2))
697 ((and (numberp value1) (numberp value2))
698 (= value1 value2))
699 (t
700 (eql value1 value2))))
701
702(defun charprops-get (charprops name &key (filter t))
703 (when (and name (filter-match filter name))
704 (etypecase charprops
705 ((satisfies ccl::plistp) (getf charprops name))
706 (hash-table (gethash name charprops)))))
707
708(defun charprops-set (charprops name value)
709 (etypecase charprops
710 ((satisfies ccl::plistp) (setf (getf charprops name) value))
711 (hash-table (setf (gethash name charprops) value)))
712 charprops)
713
714(defun same-sets (s1 s2 &key (test #'eql))
715 (and (subsetp s1 s2 :test test)
716 (subsetp s2 s1 :test test)))
717
[12536]718;; I wonder if this will be a hot spot...
[12268]719(defun charprops-equal (charprops1 charprops2 &key (filter t))
720 (setq charprops1 (charprops-as-plist charprops1 :filter filter)
721 charprops2 (charprops-as-plist charprops2 :filter filter))
722 (let (keys1 values1 keys2 values2)
723 (loop for (k1 v1) on charprops1 by #'cddr
724 do (push k1 keys1)
725 (push v1 values1))
726 (loop for (k2 v2) on charprops2 by #'cddr
727 do (push k2 keys2)
728 (push v2 values2))
729 (and (same-sets keys1 keys2)
730 (same-sets values1 values2 :test #'charprop-equal))))
731
732(defun charprops-as-plist (charprops &key (filter t))
733 (etypecase charprops
734 ((satisfies ccl::plistp) (if (eq filter t)
735 charprops
736 (loop for (k v) on charprops by #'cddr
737 when (filter-match filter k)
738 collect k and collect v)))
739 (hash-table (loop for k being the hash-keys of charprops using (hash-value v)
740 when (filter-match filter k)
741 collect k and collect v))))
742
743(defun charprops-as-hash (charprops &key (filter t))
744 (etypecase charprops
745 ((satisfies ccl::plistp) (let ((hash (make-hash-table)))
746 (loop for (k v) on charprops by #'cddr
747 when (filter-match filter k)
748 do (setf (gethash k hash) v))
749 hash))
750 (hash-table (if (eq filter t)
751 charprops
752 (let ((hash (make-hash-table)))
753 (maphash #'(lambda (k v)
754 (when (filter-match filter k)
755 (setf (gethash k hash) v)))
756 charprops))))))
757
758(defun charprops-names (charprops &key (filter t))
759 (etypecase charprops
760 ((satisfies ccl::plistp) (loop for name in charprops by #'cddr
761 when (filter-match filter name)
762 collect name))
763 (hash-table (loop for name being the hash-keys of charprops
764 when (filter-match filter name)
765 collect name))))
766
767;;; From <AppKit/NSAttributedString.h>
768(defparameter *cocoa-attributes*
[12536]769 `((:ns-font . ,#&NSFontAttributeName)
770 (:ns-paragraph-style . ,#&NSParagraphStyleAttributeName)
771 (:ns-foreground-color . ,#&NSForegroundColorAttributeName)
772 (:ns-underline-style . ,#&NSUnderlineStyleAttributeName)
773 (:ns-superscript . ,#&NSSuperscriptAttributeName)
774 (:ns-background-color . ,#&NSBackgroundColorAttributeName)
775 (:ns-attachment . ,#&NSAttachmentAttributeName)
776 (:ns-ligature . ,#&NSLigatureAttributeName)
777 (:ns-baseline-offset . ,#&NSBaselineOffsetAttributeName)
778 (:ns-kern . ,#&NSKernAttributeName)
779 (:ns-link . ,#&NSLinkAttributeName)
780 (:ns-stroke-width . ,#&NSStrokeWidthAttributeName)
781 (:ns-stroke-color . ,#&NSStrokeColorAttributeName)
782 (:ns-underline-color . ,#&NSUnderlineColorAttributeName)
783 (:ns-strikethrough-style . ,#&NSStrikethroughStyleAttributeName)
784 (:ns-strikethrough-color . ,#&NSStrikethroughColorAttributeName)
785 (:ns-shadow . ,#&NSShadowAttributeName)
786 (:ns-obliqueness . ,#&NSObliquenessAttributeName)
787 (:ns-expansion . ,#&NSExpansionAttributeName)
788 (:ns-cursor . ,#&NSCursorAttributeName)
789 (:ns-tool-tip . ,#&NSToolTipAttributeName)
[12484]790 #-cocotron
[12536]791 (:ns-character-shape . ,#&NSCharacterShapeAttributeName)
[12484]792 #-cocotron
[12536]793 (:ns-glyph-info . ,#&NSGlyphInfoAttributeName)
[12274]794 ;;(:ns-marked-clause-segment . #&NSMarkedClauseSegmentAttributeName)
795 ;;(:ns-spelling-state . #&NSSpellingStateAttributeName)
796 ))
[12268]797
Note: See TracBrowser for help on using the repository browser.