aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJoseph Arceneaux1992-09-24 01:29:22 +0000
committerJoseph Arceneaux1992-09-24 01:29:22 +0000
commit9c79dd1b203d66a6a3f2227c22667a5ab5dbb290 (patch)
tree657e7e93f609382a0ad058c2c56f9925e5c16e43 /src
parentff462f26fd1dcf45c26cf0cdc5345dc04b52daf8 (diff)
downloademacs-9c79dd1b203d66a6a3f2227c22667a5ab5dbb290.tar.gz
emacs-9c79dd1b203d66a6a3f2227c22667a5ab5dbb290.zip
See ChangeLog
Diffstat (limited to 'src')
-rw-r--r--src/intervals.c411
-rw-r--r--src/intervals.h6
-rw-r--r--src/textprop.c89
3 files changed, 260 insertions, 246 deletions
diff --git a/src/intervals.c b/src/intervals.c
index 94970e6848b..fc725b5fac8 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -368,9 +368,6 @@ split_interval_right (interval, offset)
368 368
369 new->position = position + offset - 1; 369 new->position = position + offset - 1;
370 new->parent = interval; 370 new->parent = interval;
371#if 0
372 copy_properties (interval, new);
373#endif
374 371
375 if (LEAF_INTERVAL_P (interval) || NULL_RIGHT_CHILD (interval)) 372 if (LEAF_INTERVAL_P (interval) || NULL_RIGHT_CHILD (interval))
376 { 373 {
@@ -411,12 +408,7 @@ split_interval_left (interval, offset)
411 int position = interval->position; 408 int position = interval->position;
412 int new_length = offset - 1; 409 int new_length = offset - 1;
413 410
414#if 0
415 copy_properties (interval, new);
416#endif
417
418 new->position = interval->position; 411 new->position = interval->position;
419
420 interval->position = interval->position + offset - 1; 412 interval->position = interval->position + offset - 1;
421 new->parent = interval; 413 new->parent = interval;
422 414
@@ -674,92 +666,6 @@ adjust_intervals_for_insertion (tree, position, length)
674 return tree; 666 return tree;
675} 667}
676 668
677/* Merge interval I with its lexicographic successor. Note that
678 this does not deal with the properties, or delete I. */
679
680INTERVAL
681merge_interval_right (i)
682 register INTERVAL i;
683{
684 register int absorb = LENGTH (i);
685
686 /* Zero out this interval. */
687 i->total_length -= absorb;
688
689 /* Find the succeeding interval. */
690 if (! NULL_RIGHT_CHILD (i)) /* It's below us. Add absorb
691 as we descend. */
692 {
693 i = i->right;
694 while (! NULL_LEFT_CHILD (i))
695 {
696 i->total_length += absorb;
697 i = i->left;
698 }
699
700 i->total_length += absorb;
701 return i;
702 }
703
704 while (! NULL_PARENT (i)) /* It's above us. Subtract as
705 we ascend. */
706 {
707 if (AM_LEFT_CHILD (i))
708 {
709 i = i->parent;
710 return i;
711 }
712
713 i = i->parent;
714 i->total_length -= absorb;
715 }
716
717 return NULL_INTERVAL;
718}
719
720/* Merge interval I with its lexicographic predecessor. Note that
721 this does not deal with the properties, or delete I.*/
722
723INTERVAL
724merge_interval_left (i)
725 register INTERVAL i;
726{
727 register int absorb = LENGTH (i);
728
729 /* Zero out this interval. */
730 i->total_length -= absorb;
731
732 /* Find the preceding interval. */
733 if (! NULL_LEFT_CHILD (i)) /* It's below us. Go down,
734 adding ABSORB as we go. */
735 {
736 i = i->left;
737 while (! NULL_RIGHT_CHILD (i))
738 {
739 i->total_length += absorb;
740 i = i->right;
741 }
742
743 i->total_length += absorb;
744 return i;
745 }
746
747 while (! NULL_PARENT (i)) /* It's above us. Go up,
748 subtracting ABSORB. */
749 {
750 if (AM_RIGHT_CHILD (i))
751 {
752 i = i->parent;
753 return i;
754 }
755
756 i = i->parent;
757 i->total_length -= absorb;
758 }
759
760 return NULL_INTERVAL;
761}
762
763/* Delete an node I from its interval tree by merging its subtrees 669/* Delete an node I from its interval tree by merging its subtrees
764 into one subtree which is then returned. Caller is responsible for 670 into one subtree which is then returned. Caller is responsible for
765 storing the resulting subtree into its parent. */ 671 storing the resulting subtree into its parent. */
@@ -992,7 +898,115 @@ offset_intervals (buffer, start, length)
992 else 898 else
993 adjust_intervals_for_deletion (buffer, start, -length); 899 adjust_intervals_for_deletion (buffer, start, -length);
994} 900}
901
902/* Merge interval I with its lexicographic successor. The resulting
903 interval is returned, and has the properties of the original
904 successor. The properties of I are lost. I is removed from the
905 interval tree.
906
907 IMPORTANT:
908 The caller must verify that this is not the last (rightmost)
909 interval. */
910
911INTERVAL
912merge_interval_right (i)
913 register INTERVAL i;
914{
915 register int absorb = LENGTH (i);
916 register INTERVAL successor;
917
918 /* Zero out this interval. */
919 i->total_length -= absorb;
920
921 /* Find the succeeding interval. */
922 if (! NULL_RIGHT_CHILD (i)) /* It's below us. Add absorb
923 as we descend. */
924 {
925 successor = i->right;
926 while (! NULL_LEFT_CHILD (successor))
927 {
928 successor->total_length += absorb;
929 successor = successor->left;
930 }
931
932 successor->total_length += absorb;
933 delete_interval (i);
934 return successor;
935 }
936
937 successor = i;
938 while (! NULL_PARENT (successor)) /* It's above us. Subtract as
939 we ascend. */
940 {
941 if (AM_LEFT_CHILD (successor))
942 {
943 successor = successor->parent;
944 delete_interval (i);
945 return successor;
946 }
947
948 successor = successor->parent;
949 successor->total_length -= absorb;
950 }
951
952 /* This must be the rightmost or last interval and cannot
953 be merged right. The caller should have known. */
954 abort ();
955}
956
957/* Merge interval I with its lexicographic predecessor. The resulting
958 interval is returned, and has the properties of the original predecessor.
959 The properties of I are lost. Interval node I is removed from the tree.
960
961 IMPORTANT:
962 The caller must verify that this is not the first (leftmost) interval. */
963
964INTERVAL
965merge_interval_left (i)
966 register INTERVAL i;
967{
968 register int absorb = LENGTH (i);
969 register INTERVAL predecessor;
970
971 /* Zero out this interval. */
972 i->total_length -= absorb;
973
974 /* Find the preceding interval. */
975 if (! NULL_LEFT_CHILD (i)) /* It's below us. Go down,
976 adding ABSORB as we go. */
977 {
978 predecessor = i->left;
979 while (! NULL_RIGHT_CHILD (predecessor))
980 {
981 predecessor->total_length += absorb;
982 predecessor = predecessor->right;
983 }
984
985 predecessor->total_length += absorb;
986 delete_interval (i);
987 return predecessor;
988 }
989
990 predecessor = i;
991 while (! NULL_PARENT (predecessor)) /* It's above us. Go up,
992 subtracting ABSORB. */
993 {
994 if (AM_RIGHT_CHILD (predecessor))
995 {
996 predecessor = predecessor->parent;
997 delete_interval (i);
998 return predecessor;
999 }
1000
1001 predecessor = predecessor->parent;
1002 predecessor->total_length -= absorb;
1003 }
995 1004
1005 /* This must be the leftmost or first interval and cannot
1006 be merged left. The caller should have known. */
1007 abort ();
1008}
1009
996/* Make an exact copy of interval tree SOURCE which descends from 1010/* Make an exact copy of interval tree SOURCE which descends from
997 PARENT. This is done by recursing through SOURCE, copying 1011 PARENT. This is done by recursing through SOURCE, copying
998 the current interval and its properties, and then adjusting 1012 the current interval and its properties, and then adjusting
@@ -1056,33 +1070,10 @@ make_new_interval (intervals, start, length)
1056 return slot; 1070 return slot;
1057} 1071}
1058 1072
1059void 1073/* Insert the intervals of SOURCE into BUFFER at POSITION.
1060map_intervals (source, destination, position)
1061 INTERVAL source, destination;
1062 int position;
1063{
1064 register INTERVAL i, t;
1065
1066 if (NULL_INTERVAL_P (source))
1067 return;
1068 i = find_interval (destination, position);
1069 if (NULL_INTERVAL_P (i))
1070 return;
1071
1072 t = find_interval (source, 1);
1073 while (! NULL_INTERVAL_P (t))
1074 {
1075 i = make_new_interval (destination, position, LENGTH (t));
1076 position += LENGTH (t);
1077 copy_properties (t, i);
1078 t = next_interval (t);
1079 }
1080}
1081
1082/* Insert the intervals of NEW_TREE into BUFFER at POSITION.
1083 1074
1084 This is used in insdel.c when inserting Lisp_Strings into 1075 This is used in insdel.c when inserting Lisp_Strings into
1085 the buffer. The text corresponding to NEW_TREE is already in 1076 the buffer. The text corresponding to SOURCE is already in
1086 the buffer when this is called. The intervals of new tree are 1077 the buffer when this is called. The intervals of new tree are
1087 those belonging to the string being inserted; a copy is not made. 1078 those belonging to the string being inserted; a copy is not made.
1088 1079
@@ -1111,17 +1102,17 @@ map_intervals (source, destination, position)
1111 text... */ 1102 text... */
1112 1103
1113void 1104void
1114graft_intervals_into_buffer (new_tree, position, b) 1105graft_intervals_into_buffer (source, position, buffer)
1115 INTERVAL new_tree; 1106 INTERVAL source;
1116 int position; 1107 int position;
1117 struct buffer *b; 1108 struct buffer *buffer;
1118{ 1109{
1119 register INTERVAL under, over, this; 1110 register INTERVAL under, over, this;
1120 register INTERVAL tree = b->intervals; 1111 register INTERVAL tree = buffer->intervals;
1121 1112
1122 /* If the new text has no properties, it becomes part of whatever 1113 /* If the new text has no properties, it becomes part of whatever
1123 interval it was inserted into. */ 1114 interval it was inserted into. */
1124 if (NULL_INTERVAL_P (new_tree)) 1115 if (NULL_INTERVAL_P (source))
1125 return; 1116 return;
1126 1117
1127 /* Paranoia -- the text has already been added, so this buffer 1118 /* Paranoia -- the text has already been added, so this buffer
@@ -1133,9 +1124,9 @@ graft_intervals_into_buffer (new_tree, position, b)
1133 { 1124 {
1134 /* The inserted text constitutes the whole buffer, so 1125 /* The inserted text constitutes the whole buffer, so
1135 simply copy over the interval structure. */ 1126 simply copy over the interval structure. */
1136 if (BUF_Z (b) == TOTAL_LENGTH (new_tree)) 1127 if (BUF_Z (b) == TOTAL_LENGTH (source))
1137 { 1128 {
1138 b->intervals = reproduce_tree (new_tree, tree->parent); 1129 buffer->intervals = reproduce_tree (source, tree->parent);
1139 /* Explicitly free the old tree here. */ 1130 /* Explicitly free the old tree here. */
1140 1131
1141 return; 1132 return;
@@ -1150,14 +1141,14 @@ graft_intervals_into_buffer (new_tree, position, b)
1150 } 1141 }
1151 } 1142 }
1152 else 1143 else
1153 if (TOTAL_LENGTH (tree) == TOTAL_LENGTH (new_tree)) 1144 if (TOTAL_LENGTH (tree) == TOTAL_LENGTH (source))
1154 1145
1155 /* If the buffer contains only the new string, but 1146 /* If the buffer contains only the new string, but
1156 there was already some interval tree there, then it may be 1147 there was already some interval tree there, then it may be
1157 some zero length intervals. Eventually, do something clever 1148 some zero length intervals. Eventually, do something clever
1158 about inserting properly. For now, just waste the old intervals. */ 1149 about inserting properly. For now, just waste the old intervals. */
1159 { 1150 {
1160 b->intervals = reproduce_tree (new_tree, tree->parent); 1151 buffer->intervals = reproduce_tree (source, tree->parent);
1161 /* Explicitly free the old tree here. */ 1152 /* Explicitly free the old tree here. */
1162 1153
1163 return; 1154 return;
@@ -1166,7 +1157,7 @@ graft_intervals_into_buffer (new_tree, position, b)
1166 this = under = find_interval (tree, position); 1157 this = under = find_interval (tree, position);
1167 if (NULL_INTERVAL_P (under)) /* Paranoia */ 1158 if (NULL_INTERVAL_P (under)) /* Paranoia */
1168 abort (); 1159 abort ();
1169 over = find_interval (new_tree, 1); 1160 over = find_interval (source, 1);
1170 1161
1171 /* Insertion between intervals */ 1162 /* Insertion between intervals */
1172 if (position == under->position) 1163 if (position == under->position)
@@ -1184,7 +1175,8 @@ graft_intervals_into_buffer (new_tree, position, b)
1184 over = next_interval (over); 1175 over = next_interval (over);
1185 } 1176 }
1186 else 1177 else
1187 /* This string sticks to under */ 1178 /* This string "sticks" to the first interval, `under',
1179 which means it gets those properties. */
1188 while (! NULL_INTERVAL_P (over)) 1180 while (! NULL_INTERVAL_P (over))
1189 { 1181 {
1190 position = LENGTH (over) + 1; 1182 position = LENGTH (over) + 1;
@@ -1229,7 +1221,8 @@ graft_intervals_into_buffer (new_tree, position, b)
1229 else 1221 else
1230 { 1222 {
1231 if (FRONT_STICKY (under)) 1223 if (FRONT_STICKY (under))
1232 /* The intervals stick to under */ 1224 /* The inserted text "sticks" to the interval `under',
1225 which means it gets those properties. */
1233 while (! NULL_INTERVAL_P (over)) 1226 while (! NULL_INTERVAL_P (over))
1234 { 1227 {
1235 position = LENGTH (over) + 1; 1228 position = LENGTH (over) + 1;
@@ -1251,16 +1244,16 @@ graft_intervals_into_buffer (new_tree, position, b)
1251 } 1244 }
1252 } 1245 }
1253 1246
1254 b->intervals = balance_intervals (b->intervals); 1247 buffer->intervals = balance_intervals (buffer->intervals);
1255 return; 1248 return;
1256 } 1249 }
1257 1250
1258 /* Here for insertion in the middle of an interval. */ 1251 /* Here for insertion in the middle of an interval. */
1259 1252
1260 if (TOTAL_LENGTH (new_tree) < LENGTH (this)) 1253 if (TOTAL_LENGTH (source) < LENGTH (this))
1261 { 1254 {
1262 INTERVAL end_unchanged 1255 INTERVAL end_unchanged
1263 = split_interval_right (this, TOTAL_LENGTH (new_tree) + 1); 1256 = split_interval_right (this, TOTAL_LENGTH (source) + 1);
1264 copy_properties (under, end_unchanged); 1257 copy_properties (under, end_unchanged);
1265 } 1258 }
1266 1259
@@ -1276,39 +1269,10 @@ graft_intervals_into_buffer (new_tree, position, b)
1276 over = next_interval (over); 1269 over = next_interval (over);
1277 } 1270 }
1278 1271
1279 b->intervals = balance_intervals (b->intervals); 1272 buffer->intervals = balance_intervals (buffer->intervals);
1280 return; 1273 return;
1281} 1274}
1282 1275
1283/* Intervals can have properties which are hooks to call. Look for
1284 the property HOOK on interval I, and if found, call its value as
1285 a function.*/
1286
1287void
1288run_hooks (i, hook)
1289 INTERVAL i;
1290 Lisp_Object hook;
1291{
1292 register Lisp_Object tail = i->plist;
1293 register Lisp_Object sym, val;
1294
1295 while (! NILP (tail))
1296 {
1297 sym = Fcar (tail);
1298 if (EQ (sym, hook))
1299 {
1300 Lisp_Object begin, end;
1301 XFASTINT (begin) = i->position;
1302 XFASTINT (end) = i->position + LENGTH (i) - 1;
1303 val = Fcar (Fcdr (tail));
1304 call2 (val, begin, end);
1305 return;
1306 }
1307
1308 tail = Fcdr (Fcdr (tail));
1309 }
1310}
1311
1312/* Set point in BUFFER to POSITION. If the target position is in 1276/* Set point in BUFFER to POSITION. If the target position is in
1313 an invisible interval which is not displayed with a special glyph, 1277 an invisible interval which is not displayed with a special glyph,
1314 skip intervals until we find one. Point may be at the first 1278 skip intervals until we find one. Point may be at the first
@@ -1327,6 +1291,7 @@ set_point (position, buffer)
1327 int buffer_point; 1291 int buffer_point;
1328 register Lisp_Object obj; 1292 register Lisp_Object obj;
1329 int backwards = (position < BUF_PT (buffer)) ? 1 : 0; 1293 int backwards = (position < BUF_PT (buffer)) ? 1 : 0;
1294 int old_position = buffer->text.pt;
1330 1295
1331 if (position == buffer->text.pt) 1296 if (position == buffer->text.pt)
1332 return; 1297 return;
@@ -1349,7 +1314,10 @@ set_point (position, buffer)
1349 buffer_point =(BUF_PT (buffer) == BUF_Z (buffer) 1314 buffer_point =(BUF_PT (buffer) == BUF_Z (buffer)
1350 ? BUF_Z (buffer) - 1 1315 ? BUF_Z (buffer) - 1
1351 : BUF_PT (buffer)); 1316 : BUF_PT (buffer));
1317
1318 /* We could cache this and save time. */
1352 from = find_interval (buffer->intervals, buffer_point); 1319 from = find_interval (buffer->intervals, buffer_point);
1320
1353 if (NULL_INTERVAL_P (to) || NULL_INTERVAL_P (from)) 1321 if (NULL_INTERVAL_P (to) || NULL_INTERVAL_P (from))
1354 abort (); /* Paranoia */ 1322 abort (); /* Paranoia */
1355 1323
@@ -1386,14 +1354,36 @@ set_point (position, buffer)
1386 1354
1387 /* We should run point-left and point-entered hooks here, iff the 1355 /* We should run point-left and point-entered hooks here, iff the
1388 two intervals are not equivalent. */ 1356 two intervals are not equivalent. */
1357 if (! intervals_equal (from, to))
1358 {
1359 Lisp_Object val;
1360
1361 val = Fget (Qpoint_left, from->plist);
1362 if (! NILP (val))
1363 call2 (val, old_position, position);
1364
1365 val = Fget (Qpoint_entered, to->plist);
1366 if (! NILP (val))
1367 call2 (val, old_position, position);
1368 }
1389} 1369}
1390 1370
1391/* Check for read-only intervals. Call the modification hooks if any. 1371/* Set point temporarily, without checking any text properties. */
1392 Check for the range START up to (but not including) TO.
1393 1372
1394 First all intervals of the region are checked that they are 1373INLINE void
1395 modifiable, then all the modification hooks are called in 1374temp_set_point (position, buffer)
1396 lexicographic order. */ 1375 int position;
1376 struct buffer *buffer;
1377{
1378 buffer->text.pt = position;
1379}
1380
1381/* Check for read-only intervals and signal an error if we find one.
1382 Then check for any modification hooks in the range START up to
1383 (but not including) TO. Create a list of all these hooks in
1384 lexicographic order, eliminating consecutive extra copies of the
1385 same hook. Then call those hooks in order, with START and END - 1
1386 as arguments. */
1397 1387
1398void 1388void
1399verify_interval_modification (buf, start, end) 1389verify_interval_modification (buf, start, end)
@@ -1403,6 +1393,9 @@ verify_interval_modification (buf, start, end)
1403 register INTERVAL intervals = buf->intervals; 1393 register INTERVAL intervals = buf->intervals;
1404 register INTERVAL i; 1394 register INTERVAL i;
1405 register Lisp_Object hooks = Qnil; 1395 register Lisp_Object hooks = Qnil;
1396 register prev_mod_hook = Qnil;
1397 register Lisp_Object mod_hook;
1398 struct gcpro gcpro1;
1406 1399
1407 if (NULL_INTERVAL_P (intervals)) 1400 if (NULL_INTERVAL_P (intervals))
1408 return; 1401 return;
@@ -1416,6 +1409,7 @@ verify_interval_modification (buf, start, end)
1416 1409
1417 if (start == BUF_Z (buf)) 1410 if (start == BUF_Z (buf))
1418 { 1411 {
1412 /* This should not be getting called on empty buffers. */
1419 if (BUF_Z (buf) == 1) 1413 if (BUF_Z (buf) == 1)
1420 abort (); 1414 abort ();
1421 1415
@@ -1428,19 +1422,28 @@ verify_interval_modification (buf, start, end)
1428 1422
1429 do 1423 do
1430 { 1424 {
1431 register Lisp_Object mod_hook;
1432 if (! INTERVAL_WRITABLE_P (i)) 1425 if (! INTERVAL_WRITABLE_P (i))
1433 error ("Attempt to write in a protected interval"); 1426 error ("Attempt to modify read-only text");
1427
1434 mod_hook = Fget (Qmodification, i->plist); 1428 mod_hook = Fget (Qmodification, i->plist);
1435 if (! EQ (mod_hook, Qnil)) 1429 if (! NILP (mod_hook) && ! EQ (mod_hook, prev_mod_hook))
1436 hooks = Fcons (mod_hook, hooks); 1430 {
1431 hooks = Fcons (mod_hook, hooks);
1432 prev_mod_hook = mod_hook;
1433 }
1434
1437 i = next_interval (i); 1435 i = next_interval (i);
1438 } 1436 }
1439 while (! NULL_INTERVAL_P (i) && i->position <= end); 1437 while (! NULL_INTERVAL_P (i) && i->position <= end);
1440 1438
1439 GCPRO1 (hooks);
1441 hooks = Fnreverse (hooks); 1440 hooks = Fnreverse (hooks);
1442 while (! EQ (hooks, Qnil)) 1441 while (! EQ (hooks, Qnil))
1443 call2 (Fcar (hooks), i->position, i->position + LENGTH (i) - 1); 1442 {
1443 call2 (Fcar (hooks), start, end - 1);
1444 hooks = Fcdr (hooks);
1445 }
1446 UNGCPRO;
1444} 1447}
1445 1448
1446/* Balance an interval node if the amount of text in its left and right 1449/* Balance an interval node if the amount of text in its left and right
@@ -1500,7 +1503,7 @@ balance_intervals (tree)
1500 return new_tree; 1503 return new_tree;
1501} 1504}
1502 1505
1503/* Produce an interval tree reflecting the interval structure in 1506/* Produce an interval tree reflecting the intervals in
1504 TREE from START to START + LENGTH. */ 1507 TREE from START to START + LENGTH. */
1505 1508
1506static INTERVAL 1509static INTERVAL
@@ -1526,19 +1529,14 @@ copy_intervals (tree, start, length)
1526 new = make_interval (); 1529 new = make_interval ();
1527 new->position = 1; 1530 new->position = 1;
1528 got = (LENGTH (i) - (start - i->position)); 1531 got = (LENGTH (i) - (start - i->position));
1529 new->total_length = got; 1532 new->total_length = length;
1530 copy_properties (i, new); 1533 copy_properties (i, new);
1531 1534
1532 t = new; 1535 t = new;
1533 while (got < length) 1536 while (got < length)
1534 { 1537 {
1535 i = next_interval (i); 1538 i = next_interval (i);
1536 t->right = make_interval (); 1539 t = split_interval_right (t, got + 1);
1537 t->right->parent = t;
1538 t->right->position = t->position + got - 1;
1539
1540 t = t->right;
1541 t->total_length = length - got;
1542 copy_properties (i, t); 1540 copy_properties (i, t);
1543 got += LENGTH (i); 1541 got += LENGTH (i);
1544 } 1542 }
@@ -1549,21 +1547,6 @@ copy_intervals (tree, start, length)
1549 return balance_intervals (new); 1547 return balance_intervals (new);
1550} 1548}
1551 1549
1552/* Give buffer SINK the properties of buffer SOURCE from POSITION
1553 to END. The properties are attached to SINK starting at position AT.
1554
1555 No range checking is done. */
1556
1557void
1558insert_interval_copy (source, position, end, sink, at)
1559 struct buffer *source, *sink;
1560 register int position, end, at;
1561{
1562 INTERVAL interval_copy = copy_intervals (source->intervals,
1563 position, end - position);
1564 graft_intervals_into_buffer (interval_copy, at, sink);
1565}
1566
1567/* Give STRING the properties of BUFFER from POSITION to LENGTH. */ 1550/* Give STRING the properties of BUFFER from POSITION to LENGTH. */
1568 1551
1569void 1552void
@@ -1579,37 +1562,3 @@ copy_intervals_to_string (string, buffer, position, length)
1579 interval_copy->parent = (INTERVAL) string; 1562 interval_copy->parent = (INTERVAL) string;
1580 XSTRING (string)->intervals = interval_copy; 1563 XSTRING (string)->intervals = interval_copy;
1581} 1564}
1582
1583INTERVAL
1584make_string_interval (string, start, length)
1585 struct Lisp_String *string;
1586 int start, length;
1587{
1588 if (start < 1 || start > string->size)
1589 error ("Interval index out of range");
1590 if (length < 1 || length > string->size - start + 1)
1591 error ("Interval won't fit");
1592
1593 if (length == 0)
1594 return NULL_INTERVAL;
1595
1596 return make_new_interval (string->intervals, start, length);
1597}
1598
1599/* Create an interval of length LENGTH in buffer BUF at position START. */
1600
1601INTERVAL
1602make_buffer_interval (buf, start, length)
1603 struct buffer *buf;
1604 int start, length;
1605{
1606 if (start < BUF_BEG (buf) || start > BUF_Z (buf))
1607 error ("Interval index out of range");
1608 if (length < 1 || length > BUF_Z (buf) - start)
1609 error ("Interval won't fit");
1610
1611 if (length == 0)
1612 return NULL_INTERVAL;
1613
1614 return make_new_interval (buf->intervals, start, length);
1615}
diff --git a/src/intervals.h b/src/intervals.h
index 48d3daeaad1..29fca360c98 100644
--- a/src/intervals.h
+++ b/src/intervals.h
@@ -167,15 +167,11 @@ extern INTERVAL find_interval (), next_interval (), previous_interval ();
167extern INTERVAL merge_interval_left (), merge_interval_right (); 167extern INTERVAL merge_interval_left (), merge_interval_right ();
168extern void delete_interval (); 168extern void delete_interval ();
169extern INLINE void offset_intervals (); 169extern INLINE void offset_intervals ();
170extern void map_intervals ();
171extern void graft_intervals_into_buffer (); 170extern void graft_intervals_into_buffer ();
172extern void set_point (); 171extern void set_point ();
173extern void verify_interval_modification (); 172extern void verify_interval_modification ();
174extern INTERVAL balance_intervals (); 173extern INTERVAL balance_intervals ();
175extern void insert_interval_copy();
176extern void copy_intervals_to_string (); 174extern void copy_intervals_to_string ();
177extern INTERVAL make_string_interval ();
178extern INTERVAL make_buffer_interval ();
179 175
180/* Declared in textprop.c */ 176/* Declared in textprop.c */
181 177
@@ -190,8 +186,6 @@ extern Lisp_Object Qmodification;
190extern Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple; 186extern Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
191extern Lisp_Object Qinvisible, Qread_only; 187extern Lisp_Object Qinvisible, Qread_only;
192 188
193extern void run_hooks ();
194
195extern Lisp_Object Ftext_properties_at (); 189extern Lisp_Object Ftext_properties_at ();
196extern Lisp_Object Fnext_property_change (), Fprevious_property_change (); 190extern Lisp_Object Fnext_property_change (), Fprevious_property_change ();
197extern Lisp_Object Fadd_text_properties (), Fset_text_properties (); 191extern Lisp_Object Fadd_text_properties (), Fset_text_properties ();
diff --git a/src/textprop.c b/src/textprop.c
index e7387bd1262..350ceec9b1c 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -27,6 +27,8 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
27 zero-length intervals if they are implemented. This could be done 27 zero-length intervals if they are implemented. This could be done
28 inside next_interval and previous_interval. 28 inside next_interval and previous_interval.
29 29
30 set_properties needs to deal with the interval property cache.
31
30 It is assumed that for any interval plist, a property appears 32 It is assumed that for any interval plist, a property appears
31 only once on the list. Although some code i.e., remove_properties (), 33 only once on the list. Although some code i.e., remove_properties (),
32 handles the more general case, the uniqueness of properties is 34 handles the more general case, the uniqueness of properties is
@@ -324,7 +326,6 @@ erase_properties (i)
324 return 1; 326 return 1;
325} 327}
326 328
327
328DEFUN ("text-properties-at", Ftext_properties_at, 329DEFUN ("text-properties-at", Ftext_properties_at,
329 Stext_properties_at, 1, 2, 0, 330 Stext_properties_at, 1, 2, 0,
330 "Return the list of properties held by the character at POSITION\n\ 331 "Return the list of properties held by the character at POSITION\n\
@@ -370,9 +371,34 @@ Returns nil if unsuccessful.")
370 return next->position; 371 return next->position;
371} 372}
372 373
374DEFUN ("next-single-property-change", Fnext_single_property_change,
375 Snext_single_property_change, 3, 3, 0,
376 "Return the position after POSITION in OBJECT which has a different\n\
377value for PROPERTY than the text at POSITION. OBJECT may be a string or\n\
378buffer. Returns nil if unsuccessful.")
379 (pos, object, prop)
380{
381 register INTERVAL i, next;
382 register Lisp_Object here_val;
383
384 i = validate_interval_range (object, &pos, &pos, soft);
385 if (NULL_INTERVAL_P (i))
386 return Qnil;
387
388 here_val = Fget (prop, i->plist);
389 next = next_interval (i);
390 while (! NULL_INTERVAL_P (next) && EQ (here_val, Fget (prop, next->plist)))
391 next = next_interval (next);
392
393 if (NULL_INTERVAL_P (next))
394 return Qnil;
395
396 return next->position;
397}
398
373DEFUN ("previous-property-change", Fprevious_property_change, 399DEFUN ("previous-property-change", Fprevious_property_change,
374 Sprevious_property_change, 2, 2, 0, 400 Sprevious_property_change, 2, 2, 0,
375 "Return the position before POSITION in OBJECT which has properties\n\ 401 "Return the position preceding POSITION in OBJECT which has properties\n\
376different from those at POSITION. OBJECT may be a string or buffer.\n\ 402different from those at POSITION. OBJECT may be a string or buffer.\n\
377Returns nil if unsuccessful.") 403Returns nil if unsuccessful.")
378 (pos, object) 404 (pos, object)
@@ -393,6 +419,31 @@ Returns nil if unsuccessful.")
393 return previous->position + LENGTH (previous) - 1; 419 return previous->position + LENGTH (previous) - 1;
394} 420}
395 421
422DEFUN ("previous-single-property-change", Fprevious_single_property_change,
423 Sprevious_single_property_change, 3, 3, 0,
424 "Return the position preceding POSITION in OBJECT which has a\n\
425different value for PROPERTY than the text at POSITION. OBJECT may be
426a string or buffer. Returns nil if unsuccessful.")
427 (pos, object, prop)
428{
429 register INTERVAL i, previous;
430 register Lisp_Object here_val;
431
432 i = validate_interval_range (object, &pos, &pos, soft);
433 if (NULL_INTERVAL_P (i))
434 return Qnil;
435
436 here_val = Fget (prop, i->plist);
437 previous = previous_interval (i);
438 while (! NULL_INTERVAL_P (previous)
439 && EQ (here_val, Fget (prop, previous->plist)))
440 previous = previous_interval (previous);
441 if (NULL_INTERVAL_P (previous))
442 return Qnil;
443
444 return previous->position + LENGTH (previous) - 1;
445}
446
396DEFUN ("add-text-properties", Fadd_text_properties, 447DEFUN ("add-text-properties", Fadd_text_properties,
397 Sadd_text_properties, 4, 4, 0, 448 Sadd_text_properties, 4, 4, 0,
398 "Add the PROPERTIES (a property list) to the text of OBJECT\n\ 449 "Add the PROPERTIES (a property list) to the text of OBJECT\n\
@@ -487,6 +538,7 @@ Otherwise return nil.")
487 Lisp_Object object, start, end, properties; 538 Lisp_Object object, start, end, properties;
488{ 539{
489 register INTERVAL i, unchanged; 540 register INTERVAL i, unchanged;
541 register INTERVAL prev_changed = NULL_INTERVAL;
490 register int s, len; 542 register int s, len;
491 543
492 properties = validate_plist (properties); 544 properties = validate_plist (properties);
@@ -504,15 +556,18 @@ Otherwise return nil.")
504 { 556 {
505 unchanged = i; 557 unchanged = i;
506 i = split_interval_right (unchanged, s - unchanged->position + 1); 558 i = split_interval_right (unchanged, s - unchanged->position + 1);
507 copy_properties (unchanged, i); 559 set_properties (properties, i);
508 if (LENGTH (i) > len) 560 if (LENGTH (i) > len)
509 { 561 {
510 i = split_interval_left (i, len); 562 i = split_interval_right (i, len);
511 set_properties (properties, i); 563 copy_properties (unchanged, i);
512 return Qt; 564 return Qt;
513 } 565 }
514 566
515 set_properties (properties, i); 567 if (LENGTH (i) == len)
568 return Qt;
569
570 prev_changed = i;
516 len -= LENGTH (i); 571 len -= LENGTH (i);
517 i = next_interval (i); 572 i = next_interval (i);
518 } 573 }
@@ -523,17 +578,30 @@ Otherwise return nil.")
523 { 578 {
524 if (LENGTH (i) == len) 579 if (LENGTH (i) == len)
525 { 580 {
526 set_properties (properties, i); 581 if (NULL_INTERVAL_P (prev_changed))
582 set_properties (properties, i);
583 else
584 merge_interval_left (i);
527 return Qt; 585 return Qt;
528 } 586 }
529 587
530 i = split_interval_left (i, len + 1); 588 i = split_interval_left (i, len + 1);
531 set_properties (properties, i); 589 if (NULL_INTERVAL_P (prev_changed))
590 set_properties (properties, i);
591 else
592 merge_interval_left (i);
532 return Qt; 593 return Qt;
533 } 594 }
534 595
535 len -= LENGTH (i); 596 len -= LENGTH (i);
536 set_properties (properties, i); 597 if (NULL_INTERVAL_P (prev_changed))
598 {
599 set_properties (properties, i);
600 prev_changed = i;
601 }
602 else
603 prev_changed = i = merge_interval_left (i);
604
537 i = next_interval (i); 605 i = next_interval (i);
538 } 606 }
539 607
@@ -557,6 +625,7 @@ was made, nil otherwise.")
557 625
558 s = XINT (start); 626 s = XINT (start);
559 len = XINT (end) - s; 627 len = XINT (end) - s;
628
560 if (i->position != s) 629 if (i->position != s)
561 { 630 {
562 /* No properties on this first interval -- return if 631 /* No properties on this first interval -- return if
@@ -719,7 +788,9 @@ percentage by which the left interval tree should not differ from the right.");
719 788
720 defsubr (&Stext_properties_at); 789 defsubr (&Stext_properties_at);
721 defsubr (&Snext_property_change); 790 defsubr (&Snext_property_change);
791 defsubr (&Snext_single_property_change);
722 defsubr (&Sprevious_property_change); 792 defsubr (&Sprevious_property_change);
793 defsubr (&Sprevious_single_property_change);
723 defsubr (&Sadd_text_properties); 794 defsubr (&Sadd_text_properties);
724 defsubr (&Sset_text_properties); 795 defsubr (&Sset_text_properties);
725 defsubr (&Sremove_text_properties); 796 defsubr (&Sremove_text_properties);