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