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