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