From 62dd12cb7ac04467ed19fe06b70248773abecdeb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 12 Dec 2014 00:56:14 -0800 Subject: [PATCH] Move functions from 'types.ts' to 'utilities.ts'. --- src/compiler/types.ts | 235 -------------------------------------- src/compiler/utilities.ts | 235 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+), 235 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2df24398d61..ee9694e0568 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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 (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 (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); - } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 94b51e52ba6..62d598ea2fc 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -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 (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 (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); + } } \ No newline at end of file