mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 01:49:57 -05:00
merge with origin/master
This commit is contained in:
@@ -45,6 +45,10 @@ namespace ts.BreakpointResolver {
|
||||
return createTextSpanFromBounds(start, (endNode || startNode).getEnd());
|
||||
}
|
||||
|
||||
function textSpanEndingAtNextToken(startNode: Node, previousTokenToFindNextEndToken: Node): TextSpan {
|
||||
return textSpan(startNode, findNextToken(previousTokenToFindNextEndToken, previousTokenToFindNextEndToken.parent));
|
||||
}
|
||||
|
||||
function spanInNodeIfStartsOnSameLine(node: Node, otherwiseOnNode?: Node): TextSpan {
|
||||
if (node && lineOfPosition === sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line) {
|
||||
return spanInNode(node);
|
||||
@@ -66,33 +70,6 @@ namespace ts.BreakpointResolver {
|
||||
|
||||
function spanInNode(node: Node): TextSpan {
|
||||
if (node) {
|
||||
if (isExpression(node)) {
|
||||
if (node.parent.kind === SyntaxKind.DoStatement) {
|
||||
// Set span as if on while keyword
|
||||
return spanInPreviousNode(node);
|
||||
}
|
||||
|
||||
if (node.parent.kind === SyntaxKind.Decorator) {
|
||||
// Set breakpoint on the decorator emit
|
||||
return spanInNode(node.parent);
|
||||
}
|
||||
|
||||
if (node.parent.kind === SyntaxKind.ForStatement) {
|
||||
// For now lets set the span on this expression, fix it later
|
||||
return textSpan(node);
|
||||
}
|
||||
|
||||
if (node.parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node.parent).operatorToken.kind === SyntaxKind.CommaToken) {
|
||||
// if this is comma expression, the breakpoint is possible in this expression
|
||||
return textSpan(node);
|
||||
}
|
||||
|
||||
if (node.parent.kind === SyntaxKind.ArrowFunction && (<FunctionLikeDeclaration>node.parent).body === node) {
|
||||
// If this is body of arrow function, it is allowed to have the breakpoint
|
||||
return textSpan(node);
|
||||
}
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.VariableStatement:
|
||||
// Span on first variable declaration
|
||||
@@ -120,7 +97,7 @@ namespace ts.BreakpointResolver {
|
||||
if (isFunctionBlock(node)) {
|
||||
return spanInFunctionBlock(<Block>node);
|
||||
}
|
||||
// Fall through
|
||||
// Fall through
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return spanInBlock(<Block>node);
|
||||
|
||||
@@ -137,7 +114,7 @@ namespace ts.BreakpointResolver {
|
||||
|
||||
case SyntaxKind.WhileStatement:
|
||||
// Span on while(...)
|
||||
return textSpan(node, findNextToken((<WhileStatement>node).expression, node));
|
||||
return textSpanEndingAtNextToken(node, (<WhileStatement>node).expression);
|
||||
|
||||
case SyntaxKind.DoStatement:
|
||||
// span in statement of the do statement
|
||||
@@ -149,7 +126,7 @@ namespace ts.BreakpointResolver {
|
||||
|
||||
case SyntaxKind.IfStatement:
|
||||
// set on if(..) span
|
||||
return textSpan(node, findNextToken((<IfStatement>node).expression, node));
|
||||
return textSpanEndingAtNextToken(node, (<IfStatement>node).expression);
|
||||
|
||||
case SyntaxKind.LabeledStatement:
|
||||
// span in statement
|
||||
@@ -164,13 +141,16 @@ namespace ts.BreakpointResolver {
|
||||
return spanInForStatement(<ForStatement>node);
|
||||
|
||||
case SyntaxKind.ForInStatement:
|
||||
// span of for (a in ...)
|
||||
return textSpanEndingAtNextToken(node, (<ForInStatement>node).expression);
|
||||
|
||||
case SyntaxKind.ForOfStatement:
|
||||
// span on for (a in ...)
|
||||
return textSpan(node, findNextToken((<ForInStatement | ForOfStatement>node).expression, node));
|
||||
// span in initializer
|
||||
return spanInInitializerOfForLike(<ForOfStatement | ForInStatement>node);
|
||||
|
||||
case SyntaxKind.SwitchStatement:
|
||||
// span on switch(...)
|
||||
return textSpan(node, findNextToken((<SwitchStatement>node).expression, node));
|
||||
return textSpanEndingAtNextToken(node, (<SwitchStatement>node).expression);
|
||||
|
||||
case SyntaxKind.CaseClause:
|
||||
case SyntaxKind.DefaultClause:
|
||||
@@ -210,8 +190,7 @@ namespace ts.BreakpointResolver {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.BindingElement:
|
||||
// span on complete node
|
||||
return textSpan(node);
|
||||
|
||||
@@ -222,6 +201,10 @@ namespace ts.BreakpointResolver {
|
||||
case SyntaxKind.Decorator:
|
||||
return spanInNodeArray(node.parent.decorators);
|
||||
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
return spanInBindingPattern(<BindingPattern>node);
|
||||
|
||||
// No breakpoint in interface, type alias
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
@@ -234,14 +217,17 @@ namespace ts.BreakpointResolver {
|
||||
|
||||
case SyntaxKind.CommaToken:
|
||||
return spanInPreviousNode(node)
|
||||
|
||||
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
return spanInOpenBraceToken(node);
|
||||
|
||||
case SyntaxKind.CloseBraceToken:
|
||||
return spanInCloseBraceToken(node);
|
||||
|
||||
case SyntaxKind.OpenParenToken:
|
||||
case SyntaxKind.CloseBracketToken:
|
||||
return spanInCloseBracketToken(node);
|
||||
|
||||
case SyntaxKind.OpenParenToken:
|
||||
return spanInOpenParenToken(node);
|
||||
|
||||
case SyntaxKind.CloseParenToken:
|
||||
@@ -263,15 +249,93 @@ namespace ts.BreakpointResolver {
|
||||
case SyntaxKind.FinallyKeyword:
|
||||
return spanInNextNode(node);
|
||||
|
||||
case SyntaxKind.OfKeyword:
|
||||
return spanInOfKeyword(node);
|
||||
|
||||
default:
|
||||
// If this is name of property assignment, set breakpoint in the initializer
|
||||
if (node.parent.kind === SyntaxKind.PropertyAssignment && (<PropertyDeclaration>node.parent).name === node) {
|
||||
return spanInNode((<PropertyDeclaration>node.parent).initializer);
|
||||
// Destructuring pattern in destructuring assignment
|
||||
// [a, b, c] of
|
||||
// [a, b, c] = expression
|
||||
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node)) {
|
||||
return spanInArrayLiteralOrObjectLiteralDestructuringPattern(<DestructuringPattern>node);
|
||||
}
|
||||
|
||||
// Set breakpoint on identifier element of destructuring pattern
|
||||
// a or ...c or d: x from
|
||||
// [a, b, ...c] or { a, b } or { d: x } from destructuring pattern
|
||||
if ((node.kind === SyntaxKind.Identifier ||
|
||||
node.kind == SyntaxKind.SpreadElementExpression ||
|
||||
node.kind === SyntaxKind.PropertyAssignment ||
|
||||
node.kind === SyntaxKind.ShorthandPropertyAssignment) &&
|
||||
isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
|
||||
return textSpan(node);
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.BinaryExpression) {
|
||||
const binaryExpression = <BinaryExpression>node;
|
||||
// Set breakpoint in destructuring pattern if its destructuring assignment
|
||||
// [a, b, c] or {a, b, c} of
|
||||
// [a, b, c] = expression or
|
||||
// {a, b, c} = expression
|
||||
if (isArrayLiteralOrObjectLiteralDestructuringPattern(binaryExpression.left)) {
|
||||
return spanInArrayLiteralOrObjectLiteralDestructuringPattern(
|
||||
<ArrayLiteralExpression | ObjectLiteralExpression>binaryExpression.left);
|
||||
}
|
||||
|
||||
if (binaryExpression.operatorToken.kind === SyntaxKind.EqualsToken &&
|
||||
isArrayLiteralOrObjectLiteralDestructuringPattern(binaryExpression.parent)) {
|
||||
// Set breakpoint on assignment expression element of destructuring pattern
|
||||
// a = expression of
|
||||
// [a = expression, b, c] = someExpression or
|
||||
// { a = expression, b, c } = someExpression
|
||||
return textSpan(node);
|
||||
}
|
||||
|
||||
if (binaryExpression.operatorToken.kind === SyntaxKind.CommaToken) {
|
||||
return spanInNode(binaryExpression.left);
|
||||
}
|
||||
}
|
||||
|
||||
if (isExpression(node)) {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.DoStatement:
|
||||
// Set span as if on while keyword
|
||||
return spanInPreviousNode(node);
|
||||
|
||||
case SyntaxKind.Decorator:
|
||||
// Set breakpoint on the decorator emit
|
||||
return spanInNode(node.parent);
|
||||
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
return textSpan(node);
|
||||
|
||||
case SyntaxKind.BinaryExpression:
|
||||
if ((<BinaryExpression>node.parent).operatorToken.kind === SyntaxKind.CommaToken) {
|
||||
// if this is comma expression, the breakpoint is possible in this expression
|
||||
return textSpan(node);
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.ArrowFunction:
|
||||
if ((<FunctionLikeDeclaration>node.parent).body === node) {
|
||||
// If this is body of arrow function, it is allowed to have the breakpoint
|
||||
return textSpan(node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is name of property assignment, set breakpoint in the initializer
|
||||
if (node.parent.kind === SyntaxKind.PropertyAssignment &&
|
||||
(<PropertyDeclaration>node.parent).name === node &&
|
||||
!isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.parent)) {
|
||||
return spanInNode((<PropertyDeclaration>node.parent).initializer);
|
||||
}
|
||||
|
||||
// Breakpoint in type assertion goes to its operand
|
||||
if (node.parent.kind === SyntaxKind.TypeAssertionExpression && (<TypeAssertion>node.parent).type === node) {
|
||||
return spanInNode((<TypeAssertion>node.parent).expression);
|
||||
return spanInNextNode((<TypeAssertion>node.parent).type);
|
||||
}
|
||||
|
||||
// return type of function go to previous token
|
||||
@@ -279,48 +343,70 @@ namespace ts.BreakpointResolver {
|
||||
return spanInPreviousNode(node);
|
||||
}
|
||||
|
||||
// initializer of variable/parameter declaration go to previous node
|
||||
if ((node.parent.kind === SyntaxKind.VariableDeclaration ||
|
||||
node.parent.kind === SyntaxKind.Parameter)) {
|
||||
const paramOrVarDecl = <VariableDeclaration | ParameterDeclaration>node.parent;
|
||||
if (paramOrVarDecl.initializer === node ||
|
||||
paramOrVarDecl.type === node ||
|
||||
isAssignmentOperator(node.kind)) {
|
||||
return spanInPreviousNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (node.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
const binaryExpression = <BinaryExpression>node.parent;
|
||||
if (isArrayLiteralOrObjectLiteralDestructuringPattern(binaryExpression.left) &&
|
||||
(binaryExpression.right === node ||
|
||||
binaryExpression.operatorToken === node)) {
|
||||
// If initializer of destructuring assignment move to previous token
|
||||
return spanInPreviousNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Default go to parent to set the breakpoint
|
||||
return spanInNode(node.parent);
|
||||
}
|
||||
}
|
||||
|
||||
function textSpanFromVariableDeclaration(variableDeclaration: VariableDeclaration): TextSpan {
|
||||
let declarations = variableDeclaration.parent.declarations;
|
||||
if (declarations && declarations[0] === variableDeclaration) {
|
||||
// First declaration - include let keyword
|
||||
return textSpan(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent), variableDeclaration);
|
||||
}
|
||||
else {
|
||||
// Span only on this declaration
|
||||
return textSpan(variableDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
function spanInVariableDeclaration(variableDeclaration: VariableDeclaration): TextSpan {
|
||||
// If declaration of for in statement, just set the span in parent
|
||||
if (variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement ||
|
||||
variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
|
||||
if (variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
|
||||
return spanInNode(variableDeclaration.parent.parent);
|
||||
}
|
||||
|
||||
let isParentVariableStatement = variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement;
|
||||
let isDeclarationOfForStatement = variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement && contains((<VariableDeclarationList>(<ForStatement>variableDeclaration.parent.parent).initializer).declarations, variableDeclaration);
|
||||
let declarations = isParentVariableStatement
|
||||
? (<VariableStatement>variableDeclaration.parent.parent).declarationList.declarations
|
||||
: isDeclarationOfForStatement
|
||||
? (<VariableDeclarationList>(<ForStatement>variableDeclaration.parent.parent).initializer).declarations
|
||||
: undefined;
|
||||
|
||||
// If this is a destructuring pattern set breakpoint in binding pattern
|
||||
if (isBindingPattern(variableDeclaration.name)) {
|
||||
return spanInBindingPattern(<BindingPattern>variableDeclaration.name);
|
||||
}
|
||||
|
||||
// Breakpoint is possible in variableDeclaration only if there is initialization
|
||||
if (variableDeclaration.initializer || (variableDeclaration.flags & NodeFlags.Export)) {
|
||||
if (declarations && declarations[0] === variableDeclaration) {
|
||||
if (isParentVariableStatement) {
|
||||
// First declaration - include let keyword
|
||||
return textSpan(variableDeclaration.parent, variableDeclaration);
|
||||
}
|
||||
else {
|
||||
Debug.assert(isDeclarationOfForStatement);
|
||||
// Include let keyword from for statement declarations in the span
|
||||
return textSpan(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent), variableDeclaration);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Span only on this declaration
|
||||
return textSpan(variableDeclaration);
|
||||
}
|
||||
// or its declaration from 'for of'
|
||||
if (variableDeclaration.initializer ||
|
||||
(variableDeclaration.flags & NodeFlags.Export) ||
|
||||
variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
|
||||
return textSpanFromVariableDeclaration(variableDeclaration);
|
||||
}
|
||||
else if (declarations && declarations[0] !== variableDeclaration) {
|
||||
|
||||
let declarations = variableDeclaration.parent.declarations;
|
||||
if (declarations && declarations[0] !== variableDeclaration) {
|
||||
// If we cant set breakpoint on this declaration, set it on previous one
|
||||
let indexOfCurrentDeclaration = indexOf(declarations, variableDeclaration);
|
||||
return spanInVariableDeclaration(declarations[indexOfCurrentDeclaration - 1]);
|
||||
// Because the variable declaration may be binding pattern and
|
||||
// we would like to set breakpoint in last binding element if thats the case,
|
||||
// use preceding token instead
|
||||
return spanInNode(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +417,11 @@ namespace ts.BreakpointResolver {
|
||||
}
|
||||
|
||||
function spanInParameterDeclaration(parameter: ParameterDeclaration): TextSpan {
|
||||
if (canHaveSpanInParameterDeclaration(parameter)) {
|
||||
if (isBindingPattern(parameter.name)) {
|
||||
// set breakpoint in binding pattern
|
||||
return spanInBindingPattern(<BindingPattern>parameter.name);
|
||||
}
|
||||
else if (canHaveSpanInParameterDeclaration(parameter)) {
|
||||
return textSpan(parameter);
|
||||
}
|
||||
else {
|
||||
@@ -388,11 +478,11 @@ namespace ts.BreakpointResolver {
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.IfStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
return spanInNodeIfStartsOnSameLine(block.parent, block.statements[0]);
|
||||
|
||||
// Set span on previous token if it starts on same line otherwise on the first statement of the block
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
return spanInNodeIfStartsOnSameLine(findPrecedingToken(block.pos, sourceFile, block.parent), block.statements[0]);
|
||||
}
|
||||
|
||||
@@ -400,17 +490,23 @@ namespace ts.BreakpointResolver {
|
||||
return spanInNode(block.statements[0]);
|
||||
}
|
||||
|
||||
function spanInInitializerOfForLike(forLikeStaement: ForStatement | ForOfStatement | ForInStatement): TextSpan {
|
||||
if (forLikeStaement.initializer.kind === SyntaxKind.VariableDeclarationList) {
|
||||
// declaration list, set breakpoint in first declaration
|
||||
let variableDeclarationList = <VariableDeclarationList>forLikeStaement.initializer;
|
||||
if (variableDeclarationList.declarations.length > 0) {
|
||||
return spanInNode(variableDeclarationList.declarations[0]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Expression - set breakpoint in it
|
||||
return spanInNode(forLikeStaement.initializer);
|
||||
}
|
||||
}
|
||||
|
||||
function spanInForStatement(forStatement: ForStatement): TextSpan {
|
||||
if (forStatement.initializer) {
|
||||
if (forStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
|
||||
let variableDeclarationList = <VariableDeclarationList>forStatement.initializer;
|
||||
if (variableDeclarationList.declarations.length > 0) {
|
||||
return spanInNode(variableDeclarationList.declarations[0]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return spanInNode(forStatement.initializer);
|
||||
}
|
||||
return spanInInitializerOfForLike(forStatement);
|
||||
}
|
||||
|
||||
if (forStatement.condition) {
|
||||
@@ -421,6 +517,45 @@ namespace ts.BreakpointResolver {
|
||||
}
|
||||
}
|
||||
|
||||
function spanInBindingPattern(bindingPattern: BindingPattern): TextSpan {
|
||||
// Set breakpoint in first binding element
|
||||
let firstBindingElement = forEach(bindingPattern.elements,
|
||||
element => element.kind !== SyntaxKind.OmittedExpression ? element : undefined);
|
||||
|
||||
if (firstBindingElement) {
|
||||
return spanInNode(firstBindingElement);
|
||||
}
|
||||
|
||||
// Empty binding pattern of binding element, set breakpoint on binding element
|
||||
if (bindingPattern.parent.kind === SyntaxKind.BindingElement) {
|
||||
return textSpan(bindingPattern.parent);
|
||||
}
|
||||
|
||||
// Variable declaration is used as the span
|
||||
return textSpanFromVariableDeclaration(<VariableDeclaration>bindingPattern.parent);
|
||||
}
|
||||
|
||||
function spanInArrayLiteralOrObjectLiteralDestructuringPattern(node: DestructuringPattern): TextSpan {
|
||||
Debug.assert(node.kind !== SyntaxKind.ArrayBindingPattern && node.kind !== SyntaxKind.ObjectBindingPattern);
|
||||
const elements: NodeArray<Expression | ObjectLiteralElement> =
|
||||
node.kind === SyntaxKind.ArrayLiteralExpression ?
|
||||
(<ArrayLiteralExpression>node).elements :
|
||||
(<ObjectLiteralExpression>node).properties;
|
||||
|
||||
const firstBindingElement = forEach(elements,
|
||||
element => element.kind !== SyntaxKind.OmittedExpression ? element : undefined);
|
||||
|
||||
if (firstBindingElement) {
|
||||
return spanInNode(firstBindingElement);
|
||||
}
|
||||
|
||||
// Could be ArrayLiteral from destructuring assignment or
|
||||
// just nested element in another destructuring assignment
|
||||
// set breakpoint on assignment when parent is destructuring assignment
|
||||
// Otherwise set breakpoint for this element
|
||||
return textSpan(node.parent.kind === SyntaxKind.BinaryExpression ? node.parent : node);
|
||||
}
|
||||
|
||||
// Tokens:
|
||||
function spanInOpenBraceToken(node: Node): TextSpan {
|
||||
switch (node.parent.kind) {
|
||||
@@ -472,18 +607,52 @@ namespace ts.BreakpointResolver {
|
||||
}
|
||||
return undefined;
|
||||
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
// Breakpoint in last binding element or binding pattern if it contains no elements
|
||||
let bindingPattern = <BindingPattern>node.parent;
|
||||
return spanInNode(lastOrUndefined(bindingPattern.elements) || bindingPattern);
|
||||
|
||||
// Default to parent node
|
||||
default:
|
||||
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
|
||||
// Breakpoint in last binding element or binding pattern if it contains no elements
|
||||
let objectLiteral = <ObjectLiteralExpression>node.parent;
|
||||
return textSpan(lastOrUndefined(objectLiteral.properties) || objectLiteral);
|
||||
}
|
||||
return spanInNode(node.parent);
|
||||
}
|
||||
}
|
||||
|
||||
function spanInCloseBracketToken(node: Node): TextSpan {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
// Breakpoint in last binding element or binding pattern if it contains no elements
|
||||
let bindingPattern = <BindingPattern>node.parent;
|
||||
return textSpan(lastOrUndefined(bindingPattern.elements) || bindingPattern);
|
||||
|
||||
default:
|
||||
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
|
||||
// Breakpoint in last binding element or binding pattern if it contains no elements
|
||||
let arrayLiteral = <ArrayLiteralExpression>node.parent;
|
||||
return textSpan(lastOrUndefined(arrayLiteral.elements) || arrayLiteral);
|
||||
}
|
||||
|
||||
// Default to parent node
|
||||
return spanInNode(node.parent);
|
||||
}
|
||||
}
|
||||
|
||||
function spanInOpenParenToken(node: Node): TextSpan {
|
||||
if (node.parent.kind === SyntaxKind.DoStatement) {
|
||||
// Go to while keyword and do action instead
|
||||
if (node.parent.kind === SyntaxKind.DoStatement || // Go to while keyword and do action instead
|
||||
node.parent.kind === SyntaxKind.CallExpression ||
|
||||
node.parent.kind === SyntaxKind.NewExpression) {
|
||||
return spanInPreviousNode(node);
|
||||
}
|
||||
|
||||
if (node.parent.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
return spanInNextNode(node);
|
||||
}
|
||||
|
||||
// Default to parent node
|
||||
return spanInNode(node.parent);
|
||||
}
|
||||
@@ -502,6 +671,10 @@ namespace ts.BreakpointResolver {
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForOfStatement:
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
return spanInPreviousNode(node);
|
||||
|
||||
// Default to parent node
|
||||
@@ -512,7 +685,9 @@ namespace ts.BreakpointResolver {
|
||||
|
||||
function spanInColonToken(node: Node): TextSpan {
|
||||
// Is this : specifying return annotation of the function declaration
|
||||
if (isFunctionLike(node.parent) || node.parent.kind === SyntaxKind.PropertyAssignment) {
|
||||
if (isFunctionLike(node.parent) ||
|
||||
node.parent.kind === SyntaxKind.PropertyAssignment ||
|
||||
node.parent.kind === SyntaxKind.Parameter) {
|
||||
return spanInPreviousNode(node);
|
||||
}
|
||||
|
||||
@@ -521,7 +696,7 @@ namespace ts.BreakpointResolver {
|
||||
|
||||
function spanInGreaterThanOrLessThanToken(node: Node): TextSpan {
|
||||
if (node.parent.kind === SyntaxKind.TypeAssertionExpression) {
|
||||
return spanInNode((<TypeAssertion>node.parent).expression);
|
||||
return spanInNextNode(node);
|
||||
}
|
||||
|
||||
return spanInNode(node.parent);
|
||||
@@ -530,7 +705,17 @@ namespace ts.BreakpointResolver {
|
||||
function spanInWhileKeyword(node: Node): TextSpan {
|
||||
if (node.parent.kind === SyntaxKind.DoStatement) {
|
||||
// Set span on while expression
|
||||
return textSpan(node, findNextToken((<DoStatement>node.parent).expression, node.parent));
|
||||
return textSpanEndingAtNextToken(node, (<DoStatement>node.parent).expression);
|
||||
}
|
||||
|
||||
// Default to parent node
|
||||
return spanInNode(node.parent);
|
||||
}
|
||||
|
||||
function spanInOfKeyword(node: Node): TextSpan {
|
||||
if (node.parent.kind === SyntaxKind.ForOfStatement) {
|
||||
// set using next token
|
||||
return spanInNextNode(node);
|
||||
}
|
||||
|
||||
// Default to parent node
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace ts.formatting {
|
||||
|
||||
function findOutermostParent(position: number, expectedTokenKind: SyntaxKind, sourceFile: SourceFile): Node {
|
||||
let precedingToken = findPrecedingToken(position, sourceFile);
|
||||
|
||||
|
||||
// when it is claimed that trigger character was typed at given position
|
||||
// we verify that there is a token with a matching kind whose end is equal to position (because the character was just typed).
|
||||
// If this condition is not hold - then trigger character was typed in some other context,
|
||||
@@ -151,7 +151,7 @@ namespace ts.formatting {
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
// Returns true if node is a element in some list in parent
|
||||
// i.e. parent is class declaration with the list of members and node is one of members.
|
||||
function isListElement(parent: Node, node: Node): boolean {
|
||||
@@ -198,7 +198,7 @@ namespace ts.formatting {
|
||||
if (!errors.length) {
|
||||
return rangeHasNoErrors;
|
||||
}
|
||||
|
||||
|
||||
// pick only errors that fall in range
|
||||
let sorted = errors
|
||||
.filter(d => rangeOverlapsWithStartEnd(originalRange, d.start, d.start + d.length))
|
||||
@@ -341,6 +341,14 @@ namespace ts.formatting {
|
||||
processNode(enclosingNode, enclosingNode, startLine, undecoratedStartLine, initialIndentation, delta);
|
||||
}
|
||||
|
||||
if (!formattingScanner.isOnToken()) {
|
||||
let leadingTrivia = formattingScanner.getCurrentLeadingTrivia();
|
||||
if (leadingTrivia) {
|
||||
processTrivia(leadingTrivia, enclosingNode, enclosingNode, undefined);
|
||||
trimTrailingWhitespacesForRemainingRange();
|
||||
}
|
||||
}
|
||||
|
||||
formattingScanner.close();
|
||||
|
||||
return edits;
|
||||
@@ -360,7 +368,9 @@ namespace ts.formatting {
|
||||
range: TextRange,
|
||||
inheritedIndentation: number): number {
|
||||
|
||||
if (rangeOverlapsWithStartEnd(range, startPos, endPos)) {
|
||||
if (rangeOverlapsWithStartEnd(range, startPos, endPos) ||
|
||||
rangeContainsStartEnd(range, startPos, endPos) /* Not to miss zero-range nodes e.g. JsxText */) {
|
||||
|
||||
if (inheritedIndentation !== Constants.Unknown) {
|
||||
return inheritedIndentation;
|
||||
}
|
||||
@@ -826,9 +836,7 @@ namespace ts.formatting {
|
||||
}
|
||||
|
||||
// We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line
|
||||
trimTrailingWhitespaces =
|
||||
(rule.Operation.Action & (RuleAction.NewLine | RuleAction.Space)) &&
|
||||
rule.Flag !== RuleFlags.CanDeleteNewLines;
|
||||
trimTrailingWhitespaces = !(rule.Operation.Action & RuleAction.Delete) && rule.Flag !== RuleFlags.CanDeleteNewLines;
|
||||
}
|
||||
else {
|
||||
trimTrailingWhitespaces = true;
|
||||
@@ -927,17 +935,41 @@ namespace ts.formatting {
|
||||
continue;
|
||||
}
|
||||
|
||||
let pos = lineEndPosition;
|
||||
while (pos >= lineStartPosition && isWhiteSpace(sourceFile.text.charCodeAt(pos))) {
|
||||
pos--;
|
||||
}
|
||||
if (pos !== lineEndPosition) {
|
||||
Debug.assert(pos === lineStartPosition || !isWhiteSpace(sourceFile.text.charCodeAt(pos)));
|
||||
recordDelete(pos + 1, lineEndPosition - pos);
|
||||
let whitespaceStart = getTrailingWhitespaceStartPosition(lineStartPosition, lineEndPosition);
|
||||
if (whitespaceStart !== -1) {
|
||||
Debug.assert(whitespaceStart === lineStartPosition || !isWhiteSpace(sourceFile.text.charCodeAt(whitespaceStart - 1)));
|
||||
recordDelete(whitespaceStart, lineEndPosition + 1 - whitespaceStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param start The position of the first character in range
|
||||
* @param end The position of the last character in range
|
||||
*/
|
||||
function getTrailingWhitespaceStartPosition(start: number, end: number) {
|
||||
let pos = end;
|
||||
while (pos >= start && isWhiteSpace(sourceFile.text.charCodeAt(pos))) {
|
||||
pos--;
|
||||
}
|
||||
if (pos !== end) {
|
||||
return pos + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trimming will be done for lines after the previous range
|
||||
*/
|
||||
function trimTrailingWhitespacesForRemainingRange() {
|
||||
let startPosition = previousRange ? previousRange.end : originalRange.pos;
|
||||
|
||||
let startLine = sourceFile.getLineAndCharacterOfPosition(startPosition).line;
|
||||
let endLine = sourceFile.getLineAndCharacterOfPosition(originalRange.end).line;
|
||||
|
||||
trimTrailingWhitespacesForLines(startLine, endLine + 1, previousRange);
|
||||
}
|
||||
|
||||
function newTextChange(start: number, len: number, newText: string): TextChange {
|
||||
return { span: createTextSpan(start, len), newText }
|
||||
}
|
||||
|
||||
@@ -5,21 +5,22 @@
|
||||
namespace ts.formatting {
|
||||
const standardScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.Standard);
|
||||
const jsxScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.JSX);
|
||||
|
||||
|
||||
/**
|
||||
* Scanner that is currently used for formatting
|
||||
*/
|
||||
let scanner: Scanner;
|
||||
|
||||
|
||||
export interface FormattingScanner {
|
||||
advance(): void;
|
||||
isOnToken(): boolean;
|
||||
readTokenInfo(n: Node): TokenInfo;
|
||||
getCurrentLeadingTrivia(): TextRangeWithKind[];
|
||||
lastTrailingTriviaWasNewLine(): boolean;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
const enum ScanAction{
|
||||
const enum ScanAction {
|
||||
Scan,
|
||||
RescanGreaterThanToken,
|
||||
RescanSlashToken,
|
||||
@@ -37,19 +38,20 @@ namespace ts.formatting {
|
||||
let wasNewLine: boolean = true;
|
||||
let leadingTrivia: TextRangeWithKind[];
|
||||
let trailingTrivia: TextRangeWithKind[];
|
||||
|
||||
|
||||
let savedPos: number;
|
||||
let lastScanAction: ScanAction;
|
||||
let lastTokenInfo: TokenInfo;
|
||||
|
||||
return {
|
||||
advance: advance,
|
||||
readTokenInfo: readTokenInfo,
|
||||
isOnToken: isOnToken,
|
||||
advance,
|
||||
readTokenInfo,
|
||||
isOnToken,
|
||||
getCurrentLeadingTrivia: () => leadingTrivia,
|
||||
lastTrailingTriviaWasNewLine: () => wasNewLine,
|
||||
close: () => {
|
||||
Debug.assert(scanner !== undefined);
|
||||
|
||||
|
||||
lastTokenInfo = undefined;
|
||||
scanner.setText(undefined);
|
||||
scanner = undefined;
|
||||
@@ -58,7 +60,7 @@ namespace ts.formatting {
|
||||
|
||||
function advance(): void {
|
||||
Debug.assert(scanner !== undefined);
|
||||
|
||||
|
||||
lastTokenInfo = undefined;
|
||||
let isStarted = scanner.getStartPos() !== startPos;
|
||||
|
||||
@@ -81,7 +83,7 @@ namespace ts.formatting {
|
||||
|
||||
let t: SyntaxKind;
|
||||
let pos = scanner.getStartPos();
|
||||
|
||||
|
||||
// Read leading trivia and token
|
||||
while (pos < endPos) {
|
||||
let t = scanner.getToken();
|
||||
@@ -122,10 +124,10 @@ namespace ts.formatting {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function shouldRescanJsxIdentifier(node: Node): boolean {
|
||||
if (node.parent) {
|
||||
switch(node.parent.kind) {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.JsxAttribute:
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
case SyntaxKind.JsxClosingElement:
|
||||
@@ -133,7 +135,7 @@ namespace ts.formatting {
|
||||
return node.kind === SyntaxKind.Identifier;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -142,7 +144,7 @@ namespace ts.formatting {
|
||||
}
|
||||
|
||||
function shouldRescanTemplateToken(container: Node): boolean {
|
||||
return container.kind === SyntaxKind.TemplateMiddle ||
|
||||
return container.kind === SyntaxKind.TemplateMiddle ||
|
||||
container.kind === SyntaxKind.TemplateTail;
|
||||
}
|
||||
|
||||
@@ -152,11 +154,11 @@ namespace ts.formatting {
|
||||
|
||||
function readTokenInfo(n: Node): TokenInfo {
|
||||
Debug.assert(scanner !== undefined);
|
||||
|
||||
|
||||
if (!isOnToken()) {
|
||||
// scanner is not on the token (either advance was not called yet or scanner is already past the end position)
|
||||
return {
|
||||
leadingTrivia: leadingTrivia,
|
||||
leadingTrivia,
|
||||
trailingTrivia: undefined,
|
||||
token: undefined
|
||||
};
|
||||
@@ -164,7 +166,7 @@ namespace ts.formatting {
|
||||
|
||||
// normally scanner returns the smallest available token
|
||||
// check the kind of context node to determine if scanner should have more greedy behavior and consume more text.
|
||||
let expectedScanAction =
|
||||
let expectedScanAction =
|
||||
shouldRescanGreaterThanToken(n)
|
||||
? ScanAction.RescanGreaterThanToken
|
||||
: shouldRescanSlashToken(n)
|
||||
@@ -226,7 +228,7 @@ namespace ts.formatting {
|
||||
if (trailingTrivia) {
|
||||
trailingTrivia = undefined;
|
||||
}
|
||||
while(scanner.getStartPos() < endPos) {
|
||||
while (scanner.getStartPos() < endPos) {
|
||||
currentToken = scanner.scan();
|
||||
if (!isTrivia(currentToken)) {
|
||||
break;
|
||||
@@ -261,7 +263,7 @@ namespace ts.formatting {
|
||||
|
||||
function isOnToken(): boolean {
|
||||
Debug.assert(scanner !== undefined);
|
||||
|
||||
|
||||
let current = (lastTokenInfo && lastTokenInfo.token.kind) || scanner.getToken();
|
||||
let startPos = (lastTokenInfo && lastTokenInfo.token.pos) || scanner.getStartPos();
|
||||
return startPos < endPos && current !== SyntaxKind.EndOfFileToken && !isTrivia(current);
|
||||
|
||||
@@ -219,9 +219,11 @@ namespace ts.formatting {
|
||||
public SpaceBetweenAsyncAndFunctionKeyword: Rule;
|
||||
|
||||
// Template strings
|
||||
public SpaceBetweenTagAndTemplateString: Rule;
|
||||
public NoSpaceBetweenTagAndTemplateString: Rule;
|
||||
public NoSpaceAfterTemplateHeadAndMiddle: Rule;
|
||||
public SpaceAfterTemplateHeadAndMiddle: Rule;
|
||||
public NoSpaceBeforeTemplateMiddleAndTail: Rule;
|
||||
public SpaceBeforeTemplateMiddleAndTail: Rule;
|
||||
|
||||
constructor() {
|
||||
///
|
||||
@@ -376,9 +378,7 @@ namespace ts.formatting {
|
||||
this.SpaceBetweenAsyncAndFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
|
||||
// template string
|
||||
this.SpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// These rules are higher in priority than user-configurable rules.
|
||||
this.HighPriorityCommonRules = [
|
||||
@@ -406,7 +406,7 @@ namespace ts.formatting {
|
||||
this.SpaceBeforeBinaryKeywordOperator, this.SpaceAfterBinaryKeywordOperator,
|
||||
this.SpaceAfterVoidOperator,
|
||||
this.SpaceBetweenAsyncAndOpenParen, this.SpaceBetweenAsyncAndFunctionKeyword,
|
||||
this.SpaceBetweenTagAndTemplateString, this.NoSpaceAfterTemplateHeadAndMiddle, this.NoSpaceBeforeTemplateMiddleAndTail,
|
||||
this.NoSpaceBetweenTagAndTemplateString,
|
||||
|
||||
// TypeScript-specific rules
|
||||
this.NoSpaceAfterConstructor, this.NoSpaceAfterModuleImport,
|
||||
@@ -444,7 +444,7 @@ namespace ts.formatting {
|
||||
///
|
||||
|
||||
// Insert space after comma delimiter
|
||||
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
|
||||
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// Insert space before and after binary operators
|
||||
@@ -485,6 +485,12 @@ namespace ts.formatting {
|
||||
this.NoSpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// Insert space after opening and before closing template string braces
|
||||
this.NoSpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
this.SpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
|
||||
// Insert space after function keyword for anonymous functions
|
||||
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Delete));
|
||||
@@ -705,6 +711,10 @@ namespace ts.formatting {
|
||||
return context.currentTokenSpan.kind !== SyntaxKind.CommaToken;
|
||||
}
|
||||
|
||||
static IsNextTokenNotCloseBracket(context: FormattingContext): boolean {
|
||||
return context.nextTokenSpan.kind !== SyntaxKind.CloseBracketToken;
|
||||
}
|
||||
|
||||
static IsArrowFunctionContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind === SyntaxKind.ArrowFunction;
|
||||
}
|
||||
|
||||
@@ -71,15 +71,24 @@ namespace ts.formatting {
|
||||
rules.push(this.globalRules.NoSpaceBetweenParens);
|
||||
}
|
||||
|
||||
if ( options.InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets ) {
|
||||
rules.push( this.globalRules.SpaceAfterOpenBracket );
|
||||
rules.push( this.globalRules.SpaceBeforeCloseBracket );
|
||||
rules.push( this.globalRules.NoSpaceBetweenBrackets );
|
||||
if (options.InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets) {
|
||||
rules.push(this.globalRules.SpaceAfterOpenBracket);
|
||||
rules.push(this.globalRules.SpaceBeforeCloseBracket);
|
||||
rules.push(this.globalRules.NoSpaceBetweenBrackets);
|
||||
}
|
||||
else {
|
||||
rules.push( this.globalRules.NoSpaceAfterOpenBracket );
|
||||
rules.push( this.globalRules.NoSpaceBeforeCloseBracket );
|
||||
rules.push( this.globalRules.NoSpaceBetweenBrackets );
|
||||
rules.push(this.globalRules.NoSpaceAfterOpenBracket);
|
||||
rules.push(this.globalRules.NoSpaceBeforeCloseBracket);
|
||||
rules.push(this.globalRules.NoSpaceBetweenBrackets);
|
||||
}
|
||||
|
||||
if (options.InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) {
|
||||
rules.push(this.globalRules.SpaceAfterTemplateHeadAndMiddle);
|
||||
rules.push(this.globalRules.SpaceBeforeTemplateMiddleAndTail);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterTemplateHeadAndMiddle);
|
||||
rules.push(this.globalRules.NoSpaceBeforeTemplateMiddleAndTail);
|
||||
}
|
||||
|
||||
if (options.InsertSpaceAfterSemicolonInForStatements) {
|
||||
|
||||
@@ -450,8 +450,9 @@ namespace ts.formatting {
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
case SyntaxKind.JsxElement:
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
case SyntaxKind.JsxExpression:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
@@ -467,6 +468,7 @@ namespace ts.formatting {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function nodeWillIndentChild(parent: TextRangeWithKind, child: TextRangeWithKind, indentByDefault: boolean) {
|
||||
let childKind = child ? child.kind : SyntaxKind.Unknown;
|
||||
switch (parent.kind) {
|
||||
@@ -484,6 +486,8 @@ namespace ts.formatting {
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return childKind !== SyntaxKind.Block;
|
||||
case SyntaxKind.JsxElement:
|
||||
return childKind !== SyntaxKind.JsxClosingElement;
|
||||
}
|
||||
// No explicit rule for given nodes so the result will follow the default value argument
|
||||
return indentByDefault;
|
||||
|
||||
@@ -3,19 +3,19 @@ namespace ts.NavigateTo {
|
||||
type RawNavigateToItem = { name: string; fileName: string; matchKind: PatternMatchKind; isCaseSensitive: boolean; declaration: Declaration };
|
||||
|
||||
export function getNavigateToItems(program: Program, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number): NavigateToItem[] {
|
||||
let patternMatcher = createPatternMatcher(searchValue);
|
||||
const patternMatcher = createPatternMatcher(searchValue);
|
||||
let rawItems: RawNavigateToItem[] = [];
|
||||
|
||||
// This means "compare in a case insensitive manner."
|
||||
let baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
|
||||
const baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
|
||||
|
||||
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
let nameToDeclarations = sourceFile.getNamedDeclarations();
|
||||
for (let name in nameToDeclarations) {
|
||||
let declarations = getProperty(nameToDeclarations, name);
|
||||
const nameToDeclarations = sourceFile.getNamedDeclarations();
|
||||
for (const name in nameToDeclarations) {
|
||||
const declarations = getProperty(nameToDeclarations, name);
|
||||
if (declarations) {
|
||||
// First do a quick check to see if the name of the declaration matches the
|
||||
// last portion of the (possibly) dotted name they're searching for.
|
||||
@@ -25,11 +25,11 @@ namespace ts.NavigateTo {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let declaration of declarations) {
|
||||
for (const declaration of declarations) {
|
||||
// It was a match! If the pattern has dots in it, then also see if the
|
||||
// declaration container matches as well.
|
||||
if (patternMatcher.patternContainsDots) {
|
||||
let containers = getContainers(declaration);
|
||||
const containers = getContainers(declaration);
|
||||
if (!containers) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -41,8 +41,8 @@ namespace ts.NavigateTo {
|
||||
}
|
||||
}
|
||||
|
||||
let fileName = sourceFile.fileName;
|
||||
let matchKind = bestMatchKind(matches);
|
||||
const fileName = sourceFile.fileName;
|
||||
const matchKind = bestMatchKind(matches);
|
||||
rawItems.push({ name, fileName, matchKind, isCaseSensitive: allMatchesAreCaseSensitive(matches), declaration });
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ namespace ts.NavigateTo {
|
||||
rawItems = rawItems.slice(0, maxResultCount);
|
||||
}
|
||||
|
||||
let items = map(rawItems, createNavigateToItem);
|
||||
const items = map(rawItems, createNavigateToItem);
|
||||
|
||||
return items;
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace ts.NavigateTo {
|
||||
Debug.assert(matches.length > 0);
|
||||
|
||||
// This is a case sensitive match, only if all the submatches were case sensitive.
|
||||
for (let match of matches) {
|
||||
for (const match of matches) {
|
||||
if (!match.isCaseSensitive) {
|
||||
return false;
|
||||
}
|
||||
@@ -86,16 +86,16 @@ namespace ts.NavigateTo {
|
||||
|
||||
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]) {
|
||||
if (declaration && declaration.name) {
|
||||
let text = getTextOfIdentifierOrLiteral(declaration.name);
|
||||
const text = getTextOfIdentifierOrLiteral(declaration.name);
|
||||
if (text !== undefined) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
else if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion:*/ true);
|
||||
return tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
else {
|
||||
// Don't know how to add this.
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace ts.NavigateTo {
|
||||
//
|
||||
// [X.Y.Z]() { }
|
||||
function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean {
|
||||
let text = getTextOfIdentifierOrLiteral(expression);
|
||||
const text = getTextOfIdentifierOrLiteral(expression);
|
||||
if (text !== undefined) {
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(text);
|
||||
@@ -115,24 +115,24 @@ namespace ts.NavigateTo {
|
||||
}
|
||||
|
||||
if (expression.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
let propertyAccess = <PropertyAccessExpression>expression;
|
||||
const propertyAccess = <PropertyAccessExpression>expression;
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(propertyAccess.name.text);
|
||||
}
|
||||
|
||||
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion:*/ true);
|
||||
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getContainers(declaration: Declaration) {
|
||||
let containers: string[] = [];
|
||||
const containers: string[] = [];
|
||||
|
||||
// First, if we started with a computed property name, then add all but the last
|
||||
// portion into the container array.
|
||||
if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
if (!tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion:*/ false)) {
|
||||
if (!tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion*/ false)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -155,8 +155,8 @@ namespace ts.NavigateTo {
|
||||
Debug.assert(matches.length > 0);
|
||||
let bestMatchKind = PatternMatchKind.camelCase;
|
||||
|
||||
for (let match of matches) {
|
||||
let kind = match.kind;
|
||||
for (const match of matches) {
|
||||
const kind = match.kind;
|
||||
if (kind < bestMatchKind) {
|
||||
bestMatchKind = kind;
|
||||
}
|
||||
@@ -171,13 +171,13 @@ namespace ts.NavigateTo {
|
||||
// We first sort case insensitively. So "Aaa" will come before "bar".
|
||||
// Then we sort case sensitively, so "aaa" will come before "Aaa".
|
||||
return i1.matchKind - i2.matchKind ||
|
||||
i1.name.localeCompare(i2.name, undefined, baseSensitivity) ||
|
||||
i1.name.localeCompare(i2.name, undefined, baseSensitivity) ||
|
||||
i1.name.localeCompare(i2.name);
|
||||
}
|
||||
|
||||
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
|
||||
let declaration = rawItem.declaration;
|
||||
let container = <Declaration>getContainerNode(declaration);
|
||||
const declaration = rawItem.declaration;
|
||||
const container = <Declaration>getContainerNode(declaration);
|
||||
return {
|
||||
name: rawItem.name,
|
||||
kind: getNodeKind(declaration),
|
||||
|
||||
@@ -1,180 +1,179 @@
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export module OutliningElementsCollector {
|
||||
export function collectElements(sourceFile: SourceFile): OutliningSpan[] {
|
||||
let elements: OutliningSpan[] = [];
|
||||
let collapseText = "...";
|
||||
namespace ts.OutliningElementsCollector {
|
||||
export function collectElements(sourceFile: SourceFile): OutliningSpan[] {
|
||||
const elements: OutliningSpan[] = [];
|
||||
const collapseText = "...";
|
||||
|
||||
function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean) {
|
||||
if (hintSpanNode && startElement && endElement) {
|
||||
let span: OutliningSpan = {
|
||||
textSpan: createTextSpanFromBounds(startElement.pos, endElement.end),
|
||||
hintSpan: createTextSpanFromBounds(hintSpanNode.getStart(), hintSpanNode.end),
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse
|
||||
};
|
||||
elements.push(span);
|
||||
}
|
||||
function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean) {
|
||||
if (hintSpanNode && startElement && endElement) {
|
||||
const span: OutliningSpan = {
|
||||
textSpan: createTextSpanFromBounds(startElement.pos, endElement.end),
|
||||
hintSpan: createTextSpanFromBounds(hintSpanNode.getStart(), hintSpanNode.end),
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse
|
||||
};
|
||||
elements.push(span);
|
||||
}
|
||||
}
|
||||
|
||||
function addOutliningSpanComments(commentSpan: CommentRange, autoCollapse: boolean) {
|
||||
if (commentSpan) {
|
||||
let span: OutliningSpan = {
|
||||
textSpan: createTextSpanFromBounds(commentSpan.pos, commentSpan.end),
|
||||
hintSpan: createTextSpanFromBounds(commentSpan.pos, commentSpan.end),
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse
|
||||
};
|
||||
elements.push(span);
|
||||
}
|
||||
function addOutliningSpanComments(commentSpan: CommentRange, autoCollapse: boolean) {
|
||||
if (commentSpan) {
|
||||
const span: OutliningSpan = {
|
||||
textSpan: createTextSpanFromBounds(commentSpan.pos, commentSpan.end),
|
||||
hintSpan: createTextSpanFromBounds(commentSpan.pos, commentSpan.end),
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse
|
||||
};
|
||||
elements.push(span);
|
||||
}
|
||||
}
|
||||
|
||||
function addOutliningForLeadingCommentsForNode(n: Node) {
|
||||
let comments = ts.getLeadingCommentRangesOfNode(n, sourceFile);
|
||||
function addOutliningForLeadingCommentsForNode(n: Node) {
|
||||
const comments = ts.getLeadingCommentRangesOfNode(n, sourceFile);
|
||||
|
||||
if (comments) {
|
||||
let firstSingleLineCommentStart = -1;
|
||||
let lastSingleLineCommentEnd = -1;
|
||||
let isFirstSingleLineComment = true;
|
||||
let singleLineCommentCount = 0;
|
||||
if (comments) {
|
||||
let firstSingleLineCommentStart = -1;
|
||||
let lastSingleLineCommentEnd = -1;
|
||||
let isFirstSingleLineComment = true;
|
||||
let singleLineCommentCount = 0;
|
||||
|
||||
for (let currentComment of comments) {
|
||||
for (const currentComment of comments) {
|
||||
|
||||
// For single line comments, combine consecutive ones (2 or more) into
|
||||
// a single span from the start of the first till the end of the last
|
||||
if (currentComment.kind === SyntaxKind.SingleLineCommentTrivia) {
|
||||
if (isFirstSingleLineComment) {
|
||||
firstSingleLineCommentStart = currentComment.pos;
|
||||
}
|
||||
isFirstSingleLineComment = false;
|
||||
lastSingleLineCommentEnd = currentComment.end;
|
||||
singleLineCommentCount++;
|
||||
// For single line comments, combine consecutive ones (2 or more) into
|
||||
// a single span from the start of the first till the end of the last
|
||||
if (currentComment.kind === SyntaxKind.SingleLineCommentTrivia) {
|
||||
if (isFirstSingleLineComment) {
|
||||
firstSingleLineCommentStart = currentComment.pos;
|
||||
}
|
||||
else if (currentComment.kind === SyntaxKind.MultiLineCommentTrivia) {
|
||||
combineAndAddMultipleSingleLineComments(singleLineCommentCount, firstSingleLineCommentStart, lastSingleLineCommentEnd);
|
||||
addOutliningSpanComments(currentComment, /*autoCollapse*/ false);
|
||||
isFirstSingleLineComment = false;
|
||||
lastSingleLineCommentEnd = currentComment.end;
|
||||
singleLineCommentCount++;
|
||||
}
|
||||
else if (currentComment.kind === SyntaxKind.MultiLineCommentTrivia) {
|
||||
combineAndAddMultipleSingleLineComments(singleLineCommentCount, firstSingleLineCommentStart, lastSingleLineCommentEnd);
|
||||
addOutliningSpanComments(currentComment, /*autoCollapse*/ false);
|
||||
|
||||
singleLineCommentCount = 0;
|
||||
lastSingleLineCommentEnd = -1;
|
||||
isFirstSingleLineComment = true;
|
||||
singleLineCommentCount = 0;
|
||||
lastSingleLineCommentEnd = -1;
|
||||
isFirstSingleLineComment = true;
|
||||
}
|
||||
}
|
||||
|
||||
combineAndAddMultipleSingleLineComments(singleLineCommentCount, firstSingleLineCommentStart, lastSingleLineCommentEnd);
|
||||
}
|
||||
}
|
||||
|
||||
function combineAndAddMultipleSingleLineComments(count: number, start: number, end: number) {
|
||||
|
||||
// Only outline spans of two or more consecutive single line comments
|
||||
if (count > 1) {
|
||||
const multipleSingleLineComments = {
|
||||
pos: start,
|
||||
end: end,
|
||||
kind: SyntaxKind.SingleLineCommentTrivia
|
||||
};
|
||||
|
||||
addOutliningSpanComments(multipleSingleLineComments, /*autoCollapse*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
function autoCollapse(node: Node) {
|
||||
return isFunctionBlock(node) && node.parent.kind !== SyntaxKind.ArrowFunction;
|
||||
}
|
||||
|
||||
let depth = 0;
|
||||
const maxDepth = 20;
|
||||
function walk(n: Node): void {
|
||||
if (depth > maxDepth) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDeclaration(n)) {
|
||||
addOutliningForLeadingCommentsForNode(n);
|
||||
}
|
||||
|
||||
switch (n.kind) {
|
||||
case SyntaxKind.Block:
|
||||
if (!isFunctionBlock(n)) {
|
||||
const parent = n.parent;
|
||||
const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
|
||||
// Check if the block is standalone, or 'attached' to some parent statement.
|
||||
// If the latter, we want to collaps the block, but consider its hint span
|
||||
// to be the entire span of the parent.
|
||||
if (parent.kind === SyntaxKind.DoStatement ||
|
||||
parent.kind === SyntaxKind.ForInStatement ||
|
||||
parent.kind === SyntaxKind.ForOfStatement ||
|
||||
parent.kind === SyntaxKind.ForStatement ||
|
||||
parent.kind === SyntaxKind.IfStatement ||
|
||||
parent.kind === SyntaxKind.WhileStatement ||
|
||||
parent.kind === SyntaxKind.WithStatement ||
|
||||
parent.kind === SyntaxKind.CatchClause) {
|
||||
|
||||
addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
combineAndAddMultipleSingleLineComments(singleLineCommentCount, firstSingleLineCommentStart, lastSingleLineCommentEnd);
|
||||
}
|
||||
}
|
||||
|
||||
function combineAndAddMultipleSingleLineComments(count: number, start: number, end: number) {
|
||||
// Only outline spans of two or more consecutive single line comments
|
||||
if (count > 1) {
|
||||
let multipleSingleLineComments = {
|
||||
pos: start,
|
||||
end: end,
|
||||
kind: SyntaxKind.SingleLineCommentTrivia
|
||||
}
|
||||
|
||||
addOutliningSpanComments(multipleSingleLineComments, /*autoCollapse*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
function autoCollapse(node: Node) {
|
||||
return isFunctionBlock(node) && node.parent.kind !== SyntaxKind.ArrowFunction;
|
||||
}
|
||||
|
||||
let depth = 0;
|
||||
let maxDepth = 20;
|
||||
function walk(n: Node): void {
|
||||
if (depth > maxDepth) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDeclaration(n)) {
|
||||
addOutliningForLeadingCommentsForNode(n);
|
||||
}
|
||||
|
||||
switch (n.kind) {
|
||||
case SyntaxKind.Block:
|
||||
if (!isFunctionBlock(n)) {
|
||||
let parent = n.parent;
|
||||
let openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
let closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
|
||||
// Check if the block is standalone, or 'attached' to some parent statement.
|
||||
// If the latter, we want to collaps the block, but consider its hint span
|
||||
// to be the entire span of the parent.
|
||||
if (parent.kind === SyntaxKind.DoStatement ||
|
||||
parent.kind === SyntaxKind.ForInStatement ||
|
||||
parent.kind === SyntaxKind.ForOfStatement ||
|
||||
parent.kind === SyntaxKind.ForStatement ||
|
||||
parent.kind === SyntaxKind.IfStatement ||
|
||||
parent.kind === SyntaxKind.WhileStatement ||
|
||||
parent.kind === SyntaxKind.WithStatement ||
|
||||
parent.kind === SyntaxKind.CatchClause) {
|
||||
|
||||
if (parent.kind === SyntaxKind.TryStatement) {
|
||||
// Could be the try-block, or the finally-block.
|
||||
const tryStatement = <TryStatement>parent;
|
||||
if (tryStatement.tryBlock === n) {
|
||||
addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent.kind === SyntaxKind.TryStatement) {
|
||||
// Could be the try-block, or the finally-block.
|
||||
let tryStatement = <TryStatement>parent;
|
||||
if (tryStatement.tryBlock === n) {
|
||||
addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n));
|
||||
else if (tryStatement.finallyBlock === n) {
|
||||
const finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile);
|
||||
if (finallyKeyword) {
|
||||
addOutliningSpan(finallyKeyword, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
else if (tryStatement.finallyBlock === n) {
|
||||
let finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile);
|
||||
if (finallyKeyword) {
|
||||
addOutliningSpan(finallyKeyword, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fall through.
|
||||
}
|
||||
|
||||
// Block was a standalone block. In this case we want to only collapse
|
||||
// the span of the block, independent of any parent span.
|
||||
let span = createTextSpanFromBounds(n.getStart(), n.end);
|
||||
elements.push({
|
||||
textSpan: span,
|
||||
hintSpan: span,
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse(n)
|
||||
});
|
||||
break;
|
||||
// fall through.
|
||||
}
|
||||
// Fallthrough.
|
||||
|
||||
case SyntaxKind.ModuleBlock: {
|
||||
let openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
let closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
addOutliningSpan(n.parent, openBrace, closeBrace, autoCollapse(n));
|
||||
// Block was a standalone block. In this case we want to only collapse
|
||||
// the span of the block, independent of any parent span.
|
||||
const span = createTextSpanFromBounds(n.getStart(), n.end);
|
||||
elements.push({
|
||||
textSpan: span,
|
||||
hintSpan: span,
|
||||
bannerText: collapseText,
|
||||
autoCollapse: autoCollapse(n)
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.CaseBlock: {
|
||||
let openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
let closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
let openBracket = findChildOfKind(n, SyntaxKind.OpenBracketToken, sourceFile);
|
||||
let closeBracket = findChildOfKind(n, SyntaxKind.CloseBracketToken, sourceFile);
|
||||
addOutliningSpan(n, openBracket, closeBracket, autoCollapse(n));
|
||||
break;
|
||||
// Fallthrough.
|
||||
|
||||
case SyntaxKind.ModuleBlock: {
|
||||
const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
addOutliningSpan(n.parent, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
depth++;
|
||||
forEachChild(n, walk);
|
||||
depth--;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.CaseBlock: {
|
||||
const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
const openBracket = findChildOfKind(n, SyntaxKind.OpenBracketToken, sourceFile);
|
||||
const closeBracket = findChildOfKind(n, SyntaxKind.CloseBracketToken, sourceFile);
|
||||
addOutliningSpan(n, openBracket, closeBracket, autoCollapse(n));
|
||||
break;
|
||||
}
|
||||
|
||||
walk(sourceFile);
|
||||
return elements;
|
||||
depth++;
|
||||
forEachChild(n, walk);
|
||||
depth--;
|
||||
}
|
||||
|
||||
walk(sourceFile);
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@ namespace ts {
|
||||
camelCase
|
||||
}
|
||||
|
||||
// Information about a match made by the pattern matcher between a candidate and the
|
||||
// Information about a match made by the pattern matcher between a candidate and the
|
||||
// search pattern.
|
||||
export interface PatternMatch {
|
||||
// What kind of match this was. Exact matches are better than prefix matches which are
|
||||
// What kind of match this was. Exact matches are better than prefix matches which are
|
||||
// better than substring matches which are better than CamelCase matches.
|
||||
kind: PatternMatchKind;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ts {
|
||||
// it was a better match.
|
||||
camelCaseWeight?: number;
|
||||
|
||||
// If this was a match where all constituent parts of the candidate and search pattern
|
||||
// If this was a match where all constituent parts of the candidate and search pattern
|
||||
// matched case sensitively or case insensitively. Case sensitive matches of the kind
|
||||
// are better matches than insensitive matches.
|
||||
isCaseSensitive: boolean;
|
||||
@@ -35,7 +35,7 @@ namespace ts {
|
||||
// once you no longer need it.
|
||||
export interface PatternMatcher {
|
||||
// Used to match a candidate against the last segment of a possibly dotted pattern. This
|
||||
// is useful as a quick check to prevent having to compute a container before calling
|
||||
// is useful as a quick check to prevent having to compute a container before calling
|
||||
// "getMatches".
|
||||
//
|
||||
// For example, if the search pattern is "ts.c.SK" and the candidate is "SyntaxKind", then
|
||||
@@ -55,8 +55,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// First we break up the pattern given by dots. Each portion of the pattern between the
|
||||
// dots is a 'Segment'. The 'Segment' contains information about the entire section of
|
||||
// text between the dots, as well as information about any individual 'Words' that we
|
||||
// dots is a 'Segment'. The 'Segment' contains information about the entire section of
|
||||
// text between the dots, as well as information about any individual 'Words' that we
|
||||
// can break the segment into. A 'Word' is simply a contiguous sequence of characters
|
||||
// that can appear in a typescript identifier. So "GetKeyword" would be one word, while
|
||||
// "Get Keyword" would be two words. Once we have the individual 'words', we break those
|
||||
@@ -64,20 +64,20 @@ namespace ts {
|
||||
// word, it make character spans corresponding to "U", "I" and "Element". These spans
|
||||
// are then used when doing camel cased matches against candidate patterns.
|
||||
interface Segment {
|
||||
// Information about the entire piece of text between the dots. For example, if the
|
||||
// text between the dots is 'GetKeyword', then TotalTextChunk.Text will be 'GetKeyword' and
|
||||
// Information about the entire piece of text between the dots. For example, if the
|
||||
// text between the dots is 'GetKeyword', then TotalTextChunk.Text will be 'GetKeyword' and
|
||||
// TotalTextChunk.CharacterSpans will correspond to 'Get', 'Keyword'.
|
||||
totalTextChunk: TextChunk;
|
||||
|
||||
// Information about the subwords compromising the total word. For example, if the
|
||||
// text between the dots is 'GetFoo KeywordBar', then the subwords will be 'GetFoo'
|
||||
// and 'KeywordBar'. Those individual words will have CharacterSpans of ('Get' and
|
||||
// 'Foo') and('Keyword' and 'Bar') respectively.
|
||||
// Information about the subwords compromising the total word. For example, if the
|
||||
// text between the dots is 'GetFoo KeywordBar', then the subwords will be 'GetFoo'
|
||||
// and 'KeywordBar'. Those individual words will have CharacterSpans of ('Get' and
|
||||
// 'Foo') and('Keyword' and 'Bar') respectively.
|
||||
subWordTextChunks: TextChunk[];
|
||||
}
|
||||
|
||||
// Information about a chunk of text from the pattern. The chunk is a piece of text, with
|
||||
// cached information about the character spans within in. Character spans are used for
|
||||
// Information about a chunk of text from the pattern. The chunk is a piece of text, with
|
||||
// cached information about the character spans within in. Character spans are used for
|
||||
// camel case matching.
|
||||
interface TextChunk {
|
||||
// The text of the chunk. This should be a contiguous sequence of character that could
|
||||
@@ -92,9 +92,9 @@ namespace ts {
|
||||
// for something entirely lowercase or not.
|
||||
isLowerCase: boolean;
|
||||
|
||||
// The spans in this text chunk that we think are of interest and should be matched
|
||||
// The spans in this text chunk that we think are of interest and should be matched
|
||||
// independently. For example, if the chunk is for "UIElement" the the spans of interest
|
||||
// correspond to "U", "I" and "Element". If "UIElement" isn't found as an exaxt, prefix.
|
||||
// correspond to "U", "I" and "Element". If "UIElement" isn't found as an exact, prefix.
|
||||
// or substring match, then the character spans will be used to attempt a camel case match.
|
||||
characterSpans: TextSpan[];
|
||||
}
|
||||
@@ -110,20 +110,19 @@ namespace ts {
|
||||
|
||||
export function createPatternMatcher(pattern: string): PatternMatcher {
|
||||
// We'll often see the same candidate string many times when searching (For example, when
|
||||
// we see the name of a module that is used everywhere, or the name of an overload). As
|
||||
// such, we cache the information we compute about the candidate for the life of this
|
||||
// we see the name of a module that is used everywhere, or the name of an overload). As
|
||||
// such, we cache the information we compute about the candidate for the life of this
|
||||
// pattern matcher so we don't have to compute it multiple times.
|
||||
let stringToWordSpans: Map<TextSpan[]> = {};
|
||||
const stringToWordSpans: Map<TextSpan[]> = {};
|
||||
|
||||
pattern = pattern.trim();
|
||||
|
||||
let fullPatternSegment = createSegment(pattern);
|
||||
let dotSeparatedSegments = pattern.split(".").map(p => createSegment(p.trim()));
|
||||
let invalidPattern = dotSeparatedSegments.length === 0 || forEach(dotSeparatedSegments, segmentIsInvalid);
|
||||
const dotSeparatedSegments = pattern.split(".").map(p => createSegment(p.trim()));
|
||||
const invalidPattern = dotSeparatedSegments.length === 0 || forEach(dotSeparatedSegments, segmentIsInvalid);
|
||||
|
||||
return {
|
||||
getMatches,
|
||||
getMatchesForLastSegmentOfPattern,
|
||||
getMatchesForLastSegmentOfPattern,
|
||||
patternContainsDots: dotSeparatedSegments.length > 1
|
||||
};
|
||||
|
||||
@@ -131,7 +130,7 @@ namespace ts {
|
||||
function skipMatch(candidate: string) {
|
||||
return invalidPattern || !candidate;
|
||||
}
|
||||
|
||||
|
||||
function getMatchesForLastSegmentOfPattern(candidate: string): PatternMatch[] {
|
||||
if (skipMatch(candidate)) {
|
||||
return undefined;
|
||||
@@ -148,7 +147,7 @@ namespace ts {
|
||||
// First, check that the last part of the dot separated pattern matches the name of the
|
||||
// candidate. If not, then there's no point in proceeding and doing the more
|
||||
// expensive work.
|
||||
let candidateMatch = matchSegment(candidate, lastOrUndefined(dotSeparatedSegments));
|
||||
const candidateMatch = matchSegment(candidate, lastOrUndefined(dotSeparatedSegments));
|
||||
if (!candidateMatch) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -165,16 +164,16 @@ namespace ts {
|
||||
|
||||
// So far so good. Now break up the container for the candidate and check if all
|
||||
// the dotted parts match up correctly.
|
||||
let totalMatch = candidateMatch;
|
||||
const totalMatch = candidateMatch;
|
||||
|
||||
for (let i = dotSeparatedSegments.length - 2, j = candidateContainers.length - 1;
|
||||
i >= 0;
|
||||
i--, j--) {
|
||||
i -= 1, j -= 1) {
|
||||
|
||||
let segment = dotSeparatedSegments[i];
|
||||
let containerName = candidateContainers[j];
|
||||
const segment = dotSeparatedSegments[i];
|
||||
const containerName = candidateContainers[j];
|
||||
|
||||
let containerMatch = matchSegment(containerName, segment);
|
||||
const containerMatch = matchSegment(containerName, segment);
|
||||
if (!containerMatch) {
|
||||
// This container didn't match the pattern piece. So there's no match at all.
|
||||
return undefined;
|
||||
@@ -197,7 +196,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function matchTextChunk(candidate: string, chunk: TextChunk, punctuationStripped: boolean): PatternMatch {
|
||||
let index = indexOfIgnoringCase(candidate, chunk.textLowerCase);
|
||||
const index = indexOfIgnoringCase(candidate, chunk.textLowerCase);
|
||||
if (index === 0) {
|
||||
if (chunk.text.length === candidate.length) {
|
||||
// a) Check if the part matches the candidate entirely, in an case insensitive or
|
||||
@@ -211,18 +210,18 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
let isLowercase = chunk.isLowerCase;
|
||||
const isLowercase = chunk.isLowerCase;
|
||||
if (isLowercase) {
|
||||
if (index > 0) {
|
||||
// c) If the part is entirely lowercase, then check if it is contained anywhere in the
|
||||
// candidate in a case insensitive manner. If so, return that there was a substring
|
||||
// match.
|
||||
// match.
|
||||
//
|
||||
// Note: We only have a substring match if the lowercase part is prefix match of some
|
||||
// word part. That way we don't match something like 'Class' when the user types 'a'.
|
||||
// But we would match 'FooAttribute' (since 'Attribute' starts with 'a').
|
||||
let wordSpans = getWordSpans(candidate);
|
||||
for (let span of wordSpans) {
|
||||
const wordSpans = getWordSpans(candidate);
|
||||
for (const span of wordSpans) {
|
||||
if (partStartsWith(candidate, span, chunk.text, /*ignoreCase:*/ true)) {
|
||||
return createPatternMatch(PatternMatchKind.substring, punctuationStripped,
|
||||
/*isCaseSensitive:*/ partStartsWith(candidate, span, chunk.text, /*ignoreCase:*/ false));
|
||||
@@ -242,7 +241,7 @@ namespace ts {
|
||||
if (!isLowercase) {
|
||||
// e) If the part was not entirely lowercase, then attempt a camel cased match as well.
|
||||
if (chunk.characterSpans.length > 0) {
|
||||
let candidateParts = getWordSpans(candidate);
|
||||
const candidateParts = getWordSpans(candidate);
|
||||
let camelCaseWeight = tryCamelCaseMatch(candidate, candidateParts, chunk, /*ignoreCase:*/ false);
|
||||
if (camelCaseWeight !== undefined) {
|
||||
return createPatternMatch(PatternMatchKind.camelCase, punctuationStripped, /*isCaseSensitive:*/ true, /*camelCaseWeight:*/ camelCaseWeight);
|
||||
@@ -259,8 +258,8 @@ namespace ts {
|
||||
// f) Is the pattern a substring of the candidate starting on one of the candidate's word boundaries?
|
||||
|
||||
// We could check every character boundary start of the candidate for the pattern. However, that's
|
||||
// an m * n operation in the wost case. Instead, find the first instance of the pattern
|
||||
// substring, and see if it starts on a capital letter. It seems unlikely that the user will try to
|
||||
// an m * n operation in the wost case. Instead, find the first instance of the pattern
|
||||
// substring, and see if it starts on a capital letter. It seems unlikely that the user will try to
|
||||
// filter the list based on a substring that starts on a capital letter and also with a lowercase one.
|
||||
// (Pattern: fogbar, Candidate: quuxfogbarFogBar).
|
||||
if (chunk.text.length < candidate.length) {
|
||||
@@ -275,7 +274,7 @@ namespace ts {
|
||||
|
||||
function containsSpaceOrAsterisk(text: string): boolean {
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
let ch = text.charCodeAt(i);
|
||||
const ch = text.charCodeAt(i);
|
||||
if (ch === CharacterCodes.space || ch === CharacterCodes.asterisk) {
|
||||
return true;
|
||||
}
|
||||
@@ -293,7 +292,7 @@ namespace ts {
|
||||
// Note: if the segment contains a space or an asterisk then we must assume that it's a
|
||||
// multi-word segment.
|
||||
if (!containsSpaceOrAsterisk(segment.totalTextChunk.text)) {
|
||||
let match = matchTextChunk(candidate, segment.totalTextChunk, /*punctuationStripped:*/ false);
|
||||
const match = matchTextChunk(candidate, segment.totalTextChunk, /*punctuationStripped:*/ false);
|
||||
if (match) {
|
||||
return [match];
|
||||
}
|
||||
@@ -317,7 +316,7 @@ namespace ts {
|
||||
//
|
||||
// c) If the word is entirely lowercase, then check if it is contained anywhere in the
|
||||
// candidate in a case insensitive manner. If so, return that there was a substring
|
||||
// match.
|
||||
// match.
|
||||
//
|
||||
// Note: We only have a substring match if the lowercase part is prefix match of
|
||||
// some word part. That way we don't match something like 'Class' when the user
|
||||
@@ -331,17 +330,17 @@ namespace ts {
|
||||
// e) If the word was not entirely lowercase, then attempt a camel cased match as
|
||||
// well.
|
||||
//
|
||||
// f) The word is all lower case. Is it a case insensitive substring of the candidate starting
|
||||
// f) The word is all lower case. Is it a case insensitive substring of the candidate starting
|
||||
// on a part boundary of the candidate?
|
||||
//
|
||||
// Only if all words have some sort of match is the pattern considered matched.
|
||||
|
||||
let subWordTextChunks = segment.subWordTextChunks;
|
||||
const subWordTextChunks = segment.subWordTextChunks;
|
||||
let matches: PatternMatch[] = undefined;
|
||||
|
||||
for (let subWordTextChunk of subWordTextChunks) {
|
||||
for (const subWordTextChunk of subWordTextChunks) {
|
||||
// Try to match the candidate with this word
|
||||
let result = matchTextChunk(candidate, subWordTextChunk, /*punctuationStripped:*/ true);
|
||||
const result = matchTextChunk(candidate, subWordTextChunk, /*punctuationStripped:*/ true);
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -354,18 +353,18 @@ namespace ts {
|
||||
}
|
||||
|
||||
function partStartsWith(candidate: string, candidateSpan: TextSpan, pattern: string, ignoreCase: boolean, patternSpan?: TextSpan): boolean {
|
||||
let patternPartStart = patternSpan ? patternSpan.start : 0;
|
||||
let patternPartLength = patternSpan ? patternSpan.length : pattern.length;
|
||||
const patternPartStart = patternSpan ? patternSpan.start : 0;
|
||||
const patternPartLength = patternSpan ? patternSpan.length : pattern.length;
|
||||
|
||||
if (patternPartLength > candidateSpan.length) {
|
||||
// Pattern part is longer than the candidate part. There can never be a match.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (ignoreCase) {
|
||||
for (let i = 0; i < patternPartLength; i++) {
|
||||
let ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
let ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
const ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
const ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
if (toLowerCase(ch1) !== toLowerCase(ch2)) {
|
||||
return false;
|
||||
}
|
||||
@@ -373,8 +372,8 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < patternPartLength; i++) {
|
||||
let ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
let ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
const ch1 = pattern.charCodeAt(patternPartStart + i);
|
||||
const ch2 = candidate.charCodeAt(candidateSpan.start + i);
|
||||
if (ch1 !== ch2) {
|
||||
return false;
|
||||
}
|
||||
@@ -385,12 +384,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function tryCamelCaseMatch(candidate: string, candidateParts: TextSpan[], chunk: TextChunk, ignoreCase: boolean): number {
|
||||
let chunkCharacterSpans = chunk.characterSpans;
|
||||
const chunkCharacterSpans = chunk.characterSpans;
|
||||
|
||||
// Note: we may have more pattern parts than candidate parts. This is because multiple
|
||||
// pattern parts may match a candidate part. For example "SiUI" against "SimpleUI".
|
||||
// We'll have 3 pattern parts Si/U/I against two candidate parts Simple/UI. However, U
|
||||
// and I will both match in UI.
|
||||
// and I will both match in UI.
|
||||
|
||||
let currentCandidate = 0;
|
||||
let currentChunkSpan = 0;
|
||||
@@ -426,14 +425,14 @@ namespace ts {
|
||||
// Consider the case of matching SiUI against SimpleUIElement. The candidate parts
|
||||
// will be Simple/UI/Element, and the pattern parts will be Si/U/I. We'll match 'Si'
|
||||
// against 'Simple' first. Then we'll match 'U' against 'UI'. However, we want to
|
||||
// still keep matching pattern parts against that candidate part.
|
||||
// still keep matching pattern parts against that candidate part.
|
||||
for (; currentChunkSpan < chunkCharacterSpans.length; currentChunkSpan++) {
|
||||
let chunkCharacterSpan = chunkCharacterSpans[currentChunkSpan];
|
||||
const chunkCharacterSpan = chunkCharacterSpans[currentChunkSpan];
|
||||
|
||||
if (gotOneMatchThisCandidate) {
|
||||
// We've already gotten one pattern part match in this candidate. We will
|
||||
// only continue trying to consumer pattern parts if the last part and this
|
||||
// part are both upper case.
|
||||
// part are both upper case.
|
||||
if (!isUpperCaseLetter(chunk.text.charCodeAt(chunkCharacterSpans[currentChunkSpan - 1].start)) ||
|
||||
!isUpperCaseLetter(chunk.text.charCodeAt(chunkCharacterSpans[currentChunkSpan].start))) {
|
||||
break;
|
||||
@@ -470,55 +469,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to compare two matches to determine which is better. Matches are first
|
||||
// ordered by kind (so all prefix matches always beat all substring matches). Then, if the
|
||||
// match is a camel case match, the relative weights of the match are used to determine
|
||||
// which is better (with a greater weight being better). Then if the match is of the same
|
||||
// type, then a case sensitive match is considered better than an insensitive one.
|
||||
function patternMatchCompareTo(match1: PatternMatch, match2: PatternMatch): number {
|
||||
return compareType(match1, match2) ||
|
||||
compareCamelCase(match1, match2) ||
|
||||
compareCase(match1, match2) ||
|
||||
comparePunctuation(match1, match2);
|
||||
}
|
||||
|
||||
function comparePunctuation(result1: PatternMatch, result2: PatternMatch) {
|
||||
// Consider a match to be better if it was successful without stripping punctuation
|
||||
// versus a match that had to strip punctuation to succeed.
|
||||
if (result1.punctuationStripped !== result2.punctuationStripped) {
|
||||
return result1.punctuationStripped ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareCase(result1: PatternMatch, result2: PatternMatch) {
|
||||
if (result1.isCaseSensitive !== result2.isCaseSensitive) {
|
||||
return result1.isCaseSensitive ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareType(result1: PatternMatch, result2: PatternMatch) {
|
||||
return result1.kind - result2.kind;
|
||||
}
|
||||
|
||||
function compareCamelCase(result1: PatternMatch, result2: PatternMatch) {
|
||||
if (result1.kind === PatternMatchKind.camelCase && result2.kind === PatternMatchKind.camelCase) {
|
||||
// Swap the values here. If result1 has a higher weight, then we want it to come
|
||||
// first.
|
||||
return result2.camelCaseWeight - result1.camelCaseWeight;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function createSegment(text: string): Segment {
|
||||
return {
|
||||
totalTextChunk: createTextChunk(text),
|
||||
subWordTextChunks: breakPatternIntoTextChunks(text)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// A segment is considered invalid if we couldn't find any words in it.
|
||||
@@ -536,9 +491,9 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// non-allocating manner.
|
||||
let str = String.fromCharCode(ch);
|
||||
const str = String.fromCharCode(ch);
|
||||
return str === str.toUpperCase();
|
||||
}
|
||||
|
||||
@@ -553,22 +508,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// TODO: find a way to determine this for any unicode characters in a
|
||||
// non-allocating manner.
|
||||
let str = String.fromCharCode(ch);
|
||||
const str = String.fromCharCode(ch);
|
||||
return str === str.toLowerCase();
|
||||
}
|
||||
|
||||
function containsUpperCaseLetter(string: string): boolean {
|
||||
for (let i = 0, n = string.length; i < n; i++) {
|
||||
if (isUpperCaseLetter(string.charCodeAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function startsWith(string: string, search: string) {
|
||||
for (let i = 0, n = search.length; i < n; i++) {
|
||||
if (string.charCodeAt(i) !== search.charCodeAt(i)) {
|
||||
@@ -593,8 +538,8 @@ namespace ts {
|
||||
// Assumes 'value' is already lowercase.
|
||||
function startsWithIgnoringCase(string: string, value: string, start: number): boolean {
|
||||
for (let i = 0, n = value.length; i < n; i++) {
|
||||
let ch1 = toLowerCase(string.charCodeAt(i + start));
|
||||
let ch2 = value.charCodeAt(i);
|
||||
const ch1 = toLowerCase(string.charCodeAt(i + start));
|
||||
const ch2 = value.charCodeAt(i);
|
||||
|
||||
if (ch1 !== ch2) {
|
||||
return false;
|
||||
@@ -614,7 +559,7 @@ namespace ts {
|
||||
return ch;
|
||||
}
|
||||
|
||||
// TODO: find a way to compute this for any unicode characters in a
|
||||
// TODO: find a way to compute this for any unicode characters in a
|
||||
// non-allocating manner.
|
||||
return String.fromCharCode(ch).toLowerCase().charCodeAt(0);
|
||||
}
|
||||
@@ -629,16 +574,17 @@ namespace ts {
|
||||
}
|
||||
|
||||
function breakPatternIntoTextChunks(pattern: string): TextChunk[] {
|
||||
let result: TextChunk[] = [];
|
||||
const result: TextChunk[] = [];
|
||||
let wordStart = 0;
|
||||
let wordLength = 0;
|
||||
|
||||
for (let i = 0; i < pattern.length; i++) {
|
||||
let ch = pattern.charCodeAt(i);
|
||||
const ch = pattern.charCodeAt(i);
|
||||
if (isWordChar(ch)) {
|
||||
if (wordLength++ === 0) {
|
||||
if (wordLength === 0) {
|
||||
wordStart = i;
|
||||
}
|
||||
wordLength++;
|
||||
}
|
||||
else {
|
||||
if (wordLength > 0) {
|
||||
@@ -656,13 +602,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
function createTextChunk(text: string): TextChunk {
|
||||
let textLowerCase = text.toLowerCase();
|
||||
const textLowerCase = text.toLowerCase();
|
||||
return {
|
||||
text,
|
||||
textLowerCase,
|
||||
isLowerCase: text === textLowerCase,
|
||||
characterSpans: breakIntoCharacterSpans(text)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* @internal */ export function breakIntoCharacterSpans(identifier: string): TextSpan[] {
|
||||
@@ -674,15 +620,15 @@ namespace ts {
|
||||
}
|
||||
|
||||
function breakIntoSpans(identifier: string, word: boolean): TextSpan[] {
|
||||
let result: TextSpan[] = [];
|
||||
const result: TextSpan[] = [];
|
||||
|
||||
let wordStart = 0;
|
||||
for (let i = 1, n = identifier.length; i < n; i++) {
|
||||
let lastIsDigit = isDigit(identifier.charCodeAt(i - 1));
|
||||
let currentIsDigit = isDigit(identifier.charCodeAt(i));
|
||||
const lastIsDigit = isDigit(identifier.charCodeAt(i - 1));
|
||||
const currentIsDigit = isDigit(identifier.charCodeAt(i));
|
||||
|
||||
let hasTransitionFromLowerToUpper = transitionFromLowerToUpper(identifier, word, i);
|
||||
let hasTransitionFromUpperToLower = transitionFromUpperToLower(identifier, word, i, wordStart);
|
||||
const hasTransitionFromLowerToUpper = transitionFromLowerToUpper(identifier, word, i);
|
||||
const hasTransitionFromUpperToLower = transitionFromUpperToLower(identifier, word, i, wordStart);
|
||||
|
||||
if (charIsPunctuation(identifier.charCodeAt(i - 1)) ||
|
||||
charIsPunctuation(identifier.charCodeAt(i)) ||
|
||||
@@ -738,7 +684,7 @@ namespace ts {
|
||||
|
||||
function isAllPunctuation(identifier: string, start: number, end: number): boolean {
|
||||
for (let i = start; i < end; i++) {
|
||||
let ch = identifier.charCodeAt(i);
|
||||
const ch = identifier.charCodeAt(i);
|
||||
|
||||
// We don't consider _ or $ as punctuation as there may be things with that name.
|
||||
if (!charIsPunctuation(ch) || ch === CharacterCodes._ || ch === CharacterCodes.$) {
|
||||
@@ -759,8 +705,8 @@ namespace ts {
|
||||
// etc.
|
||||
if (index !== wordStart &&
|
||||
index + 1 < identifier.length) {
|
||||
let currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
let nextIsLower = isLowerCaseLetter(identifier.charCodeAt(index + 1));
|
||||
const currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
const nextIsLower = isLowerCaseLetter(identifier.charCodeAt(index + 1));
|
||||
|
||||
if (currentIsUpper && nextIsLower) {
|
||||
// We have a transition from an upper to a lower letter here. But we only
|
||||
@@ -786,12 +732,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function transitionFromLowerToUpper(identifier: string, word: boolean, index: number): boolean {
|
||||
let lastIsUpper = isUpperCaseLetter(identifier.charCodeAt(index - 1));
|
||||
let currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
const lastIsUpper = isUpperCaseLetter(identifier.charCodeAt(index - 1));
|
||||
const currentIsUpper = isUpperCaseLetter(identifier.charCodeAt(index));
|
||||
|
||||
// See if the casing indicates we're starting a new word. Note: if we're breaking on
|
||||
// words, then just seeing an upper case character isn't enough. Instead, it has to
|
||||
// be uppercase and the previous character can't be uppercase.
|
||||
// be uppercase and the previous character can't be uppercase.
|
||||
//
|
||||
// For example, breaking "AddMetadata" on words would make: Add Metadata
|
||||
//
|
||||
@@ -802,7 +748,7 @@ namespace ts {
|
||||
// on characters would be: A M
|
||||
//
|
||||
// We break the search string on characters. But we break the symbol name on words.
|
||||
let transition = word
|
||||
const transition = word
|
||||
? (currentIsUpper && !lastIsUpper)
|
||||
: currentIsUpper;
|
||||
return transition;
|
||||
|
||||
@@ -469,7 +469,8 @@ namespace ts {
|
||||
|
||||
function pushDocCommentLineText(docComments: SymbolDisplayPart[], text: string, blankLineCount: number) {
|
||||
// Add the empty lines in between texts
|
||||
while (blankLineCount--) {
|
||||
while (blankLineCount) {
|
||||
blankLineCount--;
|
||||
docComments.push(textPart(""));
|
||||
}
|
||||
|
||||
@@ -1034,6 +1035,7 @@ namespace ts {
|
||||
* host specific questions using 'getScriptSnapshot'.
|
||||
*/
|
||||
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
|
||||
directoryExists?(directoryName: string): boolean;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1217,6 +1219,7 @@ namespace ts {
|
||||
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean;
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean;
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: boolean;
|
||||
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean;
|
||||
PlaceOpenBraceOnNewLineForFunctions: boolean;
|
||||
PlaceOpenBraceOnNewLineForControlBlocks: boolean;
|
||||
[s: string]: boolean | number | string;
|
||||
@@ -1614,6 +1617,9 @@ namespace ts {
|
||||
public static jsxOpenTagName = "jsx open tag name";
|
||||
public static jsxCloseTagName = "jsx close tag name";
|
||||
public static jsxSelfClosingTagName = "jsx self closing tag name";
|
||||
public static jsxAttribute = "jsx attribute";
|
||||
public static jsxText = "jsx text";
|
||||
public static jsxAttributeStringLiteralValue = "jsx attribute string literal value";
|
||||
}
|
||||
|
||||
export const enum ClassificationType {
|
||||
@@ -1638,6 +1644,9 @@ namespace ts {
|
||||
jsxOpenTagName = 19,
|
||||
jsxCloseTagName = 20,
|
||||
jsxSelfClosingTagName = 21,
|
||||
jsxAttribute = 22,
|
||||
jsxText = 23,
|
||||
jsxAttributeStringLiteralValue = 24,
|
||||
}
|
||||
|
||||
/// Language Service
|
||||
@@ -1911,7 +1920,8 @@ namespace ts {
|
||||
getCurrentDirectory: () => "",
|
||||
getNewLine: () => newLine,
|
||||
fileExists: (fileName): boolean => fileName === inputFileName,
|
||||
readFile: (fileName): string => ""
|
||||
readFile: (fileName): string => "",
|
||||
directoryExists: directoryExists => true
|
||||
};
|
||||
|
||||
const program = createProgram([inputFileName], options, compilerHost);
|
||||
@@ -2768,6 +2778,10 @@ namespace ts {
|
||||
// stub missing host functionality
|
||||
const entry = hostCache.getOrCreateEntry(fileName);
|
||||
return entry && entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength());
|
||||
},
|
||||
directoryExists: directoryName => {
|
||||
Debug.assert(!host.resolveModuleNames);
|
||||
return directoryProbablyExists(directoryName, host);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2972,15 +2986,9 @@ namespace ts {
|
||||
// e.g "b a" is valid quoted name but when we strip off the quotes, it is invalid.
|
||||
// We, thus, need to check if whatever was inside the quotes is actually a valid identifier name.
|
||||
if (performCharacterChecks) {
|
||||
if (!isIdentifierStart(name.charCodeAt(0), target)) {
|
||||
if (!isIdentifier(name, target)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
for (let i = 1, n = name.length; i < n; i++) {
|
||||
if (!isIdentifierPart(name.charCodeAt(i), target)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
@@ -3098,6 +3106,7 @@ namespace ts {
|
||||
}
|
||||
else if (kind === SyntaxKind.SlashToken && contextToken.parent.kind === SyntaxKind.JsxClosingElement) {
|
||||
isStartingCloseTag = true;
|
||||
location = contextToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3123,8 +3132,11 @@ namespace ts {
|
||||
}
|
||||
else if (isStartingCloseTag) {
|
||||
const tagName = (<JsxElement>contextToken.parent.parent).openingElement.tagName;
|
||||
symbols = [typeChecker.getSymbolAtLocation(tagName)];
|
||||
const tagSymbol = typeChecker.getSymbolAtLocation(tagName);
|
||||
|
||||
if (!typeChecker.isUnknownSymbol(tagSymbol)) {
|
||||
symbols = [tagSymbol];
|
||||
}
|
||||
isMemberCompletion = true;
|
||||
isNewIdentifierLocation = false;
|
||||
}
|
||||
@@ -3830,7 +3842,23 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
if (!symbols || symbols.length === 0) {
|
||||
return undefined;
|
||||
if (sourceFile.languageVariant === LanguageVariant.JSX &&
|
||||
location.parent && location.parent.kind === SyntaxKind.JsxClosingElement) {
|
||||
// In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag,
|
||||
// instead of simply giving unknown value, the completion will return the tag-name of an associated opening-element.
|
||||
// For example:
|
||||
// var x = <div> </ /*1*/> completion list at "1" will contain "div" with type any
|
||||
const tagName = (<JsxElement>location.parent.parent).openingElement.tagName;
|
||||
entries.push({
|
||||
name: (<Identifier>tagName).text,
|
||||
kind: undefined,
|
||||
kindModifiers: undefined,
|
||||
sortText: "0",
|
||||
});
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
getCompletionEntriesFromSymbols(symbols, entries);
|
||||
@@ -4438,7 +4466,7 @@ namespace ts {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
|
||||
if (!symbol) {
|
||||
if (!symbol || typeChecker.isUnknownSymbol(symbol)) {
|
||||
// Try getting just type at this position and show
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
@@ -6573,6 +6601,9 @@ namespace ts {
|
||||
case ClassificationType.jsxOpenTagName: return ClassificationTypeNames.jsxOpenTagName;
|
||||
case ClassificationType.jsxCloseTagName: return ClassificationTypeNames.jsxCloseTagName;
|
||||
case ClassificationType.jsxSelfClosingTagName: return ClassificationTypeNames.jsxSelfClosingTagName;
|
||||
case ClassificationType.jsxAttribute: return ClassificationTypeNames.jsxAttribute;
|
||||
case ClassificationType.jsxText: return ClassificationTypeNames.jsxText;
|
||||
case ClassificationType.jsxAttributeStringLiteralValue: return ClassificationTypeNames.jsxAttributeStringLiteralValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6781,12 +6812,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function classifyToken(token: Node): void {
|
||||
function classifyTokenOrJsxText(token: Node): void {
|
||||
if (nodeIsMissing(token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tokenStart = classifyLeadingTriviaAndGetTokenStart(token);
|
||||
const tokenStart = token.kind === SyntaxKind.JsxText ? token.pos : classifyLeadingTriviaAndGetTokenStart(token);
|
||||
|
||||
const tokenWidth = token.end - tokenStart;
|
||||
Debug.assert(tokenWidth >= 0);
|
||||
@@ -6822,7 +6853,8 @@ namespace ts {
|
||||
// the '=' in a variable declaration is special cased here.
|
||||
if (token.parent.kind === SyntaxKind.VariableDeclaration ||
|
||||
token.parent.kind === SyntaxKind.PropertyDeclaration ||
|
||||
token.parent.kind === SyntaxKind.Parameter) {
|
||||
token.parent.kind === SyntaxKind.Parameter ||
|
||||
token.parent.kind === SyntaxKind.JsxAttribute) {
|
||||
return ClassificationType.operator;
|
||||
}
|
||||
}
|
||||
@@ -6841,7 +6873,7 @@ namespace ts {
|
||||
return ClassificationType.numericLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.StringLiteral || tokenKind === SyntaxKind.StringLiteralType) {
|
||||
return ClassificationType.stringLiteral;
|
||||
return token.parent.kind === SyntaxKind.JsxAttribute ? ClassificationType.jsxAttributeStringLiteralValue : ClassificationType.stringLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.RegularExpressionLiteral) {
|
||||
// TODO: we should get another classification type for these literals.
|
||||
@@ -6851,6 +6883,9 @@ namespace ts {
|
||||
// TODO (drosen): we should *also* get another classification type for these literals.
|
||||
return ClassificationType.stringLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.JsxText) {
|
||||
return ClassificationType.jsxText;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.Identifier) {
|
||||
if (token) {
|
||||
switch (token.parent.kind) {
|
||||
@@ -6902,9 +6937,12 @@ namespace ts {
|
||||
return ClassificationType.jsxSelfClosingTagName;
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.JsxAttribute:
|
||||
if ((<JsxAttribute>token.parent).name === token) {
|
||||
return ClassificationType.jsxAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ClassificationType.identifier;
|
||||
}
|
||||
}
|
||||
@@ -6921,8 +6959,8 @@ namespace ts {
|
||||
const children = element.getChildren(sourceFile);
|
||||
for (let i = 0, n = children.length; i < n; i++) {
|
||||
const child = children[i];
|
||||
if (isToken(child)) {
|
||||
classifyToken(child);
|
||||
if (isToken(child) || child.kind === SyntaxKind.JsxText) {
|
||||
classifyTokenOrJsxText(child);
|
||||
}
|
||||
else {
|
||||
// Recurse into our child nodes.
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
/// <reference path='services.ts' />
|
||||
|
||||
/* @internal */
|
||||
var debugObjectHost = (<any>this);
|
||||
let debugObjectHost = (<any>this);
|
||||
|
||||
// We need to use 'null' to interface with the managed side.
|
||||
/* tslint:disable:no-null */
|
||||
/* tslint:disable:no-in-operator */
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
@@ -60,8 +64,9 @@ namespace ts {
|
||||
getNewLine?(): string;
|
||||
getProjectVersion?(): string;
|
||||
useCaseSensitiveFileNames?(): boolean;
|
||||
|
||||
|
||||
getModuleResolutionsForFile?(fileName: string): string;
|
||||
directoryExists(directoryName: string): boolean;
|
||||
}
|
||||
|
||||
/** Public interface of the the of a config service shim instance.*/
|
||||
@@ -233,8 +238,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
class ScriptSnapshotShimAdapter implements IScriptSnapshot {
|
||||
private lineStartPositions: number[] = null;
|
||||
|
||||
constructor(private scriptSnapshotShim: ScriptSnapshotShim) {
|
||||
}
|
||||
|
||||
@@ -247,14 +250,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
public getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange {
|
||||
var oldSnapshotShim = <ScriptSnapshotShimAdapter>oldSnapshot;
|
||||
var encoded = this.scriptSnapshotShim.getChangeRange(oldSnapshotShim.scriptSnapshotShim);
|
||||
const oldSnapshotShim = <ScriptSnapshotShimAdapter>oldSnapshot;
|
||||
const encoded = this.scriptSnapshotShim.getChangeRange(oldSnapshotShim.scriptSnapshotShim);
|
||||
// TODO: should this be '==='?
|
||||
if (encoded == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var decoded: { span: { start: number; length: number; }; newLength: number; } = JSON.parse(encoded);
|
||||
const decoded: { span: { start: number; length: number; }; newLength: number; } = JSON.parse(encoded);
|
||||
return createTextChangeRange(
|
||||
createTextSpan(decoded.span.start, decoded.span.length), decoded.newLength);
|
||||
}
|
||||
@@ -272,21 +275,25 @@ namespace ts {
|
||||
private files: string[];
|
||||
private loggingEnabled = false;
|
||||
private tracingEnabled = false;
|
||||
|
||||
|
||||
public resolveModuleNames: (moduleName: string[], containingFile: string) => ResolvedModule[];
|
||||
|
||||
public directoryExists: (directoryName: string) => boolean;
|
||||
|
||||
constructor(private shimHost: LanguageServiceShimHost) {
|
||||
// if shimHost is a COM object then property check will become method call with no arguments.
|
||||
// 'in' does not have this effect.
|
||||
if ("getModuleResolutionsForFile" in this.shimHost) {
|
||||
this.resolveModuleNames = (moduleNames: string[], containingFile: string) => {
|
||||
let resolutionsInFile = <Map<string>>JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile));
|
||||
const resolutionsInFile = <Map<string>>JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile));
|
||||
return map(moduleNames, name => {
|
||||
const result = lookUp(resolutionsInFile, name);
|
||||
return result ? { resolvedFileName: result } : undefined;
|
||||
});
|
||||
};
|
||||
}
|
||||
if ("directoryExists" in this.shimHost) {
|
||||
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
|
||||
}
|
||||
}
|
||||
|
||||
public log(s: string): void {
|
||||
@@ -319,7 +326,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
public getCompilationSettings(): CompilerOptions {
|
||||
var settingsJson = this.shimHost.getCompilationSettings();
|
||||
const settingsJson = this.shimHost.getCompilationSettings();
|
||||
// TODO: should this be '==='?
|
||||
if (settingsJson == null || settingsJson == "") {
|
||||
throw Error("LanguageServiceShimHostAdapter.getCompilationSettings: empty compilationSettings");
|
||||
@@ -328,17 +335,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
public getScriptFileNames(): string[] {
|
||||
var encoded = this.shimHost.getScriptFileNames();
|
||||
const encoded = this.shimHost.getScriptFileNames();
|
||||
return this.files = JSON.parse(encoded);
|
||||
}
|
||||
|
||||
public getScriptSnapshot(fileName: string): IScriptSnapshot {
|
||||
// Shim the API changes for 1.5 release. This should be removed once
|
||||
// TypeScript 1.5 has shipped.
|
||||
if (this.files && this.files.indexOf(fileName) < 0) {
|
||||
return undefined;
|
||||
}
|
||||
var scriptSnapshot = this.shimHost.getScriptSnapshot(fileName);
|
||||
const scriptSnapshot = this.shimHost.getScriptSnapshot(fileName);
|
||||
return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot);
|
||||
}
|
||||
|
||||
@@ -347,7 +349,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
public getLocalizedDiagnosticMessages(): any {
|
||||
var diagnosticMessagesJson = this.shimHost.getLocalizedDiagnosticMessages();
|
||||
const diagnosticMessagesJson = this.shimHost.getLocalizedDiagnosticMessages();
|
||||
if (diagnosticMessagesJson == null || diagnosticMessagesJson == "") {
|
||||
return null;
|
||||
}
|
||||
@@ -362,7 +364,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
public getCancellationToken(): HostCancellationToken {
|
||||
var hostCancellationToken = this.shimHost.getCancellationToken();
|
||||
const hostCancellationToken = this.shimHost.getCancellationToken();
|
||||
return new ThrottledCancellationToken(hostCancellationToken);
|
||||
}
|
||||
|
||||
@@ -371,14 +373,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
public getDefaultLibFileName(options: CompilerOptions): string {
|
||||
// Wrap the API changes for 1.5 release. This try/catch
|
||||
// should be removed once TypeScript 1.5 has shipped.
|
||||
try {
|
||||
return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
|
||||
}
|
||||
catch (e) {
|
||||
return "";
|
||||
}
|
||||
return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,8 +388,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
public isCancellationRequested(): boolean {
|
||||
var time = Date.now();
|
||||
var duration = Math.abs(time - this.lastCancellationCheckTime);
|
||||
const time = Date.now();
|
||||
const duration = Math.abs(time - this.lastCancellationCheckTime);
|
||||
if (duration > 10) {
|
||||
// Check no more than once every 10 ms.
|
||||
this.lastCancellationCheckTime = time;
|
||||
@@ -405,52 +400,48 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export class CoreServicesShimHostAdapter implements ParseConfigHost {
|
||||
export class CoreServicesShimHostAdapter implements ParseConfigHost, ModuleResolutionHost {
|
||||
|
||||
public directoryExists: (directoryName: string) => boolean;
|
||||
|
||||
constructor(private shimHost: CoreServicesShimHost) {
|
||||
if ("directoryExists" in this.shimHost) {
|
||||
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
|
||||
}
|
||||
}
|
||||
|
||||
public readDirectory(rootDir: string, extension: string, exclude: string[]): string[] {
|
||||
// Wrap the API changes for 1.5 release. This try/catch
|
||||
// should be removed once TypeScript 1.5 has shipped.
|
||||
// Also consider removing the optional designation for
|
||||
// the exclude param at this time.
|
||||
var encoded: string;
|
||||
try {
|
||||
encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude));
|
||||
}
|
||||
catch (e) {
|
||||
encoded = this.shimHost.readDirectory(rootDir, extension);
|
||||
}
|
||||
const encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude));
|
||||
return JSON.parse(encoded);
|
||||
}
|
||||
|
||||
|
||||
public fileExists(fileName: string): boolean {
|
||||
return this.shimHost.fileExists(fileName);
|
||||
}
|
||||
|
||||
|
||||
public readFile(fileName: string): string {
|
||||
return this.shimHost.readFile(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): any {
|
||||
let start: number;
|
||||
if (logPerformance) {
|
||||
logger.log(actionDescription);
|
||||
var start = Date.now();
|
||||
start = Date.now();
|
||||
}
|
||||
|
||||
var result = action();
|
||||
const result = action();
|
||||
|
||||
if (logPerformance) {
|
||||
var end = Date.now();
|
||||
logger.log(actionDescription + " completed in " + (end - start) + " msec");
|
||||
if (typeof (result) === "string") {
|
||||
var str = <string>result;
|
||||
const end = Date.now();
|
||||
logger.log(`${actionDescription} completed in ${end - start} msec`);
|
||||
if (typeof result === "string") {
|
||||
let str = result;
|
||||
if (str.length > 128) {
|
||||
str = str.substring(0, 128) + "...";
|
||||
}
|
||||
logger.log(" result.length=" + str.length + ", result='" + JSON.stringify(str) + "'");
|
||||
logger.log(` result.length=${str.length}, result='${JSON.stringify(str)}'`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,8 +450,8 @@ namespace ts {
|
||||
|
||||
function forwardJSONCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): string {
|
||||
try {
|
||||
var result = simpleForwardCall(logger, actionDescription, action, logPerformance);
|
||||
return JSON.stringify({ result: result });
|
||||
const result = simpleForwardCall(logger, actionDescription, action, logPerformance);
|
||||
return JSON.stringify({ result });
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof OperationCanceledException) {
|
||||
@@ -481,7 +472,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function realizeDiagnostics(diagnostics: Diagnostic[], newLine: string): { message: string; start: number; length: number; category: string; code: number; } []{
|
||||
export function realizeDiagnostics(diagnostics: Diagnostic[], newLine: string): { message: string; start: number; length: number; category: string; code: number; }[] {
|
||||
return diagnostics.map(d => realizeDiagnostic(d, newLine));
|
||||
}
|
||||
|
||||
@@ -540,10 +531,9 @@ namespace ts {
|
||||
*/
|
||||
public refresh(throwOnError: boolean): void {
|
||||
this.forwardJSONCall(
|
||||
"refresh(" + throwOnError + ")",
|
||||
() => {
|
||||
return <any>null;
|
||||
});
|
||||
`refresh(${throwOnError})`,
|
||||
() => <any>null
|
||||
);
|
||||
}
|
||||
|
||||
public cleanupSemanticCache(): void {
|
||||
@@ -555,63 +545,57 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
private realizeDiagnostics(diagnostics: Diagnostic[]): { message: string; start: number; length: number; category: string; }[]{
|
||||
var newLine = getNewLineOrDefaultFromHost(this.host);
|
||||
private realizeDiagnostics(diagnostics: Diagnostic[]): { message: string; start: number; length: number; category: string; }[] {
|
||||
const newLine = getNewLineOrDefaultFromHost(this.host);
|
||||
return ts.realizeDiagnostics(diagnostics, newLine);
|
||||
}
|
||||
|
||||
public getSyntacticClassifications(fileName: string, start: number, length: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getSyntacticClassifications('" + fileName + "', " + start + ", " + length + ")",
|
||||
() => {
|
||||
var classifications = this.languageService.getSyntacticClassifications(fileName, createTextSpan(start, length));
|
||||
return classifications;
|
||||
});
|
||||
`getSyntacticClassifications('${fileName}', ${start}, ${length})`,
|
||||
() => this.languageService.getSyntacticClassifications(fileName, createTextSpan(start, length))
|
||||
);
|
||||
}
|
||||
|
||||
public getSemanticClassifications(fileName: string, start: number, length: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getSemanticClassifications('" + fileName + "', " + start + ", " + length + ")",
|
||||
() => {
|
||||
var classifications = this.languageService.getSemanticClassifications(fileName, createTextSpan(start, length));
|
||||
return classifications;
|
||||
});
|
||||
`getSemanticClassifications('${fileName}', ${start}, ${length})`,
|
||||
() => this.languageService.getSemanticClassifications(fileName, createTextSpan(start, length))
|
||||
);
|
||||
}
|
||||
|
||||
public getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getEncodedSyntacticClassifications('" + fileName + "', " + start + ", " + length + ")",
|
||||
() => {
|
||||
// directly serialize the spans out to a string. This is much faster to decode
|
||||
// on the managed side versus a full JSON array.
|
||||
return convertClassifications(this.languageService.getEncodedSyntacticClassifications(fileName, createTextSpan(start, length)));
|
||||
});
|
||||
`getEncodedSyntacticClassifications('${fileName}', ${start}, ${length})`,
|
||||
// directly serialize the spans out to a string. This is much faster to decode
|
||||
// on the managed side versus a full JSON array.
|
||||
() => convertClassifications(this.languageService.getEncodedSyntacticClassifications(fileName, createTextSpan(start, length)))
|
||||
);
|
||||
}
|
||||
|
||||
public getEncodedSemanticClassifications(fileName: string, start: number, length: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getEncodedSemanticClassifications('" + fileName + "', " + start + ", " + length + ")",
|
||||
() => {
|
||||
// directly serialize the spans out to a string. This is much faster to decode
|
||||
// on the managed side versus a full JSON array.
|
||||
return convertClassifications(this.languageService.getEncodedSemanticClassifications(fileName, createTextSpan(start, length)));
|
||||
});
|
||||
`getEncodedSemanticClassifications('${fileName}', ${start}, ${length})`,
|
||||
// directly serialize the spans out to a string. This is much faster to decode
|
||||
// on the managed side versus a full JSON array.
|
||||
() => convertClassifications(this.languageService.getEncodedSemanticClassifications(fileName, createTextSpan(start, length)))
|
||||
);
|
||||
}
|
||||
|
||||
public getSyntacticDiagnostics(fileName: string): string {
|
||||
return this.forwardJSONCall(
|
||||
"getSyntacticDiagnostics('" + fileName + "')",
|
||||
`getSyntacticDiagnostics('${fileName}')`,
|
||||
() => {
|
||||
var diagnostics = this.languageService.getSyntacticDiagnostics(fileName);
|
||||
const diagnostics = this.languageService.getSyntacticDiagnostics(fileName);
|
||||
return this.realizeDiagnostics(diagnostics);
|
||||
});
|
||||
}
|
||||
|
||||
public getSemanticDiagnostics(fileName: string): string {
|
||||
return this.forwardJSONCall(
|
||||
"getSemanticDiagnostics('" + fileName + "')",
|
||||
`getSemanticDiagnostics('${fileName}')`,
|
||||
() => {
|
||||
var diagnostics = this.languageService.getSemanticDiagnostics(fileName);
|
||||
const diagnostics = this.languageService.getSemanticDiagnostics(fileName);
|
||||
return this.realizeDiagnostics(diagnostics);
|
||||
});
|
||||
}
|
||||
@@ -620,7 +604,7 @@ namespace ts {
|
||||
return this.forwardJSONCall(
|
||||
"getCompilerOptionsDiagnostics()",
|
||||
() => {
|
||||
var diagnostics = this.languageService.getCompilerOptionsDiagnostics();
|
||||
const diagnostics = this.languageService.getCompilerOptionsDiagnostics();
|
||||
return this.realizeDiagnostics(diagnostics);
|
||||
});
|
||||
}
|
||||
@@ -633,11 +617,9 @@ namespace ts {
|
||||
*/
|
||||
public getQuickInfoAtPosition(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getQuickInfoAtPosition('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
var quickInfo = this.languageService.getQuickInfoAtPosition(fileName, position);
|
||||
return quickInfo;
|
||||
});
|
||||
`getQuickInfoAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getQuickInfoAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -649,11 +631,9 @@ namespace ts {
|
||||
*/
|
||||
public getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getNameOrDottedNameSpan('" + fileName + "', " + startPos + ", " + endPos + ")",
|
||||
() => {
|
||||
var spanInfo = this.languageService.getNameOrDottedNameSpan(fileName, startPos, endPos);
|
||||
return spanInfo;
|
||||
});
|
||||
`getNameOrDottedNameSpan('${fileName}', ${startPos}, ${endPos})`,
|
||||
() => this.languageService.getNameOrDottedNameSpan(fileName, startPos, endPos)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -662,22 +642,18 @@ namespace ts {
|
||||
*/
|
||||
public getBreakpointStatementAtPosition(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getBreakpointStatementAtPosition('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
var spanInfo = this.languageService.getBreakpointStatementAtPosition(fileName, position);
|
||||
return spanInfo;
|
||||
});
|
||||
`getBreakpointStatementAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getBreakpointStatementAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
/// SIGNATUREHELP
|
||||
|
||||
public getSignatureHelpItems(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getSignatureHelpItems('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
var signatureInfo = this.languageService.getSignatureHelpItems(fileName, position);
|
||||
return signatureInfo;
|
||||
});
|
||||
`getSignatureHelpItems('${fileName}', ${position})`,
|
||||
() => this.languageService.getSignatureHelpItems(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
/// GOTO DEFINITION
|
||||
@@ -688,10 +664,9 @@ namespace ts {
|
||||
*/
|
||||
public getDefinitionAtPosition(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getDefinitionAtPosition('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
return this.languageService.getDefinitionAtPosition(fileName, position);
|
||||
});
|
||||
`getDefinitionAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getDefinitionAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
/// GOTO Type
|
||||
@@ -702,44 +677,39 @@ namespace ts {
|
||||
*/
|
||||
public getTypeDefinitionAtPosition(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getTypeDefinitionAtPosition('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
return this.languageService.getTypeDefinitionAtPosition(fileName, position);
|
||||
});
|
||||
`getTypeDefinitionAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getTypeDefinitionAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
public getRenameInfo(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getRenameInfo('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
return this.languageService.getRenameInfo(fileName, position);
|
||||
});
|
||||
`getRenameInfo('${fileName}', ${position})`,
|
||||
() => this.languageService.getRenameInfo(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): string {
|
||||
return this.forwardJSONCall(
|
||||
"findRenameLocations('" + fileName + "', " + position + ", " + findInStrings + ", " + findInComments + ")",
|
||||
() => {
|
||||
return this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments);
|
||||
});
|
||||
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments})`,
|
||||
() => this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments)
|
||||
);
|
||||
}
|
||||
|
||||
/// GET BRACE MATCHING
|
||||
public getBraceMatchingAtPosition(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getBraceMatchingAtPosition('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
var textRanges = this.languageService.getBraceMatchingAtPosition(fileName, position);
|
||||
return textRanges;
|
||||
});
|
||||
`getBraceMatchingAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getBraceMatchingAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
/// GET SMART INDENT
|
||||
public getIndentationAtPosition(fileName: string, position: number, options: string /*Services.EditorOptions*/): string {
|
||||
return this.forwardJSONCall(
|
||||
"getIndentationAtPosition('" + fileName + "', " + position + ")",
|
||||
`getIndentationAtPosition('${fileName}', ${position})`,
|
||||
() => {
|
||||
var localOptions: EditorOptions = JSON.parse(options);
|
||||
const localOptions: EditorOptions = JSON.parse(options);
|
||||
return this.languageService.getIndentationAtPosition(fileName, position, localOptions);
|
||||
});
|
||||
}
|
||||
@@ -748,35 +718,32 @@ namespace ts {
|
||||
|
||||
public getReferencesAtPosition(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getReferencesAtPosition('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
return this.languageService.getReferencesAtPosition(fileName, position);
|
||||
});
|
||||
`getReferencesAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getReferencesAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
public findReferences(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"findReferences('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
return this.languageService.findReferences(fileName, position);
|
||||
});
|
||||
`findReferences('${fileName}', ${position})`,
|
||||
() => this.languageService.findReferences(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
public getOccurrencesAtPosition(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getOccurrencesAtPosition('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
return this.languageService.getOccurrencesAtPosition(fileName, position);
|
||||
});
|
||||
`getOccurrencesAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getOccurrencesAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
public getDocumentHighlights(fileName: string, position: number, filesToSearch: string): string {
|
||||
return this.forwardJSONCall(
|
||||
"getDocumentHighlights('" + fileName + "', " + position + ")",
|
||||
`getDocumentHighlights('${fileName}', ${position})`,
|
||||
() => {
|
||||
var results = this.languageService.getDocumentHighlights(fileName, position, JSON.parse(filesToSearch));
|
||||
const results = this.languageService.getDocumentHighlights(fileName, position, JSON.parse(filesToSearch));
|
||||
// workaround for VS document higlighting issue - keep only items from the initial file
|
||||
let normalizedName = normalizeSlashes(fileName).toLowerCase();
|
||||
const normalizedName = normalizeSlashes(fileName).toLowerCase();
|
||||
return filter(results, r => normalizeSlashes(r.fileName).toLowerCase() === normalizedName);
|
||||
});
|
||||
}
|
||||
@@ -790,56 +757,49 @@ namespace ts {
|
||||
*/
|
||||
public getCompletionsAtPosition(fileName: string, position: number) {
|
||||
return this.forwardJSONCall(
|
||||
"getCompletionsAtPosition('" + fileName + "', " + position + ")",
|
||||
() => {
|
||||
var completion = this.languageService.getCompletionsAtPosition(fileName, position);
|
||||
return completion;
|
||||
});
|
||||
`getCompletionsAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getCompletionsAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
|
||||
/** Get a string based representation of a completion list entry details */
|
||||
public getCompletionEntryDetails(fileName: string, position: number, entryName: string) {
|
||||
return this.forwardJSONCall(
|
||||
"getCompletionEntryDetails('" + fileName + "', " + position + ", " + entryName + ")",
|
||||
() => {
|
||||
var details = this.languageService.getCompletionEntryDetails(fileName, position, entryName);
|
||||
return details;
|
||||
});
|
||||
`getCompletionEntryDetails('${fileName}', ${position}, '${entryName}')`,
|
||||
() => this.languageService.getCompletionEntryDetails(fileName, position, entryName)
|
||||
);
|
||||
}
|
||||
|
||||
public getFormattingEditsForRange(fileName: string, start: number, end: number, options: string/*Services.FormatCodeOptions*/): string {
|
||||
return this.forwardJSONCall(
|
||||
"getFormattingEditsForRange('" + fileName + "', " + start + ", " + end + ")",
|
||||
`getFormattingEditsForRange('${fileName}', ${start}, ${end})`,
|
||||
() => {
|
||||
var localOptions: ts.FormatCodeOptions = JSON.parse(options);
|
||||
var edits = this.languageService.getFormattingEditsForRange(fileName, start, end, localOptions);
|
||||
return edits;
|
||||
const localOptions: ts.FormatCodeOptions = JSON.parse(options);
|
||||
return this.languageService.getFormattingEditsForRange(fileName, start, end, localOptions);
|
||||
});
|
||||
}
|
||||
|
||||
public getFormattingEditsForDocument(fileName: string, options: string/*Services.FormatCodeOptions*/): string {
|
||||
return this.forwardJSONCall(
|
||||
"getFormattingEditsForDocument('" + fileName + "')",
|
||||
`getFormattingEditsForDocument('${fileName}')`,
|
||||
() => {
|
||||
var localOptions: ts.FormatCodeOptions = JSON.parse(options);
|
||||
var edits = this.languageService.getFormattingEditsForDocument(fileName, localOptions);
|
||||
return edits;
|
||||
const localOptions: ts.FormatCodeOptions = JSON.parse(options);
|
||||
return this.languageService.getFormattingEditsForDocument(fileName, localOptions);
|
||||
});
|
||||
}
|
||||
|
||||
public getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: string/*Services.FormatCodeOptions*/): string {
|
||||
return this.forwardJSONCall(
|
||||
"getFormattingEditsAfterKeystroke('" + fileName + "', " + position + ", '" + key + "')",
|
||||
`getFormattingEditsAfterKeystroke('${fileName}', ${position}, '${key}')`,
|
||||
() => {
|
||||
var localOptions: ts.FormatCodeOptions = JSON.parse(options);
|
||||
var edits = this.languageService.getFormattingEditsAfterKeystroke(fileName, position, key, localOptions);
|
||||
return edits;
|
||||
const localOptions: ts.FormatCodeOptions = JSON.parse(options);
|
||||
return this.languageService.getFormattingEditsAfterKeystroke(fileName, position, key, localOptions);
|
||||
});
|
||||
}
|
||||
|
||||
public getDocCommentTemplateAtPosition(fileName: string, position: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getDocCommentTemplateAtPosition('" + fileName + "', " + position + ")",
|
||||
`getDocCommentTemplateAtPosition('${fileName}', ${position})`,
|
||||
() => this.languageService.getDocCommentTemplateAtPosition(fileName, position)
|
||||
);
|
||||
}
|
||||
@@ -849,51 +809,38 @@ namespace ts {
|
||||
/** Return a list of symbols that are interesting to navigate to */
|
||||
public getNavigateToItems(searchValue: string, maxResultCount?: number): string {
|
||||
return this.forwardJSONCall(
|
||||
"getNavigateToItems('" + searchValue + "', " + maxResultCount+ ")",
|
||||
() => {
|
||||
var items = this.languageService.getNavigateToItems(searchValue, maxResultCount);
|
||||
return items;
|
||||
});
|
||||
`getNavigateToItems('${searchValue}', ${maxResultCount})`,
|
||||
() => this.languageService.getNavigateToItems(searchValue, maxResultCount)
|
||||
);
|
||||
}
|
||||
|
||||
public getNavigationBarItems(fileName: string): string {
|
||||
return this.forwardJSONCall(
|
||||
"getNavigationBarItems('" + fileName + "')",
|
||||
() => {
|
||||
var items = this.languageService.getNavigationBarItems(fileName);
|
||||
return items;
|
||||
});
|
||||
`getNavigationBarItems('${fileName}')`,
|
||||
() => this.languageService.getNavigationBarItems(fileName)
|
||||
);
|
||||
}
|
||||
|
||||
public getOutliningSpans(fileName: string): string {
|
||||
return this.forwardJSONCall(
|
||||
"getOutliningSpans('" + fileName + "')",
|
||||
() => {
|
||||
var items = this.languageService.getOutliningSpans(fileName);
|
||||
return items;
|
||||
});
|
||||
`getOutliningSpans('${fileName}')`,
|
||||
() => this.languageService.getOutliningSpans(fileName)
|
||||
);
|
||||
}
|
||||
|
||||
public getTodoComments(fileName: string, descriptors: string): string {
|
||||
return this.forwardJSONCall(
|
||||
"getTodoComments('" + fileName + "')",
|
||||
() => {
|
||||
var items = this.languageService.getTodoComments(fileName, JSON.parse(descriptors));
|
||||
return items;
|
||||
});
|
||||
`getTodoComments('${fileName}')`,
|
||||
() => this.languageService.getTodoComments(fileName, JSON.parse(descriptors))
|
||||
);
|
||||
}
|
||||
|
||||
/// Emit
|
||||
public getEmitOutput(fileName: string): string {
|
||||
return this.forwardJSONCall(
|
||||
"getEmitOutput('" + fileName + "')",
|
||||
() => {
|
||||
var output = this.languageService.getEmitOutput(fileName);
|
||||
// Shim the API changes for 1.5 release. This should be removed once
|
||||
// TypeScript 1.5 has shipped.
|
||||
(<any>output).emitOutputStatus = output.emitSkipped ? 1 : 0;
|
||||
return output;
|
||||
});
|
||||
`getEmitOutput('${fileName}')`,
|
||||
() => this.languageService.getEmitOutput(fileName)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -918,12 +865,11 @@ namespace ts {
|
||||
|
||||
/// COLORIZATION
|
||||
public getClassificationsForLine(text: string, lexState: EndOfLineState, classifyKeywordsInGenerics?: boolean): string {
|
||||
var classification = this.classifier.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics);
|
||||
var items = classification.entries;
|
||||
var result = "";
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
result += items[i].length + "\n";
|
||||
result += items[i].classification + "\n";
|
||||
const classification = this.classifier.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics);
|
||||
let result = "";
|
||||
for (const item of classification.entries) {
|
||||
result += item.length + "\n";
|
||||
result += item.classification + "\n";
|
||||
}
|
||||
result += classification.finalLexState;
|
||||
return result;
|
||||
@@ -940,16 +886,16 @@ namespace ts {
|
||||
private forwardJSONCall(actionDescription: string, action: () => any): any {
|
||||
return forwardJSONCall(this.logger, actionDescription, action, this.logPerformance);
|
||||
}
|
||||
|
||||
|
||||
public resolveModuleName(fileName: string, moduleName: string, compilerOptionsJson: string): string {
|
||||
return this.forwardJSONCall(`resolveModuleName('${fileName}')`, () => {
|
||||
let compilerOptions = <CompilerOptions>JSON.parse(compilerOptionsJson);
|
||||
const compilerOptions = <CompilerOptions>JSON.parse(compilerOptionsJson);
|
||||
const result = resolveModuleName(moduleName, normalizeSlashes(fileName), compilerOptions, this.host);
|
||||
return {
|
||||
resolvedFileName: result.resolvedModule ? result.resolvedModule.resolvedFileName: undefined,
|
||||
resolvedFileName: result.resolvedModule ? result.resolvedModule.resolvedFileName : undefined,
|
||||
failedLookupLocations: result.failedLookupLocations
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getPreProcessedFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
|
||||
@@ -957,8 +903,8 @@ namespace ts {
|
||||
"getPreProcessedFileInfo('" + fileName + "')",
|
||||
() => {
|
||||
// for now treat files as JavaScript
|
||||
var result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
|
||||
var convertResult = {
|
||||
const result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
|
||||
const convertResult = {
|
||||
referencedFiles: <IFileReference[]>[],
|
||||
importedFiles: <IFileReference[]>[],
|
||||
ambientExternalModules: result.ambientExternalModules,
|
||||
@@ -986,26 +932,26 @@ namespace ts {
|
||||
|
||||
public getTSConfigFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
|
||||
return this.forwardJSONCall(
|
||||
"getTSConfigFileInfo('" + fileName + "')",
|
||||
`getTSConfigFileInfo('${fileName}')`,
|
||||
() => {
|
||||
let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength());
|
||||
const text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength());
|
||||
|
||||
let result = parseConfigFileTextToJson(fileName, text);
|
||||
const result = parseConfigFileTextToJson(fileName, text);
|
||||
|
||||
if (result.error) {
|
||||
return {
|
||||
options: {},
|
||||
files: [],
|
||||
errors: [realizeDiagnostic(result.error, '\r\n')]
|
||||
errors: [realizeDiagnostic(result.error, "\r\n")]
|
||||
};
|
||||
}
|
||||
|
||||
var configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName)));
|
||||
const configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName)));
|
||||
|
||||
return {
|
||||
options: configFile.options,
|
||||
files: configFile.fileNames,
|
||||
errors: realizeDiagnostics(configFile.errors, '\r\n')
|
||||
errors: realizeDiagnostics(configFile.errors, "\r\n")
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1013,9 +959,8 @@ namespace ts {
|
||||
public getDefaultCompilationSettings(): string {
|
||||
return this.forwardJSONCall(
|
||||
"getDefaultCompilationSettings()",
|
||||
() => {
|
||||
return getDefaultCompilerOptions();
|
||||
});
|
||||
() => getDefaultCompilerOptions()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1035,8 +980,8 @@ namespace ts {
|
||||
if (this.documentRegistry === undefined) {
|
||||
this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory());
|
||||
}
|
||||
var hostAdapter = new LanguageServiceShimHostAdapter(host);
|
||||
var languageService = createLanguageService(hostAdapter, this.documentRegistry);
|
||||
const hostAdapter = new LanguageServiceShimHostAdapter(host);
|
||||
const languageService = createLanguageService(hostAdapter, this.documentRegistry);
|
||||
return new LanguageServiceShimObject(this, host, languageService);
|
||||
}
|
||||
catch (err) {
|
||||
@@ -1057,7 +1002,7 @@ namespace ts {
|
||||
|
||||
public createCoreServicesShim(host: CoreServicesShimHost): CoreServicesShim {
|
||||
try {
|
||||
var adapter = new CoreServicesShimHostAdapter(host);
|
||||
const adapter = new CoreServicesShimHostAdapter(host);
|
||||
return new CoreServicesShimObject(this, <Logger>host, adapter);
|
||||
}
|
||||
catch (err) {
|
||||
@@ -1077,7 +1022,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
public unregisterShim(shim: Shim): void {
|
||||
for (var i = 0, n = this._shims.length; i < n; i++) {
|
||||
for (let i = 0, n = this._shims.length; i < n; i++) {
|
||||
if (this._shims[i] === shim) {
|
||||
delete this._shims[i];
|
||||
return;
|
||||
@@ -1096,12 +1041,21 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/* tslint:enable:no-in-operator */
|
||||
/* tslint:enable:no-null */
|
||||
|
||||
|
||||
/// TODO: this is used by VS, clean this up on both sides of the interface
|
||||
/* @internal */
|
||||
module TypeScript.Services {
|
||||
export var TypeScriptServicesFactory = ts.TypeScriptServicesFactory;
|
||||
namespace TypeScript.Services {
|
||||
export const TypeScriptServicesFactory = ts.TypeScriptServicesFactory;
|
||||
}
|
||||
|
||||
/* tslint:disable:no-unused-variable */
|
||||
// 'toolsVersion' gets consumed by the managed side, so it's not unused.
|
||||
// TODO: it should be moved into a namespace though.
|
||||
|
||||
/* @internal */
|
||||
const toolsVersion = "1.6";
|
||||
const toolsVersion = "1.8";
|
||||
|
||||
/* tslint:enable:no-unused-variable */
|
||||
@@ -608,6 +608,36 @@ namespace ts {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isArrayLiteralOrObjectLiteralDestructuringPattern(node: Node) {
|
||||
if (node.kind === SyntaxKind.ArrayLiteralExpression ||
|
||||
node.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
// [a,b,c] from:
|
||||
// [a, b, c] = someExpression;
|
||||
if (node.parent.kind === SyntaxKind.BinaryExpression &&
|
||||
(<BinaryExpression>node.parent).left === node &&
|
||||
(<BinaryExpression>node.parent).operatorToken.kind === SyntaxKind.EqualsToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// [a, b, c] from:
|
||||
// for([a, b, c] of expression)
|
||||
if (node.parent.kind === SyntaxKind.ForOfStatement &&
|
||||
(<ForOfStatement>node.parent).initializer === node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// [a, b, c] of
|
||||
// [x, [a, b, c] ] = someExpression
|
||||
// or
|
||||
// {x, a: {a, b, c} } = someExpression
|
||||
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.kind === SyntaxKind.PropertyAssignment ? node.parent.parent : node.parent)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Display-part writer helpers
|
||||
|
||||
Reference in New Issue
Block a user