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