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