comparison src/intervals.c @ 1964:e6c49ff3a53c

(intervals_equal): Handle one arg null and other not. (set_point): Considerable rewrite. Handle intervals both before and after the old and new point values. Redo handling of invisible intervals, and of motion hooks. (textget): New function. (graft_intervals_into_buffer): create_root_interval needs Lisp object arg. Set tree to new root interval. Don't test TREE_LENGTH if buffer has no intervals. Rearrange code to copy properties so that it really does merge the inserted ones into the inherited ones. (traverse_intervals): Pass `arg' on recursive calls. (split_interval_left): Use new_length as basis for length of new. (traverse_intervals): New arg ARG.
author Richard M. Stallman <rms@gnu.org>
date Mon, 01 Mar 1993 08:56:22 +0000
parents 8bc716df45e3
children 48c83a34c005
comparison
equal deleted inserted replaced
1963:05dd60327cc4 1964:e6c49ff3a53c
89 COPY_INTERVAL_CACHE (source, target); 89 COPY_INTERVAL_CACHE (source, target);
90 target->plist = Fcopy_sequence (source->plist); 90 target->plist = Fcopy_sequence (source->plist);
91 } 91 }
92 92
93 /* Merge the properties of interval SOURCE into the properties 93 /* Merge the properties of interval SOURCE into the properties
94 of interval TARGET. */ 94 of interval TARGET. That is to say, each property in SOURCE
95 is added to TARGET if TARGET has no such property as yet. */
95 96
96 static void 97 static void
97 merge_properties (source, target) 98 merge_properties (source, target)
98 register INTERVAL source, target; 99 register INTERVAL source, target;
99 { 100 {
132 register Lisp_Object i0_cdr, i0_sym, i1_val; 133 register Lisp_Object i0_cdr, i0_sym, i1_val;
133 register i1_len; 134 register i1_len;
134 135
135 if (DEFAULT_INTERVAL_P (i0) && DEFAULT_INTERVAL_P (i1)) 136 if (DEFAULT_INTERVAL_P (i0) && DEFAULT_INTERVAL_P (i1))
136 return 1; 137 return 1;
138
139 if (DEFAULT_INTERVAL_P (i0) || DEFAULT_INTERVAL_P (i1))
140 return 0;
137 141
138 i1_len = XFASTINT (Flength (i1->plist)); 142 i1_len = XFASTINT (Flength (i1->plist));
139 if (i1_len & 0x1) /* Paranoia -- plists are always even */ 143 if (i1_len & 0x1) /* Paranoia -- plists are always even */
140 abort (); 144 abort ();
141 i1_len /= 2; 145 i1_len /= 2;
184 Lisp_Object arg; 188 Lisp_Object arg;
185 { 189 {
186 if (NULL_INTERVAL_P (tree)) 190 if (NULL_INTERVAL_P (tree))
187 return; 191 return;
188 192
189 traverse_intervals (tree->left, position, depth + 1, function); 193 traverse_intervals (tree->left, position, depth + 1, function, arg);
190 position += LEFT_TOTAL_LENGTH (tree); 194 position += LEFT_TOTAL_LENGTH (tree);
191 tree->position = position; 195 tree->position = position;
192 (*function) (tree, arg); 196 (*function) (tree, arg);
193 position += LENGTH (tree); 197 position += LENGTH (tree);
194 traverse_intervals (tree->right, position, depth + 1, function); 198 traverse_intervals (tree->right, position, depth + 1, function, arg);
195 } 199 }
196 200
197 #if 0 201 #if 0
198 /* These functions are temporary, for debugging purposes only. */ 202 /* These functions are temporary, for debugging purposes only. */
199 203
420 424
421 /* Insert the new node between INTERVAL and its left child. */ 425 /* Insert the new node between INTERVAL and its left child. */
422 new->left = interval->left; 426 new->left = interval->left;
423 new->left->parent = new; 427 new->left->parent = new;
424 interval->left = new; 428 interval->left = new;
425 new->total_length = LENGTH (new) + LEFT_TOTAL_LENGTH (new); 429 new->total_length = new_length + LEFT_TOTAL_LENGTH (new);
426 430
427 return new; 431 return new;
428 } 432 }
429 433
430 /* Find the interval containing text position POSITION in the text 434 /* Find the interval containing text position POSITION in the text
1103 graft_intervals_into_buffer (source, position, buffer) 1107 graft_intervals_into_buffer (source, position, buffer)
1104 INTERVAL source; 1108 INTERVAL source;
1105 int position; 1109 int position;
1106 struct buffer *buffer; 1110 struct buffer *buffer;
1107 { 1111 {
1108 register INTERVAL under, over, this; 1112 register INTERVAL under, over, this, prev;
1109 register INTERVAL tree = buffer->intervals; 1113 register INTERVAL tree = buffer->intervals;
1114 int middle;
1110 1115
1111 /* If the new text has no properties, it becomes part of whatever 1116 /* If the new text has no properties, it becomes part of whatever
1112 interval it was inserted into. */ 1117 interval it was inserted into. */
1113 if (NULL_INTERVAL_P (source)) 1118 if (NULL_INTERVAL_P (source))
1114 return; 1119 return;
1115
1116 /* Paranoia -- the text has already been added, so this buffer
1117 should be of non-zero length. */
1118 if (TOTAL_LENGTH (tree) == 0)
1119 abort ();
1120 1120
1121 if (NULL_INTERVAL_P (tree)) 1121 if (NULL_INTERVAL_P (tree))
1122 { 1122 {
1123 /* The inserted text constitutes the whole buffer, so 1123 /* The inserted text constitutes the whole buffer, so
1124 simply copy over the interval structure. */ 1124 simply copy over the interval structure. */
1129 1129
1130 return; 1130 return;
1131 } 1131 }
1132 1132
1133 /* Create an interval tree in which to place a copy 1133 /* Create an interval tree in which to place a copy
1134 of the intervals of the inserted string. */ 1134 of the intervals of the inserted string. */
1135 { 1135 {
1136 Lisp_Object buf; 1136 Lisp_Object buf;
1137 XSET (buf, Lisp_Buffer, buffer); 1137 XSET (buf, Lisp_Buffer, buffer);
1138 create_root_interval (buffer); 1138 tree = create_root_interval (buf);
1139 } 1139 }
1140 } 1140 }
1141 else 1141 else
1142 if (TOTAL_LENGTH (tree) == TOTAL_LENGTH (source)) 1142 if (TOTAL_LENGTH (tree) == TOTAL_LENGTH (source))
1143 1143 /* If the buffer contains only the new string, but
1144 /* If the buffer contains only the new string, but 1144 there was already some interval tree there, then it may be
1145 there was already some interval tree there, then it may be 1145 some zero length intervals. Eventually, do something clever
1146 some zero length intervals. Eventually, do something clever 1146 about inserting properly. For now, just waste the old intervals. */
1147 about inserting properly. For now, just waste the old intervals. */ 1147 {
1148 { 1148 buffer->intervals = reproduce_tree (source, tree->parent);
1149 buffer->intervals = reproduce_tree (source, tree->parent); 1149 /* Explicitly free the old tree here. */
1150 /* Explicitly free the old tree here. */ 1150
1151 1151 return;
1152 return; 1152 }
1153 } 1153 else
1154 /* Paranoia -- the text has already been added, so this buffer
1155 should be of non-zero length. */
1156 if (TOTAL_LENGTH (tree) == 0)
1157 abort ();
1154 1158
1155 this = under = find_interval (tree, position); 1159 this = under = find_interval (tree, position);
1156 if (NULL_INTERVAL_P (under)) /* Paranoia */ 1160 if (NULL_INTERVAL_P (under)) /* Paranoia */
1157 abort (); 1161 abort ();
1158 over = find_interval (source, 1); 1162 over = find_interval (source, 1);
1159 1163
1160 /* Insertion between intervals */ 1164 /* Here for insertion in the middle of an interval.
1161 if (position == under->position) 1165 Split off an equivalent interval to the right,
1162 { 1166 then don't bother with it any more. */
1163 /* First interval -- none precede it. */ 1167
1164 if (position == 1) 1168 if (position > under->position)
1165 {
1166 if (! FRONT_STICKY_P (under))
1167 /* The inserted string keeps its own properties. */
1168 while (! NULL_INTERVAL_P (over))
1169 {
1170 position = LENGTH (over) + 1;
1171 this = split_interval_left (this, position);
1172 copy_properties (over, this);
1173 over = next_interval (over);
1174 }
1175 else
1176 /* This string "sticks" to the first interval, `under',
1177 which means it gets those properties. */
1178 while (! NULL_INTERVAL_P (over))
1179 {
1180 position = LENGTH (over) + 1;
1181 this = split_interval_left (this, position);
1182 copy_properties (under, this);
1183 if (MERGE_INSERTIONS (under))
1184 merge_properties (over, this);
1185 over = next_interval (over);
1186 }
1187 }
1188 else
1189 {
1190 INTERVAL prev = previous_interval (under);
1191 if (NULL_INTERVAL_P (prev))
1192 abort ();
1193
1194 if (END_STICKY_P (prev))
1195 {
1196 if (FRONT_STICKY_P (under))
1197 /* The intervals go inbetween as the two sticky
1198 properties cancel each other. Should we change
1199 this policy? */
1200 while (! NULL_INTERVAL_P (over))
1201 {
1202 position = LENGTH (over) + 1;
1203 this = split_interval_left (this, position);
1204 copy_properties (over, this);
1205 over = next_interval (over);
1206 }
1207 else
1208 /* The intervals stick to prev */
1209 while (! NULL_INTERVAL_P (over))
1210 {
1211 position = LENGTH (over) + 1;
1212 this = split_interval_left (this, position);
1213 copy_properties (prev, this);
1214 if (MERGE_INSERTIONS (prev))
1215 merge_properties (over, this);
1216 over = next_interval (over);
1217 }
1218 }
1219 else
1220 {
1221 if (FRONT_STICKY_P (under))
1222 /* The inserted text "sticks" to the interval `under',
1223 which means it gets those properties. */
1224 while (! NULL_INTERVAL_P (over))
1225 {
1226 position = LENGTH (over) + 1;
1227 this = split_interval_left (this, position);
1228 copy_properties (under, this);
1229 if (MERGE_INSERTIONS (under))
1230 merge_properties (over, this);
1231 over = next_interval (over);
1232 }
1233 else
1234 /* The intervals go inbetween */
1235 while (! NULL_INTERVAL_P (over))
1236 {
1237 position = LENGTH (over) + 1;
1238 this = split_interval_left (this, position);
1239 copy_properties (over, this);
1240 over = next_interval (over);
1241 }
1242 }
1243 }
1244
1245 buffer->intervals = balance_intervals (buffer->intervals);
1246 return;
1247 }
1248
1249 /* Here for insertion in the middle of an interval. */
1250
1251 if (TOTAL_LENGTH (source) < LENGTH (this))
1252 { 1169 {
1253 INTERVAL end_unchanged 1170 INTERVAL end_unchanged
1254 = split_interval_right (this, TOTAL_LENGTH (source) + 1); 1171 = split_interval_left (this, position - under->position + 1);
1255 copy_properties (under, end_unchanged); 1172 copy_properties (under, end_unchanged);
1256 } 1173 under->position = position;
1257 1174 prev = 0;
1258 position = position - tree->position + 1; 1175 middle = 1;
1176 }
1177 else
1178 {
1179 prev = previous_interval (under);
1180 if (prev && !END_STICKY_P (prev))
1181 prev = 0;
1182 }
1183
1184 /* Insertion is now at beginning of UNDER. */
1185
1186 /* The inserted text "sticks" to the interval `under',
1187 which means it gets those properties. */
1259 while (! NULL_INTERVAL_P (over)) 1188 while (! NULL_INTERVAL_P (over))
1260 { 1189 {
1261 this = split_interval_right (under, position); 1190 position = LENGTH (over) + 1;
1191 if (position < LENGTH (under))
1192 this = split_interval_left (under, position);
1193 else
1194 this = under;
1262 copy_properties (over, this); 1195 copy_properties (over, this);
1263 if (MERGE_INSERTIONS (under)) 1196 /* Insertion at the end of an interval, PREV,
1197 inherits from PREV if PREV is sticky at the end. */
1198 if (prev && ! FRONT_STICKY_P (under)
1199 && MERGE_INSERTIONS (prev))
1200 merge_properties (prev, this);
1201 /* Maybe it inherits from the following interval
1202 if that is sticky at the front. */
1203 else if ((FRONT_STICKY_P (under) || middle)
1204 && MERGE_INSERTIONS (under))
1264 merge_properties (under, this); 1205 merge_properties (under, this);
1265
1266 position = LENGTH (over) + 1;
1267 over = next_interval (over); 1206 over = next_interval (over);
1268 } 1207 }
1269 1208
1270 buffer->intervals = balance_intervals (buffer->intervals); 1209 buffer->intervals = balance_intervals (buffer->intervals);
1271 return; 1210 return;
1272 } 1211 }
1273 1212
1213 textget (plist, prop)
1214 Lisp_Object plist;
1215 register Lisp_Object prop;
1216 {
1217 register Lisp_Object tail;
1218
1219 for (tail = plist; !NILP (tail); tail = Fcdr (Fcdr (tail)))
1220 {
1221 register Lisp_Object tem;
1222 tem = Fcar (tail);
1223 if (EQ (prop, tem))
1224 return Fcar (Fcdr (tail));
1225 }
1226 return Qnil;
1227 }
1228
1274 /* Set point in BUFFER to POSITION. If the target position is in 1229 /* Set point in BUFFER to POSITION. If the target position is in
1275 an invisible interval which is not displayed with a special glyph, 1230 after an invisible character which is not displayed with a special glyph,
1276 skip intervals until we find one. Point may be at the first 1231 move back to an ok place to display. */
1277 position of an invisible interval, if it is displayed with a
1278 special glyph. */
1279 1232
1280 void 1233 void
1281 set_point (position, buffer) 1234 set_point (position, buffer)
1282 register int position; 1235 register int position;
1283 register struct buffer *buffer; 1236 register struct buffer *buffer;
1284 { 1237 {
1285 register INTERVAL to, from, target; 1238 register INTERVAL to, from, toprev, fromprev, target;
1286 register int iposition = position; 1239 register int iposition = position;
1287 int buffer_point; 1240 int buffer_point;
1288 register Lisp_Object obj; 1241 register Lisp_Object obj;
1289 int backwards = (position < BUF_PT (buffer)) ? 1 : 0; 1242 int backwards = (position < BUF_PT (buffer)) ? 1 : 0;
1290 int old_position = buffer->text.pt; 1243 int old_position = buffer->text.pt;
1304 1257
1305 /* Position Z is really one past the last char in the buffer. */ 1258 /* Position Z is really one past the last char in the buffer. */
1306 if (position == BUF_Z (buffer)) 1259 if (position == BUF_Z (buffer))
1307 iposition = position - 1; 1260 iposition = position - 1;
1308 1261
1262 /* Set TO to the interval containing the char after POSITION,
1263 and TOPREV to the interval containing the char before POSITION.
1264 Either one may be null. They may be equal. */
1309 to = find_interval (buffer->intervals, iposition); 1265 to = find_interval (buffer->intervals, iposition);
1310 buffer_point =(BUF_PT (buffer) == BUF_Z (buffer) 1266 if (to->position == position)
1311 ? BUF_Z (buffer) - 1 1267 toprev = previous_interval (to);
1312 : BUF_PT (buffer)); 1268 else if (iposition != position)
1313 1269 toprev = to, to = 0;
1270 else
1271 toprev = to;
1272
1273 buffer_point = (BUF_PT (buffer) == BUF_Z (buffer)
1274 ? BUF_Z (buffer) - 1
1275 : BUF_PT (buffer));
1276
1277 /* Set FROM to the interval containing the char after PT,
1278 and FROMPREV to the interval containing the char before PT.
1279 Either one may be null. They may be equal. */
1314 /* We could cache this and save time. */ 1280 /* We could cache this and save time. */
1315 from = find_interval (buffer->intervals, buffer_point); 1281 from = find_interval (buffer->intervals, buffer_point);
1316 1282 if (from->position == BUF_PT (buffer))
1317 if (NULL_INTERVAL_P (to) || NULL_INTERVAL_P (from)) 1283 fromprev = previous_interval (from);
1318 abort (); /* Paranoia */ 1284 else if (buffer_point != BUF_PT (buffer))
1285 fromprev = from, from = 0;
1286 else
1287 fromprev = from;
1319 1288
1320 /* Moving within an interval */ 1289 /* Moving within an interval */
1321 if (to == from && INTERVAL_VISIBLE_P (to)) 1290 if (to == from && toprev == fromprev && INTERVAL_VISIBLE_P (to))
1322 { 1291 {
1323 buffer->text.pt = position; 1292 buffer->text.pt = position;
1324 return; 1293 return;
1325 } 1294 }
1326 1295
1327 /* Here for the case of moving into another interval. */ 1296 /* If the new position is after an invisible character,
1328 1297 move back over all such. */
1329 target = to; 1298 while (! NULL_INTERVAL_P (toprev)
1330 while (! INTERVAL_VISIBLE_P (to) && ! DISPLAY_INVISIBLE_GLYPH (to) 1299 && ! INTERVAL_VISIBLE_P (toprev)
1331 && ! NULL_INTERVAL_P (to)) 1300 && ! DISPLAY_INVISIBLE_GLYPH (toprev))
1332 to = (backwards ? previous_interval (to) : next_interval (to)); 1301 {
1333 if (NULL_INTERVAL_P (to)) 1302 to = toprev;
1334 return; 1303 toprev = previous_interval (toprev);
1335 1304 position = to->position;
1336 /* Here we know we are actually moving to another interval. */ 1305 }
1337 if (INTERVAL_VISIBLE_P (to)) 1306
1338 { 1307 buffer->text.pt = position;
1339 /* If we skipped some intervals, go to the closest point
1340 in the interval we've stopped at. */
1341 if (to != target)
1342 buffer->text.pt = (backwards
1343 ? to->position + LENGTH (to) - 1
1344 : to->position);
1345 else
1346 buffer->text.pt = position;
1347 }
1348 else
1349 buffer->text.pt = to->position;
1350 1308
1351 /* We run point-left and point-entered hooks here, iff the 1309 /* We run point-left and point-entered hooks here, iff the
1352 two intervals are not equivalent. These hooks take 1310 two intervals are not equivalent. These hooks take
1353 (old_point, new_point) as arguments. */ 1311 (old_point, new_point) as arguments. */
1354 if (! intervals_equal (from, to)) 1312 if (! intervals_equal (from, to)
1355 { 1313 || ! intervals_equal (fromprev, toprev))
1356 Lisp_Object val; 1314 {
1357 1315 Lisp_Object leave_after, leave_before, enter_after, enter_before;
1358 val = Fget (Qpoint_left, from->plist); 1316
1359 if (! NILP (val)) 1317 if (fromprev)
1360 call2 (val, old_position, position); 1318 leave_after = textget (fromprev->plist, Qpoint_left);
1361 1319 else
1362 val = Fget (Qpoint_entered, to->plist); 1320 leave_after = Qnil;
1363 if (! NILP (val)) 1321 if (from)
1364 call2 (val, old_position, position); 1322 leave_before = textget (from->plist, Qpoint_left);
1323 else
1324 leave_before = Qnil;
1325
1326 if (toprev)
1327 enter_after = textget (toprev->plist, Qpoint_entered);
1328 else
1329 enter_after = Qnil;
1330 if (to)
1331 enter_before = textget (to->plist, Qpoint_entered);
1332 else
1333 enter_before = Qnil;
1334
1335 if (! EQ (leave_before, enter_before) && !NILP (leave_before))
1336 call2 (leave_before, old_position, position);
1337 if (! EQ (leave_after, enter_after) && !NILP (leave_after))
1338 call2 (leave_after, old_position, position);
1339
1340 if (! EQ (enter_before, leave_before) && !NILP (enter_before))
1341 call2 (enter_before, old_position, position);
1342 if (! EQ (enter_after, leave_after) && !NILP (enter_after))
1343 call2 (enter_after, old_position, position);
1365 } 1344 }
1366 } 1345 }
1367 1346
1368 /* Set point temporarily, without checking any text properties. */ 1347 /* Set point temporarily, without checking any text properties. */
1369 1348