diff options
Diffstat (limited to 'src/Partitioning.h')
| -rw-r--r-- | src/Partitioning.h | 184 | 
1 files changed, 184 insertions, 0 deletions
diff --git a/src/Partitioning.h b/src/Partitioning.h new file mode 100644 index 000000000..e30189d75 --- /dev/null +++ b/src/Partitioning.h @@ -0,0 +1,184 @@ +// Scintilla source code edit control +/** @file Partitioning.h + ** Data structure used to partition an interval. Used for holding line start/end positions. + **/ +// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef PARTITIONING_H +#define PARTITIONING_H + +/// A split vector of integers with a method for adding a value to all elements  +/// in a range. +/// Used by the Partitioning class. + +class SplitVectorWithRangeAdd : public SplitVector<int> { +public: +	SplitVectorWithRangeAdd(int growSize_) { +		SetGrowSize(growSize_); +		ReAllocate(growSize_); +	} +	~SplitVectorWithRangeAdd() { +	} +	void RangeAddDelta(int start, int end, int delta) { +		// end is 1 past end, so end-start is number of elements to change +		int i = 0; +		int rangeLength = end - start; +		int range1Length = rangeLength; +		int part1Left = part1Length - start; +		if (range1Length > part1Left) +			range1Length = part1Left; +		while (i < range1Length) { +			body[start++] += delta; +			i++; +		} +		start += gapLength; +		while (i < rangeLength) { +			body[start++] += delta; +			i++; +		} +	} +}; + +/// Divide an interval into multiple partitions. +/// Useful for breaking a document down into sections such as lines. + +class Partitioning { +private: +	// To avoid calculating all the partition positions whenever any text is inserted +	// there may be a step somewhere in the list. +	int stepPartition; +	int stepLength; +	SplitVectorWithRangeAdd *body; + +	// Move step forward +	void ApplyStep(int partitionUpTo) { +		if (stepLength != 0) { +			body->RangeAddDelta(stepPartition+1, partitionUpTo + 1, stepLength); +		} +		stepPartition = partitionUpTo; +		if (stepPartition >= body->Length()-1) { +			stepPartition = body->Length()-1; +			stepLength = 0; +		} +	} + +	// Move step backward +	void BackStep(int partitionDownTo) { +		if (stepLength != 0) { +			body->RangeAddDelta(partitionDownTo+1, stepPartition+1, -stepLength); +		} +		stepPartition = partitionDownTo; +	} + +	void Allocate(int growSize) { +		body = new SplitVectorWithRangeAdd(growSize); +		stepPartition = 0; +		stepLength = 0; +		body->Insert(0, 0);	// This value stays 0 for ever +		body->Insert(1, 0);	// This is the end of the first partition and will be the start of the second +	} + +public: +	Partitioning(int growSize) { +		Allocate(growSize); +	} + +	~Partitioning() { +		delete body; +		body = NULL; +	} + +	int Partitions() { +		return body->Length()-1; +	} + +	void InsertPartition(int partition, int pos) { +		if (stepPartition < partition) { +			ApplyStep(partition); +		} +		body->Insert(partition, pos); +		stepPartition++; +	} + +	void SetPartitionStartPosition(int partition, int pos) { +		ApplyStep(partition+1); +		if ((partition < 0) || (partition > body->Length())) { +			return; +		} +		body->SetValueAt(partition, pos); +	} + +	void InsertText(int partitionInsert, int delta) { +		// Point all the partitions after the insertion point further along in the buffer +		if (stepLength != 0) { +			if (partitionInsert >= stepPartition) { +				// Fill in up to the new insertion point +				ApplyStep(partitionInsert); +				stepLength += delta; +			} else if (partitionInsert >= (stepPartition - body->Length() / 10)) { +				// Close to step but before so move step back +				BackStep(partitionInsert); +				stepLength += delta; +			} else { +				ApplyStep(body->Length()-1); +				stepPartition = partitionInsert; +				stepLength = delta; +			} +		} else { +			stepPartition = partitionInsert; +			stepLength = delta; +		} +	} + +	void RemovePartition(int partition) { +		if (partition > stepPartition) { +			ApplyStep(partition); +			stepPartition--; +		} else { +			stepPartition--; +		} +		body->Delete(partition); +	} + +	int PositionFromPartition(int partition) { +		PLATFORM_ASSERT(partition >= 0); +		PLATFORM_ASSERT(partition < body->Length()); +		if ((partition < 0) || (partition >= body->Length())) { +			return 0; +		} +		int pos = body->ValueAt(partition); +		if (partition > stepPartition) +			pos += stepLength; +		return pos; +	} + +	int PartitionFromPosition(int pos) { +		if (body->Length() <= 1) +			return 0; +		if (pos >= (PositionFromPartition(body->Length()-1))) +			return body->Length() - 1 - 1; +		int lower = 0; +		int upper = body->Length()-1; +		do { +			int middle = (upper + lower + 1) / 2; 	// Round high +			int posMiddle = body->ValueAt(middle); +			if (middle > stepPartition) +				posMiddle += stepLength; +			if (pos < posMiddle) { +				upper = middle - 1; +			} else { +				lower = middle; +			} +		} while (lower < upper); +		return lower; +	} + +	void DeleteAll() { +		int growSize = body->GetGrowSize(); +		delete body; +		Allocate(growSize); +	} +}; + +#endif  | 
