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