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