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