private bool MergeSingle(SingleSelectionWithLocation inserted, LocationWithIndex existing) { if (existing.Selection.Start >= inserted.Start && existing.Selection.End <= inserted.End) { // The new selection is the same or a superset of the existing selection // so we can take that. this.items[existing.Index] = inserted.Selection; // The overall selection was only changed if the existing and new selections // exactly match. return(existing.Selection.Start != inserted.Start || existing.Selection.End != inserted.End); } if (existing.Selection.Start <= inserted.Start && existing.Selection.End >= inserted.End) { // The new location is completely covered by the existing one, we should ensure // that the caret is at the end specified by the insertion but this does not // change the overall selected text. this.items[existing.Index] = existing.Selection.Selection .WithDirection(inserted.Selection); return(false); } // Neither of the selections completely covers the other so create a new selection // the encompases both. This results in a change to the overall selection. this.items[existing.Index] = inserted.ExpandTo( existing.Selection.Start, existing.Selection.End); return(true); }
private bool Merge( SingleSelectionWithLocation inserted, LocationWithIndex firstExisting, LocationWithIndex lastExisting) { return(firstExisting.Index == lastExisting.Index ? this.MergeSingle(inserted, firstExisting) : this.MergeRange(inserted, firstExisting, lastExisting)); }
private LocationWithIndex FindStart( LocationWithIndex minLocation, LocationWithIndex maxLocation, SingleSelectionWithLocation inserted) { if (!inserted.IsAfter(minLocation.Selection)) { return(minLocation); } var minIndex = minLocation.Index; var gap = maxLocation.Index - minIndex; while (true) { /* At this point we know that the inserted * location is after the min location and * that is is not after the max location */ // If the gap is 1 or 0 then the start we want // must be the max location if (gap < 2) { return(maxLocation); } // If the gap is more than 1 then we need to // reduce it, maintaining the fact that inserted // is after the min location and not after the // end location (i.e. before or overlapping the end) var centerIndex = minIndex + (gap / 2); var centerLocation = this.items[centerIndex].GetDetails(); if (inserted.IsAfter(centerLocation)) { minIndex = centerIndex; } else { maxLocation = new LocationWithIndex(centerIndex, centerLocation); }; gap = maxLocation.Index - minIndex; } }
private bool MergeRange( SingleSelectionWithLocation inserted, LocationWithIndex firstExisting, LocationWithIndex lastExisting) { if (inserted.Start <= firstExisting.Selection.Start && inserted.End >= lastExisting.Selection.End) { // If the new selection completely overlaps the existing ones // we can simply use the new selection this.items[firstExisting.Index] = inserted.Selection; } else { // If the new selection does not completely overlap the old ones // then we need to create a new selection that is the union of // the old and new ones this.items[firstExisting.Index] = inserted.ExpandTo( firstExisting.Selection.Start, lastExisting.Selection.End); } // We need to move any selections after the last existing one // up in the list to fill the hole left by replacing multiple // selections with a single one and adjust the selection count // in the list var emptyGap = lastExisting.Index - firstExisting.Index; for (int i = lastExisting.Index + 1; i < this.Count; ++i) { this.items[i - emptyGap] = this.items[i]; } this.Count -= emptyGap; // The overall selections will always have changed with a range merge return(true); }
public LocationWithIndex(int index, SingleSelectionWithLocation selection) { this.Index = index; this.Selection = selection; }
public bool IsAfter(SingleSelectionWithLocation other) => this.IsZeroLength || other.IsZeroLength ? this.Start > other.End : this.Start >= other.End;
public bool IsBefore(SingleSelectionWithLocation other) => this.IsZeroLength || other.IsZeroLength ? this.End < other.Start : this.End <= other.Start;