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