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