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