]> code.delx.au - gnu-emacs/blob - src/textprop.c
(Fprevious_single_char_property_change)
[gnu-emacs] / src / textprop.c
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21 #include <config.h>
22 #include "lisp.h"
23 #include "intervals.h"
24 #include "buffer.h"
25 #include "window.h"
26
27 #ifndef NULL
28 #define NULL (void *)0
29 #endif
30
31 /* Test for membership, allowing for t (actually any non-cons) to mean the
32 universal set. */
33
34 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
35 \f
36
37 /* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
40
41 set_properties needs to deal with the interval property cache.
42
43 It is assumed that for any interval plist, a property appears
44 only once on the list. Although some code i.e., remove_properties,
45 handles the more general case, the uniqueness of properties is
46 necessary for the system to remain consistent. This requirement
47 is enforced by the subrs installing properties onto the intervals. */
48
49 \f
50 /* Types of hooks. */
51 Lisp_Object Qmouse_left;
52 Lisp_Object Qmouse_entered;
53 Lisp_Object Qpoint_left;
54 Lisp_Object Qpoint_entered;
55 Lisp_Object Qcategory;
56 Lisp_Object Qlocal_map;
57
58 /* Visual properties text (including strings) may have. */
59 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
60 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
61
62 /* Sticky properties */
63 Lisp_Object Qfront_sticky, Qrear_nonsticky;
64
65 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
66 the o1's cdr. Otherwise, return zero. This is handy for
67 traversing plists. */
68 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
69
70 Lisp_Object Vinhibit_point_motion_hooks;
71 Lisp_Object Vdefault_text_properties;
72 Lisp_Object Vtext_property_default_nonsticky;
73
74 /* verify_interval_modification saves insertion hooks here
75 to be run later by report_interval_modification. */
76 Lisp_Object interval_insert_behind_hooks;
77 Lisp_Object interval_insert_in_front_hooks;
78
79
80 /* Signal a `text-read-only' error. This function makes it easier
81 to capture that error in GDB by putting a breakpoint on it. */
82
83 static void
84 text_read_only ()
85 {
86 Fsignal (Qtext_read_only, Qnil);
87 }
88
89
90 \f
91 /* Extract the interval at the position pointed to by BEGIN from
92 OBJECT, a string or buffer. Additionally, check that the positions
93 pointed to by BEGIN and END are within the bounds of OBJECT, and
94 reverse them if *BEGIN is greater than *END. The objects pointed
95 to by BEGIN and END may be integers or markers; if the latter, they
96 are coerced to integers.
97
98 When OBJECT is a string, we increment *BEGIN and *END
99 to make them origin-one.
100
101 Note that buffer points don't correspond to interval indices.
102 For example, point-max is 1 greater than the index of the last
103 character. This difference is handled in the caller, which uses
104 the validated points to determine a length, and operates on that.
105 Exceptions are Ftext_properties_at, Fnext_property_change, and
106 Fprevious_property_change which call this function with BEGIN == END.
107 Handle this case specially.
108
109 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
110 create an interval tree for OBJECT if one doesn't exist, provided
111 the object actually contains text. In the current design, if there
112 is no text, there can be no text properties. */
113
114 #define soft 0
115 #define hard 1
116
117 INTERVAL
118 validate_interval_range (object, begin, end, force)
119 Lisp_Object object, *begin, *end;
120 int force;
121 {
122 register INTERVAL i;
123 int searchpos;
124
125 CHECK_STRING_OR_BUFFER (object, 0);
126 CHECK_NUMBER_COERCE_MARKER (*begin, 0);
127 CHECK_NUMBER_COERCE_MARKER (*end, 0);
128
129 /* If we are asked for a point, but from a subr which operates
130 on a range, then return nothing. */
131 if (EQ (*begin, *end) && begin != end)
132 return NULL_INTERVAL;
133
134 if (XINT (*begin) > XINT (*end))
135 {
136 Lisp_Object n;
137 n = *begin;
138 *begin = *end;
139 *end = n;
140 }
141
142 if (BUFFERP (object))
143 {
144 register struct buffer *b = XBUFFER (object);
145
146 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
147 && XINT (*end) <= BUF_ZV (b)))
148 args_out_of_range (*begin, *end);
149 i = BUF_INTERVALS (b);
150
151 /* If there's no text, there are no properties. */
152 if (BUF_BEGV (b) == BUF_ZV (b))
153 return NULL_INTERVAL;
154
155 searchpos = XINT (*begin);
156 }
157 else
158 {
159 register struct Lisp_String *s = XSTRING (object);
160
161 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
162 && XINT (*end) <= s->size))
163 args_out_of_range (*begin, *end);
164 XSETFASTINT (*begin, XFASTINT (*begin));
165 if (begin != end)
166 XSETFASTINT (*end, XFASTINT (*end));
167 i = s->intervals;
168
169 if (s->size == 0)
170 return NULL_INTERVAL;
171
172 searchpos = XINT (*begin);
173 }
174
175 if (NULL_INTERVAL_P (i))
176 return (force ? create_root_interval (object) : i);
177
178 return find_interval (i, searchpos);
179 }
180
181 /* Validate LIST as a property list. If LIST is not a list, then
182 make one consisting of (LIST nil). Otherwise, verify that LIST
183 is even numbered and thus suitable as a plist. */
184
185 static Lisp_Object
186 validate_plist (list)
187 Lisp_Object list;
188 {
189 if (NILP (list))
190 return Qnil;
191
192 if (CONSP (list))
193 {
194 register int i;
195 register Lisp_Object tail;
196 for (i = 0, tail = list; !NILP (tail); i++)
197 {
198 tail = Fcdr (tail);
199 QUIT;
200 }
201 if (i & 1)
202 error ("Odd length text property list");
203 return list;
204 }
205
206 return Fcons (list, Fcons (Qnil, Qnil));
207 }
208
209 /* Return nonzero if interval I has all the properties,
210 with the same values, of list PLIST. */
211
212 static int
213 interval_has_all_properties (plist, i)
214 Lisp_Object plist;
215 INTERVAL i;
216 {
217 register Lisp_Object tail1, tail2, sym1;
218 register int found;
219
220 /* Go through each element of PLIST. */
221 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
222 {
223 sym1 = Fcar (tail1);
224 found = 0;
225
226 /* Go through I's plist, looking for sym1 */
227 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
228 if (EQ (sym1, Fcar (tail2)))
229 {
230 /* Found the same property on both lists. If the
231 values are unequal, return zero. */
232 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
233 return 0;
234
235 /* Property has same value on both lists; go to next one. */
236 found = 1;
237 break;
238 }
239
240 if (! found)
241 return 0;
242 }
243
244 return 1;
245 }
246
247 /* Return nonzero if the plist of interval I has any of the
248 properties of PLIST, regardless of their values. */
249
250 static INLINE int
251 interval_has_some_properties (plist, i)
252 Lisp_Object plist;
253 INTERVAL i;
254 {
255 register Lisp_Object tail1, tail2, sym;
256
257 /* Go through each element of PLIST. */
258 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
259 {
260 sym = Fcar (tail1);
261
262 /* Go through i's plist, looking for tail1 */
263 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
264 if (EQ (sym, Fcar (tail2)))
265 return 1;
266 }
267
268 return 0;
269 }
270 \f
271 /* Changing the plists of individual intervals. */
272
273 /* Return the value of PROP in property-list PLIST, or Qunbound if it
274 has none. */
275 static Lisp_Object
276 property_value (plist, prop)
277 Lisp_Object plist, prop;
278 {
279 Lisp_Object value;
280
281 while (PLIST_ELT_P (plist, value))
282 if (EQ (XCAR (plist), prop))
283 return XCAR (value);
284 else
285 plist = XCDR (value);
286
287 return Qunbound;
288 }
289
290 /* Set the properties of INTERVAL to PROPERTIES,
291 and record undo info for the previous values.
292 OBJECT is the string or buffer that INTERVAL belongs to. */
293
294 static void
295 set_properties (properties, interval, object)
296 Lisp_Object properties, object;
297 INTERVAL interval;
298 {
299 Lisp_Object sym, value;
300
301 if (BUFFERP (object))
302 {
303 /* For each property in the old plist which is missing from PROPERTIES,
304 or has a different value in PROPERTIES, make an undo record. */
305 for (sym = interval->plist;
306 PLIST_ELT_P (sym, value);
307 sym = XCDR (value))
308 if (! EQ (property_value (properties, XCAR (sym)),
309 XCAR (value)))
310 {
311 record_property_change (interval->position, LENGTH (interval),
312 XCAR (sym), XCAR (value),
313 object);
314 }
315
316 /* For each new property that has no value at all in the old plist,
317 make an undo record binding it to nil, so it will be removed. */
318 for (sym = properties;
319 PLIST_ELT_P (sym, value);
320 sym = XCDR (value))
321 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
322 {
323 record_property_change (interval->position, LENGTH (interval),
324 XCAR (sym), Qnil,
325 object);
326 }
327 }
328
329 /* Store new properties. */
330 interval->plist = Fcopy_sequence (properties);
331 }
332
333 /* Add the properties of PLIST to the interval I, or set
334 the value of I's property to the value of the property on PLIST
335 if they are different.
336
337 OBJECT should be the string or buffer the interval is in.
338
339 Return nonzero if this changes I (i.e., if any members of PLIST
340 are actually added to I's plist) */
341
342 static int
343 add_properties (plist, i, object)
344 Lisp_Object plist;
345 INTERVAL i;
346 Lisp_Object object;
347 {
348 Lisp_Object tail1, tail2, sym1, val1;
349 register int changed = 0;
350 register int found;
351 struct gcpro gcpro1, gcpro2, gcpro3;
352
353 tail1 = plist;
354 sym1 = Qnil;
355 val1 = Qnil;
356 /* No need to protect OBJECT, because we can GC only in the case
357 where it is a buffer, and live buffers are always protected.
358 I and its plist are also protected, via OBJECT. */
359 GCPRO3 (tail1, sym1, val1);
360
361 /* Go through each element of PLIST. */
362 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
363 {
364 sym1 = Fcar (tail1);
365 val1 = Fcar (Fcdr (tail1));
366 found = 0;
367
368 /* Go through I's plist, looking for sym1 */
369 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
370 if (EQ (sym1, Fcar (tail2)))
371 {
372 /* No need to gcpro, because tail2 protects this
373 and it must be a cons cell (we get an error otherwise). */
374 register Lisp_Object this_cdr;
375
376 this_cdr = Fcdr (tail2);
377 /* Found the property. Now check its value. */
378 found = 1;
379
380 /* The properties have the same value on both lists.
381 Continue to the next property. */
382 if (EQ (val1, Fcar (this_cdr)))
383 break;
384
385 /* Record this change in the buffer, for undo purposes. */
386 if (BUFFERP (object))
387 {
388 record_property_change (i->position, LENGTH (i),
389 sym1, Fcar (this_cdr), object);
390 }
391
392 /* I's property has a different value -- change it */
393 Fsetcar (this_cdr, val1);
394 changed++;
395 break;
396 }
397
398 if (! found)
399 {
400 /* Record this change in the buffer, for undo purposes. */
401 if (BUFFERP (object))
402 {
403 record_property_change (i->position, LENGTH (i),
404 sym1, Qnil, object);
405 }
406 i->plist = Fcons (sym1, Fcons (val1, i->plist));
407 changed++;
408 }
409 }
410
411 UNGCPRO;
412
413 return changed;
414 }
415
416 /* For any members of PLIST which are properties of I, remove them
417 from I's plist.
418 OBJECT is the string or buffer containing I. */
419
420 static int
421 remove_properties (plist, i, object)
422 Lisp_Object plist;
423 INTERVAL i;
424 Lisp_Object object;
425 {
426 register Lisp_Object tail1, tail2, sym, current_plist;
427 register int changed = 0;
428
429 current_plist = i->plist;
430 /* Go through each element of plist. */
431 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
432 {
433 sym = Fcar (tail1);
434
435 /* First, remove the symbol if its at the head of the list */
436 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
437 {
438 if (BUFFERP (object))
439 {
440 record_property_change (i->position, LENGTH (i),
441 sym, Fcar (Fcdr (current_plist)),
442 object);
443 }
444
445 current_plist = Fcdr (Fcdr (current_plist));
446 changed++;
447 }
448
449 /* Go through i's plist, looking for sym */
450 tail2 = current_plist;
451 while (! NILP (tail2))
452 {
453 register Lisp_Object this;
454 this = Fcdr (Fcdr (tail2));
455 if (EQ (sym, Fcar (this)))
456 {
457 if (BUFFERP (object))
458 {
459 record_property_change (i->position, LENGTH (i),
460 sym, Fcar (Fcdr (this)), object);
461 }
462
463 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
464 changed++;
465 }
466 tail2 = this;
467 }
468 }
469
470 if (changed)
471 i->plist = current_plist;
472 return changed;
473 }
474
475 #if 0
476 /* Remove all properties from interval I. Return non-zero
477 if this changes the interval. */
478
479 static INLINE int
480 erase_properties (i)
481 INTERVAL i;
482 {
483 if (NILP (i->plist))
484 return 0;
485
486 i->plist = Qnil;
487 return 1;
488 }
489 #endif
490 \f
491 /* Returns the interval of POSITION in OBJECT.
492 POSITION is BEG-based. */
493
494 INTERVAL
495 interval_of (position, object)
496 int position;
497 Lisp_Object object;
498 {
499 register INTERVAL i;
500 int beg, end;
501
502 if (NILP (object))
503 XSETBUFFER (object, current_buffer);
504 else if (EQ (object, Qt))
505 return NULL_INTERVAL;
506
507 CHECK_STRING_OR_BUFFER (object, 0);
508
509 if (BUFFERP (object))
510 {
511 register struct buffer *b = XBUFFER (object);
512
513 beg = BUF_BEGV (b);
514 end = BUF_ZV (b);
515 i = BUF_INTERVALS (b);
516 }
517 else
518 {
519 register struct Lisp_String *s = XSTRING (object);
520
521 beg = 0;
522 end = s->size;
523 i = s->intervals;
524 }
525
526 if (!(beg <= position && position <= end))
527 args_out_of_range (make_number (position), make_number (position));
528 if (beg == end || NULL_INTERVAL_P (i))
529 return NULL_INTERVAL;
530
531 return find_interval (i, position);
532 }
533 \f
534 DEFUN ("text-properties-at", Ftext_properties_at,
535 Stext_properties_at, 1, 2, 0,
536 "Return the list of properties of the character at POSITION in OBJECT.\n\
537 OBJECT is the string or buffer to look for the properties in;\n\
538 nil means the current buffer.\n\
539 If POSITION is at the end of OBJECT, the value is nil.")
540 (position, object)
541 Lisp_Object position, object;
542 {
543 register INTERVAL i;
544
545 if (NILP (object))
546 XSETBUFFER (object, current_buffer);
547
548 i = validate_interval_range (object, &position, &position, soft);
549 if (NULL_INTERVAL_P (i))
550 return Qnil;
551 /* If POSITION is at the end of the interval,
552 it means it's the end of OBJECT.
553 There are no properties at the very end,
554 since no character follows. */
555 if (XINT (position) == LENGTH (i) + i->position)
556 return Qnil;
557
558 return i->plist;
559 }
560
561 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
562 "Return the value of POSITION's property PROP, in OBJECT.\n\
563 OBJECT is optional and defaults to the current buffer.\n\
564 If POSITION is at the end of OBJECT, the value is nil.")
565 (position, prop, object)
566 Lisp_Object position, object;
567 Lisp_Object prop;
568 {
569 return textget (Ftext_properties_at (position, object), prop);
570 }
571
572 /* Return the value of POSITION's property PROP, in OBJECT.
573 OBJECT is optional and defaults to the current buffer.
574 If OVERLAY is non-0, then in the case that the returned property is from
575 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
576 returned in *OVERLAY.
577 If POSITION is at the end of OBJECT, the value is nil.
578 If OBJECT is a buffer, then overlay properties are considered as well as
579 text properties.
580 If OBJECT is a window, then that window's buffer is used, but
581 window-specific overlays are considered only if they are associated
582 with OBJECT. */
583 Lisp_Object
584 get_char_property_and_overlay (position, prop, object, overlay)
585 Lisp_Object position, object;
586 register Lisp_Object prop;
587 Lisp_Object *overlay;
588 {
589 struct window *w = 0;
590
591 CHECK_NUMBER_COERCE_MARKER (position, 0);
592
593 if (NILP (object))
594 XSETBUFFER (object, current_buffer);
595
596 if (WINDOWP (object))
597 {
598 w = XWINDOW (object);
599 object = w->buffer;
600 }
601 if (BUFFERP (object))
602 {
603 int posn = XINT (position);
604 int noverlays;
605 Lisp_Object *overlay_vec, tem;
606 int next_overlay;
607 int len;
608 struct buffer *obuf = current_buffer;
609
610 set_buffer_temp (XBUFFER (object));
611
612 /* First try with room for 40 overlays. */
613 len = 40;
614 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
615
616 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
617 &next_overlay, NULL, 0);
618
619 /* If there are more than 40,
620 make enough space for all, and try again. */
621 if (noverlays > len)
622 {
623 len = noverlays;
624 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
625 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
626 &next_overlay, NULL, 0);
627 }
628 noverlays = sort_overlays (overlay_vec, noverlays, w);
629
630 set_buffer_temp (obuf);
631
632 /* Now check the overlays in order of decreasing priority. */
633 while (--noverlays >= 0)
634 {
635 tem = Foverlay_get (overlay_vec[noverlays], prop);
636 if (!NILP (tem))
637 {
638 if (overlay)
639 /* Return the overlay we got the property from. */
640 *overlay = overlay_vec[noverlays];
641 return tem;
642 }
643 }
644 }
645
646 if (overlay)
647 /* Indicate that the return value is not from an overlay. */
648 *overlay = Qnil;
649
650 /* Not a buffer, or no appropriate overlay, so fall through to the
651 simpler case. */
652 return Fget_text_property (position, prop, object);
653 }
654
655 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
656 "Return the value of POSITION's property PROP, in OBJECT.\n\
657 OBJECT is optional and defaults to the current buffer.\n\
658 If POSITION is at the end of OBJECT, the value is nil.\n\
659 If OBJECT is a buffer, then overlay properties are considered as well as\n\
660 text properties.\n\
661 If OBJECT is a window, then that window's buffer is used, but window-specific\n\
662 overlays are considered only if they are associated with OBJECT.")
663 (position, prop, object)
664 Lisp_Object position, object;
665 register Lisp_Object prop;
666 {
667 return get_char_property_and_overlay (position, prop, object, 0);
668 }
669 \f
670 DEFUN ("next-char-property-change", Fnext_char_property_change,
671 Snext_char_property_change, 1, 2, 0,
672 "Return the position of next text property or overlay change.\n\
673 This scans characters forward from POSITION in OBJECT till it finds\n\
674 a change in some text property, or the beginning or end of an overlay,\n\
675 and returns the position of that.\n\
676 If none is found, the function returns (point-max).\n\
677 \n\
678 If the optional third argument LIMIT is non-nil, don't search\n\
679 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
680 (position, limit)
681 Lisp_Object position, limit;
682 {
683 Lisp_Object temp;
684
685 temp = Fnext_overlay_change (position);
686 if (! NILP (limit))
687 {
688 CHECK_NUMBER (limit, 2);
689 if (XINT (limit) < XINT (temp))
690 temp = limit;
691 }
692 return Fnext_property_change (position, Qnil, temp);
693 }
694
695 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
696 Sprevious_char_property_change, 1, 2, 0,
697 "Return the position of previous text property or overlay change.\n\
698 Scans characters backward from POSITION in OBJECT till it finds\n\
699 a change in some text property, or the beginning or end of an overlay,\n\
700 and returns the position of that.\n\
701 If none is found, the function returns (point-max).\n\
702 \n\
703 If the optional third argument LIMIT is non-nil, don't search\n\
704 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
705 (position, limit)
706 Lisp_Object position, limit;
707 {
708 Lisp_Object temp;
709
710 temp = Fprevious_overlay_change (position);
711 if (! NILP (limit))
712 {
713 CHECK_NUMBER (limit, 2);
714 if (XINT (limit) > XINT (temp))
715 temp = limit;
716 }
717 return Fprevious_property_change (position, Qnil, temp);
718 }
719
720
721 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
722 Snext_single_char_property_change, 2, 4, 0,
723 "Return the position of next text property or overlay change for a specific property.\n\
724 Scans characters forward from POSITION till it finds\n\
725 a change in the PROP property, then returns the position of the change.\n\
726 The optional third argument OBJECT is the string or buffer to scan.\n\
727 The property values are compared with `eq'.\n\
728 If the property is constant all the way to the end of OBJECT, return the\n\
729 last valid position in OBJECT.\n\
730 If the optional fourth argument LIMIT is non-nil, don't search\n\
731 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
732 (position, prop, object, limit)
733 Lisp_Object prop, position, object, limit;
734 {
735 if (STRINGP (object))
736 {
737 position = Fnext_single_property_change (position, prop, object, limit);
738 if (NILP (position))
739 {
740 if (NILP (limit))
741 position = make_number (XSTRING (object)->size);
742 else
743 position = limit;
744 }
745 }
746 else
747 {
748 Lisp_Object initial_value, value;
749 int count = specpdl_ptr - specpdl;
750
751 if (! NILP (object))
752 CHECK_BUFFER (object, 0);
753
754 if (BUFFERP (object) && current_buffer != XBUFFER (object))
755 {
756 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
757 Fset_buffer (object);
758 }
759
760 initial_value = Fget_char_property (position, prop, object);
761
762 if (NILP (limit))
763 XSETFASTINT (limit, BUF_ZV (current_buffer));
764 else
765 CHECK_NUMBER_COERCE_MARKER (limit, 0);
766
767 for (;;)
768 {
769 position = Fnext_char_property_change (position, limit);
770 if (XFASTINT (position) >= XFASTINT (limit)) {
771 position = limit;
772 break;
773 }
774
775 value = Fget_char_property (position, prop, object);
776 if (!EQ (value, initial_value))
777 break;
778 }
779
780 unbind_to (count, Qnil);
781 }
782
783 return position;
784 }
785
786 DEFUN ("previous-single-char-property-change",
787 Fprevious_single_char_property_change,
788 Sprevious_single_char_property_change, 2, 4, 0,
789 "Return the position of previous text property or overlay change for a specific property.\n\
790 Scans characters backward from POSITION till it finds\n\
791 a change in the PROP property, then returns the position of the change.\n\
792 The optional third argument OBJECT is the string or buffer to scan.\n\
793 The property values are compared with `eq'.\n\
794 If the property is constant all the way to the start of OBJECT, return the\n\
795 first valid position in OBJECT.\n\
796 If the optional fourth argument LIMIT is non-nil, don't search\n\
797 back past position LIMIT; return LIMIT if nothing is found before LIMIT.")
798 (position, prop, object, limit)
799 Lisp_Object prop, position, object, limit;
800 {
801 if (STRINGP (object))
802 {
803 position = Fprevious_single_property_change (position, prop, object, limit);
804 if (NILP (position))
805 {
806 if (NILP (limit))
807 position = make_number (XSTRING (object)->size);
808 else
809 position = limit;
810 }
811 }
812 else
813 {
814 int count = specpdl_ptr - specpdl;
815
816 if (! NILP (object))
817 CHECK_BUFFER (object, 0);
818
819 if (BUFFERP (object) && current_buffer != XBUFFER (object))
820 {
821 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
822 Fset_buffer (object);
823 }
824
825 if (NILP (limit))
826 XSETFASTINT (limit, BUF_BEGV (current_buffer));
827 else
828 CHECK_NUMBER_COERCE_MARKER (limit, 0);
829
830 if (XFASTINT (position) <= XFASTINT (limit))
831 position = limit;
832 else
833 {
834 Lisp_Object initial_value =
835 Fget_char_property (make_number (XFASTINT (position) - 1),
836 prop, object);
837
838 for (;;)
839 {
840 position = Fprevious_char_property_change (position, limit);
841
842 if (XFASTINT (position) <= XFASTINT (limit))
843 {
844 position = limit;
845 break;
846 }
847 else
848 {
849 Lisp_Object value =
850 Fget_char_property (make_number (XFASTINT (position) - 1),
851 prop, object);
852
853 if (!EQ (value, initial_value))
854 break;
855 }
856 }
857 }
858
859 unbind_to (count, Qnil);
860 }
861
862 return position;
863 }
864 \f
865 DEFUN ("next-property-change", Fnext_property_change,
866 Snext_property_change, 1, 3, 0,
867 "Return the position of next property change.\n\
868 Scans characters forward from POSITION in OBJECT till it finds\n\
869 a change in some text property, then returns the position of the change.\n\
870 The optional second argument OBJECT is the string or buffer to scan.\n\
871 Return nil if the property is constant all the way to the end of OBJECT.\n\
872 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
873 If the optional third argument LIMIT is non-nil, don't search\n\
874 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
875 (position, object, limit)
876 Lisp_Object position, object, limit;
877 {
878 register INTERVAL i, next;
879
880 if (NILP (object))
881 XSETBUFFER (object, current_buffer);
882
883 if (! NILP (limit) && ! EQ (limit, Qt))
884 CHECK_NUMBER_COERCE_MARKER (limit, 0);
885
886 i = validate_interval_range (object, &position, &position, soft);
887
888 /* If LIMIT is t, return start of next interval--don't
889 bother checking further intervals. */
890 if (EQ (limit, Qt))
891 {
892 if (NULL_INTERVAL_P (i))
893 next = i;
894 else
895 next = next_interval (i);
896
897 if (NULL_INTERVAL_P (next))
898 XSETFASTINT (position, (STRINGP (object)
899 ? XSTRING (object)->size
900 : BUF_ZV (XBUFFER (object))));
901 else
902 XSETFASTINT (position, next->position);
903 return position;
904 }
905
906 if (NULL_INTERVAL_P (i))
907 return limit;
908
909 next = next_interval (i);
910
911 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next)
912 && (NILP (limit) || next->position < XFASTINT (limit)))
913 next = next_interval (next);
914
915 if (NULL_INTERVAL_P (next))
916 return limit;
917 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
918 return limit;
919
920 XSETFASTINT (position, next->position);
921 return position;
922 }
923
924 /* Return 1 if there's a change in some property between BEG and END. */
925
926 int
927 property_change_between_p (beg, end)
928 int beg, end;
929 {
930 register INTERVAL i, next;
931 Lisp_Object object, pos;
932
933 XSETBUFFER (object, current_buffer);
934 XSETFASTINT (pos, beg);
935
936 i = validate_interval_range (object, &pos, &pos, soft);
937 if (NULL_INTERVAL_P (i))
938 return 0;
939
940 next = next_interval (i);
941 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
942 {
943 next = next_interval (next);
944 if (NULL_INTERVAL_P (next))
945 return 0;
946 if (next->position >= end)
947 return 0;
948 }
949
950 if (NULL_INTERVAL_P (next))
951 return 0;
952
953 return 1;
954 }
955
956 DEFUN ("next-single-property-change", Fnext_single_property_change,
957 Snext_single_property_change, 2, 4, 0,
958 "Return the position of next property change for a specific property.\n\
959 Scans characters forward from POSITION till it finds\n\
960 a change in the PROP property, then returns the position of the change.\n\
961 The optional third argument OBJECT is the string or buffer to scan.\n\
962 The property values are compared with `eq'.\n\
963 Return nil if the property is constant all the way to the end of OBJECT.\n\
964 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
965 If the optional fourth argument LIMIT is non-nil, don't search\n\
966 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
967 (position, prop, object, limit)
968 Lisp_Object position, prop, object, limit;
969 {
970 register INTERVAL i, next;
971 register Lisp_Object here_val;
972
973 if (NILP (object))
974 XSETBUFFER (object, current_buffer);
975
976 if (!NILP (limit))
977 CHECK_NUMBER_COERCE_MARKER (limit, 0);
978
979 i = validate_interval_range (object, &position, &position, soft);
980 if (NULL_INTERVAL_P (i))
981 return limit;
982
983 here_val = textget (i->plist, prop);
984 next = next_interval (i);
985 while (! NULL_INTERVAL_P (next)
986 && EQ (here_val, textget (next->plist, prop))
987 && (NILP (limit) || next->position < XFASTINT (limit)))
988 next = next_interval (next);
989
990 if (NULL_INTERVAL_P (next))
991 return limit;
992 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
993 return limit;
994
995 return make_number (next->position);
996 }
997
998 DEFUN ("previous-property-change", Fprevious_property_change,
999 Sprevious_property_change, 1, 3, 0,
1000 "Return the position of previous property change.\n\
1001 Scans characters backwards from POSITION in OBJECT till it finds\n\
1002 a change in some text property, then returns the position of the change.\n\
1003 The optional second argument OBJECT is the string or buffer to scan.\n\
1004 Return nil if the property is constant all the way to the start of OBJECT.\n\
1005 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
1006 If the optional third argument LIMIT is non-nil, don't search\n\
1007 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1008 (position, object, limit)
1009 Lisp_Object position, object, limit;
1010 {
1011 register INTERVAL i, previous;
1012
1013 if (NILP (object))
1014 XSETBUFFER (object, current_buffer);
1015
1016 if (!NILP (limit))
1017 CHECK_NUMBER_COERCE_MARKER (limit, 0);
1018
1019 i = validate_interval_range (object, &position, &position, soft);
1020 if (NULL_INTERVAL_P (i))
1021 return limit;
1022
1023 /* Start with the interval containing the char before point. */
1024 if (i->position == XFASTINT (position))
1025 i = previous_interval (i);
1026
1027 previous = previous_interval (i);
1028 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1029 && (NILP (limit)
1030 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1031 previous = previous_interval (previous);
1032 if (NULL_INTERVAL_P (previous))
1033 return limit;
1034 if (!NILP (limit)
1035 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
1036 return limit;
1037
1038 return make_number (previous->position + LENGTH (previous));
1039 }
1040
1041 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1042 Sprevious_single_property_change, 2, 4, 0,
1043 "Return the position of previous property change for a specific property.\n\
1044 Scans characters backward from POSITION till it finds\n\
1045 a change in the PROP property, then returns the position of the change.\n\
1046 The optional third argument OBJECT is the string or buffer to scan.\n\
1047 The property values are compared with `eq'.\n\
1048 Return nil if the property is constant all the way to the start of OBJECT.\n\
1049 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
1050 If the optional fourth argument LIMIT is non-nil, don't search\n\
1051 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1052 (position, prop, object, limit)
1053 Lisp_Object position, prop, object, limit;
1054 {
1055 register INTERVAL i, previous;
1056 register Lisp_Object here_val;
1057
1058 if (NILP (object))
1059 XSETBUFFER (object, current_buffer);
1060
1061 if (!NILP (limit))
1062 CHECK_NUMBER_COERCE_MARKER (limit, 0);
1063
1064 i = validate_interval_range (object, &position, &position, soft);
1065
1066 /* Start with the interval containing the char before point. */
1067 if (! NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1068 i = previous_interval (i);
1069
1070 if (NULL_INTERVAL_P (i))
1071 return limit;
1072
1073 here_val = textget (i->plist, prop);
1074 previous = previous_interval (i);
1075 while (! NULL_INTERVAL_P (previous)
1076 && EQ (here_val, textget (previous->plist, prop))
1077 && (NILP (limit)
1078 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1079 previous = previous_interval (previous);
1080 if (NULL_INTERVAL_P (previous))
1081 return limit;
1082 if (!NILP (limit)
1083 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
1084 return limit;
1085
1086 return make_number (previous->position + LENGTH (previous));
1087 }
1088 \f
1089 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1090
1091 DEFUN ("add-text-properties", Fadd_text_properties,
1092 Sadd_text_properties, 3, 4, 0,
1093 "Add properties to the text from START to END.\n\
1094 The third argument PROPERTIES is a property list\n\
1095 specifying the property values to add.\n\
1096 The optional fourth argument, OBJECT,\n\
1097 is the string or buffer containing the text.\n\
1098 Return t if any property value actually changed, nil otherwise.")
1099 (start, end, properties, object)
1100 Lisp_Object start, end, properties, object;
1101 {
1102 register INTERVAL i, unchanged;
1103 register int s, len, modified = 0;
1104 struct gcpro gcpro1;
1105
1106 properties = validate_plist (properties);
1107 if (NILP (properties))
1108 return Qnil;
1109
1110 if (NILP (object))
1111 XSETBUFFER (object, current_buffer);
1112
1113 i = validate_interval_range (object, &start, &end, hard);
1114 if (NULL_INTERVAL_P (i))
1115 return Qnil;
1116
1117 s = XINT (start);
1118 len = XINT (end) - s;
1119
1120 /* No need to protect OBJECT, because we GC only if it's a buffer,
1121 and live buffers are always protected. */
1122 GCPRO1 (properties);
1123
1124 /* If we're not starting on an interval boundary, we have to
1125 split this interval. */
1126 if (i->position != s)
1127 {
1128 /* If this interval already has the properties, we can
1129 skip it. */
1130 if (interval_has_all_properties (properties, i))
1131 {
1132 int got = (LENGTH (i) - (s - i->position));
1133 if (got >= len)
1134 RETURN_UNGCPRO (Qnil);
1135 len -= got;
1136 i = next_interval (i);
1137 }
1138 else
1139 {
1140 unchanged = i;
1141 i = split_interval_right (unchanged, s - unchanged->position);
1142 copy_properties (unchanged, i);
1143 }
1144 }
1145
1146 if (BUFFERP (object))
1147 modify_region (XBUFFER (object), XINT (start), XINT (end));
1148
1149 /* We are at the beginning of interval I, with LEN chars to scan. */
1150 for (;;)
1151 {
1152 if (i == 0)
1153 abort ();
1154
1155 if (LENGTH (i) >= len)
1156 {
1157 /* We can UNGCPRO safely here, because there will be just
1158 one more chance to gc, in the next call to add_properties,
1159 and after that we will not need PROPERTIES or OBJECT again. */
1160 UNGCPRO;
1161
1162 if (interval_has_all_properties (properties, i))
1163 {
1164 if (BUFFERP (object))
1165 signal_after_change (XINT (start), XINT (end) - XINT (start),
1166 XINT (end) - XINT (start));
1167
1168 return modified ? Qt : Qnil;
1169 }
1170
1171 if (LENGTH (i) == len)
1172 {
1173 add_properties (properties, i, object);
1174 if (BUFFERP (object))
1175 signal_after_change (XINT (start), XINT (end) - XINT (start),
1176 XINT (end) - XINT (start));
1177 return Qt;
1178 }
1179
1180 /* i doesn't have the properties, and goes past the change limit */
1181 unchanged = i;
1182 i = split_interval_left (unchanged, len);
1183 copy_properties (unchanged, i);
1184 add_properties (properties, i, object);
1185 if (BUFFERP (object))
1186 signal_after_change (XINT (start), XINT (end) - XINT (start),
1187 XINT (end) - XINT (start));
1188 return Qt;
1189 }
1190
1191 len -= LENGTH (i);
1192 modified += add_properties (properties, i, object);
1193 i = next_interval (i);
1194 }
1195 }
1196
1197 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1198
1199 DEFUN ("put-text-property", Fput_text_property,
1200 Sput_text_property, 4, 5, 0,
1201 "Set one property of the text from START to END.\n\
1202 The third and fourth arguments PROPERTY and VALUE\n\
1203 specify the property to add.\n\
1204 The optional fifth argument, OBJECT,\n\
1205 is the string or buffer containing the text.")
1206 (start, end, property, value, object)
1207 Lisp_Object start, end, property, value, object;
1208 {
1209 Fadd_text_properties (start, end,
1210 Fcons (property, Fcons (value, Qnil)),
1211 object);
1212 return Qnil;
1213 }
1214
1215 DEFUN ("set-text-properties", Fset_text_properties,
1216 Sset_text_properties, 3, 4, 0,
1217 "Completely replace properties of text from START to END.\n\
1218 The third argument PROPERTIES is the new property list.\n\
1219 The optional fourth argument, OBJECT,\n\
1220 is the string or buffer containing the text.\n\
1221 If OBJECT is omitted or nil, it defaults to the current buffer.\n\
1222 If PROPERTIES is nil, the effect is to remove all properties from\n\
1223 the designated part of OBJECT.")
1224 (start, end, properties, object)
1225 Lisp_Object start, end, properties, object;
1226 {
1227 return set_text_properties (start, end, properties, object, Qt);
1228 }
1229
1230
1231 /* Replace properties of text from START to END with new list of
1232 properties PROPERTIES. OBJECT is the buffer or string containing
1233 the text. OBJECT nil means use the current buffer.
1234 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1235 is non-nil if properties were replaced; it is nil if there weren't
1236 any properties to replace. */
1237
1238 Lisp_Object
1239 set_text_properties (start, end, properties, object, signal_after_change_p)
1240 Lisp_Object start, end, properties, object, signal_after_change_p;
1241 {
1242 register INTERVAL i, unchanged;
1243 register INTERVAL prev_changed = NULL_INTERVAL;
1244 register int s, len;
1245 Lisp_Object ostart, oend;
1246
1247 ostart = start;
1248 oend = end;
1249
1250 properties = validate_plist (properties);
1251
1252 if (NILP (object))
1253 XSETBUFFER (object, current_buffer);
1254
1255 /* If we want no properties for a whole string,
1256 get rid of its intervals. */
1257 if (NILP (properties) && STRINGP (object)
1258 && XFASTINT (start) == 0
1259 && XFASTINT (end) == XSTRING (object)->size)
1260 {
1261 if (! XSTRING (object)->intervals)
1262 return Qt;
1263
1264 XSTRING (object)->intervals = 0;
1265 return Qt;
1266 }
1267
1268 i = validate_interval_range (object, &start, &end, soft);
1269
1270 if (NULL_INTERVAL_P (i))
1271 {
1272 /* If buffer has no properties, and we want none, return now. */
1273 if (NILP (properties))
1274 return Qnil;
1275
1276 /* Restore the original START and END values
1277 because validate_interval_range increments them for strings. */
1278 start = ostart;
1279 end = oend;
1280
1281 i = validate_interval_range (object, &start, &end, hard);
1282 /* This can return if start == end. */
1283 if (NULL_INTERVAL_P (i))
1284 return Qnil;
1285 }
1286
1287 s = XINT (start);
1288 len = XINT (end) - s;
1289
1290 if (BUFFERP (object))
1291 modify_region (XBUFFER (object), XINT (start), XINT (end));
1292
1293 if (i->position != s)
1294 {
1295 unchanged = i;
1296 i = split_interval_right (unchanged, s - unchanged->position);
1297
1298 if (LENGTH (i) > len)
1299 {
1300 copy_properties (unchanged, i);
1301 i = split_interval_left (i, len);
1302 set_properties (properties, i, object);
1303 if (BUFFERP (object) && !NILP (signal_after_change_p))
1304 signal_after_change (XINT (start), XINT (end) - XINT (start),
1305 XINT (end) - XINT (start));
1306
1307 return Qt;
1308 }
1309
1310 set_properties (properties, i, object);
1311
1312 if (LENGTH (i) == len)
1313 {
1314 if (BUFFERP (object) && !NILP (signal_after_change_p))
1315 signal_after_change (XINT (start), XINT (end) - XINT (start),
1316 XINT (end) - XINT (start));
1317
1318 return Qt;
1319 }
1320
1321 prev_changed = i;
1322 len -= LENGTH (i);
1323 i = next_interval (i);
1324 }
1325
1326 /* We are starting at the beginning of an interval, I */
1327 while (len > 0)
1328 {
1329 if (i == 0)
1330 abort ();
1331
1332 if (LENGTH (i) >= len)
1333 {
1334 if (LENGTH (i) > len)
1335 i = split_interval_left (i, len);
1336
1337 /* We have to call set_properties even if we are going to
1338 merge the intervals, so as to make the undo records
1339 and cause redisplay to happen. */
1340 set_properties (properties, i, object);
1341 if (!NULL_INTERVAL_P (prev_changed))
1342 merge_interval_left (i);
1343 if (BUFFERP (object) && !NILP (signal_after_change_p))
1344 signal_after_change (XINT (start), XINT (end) - XINT (start),
1345 XINT (end) - XINT (start));
1346 return Qt;
1347 }
1348
1349 len -= LENGTH (i);
1350
1351 /* We have to call set_properties even if we are going to
1352 merge the intervals, so as to make the undo records
1353 and cause redisplay to happen. */
1354 set_properties (properties, i, object);
1355 if (NULL_INTERVAL_P (prev_changed))
1356 prev_changed = i;
1357 else
1358 prev_changed = i = merge_interval_left (i);
1359
1360 i = next_interval (i);
1361 }
1362
1363 if (BUFFERP (object) && !NILP (signal_after_change_p))
1364 signal_after_change (XINT (start), XINT (end) - XINT (start),
1365 XINT (end) - XINT (start));
1366 return Qt;
1367 }
1368
1369 DEFUN ("remove-text-properties", Fremove_text_properties,
1370 Sremove_text_properties, 3, 4, 0,
1371 "Remove some properties from text from START to END.\n\
1372 The third argument PROPERTIES is a property list\n\
1373 whose property names specify the properties to remove.\n\
1374 \(The values stored in PROPERTIES are ignored.)\n\
1375 The optional fourth argument, OBJECT,\n\
1376 is the string or buffer containing the text.\n\
1377 Return t if any property was actually removed, nil otherwise.")
1378 (start, end, properties, object)
1379 Lisp_Object start, end, properties, object;
1380 {
1381 register INTERVAL i, unchanged;
1382 register int s, len, modified = 0;
1383
1384 if (NILP (object))
1385 XSETBUFFER (object, current_buffer);
1386
1387 i = validate_interval_range (object, &start, &end, soft);
1388 if (NULL_INTERVAL_P (i))
1389 return Qnil;
1390
1391 s = XINT (start);
1392 len = XINT (end) - s;
1393
1394 if (i->position != s)
1395 {
1396 /* No properties on this first interval -- return if
1397 it covers the entire region. */
1398 if (! interval_has_some_properties (properties, i))
1399 {
1400 int got = (LENGTH (i) - (s - i->position));
1401 if (got >= len)
1402 return Qnil;
1403 len -= got;
1404 i = next_interval (i);
1405 }
1406 /* Split away the beginning of this interval; what we don't
1407 want to modify. */
1408 else
1409 {
1410 unchanged = i;
1411 i = split_interval_right (unchanged, s - unchanged->position);
1412 copy_properties (unchanged, i);
1413 }
1414 }
1415
1416 if (BUFFERP (object))
1417 modify_region (XBUFFER (object), XINT (start), XINT (end));
1418
1419 /* We are at the beginning of an interval, with len to scan */
1420 for (;;)
1421 {
1422 if (i == 0)
1423 abort ();
1424
1425 if (LENGTH (i) >= len)
1426 {
1427 if (! interval_has_some_properties (properties, i))
1428 return modified ? Qt : Qnil;
1429
1430 if (LENGTH (i) == len)
1431 {
1432 remove_properties (properties, i, object);
1433 if (BUFFERP (object))
1434 signal_after_change (XINT (start), XINT (end) - XINT (start),
1435 XINT (end) - XINT (start));
1436 return Qt;
1437 }
1438
1439 /* i has the properties, and goes past the change limit */
1440 unchanged = i;
1441 i = split_interval_left (i, len);
1442 copy_properties (unchanged, i);
1443 remove_properties (properties, i, object);
1444 if (BUFFERP (object))
1445 signal_after_change (XINT (start), XINT (end) - XINT (start),
1446 XINT (end) - XINT (start));
1447 return Qt;
1448 }
1449
1450 len -= LENGTH (i);
1451 modified += remove_properties (properties, i, object);
1452 i = next_interval (i);
1453 }
1454 }
1455 \f
1456 DEFUN ("text-property-any", Ftext_property_any,
1457 Stext_property_any, 4, 5, 0,
1458 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1459 If so, return the position of the first character whose property PROPERTY\n\
1460 is `eq' to VALUE. Otherwise return nil.\n\
1461 The optional fifth argument, OBJECT, is the string or buffer\n\
1462 containing the text.")
1463 (start, end, property, value, object)
1464 Lisp_Object start, end, property, value, object;
1465 {
1466 register INTERVAL i;
1467 register int e, pos;
1468
1469 if (NILP (object))
1470 XSETBUFFER (object, current_buffer);
1471 i = validate_interval_range (object, &start, &end, soft);
1472 if (NULL_INTERVAL_P (i))
1473 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1474 e = XINT (end);
1475
1476 while (! NULL_INTERVAL_P (i))
1477 {
1478 if (i->position >= e)
1479 break;
1480 if (EQ (textget (i->plist, property), value))
1481 {
1482 pos = i->position;
1483 if (pos < XINT (start))
1484 pos = XINT (start);
1485 return make_number (pos);
1486 }
1487 i = next_interval (i);
1488 }
1489 return Qnil;
1490 }
1491
1492 DEFUN ("text-property-not-all", Ftext_property_not_all,
1493 Stext_property_not_all, 4, 5, 0,
1494 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1495 If so, return the position of the first character whose property PROPERTY\n\
1496 is not `eq' to VALUE. Otherwise, return nil.\n\
1497 The optional fifth argument, OBJECT, is the string or buffer\n\
1498 containing the text.")
1499 (start, end, property, value, object)
1500 Lisp_Object start, end, property, value, object;
1501 {
1502 register INTERVAL i;
1503 register int s, e;
1504
1505 if (NILP (object))
1506 XSETBUFFER (object, current_buffer);
1507 i = validate_interval_range (object, &start, &end, soft);
1508 if (NULL_INTERVAL_P (i))
1509 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1510 s = XINT (start);
1511 e = XINT (end);
1512
1513 while (! NULL_INTERVAL_P (i))
1514 {
1515 if (i->position >= e)
1516 break;
1517 if (! EQ (textget (i->plist, property), value))
1518 {
1519 if (i->position > s)
1520 s = i->position;
1521 return make_number (s);
1522 }
1523 i = next_interval (i);
1524 }
1525 return Qnil;
1526 }
1527 \f
1528 /* I don't think this is the right interface to export; how often do you
1529 want to do something like this, other than when you're copying objects
1530 around?
1531
1532 I think it would be better to have a pair of functions, one which
1533 returns the text properties of a region as a list of ranges and
1534 plists, and another which applies such a list to another object. */
1535
1536 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1537 SRC and DEST may each refer to strings or buffers.
1538 Optional sixth argument PROP causes only that property to be copied.
1539 Properties are copied to DEST as if by `add-text-properties'.
1540 Return t if any property value actually changed, nil otherwise. */
1541
1542 /* Note this can GC when DEST is a buffer. */
1543
1544 Lisp_Object
1545 copy_text_properties (start, end, src, pos, dest, prop)
1546 Lisp_Object start, end, src, pos, dest, prop;
1547 {
1548 INTERVAL i;
1549 Lisp_Object res;
1550 Lisp_Object stuff;
1551 Lisp_Object plist;
1552 int s, e, e2, p, len, modified = 0;
1553 struct gcpro gcpro1, gcpro2;
1554
1555 i = validate_interval_range (src, &start, &end, soft);
1556 if (NULL_INTERVAL_P (i))
1557 return Qnil;
1558
1559 CHECK_NUMBER_COERCE_MARKER (pos, 0);
1560 {
1561 Lisp_Object dest_start, dest_end;
1562
1563 dest_start = pos;
1564 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1565 /* Apply this to a copy of pos; it will try to increment its arguments,
1566 which we don't want. */
1567 validate_interval_range (dest, &dest_start, &dest_end, soft);
1568 }
1569
1570 s = XINT (start);
1571 e = XINT (end);
1572 p = XINT (pos);
1573
1574 stuff = Qnil;
1575
1576 while (s < e)
1577 {
1578 e2 = i->position + LENGTH (i);
1579 if (e2 > e)
1580 e2 = e;
1581 len = e2 - s;
1582
1583 plist = i->plist;
1584 if (! NILP (prop))
1585 while (! NILP (plist))
1586 {
1587 if (EQ (Fcar (plist), prop))
1588 {
1589 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1590 break;
1591 }
1592 plist = Fcdr (Fcdr (plist));
1593 }
1594 if (! NILP (plist))
1595 {
1596 /* Must defer modifications to the interval tree in case src
1597 and dest refer to the same string or buffer. */
1598 stuff = Fcons (Fcons (make_number (p),
1599 Fcons (make_number (p + len),
1600 Fcons (plist, Qnil))),
1601 stuff);
1602 }
1603
1604 i = next_interval (i);
1605 if (NULL_INTERVAL_P (i))
1606 break;
1607
1608 p += len;
1609 s = i->position;
1610 }
1611
1612 GCPRO2 (stuff, dest);
1613
1614 while (! NILP (stuff))
1615 {
1616 res = Fcar (stuff);
1617 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1618 Fcar (Fcdr (Fcdr (res))), dest);
1619 if (! NILP (res))
1620 modified++;
1621 stuff = Fcdr (stuff);
1622 }
1623
1624 UNGCPRO;
1625
1626 return modified ? Qt : Qnil;
1627 }
1628
1629
1630 /* Return a list representing the text properties of OBJECT between
1631 START and END. if PROP is non-nil, report only on that property.
1632 Each result list element has the form (S E PLIST), where S and E
1633 are positions in OBJECT and PLIST is a property list containing the
1634 text properties of OBJECT between S and E. Value is nil if OBJECT
1635 doesn't contain text properties between START and END. */
1636
1637 Lisp_Object
1638 text_property_list (object, start, end, prop)
1639 Lisp_Object object, start, end, prop;
1640 {
1641 struct interval *i;
1642 Lisp_Object result;
1643
1644 result = Qnil;
1645
1646 i = validate_interval_range (object, &start, &end, soft);
1647 if (!NULL_INTERVAL_P (i))
1648 {
1649 int s = XINT (start);
1650 int e = XINT (end);
1651
1652 while (s < e)
1653 {
1654 int interval_end, len;
1655 Lisp_Object plist;
1656
1657 interval_end = i->position + LENGTH (i);
1658 if (interval_end > e)
1659 interval_end = e;
1660 len = interval_end - s;
1661
1662 plist = i->plist;
1663
1664 if (!NILP (prop))
1665 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1666 if (EQ (Fcar (plist), prop))
1667 {
1668 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1669 break;
1670 }
1671
1672 if (!NILP (plist))
1673 result = Fcons (Fcons (make_number (s),
1674 Fcons (make_number (s + len),
1675 Fcons (plist, Qnil))),
1676 result);
1677
1678 i = next_interval (i);
1679 if (NULL_INTERVAL_P (i))
1680 break;
1681 s = i->position;
1682 }
1683 }
1684
1685 return result;
1686 }
1687
1688
1689 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1690 (START END PLIST), where START and END are positions and PLIST is a
1691 property list containing the text properties to add. Adjust START
1692 and END positions by DELTA before adding properties. Value is
1693 non-zero if OBJECT was modified. */
1694
1695 int
1696 add_text_properties_from_list (object, list, delta)
1697 Lisp_Object object, list, delta;
1698 {
1699 struct gcpro gcpro1, gcpro2;
1700 int modified_p = 0;
1701
1702 GCPRO2 (list, object);
1703
1704 for (; CONSP (list); list = XCDR (list))
1705 {
1706 Lisp_Object item, start, end, plist, tem;
1707
1708 item = XCAR (list);
1709 start = make_number (XINT (XCAR (item)) + XINT (delta));
1710 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1711 plist = XCAR (XCDR (XCDR (item)));
1712
1713 tem = Fadd_text_properties (start, end, plist, object);
1714 if (!NILP (tem))
1715 modified_p = 1;
1716 }
1717
1718 UNGCPRO;
1719 return modified_p;
1720 }
1721
1722
1723
1724 /* Modify end-points of ranges in LIST destructively. LIST is a list
1725 as returned from text_property_list. Change end-points equal to
1726 OLD_END to NEW_END. */
1727
1728 void
1729 extend_property_ranges (list, old_end, new_end)
1730 Lisp_Object list, old_end, new_end;
1731 {
1732 for (; CONSP (list); list = XCDR (list))
1733 {
1734 Lisp_Object item, end;
1735
1736 item = XCAR (list);
1737 end = XCAR (XCDR (item));
1738
1739 if (EQ (end, old_end))
1740 XCAR (XCDR (item)) = new_end;
1741 }
1742 }
1743
1744
1745 \f
1746 /* Call the modification hook functions in LIST, each with START and END. */
1747
1748 static void
1749 call_mod_hooks (list, start, end)
1750 Lisp_Object list, start, end;
1751 {
1752 struct gcpro gcpro1;
1753 GCPRO1 (list);
1754 while (!NILP (list))
1755 {
1756 call2 (Fcar (list), start, end);
1757 list = Fcdr (list);
1758 }
1759 UNGCPRO;
1760 }
1761
1762 /* Check for read-only intervals between character positions START ... END,
1763 in BUF, and signal an error if we find one.
1764
1765 Then check for any modification hooks in the range.
1766 Create a list of all these hooks in lexicographic order,
1767 eliminating consecutive extra copies of the same hook. Then call
1768 those hooks in order, with START and END - 1 as arguments. */
1769
1770 void
1771 verify_interval_modification (buf, start, end)
1772 struct buffer *buf;
1773 int start, end;
1774 {
1775 register INTERVAL intervals = BUF_INTERVALS (buf);
1776 register INTERVAL i;
1777 Lisp_Object hooks;
1778 register Lisp_Object prev_mod_hooks;
1779 Lisp_Object mod_hooks;
1780 struct gcpro gcpro1;
1781
1782 hooks = Qnil;
1783 prev_mod_hooks = Qnil;
1784 mod_hooks = Qnil;
1785
1786 interval_insert_behind_hooks = Qnil;
1787 interval_insert_in_front_hooks = Qnil;
1788
1789 if (NULL_INTERVAL_P (intervals))
1790 return;
1791
1792 if (start > end)
1793 {
1794 int temp = start;
1795 start = end;
1796 end = temp;
1797 }
1798
1799 /* For an insert operation, check the two chars around the position. */
1800 if (start == end)
1801 {
1802 INTERVAL prev = NULL;
1803 Lisp_Object before, after;
1804
1805 /* Set I to the interval containing the char after START,
1806 and PREV to the interval containing the char before START.
1807 Either one may be null. They may be equal. */
1808 i = find_interval (intervals, start);
1809
1810 if (start == BUF_BEGV (buf))
1811 prev = 0;
1812 else if (i->position == start)
1813 prev = previous_interval (i);
1814 else if (i->position < start)
1815 prev = i;
1816 if (start == BUF_ZV (buf))
1817 i = 0;
1818
1819 /* If Vinhibit_read_only is set and is not a list, we can
1820 skip the read_only checks. */
1821 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
1822 {
1823 /* If I and PREV differ we need to check for the read-only
1824 property together with its stickiness. If either I or
1825 PREV are 0, this check is all we need.
1826 We have to take special care, since read-only may be
1827 indirectly defined via the category property. */
1828 if (i != prev)
1829 {
1830 if (! NULL_INTERVAL_P (i))
1831 {
1832 after = textget (i->plist, Qread_only);
1833
1834 /* If interval I is read-only and read-only is
1835 front-sticky, inhibit insertion.
1836 Check for read-only as well as category. */
1837 if (! NILP (after)
1838 && NILP (Fmemq (after, Vinhibit_read_only)))
1839 {
1840 Lisp_Object tem;
1841
1842 tem = textget (i->plist, Qfront_sticky);
1843 if (TMEM (Qread_only, tem)
1844 || (NILP (Fplist_get (i->plist, Qread_only))
1845 && TMEM (Qcategory, tem)))
1846 text_read_only ();
1847 }
1848 }
1849
1850 if (! NULL_INTERVAL_P (prev))
1851 {
1852 before = textget (prev->plist, Qread_only);
1853
1854 /* If interval PREV is read-only and read-only isn't
1855 rear-nonsticky, inhibit insertion.
1856 Check for read-only as well as category. */
1857 if (! NILP (before)
1858 && NILP (Fmemq (before, Vinhibit_read_only)))
1859 {
1860 Lisp_Object tem;
1861
1862 tem = textget (prev->plist, Qrear_nonsticky);
1863 if (! TMEM (Qread_only, tem)
1864 && (! NILP (Fplist_get (prev->plist,Qread_only))
1865 || ! TMEM (Qcategory, tem)))
1866 text_read_only ();
1867 }
1868 }
1869 }
1870 else if (! NULL_INTERVAL_P (i))
1871 {
1872 after = textget (i->plist, Qread_only);
1873
1874 /* If interval I is read-only and read-only is
1875 front-sticky, inhibit insertion.
1876 Check for read-only as well as category. */
1877 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
1878 {
1879 Lisp_Object tem;
1880
1881 tem = textget (i->plist, Qfront_sticky);
1882 if (TMEM (Qread_only, tem)
1883 || (NILP (Fplist_get (i->plist, Qread_only))
1884 && TMEM (Qcategory, tem)))
1885 text_read_only ();
1886
1887 tem = textget (prev->plist, Qrear_nonsticky);
1888 if (! TMEM (Qread_only, tem)
1889 && (! NILP (Fplist_get (prev->plist, Qread_only))
1890 || ! TMEM (Qcategory, tem)))
1891 text_read_only ();
1892 }
1893 }
1894 }
1895
1896 /* Run both insert hooks (just once if they're the same). */
1897 if (!NULL_INTERVAL_P (prev))
1898 interval_insert_behind_hooks
1899 = textget (prev->plist, Qinsert_behind_hooks);
1900 if (!NULL_INTERVAL_P (i))
1901 interval_insert_in_front_hooks
1902 = textget (i->plist, Qinsert_in_front_hooks);
1903 }
1904 else
1905 {
1906 /* Loop over intervals on or next to START...END,
1907 collecting their hooks. */
1908
1909 i = find_interval (intervals, start);
1910 do
1911 {
1912 if (! INTERVAL_WRITABLE_P (i))
1913 text_read_only ();
1914
1915 mod_hooks = textget (i->plist, Qmodification_hooks);
1916 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1917 {
1918 hooks = Fcons (mod_hooks, hooks);
1919 prev_mod_hooks = mod_hooks;
1920 }
1921
1922 i = next_interval (i);
1923 }
1924 /* Keep going thru the interval containing the char before END. */
1925 while (! NULL_INTERVAL_P (i) && i->position < end);
1926
1927 GCPRO1 (hooks);
1928 hooks = Fnreverse (hooks);
1929 while (! EQ (hooks, Qnil))
1930 {
1931 call_mod_hooks (Fcar (hooks), make_number (start),
1932 make_number (end));
1933 hooks = Fcdr (hooks);
1934 }
1935 UNGCPRO;
1936 }
1937 }
1938
1939 /* Run the interval hooks for an insertion on character range START ... END.
1940 verify_interval_modification chose which hooks to run;
1941 this function is called after the insertion happens
1942 so it can indicate the range of inserted text. */
1943
1944 void
1945 report_interval_modification (start, end)
1946 Lisp_Object start, end;
1947 {
1948 if (! NILP (interval_insert_behind_hooks))
1949 call_mod_hooks (interval_insert_behind_hooks, start, end);
1950 if (! NILP (interval_insert_in_front_hooks)
1951 && ! EQ (interval_insert_in_front_hooks,
1952 interval_insert_behind_hooks))
1953 call_mod_hooks (interval_insert_in_front_hooks, start, end);
1954 }
1955 \f
1956 void
1957 syms_of_textprop ()
1958 {
1959 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
1960 "Property-list used as default values.\n\
1961 The value of a property in this list is seen as the value for every\n\
1962 character that does not have its own value for that property.");
1963 Vdefault_text_properties = Qnil;
1964
1965 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
1966 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1967 This also inhibits the use of the `intangible' text property.");
1968 Vinhibit_point_motion_hooks = Qnil;
1969
1970 DEFVAR_LISP ("text-property-default-nonsticky",
1971 &Vtext_property_default_nonsticky,
1972 "Alist of properties vs the corresponding non-stickinesses.\n\
1973 Each element has the form (PROPERTY . NONSTICKINESS).\n\
1974 \n\
1975 If a character in a buffer has PROPERTY, new text inserted adjacent to\n\
1976 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,\n\
1977 inherits it if NONSTICKINESS is nil. The front-sticky and\n\
1978 rear-nonsticky properties of the character overrides NONSTICKINESS.");
1979 Vtext_property_default_nonsticky = Qnil;
1980
1981 staticpro (&interval_insert_behind_hooks);
1982 staticpro (&interval_insert_in_front_hooks);
1983 interval_insert_behind_hooks = Qnil;
1984 interval_insert_in_front_hooks = Qnil;
1985
1986
1987 /* Common attributes one might give text */
1988
1989 staticpro (&Qforeground);
1990 Qforeground = intern ("foreground");
1991 staticpro (&Qbackground);
1992 Qbackground = intern ("background");
1993 staticpro (&Qfont);
1994 Qfont = intern ("font");
1995 staticpro (&Qstipple);
1996 Qstipple = intern ("stipple");
1997 staticpro (&Qunderline);
1998 Qunderline = intern ("underline");
1999 staticpro (&Qread_only);
2000 Qread_only = intern ("read-only");
2001 staticpro (&Qinvisible);
2002 Qinvisible = intern ("invisible");
2003 staticpro (&Qintangible);
2004 Qintangible = intern ("intangible");
2005 staticpro (&Qcategory);
2006 Qcategory = intern ("category");
2007 staticpro (&Qlocal_map);
2008 Qlocal_map = intern ("local-map");
2009 staticpro (&Qfront_sticky);
2010 Qfront_sticky = intern ("front-sticky");
2011 staticpro (&Qrear_nonsticky);
2012 Qrear_nonsticky = intern ("rear-nonsticky");
2013 staticpro (&Qmouse_face);
2014 Qmouse_face = intern ("mouse-face");
2015
2016 /* Properties that text might use to specify certain actions */
2017
2018 staticpro (&Qmouse_left);
2019 Qmouse_left = intern ("mouse-left");
2020 staticpro (&Qmouse_entered);
2021 Qmouse_entered = intern ("mouse-entered");
2022 staticpro (&Qpoint_left);
2023 Qpoint_left = intern ("point-left");
2024 staticpro (&Qpoint_entered);
2025 Qpoint_entered = intern ("point-entered");
2026
2027 defsubr (&Stext_properties_at);
2028 defsubr (&Sget_text_property);
2029 defsubr (&Sget_char_property);
2030 defsubr (&Snext_char_property_change);
2031 defsubr (&Sprevious_char_property_change);
2032 defsubr (&Snext_single_char_property_change);
2033 defsubr (&Sprevious_single_char_property_change);
2034 defsubr (&Snext_property_change);
2035 defsubr (&Snext_single_property_change);
2036 defsubr (&Sprevious_property_change);
2037 defsubr (&Sprevious_single_property_change);
2038 defsubr (&Sadd_text_properties);
2039 defsubr (&Sput_text_property);
2040 defsubr (&Sset_text_properties);
2041 defsubr (&Sremove_text_properties);
2042 defsubr (&Stext_property_any);
2043 defsubr (&Stext_property_not_all);
2044 /* defsubr (&Serase_text_properties); */
2045 /* defsubr (&Scopy_text_properties); */
2046 }
2047