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