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