Fix bug #13553 with usage of IS_DIRECTORY_SEP on MS-Windows under DBCS.
[emacs.git] / src / textprop.c
blob902ef248c4931f58e3a8701953824dbb6921541a
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);
91 /* Extract the interval at the position pointed to by BEGIN from
92 OBJECT, a string or buffer. Additionally, check that the positions
93 pointed to by BEGIN and END are within the bounds of OBJECT, and
94 reverse them if *BEGIN is greater than *END. The objects pointed
95 to by BEGIN and END may be integers or markers; if the latter, they
96 are coerced to integers.
98 When OBJECT is a string, we increment *BEGIN and *END
99 to make them origin-one.
101 Note that buffer points don't correspond to interval indices.
102 For example, point-max is 1 greater than the index of the last
103 character. This difference is handled in the caller, which uses
104 the validated points to determine a length, and operates on that.
105 Exceptions are Ftext_properties_at, Fnext_property_change, and
106 Fprevious_property_change which call this function with BEGIN == END.
107 Handle this case specially.
109 If FORCE is soft (0), it's OK to return NULL. Otherwise,
110 create an interval tree for OBJECT if one doesn't exist, provided
111 the object actually contains text. In the current design, if there
112 is no text, there can be no text properties. */
114 #define soft 0
115 #define hard 1
117 INTERVAL
118 validate_interval_range (Lisp_Object object, Lisp_Object *begin, Lisp_Object *end, int force)
120 register INTERVAL i;
121 ptrdiff_t searchpos;
123 CHECK_STRING_OR_BUFFER (object);
124 CHECK_NUMBER_COERCE_MARKER (*begin);
125 CHECK_NUMBER_COERCE_MARKER (*end);
127 /* If we are asked for a point, but from a subr which operates
128 on a range, then return nothing. */
129 if (EQ (*begin, *end) && begin != end)
130 return NULL;
132 if (XINT (*begin) > XINT (*end))
134 Lisp_Object n;
135 n = *begin;
136 *begin = *end;
137 *end = n;
140 if (BUFFERP (object))
142 register struct buffer *b = XBUFFER (object);
144 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
145 && XINT (*end) <= BUF_ZV (b)))
146 args_out_of_range (*begin, *end);
147 i = buffer_intervals (b);
149 /* If there's no text, there are no properties. */
150 if (BUF_BEGV (b) == BUF_ZV (b))
151 return NULL;
153 searchpos = XINT (*begin);
155 else
157 ptrdiff_t len = SCHARS (object);
159 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
160 && XINT (*end) <= len))
161 args_out_of_range (*begin, *end);
162 XSETFASTINT (*begin, XFASTINT (*begin));
163 if (begin != end)
164 XSETFASTINT (*end, XFASTINT (*end));
165 i = string_intervals (object);
167 if (len == 0)
168 return NULL;
170 searchpos = XINT (*begin);
173 if (!i)
174 return (force ? create_root_interval (object) : i);
176 return find_interval (i, searchpos);
179 /* Validate LIST as a property list. If LIST is not a list, then
180 make one consisting of (LIST nil). Otherwise, verify that LIST
181 is even numbered and thus suitable as a plist. */
183 static Lisp_Object
184 validate_plist (Lisp_Object list)
186 if (NILP (list))
187 return Qnil;
189 if (CONSP (list))
191 register int i;
192 register Lisp_Object tail;
193 for (i = 0, tail = list; CONSP (tail); i++)
195 tail = XCDR (tail);
196 QUIT;
198 if (i & 1)
199 error ("Odd length text property list");
200 return list;
203 return Fcons (list, Fcons (Qnil, Qnil));
206 /* Return nonzero if interval I has all the properties,
207 with the same values, of list PLIST. */
209 static int
210 interval_has_all_properties (Lisp_Object plist, INTERVAL i)
212 register Lisp_Object tail1, tail2, sym1;
213 register int found;
215 /* Go through each element of PLIST. */
216 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
218 sym1 = XCAR (tail1);
219 found = 0;
221 /* Go through I's plist, looking for sym1 */
222 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
223 if (EQ (sym1, XCAR (tail2)))
225 /* Found the same property on both lists. If the
226 values are unequal, return zero. */
227 if (! EQ (Fcar (XCDR (tail1)), Fcar (XCDR (tail2))))
228 return 0;
230 /* Property has same value on both lists; go to next one. */
231 found = 1;
232 break;
235 if (! found)
236 return 0;
239 return 1;
242 /* Return nonzero if the plist of interval I has any of the
243 properties of PLIST, regardless of their values. */
245 static int
246 interval_has_some_properties (Lisp_Object plist, INTERVAL i)
248 register Lisp_Object tail1, tail2, sym;
250 /* Go through each element of PLIST. */
251 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
253 sym = XCAR (tail1);
255 /* Go through i's plist, looking for tail1 */
256 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
257 if (EQ (sym, XCAR (tail2)))
258 return 1;
261 return 0;
264 /* Return nonzero if the plist of interval I has any of the
265 property names in LIST, regardless of their values. */
267 static int
268 interval_has_some_properties_list (Lisp_Object list, INTERVAL i)
270 register Lisp_Object tail1, tail2, sym;
272 /* Go through each element of LIST. */
273 for (tail1 = list; CONSP (tail1); tail1 = XCDR (tail1))
275 sym = XCAR (tail1);
277 /* Go through i's plist, looking for tail1 */
278 for (tail2 = i->plist; CONSP (tail2); tail2 = XCDR (XCDR (tail2)))
279 if (EQ (sym, XCAR (tail2)))
280 return 1;
283 return 0;
286 /* Changing the plists of individual intervals. */
288 /* Return the value of PROP in property-list PLIST, or Qunbound if it
289 has none. */
290 static Lisp_Object
291 property_value (Lisp_Object plist, Lisp_Object prop)
293 Lisp_Object value;
295 while (PLIST_ELT_P (plist, value))
296 if (EQ (XCAR (plist), prop))
297 return XCAR (value);
298 else
299 plist = XCDR (value);
301 return Qunbound;
304 /* Set the properties of INTERVAL to PROPERTIES,
305 and record undo info for the previous values.
306 OBJECT is the string or buffer that INTERVAL belongs to. */
308 static void
309 set_properties (Lisp_Object properties, INTERVAL interval, Lisp_Object object)
311 Lisp_Object sym, value;
313 if (BUFFERP (object))
315 /* For each property in the old plist which is missing from PROPERTIES,
316 or has a different value in PROPERTIES, make an undo record. */
317 for (sym = interval->plist;
318 PLIST_ELT_P (sym, value);
319 sym = XCDR (value))
320 if (! EQ (property_value (properties, XCAR (sym)),
321 XCAR (value)))
323 record_property_change (interval->position, LENGTH (interval),
324 XCAR (sym), XCAR (value),
325 object);
328 /* For each new property that has no value at all in the old plist,
329 make an undo record binding it to nil, so it will be removed. */
330 for (sym = properties;
331 PLIST_ELT_P (sym, value);
332 sym = XCDR (value))
333 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
335 record_property_change (interval->position, LENGTH (interval),
336 XCAR (sym), Qnil,
337 object);
341 /* Store new properties. */
342 set_interval_plist (interval, Fcopy_sequence (properties));
345 /* Add the properties of PLIST to the interval I, or set
346 the value of I's property to the value of the property on PLIST
347 if they are different.
349 OBJECT should be the string or buffer the interval is in.
351 Return nonzero if this changes I (i.e., if any members of PLIST
352 are actually added to I's plist) */
354 static int
355 add_properties (Lisp_Object plist, INTERVAL i, Lisp_Object object)
357 Lisp_Object tail1, tail2, sym1, val1;
358 register int changed = 0;
359 register int found;
360 struct gcpro gcpro1, gcpro2, gcpro3;
362 tail1 = plist;
363 sym1 = Qnil;
364 val1 = Qnil;
365 /* No need to protect OBJECT, because we can GC only in the case
366 where it is a buffer, and live buffers are always protected.
367 I and its plist are also protected, via OBJECT. */
368 GCPRO3 (tail1, sym1, val1);
370 /* Go through each element of PLIST. */
371 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
373 sym1 = XCAR (tail1);
374 val1 = Fcar (XCDR (tail1));
375 found = 0;
377 /* Go through I's plist, looking for sym1 */
378 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
379 if (EQ (sym1, XCAR (tail2)))
381 /* No need to gcpro, because tail2 protects this
382 and it must be a cons cell (we get an error otherwise). */
383 register Lisp_Object this_cdr;
385 this_cdr = XCDR (tail2);
386 /* Found the property. Now check its value. */
387 found = 1;
389 /* The properties have the same value on both lists.
390 Continue to the next property. */
391 if (EQ (val1, Fcar (this_cdr)))
392 break;
394 /* Record this change in the buffer, for undo purposes. */
395 if (BUFFERP (object))
397 record_property_change (i->position, LENGTH (i),
398 sym1, Fcar (this_cdr), object);
401 /* I's property has a different value -- change it */
402 Fsetcar (this_cdr, val1);
403 changed++;
404 break;
407 if (! found)
409 /* Record this change in the buffer, for undo purposes. */
410 if (BUFFERP (object))
412 record_property_change (i->position, LENGTH (i),
413 sym1, Qnil, object);
415 set_interval_plist (i, Fcons (sym1, Fcons (val1, i->plist)));
416 changed++;
420 UNGCPRO;
422 return changed;
425 /* For any members of PLIST, or LIST,
426 which are properties of I, remove them from I's plist.
427 (If PLIST is non-nil, use that, otherwise use LIST.)
428 OBJECT is the string or buffer containing I. */
430 static int
431 remove_properties (Lisp_Object plist, Lisp_Object list, INTERVAL i, Lisp_Object object)
433 register Lisp_Object tail1, tail2, sym, current_plist;
434 register int changed = 0;
436 /* Nonzero means tail1 is a plist, otherwise it is a list. */
437 int use_plist;
439 current_plist = i->plist;
441 if (! NILP (plist))
442 tail1 = plist, use_plist = 1;
443 else
444 tail1 = list, use_plist = 0;
446 /* Go through each element of LIST or PLIST. */
447 while (CONSP (tail1))
449 sym = XCAR (tail1);
451 /* First, remove the symbol if it's at the head of the list */
452 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
454 if (BUFFERP (object))
455 record_property_change (i->position, LENGTH (i),
456 sym, XCAR (XCDR (current_plist)),
457 object);
459 current_plist = XCDR (XCDR (current_plist));
460 changed++;
463 /* Go through I's plist, looking for SYM. */
464 tail2 = current_plist;
465 while (! NILP (tail2))
467 register Lisp_Object this;
468 this = XCDR (XCDR (tail2));
469 if (CONSP (this) && EQ (sym, XCAR (this)))
471 if (BUFFERP (object))
472 record_property_change (i->position, LENGTH (i),
473 sym, XCAR (XCDR (this)), object);
475 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
476 changed++;
478 tail2 = this;
481 /* Advance thru TAIL1 one way or the other. */
482 tail1 = XCDR (tail1);
483 if (use_plist && CONSP (tail1))
484 tail1 = XCDR (tail1);
487 if (changed)
488 set_interval_plist (i, current_plist);
489 return changed;
492 /* Returns the interval of POSITION in OBJECT.
493 POSITION is BEG-based. */
495 INTERVAL
496 interval_of (ptrdiff_t position, Lisp_Object object)
498 register INTERVAL i;
499 ptrdiff_t beg, end;
501 if (NILP (object))
502 XSETBUFFER (object, current_buffer);
503 else if (EQ (object, Qt))
504 return NULL;
506 CHECK_STRING_OR_BUFFER (object);
508 if (BUFFERP (object))
510 register struct buffer *b = XBUFFER (object);
512 beg = BUF_BEGV (b);
513 end = BUF_ZV (b);
514 i = buffer_intervals (b);
516 else
518 beg = 0;
519 end = SCHARS (object);
520 i = string_intervals (object);
523 if (!(beg <= position && position <= end))
524 args_out_of_range (make_number (position), make_number (position));
525 if (beg == end || !i)
526 return NULL;
528 return find_interval (i, position);
531 DEFUN ("text-properties-at", Ftext_properties_at,
532 Stext_properties_at, 1, 2, 0,
533 doc: /* Return the list of properties of the character at POSITION in OBJECT.
534 If the optional second argument OBJECT is a buffer (or nil, which means
535 the current buffer), POSITION is a buffer position (integer or marker).
536 If OBJECT is a string, POSITION is a 0-based index into it.
537 If POSITION is at the end of OBJECT, the value is nil. */)
538 (Lisp_Object position, Lisp_Object object)
540 register INTERVAL i;
542 if (NILP (object))
543 XSETBUFFER (object, current_buffer);
545 i = validate_interval_range (object, &position, &position, soft);
546 if (!i)
547 return Qnil;
548 /* If POSITION is at the end of the interval,
549 it means it's the end of OBJECT.
550 There are no properties at the very end,
551 since no character follows. */
552 if (XINT (position) == LENGTH (i) + i->position)
553 return Qnil;
555 return i->plist;
558 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
559 doc: /* Return the value of POSITION's property PROP, in OBJECT.
560 OBJECT should be a buffer or a string; if omitted or nil, it defaults
561 to the current buffer.
562 If POSITION is at the end of OBJECT, the value is nil. */)
563 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
565 return textget (Ftext_properties_at (position, object), prop);
568 /* Return the value of char's property PROP, in OBJECT at POSITION.
569 OBJECT is optional and defaults to the current buffer.
570 If OVERLAY is non-0, then in the case that the returned property is from
571 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
572 returned in *OVERLAY.
573 If POSITION is at the end of OBJECT, the value is nil.
574 If OBJECT is a buffer, then overlay properties are considered as well as
575 text properties.
576 If OBJECT is a window, then that window's buffer is used, but
577 window-specific overlays are considered only if they are associated
578 with OBJECT. */
579 Lisp_Object
580 get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object object, Lisp_Object *overlay)
582 struct window *w = 0;
584 CHECK_NUMBER_COERCE_MARKER (position);
586 if (NILP (object))
587 XSETBUFFER (object, current_buffer);
589 if (WINDOWP (object))
591 w = XWINDOW (object);
592 object = w->buffer;
594 if (BUFFERP (object))
596 ptrdiff_t noverlays;
597 Lisp_Object *overlay_vec;
598 struct buffer *obuf = current_buffer;
600 if (XINT (position) < BUF_BEGV (XBUFFER (object))
601 || XINT (position) > BUF_ZV (XBUFFER (object)))
602 xsignal1 (Qargs_out_of_range, position);
604 set_buffer_temp (XBUFFER (object));
606 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, 0);
607 noverlays = sort_overlays (overlay_vec, noverlays, w);
609 set_buffer_temp (obuf);
611 /* Now check the overlays in order of decreasing priority. */
612 while (--noverlays >= 0)
614 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
615 if (!NILP (tem))
617 if (overlay)
618 /* Return the overlay we got the property from. */
619 *overlay = overlay_vec[noverlays];
620 return tem;
625 if (overlay)
626 /* Indicate that the return value is not from an overlay. */
627 *overlay = Qnil;
629 /* Not a buffer, or no appropriate overlay, so fall through to the
630 simpler case. */
631 return Fget_text_property (position, prop, object);
634 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
635 doc: /* Return the value of POSITION's property PROP, in OBJECT.
636 Both overlay properties and text properties are checked.
637 OBJECT is optional and defaults to the current buffer.
638 If POSITION is at the end of OBJECT, the value is nil.
639 If OBJECT is a buffer, then overlay properties are considered as well as
640 text properties.
641 If OBJECT is a window, then that window's buffer is used, but window-specific
642 overlays are considered only if they are associated with OBJECT. */)
643 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
645 return get_char_property_and_overlay (position, prop, object, 0);
648 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
649 Sget_char_property_and_overlay, 2, 3, 0,
650 doc: /* Like `get-char-property', but with extra overlay information.
651 The value is a cons cell. Its car is the return value of `get-char-property'
652 with the same arguments--that is, the value of POSITION's property
653 PROP in OBJECT. Its cdr is the overlay in which the property was
654 found, or nil, if it was found as a text property or not found at all.
656 OBJECT is optional and defaults to the current buffer. OBJECT may be
657 a string, a buffer or a window. For strings, the cdr of the return
658 value is always nil, since strings do not have overlays. If OBJECT is
659 a window, then that window's buffer is used, but window-specific
660 overlays are considered only if they are associated with OBJECT. If
661 POSITION is at the end of OBJECT, both car and cdr are nil. */)
662 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
664 Lisp_Object overlay;
665 Lisp_Object val
666 = get_char_property_and_overlay (position, prop, object, &overlay);
667 return Fcons (val, overlay);
671 DEFUN ("next-char-property-change", Fnext_char_property_change,
672 Snext_char_property_change, 1, 2, 0,
673 doc: /* Return the position of next text property or overlay change.
674 This scans characters forward in the current buffer from POSITION till
675 it finds a change in some text property, or the beginning or end of an
676 overlay, and returns the position of that.
677 If none is found up to (point-max), the function returns (point-max).
679 If the optional second argument LIMIT is non-nil, don't search
680 past position LIMIT; return LIMIT if nothing is found before LIMIT.
681 LIMIT is a no-op if it is greater than (point-max). */)
682 (Lisp_Object position, Lisp_Object limit)
684 Lisp_Object temp;
686 temp = Fnext_overlay_change (position);
687 if (! NILP (limit))
689 CHECK_NUMBER_COERCE_MARKER (limit);
690 if (XINT (limit) < XINT (temp))
691 temp = limit;
693 return Fnext_property_change (position, Qnil, temp);
696 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
697 Sprevious_char_property_change, 1, 2, 0,
698 doc: /* Return the position of previous text property or overlay change.
699 Scans characters backward in the current buffer from POSITION till it
700 finds a change in some text property, or the beginning or end of an
701 overlay, and returns the position of that.
702 If none is found since (point-min), the function returns (point-min).
704 If the optional second argument LIMIT is non-nil, don't search
705 past position LIMIT; return LIMIT if nothing is found before LIMIT.
706 LIMIT is a no-op if it is less than (point-min). */)
707 (Lisp_Object position, Lisp_Object limit)
709 Lisp_Object temp;
711 temp = Fprevious_overlay_change (position);
712 if (! NILP (limit))
714 CHECK_NUMBER_COERCE_MARKER (limit);
715 if (XINT (limit) > XINT (temp))
716 temp = limit;
718 return Fprevious_property_change (position, Qnil, temp);
722 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
723 Snext_single_char_property_change, 2, 4, 0,
724 doc: /* Return the position of next text property or overlay change for a specific property.
725 Scans characters forward from POSITION till it finds
726 a change in the PROP property, then returns the position of the change.
727 If the optional third argument OBJECT is a buffer (or nil, which means
728 the current buffer), POSITION is a buffer position (integer or marker).
729 If OBJECT is a string, POSITION is a 0-based index into it.
731 In a string, scan runs to the end of the string.
732 In a buffer, it runs to (point-max), and the value cannot exceed that.
734 The property values are compared with `eq'.
735 If the property is constant all the way to the end of OBJECT, return the
736 last valid position in OBJECT.
737 If the optional fourth argument LIMIT is non-nil, don't search
738 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
739 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
741 if (STRINGP (object))
743 position = Fnext_single_property_change (position, prop, object, limit);
744 if (NILP (position))
746 if (NILP (limit))
747 position = make_number (SCHARS (object));
748 else
750 CHECK_NUMBER (limit);
751 position = limit;
755 else
757 Lisp_Object initial_value, value;
758 ptrdiff_t count = SPECPDL_INDEX ();
760 if (! NILP (object))
761 CHECK_BUFFER (object);
763 if (BUFFERP (object) && current_buffer != XBUFFER (object))
765 record_unwind_current_buffer ();
766 Fset_buffer (object);
769 CHECK_NUMBER_COERCE_MARKER (position);
771 initial_value = Fget_char_property (position, prop, object);
773 if (NILP (limit))
774 XSETFASTINT (limit, ZV);
775 else
776 CHECK_NUMBER_COERCE_MARKER (limit);
778 if (XFASTINT (position) >= XFASTINT (limit))
780 position = limit;
781 if (XFASTINT (position) > ZV)
782 XSETFASTINT (position, ZV);
784 else
785 while (1)
787 position = Fnext_char_property_change (position, limit);
788 if (XFASTINT (position) >= XFASTINT (limit))
790 position = limit;
791 break;
794 value = Fget_char_property (position, prop, object);
795 if (!EQ (value, initial_value))
796 break;
799 unbind_to (count, Qnil);
802 return position;
805 DEFUN ("previous-single-char-property-change",
806 Fprevious_single_char_property_change,
807 Sprevious_single_char_property_change, 2, 4, 0,
808 doc: /* Return the position of previous text property or overlay change for a specific property.
809 Scans characters backward from POSITION till it finds
810 a change in the PROP property, then returns the position of the change.
811 If the optional third argument OBJECT is a buffer (or nil, which means
812 the current buffer), POSITION is a buffer position (integer or marker).
813 If OBJECT is a string, POSITION is a 0-based index into it.
815 In a string, scan runs to the start of the string.
816 In a buffer, it runs to (point-min), and the value cannot be less than that.
818 The property values are compared with `eq'.
819 If the property is constant all the way to the start of OBJECT, return the
820 first valid position in OBJECT.
821 If the optional fourth argument LIMIT is non-nil, don't search back past
822 position LIMIT; return LIMIT if nothing is found before reaching LIMIT. */)
823 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
825 if (STRINGP (object))
827 position = Fprevious_single_property_change (position, prop, object, limit);
828 if (NILP (position))
830 if (NILP (limit))
831 position = make_number (0);
832 else
834 CHECK_NUMBER (limit);
835 position = limit;
839 else
841 ptrdiff_t count = SPECPDL_INDEX ();
843 if (! NILP (object))
844 CHECK_BUFFER (object);
846 if (BUFFERP (object) && current_buffer != XBUFFER (object))
848 record_unwind_current_buffer ();
849 Fset_buffer (object);
852 CHECK_NUMBER_COERCE_MARKER (position);
854 if (NILP (limit))
855 XSETFASTINT (limit, BEGV);
856 else
857 CHECK_NUMBER_COERCE_MARKER (limit);
859 if (XFASTINT (position) <= XFASTINT (limit))
861 position = limit;
862 if (XFASTINT (position) < BEGV)
863 XSETFASTINT (position, BEGV);
865 else
867 Lisp_Object initial_value
868 = Fget_char_property (make_number (XFASTINT (position) - 1),
869 prop, object);
871 while (1)
873 position = Fprevious_char_property_change (position, limit);
875 if (XFASTINT (position) <= XFASTINT (limit))
877 position = limit;
878 break;
880 else
882 Lisp_Object value
883 = Fget_char_property (make_number (XFASTINT (position) - 1),
884 prop, object);
886 if (!EQ (value, initial_value))
887 break;
892 unbind_to (count, Qnil);
895 return position;
898 DEFUN ("next-property-change", Fnext_property_change,
899 Snext_property_change, 1, 3, 0,
900 doc: /* Return the position of next property change.
901 Scans characters forward from POSITION in OBJECT till it finds
902 a change in some text property, then returns the position of the change.
903 If the optional second argument OBJECT is a buffer (or nil, which means
904 the current buffer), POSITION is a buffer position (integer or marker).
905 If OBJECT is a string, POSITION is a 0-based index into it.
906 Return nil if the property is constant all the way to the end of OBJECT.
907 If the value is non-nil, it is a position greater than POSITION, never equal.
909 If the optional third argument LIMIT is non-nil, don't search
910 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
911 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
913 register INTERVAL i, next;
915 if (NILP (object))
916 XSETBUFFER (object, current_buffer);
918 if (!NILP (limit) && !EQ (limit, Qt))
919 CHECK_NUMBER_COERCE_MARKER (limit);
921 i = validate_interval_range (object, &position, &position, soft);
923 /* If LIMIT is t, return start of next interval--don't
924 bother checking further intervals. */
925 if (EQ (limit, Qt))
927 if (!i)
928 next = i;
929 else
930 next = next_interval (i);
932 if (!next)
933 XSETFASTINT (position, (STRINGP (object)
934 ? SCHARS (object)
935 : BUF_ZV (XBUFFER (object))));
936 else
937 XSETFASTINT (position, next->position);
938 return position;
941 if (!i)
942 return limit;
944 next = next_interval (i);
946 while (next && intervals_equal (i, next)
947 && (NILP (limit) || next->position < XFASTINT (limit)))
948 next = next_interval (next);
950 if (!next
951 || (next->position
952 >= (INTEGERP (limit)
953 ? XFASTINT (limit)
954 : (STRINGP (object)
955 ? SCHARS (object)
956 : BUF_ZV (XBUFFER (object))))))
957 return limit;
958 else
959 return make_number (next->position);
962 DEFUN ("next-single-property-change", Fnext_single_property_change,
963 Snext_single_property_change, 2, 4, 0,
964 doc: /* Return the position of next property change for a specific property.
965 Scans characters forward from POSITION till it finds
966 a change in the PROP property, then returns the position of the change.
967 If the optional third argument OBJECT is a buffer (or nil, which means
968 the current buffer), POSITION is a buffer position (integer or marker).
969 If OBJECT is a string, POSITION is a 0-based index into it.
970 The property values are compared with `eq'.
971 Return nil if the property is constant all the way to the end of OBJECT.
972 If the value is non-nil, it is a position greater than POSITION, never equal.
974 If the optional fourth argument LIMIT is non-nil, don't search
975 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
976 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
978 register INTERVAL i, next;
979 register Lisp_Object here_val;
981 if (NILP (object))
982 XSETBUFFER (object, current_buffer);
984 if (!NILP (limit))
985 CHECK_NUMBER_COERCE_MARKER (limit);
987 i = validate_interval_range (object, &position, &position, soft);
988 if (!i)
989 return limit;
991 here_val = textget (i->plist, prop);
992 next = next_interval (i);
993 while (next
994 && EQ (here_val, textget (next->plist, prop))
995 && (NILP (limit) || next->position < XFASTINT (limit)))
996 next = next_interval (next);
998 if (!next
999 || (next->position
1000 >= (INTEGERP (limit)
1001 ? XFASTINT (limit)
1002 : (STRINGP (object)
1003 ? SCHARS (object)
1004 : BUF_ZV (XBUFFER (object))))))
1005 return limit;
1006 else
1007 return make_number (next->position);
1010 DEFUN ("previous-property-change", Fprevious_property_change,
1011 Sprevious_property_change, 1, 3, 0,
1012 doc: /* Return the position of previous property change.
1013 Scans characters backwards from POSITION in OBJECT till it finds
1014 a change in some text property, then returns the position of the change.
1015 If the optional second argument OBJECT is a buffer (or nil, which means
1016 the current buffer), POSITION is a buffer position (integer or marker).
1017 If OBJECT is a string, POSITION is a 0-based index into it.
1018 Return nil if the property is constant all the way to the start of OBJECT.
1019 If the value is non-nil, it is a position less than POSITION, never equal.
1021 If the optional third argument LIMIT is non-nil, don't search
1022 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1023 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
1025 register INTERVAL i, previous;
1027 if (NILP (object))
1028 XSETBUFFER (object, current_buffer);
1030 if (!NILP (limit))
1031 CHECK_NUMBER_COERCE_MARKER (limit);
1033 i = validate_interval_range (object, &position, &position, soft);
1034 if (!i)
1035 return limit;
1037 /* Start with the interval containing the char before point. */
1038 if (i->position == XFASTINT (position))
1039 i = previous_interval (i);
1041 previous = previous_interval (i);
1042 while (previous && intervals_equal (previous, i)
1043 && (NILP (limit)
1044 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1045 previous = previous_interval (previous);
1047 if (!previous
1048 || (previous->position + LENGTH (previous)
1049 <= (INTEGERP (limit)
1050 ? XFASTINT (limit)
1051 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1052 return limit;
1053 else
1054 return make_number (previous->position + LENGTH (previous));
1057 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1058 Sprevious_single_property_change, 2, 4, 0,
1059 doc: /* Return the position of previous property change for a specific property.
1060 Scans characters backward from POSITION till it finds
1061 a change in the PROP property, then returns the position of the change.
1062 If the optional third argument OBJECT is a buffer (or nil, which means
1063 the current buffer), POSITION is a buffer position (integer or marker).
1064 If OBJECT is a string, POSITION is a 0-based index into it.
1065 The property values are compared with `eq'.
1066 Return nil if the property is constant all the way to the start of OBJECT.
1067 If the value is non-nil, it is a position less than POSITION, never equal.
1069 If the optional fourth argument LIMIT is non-nil, don't search
1070 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1071 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1073 register INTERVAL i, previous;
1074 register Lisp_Object here_val;
1076 if (NILP (object))
1077 XSETBUFFER (object, current_buffer);
1079 if (!NILP (limit))
1080 CHECK_NUMBER_COERCE_MARKER (limit);
1082 i = validate_interval_range (object, &position, &position, soft);
1084 /* Start with the interval containing the char before point. */
1085 if (i && i->position == XFASTINT (position))
1086 i = previous_interval (i);
1088 if (!i)
1089 return limit;
1091 here_val = textget (i->plist, prop);
1092 previous = previous_interval (i);
1093 while (previous
1094 && EQ (here_val, textget (previous->plist, prop))
1095 && (NILP (limit)
1096 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1097 previous = previous_interval (previous);
1099 if (!previous
1100 || (previous->position + LENGTH (previous)
1101 <= (INTEGERP (limit)
1102 ? XFASTINT (limit)
1103 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1104 return limit;
1105 else
1106 return make_number (previous->position + LENGTH (previous));
1109 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1111 DEFUN ("add-text-properties", Fadd_text_properties,
1112 Sadd_text_properties, 3, 4, 0,
1113 doc: /* Add properties to the text from START to END.
1114 The third argument PROPERTIES is a property list
1115 specifying the property values to add. If the optional fourth argument
1116 OBJECT is a buffer (or nil, which means the current buffer),
1117 START and END are buffer positions (integers or markers).
1118 If OBJECT is a string, START and END are 0-based indices into it.
1119 Return t if any property value actually changed, nil otherwise. */)
1120 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1122 register INTERVAL i, unchanged;
1123 register ptrdiff_t s, len;
1124 register int modified = 0;
1125 struct gcpro gcpro1;
1127 properties = validate_plist (properties);
1128 if (NILP (properties))
1129 return Qnil;
1131 if (NILP (object))
1132 XSETBUFFER (object, current_buffer);
1134 i = validate_interval_range (object, &start, &end, hard);
1135 if (!i)
1136 return Qnil;
1138 s = XINT (start);
1139 len = XINT (end) - s;
1141 /* No need to protect OBJECT, because we GC only if it's a buffer,
1142 and live buffers are always protected. */
1143 GCPRO1 (properties);
1145 /* If we're not starting on an interval boundary, we have to
1146 split this interval. */
1147 if (i->position != s)
1149 /* If this interval already has the properties, we can
1150 skip it. */
1151 if (interval_has_all_properties (properties, i))
1153 ptrdiff_t got = (LENGTH (i) - (s - i->position));
1154 if (got >= len)
1155 RETURN_UNGCPRO (Qnil);
1156 len -= got;
1157 i = next_interval (i);
1159 else
1161 unchanged = i;
1162 i = split_interval_right (unchanged, s - unchanged->position);
1163 copy_properties (unchanged, i);
1167 if (BUFFERP (object))
1168 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1170 /* We are at the beginning of interval I, with LEN chars to scan. */
1171 for (;;)
1173 eassert (i != 0);
1175 if (LENGTH (i) >= len)
1177 /* We can UNGCPRO safely here, because there will be just
1178 one more chance to gc, in the next call to add_properties,
1179 and after that we will not need PROPERTIES or OBJECT again. */
1180 UNGCPRO;
1182 if (interval_has_all_properties (properties, i))
1184 if (BUFFERP (object))
1185 signal_after_change (XINT (start), XINT (end) - XINT (start),
1186 XINT (end) - XINT (start));
1188 return modified ? Qt : Qnil;
1191 if (LENGTH (i) == len)
1193 add_properties (properties, i, object);
1194 if (BUFFERP (object))
1195 signal_after_change (XINT (start), XINT (end) - XINT (start),
1196 XINT (end) - XINT (start));
1197 return Qt;
1200 /* i doesn't have the properties, and goes past the change limit */
1201 unchanged = i;
1202 i = split_interval_left (unchanged, len);
1203 copy_properties (unchanged, i);
1204 add_properties (properties, i, object);
1205 if (BUFFERP (object))
1206 signal_after_change (XINT (start), XINT (end) - XINT (start),
1207 XINT (end) - XINT (start));
1208 return Qt;
1211 len -= LENGTH (i);
1212 modified += add_properties (properties, i, object);
1213 i = next_interval (i);
1217 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1219 DEFUN ("put-text-property", Fput_text_property,
1220 Sput_text_property, 4, 5, 0,
1221 doc: /* Set one property of the text from START to END.
1222 The third and fourth arguments PROPERTY and VALUE
1223 specify the property to add.
1224 If the optional fifth argument OBJECT is a buffer (or nil, which means
1225 the current buffer), START and END are buffer positions (integers or
1226 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1227 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1229 Fadd_text_properties (start, end,
1230 Fcons (property, Fcons (value, Qnil)),
1231 object);
1232 return Qnil;
1235 DEFUN ("set-text-properties", Fset_text_properties,
1236 Sset_text_properties, 3, 4, 0,
1237 doc: /* Completely replace properties of text from START to END.
1238 The third argument PROPERTIES is the new property list.
1239 If the optional fourth argument OBJECT is a buffer (or nil, which means
1240 the current buffer), START and END are buffer positions (integers or
1241 markers). If OBJECT is a string, START and END are 0-based indices into it.
1242 If PROPERTIES is nil, the effect is to remove all properties from
1243 the designated part of OBJECT. */)
1244 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1246 return set_text_properties (start, end, properties, object, Qt);
1250 /* Replace properties of text from START to END with new list of
1251 properties PROPERTIES. OBJECT is the buffer or string containing
1252 the text. OBJECT nil means use the current buffer.
1253 COHERENT_CHANGE_P nil means this is being called as an internal
1254 subroutine, rather than as a change primitive with checking of
1255 read-only, invoking change hooks, etc.. Value is nil if the
1256 function _detected_ that it did not replace any properties, non-nil
1257 otherwise. */
1259 Lisp_Object
1260 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, Lisp_Object coherent_change_p)
1262 register INTERVAL i;
1263 Lisp_Object ostart, oend;
1265 ostart = start;
1266 oend = end;
1268 properties = validate_plist (properties);
1270 if (NILP (object))
1271 XSETBUFFER (object, current_buffer);
1273 /* If we want no properties for a whole string,
1274 get rid of its intervals. */
1275 if (NILP (properties) && STRINGP (object)
1276 && XFASTINT (start) == 0
1277 && XFASTINT (end) == SCHARS (object))
1279 if (!string_intervals (object))
1280 return Qnil;
1282 set_string_intervals (object, NULL);
1283 return Qt;
1286 i = validate_interval_range (object, &start, &end, soft);
1288 if (!i)
1290 /* If buffer has no properties, and we want none, return now. */
1291 if (NILP (properties))
1292 return Qnil;
1294 /* Restore the original START and END values
1295 because validate_interval_range increments them for strings. */
1296 start = ostart;
1297 end = oend;
1299 i = validate_interval_range (object, &start, &end, hard);
1300 /* This can return if start == end. */
1301 if (!i)
1302 return Qnil;
1305 if (BUFFERP (object) && !NILP (coherent_change_p))
1306 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1308 set_text_properties_1 (start, end, properties, object, i);
1310 if (BUFFERP (object) && !NILP (coherent_change_p))
1311 signal_after_change (XINT (start), XINT (end) - XINT (start),
1312 XINT (end) - XINT (start));
1313 return Qt;
1316 /* Replace properties of text from START to END with new list of
1317 properties PROPERTIES. BUFFER is the buffer containing
1318 the text. This does not obey any hooks.
1319 You can provide the interval that START is located in as I,
1320 or pass NULL for I and this function will find it.
1321 START and END can be in any order. */
1323 void
1324 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object buffer, INTERVAL i)
1326 register INTERVAL prev_changed = NULL;
1327 register ptrdiff_t s, len;
1328 INTERVAL unchanged;
1330 if (XINT (start) < XINT (end))
1332 s = XINT (start);
1333 len = XINT (end) - s;
1335 else if (XINT (end) < XINT (start))
1337 s = XINT (end);
1338 len = XINT (start) - s;
1340 else
1341 return;
1343 if (i == NULL)
1344 i = find_interval (buffer_intervals (XBUFFER (buffer)), s);
1346 if (i->position != s)
1348 unchanged = i;
1349 i = split_interval_right (unchanged, s - unchanged->position);
1351 if (LENGTH (i) > len)
1353 copy_properties (unchanged, i);
1354 i = split_interval_left (i, len);
1355 set_properties (properties, i, buffer);
1356 return;
1359 set_properties (properties, i, buffer);
1361 if (LENGTH (i) == len)
1362 return;
1364 prev_changed = i;
1365 len -= LENGTH (i);
1366 i = next_interval (i);
1369 /* We are starting at the beginning of an interval I. LEN is positive. */
1372 eassert (i != 0);
1374 if (LENGTH (i) >= len)
1376 if (LENGTH (i) > len)
1377 i = split_interval_left (i, len);
1379 /* We have to call set_properties even if we are going to
1380 merge the intervals, so as to make the undo records
1381 and cause redisplay to happen. */
1382 set_properties (properties, i, buffer);
1383 if (prev_changed)
1384 merge_interval_left (i);
1385 return;
1388 len -= LENGTH (i);
1390 /* We have to call set_properties even if we are going to
1391 merge the intervals, so as to make the undo records
1392 and cause redisplay to happen. */
1393 set_properties (properties, i, buffer);
1394 if (!prev_changed)
1395 prev_changed = i;
1396 else
1397 prev_changed = i = merge_interval_left (i);
1399 i = next_interval (i);
1401 while (len > 0);
1404 DEFUN ("remove-text-properties", Fremove_text_properties,
1405 Sremove_text_properties, 3, 4, 0,
1406 doc: /* Remove some properties from text from START to END.
1407 The third argument PROPERTIES is a property list
1408 whose property names specify the properties to remove.
1409 \(The values stored in PROPERTIES are ignored.)
1410 If the optional fourth argument OBJECT is a buffer (or nil, which means
1411 the current buffer), START and END are buffer positions (integers or
1412 markers). If OBJECT is a string, START and END are 0-based indices into it.
1413 Return t if any property was actually removed, nil otherwise.
1415 Use `set-text-properties' if you want to remove all text properties. */)
1416 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1418 register INTERVAL i, unchanged;
1419 register ptrdiff_t s, len;
1420 register int modified = 0;
1422 if (NILP (object))
1423 XSETBUFFER (object, current_buffer);
1425 i = validate_interval_range (object, &start, &end, soft);
1426 if (!i)
1427 return Qnil;
1429 s = XINT (start);
1430 len = XINT (end) - s;
1432 if (i->position != s)
1434 /* No properties on this first interval -- return if
1435 it covers the entire region. */
1436 if (! interval_has_some_properties (properties, i))
1438 ptrdiff_t got = (LENGTH (i) - (s - i->position));
1439 if (got >= len)
1440 return Qnil;
1441 len -= got;
1442 i = next_interval (i);
1444 /* Split away the beginning of this interval; what we don't
1445 want to modify. */
1446 else
1448 unchanged = i;
1449 i = split_interval_right (unchanged, s - unchanged->position);
1450 copy_properties (unchanged, i);
1454 if (BUFFERP (object))
1455 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1457 /* We are at the beginning of an interval, with len to scan */
1458 for (;;)
1460 eassert (i != 0);
1462 if (LENGTH (i) >= len)
1464 if (! interval_has_some_properties (properties, i))
1465 return modified ? Qt : Qnil;
1467 if (LENGTH (i) == len)
1469 remove_properties (properties, Qnil, i, object);
1470 if (BUFFERP (object))
1471 signal_after_change (XINT (start), XINT (end) - XINT (start),
1472 XINT (end) - XINT (start));
1473 return Qt;
1476 /* i has the properties, and goes past the change limit */
1477 unchanged = i;
1478 i = split_interval_left (i, len);
1479 copy_properties (unchanged, i);
1480 remove_properties (properties, Qnil, i, object);
1481 if (BUFFERP (object))
1482 signal_after_change (XINT (start), XINT (end) - XINT (start),
1483 XINT (end) - XINT (start));
1484 return Qt;
1487 len -= LENGTH (i);
1488 modified += remove_properties (properties, Qnil, i, object);
1489 i = next_interval (i);
1493 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1494 Sremove_list_of_text_properties, 3, 4, 0,
1495 doc: /* Remove some properties from text from START to END.
1496 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1497 If the optional fourth argument OBJECT is a buffer (or nil, which means
1498 the current buffer), START and END are buffer positions (integers or
1499 markers). If OBJECT is a string, START and END are 0-based indices into it.
1500 Return t if any property was actually removed, nil otherwise. */)
1501 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1503 register INTERVAL i, unchanged;
1504 register ptrdiff_t s, len;
1505 register int modified = 0;
1506 Lisp_Object properties;
1507 properties = list_of_properties;
1509 if (NILP (object))
1510 XSETBUFFER (object, current_buffer);
1512 i = validate_interval_range (object, &start, &end, soft);
1513 if (!i)
1514 return Qnil;
1516 s = XINT (start);
1517 len = XINT (end) - s;
1519 if (i->position != s)
1521 /* No properties on this first interval -- return if
1522 it covers the entire region. */
1523 if (! interval_has_some_properties_list (properties, i))
1525 ptrdiff_t got = (LENGTH (i) - (s - i->position));
1526 if (got >= len)
1527 return Qnil;
1528 len -= got;
1529 i = next_interval (i);
1531 /* Split away the beginning of this interval; what we don't
1532 want to modify. */
1533 else
1535 unchanged = i;
1536 i = split_interval_right (unchanged, s - unchanged->position);
1537 copy_properties (unchanged, i);
1541 /* We are at the beginning of an interval, with len to scan.
1542 The flag `modified' records if changes have been made.
1543 When object is a buffer, we must call modify_region before changes are
1544 made and signal_after_change when we are done.
1545 We call modify_region before calling remove_properties if modified == 0,
1546 and we call signal_after_change before returning if modified != 0. */
1547 for (;;)
1549 eassert (i != 0);
1551 if (LENGTH (i) >= len)
1553 if (! interval_has_some_properties_list (properties, i))
1555 if (modified)
1557 if (BUFFERP (object))
1558 signal_after_change (XINT (start),
1559 XINT (end) - XINT (start),
1560 XINT (end) - XINT (start));
1561 return Qt;
1563 else
1564 return Qnil;
1566 else if (LENGTH (i) == len)
1568 if (!modified && BUFFERP (object))
1569 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1570 remove_properties (Qnil, properties, i, object);
1571 if (BUFFERP (object))
1572 signal_after_change (XINT (start), XINT (end) - XINT (start),
1573 XINT (end) - XINT (start));
1574 return Qt;
1576 else
1577 { /* i has the properties, and goes past the change limit. */
1578 unchanged = i;
1579 i = split_interval_left (i, len);
1580 copy_properties (unchanged, i);
1581 if (!modified && BUFFERP (object))
1582 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1583 remove_properties (Qnil, properties, i, object);
1584 if (BUFFERP (object))
1585 signal_after_change (XINT (start), XINT (end) - XINT (start),
1586 XINT (end) - XINT (start));
1587 return Qt;
1590 if (interval_has_some_properties_list (properties, i))
1592 if (!modified && BUFFERP (object))
1593 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1594 remove_properties (Qnil, properties, i, object);
1595 modified = 1;
1597 len -= LENGTH (i);
1598 i = next_interval (i);
1602 DEFUN ("text-property-any", Ftext_property_any,
1603 Stext_property_any, 4, 5, 0,
1604 doc: /* Check text from START to END for property PROPERTY equaling VALUE.
1605 If so, return the position of the first character whose property PROPERTY
1606 is `eq' to VALUE. Otherwise return nil.
1607 If the optional fifth argument OBJECT is a buffer (or nil, which means
1608 the current buffer), START and END are buffer positions (integers or
1609 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1610 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1612 register INTERVAL i;
1613 register ptrdiff_t e, pos;
1615 if (NILP (object))
1616 XSETBUFFER (object, current_buffer);
1617 i = validate_interval_range (object, &start, &end, soft);
1618 if (!i)
1619 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1620 e = XINT (end);
1622 while (i)
1624 if (i->position >= e)
1625 break;
1626 if (EQ (textget (i->plist, property), value))
1628 pos = i->position;
1629 if (pos < XINT (start))
1630 pos = XINT (start);
1631 return make_number (pos);
1633 i = next_interval (i);
1635 return Qnil;
1638 DEFUN ("text-property-not-all", Ftext_property_not_all,
1639 Stext_property_not_all, 4, 5, 0,
1640 doc: /* Check text from START to END for property PROPERTY not equaling VALUE.
1641 If so, return the position of the first character whose property PROPERTY
1642 is not `eq' to VALUE. Otherwise, return nil.
1643 If the optional fifth argument OBJECT is a buffer (or nil, which means
1644 the current buffer), START and END are buffer positions (integers or
1645 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1646 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1648 register INTERVAL i;
1649 register ptrdiff_t s, e;
1651 if (NILP (object))
1652 XSETBUFFER (object, current_buffer);
1653 i = validate_interval_range (object, &start, &end, soft);
1654 if (!i)
1655 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1656 s = XINT (start);
1657 e = XINT (end);
1659 while (i)
1661 if (i->position >= e)
1662 break;
1663 if (! EQ (textget (i->plist, property), value))
1665 if (i->position > s)
1666 s = i->position;
1667 return make_number (s);
1669 i = next_interval (i);
1671 return Qnil;
1675 /* Return the direction from which the text-property PROP would be
1676 inherited by any new text inserted at POS: 1 if it would be
1677 inherited from the char after POS, -1 if it would be inherited from
1678 the char before POS, and 0 if from neither.
1679 BUFFER can be either a buffer or nil (meaning current buffer). */
1682 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1684 Lisp_Object prev_pos, front_sticky;
1685 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1686 Lisp_Object defalt = Fassq (prop, Vtext_property_default_nonsticky);
1688 if (NILP (buffer))
1689 XSETBUFFER (buffer, current_buffer);
1691 if (CONSP (defalt) && !NILP (XCDR (defalt)))
1692 is_rear_sticky = 0;
1694 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1695 /* Consider previous character. */
1697 Lisp_Object rear_non_sticky;
1699 prev_pos = make_number (XINT (pos) - 1);
1700 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1702 if (!NILP (CONSP (rear_non_sticky)
1703 ? Fmemq (prop, rear_non_sticky)
1704 : rear_non_sticky))
1705 /* PROP is rear-non-sticky. */
1706 is_rear_sticky = 0;
1708 else
1709 return 0;
1711 /* Consider following character. */
1712 /* This signals an arg-out-of-range error if pos is outside the
1713 buffer's accessible range. */
1714 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1716 if (EQ (front_sticky, Qt)
1717 || (CONSP (front_sticky)
1718 && !NILP (Fmemq (prop, front_sticky))))
1719 /* PROP is inherited from after. */
1720 is_front_sticky = 1;
1722 /* Simple cases, where the properties are consistent. */
1723 if (is_rear_sticky && !is_front_sticky)
1724 return -1;
1725 else if (!is_rear_sticky && is_front_sticky)
1726 return 1;
1727 else if (!is_rear_sticky && !is_front_sticky)
1728 return 0;
1730 /* The stickiness properties are inconsistent, so we have to
1731 disambiguate. Basically, rear-sticky wins, _except_ if the
1732 property that would be inherited has a value of nil, in which case
1733 front-sticky wins. */
1734 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1735 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1736 return 1;
1737 else
1738 return -1;
1742 /* Copying properties between objects. */
1744 /* Add properties from START to END of SRC, starting at POS in DEST.
1745 SRC and DEST may each refer to strings or buffers.
1746 Optional sixth argument PROP causes only that property to be copied.
1747 Properties are copied to DEST as if by `add-text-properties'.
1748 Return t if any property value actually changed, nil otherwise. */
1750 /* Note this can GC when DEST is a buffer. */
1752 Lisp_Object
1753 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src, Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1755 INTERVAL i;
1756 Lisp_Object res;
1757 Lisp_Object stuff;
1758 Lisp_Object plist;
1759 ptrdiff_t s, e, e2, p, len;
1760 int modified = 0;
1761 struct gcpro gcpro1, gcpro2;
1763 i = validate_interval_range (src, &start, &end, soft);
1764 if (!i)
1765 return Qnil;
1767 CHECK_NUMBER_COERCE_MARKER (pos);
1769 Lisp_Object dest_start, dest_end;
1771 e = XINT (pos) + (XINT (end) - XINT (start));
1772 if (MOST_POSITIVE_FIXNUM < e)
1773 args_out_of_range (pos, end);
1774 dest_start = pos;
1775 XSETFASTINT (dest_end, e);
1776 /* Apply this to a copy of pos; it will try to increment its arguments,
1777 which we don't want. */
1778 validate_interval_range (dest, &dest_start, &dest_end, soft);
1781 s = XINT (start);
1782 e = XINT (end);
1783 p = XINT (pos);
1785 stuff = Qnil;
1787 while (s < e)
1789 e2 = i->position + LENGTH (i);
1790 if (e2 > e)
1791 e2 = e;
1792 len = e2 - s;
1794 plist = i->plist;
1795 if (! NILP (prop))
1796 while (! NILP (plist))
1798 if (EQ (Fcar (plist), prop))
1800 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1801 break;
1803 plist = Fcdr (Fcdr (plist));
1805 if (! NILP (plist))
1807 /* Must defer modifications to the interval tree in case src
1808 and dest refer to the same string or buffer. */
1809 stuff = Fcons (Fcons (make_number (p),
1810 Fcons (make_number (p + len),
1811 Fcons (plist, Qnil))),
1812 stuff);
1815 i = next_interval (i);
1816 if (!i)
1817 break;
1819 p += len;
1820 s = i->position;
1823 GCPRO2 (stuff, dest);
1825 while (! NILP (stuff))
1827 res = Fcar (stuff);
1828 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1829 Fcar (Fcdr (Fcdr (res))), dest);
1830 if (! NILP (res))
1831 modified++;
1832 stuff = Fcdr (stuff);
1835 UNGCPRO;
1837 return modified ? Qt : Qnil;
1841 /* Return a list representing the text properties of OBJECT between
1842 START and END. if PROP is non-nil, report only on that property.
1843 Each result list element has the form (S E PLIST), where S and E
1844 are positions in OBJECT and PLIST is a property list containing the
1845 text properties of OBJECT between S and E. Value is nil if OBJECT
1846 doesn't contain text properties between START and END. */
1848 Lisp_Object
1849 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1851 struct interval *i;
1852 Lisp_Object result;
1854 result = Qnil;
1856 i = validate_interval_range (object, &start, &end, soft);
1857 if (i)
1859 ptrdiff_t s = XINT (start);
1860 ptrdiff_t e = XINT (end);
1862 while (s < e)
1864 ptrdiff_t interval_end, len;
1865 Lisp_Object plist;
1867 interval_end = i->position + LENGTH (i);
1868 if (interval_end > e)
1869 interval_end = e;
1870 len = interval_end - s;
1872 plist = i->plist;
1874 if (!NILP (prop))
1875 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1876 if (EQ (XCAR (plist), prop))
1878 plist = Fcons (prop, Fcons (Fcar (XCDR (plist)), Qnil));
1879 break;
1882 if (!NILP (plist))
1883 result = Fcons (Fcons (make_number (s),
1884 Fcons (make_number (s + len),
1885 Fcons (plist, Qnil))),
1886 result);
1888 i = next_interval (i);
1889 if (!i)
1890 break;
1891 s = i->position;
1895 return result;
1899 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1900 (START END PLIST), where START and END are positions and PLIST is a
1901 property list containing the text properties to add. Adjust START
1902 and END positions by DELTA before adding properties. Value is
1903 non-zero if OBJECT was modified. */
1906 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
1908 struct gcpro gcpro1, gcpro2;
1909 int modified_p = 0;
1911 GCPRO2 (list, object);
1913 for (; CONSP (list); list = XCDR (list))
1915 Lisp_Object item, start, end, plist, tem;
1917 item = XCAR (list);
1918 start = make_number (XINT (XCAR (item)) + XINT (delta));
1919 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1920 plist = XCAR (XCDR (XCDR (item)));
1922 tem = Fadd_text_properties (start, end, plist, object);
1923 if (!NILP (tem))
1924 modified_p = 1;
1927 UNGCPRO;
1928 return modified_p;
1933 /* Modify end-points of ranges in LIST destructively, and return the
1934 new list. LIST is a list as returned from text_property_list.
1935 Discard properties that begin at or after NEW_END, and limit
1936 end-points to NEW_END. */
1938 Lisp_Object
1939 extend_property_ranges (Lisp_Object list, Lisp_Object new_end)
1941 Lisp_Object prev = Qnil, head = list;
1942 ptrdiff_t max = XINT (new_end);
1944 for (; CONSP (list); prev = list, list = XCDR (list))
1946 Lisp_Object item, beg, end;
1948 item = XCAR (list);
1949 beg = XCAR (item);
1950 end = XCAR (XCDR (item));
1952 if (XINT (beg) >= max)
1954 /* The start-point is past the end of the new string.
1955 Discard this property. */
1956 if (EQ (head, list))
1957 head = XCDR (list);
1958 else
1959 XSETCDR (prev, XCDR (list));
1961 else if (XINT (end) > max)
1962 /* The end-point is past the end of the new string. */
1963 XSETCAR (XCDR (item), new_end);
1966 return head;
1971 /* Call the modification hook functions in LIST, each with START and END. */
1973 static void
1974 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
1976 struct gcpro gcpro1;
1977 GCPRO1 (list);
1978 while (!NILP (list))
1980 call2 (Fcar (list), start, end);
1981 list = Fcdr (list);
1983 UNGCPRO;
1986 /* Check for read-only intervals between character positions START ... END,
1987 in BUF, and signal an error if we find one.
1989 Then check for any modification hooks in the range.
1990 Create a list of all these hooks in lexicographic order,
1991 eliminating consecutive extra copies of the same hook. Then call
1992 those hooks in order, with START and END - 1 as arguments. */
1994 void
1995 verify_interval_modification (struct buffer *buf,
1996 ptrdiff_t start, ptrdiff_t end)
1998 INTERVAL intervals = buffer_intervals (buf);
1999 INTERVAL i;
2000 Lisp_Object hooks;
2001 Lisp_Object prev_mod_hooks;
2002 Lisp_Object mod_hooks;
2003 struct gcpro gcpro1;
2005 hooks = Qnil;
2006 prev_mod_hooks = Qnil;
2007 mod_hooks = Qnil;
2009 interval_insert_behind_hooks = Qnil;
2010 interval_insert_in_front_hooks = Qnil;
2012 if (!intervals)
2013 return;
2015 if (start > end)
2017 ptrdiff_t temp = start;
2018 start = end;
2019 end = temp;
2022 /* For an insert operation, check the two chars around the position. */
2023 if (start == end)
2025 INTERVAL prev = NULL;
2026 Lisp_Object before, after;
2028 /* Set I to the interval containing the char after START,
2029 and PREV to the interval containing the char before START.
2030 Either one may be null. They may be equal. */
2031 i = find_interval (intervals, start);
2033 if (start == BUF_BEGV (buf))
2034 prev = 0;
2035 else if (i->position == start)
2036 prev = previous_interval (i);
2037 else if (i->position < start)
2038 prev = i;
2039 if (start == BUF_ZV (buf))
2040 i = 0;
2042 /* If Vinhibit_read_only is set and is not a list, we can
2043 skip the read_only checks. */
2044 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2046 /* If I and PREV differ we need to check for the read-only
2047 property together with its stickiness. If either I or
2048 PREV are 0, this check is all we need.
2049 We have to take special care, since read-only may be
2050 indirectly defined via the category property. */
2051 if (i != prev)
2053 if (i)
2055 after = textget (i->plist, Qread_only);
2057 /* If interval I is read-only and read-only is
2058 front-sticky, inhibit insertion.
2059 Check for read-only as well as category. */
2060 if (! NILP (after)
2061 && NILP (Fmemq (after, Vinhibit_read_only)))
2063 Lisp_Object tem;
2065 tem = textget (i->plist, Qfront_sticky);
2066 if (TMEM (Qread_only, tem)
2067 || (NILP (Fplist_get (i->plist, Qread_only))
2068 && TMEM (Qcategory, tem)))
2069 text_read_only (after);
2073 if (prev)
2075 before = textget (prev->plist, Qread_only);
2077 /* If interval PREV is read-only and read-only isn't
2078 rear-nonsticky, inhibit insertion.
2079 Check for read-only as well as category. */
2080 if (! NILP (before)
2081 && NILP (Fmemq (before, Vinhibit_read_only)))
2083 Lisp_Object tem;
2085 tem = textget (prev->plist, Qrear_nonsticky);
2086 if (! TMEM (Qread_only, tem)
2087 && (! NILP (Fplist_get (prev->plist,Qread_only))
2088 || ! TMEM (Qcategory, tem)))
2089 text_read_only (before);
2093 else if (i)
2095 after = textget (i->plist, Qread_only);
2097 /* If interval I is read-only and read-only is
2098 front-sticky, inhibit insertion.
2099 Check for read-only as well as category. */
2100 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2102 Lisp_Object tem;
2104 tem = textget (i->plist, Qfront_sticky);
2105 if (TMEM (Qread_only, tem)
2106 || (NILP (Fplist_get (i->plist, Qread_only))
2107 && TMEM (Qcategory, tem)))
2108 text_read_only (after);
2110 tem = textget (prev->plist, Qrear_nonsticky);
2111 if (! TMEM (Qread_only, tem)
2112 && (! NILP (Fplist_get (prev->plist, Qread_only))
2113 || ! TMEM (Qcategory, tem)))
2114 text_read_only (after);
2119 /* Run both insert hooks (just once if they're the same). */
2120 if (prev)
2121 interval_insert_behind_hooks
2122 = textget (prev->plist, Qinsert_behind_hooks);
2123 if (i)
2124 interval_insert_in_front_hooks
2125 = textget (i->plist, Qinsert_in_front_hooks);
2127 else
2129 /* Loop over intervals on or next to START...END,
2130 collecting their hooks. */
2132 i = find_interval (intervals, start);
2135 if (! INTERVAL_WRITABLE_P (i))
2136 text_read_only (textget (i->plist, Qread_only));
2138 if (!inhibit_modification_hooks)
2140 mod_hooks = textget (i->plist, Qmodification_hooks);
2141 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2143 hooks = Fcons (mod_hooks, hooks);
2144 prev_mod_hooks = mod_hooks;
2148 i = next_interval (i);
2150 /* Keep going thru the interval containing the char before END. */
2151 while (i && i->position < end);
2153 if (!inhibit_modification_hooks)
2155 GCPRO1 (hooks);
2156 hooks = Fnreverse (hooks);
2157 while (! EQ (hooks, Qnil))
2159 call_mod_hooks (Fcar (hooks), make_number (start),
2160 make_number (end));
2161 hooks = Fcdr (hooks);
2163 UNGCPRO;
2168 /* Run the interval hooks for an insertion on character range START ... END.
2169 verify_interval_modification chose which hooks to run;
2170 this function is called after the insertion happens
2171 so it can indicate the range of inserted text. */
2173 void
2174 report_interval_modification (Lisp_Object start, Lisp_Object end)
2176 if (! NILP (interval_insert_behind_hooks))
2177 call_mod_hooks (interval_insert_behind_hooks, start, end);
2178 if (! NILP (interval_insert_in_front_hooks)
2179 && ! EQ (interval_insert_in_front_hooks,
2180 interval_insert_behind_hooks))
2181 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2184 void
2185 syms_of_textprop (void)
2187 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties,
2188 doc: /* Property-list used as default values.
2189 The value of a property in this list is seen as the value for every
2190 character that does not have its own value for that property. */);
2191 Vdefault_text_properties = Qnil;
2193 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist,
2194 doc: /* Alist of alternative properties for properties without a value.
2195 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2196 If a piece of text has no direct value for a particular property, then
2197 this alist is consulted. If that property appears in the alist, then
2198 the first non-nil value from the associated alternative properties is
2199 returned. */);
2200 Vchar_property_alias_alist = Qnil;
2202 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks,
2203 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2204 This also inhibits the use of the `intangible' text property. */);
2205 Vinhibit_point_motion_hooks = Qnil;
2207 DEFVAR_LISP ("text-property-default-nonsticky",
2208 Vtext_property_default_nonsticky,
2209 doc: /* Alist of properties vs the corresponding non-stickiness.
2210 Each element has the form (PROPERTY . NONSTICKINESS).
2212 If a character in a buffer has PROPERTY, new text inserted adjacent to
2213 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2214 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2215 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2216 /* Text properties `syntax-table'and `display' should be nonsticky
2217 by default. */
2218 Vtext_property_default_nonsticky
2219 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt),
2220 Fcons (Fcons (intern_c_string ("display"), Qt), Qnil));
2222 staticpro (&interval_insert_behind_hooks);
2223 staticpro (&interval_insert_in_front_hooks);
2224 interval_insert_behind_hooks = Qnil;
2225 interval_insert_in_front_hooks = Qnil;
2228 /* Common attributes one might give text */
2230 DEFSYM (Qforeground, "foreground");
2231 DEFSYM (Qbackground, "background");
2232 DEFSYM (Qfont, "font");
2233 DEFSYM (Qstipple, "stipple");
2234 DEFSYM (Qunderline, "underline");
2235 DEFSYM (Qread_only, "read-only");
2236 DEFSYM (Qinvisible, "invisible");
2237 DEFSYM (Qintangible, "intangible");
2238 DEFSYM (Qcategory, "category");
2239 DEFSYM (Qlocal_map, "local-map");
2240 DEFSYM (Qfront_sticky, "front-sticky");
2241 DEFSYM (Qrear_nonsticky, "rear-nonsticky");
2242 DEFSYM (Qmouse_face, "mouse-face");
2243 DEFSYM (Qminibuffer_prompt, "minibuffer-prompt");
2245 /* Properties that text might use to specify certain actions */
2247 DEFSYM (Qmouse_left, "mouse-left");
2248 DEFSYM (Qmouse_entered, "mouse-entered");
2249 DEFSYM (Qpoint_left, "point-left");
2250 DEFSYM (Qpoint_entered, "point-entered");
2252 defsubr (&Stext_properties_at);
2253 defsubr (&Sget_text_property);
2254 defsubr (&Sget_char_property);
2255 defsubr (&Sget_char_property_and_overlay);
2256 defsubr (&Snext_char_property_change);
2257 defsubr (&Sprevious_char_property_change);
2258 defsubr (&Snext_single_char_property_change);
2259 defsubr (&Sprevious_single_char_property_change);
2260 defsubr (&Snext_property_change);
2261 defsubr (&Snext_single_property_change);
2262 defsubr (&Sprevious_property_change);
2263 defsubr (&Sprevious_single_property_change);
2264 defsubr (&Sadd_text_properties);
2265 defsubr (&Sput_text_property);
2266 defsubr (&Sset_text_properties);
2267 defsubr (&Sremove_text_properties);
2268 defsubr (&Sremove_list_of_text_properties);
2269 defsubr (&Stext_property_any);
2270 defsubr (&Stext_property_not_all);