(Fsubstring): Clarify doc string.
[bpt/emacs.git] / src / textprop.c
CommitLineData
d418ef42 1/* Interface code for dealing with text properties.
e138dfdc 2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002
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);
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
ad077db0 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;
566
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
PJ
572 doc: /* Return the list of properties of the character at POSITION in OBJECT.
573OBJECT is the string or buffer to look for the properties in;
574nil means the current buffer.
575If POSITION is at the end of OBJECT, the value is nil. */)
576 (position, object)
1f5e848a 577 Lisp_Object position, object;
d418ef42
JA
578{
579 register INTERVAL i;
d418ef42
JA
580
581 if (NILP (object))
c8a4fc3d 582 XSETBUFFER (object, current_buffer);
d418ef42 583
1f5e848a 584 i = validate_interval_range (object, &position, &position, soft);
d418ef42
JA
585 if (NULL_INTERVAL_P (i))
586 return Qnil;
1f5e848a 587 /* If POSITION is at the end of the interval,
d4b530ad
RS
588 it means it's the end of OBJECT.
589 There are no properties at the very end,
590 since no character follows. */
1f5e848a 591 if (XINT (position) == LENGTH (i) + i->position)
d4b530ad 592 return Qnil;
d418ef42
JA
593
594 return i->plist;
595}
596
5fbe2a44 597DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
8c1a1077
PJ
598 doc: /* Return the value of POSITION's property PROP, in OBJECT.
599OBJECT is optional and defaults to the current buffer.
600If POSITION is at the end of OBJECT, the value is nil. */)
601 (position, prop, object)
1f5e848a 602 Lisp_Object position, object;
46bb7c2b 603 Lisp_Object prop;
5fbe2a44 604{
1f5e848a 605 return textget (Ftext_properties_at (position, object), prop);
5fbe2a44
RS
606}
607
bcf97349 608/* Return the value of char's property PROP, in OBJECT at POSITION.
8d41abc4
MB
609 OBJECT is optional and defaults to the current buffer.
610 If OVERLAY is non-0, then in the case that the returned property is from
611 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
612 returned in *OVERLAY.
613 If POSITION is at the end of OBJECT, the value is nil.
614 If OBJECT is a buffer, then overlay properties are considered as well as
615 text properties.
616 If OBJECT is a window, then that window's buffer is used, but
617 window-specific overlays are considered only if they are associated
618 with OBJECT. */
619Lisp_Object
620get_char_property_and_overlay (position, prop, object, overlay)
1f5e848a 621 Lisp_Object position, object;
f5957179 622 register Lisp_Object prop;
8d41abc4 623 Lisp_Object *overlay;
f5957179
KH
624{
625 struct window *w = 0;
626
b7826503 627 CHECK_NUMBER_COERCE_MARKER (position);
f5957179
KH
628
629 if (NILP (object))
c8a4fc3d 630 XSETBUFFER (object, current_buffer);
f5957179
KH
631
632 if (WINDOWP (object))
633 {
634 w = XWINDOW (object);
64a49ca7 635 object = w->buffer;
f5957179
KH
636 }
637 if (BUFFERP (object))
638 {
1f5e848a 639 int posn = XINT (position);
f5957179
KH
640 int noverlays;
641 Lisp_Object *overlay_vec, tem;
f5957179 642 int len;
cbc55f55
RS
643 struct buffer *obuf = current_buffer;
644
645 set_buffer_temp (XBUFFER (object));
f5957179
KH
646
647 /* First try with room for 40 overlays. */
648 len = 40;
649 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
650
59a486ab 651 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
bcf97349 652 NULL, NULL, 0);
f5957179
KH
653
654 /* If there are more than 40,
655 make enough space for all, and try again. */
656 if (noverlays > len)
657 {
658 len = noverlays;
659 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
59a486ab 660 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
bcf97349 661 NULL, NULL, 0);
f5957179
KH
662 }
663 noverlays = sort_overlays (overlay_vec, noverlays, w);
664
cbc55f55
RS
665 set_buffer_temp (obuf);
666
f5957179
KH
667 /* Now check the overlays in order of decreasing priority. */
668 while (--noverlays >= 0)
669 {
670 tem = Foverlay_get (overlay_vec[noverlays], prop);
671 if (!NILP (tem))
8d41abc4
MB
672 {
673 if (overlay)
674 /* Return the overlay we got the property from. */
675 *overlay = overlay_vec[noverlays];
676 return tem;
677 }
f5957179
KH
678 }
679 }
8d41abc4
MB
680
681 if (overlay)
682 /* Indicate that the return value is not from an overlay. */
683 *overlay = Qnil;
684
f5957179
KH
685 /* Not a buffer, or no appropriate overlay, so fall through to the
686 simpler case. */
8d41abc4
MB
687 return Fget_text_property (position, prop, object);
688}
689
690DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
8c1a1077 691 doc: /* Return the value of POSITION's property PROP, in OBJECT.
8faef085 692Both overlay properties and text properties are checked.
8c1a1077
PJ
693OBJECT is optional and defaults to the current buffer.
694If POSITION is at the end of OBJECT, the value is nil.
695If OBJECT is a buffer, then overlay properties are considered as well as
696text properties.
697If OBJECT is a window, then that window's buffer is used, but window-specific
698overlays are considered only if they are associated with OBJECT. */)
699 (position, prop, object)
8d41abc4
MB
700 Lisp_Object position, object;
701 register Lisp_Object prop;
702{
703 return get_char_property_and_overlay (position, prop, object, 0);
f5957179 704}
fcab51aa
RS
705\f
706DEFUN ("next-char-property-change", Fnext_char_property_change,
707 Snext_char_property_change, 1, 2, 0,
8c1a1077
PJ
708 doc: /* Return the position of next text property or overlay change.
709This scans characters forward from POSITION till it finds a change in
710some text property, or the beginning or end of an overlay, and returns
711the position of that.
712If none is found, the function returns (point-max).
713
714If the optional third argument LIMIT is non-nil, don't search
715past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
716 (position, limit)
fcab51aa
RS
717 Lisp_Object position, limit;
718{
719 Lisp_Object temp;
720
721 temp = Fnext_overlay_change (position);
722 if (! NILP (limit))
723 {
b7826503 724 CHECK_NUMBER (limit);
fcab51aa
RS
725 if (XINT (limit) < XINT (temp))
726 temp = limit;
727 }
728 return Fnext_property_change (position, Qnil, temp);
729}
730
731DEFUN ("previous-char-property-change", Fprevious_char_property_change,
732 Sprevious_char_property_change, 1, 2, 0,
8c1a1077
PJ
733 doc: /* Return the position of previous text property or overlay change.
734Scans characters backward from POSITION till it finds a change in some
735text property, or the beginning or end of an overlay, and returns the
736position of that.
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;
f5957179 745
fcab51aa
RS
746 temp = Fprevious_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 Fprevious_property_change (position, Qnil, temp);
754}
0b0737d1
GM
755
756
b7e047fb
MB
757DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
758 Snext_single_char_property_change, 2, 4, 0,
8c1a1077
PJ
759 doc: /* Return the position of next text property or overlay change for a specific property.
760Scans characters forward from POSITION till it finds
761a change in the PROP property, then returns the position of the change.
762The optional third argument OBJECT is the string or buffer to scan.
763The property values are compared with `eq'.
764If the property is constant all the way to the end of OBJECT, return the
765last valid position in OBJECT.
766If the optional fourth argument LIMIT is non-nil, don't search
767past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
768 (position, prop, object, limit)
b7e047fb 769 Lisp_Object prop, position, object, limit;
0b0737d1
GM
770{
771 if (STRINGP (object))
772 {
b7e047fb
MB
773 position = Fnext_single_property_change (position, prop, object, limit);
774 if (NILP (position))
0b0737d1
GM
775 {
776 if (NILP (limit))
d5db4077 777 position = make_number (SCHARS (object));
0b0737d1 778 else
b7e047fb 779 position = limit;
0b0737d1
GM
780 }
781 }
782 else
783 {
784 Lisp_Object initial_value, value;
aed13378 785 int count = SPECPDL_INDEX ();
0b0737d1 786
b7e047fb 787 if (! NILP (object))
b7826503 788 CHECK_BUFFER (object);
0b0737d1
GM
789
790 if (BUFFERP (object) && current_buffer != XBUFFER (object))
791 {
792 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
793 Fset_buffer (object);
794 }
795
b7e047fb 796 initial_value = Fget_char_property (position, prop, object);
0b0737d1 797
b7e047fb
MB
798 if (NILP (limit))
799 XSETFASTINT (limit, BUF_ZV (current_buffer));
800 else
b7826503 801 CHECK_NUMBER_COERCE_MARKER (limit);
b7e047fb
MB
802
803 for (;;)
0b0737d1 804 {
b7e047fb
MB
805 position = Fnext_char_property_change (position, limit);
806 if (XFASTINT (position) >= XFASTINT (limit)) {
807 position = limit;
808 break;
809 }
810
811 value = Fget_char_property (position, prop, object);
0b0737d1
GM
812 if (!EQ (value, initial_value))
813 break;
814 }
815
816 unbind_to (count, Qnil);
817 }
818
b7e047fb 819 return position;
0b0737d1
GM
820}
821
b7e047fb
MB
822DEFUN ("previous-single-char-property-change",
823 Fprevious_single_char_property_change,
824 Sprevious_single_char_property_change, 2, 4, 0,
8c1a1077
PJ
825 doc: /* Return the position of previous text property or overlay change for a specific property.
826Scans characters backward from POSITION till it finds
827a change in the PROP property, then returns the position of the change.
828The optional third argument OBJECT is the string or buffer to scan.
829The property values are compared with `eq'.
830If the property is constant all the way to the start of OBJECT, return the
831first valid position in OBJECT.
832If the optional fourth argument LIMIT is non-nil, don't search
833back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
834 (position, prop, object, limit)
b7e047fb
MB
835 Lisp_Object prop, position, object, limit;
836{
837 if (STRINGP (object))
838 {
839 position = Fprevious_single_property_change (position, prop, object, limit);
840 if (NILP (position))
841 {
842 if (NILP (limit))
d5db4077 843 position = make_number (SCHARS (object));
b7e047fb
MB
844 else
845 position = limit;
846 }
847 }
848 else
849 {
aed13378 850 int count = SPECPDL_INDEX ();
b7e047fb
MB
851
852 if (! NILP (object))
b7826503 853 CHECK_BUFFER (object);
b7e047fb
MB
854
855 if (BUFFERP (object) && current_buffer != XBUFFER (object))
856 {
857 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
858 Fset_buffer (object);
859 }
860
861 if (NILP (limit))
862 XSETFASTINT (limit, BUF_BEGV (current_buffer));
863 else
b7826503 864 CHECK_NUMBER_COERCE_MARKER (limit);
b7e047fb 865
ce6b02e0
MB
866 if (XFASTINT (position) <= XFASTINT (limit))
867 position = limit;
868 else
b7e047fb 869 {
ce6b02e0 870 Lisp_Object initial_value =
d4225c08
KR
871 Fget_char_property (make_number (XFASTINT (position) - 1),
872 prop, object);
ce6b02e0
MB
873
874 for (;;)
875 {
876 position = Fprevious_char_property_change (position, limit);
0b0737d1 877
ce6b02e0
MB
878 if (XFASTINT (position) <= XFASTINT (limit))
879 {
880 position = limit;
881 break;
882 }
883 else
884 {
885 Lisp_Object value =
d4225c08
KR
886 Fget_char_property (make_number (XFASTINT (position) - 1),
887 prop, object);
ce6b02e0
MB
888
889 if (!EQ (value, initial_value))
890 break;
891 }
892 }
b7e047fb
MB
893 }
894
895 unbind_to (count, Qnil);
896 }
897
898 return position;
899}
fcab51aa 900\f
d418ef42 901DEFUN ("next-property-change", Fnext_property_change,
111b637d 902 Snext_property_change, 1, 3, 0,
8c1a1077
PJ
903 doc: /* Return the position of next property change.
904Scans characters forward from POSITION in OBJECT till it finds
905a change in some text property, then returns the position of the change.
906The optional second argument OBJECT is the string or buffer to scan.
907Return nil if the property is constant all the way to the end of OBJECT.
908If the value is non-nil, it is a position greater than POSITION, never equal.
909
910If the optional third argument LIMIT is non-nil, don't search
911past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
912 (position, object, limit)
1f5e848a 913 Lisp_Object position, object, limit;
d418ef42
JA
914{
915 register INTERVAL i, next;
916
5fbe2a44 917 if (NILP (object))
c8a4fc3d 918 XSETBUFFER (object, current_buffer);
5fbe2a44 919
3a232704 920 if (!NILP (limit) && !EQ (limit, Qt))
b7826503 921 CHECK_NUMBER_COERCE_MARKER (limit);
1387d54e 922
1f5e848a 923 i = validate_interval_range (object, &position, &position, soft);
d418ef42 924
041aa96f
RS
925 /* If LIMIT is t, return start of next interval--don't
926 bother checking further intervals. */
927 if (EQ (limit, Qt))
928 {
44214c1b
RS
929 if (NULL_INTERVAL_P (i))
930 next = i;
931 else
932 next = next_interval (i);
933
c7b6dfa6 934 if (NULL_INTERVAL_P (next))
1f5e848a 935 XSETFASTINT (position, (STRINGP (object)
d5db4077 936 ? SCHARS (object)
1f5e848a 937 : BUF_ZV (XBUFFER (object))));
c7b6dfa6 938 else
ad077db0 939 XSETFASTINT (position, next->position);
1f5e848a 940 return position;
041aa96f
RS
941 }
942
44214c1b
RS
943 if (NULL_INTERVAL_P (i))
944 return limit;
945
946 next = next_interval (i);
947
3a232704 948 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
ad077db0 949 && (NILP (limit) || next->position < XFASTINT (limit)))
d418ef42
JA
950 next = next_interval (next);
951
952 if (NULL_INTERVAL_P (next))
111b637d 953 return limit;
3a232704
SM
954 if (NILP (limit))
955 XSETFASTINT (limit, (STRINGP (object)
d5db4077 956 ? SCHARS (object)
3a232704
SM
957 : BUF_ZV (XBUFFER (object))));
958 if (!(next->position < XFASTINT (limit)))
111b637d 959 return limit;
d418ef42 960
ad077db0 961 XSETFASTINT (position, next->position);
1f5e848a 962 return position;
19e1c426
RS
963}
964
965/* Return 1 if there's a change in some property between BEG and END. */
966
967int
968property_change_between_p (beg, end)
969 int beg, end;
970{
971 register INTERVAL i, next;
972 Lisp_Object object, pos;
973
c8a4fc3d 974 XSETBUFFER (object, current_buffer);
e9c4fbcd 975 XSETFASTINT (pos, beg);
19e1c426
RS
976
977 i = validate_interval_range (object, &pos, &pos, soft);
978 if (NULL_INTERVAL_P (i))
979 return 0;
980
981 next = next_interval (i);
982 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
983 {
984 next = next_interval (next);
e050ef74
RS
985 if (NULL_INTERVAL_P (next))
986 return 0;
ad077db0 987 if (next->position >= end)
19e1c426
RS
988 return 0;
989 }
990
991 if (NULL_INTERVAL_P (next))
992 return 0;
993
994 return 1;
d418ef42
JA
995}
996
9c79dd1b 997DEFUN ("next-single-property-change", Fnext_single_property_change,
111b637d 998 Snext_single_property_change, 2, 4, 0,
8c1a1077
PJ
999 doc: /* Return the position of next property change for a specific property.
1000Scans characters forward from POSITION till it finds
1001a change in the PROP property, then returns the position of the change.
1002The optional third argument OBJECT is the string or buffer to scan.
1003The property values are compared with `eq'.
1004Return nil if the property is constant all the way to the end of OBJECT.
1005If the value is non-nil, it is a position greater than POSITION, never equal.
1006
1007If the optional fourth argument LIMIT is non-nil, don't search
1008past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1009 (position, prop, object, limit)
1f5e848a 1010 Lisp_Object position, prop, object, limit;
9c79dd1b
JA
1011{
1012 register INTERVAL i, next;
1013 register Lisp_Object here_val;
1014
5fbe2a44 1015 if (NILP (object))
c8a4fc3d 1016 XSETBUFFER (object, current_buffer);
5fbe2a44 1017
1387d54e 1018 if (!NILP (limit))
b7826503 1019 CHECK_NUMBER_COERCE_MARKER (limit);
1387d54e 1020
1f5e848a 1021 i = validate_interval_range (object, &position, &position, soft);
9c79dd1b 1022 if (NULL_INTERVAL_P (i))
111b637d 1023 return limit;
9c79dd1b 1024
6a0486dd 1025 here_val = textget (i->plist, prop);
9c79dd1b 1026 next = next_interval (i);
6a0486dd 1027 while (! NULL_INTERVAL_P (next)
111b637d 1028 && EQ (here_val, textget (next->plist, prop))
ad077db0 1029 && (NILP (limit) || next->position < XFASTINT (limit)))
9c79dd1b
JA
1030 next = next_interval (next);
1031
1032 if (NULL_INTERVAL_P (next))
111b637d 1033 return limit;
3a232704
SM
1034 if (NILP (limit))
1035 XSETFASTINT (limit, (STRINGP (object)
d5db4077 1036 ? SCHARS (object)
3a232704
SM
1037 : BUF_ZV (XBUFFER (object))));
1038 if (!(next->position < XFASTINT (limit)))
111b637d 1039 return limit;
9c79dd1b 1040
ad077db0 1041 return make_number (next->position);
9c79dd1b
JA
1042}
1043
d418ef42 1044DEFUN ("previous-property-change", Fprevious_property_change,
111b637d 1045 Sprevious_property_change, 1, 3, 0,
8c1a1077
PJ
1046 doc: /* Return the position of previous property change.
1047Scans characters backwards from POSITION in OBJECT till it finds
1048a change in some text property, then returns the position of the change.
1049The optional second argument OBJECT is the string or buffer to scan.
1050Return nil if the property is constant all the way to the start of OBJECT.
1051If the value is non-nil, it is a position less than POSITION, never equal.
1052
1053If the optional third argument LIMIT is non-nil, don't search
1054back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1055 (position, object, limit)
1f5e848a 1056 Lisp_Object position, object, limit;
d418ef42
JA
1057{
1058 register INTERVAL i, previous;
1059
5fbe2a44 1060 if (NILP (object))
c8a4fc3d 1061 XSETBUFFER (object, current_buffer);
5fbe2a44 1062
1387d54e 1063 if (!NILP (limit))
b7826503 1064 CHECK_NUMBER_COERCE_MARKER (limit);
1387d54e 1065
1f5e848a 1066 i = validate_interval_range (object, &position, &position, soft);
d418ef42 1067 if (NULL_INTERVAL_P (i))
111b637d 1068 return limit;
d418ef42 1069
53b7feec 1070 /* Start with the interval containing the char before point. */
1f5e848a 1071 if (i->position == XFASTINT (position))
53b7feec
RS
1072 i = previous_interval (i);
1073
d418ef42 1074 previous = previous_interval (i);
3a232704 1075 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
111b637d 1076 && (NILP (limit)
ad077db0 1077 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
d418ef42
JA
1078 previous = previous_interval (previous);
1079 if (NULL_INTERVAL_P (previous))
111b637d 1080 return limit;
3a232704
SM
1081 if (NILP (limit))
1082 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1083 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
111b637d 1084 return limit;
d418ef42 1085
ad077db0 1086 return make_number (previous->position + LENGTH (previous));
d418ef42
JA
1087}
1088
9c79dd1b 1089DEFUN ("previous-single-property-change", Fprevious_single_property_change,
111b637d 1090 Sprevious_single_property_change, 2, 4, 0,
8c1a1077
PJ
1091 doc: /* Return the position of previous property change for a specific property.
1092Scans characters backward from POSITION till it finds
1093a change in the PROP property, then returns the position of the change.
1094The optional third argument OBJECT is the string or buffer to scan.
1095The property values are compared with `eq'.
1096Return nil if the property is constant all the way to the start of OBJECT.
1097If the value is non-nil, it is a position less than POSITION, never equal.
1098
1099If the optional fourth argument LIMIT is non-nil, don't search
1100back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1f5e848a
EN
1101 (position, prop, object, limit)
1102 Lisp_Object position, prop, object, limit;
9c79dd1b
JA
1103{
1104 register INTERVAL i, previous;
1105 register Lisp_Object here_val;
1106
5fbe2a44 1107 if (NILP (object))
c8a4fc3d 1108 XSETBUFFER (object, current_buffer);
5fbe2a44 1109
1387d54e 1110 if (!NILP (limit))
b7826503 1111 CHECK_NUMBER_COERCE_MARKER (limit);
1387d54e 1112
1f5e848a 1113 i = validate_interval_range (object, &position, &position, soft);
9c79dd1b 1114
53b7feec 1115 /* Start with the interval containing the char before point. */
3a232704 1116 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
53b7feec
RS
1117 i = previous_interval (i);
1118
6873cfa3
KH
1119 if (NULL_INTERVAL_P (i))
1120 return limit;
1121
6a0486dd 1122 here_val = textget (i->plist, prop);
9c79dd1b 1123 previous = previous_interval (i);
3a232704 1124 while (!NULL_INTERVAL_P (previous)
111b637d
RS
1125 && EQ (here_val, textget (previous->plist, prop))
1126 && (NILP (limit)
ad077db0 1127 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
9c79dd1b
JA
1128 previous = previous_interval (previous);
1129 if (NULL_INTERVAL_P (previous))
111b637d 1130 return limit;
3a232704
SM
1131 if (NILP (limit))
1132 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1133 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
111b637d 1134 return limit;
9c79dd1b 1135
ad077db0 1136 return make_number (previous->position + LENGTH (previous));
9c79dd1b 1137}
fcab51aa 1138\f
c98da214
RS
1139/* Callers note, this can GC when OBJECT is a buffer (or nil). */
1140
d418ef42 1141DEFUN ("add-text-properties", Fadd_text_properties,
5fbe2a44 1142 Sadd_text_properties, 3, 4, 0,
8c1a1077
PJ
1143 doc: /* Add properties to the text from START to END.
1144The third argument PROPERTIES is a property list
1145specifying the property values to add.
1146The optional fourth argument, OBJECT,
1147is the string or buffer containing the text.
1148Return t if any property value actually changed, nil otherwise. */)
1149 (start, end, properties, object)
5fbe2a44 1150 Lisp_Object start, end, properties, object;
d418ef42
JA
1151{
1152 register INTERVAL i, unchanged;
caa31568 1153 register int s, len, modified = 0;
c98da214 1154 struct gcpro gcpro1;
d418ef42
JA
1155
1156 properties = validate_plist (properties);
1157 if (NILP (properties))
1158 return Qnil;
1159
5fbe2a44 1160 if (NILP (object))
c8a4fc3d 1161 XSETBUFFER (object, current_buffer);
5fbe2a44 1162
d418ef42
JA
1163 i = validate_interval_range (object, &start, &end, hard);
1164 if (NULL_INTERVAL_P (i))
1165 return Qnil;
1166
1167 s = XINT (start);
1168 len = XINT (end) - s;
1169
c98da214
RS
1170 /* No need to protect OBJECT, because we GC only if it's a buffer,
1171 and live buffers are always protected. */
1172 GCPRO1 (properties);
1173
d418ef42 1174 /* If we're not starting on an interval boundary, we have to
cdf3e5a2 1175 split this interval. */
d418ef42
JA
1176 if (i->position != s)
1177 {
1178 /* If this interval already has the properties, we can
cdf3e5a2 1179 skip it. */
d418ef42
JA
1180 if (interval_has_all_properties (properties, i))
1181 {
1182 int got = (LENGTH (i) - (s - i->position));
1183 if (got >= len)
64db1307 1184 RETURN_UNGCPRO (Qnil);
d418ef42 1185 len -= got;
05d5b93e 1186 i = next_interval (i);
d418ef42
JA
1187 }
1188 else
1189 {
1190 unchanged = i;
ad9c1940 1191 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 1192 copy_properties (unchanged, i);
d418ef42
JA
1193 }
1194 }
1195
2a631db1
RS
1196 if (BUFFERP (object))
1197 modify_region (XBUFFER (object), XINT (start), XINT (end));
26c76ace 1198
daa5e28f 1199 /* We are at the beginning of interval I, with LEN chars to scan. */
caa31568 1200 for (;;)
d418ef42 1201 {
d4b530ad
RS
1202 if (i == 0)
1203 abort ();
1204
d418ef42
JA
1205 if (LENGTH (i) >= len)
1206 {
c98da214
RS
1207 /* We can UNGCPRO safely here, because there will be just
1208 one more chance to gc, in the next call to add_properties,
1209 and after that we will not need PROPERTIES or OBJECT again. */
1210 UNGCPRO;
1211
d418ef42 1212 if (interval_has_all_properties (properties, i))
26c76ace 1213 {
2a631db1
RS
1214 if (BUFFERP (object))
1215 signal_after_change (XINT (start), XINT (end) - XINT (start),
1216 XINT (end) - XINT (start));
26c76ace
RS
1217
1218 return modified ? Qt : Qnil;
1219 }
d418ef42
JA
1220
1221 if (LENGTH (i) == len)
1222 {
d4b530ad 1223 add_properties (properties, i, object);
2a631db1
RS
1224 if (BUFFERP (object))
1225 signal_after_change (XINT (start), XINT (end) - XINT (start),
1226 XINT (end) - XINT (start));
d418ef42
JA
1227 return Qt;
1228 }
1229
1230 /* i doesn't have the properties, and goes past the change limit */
1231 unchanged = i;
ad9c1940 1232 i = split_interval_left (unchanged, len);
d418ef42 1233 copy_properties (unchanged, i);
d4b530ad 1234 add_properties (properties, i, object);
2a631db1
RS
1235 if (BUFFERP (object))
1236 signal_after_change (XINT (start), XINT (end) - XINT (start),
1237 XINT (end) - XINT (start));
d418ef42
JA
1238 return Qt;
1239 }
1240
1241 len -= LENGTH (i);
d4b530ad 1242 modified += add_properties (properties, i, object);
d418ef42
JA
1243 i = next_interval (i);
1244 }
1245}
1246
c98da214
RS
1247/* Callers note, this can GC when OBJECT is a buffer (or nil). */
1248
d4b530ad
RS
1249DEFUN ("put-text-property", Fput_text_property,
1250 Sput_text_property, 4, 5, 0,
8c1a1077
PJ
1251 doc: /* Set one property of the text from START to END.
1252The third and fourth arguments PROPERTY and VALUE
1253specify the property to add.
1254The optional fifth argument, OBJECT,
1255is the string or buffer containing the text. */)
1256 (start, end, property, value, object)
1f5e848a 1257 Lisp_Object start, end, property, value, object;
d4b530ad
RS
1258{
1259 Fadd_text_properties (start, end,
1f5e848a 1260 Fcons (property, Fcons (value, Qnil)),
d4b530ad
RS
1261 object);
1262 return Qnil;
1263}
1264
d418ef42 1265DEFUN ("set-text-properties", Fset_text_properties,
5fbe2a44 1266 Sset_text_properties, 3, 4, 0,
8c1a1077
PJ
1267 doc: /* Completely replace properties of text from START to END.
1268The third argument PROPERTIES is the new property list.
1269The optional fourth argument, OBJECT,
1270is the string or buffer containing the text.
1271If OBJECT is omitted or nil, it defaults to the current buffer.
1272If PROPERTIES is nil, the effect is to remove all properties from
1273the designated part of OBJECT. */)
1274 (start, end, properties, object)
1f5e848a 1275 Lisp_Object start, end, properties, object;
0087ade6
GM
1276{
1277 return set_text_properties (start, end, properties, object, Qt);
1278}
1279
1280
1281/* Replace properties of text from START to END with new list of
1282 properties PROPERTIES. OBJECT is the buffer or string containing
1283 the text. OBJECT nil means use the current buffer.
1284 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1285 is non-nil if properties were replaced; it is nil if there weren't
1286 any properties to replace. */
1287
1288Lisp_Object
1289set_text_properties (start, end, properties, object, signal_after_change_p)
1290 Lisp_Object start, end, properties, object, signal_after_change_p;
d418ef42 1291{
28ff4293 1292 register INTERVAL i;
33d7d0df
RS
1293 Lisp_Object ostart, oend;
1294
1295 ostart = start;
1296 oend = end;
d418ef42 1297
1f5e848a 1298 properties = validate_plist (properties);
d418ef42 1299
5fbe2a44 1300 if (NILP (object))
c8a4fc3d 1301 XSETBUFFER (object, current_buffer);
5fbe2a44 1302
919fa9cb
RS
1303 /* If we want no properties for a whole string,
1304 get rid of its intervals. */
1f5e848a 1305 if (NILP (properties) && STRINGP (object)
919fa9cb 1306 && XFASTINT (start) == 0
d5db4077 1307 && XFASTINT (end) == SCHARS (object))
919fa9cb 1308 {
d5db4077 1309 if (! STRING_INTERVALS (object))
26c76ace
RS
1310 return Qt;
1311
9056febe 1312 STRING_SET_INTERVALS (object, NULL_INTERVAL);
919fa9cb
RS
1313 return Qt;
1314 }
1315
facc570e 1316 i = validate_interval_range (object, &start, &end, soft);
919fa9cb 1317
d418ef42 1318 if (NULL_INTERVAL_P (i))
facc570e 1319 {
1f5e848a
EN
1320 /* If buffer has no properties, and we want none, return now. */
1321 if (NILP (properties))
facc570e
RS
1322 return Qnil;
1323
33d7d0df
RS
1324 /* Restore the original START and END values
1325 because validate_interval_range increments them for strings. */
1326 start = ostart;
1327 end = oend;
1328
facc570e
RS
1329 i = validate_interval_range (object, &start, &end, hard);
1330 /* This can return if start == end. */
1331 if (NULL_INTERVAL_P (i))
1332 return Qnil;
1333 }
d418ef42 1334
2a631db1
RS
1335 if (BUFFERP (object))
1336 modify_region (XBUFFER (object), XINT (start), XINT (end));
26c76ace 1337
78ff4175
RS
1338 set_text_properties_1 (start, end, properties, object, i);
1339
1340 if (BUFFERP (object) && !NILP (signal_after_change_p))
1341 signal_after_change (XINT (start), XINT (end) - XINT (start),
1342 XINT (end) - XINT (start));
1343 return Qt;
1344}
1345
1346/* Replace properties of text from START to END with new list of
1347 properties PROPERTIES. BUFFER is the buffer containing
1348 the text. This does not obey any hooks.
1349 You can provide the interval that START is located in as I,
ce768453 1350 or pass NULL for I and this function will find it.
49f68fd2 1351 START and END can be in any order. */
78ff4175
RS
1352
1353void
1354set_text_properties_1 (start, end, properties, buffer, i)
1355 Lisp_Object start, end, properties, buffer;
1356 INTERVAL i;
1357{
1358 register INTERVAL prev_changed = NULL_INTERVAL;
1359 register int s, len;
1360 INTERVAL unchanged;
1361
1362 s = XINT (start);
1363 len = XINT (end) - s;
49f68fd2
RS
1364 if (len == 0)
1365 return;
1366 if (len < 0)
1367 {
1368 s = s + len;
1369 len = - len;
1370 }
1371
78ff4175
RS
1372 if (i == 0)
1373 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1374
d418ef42
JA
1375 if (i->position != s)
1376 {
1377 unchanged = i;
ad9c1940 1378 i = split_interval_right (unchanged, s - unchanged->position);
7855e674 1379
d418ef42
JA
1380 if (LENGTH (i) > len)
1381 {
9c79dd1b 1382 copy_properties (unchanged, i);
ad9c1940 1383 i = split_interval_left (i, len);
78ff4175
RS
1384 set_properties (properties, i, buffer);
1385 return;
d418ef42
JA
1386 }
1387
78ff4175 1388 set_properties (properties, i, buffer);
daa5e28f 1389
9c79dd1b 1390 if (LENGTH (i) == len)
78ff4175 1391 return;
9c79dd1b
JA
1392
1393 prev_changed = i;
d418ef42
JA
1394 len -= LENGTH (i);
1395 i = next_interval (i);
1396 }
1397
cd7d971d 1398 /* We are starting at the beginning of an interval, I */
7855e674 1399 while (len > 0)
d418ef42 1400 {
d4b530ad
RS
1401 if (i == 0)
1402 abort ();
1403
d418ef42
JA
1404 if (LENGTH (i) >= len)
1405 {
cd7d971d 1406 if (LENGTH (i) > len)
ad9c1940 1407 i = split_interval_left (i, len);
d418ef42 1408
6f232881
RS
1409 /* We have to call set_properties even if we are going to
1410 merge the intervals, so as to make the undo records
1411 and cause redisplay to happen. */
78ff4175 1412 set_properties (properties, i, buffer);
6f232881 1413 if (!NULL_INTERVAL_P (prev_changed))
9c79dd1b 1414 merge_interval_left (i);
78ff4175 1415 return;
d418ef42
JA
1416 }
1417
1418 len -= LENGTH (i);
6f232881
RS
1419
1420 /* We have to call set_properties even if we are going to
1421 merge the intervals, so as to make the undo records
1422 and cause redisplay to happen. */
78ff4175 1423 set_properties (properties, i, buffer);
9c79dd1b 1424 if (NULL_INTERVAL_P (prev_changed))
6f232881 1425 prev_changed = i;
9c79dd1b
JA
1426 else
1427 prev_changed = i = merge_interval_left (i);
1428
d418ef42
JA
1429 i = next_interval (i);
1430 }
d418ef42
JA
1431}
1432
1433DEFUN ("remove-text-properties", Fremove_text_properties,
5fbe2a44 1434 Sremove_text_properties, 3, 4, 0,
8c1a1077
PJ
1435 doc: /* Remove some properties from text from START to END.
1436The third argument PROPERTIES is a property list
1437whose property names specify the properties to remove.
1438\(The values stored in PROPERTIES are ignored.)
1439The optional fourth argument, OBJECT,
1440is the string or buffer containing the text.
1441Return t if any property was actually removed, nil otherwise. */)
1442 (start, end, properties, object)
1f5e848a 1443 Lisp_Object start, end, properties, object;
d418ef42
JA
1444{
1445 register INTERVAL i, unchanged;
caa31568 1446 register int s, len, modified = 0;
d418ef42 1447
5fbe2a44 1448 if (NILP (object))
c8a4fc3d 1449 XSETBUFFER (object, current_buffer);
5fbe2a44 1450
d418ef42
JA
1451 i = validate_interval_range (object, &start, &end, soft);
1452 if (NULL_INTERVAL_P (i))
1453 return Qnil;
1454
1455 s = XINT (start);
1456 len = XINT (end) - s;
9c79dd1b 1457
d418ef42
JA
1458 if (i->position != s)
1459 {
1460 /* No properties on this first interval -- return if
cdf3e5a2 1461 it covers the entire region. */
1f5e848a 1462 if (! interval_has_some_properties (properties, i))
d418ef42
JA
1463 {
1464 int got = (LENGTH (i) - (s - i->position));
1465 if (got >= len)
1466 return Qnil;
1467 len -= got;
05d5b93e 1468 i = next_interval (i);
d418ef42 1469 }
daa5e28f
RS
1470 /* Split away the beginning of this interval; what we don't
1471 want to modify. */
d418ef42
JA
1472 else
1473 {
1474 unchanged = i;
ad9c1940 1475 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 1476 copy_properties (unchanged, i);
d418ef42
JA
1477 }
1478 }
1479
2a631db1
RS
1480 if (BUFFERP (object))
1481 modify_region (XBUFFER (object), XINT (start), XINT (end));
26c76ace 1482
d418ef42 1483 /* We are at the beginning of an interval, with len to scan */
caa31568 1484 for (;;)
d418ef42 1485 {
d4b530ad
RS
1486 if (i == 0)
1487 abort ();
1488
d418ef42
JA
1489 if (LENGTH (i) >= len)
1490 {
1f5e848a 1491 if (! interval_has_some_properties (properties, i))
d418ef42
JA
1492 return modified ? Qt : Qnil;
1493
1494 if (LENGTH (i) == len)
1495 {
11713b6d
RS
1496 remove_properties (properties, Qnil, i, object);
1497 if (BUFFERP (object))
1498 signal_after_change (XINT (start), XINT (end) - XINT (start),
1499 XINT (end) - XINT (start));
1500 return Qt;
1501 }
1502
1503 /* i has the properties, and goes past the change limit */
1504 unchanged = i;
1505 i = split_interval_left (i, len);
1506 copy_properties (unchanged, i);
1507 remove_properties (properties, Qnil, i, object);
1508 if (BUFFERP (object))
1509 signal_after_change (XINT (start), XINT (end) - XINT (start),
1510 XINT (end) - XINT (start));
1511 return Qt;
1512 }
1513
1514 len -= LENGTH (i);
1515 modified += remove_properties (properties, Qnil, i, object);
1516 i = next_interval (i);
1517 }
1518}
1519
1520DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1521 Sremove_list_of_text_properties, 3, 4, 0,
1522 doc: /* Remove some properties from text from START to END.
1523The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1524The optional fourth argument, OBJECT,
1525is the string or buffer containing the text, defaulting to the current buffer.
1526Return t if any property was actually removed, nil otherwise. */)
1527 (start, end, list_of_properties, object)
1528 Lisp_Object start, end, list_of_properties, object;
1529{
1530 register INTERVAL i, unchanged;
1531 register int s, len, modified = 0;
1532 Lisp_Object properties;
1533 properties = list_of_properties;
1534
1535 if (NILP (object))
1536 XSETBUFFER (object, current_buffer);
1537
1538 i = validate_interval_range (object, &start, &end, soft);
1539 if (NULL_INTERVAL_P (i))
1540 return Qnil;
1541
1542 s = XINT (start);
1543 len = XINT (end) - s;
1544
1545 if (i->position != s)
1546 {
1547 /* No properties on this first interval -- return if
1548 it covers the entire region. */
1549 if (! interval_has_some_properties_list (properties, i))
1550 {
1551 int got = (LENGTH (i) - (s - i->position));
1552 if (got >= len)
1553 return Qnil;
1554 len -= got;
1555 i = next_interval (i);
1556 }
1557 /* Split away the beginning of this interval; what we don't
1558 want to modify. */
1559 else
1560 {
1561 unchanged = i;
1562 i = split_interval_right (unchanged, s - unchanged->position);
1563 copy_properties (unchanged, i);
1564 }
1565 }
1566
1567 if (BUFFERP (object))
1568 modify_region (XBUFFER (object), XINT (start), XINT (end));
1569
1570 /* We are at the beginning of an interval, with len to scan */
1571 for (;;)
1572 {
1573 if (i == 0)
1574 abort ();
1575
1576 if (LENGTH (i) >= len)
1577 {
1578 if (! interval_has_some_properties_list (properties, i))
1579 return modified ? Qt : Qnil;
1580
1581 if (LENGTH (i) == len)
1582 {
1583 remove_properties (Qnil, properties, i, object);
2a631db1
RS
1584 if (BUFFERP (object))
1585 signal_after_change (XINT (start), XINT (end) - XINT (start),
1586 XINT (end) - XINT (start));
d418ef42
JA
1587 return Qt;
1588 }
1589
1590 /* i has the properties, and goes past the change limit */
daa5e28f 1591 unchanged = i;
ad9c1940 1592 i = split_interval_left (i, len);
d418ef42 1593 copy_properties (unchanged, i);
11713b6d 1594 remove_properties (Qnil, properties, i, object);
2a631db1
RS
1595 if (BUFFERP (object))
1596 signal_after_change (XINT (start), XINT (end) - XINT (start),
1597 XINT (end) - XINT (start));
d418ef42
JA
1598 return Qt;
1599 }
1600
1601 len -= LENGTH (i);
11713b6d 1602 modified += remove_properties (Qnil, properties, i, object);
d418ef42
JA
1603 i = next_interval (i);
1604 }
1605}
fcab51aa 1606\f
ad9c1940
JB
1607DEFUN ("text-property-any", Ftext_property_any,
1608 Stext_property_any, 4, 5, 0,
8c1a1077
PJ
1609 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1610If so, return the position of the first character whose property PROPERTY
1611is `eq' to VALUE. Otherwise return nil.
1612The optional fifth argument, OBJECT, is the string or buffer
1613containing the text. */)
1614 (start, end, property, value, object)
1615 Lisp_Object start, end, property, value, object;
ad9c1940
JB
1616{
1617 register INTERVAL i;
1618 register int e, pos;
1619
1620 if (NILP (object))
c8a4fc3d 1621 XSETBUFFER (object, current_buffer);
ad9c1940 1622 i = validate_interval_range (object, &start, &end, soft);
2084fddb
KH
1623 if (NULL_INTERVAL_P (i))
1624 return (!NILP (value) || EQ (start, end) ? Qnil : start);
ad9c1940
JB
1625 e = XINT (end);
1626
1627 while (! NULL_INTERVAL_P (i))
1628 {
1629 if (i->position >= e)
1630 break;
1f5e848a 1631 if (EQ (textget (i->plist, property), value))
ad9c1940
JB
1632 {
1633 pos = i->position;
1634 if (pos < XINT (start))
1635 pos = XINT (start);
ad077db0 1636 return make_number (pos);
ad9c1940
JB
1637 }
1638 i = next_interval (i);
1639 }
1640 return Qnil;
1641}
1642
1643DEFUN ("text-property-not-all", Ftext_property_not_all,
1644 Stext_property_not_all, 4, 5, 0,
8c1a1077
PJ
1645 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1646If so, return the position of the first character whose property PROPERTY
1647is not `eq' to VALUE. Otherwise, return nil.
1648The optional fifth argument, OBJECT, is the string or buffer
1649containing the text. */)
1650 (start, end, property, value, object)
1651 Lisp_Object start, end, property, value, object;
ad9c1940
JB
1652{
1653 register INTERVAL i;
1654 register int s, e;
1655
1656 if (NILP (object))
c8a4fc3d 1657 XSETBUFFER (object, current_buffer);
ad9c1940
JB
1658 i = validate_interval_range (object, &start, &end, soft);
1659 if (NULL_INTERVAL_P (i))
916a3119 1660 return (NILP (value) || EQ (start, end)) ? Qnil : start;
ad9c1940
JB
1661 s = XINT (start);
1662 e = XINT (end);
1663
1664 while (! NULL_INTERVAL_P (i))
1665 {
1666 if (i->position >= e)
1667 break;
1f5e848a 1668 if (! EQ (textget (i->plist, property), value))
ad9c1940
JB
1669 {
1670 if (i->position > s)
1671 s = i->position;
ad077db0 1672 return make_number (s);
ad9c1940
JB
1673 }
1674 i = next_interval (i);
1675 }
1676 return Qnil;
1677}
e138dfdc
MB
1678
1679\f
1680/* Return the direction from which the text-property PROP would be
1681 inherited by any new text inserted at POS: 1 if it would be
1682 inherited from the char after POS, -1 if it would be inherited from
1683 the char before POS, and 0 if from neither. */
1684
1685int
1686text_property_stickiness (prop, pos)
1687 Lisp_Object prop;
1688 Lisp_Object pos;
1689{
1690 Lisp_Object prev_pos, front_sticky;
1691 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1692
1693 if (XINT (pos) > BEGV)
1694 /* Consider previous character. */
1695 {
1696 Lisp_Object rear_non_sticky;
1697
1698 prev_pos = make_number (XINT (pos) - 1);
1699 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, Qnil);
1700
1701 if (!NILP (CONSP (rear_non_sticky)
1702 ? Fmemq (prop, rear_non_sticky)
1703 : rear_non_sticky))
1704 /* PROP is rear-non-sticky. */
1705 is_rear_sticky = 0;
1706 }
1707
1708 /* Consider following character. */
1709 front_sticky = Fget_text_property (pos, Qfront_sticky, Qnil);
1710
1711 if (EQ (front_sticky, Qt)
1712 || (CONSP (front_sticky)
1713 && !NILP (Fmemq (prop, front_sticky))))
1714 /* PROP is inherited from after. */
1715 is_front_sticky = 1;
1716
1717 /* Simple cases, where the properties are consistent. */
1718 if (is_rear_sticky && !is_front_sticky)
1719 return -1;
1720 else if (!is_rear_sticky && is_front_sticky)
1721 return 1;
1722 else if (!is_rear_sticky && !is_front_sticky)
1723 return 0;
1724
1725 /* The stickiness properties are inconsistent, so we have to
1726 disambiguate. Basically, rear-sticky wins, _except_ if the
1727 property that would be inherited has a value of nil, in which case
1728 front-sticky wins. */
1729 if (XINT (pos) == BEGV || NILP (Fget_text_property (prev_pos, prop, Qnil)))
1730 return 1;
1731 else
1732 return -1;
1733}
1734
fcab51aa 1735\f
15e4954b
JB
1736/* I don't think this is the right interface to export; how often do you
1737 want to do something like this, other than when you're copying objects
1738 around?
1739
1740 I think it would be better to have a pair of functions, one which
1741 returns the text properties of a region as a list of ranges and
1742 plists, and another which applies such a list to another object. */
1743
c98da214
RS
1744/* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1745 SRC and DEST may each refer to strings or buffers.
1746 Optional sixth argument PROP causes only that property to be copied.
1747 Properties are copied to DEST as if by `add-text-properties'.
1748 Return t if any property value actually changed, nil otherwise. */
1749
1750/* Note this can GC when DEST is a buffer. */
ad077db0 1751
15e4954b
JB
1752Lisp_Object
1753copy_text_properties (start, end, src, pos, dest, prop)
1754 Lisp_Object start, end, src, pos, dest, prop;
1755{
1756 INTERVAL i;
1757 Lisp_Object res;
1758 Lisp_Object stuff;
1759 Lisp_Object plist;
1760 int s, e, e2, p, len, modified = 0;
c98da214 1761 struct gcpro gcpro1, gcpro2;
15e4954b
JB
1762
1763 i = validate_interval_range (src, &start, &end, soft);
1764 if (NULL_INTERVAL_P (i))
1765 return Qnil;
1766
b7826503 1767 CHECK_NUMBER_COERCE_MARKER (pos);
15e4954b
JB
1768 {
1769 Lisp_Object dest_start, dest_end;
1770
1771 dest_start = pos;
e9c4fbcd 1772 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
15e4954b
JB
1773 /* Apply this to a copy of pos; it will try to increment its arguments,
1774 which we don't want. */
1775 validate_interval_range (dest, &dest_start, &dest_end, soft);
1776 }
1777
1778 s = XINT (start);
1779 e = XINT (end);
1780 p = XINT (pos);
1781
1782 stuff = Qnil;
1783
1784 while (s < e)
1785 {
1786 e2 = i->position + LENGTH (i);
1787 if (e2 > e)
1788 e2 = e;
1789 len = e2 - s;
1790
1791 plist = i->plist;
1792 if (! NILP (prop))
1793 while (! NILP (plist))
1794 {
1795 if (EQ (Fcar (plist), prop))
1796 {
1797 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1798 break;
1799 }
1800 plist = Fcdr (Fcdr (plist));
1801 }
1802 if (! NILP (plist))
1803 {
1804 /* Must defer modifications to the interval tree in case src
cdf3e5a2 1805 and dest refer to the same string or buffer. */
15e4954b
JB
1806 stuff = Fcons (Fcons (make_number (p),
1807 Fcons (make_number (p + len),
1808 Fcons (plist, Qnil))),
1809 stuff);
1810 }
1811
1812 i = next_interval (i);
1813 if (NULL_INTERVAL_P (i))
1814 break;
1815
1816 p += len;
1817 s = i->position;
1818 }
1819
c98da214
RS
1820 GCPRO2 (stuff, dest);
1821
15e4954b
JB
1822 while (! NILP (stuff))
1823 {
1824 res = Fcar (stuff);
1825 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1826 Fcar (Fcdr (Fcdr (res))), dest);
1827 if (! NILP (res))
1828 modified++;
1829 stuff = Fcdr (stuff);
1830 }
1831
c98da214
RS
1832 UNGCPRO;
1833
15e4954b
JB
1834 return modified ? Qt : Qnil;
1835}
9dd7eec6
GM
1836
1837
1838/* Return a list representing the text properties of OBJECT between
1839 START and END. if PROP is non-nil, report only on that property.
1840 Each result list element has the form (S E PLIST), where S and E
1841 are positions in OBJECT and PLIST is a property list containing the
1842 text properties of OBJECT between S and E. Value is nil if OBJECT
1843 doesn't contain text properties between START and END. */
1844
1845Lisp_Object
1846text_property_list (object, start, end, prop)
1847 Lisp_Object object, start, end, prop;
1848{
1849 struct interval *i;
1850 Lisp_Object result;
9dd7eec6
GM
1851
1852 result = Qnil;
1853
1854 i = validate_interval_range (object, &start, &end, soft);
1855 if (!NULL_INTERVAL_P (i))
1856 {
1857 int s = XINT (start);
1858 int e = XINT (end);
1859
1860 while (s < e)
1861 {
1862 int interval_end, len;
1863 Lisp_Object plist;
1864
1865 interval_end = i->position + LENGTH (i);
1866 if (interval_end > e)
1867 interval_end = e;
1868 len = interval_end - s;
1869
1870 plist = i->plist;
1871
1872 if (!NILP (prop))
1873 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1874 if (EQ (Fcar (plist), prop))
1875 {
1876 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1877 break;
1878 }
1879
1880 if (!NILP (plist))
1881 result = Fcons (Fcons (make_number (s),
1882 Fcons (make_number (s + len),
1883 Fcons (plist, Qnil))),
1884 result);
1885
1886 i = next_interval (i);
1887 if (NULL_INTERVAL_P (i))
1888 break;
1889 s = i->position;
1890 }
1891 }
1892
1893 return result;
1894}
1895
1896
1897/* Add text properties to OBJECT from LIST. LIST is a list of triples
1898 (START END PLIST), where START and END are positions and PLIST is a
1899 property list containing the text properties to add. Adjust START
1900 and END positions by DELTA before adding properties. Value is
1901 non-zero if OBJECT was modified. */
1902
1903int
1904add_text_properties_from_list (object, list, delta)
1905 Lisp_Object object, list, delta;
1906{
1907 struct gcpro gcpro1, gcpro2;
1908 int modified_p = 0;
1909
1910 GCPRO2 (list, object);
1911
1912 for (; CONSP (list); list = XCDR (list))
1913 {
1914 Lisp_Object item, start, end, plist, tem;
1915
1916 item = XCAR (list);
1917 start = make_number (XINT (XCAR (item)) + XINT (delta));
1918 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1919 plist = XCAR (XCDR (XCDR (item)));
1920
1921 tem = Fadd_text_properties (start, end, plist, object);
1922 if (!NILP (tem))
1923 modified_p = 1;
1924 }
1925
1926 UNGCPRO;
1927 return modified_p;
1928}
1929
1930
1931
1932/* Modify end-points of ranges in LIST destructively. LIST is a list
1933 as returned from text_property_list. Change end-points equal to
1934 OLD_END to NEW_END. */
1935
1936void
1937extend_property_ranges (list, old_end, new_end)
1938 Lisp_Object list, old_end, new_end;
1939{
1940 for (; CONSP (list); list = XCDR (list))
1941 {
1942 Lisp_Object item, end;
1943
1944 item = XCAR (list);
1945 end = XCAR (XCDR (item));
1946
1947 if (EQ (end, old_end))
f3fbd155 1948 XSETCAR (XCDR (item), new_end);
9dd7eec6
GM
1949 }
1950}
1951
1952
318d2fa8
RS
1953\f
1954/* Call the modification hook functions in LIST, each with START and END. */
1955
1956static void
1957call_mod_hooks (list, start, end)
1958 Lisp_Object list, start, end;
1959{
1960 struct gcpro gcpro1;
1961 GCPRO1 (list);
1962 while (!NILP (list))
1963 {
1964 call2 (Fcar (list), start, end);
1965 list = Fcdr (list);
1966 }
1967 UNGCPRO;
1968}
1969
96f90544
RS
1970/* Check for read-only intervals between character positions START ... END,
1971 in BUF, and signal an error if we find one.
1972
1973 Then check for any modification hooks in the range.
1974 Create a list of all these hooks in lexicographic order,
1975 eliminating consecutive extra copies of the same hook. Then call
1976 those hooks in order, with START and END - 1 as arguments. */
15e4954b 1977
318d2fa8
RS
1978void
1979verify_interval_modification (buf, start, end)
1980 struct buffer *buf;
1981 int start, end;
1982{
1983 register INTERVAL intervals = BUF_INTERVALS (buf);
695f302f 1984 register INTERVAL i;
318d2fa8
RS
1985 Lisp_Object hooks;
1986 register Lisp_Object prev_mod_hooks;
1987 Lisp_Object mod_hooks;
1988 struct gcpro gcpro1;
1989
1990 hooks = Qnil;
1991 prev_mod_hooks = Qnil;
1992 mod_hooks = Qnil;
1993
1994 interval_insert_behind_hooks = Qnil;
1995 interval_insert_in_front_hooks = Qnil;
1996
1997 if (NULL_INTERVAL_P (intervals))
1998 return;
1999
2000 if (start > end)
2001 {
2002 int temp = start;
2003 start = end;
2004 end = temp;
2005 }
2006
2007 /* For an insert operation, check the two chars around the position. */
2008 if (start == end)
2009 {
7cb66899 2010 INTERVAL prev = NULL;
318d2fa8
RS
2011 Lisp_Object before, after;
2012
2013 /* Set I to the interval containing the char after START,
2014 and PREV to the interval containing the char before START.
2015 Either one may be null. They may be equal. */
2016 i = find_interval (intervals, start);
2017
2018 if (start == BUF_BEGV (buf))
2019 prev = 0;
2020 else if (i->position == start)
2021 prev = previous_interval (i);
2022 else if (i->position < start)
2023 prev = i;
2024 if (start == BUF_ZV (buf))
2025 i = 0;
2026
2027 /* If Vinhibit_read_only is set and is not a list, we can
2028 skip the read_only checks. */
2029 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2030 {
2031 /* If I and PREV differ we need to check for the read-only
cdf3e5a2 2032 property together with its stickiness. If either I or
318d2fa8
RS
2033 PREV are 0, this check is all we need.
2034 We have to take special care, since read-only may be
2035 indirectly defined via the category property. */
2036 if (i != prev)
2037 {
2038 if (! NULL_INTERVAL_P (i))
2039 {
2040 after = textget (i->plist, Qread_only);
2041
2042 /* If interval I is read-only and read-only is
2043 front-sticky, inhibit insertion.
2044 Check for read-only as well as category. */
2045 if (! NILP (after)
2046 && NILP (Fmemq (after, Vinhibit_read_only)))
2047 {
2048 Lisp_Object tem;
2049
2050 tem = textget (i->plist, Qfront_sticky);
2051 if (TMEM (Qread_only, tem)
2052 || (NILP (Fplist_get (i->plist, Qread_only))
2053 && TMEM (Qcategory, tem)))
bcf97349 2054 text_read_only (after);
318d2fa8
RS
2055 }
2056 }
2057
2058 if (! NULL_INTERVAL_P (prev))
2059 {
2060 before = textget (prev->plist, Qread_only);
2061
2062 /* If interval PREV is read-only and read-only isn't
2063 rear-nonsticky, inhibit insertion.
2064 Check for read-only as well as category. */
2065 if (! NILP (before)
2066 && NILP (Fmemq (before, Vinhibit_read_only)))
2067 {
2068 Lisp_Object tem;
2069
2070 tem = textget (prev->plist, Qrear_nonsticky);
2071 if (! TMEM (Qread_only, tem)
2072 && (! NILP (Fplist_get (prev->plist,Qread_only))
2073 || ! TMEM (Qcategory, tem)))
bcf97349 2074 text_read_only (before);
318d2fa8
RS
2075 }
2076 }
2077 }
2078 else if (! NULL_INTERVAL_P (i))
2079 {
2080 after = textget (i->plist, Qread_only);
2081
2082 /* If interval I is read-only and read-only is
2083 front-sticky, inhibit insertion.
2084 Check for read-only as well as category. */
2085 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2086 {
2087 Lisp_Object tem;
2088
2089 tem = textget (i->plist, Qfront_sticky);
2090 if (TMEM (Qread_only, tem)
2091 || (NILP (Fplist_get (i->plist, Qread_only))
2092 && TMEM (Qcategory, tem)))
bcf97349 2093 text_read_only (after);
318d2fa8
RS
2094
2095 tem = textget (prev->plist, Qrear_nonsticky);
2096 if (! TMEM (Qread_only, tem)
2097 && (! NILP (Fplist_get (prev->plist, Qread_only))
2098 || ! TMEM (Qcategory, tem)))
bcf97349 2099 text_read_only (after);
318d2fa8
RS
2100 }
2101 }
2102 }
2103
2104 /* Run both insert hooks (just once if they're the same). */
2105 if (!NULL_INTERVAL_P (prev))
2106 interval_insert_behind_hooks
2107 = textget (prev->plist, Qinsert_behind_hooks);
2108 if (!NULL_INTERVAL_P (i))
2109 interval_insert_in_front_hooks
2110 = textget (i->plist, Qinsert_in_front_hooks);
2111 }
0ba7995b 2112 else
318d2fa8
RS
2113 {
2114 /* Loop over intervals on or next to START...END,
2115 collecting their hooks. */
2116
2117 i = find_interval (intervals, start);
2118 do
2119 {
2120 if (! INTERVAL_WRITABLE_P (i))
bcf97349 2121 text_read_only (textget (i->plist, Qread_only));
318d2fa8 2122
0ba7995b 2123 if (!inhibit_modification_hooks)
318d2fa8 2124 {
0ba7995b
GM
2125 mod_hooks = textget (i->plist, Qmodification_hooks);
2126 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2127 {
2128 hooks = Fcons (mod_hooks, hooks);
2129 prev_mod_hooks = mod_hooks;
2130 }
318d2fa8
RS
2131 }
2132
2133 i = next_interval (i);
2134 }
2135 /* Keep going thru the interval containing the char before END. */
2136 while (! NULL_INTERVAL_P (i) && i->position < end);
2137
0ba7995b 2138 if (!inhibit_modification_hooks)
318d2fa8 2139 {
0ba7995b
GM
2140 GCPRO1 (hooks);
2141 hooks = Fnreverse (hooks);
2142 while (! EQ (hooks, Qnil))
2143 {
2144 call_mod_hooks (Fcar (hooks), make_number (start),
2145 make_number (end));
2146 hooks = Fcdr (hooks);
2147 }
2148 UNGCPRO;
318d2fa8 2149 }
318d2fa8
RS
2150 }
2151}
2152
96f90544 2153/* Run the interval hooks for an insertion on character range START ... END.
318d2fa8
RS
2154 verify_interval_modification chose which hooks to run;
2155 this function is called after the insertion happens
2156 so it can indicate the range of inserted text. */
2157
2158void
2159report_interval_modification (start, end)
2160 Lisp_Object start, end;
2161{
2162 if (! NILP (interval_insert_behind_hooks))
2e34157c 2163 call_mod_hooks (interval_insert_behind_hooks, start, end);
318d2fa8
RS
2164 if (! NILP (interval_insert_in_front_hooks)
2165 && ! EQ (interval_insert_in_front_hooks,
2166 interval_insert_behind_hooks))
2e34157c 2167 call_mod_hooks (interval_insert_in_front_hooks, start, end);
318d2fa8
RS
2168}
2169\f
d418ef42
JA
2170void
2171syms_of_textprop ()
2172{
ad1b2f20 2173 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
8c1a1077
PJ
2174 doc: /* Property-list used as default values.
2175The value of a property in this list is seen as the value for every
2176character that does not have its own value for that property. */);
ad1b2f20 2177 Vdefault_text_properties = Qnil;
c7dd82a3 2178
49d110a8
CW
2179 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2180 doc: /* Alist of alternative properties for properties without a value.
2181Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2182If a piece of text has no direct value for a particular property, then
2183this alist is consulted. If that property appears in the alist, then
2184the first non-nil value from the associated alternative properties is
2185returned. */);
2186 Vchar_property_alias_alist = Qnil;
2187
688a5a0f 2188 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
8c1a1077
PJ
2189 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2190This also inhibits the use of the `intangible' text property. */);
688a5a0f 2191 Vinhibit_point_motion_hooks = Qnil;
318d2fa8 2192
abc2f676
KH
2193 DEFVAR_LISP ("text-property-default-nonsticky",
2194 &Vtext_property_default_nonsticky,
8c1a1077
PJ
2195 doc: /* Alist of properties vs the corresponding non-stickinesses.
2196Each element has the form (PROPERTY . NONSTICKINESS).
2197
2198If a character in a buffer has PROPERTY, new text inserted adjacent to
2199the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2200inherits it if NONSTICKINESS is nil. The front-sticky and
2201rear-nonsticky properties of the character overrides NONSTICKINESS. */);
abc2f676
KH
2202 Vtext_property_default_nonsticky = Qnil;
2203
318d2fa8
RS
2204 staticpro (&interval_insert_behind_hooks);
2205 staticpro (&interval_insert_in_front_hooks);
2206 interval_insert_behind_hooks = Qnil;
2207 interval_insert_in_front_hooks = Qnil;
2208
688a5a0f 2209
d418ef42
JA
2210 /* Common attributes one might give text */
2211
2212 staticpro (&Qforeground);
2213 Qforeground = intern ("foreground");
2214 staticpro (&Qbackground);
2215 Qbackground = intern ("background");
2216 staticpro (&Qfont);
2217 Qfont = intern ("font");
2218 staticpro (&Qstipple);
2219 Qstipple = intern ("stipple");
2220 staticpro (&Qunderline);
2221 Qunderline = intern ("underline");
2222 staticpro (&Qread_only);
2223 Qread_only = intern ("read-only");
2224 staticpro (&Qinvisible);
2225 Qinvisible = intern ("invisible");
46b4e741
KH
2226 staticpro (&Qintangible);
2227 Qintangible = intern ("intangible");
dc70cea7
RS
2228 staticpro (&Qcategory);
2229 Qcategory = intern ("category");
2230 staticpro (&Qlocal_map);
2231 Qlocal_map = intern ("local-map");
19e1c426
RS
2232 staticpro (&Qfront_sticky);
2233 Qfront_sticky = intern ("front-sticky");
2234 staticpro (&Qrear_nonsticky);
2235 Qrear_nonsticky = intern ("rear-nonsticky");
69bb837e
RS
2236 staticpro (&Qmouse_face);
2237 Qmouse_face = intern ("mouse-face");
d418ef42
JA
2238
2239 /* Properties that text might use to specify certain actions */
2240
2241 staticpro (&Qmouse_left);
2242 Qmouse_left = intern ("mouse-left");
2243 staticpro (&Qmouse_entered);
2244 Qmouse_entered = intern ("mouse-entered");
2245 staticpro (&Qpoint_left);
2246 Qpoint_left = intern ("point-left");
2247 staticpro (&Qpoint_entered);
2248 Qpoint_entered = intern ("point-entered");
d418ef42
JA
2249
2250 defsubr (&Stext_properties_at);
5fbe2a44 2251 defsubr (&Sget_text_property);
eb769fd7 2252 defsubr (&Sget_char_property);
fcab51aa
RS
2253 defsubr (&Snext_char_property_change);
2254 defsubr (&Sprevious_char_property_change);
b7e047fb
MB
2255 defsubr (&Snext_single_char_property_change);
2256 defsubr (&Sprevious_single_char_property_change);
d418ef42 2257 defsubr (&Snext_property_change);
9c79dd1b 2258 defsubr (&Snext_single_property_change);
d418ef42 2259 defsubr (&Sprevious_property_change);
9c79dd1b 2260 defsubr (&Sprevious_single_property_change);
d418ef42 2261 defsubr (&Sadd_text_properties);
d4b530ad 2262 defsubr (&Sput_text_property);
d418ef42
JA
2263 defsubr (&Sset_text_properties);
2264 defsubr (&Sremove_text_properties);
11713b6d 2265 defsubr (&Sremove_list_of_text_properties);
ad9c1940
JB
2266 defsubr (&Stext_property_any);
2267 defsubr (&Stext_property_not_all);
5fbe2a44 2268/* defsubr (&Serase_text_properties); */
15e4954b 2269/* defsubr (&Scopy_text_properties); */
d418ef42 2270}
25013c26 2271