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