Breakpoint span in variable declarations in new language service

Also updates the fourslash breakpoints baseline to be more readable
This commit is contained in:
Sheetal Nandi 2014-10-17 15:39:31 -07:00
parent a6eb698f5b
commit 06d29a00f2
6 changed files with 183 additions and 14 deletions

View File

@ -55,6 +55,7 @@ var servicesSources = [
].map(function (f) {
return path.join(compilerDirectory, f);
}).concat([
"breakpoints.ts",
"services.ts",
"shims.ts",
"signatureHelp.ts",

View File

@ -985,13 +985,23 @@ module FourSlash {
return item.parameters[currentParam];
}
public getBreakpointStatementLocation(pos: number) {
private alignmentForExtraInfo = 50;
public getBreakpointStatementLocation(pos: number, prefixString: string) {
this.taoInvalidReason = 'getBreakpointStatementLocation NYI';
var spanInfo = this.languageService.getBreakpointStatementAtPosition(this.activeFile.fileName, pos);
var resultString = "\n**Pos: " + pos + " SpanInfo: " + JSON.stringify(spanInfo) + "\n** Statement: ";
if (spanInfo !== null) {
resultString = resultString + this.activeFile.content.substr(spanInfo.start(), spanInfo.length());
var resultString = "SpanInfo: " + JSON.stringify(spanInfo);
if (spanInfo) {
var spanString = this.activeFile.content.substr(spanInfo.start(), spanInfo.length());
var spanLineMap = ts.getLineStarts(spanString);
for (var i = 0; i < spanLineMap.length; i++) {
if (!i) {
resultString += "\n";
}
resultString += prefixString + spanString.substring(spanLineMap[i], spanLineMap[i + 1]);
}
resultString += "\n" + prefixString + ":=> (" + this.getLineColStringAtPosition(spanInfo.start()) + ") to (" + this.getLineColStringAtPosition(spanInfo.end()) + ")";
}
return resultString;
}
@ -1003,12 +1013,60 @@ module FourSlash {
"Breakpoint Locations for " + this.activeFile.fileName,
this.testData.globalOptions[testOptMetadataNames.baselineFile],
() => {
var fileLength = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getLength();
var fileLineMap = ts.getLineStarts(this.activeFile.content);
var nextLine = 0;
var resultString = "";
for (var pos = 0; pos < fileLength; pos++) {
resultString = resultString + this.getBreakpointStatementLocation(pos);
var currentLine: string;
var previousSpanInfo: string;
var startColumn: number;
var length: number;
var prefixString = " >";
var addSpanInfoString = () => {
if (previousSpanInfo) {
resultString += currentLine;
var thisLineMarker = repeatString(startColumn, " ") + repeatString(length, "~");
thisLineMarker += repeatString(this.alignmentForExtraInfo - thisLineMarker.length - prefixString.length + 1, " ");
resultString += thisLineMarker;
resultString += "=> Pos: (" + (pos - length) + " to " + (pos - 1) + ") ";
resultString += " " + previousSpanInfo;
previousSpanInfo = undefined;
}
};
for (var pos = 0; pos < this.activeFile.content.length; pos++) {
if (pos === 0 || pos === fileLineMap[nextLine]) {
nextLine++;
addSpanInfoString();
if (resultString.length) {
resultString += "\n--------------------------------";
}
currentLine = "\n" + nextLine.toString() + repeatString(3 - nextLine.toString().length, " ") + ">" + this.activeFile.content.substring(pos, fileLineMap[nextLine]) + "\n ";
startColumn = 0;
length = 0;
}
var spanInfo = this.getBreakpointStatementLocation(pos, prefixString);
if (previousSpanInfo && previousSpanInfo !== spanInfo) {
addSpanInfoString();
previousSpanInfo = spanInfo;
startColumn = startColumn + length;
length = 1;
}
else {
previousSpanInfo = spanInfo;
length++;
}
}
addSpanInfoString();
return resultString;
function repeatString(count: number, char: string) {
var result = "";
for (var i = 0; i < count; i++) {
result += char;
}
return result;
}
},
true /* run immediately */);
}
@ -1056,7 +1114,7 @@ module FourSlash {
}
public printBreakpointLocation(pos: number) {
Harness.IO.log(this.getBreakpointStatementLocation(pos));
Harness.IO.log("\n**Pos: " + pos + " " + this.getBreakpointStatementLocation(pos, " "));
}
public printBreakpointAtCurrentLocation() {
@ -1502,7 +1560,7 @@ module FourSlash {
throw new Error('verifyCaretAtMarker failed - expected to be in file "' + pos.fileName + '", but was in file "' + this.activeFile.fileName + '"');
}
if (pos.position !== this.currentCaretPosition) {
throw new Error('verifyCaretAtMarker failed - expected to be at marker "/*' + markerName + '*/, but was at position ' + this.currentCaretPosition + '(' + this.getLineColStringAtCaret() + ')');
throw new Error('verifyCaretAtMarker failed - expected to be at marker "/*' + markerName + '*/, but was at position ' + this.currentCaretPosition + '(' + this.getLineColStringAtPosition(this.currentCaretPosition) + ')');
}
}
@ -2102,8 +2160,8 @@ module FourSlash {
return this.languageServiceShimHost.positionToZeroBasedLineCol(this.activeFile.fileName, this.currentCaretPosition).line + 1;
}
private getLineColStringAtCaret() {
var pos = this.languageServiceShimHost.positionToZeroBasedLineCol(this.activeFile.fileName, this.currentCaretPosition);
private getLineColStringAtPosition(position: number) {
var pos = this.languageServiceShimHost.positionToZeroBasedLineCol(this.activeFile.fileName, position);
return 'line ' + (pos.line + 1) + ', col ' + pos.character;
}

View File

@ -1082,4 +1082,93 @@ module TypeScript.Services.Breakpoints {
var breakpointResolver = new BreakpointResolver(posLine, lineMap);
return breakpointResolver.breakpointSpanOf(positionedToken);
}
}
module ts.Breakpoints {
/**
* Get the breakpoint span in given sourceFile
*/
export function spanInSourceFileAtLocation(sourceFile: SourceFile, position: number) {
// Cannot set breakpoint in dts file
if (sourceFile.flags & NodeFlags.DeclarationFile) {
return;
}
var tokenAtLocation = getTokenAtPosition(sourceFile, position);
var lineOfPosition = sourceFile.getLineAndCharacterFromPosition(position).line;
if (sourceFile.getLineAndCharacterFromPosition(tokenAtLocation.getStart()).line > lineOfPosition) {
// Get previous token if the token is returned starts on new line
// eg: var x =10; |--- curser is here
// var y = 10;
// token at position will return var keyword on second line as the token but we would like to use
// token on same line if trailing trivia (comments or white spaces on same line) part of the last token on that line
tokenAtLocation = findPrecedingToken(tokenAtLocation.pos, sourceFile);
}
// Cannot set breakpoint in ambient declarations
if (isInAmbientContext(tokenAtLocation)) {
return;
}
// Get the span in the node based on its syntax
return spanInNode(tokenAtLocation);
function textSpan(startNode: Node, endNode?: Node) {
return TypeScript.TextSpan.fromBounds(startNode.getStart(), (endNode || startNode).getEnd());
}
function spanInNodeIfStartsOnSameLine(node: Node): TypeScript.TextSpan {
if (node && sourceFile.getLineAndCharacterFromPosition(position).line === sourceFile.getLineAndCharacterFromPosition(node.getStart()).line) {
return spanInNode(node);
}
}
function spanInNode(node: Node): TypeScript.TextSpan {
if (node) {
switch (node.kind) {
case SyntaxKind.VariableStatement:
return spanInVariableStatement(<VariableStatement>node);
case SyntaxKind.VariableDeclaration:
return spanInVariableDeclaration(<VariableDeclaration>node);
case SyntaxKind.SemicolonToken:
case SyntaxKind.EndOfFileToken:
return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile));
default:
// Default go to parent to set the breakpoint
return spanInNode(node.parent);
}
}
function spanInVariableDeclaration(variableDeclaration: VariableDeclaration): TypeScript.TextSpan {
var isParentVariableStatement = variableDeclaration.parent.kind === SyntaxKind.VariableStatement;
var isfirstDeclarationOfVariableStatement = isParentVariableStatement &&
(<VariableStatement>variableDeclaration.parent).declarations[0] === variableDeclaration;
// Breakpoint is possible in variableDeclaration only if there is initialization
if (variableDeclaration.initializer) {
if (isfirstDeclarationOfVariableStatement) {
// First declaration - include var keyword
return textSpan(variableDeclaration.parent, variableDeclaration);
}
else {
// Span only on this declaration
return textSpan(variableDeclaration);
}
}
else if (!isfirstDeclarationOfVariableStatement && isParentVariableStatement) {
// If we cant set breakpoint on this declaration, set it on previous one
var variableStatement = <VariableStatement>variableDeclaration.parent;
var indexOfCurrentDeclaration = indexOf(variableStatement.declarations, variableDeclaration);
return spanInVariableDeclaration(variableStatement.declarations[indexOfCurrentDeclaration - 1]);
}
}
function spanInVariableStatement(variableStatement: VariableStatement): TypeScript.TextSpan {
return spanInVariableDeclaration(variableStatement.declarations[0]);
}
}
}
}

View File

@ -4737,9 +4737,7 @@ module ts {
function getBreakpointStatementAtPosition(filename: string, position: number) {
// doesn't use compiler - no need to synchronize with host
filename = TypeScript.switchToForwardSlashes(filename);
var syntaxtree = getSyntaxTree(filename);
return TypeScript.Services.Breakpoints.getBreakpointLocation(syntaxtree, position);
return Breakpoints.spanInSourceFileAtLocation(getCurrentSourceFile(filename), position);
}
function getNavigationBarItems(filename: string): NavigationBarItem[] {

View File

@ -0,0 +1,23 @@
1 >var a = 10;
~~~~~~~~~~~~ => Pos: (0 to 11) SpanInfo: {"start":0,"length":10}
>var a = 10
>:=> (line 1, col 0) to (line 1, col 10)
--------------------------------
2 >var b;
~~~~~~~ => Pos: (12 to 18) SpanInfo: undefined
--------------------------------
3 >var c = 10, d, e;
~~~~~~~~~~~~~~~~~~ => Pos: (19 to 36) SpanInfo: {"start":19,"length":10}
>var c = 10
>:=> (line 3, col 0) to (line 3, col 10)
--------------------------------
4 >var c2, d2 = 10;
~~~~~~~ => Pos: (37 to 43) SpanInfo: undefined
4 >var c2, d2 = 10;
~~~~~~~~~ => Pos: (44 to 52) SpanInfo: {"start":45,"length":7}
>d2 = 10
>:=> (line 4, col 8) to (line 4, col 15)