Mercurial > emacs
changeset 1157:a4a446feb297
Initial revision
author | Joseph Arceneaux <jla@gnu.org> |
---|---|
date | Thu, 17 Sep 1992 02:26:53 +0000 |
parents | 2a92ddfaf6ba |
children | b48094d3cc42 |
files | src/intervals.c |
diffstat | 1 files changed, 1529 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/intervals.c Thu Sep 17 02:26:53 1992 +0000 @@ -0,0 +1,1529 @@ +/* Code for doing intervals. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* NOTES: + + Have to ensure that we can't put symbol nil on a plist, or some + functions may work incorrectly. + + An idea: Have the owner of the tree keep count of splits and/or + insertion lengths (in intervals), and balance after every N. + + Need to call *_left_hook when buffer is killed. + + Scan for zero-length, or 0-length to see notes about handling + zero length interval-markers. + + There are comments around about freeing intervals. It might be + faster to explicitly free them (put them on the free list) than + to GC them. + +*/ + + +#include "config.h" +#include "lisp.h" +#include "intervals.h" +#include "buffer.h" +#include "screen.h" + +/* Factor for weight-balancing interval trees. */ +Lisp_Object interval_balance_threshold; + +/* Utility functions for intervals. */ + + +/* Create the root interval of some object, a buffer or string. */ + +INTERVAL +create_root_interval (parent) + Lisp_Object parent; +{ + INTERVAL new = make_interval (); + + if (XTYPE (parent) == Lisp_Buffer) + { + new->total_length = BUF_Z (XBUFFER (parent)) - 1; + XBUFFER (parent)->intervals = new; + } + else if (XTYPE (parent) == Lisp_String) + { + new->total_length = XSTRING (parent)->size; + XSTRING (parent)->intervals = new; + } + + new->parent = (INTERVAL) parent; + new->position = 1; + + return new; +} + +/* Make the interval TARGET have exactly the properties of SOURCE */ + +void +copy_properties (source, target) + register INTERVAL source, target; +{ + if (DEFAULT_INTERVAL_P (source) && DEFAULT_INTERVAL_P (target)) + return; + + COPY_INTERVAL_CACHE (source, target); + target->plist = Fcopy_sequence (source->plist); +} + +/* Merge the properties of interval SOURCE into the properties + of interval TARGET. */ + +static void +merge_properties (source, target) + register INTERVAL source, target; +{ + register Lisp_Object o, sym, val; + + if (DEFAULT_INTERVAL_P (source) && DEFAULT_INTERVAL_P (target)) + return; + + MERGE_INTERVAL_CACHE (source, target); + + o = source->plist; + while (! EQ (o, Qnil)) + { + sym = Fcar (o); + val = Fmemq (sym, target->plist); + + if (NILP (val)) + { + o = Fcdr (o); + val = Fcar (o); + target->plist = Fcons (sym, Fcons (val, target->plist)); + o = Fcdr (o); + } + else + o = Fcdr (Fcdr (o)); + } +} + +/* Return 1 if the two intervals have the same properties, + 0 otherwise. */ + +int +intervals_equal (i0, i1) + INTERVAL i0, i1; +{ + register Lisp_Object i0_cdr, i0_sym, i1_val; + register i1_len; + + if (DEFAULT_INTERVAL_P (i0) && DEFAULT_INTERVAL_P (i1)) + return 1; + + i1_len = XFASTINT (Flength (i1->plist)); + if (i1_len & 0x1) /* Paranoia -- plists are always even */ + abort (); + i1_len /= 2; + i0_cdr = i0->plist; + while (!NILP (i0_cdr)) + { + /* Lengths of the two plists were unequal */ + if (i1_len == 0) + return 0; + + i0_sym = Fcar (i0_cdr); + i1_val = Fmemq (i0_sym, i1->plist); + + /* i0 has something i1 doesn't */ + if (EQ (i1_val, Qnil)) + return 0; + + /* i0 and i1 both have sym, but it has different values in each */ + i0_cdr = Fcdr (i0_cdr); + if (! Fequal (i1_val, Fcar (i0_cdr))) + return 0; + + i0_cdr = Fcdr (i0_cdr); + i1_len--; + } + + /* Lengths of the two plists were unequal */ + if (i1_len > 0) + return 0; + + return 1; +} + +static int icount; +static int idepth; +static int zero_length; + +static int depth; + +/* Traverse an interval tree TREE, performing FUNCTION on each node. + + Perhaps we should pass the depth as an argument. */ + +void +traverse_intervals (tree, position, function) + INTERVAL tree; + int position; + void (* function) (); +{ + if (NULL_INTERVAL_P (tree)) + return; + + depth++; + traverse_intervals (tree->left, position, function); + position += LEFT_TOTAL_LENGTH (tree); + tree->position = position; + (*function) (tree); + position += LENGTH (tree); + traverse_intervals (tree->right, position, function); + depth--; +} + +#if 0 +/* These functions are temporary, for debugging purposes only. */ + +INTERVAL search_interval, found_interval; + +void +check_for_interval (i) + register INTERVAL i; +{ + if (i == search_interval) + { + found_interval = i; + icount++; + } +} + +INTERVAL +search_for_interval (i, tree) + register INTERVAL i, tree; +{ + icount = 0; + search_interval = i; + found_interval = NULL_INTERVAL; + traverse_intervals (tree, 1, &check_for_interval); + return found_interval; +} + +static void +inc_interval_count (i) + INTERVAL i; +{ + icount++; + if (LENGTH (i) == 0) + zero_length++; + if (depth > idepth) + idepth = depth; +} + +int +count_intervals (i) + register INTERVAL i; +{ + icount = 0; + idepth = 0; + zero_length = 0; + traverse_intervals (i, 1, &inc_interval_count); + + return icount; +} + +static INTERVAL +root_interval (interval) + INTERVAL interval; +{ + register INTERVAL i = interval; + + while (! ROOT_INTERVAL_P (i)) + i = i->parent; + + return i; +} +#endif + +/* Assuming that a left child exists, perform the following operation: + + A B + / \ / \ + B => A + / \ / \ + c c +*/ + +static INTERVAL +rotate_right (interval) + INTERVAL interval; +{ + INTERVAL i; + INTERVAL B = interval->left; + int len = LENGTH (interval); + + /* Deal with any Parent of A; make it point to B. */ + if (! ROOT_INTERVAL_P (interval)) + if (AM_LEFT_CHILD (interval)) + interval->parent->left = interval->left; + else + interval->parent->right = interval->left; + interval->left->parent = interval->parent; + + /* B gets the same length as A, since it get A's position in the tree. */ + interval->left->total_length = interval->total_length; + + /* B becomes the parent of A. */ + i = interval->left->right; + interval->left->right = interval; + interval->parent = interval->left; + + /* A gets c as left child. */ + interval->left = i; + if (! NULL_INTERVAL_P (i)) + i->parent = interval; + interval->total_length = (len + LEFT_TOTAL_LENGTH (interval) + + RIGHT_TOTAL_LENGTH (interval)); + + return B; +} + +/* Assuming that a right child exists, perform the following operation: + + A B + / \ / \ + B => A + / \ / \ + c c +*/ + +static INTERVAL +rotate_left (interval) + INTERVAL interval; +{ + INTERVAL i; + INTERVAL B = interval->right; + int len = LENGTH (interval); + + /* Deal with the parent of A. */ + if (! ROOT_INTERVAL_P (interval)) + if (AM_LEFT_CHILD (interval)) + interval->parent->left = interval->right; + else + interval->parent->right = interval->right; + interval->right->parent = interval->parent; + + /* B must have the same total length of A. */ + interval->right->total_length = interval->total_length; + + /* Make B the parent of A */ + i = interval->right->left; + interval->right->left = interval; + interval->parent = interval->right; + + /* Make A point to c */ + interval->right = i; + if (! NULL_INTERVAL_P (i)) + i->parent = interval; + interval->total_length = (len + LEFT_TOTAL_LENGTH (interval) + + RIGHT_TOTAL_LENGTH (interval)); + + return B; +} + +/* Split an interval into two. The second (RIGHT) half is returned as + the new interval. The size and position of the interval being split are + stored within it, having been found by find_interval (). The properties + are reset; it is up to the caller to do the right thing. + + Note that this does not change the position of INTERVAL; if it is a root, + it is still a root after this operation. */ + +INTERVAL +split_interval_right (interval, relative_position) + INTERVAL interval; + int relative_position; +{ + INTERVAL new = make_interval (); + int position = interval->position; + int new_length = LENGTH (interval) - relative_position + 1; + + new->position = position + relative_position - 1; + new->parent = interval; +#if 0 + copy_properties (interval, new); +#endif + + if (LEAF_INTERVAL_P (interval) || NULL_RIGHT_CHILD (interval)) + { + interval->right = new; + new->total_length = new_length; + + return new; + } + + /* Insert the new node between INTERVAL and its right child. */ + new->right = interval->right; + interval->right->parent = new; + interval->right = new; + + new->total_length = new_length + new->right->total_length; + + return new; +} + +/* Split an interval into two. The first (LEFT) half is returned as + the new interval. The size and position of the interval being split + are stored within it, having been found by find_interval (). The + properties are reset; it is up to the caller to do the right thing. + + Note that this does not change the position of INTERVAL in the tree; if it + is a root, it is still a root after this operation. */ + +INTERVAL +split_interval_left (interval, relative_position) + INTERVAL interval; + int relative_position; +{ + INTERVAL new = make_interval (); + int position = interval->position; + int new_length = relative_position - 1; + +#if 0 + copy_properties (interval, new); +#endif + + new->position = interval->position; + + interval->position = interval->position + relative_position - 1; + new->parent = interval; + + if (NULL_LEFT_CHILD (interval)) + { + interval->left = new; + new->total_length = new_length; + + return new; + } + + /* Insert the new node between INTERVAL and its left child. */ + new->left = interval->left; + new->left->parent = new; + interval->left = new; + new->total_length = LENGTH (new) + LEFT_TOTAL_LENGTH (new); + + return new; +} + +/* Find the interval containing POSITION in TREE. POSITION is relative + to the start of TREE. */ + +INTERVAL +find_interval (tree, position) + register INTERVAL tree; + register int position; +{ + register int relative_position = position; + + if (NULL_INTERVAL_P (tree)) + return NULL_INTERVAL; + + if (position > TOTAL_LENGTH (tree)) + abort (); /* Paranoia */ +#if 0 + position = TOTAL_LENGTH (tree); +#endif + + while (1) + { + if (relative_position <= LEFT_TOTAL_LENGTH (tree)) + { + tree = tree->left; + } + else if (relative_position > (TOTAL_LENGTH (tree) + - RIGHT_TOTAL_LENGTH (tree))) + { + relative_position -= (TOTAL_LENGTH (tree) + - RIGHT_TOTAL_LENGTH (tree)); + tree = tree->right; + } + else + { + tree->position = LEFT_TOTAL_LENGTH (tree) + + position - relative_position + 1; + return tree; + } + } +} + +/* Find the succeeding interval (lexicographically) to INTERVAL. + Sets the `position' field based on that of INTERVAL. + + Note that those values are only correct if they were also correct + in INTERVAL. */ + +INTERVAL +next_interval (interval) + register INTERVAL interval; +{ + register INTERVAL i = interval; + register int next_position; + + if (NULL_INTERVAL_P (i)) + return NULL_INTERVAL; + next_position = interval->position + LENGTH (interval); + + if (! NULL_RIGHT_CHILD (i)) + { + i = i->right; + while (! NULL_LEFT_CHILD (i)) + i = i->left; + + i->position = next_position; + return i; + } + + while (! NULL_PARENT (i)) + { + if (AM_LEFT_CHILD (i)) + { + i = i->parent; + i->position = next_position; + return i; + } + + i = i->parent; + } + + return NULL_INTERVAL; +} + +/* Find the preceding interval (lexicographically) to INTERVAL. + Sets the `position' field based on that of INTERVAL. + + Note that those values are only correct if they were also correct + in INTERVAL. */ + +INTERVAL +previous_interval (interval) + register INTERVAL interval; +{ + register INTERVAL i; + register position_of_previous; + + if (NULL_INTERVAL_P (interval)) + return NULL_INTERVAL; + + if (! NULL_LEFT_CHILD (interval)) + { + i = interval->left; + while (! NULL_RIGHT_CHILD (i)) + i = i->right; + + i->position = interval->position - LENGTH (i); + return i; + } + + i = interval; + while (! NULL_PARENT (i)) + { + if (AM_RIGHT_CHILD (i)) + { + i = i->parent; + + i->position = interval->position - LENGTH (i); + return i; + } + i = i->parent; + } + + return NULL_INTERVAL; +} + +/* Traverse a path down the interval tree TREE to the interval + containing POSITION, adjusting all nodes on the path for + an addition of LENGTH characters. Insertion between two intervals + (i.e., point == i->position, where i is second interval) means + text goes into second interval. + + Modifications are needed to handle the hungry bits -- after simply + finding the interval at position (don't add length going down), + if it's the beginning of the interval, get the previous interval + and check the hugry bits of both. Then add the length going back up + to the root. */ + +static INTERVAL +adjust_intervals_for_insertion (tree, position, length) + INTERVAL tree; + int position, length; +{ + register int relative_position; + register INTERVAL this; + + if (TOTAL_LENGTH (tree) == 0) /* Paranoia */ + abort (); + + /* If inserting at point-max of a buffer, that position + will be out of range */ + if (position > TOTAL_LENGTH (tree)) + position = TOTAL_LENGTH (tree); + relative_position = position; + this = tree; + + while (1) + { + if (relative_position <= LEFT_TOTAL_LENGTH (this)) + { + this->total_length += length; + this = this->left; + } + else if (relative_position > (TOTAL_LENGTH (this) + - RIGHT_TOTAL_LENGTH (this))) + { + relative_position -= (TOTAL_LENGTH (this) + - RIGHT_TOTAL_LENGTH (this)); + this->total_length += length; + this = this->right; + } + else + { + /* If we are to use zero-length intervals as buffer pointers, + then this code will have to change. */ + this->total_length += length; + this->position = LEFT_TOTAL_LENGTH (this) + + position - relative_position + 1; + return tree; + } + } +} + +/* Merge interval I with its lexicographic successor. Note that + this does not deal with the properties, or delete I. */ + +INTERVAL +merge_interval_right (i) + register INTERVAL i; +{ + register int absorb = LENGTH (i); + + /* Zero out this interval. */ + i->total_length -= absorb; + + /* Find the succeeding interval. */ + if (! NULL_RIGHT_CHILD (i)) /* It's below us. Add absorb + as we descend. */ + { + i = i->right; + while (! NULL_LEFT_CHILD (i)) + { + i->total_length += absorb; + i = i->left; + } + + i->total_length += absorb; + return i; + } + + while (! NULL_PARENT (i)) /* It's above us. Subtract as + we ascend. */ + { + if (AM_LEFT_CHILD (i)) + { + i = i->parent; + return i; + } + + i = i->parent; + i->total_length -= absorb; + } + + return NULL_INTERVAL; +} + +/* Merge interval I with its lexicographic predecessor. Note that + this does not deal with the properties, or delete I.*/ + +INTERVAL +merge_interval_left (i) + register INTERVAL i; +{ + register int absorb = LENGTH (i); + + /* Zero out this interval. */ + i->total_length -= absorb; + + /* Find the preceding interval. */ + if (! NULL_LEFT_CHILD (i)) /* It's below us. Go down, + adding ABSORB as we go. */ + { + i = i->left; + while (! NULL_RIGHT_CHILD (i)) + { + i->total_length += absorb; + i = i->right; + } + + i->total_length += absorb; + return i; + } + + while (! NULL_PARENT (i)) /* It's above us. Go up, + subtracting ABSORB. */ + { + if (AM_RIGHT_CHILD (i)) + { + i = i->parent; + return i; + } + + i = i->parent; + i->total_length -= absorb; + } + + return NULL_INTERVAL; +} + +/* Delete an interval node from its btree by merging its subtrees + into one subtree which is returned. Caller is responsible for + storing the resulting subtree into its parent. */ + +static INTERVAL +delete_node (i) + register INTERVAL i; +{ + register INTERVAL migrate, this; + register int migrate_amt; + + if (NULL_INTERVAL_P (i->left)) + return i->right; + if (NULL_INTERVAL_P (i->right)) + return i->left; + + migrate = i->left; + migrate_amt = i->left->total_length; + this = i->right; + this->total_length += migrate_amt; + while (! NULL_INTERVAL_P (this->left)) + { + this = this->left; + this->total_length += migrate_amt; + } + this->left = migrate; + migrate->parent = this; + + return i->right; +} + +/* Delete interval I from its tree by calling `delete_node' + and properly connecting the resultant subtree. + + I is presumed to be empty; that is, no adjustments are made + for the length of I. */ + +void +delete_interval (i) + register INTERVAL i; +{ + register INTERVAL parent; + int amt = LENGTH (i); + + if (amt > 0) /* Only used on zero-length intervals now. */ + abort (); + + if (ROOT_INTERVAL_P (i)) + { + Lisp_Object owner = (Lisp_Object) i->parent; + parent = delete_node (i); + if (! NULL_INTERVAL_P (parent)) + parent->parent = (INTERVAL) owner; + + if (XTYPE (owner) == Lisp_Buffer) + XBUFFER (owner)->intervals = parent; + else if (XTYPE (owner) == Lisp_String) + XSTRING (owner)->intervals = parent; + else + abort (); + + return; + } + + parent = i->parent; + if (AM_LEFT_CHILD (i)) + { + parent->left = delete_node (i); + if (! NULL_INTERVAL_P (parent->left)) + parent->left->parent = parent; + } + else + { + parent->right = delete_node (i); + if (! NULL_INTERVAL_P (parent->right)) + parent->right->parent = parent; + } +} + +/* Recurse down to the interval containing FROM. Then delete as much + as possible (up to AMOUNT) from that interval, adjusting parental + intervals on the way up. If an interval is zeroed out, then + it is deleted. + + Returns the amount deleted. */ + +static int +interval_deletion_adjustment (tree, from, amount) + register INTERVAL tree; + register int from, amount; +{ + register int relative_position = from; + + if (NULL_INTERVAL_P (tree)) + return 0; + + /* Left branch */ + if (relative_position <= LEFT_TOTAL_LENGTH (tree)) + { + int subtract = interval_deletion_adjustment (tree->left, + relative_position, + amount); + tree->total_length -= subtract; + return subtract; + } + /* Right branch */ + else if (relative_position > (TOTAL_LENGTH (tree) + - RIGHT_TOTAL_LENGTH (tree))) + { + int subtract; + + relative_position -= (tree->total_length + - RIGHT_TOTAL_LENGTH (tree)); + subtract = interval_deletion_adjustment (tree->right, + relative_position, + amount); + tree->total_length -= subtract; + return subtract; + } + /* Here -- this node */ + else + { + /* If this is a zero-length, marker interval, then + we must skip it. */ + + if (relative_position == LEFT_TOTAL_LENGTH (tree) + 1) + { + /* This means we're deleting from the beginning of this interval. */ + register int my_amount = LENGTH (tree); + + if (amount < my_amount) + { + tree->total_length -= amount; + return amount; + } + else + { + tree->total_length -= my_amount; + if (LENGTH (tree) != 0) + abort (); /* Paranoia */ + + delete_interval (tree); + return my_amount; + } + } + else /* Deleting starting in the middle. */ + { + register int my_amount = ((tree->total_length + - RIGHT_TOTAL_LENGTH (tree)) + - relative_position + 1); + + if (amount <= my_amount) + { + tree->total_length -= amount; + return amount; + } + else + { + tree->total_length -= my_amount; + return my_amount; + } + } + } + + abort (); +} + +static void +adjust_intervals_for_deletion (buffer, start, length) + struct buffer *buffer; + int start, length; +{ + register int left_to_delete = length; + register INTERVAL tree = buffer->intervals; + register int deleted; + + if (NULL_INTERVAL_P (tree)) + return; + + if (length == TOTAL_LENGTH (tree)) + { + buffer->intervals = NULL_INTERVAL; + return; + } + + if (ONLY_INTERVAL_P (tree)) + { + tree->total_length -= length; + return; + } + + if (start > TOTAL_LENGTH (tree)) + start = TOTAL_LENGTH (tree); + while (left_to_delete > 0) + { + left_to_delete -= interval_deletion_adjustment (tree, start, + left_to_delete); + tree = buffer->intervals; + if (left_to_delete == tree->total_length) + { + buffer->intervals = NULL_INTERVAL; + return; + } + } +} + +/* Note that all intervals in OBJECT after START have slid by LENGTH. */ + +INLINE void +offset_intervals (buffer, start, length) + struct buffer *buffer; + int start, length; +{ + if (NULL_INTERVAL_P (buffer->intervals) || length == 0) + return; + + if (length > 0) + adjust_intervals_for_insertion (buffer->intervals, start, length); + else + adjust_intervals_for_deletion (buffer, start, -length); +} + +static INTERVAL +reproduce_tree (source, parent) + INTERVAL source, parent; +{ + register INTERVAL t = make_interval (); + + bcopy (source, t, INTERVAL_SIZE); + copy_properties (source, t); + t->parent = parent; + if (! NULL_LEFT_CHILD (source)) + t->left = reproduce_tree (source->left, t); + if (! NULL_RIGHT_CHILD (source)) + t->right = reproduce_tree (source->right, t); + + return t; +} + +static INTERVAL +make_new_interval (intervals, start, length) + INTERVAL intervals; + int start, length; +{ + INTERVAL slot; + + slot = find_interval (intervals, start); + if (start + length > slot->position + LENGTH (slot)) + error ("Interval would overlap"); + + if (start == slot->position && length == LENGTH (slot)) + return slot; + + if (slot->position == start) + { + /* New right node. */ + split_interval_right (slot, length + 1); + return slot; + } + + if (slot->position + LENGTH (slot) == start + length) + { + /* New left node. */ + split_interval_left (slot, LENGTH (slot) - length + 1); + return slot; + } + + /* Convert interval SLOT into three intervals. */ + split_interval_left (slot, start - slot->position + 1); + split_interval_right (slot, length + 1); + return slot; +} + +void +map_intervals (source, destination, position) + INTERVAL source, destination; + int position; +{ + register INTERVAL i, t; + + if (NULL_INTERVAL_P (source)) + return; + i = find_interval (destination, position); + if (NULL_INTERVAL_P (i)) + return; + + t = find_interval (source, 1); + while (! NULL_INTERVAL_P (t)) + { + i = make_new_interval (destination, position, LENGTH (t)); + position += LENGTH (t); + copy_properties (t, i); + t = next_interval (t); + } +} + +/* Insert the intervals of NEW_TREE into BUFFER at POSITION. + + This is used in insdel.c when inserting Lisp_Strings into + the buffer. The text corresponding to NEW_TREE is already in + the buffer when this is called. The intervals of new tree are + those belonging to the string being inserted; a copy is not made. + + If the inserted text had no intervals associated, this function + simply returns -- offset_intervals should handle placing the + text in the correct interval, depending on the hungry bits. + + If the inserted text had properties (intervals), then there are two + cases -- either insertion happened in the middle of some interval, + or between two intervals. + + If the text goes into the middle of an interval, then new + intervals are created in the middle with only the properties of + the new text, *unless* the macro MERGE_INSERTIONS is true, in + which case the new text has the union of its properties and those + of the text into which it was inserted. + + If the text goes between two intervals, then if neither interval + had its appropriate hungry property set (front_hungry, rear_hungry), + the new text has only its properties. If one of the hungry properties + is set, then the new text "sticks" to that region and its properties + depend on merging as above. If both the preceding and succeding + intervals to the new text are "hungry", then the new text retains + only its properties, as if neither hungry property were set. Perhaps + we should consider merging all three sets of properties onto the new + text... */ + +void +graft_intervals_into_buffer (new_tree, position, b) + INTERVAL new_tree; + int position; + struct buffer *b; +{ + register INTERVAL under, over, this; + register INTERVAL tree = b->intervals; + + /* If the new text has no properties, it becomes part of whatever + interval it was inserted into. */ + if (NULL_INTERVAL_P (new_tree)) + return; + + /* Paranoia -- the text has already been added, so this buffer + should be of non-zero length. */ + if (TOTAL_LENGTH (tree) == 0) + abort (); + + if (NULL_INTERVAL_P (tree)) + { + /* The inserted text constitutes the whole buffer, so + simply copy over the interval structure. */ + if (BUF_Z (b) == TOTAL_LENGTH (new_tree)) + { + b->intervals = reproduce_tree (new_tree, tree->parent); + /* Explicitly free the old tree here. */ + + return; + } + + /* Create an interval tree in which to place a copy + of the intervals of the inserted string. */ + { + Lisp_Object buffer; + XSET (buffer, Lisp_Buffer, b); + create_root_interval (buffer); + } + } + else + if (TOTAL_LENGTH (tree) == TOTAL_LENGTH (new_tree)) + + /* If the buffer contains only the new string, but + there was already some interval tree there, then it may be + some zero length intervals. Eventually, do something clever + about inserting properly. For now, just waste the old intervals. */ + { + b->intervals = reproduce_tree (new_tree, tree->parent); + /* Explicitly free the old tree here. */ + + return; + } + + this = under = find_interval (tree, position); + if (NULL_INTERVAL_P (under)) /* Paranoia */ + abort (); + over = find_interval (new_tree, 1); + + /* Insertion between intervals */ + if (position == under->position) + { + /* First interval -- none precede it. */ + if (position == 1) + { + if (! under->front_hungry) + /* The inserted string keeps its own properties. */ + while (! NULL_INTERVAL_P (over)) + { + position = LENGTH (over) + 1; + this = split_interval_left (this, position); + copy_properties (over, this); + over = next_interval (over); + } + else + /* This string sticks to under */ + while (! NULL_INTERVAL_P (over)) + { + position = LENGTH (over) + 1; + this = split_interval_left (this, position); + copy_properties (under, this); + if (MERGE_INSERTIONS (under)) + merge_properties (over, this); + over = next_interval (over); + } + } + else + { + INTERVAL prev = previous_interval (under); + if (NULL_INTERVAL_P (prev)) + abort (); + + if (prev->rear_hungry) + { + if (under->front_hungry) + /* The intervals go inbetween as the two hungry + properties cancel each other. Should we change + this policy? */ + while (! NULL_INTERVAL_P (over)) + { + position = LENGTH (over) + 1; + this = split_interval_left (this, position); + copy_properties (over, this); + over = next_interval (over); + } + else + /* The intervals stick to prev */ + while (! NULL_INTERVAL_P (over)) + { + position = LENGTH (over) + 1; + this = split_interval_left (this, position); + copy_properties (prev, this); + if (MERGE_INSERTIONS (prev)) + merge_properties (over, this); + over = next_interval (over); + } + } + else + { + if (under->front_hungry) + /* The intervals stick to under */ + while (! NULL_INTERVAL_P (over)) + { + position = LENGTH (over) + 1; + this = split_interval_left (this, position); + copy_properties (under, this); + if (MERGE_INSERTIONS (under)) + merge_properties (over, this); + over = next_interval (over); + } + else + /* The intervals go inbetween */ + while (! NULL_INTERVAL_P (over)) + { + position = LENGTH (over) + 1; + this = split_interval_left (this, position); + copy_properties (over, this); + over = next_interval (over); + } + } + } + + b->intervals = balance_intervals (b->intervals); + return; + } + + /* Here for insertion in the middle of an interval. */ + + if (TOTAL_LENGTH (new_tree) < LENGTH (this)) + { + INTERVAL end_unchanged + = split_interval_right (this, TOTAL_LENGTH (new_tree) + 1); + copy_properties (under, end_unchanged); + } + + position = position - tree->position + 1; + while (! NULL_INTERVAL_P (over)) + { + this = split_interval_right (under, position); + copy_properties (over, this); + if (MERGE_INSERTIONS (under)) + merge_properties (under, this); + + position = LENGTH (over) + 1; + over = next_interval (over); + } + + b->intervals = balance_intervals (b->intervals); + return; +} + +/* Intervals can have properties which are hooks to call. Look for + the property HOOK on interval I, and if found, call its value as + a function.*/ + +void +run_hooks (i, hook) + INTERVAL i; + Lisp_Object hook; +{ + register Lisp_Object tail = i->plist; + register Lisp_Object sym, val; + + while (! NILP (tail)) + { + sym = Fcar (tail); + if (EQ (sym, hook)) + { + Lisp_Object begin, end; + XFASTINT (begin) = i->position; + XFASTINT (end) = i->position + LENGTH (i) - 1; + val = Fcar (Fcdr (tail)); + call2 (val, begin, end); + return; + } + + tail = Fcdr (Fcdr (tail)); + } +} + +/* Set point in BUFFER to POSITION. If the target position is in + an invisible interval which is not displayed with a special glyph, + skip intervals until we find one. Point may be at the first + position of an invisible interval, if it is displayed with a + special glyph. + + This is the only place `PT' is an lvalue in all of emacs. */ + +void +set_point (position, buffer) + register int position; + register struct buffer *buffer; +{ + register INTERVAL to, from, target; + register int iposition = position; + int buffer_point; + register Lisp_Object obj; + int backwards = (position < BUF_PT (buffer)) ? 1 : 0; + + if (position == buffer->text.pt) + return; + + if (NULL_INTERVAL_P (buffer->intervals)) + { + buffer->text.pt = position; + return; + } + + /* Perhaps we should just change `position' to the limit. */ + if (position > BUF_Z (buffer) || position < BUF_BEG (buffer)) + abort (); + + /* Position Z is really one past the last char in the buffer. */ + if (position == BUF_Z (buffer)) + iposition = position - 1; + + to = find_interval (buffer->intervals, iposition); + buffer_point =(BUF_PT (buffer) == BUF_Z (buffer) + ? BUF_Z (buffer) - 1 + : BUF_PT (buffer)); + from = find_interval (buffer->intervals, buffer_point); + if (NULL_INTERVAL_P (to) || NULL_INTERVAL_P (from)) + abort (); /* Paranoia */ + + /* Moving within an interval */ + if (to == from && INTERVAL_VISIBLE_P (to)) + { + buffer->text.pt = position; + return; + } + + /* Here for the case of moving into another interval. */ + + target = to; + while (! INTERVAL_VISIBLE_P (to) && ! DISPLAY_INVISIBLE_GLYPH (to) + && ! NULL_INTERVAL_P (to)) + to = (backwards ? previous_interval (to) : next_interval (to)); + if (NULL_INTERVAL_P (to)) + return; + + /* Here we know we are actually moving to another interval. */ + if (INTERVAL_VISIBLE_P (to)) + { + /* If we skipped some intervals, go to the closest point + in the interval we've stopped at. */ + if (to != target) + buffer->text.pt = (backwards + ? to->position + LENGTH (to) - 1 + : to->position); + else + buffer->text.pt = position; + } + else + buffer->text.pt = to->position; + + /* We should run point-left and point-entered hooks here, iff the + two intervals are not equivalent. */ +} + +/* Check for read-only intervals. Call the modification hooks if any. + Check for the range START up to (but not including) TO. + + First all intervals of the region are checked that they are + modifiable, then all the modification hooks are called in + lexicographic order. */ + +void +verify_interval_modification (buf, start, end) + struct buffer *buf; + int start, end; +{ + register INTERVAL intervals = buf->intervals; + register INTERVAL i; + register Lisp_Object hooks = Qnil; + + if (NULL_INTERVAL_P (intervals)) + return; + + if (start > end) + { + int temp = start; + start = end; + end = temp; + } + + if (start == BUF_Z (buf)) + { + if (BUF_Z (buf) == 1) + abort (); + + i = find_interval (intervals, start - 1); + if (! END_HUNGRY_P (i)) + return; + } + else + i = find_interval (intervals, start); + + do + { + register Lisp_Object mod_hook; + if (! INTERVAL_WRITABLE_P (i)) + error ("Attempt to write in a protected interval"); + mod_hook = Fget (Qmodification, i->plist); + if (! EQ (mod_hook, Qnil)) + hooks = Fcons (mod_hook, hooks); + i = next_interval (i); + } + while (! NULL_INTERVAL_P (i) && i->position <= end); + + hooks = Fnreverse (hooks); + while (! EQ (hooks, Qnil)) + call2 (Fcar (hooks), i->position, i->position + LENGTH (i) - 1); +} + +/* Balance an interval node if the amount of text in its left and right + subtrees differs by more than the percentage specified by + `interval-balance-threshold'. */ + +static INTERVAL +balance_an_interval (i) + INTERVAL i; +{ + register int total_children_size = (LEFT_TOTAL_LENGTH (i) + + RIGHT_TOTAL_LENGTH (i)); + register int threshold = (XFASTINT (interval_balance_threshold) + * (total_children_size / 100)); + + if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i) + && (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold) + return rotate_right (i); + + if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i) + && (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold) + return rotate_right (i); + +#if 0 + if (LEFT_TOTAL_LENGTH (i) > + (RIGHT_TOTAL_LENGTH (i) + XINT (interval_balance_threshold))) + return rotate_right (i); + + if (RIGHT_TOTAL_LENGTH (i) > + (LEFT_TOTAL_LENGTH (i) + XINT (interval_balance_threshold))) + return rotate_left (i); +#endif + + return i; +} + +/* Balance the interval tree TREE. Balancing is by weight + (the amount of text). */ + +INTERVAL +balance_intervals (tree) + register INTERVAL tree; +{ + register INTERVAL new_tree; + + if (NULL_INTERVAL_P (tree)) + return NULL_INTERVAL; + + new_tree = tree; + do + { + tree = new_tree; + new_tree = balance_an_interval (new_tree); + } + while (new_tree != tree); + + return new_tree; +} + +/* Produce an interval tree reflecting the interval structure in + TREE from START to START + LENGTH. */ + +static INTERVAL +copy_intervals (tree, start, length) + INTERVAL tree; + int start, length; +{ + register INTERVAL i, new, t; + register int got; + + if (NULL_INTERVAL_P (tree) || length <= 0) + return NULL_INTERVAL; + + i = find_interval (tree, start); + if (NULL_INTERVAL_P (i) || LENGTH (i) == 0) + abort (); + + /* If there is only one interval and it's the default, return nil. */ + if ((start - i->position + 1 + length) < LENGTH (i) + && DEFAULT_INTERVAL_P (i)) + return NULL_INTERVAL; + + new = make_interval (); + new->position = 1; + got = (LENGTH (i) - (start - i->position)); + new->total_length = got; + copy_properties (i, new); + + t = new; + while (got < length) + { + i = next_interval (i); + t->right = make_interval (); + t->right->parent = t; + t->right->position = t->position + got - 1; + + t = t->right; + t->total_length = length - got; + copy_properties (i, t); + got += LENGTH (i); + } + + if (got > length) + t->total_length -= (got - length); + + return balance_intervals (new); +} + +/* Give buffer SINK the properties of buffer SOURCE from POSITION + to END. The properties are attached to SINK starting at position AT. + + No range checking is done. */ + +void +insert_interval_copy (source, position, end, sink, at) + struct buffer *source, *sink; + register int position, end, at; +{ + INTERVAL interval_copy = copy_intervals (source->intervals, + position, end - position); + graft_intervals_into_buffer (interval_copy, at, sink); +} + +/* Give STRING the properties of BUFFER from POSITION to LENGTH. */ + +void +copy_intervals_to_string (string, buffer, position, length) + Lisp_Object string, buffer; + int position, length; +{ + INTERVAL interval_copy = copy_intervals (XBUFFER (buffer)->intervals, + position, length); + if (NULL_INTERVAL_P (interval_copy)) + return; + + interval_copy->parent = (INTERVAL) string; + XSTRING (string)->intervals = interval_copy; +} + +INTERVAL +make_string_interval (string, start, length) + struct Lisp_String *string; + int start, length; +{ + if (start < 1 || start > string->size) + error ("Interval index out of range"); + if (length < 1 || length > string->size - start + 1) + error ("Interval won't fit"); + + if (length == 0) + return NULL_INTERVAL; + + return make_new_interval (string->intervals, start, length); +} + +/* Create an interval of length LENGTH in buffer BUF at position START. */ + +INTERVAL +make_buffer_interval (buf, start, length) + struct buffer *buf; + int start, length; +{ + if (start < BUF_BEG (buf) || start > BUF_Z (buf)) + error ("Interval index out of range"); + if (length < 1 || length > BUF_Z (buf) - start) + error ("Interval won't fit"); + + if (length == 0) + return NULL_INTERVAL; + + return make_new_interval (buf->intervals, start, length); +}