merge with origin/master

This commit is contained in:
vladima
2016-01-11 22:12:03 -08:00
577 changed files with 128288 additions and 21686 deletions

View File

@@ -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

View File

@@ -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 }
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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),

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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.

View File

@@ -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 */

View File

@@ -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