Mercurial > emacs
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 |