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