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