[lice @ .darcsignore: put ignore file under control, and ignore fasl files.]
[lice.git] / textprop.lisp
blob44e60d660eba8066ad7d683627a86555727f945b
1 (in-package :lice)
3 ;; This function is not translated well
4 (defun validate-interval-range (object begin end force)
5 (let (i searchpos)
6 ;; /* If we are asked for a point, but from a subr which operates
7 ;; on a range, then return nothing. */
8 ;; if (EQ (*begin, *end) && begin != end)
9 ;; return NULL_INTERVAL;
10 (when (> begin end)
11 ;; MOVITZ doesn't have psetf
12 (let ((tmp begin))
13 (setf begin end
14 end tmp))
15 ;; (psetf begin end
16 ;; end begin)
18 (if (typep object 'buffer)
19 (progn
20 (when (not (and (<= (buffer-min object) begin)
21 (<= begin end)
22 (<= end (buffer-max object))))
23 (signal 'args-out-of-range))
24 (setf i (intervals object))
25 (when (= (buffer-min object) (buffer-max object))
26 (return-from validate-interval-range (values nil begin end)))
27 (setf searchpos begin))
28 (let ((len (length (pstring-data object))))
29 (when (not (and (<= 0 begin)
30 (<= begin end)
31 (<= end len)))
32 (signal 'args-out-of-range))
33 (setf i (intervals object))
34 (when (zerop len)
35 (return-from validate-interval-range (values nil begin end)))
36 (setf searchpos begin)))
37 (if i
38 (values (find-interval i searchpos) begin end)
39 (if force
40 (values (create-root-interval object) begin end)
41 (values i begin end)))))
43 (defun set-text-properties (start end properties &optional (object (current-buffer)))
44 (let ((start-bk start)
45 (end-bk end)
47 (setf properties (validate-plist properties))
48 ;; If we want no properties for a whole string, get rid of its
49 ;; intervals.
50 (when (and (null properties)
51 (typep object 'pstring)
52 (zerop start)
53 (= end (length (pstring-data object))))
54 (when (null (intervals object))
55 (return-from set-text-properties t))
56 (setf (intervals object) nil)
57 (return-from set-text-properties t))
58 (multiple-value-setq (i start end)
59 (validate-interval-range object start end nil))
60 (when (null i)
61 (when (null properties)
62 (return-from set-text-properties nil))
63 ;; /* Restore the original START and END values because
64 ;; validate_interval_range increments them for strings. */
65 (setf start start-bk
66 end end-bk)
67 (multiple-value-setq (i start end)
68 (validate-interval-range object start end t))
69 ;; /* This can return if start == end. */
70 (when (null i)
71 (return-from set-text-properties nil)))
73 ;; TODO: add this
74 ;; (when (typep object 'buffer)
75 ;; ;; modify_region (XBUFFER (object), XINT (start), XINT (end));
76 (set-text-properties-1 start end properties object i)
78 ;; if (BUFFERP (object) && !NILP (signal_after_change_p))
79 ;; signal_after_change (XINT (start), XINT (end) - XINT (start),
80 ;; XINT (end) - XINT (start));
81 t))
83 (defun set-text-properties-1 (start end properties buffer i)
84 (let ((len (- end start))
85 (prev-changed nil)
86 unchanged)
87 (when (zerop len)
88 (return-from set-text-properties-1))
89 (when (minusp len)
90 (incf start len)
91 (setf len (abs len)))
92 (when (null i)
93 (setf i (find-interval (intervals buffer) start)))
94 (when (/= (interval-pt i) start)
95 (setf unchanged i
96 i (split-interval-right unchanged (- start (interval-pt unchanged))))
97 (when (> (interval-text-length i) len)
98 (copy-properties unchanged i)
99 (setf i (split-interval-left i len))
100 (set-properties properties i buffer)
101 (return-from set-text-properties-1))
102 (set-properties properties i buffer)
103 (when (= (interval-text-length i) len)
104 (return-from set-text-properties-1))
105 (setf prev-changed i)
106 (decf len (interval-text-length i))
107 (setf i (next-interval i)))
108 (loop while (> len 0)
109 do (progn
110 (when (null i)
111 (error "borked."))
112 (when (>= (interval-text-length i) len)
113 (when (> (interval-text-length i) len)
114 (setf i (split-interval-left i len)))
115 (set-properties properties i buffer)
116 (when prev-changed
117 (merge-interval-left i))
118 (return-from set-text-properties-1))
119 (decf len (interval-text-length i))
120 ;; We have to call set_properties even if we are going
121 ;; to merge the intervals, so as to make the undo
122 ;; records and cause redisplay to happen.
123 (set-properties properties i buffer)
124 (if (null prev-changed)
125 (setf prev-changed i)
126 (setf prev-changed (merge-interval-left i)
127 i prev-changed))
128 (setf i (next-interval i))))))
130 (defun copy-properties (source target)
131 (when (and (default-interval-p source)
132 (default-interval-p target))
133 (return-from copy-properties))
134 (setf (interval-plist target) (copy-list (interval-plist source))))
136 (defun set-properties (properties interval object)
137 (when (typep object 'buffer)
138 ;; record undo info
140 (setf (interval-plist interval) (copy-tree properties)))
142 (defun add-properties (plist i object)
143 "Add the properties in plist to interval I. OBJECT should be the
144 string of buffer containing the interval."
145 (let ((changed nil))
146 (doplist (sym val plist changed)
147 (let ((found (getf (interval-plist i) sym)))
148 (if found
149 (progn
150 ;; record-property-change
151 (when (not (eql found val))
152 (setf (getf (interval-plist i) sym) val
153 changed t)))
154 (progn
155 ;; record-property-change
156 (setf (getf (interval-plist i) sym) val
157 changed t)))))))
159 (defun validate-plist (list)
160 "/* Validate LIST as a property list. If LIST is not a list, then
161 make one consisting of (LIST nil). Otherwise, verify that LIST is
162 even numbered and thus suitable as a plist. */"
163 (cond ((null list) nil)
164 ((consp list)
165 (if (oddp (length list))
166 (error "odd length property list")
167 list))
168 (t (list (list list nil)))))
170 (defun interval-has-all-properties (plist i)
171 "/* Return nonzero if interval I has all the properties, with the same
172 values, of list PLIST. */"
173 (doplist (sym val plist t)
174 (let ((found (getf (interval-plist i) sym)))
175 (if found
176 (when (not (eql found val))
177 (return-from interval-has-all-properties nil))
178 (return-from interval-has-all-properties nil)))))
180 (defun add-text-properties (start end properties &optional (object (current-buffer)))
181 "Add properties to the text from START to END. The third argument
182 PROPERTIES is a property list specifying the property values to add.
183 If the optional fourth argument OBJECT is a buffer (or nil, which
184 means the current buffer), START and END are buffer positions
185 (integers or markers). If OBJECT is a string, START and END are
186 0-based indices into it. Return t if any property value actually
187 changed, nil otherwise."
188 (let ((len (- end start))
189 (modified 0)
190 i unchanged)
191 (setf properties (validate-plist properties))
192 (when (null properties)
193 (return-from add-text-properties nil))
194 (multiple-value-setq (i start end) (validate-interval-range object start end t))
195 (when (null i)
196 (return-from add-text-properties nil))
197 ;; If we're not starting on an interval boundary, we have to split
198 ;; this interval.
199 (when (/= (interval-pt i) start)
200 (if (interval-has-all-properties properties i)
201 (let ((got (- (interval-text-length i) (- start (interval-pt i)))))
202 (when (>= got len)
203 (return-from add-text-properties nil))
204 (decf len got)
205 (setf i (next-interval i)))
206 (progn
207 (setf unchanged i)
208 (setf i (split-interval-right unchanged (- start (interval-pt unchanged))))
209 (copy-properties unchanged i))))
210 ;; if (BUFFERP (object))
211 ;; modify_region (XBUFFER (object), XINT (start), XINT (end));
213 ;; We are at the beginning of interval I, with LEN chars to scan.
214 (loop
215 (when (null i)
216 (error "BORK."))
217 (when (>= (interval-text-length i) len)
218 (when (interval-has-all-properties properties i)
219 (return-from add-text-properties modified))
220 (when (= (interval-text-length i) len)
221 (add-properties properties i object)
222 (return-from add-text-properties t))
223 (setf unchanged i
224 i (split-interval-left unchanged len))
225 (copy-properties unchanged i)
226 (add-properties properties i object)
227 (return-from add-text-properties t))
228 (decf len (interval-text-length i))
229 (setf modified (add-properties properties i object)
230 i (next-interval i)))))
232 (defun put-text-property (start end property value object)
233 "Set one property of the text from START to END. The third and
234 fourth arguments PROPERTY and VALUE specify the property to add. If
235 the optional fifth argument OBJECT is a buffer (or nil, which means
236 the current buffer), START and END are buffer positions (integers or
237 markers). If OBJECT is a string, START and END are 0-based indices
238 into it."
239 (add-text-properties start end (list property value) object))
241 (defun remove-properties (plist list i object)
242 (doplist (sym val plist)
243 (remf sym (interval-plist i)))
244 (dolist (sym list)
245 (remf sym (interval-plist i))))
247 (defun remove-text-properties (start end properties &optional (object (current-buffer)))
248 "Remove some properties from text from START to END. The third
249 argument PROPERTIES is a property list whose property names specify
250 the properties to remove. \(The values stored in PROPERTIES are
251 ignored.) If the optional fourth argument OBJECT is a buffer (or nil,
252 which means the current buffer), START and END are buffer positions
253 (integers or markers). If OBJECT is a string, START and END are
254 0-based indices into it. Return t if any property was actually
255 removed, nil otherwise.
257 Use set-text-properties if you want to remove all text properties."
258 (let (i unchanged len (modified nil))
259 (multiple-value-setq (i start end)
260 (validate-interval-range object start end nil))
261 (when (null i)
262 (return-from remove-text-properties nil))
263 (setf len (- end start))
264 (when (/= (interval-pt i) start)
265 (if (not (interval-has-all-properties properties i))
266 (let ((got (- (interval-text-length i) (- start (interval-pt i)))))
267 (when (>= got len)
268 (return-from remove-text-properties nil))
269 (decf len got)
270 (setf i (next-interval i)))
271 (progn
272 (setf unchanged i
273 i (split-interval-right unchanged (- start (interval-pt unchanged))))
274 (copy-properties unchanged i))))
275 (loop
276 (unless i
277 (error "BORK."))
278 (when (>= (interval-text-length i) len)
279 (unless (interval-has-all-properties properties i)
280 (return-from remove-text-properties modified))
281 (when (= (interval-text-length i) len)
282 (remove-properties properties nil i object)
283 (return-from remove-text-properties t))
284 (setf unchanged i
285 i (split-interval-left i len))
286 (copy-properties unchanged i)
287 (remove-properties properties nil i object)
288 (return-from remove-text-properties t))
289 (decf len (interval-text-length i))
290 (setf modified (remove-properties properties nil i object)
291 i (next-interval i)))))
293 (defun next-single-char-property-change (position prop &optional (object (current-buffer)) limit)
294 "/* Return the position of next text property or overlay change for a specific property.
295 Scans characters forward from POSITION till it finds
296 a change in the PROP property, then returns the position of the change.
297 If the optional third argument OBJECT is a buffer (or nil, which means
298 the current buffer), POSITION is a buffer position (integer or marker).
299 If OBJECT is a string, POSITION is a 0-based index into it.
301 The property values are compared with `eql' by default.
302 If the property is constant all the way to the end of OBJECT, return the
303 last valid position in OBJECT.
304 If the optional fourth argument LIMIT is non-nil, don't search
305 past position LIMIT; return LIMIT if nothing is found before LIMIT. */"
306 (if (typep object 'pstring)
307 (progn
308 (setf position (next-single-property-change position prop object limit))
309 (unless position
310 (if (null limit)
311 (setf position (pstring-length object))
312 (setf position limit))))
313 (let ((initial-value (get-char-property position prop object))
314 value)
315 ;; (when (and (typep object 'buffer)
316 ;; (not (eq object (current-buffer))))
317 ;; (
318 (when (null limit)
319 (setf limit (buffer-max object)))
320 (loop
321 (setf position (next-char-property-change position limit object))
322 (when (>= position limit)
323 (return limit))
324 (setf value (get-char-property position prop object))
325 (unless (eq value initial-value)
326 (return position))))))
328 (defun text-properties-at (position &optional (object (current-buffer)))
329 (multiple-value-bind (i position) (validate-interval-range object position position t)
330 (unless (null i)
331 ;; If POSITION is at the end of the interval,
332 ;; it means it's the end of OBJECT.
333 ;; There are no properties at the very end,
334 ;; since no character follows.
335 (unless (= position (+ (interval-text-length i) (interval-pt i)))
336 (interval-plist i)))))
338 (defun get-text-property (position prop object)
339 (getf (text-properties-at position object) prop))
341 (defun get-char-property-and-overlay (position prop object overlay)
342 (get-text-property position prop object))
344 (defun get-char-property (position prop &optional (object (current-buffer)))
345 (get-char-property-and-overlay position prop object 0))
347 (defun previous-single-char-property-change (position prop &optional (object (current-buffer)) limit)
348 (cond ((typep object 'pstring)
349 (setf position (previous-single-property-change position prop object limit))
350 (when (null position)
351 (setf position (or limit
352 (pstring-length object)))))
354 (unless limit
355 (setf limit (buffer-min object)))
356 (if (<= position limit)
357 (setf position limit)
358 (let ((initial-value (get-char-property (1- position) prop object))
359 value)
360 (loop
361 (setf position (previous-char-property-change position limit object))
362 (when (<= position limit)
363 (return limit))
364 (setf value (get-char-property (1- position) prop object))
365 (unless (eq value initial-value)
366 (return position))))))))
368 (defun next-property-change (position &optional object limit)
369 "Return the position of next property change.
370 Scans characters forward from POSITION in OBJECT till it finds
371 a change in some text property, then returns the position of the change.
372 If the optional second argument OBJECT is a buffer (or nil, which means
373 the current buffer), POSITION is a buffer position (integer or marker).
374 If OBJECT is a string, POSITION is a 0-based index into it.
375 Return nil if the property is constant all the way to the end of OBJECT.
376 If the value is non-nil, it is a position greater than POSITION, never equal.
378 If the optional third argument LIMIT is non-nil, don't search
379 past position LIMIT; return LIMIT if nothing is found before LIMIT."
380 (let (i next)
381 (multiple-value-setq (i position) (validate-interval-range object position position nil))
382 (when (eq limit t)
383 (setf next (if (null i)
385 (next-interval i)))
386 (setf position (if (null next)
387 (cond ((typep object 'pstring) (pstring-length object))
388 ((typep object 'buffer) (buffer-max object)))
389 (interval-pt next)))
390 (return-from next-property-change position))
392 (when (null i)
393 (return-from next-property-change limit))
394 (setf next (next-interval i))
395 (loop while (and next
396 (intervals-equal i next)
397 (or (null limit)
398 (< (interval-pt next)
399 limit)))
400 do (setf next (next-interval next)))
401 (when (null next)
402 (return-from next-property-change limit))
403 (when (null limit)
404 (setf limit (cond ((typep object 'pstring) (pstring-length object))
405 ((typep object 'buffer) (buffer-max object)))))
406 (unless (< (interval-pt next) limit)
407 (return-from next-property-change limit))
408 ;; FIXME: This is silly code.
409 (setf position (interval-pt next))
410 position))
412 (defun next-char-property-change (position &optional limit (buffer (current-buffer)))
413 "Return the position of next text property or overlay change.
414 This scans characters forward in the current buffer from POSITION till
415 it finds a change in some text property, or the beginning or end of an
416 overlay, and returns the position of that.
417 If none is found, the function returns (point-max).
419 If the optional third argument LIMIT is non-nil, don't search
420 past position LIMIT; return LIMIT if nothing is found before LIMIT."
421 ;; temp = Fnext_overlay_change (position);
422 (next-property-change position buffer (or limit (buffer-max buffer))))
424 (defun previous-char-property-change (position &optional limit (buffer (current-buffer)))
425 "Return the position of previous text property or overlay change.
426 Scans characters backward in the current buffer from POSITION till it
427 finds a change in some text property, or the beginning or end of an
428 overlay, and returns the position of that.
429 If none is found, the function returns (point-max).
431 If the optional third argument LIMIT is non-nil, don't search
432 past position LIMIT; return LIMIT if nothing is found before LIMIT."
433 (previous-property-change position buffer (or limit (buffer-min buffer))))
435 (defun next-single-property-change (position prop &optional (object (current-buffer)) limit)
436 (let (i next here-val)
437 (multiple-value-setq (i position)
438 (validate-interval-range object position position nil))
439 (when (null i)
440 (return-from next-single-property-change limit))
441 (setf here-val (getf (interval-plist i) prop)
442 next (next-interval i))
443 ;; walk the intervals til we find one with a different plist val
444 ;; for prop.
445 (loop while (and next
446 (eql here-val (getf (interval-plist next) prop))
447 (or (null limit)
448 (< (interval-pt next) limit)))
449 do (setf next (next-interval next)))
450 ;; FIXME: this code should be cleaned.
451 (when (null next)
452 (return-from next-single-property-change limit))
453 (setf limit (or limit
454 (cond ((typep object 'pstring) (pstring-length object))
455 ((typep object 'buffer) (buffer-max object)))))
456 (when (>= (interval-pt next) limit)
457 (return-from next-single-property-change limit))
458 (interval-pt next)))
460 (defun previous-single-property-change (position prop &optional (object (current-buffer)) limit)
461 (let (i previous here-val)
462 (multiple-value-setq (i position) (validate-interval-range object position position nil))
463 (when (and i
464 (= (interval-pt i) position))
465 (setf i (previous-interval i)))
466 (unless i
467 (return-from previous-single-property-change limit))
468 (setf here-val (getf (interval-plist i) prop)
469 previous (previous-interval i))
470 (loop while (and previous
471 (eql here-val (getf (interval-plist previous) prop))
472 (or (null limit)
473 (> (+ (interval-pt previous) (interval-text-length previous))
474 limit)))
475 do (setf previous (previous-interval previous)))
476 ;; FIXME: this code should be cleaned.
477 (when (null previous)
478 (return-from previous-single-property-change limit))
479 (setf limit (or limit
480 (cond ((typep object 'pstring) 0)
481 ((typep object 'buffer) (buffer-min object)))))
482 (when (<= (+ (interval-pt previous) (interval-text-length previous))
483 limit)
484 (return-from previous-single-property-change limit))
485 (+ (interval-pt previous) (interval-text-length previous))))
487 (defun previous-property-change (position &optional (object (current-buffer)) limit)
488 "Return the position of previous property change.
489 Scans characters backwards from POSITION in OBJECT till it finds
490 a change in some text property, then returns the position of the change.
491 If the optional second argument OBJECT is a buffer (or nil, which means
492 the current buffer), POSITION is a buffer position (integer or marker).
493 If OBJECT is a string, POSITION is a 0-based index into it.
494 Return nil if the property is constant all the way to the start of OBJECT.
495 If the value is non-nil, it is a position less than POSITION, never equal.
497 If the optional third argument LIMIT is non-nil, don't search
498 back past position LIMIT; return LIMIT if nothing is found until LIMIT."
499 (let (i previous)
500 (multiple-value-setq (i position) (validate-interval-range object position position nil))
501 (unless i
502 (return-from previous-property-change limit))
503 (when (= (interval-pt i) position)
504 (setf i (previous-interval i)))
505 (setf previous (previous-interval i))
506 (loop while (and previous
507 (intervals-equal previous i)
508 (or (null limit)
509 (> (+ (interval-pt previous)
510 (interval-text-length previous))
511 limit)))
512 do (setf previous (previous-interval previous)))
513 ;; FIXME: this code needs cleaning
514 (when (null previous)
515 (return-from previous-property-change limit))
516 (setf limit (or limit
517 (cond ((typep object 'pstring) 0)
518 ((typep object 'buffer) (buffer-min object)))))
519 (when (<= (+ (interval-pt previous) (interval-text-length previous))
520 limit)
521 (return-from previous-property-change limit))
522 (+ (interval-pt previous) (interval-text-length previous))))
524 (defun text-property-stickiness (prop pos &optional (buffer (current-buffer)))
525 "Return the direction from which the text-property PROP would be
526 inherited by any new text inserted at POS: AFTER if it would be
527 inherited from the char after POS, BEFORE if it would be inherited from
528 the char before POS, and NIL if from neither.
529 BUFFER can be either a buffer or nil (meaning current buffer)."
530 (labels ((tmem (sym set)
531 ;; Test for membership, allowing for t (actually any
532 ;; non-cons) to mean the universal set."
533 (if (consp set)
534 (find sym set)
535 set)))
536 (let ((is-rear-sticky t)
537 (is-front-sticky nil)
538 prev-pos front-sticky)
539 (when (> pos (begv buffer))
540 ;; Consider previous character.
541 (setf prev-pos (1- pos))
542 (let ((rear-non-sticky (get-text-property prev-pos 'rear-nonsticky buffer)))
543 (when (tmem prop rear-non-sticky)
544 ;; PROP is rear-non-sticky
545 (setf is-rear-sticky nil))))
546 ;; Consider following character.
547 (setf front-sticky (get-text-property pos 'front-sticky buffer))
548 (when (or (eq front-sticky t)
549 (and (consp front-sticky)
550 (find prop front-sticky)))
551 ;; PROP is inherited from after
552 (setf is-front-sticky t))
553 ;; return the symbol
554 (cond
555 ;; Simple cases, where the properties are consistent.
556 ((and is-rear-sticky
557 (not is-front-sticky))
558 'before)
559 ((and (not is-rear-sticky)
560 is-front-sticky)
561 'after)
562 ((and (not is-rear-sticky)
563 (not is-front-sticky))
564 nil)
565 ((or (= pos (begv buffer))
566 (null (get-text-property prev-pos prop buffer)))
567 'after)
569 'before)))))
571 (provide :lice-0.1/textprop)