Merge branch 'transforms' into transforms-generators

This commit is contained in:
Ron Buckton
2016-06-13 11:10:26 -07:00
1447 changed files with 131289 additions and 48578 deletions
+21 -21
View File
@@ -15,7 +15,7 @@ namespace ts.BreakpointResolver {
}
let tokenAtLocation = getTokenAtPosition(sourceFile, position);
let lineOfPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
const lineOfPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
if (sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getStart(sourceFile)).line > lineOfPosition) {
// Get previous token if the token is returned starts on new line
// eg: let x =10; |--- cursor is here
@@ -24,7 +24,7 @@ namespace ts.BreakpointResolver {
// token on same line if trailing trivia (comments or white spaces on same line) part of the last token on that line
tokenAtLocation = findPrecedingToken(tokenAtLocation.pos, sourceFile);
// Its a blank line
// It's a blank line
if (!tokenAtLocation || sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getEnd()).line !== lineOfPosition) {
return undefined;
}
@@ -216,7 +216,7 @@ namespace ts.BreakpointResolver {
return spanInNodeIfStartsOnSameLine(findPrecedingToken(node.pos, sourceFile));
case SyntaxKind.CommaToken:
return spanInPreviousNode(node)
return spanInPreviousNode(node);
case SyntaxKind.OpenBraceToken:
return spanInOpenBraceToken(node);
@@ -312,7 +312,7 @@ namespace ts.BreakpointResolver {
case SyntaxKind.BinaryExpression:
if ((<BinaryExpression>node.parent).operatorToken.kind === SyntaxKind.CommaToken) {
// if this is comma expression, the breakpoint is possible in this expression
// If this is a comma expression, the breakpoint is possible in this expression
return textSpan(node);
}
break;
@@ -370,7 +370,7 @@ namespace ts.BreakpointResolver {
}
function textSpanFromVariableDeclaration(variableDeclaration: VariableDeclaration): TextSpan {
let declarations = variableDeclaration.parent.declarations;
const declarations = variableDeclaration.parent.declarations;
if (declarations && declarations[0] === variableDeclaration) {
// First declaration - include let keyword
return textSpan(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent), variableDeclaration);
@@ -387,7 +387,7 @@ namespace ts.BreakpointResolver {
return spanInNode(variableDeclaration.parent.parent);
}
// If this is a destructuring pattern set breakpoint in binding pattern
// If this is a destructuring pattern, set breakpoint in binding pattern
if (isBindingPattern(variableDeclaration.name)) {
return spanInBindingPattern(<BindingPattern>variableDeclaration.name);
}
@@ -400,11 +400,11 @@ namespace ts.BreakpointResolver {
return textSpanFromVariableDeclaration(variableDeclaration);
}
let declarations = variableDeclaration.parent.declarations;
const declarations = variableDeclaration.parent.declarations;
if (declarations && declarations[0] !== variableDeclaration) {
// If we cant set breakpoint on this declaration, set it on previous one
// If we cannot set breakpoint on this declaration, set it on previous one
// Because the variable declaration may be binding pattern and
// we would like to set breakpoint in last binding element if thats the case,
// we would like to set breakpoint in last binding element if that's the case,
// use preceding token instead
return spanInNode(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent));
}
@@ -418,15 +418,15 @@ namespace ts.BreakpointResolver {
function spanInParameterDeclaration(parameter: ParameterDeclaration): TextSpan {
if (isBindingPattern(parameter.name)) {
// set breakpoint in binding pattern
// Set breakpoint in binding pattern
return spanInBindingPattern(<BindingPattern>parameter.name);
}
else if (canHaveSpanInParameterDeclaration(parameter)) {
return textSpan(parameter);
}
else {
let functionDeclaration = <FunctionLikeDeclaration>parameter.parent;
let indexOfParameter = indexOf(functionDeclaration.parameters, parameter);
const functionDeclaration = <FunctionLikeDeclaration>parameter.parent;
const indexOfParameter = indexOf(functionDeclaration.parameters, parameter);
if (indexOfParameter) {
// Not a first parameter, go to previous parameter
return spanInParameterDeclaration(functionDeclaration.parameters[indexOfParameter - 1]);
@@ -459,7 +459,7 @@ namespace ts.BreakpointResolver {
}
function spanInFunctionBlock(block: Block): TextSpan {
let nodeForSpanInBlock = block.statements.length ? block.statements[0] : block.getLastToken();
const nodeForSpanInBlock = block.statements.length ? block.statements[0] : block.getLastToken();
if (canFunctionHaveSpanInWholeDeclaration(<FunctionLikeDeclaration>block.parent)) {
return spanInNodeIfStartsOnSameLine(block.parent, nodeForSpanInBlock);
}
@@ -492,8 +492,8 @@ namespace ts.BreakpointResolver {
function spanInInitializerOfForLike(forLikeStatement: ForStatement | ForOfStatement | ForInStatement): TextSpan {
if (forLikeStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
// declaration list, set breakpoint in first declaration
let variableDeclarationList = <VariableDeclarationList>forLikeStatement.initializer;
// Declaration list - set breakpoint in first declaration
const variableDeclarationList = <VariableDeclarationList>forLikeStatement.initializer;
if (variableDeclarationList.declarations.length > 0) {
return spanInNode(variableDeclarationList.declarations[0]);
}
@@ -519,7 +519,7 @@ namespace ts.BreakpointResolver {
function spanInBindingPattern(bindingPattern: BindingPattern): TextSpan {
// Set breakpoint in first binding element
let firstBindingElement = forEach(bindingPattern.elements,
const firstBindingElement = forEach(bindingPattern.elements,
element => element.kind !== SyntaxKind.OmittedExpression ? element : undefined);
if (firstBindingElement) {
@@ -578,7 +578,7 @@ namespace ts.BreakpointResolver {
function spanInCloseBraceToken(node: Node): TextSpan {
switch (node.parent.kind) {
case SyntaxKind.ModuleBlock:
// If this is not instantiated module block no bp span
// If this is not an instantiated module block, no bp span
if (getModuleInstanceState(node.parent.parent) !== ModuleInstanceState.Instantiated) {
return undefined;
}
@@ -593,7 +593,7 @@ namespace ts.BreakpointResolver {
// Span on close brace token
return textSpan(node);
}
// fall through.
// fall through
case SyntaxKind.CatchClause:
return spanInNode(lastOrUndefined((<Block>node.parent).statements));
@@ -616,7 +616,7 @@ namespace ts.BreakpointResolver {
default:
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
// Breakpoint in last binding element or binding pattern if it contains no elements
let objectLiteral = <ObjectLiteralExpression>node.parent;
const objectLiteral = <ObjectLiteralExpression>node.parent;
return textSpan(lastOrUndefined(objectLiteral.properties) || objectLiteral);
}
return spanInNode(node.parent);
@@ -633,7 +633,7 @@ namespace ts.BreakpointResolver {
default:
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) {
// Breakpoint in last binding element or binding pattern if it contains no elements
let arrayLiteral = <ArrayLiteralExpression>node.parent;
const arrayLiteral = <ArrayLiteralExpression>node.parent;
return textSpan(lastOrUndefined(arrayLiteral.elements) || arrayLiteral);
}
@@ -714,7 +714,7 @@ namespace ts.BreakpointResolver {
function spanInOfKeyword(node: Node): TextSpan {
if (node.parent.kind === SyntaxKind.ForOfStatement) {
// set using next token
// Set using next token
return spanInNextNode(node);
}
+153 -148
View File
@@ -20,14 +20,14 @@ namespace ts.formatting {
Unknown = -1
}
/*
/*
* Indentation for the scope that can be dynamically recomputed.
* i.e
* i.e
* while(true)
* { let x;
* }
* Normally indentation is applied only to the first token in line so at glance 'var' should not be touched.
* However if some format rule adds new line between '}' and 'var' 'var' will become
* Normally indentation is applied only to the first token in line so at glance 'let' should not be touched.
* However if some format rule adds new line between '}' and 'let' 'let' will become
* the first token in line so it should be indented
*/
interface DynamicIndentation {
@@ -48,15 +48,15 @@ namespace ts.formatting {
* foo(bar({
* $
* }))
* Both 'foo', 'bar' introduce new indentation with delta = 4, but total indentation in $ is not 8.
* Both 'foo', 'bar' introduce new indentation with delta = 4, but total indentation in $ is not 8.
* foo: { indentation: 0, delta: 4 }
* bar: { indentation: foo.indentation + foo.delta = 4, delta: 4} however 'foo' and 'bar' are on the same line
* so bar inherits indentation from foo and bar.delta will be 4
*
*
*/
getDelta(child: TextRangeWithKind): number;
/**
* Formatter calls this function when rule adds or deletes new lines from the text
* Formatter calls this function when rule adds or deletes new lines from the text
* so indentation scope can adjust values of indentation and delta.
*/
recomputeIndentation(lineAddedByFormatting: boolean): void;
@@ -64,11 +64,11 @@ namespace ts.formatting {
interface Indentation {
indentation: number;
delta: number
delta: number;
}
export function formatOnEnter(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
let line = sourceFile.getLineAndCharacterOfPosition(position).line;
const line = sourceFile.getLineAndCharacterOfPosition(position).line;
if (line === 0) {
return [];
}
@@ -81,12 +81,18 @@ namespace ts.formatting {
while (isWhiteSpace(sourceFile.text.charCodeAt(endOfFormatSpan)) && !isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) {
endOfFormatSpan--;
}
let span = {
// if the character at the end of the span is a line break, we shouldn't include it, because it indicates we don't want to
// touch the current line at all. Also, on some OSes the line break consists of two characters (\r\n), we should test if the
// previous character before the end of format span is line break character as well.
if (isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) {
endOfFormatSpan--;
}
const span = {
// get start position for the previous line
pos: getStartPositionOfLine(line - 1, sourceFile),
// end value is exclusive so add 1 to the result
end: endOfFormatSpan + 1
}
};
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnEnter);
}
@@ -99,7 +105,7 @@ namespace ts.formatting {
}
export function formatDocument(sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
let span = {
const span = {
pos: 0,
end: sourceFile.text.length
};
@@ -108,7 +114,7 @@ namespace ts.formatting {
export function formatSelection(start: number, end: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] {
// format from the beginning of the line
let span = {
const span = {
pos: getLineStartPositionForPosition(start, sourceFile),
end: end
};
@@ -116,11 +122,11 @@ namespace ts.formatting {
}
function formatOutermostParent(position: number, expectedLastToken: SyntaxKind, sourceFile: SourceFile, options: FormatCodeOptions, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] {
let parent = findOutermostParent(position, expectedLastToken, sourceFile);
const parent = findOutermostParent(position, expectedLastToken, sourceFile);
if (!parent) {
return [];
}
let span = {
const span = {
pos: getLineStartPositionForPosition(parent.getStart(sourceFile), sourceFile),
end: parent.end
};
@@ -128,11 +134,11 @@ namespace ts.formatting {
}
function findOutermostParent(position: number, expectedTokenKind: SyntaxKind, sourceFile: SourceFile): Node {
let precedingToken = findPrecedingToken(position, sourceFile);
const precedingToken = findPrecedingToken(position, sourceFile);
// when it is claimed that trigger character was typed at given position
// 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,
// If this condition is not hold - then trigger character was typed in some other context,
// i.e.in comment and thus should not trigger autoformatting
if (!precedingToken ||
precedingToken.kind !== expectedTokenKind ||
@@ -142,12 +148,12 @@ namespace ts.formatting {
// walk up and search for the parent node that ends at the same position with precedingToken.
// for cases like this
//
//
// let x = 1;
// while (true) {
// }
// }
// after typing close curly in while statement we want to reformat just the while statement.
// However if we just walk upwards searching for the parent that has the same end value -
// However if we just walk upwards searching for the parent that has the same end value -
// we'll end up with the whole source file. isListElement allows to stop on the list element level
let current = precedingToken;
while (current &&
@@ -168,7 +174,7 @@ namespace ts.formatting {
case SyntaxKind.InterfaceDeclaration:
return rangeContainsRange((<InterfaceDeclaration>parent).members, node);
case SyntaxKind.ModuleDeclaration:
let body = (<ModuleDeclaration>parent).body;
const body = (<ModuleDeclaration>parent).body;
return body && body.kind === SyntaxKind.Block && rangeContainsRange((<Block>body).statements, node);
case SyntaxKind.SourceFile:
case SyntaxKind.Block:
@@ -186,9 +192,9 @@ namespace ts.formatting {
return find(sourceFile);
function find(n: Node): Node {
let candidate = forEachChild(n, c => startEndContainsRange(c.getStart(sourceFile), c.end, range) && c);
const candidate = forEachChild(n, c => startEndContainsRange(c.getStart(sourceFile), c.end, range) && c);
if (candidate) {
let result = find(candidate);
const result = find(candidate);
if (result) {
return result;
}
@@ -208,7 +214,7 @@ namespace ts.formatting {
}
// pick only errors that fall in range
let sorted = errors
const sorted = errors
.filter(d => rangeOverlapsWithStartEnd(originalRange, d.start, d.start + d.length))
.sort((e1, e2) => e1.start - e2.start);
@@ -223,11 +229,11 @@ namespace ts.formatting {
// 'index' tracks the index of the most recent error that was checked.
while (true) {
if (index >= sorted.length) {
// all errors in the range were already checked -> no error in specified range
// all errors in the range were already checked -> no error in specified range
return false;
}
let error = sorted[index];
const error = sorted[index];
if (r.end <= error.start) {
// specified range ends before the error refered by 'index' - no error in range
return false;
@@ -249,16 +255,16 @@ namespace ts.formatting {
/**
* Start of the original range might fall inside the comment - scanner will not yield appropriate results
* This function will look for token that is located before the start of target range
* This function will look for token that is located before the start of target range
* and return its end as start position for the scanner.
*/
function getScanStartPosition(enclosingNode: Node, originalRange: TextRange, sourceFile: SourceFile): number {
let start = enclosingNode.getStart(sourceFile);
const start = enclosingNode.getStart(sourceFile);
if (start === originalRange.pos && enclosingNode.end === originalRange.end) {
return start;
}
let precedingToken = findPrecedingToken(originalRange.pos, sourceFile);
const precedingToken = findPrecedingToken(originalRange.pos, sourceFile);
if (!precedingToken) {
// no preceding token found - start from the beginning of enclosing node
return enclosingNode.pos;
@@ -274,7 +280,7 @@ namespace ts.formatting {
}
/*
* For cases like
* For cases like
* if (a ||
* b ||$
* c) {...}
@@ -284,15 +290,15 @@ namespace ts.formatting {
* Initial indentation for this node will be 0.
* Binary expressions don't introduce new indentation scopes, however it is possible
* that some parent node on the same line does - like if statement in this case.
* Note that we are considering parents only from the same line with initial node -
* if parent is on the different line - its delta was already contributed
* Note that we are considering parents only from the same line with initial node -
* if parent is on the different line - its delta was already contributed
* to the initial indentation.
*/
function getOwnOrInheritedDelta(n: Node, options: FormatCodeOptions, sourceFile: SourceFile): number {
let previousLine = Constants.Unknown;
let child: Node;
while (n) {
let line = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile)).line;
const line = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile)).line;
if (previousLine !== Constants.Unknown && line !== previousLine) {
break;
}
@@ -314,17 +320,17 @@ namespace ts.formatting {
rulesProvider: RulesProvider,
requestKind: FormattingRequestKind): TextChange[] {
let rangeContainsError = prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange);
const rangeContainsError = prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange);
// formatting context is used by rules provider
let formattingContext = new FormattingContext(sourceFile, requestKind);
const formattingContext = new FormattingContext(sourceFile, requestKind);
// find the smallest node that fully wraps the range and compute the initial indentation for the node
let enclosingNode = findEnclosingNode(originalRange, sourceFile);
const enclosingNode = findEnclosingNode(originalRange, sourceFile);
let formattingScanner = getFormattingScanner(sourceFile, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end);
const formattingScanner = getFormattingScanner(sourceFile, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end);
let initialIndentation = SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, options);
const initialIndentation = SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, options);
let previousRangeHasError: boolean;
let previousRange: TextRangeWithKind;
@@ -334,23 +340,23 @@ namespace ts.formatting {
let lastIndentedLine: number;
let indentationOnLastIndentedLine: number;
let edits: TextChange[] = [];
const edits: TextChange[] = [];
formattingScanner.advance();
if (formattingScanner.isOnToken()) {
let startLine = sourceFile.getLineAndCharacterOfPosition(enclosingNode.getStart(sourceFile)).line;
const startLine = sourceFile.getLineAndCharacterOfPosition(enclosingNode.getStart(sourceFile)).line;
let undecoratedStartLine = startLine;
if (enclosingNode.decorators) {
undecoratedStartLine = sourceFile.getLineAndCharacterOfPosition(getNonDecoratorTokenPosOfNode(enclosingNode, sourceFile)).line;
}
let delta = getOwnOrInheritedDelta(enclosingNode, options, sourceFile);
const delta = getOwnOrInheritedDelta(enclosingNode, options, sourceFile);
processNode(enclosingNode, enclosingNode, startLine, undecoratedStartLine, initialIndentation, delta);
}
if (!formattingScanner.isOnToken()) {
let leadingTrivia = formattingScanner.getCurrentLeadingTrivia();
const leadingTrivia = formattingScanner.getCurrentLeadingTrivia();
if (leadingTrivia) {
processTrivia(leadingTrivia, enclosingNode, enclosingNode, undefined);
trimTrailingWhitespacesForRemainingRange();
@@ -364,10 +370,10 @@ namespace ts.formatting {
// local functions
/** Tries to compute the indentation for a list element.
* If list element is not in range then
* function will pick its actual indentation
* If list element is not in range then
* function will pick its actual indentation
* so it can be pushed downstream as inherited indentation.
* If list element is in the range - its indentation will be equal
* If list element is in the range - its indentation will be equal
* to inherited indentation from its predecessors.
*/
function tryComputeIndentationForListItem(startPos: number,
@@ -378,17 +384,17 @@ namespace ts.formatting {
if (rangeOverlapsWithStartEnd(range, startPos, endPos) ||
rangeContainsStartEnd(range, startPos, endPos) /* Not to miss zero-range nodes e.g. JsxText */) {
if (inheritedIndentation !== Constants.Unknown) {
return inheritedIndentation;
}
}
else {
let startLine = sourceFile.getLineAndCharacterOfPosition(startPos).line;
let startLinePosition = getLineStartPositionForPosition(startPos, sourceFile);
let column = SmartIndenter.findFirstNonWhitespaceColumn(startLinePosition, startPos, sourceFile, options);
const startLine = sourceFile.getLineAndCharacterOfPosition(startPos).line;
const startLinePosition = getLineStartPositionForPosition(startPos, sourceFile);
const column = SmartIndenter.findFirstNonWhitespaceColumn(startLinePosition, startPos, sourceFile, options);
if (startLine !== parentStartLine || startPos === column) {
return column
return column;
}
}
@@ -404,7 +410,7 @@ namespace ts.formatting {
effectiveParentStartLine: number): Indentation {
let indentation = inheritedIndentation;
var delta = SmartIndenter.shouldIndentChildNode(node) ? options.IndentSize : 0;
let delta = SmartIndenter.shouldIndentChildNode(node) ? options.IndentSize : 0;
if (effectiveParentStartLine === startLine) {
// if node is located on the same line with the parent
@@ -427,7 +433,7 @@ namespace ts.formatting {
return {
indentation,
delta
}
};
}
function getFirstNonDecoratorTokenOfNode(node: Node) {
@@ -511,7 +517,7 @@ namespace ts.formatting {
}
}
}
}
};
function getEffectiveDelta(delta: number, child: TextRangeWithKind) {
// Delta value should be zero when the node explicitly prevents indentation of the child node
@@ -524,17 +530,17 @@ namespace ts.formatting {
return;
}
let nodeDynamicIndentation = getDynamicIndentation(node, nodeStartLine, indentation, delta);
const nodeDynamicIndentation = getDynamicIndentation(node, nodeStartLine, indentation, delta);
// a useful observations when tracking context node
// /
// [a]
// / | \
// / | \
// [b] [c] [d]
// node 'a' is a context node for nodes 'b', 'c', 'd'
// node 'a' is a context node for nodes 'b', 'c', 'd'
// except for the leftmost leaf token in [b] - in this case context node ('e') is located somewhere above 'a'
// this rule can be applied recursively to child nodes of 'a'.
//
//
// context node is set to parent node value after processing every child node
// context node is set to parent of the token after processing every token
@@ -545,7 +551,7 @@ namespace ts.formatting {
forEachChild(
node,
child => {
processChildNode(child, /*inheritedIndentation*/ Constants.Unknown, node, nodeDynamicIndentation, nodeStartLine, undecoratedNodeStartLine, /*isListElement*/ false)
processChildNode(child, /*inheritedIndentation*/ Constants.Unknown, node, nodeDynamicIndentation, nodeStartLine, undecoratedNodeStartLine, /*isListItem*/ false);
},
(nodes: NodeArray<Node>) => {
processChildNodes(nodes, node, nodeStartLine, nodeDynamicIndentation);
@@ -553,7 +559,7 @@ namespace ts.formatting {
// proceed any tokens in the node that are located after child nodes
while (formattingScanner.isOnToken()) {
let tokenInfo = formattingScanner.readTokenInfo(node);
const tokenInfo = formattingScanner.readTokenInfo(node);
if (tokenInfo.token.end > node.end) {
break;
}
@@ -567,11 +573,12 @@ namespace ts.formatting {
parentDynamicIndentation: DynamicIndentation,
parentStartLine: number,
undecoratedParentStartLine: number,
isListItem: boolean): number {
isListItem: boolean,
isFirstListItem?: boolean): number {
let childStartPos = child.getStart(sourceFile);
const childStartPos = child.getStart(sourceFile);
let childStartLine = sourceFile.getLineAndCharacterOfPosition(childStartPos).line;
const childStartLine = sourceFile.getLineAndCharacterOfPosition(childStartPos).line;
let undecoratedChildStartLine = childStartLine;
if (child.decorators) {
@@ -598,7 +605,7 @@ namespace ts.formatting {
while (formattingScanner.isOnToken()) {
// proceed any parent tokens that are located prior to child.getStart()
let tokenInfo = formattingScanner.readTokenInfo(node);
const tokenInfo = formattingScanner.readTokenInfo(node);
if (tokenInfo.token.end > childStartPos) {
// stop when formatting scanner advances past the beginning of the child
break;
@@ -613,19 +620,23 @@ namespace ts.formatting {
if (isToken(child)) {
// if child node is a token, it does not impact indentation, proceed it using parent indentation scope rules
let tokenInfo = formattingScanner.readTokenInfo(child);
const tokenInfo = formattingScanner.readTokenInfo(child);
Debug.assert(tokenInfo.token.end === child.end);
consumeTokenAndAdvanceScanner(tokenInfo, node, parentDynamicIndentation, child);
return inheritedIndentation;
}
let effectiveParentStartLine = child.kind === SyntaxKind.Decorator ? childStartLine : undecoratedParentStartLine;
let childIndentation = computeIndentation(child, childStartLine, childIndentationAmount, node, parentDynamicIndentation, effectiveParentStartLine);
const effectiveParentStartLine = child.kind === SyntaxKind.Decorator ? childStartLine : undecoratedParentStartLine;
const childIndentation = computeIndentation(child, childStartLine, childIndentationAmount, node, parentDynamicIndentation, effectiveParentStartLine);
processNode(child, childContextNode, childStartLine, undecoratedChildStartLine, childIndentation.indentation, childIndentation.delta);
childContextNode = node;
if (isFirstListItem && parent.kind === SyntaxKind.ArrayLiteralExpression && inheritedIndentation === Constants.Unknown) {
inheritedIndentation = childIndentation.indentation;
}
return inheritedIndentation;
}
@@ -634,8 +645,8 @@ namespace ts.formatting {
parentStartLine: number,
parentDynamicIndentation: DynamicIndentation): void {
let listStartToken = getOpenTokenForList(parent, nodes);
let listEndToken = getCloseTokenForOpenToken(listStartToken);
const listStartToken = getOpenTokenForList(parent, nodes);
const listEndToken = getCloseTokenForOpenToken(listStartToken);
let listDynamicIndentation = parentDynamicIndentation;
let startLine = parentStartLine;
@@ -643,7 +654,7 @@ namespace ts.formatting {
if (listStartToken !== SyntaxKind.Unknown) {
// introduce a new indentation scope for lists (including list start and end tokens)
while (formattingScanner.isOnToken()) {
let tokenInfo = formattingScanner.readTokenInfo(parent);
const tokenInfo = formattingScanner.readTokenInfo(parent);
if (tokenInfo.token.end > nodes.pos) {
// stop when formatting scanner moves past the beginning of node list
break;
@@ -651,7 +662,7 @@ namespace ts.formatting {
else if (tokenInfo.token.kind === listStartToken) {
// consume list start token
startLine = sourceFile.getLineAndCharacterOfPosition(tokenInfo.token.pos).line;
let indentation =
const indentation =
computeIndentation(tokenInfo.token, startLine, Constants.Unknown, parent, parentDynamicIndentation, parentStartLine);
listDynamicIndentation = getDynamicIndentation(parent, parentStartLine, indentation.indentation, indentation.delta);
@@ -665,16 +676,17 @@ namespace ts.formatting {
}
let inheritedIndentation = Constants.Unknown;
for (let child of nodes) {
inheritedIndentation = processChildNode(child, inheritedIndentation, node, listDynamicIndentation, startLine, startLine, /*isListElement*/ true)
for (let i = 0; i < nodes.length; i++) {
const child = nodes[i];
inheritedIndentation = processChildNode(child, inheritedIndentation, node, listDynamicIndentation, startLine, startLine, /*isListItem*/ true, /*isFirstListItem*/ i === 0);
}
if (listEndToken !== SyntaxKind.Unknown) {
if (formattingScanner.isOnToken()) {
let tokenInfo = formattingScanner.readTokenInfo(parent);
const tokenInfo = formattingScanner.readTokenInfo(parent);
// consume the list end token only if it is still belong to the parent
// there might be the case when current token matches end token but does not considered as one
// function (x: function) <--
// function (x: function) <--
// without this check close paren will be interpreted as list end token for function expression which is wrong
if (tokenInfo.token.kind === listEndToken && rangeContainsRange(parent, tokenInfo.token)) {
// consume list end token
@@ -687,7 +699,7 @@ namespace ts.formatting {
function consumeTokenAndAdvanceScanner(currentTokenInfo: TokenInfo, parent: Node, dynamicIndentation: DynamicIndentation, container?: Node): void {
Debug.assert(rangeContainsRange(parent, currentTokenInfo.token));
let lastTriviaWasNewLine = formattingScanner.lastTrailingTriviaWasNewLine();
const lastTriviaWasNewLine = formattingScanner.lastTrailingTriviaWasNewLine();
let indentToken = false;
if (currentTokenInfo.leadingTrivia) {
@@ -695,13 +707,13 @@ namespace ts.formatting {
}
let lineAdded: boolean;
let isTokenInRange = rangeContainsRange(originalRange, currentTokenInfo.token);
const isTokenInRange = rangeContainsRange(originalRange, currentTokenInfo.token);
let tokenStart = sourceFile.getLineAndCharacterOfPosition(currentTokenInfo.token.pos);
const tokenStart = sourceFile.getLineAndCharacterOfPosition(currentTokenInfo.token.pos);
if (isTokenInRange) {
let rangeHasError = rangeContainsError(currentTokenInfo.token);
const rangeHasError = rangeContainsError(currentTokenInfo.token);
// save previousRange since processRange will overwrite this value with current one
let savePreviousRange = previousRange;
const savePreviousRange = previousRange;
lineAdded = processRange(currentTokenInfo.token, tokenStart, parent, childContextNode, dynamicIndentation);
if (rangeHasError) {
// do not indent comments\token if token range overlaps with some error
@@ -724,29 +736,28 @@ namespace ts.formatting {
}
if (indentToken) {
let tokenIndentation = (isTokenInRange && !rangeContainsError(currentTokenInfo.token)) ?
const tokenIndentation = (isTokenInRange && !rangeContainsError(currentTokenInfo.token)) ?
dynamicIndentation.getIndentationForToken(tokenStart.line, currentTokenInfo.token.kind, container) :
Constants.Unknown;
let indentNextTokenOrTrivia = true;
if (currentTokenInfo.leadingTrivia) {
let commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation, container);
let indentNextTokenOrTrivia = true;
for (let triviaItem of currentTokenInfo.leadingTrivia) {
if (!rangeContainsRange(originalRange, triviaItem)) {
continue;
}
const commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation, container);
for (const triviaItem of currentTokenInfo.leadingTrivia) {
const triviaInRange = rangeContainsRange(originalRange, triviaItem);
switch (triviaItem.kind) {
case SyntaxKind.MultiLineCommentTrivia:
indentMultilineComment(triviaItem, commentIndentation, /*firstLineIsIndented*/ !indentNextTokenOrTrivia);
if (triviaInRange) {
indentMultilineComment(triviaItem, commentIndentation, /*firstLineIsIndented*/ !indentNextTokenOrTrivia);
}
indentNextTokenOrTrivia = false;
break;
case SyntaxKind.SingleLineCommentTrivia:
if (indentNextTokenOrTrivia) {
if (indentNextTokenOrTrivia && triviaInRange) {
insertIndentation(triviaItem.pos, commentIndentation, /*lineAdded*/ false);
indentNextTokenOrTrivia = false;
}
indentNextTokenOrTrivia = false;
break;
case SyntaxKind.NewLineTrivia:
indentNextTokenOrTrivia = true;
@@ -756,7 +767,7 @@ namespace ts.formatting {
}
// indent token only if is it is in target range and does not overlap with any error ranges
if (tokenIndentation !== Constants.Unknown) {
if (tokenIndentation !== Constants.Unknown && indentNextTokenOrTrivia) {
insertIndentation(currentTokenInfo.token.pos, tokenIndentation, lineAdded);
lastIndentedLine = tokenStart.line;
@@ -771,9 +782,9 @@ namespace ts.formatting {
}
function processTrivia(trivia: TextRangeWithKind[], parent: Node, contextNode: Node, dynamicIndentation: DynamicIndentation): void {
for (let triviaItem of trivia) {
for (const triviaItem of trivia) {
if (isComment(triviaItem.kind) && rangeContainsRange(originalRange, triviaItem)) {
let triviaItemStart = sourceFile.getLineAndCharacterOfPosition(triviaItem.pos);
const triviaItemStart = sourceFile.getLineAndCharacterOfPosition(triviaItem.pos);
processRange(triviaItem, triviaItemStart, parent, contextNode, dynamicIndentation);
}
}
@@ -785,17 +796,17 @@ namespace ts.formatting {
contextNode: Node,
dynamicIndentation: DynamicIndentation): boolean {
let rangeHasError = rangeContainsError(range);
const rangeHasError = rangeContainsError(range);
let lineAdded: boolean;
if (!rangeHasError && !previousRangeHasError) {
if (!previousRange) {
// trim whitespaces starting from the beginning of the span up to the current line
let originalStart = sourceFile.getLineAndCharacterOfPosition(originalRange.pos);
const originalStart = sourceFile.getLineAndCharacterOfPosition(originalRange.pos);
trimTrailingWhitespacesForLines(originalStart.line, rangeStart.line);
}
else {
lineAdded =
processPair(range, rangeStart.line, parent, previousRange, previousRangeStartLine, previousParent, contextNode, dynamicIndentation)
processPair(range, rangeStart.line, parent, previousRange, previousRangeStartLine, previousParent, contextNode, dynamicIndentation);
}
}
@@ -818,7 +829,7 @@ namespace ts.formatting {
formattingContext.updateContext(previousItem, previousParent, currentItem, currentParent, contextNode);
let rule = rulesProvider.getRulesMap().GetRule(formattingContext);
const rule = rulesProvider.getRulesMap().GetRule(formattingContext);
let trimTrailingWhitespaces: boolean;
let lineAdded: boolean;
@@ -827,19 +838,19 @@ namespace ts.formatting {
if (rule.Operation.Action & (RuleAction.Space | RuleAction.Delete) && currentStartLine !== previousStartLine) {
lineAdded = false;
// Handle the case where the next line is moved to be the end of this line.
// Handle the case where the next line is moved to be the end of this line.
// In this case we don't indent the next line in the next pass.
if (currentParent.getStart(sourceFile) === currentItem.pos) {
dynamicIndentation.recomputeIndentation(/*lineAdded*/ false);
dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ false);
}
}
else if (rule.Operation.Action & RuleAction.NewLine && currentStartLine === previousStartLine) {
lineAdded = true;
// Handle the case where token2 is moved to the new line.
// Handle the case where token2 is moved to the new line.
// In this case we indent token2 in the next pass but we set
// sameLineIndent flag to notify the indenter that the indentation is within the line.
if (currentParent.getStart(sourceFile) === currentItem.pos) {
dynamicIndentation.recomputeIndentation(/*lineAdded*/ true);
dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ true);
}
}
@@ -859,25 +870,29 @@ namespace ts.formatting {
}
function insertIndentation(pos: number, indentation: number, lineAdded: boolean): void {
let indentationString = getIndentationString(indentation, options);
const indentationString = getIndentationString(indentation, options);
if (lineAdded) {
// new line is added before the token by the formatting rules
// insert indentation string at the very beginning of the token
recordReplace(pos, 0, indentationString);
}
else {
let tokenStart = sourceFile.getLineAndCharacterOfPosition(pos);
if (indentation !== tokenStart.character) {
let startLinePosition = getStartPositionOfLine(tokenStart.line, sourceFile);
const tokenStart = sourceFile.getLineAndCharacterOfPosition(pos);
const startLinePosition = getStartPositionOfLine(tokenStart.line, sourceFile);
if (indentation !== tokenStart.character || indentationIsDifferent(indentationString, startLinePosition)) {
recordReplace(startLinePosition, tokenStart.character, indentationString);
}
}
}
function indentationIsDifferent(indentationString: string, startLinePosition: number): boolean {
return indentationString !== sourceFile.text.substr(startLinePosition , indentationString.length);
}
function indentMultilineComment(commentRange: TextRange, indentation: number, firstLineIsIndented: boolean) {
// split comment in lines
let startLine = sourceFile.getLineAndCharacterOfPosition(commentRange.pos).line;
let endLine = sourceFile.getLineAndCharacterOfPosition(commentRange.end).line;
const endLine = sourceFile.getLineAndCharacterOfPosition(commentRange.end).line;
let parts: TextRange[];
if (startLine === endLine) {
if (!firstLineIsIndented) {
@@ -889,8 +904,8 @@ namespace ts.formatting {
else {
parts = [];
let startPos = commentRange.pos;
for (let line = startLine; line < endLine; ++line) {
let endOfLine = getEndLinePosition(line, sourceFile);
for (let line = startLine; line < endLine; line++) {
const endOfLine = getEndLinePosition(line, sourceFile);
parts.push({ pos: startPos, end: endOfLine });
startPos = getStartPositionOfLine(line + 1, sourceFile);
}
@@ -898,9 +913,9 @@ namespace ts.formatting {
parts.push({ pos: startPos, end: commentRange.end });
}
let startLinePos = getStartPositionOfLine(startLine, sourceFile);
const startLinePos = getStartPositionOfLine(startLine, sourceFile);
let nonWhitespaceColumnInFirstPart =
const nonWhitespaceColumnInFirstPart =
SmartIndenter.findFirstNonWhitespaceCharacterAndColumn(startLinePos, parts[0].pos, sourceFile, options);
if (indentation === nonWhitespaceColumnInFirstPart.column) {
@@ -914,17 +929,17 @@ namespace ts.formatting {
}
// shift all parts on the delta size
let delta = indentation - nonWhitespaceColumnInFirstPart.column;
for (let i = startIndex, len = parts.length; i < len; ++i, ++startLine) {
let startLinePos = getStartPositionOfLine(startLine, sourceFile);
let nonWhitespaceCharacterAndColumn =
const delta = indentation - nonWhitespaceColumnInFirstPart.column;
for (let i = startIndex, len = parts.length; i < len; i++, startLine++) {
const startLinePos = getStartPositionOfLine(startLine, sourceFile);
const nonWhitespaceCharacterAndColumn =
i === 0
? nonWhitespaceColumnInFirstPart
: SmartIndenter.findFirstNonWhitespaceCharacterAndColumn(parts[i].pos, parts[i].end, sourceFile, options);
let newIndentation = nonWhitespaceCharacterAndColumn.column + delta;
const newIndentation = nonWhitespaceCharacterAndColumn.column + delta;
if (newIndentation > 0) {
let indentationString = getIndentationString(newIndentation, options);
const indentationString = getIndentationString(newIndentation, options);
recordReplace(startLinePos, nonWhitespaceCharacterAndColumn.character, indentationString);
}
else {
@@ -934,16 +949,16 @@ namespace ts.formatting {
}
function trimTrailingWhitespacesForLines(line1: number, line2: number, range?: TextRangeWithKind) {
for (let line = line1; line < line2; ++line) {
let lineStartPosition = getStartPositionOfLine(line, sourceFile);
let lineEndPosition = getEndLinePosition(line, sourceFile);
for (let line = line1; line < line2; line++) {
const lineStartPosition = getStartPositionOfLine(line, sourceFile);
const lineEndPosition = getEndLinePosition(line, sourceFile);
// do not trim whitespaces in comments or template expression
if (range && (isComment(range.kind) || isStringOrRegularExpressionOrTemplateLiteral(range.kind)) && range.pos <= lineEndPosition && range.end > lineEndPosition) {
continue;
}
let whitespaceStart = getTrailingWhitespaceStartPosition(lineStartPosition, lineEndPosition);
const whitespaceStart = getTrailingWhitespaceStartPosition(lineStartPosition, lineEndPosition);
if (whitespaceStart !== -1) {
Debug.assert(whitespaceStart === lineStartPosition || !isWhiteSpace(sourceFile.text.charCodeAt(whitespaceStart - 1)));
recordDelete(whitespaceStart, lineEndPosition + 1 - whitespaceStart);
@@ -970,16 +985,16 @@ namespace ts.formatting {
* Trimming will be done for lines after the previous range
*/
function trimTrailingWhitespacesForRemainingRange() {
let startPosition = previousRange ? previousRange.end : originalRange.pos;
const startPosition = previousRange ? previousRange.end : originalRange.pos;
let startLine = sourceFile.getLineAndCharacterOfPosition(startPosition).line;
let endLine = sourceFile.getLineAndCharacterOfPosition(originalRange.end).line;
const startLine = sourceFile.getLineAndCharacterOfPosition(startPosition).line;
const 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 }
return { span: createTextSpan(start, len), newText };
}
function recordDelete(start: number, len: number) {
@@ -1000,7 +1015,6 @@ namespace ts.formatting {
currentRange: TextRangeWithKind,
currentStartLine: number): void {
let between: TextRange;
switch (rule.Operation.Action) {
case RuleAction.Ignore:
// no action required
@@ -1020,7 +1034,7 @@ namespace ts.formatting {
}
// edit should not be applied only if we have one line feed between elements
let lineDelta = currentStartLine - previousStartLine;
const lineDelta = currentStartLine - previousStartLine;
if (lineDelta !== 1) {
recordReplace(previousRange.end, currentRange.pos - previousRange.end, options.NewLineCharacter);
}
@@ -1031,7 +1045,7 @@ namespace ts.formatting {
return;
}
let posDelta = currentRange.pos - previousRange.end;
const posDelta = currentRange.pos - previousRange.end;
if (posDelta !== 1 || sourceFile.text.charCodeAt(previousRange.end) !== CharacterCodes.space) {
recordReplace(previousRange.end, currentRange.pos - previousRange.end, " ");
}
@@ -1040,15 +1054,6 @@ namespace ts.formatting {
}
}
function isSomeBlock(kind: SyntaxKind): boolean {
switch (kind) {
case SyntaxKind.Block:
case SyntaxKind.ModuleBlock:
return true;
}
return false;
}
function getOpenTokenForList(node: Node, list: Node[]) {
switch (node.kind) {
case SyntaxKind.Constructor:
@@ -1093,13 +1098,13 @@ namespace ts.formatting {
return SyntaxKind.Unknown;
}
var internedSizes: { tabSize: number; indentSize: number };
var internedTabsIndentation: string[];
var internedSpacesIndentation: string[];
let internedSizes: { tabSize: number; indentSize: number };
let internedTabsIndentation: string[];
let internedSpacesIndentation: string[];
export function getIndentationString(indentation: number, options: FormatCodeOptions): string {
// reset interned strings if FormatCodeOptions were changed
let resetInternedStrings =
const resetInternedStrings =
!internedSizes || (internedSizes.tabSize !== options.TabSize || internedSizes.indentSize !== options.IndentSize);
if (resetInternedStrings) {
@@ -1108,8 +1113,8 @@ namespace ts.formatting {
}
if (!options.ConvertTabsToSpaces) {
let tabs = Math.floor(indentation / options.TabSize);
let spaces = indentation - tabs * options.TabSize;
const tabs = Math.floor(indentation / options.TabSize);
const spaces = indentation - tabs * options.TabSize;
let tabString: string;
if (!internedTabsIndentation) {
@@ -1117,7 +1122,7 @@ namespace ts.formatting {
}
if (internedTabsIndentation[tabs] === undefined) {
internedTabsIndentation[tabs] = tabString = repeat('\t', tabs);
internedTabsIndentation[tabs] = tabString = repeat("\t", tabs);
}
else {
tabString = internedTabsIndentation[tabs];
@@ -1127,8 +1132,8 @@ namespace ts.formatting {
}
else {
let spacesString: string;
let quotient = Math.floor(indentation / options.IndentSize);
let remainder = indentation % options.IndentSize;
const quotient = Math.floor(indentation / options.IndentSize);
const remainder = indentation % options.IndentSize;
if (!internedSpacesIndentation) {
internedSpacesIndentation = [];
}
@@ -1146,11 +1151,11 @@ namespace ts.formatting {
function repeat(value: string, count: number): string {
let s = "";
for (let i = 0; i < count; ++i) {
for (let i = 0; i < count; i++) {
s += value;
}
return s;
}
}
}
}
+8 -8
View File
@@ -57,8 +57,8 @@ namespace ts.formatting {
public TokensAreOnSameLine(): boolean {
if (this.tokensAreOnSameLine === undefined) {
let startLine = this.sourceFile.getLineAndCharacterOfPosition(this.currentTokenSpan.pos).line;
let endLine = this.sourceFile.getLineAndCharacterOfPosition(this.nextTokenSpan.pos).line;
const startLine = this.sourceFile.getLineAndCharacterOfPosition(this.currentTokenSpan.pos).line;
const endLine = this.sourceFile.getLineAndCharacterOfPosition(this.nextTokenSpan.pos).line;
this.tokensAreOnSameLine = (startLine === endLine);
}
@@ -82,17 +82,17 @@ namespace ts.formatting {
}
private NodeIsOnOneLine(node: Node): boolean {
let startLine = this.sourceFile.getLineAndCharacterOfPosition(node.getStart(this.sourceFile)).line;
let endLine = this.sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line;
const startLine = this.sourceFile.getLineAndCharacterOfPosition(node.getStart(this.sourceFile)).line;
const endLine = this.sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line;
return startLine === endLine;
}
private BlockIsOnOneLine(node: Node): boolean {
let openBrace = findChildOfKind(node, SyntaxKind.OpenBraceToken, this.sourceFile);
let closeBrace = findChildOfKind(node, SyntaxKind.CloseBraceToken, this.sourceFile);
const openBrace = findChildOfKind(node, SyntaxKind.OpenBraceToken, this.sourceFile);
const closeBrace = findChildOfKind(node, SyntaxKind.CloseBraceToken, this.sourceFile);
if (openBrace && closeBrace) {
let startLine = this.sourceFile.getLineAndCharacterOfPosition(openBrace.getEnd()).line;
let endLine = this.sourceFile.getLineAndCharacterOfPosition(closeBrace.getStart(this.sourceFile)).line;
const startLine = this.sourceFile.getLineAndCharacterOfPosition(openBrace.getEnd()).line;
const endLine = this.sourceFile.getLineAndCharacterOfPosition(closeBrace.getStart(this.sourceFile)).line;
return startLine === endLine;
}
return false;
+17 -18
View File
@@ -35,7 +35,7 @@ namespace ts.formatting {
scanner.setText(sourceFile.text);
scanner.setTextPos(startPos);
let wasNewLine: boolean = true;
let wasNewLine = true;
let leadingTrivia: TextRangeWithKind[];
let trailingTrivia: TextRangeWithKind[];
@@ -56,13 +56,13 @@ namespace ts.formatting {
scanner.setText(undefined);
scanner = undefined;
}
}
};
function advance(): void {
Debug.assert(scanner !== undefined);
lastTokenInfo = undefined;
let isStarted = scanner.getStartPos() !== startPos;
const isStarted = scanner.getStartPos() !== startPos;
if (isStarted) {
if (trailingTrivia) {
@@ -81,23 +81,22 @@ namespace ts.formatting {
scanner.scan();
}
let t: SyntaxKind;
let pos = scanner.getStartPos();
// Read leading trivia and token
while (pos < endPos) {
let t = scanner.getToken();
const t = scanner.getToken();
if (!isTrivia(t)) {
break;
}
// consume leading trivia
scanner.scan();
let item = {
const item = {
pos: pos,
end: scanner.getStartPos(),
kind: t
}
};
pos = scanner.getStartPos();
@@ -166,16 +165,16 @@ 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 =
const expectedScanAction =
shouldRescanGreaterThanToken(n)
? ScanAction.RescanGreaterThanToken
: shouldRescanSlashToken(n)
? ScanAction.RescanSlashToken
: shouldRescanSlashToken(n)
? ScanAction.RescanSlashToken
: shouldRescanTemplateToken(n)
? ScanAction.RescanTemplateToken
: shouldRescanJsxIdentifier(n)
? ScanAction.RescanJsxIdentifier
: ScanAction.Scan
? ScanAction.RescanJsxIdentifier
: ScanAction.Scan;
if (lastTokenInfo && expectedScanAction === lastScanAction) {
// readTokenInfo was called before with the same expected scan action.
@@ -218,11 +217,11 @@ namespace ts.formatting {
lastScanAction = ScanAction.Scan;
}
let token: TextRangeWithKind = {
const token: TextRangeWithKind = {
pos: scanner.getStartPos(),
end: scanner.getTextPos(),
kind: currentToken
}
};
// consume trailing trivia
if (trailingTrivia) {
@@ -233,7 +232,7 @@ namespace ts.formatting {
if (!isTrivia(currentToken)) {
break;
}
let trivia = {
const trivia = {
pos: scanner.getStartPos(),
end: scanner.getTextPos(),
kind: currentToken
@@ -256,7 +255,7 @@ namespace ts.formatting {
leadingTrivia: leadingTrivia,
trailingTrivia: trailingTrivia,
token: token
}
};
return fixTokenKind(lastTokenInfo, n);
}
@@ -264,8 +263,8 @@ 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();
const current = (lastTokenInfo && lastTokenInfo.token.kind) || scanner.getToken();
const startPos = (lastTokenInfo && lastTokenInfo.token.pos) || scanner.getStartPos();
return startPos < endPos && current !== SyntaxKind.EndOfFileToken && !isTrivia(current);
}
+3 -12
View File
@@ -3,13 +3,7 @@
/* @internal */
namespace ts.formatting {
export class RuleOperation {
public Context: RuleOperationContext;
public Action: RuleAction;
constructor() {
this.Context = null;
this.Action = null;
}
constructor(public Context: RuleOperationContext, public Action: RuleAction) {}
public toString(): string {
return "[context=" + this.Context + "," +
@@ -17,14 +11,11 @@ namespace ts.formatting {
}
static create1(action: RuleAction) {
return RuleOperation.create2(RuleOperationContext.Any, action)
return RuleOperation.create2(RuleOperationContext.Any, action);
}
static create2(context: RuleOperationContext, action: RuleAction) {
let result = new RuleOperation();
result.Context = context;
result.Action = action;
return result;
return new RuleOperation(context, action);
}
}
}
@@ -5,7 +5,7 @@ namespace ts.formatting {
export class RuleOperationContext {
private customContextChecks: { (context: FormattingContext): boolean; }[];
constructor(...funcs: { (context: FormattingContext): boolean; }[]) {
this.customContextChecks = funcs;
}
@@ -22,7 +22,7 @@ namespace ts.formatting {
return true;
}
for (let check of this.customContextChecks) {
for (const check of this.customContextChecks) {
if (!check(context)) {
return false;
}
+11 -11
View File
@@ -4,8 +4,8 @@
namespace ts.formatting {
export class Rules {
public getRuleName(rule: Rule) {
let o: ts.Map<any> = <any>this;
for (let name in o) {
const o: ts.Map<any> = <any>this;
for (const name in o) {
if (o[name] === rule) {
return name;
}
@@ -166,7 +166,7 @@ namespace ts.formatting {
public NoSpaceAfterKeywordInControl: Rule;
// Open Brace braces after function
//TypeScript: Function can have return types, which can be made of tons of different token kinds
// TypeScript: Function can have return types, which can be made of tons of different token kinds
public FunctionOpenBraceLeftTokenRange: Shared.TokenRange;
public SpaceBeforeOpenBraceInFunction: Rule;
public NewLineBeforeOpenBraceInFunction: Rule;
@@ -458,7 +458,7 @@ namespace ts.formatting {
this.NoSpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext), RuleAction.Delete));
// Open Brace braces after function
//TypeScript: Function can have return types, which can be made of tons of different token kinds
// TypeScript: Function can have return types, which can be made of tons of different token kinds
this.NewLineBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
// Open Brace braces after TypeScript module/class/interface
@@ -616,17 +616,17 @@ namespace ts.formatting {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
//case SyntaxKind.MemberFunctionDeclaration:
// case SyntaxKind.MemberFunctionDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
///case SyntaxKind.MethodSignature:
// case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.FunctionExpression:
case SyntaxKind.Constructor:
case SyntaxKind.ArrowFunction:
//case SyntaxKind.ConstructorDeclaration:
//case SyntaxKind.SimpleArrowFunctionExpression:
//case SyntaxKind.ParenthesizedArrowFunctionExpression:
// case SyntaxKind.ConstructorDeclaration:
// case SyntaxKind.SimpleArrowFunctionExpression:
// case SyntaxKind.ParenthesizedArrowFunctionExpression:
case SyntaxKind.InterfaceDeclaration: // This one is not truly a function, but for formatting purposes, it acts just like one
return true;
}
@@ -724,7 +724,7 @@ namespace ts.formatting {
}
static IsNotBeforeBlockInFunctionDeclarationContext(context: FormattingContext): boolean {
return !Rules.IsFunctionDeclContext(context) && !Rules.IsBeforeBlockContext(context)
return !Rules.IsFunctionDeclContext(context) && !Rules.IsBeforeBlockContext(context);
}
static IsEndOfDecoratorContextOnSameLine(context: FormattingContext): boolean {
@@ -755,7 +755,7 @@ namespace ts.formatting {
}
static IsObjectTypeContext(context: FormattingContext): boolean {
return context.contextNode.kind === SyntaxKind.TypeLiteral;// && context.contextNode.parent.kind !== SyntaxKind.InterfaceDeclaration;
return context.contextNode.kind === SyntaxKind.TypeLiteral; // && context.contextNode.parent.kind !== SyntaxKind.InterfaceDeclaration;
}
static IsTypeArgumentOrParameterOrAssertion(token: TextRangeWithKind, parent: Node): boolean {
+17 -17
View File
@@ -12,17 +12,17 @@ namespace ts.formatting {
}
static create(rules: Rule[]): RulesMap {
let result = new RulesMap();
const result = new RulesMap();
result.Initialize(rules);
return result;
}
public Initialize(rules: Rule[]) {
this.mapRowLength = SyntaxKind.LastToken + 1;
this.map = <any> new Array(this.mapRowLength * this.mapRowLength);//new Array<RulesBucket>(this.mapRowLength * this.mapRowLength);
this.map = <any> new Array(this.mapRowLength * this.mapRowLength); // new Array<RulesBucket>(this.mapRowLength * this.mapRowLength);
// This array is used only during construction of the rulesbucket in the map
let rulesBucketConstructionStateList: RulesBucketConstructionState[] = <any> new Array(this.map.length);//new Array<RulesBucketConstructionState>(this.map.length);
const rulesBucketConstructionStateList: RulesBucketConstructionState[] = <any>new Array(this.map.length); // new Array<RulesBucketConstructionState>(this.map.length);
this.FillRules(rules, rulesBucketConstructionStateList);
return this.map;
@@ -35,18 +35,18 @@ namespace ts.formatting {
}
private GetRuleBucketIndex(row: number, column: number): number {
let rulesBucketIndex = (row * this.mapRowLength) + column;
//Debug.Assert(rulesBucketIndex < this.map.Length, "Trying to access an index outside the array.");
const rulesBucketIndex = (row * this.mapRowLength) + column;
// Debug.Assert(rulesBucketIndex < this.map.Length, "Trying to access an index outside the array.");
return rulesBucketIndex;
}
private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void {
let specificRule = rule.Descriptor.LeftTokenRange !== Shared.TokenRange.Any &&
const specificRule = rule.Descriptor.LeftTokenRange !== Shared.TokenRange.Any &&
rule.Descriptor.RightTokenRange !== Shared.TokenRange.Any;
rule.Descriptor.LeftTokenRange.GetTokens().forEach((left) => {
rule.Descriptor.RightTokenRange.GetTokens().forEach((right) => {
let rulesBucketIndex = this.GetRuleBucketIndex(left, right);
const rulesBucketIndex = this.GetRuleBucketIndex(left, right);
let rulesBucket = this.map[rulesBucketIndex];
if (rulesBucket === undefined) {
@@ -54,26 +54,26 @@ namespace ts.formatting {
}
rulesBucket.AddRule(rule, specificRule, rulesBucketConstructionStateList, rulesBucketIndex);
})
})
});
});
}
public GetRule(context: FormattingContext): Rule {
let bucketIndex = this.GetRuleBucketIndex(context.currentTokenSpan.kind, context.nextTokenSpan.kind);
let bucket = this.map[bucketIndex];
if (bucket != null) {
for (let rule of bucket.Rules()) {
const bucketIndex = this.GetRuleBucketIndex(context.currentTokenSpan.kind, context.nextTokenSpan.kind);
const bucket = this.map[bucketIndex];
if (bucket) {
for (const rule of bucket.Rules()) {
if (rule.Operation.Context.InContext(context)) {
return rule;
}
}
}
return null;
return undefined;
}
}
let MaskBitSize = 5;
let Mask = 0x1f;
const MaskBitSize = 5;
const Mask = 0x1f;
export enum RulesPosition {
IgnoreRulesSpecific = 0,
@@ -167,7 +167,7 @@ namespace ts.formatting {
if (state === undefined) {
state = constructionState[rulesBucketIndex] = new RulesBucketConstructionState();
}
let index = state.GetInsertionIndex(position);
const index = state.GetInsertionIndex(position);
this.rules.splice(index, 0, rule);
state.IncreaseInsertionIndex(position);
}
+3 -4
View File
@@ -25,10 +25,9 @@ namespace ts.formatting {
}
public ensureUpToDate(options: ts.FormatCodeOptions) {
// TODO: Should this be '==='?
if (this.options == null || !ts.compareDataObjects(this.options, options)) {
let activeRules = this.createActiveRules(options);
let rulesMap = RulesMap.create(activeRules);
if (!this.options || !ts.compareDataObjects(this.options, options)) {
const activeRules = this.createActiveRules(options);
const rulesMap = RulesMap.create(activeRules);
this.activeRules = activeRules;
this.rulesMap = rulesMap;
+35 -35
View File
@@ -2,7 +2,7 @@
/* @internal */
namespace ts.formatting {
export module SmartIndenter {
export namespace SmartIndenter {
const enum Value {
Unknown = -1
@@ -19,18 +19,18 @@ namespace ts.formatting {
return 0;
}
let precedingToken = findPrecedingToken(position, sourceFile);
const precedingToken = findPrecedingToken(position, sourceFile);
if (!precedingToken) {
return 0;
}
// no indentation in string \regex\template literals
let precedingTokenIsLiteral = isStringOrRegularExpressionOrTemplateLiteral(precedingToken.kind);
const precedingTokenIsLiteral = isStringOrRegularExpressionOrTemplateLiteral(precedingToken.kind);
if (precedingTokenIsLiteral && precedingToken.getStart(sourceFile) <= position && precedingToken.end > position) {
return 0;
}
let lineAtPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
const lineAtPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
// indentation is first non-whitespace character in a previous line
// for block indentation, we should look for a line which contains something that's not
@@ -40,21 +40,21 @@ namespace ts.formatting {
// move backwards until we find a line with a non-whitespace character,
// then find the first non-whitespace character for that line.
let current = position;
while (current > 0){
let char = sourceFile.text.charCodeAt(current);
while (current > 0) {
const char = sourceFile.text.charCodeAt(current);
if (!isWhiteSpace(char) && !isLineBreak(char)) {
break;
}
current--;
}
let lineStart = ts.getLineStartPositionForPosition(current, sourceFile);
const lineStart = ts.getLineStartPositionForPosition(current, sourceFile);
return SmartIndenter.findFirstNonWhitespaceColumn(lineStart, current, sourceFile, options);
}
if (precedingToken.kind === SyntaxKind.CommaToken && precedingToken.parent.kind !== SyntaxKind.BinaryExpression) {
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
let actualIndentation = getActualIndentationForListItemBeforeComma(precedingToken, sourceFile, options);
const actualIndentation = getActualIndentationForListItemBeforeComma(precedingToken, sourceFile, options);
if (actualIndentation !== Value.Unknown) {
return actualIndentation;
}
@@ -104,7 +104,7 @@ namespace ts.formatting {
}
export function getIndentationForNode(n: Node, ignoreActualIndentationRange: TextRange, sourceFile: SourceFile, options: FormatCodeOptions): number {
let start = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
const start = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
return getIndentationForNodeWorker(n, start, ignoreActualIndentationRange, /*indentationDelta*/ 0, sourceFile, options);
}
@@ -124,19 +124,19 @@ namespace ts.formatting {
while (parent) {
let useActualIndentation = true;
if (ignoreActualIndentationRange) {
let start = current.getStart(sourceFile);
const start = current.getStart(sourceFile);
useActualIndentation = start < ignoreActualIndentationRange.pos || start > ignoreActualIndentationRange.end;
}
if (useActualIndentation) {
// check if current node is a list item - if yes, take indentation from it
let actualIndentation = getActualIndentationForListItem(current, sourceFile, options);
const actualIndentation = getActualIndentationForListItem(current, sourceFile, options);
if (actualIndentation !== Value.Unknown) {
return actualIndentation + indentationDelta;
}
}
parentStart = getParentStart(parent, current, sourceFile);
let parentAndChildShareLine =
const parentAndChildShareLine =
parentStart.line === currentStart.line ||
childStartsOnTheSameLineWithElseInIfStatement(parent, current, currentStart.line, sourceFile);
@@ -167,7 +167,7 @@ namespace ts.formatting {
function getParentStart(parent: Node, child: Node, sourceFile: SourceFile): LineAndCharacter {
let containingList = getContainingList(child, sourceFile);
const containingList = getContainingList(child, sourceFile);
if (containingList) {
return sourceFile.getLineAndCharacterOfPosition(containingList.pos);
}
@@ -180,7 +180,7 @@ namespace ts.formatting {
*/
function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: EditorOptions): number {
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
let commaItemInfo = findListItemInfo(commaToken);
const commaItemInfo = findListItemInfo(commaToken);
if (commaItemInfo && commaItemInfo.listItemIndex > 0) {
return deriveActualIndentationFromList(commaItemInfo.list.getChildren(), commaItemInfo.listItemIndex - 1, sourceFile, options);
}
@@ -203,7 +203,7 @@ namespace ts.formatting {
// actual indentation is used for statements\declarations if one of cases below is true:
// - parent is SourceFile - by default immediate children of SourceFile are not indented except when user indents them manually
// - parent and child are not on the same line
let useActualIndentation =
const useActualIndentation =
(isDeclaration(current) || isStatementButNotDeclaration(current)) &&
(parent.kind === SyntaxKind.SourceFile || !parentAndChildShareLine);
@@ -215,7 +215,7 @@ namespace ts.formatting {
}
function nextTokenIsCurlyBraceOnSameLineAsCursor(precedingToken: Node, current: Node, lineAtPosition: number, sourceFile: SourceFile): boolean {
let nextToken = findNextToken(precedingToken, current);
const nextToken = findNextToken(precedingToken, current);
if (!nextToken) {
return false;
}
@@ -234,7 +234,7 @@ namespace ts.formatting {
// class A {
// $}
let nextTokenStartLine = getStartLineAndCharacterForNode(nextToken, sourceFile).line;
const nextTokenStartLine = getStartLineAndCharacterForNode(nextToken, sourceFile).line;
return lineAtPosition === nextTokenStartLine;
}
@@ -247,10 +247,10 @@ namespace ts.formatting {
export function childStartsOnTheSameLineWithElseInIfStatement(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFile): boolean {
if (parent.kind === SyntaxKind.IfStatement && (<IfStatement>parent).elseStatement === child) {
let elseKeyword = findChildOfKind(parent, SyntaxKind.ElseKeyword, sourceFile);
const elseKeyword = findChildOfKind(parent, SyntaxKind.ElseKeyword, sourceFile);
Debug.assert(elseKeyword !== undefined);
let elseKeywordStartLine = getStartLineAndCharacterForNode(elseKeyword, sourceFile).line;
const elseKeywordStartLine = getStartLineAndCharacterForNode(elseKeyword, sourceFile).line;
return elseKeywordStartLine === childStartLine;
}
@@ -277,7 +277,7 @@ namespace ts.formatting {
case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature: {
let start = node.getStart(sourceFile);
const start = node.getStart(sourceFile);
if ((<SignatureDeclaration>node.parent).typeParameters &&
rangeContainsStartEnd((<SignatureDeclaration>node.parent).typeParameters, start, node.getEnd())) {
return (<SignatureDeclaration>node.parent).typeParameters;
@@ -289,7 +289,7 @@ namespace ts.formatting {
}
case SyntaxKind.NewExpression:
case SyntaxKind.CallExpression: {
let start = node.getStart(sourceFile);
const start = node.getStart(sourceFile);
if ((<CallExpression>node.parent).typeArguments &&
rangeContainsStartEnd((<CallExpression>node.parent).typeArguments, start, node.getEnd())) {
return (<CallExpression>node.parent).typeArguments;
@@ -306,11 +306,11 @@ namespace ts.formatting {
}
function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: EditorOptions): number {
let containingList = getContainingList(node, sourceFile);
const containingList = getContainingList(node, sourceFile);
return containingList ? getActualIndentationFromList(containingList) : Value.Unknown;
function getActualIndentationFromList(list: Node[]): number {
let index = indexOf(list, node);
const index = indexOf(list, node);
return index !== -1 ? deriveActualIndentationFromList(list, index, sourceFile, options) : Value.Unknown;
}
}
@@ -327,15 +327,15 @@ namespace ts.formatting {
node.parent.kind === SyntaxKind.NewExpression) &&
(<CallExpression>node.parent).expression !== node) {
let fullCallOrNewExpression = (<CallExpression | NewExpression>node.parent).expression;
let startingExpression = getStartingExpression(<PropertyAccessExpression | CallExpression | ElementAccessExpression>fullCallOrNewExpression);
const fullCallOrNewExpression = (<CallExpression | NewExpression>node.parent).expression;
const startingExpression = getStartingExpression(<PropertyAccessExpression | CallExpression | ElementAccessExpression>fullCallOrNewExpression);
if (fullCallOrNewExpression === startingExpression) {
return Value.Unknown;
}
let fullCallOrNewExpressionEnd = sourceFile.getLineAndCharacterOfPosition(fullCallOrNewExpression.end);
let startingExpressionEnd = sourceFile.getLineAndCharacterOfPosition(startingExpression.end);
const fullCallOrNewExpressionEnd = sourceFile.getLineAndCharacterOfPosition(fullCallOrNewExpression.end);
const startingExpressionEnd = sourceFile.getLineAndCharacterOfPosition(startingExpression.end);
if (fullCallOrNewExpressionEnd.line === startingExpressionEnd.line) {
return Value.Unknown;
@@ -365,17 +365,17 @@ namespace ts.formatting {
function deriveActualIndentationFromList(list: Node[], index: number, sourceFile: SourceFile, options: EditorOptions): number {
Debug.assert(index >= 0 && index < list.length);
let node = list[index];
const node = list[index];
// walk toward the start of the list starting from current node and check if the line is the same for all items.
// if end line for item [i - 1] differs from the start line for item [i] - find column of the first non-whitespace character on the line of item [i]
let lineAndCharacter = getStartLineAndCharacterForNode(node, sourceFile);
for (let i = index - 1; i >= 0; --i) {
for (let i = index - 1; i >= 0; i--) {
if (list[i].kind === SyntaxKind.CommaToken) {
continue;
}
// skip list items that ends on the same line with the current list element
let prevEndLine = sourceFile.getLineAndCharacterOfPosition(list[i].end).line;
const prevEndLine = sourceFile.getLineAndCharacterOfPosition(list[i].end).line;
if (prevEndLine !== lineAndCharacter.line) {
return findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter, sourceFile, options);
}
@@ -386,7 +386,7 @@ namespace ts.formatting {
}
function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: EditorOptions): number {
let lineStart = sourceFile.getPositionOfLineAndCharacter(lineAndCharacter.line, 0);
const lineStart = sourceFile.getPositionOfLineAndCharacter(lineAndCharacter.line, 0);
return findFirstNonWhitespaceColumn(lineStart, lineStart + lineAndCharacter.character, sourceFile, options);
}
@@ -400,8 +400,8 @@ namespace ts.formatting {
export function findFirstNonWhitespaceCharacterAndColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorOptions) {
let character = 0;
let column = 0;
for (let pos = startPos; pos < endPos; ++pos) {
let ch = sourceFile.text.charCodeAt(pos);
for (let pos = startPos; pos < endPos; pos++) {
const ch = sourceFile.text.charCodeAt(pos);
if (!isWhiteSpace(ch)) {
break;
}
@@ -470,7 +470,7 @@ namespace ts.formatting {
/* @internal */
export function nodeWillIndentChild(parent: TextRangeWithKind, child: TextRangeWithKind, indentByDefault: boolean) {
let childKind = child ? child.kind : SyntaxKind.Unknown;
const childKind = child ? child.kind : SyntaxKind.Unknown;
switch (parent.kind) {
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
@@ -497,7 +497,7 @@ namespace ts.formatting {
Function returns true when the parent node should indent the given child by an explicit rule
*/
export function shouldIndentChildNode(parent: TextRangeWithKind, child?: TextRangeWithKind): boolean {
return nodeContentIsAlwaysIndented(parent.kind) || nodeWillIndentChild(parent, child, false);
return nodeContentIsAlwaysIndented(parent.kind) || nodeWillIndentChild(parent, child, /*indentByDefault*/ false);
}
}
}
+2 -2
View File
@@ -2,7 +2,7 @@
/* @internal */
namespace ts.formatting {
export module Shared {
export namespace Shared {
export interface ITokenAccess {
GetTokens(): SyntaxKind[];
Contains(token: SyntaxKind): boolean;
@@ -60,7 +60,7 @@ namespace ts.formatting {
export class TokenAllAccess implements ITokenAccess {
public GetTokens(): SyntaxKind[] {
let result: SyntaxKind[] = [];
const result: SyntaxKind[] = [];
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
result.push(token);
}
+14 -1
View File
@@ -2,7 +2,7 @@
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[] {
export function getNavigateToItems(program: Program, checker: TypeChecker, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number): NavigateToItem[] {
const patternMatcher = createPatternMatcher(searchValue);
let rawItems: RawNavigateToItem[] = [];
@@ -49,6 +49,19 @@ namespace ts.NavigateTo {
}
});
// Remove imports when the imported declaration is already in the list and has the same name.
rawItems = filter(rawItems, item => {
const decl = item.declaration;
if (decl.kind === SyntaxKind.ImportClause || decl.kind === SyntaxKind.ImportSpecifier || decl.kind === SyntaxKind.ImportEqualsDeclaration) {
const importer = checker.getSymbolAtLocation(decl.name);
const imported = checker.getAliasedSymbol(importer);
return importer.name !== imported.name;
}
else {
return true;
}
});
rawItems.sort(compareNavigateToItems);
if (maxResultCount !== undefined) {
rawItems = rawItems.slice(0, maxResultCount);
+124 -72
View File
@@ -9,16 +9,10 @@ namespace ts.NavigationBar {
return getJsNavigationBarItems(sourceFile, compilerOptions);
}
// If the source file has any child items, then it included in the tree
// and takes lexical ownership of all other top-level items.
let hasGlobalNode = false;
return getItemsWorker(getTopLevelNodes(sourceFile), createTopLevelItem);
function getIndent(node: Node): number {
// If we have a global node in the tree,
// then it adds an extra layer of depth to all subnodes.
let indent = hasGlobalNode ? 1 : 0;
let indent = 1; // Global node is the only one with indent 0.
let current = node.parent;
while (current) {
@@ -46,7 +40,7 @@ namespace ts.NavigationBar {
}
function getChildNodes(nodes: Node[]): Node[] {
let childNodes: Node[] = [];
const childNodes: Node[] = [];
function visit(node: Node) {
switch (node.kind) {
@@ -104,12 +98,13 @@ namespace ts.NavigationBar {
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
case SyntaxKind.TypeAliasDeclaration:
childNodes.push(node);
break;
}
}
//for (let i = 0, n = nodes.length; i < n; i++) {
// for (let i = 0, n = nodes.length; i < n; i++) {
// let node = nodes[i];
// if (node.kind === SyntaxKind.ClassDeclaration ||
@@ -123,13 +118,13 @@ namespace ts.NavigationBar {
// else if (node.kind === SyntaxKind.VariableStatement) {
// childNodes.push.apply(childNodes, (<VariableStatement>node).declarations);
// }
//}
// }
forEach(nodes, visit);
return sortNodes(childNodes);
}
function getTopLevelNodes(node: SourceFile): Node[] {
let topLevelNodes: Node[] = [];
const topLevelNodes: Node[] = [];
topLevelNodes.push(node);
addTopLevelNodes(node.statements, topLevelNodes);
@@ -140,7 +135,7 @@ namespace ts.NavigationBar {
function sortNodes(nodes: Node[]): Node[] {
return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => {
if (n1.name && n2.name) {
return getPropertyNameForPropertyNameNode(n1.name).localeCompare(getPropertyNameForPropertyNameNode(n2.name));
return localeCompareFix(getPropertyNameForPropertyNameNode(n1.name), getPropertyNameForPropertyNameNode(n2.name));
}
else if (n1.name) {
return 1;
@@ -152,12 +147,22 @@ namespace ts.NavigationBar {
return n1.kind - n2.kind;
}
});
// node 0.10 treats "a" as greater than "B".
// For consistency, sort alphabetically, falling back to which is lower-case.
function localeCompareFix(a: string, b: string) {
const cmp = a.toLowerCase().localeCompare(b.toLowerCase());
if (cmp !== 0)
return cmp;
// Return the *opposite* of the `<` operator, which works the same in node 0.10 and 6.0.
return a < b ? 1 : a > b ? -1 : 0;
}
}
function addTopLevelNodes(nodes: Node[], topLevelNodes: Node[]): void {
nodes = sortNodes(nodes);
for (let node of nodes) {
for (const node of nodes) {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
topLevelNodes.push(node);
@@ -176,6 +181,7 @@ namespace ts.NavigationBar {
break;
case SyntaxKind.EnumDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
topLevelNodes.push(node);
break;
@@ -197,7 +203,7 @@ namespace ts.NavigationBar {
}
function hasNamedFunctionDeclarations(nodes: NodeArray<Statement>): boolean {
for (let s of nodes) {
for (const s of nodes) {
if (s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((<FunctionDeclaration>s).name.text)) {
return true;
}
@@ -237,17 +243,17 @@ namespace ts.NavigationBar {
}
function getItemsWorker(nodes: Node[], createItem: (n: Node) => ts.NavigationBarItem): ts.NavigationBarItem[] {
let items: ts.NavigationBarItem[] = [];
const items: ts.NavigationBarItem[] = [];
let keyToItem: Map<NavigationBarItem> = {};
const keyToItem: Map<NavigationBarItem> = {};
for (let child of nodes) {
let item = createItem(child);
for (const child of nodes) {
const item = createItem(child);
if (item !== undefined) {
if (item.text.length > 0) {
let key = item.text + "-" + item.kind + "-" + item.indent;
const key = item.text + "-" + item.kind + "-" + item.indent;
let itemWithSameName = keyToItem[key];
const itemWithSameName = keyToItem[key];
if (itemWithSameName) {
// We had an item with the same name. Merge these items together.
merge(itemWithSameName, item);
@@ -274,8 +280,8 @@ namespace ts.NavigationBar {
// Next, recursively merge or add any children in the source as appropriate.
outer:
for (let sourceChild of source.childItems) {
for (let targetChild of target.childItems) {
for (const sourceChild of source.childItems) {
for (const targetChild of target.childItems) {
if (targetChild.text === sourceChild.text && targetChild.kind === sourceChild.kind) {
// Found a match. merge them.
merge(targetChild, sourceChild);
@@ -313,9 +319,21 @@ namespace ts.NavigationBar {
case SyntaxKind.IndexSignature:
return createItem(node, "[]", ts.ScriptElementKind.indexSignatureElement);
case SyntaxKind.EnumDeclaration:
return createItem(node, getTextOfNode((<EnumDeclaration>node).name), ts.ScriptElementKind.enumElement);
case SyntaxKind.EnumMember:
return createItem(node, getTextOfNode((<EnumMember>node).name), ts.ScriptElementKind.memberVariableElement);
case SyntaxKind.ModuleDeclaration:
return createItem(node, getModuleName(<ModuleDeclaration>node), ts.ScriptElementKind.moduleElement);
case SyntaxKind.InterfaceDeclaration:
return createItem(node, getTextOfNode((<InterfaceDeclaration>node).name), ts.ScriptElementKind.interfaceElement);
case SyntaxKind.TypeAliasDeclaration:
return createItem(node, getTextOfNode((<TypeAliasDeclaration>node).name), ts.ScriptElementKind.typeElement);
case SyntaxKind.CallSignature:
return createItem(node, "()", ts.ScriptElementKind.callSignatureElement);
@@ -326,6 +344,9 @@ namespace ts.NavigationBar {
case SyntaxKind.PropertySignature:
return createItem(node, getTextOfNode((<PropertyDeclaration>node).name), ts.ScriptElementKind.memberVariableElement);
case SyntaxKind.ClassDeclaration:
return createItem(node, getTextOfNode((<ClassDeclaration>node).name), ts.ScriptElementKind.classElement);
case SyntaxKind.FunctionDeclaration:
return createItem(node, getTextOfNode((<FunctionLikeDeclaration>node).name), ts.ScriptElementKind.functionElement);
@@ -382,7 +403,7 @@ namespace ts.NavigationBar {
return !text || text.trim() === "";
}
function getNavigationBarItem(text: string, kind: string, kindModifiers: string, spans: TextSpan[], childItems: NavigationBarItem[] = [], indent: number = 0): NavigationBarItem {
function getNavigationBarItem(text: string, kind: string, kindModifiers: string, spans: TextSpan[], childItems: NavigationBarItem[] = [], indent = 0): NavigationBarItem {
if (isEmpty(text)) {
return undefined;
}
@@ -422,34 +443,17 @@ namespace ts.NavigationBar {
case SyntaxKind.FunctionDeclaration:
return createFunctionItem(<FunctionDeclaration>node);
case SyntaxKind.TypeAliasDeclaration:
return createTypeAliasItem(<TypeAliasDeclaration>node);
}
return undefined;
function getModuleName(moduleDeclaration: ModuleDeclaration): string {
// We want to maintain quotation marks.
if (isAmbientModule(moduleDeclaration)) {
return getTextOfNode(moduleDeclaration.name);
}
// Otherwise, we need to aggregate each identifier to build up the qualified name.
let result: string[] = [];
result.push(moduleDeclaration.name.text);
while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
moduleDeclaration = <ModuleDeclaration>moduleDeclaration.body;
result.push(moduleDeclaration.name.text);
}
return result.join(".");
}
function createModuleItem(node: ModuleDeclaration): NavigationBarItem {
let moduleName = getModuleName(node);
const moduleName = getModuleName(node);
let childItems = getItemsWorker(getChildNodes((<Block>getInnermostModule(node).body).statements), createChildItem);
const childItems = getItemsWorker(getChildNodes((<Block>getInnermostModule(node).body).statements), createChildItem);
return getNavigationBarItem(moduleName,
ts.ScriptElementKind.moduleElement,
@@ -461,9 +465,9 @@ namespace ts.NavigationBar {
function createFunctionItem(node: FunctionDeclaration): ts.NavigationBarItem {
if (node.body && node.body.kind === SyntaxKind.Block) {
let childItems = getItemsWorker(sortNodes((<Block>node.body).statements), createChildItem);
const childItems = getItemsWorker(sortNodes((<Block>node.body).statements), createChildItem);
return getNavigationBarItem(!node.name ? "default": node.name.text,
return getNavigationBarItem(!node.name ? "default" : node.name.text,
ts.ScriptElementKind.functionElement,
getNodeModifiers(node),
[getNodeSpan(node)],
@@ -474,9 +478,18 @@ namespace ts.NavigationBar {
return undefined;
}
function createTypeAliasItem(node: TypeAliasDeclaration): ts.NavigationBarItem {
return getNavigationBarItem(node.name.text,
ts.ScriptElementKind.typeElement,
getNodeModifiers(node),
[getNodeSpan(node)],
[],
getIndent(node));
}
function createMemberFunctionLikeItem(node: MethodDeclaration | ConstructorDeclaration): ts.NavigationBarItem {
if (node.body && node.body.kind === SyntaxKind.Block) {
let childItems = getItemsWorker(sortNodes((<Block>node.body).statements), createChildItem);
const childItems = getItemsWorker(sortNodes((<Block>node.body).statements), createChildItem);
let scriptElementKind: string;
let memberFunctionName: string;
if (node.kind === SyntaxKind.MethodDeclaration) {
@@ -500,16 +513,11 @@ namespace ts.NavigationBar {
}
function createSourceFileItem(node: SourceFile): ts.NavigationBarItem {
let childItems = getItemsWorker(getChildNodes(node.statements), createChildItem);
const childItems = getItemsWorker(getChildNodes(node.statements), createChildItem);
if (childItems === undefined || childItems.length === 0) {
return undefined;
}
hasGlobalNode = true;
let rootName = isExternalModule(node)
const rootName = isExternalModule(node)
? "\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(node.fileName)))) + "\""
: "<global>"
: "<global>";
return getNavigationBarItem(rootName,
ts.ScriptElementKind.moduleElement,
@@ -522,14 +530,14 @@ namespace ts.NavigationBar {
let childItems: NavigationBarItem[];
if (node.members) {
let constructor = <ConstructorDeclaration>forEach(node.members, member => {
const constructor = <ConstructorDeclaration>forEach(node.members, member => {
return member.kind === SyntaxKind.Constructor && member;
});
// Add the constructor parameters in as children of the class (for property parameters).
// Note that *all non-binding pattern named* parameters will be added to the nodes array, but parameters that
// are not properties will be filtered out later by createChildItem.
let nodes: Node[] = removeDynamicallyNamedProperties(node);
const nodes: Node[] = removeDynamicallyNamedProperties(node);
if (constructor) {
addRange(nodes, filter(constructor.parameters, p => !isBindingPattern(p.name)));
}
@@ -537,7 +545,7 @@ namespace ts.NavigationBar {
childItems = getItemsWorker(sortNodes(nodes), createChildItem);
}
var nodeName = !node.name ? "default" : node.name.text;
const nodeName = !node.name ? "default" : node.name.text;
return getNavigationBarItem(
nodeName,
@@ -549,7 +557,7 @@ namespace ts.NavigationBar {
}
function createEnumItem(node: EnumDeclaration): ts.NavigationBarItem {
let childItems = getItemsWorker(sortNodes(removeComputedProperties(node)), createChildItem);
const childItems = getItemsWorker(sortNodes(removeComputedProperties(node)), createChildItem);
return getNavigationBarItem(
node.name.text,
ts.ScriptElementKind.enumElement,
@@ -560,7 +568,7 @@ namespace ts.NavigationBar {
}
function createInterfaceItem(node: InterfaceDeclaration): ts.NavigationBarItem {
let childItems = getItemsWorker(sortNodes(removeDynamicallyNamedProperties(node)), createChildItem);
const childItems = getItemsWorker(sortNodes(removeDynamicallyNamedProperties(node)), createChildItem);
return getNavigationBarItem(
node.name.text,
ts.ScriptElementKind.interfaceElement,
@@ -571,6 +579,26 @@ namespace ts.NavigationBar {
}
}
function getModuleName(moduleDeclaration: ModuleDeclaration): string {
// We want to maintain quotation marks.
if (isAmbientModule(moduleDeclaration)) {
return getTextOfNode(moduleDeclaration.name);
}
// Otherwise, we need to aggregate each identifier to build up the qualified name.
const result: string[] = [];
result.push(moduleDeclaration.name.text);
while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
moduleDeclaration = <ModuleDeclaration>moduleDeclaration.body;
result.push(moduleDeclaration.name.text);
}
return result.join(".");
}
function removeComputedProperties(node: EnumDeclaration): Declaration[] {
return filter<Declaration>(node.members, member => member.name === undefined || member.name.kind !== SyntaxKind.ComputedPropertyName);
}
@@ -578,7 +606,7 @@ namespace ts.NavigationBar {
/**
* Like removeComputedProperties, but retains the properties with well known symbol names
*/
function removeDynamicallyNamedProperties(node: ClassDeclaration | InterfaceDeclaration): Declaration[]{
function removeDynamicallyNamedProperties(node: ClassDeclaration | InterfaceDeclaration): Declaration[] {
return filter<Declaration>(node.members, member => !hasDynamicName(member));
}
@@ -606,11 +634,11 @@ namespace ts.NavigationBar {
const anonClassText = "<class>";
let indent = 0;
let rootName = isExternalModule(sourceFile) ?
const rootName = isExternalModule(sourceFile) ?
"\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(sourceFile.fileName)))) + "\""
: "<global>";
let sourceFileItem = getNavBarItem(rootName, ScriptElementKind.moduleElement, [getNodeSpan(sourceFile)]);
const sourceFileItem = getNavBarItem(rootName, ScriptElementKind.moduleElement, [getNodeSpan(sourceFile)]);
let topItem = sourceFileItem;
// Walk the whole file, because we want to also find function expressions - which may be in variable initializer,
@@ -624,6 +652,12 @@ namespace ts.NavigationBar {
topItem.childItems.push(newItem);
}
if (node.jsDocComments && node.jsDocComments.length > 0) {
for (const jsDocComment of node.jsDocComments) {
visitNode(jsDocComment);
}
}
// Add a level if traversing into a container
if (newItem && (isFunctionLike(node) || isClassLike(node))) {
const lastTop = topItem;
@@ -643,12 +677,12 @@ namespace ts.NavigationBar {
}
}
function createNavBarItem(node: Node) : NavigationBarItem {
function createNavBarItem(node: Node): NavigationBarItem {
switch (node.kind) {
case SyntaxKind.VariableDeclaration:
// Only add to the navbar if at the top-level of the file
// Note: "const" and "let" are also SyntaxKind.VariableDeclarations
if(node.parent/*VariableDeclarationList*/.parent/*VariableStatement*/
if (node.parent/*VariableDeclarationList*/.parent/*VariableStatement*/
.parent/*SourceFile*/.kind !== SyntaxKind.SourceFile) {
return undefined;
}
@@ -703,6 +737,27 @@ namespace ts.NavigationBar {
}
const declName = declarationNameToString(decl.name);
return getNavBarItem(declName, ScriptElementKind.constElement, [getNodeSpan(node)]);
case SyntaxKind.JSDocTypedefTag:
if ((<JSDocTypedefTag>node).name) {
return getNavBarItem(
(<JSDocTypedefTag>node).name.text,
ScriptElementKind.typeElement,
[getNodeSpan(node)]);
}
else {
const parentNode = node.parent && node.parent.parent;
if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) {
if ((<VariableStatement>parentNode).declarationList.declarations.length > 0) {
const nameIdentifier = (<VariableStatement>parentNode).declarationList.declarations[0].name;
if (nameIdentifier.kind === SyntaxKind.Identifier) {
return getNavBarItem(
(<Identifier>nameIdentifier).text,
ScriptElementKind.typeElement,
[getNodeSpan(node)]);
}
}
}
}
default:
return undefined;
}
@@ -711,7 +766,7 @@ namespace ts.NavigationBar {
function getNavBarItem(text: string, kind: string, spans: TextSpan[], kindModifiers = ScriptElementKindModifier.none): NavigationBarItem {
return {
text, kind, kindModifiers, spans, childItems: [], indent, bolded: false, grayed: false
}
};
}
function getDefineModuleItem(node: Node): NavigationBarItem {
@@ -724,7 +779,7 @@ namespace ts.NavigationBar {
return undefined;
}
const callExpr = node.parent as CallExpression;
if (callExpr.expression.kind !== SyntaxKind.Identifier || callExpr.expression.getText() !== 'define') {
if (callExpr.expression.kind !== SyntaxKind.Identifier || callExpr.expression.getText() !== "define") {
return undefined;
}
@@ -758,9 +813,6 @@ namespace ts.NavigationBar {
else if (fnExpr.parent.kind === SyntaxKind.BinaryExpression &&
(fnExpr.parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
fnName = (fnExpr.parent as BinaryExpression).left.getText();
if (fnName.length > 20) {
fnName = fnName.substring(0, 17) + "...";
}
}
// See if it is a property assignment, and if so use the property name
else if (fnExpr.parent.kind === SyntaxKind.PropertyAssignment &&
@@ -776,7 +828,7 @@ namespace ts.NavigationBar {
}
function getNodeSpan(node: Node) {
return node.kind === SyntaxKind.SourceFile
return node.kind === SyntaxKind.SourceFile
? createTextSpanFromBounds(node.getFullStart(), node.getEnd())
: createTextSpanFromBounds(node.getStart(), node.getEnd());
}
+385 -181
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -61,6 +61,7 @@ namespace ts {
getLocalizedDiagnosticMessages(): string;
getCancellationToken(): HostCancellationToken;
getCurrentDirectory(): string;
getDirectories(path: string): string[];
getDefaultLibFileName(options: string): string;
getNewLine?(): string;
getProjectVersion?(): string;
@@ -400,6 +401,10 @@ namespace ts {
return this.shimHost.getCurrentDirectory();
}
public getDirectories(path: string): string[] {
return this.shimHost.getDirectories(path);
}
public getDefaultLibFileName(options: CompilerOptions): string {
return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
}
@@ -431,11 +436,15 @@ namespace ts {
export class CoreServicesShimHostAdapter implements ParseConfigHost, ModuleResolutionHost {
public directoryExists: (directoryName: string) => boolean;
public realpath: (path: string) => string;
constructor(private shimHost: CoreServicesShimHost) {
if ("directoryExists" in this.shimHost) {
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
}
if ("realpath" in this.shimHost) {
this.realpath = path => this.shimHost.realpath(path);
}
}
public readDirectory(rootDir: string, extension: string, exclude: string[], depth?: number): string[] {
@@ -997,6 +1006,7 @@ namespace ts {
options: {},
typingOptions: {},
files: [],
raw: {},
errors: [realizeDiagnostic(result.error, "\r\n")]
};
}
@@ -1008,6 +1018,7 @@ namespace ts {
options: configFile.options,
typingOptions: configFile.typingOptions,
files: configFile.fileNames,
raw: configFile.raw,
errors: realizeDiagnostics(configFile.errors, "\r\n")
};
});
+74 -74
View File
@@ -7,7 +7,7 @@ namespace ts.SignatureHelp {
// To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it
// will return the generic identifier that started the expression (e.g. "foo" in "foo<any, |"). It is then up to the caller to ensure that this is a valid generic expression through
// looking up the type. The method will also keep track of the parameter index inside the expression.
//public static isInPartiallyWrittenTypeArgumentList(syntaxTree: TypeScript.SyntaxTree, position: number): any {
// public static isInPartiallyWrittenTypeArgumentList(syntaxTree: TypeScript.SyntaxTree, position: number): any {
// let token = Syntax.findTokenOnLeft(syntaxTree.sourceUnit(), position, /*includeSkippedTokens*/ true);
// if (token && TypeScript.Syntax.hasAncestorOfKind(token, TypeScript.SyntaxKind.TypeParameterList)) {
@@ -125,9 +125,9 @@ namespace ts.SignatureHelp {
// }
// return null;
//}
// }
//private static moveBackUpTillMatchingTokenKind(token: TypeScript.ISyntaxToken, tokenKind: TypeScript.SyntaxKind, matchingTokenKind: TypeScript.SyntaxKind): TypeScript.ISyntaxToken {
// private static moveBackUpTillMatchingTokenKind(token: TypeScript.ISyntaxToken, tokenKind: TypeScript.SyntaxKind, matchingTokenKind: TypeScript.SyntaxKind): TypeScript.ISyntaxToken {
// if (!token || token.kind() !== tokenKind) {
// throw TypeScript.Errors.invalidOperation();
// }
@@ -161,8 +161,8 @@ namespace ts.SignatureHelp {
// // Did not find matching token
// return null;
//}
let emptyArray: any[] = [];
// }
const emptyArray: any[] = [];
const enum ArgumentListKind {
TypeArguments,
@@ -179,16 +179,16 @@ namespace ts.SignatureHelp {
}
export function getSignatureHelpItems(program: Program, sourceFile: SourceFile, position: number, cancellationToken: CancellationToken): SignatureHelpItems {
let typeChecker = program.getTypeChecker();
const typeChecker = program.getTypeChecker();
// Decide whether to show signature help
let startingToken = findTokenOnLeftOfPosition(sourceFile, position);
const startingToken = findTokenOnLeftOfPosition(sourceFile, position);
if (!startingToken) {
// We are at the beginning of the file
return undefined;
}
let argumentInfo = getContainingArgumentInfo(startingToken);
const argumentInfo = getContainingArgumentInfo(startingToken);
cancellationToken.throwIfCancellationRequested();
// Semantic filtering of signature help
@@ -196,9 +196,9 @@ namespace ts.SignatureHelp {
return undefined;
}
let call = argumentInfo.invocation;
let candidates = <Signature[]>[];
let resolvedSignature = typeChecker.getResolvedSignature(call, candidates);
const call = argumentInfo.invocation;
const candidates = <Signature[]>[];
const resolvedSignature = typeChecker.getResolvedSignature(call, candidates);
cancellationToken.throwIfCancellationRequested();
if (!candidates.length) {
@@ -219,9 +219,9 @@ namespace ts.SignatureHelp {
}
// See if we can find some symbol with the call expression name that has call signatures.
let callExpression = <CallExpression>argumentInfo.invocation;
let expression = callExpression.expression;
let name = expression.kind === SyntaxKind.Identifier
const callExpression = <CallExpression>argumentInfo.invocation;
const expression = callExpression.expression;
const name = expression.kind === SyntaxKind.Identifier
? <Identifier> expression
: expression.kind === SyntaxKind.PropertyAccessExpression
? (<PropertyAccessExpression>expression).name
@@ -231,18 +231,18 @@ namespace ts.SignatureHelp {
return undefined;
}
let typeChecker = program.getTypeChecker();
for (let sourceFile of program.getSourceFiles()) {
let nameToDeclarations = sourceFile.getNamedDeclarations();
let declarations = getProperty(nameToDeclarations, name.text);
const typeChecker = program.getTypeChecker();
for (const sourceFile of program.getSourceFiles()) {
const nameToDeclarations = sourceFile.getNamedDeclarations();
const declarations = getProperty(nameToDeclarations, name.text);
if (declarations) {
for (let declaration of declarations) {
let symbol = declaration.symbol;
for (const declaration of declarations) {
const symbol = declaration.symbol;
if (symbol) {
let type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration);
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration);
if (type) {
let callSignatures = type.getCallSignatures();
const callSignatures = type.getCallSignatures();
if (callSignatures && callSignatures.length) {
return createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo);
}
@@ -259,7 +259,7 @@ namespace ts.SignatureHelp {
*/
function getImmediatelyContainingArgumentInfo(node: Node): ArgumentListInfo {
if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) {
let callExpression = <CallExpression>node.parent;
const callExpression = <CallExpression>node.parent;
// There are 3 cases to handle:
// 1. The token introduces a list, and should begin a sig help session
// 2. The token is either not associated with a list, or ends a list, so the session should end
@@ -278,8 +278,8 @@ namespace ts.SignatureHelp {
node.kind === SyntaxKind.OpenParenToken) {
// Find the list that starts right *after* the < or ( token.
// If the user has just opened a list, consider this item 0.
let list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile);
let isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
const list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile);
const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
Debug.assert(list !== undefined);
return {
kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments,
@@ -296,16 +296,16 @@ namespace ts.SignatureHelp {
// - Between the type arguments and the arguments (greater than token)
// - On the target of the call (parent.func)
// - On the 'new' keyword in a 'new' expression
let listItemInfo = findListItemInfo(node);
const listItemInfo = findListItemInfo(node);
if (listItemInfo) {
let list = listItemInfo.list;
let isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
const list = listItemInfo.list;
const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
let argumentIndex = getArgumentIndex(list, node);
let argumentCount = getArgumentCount(list);
const argumentIndex = getArgumentIndex(list, node);
const argumentCount = getArgumentCount(list);
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount,
`argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
`argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
return {
kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments,
@@ -324,18 +324,18 @@ namespace ts.SignatureHelp {
}
}
else if (node.kind === SyntaxKind.TemplateHead && node.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) {
let templateExpression = <TemplateExpression>node.parent;
let tagExpression = <TaggedTemplateExpression>templateExpression.parent;
const templateExpression = <TemplateExpression>node.parent;
const tagExpression = <TaggedTemplateExpression>templateExpression.parent;
Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression);
let argumentIndex = isInsideTemplateLiteral(<LiteralExpression>node, position) ? 0 : 1;
const argumentIndex = isInsideTemplateLiteral(<LiteralExpression>node, position) ? 0 : 1;
return getArgumentListInfoForTemplate(tagExpression, argumentIndex);
}
else if (node.parent.kind === SyntaxKind.TemplateSpan && node.parent.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) {
let templateSpan = <TemplateSpan>node.parent;
let templateExpression = <TemplateExpression>templateSpan.parent;
let tagExpression = <TaggedTemplateExpression>templateExpression.parent;
const templateSpan = <TemplateSpan>node.parent;
const templateExpression = <TemplateExpression>templateSpan.parent;
const tagExpression = <TaggedTemplateExpression>templateExpression.parent;
Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression);
// If we're just after a template tail, don't show signature help.
@@ -343,12 +343,12 @@ namespace ts.SignatureHelp {
return undefined;
}
let spanIndex = templateExpression.templateSpans.indexOf(templateSpan);
let argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node);
const spanIndex = templateExpression.templateSpans.indexOf(templateSpan);
const argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node);
return getArgumentListInfoForTemplate(tagExpression, argumentIndex);
}
return undefined;
}
@@ -365,8 +365,8 @@ namespace ts.SignatureHelp {
// that trailing comma in the list, and we'll have generated the appropriate
// arg index.
let argumentIndex = 0;
let listChildren = argumentsList.getChildren();
for (let child of listChildren) {
const listChildren = argumentsList.getChildren();
for (const child of listChildren) {
if (child === node) {
break;
}
@@ -390,7 +390,7 @@ namespace ts.SignatureHelp {
// we'll have: 'a' '<comma>' '<missing>'
// That will give us 2 non-commas. We then add one for the last comma, givin us an
// arg count of 3.
let listChildren = argumentsList.getChildren();
const listChildren = argumentsList.getChildren();
let argumentCount = countWhere(listChildren, arg => arg.kind !== SyntaxKind.CommaToken);
if (listChildren.length > 0 && lastOrUndefined(listChildren).kind === SyntaxKind.CommaToken) {
@@ -426,11 +426,11 @@ namespace ts.SignatureHelp {
function getArgumentListInfoForTemplate(tagExpression: TaggedTemplateExpression, argumentIndex: number): ArgumentListInfo {
// argumentCount is either 1 or (numSpans + 1) to account for the template strings array argument.
let argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral
const argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral
? 1
: (<TemplateExpression>tagExpression.template).templateSpans.length + 1;
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
return {
kind: ArgumentListKind.TaggedTemplateArguments,
@@ -450,14 +450,14 @@ namespace ts.SignatureHelp {
//
// The applicable span is from the first bar to the second bar (inclusive,
// but not including parentheses)
let applicableSpanStart = argumentsList.getFullStart();
let applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false);
const applicableSpanStart = argumentsList.getFullStart();
const applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false);
return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart);
}
function getApplicableSpanForTaggedTemplate(taggedTemplate: TaggedTemplateExpression): TextSpan {
let template = taggedTemplate.template;
let applicableSpanStart = template.getStart();
const template = taggedTemplate.template;
const applicableSpanStart = template.getStart();
let applicableSpanEnd = template.getEnd();
// We need to adjust the end position for the case where the template does not have a tail.
@@ -470,7 +470,7 @@ namespace ts.SignatureHelp {
// This is because a Missing node has no width. However, what we actually want is to include trivia
// leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail.
if (template.kind === SyntaxKind.TemplateExpression) {
let lastSpan = lastOrUndefined((<TemplateExpression>template).templateSpans);
const lastSpan = lastOrUndefined((<TemplateExpression>template).templateSpans);
if (lastSpan.literal.getFullWidth() === 0) {
applicableSpanEnd = skipTrivia(sourceFile.text, applicableSpanEnd, /*stopAfterLineBreak*/ false);
}
@@ -491,7 +491,7 @@ namespace ts.SignatureHelp {
Debug.fail("Node of kind " + n.kind + " is not a subspan of its parent of kind " + n.parent.kind);
}
let argumentInfo = getImmediatelyContainingArgumentInfo(n);
const argumentInfo = getImmediatelyContainingArgumentInfo(n);
if (argumentInfo) {
return argumentInfo;
}
@@ -503,8 +503,8 @@ namespace ts.SignatureHelp {
}
function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node {
let children = parent.getChildren(sourceFile);
let indexOfOpenerToken = children.indexOf(openerToken);
const children = parent.getChildren(sourceFile);
const indexOfOpenerToken = children.indexOf(openerToken);
Debug.assert(indexOfOpenerToken >= 0 && children.length > indexOfOpenerToken + 1);
return children[indexOfOpenerToken + 1];
}
@@ -521,7 +521,7 @@ namespace ts.SignatureHelp {
let maxParamsSignatureIndex = -1;
let maxParams = -1;
for (let i = 0; i < candidates.length; i++) {
let candidate = candidates[i];
const candidate = candidates[i];
if (candidate.hasRestParameter || candidate.parameters.length >= argumentCount) {
return i;
@@ -537,17 +537,17 @@ namespace ts.SignatureHelp {
}
function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListInfo: ArgumentListInfo): SignatureHelpItems {
let applicableSpan = argumentListInfo.argumentsSpan;
let isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments;
const applicableSpan = argumentListInfo.argumentsSpan;
const isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments;
let invocation = argumentListInfo.invocation;
let callTarget = getInvokedExpression(invocation)
let callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget);
let callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined);
let items: SignatureHelpItem[] = map(candidates, candidateSignature => {
const invocation = argumentListInfo.invocation;
const callTarget = getInvokedExpression(invocation);
const callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget);
const callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined);
const items: SignatureHelpItem[] = map(candidates, candidateSignature => {
let signatureHelpParameters: SignatureHelpParameter[];
let prefixDisplayParts: SymbolDisplayPart[] = [];
let suffixDisplayParts: SymbolDisplayPart[] = [];
const prefixDisplayParts: SymbolDisplayPart[] = [];
const suffixDisplayParts: SymbolDisplayPart[] = [];
if (callTargetDisplayParts) {
addRange(prefixDisplayParts, callTargetDisplayParts);
@@ -555,28 +555,28 @@ namespace ts.SignatureHelp {
if (isTypeParameterList) {
prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken));
let typeParameters = candidateSignature.typeParameters;
const typeParameters = candidateSignature.typeParameters;
signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray;
suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken));
let parameterParts = mapToDisplayParts(writer =>
const parameterParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation));
addRange(suffixDisplayParts, parameterParts);
}
else {
let typeParameterParts = mapToDisplayParts(writer =>
const typeParameterParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation));
addRange(prefixDisplayParts, typeParameterParts);
prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
let parameters = candidateSignature.parameters;
const parameters = candidateSignature.parameters;
signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray;
suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
}
let returnTypeParts = mapToDisplayParts(writer =>
const returnTypeParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildReturnTypeDisplay(candidateSignature, writer, invocation));
addRange(suffixDisplayParts, returnTypeParts);
return {
isVariadic: candidateSignature.hasRestParameter,
prefixDisplayParts,
@@ -587,17 +587,17 @@ namespace ts.SignatureHelp {
};
});
let argumentIndex = argumentListInfo.argumentIndex;
const argumentIndex = argumentListInfo.argumentIndex;
// argumentCount is the *apparent* number of arguments.
let argumentCount = argumentListInfo.argumentCount;
const argumentCount = argumentListInfo.argumentCount;
let selectedItemIndex = candidates.indexOf(bestSignature);
if (selectedItemIndex < 0) {
selectedItemIndex = selectBestInvalidOverloadIndex(candidates, argumentCount);
}
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
return {
items,
@@ -608,7 +608,7 @@ namespace ts.SignatureHelp {
};
function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter {
let displayParts = mapToDisplayParts(writer =>
const displayParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation));
return {
@@ -620,7 +620,7 @@ namespace ts.SignatureHelp {
}
function createSignatureHelpParameterForTypeParameter(typeParameter: TypeParameter): SignatureHelpParameter {
let displayParts = mapToDisplayParts(writer =>
const displayParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(typeParameter, writer, invocation));
return {
+14
View File
@@ -15,6 +15,20 @@
"../compiler/utilities.ts",
"../compiler/binder.ts",
"../compiler/checker.ts",
"../compiler/factory.ts",
"../compiler/visitor.ts",
"../compiler/transformers/ts.ts",
"../compiler/transformers/jsx.ts",
"../compiler/transformers/es7.ts",
"../compiler/transformers/es6.ts",
"../compiler/transformers/generators.ts",
"../compiler/transformers/destructuring.ts",
"../compiler/transformers/module/module.ts",
"../compiler/transformers/module/system.ts",
"../compiler/transformers/module/es6.ts",
"../compiler/transformer.ts",
"../compiler/comments.ts",
"../compiler/sourcemap.ts",
"../compiler/emitter.ts",
"../compiler/program.ts",
"../compiler/declarationEmitter.ts",
+106 -69
View File
@@ -7,8 +7,8 @@ namespace ts {
}
export function getLineStartPositionForPosition(position: number, sourceFile: SourceFile): number {
let lineStarts = sourceFile.getLineStarts();
let line = sourceFile.getLineAndCharacterOfPosition(position).line;
const lineStarts = sourceFile.getLineStarts();
const line = sourceFile.getLineAndCharacterOfPosition(position).line;
return lineStarts[line];
}
@@ -29,8 +29,8 @@ namespace ts {
}
export function startEndOverlapsWithStartEnd(start1: number, end1: number, start2: number, end2: number) {
let start = Math.max(start1, start2);
let end = Math.min(end1, end2);
const start = Math.max(start1, start2);
const end = Math.min(end1, end2);
return start < end;
}
@@ -131,7 +131,7 @@ namespace ts {
return isCompletedNode((<IterationStatement>n).statement, sourceFile);
case SyntaxKind.DoStatement:
// rough approximation: if DoStatement has While keyword - then if node is completed is checking the presence of ')';
let hasWhileKeyword = findChildOfKind(n, SyntaxKind.WhileKeyword, sourceFile);
const hasWhileKeyword = findChildOfKind(n, SyntaxKind.WhileKeyword, sourceFile);
if (hasWhileKeyword) {
return nodeEndsWith(n, SyntaxKind.CloseParenToken, sourceFile);
}
@@ -145,13 +145,13 @@ namespace ts {
case SyntaxKind.VoidExpression:
case SyntaxKind.YieldExpression:
case SyntaxKind.SpreadElementExpression:
let unaryWordExpression = (<TypeOfExpression | DeleteExpression | VoidExpression | YieldExpression | SpreadElementExpression>n);
const unaryWordExpression = (<TypeOfExpression | DeleteExpression | VoidExpression | YieldExpression | SpreadElementExpression>n);
return isCompletedNode(unaryWordExpression.expression, sourceFile);
case SyntaxKind.TaggedTemplateExpression:
return isCompletedNode((<TaggedTemplateExpression>n).template, sourceFile);
case SyntaxKind.TemplateExpression:
let lastSpan = lastOrUndefined((<TemplateExpression>n).templateSpans);
const lastSpan = lastOrUndefined((<TemplateExpression>n).templateSpans);
return isCompletedNode(lastSpan, sourceFile);
case SyntaxKind.TemplateSpan:
return nodeIsPresent((<TemplateSpan>n).literal);
@@ -173,9 +173,9 @@ namespace ts {
* If child at position 'length - 1' is 'SemicolonToken' it is skipped and 'expectedLastToken' is compared with child at position 'length - 2'.
*/
function nodeEndsWith(n: Node, expectedLastToken: SyntaxKind, sourceFile: SourceFile): boolean {
let children = n.getChildren(sourceFile);
const children = n.getChildren(sourceFile);
if (children.length) {
let last = lastOrUndefined(children);
const last = lastOrUndefined(children);
if (last.kind === expectedLastToken) {
return true;
}
@@ -187,7 +187,7 @@ namespace ts {
}
export function findListItemInfo(node: Node): ListItemInfo {
let list = findContainingList(node);
const list = findContainingList(node);
// It is possible at this point for syntaxList to be undefined, either if
// node.parent had no list child, or if none of its list children contained
@@ -197,8 +197,8 @@ namespace ts {
return undefined;
}
let children = list.getChildren();
let listItemIndex = indexOf(children, node);
const children = list.getChildren();
const listItemIndex = indexOf(children, node);
return {
listItemIndex,
@@ -219,7 +219,7 @@ namespace ts {
// be parented by the container of the SyntaxList, not the SyntaxList itself.
// In order to find the list item index, we first need to locate SyntaxList itself and then search
// for the position of the relevant node (or comma).
let syntaxList = forEach(node.parent.getChildren(), c => {
const syntaxList = forEach(node.parent.getChildren(), c => {
// find syntax list that covers the span of the node
if (c.kind === SyntaxKind.SyntaxList && c.pos <= node.pos && c.end >= node.end) {
return c;
@@ -234,29 +234,29 @@ namespace ts {
/* Gets the token whose text has range [start, end) and
* position >= start and (position < end or (position === end && token is keyword or identifier))
*/
export function getTouchingWord(sourceFile: SourceFile, position: number): Node {
return getTouchingToken(sourceFile, position, n => isWord(n.kind));
export function getTouchingWord(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node {
return getTouchingToken(sourceFile, position, n => isWord(n.kind), includeJsDocComment);
}
/* Gets the token whose text has range [start, end) and position >= start
* and (position < end or (position === end && token is keyword or identifier or numeric/string literal))
*/
export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node {
return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind));
export function getTouchingPropertyName(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node {
return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind), includeJsDocComment);
}
/** Returns the token if position is in [start, end) or if position === end and includeItemAtEndPosition(token) === true */
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition);
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean, includeJsDocComment = false): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition, includeJsDocComment);
}
/** Returns a token if position is in [start-of-leading-trivia, end) */
export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined);
export function getTokenAtPosition(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined, includeJsDocComment);
}
/** Get the token whose text contains the position */
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean): Node {
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean, includeJsDocComment = false): Node {
let current: Node = sourceFile;
outer: while (true) {
if (isToken(current)) {
@@ -264,24 +264,49 @@ namespace ts {
return current;
}
if (includeJsDocComment) {
const jsDocChildren = ts.filter(current.getChildren(), isJSDocNode);
for (const jsDocChild of jsDocChildren) {
const start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment);
if (start <= position) {
const end = jsDocChild.getEnd();
if (position < end || (position === end && jsDocChild.kind === SyntaxKind.EndOfFileToken)) {
current = jsDocChild;
continue outer;
}
else if (includeItemAtEndPosition && end === position) {
const previousToken = findPrecedingToken(position, sourceFile, jsDocChild);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
}
}
}
}
// find the child that contains 'position'
for (let i = 0, n = current.getChildCount(sourceFile); i < n; i++) {
let child = current.getChildAt(i);
let start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile);
const child = current.getChildAt(i);
// all jsDocComment nodes were already visited
if (isJSDocNode(child)) {
continue;
}
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, includeJsDocComment);
if (start <= position) {
let end = child.getEnd();
const end = child.getEnd();
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
current = child;
continue outer;
}
else if (includeItemAtEndPosition && end === position) {
let previousToken = findPrecedingToken(position, sourceFile, child);
const previousToken = findPrecedingToken(position, sourceFile, child);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
}
}
}
return current;
}
}
@@ -297,7 +322,7 @@ namespace ts {
export function findTokenOnLeftOfPosition(file: SourceFile, position: number): Node {
// Ideally, getTokenAtPosition should return a token. However, it is currently
// broken, so we do a check to make sure the result was indeed a token.
let tokenAtPosition = getTokenAtPosition(file, position);
const tokenAtPosition = getTokenAtPosition(file, position);
if (isToken(tokenAtPosition) && position > tokenAtPosition.getStart(file) && position < tokenAtPosition.getEnd()) {
return tokenAtPosition;
}
@@ -314,9 +339,9 @@ namespace ts {
return n;
}
let children = n.getChildren();
for (let child of children) {
let shouldDiveInChildNode =
const children = n.getChildren();
for (const child of children) {
const shouldDiveInChildNode =
// previous token is enclosed somewhere in the child
(child.pos <= previousToken.pos && child.end > previousToken.end) ||
// previous token ends exactly at the beginning of child
@@ -339,8 +364,8 @@ namespace ts {
return n;
}
let children = n.getChildren();
let candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length);
const children = n.getChildren();
const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length);
return candidate && findRightmostToken(candidate);
}
@@ -352,7 +377,7 @@ namespace ts {
const children = n.getChildren();
for (let i = 0, len = children.length; i < len; i++) {
let child = children[i];
const child = children[i];
// condition 'position < child.end' checks if child node end after the position
// in the example below this condition will be false for 'aaaa' and 'bbbb' and true for 'ccc'
// aaaa___bbbb___$__ccc
@@ -369,8 +394,8 @@ namespace ts {
if (lookInPreviousChild) {
// actual start of the node is past the position - previous token should be at the end of previous child
let candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i);
return candidate && findRightmostToken(candidate)
const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i);
return candidate && findRightmostToken(candidate);
}
else {
// candidate should be in this node
@@ -386,14 +411,14 @@ namespace ts {
// Try to find the rightmost token in the file without filtering.
// Namely we are skipping the check: 'position < node.end'
if (children.length) {
let candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length);
const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length);
return candidate && findRightmostToken(candidate);
}
}
/// finds last node that is considered as candidate for search (isCandidate(node) === true) starting from 'exclusiveStartPosition'
function findRightmostChildNodeWithTokens(children: Node[], exclusiveStartPosition: number): Node {
for (let i = exclusiveStartPosition - 1; i >= 0; --i) {
for (let i = exclusiveStartPosition - 1; i >= 0; i--) {
if (nodeHasTokens(children[i])) {
return children[i];
}
@@ -402,7 +427,7 @@ namespace ts {
}
export function isInString(sourceFile: SourceFile, position: number) {
let token = getTokenAtPosition(sourceFile, position);
const token = getTokenAtPosition(sourceFile, position);
return token && (token.kind === SyntaxKind.StringLiteral || token.kind === SyntaxKind.StringLiteralType) && position > token.getStart(sourceFile);
}
@@ -414,12 +439,16 @@ namespace ts {
* returns true if the position is in between the open and close elements of an JSX expression.
*/
export function isInsideJsxElementOrAttribute(sourceFile: SourceFile, position: number) {
let token = getTokenAtPosition(sourceFile, position);
const token = getTokenAtPosition(sourceFile, position);
if (!token) {
return false;
}
if (token.kind === SyntaxKind.JsxText) {
return true;
}
// <div>Hello |</div>
if (token.kind === SyntaxKind.LessThanToken && token.parent.kind === SyntaxKind.JsxText) {
return true;
@@ -430,7 +459,7 @@ namespace ts {
return true;
}
// <div> {
// <div> {
// |
// } < /div>
if (token && token.kind === SyntaxKind.CloseBraceToken && token.parent.kind === SyntaxKind.JsxExpression) {
@@ -446,7 +475,7 @@ namespace ts {
}
export function isInTemplateString(sourceFile: SourceFile, position: number) {
let token = getTokenAtPosition(sourceFile, position);
const token = getTokenAtPosition(sourceFile, position);
return isTemplateLiteralKind(token.kind) && position > token.getStart(sourceFile);
}
@@ -455,10 +484,10 @@ namespace ts {
* satisfies predicate, and false otherwise.
*/
export function isInCommentHelper(sourceFile: SourceFile, position: number, predicate?: (c: CommentRange) => boolean): boolean {
let token = getTokenAtPosition(sourceFile, position);
const token = getTokenAtPosition(sourceFile, position);
if (token && position <= token.getStart(sourceFile)) {
let commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
// The end marker of a single-line comment does not include the newline character.
// In the following case, we are inside a comment (^ denotes the cursor position):
@@ -482,16 +511,16 @@ namespace ts {
}
export function hasDocComment(sourceFile: SourceFile, position: number) {
let token = getTokenAtPosition(sourceFile, position);
const token = getTokenAtPosition(sourceFile, position);
// First, we have to see if this position actually landed in a comment.
let commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
return forEach(commentRanges, jsDocPrefix);
function jsDocPrefix(c: CommentRange): boolean {
var text = sourceFile.text;
return text.length >= c.pos + 3 && text[c.pos] === '/' && text[c.pos + 1] === '*' && text[c.pos + 2] === '*';
const text = sourceFile.text;
return text.length >= c.pos + 3 && text[c.pos] === "/" && text[c.pos + 1] === "*" && text[c.pos + 2] === "*";
}
}
@@ -515,11 +544,12 @@ namespace ts {
}
if (node) {
let jsDocComment = node.jsDocComment;
if (jsDocComment) {
for (let tag of jsDocComment.tags) {
if (tag.pos <= position && position <= tag.end) {
return tag;
if (node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.pos <= position && position <= tag.end) {
return tag;
}
}
}
}
@@ -535,8 +565,8 @@ namespace ts {
}
export function getNodeModifiers(node: Node): string {
let flags = getCombinedModifierFlags(node);
let result: string[] = [];
const flags = getCombinedModifierFlags(node);
const result: string[] = [];
if (flags & ModifierFlags.Private) result.push(ScriptElementKindModifier.privateMemberModifier);
if (flags & ModifierFlags.Protected) result.push(ScriptElementKindModifier.protectedMemberModifier);
@@ -546,7 +576,7 @@ namespace ts {
if (flags & ModifierFlags.Export) result.push(ScriptElementKindModifier.exportedModifier);
if (isInAmbientContext(node)) result.push(ScriptElementKindModifier.ambientModifier);
return result.length > 0 ? result.join(',') : ScriptElementKindModifier.none;
return result.length > 0 ? result.join(",") : ScriptElementKindModifier.none;
}
export function getTypeArgumentOrTypeParameterList(node: Node): NodeArray<Node> {
@@ -608,7 +638,7 @@ namespace ts {
}
export function compareDataObjects(dst: any, src: any): boolean {
for (let e in dst) {
for (const e in dst) {
if (typeof dst[e] === "object") {
if (!compareDataObjects(dst[e], src[e])) {
return false;
@@ -661,7 +691,7 @@ namespace ts {
return symbol.declarations && symbol.declarations.length > 0 && symbol.declarations[0].kind === SyntaxKind.Parameter;
}
let displayPartWriter = getDisplayPartWriter();
const displayPartWriter = getDisplayPartWriter();
function getDisplayPartWriter(): DisplayPartsSymbolWriter {
let displayParts: SymbolDisplayPart[];
let lineStart: boolean;
@@ -687,7 +717,7 @@ namespace ts {
function writeIndent() {
if (lineStart) {
let indentString = getIndentString(indent);
const indentString = getIndentString(indent);
if (indentString) {
displayParts.push(displayPart(indentString, SymbolDisplayPartKind.space));
}
@@ -711,7 +741,7 @@ namespace ts {
}
function resetWriter() {
displayParts = []
displayParts = [];
lineStart = true;
indent = 0;
}
@@ -721,7 +751,7 @@ namespace ts {
return displayPart(text, displayPartKind(symbol), symbol);
function displayPartKind(symbol: Symbol): SymbolDisplayPartKind {
let flags = symbol.flags;
const flags = symbol.flags;
if (flags & SymbolFlags.Variable) {
return isFirstDeclarationOfSymbolParameter(symbol) ? SymbolDisplayPartKind.parameterName : SymbolDisplayPartKind.localName;
@@ -768,7 +798,7 @@ namespace ts {
}
export function textOrKeywordPart(text: string) {
var kind = stringToToken(text);
const kind = stringToToken(text);
return kind === undefined
? textPart(text)
: keywordPart(kind);
@@ -792,7 +822,7 @@ namespace ts {
export function mapToDisplayParts(writeDisplayParts: (writer: DisplayPartsSymbolWriter) => void): SymbolDisplayPart[] {
writeDisplayParts(displayPartWriter);
let result = displayPartWriter.displayParts();
const result = displayPartWriter.displayParts();
displayPartWriter.clear();
return result;
}
@@ -821,12 +851,16 @@ namespace ts {
if (isImportOrExportSpecifierName(location)) {
return location.getText();
}
else if (isStringOrNumericLiteral(location.kind) &&
location.parent.kind === SyntaxKind.ComputedPropertyName) {
return (<LiteralExpression>location).text;
}
// Try to get the local symbol if we're dealing with an 'export default'
// since that symbol has the "true" name.
let localExportDefaultSymbol = getLocalSymbolForExportDefault(symbol);
const localExportDefaultSymbol = getLocalSymbolForExportDefault(symbol);
let name = typeChecker.symbolToString(localExportDefaultSymbol || symbol);
const name = typeChecker.symbolToString(localExportDefaultSymbol || symbol);
return name;
}
@@ -843,7 +877,7 @@ namespace ts {
* @return non-quoted string
*/
export function stripQuotes(name: string) {
let length = name.length;
const length = name.length;
if (length >= 2 &&
name.charCodeAt(0) === name.charCodeAt(length - 1) &&
(name.charCodeAt(0) === CharacterCodes.doubleQuote || name.charCodeAt(0) === CharacterCodes.singleQuote)) {
@@ -858,12 +892,15 @@ namespace ts {
}
export function getScriptKind(fileName: string, host?: LanguageServiceHost): ScriptKind {
// First check to see if the script kind can be determined from the file name
var scriptKind = getScriptKindFromFileName(fileName);
if (scriptKind === ScriptKind.Unknown && host && host.getScriptKind) {
// Next check to see if the host can resolve the script kind
// First check to see if the script kind was specified by the host. Chances are the host
// may override the default script kind for the file extension.
let scriptKind: ScriptKind;
if (host && host.getScriptKind) {
scriptKind = host.getScriptKind(fileName);
}
if (!scriptKind || scriptKind === ScriptKind.Unknown) {
scriptKind = getScriptKindFromFileName(fileName);
}
return ensureScriptKind(fileName, scriptKind);
}
}