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