]> code.delx.au - gnu-emacs/blob - src/textprop.c
ffdebb53620b4ca886acae93b02dff166e62b029
[gnu-emacs] / src / textprop.c
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include <config.h>
21 #include <setjmp.h>
22 #include "lisp.h"
23 #include "intervals.h"
24 #include "buffer.h"
25 #include "window.h"
26
27 #ifndef NULL
28 #define NULL (void *)0
29 #endif
30
31 /* Test for membership, allowing for t (actually any non-cons) to mean the
32 universal set. */
33
34 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
35 \f
36
37 /* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
40
41 set_properties needs to deal with the interval property cache.
42
43 It is assumed that for any interval plist, a property appears
44 only once on the list. Although some code i.e., remove_properties,
45 handles the more general case, the uniqueness of properties is
46 necessary for the system to remain consistent. This requirement
47 is enforced by the subrs installing properties onto the intervals. */
48
49 \f
50 /* Types of hooks. */
51 Lisp_Object Qmouse_left;
52 Lisp_Object Qmouse_entered;
53 Lisp_Object Qpoint_left;
54 Lisp_Object Qpoint_entered;
55 Lisp_Object Qcategory;
56 Lisp_Object Qlocal_map;
57
58 /* Visual properties text (including strings) may have. */
59 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
60 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
61 Lisp_Object Qminibuffer_prompt;
62
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky, Qrear_nonsticky;
65
66 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
68 traversing plists. */
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
70
71 /* verify_interval_modification saves insertion hooks here
72 to be run later by report_interval_modification. */
73 Lisp_Object interval_insert_behind_hooks;
74 Lisp_Object interval_insert_in_front_hooks;
75
76 static void text_read_only (Lisp_Object) NO_RETURN;
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 DEFUN ("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 DEFUN ("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 DEFUN ("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 DEFUN ("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 DEFUN ("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 DEFUN ("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 DEFUN ("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 DEFUN ("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 /* Return 1 if there's a change in some property between BEG and END. */
979
980 int
981 property_change_between_p (EMACS_INT beg, EMACS_INT end)
982 {
983 register INTERVAL i, next;
984 Lisp_Object object, pos;
985
986 XSETBUFFER (object, current_buffer);
987 XSETFASTINT (pos, beg);
988
989 i = validate_interval_range (object, &pos, &pos, soft);
990 if (NULL_INTERVAL_P (i))
991 return 0;
992
993 next = next_interval (i);
994 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
995 {
996 next = next_interval (next);
997 if (NULL_INTERVAL_P (next))
998 return 0;
999 if (next->position >= end)
1000 return 0;
1001 }
1002
1003 if (NULL_INTERVAL_P (next))
1004 return 0;
1005
1006 return 1;
1007 }
1008
1009 DEFUN ("next-single-property-change", Fnext_single_property_change,
1010 Snext_single_property_change, 2, 4, 0,
1011 doc: /* Return the position of next property change for a specific property.
1012 Scans characters forward from POSITION till it finds
1013 a change in the PROP property, then returns the position of the change.
1014 If the optional third argument OBJECT is a buffer (or nil, which means
1015 the current buffer), POSITION is a buffer position (integer or marker).
1016 If OBJECT is a string, POSITION is a 0-based index into it.
1017 The property values are compared with `eq'.
1018 Return nil if the property is constant all the way to the end of OBJECT.
1019 If the value is non-nil, it is a position greater than POSITION, never equal.
1020
1021 If the optional fourth argument LIMIT is non-nil, don't search
1022 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1023 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1024 {
1025 register INTERVAL i, next;
1026 register Lisp_Object here_val;
1027
1028 if (NILP (object))
1029 XSETBUFFER (object, current_buffer);
1030
1031 if (!NILP (limit))
1032 CHECK_NUMBER_COERCE_MARKER (limit);
1033
1034 i = validate_interval_range (object, &position, &position, soft);
1035 if (NULL_INTERVAL_P (i))
1036 return limit;
1037
1038 here_val = textget (i->plist, prop);
1039 next = next_interval (i);
1040 while (! NULL_INTERVAL_P (next)
1041 && EQ (here_val, textget (next->plist, prop))
1042 && (NILP (limit) || next->position < XFASTINT (limit)))
1043 next = next_interval (next);
1044
1045 if (NULL_INTERVAL_P (next)
1046 || (next->position
1047 >= (INTEGERP (limit)
1048 ? XFASTINT (limit)
1049 : (STRINGP (object)
1050 ? SCHARS (object)
1051 : BUF_ZV (XBUFFER (object))))))
1052 return limit;
1053 else
1054 return make_number (next->position);
1055 }
1056
1057 DEFUN ("previous-property-change", Fprevious_property_change,
1058 Sprevious_property_change, 1, 3, 0,
1059 doc: /* Return the position of previous property change.
1060 Scans characters backwards from POSITION in OBJECT till it finds
1061 a change in some text property, then returns the position of the change.
1062 If the optional second argument OBJECT is a buffer (or nil, which means
1063 the current buffer), POSITION is a buffer position (integer or marker).
1064 If OBJECT is a string, POSITION is a 0-based index into it.
1065 Return nil if the property is constant all the way to the start of OBJECT.
1066 If the value is non-nil, it is a position less than POSITION, never equal.
1067
1068 If the optional third argument LIMIT is non-nil, don't search
1069 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1070 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
1071 {
1072 register INTERVAL i, previous;
1073
1074 if (NILP (object))
1075 XSETBUFFER (object, current_buffer);
1076
1077 if (!NILP (limit))
1078 CHECK_NUMBER_COERCE_MARKER (limit);
1079
1080 i = validate_interval_range (object, &position, &position, soft);
1081 if (NULL_INTERVAL_P (i))
1082 return limit;
1083
1084 /* Start with the interval containing the char before point. */
1085 if (i->position == XFASTINT (position))
1086 i = previous_interval (i);
1087
1088 previous = previous_interval (i);
1089 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1090 && (NILP (limit)
1091 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1092 previous = previous_interval (previous);
1093
1094 if (NULL_INTERVAL_P (previous)
1095 || (previous->position + LENGTH (previous)
1096 <= (INTEGERP (limit)
1097 ? XFASTINT (limit)
1098 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1099 return limit;
1100 else
1101 return make_number (previous->position + LENGTH (previous));
1102 }
1103
1104 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1105 Sprevious_single_property_change, 2, 4, 0,
1106 doc: /* Return the position of previous property change for a specific property.
1107 Scans characters backward from POSITION till it finds
1108 a change in the PROP property, then returns the position of the change.
1109 If the optional third argument OBJECT is a buffer (or nil, which means
1110 the current buffer), POSITION is a buffer position (integer or marker).
1111 If OBJECT is a string, POSITION is a 0-based index into it.
1112 The property values are compared with `eq'.
1113 Return nil if the property is constant all the way to the start of OBJECT.
1114 If the value is non-nil, it is a position less than POSITION, never equal.
1115
1116 If the optional fourth argument LIMIT is non-nil, don't search
1117 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1118 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1119 {
1120 register INTERVAL i, previous;
1121 register Lisp_Object here_val;
1122
1123 if (NILP (object))
1124 XSETBUFFER (object, current_buffer);
1125
1126 if (!NILP (limit))
1127 CHECK_NUMBER_COERCE_MARKER (limit);
1128
1129 i = validate_interval_range (object, &position, &position, soft);
1130
1131 /* Start with the interval containing the char before point. */
1132 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1133 i = previous_interval (i);
1134
1135 if (NULL_INTERVAL_P (i))
1136 return limit;
1137
1138 here_val = textget (i->plist, prop);
1139 previous = previous_interval (i);
1140 while (!NULL_INTERVAL_P (previous)
1141 && EQ (here_val, textget (previous->plist, prop))
1142 && (NILP (limit)
1143 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1144 previous = previous_interval (previous);
1145
1146 if (NULL_INTERVAL_P (previous)
1147 || (previous->position + LENGTH (previous)
1148 <= (INTEGERP (limit)
1149 ? XFASTINT (limit)
1150 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1151 return limit;
1152 else
1153 return make_number (previous->position + LENGTH (previous));
1154 }
1155 \f
1156 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1157
1158 DEFUN ("add-text-properties", Fadd_text_properties,
1159 Sadd_text_properties, 3, 4, 0,
1160 doc: /* Add properties to the text from START to END.
1161 The third argument PROPERTIES is a property list
1162 specifying the property values to add. If the optional fourth argument
1163 OBJECT is a buffer (or nil, which means the current buffer),
1164 START and END are buffer positions (integers or markers).
1165 If OBJECT is a string, START and END are 0-based indices into it.
1166 Return t if any property value actually changed, nil otherwise. */)
1167 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1168 {
1169 register INTERVAL i, unchanged;
1170 register EMACS_INT s, len;
1171 register int modified = 0;
1172 struct gcpro gcpro1;
1173
1174 properties = validate_plist (properties);
1175 if (NILP (properties))
1176 return Qnil;
1177
1178 if (NILP (object))
1179 XSETBUFFER (object, current_buffer);
1180
1181 i = validate_interval_range (object, &start, &end, hard);
1182 if (NULL_INTERVAL_P (i))
1183 return Qnil;
1184
1185 s = XINT (start);
1186 len = XINT (end) - s;
1187
1188 /* No need to protect OBJECT, because we GC only if it's a buffer,
1189 and live buffers are always protected. */
1190 GCPRO1 (properties);
1191
1192 /* If we're not starting on an interval boundary, we have to
1193 split this interval. */
1194 if (i->position != s)
1195 {
1196 /* If this interval already has the properties, we can
1197 skip it. */
1198 if (interval_has_all_properties (properties, i))
1199 {
1200 EMACS_INT got = (LENGTH (i) - (s - i->position));
1201 if (got >= len)
1202 RETURN_UNGCPRO (Qnil);
1203 len -= got;
1204 i = next_interval (i);
1205 }
1206 else
1207 {
1208 unchanged = i;
1209 i = split_interval_right (unchanged, s - unchanged->position);
1210 copy_properties (unchanged, i);
1211 }
1212 }
1213
1214 if (BUFFERP (object))
1215 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1216
1217 /* We are at the beginning of interval I, with LEN chars to scan. */
1218 for (;;)
1219 {
1220 if (i == 0)
1221 abort ();
1222
1223 if (LENGTH (i) >= len)
1224 {
1225 /* We can UNGCPRO safely here, because there will be just
1226 one more chance to gc, in the next call to add_properties,
1227 and after that we will not need PROPERTIES or OBJECT again. */
1228 UNGCPRO;
1229
1230 if (interval_has_all_properties (properties, i))
1231 {
1232 if (BUFFERP (object))
1233 signal_after_change (XINT (start), XINT (end) - XINT (start),
1234 XINT (end) - XINT (start));
1235
1236 return modified ? Qt : Qnil;
1237 }
1238
1239 if (LENGTH (i) == len)
1240 {
1241 add_properties (properties, i, object);
1242 if (BUFFERP (object))
1243 signal_after_change (XINT (start), XINT (end) - XINT (start),
1244 XINT (end) - XINT (start));
1245 return Qt;
1246 }
1247
1248 /* i doesn't have the properties, and goes past the change limit */
1249 unchanged = i;
1250 i = split_interval_left (unchanged, len);
1251 copy_properties (unchanged, i);
1252 add_properties (properties, i, object);
1253 if (BUFFERP (object))
1254 signal_after_change (XINT (start), XINT (end) - XINT (start),
1255 XINT (end) - XINT (start));
1256 return Qt;
1257 }
1258
1259 len -= LENGTH (i);
1260 modified += add_properties (properties, i, object);
1261 i = next_interval (i);
1262 }
1263 }
1264
1265 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1266
1267 DEFUN ("put-text-property", Fput_text_property,
1268 Sput_text_property, 4, 5, 0,
1269 doc: /* Set one property of the text from START to END.
1270 The third and fourth arguments PROPERTY and VALUE
1271 specify the property to add.
1272 If the optional fifth argument OBJECT is a buffer (or nil, which means
1273 the current buffer), START and END are buffer positions (integers or
1274 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1275 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1276 {
1277 Fadd_text_properties (start, end,
1278 Fcons (property, Fcons (value, Qnil)),
1279 object);
1280 return Qnil;
1281 }
1282
1283 DEFUN ("set-text-properties", Fset_text_properties,
1284 Sset_text_properties, 3, 4, 0,
1285 doc: /* Completely replace properties of text from START to END.
1286 The third argument PROPERTIES is the new property list.
1287 If the optional fourth argument OBJECT is a buffer (or nil, which means
1288 the current buffer), START and END are buffer positions (integers or
1289 markers). If OBJECT is a string, START and END are 0-based indices into it.
1290 If PROPERTIES is nil, the effect is to remove all properties from
1291 the designated part of OBJECT. */)
1292 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1293 {
1294 return set_text_properties (start, end, properties, object, Qt);
1295 }
1296
1297
1298 /* Replace properties of text from START to END with new list of
1299 properties PROPERTIES. OBJECT is the buffer or string containing
1300 the text. OBJECT nil means use the current buffer.
1301 COHERENT_CHANGE_P nil means this is being called as an internal
1302 subroutine, rather than as a change primitive with checking of
1303 read-only, invoking change hooks, etc.. Value is nil if the
1304 function _detected_ that it did not replace any properties, non-nil
1305 otherwise. */
1306
1307 Lisp_Object
1308 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, Lisp_Object coherent_change_p)
1309 {
1310 register INTERVAL i;
1311 Lisp_Object ostart, oend;
1312
1313 ostart = start;
1314 oend = end;
1315
1316 properties = validate_plist (properties);
1317
1318 if (NILP (object))
1319 XSETBUFFER (object, current_buffer);
1320
1321 /* If we want no properties for a whole string,
1322 get rid of its intervals. */
1323 if (NILP (properties) && STRINGP (object)
1324 && XFASTINT (start) == 0
1325 && XFASTINT (end) == SCHARS (object))
1326 {
1327 if (! STRING_INTERVALS (object))
1328 return Qnil;
1329
1330 STRING_SET_INTERVALS (object, NULL_INTERVAL);
1331 return Qt;
1332 }
1333
1334 i = validate_interval_range (object, &start, &end, soft);
1335
1336 if (NULL_INTERVAL_P (i))
1337 {
1338 /* If buffer has no properties, and we want none, return now. */
1339 if (NILP (properties))
1340 return Qnil;
1341
1342 /* Restore the original START and END values
1343 because validate_interval_range increments them for strings. */
1344 start = ostart;
1345 end = oend;
1346
1347 i = validate_interval_range (object, &start, &end, hard);
1348 /* This can return if start == end. */
1349 if (NULL_INTERVAL_P (i))
1350 return Qnil;
1351 }
1352
1353 if (BUFFERP (object) && !NILP (coherent_change_p))
1354 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1355
1356 set_text_properties_1 (start, end, properties, object, i);
1357
1358 if (BUFFERP (object) && !NILP (coherent_change_p))
1359 signal_after_change (XINT (start), XINT (end) - XINT (start),
1360 XINT (end) - XINT (start));
1361 return Qt;
1362 }
1363
1364 /* Replace properties of text from START to END with new list of
1365 properties PROPERTIES. BUFFER is the buffer containing
1366 the text. This does not obey any hooks.
1367 You can provide the interval that START is located in as I,
1368 or pass NULL for I and this function will find it.
1369 START and END can be in any order. */
1370
1371 void
1372 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object buffer, INTERVAL i)
1373 {
1374 register INTERVAL prev_changed = NULL_INTERVAL;
1375 register EMACS_INT s, len;
1376 INTERVAL unchanged;
1377
1378 s = XINT (start);
1379 len = XINT (end) - s;
1380 if (len == 0)
1381 return;
1382 if (len < 0)
1383 {
1384 s = s + len;
1385 len = - len;
1386 }
1387
1388 if (i == 0)
1389 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1390
1391 if (i->position != s)
1392 {
1393 unchanged = i;
1394 i = split_interval_right (unchanged, s - unchanged->position);
1395
1396 if (LENGTH (i) > len)
1397 {
1398 copy_properties (unchanged, i);
1399 i = split_interval_left (i, len);
1400 set_properties (properties, i, buffer);
1401 return;
1402 }
1403
1404 set_properties (properties, i, buffer);
1405
1406 if (LENGTH (i) == len)
1407 return;
1408
1409 prev_changed = i;
1410 len -= LENGTH (i);
1411 i = next_interval (i);
1412 }
1413
1414 /* We are starting at the beginning of an interval, I */
1415 while (len > 0)
1416 {
1417 if (i == 0)
1418 abort ();
1419
1420 if (LENGTH (i) >= len)
1421 {
1422 if (LENGTH (i) > len)
1423 i = split_interval_left (i, len);
1424
1425 /* We have to call set_properties even if we are going to
1426 merge the intervals, so as to make the undo records
1427 and cause redisplay to happen. */
1428 set_properties (properties, i, buffer);
1429 if (!NULL_INTERVAL_P (prev_changed))
1430 merge_interval_left (i);
1431 return;
1432 }
1433
1434 len -= LENGTH (i);
1435
1436 /* We have to call set_properties even if we are going to
1437 merge the intervals, so as to make the undo records
1438 and cause redisplay to happen. */
1439 set_properties (properties, i, buffer);
1440 if (NULL_INTERVAL_P (prev_changed))
1441 prev_changed = i;
1442 else
1443 prev_changed = i = merge_interval_left (i);
1444
1445 i = next_interval (i);
1446 }
1447 }
1448
1449 DEFUN ("remove-text-properties", Fremove_text_properties,
1450 Sremove_text_properties, 3, 4, 0,
1451 doc: /* Remove some properties from text from START to END.
1452 The third argument PROPERTIES is a property list
1453 whose property names specify the properties to remove.
1454 \(The values stored in PROPERTIES are ignored.)
1455 If the optional fourth argument OBJECT is a buffer (or nil, which means
1456 the current buffer), START and END are buffer positions (integers or
1457 markers). If OBJECT is a string, START and END are 0-based indices into it.
1458 Return t if any property was actually removed, nil otherwise.
1459
1460 Use `set-text-properties' if you want to remove all text properties. */)
1461 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1462 {
1463 register INTERVAL i, unchanged;
1464 register EMACS_INT s, len;
1465 register int modified = 0;
1466
1467 if (NILP (object))
1468 XSETBUFFER (object, current_buffer);
1469
1470 i = validate_interval_range (object, &start, &end, soft);
1471 if (NULL_INTERVAL_P (i))
1472 return Qnil;
1473
1474 s = XINT (start);
1475 len = XINT (end) - s;
1476
1477 if (i->position != s)
1478 {
1479 /* No properties on this first interval -- return if
1480 it covers the entire region. */
1481 if (! interval_has_some_properties (properties, i))
1482 {
1483 EMACS_INT got = (LENGTH (i) - (s - i->position));
1484 if (got >= len)
1485 return Qnil;
1486 len -= got;
1487 i = next_interval (i);
1488 }
1489 /* Split away the beginning of this interval; what we don't
1490 want to modify. */
1491 else
1492 {
1493 unchanged = i;
1494 i = split_interval_right (unchanged, s - unchanged->position);
1495 copy_properties (unchanged, i);
1496 }
1497 }
1498
1499 if (BUFFERP (object))
1500 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1501
1502 /* We are at the beginning of an interval, with len to scan */
1503 for (;;)
1504 {
1505 if (i == 0)
1506 abort ();
1507
1508 if (LENGTH (i) >= len)
1509 {
1510 if (! interval_has_some_properties (properties, i))
1511 return modified ? Qt : Qnil;
1512
1513 if (LENGTH (i) == len)
1514 {
1515 remove_properties (properties, Qnil, i, object);
1516 if (BUFFERP (object))
1517 signal_after_change (XINT (start), XINT (end) - XINT (start),
1518 XINT (end) - XINT (start));
1519 return Qt;
1520 }
1521
1522 /* i has the properties, and goes past the change limit */
1523 unchanged = i;
1524 i = split_interval_left (i, len);
1525 copy_properties (unchanged, i);
1526 remove_properties (properties, Qnil, i, object);
1527 if (BUFFERP (object))
1528 signal_after_change (XINT (start), XINT (end) - XINT (start),
1529 XINT (end) - XINT (start));
1530 return Qt;
1531 }
1532
1533 len -= LENGTH (i);
1534 modified += remove_properties (properties, Qnil, i, object);
1535 i = next_interval (i);
1536 }
1537 }
1538
1539 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1540 Sremove_list_of_text_properties, 3, 4, 0,
1541 doc: /* Remove some properties from text from START to END.
1542 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1543 If the optional fourth argument OBJECT is a buffer (or nil, which means
1544 the current buffer), START and END are buffer positions (integers or
1545 markers). If OBJECT is a string, START and END are 0-based indices into it.
1546 Return t if any property was actually removed, nil otherwise. */)
1547 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1548 {
1549 register INTERVAL i, unchanged;
1550 register EMACS_INT s, len;
1551 register int modified = 0;
1552 Lisp_Object properties;
1553 properties = list_of_properties;
1554
1555 if (NILP (object))
1556 XSETBUFFER (object, current_buffer);
1557
1558 i = validate_interval_range (object, &start, &end, soft);
1559 if (NULL_INTERVAL_P (i))
1560 return Qnil;
1561
1562 s = XINT (start);
1563 len = XINT (end) - s;
1564
1565 if (i->position != s)
1566 {
1567 /* No properties on this first interval -- return if
1568 it covers the entire region. */
1569 if (! interval_has_some_properties_list (properties, i))
1570 {
1571 EMACS_INT got = (LENGTH (i) - (s - i->position));
1572 if (got >= len)
1573 return Qnil;
1574 len -= got;
1575 i = next_interval (i);
1576 }
1577 /* Split away the beginning of this interval; what we don't
1578 want to modify. */
1579 else
1580 {
1581 unchanged = i;
1582 i = split_interval_right (unchanged, s - unchanged->position);
1583 copy_properties (unchanged, i);
1584 }
1585 }
1586
1587 /* We are at the beginning of an interval, with len to scan.
1588 The flag `modified' records if changes have been made.
1589 When object is a buffer, we must call modify_region before changes are
1590 made and signal_after_change when we are done.
1591 We call modify_region before calling remove_properties if modified == 0,
1592 and we call signal_after_change before returning if modified != 0. */
1593 for (;;)
1594 {
1595 if (i == 0)
1596 abort ();
1597
1598 if (LENGTH (i) >= len)
1599 {
1600 if (! interval_has_some_properties_list (properties, i))
1601 {
1602 if (modified)
1603 {
1604 if (BUFFERP (object))
1605 signal_after_change (XINT (start),
1606 XINT (end) - XINT (start),
1607 XINT (end) - XINT (start));
1608 return Qt;
1609 }
1610 else
1611 return Qnil;
1612 }
1613 else if (LENGTH (i) == len)
1614 {
1615 if (!modified && BUFFERP (object))
1616 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1617 remove_properties (Qnil, properties, i, object);
1618 if (BUFFERP (object))
1619 signal_after_change (XINT (start), XINT (end) - XINT (start),
1620 XINT (end) - XINT (start));
1621 return Qt;
1622 }
1623 else
1624 { /* i has the properties, and goes past the change limit. */
1625 unchanged = i;
1626 i = split_interval_left (i, len);
1627 copy_properties (unchanged, i);
1628 if (!modified && BUFFERP (object))
1629 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1630 remove_properties (Qnil, properties, i, object);
1631 if (BUFFERP (object))
1632 signal_after_change (XINT (start), XINT (end) - XINT (start),
1633 XINT (end) - XINT (start));
1634 return Qt;
1635 }
1636 }
1637 if (interval_has_some_properties_list (properties, i))
1638 {
1639 if (!modified && BUFFERP (object))
1640 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1641 remove_properties (Qnil, properties, i, object);
1642 modified = 1;
1643 }
1644 len -= LENGTH (i);
1645 i = next_interval (i);
1646 }
1647 }
1648 \f
1649 DEFUN ("text-property-any", Ftext_property_any,
1650 Stext_property_any, 4, 5, 0,
1651 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1652 If so, return the position of the first character whose property PROPERTY
1653 is `eq' to VALUE. Otherwise return nil.
1654 If the optional fifth argument OBJECT is a buffer (or nil, which means
1655 the current buffer), START and END are buffer positions (integers or
1656 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1657 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1658 {
1659 register INTERVAL i;
1660 register EMACS_INT e, pos;
1661
1662 if (NILP (object))
1663 XSETBUFFER (object, current_buffer);
1664 i = validate_interval_range (object, &start, &end, soft);
1665 if (NULL_INTERVAL_P (i))
1666 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1667 e = XINT (end);
1668
1669 while (! NULL_INTERVAL_P (i))
1670 {
1671 if (i->position >= e)
1672 break;
1673 if (EQ (textget (i->plist, property), value))
1674 {
1675 pos = i->position;
1676 if (pos < XINT (start))
1677 pos = XINT (start);
1678 return make_number (pos);
1679 }
1680 i = next_interval (i);
1681 }
1682 return Qnil;
1683 }
1684
1685 DEFUN ("text-property-not-all", Ftext_property_not_all,
1686 Stext_property_not_all, 4, 5, 0,
1687 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1688 If so, return the position of the first character whose property PROPERTY
1689 is not `eq' to VALUE. Otherwise, return nil.
1690 If the optional fifth argument OBJECT is a buffer (or nil, which means
1691 the current buffer), START and END are buffer positions (integers or
1692 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1693 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1694 {
1695 register INTERVAL i;
1696 register EMACS_INT s, e;
1697
1698 if (NILP (object))
1699 XSETBUFFER (object, current_buffer);
1700 i = validate_interval_range (object, &start, &end, soft);
1701 if (NULL_INTERVAL_P (i))
1702 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1703 s = XINT (start);
1704 e = XINT (end);
1705
1706 while (! NULL_INTERVAL_P (i))
1707 {
1708 if (i->position >= e)
1709 break;
1710 if (! EQ (textget (i->plist, property), value))
1711 {
1712 if (i->position > s)
1713 s = i->position;
1714 return make_number (s);
1715 }
1716 i = next_interval (i);
1717 }
1718 return Qnil;
1719 }
1720
1721 \f
1722 /* Return the direction from which the text-property PROP would be
1723 inherited by any new text inserted at POS: 1 if it would be
1724 inherited from the char after POS, -1 if it would be inherited from
1725 the char before POS, and 0 if from neither.
1726 BUFFER can be either a buffer or nil (meaning current buffer). */
1727
1728 int
1729 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1730 {
1731 Lisp_Object prev_pos, front_sticky;
1732 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1733
1734 if (NILP (buffer))
1735 XSETBUFFER (buffer, current_buffer);
1736
1737 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1738 /* Consider previous character. */
1739 {
1740 Lisp_Object rear_non_sticky;
1741
1742 prev_pos = make_number (XINT (pos) - 1);
1743 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1744
1745 if (!NILP (CONSP (rear_non_sticky)
1746 ? Fmemq (prop, rear_non_sticky)
1747 : rear_non_sticky))
1748 /* PROP is rear-non-sticky. */
1749 is_rear_sticky = 0;
1750 }
1751 else
1752 return 0;
1753
1754 /* Consider following character. */
1755 /* This signals an arg-out-of-range error if pos is outside the
1756 buffer's accessible range. */
1757 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1758
1759 if (EQ (front_sticky, Qt)
1760 || (CONSP (front_sticky)
1761 && !NILP (Fmemq (prop, front_sticky))))
1762 /* PROP is inherited from after. */
1763 is_front_sticky = 1;
1764
1765 /* Simple cases, where the properties are consistent. */
1766 if (is_rear_sticky && !is_front_sticky)
1767 return -1;
1768 else if (!is_rear_sticky && is_front_sticky)
1769 return 1;
1770 else if (!is_rear_sticky && !is_front_sticky)
1771 return 0;
1772
1773 /* The stickiness properties are inconsistent, so we have to
1774 disambiguate. Basically, rear-sticky wins, _except_ if the
1775 property that would be inherited has a value of nil, in which case
1776 front-sticky wins. */
1777 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1778 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1779 return 1;
1780 else
1781 return -1;
1782 }
1783
1784 \f
1785 /* I don't think this is the right interface to export; how often do you
1786 want to do something like this, other than when you're copying objects
1787 around?
1788
1789 I think it would be better to have a pair of functions, one which
1790 returns the text properties of a region as a list of ranges and
1791 plists, and another which applies such a list to another object. */
1792
1793 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1794 SRC and DEST may each refer to strings or buffers.
1795 Optional sixth argument PROP causes only that property to be copied.
1796 Properties are copied to DEST as if by `add-text-properties'.
1797 Return t if any property value actually changed, nil otherwise. */
1798
1799 /* Note this can GC when DEST is a buffer. */
1800
1801 Lisp_Object
1802 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src, Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1803 {
1804 INTERVAL i;
1805 Lisp_Object res;
1806 Lisp_Object stuff;
1807 Lisp_Object plist;
1808 EMACS_INT s, e, e2, p, len;
1809 int modified = 0;
1810 struct gcpro gcpro1, gcpro2;
1811
1812 i = validate_interval_range (src, &start, &end, soft);
1813 if (NULL_INTERVAL_P (i))
1814 return Qnil;
1815
1816 CHECK_NUMBER_COERCE_MARKER (pos);
1817 {
1818 Lisp_Object dest_start, dest_end;
1819
1820 dest_start = pos;
1821 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1822 /* Apply this to a copy of pos; it will try to increment its arguments,
1823 which we don't want. */
1824 validate_interval_range (dest, &dest_start, &dest_end, soft);
1825 }
1826
1827 s = XINT (start);
1828 e = XINT (end);
1829 p = XINT (pos);
1830
1831 stuff = Qnil;
1832
1833 while (s < e)
1834 {
1835 e2 = i->position + LENGTH (i);
1836 if (e2 > e)
1837 e2 = e;
1838 len = e2 - s;
1839
1840 plist = i->plist;
1841 if (! NILP (prop))
1842 while (! NILP (plist))
1843 {
1844 if (EQ (Fcar (plist), prop))
1845 {
1846 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1847 break;
1848 }
1849 plist = Fcdr (Fcdr (plist));
1850 }
1851 if (! NILP (plist))
1852 {
1853 /* Must defer modifications to the interval tree in case src
1854 and dest refer to the same string or buffer. */
1855 stuff = Fcons (Fcons (make_number (p),
1856 Fcons (make_number (p + len),
1857 Fcons (plist, Qnil))),
1858 stuff);
1859 }
1860
1861 i = next_interval (i);
1862 if (NULL_INTERVAL_P (i))
1863 break;
1864
1865 p += len;
1866 s = i->position;
1867 }
1868
1869 GCPRO2 (stuff, dest);
1870
1871 while (! NILP (stuff))
1872 {
1873 res = Fcar (stuff);
1874 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1875 Fcar (Fcdr (Fcdr (res))), dest);
1876 if (! NILP (res))
1877 modified++;
1878 stuff = Fcdr (stuff);
1879 }
1880
1881 UNGCPRO;
1882
1883 return modified ? Qt : Qnil;
1884 }
1885
1886
1887 /* Return a list representing the text properties of OBJECT between
1888 START and END. if PROP is non-nil, report only on that property.
1889 Each result list element has the form (S E PLIST), where S and E
1890 are positions in OBJECT and PLIST is a property list containing the
1891 text properties of OBJECT between S and E. Value is nil if OBJECT
1892 doesn't contain text properties between START and END. */
1893
1894 Lisp_Object
1895 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1896 {
1897 struct interval *i;
1898 Lisp_Object result;
1899
1900 result = Qnil;
1901
1902 i = validate_interval_range (object, &start, &end, soft);
1903 if (!NULL_INTERVAL_P (i))
1904 {
1905 EMACS_INT s = XINT (start);
1906 EMACS_INT e = XINT (end);
1907
1908 while (s < e)
1909 {
1910 EMACS_INT interval_end, len;
1911 Lisp_Object plist;
1912
1913 interval_end = i->position + LENGTH (i);
1914 if (interval_end > e)
1915 interval_end = e;
1916 len = interval_end - s;
1917
1918 plist = i->plist;
1919
1920 if (!NILP (prop))
1921 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1922 if (EQ (XCAR (plist), prop))
1923 {
1924 plist = Fcons (prop, Fcons (Fcar (XCDR (plist)), Qnil));
1925 break;
1926 }
1927
1928 if (!NILP (plist))
1929 result = Fcons (Fcons (make_number (s),
1930 Fcons (make_number (s + len),
1931 Fcons (plist, Qnil))),
1932 result);
1933
1934 i = next_interval (i);
1935 if (NULL_INTERVAL_P (i))
1936 break;
1937 s = i->position;
1938 }
1939 }
1940
1941 return result;
1942 }
1943
1944
1945 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1946 (START END PLIST), where START and END are positions and PLIST is a
1947 property list containing the text properties to add. Adjust START
1948 and END positions by DELTA before adding properties. Value is
1949 non-zero if OBJECT was modified. */
1950
1951 int
1952 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
1953 {
1954 struct gcpro gcpro1, gcpro2;
1955 int modified_p = 0;
1956
1957 GCPRO2 (list, object);
1958
1959 for (; CONSP (list); list = XCDR (list))
1960 {
1961 Lisp_Object item, start, end, plist, tem;
1962
1963 item = XCAR (list);
1964 start = make_number (XINT (XCAR (item)) + XINT (delta));
1965 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1966 plist = XCAR (XCDR (XCDR (item)));
1967
1968 tem = Fadd_text_properties (start, end, plist, object);
1969 if (!NILP (tem))
1970 modified_p = 1;
1971 }
1972
1973 UNGCPRO;
1974 return modified_p;
1975 }
1976
1977
1978
1979 /* Modify end-points of ranges in LIST destructively, and return the
1980 new list. LIST is a list as returned from text_property_list.
1981 Discard properties that begin at or after NEW_END, and limit
1982 end-points to NEW_END. */
1983
1984 Lisp_Object
1985 extend_property_ranges (Lisp_Object list, Lisp_Object new_end)
1986 {
1987 Lisp_Object prev = Qnil, head = list;
1988 EMACS_INT max = XINT (new_end);
1989
1990 for (; CONSP (list); prev = list, list = XCDR (list))
1991 {
1992 Lisp_Object item, beg, end;
1993
1994 item = XCAR (list);
1995 beg = XCAR (item);
1996 end = XCAR (XCDR (item));
1997
1998 if (XINT (beg) >= max)
1999 {
2000 /* The start-point is past the end of the new string.
2001 Discard this property. */
2002 if (EQ (head, list))
2003 head = XCDR (list);
2004 else
2005 XSETCDR (prev, XCDR (list));
2006 }
2007 else if (XINT (end) > max)
2008 /* The end-point is past the end of the new string. */
2009 XSETCAR (XCDR (item), new_end);
2010 }
2011
2012 return head;
2013 }
2014
2015
2016 \f
2017 /* Call the modification hook functions in LIST, each with START and END. */
2018
2019 static void
2020 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
2021 {
2022 struct gcpro gcpro1;
2023 GCPRO1 (list);
2024 while (!NILP (list))
2025 {
2026 call2 (Fcar (list), start, end);
2027 list = Fcdr (list);
2028 }
2029 UNGCPRO;
2030 }
2031
2032 /* Check for read-only intervals between character positions START ... END,
2033 in BUF, and signal an error if we find one.
2034
2035 Then check for any modification hooks in the range.
2036 Create a list of all these hooks in lexicographic order,
2037 eliminating consecutive extra copies of the same hook. Then call
2038 those hooks in order, with START and END - 1 as arguments. */
2039
2040 void
2041 verify_interval_modification (struct buffer *buf, int start, int end)
2042 {
2043 register INTERVAL intervals = BUF_INTERVALS (buf);
2044 register INTERVAL i;
2045 Lisp_Object hooks;
2046 register Lisp_Object prev_mod_hooks;
2047 Lisp_Object mod_hooks;
2048 struct gcpro gcpro1;
2049
2050 hooks = Qnil;
2051 prev_mod_hooks = Qnil;
2052 mod_hooks = Qnil;
2053
2054 interval_insert_behind_hooks = Qnil;
2055 interval_insert_in_front_hooks = Qnil;
2056
2057 if (NULL_INTERVAL_P (intervals))
2058 return;
2059
2060 if (start > end)
2061 {
2062 EMACS_INT temp = start;
2063 start = end;
2064 end = temp;
2065 }
2066
2067 /* For an insert operation, check the two chars around the position. */
2068 if (start == end)
2069 {
2070 INTERVAL prev = NULL;
2071 Lisp_Object before, after;
2072
2073 /* Set I to the interval containing the char after START,
2074 and PREV to the interval containing the char before START.
2075 Either one may be null. They may be equal. */
2076 i = find_interval (intervals, start);
2077
2078 if (start == BUF_BEGV (buf))
2079 prev = 0;
2080 else if (i->position == start)
2081 prev = previous_interval (i);
2082 else if (i->position < start)
2083 prev = i;
2084 if (start == BUF_ZV (buf))
2085 i = 0;
2086
2087 /* If Vinhibit_read_only is set and is not a list, we can
2088 skip the read_only checks. */
2089 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2090 {
2091 /* If I and PREV differ we need to check for the read-only
2092 property together with its stickiness. If either I or
2093 PREV are 0, this check is all we need.
2094 We have to take special care, since read-only may be
2095 indirectly defined via the category property. */
2096 if (i != prev)
2097 {
2098 if (! NULL_INTERVAL_P (i))
2099 {
2100 after = textget (i->plist, Qread_only);
2101
2102 /* If interval I is read-only and read-only is
2103 front-sticky, inhibit insertion.
2104 Check for read-only as well as category. */
2105 if (! NILP (after)
2106 && NILP (Fmemq (after, Vinhibit_read_only)))
2107 {
2108 Lisp_Object tem;
2109
2110 tem = textget (i->plist, Qfront_sticky);
2111 if (TMEM (Qread_only, tem)
2112 || (NILP (Fplist_get (i->plist, Qread_only))
2113 && TMEM (Qcategory, tem)))
2114 text_read_only (after);
2115 }
2116 }
2117
2118 if (! NULL_INTERVAL_P (prev))
2119 {
2120 before = textget (prev->plist, Qread_only);
2121
2122 /* If interval PREV is read-only and read-only isn't
2123 rear-nonsticky, inhibit insertion.
2124 Check for read-only as well as category. */
2125 if (! NILP (before)
2126 && NILP (Fmemq (before, Vinhibit_read_only)))
2127 {
2128 Lisp_Object tem;
2129
2130 tem = textget (prev->plist, Qrear_nonsticky);
2131 if (! TMEM (Qread_only, tem)
2132 && (! NILP (Fplist_get (prev->plist,Qread_only))
2133 || ! TMEM (Qcategory, tem)))
2134 text_read_only (before);
2135 }
2136 }
2137 }
2138 else if (! NULL_INTERVAL_P (i))
2139 {
2140 after = textget (i->plist, Qread_only);
2141
2142 /* If interval I is read-only and read-only is
2143 front-sticky, inhibit insertion.
2144 Check for read-only as well as category. */
2145 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2146 {
2147 Lisp_Object tem;
2148
2149 tem = textget (i->plist, Qfront_sticky);
2150 if (TMEM (Qread_only, tem)
2151 || (NILP (Fplist_get (i->plist, Qread_only))
2152 && TMEM (Qcategory, tem)))
2153 text_read_only (after);
2154
2155 tem = textget (prev->plist, Qrear_nonsticky);
2156 if (! TMEM (Qread_only, tem)
2157 && (! NILP (Fplist_get (prev->plist, Qread_only))
2158 || ! TMEM (Qcategory, tem)))
2159 text_read_only (after);
2160 }
2161 }
2162 }
2163
2164 /* Run both insert hooks (just once if they're the same). */
2165 if (!NULL_INTERVAL_P (prev))
2166 interval_insert_behind_hooks
2167 = textget (prev->plist, Qinsert_behind_hooks);
2168 if (!NULL_INTERVAL_P (i))
2169 interval_insert_in_front_hooks
2170 = textget (i->plist, Qinsert_in_front_hooks);
2171 }
2172 else
2173 {
2174 /* Loop over intervals on or next to START...END,
2175 collecting their hooks. */
2176
2177 i = find_interval (intervals, start);
2178 do
2179 {
2180 if (! INTERVAL_WRITABLE_P (i))
2181 text_read_only (textget (i->plist, Qread_only));
2182
2183 if (!inhibit_modification_hooks)
2184 {
2185 mod_hooks = textget (i->plist, Qmodification_hooks);
2186 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2187 {
2188 hooks = Fcons (mod_hooks, hooks);
2189 prev_mod_hooks = mod_hooks;
2190 }
2191 }
2192
2193 i = next_interval (i);
2194 }
2195 /* Keep going thru the interval containing the char before END. */
2196 while (! NULL_INTERVAL_P (i) && i->position < end);
2197
2198 if (!inhibit_modification_hooks)
2199 {
2200 GCPRO1 (hooks);
2201 hooks = Fnreverse (hooks);
2202 while (! EQ (hooks, Qnil))
2203 {
2204 call_mod_hooks (Fcar (hooks), make_number (start),
2205 make_number (end));
2206 hooks = Fcdr (hooks);
2207 }
2208 UNGCPRO;
2209 }
2210 }
2211 }
2212
2213 /* Run the interval hooks for an insertion on character range START ... END.
2214 verify_interval_modification chose which hooks to run;
2215 this function is called after the insertion happens
2216 so it can indicate the range of inserted text. */
2217
2218 void
2219 report_interval_modification (Lisp_Object start, Lisp_Object end)
2220 {
2221 if (! NILP (interval_insert_behind_hooks))
2222 call_mod_hooks (interval_insert_behind_hooks, start, end);
2223 if (! NILP (interval_insert_in_front_hooks)
2224 && ! EQ (interval_insert_in_front_hooks,
2225 interval_insert_behind_hooks))
2226 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2227 }
2228 \f
2229 void
2230 syms_of_textprop (void)
2231 {
2232 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties,
2233 doc: /* Property-list used as default values.
2234 The value of a property in this list is seen as the value for every
2235 character that does not have its own value for that property. */);
2236 Vdefault_text_properties = Qnil;
2237
2238 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist,
2239 doc: /* Alist of alternative properties for properties without a value.
2240 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2241 If a piece of text has no direct value for a particular property, then
2242 this alist is consulted. If that property appears in the alist, then
2243 the first non-nil value from the associated alternative properties is
2244 returned. */);
2245 Vchar_property_alias_alist = Qnil;
2246
2247 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks,
2248 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2249 This also inhibits the use of the `intangible' text property. */);
2250 Vinhibit_point_motion_hooks = Qnil;
2251
2252 DEFVAR_LISP ("text-property-default-nonsticky",
2253 Vtext_property_default_nonsticky,
2254 doc: /* Alist of properties vs the corresponding non-stickinesses.
2255 Each element has the form (PROPERTY . NONSTICKINESS).
2256
2257 If a character in a buffer has PROPERTY, new text inserted adjacent to
2258 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2259 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2260 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2261 /* Text property `syntax-table' should be nonsticky by default. */
2262 Vtext_property_default_nonsticky
2263 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt), Qnil);
2264
2265 staticpro (&interval_insert_behind_hooks);
2266 staticpro (&interval_insert_in_front_hooks);
2267 interval_insert_behind_hooks = Qnil;
2268 interval_insert_in_front_hooks = Qnil;
2269
2270
2271 /* Common attributes one might give text */
2272
2273 staticpro (&Qforeground);
2274 Qforeground = intern_c_string ("foreground");
2275 staticpro (&Qbackground);
2276 Qbackground = intern_c_string ("background");
2277 staticpro (&Qfont);
2278 Qfont = intern_c_string ("font");
2279 staticpro (&Qstipple);
2280 Qstipple = intern_c_string ("stipple");
2281 staticpro (&Qunderline);
2282 Qunderline = intern_c_string ("underline");
2283 staticpro (&Qread_only);
2284 Qread_only = intern_c_string ("read-only");
2285 staticpro (&Qinvisible);
2286 Qinvisible = intern_c_string ("invisible");
2287 staticpro (&Qintangible);
2288 Qintangible = intern_c_string ("intangible");
2289 staticpro (&Qcategory);
2290 Qcategory = intern_c_string ("category");
2291 staticpro (&Qlocal_map);
2292 Qlocal_map = intern_c_string ("local-map");
2293 staticpro (&Qfront_sticky);
2294 Qfront_sticky = intern_c_string ("front-sticky");
2295 staticpro (&Qrear_nonsticky);
2296 Qrear_nonsticky = intern_c_string ("rear-nonsticky");
2297 staticpro (&Qmouse_face);
2298 Qmouse_face = intern_c_string ("mouse-face");
2299 staticpro (&Qminibuffer_prompt);
2300 Qminibuffer_prompt = intern_c_string ("minibuffer-prompt");
2301
2302 /* Properties that text might use to specify certain actions */
2303
2304 staticpro (&Qmouse_left);
2305 Qmouse_left = intern_c_string ("mouse-left");
2306 staticpro (&Qmouse_entered);
2307 Qmouse_entered = intern_c_string ("mouse-entered");
2308 staticpro (&Qpoint_left);
2309 Qpoint_left = intern_c_string ("point-left");
2310 staticpro (&Qpoint_entered);
2311 Qpoint_entered = intern_c_string ("point-entered");
2312
2313 defsubr (&Stext_properties_at);
2314 defsubr (&Sget_text_property);
2315 defsubr (&Sget_char_property);
2316 defsubr (&Sget_char_property_and_overlay);
2317 defsubr (&Snext_char_property_change);
2318 defsubr (&Sprevious_char_property_change);
2319 defsubr (&Snext_single_char_property_change);
2320 defsubr (&Sprevious_single_char_property_change);
2321 defsubr (&Snext_property_change);
2322 defsubr (&Snext_single_property_change);
2323 defsubr (&Sprevious_property_change);
2324 defsubr (&Sprevious_single_property_change);
2325 defsubr (&Sadd_text_properties);
2326 defsubr (&Sput_text_property);
2327 defsubr (&Sset_text_properties);
2328 defsubr (&Sremove_text_properties);
2329 defsubr (&Sremove_list_of_text_properties);
2330 defsubr (&Stext_property_any);
2331 defsubr (&Stext_property_not_all);
2332 /* defsubr (&Serase_text_properties); */
2333 /* defsubr (&Scopy_text_properties); */
2334 }
2335