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