Move functions from 'types.ts' to 'utilities.ts'.

This commit is contained in:
Cyrus Najmabadi 2014-12-12 00:56:14 -08:00
parent f218ce7684
commit 62dd12cb7a
2 changed files with 235 additions and 235 deletions

View File

@ -1637,245 +1637,10 @@ module ts {
intersection(span: TextSpan): TextSpan;
}
var textSpanConstructor = (function () {
function textSpanConstructor(start: number, length: number) {
if (start < 0) {
throw new Error("start < 0");
}
if (length < 0) {
throw new Error("start < 0");
}
this._start = start;
this._length = length;
}
textSpanConstructor.prototype = {
toJSON(key: string) {
return { start: this._start, length: this._length }
},
start() {
return this._start
},
length() {
return this._length
},
end() {
return this._start + this._length
},
isEmpty() {
return this._length === 0
},
containsPosition(position: number) {
return position >= this._start && position < this.end()
},
containsTextSpan(span: TextSpan) {
return span.start() >= this._start && span.end() <= this.end()
},
overlapsWith(span: TextSpan) {
var overlapStart = Math.max(this._start, span.start());
var overlapEnd = Math.min(this.end(), span.end());
return overlapStart < overlapEnd;
},
overlap(span: TextSpan) {
var overlapStart = Math.max(this._start, span.start());
var overlapEnd = Math.min(this.end(), span.end());
if (overlapStart < overlapEnd) {
return createTextSpanFromBounds(overlapStart, overlapEnd);
}
return undefined;
},
intersectsWithTextSpan(span: TextSpan) {
return span.start() <= this.end() && span.end() >= this._start
},
intersectsWith(start: number, length: number) {
var end = start + length;
return start <= this.end() && end >= this._start;
},
intersectsWithPosition(position: number) {
return position <= this.end() && position >= this._start;
},
intersection(span: TextSpan) {
var intersectStart = Math.max(this._start, span.start());
var intersectEnd = Math.min(this.end(), span.end());
if (intersectStart <= intersectEnd) {
return createTextSpanFromBounds(intersectStart, intersectEnd);
}
return undefined;
}
};
return textSpanConstructor;
})();
export function createTextSpan(start: number, length: number): TextSpan {
return new (<any>textSpanConstructor)(start, length);
}
export function createTextSpanFromBounds(start: number, end: number) {
return createTextSpan(start, end - start);
}
export interface TextChangeRange {
span(): TextSpan;
newLength(): number;
newSpan(): TextSpan;
isUnchanged(): boolean;
}
var textChangeRangeConstructor = (function () {
function textChangeRangeConstructor(span: TextSpan, newLength: number) {
if (newLength < 0) {
throw new Error("newLength < 0");
}
this._span = span;
this._newLength = newLength;
}
textChangeRangeConstructor.prototype = {
span() {
return this._span;
},
newLength() {
return this._newLength;
},
newSpan() {
return createTextSpan(this.span().start(), this.newLength());
},
isUnchanged() {
return this.span().isEmpty() && this.newLength() === 0;
}
};
return textChangeRangeConstructor;
})();
export function createTextChangeRange(span: TextSpan, newLength: number): TextChangeRange {
return new (<any>textChangeRangeConstructor)(span, newLength);
}
export var unchangedTextChangeRange = createTextChangeRange(createTextSpan(0, 0), 0);
/**
* Called to merge all the changes that occurred across several versions of a script snapshot
* into a single change. i.e. if a user keeps making successive edits to a script we will
* have a text change from V1 to V2, V2 to V3, ..., Vn.
*
* This function will then merge those changes into a single change range valid between V1 and
* Vn.
*/
export function collapseTextChangeRangesAcrossMultipleVersions(changes: TextChangeRange[]): TextChangeRange {
if (changes.length === 0) {
return unchangedTextChangeRange;
}
if (changes.length === 1) {
return changes[0];
}
// We change from talking about { { oldStart, oldLength }, newLength } to { oldStart, oldEnd, newEnd }
// as it makes things much easier to reason about.
var change0 = changes[0];
var oldStartN = change0.span().start();
var oldEndN = change0.span().end();
var newEndN = oldStartN + change0.newLength();
for (var i = 1; i < changes.length; i++) {
var nextChange = changes[i];
// Consider the following case:
// i.e. two edits. The first represents the text change range { { 10, 50 }, 30 }. i.e. The span starting
// at 10, with length 50 is reduced to length 30. The second represents the text change range { { 30, 30 }, 40 }.
// i.e. the span starting at 30 with length 30 is increased to length 40.
//
// 0 10 20 30 40 50 60 70 80 90 100
// -------------------------------------------------------------------------------------------------------
// | /
// | /----
// T1 | /----
// | /----
// | /----
// -------------------------------------------------------------------------------------------------------
// | \
// | \
// T2 | \
// | \
// | \
// -------------------------------------------------------------------------------------------------------
//
// Merging these turns out to not be too difficult. First, determining the new start of the change is trivial
// it's just the min of the old and new starts. i.e.:
//
// 0 10 20 30 40 50 60 70 80 90 100
// ------------------------------------------------------------*------------------------------------------
// | /
// | /----
// T1 | /----
// | /----
// | /----
// ----------------------------------------$-------------------$------------------------------------------
// . | \
// . | \
// T2 . | \
// . | \
// . | \
// ----------------------------------------------------------------------*--------------------------------
//
// (Note the dots represent the newly inferrred start.
// Determining the new and old end is also pretty simple. Basically it boils down to paying attention to the
// absolute positions at the asterixes, and the relative change between the dollar signs. Basically, we see
// which if the two $'s precedes the other, and we move that one forward until they line up. in this case that
// means:
//
// 0 10 20 30 40 50 60 70 80 90 100
// --------------------------------------------------------------------------------*----------------------
// | /
// | /----
// T1 | /----
// | /----
// | /----
// ------------------------------------------------------------$------------------------------------------
// . | \
// . | \
// T2 . | \
// . | \
// . | \
// ----------------------------------------------------------------------*--------------------------------
//
// In other words (in this case), we're recognizing that the second edit happened after where the first edit
// ended with a delta of 20 characters (60 - 40). Thus, if we go back in time to where the first edit started
// that's the same as if we started at char 80 instead of 60.
//
// As it so happens, the same logic applies if the second edit precedes the first edit. In that case rahter
// than pusing the first edit forward to match the second, we'll push the second edit forward to match the
// first.
//
// In this case that means we have { oldStart: 10, oldEnd: 80, newEnd: 70 } or, in TextChangeRange
// semantics: { { start: 10, length: 70 }, newLength: 60 }
//
// The math then works out as follows.
// If we have { oldStart1, oldEnd1, newEnd1 } and { oldStart2, oldEnd2, newEnd2 } then we can compute the
// final result like so:
//
// {
// oldStart3: Min(oldStart1, oldStart2),
// oldEnd3 : Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)),
// newEnd3 : Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2))
// }
var oldStart1 = oldStartN;
var oldEnd1 = oldEndN;
var newEnd1 = newEndN;
var oldStart2 = nextChange.span().start();
var oldEnd2 = nextChange.span().end();
var newEnd2 = oldStart2 + nextChange.newLength();
oldStartN = Math.min(oldStart1, oldStart2);
oldEndN = Math.max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1));
newEndN = Math.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2));
}
return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength: */newEndN - oldStartN);
}
}

View File

@ -708,6 +708,7 @@ module ts {
}
}
}
return undefined;
}
@ -733,4 +734,238 @@ module ts {
return false;
}
var textSpanConstructor = (function () {
function textSpanConstructor(start: number, length: number) {
if (start < 0) {
throw new Error("start < 0");
}
if (length < 0) {
throw new Error("start < 0");
}
this._start = start;
this._length = length;
}
textSpanConstructor.prototype = {
toJSON(key: string) {
return { start: this._start, length: this._length }
},
start() {
return this._start
},
length() {
return this._length
},
end() {
return this._start + this._length
},
isEmpty() {
return this._length === 0
},
containsPosition(position: number) {
return position >= this._start && position < this.end()
},
containsTextSpan(span: TextSpan) {
return span.start() >= this._start && span.end() <= this.end()
},
overlapsWith(span: TextSpan) {
var overlapStart = Math.max(this._start, span.start());
var overlapEnd = Math.min(this.end(), span.end());
return overlapStart < overlapEnd;
},
overlap(span: TextSpan) {
var overlapStart = Math.max(this._start, span.start());
var overlapEnd = Math.min(this.end(), span.end());
if (overlapStart < overlapEnd) {
return createTextSpanFromBounds(overlapStart, overlapEnd);
}
return undefined;
},
intersectsWithTextSpan(span: TextSpan) {
return span.start() <= this.end() && span.end() >= this._start
},
intersectsWith(start: number, length: number) {
var end = start + length;
return start <= this.end() && end >= this._start;
},
intersectsWithPosition(position: number) {
return position <= this.end() && position >= this._start;
},
intersection(span: TextSpan) {
var intersectStart = Math.max(this._start, span.start());
var intersectEnd = Math.min(this.end(), span.end());
if (intersectStart <= intersectEnd) {
return createTextSpanFromBounds(intersectStart, intersectEnd);
}
return undefined;
}
};
return textSpanConstructor;
})();
export function createTextSpan(start: number, length: number): TextSpan {
return new (<any>textSpanConstructor)(start, length);
}
export function createTextSpanFromBounds(start: number, end: number) {
return createTextSpan(start, end - start);
}
var textChangeRangeConstructor = (function () {
function textChangeRangeConstructor(span: TextSpan, newLength: number) {
if (newLength < 0) {
throw new Error("newLength < 0");
}
this._span = span;
this._newLength = newLength;
}
textChangeRangeConstructor.prototype = {
span() {
return this._span;
},
newLength() {
return this._newLength;
},
newSpan() {
return createTextSpan(this.span().start(), this.newLength());
},
isUnchanged() {
return this.span().isEmpty() && this.newLength() === 0;
}
};
return textChangeRangeConstructor;
})();
export function createTextChangeRange(span: TextSpan, newLength: number): TextChangeRange {
return new (<any>textChangeRangeConstructor)(span, newLength);
}
export var unchangedTextChangeRange = createTextChangeRange(createTextSpan(0, 0), 0);
/**
* Called to merge all the changes that occurred across several versions of a script snapshot
* into a single change. i.e. if a user keeps making successive edits to a script we will
* have a text change from V1 to V2, V2 to V3, ..., Vn.
*
* This function will then merge those changes into a single change range valid between V1 and
* Vn.
*/
export function collapseTextChangeRangesAcrossMultipleVersions(changes: TextChangeRange[]): TextChangeRange {
if (changes.length === 0) {
return unchangedTextChangeRange;
}
if (changes.length === 1) {
return changes[0];
}
// We change from talking about { { oldStart, oldLength }, newLength } to { oldStart, oldEnd, newEnd }
// as it makes things much easier to reason about.
var change0 = changes[0];
var oldStartN = change0.span().start();
var oldEndN = change0.span().end();
var newEndN = oldStartN + change0.newLength();
for (var i = 1; i < changes.length; i++) {
var nextChange = changes[i];
// Consider the following case:
// i.e. two edits. The first represents the text change range { { 10, 50 }, 30 }. i.e. The span starting
// at 10, with length 50 is reduced to length 30. The second represents the text change range { { 30, 30 }, 40 }.
// i.e. the span starting at 30 with length 30 is increased to length 40.
//
// 0 10 20 30 40 50 60 70 80 90 100
// -------------------------------------------------------------------------------------------------------
// | /
// | /----
// T1 | /----
// | /----
// | /----
// -------------------------------------------------------------------------------------------------------
// | \
// | \
// T2 | \
// | \
// | \
// -------------------------------------------------------------------------------------------------------
//
// Merging these turns out to not be too difficult. First, determining the new start of the change is trivial
// it's just the min of the old and new starts. i.e.:
//
// 0 10 20 30 40 50 60 70 80 90 100
// ------------------------------------------------------------*------------------------------------------
// | /
// | /----
// T1 | /----
// | /----
// | /----
// ----------------------------------------$-------------------$------------------------------------------
// . | \
// . | \
// T2 . | \
// . | \
// . | \
// ----------------------------------------------------------------------*--------------------------------
//
// (Note the dots represent the newly inferrred start.
// Determining the new and old end is also pretty simple. Basically it boils down to paying attention to the
// absolute positions at the asterixes, and the relative change between the dollar signs. Basically, we see
// which if the two $'s precedes the other, and we move that one forward until they line up. in this case that
// means:
//
// 0 10 20 30 40 50 60 70 80 90 100
// --------------------------------------------------------------------------------*----------------------
// | /
// | /----
// T1 | /----
// | /----
// | /----
// ------------------------------------------------------------$------------------------------------------
// . | \
// . | \
// T2 . | \
// . | \
// . | \
// ----------------------------------------------------------------------*--------------------------------
//
// In other words (in this case), we're recognizing that the second edit happened after where the first edit
// ended with a delta of 20 characters (60 - 40). Thus, if we go back in time to where the first edit started
// that's the same as if we started at char 80 instead of 60.
//
// As it so happens, the same logic applies if the second edit precedes the first edit. In that case rahter
// than pusing the first edit forward to match the second, we'll push the second edit forward to match the
// first.
//
// In this case that means we have { oldStart: 10, oldEnd: 80, newEnd: 70 } or, in TextChangeRange
// semantics: { { start: 10, length: 70 }, newLength: 60 }
//
// The math then works out as follows.
// If we have { oldStart1, oldEnd1, newEnd1 } and { oldStart2, oldEnd2, newEnd2 } then we can compute the
// final result like so:
//
// {
// oldStart3: Min(oldStart1, oldStart2),
// oldEnd3 : Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)),
// newEnd3 : Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2))
// }
var oldStart1 = oldStartN;
var oldEnd1 = oldEndN;
var newEnd1 = newEndN;
var oldStart2 = nextChange.span().start();
var oldEnd2 = nextChange.span().end();
var newEnd2 = oldStart2 + nextChange.newLength();
oldStartN = Math.min(oldStart1, oldStart2);
oldEndN = Math.max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1));
newEndN = Math.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2));
}
return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength: */newEndN - oldStartN);
}
}