gamegrid-add-score-with-update-game-score fix
[emacs.git] / src / textprop.c
blobc1f6e59bf2e9bc16b0061250595e2fd2084927b6
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2013 Free Software Foundation,
3 Inc.
5 This file is part of GNU Emacs.
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.
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.
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/>. */
20 #include <config.h>
22 #include "lisp.h"
23 #include "intervals.h"
24 #include "character.h"
25 #include "buffer.h"
26 #include "window.h"
28 /* Test for membership, allowing for t (actually any non-cons) to mean the
29 universal set. */
31 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
34 /* NOTES: previous- and next- property change will have to skip
35 zero-length intervals if they are implemented. This could be done
36 inside next_interval and previous_interval.
38 set_properties needs to deal with the interval property cache.
40 It is assumed that for any interval plist, a property appears
41 only once on the list. Although some code i.e., remove_properties,
42 handles the more general case, the uniqueness of properties is
43 necessary for the system to remain consistent. This requirement
44 is enforced by the subrs installing properties onto the intervals. */
47 /* Types of hooks. */
48 static Lisp_Object Qmouse_left;
49 static Lisp_Object Qmouse_entered;
50 Lisp_Object Qpoint_left;
51 Lisp_Object Qpoint_entered;
52 Lisp_Object Qcategory;
53 Lisp_Object Qlocal_map;
55 /* Visual properties text (including strings) may have. */
56 static Lisp_Object Qforeground, Qbackground, Qunderline;
57 Lisp_Object Qfont;
58 static Lisp_Object Qstipple;
59 Lisp_Object Qinvisible, Qintangible, Qmouse_face;
60 static Lisp_Object Qread_only;
61 Lisp_Object Qminibuffer_prompt;
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky, Qrear_nonsticky;
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)))
71 /* verify_interval_modification saves insertion hooks here
72 to be run later by report_interval_modification. */
73 static Lisp_Object interval_insert_behind_hooks;
74 static Lisp_Object interval_insert_in_front_hooks;
77 /* Signal a `text-read-only' error. This function makes it easier
78 to capture that error in GDB by putting a breakpoint on it. */
80 static _Noreturn void
81 text_read_only (Lisp_Object propval)
83 if (STRINGP (propval))
84 xsignal1 (Qtext_read_only, propval);
86 xsignal0 (Qtext_read_only);
89 /* Prepare to modify the region of BUFFER from START to END. */
91 static void
92 modify_region (Lisp_Object buffer, Lisp_Object start, Lisp_Object end)
94 struct buffer *buf = XBUFFER (buffer), *old = current_buffer;
96 set_buffer_internal (buf);
97 modify_region_1 (XINT (start), XINT (end), true);
98 set_buffer_internal (old);
101 /* Extract the interval at the position pointed to by BEGIN from
102 OBJECT, a string or buffer. Additionally, check that the positions
103 pointed to by BEGIN and END are within the bounds of OBJECT, and
104 reverse them if *BEGIN is greater than *END. The objects pointed
105 to by BEGIN and END may be integers or markers; if the latter, they
106 are coerced to integers.
108 When OBJECT is a string, we increment *BEGIN and *END
109 to make them origin-one.
111 Note that buffer points don't correspond to interval indices.
112 For example, point-max is 1 greater than the index of the last
113 character. This difference is handled in the caller, which uses
114 the validated points to determine a length, and operates on that.
115 Exceptions are Ftext_properties_at, Fnext_property_change, and
116 Fprevious_property_change which call this function with BEGIN == END.
117 Handle this case specially.
119 If FORCE is soft (0), it's OK to return NULL. Otherwise,
120 create an interval tree for OBJECT if one doesn't exist, provided
121 the object actually contains text. In the current design, if there
122 is no text, there can be no text properties. */
124 #define soft 0
125 #define hard 1
127 INTERVAL
128 validate_interval_range (Lisp_Object object, Lisp_Object *begin, Lisp_Object *end, int force)
130 register INTERVAL i;
131 ptrdiff_t searchpos;
133 CHECK_STRING_OR_BUFFER (object);
134 CHECK_NUMBER_COERCE_MARKER (*begin);
135 CHECK_NUMBER_COERCE_MARKER (*end);
137 /* If we are asked for a point, but from a subr which operates
138 on a range, then return nothing. */
139 if (EQ (*begin, *end) && begin != end)
140 return NULL;
142 if (XINT (*begin) > XINT (*end))
144 Lisp_Object n;
145 n = *begin;
146 *begin = *end;
147 *end = n;
150 if (BUFFERP (object))
152 register struct buffer *b = XBUFFER (object);
154 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
155 && XINT (*end) <= BUF_ZV (b)))
156 args_out_of_range (*begin, *end);
157 i = buffer_intervals (b);
159 /* If there's no text, there are no properties. */
160 if (BUF_BEGV (b) == BUF_ZV (b))
161 return NULL;
163 searchpos = XINT (*begin);
165 else
167 ptrdiff_t len = SCHARS (object);
169 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
170 && XINT (*end) <= len))
171 args_out_of_range (*begin, *end);
172 XSETFASTINT (*begin, XFASTINT (*begin));
173 if (begin != end)
174 XSETFASTINT (*end, XFASTINT (*end));
175 i = string_intervals (object);
177 if (len == 0)
178 return NULL;
180 searchpos = XINT (*begin);
183 if (!i)
184 return (force ? create_root_interval (object) : i);
186 return find_interval (i, searchpos);
189 /* Validate LIST as a property list. If LIST is not a list, then
190 make one consisting of (LIST nil). Otherwise, verify that LIST
191 is even numbered and thus suitable as a plist. */
193 static Lisp_Object
194 validate_plist (Lisp_Object list)
196 if (NILP (list))
197 return Qnil;
199 if (CONSP (list))
201 register int i;
202 register Lisp_Object tail;
203 for (i = 0, tail = list; CONSP (tail); i++)
205 tail = XCDR (tail);
206 QUIT;
208 if (i & 1)
209 error ("Odd length text property list");
210 return list;
213 return Fcons (list, Fcons (Qnil, Qnil));
216 /* Return nonzero if interval I has all the properties,
217 with the same values, of list PLIST. */
219 static int
220 interval_has_all_properties (Lisp_Object plist, INTERVAL i)
222 register Lisp_Object tail1, tail2, sym1;
223 register int found;
225 /* Go through each element of PLIST. */
226 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
228 sym1 = XCAR (tail1);
229 found = 0;
231 /* Go through I's plist, looking for sym1 */
232 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
233 if (EQ (sym1, XCAR (tail2)))
235 /* Found the same property on both lists. If the
236 values are unequal, return zero. */
237 if (! EQ (Fcar (XCDR (tail1)), Fcar (XCDR (tail2))))
238 return 0;
240 /* Property has same value on both lists; go to next one. */
241 found = 1;
242 break;
245 if (! found)
246 return 0;
249 return 1;
252 /* Return nonzero if the plist of interval I has any of the
253 properties of PLIST, regardless of their values. */
255 static int
256 interval_has_some_properties (Lisp_Object plist, INTERVAL i)
258 register Lisp_Object tail1, tail2, sym;
260 /* Go through each element of PLIST. */
261 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
263 sym = XCAR (tail1);
265 /* Go through i's plist, looking for tail1 */
266 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
267 if (EQ (sym, XCAR (tail2)))
268 return 1;
271 return 0;
274 /* Return nonzero if the plist of interval I has any of the
275 property names in LIST, regardless of their values. */
277 static int
278 interval_has_some_properties_list (Lisp_Object list, INTERVAL i)
280 register Lisp_Object tail1, tail2, sym;
282 /* Go through each element of LIST. */
283 for (tail1 = list; CONSP (tail1); tail1 = XCDR (tail1))
285 sym = XCAR (tail1);
287 /* Go through i's plist, looking for tail1 */
288 for (tail2 = i->plist; CONSP (tail2); tail2 = XCDR (XCDR (tail2)))
289 if (EQ (sym, XCAR (tail2)))
290 return 1;
293 return 0;
296 /* Changing the plists of individual intervals. */
298 /* Return the value of PROP in property-list PLIST, or Qunbound if it
299 has none. */
300 static Lisp_Object
301 property_value (Lisp_Object plist, Lisp_Object prop)
303 Lisp_Object value;
305 while (PLIST_ELT_P (plist, value))
306 if (EQ (XCAR (plist), prop))
307 return XCAR (value);
308 else
309 plist = XCDR (value);
311 return Qunbound;
314 /* Set the properties of INTERVAL to PROPERTIES,
315 and record undo info for the previous values.
316 OBJECT is the string or buffer that INTERVAL belongs to. */
318 static void
319 set_properties (Lisp_Object properties, INTERVAL interval, Lisp_Object object)
321 Lisp_Object sym, value;
323 if (BUFFERP (object))
325 /* For each property in the old plist which is missing from PROPERTIES,
326 or has a different value in PROPERTIES, make an undo record. */
327 for (sym = interval->plist;
328 PLIST_ELT_P (sym, value);
329 sym = XCDR (value))
330 if (! EQ (property_value (properties, XCAR (sym)),
331 XCAR (value)))
333 record_property_change (interval->position, LENGTH (interval),
334 XCAR (sym), XCAR (value),
335 object);
338 /* For each new property that has no value at all in the old plist,
339 make an undo record binding it to nil, so it will be removed. */
340 for (sym = properties;
341 PLIST_ELT_P (sym, value);
342 sym = XCDR (value))
343 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
345 record_property_change (interval->position, LENGTH (interval),
346 XCAR (sym), Qnil,
347 object);
351 /* Store new properties. */
352 set_interval_plist (interval, Fcopy_sequence (properties));
355 /* Add the properties of PLIST to the interval I, or set
356 the value of I's property to the value of the property on PLIST
357 if they are different.
359 OBJECT should be the string or buffer the interval is in.
361 Return nonzero if this changes I (i.e., if any members of PLIST
362 are actually added to I's plist) */
364 static int
365 add_properties (Lisp_Object plist, INTERVAL i, Lisp_Object object)
367 Lisp_Object tail1, tail2, sym1, val1;
368 register int changed = 0;
369 register int found;
370 struct gcpro gcpro1, gcpro2, gcpro3;
372 tail1 = plist;
373 sym1 = Qnil;
374 val1 = Qnil;
375 /* No need to protect OBJECT, because we can GC only in the case
376 where it is a buffer, and live buffers are always protected.
377 I and its plist are also protected, via OBJECT. */
378 GCPRO3 (tail1, sym1, val1);
380 /* Go through each element of PLIST. */
381 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
383 sym1 = XCAR (tail1);
384 val1 = Fcar (XCDR (tail1));
385 found = 0;
387 /* Go through I's plist, looking for sym1 */
388 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
389 if (EQ (sym1, XCAR (tail2)))
391 /* No need to gcpro, because tail2 protects this
392 and it must be a cons cell (we get an error otherwise). */
393 register Lisp_Object this_cdr;
395 this_cdr = XCDR (tail2);
396 /* Found the property. Now check its value. */
397 found = 1;
399 /* The properties have the same value on both lists.
400 Continue to the next property. */
401 if (EQ (val1, Fcar (this_cdr)))
402 break;
404 /* Record this change in the buffer, for undo purposes. */
405 if (BUFFERP (object))
407 record_property_change (i->position, LENGTH (i),
408 sym1, Fcar (this_cdr), object);
411 /* I's property has a different value -- change it */
412 Fsetcar (this_cdr, val1);
413 changed++;
414 break;
417 if (! found)
419 /* Record this change in the buffer, for undo purposes. */
420 if (BUFFERP (object))
422 record_property_change (i->position, LENGTH (i),
423 sym1, Qnil, object);
425 set_interval_plist (i, Fcons (sym1, Fcons (val1, i->plist)));
426 changed++;
430 UNGCPRO;
432 return changed;
435 /* For any members of PLIST, or LIST,
436 which are properties of I, remove them from I's plist.
437 (If PLIST is non-nil, use that, otherwise use LIST.)
438 OBJECT is the string or buffer containing I. */
440 static int
441 remove_properties (Lisp_Object plist, Lisp_Object list, INTERVAL i, Lisp_Object object)
443 register Lisp_Object tail1, tail2, sym, current_plist;
444 register int changed = 0;
446 /* Nonzero means tail1 is a plist, otherwise it is a list. */
447 int use_plist;
449 current_plist = i->plist;
451 if (! NILP (plist))
452 tail1 = plist, use_plist = 1;
453 else
454 tail1 = list, use_plist = 0;
456 /* Go through each element of LIST or PLIST. */
457 while (CONSP (tail1))
459 sym = XCAR (tail1);
461 /* First, remove the symbol if it's at the head of the list */
462 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
464 if (BUFFERP (object))
465 record_property_change (i->position, LENGTH (i),
466 sym, XCAR (XCDR (current_plist)),
467 object);
469 current_plist = XCDR (XCDR (current_plist));
470 changed++;
473 /* Go through I's plist, looking for SYM. */
474 tail2 = current_plist;
475 while (! NILP (tail2))
477 register Lisp_Object this;
478 this = XCDR (XCDR (tail2));
479 if (CONSP (this) && EQ (sym, XCAR (this)))
481 if (BUFFERP (object))
482 record_property_change (i->position, LENGTH (i),
483 sym, XCAR (XCDR (this)), object);
485 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
486 changed++;
488 tail2 = this;
491 /* Advance thru TAIL1 one way or the other. */
492 tail1 = XCDR (tail1);
493 if (use_plist && CONSP (tail1))
494 tail1 = XCDR (tail1);
497 if (changed)
498 set_interval_plist (i, current_plist);
499 return changed;
502 /* Returns the interval of POSITION in OBJECT.
503 POSITION is BEG-based. */
505 INTERVAL
506 interval_of (ptrdiff_t position, Lisp_Object object)
508 register INTERVAL i;
509 ptrdiff_t beg, end;
511 if (NILP (object))
512 XSETBUFFER (object, current_buffer);
513 else if (EQ (object, Qt))
514 return NULL;
516 CHECK_STRING_OR_BUFFER (object);
518 if (BUFFERP (object))
520 register struct buffer *b = XBUFFER (object);
522 beg = BUF_BEGV (b);
523 end = BUF_ZV (b);
524 i = buffer_intervals (b);
526 else
528 beg = 0;
529 end = SCHARS (object);
530 i = string_intervals (object);
533 if (!(beg <= position && position <= end))
534 args_out_of_range (make_number (position), make_number (position));
535 if (beg == end || !i)
536 return NULL;
538 return find_interval (i, position);
541 DEFUN ("text-properties-at", Ftext_properties_at,
542 Stext_properties_at, 1, 2, 0,
543 doc: /* Return the list of properties of the character at POSITION in OBJECT.
544 If the optional second argument OBJECT is a buffer (or nil, which means
545 the current buffer), POSITION is a buffer position (integer or marker).
546 If OBJECT is a string, POSITION is a 0-based index into it.
547 If POSITION is at the end of OBJECT, the value is nil. */)
548 (Lisp_Object position, Lisp_Object object)
550 register INTERVAL i;
552 if (NILP (object))
553 XSETBUFFER (object, current_buffer);
555 i = validate_interval_range (object, &position, &position, soft);
556 if (!i)
557 return Qnil;
558 /* If POSITION is at the end of the interval,
559 it means it's the end of OBJECT.
560 There are no properties at the very end,
561 since no character follows. */
562 if (XINT (position) == LENGTH (i) + i->position)
563 return Qnil;
565 return i->plist;
568 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
569 doc: /* Return the value of POSITION's property PROP, in OBJECT.
570 OBJECT should be a buffer or a string; if omitted or nil, it defaults
571 to the current buffer.
572 If POSITION is at the end of OBJECT, the value is nil. */)
573 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
575 return textget (Ftext_properties_at (position, object), prop);
578 /* Return the value of char's property PROP, in OBJECT at POSITION.
579 OBJECT is optional and defaults to the current buffer.
580 If OVERLAY is non-0, then in the case that the returned property is from
581 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
582 returned in *OVERLAY.
583 If POSITION is at the end of OBJECT, the value is nil.
584 If OBJECT is a buffer, then overlay properties are considered as well as
585 text properties.
586 If OBJECT is a window, then that window's buffer is used, but
587 window-specific overlays are considered only if they are associated
588 with OBJECT. */
589 Lisp_Object
590 get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object object, Lisp_Object *overlay)
592 struct window *w = 0;
594 CHECK_NUMBER_COERCE_MARKER (position);
596 if (NILP (object))
597 XSETBUFFER (object, current_buffer);
599 if (WINDOWP (object))
601 w = XWINDOW (object);
602 object = w->buffer;
604 if (BUFFERP (object))
606 ptrdiff_t noverlays;
607 Lisp_Object *overlay_vec;
608 struct buffer *obuf = current_buffer;
610 if (XINT (position) < BUF_BEGV (XBUFFER (object))
611 || XINT (position) > BUF_ZV (XBUFFER (object)))
612 xsignal1 (Qargs_out_of_range, position);
614 set_buffer_temp (XBUFFER (object));
616 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, 0);
617 noverlays = sort_overlays (overlay_vec, noverlays, w);
619 set_buffer_temp (obuf);
621 /* Now check the overlays in order of decreasing priority. */
622 while (--noverlays >= 0)
624 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
625 if (!NILP (tem))
627 if (overlay)
628 /* Return the overlay we got the property from. */
629 *overlay = overlay_vec[noverlays];
630 return tem;
635 if (overlay)
636 /* Indicate that the return value is not from an overlay. */
637 *overlay = Qnil;
639 /* Not a buffer, or no appropriate overlay, so fall through to the
640 simpler case. */
641 return Fget_text_property (position, prop, object);
644 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
645 doc: /* Return the value of POSITION's property PROP, in OBJECT.
646 Both overlay properties and text properties are checked.
647 OBJECT is optional and defaults to the current buffer.
648 If POSITION is at the end of OBJECT, the value is nil.
649 If OBJECT is a buffer, then overlay properties are considered as well as
650 text properties.
651 If OBJECT is a window, then that window's buffer is used, but window-specific
652 overlays are considered only if they are associated with OBJECT. */)
653 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
655 return get_char_property_and_overlay (position, prop, object, 0);
658 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
659 Sget_char_property_and_overlay, 2, 3, 0,
660 doc: /* Like `get-char-property', but with extra overlay information.
661 The value is a cons cell. Its car is the return value of `get-char-property'
662 with the same arguments--that is, the value of POSITION's property
663 PROP in OBJECT. Its cdr is the overlay in which the property was
664 found, or nil, if it was found as a text property or not found at all.
666 OBJECT is optional and defaults to the current buffer. OBJECT may be
667 a string, a buffer or a window. For strings, the cdr of the return
668 value is always nil, since strings do not have overlays. If OBJECT is
669 a window, then that window's buffer is used, but window-specific
670 overlays are considered only if they are associated with OBJECT. If
671 POSITION is at the end of OBJECT, both car and cdr are nil. */)
672 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
674 Lisp_Object overlay;
675 Lisp_Object val
676 = get_char_property_and_overlay (position, prop, object, &overlay);
677 return Fcons (val, overlay);
681 DEFUN ("next-char-property-change", Fnext_char_property_change,
682 Snext_char_property_change, 1, 2, 0,
683 doc: /* Return the position of next text property or overlay change.
684 This scans characters forward in the current buffer from POSITION till
685 it finds a change in some text property, or the beginning or end of an
686 overlay, and returns the position of that.
687 If none is found up to (point-max), the function returns (point-max).
689 If the optional second argument LIMIT is non-nil, don't search
690 past position LIMIT; return LIMIT if nothing is found before LIMIT.
691 LIMIT is a no-op if it is greater than (point-max). */)
692 (Lisp_Object position, Lisp_Object limit)
694 Lisp_Object temp;
696 temp = Fnext_overlay_change (position);
697 if (! NILP (limit))
699 CHECK_NUMBER_COERCE_MARKER (limit);
700 if (XINT (limit) < XINT (temp))
701 temp = limit;
703 return Fnext_property_change (position, Qnil, temp);
706 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
707 Sprevious_char_property_change, 1, 2, 0,
708 doc: /* Return the position of previous text property or overlay change.
709 Scans characters backward in the current buffer from POSITION till it
710 finds a change in some text property, or the beginning or end of an
711 overlay, and returns the position of that.
712 If none is found since (point-min), the function returns (point-min).
714 If the optional second argument LIMIT is non-nil, don't search
715 past position LIMIT; return LIMIT if nothing is found before LIMIT.
716 LIMIT is a no-op if it is less than (point-min). */)
717 (Lisp_Object position, Lisp_Object limit)
719 Lisp_Object temp;
721 temp = Fprevious_overlay_change (position);
722 if (! NILP (limit))
724 CHECK_NUMBER_COERCE_MARKER (limit);
725 if (XINT (limit) > XINT (temp))
726 temp = limit;
728 return Fprevious_property_change (position, Qnil, temp);
732 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
733 Snext_single_char_property_change, 2, 4, 0,
734 doc: /* Return the position of next text property or overlay change for a specific property.
735 Scans characters forward from POSITION till it finds
736 a change in the PROP property, then returns the position of the change.
737 If the optional third argument OBJECT is a buffer (or nil, which means
738 the current buffer), POSITION is a buffer position (integer or marker).
739 If OBJECT is a string, POSITION is a 0-based index into it.
741 In a string, scan runs to the end of the string.
742 In a buffer, it runs to (point-max), and the value cannot exceed that.
744 The property values are compared with `eq'.
745 If the property is constant all the way to the end of OBJECT, return the
746 last valid position in OBJECT.
747 If the optional fourth argument LIMIT is non-nil, don't search
748 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
749 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
751 if (STRINGP (object))
753 position = Fnext_single_property_change (position, prop, object, limit);
754 if (NILP (position))
756 if (NILP (limit))
757 position = make_number (SCHARS (object));
758 else
760 CHECK_NUMBER (limit);
761 position = limit;
765 else
767 Lisp_Object initial_value, value;
768 ptrdiff_t count = SPECPDL_INDEX ();
770 if (! NILP (object))
771 CHECK_BUFFER (object);
773 if (BUFFERP (object) && current_buffer != XBUFFER (object))
775 record_unwind_current_buffer ();
776 Fset_buffer (object);
779 CHECK_NUMBER_COERCE_MARKER (position);
781 initial_value = Fget_char_property (position, prop, object);
783 if (NILP (limit))
784 XSETFASTINT (limit, ZV);
785 else
786 CHECK_NUMBER_COERCE_MARKER (limit);
788 if (XFASTINT (position) >= XFASTINT (limit))
790 position = limit;
791 if (XFASTINT (position) > ZV)
792 XSETFASTINT (position, ZV);
794 else
795 while (1)
797 position = Fnext_char_property_change (position, limit);
798 if (XFASTINT (position) >= XFASTINT (limit))
800 position = limit;
801 break;
804 value = Fget_char_property (position, prop, object);
805 if (!EQ (value, initial_value))
806 break;
809 unbind_to (count, Qnil);
812 return position;
815 DEFUN ("previous-single-char-property-change",
816 Fprevious_single_char_property_change,
817 Sprevious_single_char_property_change, 2, 4, 0,
818 doc: /* Return the position of previous text property or overlay change for a specific property.
819 Scans characters backward from POSITION till it finds
820 a change in the PROP property, then returns the position of the change.
821 If the optional third argument OBJECT is a buffer (or nil, which means
822 the current buffer), POSITION is a buffer position (integer or marker).
823 If OBJECT is a string, POSITION is a 0-based index into it.
825 In a string, scan runs to the start of the string.
826 In a buffer, it runs to (point-min), and the value cannot be less than that.
828 The property values are compared with `eq'.
829 If the property is constant all the way to the start of OBJECT, return the
830 first valid position in OBJECT.
831 If the optional fourth argument LIMIT is non-nil, don't search back past
832 position LIMIT; return LIMIT if nothing is found before reaching LIMIT. */)
833 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
835 if (STRINGP (object))
837 position = Fprevious_single_property_change (position, prop, object, limit);
838 if (NILP (position))
840 if (NILP (limit))
841 position = make_number (0);
842 else
844 CHECK_NUMBER (limit);
845 position = limit;
849 else
851 ptrdiff_t count = SPECPDL_INDEX ();
853 if (! NILP (object))
854 CHECK_BUFFER (object);
856 if (BUFFERP (object) && current_buffer != XBUFFER (object))
858 record_unwind_current_buffer ();
859 Fset_buffer (object);
862 CHECK_NUMBER_COERCE_MARKER (position);
864 if (NILP (limit))
865 XSETFASTINT (limit, BEGV);
866 else
867 CHECK_NUMBER_COERCE_MARKER (limit);
869 if (XFASTINT (position) <= XFASTINT (limit))
871 position = limit;
872 if (XFASTINT (position) < BEGV)
873 XSETFASTINT (position, BEGV);
875 else
877 Lisp_Object initial_value
878 = Fget_char_property (make_number (XFASTINT (position) - 1),
879 prop, object);
881 while (1)
883 position = Fprevious_char_property_change (position, limit);
885 if (XFASTINT (position) <= XFASTINT (limit))
887 position = limit;
888 break;
890 else
892 Lisp_Object value
893 = Fget_char_property (make_number (XFASTINT (position) - 1),
894 prop, object);
896 if (!EQ (value, initial_value))
897 break;
902 unbind_to (count, Qnil);
905 return position;
908 DEFUN ("next-property-change", Fnext_property_change,
909 Snext_property_change, 1, 3, 0,
910 doc: /* Return the position of next property change.
911 Scans characters forward from POSITION in OBJECT till it finds
912 a change in some text property, then returns the position of the change.
913 If the optional second argument OBJECT is a buffer (or nil, which means
914 the current buffer), POSITION is a buffer position (integer or marker).
915 If OBJECT is a string, POSITION is a 0-based index into it.
916 Return nil if the property is constant all the way to the end of OBJECT.
917 If the value is non-nil, it is a position greater than POSITION, never equal.
919 If the optional third argument LIMIT is non-nil, don't search
920 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
921 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
923 register INTERVAL i, next;
925 if (NILP (object))
926 XSETBUFFER (object, current_buffer);
928 if (!NILP (limit) && !EQ (limit, Qt))
929 CHECK_NUMBER_COERCE_MARKER (limit);
931 i = validate_interval_range (object, &position, &position, soft);
933 /* If LIMIT is t, return start of next interval--don't
934 bother checking further intervals. */
935 if (EQ (limit, Qt))
937 if (!i)
938 next = i;
939 else
940 next = next_interval (i);
942 if (!next)
943 XSETFASTINT (position, (STRINGP (object)
944 ? SCHARS (object)
945 : BUF_ZV (XBUFFER (object))));
946 else
947 XSETFASTINT (position, next->position);
948 return position;
951 if (!i)
952 return limit;
954 next = next_interval (i);
956 while (next && intervals_equal (i, next)
957 && (NILP (limit) || next->position < XFASTINT (limit)))
958 next = next_interval (next);
960 if (!next
961 || (next->position
962 >= (INTEGERP (limit)
963 ? XFASTINT (limit)
964 : (STRINGP (object)
965 ? SCHARS (object)
966 : BUF_ZV (XBUFFER (object))))))
967 return limit;
968 else
969 return make_number (next->position);
972 DEFUN ("next-single-property-change", Fnext_single_property_change,
973 Snext_single_property_change, 2, 4, 0,
974 doc: /* Return the position of next property change for a specific property.
975 Scans characters forward from POSITION till it finds
976 a change in the PROP property, then returns the position of the change.
977 If the optional third argument OBJECT is a buffer (or nil, which means
978 the current buffer), POSITION is a buffer position (integer or marker).
979 If OBJECT is a string, POSITION is a 0-based index into it.
980 The property values are compared with `eq'.
981 Return nil if the property is constant all the way to the end of OBJECT.
982 If the value is non-nil, it is a position greater than POSITION, never equal.
984 If the optional fourth argument LIMIT is non-nil, don't search
985 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
986 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
988 register INTERVAL i, next;
989 register Lisp_Object here_val;
991 if (NILP (object))
992 XSETBUFFER (object, current_buffer);
994 if (!NILP (limit))
995 CHECK_NUMBER_COERCE_MARKER (limit);
997 i = validate_interval_range (object, &position, &position, soft);
998 if (!i)
999 return limit;
1001 here_val = textget (i->plist, prop);
1002 next = next_interval (i);
1003 while (next
1004 && EQ (here_val, textget (next->plist, prop))
1005 && (NILP (limit) || next->position < XFASTINT (limit)))
1006 next = next_interval (next);
1008 if (!next
1009 || (next->position
1010 >= (INTEGERP (limit)
1011 ? XFASTINT (limit)
1012 : (STRINGP (object)
1013 ? SCHARS (object)
1014 : BUF_ZV (XBUFFER (object))))))
1015 return limit;
1016 else
1017 return make_number (next->position);
1020 DEFUN ("previous-property-change", Fprevious_property_change,
1021 Sprevious_property_change, 1, 3, 0,
1022 doc: /* Return the position of previous property change.
1023 Scans characters backwards from POSITION in OBJECT till it finds
1024 a change in some text property, then returns the position of the change.
1025 If the optional second argument OBJECT is a buffer (or nil, which means
1026 the current buffer), POSITION is a buffer position (integer or marker).
1027 If OBJECT is a string, POSITION is a 0-based index into it.
1028 Return nil if the property is constant all the way to the start of OBJECT.
1029 If the value is non-nil, it is a position less than POSITION, never equal.
1031 If the optional third argument LIMIT is non-nil, don't search
1032 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1033 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
1035 register INTERVAL i, previous;
1037 if (NILP (object))
1038 XSETBUFFER (object, current_buffer);
1040 if (!NILP (limit))
1041 CHECK_NUMBER_COERCE_MARKER (limit);
1043 i = validate_interval_range (object, &position, &position, soft);
1044 if (!i)
1045 return limit;
1047 /* Start with the interval containing the char before point. */
1048 if (i->position == XFASTINT (position))
1049 i = previous_interval (i);
1051 previous = previous_interval (i);
1052 while (previous && intervals_equal (previous, i)
1053 && (NILP (limit)
1054 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1055 previous = previous_interval (previous);
1057 if (!previous
1058 || (previous->position + LENGTH (previous)
1059 <= (INTEGERP (limit)
1060 ? XFASTINT (limit)
1061 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1062 return limit;
1063 else
1064 return make_number (previous->position + LENGTH (previous));
1067 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1068 Sprevious_single_property_change, 2, 4, 0,
1069 doc: /* Return the position of previous property change for a specific property.
1070 Scans characters backward from POSITION till it finds
1071 a change in the PROP property, then returns the position of the change.
1072 If the optional third argument OBJECT is a buffer (or nil, which means
1073 the current buffer), POSITION is a buffer position (integer or marker).
1074 If OBJECT is a string, POSITION is a 0-based index into it.
1075 The property values are compared with `eq'.
1076 Return nil if the property is constant all the way to the start of OBJECT.
1077 If the value is non-nil, it is a position less than POSITION, never equal.
1079 If the optional fourth argument LIMIT is non-nil, don't search
1080 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1081 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1083 register INTERVAL i, previous;
1084 register Lisp_Object here_val;
1086 if (NILP (object))
1087 XSETBUFFER (object, current_buffer);
1089 if (!NILP (limit))
1090 CHECK_NUMBER_COERCE_MARKER (limit);
1092 i = validate_interval_range (object, &position, &position, soft);
1094 /* Start with the interval containing the char before point. */
1095 if (i && i->position == XFASTINT (position))
1096 i = previous_interval (i);
1098 if (!i)
1099 return limit;
1101 here_val = textget (i->plist, prop);
1102 previous = previous_interval (i);
1103 while (previous
1104 && EQ (here_val, textget (previous->plist, prop))
1105 && (NILP (limit)
1106 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1107 previous = previous_interval (previous);
1109 if (!previous
1110 || (previous->position + LENGTH (previous)
1111 <= (INTEGERP (limit)
1112 ? XFASTINT (limit)
1113 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1114 return limit;
1115 else
1116 return make_number (previous->position + LENGTH (previous));
1119 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1121 DEFUN ("add-text-properties", Fadd_text_properties,
1122 Sadd_text_properties, 3, 4, 0,
1123 doc: /* Add properties to the text from START to END.
1124 The third argument PROPERTIES is a property list
1125 specifying the property values to add. If the optional fourth argument
1126 OBJECT is a buffer (or nil, which means the current buffer),
1127 START and END are buffer positions (integers or markers).
1128 If OBJECT is a string, START and END are 0-based indices into it.
1129 Return t if any property value actually changed, nil otherwise. */)
1130 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1132 register INTERVAL i, unchanged;
1133 register ptrdiff_t s, len;
1134 register int modified = 0;
1135 struct gcpro gcpro1;
1137 properties = validate_plist (properties);
1138 if (NILP (properties))
1139 return Qnil;
1141 if (NILP (object))
1142 XSETBUFFER (object, current_buffer);
1144 i = validate_interval_range (object, &start, &end, hard);
1145 if (!i)
1146 return Qnil;
1148 s = XINT (start);
1149 len = XINT (end) - s;
1151 /* No need to protect OBJECT, because we GC only if it's a buffer,
1152 and live buffers are always protected. */
1153 GCPRO1 (properties);
1155 /* If we're not starting on an interval boundary, we have to
1156 split this interval. */
1157 if (i->position != s)
1159 /* If this interval already has the properties, we can
1160 skip it. */
1161 if (interval_has_all_properties (properties, i))
1163 ptrdiff_t got = (LENGTH (i) - (s - i->position));
1164 if (got >= len)
1165 RETURN_UNGCPRO (Qnil);
1166 len -= got;
1167 i = next_interval (i);
1169 else
1171 unchanged = i;
1172 i = split_interval_right (unchanged, s - unchanged->position);
1173 copy_properties (unchanged, i);
1177 if (BUFFERP (object))
1178 modify_region (object, start, end);
1180 /* We are at the beginning of interval I, with LEN chars to scan. */
1181 for (;;)
1183 eassert (i != 0);
1185 if (LENGTH (i) >= len)
1187 /* We can UNGCPRO safely here, because there will be just
1188 one more chance to gc, in the next call to add_properties,
1189 and after that we will not need PROPERTIES or OBJECT again. */
1190 UNGCPRO;
1192 if (interval_has_all_properties (properties, i))
1194 if (BUFFERP (object))
1195 signal_after_change (XINT (start), XINT (end) - XINT (start),
1196 XINT (end) - XINT (start));
1198 return modified ? Qt : Qnil;
1201 if (LENGTH (i) == len)
1203 add_properties (properties, i, object);
1204 if (BUFFERP (object))
1205 signal_after_change (XINT (start), XINT (end) - XINT (start),
1206 XINT (end) - XINT (start));
1207 return Qt;
1210 /* i doesn't have the properties, and goes past the change limit */
1211 unchanged = i;
1212 i = split_interval_left (unchanged, len);
1213 copy_properties (unchanged, i);
1214 add_properties (properties, i, object);
1215 if (BUFFERP (object))
1216 signal_after_change (XINT (start), XINT (end) - XINT (start),
1217 XINT (end) - XINT (start));
1218 return Qt;
1221 len -= LENGTH (i);
1222 modified += add_properties (properties, i, object);
1223 i = next_interval (i);
1227 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1229 DEFUN ("put-text-property", Fput_text_property,
1230 Sput_text_property, 4, 5, 0,
1231 doc: /* Set one property of the text from START to END.
1232 The third and fourth arguments PROPERTY and VALUE
1233 specify the property to add.
1234 If the optional fifth argument OBJECT is a buffer (or nil, which means
1235 the current buffer), START and END are buffer positions (integers or
1236 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1237 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1239 Fadd_text_properties (start, end,
1240 Fcons (property, Fcons (value, Qnil)),
1241 object);
1242 return Qnil;
1245 DEFUN ("set-text-properties", Fset_text_properties,
1246 Sset_text_properties, 3, 4, 0,
1247 doc: /* Completely replace properties of text from START to END.
1248 The third argument PROPERTIES is the new property list.
1249 If the optional fourth argument OBJECT is a buffer (or nil, which means
1250 the current buffer), START and END are buffer positions (integers or
1251 markers). If OBJECT is a string, START and END are 0-based indices into it.
1252 If PROPERTIES is nil, the effect is to remove all properties from
1253 the designated part of OBJECT. */)
1254 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1256 return set_text_properties (start, end, properties, object, Qt);
1260 /* Replace properties of text from START to END with new list of
1261 properties PROPERTIES. OBJECT is the buffer or string containing
1262 the text. OBJECT nil means use the current buffer.
1263 COHERENT_CHANGE_P nil means this is being called as an internal
1264 subroutine, rather than as a change primitive with checking of
1265 read-only, invoking change hooks, etc.. Value is nil if the
1266 function _detected_ that it did not replace any properties, non-nil
1267 otherwise. */
1269 Lisp_Object
1270 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, Lisp_Object coherent_change_p)
1272 register INTERVAL i;
1273 Lisp_Object ostart, oend;
1275 ostart = start;
1276 oend = end;
1278 properties = validate_plist (properties);
1280 if (NILP (object))
1281 XSETBUFFER (object, current_buffer);
1283 /* If we want no properties for a whole string,
1284 get rid of its intervals. */
1285 if (NILP (properties) && STRINGP (object)
1286 && XFASTINT (start) == 0
1287 && XFASTINT (end) == SCHARS (object))
1289 if (!string_intervals (object))
1290 return Qnil;
1292 set_string_intervals (object, NULL);
1293 return Qt;
1296 i = validate_interval_range (object, &start, &end, soft);
1298 if (!i)
1300 /* If buffer has no properties, and we want none, return now. */
1301 if (NILP (properties))
1302 return Qnil;
1304 /* Restore the original START and END values
1305 because validate_interval_range increments them for strings. */
1306 start = ostart;
1307 end = oend;
1309 i = validate_interval_range (object, &start, &end, hard);
1310 /* This can return if start == end. */
1311 if (!i)
1312 return Qnil;
1315 if (BUFFERP (object) && !NILP (coherent_change_p))
1316 modify_region (object, start, end);
1318 set_text_properties_1 (start, end, properties, object, i);
1320 if (BUFFERP (object) && !NILP (coherent_change_p))
1321 signal_after_change (XINT (start), XINT (end) - XINT (start),
1322 XINT (end) - XINT (start));
1323 return Qt;
1326 /* Replace properties of text from START to END with new list of
1327 properties PROPERTIES. OBJECT is the buffer or string containing
1328 the text. This does not obey any hooks.
1329 You should provide the interval that START is located in as I.
1330 START and END can be in any order. */
1332 void
1333 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, INTERVAL i)
1335 register INTERVAL prev_changed = NULL;
1336 register ptrdiff_t s, len;
1337 INTERVAL unchanged;
1339 if (XINT (start) < XINT (end))
1341 s = XINT (start);
1342 len = XINT (end) - s;
1344 else if (XINT (end) < XINT (start))
1346 s = XINT (end);
1347 len = XINT (start) - s;
1349 else
1350 return;
1352 eassert (i);
1354 if (i->position != s)
1356 unchanged = i;
1357 i = split_interval_right (unchanged, s - unchanged->position);
1359 if (LENGTH (i) > len)
1361 copy_properties (unchanged, i);
1362 i = split_interval_left (i, len);
1363 set_properties (properties, i, object);
1364 return;
1367 set_properties (properties, i, object);
1369 if (LENGTH (i) == len)
1370 return;
1372 prev_changed = i;
1373 len -= LENGTH (i);
1374 i = next_interval (i);
1377 /* We are starting at the beginning of an interval I. LEN is positive. */
1380 eassert (i != 0);
1382 if (LENGTH (i) >= len)
1384 if (LENGTH (i) > len)
1385 i = split_interval_left (i, len);
1387 /* We have to call set_properties even if we are going to
1388 merge the intervals, so as to make the undo records
1389 and cause redisplay to happen. */
1390 set_properties (properties, i, object);
1391 if (prev_changed)
1392 merge_interval_left (i);
1393 return;
1396 len -= LENGTH (i);
1398 /* We have to call set_properties even if we are going to
1399 merge the intervals, so as to make the undo records
1400 and cause redisplay to happen. */
1401 set_properties (properties, i, object);
1402 if (!prev_changed)
1403 prev_changed = i;
1404 else
1405 prev_changed = i = merge_interval_left (i);
1407 i = next_interval (i);
1409 while (len > 0);
1412 DEFUN ("remove-text-properties", Fremove_text_properties,
1413 Sremove_text_properties, 3, 4, 0,
1414 doc: /* Remove some properties from text from START to END.
1415 The third argument PROPERTIES is a property list
1416 whose property names specify the properties to remove.
1417 \(The values stored in PROPERTIES are ignored.)
1418 If the optional fourth argument OBJECT is a buffer (or nil, which means
1419 the current buffer), START and END are buffer positions (integers or
1420 markers). If OBJECT is a string, START and END are 0-based indices into it.
1421 Return t if any property was actually removed, nil otherwise.
1423 Use `set-text-properties' if you want to remove all text properties. */)
1424 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1426 register INTERVAL i, unchanged;
1427 register ptrdiff_t s, len;
1428 register int modified = 0;
1430 if (NILP (object))
1431 XSETBUFFER (object, current_buffer);
1433 i = validate_interval_range (object, &start, &end, soft);
1434 if (!i)
1435 return Qnil;
1437 s = XINT (start);
1438 len = XINT (end) - s;
1440 if (i->position != s)
1442 /* No properties on this first interval -- return if
1443 it covers the entire region. */
1444 if (! interval_has_some_properties (properties, i))
1446 ptrdiff_t got = (LENGTH (i) - (s - i->position));
1447 if (got >= len)
1448 return Qnil;
1449 len -= got;
1450 i = next_interval (i);
1452 /* Split away the beginning of this interval; what we don't
1453 want to modify. */
1454 else
1456 unchanged = i;
1457 i = split_interval_right (unchanged, s - unchanged->position);
1458 copy_properties (unchanged, i);
1462 if (BUFFERP (object))
1463 modify_region (object, start, end);
1465 /* We are at the beginning of an interval, with len to scan */
1466 for (;;)
1468 eassert (i != 0);
1470 if (LENGTH (i) >= len)
1472 if (! interval_has_some_properties (properties, i))
1473 return modified ? Qt : Qnil;
1475 if (LENGTH (i) == len)
1477 remove_properties (properties, Qnil, i, object);
1478 if (BUFFERP (object))
1479 signal_after_change (XINT (start), XINT (end) - XINT (start),
1480 XINT (end) - XINT (start));
1481 return Qt;
1484 /* i has the properties, and goes past the change limit */
1485 unchanged = i;
1486 i = split_interval_left (i, len);
1487 copy_properties (unchanged, i);
1488 remove_properties (properties, Qnil, i, object);
1489 if (BUFFERP (object))
1490 signal_after_change (XINT (start), XINT (end) - XINT (start),
1491 XINT (end) - XINT (start));
1492 return Qt;
1495 len -= LENGTH (i);
1496 modified += remove_properties (properties, Qnil, i, object);
1497 i = next_interval (i);
1501 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1502 Sremove_list_of_text_properties, 3, 4, 0,
1503 doc: /* Remove some properties from text from START to END.
1504 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1505 If the optional fourth argument OBJECT is a buffer (or nil, which means
1506 the current buffer), START and END are buffer positions (integers or
1507 markers). If OBJECT is a string, START and END are 0-based indices into it.
1508 Return t if any property was actually removed, nil otherwise. */)
1509 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1511 register INTERVAL i, unchanged;
1512 register ptrdiff_t s, len;
1513 register int modified = 0;
1514 Lisp_Object properties;
1515 properties = list_of_properties;
1517 if (NILP (object))
1518 XSETBUFFER (object, current_buffer);
1520 i = validate_interval_range (object, &start, &end, soft);
1521 if (!i)
1522 return Qnil;
1524 s = XINT (start);
1525 len = XINT (end) - s;
1527 if (i->position != s)
1529 /* No properties on this first interval -- return if
1530 it covers the entire region. */
1531 if (! interval_has_some_properties_list (properties, i))
1533 ptrdiff_t got = (LENGTH (i) - (s - i->position));
1534 if (got >= len)
1535 return Qnil;
1536 len -= got;
1537 i = next_interval (i);
1539 /* Split away the beginning of this interval; what we don't
1540 want to modify. */
1541 else
1543 unchanged = i;
1544 i = split_interval_right (unchanged, s - unchanged->position);
1545 copy_properties (unchanged, i);
1549 /* We are at the beginning of an interval, with len to scan.
1550 The flag `modified' records if changes have been made.
1551 When object is a buffer, we must call modify_region before changes are
1552 made and signal_after_change when we are done.
1553 We call modify_region before calling remove_properties if modified == 0,
1554 and we call signal_after_change before returning if modified != 0. */
1555 for (;;)
1557 eassert (i != 0);
1559 if (LENGTH (i) >= len)
1561 if (! interval_has_some_properties_list (properties, i))
1563 if (modified)
1565 if (BUFFERP (object))
1566 signal_after_change (XINT (start),
1567 XINT (end) - XINT (start),
1568 XINT (end) - XINT (start));
1569 return Qt;
1571 else
1572 return Qnil;
1574 else if (LENGTH (i) == len)
1576 if (!modified && BUFFERP (object))
1577 modify_region (object, start, end);
1578 remove_properties (Qnil, properties, i, object);
1579 if (BUFFERP (object))
1580 signal_after_change (XINT (start), XINT (end) - XINT (start),
1581 XINT (end) - XINT (start));
1582 return Qt;
1584 else
1585 { /* i has the properties, and goes past the change limit. */
1586 unchanged = i;
1587 i = split_interval_left (i, len);
1588 copy_properties (unchanged, i);
1589 if (!modified && BUFFERP (object))
1590 modify_region (object, start, end);
1591 remove_properties (Qnil, properties, i, object);
1592 if (BUFFERP (object))
1593 signal_after_change (XINT (start), XINT (end) - XINT (start),
1594 XINT (end) - XINT (start));
1595 return Qt;
1598 if (interval_has_some_properties_list (properties, i))
1600 if (!modified && BUFFERP (object))
1601 modify_region (object, start, end);
1602 remove_properties (Qnil, properties, i, object);
1603 modified = 1;
1605 len -= LENGTH (i);
1606 i = next_interval (i);
1610 DEFUN ("text-property-any", Ftext_property_any,
1611 Stext_property_any, 4, 5, 0,
1612 doc: /* Check text from START to END for property PROPERTY equaling VALUE.
1613 If so, return the position of the first character whose property PROPERTY
1614 is `eq' to VALUE. Otherwise return nil.
1615 If the optional fifth argument OBJECT is a buffer (or nil, which means
1616 the current buffer), START and END are buffer positions (integers or
1617 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1618 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1620 register INTERVAL i;
1621 register ptrdiff_t e, pos;
1623 if (NILP (object))
1624 XSETBUFFER (object, current_buffer);
1625 i = validate_interval_range (object, &start, &end, soft);
1626 if (!i)
1627 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1628 e = XINT (end);
1630 while (i)
1632 if (i->position >= e)
1633 break;
1634 if (EQ (textget (i->plist, property), value))
1636 pos = i->position;
1637 if (pos < XINT (start))
1638 pos = XINT (start);
1639 return make_number (pos);
1641 i = next_interval (i);
1643 return Qnil;
1646 DEFUN ("text-property-not-all", Ftext_property_not_all,
1647 Stext_property_not_all, 4, 5, 0,
1648 doc: /* Check text from START to END for property PROPERTY not equaling VALUE.
1649 If so, return the position of the first character whose property PROPERTY
1650 is not `eq' to VALUE. Otherwise, return nil.
1651 If the optional fifth argument OBJECT is a buffer (or nil, which means
1652 the current buffer), START and END are buffer positions (integers or
1653 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1654 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1656 register INTERVAL i;
1657 register ptrdiff_t s, e;
1659 if (NILP (object))
1660 XSETBUFFER (object, current_buffer);
1661 i = validate_interval_range (object, &start, &end, soft);
1662 if (!i)
1663 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1664 s = XINT (start);
1665 e = XINT (end);
1667 while (i)
1669 if (i->position >= e)
1670 break;
1671 if (! EQ (textget (i->plist, property), value))
1673 if (i->position > s)
1674 s = i->position;
1675 return make_number (s);
1677 i = next_interval (i);
1679 return Qnil;
1683 /* Return the direction from which the text-property PROP would be
1684 inherited by any new text inserted at POS: 1 if it would be
1685 inherited from the char after POS, -1 if it would be inherited from
1686 the char before POS, and 0 if from neither.
1687 BUFFER can be either a buffer or nil (meaning current buffer). */
1690 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1692 Lisp_Object prev_pos, front_sticky;
1693 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1694 Lisp_Object defalt = Fassq (prop, Vtext_property_default_nonsticky);
1696 if (NILP (buffer))
1697 XSETBUFFER (buffer, current_buffer);
1699 if (CONSP (defalt) && !NILP (XCDR (defalt)))
1700 is_rear_sticky = 0;
1702 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1703 /* Consider previous character. */
1705 Lisp_Object rear_non_sticky;
1707 prev_pos = make_number (XINT (pos) - 1);
1708 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1710 if (!NILP (CONSP (rear_non_sticky)
1711 ? Fmemq (prop, rear_non_sticky)
1712 : rear_non_sticky))
1713 /* PROP is rear-non-sticky. */
1714 is_rear_sticky = 0;
1716 else
1717 return 0;
1719 /* Consider following character. */
1720 /* This signals an arg-out-of-range error if pos is outside the
1721 buffer's accessible range. */
1722 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1724 if (EQ (front_sticky, Qt)
1725 || (CONSP (front_sticky)
1726 && !NILP (Fmemq (prop, front_sticky))))
1727 /* PROP is inherited from after. */
1728 is_front_sticky = 1;
1730 /* Simple cases, where the properties are consistent. */
1731 if (is_rear_sticky && !is_front_sticky)
1732 return -1;
1733 else if (!is_rear_sticky && is_front_sticky)
1734 return 1;
1735 else if (!is_rear_sticky && !is_front_sticky)
1736 return 0;
1738 /* The stickiness properties are inconsistent, so we have to
1739 disambiguate. Basically, rear-sticky wins, _except_ if the
1740 property that would be inherited has a value of nil, in which case
1741 front-sticky wins. */
1742 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1743 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1744 return 1;
1745 else
1746 return -1;
1750 /* Copying properties between objects. */
1752 /* Add properties from START to END of SRC, starting at POS in DEST.
1753 SRC and DEST may each refer to strings or buffers.
1754 Optional sixth argument PROP causes only that property to be copied.
1755 Properties are copied to DEST as if by `add-text-properties'.
1756 Return t if any property value actually changed, nil otherwise. */
1758 /* Note this can GC when DEST is a buffer. */
1760 Lisp_Object
1761 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src, Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1763 INTERVAL i;
1764 Lisp_Object res;
1765 Lisp_Object stuff;
1766 Lisp_Object plist;
1767 ptrdiff_t s, e, e2, p, len;
1768 int modified = 0;
1769 struct gcpro gcpro1, gcpro2;
1771 i = validate_interval_range (src, &start, &end, soft);
1772 if (!i)
1773 return Qnil;
1775 CHECK_NUMBER_COERCE_MARKER (pos);
1777 Lisp_Object dest_start, dest_end;
1779 e = XINT (pos) + (XINT (end) - XINT (start));
1780 if (MOST_POSITIVE_FIXNUM < e)
1781 args_out_of_range (pos, end);
1782 dest_start = pos;
1783 XSETFASTINT (dest_end, e);
1784 /* Apply this to a copy of pos; it will try to increment its arguments,
1785 which we don't want. */
1786 validate_interval_range (dest, &dest_start, &dest_end, soft);
1789 s = XINT (start);
1790 e = XINT (end);
1791 p = XINT (pos);
1793 stuff = Qnil;
1795 while (s < e)
1797 e2 = i->position + LENGTH (i);
1798 if (e2 > e)
1799 e2 = e;
1800 len = e2 - s;
1802 plist = i->plist;
1803 if (! NILP (prop))
1804 while (! NILP (plist))
1806 if (EQ (Fcar (plist), prop))
1808 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1809 break;
1811 plist = Fcdr (Fcdr (plist));
1813 if (! NILP (plist))
1815 /* Must defer modifications to the interval tree in case src
1816 and dest refer to the same string or buffer. */
1817 stuff = Fcons (Fcons (make_number (p),
1818 Fcons (make_number (p + len),
1819 Fcons (plist, Qnil))),
1820 stuff);
1823 i = next_interval (i);
1824 if (!i)
1825 break;
1827 p += len;
1828 s = i->position;
1831 GCPRO2 (stuff, dest);
1833 while (! NILP (stuff))
1835 res = Fcar (stuff);
1836 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1837 Fcar (Fcdr (Fcdr (res))), dest);
1838 if (! NILP (res))
1839 modified++;
1840 stuff = Fcdr (stuff);
1843 UNGCPRO;
1845 return modified ? Qt : Qnil;
1849 /* Return a list representing the text properties of OBJECT between
1850 START and END. if PROP is non-nil, report only on that property.
1851 Each result list element has the form (S E PLIST), where S and E
1852 are positions in OBJECT and PLIST is a property list containing the
1853 text properties of OBJECT between S and E. Value is nil if OBJECT
1854 doesn't contain text properties between START and END. */
1856 Lisp_Object
1857 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1859 struct interval *i;
1860 Lisp_Object result;
1862 result = Qnil;
1864 i = validate_interval_range (object, &start, &end, soft);
1865 if (i)
1867 ptrdiff_t s = XINT (start);
1868 ptrdiff_t e = XINT (end);
1870 while (s < e)
1872 ptrdiff_t interval_end, len;
1873 Lisp_Object plist;
1875 interval_end = i->position + LENGTH (i);
1876 if (interval_end > e)
1877 interval_end = e;
1878 len = interval_end - s;
1880 plist = i->plist;
1882 if (!NILP (prop))
1883 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1884 if (EQ (XCAR (plist), prop))
1886 plist = Fcons (prop, Fcons (Fcar (XCDR (plist)), Qnil));
1887 break;
1890 if (!NILP (plist))
1891 result = Fcons (Fcons (make_number (s),
1892 Fcons (make_number (s + len),
1893 Fcons (plist, Qnil))),
1894 result);
1896 i = next_interval (i);
1897 if (!i)
1898 break;
1899 s = i->position;
1903 return result;
1907 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1908 (START END PLIST), where START and END are positions and PLIST is a
1909 property list containing the text properties to add. Adjust START
1910 and END positions by DELTA before adding properties. Value is
1911 non-zero if OBJECT was modified. */
1914 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
1916 struct gcpro gcpro1, gcpro2;
1917 int modified_p = 0;
1919 GCPRO2 (list, object);
1921 for (; CONSP (list); list = XCDR (list))
1923 Lisp_Object item, start, end, plist, tem;
1925 item = XCAR (list);
1926 start = make_number (XINT (XCAR (item)) + XINT (delta));
1927 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1928 plist = XCAR (XCDR (XCDR (item)));
1930 tem = Fadd_text_properties (start, end, plist, object);
1931 if (!NILP (tem))
1932 modified_p = 1;
1935 UNGCPRO;
1936 return modified_p;
1941 /* Modify end-points of ranges in LIST destructively, and return the
1942 new list. LIST is a list as returned from text_property_list.
1943 Discard properties that begin at or after NEW_END, and limit
1944 end-points to NEW_END. */
1946 Lisp_Object
1947 extend_property_ranges (Lisp_Object list, Lisp_Object new_end)
1949 Lisp_Object prev = Qnil, head = list;
1950 ptrdiff_t max = XINT (new_end);
1952 for (; CONSP (list); prev = list, list = XCDR (list))
1954 Lisp_Object item, beg, end;
1956 item = XCAR (list);
1957 beg = XCAR (item);
1958 end = XCAR (XCDR (item));
1960 if (XINT (beg) >= max)
1962 /* The start-point is past the end of the new string.
1963 Discard this property. */
1964 if (EQ (head, list))
1965 head = XCDR (list);
1966 else
1967 XSETCDR (prev, XCDR (list));
1969 else if (XINT (end) > max)
1970 /* The end-point is past the end of the new string. */
1971 XSETCAR (XCDR (item), new_end);
1974 return head;
1979 /* Call the modification hook functions in LIST, each with START and END. */
1981 static void
1982 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
1984 struct gcpro gcpro1;
1985 GCPRO1 (list);
1986 while (!NILP (list))
1988 call2 (Fcar (list), start, end);
1989 list = Fcdr (list);
1991 UNGCPRO;
1994 /* Check for read-only intervals between character positions START ... END,
1995 in BUF, and signal an error if we find one.
1997 Then check for any modification hooks in the range.
1998 Create a list of all these hooks in lexicographic order,
1999 eliminating consecutive extra copies of the same hook. Then call
2000 those hooks in order, with START and END - 1 as arguments. */
2002 void
2003 verify_interval_modification (struct buffer *buf,
2004 ptrdiff_t start, ptrdiff_t end)
2006 INTERVAL intervals = buffer_intervals (buf);
2007 INTERVAL i;
2008 Lisp_Object hooks;
2009 Lisp_Object prev_mod_hooks;
2010 Lisp_Object mod_hooks;
2011 struct gcpro gcpro1;
2013 hooks = Qnil;
2014 prev_mod_hooks = Qnil;
2015 mod_hooks = Qnil;
2017 interval_insert_behind_hooks = Qnil;
2018 interval_insert_in_front_hooks = Qnil;
2020 if (!intervals)
2021 return;
2023 if (start > end)
2025 ptrdiff_t temp = start;
2026 start = end;
2027 end = temp;
2030 /* For an insert operation, check the two chars around the position. */
2031 if (start == end)
2033 INTERVAL prev = NULL;
2034 Lisp_Object before, after;
2036 /* Set I to the interval containing the char after START,
2037 and PREV to the interval containing the char before START.
2038 Either one may be null. They may be equal. */
2039 i = find_interval (intervals, start);
2041 if (start == BUF_BEGV (buf))
2042 prev = 0;
2043 else if (i->position == start)
2044 prev = previous_interval (i);
2045 else if (i->position < start)
2046 prev = i;
2047 if (start == BUF_ZV (buf))
2048 i = 0;
2050 /* If Vinhibit_read_only is set and is not a list, we can
2051 skip the read_only checks. */
2052 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2054 /* If I and PREV differ we need to check for the read-only
2055 property together with its stickiness. If either I or
2056 PREV are 0, this check is all we need.
2057 We have to take special care, since read-only may be
2058 indirectly defined via the category property. */
2059 if (i != prev)
2061 if (i)
2063 after = textget (i->plist, Qread_only);
2065 /* If interval I is read-only and read-only is
2066 front-sticky, inhibit insertion.
2067 Check for read-only as well as category. */
2068 if (! NILP (after)
2069 && NILP (Fmemq (after, Vinhibit_read_only)))
2071 Lisp_Object tem;
2073 tem = textget (i->plist, Qfront_sticky);
2074 if (TMEM (Qread_only, tem)
2075 || (NILP (Fplist_get (i->plist, Qread_only))
2076 && TMEM (Qcategory, tem)))
2077 text_read_only (after);
2081 if (prev)
2083 before = textget (prev->plist, Qread_only);
2085 /* If interval PREV is read-only and read-only isn't
2086 rear-nonsticky, inhibit insertion.
2087 Check for read-only as well as category. */
2088 if (! NILP (before)
2089 && NILP (Fmemq (before, Vinhibit_read_only)))
2091 Lisp_Object tem;
2093 tem = textget (prev->plist, Qrear_nonsticky);
2094 if (! TMEM (Qread_only, tem)
2095 && (! NILP (Fplist_get (prev->plist,Qread_only))
2096 || ! TMEM (Qcategory, tem)))
2097 text_read_only (before);
2101 else if (i)
2103 after = textget (i->plist, Qread_only);
2105 /* If interval I is read-only and read-only is
2106 front-sticky, inhibit insertion.
2107 Check for read-only as well as category. */
2108 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2110 Lisp_Object tem;
2112 tem = textget (i->plist, Qfront_sticky);
2113 if (TMEM (Qread_only, tem)
2114 || (NILP (Fplist_get (i->plist, Qread_only))
2115 && TMEM (Qcategory, tem)))
2116 text_read_only (after);
2118 tem = textget (prev->plist, Qrear_nonsticky);
2119 if (! TMEM (Qread_only, tem)
2120 && (! NILP (Fplist_get (prev->plist, Qread_only))
2121 || ! TMEM (Qcategory, tem)))
2122 text_read_only (after);
2127 /* Run both insert hooks (just once if they're the same). */
2128 if (prev)
2129 interval_insert_behind_hooks
2130 = textget (prev->plist, Qinsert_behind_hooks);
2131 if (i)
2132 interval_insert_in_front_hooks
2133 = textget (i->plist, Qinsert_in_front_hooks);
2135 else
2137 /* Loop over intervals on or next to START...END,
2138 collecting their hooks. */
2140 i = find_interval (intervals, start);
2143 if (! INTERVAL_WRITABLE_P (i))
2144 text_read_only (textget (i->plist, Qread_only));
2146 if (!inhibit_modification_hooks)
2148 mod_hooks = textget (i->plist, Qmodification_hooks);
2149 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2151 hooks = Fcons (mod_hooks, hooks);
2152 prev_mod_hooks = mod_hooks;
2156 i = next_interval (i);
2158 /* Keep going thru the interval containing the char before END. */
2159 while (i && i->position < end);
2161 if (!inhibit_modification_hooks)
2163 GCPRO1 (hooks);
2164 hooks = Fnreverse (hooks);
2165 while (! EQ (hooks, Qnil))
2167 call_mod_hooks (Fcar (hooks), make_number (start),
2168 make_number (end));
2169 hooks = Fcdr (hooks);
2171 UNGCPRO;
2176 /* Run the interval hooks for an insertion on character range START ... END.
2177 verify_interval_modification chose which hooks to run;
2178 this function is called after the insertion happens
2179 so it can indicate the range of inserted text. */
2181 void
2182 report_interval_modification (Lisp_Object start, Lisp_Object end)
2184 if (! NILP (interval_insert_behind_hooks))
2185 call_mod_hooks (interval_insert_behind_hooks, start, end);
2186 if (! NILP (interval_insert_in_front_hooks)
2187 && ! EQ (interval_insert_in_front_hooks,
2188 interval_insert_behind_hooks))
2189 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2192 void
2193 syms_of_textprop (void)
2195 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties,
2196 doc: /* Property-list used as default values.
2197 The value of a property in this list is seen as the value for every
2198 character that does not have its own value for that property. */);
2199 Vdefault_text_properties = Qnil;
2201 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist,
2202 doc: /* Alist of alternative properties for properties without a value.
2203 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2204 If a piece of text has no direct value for a particular property, then
2205 this alist is consulted. If that property appears in the alist, then
2206 the first non-nil value from the associated alternative properties is
2207 returned. */);
2208 Vchar_property_alias_alist = Qnil;
2210 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks,
2211 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2212 This also inhibits the use of the `intangible' text property. */);
2213 Vinhibit_point_motion_hooks = Qnil;
2215 DEFVAR_LISP ("text-property-default-nonsticky",
2216 Vtext_property_default_nonsticky,
2217 doc: /* Alist of properties vs the corresponding non-stickiness.
2218 Each element has the form (PROPERTY . NONSTICKINESS).
2220 If a character in a buffer has PROPERTY, new text inserted adjacent to
2221 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2222 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2223 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2224 /* Text properties `syntax-table'and `display' should be nonsticky
2225 by default. */
2226 Vtext_property_default_nonsticky
2227 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt),
2228 Fcons (Fcons (intern_c_string ("display"), Qt), Qnil));
2230 staticpro (&interval_insert_behind_hooks);
2231 staticpro (&interval_insert_in_front_hooks);
2232 interval_insert_behind_hooks = Qnil;
2233 interval_insert_in_front_hooks = Qnil;
2236 /* Common attributes one might give text */
2238 DEFSYM (Qforeground, "foreground");
2239 DEFSYM (Qbackground, "background");
2240 DEFSYM (Qfont, "font");
2241 DEFSYM (Qstipple, "stipple");
2242 DEFSYM (Qunderline, "underline");
2243 DEFSYM (Qread_only, "read-only");
2244 DEFSYM (Qinvisible, "invisible");
2245 DEFSYM (Qintangible, "intangible");
2246 DEFSYM (Qcategory, "category");
2247 DEFSYM (Qlocal_map, "local-map");
2248 DEFSYM (Qfront_sticky, "front-sticky");
2249 DEFSYM (Qrear_nonsticky, "rear-nonsticky");
2250 DEFSYM (Qmouse_face, "mouse-face");
2251 DEFSYM (Qminibuffer_prompt, "minibuffer-prompt");
2253 /* Properties that text might use to specify certain actions */
2255 DEFSYM (Qmouse_left, "mouse-left");
2256 DEFSYM (Qmouse_entered, "mouse-entered");
2257 DEFSYM (Qpoint_left, "point-left");
2258 DEFSYM (Qpoint_entered, "point-entered");
2260 defsubr (&Stext_properties_at);
2261 defsubr (&Sget_text_property);
2262 defsubr (&Sget_char_property);
2263 defsubr (&Sget_char_property_and_overlay);
2264 defsubr (&Snext_char_property_change);
2265 defsubr (&Sprevious_char_property_change);
2266 defsubr (&Snext_single_char_property_change);
2267 defsubr (&Sprevious_single_char_property_change);
2268 defsubr (&Snext_property_change);
2269 defsubr (&Snext_single_property_change);
2270 defsubr (&Sprevious_property_change);
2271 defsubr (&Sprevious_single_property_change);
2272 defsubr (&Sadd_text_properties);
2273 defsubr (&Sput_text_property);
2274 defsubr (&Sset_text_properties);
2275 defsubr (&Sremove_text_properties);
2276 defsubr (&Sremove_list_of_text_properties);
2277 defsubr (&Stext_property_any);
2278 defsubr (&Stext_property_not_all);