Skip to content

Commit 0d9b2c8

Browse files
mhegazyDanielRosenwasser
authored andcommitted
move text defintions to services.ts
1 parent 4deea66 commit 0d9b2c8

File tree

3 files changed

+295
-298
lines changed

3 files changed

+295
-298
lines changed

src/services/formatting/references.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
// limitations under the License.
1414
//
1515

16-
///<reference path='..\text.ts' />
1716
///<reference path='..\services.ts' />
1817
///<reference path='formattingContext.ts' />
1918
///<reference path='formattingRequestKind.ts' />

src/services/services.ts

Lines changed: 295 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
/// <reference path="..\compiler\parser.ts"/>
55
/// <reference path="..\compiler\checker.ts"/>
66

7-
/// <reference path='text.ts' />
87
/// <reference path='outliningElementsCollector.ts' />
98
/// <reference path='navigationBar.ts' />
109
/// <reference path='breakpoints.ts' />
@@ -933,6 +932,301 @@ module ts {
933932
dispose(): void;
934933
}
935934

935+
export class TextSpan {
936+
private _start: number;
937+
private _length: number;
938+
939+
/**
940+
* Creates a TextSpan instance beginning with the position Start and having the Length
941+
* specified with length.
942+
*/
943+
constructor(start: number, length: number) {
944+
Debug.assert(start >= 0, "start");
945+
Debug.assert(length >= 0, "length");
946+
947+
this._start = start;
948+
this._length = length;
949+
}
950+
951+
public toJSON(key: any): any {
952+
return { start: this._start, length: this._length };
953+
}
954+
955+
public start(): number {
956+
return this._start;
957+
}
958+
959+
public length(): number {
960+
return this._length;
961+
}
962+
963+
public end(): number {
964+
return this._start + this._length;
965+
}
966+
967+
public isEmpty(): boolean {
968+
return this._length === 0;
969+
}
970+
971+
/**
972+
* Determines whether the position lies within the span. Returns true if the position is greater than or equal to Start and strictly less
973+
* than End, otherwise false.
974+
* @param position The position to check.
975+
*/
976+
public containsPosition(position: number): boolean {
977+
return position >= this._start && position < this.end();
978+
}
979+
980+
/**
981+
* Determines whether span falls completely within this span. Returns true if the specified span falls completely within this span, otherwise false.
982+
* @param span The span to check.
983+
*/
984+
public containsTextSpan(span: TextSpan): boolean {
985+
return span._start >= this._start && span.end() <= this.end();
986+
}
987+
988+
/**
989+
* Determines whether the given span overlaps this span. Two spans are considered to overlap
990+
* if they have positions in common and neither is empty. Empty spans do not overlap with any
991+
* other span. Returns true if the spans overlap, false otherwise.
992+
* @param span The span to check.
993+
*/
994+
public overlapsWith(span: TextSpan): boolean {
995+
var overlapStart = Math.max(this._start, span._start);
996+
var overlapEnd = Math.min(this.end(), span.end());
997+
998+
return overlapStart < overlapEnd;
999+
}
1000+
1001+
/**
1002+
* Returns the overlap with the given span, or undefined if there is no overlap.
1003+
* @param span The span to check.
1004+
*/
1005+
public overlap(span: TextSpan): TextSpan {
1006+
var overlapStart = Math.max(this._start, span._start);
1007+
var overlapEnd = Math.min(this.end(), span.end());
1008+
1009+
if (overlapStart < overlapEnd) {
1010+
return TextSpan.fromBounds(overlapStart, overlapEnd);
1011+
}
1012+
1013+
return undefined;
1014+
}
1015+
1016+
/**
1017+
* Determines whether span intersects this span. Two spans are considered to
1018+
* intersect if they have positions in common or the end of one span
1019+
* coincides with the start of the other span. Returns true if the spans intersect, false otherwise.
1020+
* @param The span to check.
1021+
*/
1022+
public intersectsWithTextSpan(span: TextSpan): boolean {
1023+
return span._start <= this.end() && span.end() >= this._start;
1024+
}
1025+
1026+
public intersectsWith(start: number, length: number): boolean {
1027+
var end = start + length;
1028+
return start <= this.end() && end >= this._start;
1029+
}
1030+
1031+
/**
1032+
* Determines whether the given position intersects this span.
1033+
* A position is considered to intersect if it is between the start and
1034+
* end positions (inclusive) of this span. Returns true if the position intersects, false otherwise.
1035+
* @param position The position to check.
1036+
*/
1037+
public intersectsWithPosition(position: number): boolean {
1038+
return position <= this.end() && position >= this._start;
1039+
}
1040+
1041+
/**
1042+
* Returns the intersection with the given span, or undefined if there is no intersection.
1043+
* @param span The span to check.
1044+
*/
1045+
public intersection(span: TextSpan): TextSpan {
1046+
var intersectStart = Math.max(this._start, span._start);
1047+
var intersectEnd = Math.min(this.end(), span.end());
1048+
1049+
if (intersectStart <= intersectEnd) {
1050+
return TextSpan.fromBounds(intersectStart, intersectEnd);
1051+
}
1052+
1053+
return undefined;
1054+
}
1055+
1056+
/**
1057+
* Creates a new TextSpan from the given start and end positions
1058+
* as opposed to a position and length.
1059+
*/
1060+
public static fromBounds(start: number, end: number): TextSpan {
1061+
Debug.assert(start >= 0);
1062+
Debug.assert(end - start >= 0);
1063+
return new TextSpan(start, end - start);
1064+
}
1065+
}
1066+
1067+
export class TextChangeRange {
1068+
public static unchanged = new TextChangeRange(new TextSpan(0, 0), 0);
1069+
1070+
private _span: TextSpan;
1071+
private _newLength: number;
1072+
1073+
/**
1074+
* Initializes a new instance of TextChangeRange.
1075+
*/
1076+
constructor(span: TextSpan, newLength: number) {
1077+
Debug.assert(newLength >= 0, "newLength");
1078+
1079+
this._span = span;
1080+
this._newLength = newLength;
1081+
}
1082+
1083+
/**
1084+
* The span of text before the edit which is being changed
1085+
*/
1086+
public span(): TextSpan {
1087+
return this._span;
1088+
}
1089+
1090+
/**
1091+
* Width of the span after the edit. A 0 here would represent a delete
1092+
*/
1093+
public newLength(): number {
1094+
return this._newLength;
1095+
}
1096+
1097+
public newSpan(): TextSpan {
1098+
return new TextSpan(this.span().start(), this.newLength());
1099+
}
1100+
1101+
public isUnchanged(): boolean {
1102+
return this.span().isEmpty() && this.newLength() === 0;
1103+
}
1104+
1105+
/**
1106+
* Called to merge all the changes that occurred across several versions of a script snapshot
1107+
* into a single change. i.e. if a user keeps making successive edits to a script we will
1108+
* have a text change from V1 to V2, V2 to V3, ..., Vn.
1109+
*
1110+
* This function will then merge those changes into a single change range valid between V1 and
1111+
* Vn.
1112+
*/
1113+
public static collapseChangesAcrossMultipleVersions(changes: TextChangeRange[]): TextChangeRange {
1114+
if (changes.length === 0) {
1115+
return TextChangeRange.unchanged;
1116+
}
1117+
1118+
if (changes.length === 1) {
1119+
return changes[0];
1120+
}
1121+
1122+
// We change from talking about { { oldStart, oldLength }, newLength } to { oldStart, oldEnd, newEnd }
1123+
// as it makes things much easier to reason about.
1124+
var change0 = changes[0];
1125+
1126+
var oldStartN = change0.span().start();
1127+
var oldEndN = change0.span().end();
1128+
var newEndN = oldStartN + change0.newLength();
1129+
1130+
for (var i = 1; i < changes.length; i++) {
1131+
var nextChange = changes[i];
1132+
1133+
// Consider the following case:
1134+
// i.e. two edits. The first represents the text change range { { 10, 50 }, 30 }. i.e. The span starting
1135+
// at 10, with length 50 is reduced to length 30. The second represents the text change range { { 30, 30 }, 40 }.
1136+
// i.e. the span starting at 30 with length 30 is increased to length 40.
1137+
//
1138+
// 0 10 20 30 40 50 60 70 80 90 100
1139+
// -------------------------------------------------------------------------------------------------------
1140+
// | /
1141+
// | /----
1142+
// T1 | /----
1143+
// | /----
1144+
// | /----
1145+
// -------------------------------------------------------------------------------------------------------
1146+
// | \
1147+
// | \
1148+
// T2 | \
1149+
// | \
1150+
// | \
1151+
// -------------------------------------------------------------------------------------------------------
1152+
//
1153+
// Merging these turns out to not be too difficult. First, determining the new start of the change is trivial
1154+
// it's just the min of the old and new starts. i.e.:
1155+
//
1156+
// 0 10 20 30 40 50 60 70 80 90 100
1157+
// ------------------------------------------------------------*------------------------------------------
1158+
// | /
1159+
// | /----
1160+
// T1 | /----
1161+
// | /----
1162+
// | /----
1163+
// ----------------------------------------$-------------------$------------------------------------------
1164+
// . | \
1165+
// . | \
1166+
// T2 . | \
1167+
// . | \
1168+
// . | \
1169+
// ----------------------------------------------------------------------*--------------------------------
1170+
//
1171+
// (Note the dots represent the newly inferrred start.
1172+
// Determining the new and old end is also pretty simple. Basically it boils down to paying attention to the
1173+
// absolute positions at the asterixes, and the relative change between the dollar signs. Basically, we see
1174+
// which if the two $'s precedes the other, and we move that one forward until they line up. in this case that
1175+
// means:
1176+
//
1177+
// 0 10 20 30 40 50 60 70 80 90 100
1178+
// --------------------------------------------------------------------------------*----------------------
1179+
// | /
1180+
// | /----
1181+
// T1 | /----
1182+
// | /----
1183+
// | /----
1184+
// ------------------------------------------------------------$------------------------------------------
1185+
// . | \
1186+
// . | \
1187+
// T2 . | \
1188+
// . | \
1189+
// . | \
1190+
// ----------------------------------------------------------------------*--------------------------------
1191+
//
1192+
// In other words (in this case), we're recognizing that the second edit happened after where the first edit
1193+
// ended with a delta of 20 characters (60 - 40). Thus, if we go back in time to where the first edit started
1194+
// that's the same as if we started at char 80 instead of 60.
1195+
//
1196+
// As it so happens, the same logic applies if the second edit precedes the first edit. In that case rahter
1197+
// than pusing the first edit forward to match the second, we'll push the second edit forward to match the
1198+
// first.
1199+
//
1200+
// In this case that means we have { oldStart: 10, oldEnd: 80, newEnd: 70 } or, in TextChangeRange
1201+
// semantics: { { start: 10, length: 70 }, newLength: 60 }
1202+
//
1203+
// The math then works out as follows.
1204+
// If we have { oldStart1, oldEnd1, newEnd1 } and { oldStart2, oldEnd2, newEnd2 } then we can compute the
1205+
// final result like so:
1206+
//
1207+
// {
1208+
// oldStart3: Min(oldStart1, oldStart2),
1209+
// oldEnd3 : Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)),
1210+
// newEnd3 : Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2))
1211+
// }
1212+
1213+
var oldStart1 = oldStartN;
1214+
var oldEnd1 = oldEndN;
1215+
var newEnd1 = newEndN;
1216+
1217+
var oldStart2 = nextChange.span().start();
1218+
var oldEnd2 = nextChange.span().end();
1219+
var newEnd2 = oldStart2 + nextChange.newLength();
1220+
1221+
oldStartN = Math.min(oldStart1, oldStart2);
1222+
oldEndN = Math.max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1));
1223+
newEndN = Math.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2));
1224+
}
1225+
1226+
return new TextChangeRange(TextSpan.fromBounds(oldStartN, oldEndN), /*newLength: */newEndN - oldStartN);
1227+
}
1228+
}
1229+
9361230
export interface ClassifiedSpan {
9371231
textSpan: TextSpan;
9381232
classificationType: string; // ClassificationTypeNames

0 commit comments

Comments
 (0)