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