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