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