mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into stringLiteralCompletions
This commit is contained in:
+225
-200
@@ -77,12 +77,14 @@ namespace ts {
|
||||
// Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements...
|
||||
IsBlockScopedContainer = 1 << 1,
|
||||
|
||||
HasLocals = 1 << 2,
|
||||
// The current node is the container of a control flow path. The current control flow should
|
||||
// be saved and restored, and a new control flow initialized within the container.
|
||||
IsControlFlowContainer = 1 << 2,
|
||||
|
||||
// If the current node is a container that also container that also contains locals. Examples:
|
||||
//
|
||||
// Functions, Methods, Modules, Source-files.
|
||||
IsContainerWithLocals = IsContainer | HasLocals
|
||||
IsFunctionLike = 1 << 3,
|
||||
IsFunctionExpression = 1 << 4,
|
||||
HasLocals = 1 << 5,
|
||||
IsInterface = 1 << 6,
|
||||
}
|
||||
|
||||
const binder = createBinder();
|
||||
@@ -103,22 +105,19 @@ namespace ts {
|
||||
let lastContainer: Node;
|
||||
let seenThisKeyword: boolean;
|
||||
|
||||
// state used by reachability checks
|
||||
let hasExplicitReturn: boolean;
|
||||
// state used by control flow analysis
|
||||
let currentFlow: FlowNode;
|
||||
let currentBreakTarget: FlowLabel;
|
||||
let currentContinueTarget: FlowLabel;
|
||||
let currentReturnTarget: FlowLabel;
|
||||
let currentTrueTarget: FlowLabel;
|
||||
let currentFalseTarget: FlowLabel;
|
||||
let preSwitchCaseFlow: FlowNode;
|
||||
let activeLabels: ActiveLabel[];
|
||||
let hasExplicitReturn: boolean;
|
||||
|
||||
// state used for emit helpers
|
||||
let hasClassExtends: boolean;
|
||||
let hasAsyncFunctions: boolean;
|
||||
let hasDecorators: boolean;
|
||||
let hasParameterDecorators: boolean;
|
||||
let hasJsxSpreadAttribute: boolean;
|
||||
let emitFlags: NodeFlags;
|
||||
|
||||
// If this file is an external module, then it is automatically in strict-mode according to
|
||||
// ES6. If it is not an external module, then we'll determine if it is in strict mode or
|
||||
@@ -156,18 +155,15 @@ namespace ts {
|
||||
blockScopeContainer = undefined;
|
||||
lastContainer = undefined;
|
||||
seenThisKeyword = false;
|
||||
hasExplicitReturn = false;
|
||||
currentFlow = undefined;
|
||||
currentBreakTarget = undefined;
|
||||
currentContinueTarget = undefined;
|
||||
currentReturnTarget = undefined;
|
||||
currentTrueTarget = undefined;
|
||||
currentFalseTarget = undefined;
|
||||
activeLabels = undefined;
|
||||
hasClassExtends = false;
|
||||
hasAsyncFunctions = false;
|
||||
hasDecorators = false;
|
||||
hasParameterDecorators = false;
|
||||
hasJsxSpreadAttribute = false;
|
||||
hasExplicitReturn = false;
|
||||
emitFlags = NodeFlags.None;
|
||||
}
|
||||
|
||||
return bindSourceFile;
|
||||
@@ -267,6 +263,18 @@ namespace ts {
|
||||
let functionType = <JSDocFunctionType>node.parent;
|
||||
let index = indexOf(functionType.parameters, node);
|
||||
return "p" + index;
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
const parentNode = node.parent && node.parent.parent;
|
||||
let nameFromParentNode: string;
|
||||
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) {
|
||||
nameFromParentNode = (<Identifier>nameIdentifier).text;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nameFromParentNode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,17 +408,13 @@ namespace ts {
|
||||
// All container nodes are kept on a linked list in declaration order. This list is used by
|
||||
// the getLocalNameOfContainer function in the type checker to validate that the local name
|
||||
// used for a container is unique.
|
||||
function bindChildren(node: Node) {
|
||||
function bindContainer(node: Node, containerFlags: ContainerFlags) {
|
||||
// Before we recurse into a node's children, we first save the existing parent, container
|
||||
// and block-container. Then after we pop out of processing the children, we restore
|
||||
// these saved values.
|
||||
const saveParent = parent;
|
||||
const saveContainer = container;
|
||||
const savedBlockScopeContainer = blockScopeContainer;
|
||||
|
||||
// This node will now be set as the parent of all of its children as we recurse into them.
|
||||
parent = node;
|
||||
|
||||
// Depending on what kind of node this is, we may have to adjust the current container
|
||||
// and block-container. If the current node is a container, then it is automatically
|
||||
// considered the current block-container as well. Also, for containers that we know
|
||||
@@ -428,115 +432,90 @@ namespace ts {
|
||||
// reusing a node from a previous compilation, that node may have had 'locals' created
|
||||
// for it. We must clear this so we don't accidentally move any stale data forward from
|
||||
// a previous compilation.
|
||||
const containerFlags = getContainerFlags(node);
|
||||
if (containerFlags & ContainerFlags.IsContainer) {
|
||||
container = blockScopeContainer = node;
|
||||
|
||||
if (containerFlags & ContainerFlags.HasLocals) {
|
||||
container.locals = {};
|
||||
}
|
||||
|
||||
addToContainerChain(container);
|
||||
}
|
||||
else if (containerFlags & ContainerFlags.IsBlockScopedContainer) {
|
||||
blockScopeContainer = node;
|
||||
blockScopeContainer.locals = undefined;
|
||||
}
|
||||
|
||||
let savedHasExplicitReturn: boolean;
|
||||
let savedCurrentFlow: FlowNode;
|
||||
let savedBreakTarget: FlowLabel;
|
||||
let savedContinueTarget: FlowLabel;
|
||||
let savedActiveLabels: ActiveLabel[];
|
||||
|
||||
const kind = node.kind;
|
||||
let flags = node.flags;
|
||||
|
||||
// reset all reachability check related flags on node (for incremental scenarios)
|
||||
flags &= ~NodeFlags.ReachabilityCheckFlags;
|
||||
|
||||
// reset all emit helper flags on node (for incremental scenarios)
|
||||
flags &= ~NodeFlags.EmitHelperFlags;
|
||||
|
||||
if (kind === SyntaxKind.InterfaceDeclaration) {
|
||||
seenThisKeyword = false;
|
||||
}
|
||||
|
||||
const saveState = kind === SyntaxKind.SourceFile || kind === SyntaxKind.ModuleBlock || isFunctionLikeKind(kind);
|
||||
if (saveState) {
|
||||
savedHasExplicitReturn = hasExplicitReturn;
|
||||
savedCurrentFlow = currentFlow;
|
||||
savedBreakTarget = currentBreakTarget;
|
||||
savedContinueTarget = currentContinueTarget;
|
||||
savedActiveLabels = activeLabels;
|
||||
|
||||
hasExplicitReturn = false;
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
if (containerFlags & ContainerFlags.IsControlFlowContainer) {
|
||||
const saveCurrentFlow = currentFlow;
|
||||
const saveBreakTarget = currentBreakTarget;
|
||||
const saveContinueTarget = currentContinueTarget;
|
||||
const saveReturnTarget = currentReturnTarget;
|
||||
const saveActiveLabels = activeLabels;
|
||||
const saveHasExplicitReturn = hasExplicitReturn;
|
||||
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !!getImmediatelyInvokedFunctionExpression(node);
|
||||
// An IIFE is considered part of the containing control flow. Return statements behave
|
||||
// similarly to break statements that exit to a label just past the statement body.
|
||||
if (isIIFE) {
|
||||
currentReturnTarget = createBranchLabel();
|
||||
}
|
||||
else {
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
if (containerFlags & ContainerFlags.IsFunctionExpression) {
|
||||
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction>node;
|
||||
}
|
||||
currentReturnTarget = undefined;
|
||||
}
|
||||
currentBreakTarget = undefined;
|
||||
currentContinueTarget = undefined;
|
||||
activeLabels = undefined;
|
||||
}
|
||||
|
||||
if (isInJavaScriptFile(node) && node.jsDocComment) {
|
||||
bind(node.jsDocComment);
|
||||
}
|
||||
|
||||
bindReachableStatement(node);
|
||||
|
||||
if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
|
||||
flags |= NodeFlags.HasImplicitReturn;
|
||||
if (hasExplicitReturn) {
|
||||
flags |= NodeFlags.HasExplicitReturn;
|
||||
hasExplicitReturn = false;
|
||||
bindChildren(node);
|
||||
// Reset all reachability check related flags on node (for incremental scenarios)
|
||||
// Reset all emit helper flags on node (for incremental scenarios)
|
||||
node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
|
||||
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
|
||||
node.flags |= NodeFlags.HasImplicitReturn;
|
||||
if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
|
||||
}
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
node.flags |= emitFlags;
|
||||
}
|
||||
if (isIIFE) {
|
||||
addAntecedent(currentReturnTarget, currentFlow);
|
||||
currentFlow = finishFlowLabel(currentReturnTarget);
|
||||
}
|
||||
else {
|
||||
currentFlow = saveCurrentFlow;
|
||||
}
|
||||
currentBreakTarget = saveBreakTarget;
|
||||
currentContinueTarget = saveContinueTarget;
|
||||
currentReturnTarget = saveReturnTarget;
|
||||
activeLabels = saveActiveLabels;
|
||||
hasExplicitReturn = saveHasExplicitReturn;
|
||||
}
|
||||
|
||||
if (kind === SyntaxKind.InterfaceDeclaration) {
|
||||
flags = seenThisKeyword ? flags | NodeFlags.ContainsThis : flags & ~NodeFlags.ContainsThis;
|
||||
else if (containerFlags & ContainerFlags.IsInterface) {
|
||||
seenThisKeyword = false;
|
||||
bindChildren(node);
|
||||
node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis;
|
||||
}
|
||||
|
||||
if (kind === SyntaxKind.SourceFile) {
|
||||
if (hasClassExtends) {
|
||||
flags |= NodeFlags.HasClassExtends;
|
||||
}
|
||||
if (hasDecorators) {
|
||||
flags |= NodeFlags.HasDecorators;
|
||||
}
|
||||
if (hasParameterDecorators) {
|
||||
flags |= NodeFlags.HasParamDecorators;
|
||||
}
|
||||
if (hasAsyncFunctions) {
|
||||
flags |= NodeFlags.HasAsyncFunctions;
|
||||
}
|
||||
if (hasJsxSpreadAttribute) {
|
||||
flags |= NodeFlags.HasJsxSpreadAttribute;
|
||||
}
|
||||
else {
|
||||
bindChildren(node);
|
||||
}
|
||||
|
||||
node.flags = flags;
|
||||
|
||||
if (saveState) {
|
||||
hasExplicitReturn = savedHasExplicitReturn;
|
||||
currentFlow = savedCurrentFlow;
|
||||
currentBreakTarget = savedBreakTarget;
|
||||
currentContinueTarget = savedContinueTarget;
|
||||
activeLabels = savedActiveLabels;
|
||||
}
|
||||
|
||||
container = saveContainer;
|
||||
parent = saveParent;
|
||||
blockScopeContainer = savedBlockScopeContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if node and its subnodes were successfully traversed.
|
||||
* Returning false means that node was not examined and caller needs to dive into the node himself.
|
||||
*/
|
||||
function bindReachableStatement(node: Node): void {
|
||||
function bindChildren(node: Node): void {
|
||||
// Binding of JsDocComment should be done before the current block scope container changes.
|
||||
// because the scope of JsDocComment should not be affected by whether the current node is a
|
||||
// container or not.
|
||||
if (isInJavaScriptFile(node) && node.jsDocComments) {
|
||||
for (const jsDocComment of node.jsDocComments) {
|
||||
bind(jsDocComment);
|
||||
}
|
||||
}
|
||||
if (checkUnreachable(node)) {
|
||||
forEachChild(node, bind);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.WhileStatement:
|
||||
bindWhileStatement(<WhileStatement>node);
|
||||
@@ -580,12 +559,18 @@ namespace ts {
|
||||
case SyntaxKind.BinaryExpression:
|
||||
bindBinaryExpressionFlow(<BinaryExpression>node);
|
||||
break;
|
||||
case SyntaxKind.DeleteExpression:
|
||||
bindDeleteExpressionFlow(<DeleteExpression>node);
|
||||
break;
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
bindConditionalExpressionFlow(<ConditionalExpression>node);
|
||||
break;
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
bindVariableDeclarationFlow(<VariableDeclaration>node);
|
||||
break;
|
||||
case SyntaxKind.CallExpression:
|
||||
bindCallExpressionFlow(<CallExpression>node);
|
||||
break;
|
||||
default:
|
||||
forEachChild(node, bind);
|
||||
break;
|
||||
@@ -696,23 +681,6 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
function skipSimpleConditionalFlow(flow: FlowNode) {
|
||||
// We skip over simple conditional flows of the form 'x ? aaa : bbb', where 'aaa' and 'bbb' contain
|
||||
// no constructs that affect control flow type analysis. Such simple flows have no effect on the
|
||||
// code paths that follow and ignoring them means we'll do less work.
|
||||
if (flow.flags & FlowFlags.BranchLabel && (<FlowLabel>flow).antecedents.length === 2) {
|
||||
const a = (<FlowLabel>flow).antecedents[0];
|
||||
const b = (<FlowLabel>flow).antecedents[1];
|
||||
if ((a.flags & FlowFlags.TrueCondition && b.flags & FlowFlags.FalseCondition ||
|
||||
a.flags & FlowFlags.FalseCondition && b.flags & FlowFlags.TrueCondition) &&
|
||||
(<FlowCondition>a).antecedent === (<FlowCondition>b).antecedent &&
|
||||
(<FlowCondition>a).expression === (<FlowCondition>b).expression) {
|
||||
return (<FlowCondition>a).antecedent;
|
||||
}
|
||||
}
|
||||
return flow;
|
||||
}
|
||||
|
||||
function finishFlowLabel(flow: FlowLabel): FlowNode {
|
||||
const antecedents = flow.antecedents;
|
||||
if (!antecedents) {
|
||||
@@ -721,7 +689,7 @@ namespace ts {
|
||||
if (antecedents.length === 1) {
|
||||
return antecedents[0];
|
||||
}
|
||||
return skipSimpleConditionalFlow(flow);
|
||||
return flow;
|
||||
}
|
||||
|
||||
function isStatementCondition(node: Node) {
|
||||
@@ -862,6 +830,9 @@ namespace ts {
|
||||
bind(node.expression);
|
||||
if (node.kind === SyntaxKind.ReturnStatement) {
|
||||
hasExplicitReturn = true;
|
||||
if (currentReturnTarget) {
|
||||
addAntecedent(currentReturnTarget, currentFlow);
|
||||
}
|
||||
}
|
||||
currentFlow = unreachableFlow;
|
||||
}
|
||||
@@ -926,8 +897,8 @@ namespace ts {
|
||||
preSwitchCaseFlow = currentFlow;
|
||||
bind(node.caseBlock);
|
||||
addAntecedent(postSwitchLabel, currentFlow);
|
||||
const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause);
|
||||
if (!hasDefault) {
|
||||
const hasNonEmptyDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause && c.statements.length);
|
||||
if (!hasNonEmptyDefault) {
|
||||
addAntecedent(postSwitchLabel, preSwitchCaseFlow);
|
||||
}
|
||||
currentBreakTarget = saveBreakTarget;
|
||||
@@ -1072,6 +1043,13 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function bindDeleteExpressionFlow(node: DeleteExpression) {
|
||||
forEachChild(node, bind);
|
||||
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
bindAssignmentTargetFlow(node.expression);
|
||||
}
|
||||
}
|
||||
|
||||
function bindConditionalExpressionFlow(node: ConditionalExpression) {
|
||||
const trueLabel = createBranchLabel();
|
||||
const falseLabel = createBranchLabel();
|
||||
@@ -1105,35 +1083,67 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function bindCallExpressionFlow(node: CallExpression) {
|
||||
// If the target of the call expression is a function expression or arrow function we have
|
||||
// an immediately invoked function expression (IIFE). Initialize the flowNode property to
|
||||
// the current control flow (which includes evaluation of the IIFE arguments).
|
||||
let expr: Expression = node.expression;
|
||||
while (expr.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
expr = (<ParenthesizedExpression>expr).expression;
|
||||
}
|
||||
if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
|
||||
forEach(node.typeArguments, bind);
|
||||
forEach(node.arguments, bind);
|
||||
bind(node.expression);
|
||||
}
|
||||
else {
|
||||
forEachChild(node, bind);
|
||||
}
|
||||
}
|
||||
|
||||
function getContainerFlags(node: Node): ContainerFlags {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.JSDocRecordType:
|
||||
return ContainerFlags.IsContainer;
|
||||
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
|
||||
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
|
||||
|
||||
case SyntaxKind.SourceFile:
|
||||
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;
|
||||
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.CallSignature:
|
||||
case SyntaxKind.ConstructSignature:
|
||||
case SyntaxKind.IndexSignature:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
|
||||
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.SourceFile:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
return ContainerFlags.IsContainerWithLocals;
|
||||
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression;
|
||||
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return ContainerFlags.IsControlFlowContainer;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
return (<PropertyDeclaration>node).initializer ? ContainerFlags.IsControlFlowContainer : 0;
|
||||
|
||||
case SyntaxKind.CatchClause:
|
||||
case SyntaxKind.ForStatement:
|
||||
@@ -1201,6 +1211,7 @@ namespace ts {
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.JSDocRecordType:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
// Interface/Object-types always have their children added to the 'members' of
|
||||
// their container. They are only accessible through an instance of their
|
||||
// container, and are never in scope otherwise (even inside the body of the
|
||||
@@ -1229,7 +1240,7 @@ namespace ts {
|
||||
// their container in the tree. To accomplish this, we simply add their declared
|
||||
// symbol to the 'locals' of the container. These symbols can then be found as
|
||||
// the type checker walks up the containers, checking them for matching names.
|
||||
return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes);
|
||||
return declareSymbol(container.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1567,15 +1578,9 @@ namespace ts {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
node.parent = parent;
|
||||
|
||||
const savedInStrictMode = inStrictMode;
|
||||
if (!savedInStrictMode) {
|
||||
updateStrictMode(node);
|
||||
}
|
||||
|
||||
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
|
||||
const saveInStrictMode = inStrictMode;
|
||||
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
|
||||
// and then potentially add the symbol to an appropriate symbol table. Possible
|
||||
// destination symbol tables are:
|
||||
//
|
||||
@@ -1583,47 +1588,40 @@ namespace ts {
|
||||
// 2) The 'members' table of the current container's symbol.
|
||||
// 3) The 'locals' table of the current container.
|
||||
//
|
||||
// However, not all symbols will end up in any of these tables. 'Anonymous' symbols
|
||||
// However, not all symbols will end up in any of these tables. 'Anonymous' symbols
|
||||
// (like TypeLiterals for example) will not be put in any table.
|
||||
bindWorker(node);
|
||||
|
||||
// Then we recurse into the children of the node to bind them as well. For certain
|
||||
// symbols we do specialized work when we recurse. For example, we'll keep track of
|
||||
// the current 'container' node when it changes. This helps us know which symbol table
|
||||
// a local should go into for example.
|
||||
bindChildren(node);
|
||||
|
||||
inStrictMode = savedInStrictMode;
|
||||
}
|
||||
|
||||
function updateStrictMode(node: Node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
case SyntaxKind.ModuleBlock:
|
||||
updateStrictModeStatementList((<SourceFile | ModuleBlock>node).statements);
|
||||
return;
|
||||
case SyntaxKind.Block:
|
||||
if (isFunctionLike(node.parent)) {
|
||||
updateStrictModeStatementList((<Block>node).statements);
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
// All classes are automatically in strict mode in ES6.
|
||||
inStrictMode = true;
|
||||
return;
|
||||
// Then we recurse into the children of the node to bind them as well. For certain
|
||||
// symbols we do specialized work when we recurse. For example, we'll keep track of
|
||||
// the current 'container' node when it changes. This helps us know which symbol table
|
||||
// a local should go into for example. Since terminal nodes are known not to have
|
||||
// children, as an optimization we don't process those.
|
||||
if (node.kind > SyntaxKind.LastToken) {
|
||||
const saveParent = parent;
|
||||
parent = node;
|
||||
const containerFlags = getContainerFlags(node);
|
||||
if (containerFlags === ContainerFlags.None) {
|
||||
bindChildren(node);
|
||||
}
|
||||
else {
|
||||
bindContainer(node, containerFlags);
|
||||
}
|
||||
parent = saveParent;
|
||||
}
|
||||
inStrictMode = saveInStrictMode;
|
||||
}
|
||||
|
||||
function updateStrictModeStatementList(statements: NodeArray<Statement>) {
|
||||
for (const statement of statements) {
|
||||
if (!isPrologueDirective(statement)) {
|
||||
return;
|
||||
}
|
||||
if (!inStrictMode) {
|
||||
for (const statement of statements) {
|
||||
if (!isPrologueDirective(statement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isUseStrictPrologueDirective(<ExpressionStatement>statement)) {
|
||||
inStrictMode = true;
|
||||
return;
|
||||
if (isUseStrictPrologueDirective(<ExpressionStatement>statement)) {
|
||||
inStrictMode = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1703,6 +1701,8 @@ namespace ts {
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.JSDocRecordMember:
|
||||
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property | ((<PropertyDeclaration>node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
return bindJSDocProperty(<JSDocPropertyTag>node);
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
|
||||
@@ -1710,7 +1710,7 @@ namespace ts {
|
||||
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
|
||||
|
||||
case SyntaxKind.JsxSpreadAttribute:
|
||||
hasJsxSpreadAttribute = true;
|
||||
emitFlags |= NodeFlags.HasJsxSpreadAttribute;
|
||||
return;
|
||||
|
||||
case SyntaxKind.CallSignature:
|
||||
@@ -1738,6 +1738,7 @@ namespace ts {
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.JSDocRecordType:
|
||||
return bindAnonymousDeclaration(<TypeLiteralNode>node, SymbolFlags.TypeLiteral, "__type");
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
@@ -1755,9 +1756,12 @@ namespace ts {
|
||||
// Members of classes, interfaces, and modules
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
// All classes are automatically in strict mode in ES6.
|
||||
inStrictMode = true;
|
||||
return bindClassLikeDeclaration(<ClassLikeDeclaration>node);
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
@@ -1771,8 +1775,8 @@ namespace ts {
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
case SyntaxKind.ExportSpecifier:
|
||||
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
|
||||
case SyntaxKind.GlobalModuleExportDeclaration:
|
||||
return bindGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
|
||||
case SyntaxKind.NamespaceExportDeclaration:
|
||||
return bindNamespaceExportDeclaration(<NamespaceExportDeclaration>node);
|
||||
case SyntaxKind.ImportClause:
|
||||
return bindImportClause(<ImportClause>node);
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
@@ -1780,7 +1784,15 @@ namespace ts {
|
||||
case SyntaxKind.ExportAssignment:
|
||||
return bindExportAssignment(<ExportAssignment>node);
|
||||
case SyntaxKind.SourceFile:
|
||||
updateStrictModeStatementList((<SourceFile>node).statements);
|
||||
return bindSourceFileIfExternalModule();
|
||||
case SyntaxKind.Block:
|
||||
if (!isFunctionLike(node.parent)) {
|
||||
return;
|
||||
}
|
||||
// Fall through
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return updateStrictModeStatementList((<Block | ModuleBlock>node).statements);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1822,7 +1834,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function bindGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration) {
|
||||
function bindNamespaceExportDeclaration(node: NamespaceExportDeclaration) {
|
||||
if (node.modifiers && node.modifiers.length) {
|
||||
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Modifiers_cannot_appear_here));
|
||||
}
|
||||
@@ -1887,12 +1899,20 @@ namespace ts {
|
||||
}
|
||||
|
||||
function bindThisPropertyAssignment(node: BinaryExpression) {
|
||||
// Declare a 'member' in case it turns out the container was an ES5 class
|
||||
if (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.FunctionDeclaration) {
|
||||
container.symbol.members = container.symbol.members || {};
|
||||
// It's acceptable for multiple 'this' assignments of the same identifier to occur
|
||||
declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
|
||||
// Declare a 'member' in case it turns out the container was an ES5 class or ES6 constructor
|
||||
let assignee: Node;
|
||||
if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionDeclaration) {
|
||||
assignee = container;
|
||||
}
|
||||
else if (container.kind === SyntaxKind.Constructor) {
|
||||
assignee = container.parent;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
assignee.symbol.members = assignee.symbol.members || {};
|
||||
// It's acceptable for multiple 'this' assignments of the same identifier to occur
|
||||
declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
|
||||
}
|
||||
|
||||
function bindPrototypePropertyAssignment(node: BinaryExpression) {
|
||||
@@ -1934,10 +1954,10 @@ namespace ts {
|
||||
function bindClassLikeDeclaration(node: ClassLikeDeclaration) {
|
||||
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
|
||||
if (getClassExtendsHeritageClauseElement(node) !== undefined) {
|
||||
hasClassExtends = true;
|
||||
emitFlags |= NodeFlags.HasClassExtends;
|
||||
}
|
||||
if (nodeIsDecorated(node)) {
|
||||
hasDecorators = true;
|
||||
emitFlags |= NodeFlags.HasDecorators;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2013,8 +2033,7 @@ namespace ts {
|
||||
if (!isDeclarationFile(file) &&
|
||||
!isInAmbientContext(node) &&
|
||||
nodeIsDecorated(node)) {
|
||||
hasDecorators = true;
|
||||
hasParameterDecorators = true;
|
||||
emitFlags |= (NodeFlags.HasDecorators | NodeFlags.HasParamDecorators);
|
||||
}
|
||||
|
||||
if (inStrictMode) {
|
||||
@@ -2034,14 +2053,14 @@ namespace ts {
|
||||
// containing class.
|
||||
if (isParameterPropertyDeclaration(node)) {
|
||||
const classDeclaration = <ClassLikeDeclaration>node.parent.parent;
|
||||
declareSymbol(classDeclaration.symbol.members, classDeclaration.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
|
||||
declareSymbol(classDeclaration.symbol.members, classDeclaration.symbol, node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
|
||||
}
|
||||
}
|
||||
|
||||
function bindFunctionDeclaration(node: FunctionDeclaration) {
|
||||
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
|
||||
if (isAsyncFunctionLike(node)) {
|
||||
hasAsyncFunctions = true;
|
||||
emitFlags |= NodeFlags.HasAsyncFunctions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2058,10 +2077,12 @@ namespace ts {
|
||||
function bindFunctionExpression(node: FunctionExpression) {
|
||||
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
|
||||
if (isAsyncFunctionLike(node)) {
|
||||
hasAsyncFunctions = true;
|
||||
emitFlags |= NodeFlags.HasAsyncFunctions;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFlow) {
|
||||
node.flowNode = currentFlow;
|
||||
}
|
||||
checkStrictModeFunctionName(<FunctionExpression>node);
|
||||
const bindingName = (<FunctionExpression>node).name ? (<FunctionExpression>node).name.text : "__function";
|
||||
return bindAnonymousDeclaration(<FunctionExpression>node, SymbolFlags.Function, bindingName);
|
||||
@@ -2070,10 +2091,10 @@ namespace ts {
|
||||
function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
|
||||
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
|
||||
if (isAsyncFunctionLike(node)) {
|
||||
hasAsyncFunctions = true;
|
||||
emitFlags |= NodeFlags.HasAsyncFunctions;
|
||||
}
|
||||
if (nodeIsDecorated(node)) {
|
||||
hasDecorators = true;
|
||||
emitFlags |= NodeFlags.HasDecorators;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2082,6 +2103,10 @@ namespace ts {
|
||||
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
|
||||
}
|
||||
|
||||
function bindJSDocProperty(node: JSDocPropertyTag) {
|
||||
return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
|
||||
}
|
||||
|
||||
// reachability checks
|
||||
|
||||
function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {
|
||||
|
||||
+612
-382
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,11 @@ namespace ts {
|
||||
type: "boolean",
|
||||
description: Diagnostics.Print_this_message,
|
||||
},
|
||||
{
|
||||
name: "help",
|
||||
shortName: "?",
|
||||
type: "boolean"
|
||||
},
|
||||
{
|
||||
name: "init",
|
||||
type: "boolean",
|
||||
@@ -139,6 +144,11 @@ namespace ts {
|
||||
name: "skipDefaultLibCheck",
|
||||
type: "boolean",
|
||||
},
|
||||
{
|
||||
name: "skipLibCheck",
|
||||
type: "boolean",
|
||||
description: Diagnostics.Skip_type_checking_of_declaration_files,
|
||||
},
|
||||
{
|
||||
name: "out",
|
||||
type: "string",
|
||||
@@ -375,6 +385,7 @@ namespace ts {
|
||||
"es2015": "lib.es2015.d.ts",
|
||||
"es7": "lib.es2016.d.ts",
|
||||
"es2016": "lib.es2016.d.ts",
|
||||
"es2017": "lib.es2017.d.ts",
|
||||
// Host only
|
||||
"dom": "lib.dom.d.ts",
|
||||
"webworker": "lib.webworker.d.ts",
|
||||
@@ -389,7 +400,8 @@ namespace ts {
|
||||
"es2015.reflect": "lib.es2015.reflect.d.ts",
|
||||
"es2015.symbol": "lib.es2015.symbol.d.ts",
|
||||
"es2015.symbol.wellknown": "lib.es2015.symbol.wellknown.d.ts",
|
||||
"es2016.array.include": "lib.es2016.array.include.d.ts"
|
||||
"es2016.array.include": "lib.es2016.array.include.d.ts",
|
||||
"es2017.object": "lib.es2017.object.d.ts"
|
||||
},
|
||||
},
|
||||
description: Diagnostics.Specify_library_files_to_be_included_in_the_compilation_Colon
|
||||
@@ -699,11 +711,11 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
// by default exclude node_modules, and any specificied output directory
|
||||
exclude = ["node_modules"];
|
||||
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
|
||||
if (outDir) {
|
||||
exclude.push(outDir);
|
||||
}
|
||||
exclude = ["node_modules", "bower_components", "jspm_packages"];
|
||||
}
|
||||
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
|
||||
if (outDir) {
|
||||
exclude.push(outDir);
|
||||
}
|
||||
exclude = map(exclude, normalizeSlashes);
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace ts {
|
||||
// Emit reference in dts, if the file reference was not already emitted
|
||||
if (referencedFile && !contains(emittedReferencedFiles, referencedFile)) {
|
||||
// Add a reference to generated dts file,
|
||||
// global file reference is added only
|
||||
// global file reference is added only
|
||||
// - if it is not bundled emit (because otherwise it would be self reference)
|
||||
// - and it is not already added
|
||||
if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference)) {
|
||||
@@ -148,7 +148,7 @@ namespace ts {
|
||||
|
||||
if (!isBundledEmit && isExternalModule(sourceFile) && sourceFile.moduleAugmentations.length && !resultHasExternalModuleIndicator) {
|
||||
// if file was external module with augmentations - this fact should be preserved in .d.ts as well.
|
||||
// in case if we didn't write any external module specifiers in .d.ts we need to emit something
|
||||
// in case if we didn't write any external module specifiers in .d.ts we need to emit something
|
||||
// that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here.
|
||||
write("export {};");
|
||||
writeLine();
|
||||
@@ -395,6 +395,7 @@ namespace ts {
|
||||
case SyntaxKind.VoidKeyword:
|
||||
case SyntaxKind.UndefinedKeyword:
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
case SyntaxKind.ThisType:
|
||||
case SyntaxKind.StringLiteralType:
|
||||
return writeTextOfNode(currentText, type);
|
||||
@@ -765,7 +766,7 @@ namespace ts {
|
||||
|
||||
function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) {
|
||||
// emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations).
|
||||
// the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered
|
||||
// the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered
|
||||
// external modules since they are indistinguishable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}'
|
||||
// so compiler will treat them as external modules.
|
||||
resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration;
|
||||
@@ -1051,7 +1052,7 @@ namespace ts {
|
||||
function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) {
|
||||
if (constructorDeclaration) {
|
||||
forEach(constructorDeclaration.parameters, param => {
|
||||
if (param.flags & NodeFlags.AccessibilityModifier) {
|
||||
if (param.flags & NodeFlags.ParameterPropertyModifier) {
|
||||
emitPropertyDeclaration(param);
|
||||
}
|
||||
});
|
||||
@@ -1129,7 +1130,7 @@ namespace ts {
|
||||
// what we want, namely the name expression enclosed in brackets.
|
||||
writeTextOfNode(currentText, node.name);
|
||||
// If optional property emit ?
|
||||
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && hasQuestionToken(node)) {
|
||||
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.Parameter) && hasQuestionToken(node)) {
|
||||
write("?");
|
||||
}
|
||||
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) {
|
||||
|
||||
@@ -315,10 +315,6 @@
|
||||
"category": "Error",
|
||||
"code": 1110
|
||||
},
|
||||
"A class member cannot be declared optional.": {
|
||||
"category": "Error",
|
||||
"code": 1112
|
||||
},
|
||||
"A 'default' clause cannot appear more than once in a 'switch' statement.": {
|
||||
"category": "Error",
|
||||
"code": 1113
|
||||
@@ -447,7 +443,7 @@
|
||||
"category": "Error",
|
||||
"code": 1147
|
||||
},
|
||||
"Cannot compile modules unless the '--module' flag is provided with a valid module type. Consider setting the 'module' compiler option in a 'tsconfig.json' file.": {
|
||||
"Cannot use imports, exports, or module augmentations when '--module' is 'none'.": {
|
||||
"category": "Error",
|
||||
"code": 1148
|
||||
},
|
||||
@@ -823,6 +819,10 @@
|
||||
"category": "Error",
|
||||
"code": 1252
|
||||
},
|
||||
"'{0}' tag cannot be used independently as a top level JSDoc tag.": {
|
||||
"category": "Error",
|
||||
"code": 1253
|
||||
},
|
||||
"'with' statements are not allowed in an async function block.": {
|
||||
"category": "Error",
|
||||
"code": 1300
|
||||
@@ -1751,6 +1751,10 @@
|
||||
"category": "Error",
|
||||
"code": 2533
|
||||
},
|
||||
"A function returning 'never' cannot have a reachable end point.": {
|
||||
"category": "Error",
|
||||
"code": 2534
|
||||
},
|
||||
"JSX element attributes type '{0}' may not be a union type.": {
|
||||
"category": "Error",
|
||||
"code": 2600
|
||||
@@ -1923,6 +1927,14 @@
|
||||
"category": "Error",
|
||||
"code": 2685
|
||||
},
|
||||
"Identifier '{0}' must be imported from a module": {
|
||||
"category": "Error",
|
||||
"code": 2686
|
||||
},
|
||||
"All declarations of '{0}' must have identical modifiers.": {
|
||||
"category": "Error",
|
||||
"code": 2687
|
||||
},
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 4000
|
||||
@@ -2296,7 +2308,7 @@
|
||||
"category": "Error",
|
||||
"code": 5062
|
||||
},
|
||||
"Substututions for pattern '{0}' should be an array.": {
|
||||
"Substitutions for pattern '{0}' should be an array.": {
|
||||
"category": "Error",
|
||||
"code": 5063
|
||||
},
|
||||
@@ -2348,6 +2360,10 @@
|
||||
"category": "Message",
|
||||
"code": 6011
|
||||
},
|
||||
"Skip type checking of declaration files.": {
|
||||
"category": "Message",
|
||||
"code": 6012
|
||||
},
|
||||
"Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES2015'": {
|
||||
"category": "Message",
|
||||
"code": 6015
|
||||
@@ -2751,7 +2767,12 @@
|
||||
"Resolving real path for '{0}', result '{1}'": {
|
||||
"category": "Message",
|
||||
"code": 6130
|
||||
},
|
||||
},
|
||||
"Cannot compile modules using option '{0}' unless the '--module' flag is 'amd' or 'system'.": {
|
||||
"category": "Error",
|
||||
"code": 6131
|
||||
},
|
||||
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
"code": 7005
|
||||
|
||||
+94
-30
@@ -1953,7 +1953,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
write("Object.defineProperty(");
|
||||
emit(tempVar);
|
||||
write(", ");
|
||||
emitStart(node.name);
|
||||
emitStart(property.name);
|
||||
emitExpressionForPropertyName(property.name);
|
||||
emitEnd(property.name);
|
||||
write(", {");
|
||||
@@ -2613,11 +2613,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
return isSourceFileLevelDeclarationInSystemJsModule(targetDeclaration, /*isExported*/ true);
|
||||
}
|
||||
|
||||
function isNameOfExportedDeclarationInNonES6Module(node: Node): boolean {
|
||||
if (modulekind === ModuleKind.System || node.kind !== SyntaxKind.Identifier || nodeIsSynthesized(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, (<Identifier>node).text);
|
||||
}
|
||||
|
||||
function emitPrefixUnaryExpression(node: PrefixUnaryExpression) {
|
||||
const exportChanged = (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) &&
|
||||
const isPlusPlusOrMinusMinus = (node.operator === SyntaxKind.PlusPlusToken
|
||||
|| node.operator === SyntaxKind.MinusMinusToken);
|
||||
const externalExportChanged = isPlusPlusOrMinusMinus &&
|
||||
isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand);
|
||||
|
||||
if (exportChanged) {
|
||||
if (externalExportChanged) {
|
||||
// emit
|
||||
// ++x
|
||||
// as
|
||||
@@ -2626,6 +2636,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
emitNodeWithoutSourceMap(node.operand);
|
||||
write(`", `);
|
||||
}
|
||||
const internalExportChanged = isPlusPlusOrMinusMinus &&
|
||||
isNameOfExportedDeclarationInNonES6Module(node.operand);
|
||||
|
||||
if (internalExportChanged) {
|
||||
emitAliasEqual(<Identifier> node.operand);
|
||||
}
|
||||
|
||||
write(tokenToString(node.operator));
|
||||
// In some cases, we need to emit a space between the operator and the operand. One obvious case
|
||||
@@ -2651,14 +2667,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
}
|
||||
emit(node.operand);
|
||||
|
||||
if (exportChanged) {
|
||||
if (externalExportChanged) {
|
||||
write(")");
|
||||
}
|
||||
}
|
||||
|
||||
function emitPostfixUnaryExpression(node: PostfixUnaryExpression) {
|
||||
const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand);
|
||||
if (exportChanged) {
|
||||
const externalExportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand);
|
||||
const internalExportChanged = isNameOfExportedDeclarationInNonES6Module(node.operand);
|
||||
|
||||
if (externalExportChanged) {
|
||||
// export function returns the value that was passes as the second argument
|
||||
// however for postfix unary expressions result value should be the value before modification.
|
||||
// emit 'x++' as '(export('x', ++x) - 1)' and 'x--' as '(export('x', --x) + 1)'
|
||||
@@ -2676,6 +2694,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
write(") + 1)");
|
||||
}
|
||||
}
|
||||
else if (internalExportChanged) {
|
||||
emitAliasEqual(<Identifier> node.operand);
|
||||
emit(node.operand);
|
||||
if (node.operator === SyntaxKind.PlusPlusToken) {
|
||||
write(" += 1");
|
||||
}
|
||||
else {
|
||||
write(" -= 1");
|
||||
}
|
||||
}
|
||||
else {
|
||||
emit(node.operand);
|
||||
write(tokenToString(node.operator));
|
||||
@@ -2777,24 +2805,50 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
}
|
||||
}
|
||||
|
||||
function emitAliasEqual(name: Identifier): boolean {
|
||||
for (const specifier of exportSpecifiers[name.text]) {
|
||||
emitStart(specifier.name);
|
||||
emitContainingModuleName(specifier);
|
||||
if (languageVersion === ScriptTarget.ES3 && name.text === "default") {
|
||||
write('["default"]');
|
||||
}
|
||||
else {
|
||||
write(".");
|
||||
emitNodeWithCommentsAndWithoutSourcemap(specifier.name);
|
||||
}
|
||||
emitEnd(specifier.name);
|
||||
write(" = ");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function emitBinaryExpression(node: BinaryExpression) {
|
||||
if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken &&
|
||||
(node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) {
|
||||
emitDestructuring(node, node.parent.kind === SyntaxKind.ExpressionStatement);
|
||||
}
|
||||
else {
|
||||
const exportChanged =
|
||||
node.operatorToken.kind >= SyntaxKind.FirstAssignment &&
|
||||
node.operatorToken.kind <= SyntaxKind.LastAssignment &&
|
||||
const isAssignment = isAssignmentOperator(node.operatorToken.kind);
|
||||
|
||||
const externalExportChanged = isAssignment &&
|
||||
isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.left);
|
||||
|
||||
if (exportChanged) {
|
||||
if (externalExportChanged) {
|
||||
// emit assignment 'x <op> y' as 'exports("x", x <op> y)'
|
||||
write(`${exportFunctionForFile}("`);
|
||||
emitNodeWithoutSourceMap(node.left);
|
||||
write(`", `);
|
||||
}
|
||||
|
||||
const internalExportChanged = isAssignment &&
|
||||
isNameOfExportedDeclarationInNonES6Module(node.left);
|
||||
|
||||
if (internalExportChanged) {
|
||||
// export { foo }
|
||||
// emit foo = 2 as exports.foo = foo = 2
|
||||
emitAliasEqual(<Identifier>node.left);
|
||||
}
|
||||
|
||||
if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) {
|
||||
// Downleveled emit exponentiation operator using Math.pow
|
||||
emitExponentiationOperator(node);
|
||||
@@ -2815,7 +2869,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator);
|
||||
}
|
||||
|
||||
if (exportChanged) {
|
||||
if (externalExportChanged) {
|
||||
write(")");
|
||||
}
|
||||
}
|
||||
@@ -4979,7 +5033,7 @@ const _super = (function (geti, seti) {
|
||||
|
||||
function emitParameterPropertyAssignments(node: ConstructorDeclaration) {
|
||||
forEach(node.parameters, param => {
|
||||
if (param.flags & NodeFlags.AccessibilityModifier) {
|
||||
if (param.flags & NodeFlags.ParameterPropertyModifier) {
|
||||
writeLine();
|
||||
emitStart(param);
|
||||
emitStart(param.name);
|
||||
@@ -5359,17 +5413,17 @@ const _super = (function (geti, seti) {
|
||||
//
|
||||
// TypeScript | Javascript
|
||||
// --------------------------------|------------------------------------
|
||||
// @dec | let C_1;
|
||||
// class C { | let C = C_1 = class C {
|
||||
// static x() { return C.y; } | static x() { return C_1.y; }
|
||||
// static y = 1; | }
|
||||
// @dec | let C_1 = class C {
|
||||
// class C { | static x() { return C_1.y; }
|
||||
// static x() { return C.y; } | }
|
||||
// static y = 1; | let C = C_1;
|
||||
// } | C.y = 1;
|
||||
// | C = C_1 = __decorate([dec], C);
|
||||
// --------------------------------|------------------------------------
|
||||
// @dec | let C_1;
|
||||
// export class C { | export let C = C_1 = class C {
|
||||
// static x() { return C.y; } | static x() { return C_1.y; }
|
||||
// static y = 1; | }
|
||||
// @dec | let C_1 = class C {
|
||||
// export class C { | static x() { return C_1.y; }
|
||||
// static x() { return C.y; } | }
|
||||
// static y = 1; | export let C = C_1;
|
||||
// } | C.y = 1;
|
||||
// | C = C_1 = __decorate([dec], C);
|
||||
// ---------------------------------------------------------------------
|
||||
@@ -5398,10 +5452,10 @@ const _super = (function (geti, seti) {
|
||||
//
|
||||
// TypeScript | Javascript
|
||||
// --------------------------------|------------------------------------
|
||||
// @dec | let C_1;
|
||||
// export default class C { | let C = C_1 = class C {
|
||||
// static x() { return C.y; } | static x() { return C_1.y; }
|
||||
// static y = 1; | }
|
||||
// @dec | let C_1 = class C {
|
||||
// export default class C { | static x() { return C_1.y; }
|
||||
// static x() { return C.y; } | };
|
||||
// static y = 1; | let C = C_1;
|
||||
// } | C.y = 1;
|
||||
// | C = C_1 = __decorate([dec], C);
|
||||
// | export default C;
|
||||
@@ -5410,25 +5464,25 @@ const _super = (function (geti, seti) {
|
||||
//
|
||||
|
||||
// NOTE: we reuse the same rewriting logic for cases when targeting ES6 and module kind is System.
|
||||
// Because of hoisting top level class declaration need to be emitted as class expressions.
|
||||
// Because of hoisting top level class declaration need to be emitted as class expressions.
|
||||
// Double bind case is only required if node is decorated.
|
||||
if (isDecorated && resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithBodyScopedClassBinding) {
|
||||
decoratedClassAlias = unescapeIdentifier(makeUniqueName(node.name ? node.name.text : "default"));
|
||||
decoratedClassAliases[getNodeId(node)] = decoratedClassAlias;
|
||||
write(`let ${decoratedClassAlias};`);
|
||||
writeLine();
|
||||
}
|
||||
|
||||
if (isES6ExportedDeclaration(node) && !(node.flags & NodeFlags.Default)) {
|
||||
if (isES6ExportedDeclaration(node) && !(node.flags & NodeFlags.Default) && decoratedClassAlias === undefined) {
|
||||
write("export ");
|
||||
}
|
||||
|
||||
if (!isHoistedDeclarationInSystemModule) {
|
||||
write("let ");
|
||||
}
|
||||
emitDeclarationName(node);
|
||||
if (decoratedClassAlias !== undefined) {
|
||||
write(` = ${decoratedClassAlias}`);
|
||||
write(`${decoratedClassAlias}`);
|
||||
}
|
||||
else {
|
||||
emitDeclarationName(node);
|
||||
}
|
||||
|
||||
write(" = ");
|
||||
@@ -5490,6 +5544,16 @@ const _super = (function (geti, seti) {
|
||||
emitToken(SyntaxKind.CloseBraceToken, node.members.end);
|
||||
|
||||
if (rewriteAsClassExpression) {
|
||||
if (decoratedClassAlias !== undefined) {
|
||||
write(";");
|
||||
writeLine();
|
||||
if (isES6ExportedDeclaration(node) && !(node.flags & NodeFlags.Default)) {
|
||||
write("export ");
|
||||
}
|
||||
write("let ");
|
||||
emitDeclarationName(node);
|
||||
write(` = ${decoratedClassAlias}`);
|
||||
}
|
||||
decoratedClassAliases[getNodeId(node)] = undefined;
|
||||
write(";");
|
||||
}
|
||||
|
||||
+170
-22
@@ -303,8 +303,8 @@ namespace ts {
|
||||
case SyntaxKind.ImportClause:
|
||||
return visitNode(cbNode, (<ImportClause>node).name) ||
|
||||
visitNode(cbNode, (<ImportClause>node).namedBindings);
|
||||
case SyntaxKind.GlobalModuleExportDeclaration:
|
||||
return visitNode(cbNode, (<GlobalModuleExportDeclaration>node).name);
|
||||
case SyntaxKind.NamespaceExportDeclaration:
|
||||
return visitNode(cbNode, (<NamespaceExportDeclaration>node).name);
|
||||
|
||||
case SyntaxKind.NamespaceImport:
|
||||
return visitNode(cbNode, (<NamespaceImport>node).name);
|
||||
@@ -401,6 +401,15 @@ namespace ts {
|
||||
return visitNode(cbNode, (<JSDocTypeTag>node).typeExpression);
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return visitNodes(cbNodes, (<JSDocTemplateTag>node).typeParameters);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
return visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression) ||
|
||||
visitNode(cbNode, (<JSDocTypedefTag>node).name) ||
|
||||
visitNode(cbNode, (<JSDocTypedefTag>node).jsDocTypeLiteral);
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
return visitNodes(cbNodes, (<JSDocTypeLiteral>node).jsDocPropertyTags);
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
return visitNode(cbNode, (<JSDocPropertyTag>node).typeExpression) ||
|
||||
visitNode(cbNode, (<JSDocPropertyTag>node).name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +440,14 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) {
|
||||
return Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
|
||||
const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
|
||||
if (result && result.jsDocComment) {
|
||||
// because the jsDocComment was parsed out of the source file, it might
|
||||
// not be covered by the fixupParentReferences.
|
||||
Parser.fixupParentReferences(result.jsDocComment);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
@@ -628,9 +644,14 @@ namespace ts {
|
||||
if (comments) {
|
||||
for (const comment of comments) {
|
||||
const jsDocComment = JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos);
|
||||
if (jsDocComment) {
|
||||
node.jsDocComment = jsDocComment;
|
||||
if (!jsDocComment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!node.jsDocComments) {
|
||||
node.jsDocComments = [];
|
||||
}
|
||||
node.jsDocComments.push(jsDocComment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -638,14 +659,14 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function fixupParentReferences(sourceFile: Node) {
|
||||
export function fixupParentReferences(rootNode: Node) {
|
||||
// normally parent references are set during binding. However, for clients that only need
|
||||
// a syntax tree, and no semantic features, then the binding process is an unnecessary
|
||||
// overhead. This functions allows us to set all the parents, without all the expense of
|
||||
// binding.
|
||||
|
||||
let parent: Node = sourceFile;
|
||||
forEachChild(sourceFile, visitNode);
|
||||
let parent: Node = rootNode;
|
||||
forEachChild(rootNode, visitNode);
|
||||
return;
|
||||
|
||||
function visitNode(n: Node): void {
|
||||
@@ -658,6 +679,13 @@ namespace ts {
|
||||
const saveParent = parent;
|
||||
parent = n;
|
||||
forEachChild(n, visitNode);
|
||||
if (n.jsDocComments) {
|
||||
for (const jsDocComment of n.jsDocComments) {
|
||||
jsDocComment.parent = n;
|
||||
parent = jsDocComment;
|
||||
forEachChild(jsDocComment, visitNode);
|
||||
}
|
||||
}
|
||||
parent = saveParent;
|
||||
}
|
||||
}
|
||||
@@ -2368,6 +2396,7 @@ namespace ts {
|
||||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.UndefinedKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
// If these are followed by a dot, then parse these out as a dotted type reference instead.
|
||||
const node = tryParse(parseKeywordAndNoDot);
|
||||
return node || parseTypeReference();
|
||||
@@ -2410,6 +2439,7 @@ namespace ts {
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
case SyntaxKind.TypeOfKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
case SyntaxKind.LessThanToken:
|
||||
@@ -2702,7 +2732,7 @@ namespace ts {
|
||||
// 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]
|
||||
// 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]
|
||||
// Production (1) of AsyncArrowFunctionExpression is parsed in "tryParseAsyncSimpleArrowFunctionExpression".
|
||||
// And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression".
|
||||
// And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression".
|
||||
//
|
||||
// If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is
|
||||
// not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done
|
||||
@@ -3394,8 +3424,8 @@ namespace ts {
|
||||
if (sourceFile.languageVariant !== LanguageVariant.JSX) {
|
||||
return false;
|
||||
}
|
||||
// We are in JSX context and the token is part of JSXElement.
|
||||
// Fall through
|
||||
// We are in JSX context and the token is part of JSXElement.
|
||||
// Fall through
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@@ -4097,9 +4127,9 @@ namespace ts {
|
||||
const isAsync = !!(node.flags & NodeFlags.Async);
|
||||
node.name =
|
||||
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
|
||||
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
|
||||
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
|
||||
parseOptionalIdentifier();
|
||||
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
|
||||
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
|
||||
parseOptionalIdentifier();
|
||||
|
||||
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
|
||||
node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
|
||||
@@ -5342,8 +5372,8 @@ namespace ts {
|
||||
return nextToken() === SyntaxKind.SlashToken;
|
||||
}
|
||||
|
||||
function parseGlobalModuleExportDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): GlobalModuleExportDeclaration {
|
||||
const exportDeclaration = <GlobalModuleExportDeclaration>createNode(SyntaxKind.GlobalModuleExportDeclaration, fullStart);
|
||||
function parseGlobalModuleExportDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): NamespaceExportDeclaration {
|
||||
const exportDeclaration = <NamespaceExportDeclaration>createNode(SyntaxKind.NamespaceExportDeclaration, fullStart);
|
||||
exportDeclaration.decorators = decorators;
|
||||
exportDeclaration.modifiers = modifiers;
|
||||
parseExpected(SyntaxKind.AsKeyword);
|
||||
@@ -5889,7 +5919,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkForEmptyTypeArgumentList(typeArguments: NodeArray<Node>) {
|
||||
if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) {
|
||||
if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) {
|
||||
const start = typeArguments.pos - "<".length;
|
||||
const end = skipTrivia(sourceText, typeArguments.end) + ">".length;
|
||||
return parseErrorAtPosition(start, end - start, Diagnostics.Type_argument_list_cannot_be_empty);
|
||||
@@ -6050,7 +6080,6 @@ namespace ts {
|
||||
Debug.assert(end <= content.length);
|
||||
|
||||
let tags: NodeArray<JSDocTag>;
|
||||
|
||||
let result: JSDocComment;
|
||||
|
||||
// Check for /** (JSDoc opening part)
|
||||
@@ -6157,6 +6186,8 @@ namespace ts {
|
||||
return handleTemplateTag(atToken, tagName);
|
||||
case "type":
|
||||
return handleTypeTag(atToken, tagName);
|
||||
case "typedef":
|
||||
return handleTypedefTag(atToken, tagName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6264,6 +6295,122 @@ namespace ts {
|
||||
return finishNode(result);
|
||||
}
|
||||
|
||||
function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag {
|
||||
const typeExpression = tryParseTypeExpression();
|
||||
skipWhitespace();
|
||||
const name = parseJSDocIdentifierName();
|
||||
if (!name) {
|
||||
parseErrorAtPosition(scanner.getStartPos(), /*length*/ 0, Diagnostics.Identifier_expected);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
|
||||
result.atToken = atToken;
|
||||
result.tagName = tagName;
|
||||
result.name = name;
|
||||
result.typeExpression = typeExpression;
|
||||
return finishNode(result);
|
||||
}
|
||||
|
||||
function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag {
|
||||
const typeExpression = tryParseTypeExpression();
|
||||
skipWhitespace();
|
||||
|
||||
const typedefTag = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
|
||||
typedefTag.atToken = atToken;
|
||||
typedefTag.tagName = tagName;
|
||||
typedefTag.name = parseJSDocIdentifierName();
|
||||
typedefTag.typeExpression = typeExpression;
|
||||
|
||||
if (typeExpression) {
|
||||
if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
|
||||
const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
|
||||
if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
|
||||
const name = <Identifier>jsDocTypeReference.name;
|
||||
if (name.text === "Object") {
|
||||
typedefTag.jsDocTypeLiteral = scanChildTags();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!typedefTag.jsDocTypeLiteral) {
|
||||
typedefTag.jsDocTypeLiteral = typeExpression.type;
|
||||
}
|
||||
}
|
||||
else {
|
||||
typedefTag.jsDocTypeLiteral = scanChildTags();
|
||||
}
|
||||
|
||||
return finishNode(typedefTag);
|
||||
|
||||
function scanChildTags(): JSDocTypeLiteral {
|
||||
const jsDocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, scanner.getStartPos());
|
||||
let resumePos = scanner.getStartPos();
|
||||
let canParseTag = true;
|
||||
let seenAsterisk = false;
|
||||
let parentTagTerminated = false;
|
||||
|
||||
while (token !== SyntaxKind.EndOfFileToken && !parentTagTerminated) {
|
||||
nextJSDocToken();
|
||||
switch (token) {
|
||||
case SyntaxKind.AtToken:
|
||||
if (canParseTag) {
|
||||
parentTagTerminated = !tryParseChildTag(jsDocTypeLiteral);
|
||||
}
|
||||
seenAsterisk = false;
|
||||
break;
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
resumePos = scanner.getStartPos() - 1;
|
||||
canParseTag = true;
|
||||
seenAsterisk = false;
|
||||
break;
|
||||
case SyntaxKind.AsteriskToken:
|
||||
if (seenAsterisk) {
|
||||
canParseTag = false;
|
||||
}
|
||||
seenAsterisk = true;
|
||||
break;
|
||||
case SyntaxKind.Identifier:
|
||||
canParseTag = false;
|
||||
case SyntaxKind.EndOfFileToken:
|
||||
break;
|
||||
}
|
||||
}
|
||||
scanner.setTextPos(resumePos);
|
||||
return finishNode(jsDocTypeLiteral);
|
||||
}
|
||||
}
|
||||
|
||||
function tryParseChildTag(parentTag: JSDocTypeLiteral): boolean {
|
||||
Debug.assert(token === SyntaxKind.AtToken);
|
||||
const atToken = createNode(SyntaxKind.AtToken, scanner.getStartPos());
|
||||
atToken.end = scanner.getTextPos();
|
||||
nextJSDocToken();
|
||||
|
||||
const tagName = parseJSDocIdentifierName();
|
||||
if (!tagName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (tagName.text) {
|
||||
case "type":
|
||||
if (parentTag.jsDocTypeTag) {
|
||||
// already has a @type tag, terminate the parent tag now.
|
||||
return false;
|
||||
}
|
||||
parentTag.jsDocTypeTag = handleTypeTag(atToken, tagName);
|
||||
return true;
|
||||
case "prop":
|
||||
case "property":
|
||||
if (!parentTag.jsDocPropertyTags) {
|
||||
parentTag.jsDocPropertyTags = <NodeArray<JSDocPropertyTag>>[];
|
||||
}
|
||||
const propertyTag = handlePropertyTag(atToken, tagName);
|
||||
parentTag.jsDocPropertyTags.push(propertyTag);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag {
|
||||
if (forEach(tags, t => t.kind === SyntaxKind.JSDocTemplateTag)) {
|
||||
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text);
|
||||
@@ -6433,10 +6580,6 @@ namespace ts {
|
||||
node._children = undefined;
|
||||
}
|
||||
|
||||
if (node.jsDocComment) {
|
||||
node.jsDocComment = undefined;
|
||||
}
|
||||
|
||||
node.pos += delta;
|
||||
node.end += delta;
|
||||
|
||||
@@ -6445,6 +6588,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
forEachChild(node, visitNode, visitArray);
|
||||
if (node.jsDocComments) {
|
||||
for (const jsDocComment of node.jsDocComments) {
|
||||
forEachChild(jsDocComment, visitNode, visitArray);
|
||||
}
|
||||
}
|
||||
checkNodePositions(node, aggressiveChecks);
|
||||
}
|
||||
|
||||
|
||||
+70
-34
@@ -196,8 +196,8 @@ namespace ts {
|
||||
traceEnabled
|
||||
};
|
||||
|
||||
// use typesRoot and fallback to directory that contains tsconfig if typesRoot is not set
|
||||
const rootDir = options.typesRoot || (options.configFilePath ? getDirectoryPath(options.configFilePath) : undefined);
|
||||
// use typesRoot and fallback to directory that contains tsconfig or current directory if typesRoot is not set
|
||||
const rootDir = options.typesRoot || (options.configFilePath ? getDirectoryPath(options.configFilePath) : (host.getCurrentDirectory && host.getCurrentDirectory()));
|
||||
|
||||
if (traceEnabled) {
|
||||
if (containingFile === undefined) {
|
||||
@@ -875,6 +875,19 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getDefaultTypeDirectiveNames(rootPath: string): string[] {
|
||||
const localTypes = combinePaths(rootPath, "types");
|
||||
const npmTypes = combinePaths(rootPath, "node_modules/@types");
|
||||
let result: string[] = [];
|
||||
if (sys.directoryExists(localTypes)) {
|
||||
result = result.concat(sys.getDirectories(localTypes));
|
||||
}
|
||||
if (sys.directoryExists(npmTypes)) {
|
||||
result = result.concat(sys.getDirectories(npmTypes));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getDefaultLibLocation(): string {
|
||||
return getDirectoryPath(normalizePath(sys.getExecutingFilePath()));
|
||||
}
|
||||
@@ -883,6 +896,7 @@ namespace ts {
|
||||
const realpath = sys.realpath && ((path: string) => sys.realpath(path));
|
||||
|
||||
return {
|
||||
getDefaultTypeDirectiveNames,
|
||||
getSourceFile,
|
||||
getDefaultLibLocation,
|
||||
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
|
||||
@@ -958,6 +972,23 @@ namespace ts {
|
||||
return resolutions;
|
||||
}
|
||||
|
||||
export function getDefaultTypeDirectiveNames(options: CompilerOptions, rootFiles: string[], host: CompilerHost): string[] {
|
||||
// Use explicit type list from tsconfig.json
|
||||
if (options.types) {
|
||||
return options.types;
|
||||
}
|
||||
|
||||
// or load all types from the automatic type import fields
|
||||
if (host && host.getDefaultTypeDirectiveNames) {
|
||||
const commonRoot = computeCommonSourceDirectoryOfFilenames(rootFiles, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
|
||||
if (commonRoot) {
|
||||
return host.getDefaultTypeDirectiveNames(commonRoot);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
|
||||
let program: Program;
|
||||
let files: SourceFile[] = [];
|
||||
@@ -1005,15 +1036,18 @@ namespace ts {
|
||||
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createFileMap<SourceFile>(fileName => fileName.toLowerCase()) : undefined;
|
||||
|
||||
if (!tryReuseStructureFromOldProgram()) {
|
||||
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));
|
||||
|
||||
// load type declarations specified via 'types' argument
|
||||
if (options.types && options.types.length) {
|
||||
const resolutions = resolveTypeReferenceDirectiveNamesWorker(options.types, /*containingFile*/ undefined);
|
||||
for (let i = 0; i < options.types.length; i++) {
|
||||
processTypeReferenceDirective(options.types[i], resolutions[i]);
|
||||
const typeReferences: string[] = getDefaultTypeDirectiveNames(options, rootNames, host);
|
||||
|
||||
if (typeReferences) {
|
||||
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, /*containingFile*/ undefined);
|
||||
for (let i = 0; i < typeReferences.length; i++) {
|
||||
processTypeReferenceDirective(typeReferences[i], resolutions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));
|
||||
// Do not process the default library if:
|
||||
// - The '--noLib' flag is used.
|
||||
// - A 'no-default-lib' reference comment is encountered in
|
||||
@@ -1039,6 +1073,7 @@ namespace ts {
|
||||
program = {
|
||||
getRootFileNames: () => rootNames,
|
||||
getSourceFile,
|
||||
getSourceFileByPath,
|
||||
getSourceFiles: () => files,
|
||||
getCompilerOptions: () => options,
|
||||
getSyntacticDiagnostics,
|
||||
@@ -1108,6 +1143,7 @@ namespace ts {
|
||||
// if any of these properties has changed - structure cannot be reused
|
||||
const oldOptions = oldProgram.getCompilerOptions();
|
||||
if ((oldOptions.module !== options.module) ||
|
||||
(oldOptions.moduleResolution !== options.moduleResolution) ||
|
||||
(oldOptions.noResolve !== options.noResolve) ||
|
||||
(oldOptions.target !== options.target) ||
|
||||
(oldOptions.noLib !== options.noLib) ||
|
||||
@@ -1115,7 +1151,11 @@ namespace ts {
|
||||
(oldOptions.allowJs !== options.allowJs) ||
|
||||
(oldOptions.rootDir !== options.rootDir) ||
|
||||
(oldOptions.typesSearchPaths !== options.typesSearchPaths) ||
|
||||
(oldOptions.configFilePath !== options.configFilePath)) {
|
||||
(oldOptions.configFilePath !== options.configFilePath) ||
|
||||
(oldOptions.baseUrl !== options.baseUrl) ||
|
||||
(oldOptions.typesRoot !== options.typesRoot) ||
|
||||
!arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) ||
|
||||
!mapIsEqualTo(oldOptions.paths, options.paths)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1137,7 +1177,10 @@ namespace ts {
|
||||
const modifiedSourceFiles: SourceFile[] = [];
|
||||
|
||||
for (const oldSourceFile of oldProgram.getSourceFiles()) {
|
||||
let newSourceFile = host.getSourceFile(oldSourceFile.fileName, options.target);
|
||||
let newSourceFile = host.getSourceFileByPath
|
||||
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
|
||||
: host.getSourceFile(oldSourceFile.fileName, options.target);
|
||||
|
||||
if (!newSourceFile) {
|
||||
return false;
|
||||
}
|
||||
@@ -1232,6 +1275,7 @@ namespace ts {
|
||||
getCurrentDirectory: () => currentDirectory,
|
||||
getNewLine: () => host.getNewLine(),
|
||||
getSourceFile: program.getSourceFile,
|
||||
getSourceFileByPath: program.getSourceFileByPath,
|
||||
getSourceFiles: program.getSourceFiles,
|
||||
writeFile: writeFileCallback || (
|
||||
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
|
||||
@@ -1307,7 +1351,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getSourceFile(fileName: string): SourceFile {
|
||||
return filesByName.get(toPath(fileName, currentDirectory, getCanonicalFileName));
|
||||
return getSourceFileByPath(toPath(fileName, currentDirectory, getCanonicalFileName));
|
||||
}
|
||||
|
||||
function getSourceFileByPath(path: Path): SourceFile {
|
||||
return filesByName.get(path);
|
||||
}
|
||||
|
||||
function getDiagnosticsHelper(
|
||||
@@ -1749,10 +1797,6 @@ namespace ts {
|
||||
reportFileNamesDifferOnlyInCasingError(fileName, file.fileName, refFile, refPos, refEnd);
|
||||
}
|
||||
|
||||
if (file) {
|
||||
file.wasReferenced = file.wasReferenced || isReference;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -1769,7 +1813,6 @@ namespace ts {
|
||||
|
||||
filesByName.set(path, file);
|
||||
if (file) {
|
||||
file.wasReferenced = file.wasReferenced || isReference;
|
||||
file.path = path;
|
||||
|
||||
if (host.useCaseSensitiveFileNames()) {
|
||||
@@ -1895,26 +1938,13 @@ namespace ts {
|
||||
// add file to program only if:
|
||||
// - resolution was successful
|
||||
// - noResolve is falsy
|
||||
// - module name come from the list fo imports
|
||||
// - module name comes from the list of imports
|
||||
const shouldAddFile = resolution &&
|
||||
!options.noResolve &&
|
||||
i < file.imports.length;
|
||||
|
||||
if (shouldAddFile) {
|
||||
const importedFile = findSourceFile(resolution.resolvedFileName, toPath(resolution.resolvedFileName, currentDirectory, getCanonicalFileName), /*isDefaultLib*/ false, /*isReference*/ false, file, skipTrivia(file.text, file.imports[i].pos), file.imports[i].end);
|
||||
|
||||
if (importedFile && resolution.isExternalLibraryImport) {
|
||||
// Since currently irrespective of allowJs, we only look for supportedTypeScript extension external module files,
|
||||
// this check is ok. Otherwise this would be never true for javascript file
|
||||
if (!isExternalModule(importedFile) && importedFile.statements.length) {
|
||||
const start = getTokenPosOfNode(file.imports[i], file);
|
||||
fileProcessingDiagnostics.add(createFileDiagnostic(file, start, file.imports[i].end - start, Diagnostics.Exported_external_package_typings_file_0_is_not_a_module_Please_contact_the_package_author_to_update_the_package_definition, importedFile.fileName));
|
||||
}
|
||||
else if (importedFile.referencedFiles.length) {
|
||||
const firstRef = importedFile.referencedFiles[0];
|
||||
fileProcessingDiagnostics.add(createFileDiagnostic(importedFile, firstRef.pos, firstRef.end - firstRef.pos, Diagnostics.Exported_external_package_typings_file_cannot_contain_tripleslash_references_Please_contact_the_package_author_to_update_the_package_definition));
|
||||
}
|
||||
}
|
||||
findSourceFile(resolution.resolvedFileName, toPath(resolution.resolvedFileName, currentDirectory, getCanonicalFileName), /*isDefaultLib*/ false, /*isReference*/ false, file, skipTrivia(file.text, file.imports[i].pos), file.imports[i].end);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2008,7 +2038,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Substututions_for_pattern_0_should_be_an_array, key));
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2067,7 +2097,7 @@ namespace ts {
|
||||
else if (firstExternalModuleSourceFile && languageVersion < ScriptTarget.ES6 && options.module === ModuleKind.None) {
|
||||
// We cannot use createDiagnosticFromNode because nodes do not have parents yet
|
||||
const span = getErrorSpanForNode(firstExternalModuleSourceFile, firstExternalModuleSourceFile.externalModuleIndicator);
|
||||
programDiagnostics.add(createFileDiagnostic(firstExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_modules_unless_the_module_flag_is_provided_with_a_valid_module_type_Consider_setting_the_module_compiler_option_in_a_tsconfig_json_file));
|
||||
programDiagnostics.add(createFileDiagnostic(firstExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_use_imports_exports_or_module_augmentations_when_module_is_none));
|
||||
}
|
||||
|
||||
// Cannot specify module gen target of es6 when below es6
|
||||
@@ -2076,8 +2106,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Cannot specify module gen that isn't amd or system with --out
|
||||
if (outFile && options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile"));
|
||||
if (outFile) {
|
||||
if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile"));
|
||||
}
|
||||
else if (options.module === undefined && firstExternalModuleSourceFile) {
|
||||
const span = getErrorSpanForNode(firstExternalModuleSourceFile, firstExternalModuleSourceFile.externalModuleIndicator);
|
||||
programDiagnostics.add(createFileDiagnostic(firstExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_modules_using_option_0_unless_the_module_flag_is_amd_or_system, options.out ? "out" : "outFile"));
|
||||
}
|
||||
}
|
||||
|
||||
// there has to be common source directory if user specified --outdir || --sourceRoot
|
||||
|
||||
@@ -91,6 +91,7 @@ namespace ts {
|
||||
"let": SyntaxKind.LetKeyword,
|
||||
"module": SyntaxKind.ModuleKeyword,
|
||||
"namespace": SyntaxKind.NamespaceKeyword,
|
||||
"never": SyntaxKind.NeverKeyword,
|
||||
"new": SyntaxKind.NewKeyword,
|
||||
"null": SyntaxKind.NullKeyword,
|
||||
"number": SyntaxKind.NumberKeyword,
|
||||
@@ -433,7 +434,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean): number {
|
||||
export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean, stopAtComments = false): number {
|
||||
// Using ! with a greater than test is a fast way of testing the following conditions:
|
||||
// pos === undefined || pos === null || isNaN(pos) || pos < 0;
|
||||
if (!(pos >= 0)) {
|
||||
@@ -461,6 +462,9 @@ namespace ts {
|
||||
pos++;
|
||||
continue;
|
||||
case CharacterCodes.slash:
|
||||
if (stopAtComments) {
|
||||
break;
|
||||
}
|
||||
if (text.charCodeAt(pos + 1) === CharacterCodes.slash) {
|
||||
pos += 2;
|
||||
while (pos < text.length) {
|
||||
|
||||
@@ -168,7 +168,7 @@ namespace ts {
|
||||
sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] :
|
||||
defaultLastEncodedSourceMapSpan;
|
||||
|
||||
// TODO: Update lastEncodedNameIndex
|
||||
// TODO: Update lastEncodedNameIndex
|
||||
// Since we dont support this any more, lets not worry about it right now.
|
||||
// When we start supporting nameIndex, we will get back to this
|
||||
|
||||
|
||||
+15
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace ts {
|
||||
export type FileWatcherCallback = (fileName: string, removed?: boolean) => void;
|
||||
export type DirectoryWatcherCallback = (directoryName: string) => void;
|
||||
export type DirectoryWatcherCallback = (fileName: string) => void;
|
||||
export interface WatchedFile {
|
||||
fileName: string;
|
||||
callback: FileWatcherCallback;
|
||||
@@ -24,6 +24,7 @@ namespace ts {
|
||||
createDirectory(path: string): void;
|
||||
getExecutingFilePath(): string;
|
||||
getCurrentDirectory(): string;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extension?: string, exclude?: string[]): string[];
|
||||
getModifiedTime?(path: string): Date;
|
||||
createHash?(data: string): string;
|
||||
@@ -71,6 +72,7 @@ namespace ts {
|
||||
resolvePath(path: string): string;
|
||||
readFile(path: string): string;
|
||||
writeFile(path: string, contents: string): void;
|
||||
getDirectories(path: string): string[];
|
||||
readDirectory(path: string, extension?: string, exclude?: string[]): string[];
|
||||
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
|
||||
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
|
||||
@@ -161,6 +163,11 @@ namespace ts {
|
||||
return result.sort();
|
||||
}
|
||||
|
||||
function getDirectories(path: string): string[] {
|
||||
const folder = fso.GetFolder(path);
|
||||
return getNames(folder.subfolders);
|
||||
}
|
||||
|
||||
function readDirectory(path: string, extension?: string, exclude?: string[]): string[] {
|
||||
const result: string[] = [];
|
||||
exclude = map(exclude, s => getCanonicalPath(combinePaths(path, s)));
|
||||
@@ -214,6 +221,7 @@ namespace ts {
|
||||
getCurrentDirectory() {
|
||||
return new ActiveXObject("WScript.Shell").CurrentDirectory;
|
||||
},
|
||||
getDirectories,
|
||||
readDirectory,
|
||||
exit(exitCode?: number): void {
|
||||
try {
|
||||
@@ -402,6 +410,10 @@ namespace ts {
|
||||
return fileSystemEntryExists(path, FileSystemEntryKind.Directory);
|
||||
}
|
||||
|
||||
function getDirectories(path: string): string[] {
|
||||
return filter<string>(_fs.readdirSync(path), p => fileSystemEntryExists(combinePaths(path, p), FileSystemEntryKind.Directory));
|
||||
}
|
||||
|
||||
function readDirectory(path: string, extension?: string, exclude?: string[]): string[] {
|
||||
const result: string[] = [];
|
||||
exclude = map(exclude, s => getCanonicalPath(combinePaths(path, s)));
|
||||
@@ -507,6 +519,7 @@ namespace ts {
|
||||
getCurrentDirectory() {
|
||||
return process.cwd();
|
||||
},
|
||||
getDirectories,
|
||||
readDirectory,
|
||||
getModifiedTime(path) {
|
||||
try {
|
||||
@@ -561,6 +574,7 @@ namespace ts {
|
||||
createDirectory: ChakraHost.createDirectory,
|
||||
getExecutingFilePath: () => ChakraHost.executingFile,
|
||||
getCurrentDirectory: () => ChakraHost.currentDirectory,
|
||||
getDirectories: ChakraHost.getDirectories,
|
||||
readDirectory: ChakraHost.readDirectory,
|
||||
exit: ChakraHost.quit,
|
||||
realpath
|
||||
|
||||
+64
-13
@@ -164,6 +164,7 @@ namespace ts {
|
||||
IsKeyword,
|
||||
ModuleKeyword,
|
||||
NamespaceKeyword,
|
||||
NeverKeyword,
|
||||
ReadonlyKeyword,
|
||||
RequireKeyword,
|
||||
NumberKeyword,
|
||||
@@ -276,7 +277,7 @@ namespace ts {
|
||||
ModuleDeclaration,
|
||||
ModuleBlock,
|
||||
CaseBlock,
|
||||
GlobalModuleExportDeclaration,
|
||||
NamespaceExportDeclaration,
|
||||
ImportEqualsDeclaration,
|
||||
ImportDeclaration,
|
||||
ImportClause,
|
||||
@@ -342,6 +343,9 @@ namespace ts {
|
||||
JSDocReturnTag,
|
||||
JSDocTypeTag,
|
||||
JSDocTemplateTag,
|
||||
JSDocTypedefTag,
|
||||
JSDocPropertyTag,
|
||||
JSDocTypeLiteral,
|
||||
|
||||
// Synthesized list
|
||||
SyntaxList,
|
||||
@@ -371,6 +375,10 @@ namespace ts {
|
||||
FirstBinaryOperator = LessThanToken,
|
||||
LastBinaryOperator = CaretEqualsToken,
|
||||
FirstNode = QualifiedName,
|
||||
FirstJSDocNode = JSDocTypeExpression,
|
||||
LastJSDocNode = JSDocTypeLiteral,
|
||||
FirstJSDocTagNode = JSDocComment,
|
||||
LastJSDocTagNode = JSDocTypeLiteral
|
||||
}
|
||||
|
||||
export const enum NodeFlags {
|
||||
@@ -407,12 +415,15 @@ namespace ts {
|
||||
HasAggregatedChildData = 1 << 29, // If we've computed data from children and cached it in this node
|
||||
HasJsxSpreadAttribute = 1 << 30,
|
||||
|
||||
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async,
|
||||
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async | Readonly,
|
||||
AccessibilityModifier = Public | Private | Protected,
|
||||
// Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property.
|
||||
ParameterPropertyModifier = AccessibilityModifier | Readonly,
|
||||
BlockScoped = Let | Const,
|
||||
|
||||
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
|
||||
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions,
|
||||
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
|
||||
|
||||
// Parsing context flags
|
||||
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
|
||||
@@ -445,7 +456,7 @@ namespace ts {
|
||||
modifiers?: ModifiersArray; // Array of modifiers
|
||||
/* @internal */ id?: number; // Unique id (used to look up NodeLinks)
|
||||
parent?: Node; // Parent node (initialized by binding
|
||||
/* @internal */ jsDocComment?: JSDocComment; // JSDoc for the node, if it has any. Only for .js files.
|
||||
/* @internal */ jsDocComments?: JSDocComment[]; // JSDoc for the node, if it has any. Only for .js files.
|
||||
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
|
||||
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
|
||||
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
|
||||
@@ -609,6 +620,7 @@ namespace ts {
|
||||
// SyntaxKind.PropertyAssignment
|
||||
// SyntaxKind.ShorthandPropertyAssignment
|
||||
// SyntaxKind.EnumMember
|
||||
// SyntaxKind.JSDocPropertyTag
|
||||
export interface VariableLikeDeclaration extends Declaration {
|
||||
propertyName?: PropertyName;
|
||||
dotDotDotToken?: Node;
|
||||
@@ -1338,8 +1350,8 @@ namespace ts {
|
||||
name: Identifier;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.GlobalModuleImport)
|
||||
export interface GlobalModuleExportDeclaration extends DeclarationStatement {
|
||||
// @kind(SyntaxKind.NamespaceExportDeclaration)
|
||||
export interface NamespaceExportDeclaration extends DeclarationStatement {
|
||||
name: Identifier;
|
||||
moduleReference: LiteralLikeNode;
|
||||
}
|
||||
@@ -1507,6 +1519,25 @@ namespace ts {
|
||||
typeExpression: JSDocTypeExpression;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.JSDocTypedefTag)
|
||||
export interface JSDocTypedefTag extends JSDocTag, Declaration {
|
||||
name?: Identifier;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
jsDocTypeLiteral?: JSDocTypeLiteral;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.JSDocPropertyTag)
|
||||
export interface JSDocPropertyTag extends JSDocTag, TypeElement {
|
||||
name: Identifier;
|
||||
typeExpression: JSDocTypeExpression;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.JSDocTypeLiteral)
|
||||
export interface JSDocTypeLiteral extends JSDocType {
|
||||
jsDocPropertyTags?: NodeArray<JSDocPropertyTag>;
|
||||
jsDocTypeTag?: JSDocTypeTag;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.JSDocParameterTag)
|
||||
export interface JSDocParameterTag extends JSDocTag {
|
||||
preParameterName?: Identifier;
|
||||
@@ -1534,6 +1565,13 @@ namespace ts {
|
||||
id?: number; // Node id used by flow type cache in checker
|
||||
}
|
||||
|
||||
// FlowStart represents the start of a control flow. For a function expression or arrow
|
||||
// function, the container property references the function (which in turn has a flowNode
|
||||
// property for the containing control flow).
|
||||
export interface FlowStart extends FlowNode {
|
||||
container?: FunctionExpression | ArrowFunction;
|
||||
}
|
||||
|
||||
// FlowLabel represents a junction with multiple possible preceding control flows.
|
||||
export interface FlowLabel extends FlowNode {
|
||||
antecedents: FlowNode[];
|
||||
@@ -1596,8 +1634,6 @@ namespace ts {
|
||||
/* @internal */ externalModuleIndicator: Node;
|
||||
// The first node that causes this file to be a CommonJS module
|
||||
/* @internal */ commonJsModuleIndicator: Node;
|
||||
// True if the file was a root file in a compilation or a /// reference targets
|
||||
/* @internal */ wasReferenced?: boolean;
|
||||
|
||||
/* @internal */ identifiers: Map<string>;
|
||||
/* @internal */ nodeCount: number;
|
||||
@@ -1627,6 +1663,7 @@ namespace ts {
|
||||
export interface ScriptReferenceHost {
|
||||
getCompilerOptions(): CompilerOptions;
|
||||
getSourceFile(fileName: string): SourceFile;
|
||||
getSourceFileByPath(path: Path): SourceFile;
|
||||
getCurrentDirectory(): string;
|
||||
}
|
||||
|
||||
@@ -1769,6 +1806,7 @@ namespace ts {
|
||||
getIndexTypeOfType(type: Type, kind: IndexKind): Type;
|
||||
getBaseTypes(type: InterfaceType): ObjectType[];
|
||||
getReturnTypeOfSignature(signature: Signature): Type;
|
||||
getNonNullableType(type: Type): Type;
|
||||
|
||||
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
|
||||
getSymbolAtLocation(node: Node): Symbol;
|
||||
@@ -2022,7 +2060,7 @@ namespace ts {
|
||||
BlockScopedVariableExcludes = Value,
|
||||
|
||||
ParameterExcludes = Value,
|
||||
PropertyExcludes = Value,
|
||||
PropertyExcludes = None,
|
||||
EnumMemberExcludes = Value,
|
||||
FunctionExcludes = Value & ~(Function | ValueModule),
|
||||
ClassExcludes = (Value | Type) & ~(ValueModule | Interface), // class-interface mergability done in checker.ts
|
||||
@@ -2159,7 +2197,7 @@ namespace ts {
|
||||
/* @internal */
|
||||
FreshObjectLiteral = 0x00100000, // Fresh object literal type
|
||||
/* @internal */
|
||||
ContainsUndefinedOrNull = 0x00200000, // Type is or contains undefined or null type
|
||||
ContainsWideningType = 0x00200000, // Type is or contains undefined or null widening type
|
||||
/* @internal */
|
||||
ContainsObjectLiteral = 0x00400000, // Type is or contains object literal type
|
||||
/* @internal */
|
||||
@@ -2167,11 +2205,12 @@ namespace ts {
|
||||
ESSymbol = 0x01000000, // Type of symbol primitive introduced in ES6
|
||||
ThisType = 0x02000000, // This type
|
||||
ObjectLiteralPatternWithComputedProperties = 0x04000000, // Object literal type implied by binding pattern has computed properties
|
||||
Never = 0x08000000, // Never type
|
||||
|
||||
/* @internal */
|
||||
Nullable = Undefined | Null,
|
||||
/* @internal */
|
||||
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
|
||||
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null | Never,
|
||||
/* @internal */
|
||||
Primitive = String | Number | Boolean | ESSymbol | Void | Undefined | Null | StringLiteral | Enum,
|
||||
StringLike = String | StringLiteral,
|
||||
@@ -2179,11 +2218,14 @@ namespace ts {
|
||||
ObjectType = Class | Interface | Reference | Tuple | Anonymous,
|
||||
UnionOrIntersection = Union | Intersection,
|
||||
StructuredType = ObjectType | Union | Intersection,
|
||||
Narrowable = Any | ObjectType | Union | TypeParameter,
|
||||
|
||||
// 'Narrowable' types are types where narrowing actually narrows.
|
||||
// This *should* be every type other than null, undefined, void, and never
|
||||
Narrowable = Any | StructuredType | TypeParameter | StringLike | NumberLike | Boolean | ESSymbol,
|
||||
/* @internal */
|
||||
RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral,
|
||||
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
|
||||
/* @internal */
|
||||
PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType
|
||||
PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | ContainsAnyFunctionType
|
||||
}
|
||||
|
||||
export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
|
||||
@@ -2499,6 +2541,7 @@ namespace ts {
|
||||
allowJs?: boolean;
|
||||
noImplicitUseStrict?: boolean;
|
||||
strictNullChecks?: boolean;
|
||||
skipLibCheck?: boolean;
|
||||
listEmittedFiles?: boolean;
|
||||
lib?: string[];
|
||||
/* @internal */ stripInternal?: boolean;
|
||||
@@ -2781,6 +2824,7 @@ namespace ts {
|
||||
trace?(s: string): void;
|
||||
directoryExists?(directoryName: string): boolean;
|
||||
realpath?(path: string): string;
|
||||
getCurrentDirectory?(): string;
|
||||
}
|
||||
|
||||
export interface ResolvedModule {
|
||||
@@ -2813,9 +2857,11 @@ namespace ts {
|
||||
|
||||
export interface CompilerHost extends ModuleResolutionHost {
|
||||
getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
|
||||
getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
|
||||
getCancellationToken?(): CancellationToken;
|
||||
getDefaultLibFileName(options: CompilerOptions): string;
|
||||
getDefaultLibLocation?(): string;
|
||||
getDefaultTypeDirectiveNames?(rootPath: string): string[];
|
||||
writeFile: WriteFileCallback;
|
||||
getCurrentDirectory(): string;
|
||||
getCanonicalFileName(fileName: string): string;
|
||||
@@ -2867,4 +2913,9 @@ namespace ts {
|
||||
|
||||
/* @internal */ reattachFileDiagnostics(newFile: SourceFile): void;
|
||||
}
|
||||
|
||||
// SyntaxKind.SyntaxList
|
||||
export interface SyntaxList extends Node {
|
||||
_children: Node[];
|
||||
}
|
||||
}
|
||||
|
||||
+100
-34
@@ -84,6 +84,25 @@ namespace ts {
|
||||
return node.end - node.pos;
|
||||
}
|
||||
|
||||
export function mapIsEqualTo<T>(map1: Map<T>, map2: Map<T>): boolean {
|
||||
if (!map1 || !map2) {
|
||||
return map1 === map2;
|
||||
}
|
||||
return containsAll(map1, map2) && containsAll(map2, map1);
|
||||
}
|
||||
|
||||
function containsAll<T>(map: Map<T>, other: Map<T>): boolean {
|
||||
for (const key in map) {
|
||||
if (!hasProperty(map, key)) {
|
||||
continue;
|
||||
}
|
||||
if (!hasProperty(other, key) || map[key] !== other[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function arrayIsEqualTo<T>(array1: T[], array2: T[], equaler?: (a: T, b: T) => boolean): boolean {
|
||||
if (!array1 || !array2) {
|
||||
return array1 === array2;
|
||||
@@ -268,16 +287,36 @@ namespace ts {
|
||||
return !nodeIsMissing(node);
|
||||
}
|
||||
|
||||
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile): number {
|
||||
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile, includeJsDocComment?: boolean): number {
|
||||
// With nodes that have no width (i.e. 'Missing' nodes), we actually *don't*
|
||||
// want to skip trivia because this will launch us forward to the next token.
|
||||
if (nodeIsMissing(node)) {
|
||||
return node.pos;
|
||||
}
|
||||
|
||||
if (isJSDocNode(node)) {
|
||||
return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
|
||||
}
|
||||
|
||||
if (includeJsDocComment && node.jsDocComments && node.jsDocComments.length > 0) {
|
||||
return getTokenPosOfNode(node.jsDocComments[0]);
|
||||
}
|
||||
|
||||
// For a syntax list, it is possible that one of its children has JSDocComment nodes, while
|
||||
// the syntax list itself considers them as normal trivia. Therefore if we simply skip
|
||||
// trivia for the list, we may have skipped the JSDocComment as well. So we should process its
|
||||
// first child to determine the actual position of its first token.
|
||||
if (node.kind === SyntaxKind.SyntaxList && (<SyntaxList>node)._children.length > 0) {
|
||||
return getTokenPosOfNode((<SyntaxList>node)._children[0], sourceFile, includeJsDocComment);
|
||||
}
|
||||
|
||||
return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos);
|
||||
}
|
||||
|
||||
export function isJSDocNode(node: Node) {
|
||||
return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode;
|
||||
}
|
||||
|
||||
export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFile): number {
|
||||
if (nodeIsMissing(node) || !node.decorators) {
|
||||
return getTokenPosOfNode(node, sourceFile);
|
||||
@@ -594,6 +633,7 @@ namespace ts {
|
||||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.UndefinedKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
return true;
|
||||
case SyntaxKind.VoidKeyword:
|
||||
return node.parent.kind !== SyntaxKind.VoidExpression;
|
||||
@@ -840,15 +880,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function getContainingFunctionOrModule(node: Node): Node {
|
||||
while (true) {
|
||||
node = node.parent;
|
||||
if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getContainingClass(node: Node): ClassLikeDeclaration {
|
||||
while (true) {
|
||||
node = node.parent;
|
||||
@@ -967,6 +998,20 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression {
|
||||
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
|
||||
let prev = func;
|
||||
let parent = func.parent;
|
||||
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
prev = parent;
|
||||
parent = parent.parent;
|
||||
}
|
||||
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
|
||||
return parent as CallExpression;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a node is a property or element access expression for super.
|
||||
*/
|
||||
@@ -1241,8 +1286,15 @@ namespace ts {
|
||||
else if (lhs.expression.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
// chained dot, e.g. x.y.z = expr; this var is the 'x.y' part
|
||||
const innerPropertyAccess = <PropertyAccessExpression>lhs.expression;
|
||||
if (innerPropertyAccess.expression.kind === SyntaxKind.Identifier && innerPropertyAccess.name.text === "prototype") {
|
||||
return SpecialPropertyAssignmentKind.PrototypeProperty;
|
||||
if (innerPropertyAccess.expression.kind === SyntaxKind.Identifier) {
|
||||
// module.exports.name = expr
|
||||
const innerPropertyAccessIdentifier = <Identifier>innerPropertyAccess.expression;
|
||||
if (innerPropertyAccessIdentifier.text === "module" && innerPropertyAccess.name.text === "exports") {
|
||||
return SpecialPropertyAssignmentKind.ExportsProperty;
|
||||
}
|
||||
if (innerPropertyAccess.name.text === "prototype") {
|
||||
return SpecialPropertyAssignmentKind.PrototypeProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1295,21 +1347,23 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const jsDocComment = getJSDocComment(node, checkParentVariableStatement);
|
||||
if (!jsDocComment) {
|
||||
const jsDocComments = getJSDocComments(node, checkParentVariableStatement);
|
||||
if (!jsDocComments) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
for (const tag of jsDocComment.tags) {
|
||||
if (tag.kind === kind) {
|
||||
return tag;
|
||||
for (const jsDocComment of jsDocComments) {
|
||||
for (const tag of jsDocComment.tags) {
|
||||
if (tag.kind === kind) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getJSDocComment(node: Node, checkParentVariableStatement: boolean): JSDocComment {
|
||||
if (node.jsDocComment) {
|
||||
return node.jsDocComment;
|
||||
function getJSDocComments(node: Node, checkParentVariableStatement: boolean): JSDocComment[] {
|
||||
if (node.jsDocComments) {
|
||||
return node.jsDocComments;
|
||||
}
|
||||
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
|
||||
// /**
|
||||
@@ -1325,7 +1379,7 @@ namespace ts {
|
||||
|
||||
const variableStatementNode = isInitializerOfVariableDeclarationInStatement ? node.parent.parent.parent : undefined;
|
||||
if (variableStatementNode) {
|
||||
return variableStatementNode.jsDocComment;
|
||||
return variableStatementNode.jsDocComments;
|
||||
}
|
||||
|
||||
// Also recognize when the node is the RHS of an assignment expression
|
||||
@@ -1336,12 +1390,12 @@ namespace ts {
|
||||
(parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken &&
|
||||
parent.parent.kind === SyntaxKind.ExpressionStatement;
|
||||
if (isSourceOfAssignmentExpressionStatement) {
|
||||
return parent.parent.jsDocComment;
|
||||
return parent.parent.jsDocComments;
|
||||
}
|
||||
|
||||
const isPropertyAssignmentExpression = parent && parent.kind === SyntaxKind.PropertyAssignment;
|
||||
if (isPropertyAssignmentExpression) {
|
||||
return parent.jsDocComment;
|
||||
return parent.jsDocComments;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1366,14 +1420,16 @@ namespace ts {
|
||||
// annotation.
|
||||
const parameterName = (<Identifier>parameter.name).text;
|
||||
|
||||
const jsDocComment = getJSDocComment(parameter.parent, /*checkParentVariableStatement*/ true);
|
||||
if (jsDocComment) {
|
||||
for (const tag of jsDocComment.tags) {
|
||||
if (tag.kind === SyntaxKind.JSDocParameterTag) {
|
||||
const parameterTag = <JSDocParameterTag>tag;
|
||||
const name = parameterTag.preParameterName || parameterTag.postParameterName;
|
||||
if (name.text === parameterName) {
|
||||
return parameterTag;
|
||||
const jsDocComments = getJSDocComments(parameter.parent, /*checkParentVariableStatement*/ true);
|
||||
if (jsDocComments) {
|
||||
for (const jsDocComment of jsDocComments) {
|
||||
for (const tag of jsDocComment.tags) {
|
||||
if (tag.kind === SyntaxKind.JSDocParameterTag) {
|
||||
const parameterTag = <JSDocParameterTag>tag;
|
||||
const name = parameterTag.preParameterName || parameterTag.postParameterName;
|
||||
if (name.text === parameterName) {
|
||||
return parameterTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1498,6 +1554,7 @@ namespace ts {
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.TypeParameter:
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1565,6 +1622,12 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isLiteralComputedPropertyDeclarationName(node: Node) {
|
||||
return (node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NumericLiteral) &&
|
||||
node.parent.kind === SyntaxKind.ComputedPropertyName &&
|
||||
isDeclaration(node.parent.parent);
|
||||
}
|
||||
|
||||
// Return true if the given identifier is classified as an IdentifierName
|
||||
export function isIdentifierName(node: Identifier): boolean {
|
||||
let parent = node.parent;
|
||||
@@ -1610,7 +1673,7 @@ namespace ts {
|
||||
// export default ...
|
||||
export function isAliasSymbolDeclaration(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
|
||||
node.kind === SyntaxKind.GlobalModuleExportDeclaration ||
|
||||
node.kind === SyntaxKind.NamespaceExportDeclaration ||
|
||||
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
|
||||
node.kind === SyntaxKind.NamespaceImport ||
|
||||
node.kind === SyntaxKind.ImportSpecifier ||
|
||||
@@ -1741,7 +1804,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getPropertyNameForPropertyNameNode(name: DeclarationName): string {
|
||||
if (name.kind === SyntaxKind.Identifier || name.kind === SyntaxKind.StringLiteral || name.kind === SyntaxKind.NumericLiteral) {
|
||||
if (name.kind === SyntaxKind.Identifier || name.kind === SyntaxKind.StringLiteral || name.kind === SyntaxKind.NumericLiteral || name.kind === SyntaxKind.Parameter) {
|
||||
return (<Identifier | LiteralExpression>name).text;
|
||||
}
|
||||
if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
@@ -1750,6 +1813,9 @@ namespace ts {
|
||||
const rightHandSideName = (<PropertyAccessExpression>nameExpression).name.text;
|
||||
return getPropertyNameForKnownSymbolName(rightHandSideName);
|
||||
}
|
||||
else if (nameExpression.kind === SyntaxKind.StringLiteral || nameExpression.kind === SyntaxKind.NumericLiteral) {
|
||||
return (<LiteralExpression>nameExpression).text;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -3012,7 +3078,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function isParameterPropertyDeclaration(node: ParameterDeclaration): boolean {
|
||||
return node.flags & NodeFlags.AccessibilityModifier && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
|
||||
return node.flags & NodeFlags.ParameterPropertyModifier && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
|
||||
}
|
||||
|
||||
export function startsWith(str: string, prefix: string): boolean {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/// <reference path="harness.ts" />
|
||||
/// <reference path="runnerbase.ts" />
|
||||
/// <reference path="typeWriter.ts" />
|
||||
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
const enum CompilerTestType {
|
||||
@@ -11,7 +12,7 @@ const enum CompilerTestType {
|
||||
|
||||
class CompilerBaselineRunner extends RunnerBase {
|
||||
private basePath = "tests/cases";
|
||||
private testSuiteName: string;
|
||||
private testSuiteName: TestRunnerKind;
|
||||
private errors: boolean;
|
||||
private emit: boolean;
|
||||
private decl: boolean;
|
||||
@@ -40,6 +41,14 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
this.basePath += "/" + this.testSuiteName;
|
||||
}
|
||||
|
||||
public kind() {
|
||||
return this.testSuiteName;
|
||||
}
|
||||
|
||||
public enumerateTestFiles() {
|
||||
return this.enumerateFiles(this.basePath, /\.tsx?$/, { recursive: true });
|
||||
}
|
||||
|
||||
private makeUnitName(name: string, root: string) {
|
||||
return ts.isRootedDiskPath(name) ? name : ts.combinePaths(root, name);
|
||||
};
|
||||
@@ -107,7 +116,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
}
|
||||
|
||||
const output = Harness.Compiler.compileFiles(
|
||||
toBeCompiled, otherFiles, harnessSettings, /*options*/ tsConfigOptions, /*currentDirectory*/ undefined);
|
||||
toBeCompiled, otherFiles, harnessSettings, /*options*/ tsConfigOptions, /*currentDirectory*/ harnessSettings["currentDirectory"]);
|
||||
|
||||
options = output.options;
|
||||
result = output.result;
|
||||
@@ -145,7 +154,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
|
||||
it (`Correct module resolution tracing for ${fileName}`, () => {
|
||||
if (options.traceResolution) {
|
||||
Harness.Baseline.runBaseline("Correct sourcemap content for " + fileName, justName.replace(/\.tsx?$/, ".trace.json"), () => {
|
||||
Harness.Baseline.runBaseline("Correct module resolution tracing for " + fileName, justName.replace(/\.tsx?$/, ".trace.json"), () => {
|
||||
return JSON.stringify(result.traceResults || [], undefined, 4);
|
||||
});
|
||||
}
|
||||
@@ -390,7 +399,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
|
||||
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
|
||||
if (this.tests.length === 0) {
|
||||
const testFiles = this.enumerateFiles(this.basePath, /\.tsx?$/, { recursive: true });
|
||||
const testFiles = this.enumerateTestFiles();
|
||||
testFiles.forEach(fn => {
|
||||
fn = fn.replace(/\\/g, "/");
|
||||
this.checkTestCodeOutput(fn);
|
||||
|
||||
Vendored
+1
-2
@@ -169,7 +169,6 @@ declare module chai {
|
||||
function notEqual(actual: any, expected: any, message?: string): void;
|
||||
function isTrue(value: any, message?: string): void;
|
||||
function isFalse(value: any, message?: string): void;
|
||||
function isNull(value: any, message?: string): void;
|
||||
function isNotNull(value: any, message?: string): void;
|
||||
function isOk(actual: any, message?: string): void;
|
||||
}
|
||||
}
|
||||
+79
-109
@@ -18,7 +18,6 @@
|
||||
/// <reference path="harnessLanguageService.ts" />
|
||||
/// <reference path="harness.ts" />
|
||||
/// <reference path="fourslashRunner.ts" />
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
namespace FourSlash {
|
||||
ts.disableIncrementalParsing = false;
|
||||
@@ -198,7 +197,7 @@ namespace FourSlash {
|
||||
public lastKnownMarker: string = "";
|
||||
|
||||
// The file that's currently 'opened'
|
||||
public activeFile: FourSlashFile = null;
|
||||
public activeFile: FourSlashFile;
|
||||
|
||||
// Whether or not we should format on keystrokes
|
||||
public enableFormatting = true;
|
||||
@@ -747,7 +746,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
const missingItem = { fileName: fileName, start: start, end: end, isWriteAccess: isWriteAccess };
|
||||
this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(references, undefined, 2)})`);
|
||||
this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(references)})`);
|
||||
}
|
||||
|
||||
public verifyReferencesCountIs(count: number, localFilesOnly = true) {
|
||||
@@ -801,7 +800,7 @@ namespace FourSlash {
|
||||
|
||||
private testDiagnostics(expected: string, diagnostics: ts.Diagnostic[]) {
|
||||
const realized = ts.realizeDiagnostics(diagnostics, "\r\n");
|
||||
const actual = JSON.stringify(realized, undefined, 2);
|
||||
const actual = stringify(realized);
|
||||
assert.equal(actual, expected);
|
||||
}
|
||||
|
||||
@@ -876,7 +875,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
if (ranges.length !== references.length) {
|
||||
this.raiseError("Rename location count does not match result.\n\nExpected: " + JSON.stringify(ranges, undefined, 2) + "\n\nActual:" + JSON.stringify(references, undefined, 2));
|
||||
this.raiseError("Rename location count does not match result.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + stringify(references));
|
||||
}
|
||||
|
||||
ranges = ranges.sort((r1, r2) => r1.start - r2.start);
|
||||
@@ -889,7 +888,7 @@ namespace FourSlash {
|
||||
if (reference.textSpan.start !== range.start ||
|
||||
ts.textSpanEnd(reference.textSpan) !== range.end) {
|
||||
|
||||
this.raiseError("Rename location results do not match.\n\nExpected: " + JSON.stringify(ranges, undefined, 2) + "\n\nActual:" + JSON.stringify(references, undefined, 2));
|
||||
this.raiseError("Rename location results do not match.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + JSON.stringify(references));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -922,7 +921,7 @@ namespace FourSlash {
|
||||
|
||||
public verifyCurrentParameterIsletiable(isVariable: boolean) {
|
||||
const signature = this.getActiveSignatureHelpItem();
|
||||
assert.isNotNull(signature);
|
||||
assert.isOk(signature);
|
||||
assert.equal(isVariable, signature.isVariadic);
|
||||
}
|
||||
|
||||
@@ -973,7 +972,7 @@ namespace FourSlash {
|
||||
}
|
||||
else {
|
||||
if (actual) {
|
||||
this.raiseError(`Expected no signature help, but got "${JSON.stringify(actual, undefined, 2)}"`);
|
||||
this.raiseError(`Expected no signature help, but got "${stringify(actual)}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1096,14 +1095,6 @@ namespace FourSlash {
|
||||
}
|
||||
addSpanInfoString();
|
||||
return resultString;
|
||||
|
||||
function repeatString(count: number, char: string) {
|
||||
let result = "";
|
||||
for (let i = 0; i < count; i++) {
|
||||
result += char;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public getBreakpointStatementLocation(pos: number) {
|
||||
@@ -1185,7 +1176,7 @@ namespace FourSlash {
|
||||
|
||||
public printCurrentParameterHelp() {
|
||||
const help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition);
|
||||
Harness.IO.log(JSON.stringify(help, undefined, 2));
|
||||
Harness.IO.log(stringify(help));
|
||||
}
|
||||
|
||||
public printCurrentQuickInfo() {
|
||||
@@ -1227,7 +1218,7 @@ namespace FourSlash {
|
||||
|
||||
public printCurrentSignatureHelp() {
|
||||
const sigHelp = this.getActiveSignatureHelpItem();
|
||||
Harness.IO.log(JSON.stringify(sigHelp, undefined, 2));
|
||||
Harness.IO.log(stringify(sigHelp));
|
||||
}
|
||||
|
||||
public printMemberListMembers() {
|
||||
@@ -1257,7 +1248,7 @@ namespace FourSlash {
|
||||
public printReferences() {
|
||||
const references = this.getReferencesAtCaret();
|
||||
ts.forEach(references, entry => {
|
||||
Harness.IO.log(JSON.stringify(entry, undefined, 2));
|
||||
Harness.IO.log(stringify(entry));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1495,6 +1486,12 @@ namespace FourSlash {
|
||||
this.fixCaretPosition();
|
||||
}
|
||||
|
||||
public formatOnType(pos: number, key: string) {
|
||||
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeOptions);
|
||||
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
this.fixCaretPosition();
|
||||
}
|
||||
|
||||
private updateMarkersForEdit(fileName: string, minChar: number, limChar: number, text: string) {
|
||||
for (let i = 0; i < this.testData.markers.length; i++) {
|
||||
const marker = this.testData.markers[i];
|
||||
@@ -1748,8 +1745,8 @@ namespace FourSlash {
|
||||
|
||||
function jsonMismatchString() {
|
||||
return Harness.IO.newLine() +
|
||||
"expected: '" + Harness.IO.newLine() + JSON.stringify(expected, undefined, 2) + "'" + Harness.IO.newLine() +
|
||||
"actual: '" + Harness.IO.newLine() + JSON.stringify(actual, undefined, 2) + "'";
|
||||
"expected: '" + Harness.IO.newLine() + stringify(expected) + "'" + Harness.IO.newLine() +
|
||||
"actual: '" + Harness.IO.newLine() + stringify(actual) + "'";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1919,7 +1916,7 @@ namespace FourSlash {
|
||||
public verifyNavigationItemsCount(expected: number, searchValue: string, matchKind?: string) {
|
||||
const items = this.languageService.getNavigateToItems(searchValue);
|
||||
let actual = 0;
|
||||
let item: ts.NavigateToItem = null;
|
||||
let item: ts.NavigateToItem;
|
||||
|
||||
// Count only the match that match the same MatchKind
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
@@ -1964,61 +1961,29 @@ namespace FourSlash {
|
||||
// if there was an explicit match kind specified, then it should be validated.
|
||||
if (matchKind !== undefined) {
|
||||
const missingItem = { name: name, kind: kind, searchValue: searchValue, matchKind: matchKind, fileName: fileName, parentName: parentName };
|
||||
this.raiseError(`verifyNavigationItemsListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(items, undefined, 2)})`);
|
||||
this.raiseError(`verifyNavigationItemsListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(items)})`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyGetScriptLexicalStructureListCount(expected: number) {
|
||||
public verifyNavigationBar(json: any) {
|
||||
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
|
||||
const actual = this.getNavigationBarItemsCount(items);
|
||||
|
||||
if (expected !== actual) {
|
||||
this.raiseError(`verifyGetScriptLexicalStructureListCount failed - found: ${actual} navigation items, expected: ${expected}.`);
|
||||
if (JSON.stringify(items, replacer) !== JSON.stringify(json)) {
|
||||
this.raiseError(`verifyNavigationBar failed - expected: ${stringify(json)}, got: ${stringify(items, replacer)}`);
|
||||
}
|
||||
}
|
||||
|
||||
private getNavigationBarItemsCount(items: ts.NavigationBarItem[]) {
|
||||
let result = 0;
|
||||
if (items) {
|
||||
for (let i = 0, n = items.length; i < n; i++) {
|
||||
result++;
|
||||
result += this.getNavigationBarItemsCount(items[i].childItems);
|
||||
// Make the data easier to read.
|
||||
function replacer(key: string, value: any) {
|
||||
switch (key) {
|
||||
case "spans":
|
||||
// We won't ever check this.
|
||||
return undefined;
|
||||
case "childItems":
|
||||
return value.length === 0 ? undefined : value;
|
||||
default:
|
||||
// Omit falsy values, those are presumed to be the default.
|
||||
return value || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public verifyGetScriptLexicalStructureListContains(name: string, kind: string) {
|
||||
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
|
||||
|
||||
if (!items || items.length === 0) {
|
||||
this.raiseError("verifyGetScriptLexicalStructureListContains failed - found 0 navigation items, expected at least one.");
|
||||
}
|
||||
|
||||
if (this.navigationBarItemsContains(items, name, kind)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const missingItem = { name: name, kind: kind };
|
||||
this.raiseError(`verifyGetScriptLexicalStructureListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(items, undefined, 2)})`);
|
||||
}
|
||||
|
||||
private navigationBarItemsContains(items: ts.NavigationBarItem[], name: string, kind: string) {
|
||||
if (items) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
if (item && item.text === name && item.kind === kind) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.navigationBarItemsContains(item.childItems, name, kind)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public printNavigationItems(searchValue: string) {
|
||||
@@ -2033,15 +1998,15 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public printScriptLexicalStructureItems() {
|
||||
public printNavigationBar() {
|
||||
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
|
||||
const length = items && items.length;
|
||||
|
||||
Harness.IO.log(`NavigationItems list (${length} items)`);
|
||||
Harness.IO.log(`Navigation bar (${length} items)`);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const item = items[i];
|
||||
Harness.IO.log(`name: ${item.text}, kind: ${item.kind}`);
|
||||
Harness.IO.log(`${repeatString(item.indent, " ")}name: ${item.text}, kind: ${item.kind}, childItems: ${item.childItems.map(child => child.text)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2066,7 +2031,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
const missingItem = { fileName: fileName, start: start, end: end, isWriteAccess: isWriteAccess };
|
||||
this.raiseError(`verifyOccurrencesAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(occurrences, undefined, 2)})`);
|
||||
this.raiseError(`verifyOccurrencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(occurrences)})`);
|
||||
}
|
||||
|
||||
public verifyOccurrencesAtPositionListCount(expectedCount: number) {
|
||||
@@ -2105,7 +2070,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
const missingItem = { fileName: fileName, start: start, end: end, kind: kind };
|
||||
this.raiseError(`verifyDocumentHighlightsAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(documentHighlights, undefined, 2)})`);
|
||||
this.raiseError(`verifyDocumentHighlightsAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(documentHighlights)})`);
|
||||
}
|
||||
|
||||
public verifyDocumentHighlightsAtPositionListCount(expectedCount: number, fileNamesToSearch: string[]) {
|
||||
@@ -2171,13 +2136,13 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
const itemsString = items.map((item) => JSON.stringify({ name: item.name, kind: item.kind }, undefined, 2)).join(",\n");
|
||||
const itemsString = items.map(item => stringify({ name: item.name, kind: item.kind })).join(",\n");
|
||||
|
||||
this.raiseError(`Expected "${JSON.stringify({ name, text, documentation, kind }, undefined, 2)}" to be in list [${itemsString}]`);
|
||||
this.raiseError(`Expected "${stringify({ name, text, documentation, kind })}" to be in list [${itemsString}]`);
|
||||
}
|
||||
|
||||
private findFile(indexOrName: any) {
|
||||
let result: FourSlashFile = null;
|
||||
let result: FourSlashFile;
|
||||
if (typeof indexOrName === "number") {
|
||||
const index = <number>indexOrName;
|
||||
if (index >= this.testData.files.length) {
|
||||
@@ -2346,10 +2311,16 @@ ${code}
|
||||
const ranges: Range[] = [];
|
||||
|
||||
// Stuff related to the subfile we're parsing
|
||||
let currentFileContent: string = null;
|
||||
let currentFileContent: string = undefined;
|
||||
let currentFileName = fileName;
|
||||
let currentFileOptions: { [s: string]: string } = {};
|
||||
|
||||
function resetLocalData() {
|
||||
currentFileContent = undefined;
|
||||
currentFileOptions = {};
|
||||
currentFileName = fileName;
|
||||
}
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let line = lines[i];
|
||||
const lineLength = line.length;
|
||||
@@ -2362,7 +2333,7 @@ ${code}
|
||||
// Subfile content line
|
||||
|
||||
// Append to the current subfile content, inserting a newline needed
|
||||
if (currentFileContent === null) {
|
||||
if (currentFileContent === undefined) {
|
||||
currentFileContent = "";
|
||||
}
|
||||
else {
|
||||
@@ -2394,10 +2365,7 @@ ${code}
|
||||
// Store result file
|
||||
files.push(file);
|
||||
|
||||
// Reset local data
|
||||
currentFileContent = null;
|
||||
currentFileOptions = {};
|
||||
currentFileName = fileName;
|
||||
resetLocalData();
|
||||
}
|
||||
|
||||
currentFileName = basePath + "/" + match[2];
|
||||
@@ -2424,10 +2392,7 @@ ${code}
|
||||
// Store result file
|
||||
files.push(file);
|
||||
|
||||
// Reset local data
|
||||
currentFileContent = null;
|
||||
currentFileOptions = {};
|
||||
currentFileName = fileName;
|
||||
resetLocalData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2492,7 +2457,7 @@ ${code}
|
||||
|
||||
if (markerValue === undefined) {
|
||||
reportError(fileName, location.sourceLine, location.sourceColumn, "Object markers can not be empty");
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const marker: Marker = {
|
||||
@@ -2521,7 +2486,7 @@ ${code}
|
||||
if (markerMap[name] !== undefined) {
|
||||
const message = "Marker '" + name + "' is duplicated in the source file contents.";
|
||||
reportError(marker.fileName, location.sourceLine, location.sourceColumn, message);
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
markerMap[name] = marker;
|
||||
@@ -2540,7 +2505,7 @@ ${code}
|
||||
let output = "";
|
||||
|
||||
/// The current marker (or maybe multi-line comment?) we're parsing, possibly
|
||||
let openMarker: LocationInformation = null;
|
||||
let openMarker: LocationInformation = undefined;
|
||||
|
||||
/// A stack of the open range markers that are still unclosed
|
||||
const openRanges: RangeLocationInformation[] = [];
|
||||
@@ -2648,7 +2613,7 @@ ${code}
|
||||
difference += i + 1 - openMarker.sourcePosition;
|
||||
|
||||
// Reset the state
|
||||
openMarker = null;
|
||||
openMarker = undefined;
|
||||
state = State.none;
|
||||
}
|
||||
break;
|
||||
@@ -2670,7 +2635,7 @@ ${code}
|
||||
difference += i + 1 - openMarker.sourcePosition;
|
||||
|
||||
// Reset the state
|
||||
openMarker = null;
|
||||
openMarker = undefined;
|
||||
state = State.none;
|
||||
}
|
||||
else if (validMarkerChars.indexOf(currentChar) < 0) {
|
||||
@@ -2682,7 +2647,7 @@ ${code}
|
||||
// Bail out the text we've gathered so far back into the output
|
||||
flush(i);
|
||||
lastNormalCharPosition = i;
|
||||
openMarker = null;
|
||||
openMarker = undefined;
|
||||
|
||||
state = State.none;
|
||||
}
|
||||
@@ -2713,7 +2678,7 @@ ${code}
|
||||
reportError(fileName, openRange.sourceLine, openRange.sourceColumn, "Unterminated range.");
|
||||
}
|
||||
|
||||
if (openMarker !== null) {
|
||||
if (openMarker) {
|
||||
reportError(fileName, openMarker.sourceLine, openMarker.sourceColumn, "Unterminated marker.");
|
||||
}
|
||||
|
||||
@@ -2728,6 +2693,18 @@ ${code}
|
||||
fileName: fileName
|
||||
};
|
||||
}
|
||||
|
||||
function repeatString(count: number, char: string) {
|
||||
let result = "";
|
||||
for (let i = 0; i < count; i++) {
|
||||
result += char;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function stringify(data: any, replacer?: (key: string, value: any) => any): string {
|
||||
return JSON.stringify(data, replacer, 2);
|
||||
}
|
||||
}
|
||||
|
||||
namespace FourSlashInterface {
|
||||
@@ -3029,19 +3006,8 @@ namespace FourSlashInterface {
|
||||
this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true);
|
||||
}
|
||||
|
||||
public getScriptLexicalStructureListCount(count: number) {
|
||||
this.state.verifyGetScriptLexicalStructureListCount(count);
|
||||
}
|
||||
|
||||
// TODO: figure out what to do with the unused arguments.
|
||||
public getScriptLexicalStructureListContains(
|
||||
name: string,
|
||||
kind: string,
|
||||
fileName?: string,
|
||||
parentName?: string,
|
||||
isAdditionalSpan?: boolean,
|
||||
markerPosition?: number) {
|
||||
this.state.verifyGetScriptLexicalStructureListContains(name, kind);
|
||||
public navigationBar(json: any) {
|
||||
this.state.verifyNavigationBar(json);
|
||||
}
|
||||
|
||||
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string) {
|
||||
@@ -3234,8 +3200,8 @@ namespace FourSlashInterface {
|
||||
this.state.printNavigationItems(searchValue);
|
||||
}
|
||||
|
||||
public printScriptLexicalStructureItems() {
|
||||
this.state.printScriptLexicalStructureItems();
|
||||
public printNavigationBar() {
|
||||
this.state.printNavigationBar();
|
||||
}
|
||||
|
||||
public printReferences() {
|
||||
@@ -3267,6 +3233,10 @@ namespace FourSlashInterface {
|
||||
this.state.formatSelection(this.state.getMarkerByName(startMarker).position, this.state.getMarkerByName(endMarker).position);
|
||||
}
|
||||
|
||||
public onType(posMarker: string, key: string) {
|
||||
this.state.formatOnType(this.state.getMarkerByName(posMarker).position, key);
|
||||
}
|
||||
|
||||
public setOption(name: string, value: number): void;
|
||||
public setOption(name: string, value: string): void;
|
||||
public setOption(name: string, value: boolean): void;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
///<reference path="fourslash.ts" />
|
||||
///<reference path="harness.ts"/>
|
||||
///<reference path="runnerbase.ts" />
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
const enum FourSlashTestType {
|
||||
Native,
|
||||
@@ -12,7 +11,7 @@ const enum FourSlashTestType {
|
||||
|
||||
class FourSlashRunner extends RunnerBase {
|
||||
protected basePath: string;
|
||||
protected testSuiteName: string;
|
||||
protected testSuiteName: TestRunnerKind;
|
||||
|
||||
constructor(private testType: FourSlashTestType) {
|
||||
super();
|
||||
@@ -36,9 +35,17 @@ class FourSlashRunner extends RunnerBase {
|
||||
}
|
||||
}
|
||||
|
||||
public enumerateTestFiles() {
|
||||
return this.enumerateFiles(this.basePath, /\.ts/i, { recursive: false });
|
||||
}
|
||||
|
||||
public kind() {
|
||||
return this.testSuiteName;
|
||||
}
|
||||
|
||||
public initializeTests() {
|
||||
if (this.tests.length === 0) {
|
||||
this.tests = this.enumerateFiles(this.basePath, /\.ts/i, { recursive: false });
|
||||
this.tests = this.enumerateTestFiles();
|
||||
}
|
||||
|
||||
describe(this.testSuiteName + " tests", () => {
|
||||
|
||||
+66
-39
@@ -1,7 +1,7 @@
|
||||
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
@@ -23,7 +23,6 @@
|
||||
/// <reference path="external\chai.d.ts"/>
|
||||
/// <reference path="sourceMapRecorder.ts"/>
|
||||
/// <reference path="runnerbase.ts"/>
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
// Block scoped definitions work poorly for global variables, temporarily enable var
|
||||
/* tslint:disable:no-var-keyword */
|
||||
@@ -32,7 +31,7 @@
|
||||
var _chai: typeof chai = require("chai");
|
||||
var assert: typeof _chai.assert = _chai.assert;
|
||||
declare var __dirname: string; // Node-specific
|
||||
var global = <any>Function("return this").call(null);
|
||||
var global = <any>Function("return this").call(undefined);
|
||||
/* tslint:enable:no-var-keyword */
|
||||
|
||||
namespace Utils {
|
||||
@@ -127,7 +126,7 @@ namespace Utils {
|
||||
export function memoize<T extends Function>(f: T): T {
|
||||
const cache: { [idx: string]: any } = {};
|
||||
|
||||
return <any>(function () {
|
||||
return <any>(function() {
|
||||
const key = Array.prototype.join.call(arguments);
|
||||
const cachedResult = cache[key];
|
||||
if (cachedResult) {
|
||||
@@ -222,6 +221,19 @@ namespace Utils {
|
||||
return k;
|
||||
}
|
||||
|
||||
// For some markers in SyntaxKind, we should print its original syntax name instead of
|
||||
// the marker name in tests.
|
||||
if (k === (<any>ts).SyntaxKind.FirstJSDocNode ||
|
||||
k === (<any>ts).SyntaxKind.LastJSDocNode ||
|
||||
k === (<any>ts).SyntaxKind.FirstJSDocTagNode ||
|
||||
k === (<any>ts).SyntaxKind.LastJSDocTagNode) {
|
||||
for (const kindName in (<any>ts).SyntaxKind) {
|
||||
if ((<any>ts).SyntaxKind[kindName] === k) {
|
||||
return kindName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (<any>ts).SyntaxKind[k];
|
||||
}
|
||||
|
||||
@@ -351,7 +363,7 @@ namespace Utils {
|
||||
assert.equal(node1.end, node2.end, "node1.end !== node2.end");
|
||||
assert.equal(node1.kind, node2.kind, "node1.kind !== node2.kind");
|
||||
|
||||
// call this on both nodes to ensure all propagated flags have been set (and thus can be
|
||||
// call this on both nodes to ensure all propagated flags have been set (and thus can be
|
||||
// compared).
|
||||
assert.equal(ts.containsParseError(node1), ts.containsParseError(node2));
|
||||
assert.equal(node1.flags, node2.flags, "node1.flags !== node2.flags");
|
||||
@@ -558,15 +570,9 @@ namespace Harness {
|
||||
}
|
||||
|
||||
export function directoryName(path: string) {
|
||||
let dirPath = pathModule.dirname(path);
|
||||
|
||||
const dirPath = pathModule.dirname(path);
|
||||
// Node will just continue to repeat the root path, rather than return null
|
||||
if (dirPath === path) {
|
||||
dirPath = null;
|
||||
}
|
||||
else {
|
||||
return dirPath;
|
||||
}
|
||||
return dirPath === path ? undefined : dirPath;
|
||||
}
|
||||
|
||||
export let listFiles: typeof IO.listFiles = (path, spec?, options?) => {
|
||||
@@ -609,7 +615,7 @@ namespace Harness {
|
||||
export const getCurrentDirectory = () => "";
|
||||
export const args = () => <string[]>[];
|
||||
export const getExecutingFilePath = () => "";
|
||||
export const exit = (exitCode: number) => {};
|
||||
export const exit = (exitCode: number) => { };
|
||||
|
||||
export let log = (s: string) => console.log(s);
|
||||
|
||||
@@ -634,7 +640,7 @@ namespace Harness {
|
||||
xhr.send();
|
||||
}
|
||||
catch (e) {
|
||||
return { status: 404, responseText: null };
|
||||
return { status: 404, responseText: undefined };
|
||||
}
|
||||
|
||||
return waitForXHR(xhr);
|
||||
@@ -651,7 +657,7 @@ namespace Harness {
|
||||
}
|
||||
catch (e) {
|
||||
log(`XHR Error: ${e}`);
|
||||
return { status: 500, responseText: null };
|
||||
return { status: 500, responseText: undefined };
|
||||
}
|
||||
|
||||
return waitForXHR(xhr);
|
||||
@@ -663,7 +669,7 @@ namespace Harness {
|
||||
}
|
||||
|
||||
export function deleteFile(path: string) {
|
||||
Http.writeToServerSync(serverRoot + path, "DELETE", null);
|
||||
Http.writeToServerSync(serverRoot + path, "DELETE");
|
||||
}
|
||||
|
||||
export function directoryExists(path: string): boolean {
|
||||
@@ -674,7 +680,7 @@ namespace Harness {
|
||||
let dirPath = path;
|
||||
// root of the server
|
||||
if (dirPath.match(/localhost:\d+$/) || dirPath.match(/localhost:\d+\/$/)) {
|
||||
dirPath = null;
|
||||
dirPath = undefined;
|
||||
// path + fileName
|
||||
}
|
||||
else if (dirPath.indexOf(".") === -1) {
|
||||
@@ -722,7 +728,7 @@ namespace Harness {
|
||||
return response.responseText;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,7 +764,7 @@ namespace Harness {
|
||||
(emittedFile: string, emittedLine: number, emittedColumn: number, sourceFile: string, sourceLine: number, sourceColumn: number, sourceName: string): void;
|
||||
}
|
||||
|
||||
// Settings
|
||||
// Settings
|
||||
export let userSpecifiedRoot = "";
|
||||
export let lightMode = false;
|
||||
|
||||
@@ -797,7 +803,7 @@ namespace Harness {
|
||||
fileName: string,
|
||||
sourceText: string,
|
||||
languageVersion: ts.ScriptTarget) {
|
||||
// We'll only assert invariants outside of light mode.
|
||||
// We'll only assert invariants outside of light mode.
|
||||
const shouldAssertInvariants = !Harness.lightMode;
|
||||
|
||||
// Only set the parent nodes if we're asserting invariants. We don't need them otherwise.
|
||||
@@ -826,7 +832,7 @@ namespace Harness {
|
||||
}
|
||||
|
||||
if (!libFileNameSourceFileMap[fileName]) {
|
||||
libFileNameSourceFileMap[fileName] = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest);
|
||||
libFileNameSourceFileMap[fileName] = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest);
|
||||
}
|
||||
return libFileNameSourceFileMap[fileName];
|
||||
}
|
||||
@@ -900,6 +906,20 @@ namespace Harness {
|
||||
|
||||
|
||||
return {
|
||||
getDefaultTypeDirectiveNames: (path: string) => {
|
||||
const results: string[] = [];
|
||||
fileMap.forEachValue((key, value) => {
|
||||
const rx = /node_modules\/@types\/(\w+)/;
|
||||
const typeNameResult = rx.exec(key);
|
||||
if (typeNameResult) {
|
||||
const typeName = typeNameResult[1];
|
||||
if (results.indexOf(typeName) < 0) {
|
||||
results.push(typeName);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
},
|
||||
getCurrentDirectory: () => currentDirectory,
|
||||
getSourceFile,
|
||||
getDefaultLibFileName,
|
||||
@@ -928,7 +948,7 @@ namespace Harness {
|
||||
libFiles?: string;
|
||||
}
|
||||
|
||||
// Additional options not already in ts.optionDeclarations
|
||||
// Additional options not already in ts.optionDeclarations
|
||||
const harnessOptionDeclarations: ts.CommandLineOption[] = [
|
||||
{ name: "allowNonTsExtensions", type: "boolean" },
|
||||
{ name: "useCaseSensitiveFileNames", type: "boolean" },
|
||||
@@ -939,6 +959,7 @@ namespace Harness {
|
||||
{ name: "noErrorTruncation", type: "boolean" },
|
||||
{ name: "suppressOutputPathCheck", type: "boolean" },
|
||||
{ name: "noImplicitReferences", type: "boolean" },
|
||||
{ name: "currentDirectory", type: "string" },
|
||||
{ name: "symlink", type: "string" }
|
||||
];
|
||||
|
||||
@@ -1179,7 +1200,7 @@ namespace Harness {
|
||||
errLines.forEach(e => outputLines.push(e));
|
||||
|
||||
// do not count errors from lib.d.ts here, they are computed separately as numLibraryDiagnostics
|
||||
// if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers)
|
||||
// if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers)
|
||||
// then they will be added twice thus triggering 'total errors' assertion with condition
|
||||
// 'totalErrorsReportedInNonLibraryFiles + numLibraryDiagnostics + numTest262HarnessDiagnostics, diagnostics.length
|
||||
|
||||
@@ -1403,7 +1424,9 @@ namespace Harness {
|
||||
const opts: CompilerSettings = {};
|
||||
|
||||
let match: RegExpExecArray;
|
||||
while ((match = optionRegex.exec(content)) != null) {
|
||||
/* tslint:disable:no-null-keyword */
|
||||
while ((match = optionRegex.exec(content)) !== null) {
|
||||
/* tslint:enable:no-null-keyword */
|
||||
opts[match[1]] = match[2];
|
||||
}
|
||||
|
||||
@@ -1420,9 +1443,9 @@ namespace Harness {
|
||||
const lines = Utils.splitContentByNewlines(code);
|
||||
|
||||
// Stuff related to the subfile we're parsing
|
||||
let currentFileContent: string = null;
|
||||
let currentFileContent: string = undefined;
|
||||
let currentFileOptions: any = {};
|
||||
let currentFileName: any = null;
|
||||
let currentFileName: any = undefined;
|
||||
let refs: string[] = [];
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
@@ -1441,16 +1464,16 @@ namespace Harness {
|
||||
if (currentFileName) {
|
||||
// Store result file
|
||||
const newTestFile = {
|
||||
content: currentFileContent,
|
||||
name: currentFileName,
|
||||
fileOptions: currentFileOptions,
|
||||
originalFilePath: fileName,
|
||||
references: refs
|
||||
};
|
||||
content: currentFileContent,
|
||||
name: currentFileName,
|
||||
fileOptions: currentFileOptions,
|
||||
originalFilePath: fileName,
|
||||
references: refs
|
||||
};
|
||||
testUnitData.push(newTestFile);
|
||||
|
||||
// Reset local data
|
||||
currentFileContent = null;
|
||||
currentFileContent = undefined;
|
||||
currentFileOptions = {};
|
||||
currentFileName = testMetaData[2];
|
||||
refs = [];
|
||||
@@ -1463,7 +1486,7 @@ namespace Harness {
|
||||
else {
|
||||
// Subfile content line
|
||||
// Append to the current subfile content, inserting a newline needed
|
||||
if (currentFileContent === null) {
|
||||
if (currentFileContent === undefined) {
|
||||
currentFileContent = "";
|
||||
}
|
||||
else {
|
||||
@@ -1487,7 +1510,7 @@ namespace Harness {
|
||||
};
|
||||
testUnitData.push(newTestFile2);
|
||||
|
||||
// unit tests always list files explicitly
|
||||
// unit tests always list files explicitly
|
||||
const parseConfigHost: ts.ParseConfigHost = {
|
||||
readDirectory: (name) => []
|
||||
};
|
||||
@@ -1544,10 +1567,10 @@ namespace Harness {
|
||||
|
||||
function baselinePath(fileName: string, type: string, baselineFolder: string, subfolder?: string) {
|
||||
if (subfolder !== undefined) {
|
||||
return Harness.userSpecifiedRoot + baselineFolder + "/" + subfolder + "/" + type + "/" + fileName;
|
||||
return Harness.userSpecifiedRoot + baselineFolder + "/" + subfolder + "/" + type + "/" + fileName;
|
||||
}
|
||||
else {
|
||||
return Harness.userSpecifiedRoot + baselineFolder + "/" + type + "/" + fileName;
|
||||
return Harness.userSpecifiedRoot + baselineFolder + "/" + type + "/" + fileName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1586,7 +1609,9 @@ namespace Harness {
|
||||
|
||||
// Store the content in the 'local' folder so we
|
||||
// can accept it later (manually)
|
||||
/* tslint:disable:no-null-keyword */
|
||||
if (actual !== null) {
|
||||
/* tslint:enable:no-null-keyword */
|
||||
IO.writeFile(actualFileName, actual);
|
||||
}
|
||||
|
||||
@@ -1603,7 +1628,9 @@ namespace Harness {
|
||||
|
||||
const refFileName = referencePath(relativeFileName, opts && opts.Baselinefolder, opts && opts.Subfolder);
|
||||
|
||||
/* tslint:disable:no-null-keyword */
|
||||
if (actual === null) {
|
||||
/* tslint:enable:no-null-keyword */
|
||||
actual = "<no content>";
|
||||
}
|
||||
|
||||
@@ -1616,7 +1643,7 @@ namespace Harness {
|
||||
}
|
||||
|
||||
function writeComparison(expected: string, actual: string, relativeFileName: string, actualFileName: string, descriptionForDescribe: string) {
|
||||
const encoded_actual = Utils.encodeString(actual);
|
||||
const encoded_actual = Utils.encodeString(actual);
|
||||
if (expected != encoded_actual) {
|
||||
// Overwrite & issue error
|
||||
const errMsg = "The baseline file " + relativeFileName + " has changed.";
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Harness.LanguageService {
|
||||
*/
|
||||
public positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter {
|
||||
const script: ScriptInfo = this.fileNameToScript[fileName];
|
||||
assert.isNotNull(script);
|
||||
assert.isOk(script);
|
||||
|
||||
return ts.computeLineAndCharacterOfPosition(script.getLineMap(), position);
|
||||
}
|
||||
@@ -182,6 +182,7 @@ namespace Harness.LanguageService {
|
||||
class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost {
|
||||
getCompilationSettings() { return this.settings; }
|
||||
getCancellationToken() { return this.cancellationToken; }
|
||||
getDirectories(path: string): string[] { return []; }
|
||||
getCurrentDirectory(): string { return ""; }
|
||||
getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; }
|
||||
getScriptFileNames(): string[] { return this.getFilenames(); }
|
||||
@@ -268,6 +269,7 @@ namespace Harness.LanguageService {
|
||||
getCompilationSettings(): string { return JSON.stringify(this.nativeHost.getCompilationSettings()); }
|
||||
getCancellationToken(): ts.HostCancellationToken { return this.nativeHost.getCancellationToken(); }
|
||||
getCurrentDirectory(): string { return this.nativeHost.getCurrentDirectory(); }
|
||||
getDirectories(path: string) { return this.nativeHost.getDirectories(path); }
|
||||
getDefaultLibFileName(): string { return this.nativeHost.getDefaultLibFileName(); }
|
||||
getScriptFileNames(): string { return JSON.stringify(this.nativeHost.getScriptFileNames()); }
|
||||
getScriptSnapshot(fileName: string): ts.ScriptSnapshotShim {
|
||||
@@ -600,6 +602,10 @@ namespace Harness.LanguageService {
|
||||
return this.host.getCurrentDirectory();
|
||||
}
|
||||
|
||||
getDirectories(path: string): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
readDirectory(path: string, extension?: string): string[] {
|
||||
throw new Error("Not implemented Yet.");
|
||||
}
|
||||
@@ -641,6 +647,14 @@ namespace Harness.LanguageService {
|
||||
|
||||
startGroup(): void {
|
||||
}
|
||||
|
||||
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any {
|
||||
return setTimeout(callback, ms, args);
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId: any): void {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
|
||||
|
||||
+20
-19
@@ -1,6 +1,6 @@
|
||||
declare var require: any, process: any;
|
||||
var fs: any = require('fs');
|
||||
var path: any = require('path');
|
||||
declare const require: any, process: any;
|
||||
const fs: any = require("fs");
|
||||
const path: any = require("path");
|
||||
|
||||
function instrumentForRecording(fn: string, tscPath: string) {
|
||||
instrument(tscPath, `
|
||||
@@ -14,31 +14,31 @@ ts.sys = Playback.wrapSystem(ts.sys);
|
||||
ts.sys.startReplay("${ logFilename }");`);
|
||||
}
|
||||
|
||||
function instrument(tscPath: string, prepareCode: string, cleanupCode: string = '') {
|
||||
var bak = tscPath + '.bak';
|
||||
function instrument(tscPath: string, prepareCode: string, cleanupCode = "") {
|
||||
const bak = `${tscPath}.bak`;
|
||||
fs.exists(bak, (backupExists: boolean) => {
|
||||
var filename = tscPath;
|
||||
let filename = tscPath;
|
||||
if (backupExists) {
|
||||
filename = bak;
|
||||
}
|
||||
|
||||
fs.readFile(filename, 'utf-8', (err: any, tscContent: string) => {
|
||||
fs.readFile(filename, "utf-8", (err: any, tscContent: string) => {
|
||||
if (err) throw err;
|
||||
|
||||
fs.writeFile(bak, tscContent, (err: any) => {
|
||||
if (err) throw err;
|
||||
|
||||
fs.readFile(path.resolve(path.dirname(tscPath) + '/loggedIO.js'), 'utf-8', (err: any, loggerContent: string) => {
|
||||
fs.readFile(path.resolve(path.dirname(tscPath) + "/loggedIO.js"), "utf-8", (err: any, loggerContent: string) => {
|
||||
if (err) throw err;
|
||||
|
||||
var invocationLine = 'ts.executeCommandLine(ts.sys.args);';
|
||||
var index1 = tscContent.indexOf(invocationLine);
|
||||
const invocationLine = "ts.executeCommandLine(ts.sys.args);";
|
||||
const index1 = tscContent.indexOf(invocationLine);
|
||||
if (index1 < 0) {
|
||||
throw new Error("Could not find " + invocationLine);
|
||||
throw new Error(`Could not find ${invocationLine}`);
|
||||
}
|
||||
|
||||
var index2 = index1 + invocationLine.length;
|
||||
var newContent = tscContent.substr(0, index1) + loggerContent + prepareCode + invocationLine + cleanupCode + tscContent.substr(index2) + '\r\n';
|
||||
const index2 = index1 + invocationLine.length;
|
||||
const newContent = tscContent.substr(0, index1) + loggerContent + prepareCode + invocationLine + cleanupCode + tscContent.substr(index2) + "\r\n";
|
||||
fs.writeFile(tscPath, newContent);
|
||||
});
|
||||
});
|
||||
@@ -46,15 +46,16 @@ function instrument(tscPath: string, prepareCode: string, cleanupCode: string =
|
||||
});
|
||||
}
|
||||
|
||||
var isJson = (arg: string) => arg.indexOf(".json") > 0;
|
||||
const isJson = (arg: string) => arg.indexOf(".json") > 0;
|
||||
|
||||
var record = process.argv.indexOf('record');
|
||||
var tscPath = process.argv[process.argv.length - 1];
|
||||
const record = process.argv.indexOf("record");
|
||||
const tscPath = process.argv[process.argv.length - 1];
|
||||
if (record >= 0) {
|
||||
console.log('Instrumenting ' + tscPath + ' for recording');
|
||||
console.log(`Instrumenting ${tscPath} for recording`);
|
||||
instrumentForRecording(process.argv[record + 1], tscPath);
|
||||
} else if (process.argv.some(isJson)) {
|
||||
var filename = process.argv.filter(isJson)[0];
|
||||
}
|
||||
else if (process.argv.some(isJson)) {
|
||||
const filename = process.argv.filter(isJson)[0];
|
||||
instrumentForReplay(filename, tscPath);
|
||||
}
|
||||
|
||||
|
||||
+4
-10
@@ -1,7 +1,6 @@
|
||||
/// <reference path="..\..\src\compiler\sys.ts" />
|
||||
/// <reference path="..\..\src\harness\harness.ts" />
|
||||
/// <reference path="..\..\src\harness\runnerbase.ts" />
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
interface FileInformation {
|
||||
contents: string;
|
||||
@@ -94,7 +93,7 @@ namespace Playback {
|
||||
return lookup[s] = func(s);
|
||||
});
|
||||
run.reset = () => {
|
||||
lookup = null;
|
||||
lookup = undefined;
|
||||
};
|
||||
|
||||
return run;
|
||||
@@ -170,7 +169,8 @@ namespace Playback {
|
||||
path => callAndRecord(underlying.fileExists(path), recordLog.fileExists, { path }),
|
||||
memoize(path => {
|
||||
// If we read from the file, it must exist
|
||||
if (findResultByPath(wrapper, replayLog.filesRead, path, null) !== null) {
|
||||
const noResult = {};
|
||||
if (findResultByPath(wrapper, replayLog.filesRead, path, noResult) !== noResult) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
@@ -226,13 +226,7 @@ namespace Playback {
|
||||
(path, extension, exclude) => findResultByPath(wrapper,
|
||||
replayLog.directoriesRead.filter(
|
||||
d => {
|
||||
if (d.extension === extension) {
|
||||
if (d.exclude) {
|
||||
return ts.arrayIsEqualTo(d.exclude, exclude);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return d.extension === extension;
|
||||
}
|
||||
), path));
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
///<reference path="harness.ts" />
|
||||
///<reference path="runnerbase.ts" />
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
// Test case is json of below type in tests/cases/project/
|
||||
interface ProjectRunnerTestCase {
|
||||
@@ -37,11 +36,19 @@ interface BatchCompileProjectTestCaseResult extends CompileProjectFilesResult {
|
||||
}
|
||||
|
||||
class ProjectRunner extends RunnerBase {
|
||||
|
||||
public enumerateTestFiles() {
|
||||
return this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true });
|
||||
}
|
||||
|
||||
public kind(): TestRunnerKind {
|
||||
return "project";
|
||||
}
|
||||
|
||||
public initializeTests() {
|
||||
if (this.tests.length === 0) {
|
||||
const testFiles = this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true });
|
||||
const testFiles = this.enumerateTestFiles();
|
||||
testFiles.forEach(fn => {
|
||||
fn = fn.replace(/\\/g, "/");
|
||||
this.runProjectTestCase(fn);
|
||||
});
|
||||
}
|
||||
@@ -53,7 +60,7 @@ class ProjectRunner extends RunnerBase {
|
||||
private runProjectTestCase(testCaseFileName: string) {
|
||||
let testCase: ProjectRunnerTestCase & ts.CompilerOptions;
|
||||
|
||||
let testFileText: string = null;
|
||||
let testFileText: string;
|
||||
try {
|
||||
testFileText = Harness.IO.readFile(testCaseFileName);
|
||||
}
|
||||
@@ -237,7 +244,7 @@ class ProjectRunner extends RunnerBase {
|
||||
mapRoot: testCase.resolveMapRoot && testCase.mapRoot ? Harness.IO.resolvePath(testCase.mapRoot) : testCase.mapRoot,
|
||||
sourceRoot: testCase.resolveSourceRoot && testCase.sourceRoot ? Harness.IO.resolvePath(testCase.sourceRoot) : testCase.sourceRoot,
|
||||
module: moduleKind,
|
||||
moduleResolution: ts.ModuleResolutionKind.Classic, // currently all tests use classic module resolution kind, this will change in the future
|
||||
moduleResolution: ts.ModuleResolutionKind.Classic, // currently all tests use classic module resolution kind, this will change in the future
|
||||
};
|
||||
// Set the values specified using json
|
||||
const optionNameMap: ts.Map<ts.CommandLineOption> = {};
|
||||
|
||||
+118
-11
@@ -20,8 +20,6 @@
|
||||
/// <reference path="rwcRunner.ts" />
|
||||
/// <reference path="harness.ts" />
|
||||
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
let runners: RunnerBase[] = [];
|
||||
let iterations = 1;
|
||||
|
||||
@@ -33,18 +31,90 @@ function runTests(runners: RunnerBase[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options
|
||||
let mytestconfig = "mytest.config";
|
||||
let testconfig = "test.config";
|
||||
let testConfigFile =
|
||||
Harness.IO.fileExists(mytestconfig) ? Harness.IO.readFile(mytestconfig) :
|
||||
(Harness.IO.fileExists(testconfig) ? Harness.IO.readFile(testconfig) : "");
|
||||
function tryGetConfig(args: string[]) {
|
||||
const prefix = "--config=";
|
||||
const configPath = ts.forEach(args, arg => arg.lastIndexOf(prefix, 0) === 0 && arg.substr(prefix.length));
|
||||
// strip leading and trailing quotes from the path (necessary on Windows since shell does not do it automatically)
|
||||
return configPath && configPath.replace(/(^[\"'])|([\"']$)/g, "");
|
||||
}
|
||||
|
||||
if (testConfigFile !== "") {
|
||||
const testConfig = JSON.parse(testConfigFile);
|
||||
function createRunner(kind: TestRunnerKind): RunnerBase {
|
||||
switch (kind) {
|
||||
case "conformance":
|
||||
return new CompilerBaselineRunner(CompilerTestType.Conformance);
|
||||
case "compiler":
|
||||
return new CompilerBaselineRunner(CompilerTestType.Regressions);
|
||||
case "fourslash":
|
||||
return new FourSlashRunner(FourSlashTestType.Native);
|
||||
case "fourslash-shims":
|
||||
return new FourSlashRunner(FourSlashTestType.Shims);
|
||||
case "fourslash-shims-pp":
|
||||
return new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess);
|
||||
case "fourslash-server":
|
||||
return new FourSlashRunner(FourSlashTestType.Server);
|
||||
case "project":
|
||||
return new ProjectRunner();
|
||||
case "rwc":
|
||||
return new RWCRunner();
|
||||
case "test262":
|
||||
return new Test262BaselineRunner();
|
||||
}
|
||||
}
|
||||
|
||||
// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options
|
||||
|
||||
const mytestconfigFileName = "mytest.config";
|
||||
const testconfigFileName = "test.config";
|
||||
|
||||
const customConfig = tryGetConfig(Harness.IO.args());
|
||||
let testConfigContent =
|
||||
customConfig && Harness.IO.fileExists(customConfig)
|
||||
? Harness.IO.readFile(customConfig)
|
||||
: Harness.IO.fileExists(mytestconfigFileName)
|
||||
? Harness.IO.readFile(mytestconfigFileName)
|
||||
: Harness.IO.fileExists(testconfigFileName) ? Harness.IO.readFile(testconfigFileName) : "";
|
||||
|
||||
let taskConfigsFolder: string;
|
||||
let workerCount: number;
|
||||
let runUnitTests = true;
|
||||
|
||||
interface TestConfig {
|
||||
light?: boolean;
|
||||
taskConfigsFolder?: string;
|
||||
workerCount?: number;
|
||||
tasks?: TaskSet[];
|
||||
test?: string[];
|
||||
runUnitTests?: boolean;
|
||||
}
|
||||
|
||||
interface TaskSet {
|
||||
runner: TestRunnerKind;
|
||||
files: string[];
|
||||
}
|
||||
|
||||
if (testConfigContent !== "") {
|
||||
const testConfig = <TestConfig>JSON.parse(testConfigContent);
|
||||
if (testConfig.light) {
|
||||
Harness.lightMode = true;
|
||||
}
|
||||
if (testConfig.taskConfigsFolder) {
|
||||
taskConfigsFolder = testConfig.taskConfigsFolder;
|
||||
}
|
||||
if (testConfig.runUnitTests !== undefined) {
|
||||
runUnitTests = testConfig.runUnitTests;
|
||||
}
|
||||
if (testConfig.workerCount) {
|
||||
workerCount = testConfig.workerCount;
|
||||
}
|
||||
if (testConfig.tasks) {
|
||||
for (const taskSet of testConfig.tasks) {
|
||||
const runner = createRunner(taskSet.runner);
|
||||
for (const file of taskSet.files) {
|
||||
runner.addTest(file);
|
||||
}
|
||||
runners.push(runner);
|
||||
}
|
||||
}
|
||||
|
||||
if (testConfig.test && testConfig.test.length > 0) {
|
||||
for (const option of testConfig.test) {
|
||||
@@ -108,4 +178,41 @@ if (runners.length === 0) {
|
||||
// runners.push(new GeneratedFourslashRunner());
|
||||
}
|
||||
|
||||
runTests(runners);
|
||||
if (taskConfigsFolder) {
|
||||
// this instance of mocha should only partition work but not run actual tests
|
||||
runUnitTests = false;
|
||||
const workerConfigs: TestConfig[] = [];
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
// pass light mode settings to workers
|
||||
workerConfigs.push({ light: Harness.lightMode, tasks: [] });
|
||||
}
|
||||
|
||||
for (const runner of runners) {
|
||||
const files = runner.enumerateTestFiles();
|
||||
const chunkSize = Math.floor(files.length / workerCount) + 1; // add extra 1 to prevent missing tests due to rounding
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
const startPos = i * chunkSize;
|
||||
const len = Math.min(chunkSize, files.length - startPos);
|
||||
if (len !== 0) {
|
||||
workerConfigs[i].tasks.push({
|
||||
runner: runner.kind(),
|
||||
files: files.slice(startPos, startPos + len)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
const config = workerConfigs[i];
|
||||
// use last worker to run unit tests
|
||||
config.runUnitTests = i === workerCount - 1;
|
||||
Harness.IO.writeFile(ts.combinePaths(taskConfigsFolder, `task-config${i}.json`), JSON.stringify(workerConfigs[i]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
runTests(runners);
|
||||
}
|
||||
if (!runUnitTests) {
|
||||
// patch `describe` to skip unit tests
|
||||
describe = <any>describe.skip;
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
/// <reference path="harness.ts" />
|
||||
|
||||
|
||||
type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | "test262";
|
||||
type CompilerTestKind = "conformance" | "compiler";
|
||||
type FourslashTestKind = "fourslash" | "fourslash-shims" | "fourslash-shims-pp" | "fourslash-server";
|
||||
|
||||
abstract class RunnerBase {
|
||||
constructor() { }
|
||||
|
||||
@@ -12,10 +17,14 @@ abstract class RunnerBase {
|
||||
}
|
||||
|
||||
public enumerateFiles(folder: string, regex?: RegExp, options?: { recursive: boolean }): string[] {
|
||||
return Harness.IO.listFiles(Harness.userSpecifiedRoot + folder, regex, { recursive: (options ? options.recursive : false) });
|
||||
return ts.map(Harness.IO.listFiles(Harness.userSpecifiedRoot + folder, regex, { recursive: (options ? options.recursive : false) }), ts.normalizeSlashes);
|
||||
}
|
||||
|
||||
/** Setup the runner's tests so that they are ready to be executed by the harness
|
||||
abstract kind(): TestRunnerKind;
|
||||
|
||||
abstract enumerateTestFiles(): string[];
|
||||
|
||||
/** Setup the runner's tests so that they are ready to be executed by the harness
|
||||
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
|
||||
*/
|
||||
public abstract initializeTests(): void;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/// <reference path="runnerbase.ts" />
|
||||
/// <reference path="loggedIO.ts" />
|
||||
/// <reference path="..\compiler\commandLineParser.ts"/>
|
||||
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
namespace RWC {
|
||||
@@ -123,7 +124,7 @@ namespace RWC {
|
||||
opts.options.noLib = true;
|
||||
|
||||
// Emit the results
|
||||
compilerOptions = null;
|
||||
compilerOptions = undefined;
|
||||
const output = Harness.Compiler.compileFiles(
|
||||
inputFiles,
|
||||
otherFiles,
|
||||
@@ -139,7 +140,7 @@ namespace RWC {
|
||||
|
||||
function getHarnessCompilerInputUnit(fileName: string): Harness.Compiler.TestFile {
|
||||
const unitName = ts.normalizeSlashes(Harness.IO.resolvePath(fileName));
|
||||
let content: string = null;
|
||||
let content: string;
|
||||
try {
|
||||
content = Harness.IO.readFile(unitName);
|
||||
}
|
||||
@@ -223,12 +224,20 @@ namespace RWC {
|
||||
class RWCRunner extends RunnerBase {
|
||||
private static sourcePath = "internal/cases/rwc/";
|
||||
|
||||
public enumerateTestFiles() {
|
||||
return Harness.IO.listFiles(RWCRunner.sourcePath, /.+\.json$/);
|
||||
}
|
||||
|
||||
public kind(): TestRunnerKind {
|
||||
return "rwc";
|
||||
}
|
||||
|
||||
/** Setup the runner's tests so that they are ready to be executed by the harness
|
||||
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
|
||||
*/
|
||||
public initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
const testList = Harness.IO.listFiles(RWCRunner.sourcePath, /.+\.json$/);
|
||||
const testList = this.enumerateTestFiles();
|
||||
for (let i = 0; i < testList.length; i++) {
|
||||
this.runTest(testList[i]);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/// <reference path="harness.ts" />
|
||||
/// <reference path="runnerbase.ts" />
|
||||
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
|
||||
/* tslint:disable:no-null-keyword */
|
||||
|
||||
class Test262BaselineRunner extends RunnerBase {
|
||||
@@ -97,12 +98,20 @@ class Test262BaselineRunner extends RunnerBase {
|
||||
});
|
||||
}
|
||||
|
||||
public kind(): TestRunnerKind {
|
||||
return "test262";
|
||||
}
|
||||
|
||||
public enumerateTestFiles() {
|
||||
return ts.map(this.enumerateFiles(Test262BaselineRunner.basePath, Test262BaselineRunner.testFileExtensionRegex, { recursive: true }), ts.normalizePath);
|
||||
}
|
||||
|
||||
public initializeTests() {
|
||||
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
|
||||
if (this.tests.length === 0) {
|
||||
const testFiles = this.enumerateFiles(Test262BaselineRunner.basePath, Test262BaselineRunner.testFileExtensionRegex, { recursive: true });
|
||||
const testFiles = this.enumerateTestFiles();
|
||||
testFiles.forEach(fn => {
|
||||
this.runTest(ts.normalizePath(fn));
|
||||
this.runTest(fn);
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
||||
Vendored
+4
-6
@@ -4,7 +4,7 @@ interface Map<K, V> {
|
||||
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
|
||||
get(key: K): V | undefined;
|
||||
has(key: K): boolean;
|
||||
set(key: K, value?: V): Map<K, V>;
|
||||
set(key: K, value?: V): this;
|
||||
readonly size: number;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ interface WeakMap<K, V> {
|
||||
delete(key: K): boolean;
|
||||
get(key: K): V | undefined;
|
||||
has(key: K): boolean;
|
||||
set(key: K, value?: V): WeakMap<K, V>;
|
||||
|
||||
set(key: K, value?: V): this;
|
||||
}
|
||||
|
||||
interface WeakMapConstructor {
|
||||
@@ -32,7 +31,7 @@ interface WeakMapConstructor {
|
||||
declare var WeakMap: WeakMapConstructor;
|
||||
|
||||
interface Set<T> {
|
||||
add(value: T): Set<T>;
|
||||
add(value: T): this;
|
||||
clear(): void;
|
||||
delete(value: T): boolean;
|
||||
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void;
|
||||
@@ -48,11 +47,10 @@ interface SetConstructor {
|
||||
declare var Set: SetConstructor;
|
||||
|
||||
interface WeakSet<T> {
|
||||
add(value: T): WeakSet<T>;
|
||||
add(value: T): this;
|
||||
clear(): void;
|
||||
delete(value: T): boolean;
|
||||
has(value: T): boolean;
|
||||
|
||||
}
|
||||
|
||||
interface WeakSetConstructor {
|
||||
|
||||
Vendored
+14
-6
@@ -16,12 +16,12 @@ interface Array<T> {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: T) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: T) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Returns the this object after filling the section identified by start and end with value
|
||||
@@ -31,7 +31,7 @@ interface Array<T> {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: T, start?: number, end?: number): T[];
|
||||
fill(value: T, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the this object after copying a section of the array identified by start and end
|
||||
@@ -42,7 +42,7 @@ interface Array<T> {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): T[];
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
}
|
||||
|
||||
interface ArrayConstructor {
|
||||
@@ -402,6 +402,14 @@ interface String {
|
||||
*/
|
||||
endsWith(searchString: string, endPosition?: number): boolean;
|
||||
|
||||
/**
|
||||
* Returns the String value result of normalizing the string into the normalization form
|
||||
* named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.
|
||||
* @param form Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default
|
||||
* is "NFC"
|
||||
*/
|
||||
normalize(form: "NFC" | "NFD" | "NFKC" | "NFKD"): string;
|
||||
|
||||
/**
|
||||
* Returns the String value result of normalizing the string into the normalization form
|
||||
* named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.
|
||||
@@ -485,4 +493,4 @@ interface StringConstructor {
|
||||
* @param substitutions A set of substitution values.
|
||||
*/
|
||||
raw(template: TemplateStringsArray, ...substitutions: any[]): string;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
/// <reference path="lib.es2016.d.ts" />
|
||||
/// <reference path="lib.es2017.object.d.ts" />
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
interface ObjectConstructor {
|
||||
/**
|
||||
* Returns an array of values of the enumerable properties of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
values<T>(o: { [s: string]: T }): T[];
|
||||
values(o: any): any[];
|
||||
/**
|
||||
* Returns an array of key/values of the enumerable properties of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
entries<T>(o: { [s: string]: T }): [string, T][];
|
||||
entries(o: any): [string, any][];
|
||||
}
|
||||
Vendored
+73
-76
@@ -136,12 +136,24 @@ interface ObjectConstructor {
|
||||
*/
|
||||
getOwnPropertyNames(o: any): string[];
|
||||
|
||||
/**
|
||||
* Creates an object that has null prototype.
|
||||
* @param o Object to use as a prototype. May be null
|
||||
*/
|
||||
create(o: null): any;
|
||||
|
||||
/**
|
||||
* Creates an object that has the specified prototype, and that optionally contains specified properties.
|
||||
* @param o Object to use as a prototype. May be null
|
||||
*/
|
||||
create<T>(o: T): T;
|
||||
|
||||
/**
|
||||
* Creates an object that has the specified prototype, and that optionally contains specified properties.
|
||||
* @param o Object to use as a prototype. May be null
|
||||
* @param properties JavaScript object that contains one or more property descriptors.
|
||||
*/
|
||||
create(o: any, properties?: PropertyDescriptorMap): any;
|
||||
create(o: any, properties: PropertyDescriptorMap): any;
|
||||
|
||||
/**
|
||||
* Adds a property to an object, or modifies attributes of an existing property.
|
||||
@@ -864,6 +876,7 @@ declare const RegExp: RegExpConstructor;
|
||||
interface Error {
|
||||
name: string;
|
||||
message: string;
|
||||
stack?: string;
|
||||
}
|
||||
|
||||
interface ErrorConstructor {
|
||||
@@ -948,38 +961,22 @@ interface JSON {
|
||||
* If a member contains nested objects, the nested objects are transformed before the parent object is.
|
||||
*/
|
||||
parse(text: string, reviver?: (key: any, value: any) => any): any;
|
||||
/**
|
||||
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
|
||||
* @param value A JavaScript value, usually an object or array, to be converted.
|
||||
*/
|
||||
stringify(value: any): string;
|
||||
/**
|
||||
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
|
||||
* @param value A JavaScript value, usually an object or array, to be converted.
|
||||
* @param replacer A function that transforms the results.
|
||||
*/
|
||||
stringify(value: any, replacer: (key: string, value: any) => any): string;
|
||||
/**
|
||||
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
|
||||
* @param value A JavaScript value, usually an object or array, to be converted.
|
||||
* @param replacer Array that transforms the results.
|
||||
*/
|
||||
stringify(value: any, replacer: any[]): string;
|
||||
/**
|
||||
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
|
||||
* @param value A JavaScript value, usually an object or array, to be converted.
|
||||
* @param replacer A function that transforms the results.
|
||||
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
||||
*/
|
||||
stringify(value: any, replacer: (key: string, value: any) => any, space: string | number): string;
|
||||
stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number): string;
|
||||
/**
|
||||
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
|
||||
* @param value A JavaScript value, usually an object or array, to be converted.
|
||||
* @param replacer Array that transforms the results.
|
||||
* @param replacer An array of strings and numbers that acts as a white list for selecting the object properties that will be stringified.
|
||||
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
||||
*/
|
||||
stringify(value: any, replacer: any[], space: string | number): string;
|
||||
stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
|
||||
*/
|
||||
@@ -1139,7 +1136,7 @@ interface Array<T> {
|
||||
* Sorts an array.
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: T, b: T) => number): T[];
|
||||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
/**
|
||||
* Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.
|
||||
* @param start The zero-based location in the array from which to start removing elements.
|
||||
@@ -1481,7 +1478,7 @@ interface Int8Array {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): Int8Array;
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Determines whether all the members of an array satisfy the specified test.
|
||||
@@ -1501,7 +1498,7 @@ interface Int8Array {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: number, start?: number, end?: number): Int8Array;
|
||||
fill(value: number, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
@@ -1527,12 +1524,12 @@ interface Int8Array {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -1670,7 +1667,7 @@ interface Int8Array {
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If
|
||||
* omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: number, b: number) => number): Int8Array;
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Int8Array view of the ArrayBuffer store for this array, referencing the elements
|
||||
@@ -1754,7 +1751,7 @@ interface Uint8Array {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): Uint8Array;
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Determines whether all the members of an array satisfy the specified test.
|
||||
@@ -1774,7 +1771,7 @@ interface Uint8Array {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: number, start?: number, end?: number): Uint8Array;
|
||||
fill(value: number, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
@@ -1800,12 +1797,12 @@ interface Uint8Array {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -1943,7 +1940,7 @@ interface Uint8Array {
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If
|
||||
* omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: number, b: number) => number): Uint8Array;
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Uint8Array view of the ArrayBuffer store for this array, referencing the elements
|
||||
@@ -2028,7 +2025,7 @@ interface Uint8ClampedArray {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): Uint8ClampedArray;
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Determines whether all the members of an array satisfy the specified test.
|
||||
@@ -2048,7 +2045,7 @@ interface Uint8ClampedArray {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: number, start?: number, end?: number): Uint8ClampedArray;
|
||||
fill(value: number, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
@@ -2074,12 +2071,12 @@ interface Uint8ClampedArray {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -2217,7 +2214,7 @@ interface Uint8ClampedArray {
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If
|
||||
* omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: number, b: number) => number): Uint8ClampedArray;
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Uint8ClampedArray view of the ArrayBuffer store for this array, referencing the elements
|
||||
@@ -2301,7 +2298,7 @@ interface Int16Array {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): Int16Array;
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Determines whether all the members of an array satisfy the specified test.
|
||||
@@ -2321,7 +2318,7 @@ interface Int16Array {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: number, start?: number, end?: number): Int16Array;
|
||||
fill(value: number, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
@@ -2347,12 +2344,12 @@ interface Int16Array {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -2490,7 +2487,7 @@ interface Int16Array {
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If
|
||||
* omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: number, b: number) => number): Int16Array;
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Int16Array view of the ArrayBuffer store for this array, referencing the elements
|
||||
@@ -2575,7 +2572,7 @@ interface Uint16Array {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): Uint16Array;
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Determines whether all the members of an array satisfy the specified test.
|
||||
@@ -2595,7 +2592,7 @@ interface Uint16Array {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: number, start?: number, end?: number): Uint16Array;
|
||||
fill(value: number, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
@@ -2621,12 +2618,12 @@ interface Uint16Array {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -2764,7 +2761,7 @@ interface Uint16Array {
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If
|
||||
* omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: number, b: number) => number): Uint16Array;
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Uint16Array view of the ArrayBuffer store for this array, referencing the elements
|
||||
@@ -2848,7 +2845,7 @@ interface Int32Array {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): Int32Array;
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Determines whether all the members of an array satisfy the specified test.
|
||||
@@ -2868,7 +2865,7 @@ interface Int32Array {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: number, start?: number, end?: number): Int32Array;
|
||||
fill(value: number, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
@@ -2894,12 +2891,12 @@ interface Int32Array {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -3037,7 +3034,7 @@ interface Int32Array {
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If
|
||||
* omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: number, b: number) => number): Int32Array;
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Int32Array view of the ArrayBuffer store for this array, referencing the elements
|
||||
@@ -3121,7 +3118,7 @@ interface Uint32Array {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): Uint32Array;
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Determines whether all the members of an array satisfy the specified test.
|
||||
@@ -3141,7 +3138,7 @@ interface Uint32Array {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: number, start?: number, end?: number): Uint32Array;
|
||||
fill(value: number, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
@@ -3167,12 +3164,12 @@ interface Uint32Array {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -3310,7 +3307,7 @@ interface Uint32Array {
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If
|
||||
* omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: number, b: number) => number): Uint32Array;
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Uint32Array view of the ArrayBuffer store for this array, referencing the elements
|
||||
@@ -3394,7 +3391,7 @@ interface Float32Array {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): Float32Array;
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Determines whether all the members of an array satisfy the specified test.
|
||||
@@ -3414,7 +3411,7 @@ interface Float32Array {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: number, start?: number, end?: number): Float32Array;
|
||||
fill(value: number, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
@@ -3440,12 +3437,12 @@ interface Float32Array {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -3583,7 +3580,7 @@ interface Float32Array {
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If
|
||||
* omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: number, b: number) => number): Float32Array;
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Float32Array view of the ArrayBuffer store for this array, referencing the elements
|
||||
@@ -3668,7 +3665,7 @@ interface Float64Array {
|
||||
* is treated as length+end.
|
||||
* @param end If not specified, length of the this object is used as its default value.
|
||||
*/
|
||||
copyWithin(target: number, start: number, end?: number): Float64Array;
|
||||
copyWithin(target: number, start: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Determines whether all the members of an array satisfy the specified test.
|
||||
@@ -3688,7 +3685,7 @@ interface Float64Array {
|
||||
* @param end index to stop filling the array at. If end is negative, it is treated as
|
||||
* length+end.
|
||||
*/
|
||||
fill(value: number, start?: number, end?: number): Float64Array;
|
||||
fill(value: number, start?: number, end?: number): this;
|
||||
|
||||
/**
|
||||
* Returns the elements of an array that meet the condition specified in a callback function.
|
||||
@@ -3714,12 +3711,12 @@ interface Float64Array {
|
||||
* Returns the index of the first element in the array where predicate is true, and undefined
|
||||
* otherwise.
|
||||
* @param predicate find calls predicate once for each element of the array, in ascending
|
||||
* order, until it finds one where predicate returns true. If such an element is found, find
|
||||
* immediately returns that element value. Otherwise, find returns undefined.
|
||||
* order, until it finds one where predicate returns true. If such an element is found,
|
||||
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
|
||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
||||
* predicate. If it is not provided, undefined is used instead.
|
||||
*/
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number | undefined;
|
||||
findIndex(predicate: (value: number) => boolean, thisArg?: any): number;
|
||||
|
||||
/**
|
||||
* Performs the specified action for each element in an array.
|
||||
@@ -3857,7 +3854,7 @@ interface Float64Array {
|
||||
* @param compareFn The name of the function used to determine the order of the elements. If
|
||||
* omitted, the elements are sorted in ascending, ASCII character order.
|
||||
*/
|
||||
sort(compareFn?: (a: number, b: number) => number): Float64Array;
|
||||
sort(compareFn?: (a: number, b: number) => number): this;
|
||||
|
||||
/**
|
||||
* Gets a new Float64Array view of the ArrayBuffer store for this array, referencing the elements
|
||||
|
||||
+169
-156
@@ -1,5 +1,5 @@
|
||||
/// <reference path="session.ts" />
|
||||
|
||||
|
||||
namespace ts.server {
|
||||
|
||||
export interface SessionClientHost extends LanguageServiceHost {
|
||||
@@ -21,27 +21,26 @@ namespace ts.server {
|
||||
|
||||
export class SessionClient implements LanguageService {
|
||||
private sequence: number = 0;
|
||||
private fileMapping: ts.Map<string> = {};
|
||||
private lineMaps: ts.Map<number[]> = {};
|
||||
private messages: string[] = [];
|
||||
private lastRenameEntry: RenameEntry;
|
||||
|
||||
|
||||
constructor(private host: SessionClientHost) {
|
||||
}
|
||||
|
||||
public onMessage(message: string): void {
|
||||
public onMessage(message: string): void {
|
||||
this.messages.push(message);
|
||||
}
|
||||
|
||||
private writeMessage(message: string): void {
|
||||
private writeMessage(message: string): void {
|
||||
this.host.writeMessage(message);
|
||||
}
|
||||
|
||||
private getLineMap(fileName: string): number[] {
|
||||
var lineMap = ts.lookUp(this.lineMaps, fileName);
|
||||
private getLineMap(fileName: string): number[] {
|
||||
let lineMap = ts.lookUp(this.lineMaps, fileName);
|
||||
if (!lineMap) {
|
||||
var scriptSnapshot = this.host.getScriptSnapshot(fileName);
|
||||
lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
|
||||
const scriptSnapshot = this.host.getScriptSnapshot(fileName);
|
||||
lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
|
||||
}
|
||||
return lineMap;
|
||||
}
|
||||
@@ -51,7 +50,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private positionToOneBasedLineOffset(fileName: string, position: number): protocol.Location {
|
||||
var lineOffset = ts.computeLineAndCharacterOfPosition(this.getLineMap(fileName), position);
|
||||
const lineOffset = ts.computeLineAndCharacterOfPosition(this.getLineMap(fileName), position);
|
||||
return {
|
||||
line: lineOffset.line + 1,
|
||||
offset: lineOffset.character + 1
|
||||
@@ -59,8 +58,8 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private convertCodeEditsToTextChange(fileName: string, codeEdit: protocol.CodeEdit): ts.TextChange {
|
||||
var start = this.lineOffsetToPosition(fileName, codeEdit.start);
|
||||
var end = this.lineOffsetToPosition(fileName, codeEdit.end);
|
||||
const start = this.lineOffsetToPosition(fileName, codeEdit.start);
|
||||
const end = this.lineOffsetToPosition(fileName, codeEdit.end);
|
||||
|
||||
return {
|
||||
span: ts.createTextSpanFromBounds(start, end),
|
||||
@@ -69,12 +68,13 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private processRequest<T extends protocol.Request>(command: string, args?: any): T {
|
||||
var request: protocol.Request = {
|
||||
seq: this.sequence++,
|
||||
const request: protocol.Request = {
|
||||
seq: this.sequence,
|
||||
type: "request",
|
||||
arguments: args,
|
||||
command
|
||||
};
|
||||
this.sequence++;
|
||||
|
||||
this.writeMessage(JSON.stringify(request));
|
||||
|
||||
@@ -82,34 +82,29 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private processResponse<T extends protocol.Response>(request: protocol.Request): T {
|
||||
var lastMessage = this.messages.shift();
|
||||
Debug.assert(!!lastMessage, "Did not receive any responses.");
|
||||
|
||||
// Read the content length
|
||||
var contentLengthPrefix = "Content-Length: ";
|
||||
var lines = lastMessage.split("\r\n");
|
||||
Debug.assert(lines.length >= 2, "Malformed response: Expected 3 lines in the response.");
|
||||
|
||||
var contentLengthText = lines[0];
|
||||
Debug.assert(contentLengthText.indexOf(contentLengthPrefix) === 0, "Malformed response: Response text did not contain content-length header.");
|
||||
var contentLength = parseInt(contentLengthText.substring(contentLengthPrefix.length));
|
||||
|
||||
// Read the body
|
||||
var responseBody = lines[2];
|
||||
|
||||
// Verify content length
|
||||
Debug.assert(responseBody.length + 1 === contentLength, "Malformed response: Content length did not match the response's body length.");
|
||||
|
||||
try {
|
||||
var response: T = JSON.parse(responseBody);
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error("Malformed response: Failed to parse server response: " + lastMessage + ". \r\n Error details: " + e.message);
|
||||
let foundResponseMessage = false;
|
||||
let lastMessage: string;
|
||||
let response: T;
|
||||
while (!foundResponseMessage) {
|
||||
lastMessage = this.messages.shift();
|
||||
Debug.assert(!!lastMessage, "Did not receive any responses.");
|
||||
const responseBody = processMessage(lastMessage);
|
||||
try {
|
||||
response = JSON.parse(responseBody);
|
||||
// the server may emit events before emitting the response. We
|
||||
// want to ignore these events for testing purpose.
|
||||
if (response.type === "response") {
|
||||
foundResponseMessage = true;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error("Malformed response: Failed to parse server response: " + lastMessage + ". \r\n Error details: " + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// verify the sequence numbers
|
||||
Debug.assert(response.request_seq === request.seq, "Malformed response: response sequence number did not match request sequence number.");
|
||||
|
||||
|
||||
// unmarshal errors
|
||||
if (!response.success) {
|
||||
throw new Error("Error " + response.message);
|
||||
@@ -118,15 +113,33 @@ namespace ts.server {
|
||||
Debug.assert(!!response.body, "Malformed response: Unexpected empty response body.");
|
||||
|
||||
return response;
|
||||
|
||||
function processMessage(message: string) {
|
||||
// Read the content length
|
||||
const contentLengthPrefix = "Content-Length: ";
|
||||
const lines = message.split("\r\n");
|
||||
Debug.assert(lines.length >= 2, "Malformed response: Expected 3 lines in the response.");
|
||||
|
||||
const contentLengthText = lines[0];
|
||||
Debug.assert(contentLengthText.indexOf(contentLengthPrefix) === 0, "Malformed response: Response text did not contain content-length header.");
|
||||
const contentLength = parseInt(contentLengthText.substring(contentLengthPrefix.length));
|
||||
|
||||
// Read the body
|
||||
const responseBody = lines[2];
|
||||
|
||||
// Verify content length
|
||||
Debug.assert(responseBody.length + 1 === contentLength, "Malformed response: Content length did not match the response's body length.");
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
|
||||
openFile(fileName: string, content?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void {
|
||||
var args: protocol.OpenRequestArgs = { file: fileName, fileContent: content, scriptKindName };
|
||||
openFile(fileName: string, content?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void {
|
||||
const args: protocol.OpenRequestArgs = { file: fileName, fileContent: content, scriptKindName };
|
||||
this.processRequest(CommandNames.Open, args);
|
||||
}
|
||||
|
||||
closeFile(fileName: string): void {
|
||||
var args: protocol.FileRequestArgs = { file: fileName };
|
||||
const args: protocol.FileRequestArgs = { file: fileName };
|
||||
this.processRequest(CommandNames.Close, args);
|
||||
}
|
||||
|
||||
@@ -134,10 +147,10 @@ namespace ts.server {
|
||||
// clear the line map after an edit
|
||||
this.lineMaps[fileName] = undefined;
|
||||
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, start);
|
||||
var endLineOffset = this.positionToOneBasedLineOffset(fileName, end);
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, start);
|
||||
const endLineOffset = this.positionToOneBasedLineOffset(fileName, end);
|
||||
|
||||
var args: protocol.ChangeRequestArgs = {
|
||||
const args: protocol.ChangeRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
@@ -150,18 +163,18 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.QuickInfoRequest>(CommandNames.Quickinfo, args);
|
||||
var response = this.processResponse<protocol.QuickInfoResponse>(request);
|
||||
const request = this.processRequest<protocol.QuickInfoRequest>(CommandNames.Quickinfo, args);
|
||||
const response = this.processResponse<protocol.QuickInfoResponse>(request);
|
||||
|
||||
var start = this.lineOffsetToPosition(fileName, response.body.start);
|
||||
var end = this.lineOffsetToPosition(fileName, response.body.end);
|
||||
const start = this.lineOffsetToPosition(fileName, response.body.start);
|
||||
const end = this.lineOffsetToPosition(fileName, response.body.end);
|
||||
|
||||
return {
|
||||
kind: response.body.kind,
|
||||
@@ -173,68 +186,68 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getProjectInfo(fileName: string, needFileNameList: boolean): protocol.ProjectInfo {
|
||||
var args: protocol.ProjectInfoRequestArgs = {
|
||||
const args: protocol.ProjectInfoRequestArgs = {
|
||||
file: fileName,
|
||||
needFileNameList: needFileNameList
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.ProjectInfoRequest>(CommandNames.ProjectInfo, args);
|
||||
var response = this.processResponse<protocol.ProjectInfoResponse>(request);
|
||||
const request = this.processRequest<protocol.ProjectInfoRequest>(CommandNames.ProjectInfo, args);
|
||||
const response = this.processResponse<protocol.ProjectInfoResponse>(request);
|
||||
|
||||
return {
|
||||
configFileName: response.body.configFileName,
|
||||
fileNames: response.body.fileNames
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.CompletionsRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.CompletionsRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
prefix: undefined
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.CompletionsRequest>(CommandNames.Completions, args);
|
||||
var response = this.processResponse<protocol.CompletionsResponse>(request);
|
||||
const request = this.processRequest<protocol.CompletionsRequest>(CommandNames.Completions, args);
|
||||
const response = this.processResponse<protocol.CompletionsResponse>(request);
|
||||
|
||||
return {
|
||||
return {
|
||||
isMemberCompletion: false,
|
||||
isNewIdentifierLocation: false,
|
||||
entries: response.body
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.CompletionDetailsRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.CompletionDetailsRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
entryNames: [entryName]
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.CompletionDetailsRequest>(CommandNames.CompletionDetails, args);
|
||||
var response = this.processResponse<protocol.CompletionDetailsResponse>(request);
|
||||
const request = this.processRequest<protocol.CompletionDetailsRequest>(CommandNames.CompletionDetails, args);
|
||||
const response = this.processResponse<protocol.CompletionDetailsResponse>(request);
|
||||
Debug.assert(response.body.length === 1, "Unexpected length of completion details response body.");
|
||||
return response.body[0];
|
||||
}
|
||||
|
||||
getNavigateToItems(searchValue: string): NavigateToItem[] {
|
||||
var args: protocol.NavtoRequestArgs = {
|
||||
const args: protocol.NavtoRequestArgs = {
|
||||
searchValue,
|
||||
file: this.host.getScriptFileNames()[0]
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.NavtoRequest>(CommandNames.Navto, args);
|
||||
var response = this.processResponse<protocol.NavtoResponse>(request);
|
||||
const request = this.processRequest<protocol.NavtoRequest>(CommandNames.Navto, args);
|
||||
const response = this.processResponse<protocol.NavtoResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
var fileName = entry.file;
|
||||
var start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
var end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
|
||||
return {
|
||||
name: entry.name,
|
||||
containerName: entry.containerName || "",
|
||||
@@ -250,9 +263,9 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): ts.TextChange[] {
|
||||
var startLineOffset = this.positionToOneBasedLineOffset(fileName, start);
|
||||
var endLineOffset = this.positionToOneBasedLineOffset(fileName, end);
|
||||
var args: protocol.FormatRequestArgs = {
|
||||
const startLineOffset = this.positionToOneBasedLineOffset(fileName, start);
|
||||
const endLineOffset = this.positionToOneBasedLineOffset(fileName, end);
|
||||
const args: protocol.FormatRequestArgs = {
|
||||
file: fileName,
|
||||
line: startLineOffset.line,
|
||||
offset: startLineOffset.offset,
|
||||
@@ -261,10 +274,10 @@ namespace ts.server {
|
||||
};
|
||||
|
||||
// TODO: handle FormatCodeOptions
|
||||
var request = this.processRequest<protocol.FormatRequest>(CommandNames.Format, args);
|
||||
var response = this.processResponse<protocol.FormatResponse>(request);
|
||||
const request = this.processRequest<protocol.FormatRequest>(CommandNames.Format, args);
|
||||
const response = this.processResponse<protocol.FormatResponse>(request);
|
||||
|
||||
return response.body.map(entry=> this.convertCodeEditsToTextChange(fileName, entry));
|
||||
return response.body.map(entry => this.convertCodeEditsToTextChange(fileName, entry));
|
||||
}
|
||||
|
||||
getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] {
|
||||
@@ -272,8 +285,8 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): ts.TextChange[] {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.FormatOnKeyRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FormatOnKeyRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
@@ -281,27 +294,27 @@ namespace ts.server {
|
||||
};
|
||||
|
||||
// TODO: handle FormatCodeOptions
|
||||
var request = this.processRequest<protocol.FormatOnKeyRequest>(CommandNames.Formatonkey, args);
|
||||
var response = this.processResponse<protocol.FormatResponse>(request);
|
||||
const request = this.processRequest<protocol.FormatOnKeyRequest>(CommandNames.Formatonkey, args);
|
||||
const response = this.processResponse<protocol.FormatResponse>(request);
|
||||
|
||||
return response.body.map(entry=> this.convertCodeEditsToTextChange(fileName, entry));
|
||||
return response.body.map(entry => this.convertCodeEditsToTextChange(fileName, entry));
|
||||
}
|
||||
|
||||
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.DefinitionRequest>(CommandNames.Definition, args);
|
||||
var response = this.processResponse<protocol.DefinitionResponse>(request);
|
||||
const request = this.processRequest<protocol.DefinitionRequest>(CommandNames.Definition, args);
|
||||
const response = this.processResponse<protocol.DefinitionResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
var fileName = entry.file;
|
||||
var start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
var end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
@@ -314,20 +327,20 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.TypeDefinitionRequest>(CommandNames.TypeDefinition, args);
|
||||
var response = this.processResponse<protocol.TypeDefinitionResponse>(request);
|
||||
const request = this.processRequest<protocol.TypeDefinitionRequest>(CommandNames.TypeDefinition, args);
|
||||
const response = this.processResponse<protocol.TypeDefinitionResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
var fileName = entry.file;
|
||||
var start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
var end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
@@ -339,26 +352,26 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
findReferences(fileName: string, position: number): ReferencedSymbol[]{
|
||||
findReferences(fileName: string, position: number): ReferencedSymbol[] {
|
||||
// Not yet implemented.
|
||||
return [];
|
||||
}
|
||||
|
||||
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.ReferencesRequest>(CommandNames.References, args);
|
||||
var response = this.processResponse<protocol.ReferencesResponse>(request);
|
||||
const request = this.processRequest<protocol.ReferencesRequest>(CommandNames.References, args);
|
||||
const response = this.processResponse<protocol.ReferencesResponse>(request);
|
||||
|
||||
return response.body.refs.map(entry => {
|
||||
var fileName = entry.file;
|
||||
var start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
var end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
fileName: fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
@@ -384,8 +397,8 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getRenameInfo(fileName: string, position: number, findInStrings?: boolean, findInComments?: boolean): RenameInfo {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.RenameRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.RenameRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
@@ -393,14 +406,14 @@ namespace ts.server {
|
||||
findInComments
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.RenameRequest>(CommandNames.Rename, args);
|
||||
var response = this.processResponse<protocol.RenameResponse>(request);
|
||||
var locations: RenameLocation[] = [];
|
||||
const request = this.processRequest<protocol.RenameRequest>(CommandNames.Rename, args);
|
||||
const response = this.processResponse<protocol.RenameResponse>(request);
|
||||
const locations: RenameLocation[] = [];
|
||||
response.body.locs.map((entry: protocol.SpanGroup) => {
|
||||
var fileName = entry.file;
|
||||
const fileName = entry.file;
|
||||
entry.locs.map((loc: protocol.TextSpan) => {
|
||||
var start = this.lineOffsetToPosition(fileName, loc.start);
|
||||
var end = this.lineOffsetToPosition(fileName, loc.end);
|
||||
const start = this.lineOffsetToPosition(fileName, loc.start);
|
||||
const end = this.lineOffsetToPosition(fileName, loc.end);
|
||||
locations.push({
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
fileName: fileName
|
||||
@@ -444,21 +457,21 @@ namespace ts.server {
|
||||
text: item.text,
|
||||
kind: item.kind,
|
||||
kindModifiers: item.kindModifiers || "",
|
||||
spans: item.spans.map(span=> createTextSpanFromBounds(this.lineOffsetToPosition(fileName, span.start), this.lineOffsetToPosition(fileName, span.end))),
|
||||
spans: item.spans.map(span => createTextSpanFromBounds(this.lineOffsetToPosition(fileName, span.start), this.lineOffsetToPosition(fileName, span.end))),
|
||||
childItems: this.decodeNavigationBarItems(item.childItems, fileName),
|
||||
indent: 0,
|
||||
indent: item.indent,
|
||||
bolded: false,
|
||||
grayed: false
|
||||
}));
|
||||
}
|
||||
|
||||
getNavigationBarItems(fileName: string): NavigationBarItem[] {
|
||||
var args: protocol.FileRequestArgs = {
|
||||
const args: protocol.FileRequestArgs = {
|
||||
file: fileName
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.NavBarRequest>(CommandNames.NavBar, args);
|
||||
var response = this.processResponse<protocol.NavBarResponse>(request);
|
||||
const request = this.processRequest<protocol.NavBarRequest>(CommandNames.NavBar, args);
|
||||
const response = this.processResponse<protocol.NavBarResponse>(request);
|
||||
|
||||
return this.decodeNavigationBarItems(response.body, fileName);
|
||||
}
|
||||
@@ -472,26 +485,26 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.SignatureHelpRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.SignatureHelpRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.SignatureHelpRequest>(CommandNames.SignatureHelp, args);
|
||||
var response = this.processResponse<protocol.SignatureHelpResponse>(request);
|
||||
|
||||
|
||||
const request = this.processRequest<protocol.SignatureHelpRequest>(CommandNames.SignatureHelp, args);
|
||||
const response = this.processResponse<protocol.SignatureHelpResponse>(request);
|
||||
|
||||
if (!response.body) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var helpItems: protocol.SignatureHelpItems = response.body;
|
||||
var span = helpItems.applicableSpan;
|
||||
var start = this.lineOffsetToPosition(fileName, span.start);
|
||||
var end = this.lineOffsetToPosition(fileName, span.end);
|
||||
|
||||
var result: SignatureHelpItems = {
|
||||
const helpItems: protocol.SignatureHelpItems = response.body;
|
||||
const span = helpItems.applicableSpan;
|
||||
const start = this.lineOffsetToPosition(fileName, span.start);
|
||||
const end = this.lineOffsetToPosition(fileName, span.end);
|
||||
|
||||
const result: SignatureHelpItems = {
|
||||
items: helpItems.items,
|
||||
applicableSpan: {
|
||||
start: start,
|
||||
@@ -499,26 +512,26 @@ namespace ts.server {
|
||||
},
|
||||
selectedItemIndex: helpItems.selectedItemIndex,
|
||||
argumentIndex: helpItems.argumentIndex,
|
||||
argumentCount: helpItems.argumentCount,
|
||||
}
|
||||
argumentCount: helpItems.argumentCount,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.OccurrencesRequest>(CommandNames.Occurrences, args);
|
||||
var response = this.processResponse<protocol.OccurrencesResponse>(request);
|
||||
const request = this.processRequest<protocol.OccurrencesRequest>(CommandNames.Occurrences, args);
|
||||
const response = this.processResponse<protocol.OccurrencesResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
var fileName = entry.file;
|
||||
var start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
var end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
const fileName = entry.file;
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
@@ -528,17 +541,17 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] {
|
||||
let { line, offset } = this.positionToOneBasedLineOffset(fileName, position);
|
||||
let args: protocol.DocumentHighlightsRequestArgs = { file: fileName, line, offset, filesToSearch };
|
||||
const { line, offset } = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.DocumentHighlightsRequestArgs = { file: fileName, line, offset, filesToSearch };
|
||||
|
||||
let request = this.processRequest<protocol.DocumentHighlightsRequest>(CommandNames.DocumentHighlights, args);
|
||||
let response = this.processResponse<protocol.DocumentHighlightsResponse>(request);
|
||||
const request = this.processRequest<protocol.DocumentHighlightsRequest>(CommandNames.DocumentHighlights, args);
|
||||
const response = this.processResponse<protocol.DocumentHighlightsResponse>(request);
|
||||
|
||||
let self = this;
|
||||
const self = this;
|
||||
return response.body.map(convertToDocumentHighlights);
|
||||
|
||||
function convertToDocumentHighlights(item: ts.server.protocol.DocumentHighlightsItem): ts.DocumentHighlights {
|
||||
let { file, highlightSpans } = item;
|
||||
const { file, highlightSpans } = item;
|
||||
|
||||
return {
|
||||
fileName: file,
|
||||
@@ -546,8 +559,8 @@ namespace ts.server {
|
||||
};
|
||||
|
||||
function convertHighlightSpan(span: ts.server.protocol.HighlightSpan): ts.HighlightSpan {
|
||||
let start = self.lineOffsetToPosition(file, span.start);
|
||||
let end = self.lineOffsetToPosition(file, span.end);
|
||||
const start = self.lineOffsetToPosition(file, span.start);
|
||||
const end = self.lineOffsetToPosition(file, span.end);
|
||||
return {
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
kind: span.kind
|
||||
@@ -561,31 +574,31 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
|
||||
getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
isValidBraceCompletionAtPostion(fileName: string, position: number, openingBrace: number): boolean {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
throw new Error("Not Implemented Yet.");
|
||||
}
|
||||
|
||||
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
|
||||
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
var args: protocol.FileLocationRequestArgs = {
|
||||
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
|
||||
const args: protocol.FileLocationRequestArgs = {
|
||||
file: fileName,
|
||||
line: lineOffset.line,
|
||||
offset: lineOffset.offset,
|
||||
};
|
||||
|
||||
var request = this.processRequest<protocol.BraceRequest>(CommandNames.Brace, args);
|
||||
var response = this.processResponse<protocol.BraceResponse>(request);
|
||||
const request = this.processRequest<protocol.BraceRequest>(CommandNames.Brace, args);
|
||||
const response = this.processResponse<protocol.BraceResponse>(request);
|
||||
|
||||
return response.body.map(entry => {
|
||||
var start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
var end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
const start = this.lineOffsetToPosition(fileName, entry.start);
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
start: start,
|
||||
length: end - start,
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace ts.server {
|
||||
children: ScriptInfo[] = []; // files referenced by this file
|
||||
defaultProject: Project; // project to use by default for file
|
||||
fileWatcher: FileWatcher;
|
||||
formatCodeOptions = ts.clone(CompilerService.defaultFormatCodeOptions);
|
||||
formatCodeOptions = ts.clone(CompilerService.getDefaultFormatCodeOptions(this.host));
|
||||
path: Path;
|
||||
scriptKind: ScriptKind;
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
removeRoot(info: ScriptInfo) {
|
||||
if (!this.filenameToScript.contains(info.path)) {
|
||||
if (this.filenameToScript.contains(info.path)) {
|
||||
this.filenameToScript.remove(info.path);
|
||||
this.roots = copyListRemovingItem(info, this.roots);
|
||||
this.resolvedModuleNames.remove(info.path);
|
||||
@@ -533,7 +533,7 @@ namespace ts.server {
|
||||
// number becomes 0 for a watcher, then we should close it.
|
||||
directoryWatchersRefCount: ts.Map<number> = {};
|
||||
hostConfiguration: HostConfiguration;
|
||||
timerForDetectingProjectFileListChanges: Map<NodeJS.Timer> = {};
|
||||
timerForDetectingProjectFileListChanges: Map<any> = {};
|
||||
|
||||
constructor(public host: ServerHost, public psLogger: Logger, public eventHandler?: ProjectServiceEventHandler) {
|
||||
// ts.disableIncrementalParsing = true;
|
||||
@@ -542,7 +542,7 @@ namespace ts.server {
|
||||
|
||||
addDefaultHostConfiguration() {
|
||||
this.hostConfiguration = {
|
||||
formatCodeOptions: ts.clone(CompilerService.defaultFormatCodeOptions),
|
||||
formatCodeOptions: ts.clone(CompilerService.getDefaultFormatCodeOptions(this.host)),
|
||||
hostInfo: "Unknown host"
|
||||
};
|
||||
}
|
||||
@@ -593,9 +593,9 @@ namespace ts.server {
|
||||
|
||||
startTimerForDetectingProjectFileListChanges(project: Project) {
|
||||
if (this.timerForDetectingProjectFileListChanges[project.projectFilename]) {
|
||||
clearTimeout(this.timerForDetectingProjectFileListChanges[project.projectFilename]);
|
||||
this.host.clearTimeout(this.timerForDetectingProjectFileListChanges[project.projectFilename]);
|
||||
}
|
||||
this.timerForDetectingProjectFileListChanges[project.projectFilename] = setTimeout(
|
||||
this.timerForDetectingProjectFileListChanges[project.projectFilename] = this.host.setTimeout(
|
||||
() => this.handleProjectFileListChanges(project),
|
||||
250
|
||||
);
|
||||
@@ -1122,8 +1122,13 @@ namespace ts.server {
|
||||
return { configFileName, configFileErrors: configResult.errors };
|
||||
}
|
||||
else {
|
||||
// even if opening config file was successful, it could still
|
||||
// contain errors that were tolerated.
|
||||
this.log("Opened configuration file " + configFileName, "Info");
|
||||
this.configuredProjects.push(configResult.project);
|
||||
if (configResult.errors && configResult.errors.length > 0) {
|
||||
return { configFileName, configFileErrors: configResult.errors };
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1133,7 +1138,7 @@ namespace ts.server {
|
||||
else {
|
||||
this.log("No config files found.");
|
||||
}
|
||||
return {};
|
||||
return configFileName ? { configFileName } : {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1261,14 +1266,14 @@ namespace ts.server {
|
||||
}
|
||||
else {
|
||||
const project = this.createProject(configFilename, projectOptions);
|
||||
let errors: Diagnostic[];
|
||||
for (const rootFilename of projectOptions.files) {
|
||||
if (this.host.fileExists(rootFilename)) {
|
||||
const info = this.openFile(rootFilename, /*openedByClient*/ clientFileName == rootFilename);
|
||||
project.addRoot(info);
|
||||
}
|
||||
else {
|
||||
const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, rootFilename);
|
||||
return { success: false, errors: [error] };
|
||||
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.File_0_not_found, rootFilename));
|
||||
}
|
||||
}
|
||||
project.finishGraph();
|
||||
@@ -1279,7 +1284,7 @@ namespace ts.server {
|
||||
path => this.directoryWatchedForSourceFilesChanged(project, path),
|
||||
/*recursive*/ true
|
||||
);
|
||||
return { success: true, project: project };
|
||||
return { success: true, project: project, errors };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1295,7 +1300,7 @@ namespace ts.server {
|
||||
}
|
||||
else {
|
||||
const oldFileNames = project.compilerService.host.roots.map(info => info.fileName);
|
||||
const newFileNames = projectOptions.files;
|
||||
const newFileNames = ts.filter(projectOptions.files, f => this.host.fileExists(f));
|
||||
const fileNamesToRemove = oldFileNames.filter(f => newFileNames.indexOf(f) < 0);
|
||||
const fileNamesToAdd = newFileNames.filter(f => oldFileNames.indexOf(f) < 0);
|
||||
|
||||
@@ -1377,23 +1382,25 @@ namespace ts.server {
|
||||
return ts.isExternalModule(sourceFile);
|
||||
}
|
||||
|
||||
static defaultFormatCodeOptions: ts.FormatCodeOptions = {
|
||||
IndentSize: 4,
|
||||
TabSize: 4,
|
||||
NewLineCharacter: ts.sys ? ts.sys.newLine : "\n",
|
||||
ConvertTabsToSpaces: true,
|
||||
IndentStyle: ts.IndentStyle.Smart,
|
||||
InsertSpaceAfterCommaDelimiter: true,
|
||||
InsertSpaceAfterSemicolonInForStatements: true,
|
||||
InsertSpaceBeforeAndAfterBinaryOperators: true,
|
||||
InsertSpaceAfterKeywordsInControlFlowStatements: true,
|
||||
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
|
||||
PlaceOpenBraceOnNewLineForFunctions: false,
|
||||
PlaceOpenBraceOnNewLineForControlBlocks: false,
|
||||
};
|
||||
static getDefaultFormatCodeOptions(host: ServerHost): ts.FormatCodeOptions {
|
||||
return ts.clone({
|
||||
IndentSize: 4,
|
||||
TabSize: 4,
|
||||
NewLineCharacter: host.newLine || "\n",
|
||||
ConvertTabsToSpaces: true,
|
||||
IndentStyle: ts.IndentStyle.Smart,
|
||||
InsertSpaceAfterCommaDelimiter: true,
|
||||
InsertSpaceAfterSemicolonInForStatements: true,
|
||||
InsertSpaceBeforeAndAfterBinaryOperators: true,
|
||||
InsertSpaceAfterKeywordsInControlFlowStatements: true,
|
||||
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
|
||||
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
|
||||
PlaceOpenBraceOnNewLineForFunctions: false,
|
||||
PlaceOpenBraceOnNewLineForControlBlocks: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface LineCollection {
|
||||
|
||||
Vendored
+5
@@ -1242,6 +1242,11 @@ declare namespace ts.server.protocol {
|
||||
* Optional children.
|
||||
*/
|
||||
childItems?: NavigationBarItem[];
|
||||
|
||||
/**
|
||||
* Number of levels deep this item should appear.
|
||||
*/
|
||||
indent: number;
|
||||
}
|
||||
|
||||
export interface NavBarResponse extends Response {
|
||||
|
||||
@@ -266,16 +266,21 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
const sys = <ServerHost>ts.sys;
|
||||
|
||||
// Override sys.write because fs.writeSync is not reliable on Node 4
|
||||
ts.sys.write = (s: string) => writeMessage(s);
|
||||
ts.sys.watchFile = (fileName, callback) => {
|
||||
sys.write = (s: string) => writeMessage(s);
|
||||
sys.watchFile = (fileName, callback) => {
|
||||
const watchedFile = pollingWatchedFileSet.addFile(fileName, callback);
|
||||
return {
|
||||
close: () => pollingWatchedFileSet.removeFile(watchedFile)
|
||||
};
|
||||
};
|
||||
|
||||
const ioSession = new IOSession(ts.sys, logger);
|
||||
sys.setTimeout = setTimeout;
|
||||
sys.clearTimeout = clearTimeout;
|
||||
|
||||
const ioSession = new IOSession(sys, logger);
|
||||
process.on("uncaughtException", function(err: Error) {
|
||||
ioSession.logError(err, "unknown");
|
||||
});
|
||||
|
||||
@@ -133,6 +133,8 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export interface ServerHost extends ts.System {
|
||||
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
|
||||
clearTimeout(timeoutId: any): void;
|
||||
}
|
||||
|
||||
export class Session {
|
||||
@@ -870,7 +872,8 @@ namespace ts.server {
|
||||
start: compilerService.host.positionToLineOffset(fileName, span.start),
|
||||
end: compilerService.host.positionToLineOffset(fileName, ts.textSpanEnd(span))
|
||||
})),
|
||||
childItems: this.decorateNavigationBarItem(project, fileName, item.childItems)
|
||||
childItems: this.decorateNavigationBarItem(project, fileName, item.childItems),
|
||||
indent: item.indent
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
+31
-31
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0.
|
||||
// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0.
|
||||
// See LICENSE.txt in the project root for complete license information.
|
||||
|
||||
/// <reference path='services.ts' />
|
||||
@@ -15,16 +15,16 @@ 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
|
||||
// let y = 10;
|
||||
// token at position will return let keyword on second line as the token but we would like to use
|
||||
// let y = 10;
|
||||
// token at position will return let keyword on second line as the token but we would like to use
|
||||
// token on same line if trailing trivia (comments or white spaces on same line) part of the last token on that line
|
||||
tokenAtLocation = findPrecedingToken(tokenAtLocation.pos, sourceFile);
|
||||
|
||||
// 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);
|
||||
@@ -261,7 +261,7 @@ namespace ts.BreakpointResolver {
|
||||
}
|
||||
|
||||
// Set breakpoint on identifier element of destructuring pattern
|
||||
// a or ...c or d: x from
|
||||
// a or ...c or d: x from
|
||||
// [a, b, ...c] or { a, b } or { d: x } from destructuring pattern
|
||||
if ((node.kind === SyntaxKind.Identifier ||
|
||||
node.kind == SyntaxKind.SpreadElementExpression ||
|
||||
@@ -275,7 +275,7 @@ namespace ts.BreakpointResolver {
|
||||
const binaryExpression = <BinaryExpression>node;
|
||||
// Set breakpoint in destructuring pattern if its destructuring assignment
|
||||
// [a, b, c] or {a, b, c} of
|
||||
// [a, b, c] = expression or
|
||||
// [a, b, c] = expression or
|
||||
// {a, b, c} = expression
|
||||
if (isArrayLiteralOrObjectLiteralDestructuringPattern(binaryExpression.left)) {
|
||||
return spanInArrayLiteralOrObjectLiteralDestructuringPattern(
|
||||
@@ -285,8 +285,8 @@ namespace ts.BreakpointResolver {
|
||||
if (binaryExpression.operatorToken.kind === SyntaxKind.EqualsToken &&
|
||||
isArrayLiteralOrObjectLiteralDestructuringPattern(binaryExpression.parent)) {
|
||||
// Set breakpoint on assignment expression element of destructuring pattern
|
||||
// a = expression of
|
||||
// [a = expression, b, c] = someExpression or
|
||||
// a = expression of
|
||||
// [a = expression, b, c] = someExpression or
|
||||
// { a = expression, b, c } = someExpression
|
||||
return textSpan(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
|
||||
// Because the variable declaration may be binding pattern and
|
||||
// we would like to set breakpoint in last binding element if thats the case,
|
||||
// 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 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) {
|
||||
@@ -549,7 +549,7 @@ namespace ts.BreakpointResolver {
|
||||
return spanInNode(firstBindingElement);
|
||||
}
|
||||
|
||||
// Could be ArrayLiteral from destructuring assignment or
|
||||
// Could be ArrayLiteral from destructuring assignment or
|
||||
// just nested element in another destructuring assignment
|
||||
// set breakpoint on assignment when parent is destructuring assignment
|
||||
// Otherwise set breakpoint for this element
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -723,4 +723,4 @@ namespace ts.BreakpointResolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,16 +736,16 @@ 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);
|
||||
const commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation, container);
|
||||
|
||||
for (let triviaItem of currentTokenInfo.leadingTrivia) {
|
||||
const triviaInRange = rangeContainsRange(originalRange, triviaItem);
|
||||
for (const triviaItem of currentTokenInfo.leadingTrivia) {
|
||||
const triviaInRange = rangeContainsRange(originalRange, triviaItem);
|
||||
switch (triviaItem.kind) {
|
||||
case SyntaxKind.MultiLineCommentTrivia:
|
||||
if (triviaInRange) {
|
||||
@@ -770,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);
|
||||
}
|
||||
}
|
||||
@@ -784,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,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;
|
||||
@@ -826,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -858,15 +870,15 @@ 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);
|
||||
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);
|
||||
}
|
||||
@@ -880,7 +892,7 @@ namespace ts.formatting {
|
||||
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) {
|
||||
@@ -892,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);
|
||||
}
|
||||
@@ -901,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) {
|
||||
@@ -917,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 {
|
||||
@@ -937,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);
|
||||
@@ -973,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) {
|
||||
@@ -1003,7 +1015,6 @@ namespace ts.formatting {
|
||||
currentRange: TextRangeWithKind,
|
||||
currentStartLine: number): void {
|
||||
|
||||
let between: TextRange;
|
||||
switch (rule.Operation.Action) {
|
||||
case RuleAction.Ignore:
|
||||
// no action required
|
||||
@@ -1023,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);
|
||||
}
|
||||
@@ -1034,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, " ");
|
||||
}
|
||||
@@ -1043,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:
|
||||
@@ -1096,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) {
|
||||
@@ -1111,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) {
|
||||
@@ -1120,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];
|
||||
@@ -1130,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 = [];
|
||||
}
|
||||
@@ -1149,7 +1151,7 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,14 +263,14 @@ 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);
|
||||
}
|
||||
|
||||
// when containing node in the tree is token
|
||||
// when containing node in the tree is token
|
||||
// but its kind differs from the kind that was returned by the scanner,
|
||||
// then kind needs to be fixed. This might happen in cases
|
||||
// then kind needs to be fixed. This might happen in cases
|
||||
// when parser interprets token differently, i.e keyword treated as identifier
|
||||
function fixTokenKind(tokenInfo: TokenInfo, container: Node): TokenInfo {
|
||||
if (isToken(container) && tokenInfo.token.kind !== container.kind) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -313,7 +313,7 @@ namespace ts.formatting {
|
||||
this.SpaceAfterVoidOperator = new Rule(RuleDescriptor.create3(SyntaxKind.VoidKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsVoidOpContext), RuleAction.Space));
|
||||
|
||||
this.NoSpaceBetweenReturnAndSemicolon = new Rule(RuleDescriptor.create1(SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
|
||||
// Add a space between statements. All keywords except (do,else,case) has open/close parens after them.
|
||||
// So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any]
|
||||
this.SpaceBetweenStatements = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNotForContext), RuleAction.Space));
|
||||
@@ -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
|
||||
@@ -554,17 +554,17 @@ namespace ts.formatting {
|
||||
static IsSameLineTokenOrBeforeMultilineBlockContext(context: FormattingContext): boolean {
|
||||
//// This check is mainly used inside SpaceBeforeOpenBraceInControl and SpaceBeforeOpenBraceInFunction.
|
||||
////
|
||||
//// Ex:
|
||||
//// Ex:
|
||||
//// if (1) { ....
|
||||
//// * ) and { are on the same line so apply the rule. Here we don't care whether it's same or multi block context
|
||||
////
|
||||
//// Ex:
|
||||
//// Ex:
|
||||
//// if (1)
|
||||
//// { ... }
|
||||
//// * ) and { are on different lines. We only need to format if the block is multiline context. So in this case we don't format.
|
||||
////
|
||||
//// Ex:
|
||||
//// if (1)
|
||||
//// if (1)
|
||||
//// { ...
|
||||
//// }
|
||||
//// * ) and { are on different lines. We only need to format if the block is multiline context. So in this case we format.
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
@@ -95,9 +95,9 @@ namespace ts.formatting {
|
||||
//// 4- Context rules with any token combination
|
||||
//// 5- Non-context rules with specific token combination
|
||||
//// 6- Non-context rules with any token combination
|
||||
////
|
||||
////
|
||||
//// The member rulesInsertionIndexBitmap is used to describe the number of rules
|
||||
//// in each sub-bucket (above) hence can be used to know the index of where to insert
|
||||
//// in each sub-bucket (above) hence can be used to know the index of where to insert
|
||||
//// the next rule. It's a bitmap which contains 6 different sections each is given 5 bits.
|
||||
////
|
||||
//// Example:
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) || isStatement(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;
|
||||
}
|
||||
@@ -467,10 +467,10 @@ namespace ts.formatting {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* @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,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);
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
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[] = [];
|
||||
|
||||
// This means "compare in a case insensitive manner."
|
||||
const baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
|
||||
|
||||
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
|
||||
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
|
||||
forEach(program.getSourceFiles(), sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace ts.NavigateTo {
|
||||
for (const name in nameToDeclarations) {
|
||||
const declarations = getProperty(nameToDeclarations, name);
|
||||
if (declarations) {
|
||||
// First do a quick check to see if the name of the declaration matches the
|
||||
// First do a quick check to see if the name of the declaration matches the
|
||||
// last portion of the (possibly) dotted name they're searching for.
|
||||
let matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace ts.NavigateTo {
|
||||
}
|
||||
|
||||
for (const declaration of declarations) {
|
||||
// It was a match! If the pattern has dots in it, then also see if the
|
||||
// It was a match! If the pattern has dots in it, then also see if the
|
||||
// declaration container matches as well.
|
||||
if (patternMatcher.patternContainsDots) {
|
||||
const containers = getContainers(declaration);
|
||||
@@ -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
-69
@@ -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;
|
||||
}
|
||||
|
||||
@@ -773,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());
|
||||
}
|
||||
|
||||
+253
-142
@@ -20,7 +20,7 @@ namespace ts {
|
||||
getChildCount(sourceFile?: SourceFile): number;
|
||||
getChildAt(index: number, sourceFile?: SourceFile): Node;
|
||||
getChildren(sourceFile?: SourceFile): Node[];
|
||||
getStart(sourceFile?: SourceFile): number;
|
||||
getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number;
|
||||
getFullStart(): number;
|
||||
getEnd(): number;
|
||||
getWidth(sourceFile?: SourceFile): number;
|
||||
@@ -50,6 +50,7 @@ namespace ts {
|
||||
getStringIndexType(): Type;
|
||||
getNumberIndexType(): Type;
|
||||
getBaseTypes(): ObjectType[];
|
||||
getNonNullableType(): Type;
|
||||
}
|
||||
|
||||
export interface Signature {
|
||||
@@ -171,6 +172,9 @@ namespace ts {
|
||||
"static",
|
||||
"throws",
|
||||
"type",
|
||||
"typedef",
|
||||
"property",
|
||||
"prop",
|
||||
"version"
|
||||
];
|
||||
let jsDocCompletionEntries: CompletionEntry[];
|
||||
@@ -188,6 +192,7 @@ namespace ts {
|
||||
public end: number;
|
||||
public flags: NodeFlags;
|
||||
public parent: Node;
|
||||
public jsDocComments: JSDocComment[];
|
||||
private _children: Node[];
|
||||
|
||||
constructor(kind: SyntaxKind, pos: number, end: number) {
|
||||
@@ -202,8 +207,8 @@ namespace ts {
|
||||
return getSourceFileOfNode(this);
|
||||
}
|
||||
|
||||
public getStart(sourceFile?: SourceFile): number {
|
||||
return getTokenPosOfNode(this, sourceFile);
|
||||
public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number {
|
||||
return getTokenPosOfNode(this, sourceFile, includeJsDocComment);
|
||||
}
|
||||
|
||||
public getFullStart(): number {
|
||||
@@ -234,12 +239,14 @@ namespace ts {
|
||||
return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd());
|
||||
}
|
||||
|
||||
private addSyntheticNodes(nodes: Node[], pos: number, end: number): number {
|
||||
private addSyntheticNodes(nodes: Node[], pos: number, end: number, useJSDocScanner?: boolean): number {
|
||||
scanner.setTextPos(pos);
|
||||
while (pos < end) {
|
||||
const token = scanner.scan();
|
||||
const token = useJSDocScanner ? scanner.scanJSDocToken() : scanner.scan();
|
||||
const textPos = scanner.getTextPos();
|
||||
nodes.push(createNode(token, pos, textPos, 0, this));
|
||||
if (textPos <= end) {
|
||||
nodes.push(createNode(token, pos, textPos, 0, this));
|
||||
}
|
||||
pos = textPos;
|
||||
}
|
||||
return pos;
|
||||
@@ -269,20 +276,27 @@ namespace ts {
|
||||
scanner.setText((sourceFile || this.getSourceFile()).text);
|
||||
children = [];
|
||||
let pos = this.pos;
|
||||
const useJSDocScanner = this.kind >= SyntaxKind.FirstJSDocTagNode && this.kind <= SyntaxKind.LastJSDocTagNode;
|
||||
const processNode = (node: Node) => {
|
||||
if (pos < node.pos) {
|
||||
pos = this.addSyntheticNodes(children, pos, node.pos);
|
||||
pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner);
|
||||
}
|
||||
children.push(node);
|
||||
pos = node.end;
|
||||
};
|
||||
const processNodes = (nodes: NodeArray<Node>) => {
|
||||
if (pos < nodes.pos) {
|
||||
pos = this.addSyntheticNodes(children, pos, nodes.pos);
|
||||
pos = this.addSyntheticNodes(children, pos, nodes.pos, useJSDocScanner);
|
||||
}
|
||||
children.push(this.createSyntaxList(<NodeArray<Node>>nodes));
|
||||
pos = nodes.end;
|
||||
};
|
||||
// jsDocComments need to be the first children
|
||||
if (this.jsDocComments) {
|
||||
for (const jsDocComment of this.jsDocComments) {
|
||||
processNode(jsDocComment);
|
||||
}
|
||||
}
|
||||
forEachChild(this, processNode, processNodes);
|
||||
if (pos < this.end) {
|
||||
this.addSyntheticNodes(children, pos, this.end);
|
||||
@@ -735,6 +749,9 @@ namespace ts {
|
||||
? this.checker.getBaseTypes(<InterfaceType><Type>this)
|
||||
: undefined;
|
||||
}
|
||||
getNonNullableType(): Type {
|
||||
return this.checker.getNonNullableType(this);
|
||||
}
|
||||
}
|
||||
|
||||
class SignatureObject implements Signature {
|
||||
@@ -904,6 +921,7 @@ namespace ts {
|
||||
function visit(node: Node): void {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
const functionDeclaration = <FunctionLikeDeclaration>node;
|
||||
@@ -930,6 +948,7 @@ namespace ts {
|
||||
break;
|
||||
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
@@ -944,34 +963,25 @@ namespace ts {
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
addDeclaration(<Declaration>node);
|
||||
// fall through
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.VariableStatement:
|
||||
case SyntaxKind.VariableDeclarationList:
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
case SyntaxKind.ArrayBindingPattern:
|
||||
case SyntaxKind.ModuleBlock:
|
||||
forEachChild(node, visit);
|
||||
break;
|
||||
|
||||
case SyntaxKind.Block:
|
||||
if (isFunctionBlock(node)) {
|
||||
forEachChild(node, visit);
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.Parameter:
|
||||
// Only consider properties defined as constructor parameters
|
||||
if (!(node.flags & NodeFlags.AccessibilityModifier)) {
|
||||
// Only consider parameter properties
|
||||
if (!(node.flags & NodeFlags.ParameterPropertyModifier)) {
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.BindingElement:
|
||||
if (isBindingPattern((<VariableDeclaration>node).name)) {
|
||||
forEachChild((<VariableDeclaration>node).name, visit);
|
||||
case SyntaxKind.BindingElement: {
|
||||
const decl = <VariableDeclaration> node;
|
||||
if (isBindingPattern(decl.name)) {
|
||||
forEachChild(decl.name, visit);
|
||||
break;
|
||||
}
|
||||
if (decl.initializer)
|
||||
visit(decl.initializer);
|
||||
}
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
@@ -1008,6 +1018,9 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
forEachChild(node, visit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1481,6 +1494,15 @@ namespace ts {
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
acquireDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
/**
|
||||
* Request an updated version of an already existing SourceFile with a given fileName
|
||||
* and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
|
||||
@@ -1500,6 +1522,16 @@ namespace ts {
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
updateDocumentWithKey(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
scriptKind?: ScriptKind): SourceFile;
|
||||
|
||||
getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey;
|
||||
/**
|
||||
* Informs the DocumentRegistry that a file is not needed any longer.
|
||||
*
|
||||
@@ -1511,76 +1543,86 @@ namespace ts {
|
||||
*/
|
||||
releaseDocument(fileName: string, compilationSettings: CompilerOptions): void;
|
||||
|
||||
releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void;
|
||||
|
||||
reportStats(): string;
|
||||
}
|
||||
|
||||
export type DocumentRegistryBucketKey = string & { __bucketKey: any };
|
||||
|
||||
// TODO: move these to enums
|
||||
export namespace ScriptElementKind {
|
||||
export const unknown = "";
|
||||
export const warning = "warning";
|
||||
|
||||
// predefined type (void) or keyword (class)
|
||||
/** predefined type (void) or keyword (class) */
|
||||
export const keyword = "keyword";
|
||||
|
||||
// top level script node
|
||||
/** top level script node */
|
||||
export const scriptElement = "script";
|
||||
|
||||
// module foo {}
|
||||
/** module foo {} */
|
||||
export const moduleElement = "module";
|
||||
|
||||
// class X {}
|
||||
/** class X {} */
|
||||
export const classElement = "class";
|
||||
|
||||
// var x = class X {}
|
||||
/** var x = class X {} */
|
||||
export const localClassElement = "local class";
|
||||
|
||||
// interface Y {}
|
||||
/** interface Y {} */
|
||||
export const interfaceElement = "interface";
|
||||
|
||||
// type T = ...
|
||||
/** type T = ... */
|
||||
export const typeElement = "type";
|
||||
|
||||
// enum E
|
||||
/** enum E */
|
||||
export const enumElement = "enum";
|
||||
|
||||
// Inside module and script only
|
||||
// const v = ..
|
||||
/**
|
||||
* Inside module and script only
|
||||
* const v = ..
|
||||
*/
|
||||
export const variableElement = "var";
|
||||
|
||||
// Inside function
|
||||
/** Inside function */
|
||||
export const localVariableElement = "local var";
|
||||
|
||||
// Inside module and script only
|
||||
// function f() { }
|
||||
/**
|
||||
* Inside module and script only
|
||||
* function f() { }
|
||||
*/
|
||||
export const functionElement = "function";
|
||||
|
||||
// Inside function
|
||||
/** Inside function */
|
||||
export const localFunctionElement = "local function";
|
||||
|
||||
// class X { [public|private]* foo() {} }
|
||||
/** class X { [public|private]* foo() {} } */
|
||||
export const memberFunctionElement = "method";
|
||||
|
||||
// class X { [public|private]* [get|set] foo:number; }
|
||||
/** class X { [public|private]* [get|set] foo:number; } */
|
||||
export const memberGetAccessorElement = "getter";
|
||||
export const memberSetAccessorElement = "setter";
|
||||
|
||||
// class X { [public|private]* foo:number; }
|
||||
// interface Y { foo:number; }
|
||||
/**
|
||||
* class X { [public|private]* foo:number; }
|
||||
* interface Y { foo:number; }
|
||||
*/
|
||||
export const memberVariableElement = "property";
|
||||
|
||||
// class X { constructor() { } }
|
||||
/** class X { constructor() { } } */
|
||||
export const constructorImplementationElement = "constructor";
|
||||
|
||||
// interface Y { ():number; }
|
||||
/** interface Y { ():number; } */
|
||||
export const callSignatureElement = "call";
|
||||
|
||||
// interface Y { []:number; }
|
||||
/** interface Y { []:number; } */
|
||||
export const indexSignatureElement = "index";
|
||||
|
||||
// interface Y { new():Y; }
|
||||
/** interface Y { new():Y; } */
|
||||
export const constructSignatureElement = "construct";
|
||||
|
||||
// function foo(*Y*: string)
|
||||
/** function foo(*Y*: string) */
|
||||
export const parameterElement = "parameter";
|
||||
|
||||
export const typeParameterElement = "type parameter";
|
||||
@@ -1783,11 +1825,13 @@ namespace ts {
|
||||
|
||||
public getOrCreateEntry(fileName: string): HostFileInformation {
|
||||
const path = toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
|
||||
if (this.contains(path)) {
|
||||
return this.getEntry(path);
|
||||
}
|
||||
return this.getOrCreateEntryByPath(fileName, path);
|
||||
}
|
||||
|
||||
return this.createEntry(fileName, path);
|
||||
public getOrCreateEntryByPath(fileName: string, path: Path): HostFileInformation {
|
||||
return this.contains(path)
|
||||
? this.getEntry(path)
|
||||
: this.createEntry(fileName, path);
|
||||
}
|
||||
|
||||
public getRootFileNames(): string[] {
|
||||
@@ -2041,12 +2085,11 @@ namespace ts {
|
||||
const buckets: Map<FileMap<DocumentRegistryEntry>> = {};
|
||||
const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
|
||||
|
||||
function getKeyFromCompilationSettings(settings: CompilerOptions): string {
|
||||
return "_" + settings.target + "|" + settings.module + "|" + settings.noResolve + "|" + settings.jsx + +"|" + settings.allowJs;
|
||||
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
|
||||
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${settings.typesRoot}|${settings.typesSearchPaths}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
|
||||
}
|
||||
|
||||
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
|
||||
const key = getKeyFromCompilationSettings(settings);
|
||||
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
|
||||
let bucket = lookUp(buckets, key);
|
||||
if (!bucket && createIfMissing) {
|
||||
buckets[key] = bucket = createFileMap<DocumentRegistryEntry>();
|
||||
@@ -2075,23 +2118,36 @@ namespace ts {
|
||||
}
|
||||
|
||||
function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true, scriptKind);
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return acquireDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
|
||||
}
|
||||
|
||||
function acquireDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ true, scriptKind);
|
||||
}
|
||||
|
||||
function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false, scriptKind);
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return updateDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
|
||||
}
|
||||
|
||||
function updateDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
|
||||
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ false, scriptKind);
|
||||
}
|
||||
|
||||
function acquireOrUpdateDocument(
|
||||
fileName: string,
|
||||
path: Path,
|
||||
compilationSettings: CompilerOptions,
|
||||
key: DocumentRegistryBucketKey,
|
||||
scriptSnapshot: IScriptSnapshot,
|
||||
version: string,
|
||||
acquiring: boolean,
|
||||
scriptKind?: ScriptKind): SourceFile {
|
||||
|
||||
const bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true);
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true);
|
||||
let entry = bucket.get(path);
|
||||
if (!entry) {
|
||||
Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?");
|
||||
@@ -2129,10 +2185,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
function releaseDocument(fileName: string, compilationSettings: CompilerOptions): void {
|
||||
const bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/false);
|
||||
Debug.assert(bucket !== undefined);
|
||||
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return releaseDocumentWithKey(path, key);
|
||||
}
|
||||
|
||||
function releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void {
|
||||
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/false);
|
||||
Debug.assert(bucket !== undefined);
|
||||
|
||||
const entry = bucket.get(path);
|
||||
entry.languageServiceRefCount--;
|
||||
@@ -2145,9 +2205,13 @@ namespace ts {
|
||||
|
||||
return {
|
||||
acquireDocument,
|
||||
acquireDocumentWithKey,
|
||||
updateDocument,
|
||||
updateDocumentWithKey,
|
||||
releaseDocument,
|
||||
reportStats
|
||||
releaseDocumentWithKey,
|
||||
reportStats,
|
||||
getKeyForCompilationSettings
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2581,10 +2645,33 @@ namespace ts {
|
||||
isFunctionLike(node.parent) && (<FunctionLikeDeclaration>node.parent).name === node;
|
||||
}
|
||||
|
||||
/** Returns true if node is a name of an object literal property, e.g. "a" in x = { "a": 1 } */
|
||||
function isNameOfPropertyAssignment(node: Node): boolean {
|
||||
return (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NumericLiteral) &&
|
||||
(node.parent.kind === SyntaxKind.PropertyAssignment || node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) && (<PropertyDeclaration>node.parent).name === node;
|
||||
function isObjectLiteralPropertyDeclaration(node: Node): node is ObjectLiteralElement {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 }
|
||||
*/
|
||||
function getContainingObjectLiteralElement(node: Node): ObjectLiteralElement {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
if (node.parent.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return isObjectLiteralPropertyDeclaration(node.parent.parent) ? node.parent.parent : undefined;
|
||||
}
|
||||
// intential fall through
|
||||
case SyntaxKind.Identifier:
|
||||
return isObjectLiteralPropertyDeclaration(node.parent) && node.parent.name === node ? node.parent : undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isLiteralNameOfPropertyDeclarationOrIndexAccess(node: Node): boolean {
|
||||
@@ -2602,6 +2689,8 @@ namespace ts {
|
||||
return (<Declaration>node.parent).name === node;
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
return (<ElementAccessExpression>node.parent).argumentExpression === node;
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2700,7 +2789,9 @@ namespace ts {
|
||||
/* @internal */ export function getNodeKind(node: Node): string {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement;
|
||||
case SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
return ScriptElementKind.classElement;
|
||||
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
|
||||
case SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement;
|
||||
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
|
||||
@@ -2710,7 +2801,9 @@ namespace ts {
|
||||
: isLet(node)
|
||||
? ScriptElementKind.letElement
|
||||
: ScriptElementKind.variableElement;
|
||||
case SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement;
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
return ScriptElementKind.functionElement;
|
||||
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
|
||||
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
@@ -2725,7 +2818,7 @@ namespace ts {
|
||||
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
|
||||
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
|
||||
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
|
||||
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
|
||||
case SyntaxKind.Parameter: return (node.flags & NodeFlags.ParameterPropertyModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
case SyntaxKind.ImportClause:
|
||||
@@ -2826,6 +2919,7 @@ namespace ts {
|
||||
const changesInCompilationSettingsAffectSyntax = oldSettings &&
|
||||
(oldSettings.target !== newSettings.target ||
|
||||
oldSettings.module !== newSettings.module ||
|
||||
oldSettings.moduleResolution !== newSettings.moduleResolution ||
|
||||
oldSettings.noResolve !== newSettings.noResolve ||
|
||||
oldSettings.jsx !== newSettings.jsx ||
|
||||
oldSettings.allowJs !== newSettings.allowJs);
|
||||
@@ -2833,6 +2927,7 @@ namespace ts {
|
||||
// Now create a new compiler
|
||||
const compilerHost: CompilerHost = {
|
||||
getSourceFile: getOrCreateSourceFile,
|
||||
getSourceFileByPath: getOrCreateSourceFileByPath,
|
||||
getCancellationToken: () => cancellationToken,
|
||||
getCanonicalFileName,
|
||||
useCaseSensitiveFileNames: () => useCaseSensitivefileNames,
|
||||
@@ -2868,15 +2963,17 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
|
||||
const newProgram = createProgram(hostCache.getRootFileNames(), newSettings, compilerHost, program);
|
||||
|
||||
// Release any files we have acquired in the old program but are
|
||||
// not part of the new program.
|
||||
if (program) {
|
||||
const oldSourceFiles = program.getSourceFiles();
|
||||
const oldSettingsKey = documentRegistry.getKeyForCompilationSettings(oldSettings);
|
||||
for (const oldSourceFile of oldSourceFiles) {
|
||||
if (!newProgram.getSourceFile(oldSourceFile.fileName) || changesInCompilationSettingsAffectSyntax) {
|
||||
documentRegistry.releaseDocument(oldSourceFile.fileName, oldSettings);
|
||||
documentRegistry.releaseDocumentWithKey(oldSourceFile.path, oldSettingsKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2893,11 +2990,15 @@ namespace ts {
|
||||
return;
|
||||
|
||||
function getOrCreateSourceFile(fileName: string): SourceFile {
|
||||
return getOrCreateSourceFileByPath(fileName, toPath(fileName, currentDirectory, getCanonicalFileName));
|
||||
}
|
||||
|
||||
function getOrCreateSourceFileByPath(fileName: string, path: Path): SourceFile {
|
||||
Debug.assert(hostCache !== undefined);
|
||||
// The program is asking for this file, check first if the host can locate it.
|
||||
// If the host can not locate the file, then it does not exist. return undefined
|
||||
// to the program to allow reporting of errors for missing files.
|
||||
const hostFileInformation = hostCache.getOrCreateEntry(fileName);
|
||||
const hostFileInformation = hostCache.getOrCreateEntryByPath(fileName, path);
|
||||
if (!hostFileInformation) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -2907,7 +3008,7 @@ namespace ts {
|
||||
// can not be reused. we have to dump all syntax trees and create new ones.
|
||||
if (!changesInCompilationSettingsAffectSyntax) {
|
||||
// Check if the old program had this file already
|
||||
const oldSourceFile = program && program.getSourceFile(fileName);
|
||||
const oldSourceFile = program && program.getSourceFileByPath(path);
|
||||
if (oldSourceFile) {
|
||||
// We already had a source file for this file name. Go to the registry to
|
||||
// ensure that we get the right up to date version of it. We need this to
|
||||
@@ -2934,16 +3035,16 @@ namespace ts {
|
||||
// We do not support the scenario where a host can modify a registered
|
||||
// file's script kind, i.e. in one project some file is treated as ".ts"
|
||||
// and in another as ".js"
|
||||
Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + fileName);
|
||||
Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + path);
|
||||
|
||||
return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
|
||||
return documentRegistry.updateDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
|
||||
}
|
||||
|
||||
// We didn't already have the file. Fall through and acquire it from the registry.
|
||||
}
|
||||
|
||||
// Could not find this file in the old program, create a new SourceFile for it.
|
||||
return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
|
||||
return documentRegistry.acquireDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
|
||||
}
|
||||
|
||||
function sourceFileUpToDate(sourceFile: SourceFile): boolean {
|
||||
@@ -4359,7 +4460,7 @@ namespace ts {
|
||||
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
|
||||
displayParts.push(spacePart());
|
||||
}
|
||||
if (!(type.flags & TypeFlags.Anonymous)) {
|
||||
if (!(type.flags & TypeFlags.Anonymous) && type.symbol) {
|
||||
addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
|
||||
}
|
||||
addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature);
|
||||
@@ -4376,7 +4477,7 @@ namespace ts {
|
||||
(location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration
|
||||
// get the signature from the declaration and write it
|
||||
const functionDeclaration = <FunctionLikeDeclaration>location.parent;
|
||||
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getConstructSignatures() : type.getCallSignatures();
|
||||
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
|
||||
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
|
||||
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
|
||||
}
|
||||
@@ -4574,7 +4675,7 @@ namespace ts {
|
||||
symbolFlags & SymbolFlags.Signature ||
|
||||
symbolFlags & SymbolFlags.Accessor ||
|
||||
symbolKind === ScriptElementKind.memberFunctionElement) {
|
||||
const allSignatures = type.getCallSignatures();
|
||||
const allSignatures = type.getNonNullableType().getCallSignatures();
|
||||
addSignatureDisplayParts(allSignatures[0], allSignatures);
|
||||
}
|
||||
}
|
||||
@@ -4655,7 +4756,7 @@ namespace ts {
|
||||
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
if (!node) {
|
||||
if (node === sourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -4812,18 +4913,6 @@ namespace ts {
|
||||
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Labels
|
||||
if (isJumpStatementTarget(node)) {
|
||||
const labelName = (<Identifier>node).text;
|
||||
const label = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
|
||||
return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined;
|
||||
}
|
||||
|
||||
/// Triple slash reference comments
|
||||
const comment = findReferenceInPosition(sourceFile.referencedFiles, position);
|
||||
if (comment) {
|
||||
@@ -4833,6 +4922,7 @@ namespace ts {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Type reference directives
|
||||
const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position);
|
||||
if (typeReferenceDirective) {
|
||||
@@ -4843,6 +4933,18 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
if (node === sourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Labels
|
||||
if (isJumpStatementTarget(node)) {
|
||||
const labelName = (<Identifier>node).text;
|
||||
const label = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
|
||||
return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined;
|
||||
}
|
||||
|
||||
const typeChecker = program.getTypeChecker();
|
||||
let symbol = typeChecker.getSymbolAtLocation(node);
|
||||
|
||||
@@ -4901,7 +5003,7 @@ namespace ts {
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
if (!node) {
|
||||
if (node === sourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -4951,8 +5053,7 @@ namespace ts {
|
||||
function getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] {
|
||||
synchronizeHostData();
|
||||
|
||||
filesToSearch = map(filesToSearch, normalizeSlashes);
|
||||
const sourceFilesToSearch = filter(program.getSourceFiles(), f => contains(filesToSearch, f.fileName));
|
||||
const sourceFilesToSearch = map(filesToSearch, f => program.getSourceFile(f));
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
|
||||
const node = getTouchingWord(sourceFile, position);
|
||||
@@ -5637,8 +5738,8 @@ namespace ts {
|
||||
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
if (!node) {
|
||||
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
if (node === sourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -5999,7 +6100,8 @@ namespace ts {
|
||||
const sourceFile = container.getSourceFile();
|
||||
const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</;
|
||||
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, container.getStart(), container.getEnd());
|
||||
const start = findInComments ? container.getFullStart() : container.getStart();
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, start, container.getEnd());
|
||||
|
||||
if (possiblePositions.length) {
|
||||
// Build the set of symbols to search for, initially it has only the current symbol
|
||||
@@ -6295,7 +6397,8 @@ namespace ts {
|
||||
// If the location is name of property symbol from object literal destructuring pattern
|
||||
// Search the property symbol
|
||||
// for ( { property: p2 } of elems) { }
|
||||
if (isNameOfPropertyAssignment(location) && location.parent.kind !== SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const containingObjectLiteralElement = getContainingObjectLiteralElement(location);
|
||||
if (containingObjectLiteralElement && containingObjectLiteralElement.kind !== SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const propertySymbol = getPropertySymbolOfDestructuringAssignment(location);
|
||||
if (propertySymbol) {
|
||||
result.push(propertySymbol);
|
||||
@@ -6321,8 +6424,8 @@ namespace ts {
|
||||
// If the location is in a context sensitive location (i.e. in an object literal) try
|
||||
// to get a contextual type for it, and add the property symbol from the contextual
|
||||
// type to the search set
|
||||
if (isNameOfPropertyAssignment(location)) {
|
||||
forEach(getPropertySymbolsFromContextualType(location), contextualSymbol => {
|
||||
if (containingObjectLiteralElement) {
|
||||
forEach(getPropertySymbolsFromContextualType(containingObjectLiteralElement), contextualSymbol => {
|
||||
addRange(result, typeChecker.getRootSymbols(contextualSymbol));
|
||||
});
|
||||
|
||||
@@ -6449,8 +6552,9 @@ namespace ts {
|
||||
// If the reference location is in an object literal, try to get the contextual type for the
|
||||
// object literal, lookup the property symbol in the contextual type, and use this symbol to
|
||||
// compare to our searchSymbol
|
||||
if (isNameOfPropertyAssignment(referenceLocation)) {
|
||||
const contextualSymbol = forEach(getPropertySymbolsFromContextualType(referenceLocation), contextualSymbol => {
|
||||
const containingObjectLiteralElement = getContainingObjectLiteralElement(referenceLocation);
|
||||
if (containingObjectLiteralElement) {
|
||||
const contextualSymbol = forEach(getPropertySymbolsFromContextualType(containingObjectLiteralElement), contextualSymbol => {
|
||||
return forEach(typeChecker.getRootSymbols(contextualSymbol), s => searchSymbols.indexOf(s) >= 0 ? s : undefined);
|
||||
});
|
||||
|
||||
@@ -6496,37 +6600,38 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function getPropertySymbolsFromContextualType(node: Node): Symbol[] {
|
||||
if (isNameOfPropertyAssignment(node)) {
|
||||
const objectLiteral = <ObjectLiteralExpression>node.parent.parent;
|
||||
const contextualType = typeChecker.getContextualType(objectLiteral);
|
||||
const name = (<Identifier>node).text;
|
||||
if (contextualType) {
|
||||
if (contextualType.flags & TypeFlags.Union) {
|
||||
// This is a union type, first see if the property we are looking for is a union property (i.e. exists in all types)
|
||||
// if not, search the constituent types for the property
|
||||
const unionProperty = contextualType.getProperty(name);
|
||||
if (unionProperty) {
|
||||
return [unionProperty];
|
||||
}
|
||||
else {
|
||||
const result: Symbol[] = [];
|
||||
forEach((<UnionType>contextualType).types, t => {
|
||||
const symbol = t.getProperty(name);
|
||||
if (symbol) {
|
||||
result.push(symbol);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const symbol = contextualType.getProperty(name);
|
||||
if (symbol) {
|
||||
return [symbol];
|
||||
}
|
||||
}
|
||||
function getNameFromObjectLiteralElement(node: ObjectLiteralElement) {
|
||||
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
const nameExpression = (<ComputedPropertyName>node.name).expression;
|
||||
// treat computed property names where expression is string/numeric literal as just string/numeric literal
|
||||
if (isStringOrNumericLiteral(nameExpression.kind)) {
|
||||
return (<LiteralExpression>nameExpression).text;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
return (<Identifier | LiteralExpression>node.name).text;
|
||||
}
|
||||
|
||||
function getPropertySymbolsFromContextualType(node: ObjectLiteralElement): Symbol[] {
|
||||
const objectLiteral = <ObjectLiteralExpression>node.parent;
|
||||
const contextualType = typeChecker.getContextualType(objectLiteral);
|
||||
const name = getNameFromObjectLiteralElement(node);
|
||||
if (name && contextualType) {
|
||||
const result: Symbol[] = [];
|
||||
const symbol = contextualType.getProperty(name);
|
||||
if (symbol) {
|
||||
result.push(symbol);
|
||||
}
|
||||
|
||||
if (contextualType.flags & TypeFlags.Union) {
|
||||
forEach((<UnionType>contextualType).types, t => {
|
||||
const symbol = t.getProperty(name);
|
||||
if (symbol) {
|
||||
result.push(symbol);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -6603,8 +6708,8 @@ namespace ts {
|
||||
/// NavigateTo
|
||||
function getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[] {
|
||||
synchronizeHostData();
|
||||
|
||||
return ts.NavigateTo.getNavigateToItems(program, cancellationToken, searchValue, maxResultCount);
|
||||
const checker = getProgram().getTypeChecker();
|
||||
return ts.NavigateTo.getNavigateToItems(program, checker, cancellationToken, searchValue, maxResultCount);
|
||||
}
|
||||
|
||||
function getEmitOutput(fileName: string): EmitOutput {
|
||||
@@ -6802,7 +6907,7 @@ namespace ts {
|
||||
// Get node at the location
|
||||
const node = getTouchingPropertyName(sourceFile, startPos);
|
||||
|
||||
if (!node) {
|
||||
if (node === sourceFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -7587,11 +7692,11 @@ namespace ts {
|
||||
|
||||
function isValidBraceCompletionAtPostion(fileName: string, position: number, openingBrace: number): boolean {
|
||||
|
||||
// '<' is currently not supported, figuring out if we're in a Generic Type vs. a comparison is too
|
||||
// '<' is currently not supported, figuring out if we're in a Generic Type vs. a comparison is too
|
||||
// expensive to do during typing scenarios
|
||||
// i.e. whether we're dealing with:
|
||||
// var x = new foo<| ( with class foo<T>{} )
|
||||
// or
|
||||
// or
|
||||
// var y = 3 <|
|
||||
if (openingBrace === CharacterCodes.lessThan) {
|
||||
return false;
|
||||
@@ -7826,7 +7931,7 @@ namespace ts {
|
||||
const defaultLibFileName = host.getDefaultLibFileName(host.getCompilationSettings());
|
||||
const canonicalDefaultLibName = getCanonicalFileName(ts.normalizePath(defaultLibFileName));
|
||||
|
||||
const node = getTouchingWord(sourceFile, position);
|
||||
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
|
||||
// Can only rename an identifier.
|
||||
if (node) {
|
||||
@@ -7988,13 +8093,19 @@ namespace ts {
|
||||
// "a['propname']" then we want to store "propname" in the name table.
|
||||
if (isDeclarationName(node) ||
|
||||
node.parent.kind === SyntaxKind.ExternalModuleReference ||
|
||||
isArgumentOfElementAccessExpression(node)) {
|
||||
isArgumentOfElementAccessExpression(node) ||
|
||||
isLiteralComputedPropertyDeclarationName(node)) {
|
||||
|
||||
nameTable[(<LiteralExpression>node).text] = nameTable[(<LiteralExpression>node).text] === undefined ? node.pos : -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
forEachChild(node, walk);
|
||||
if (node.jsDocComments) {
|
||||
for (const jsDocComment of node.jsDocComments) {
|
||||
forEachChild(jsDocComment, walk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace ts {
|
||||
getLocalizedDiagnosticMessages(): string;
|
||||
getCancellationToken(): HostCancellationToken;
|
||||
getCurrentDirectory(): string;
|
||||
getDirectories(path: string): string[];
|
||||
getDefaultLibFileName(options: string): string;
|
||||
getNewLine?(): string;
|
||||
getProjectVersion?(): string;
|
||||
@@ -294,7 +295,7 @@ namespace ts {
|
||||
|
||||
constructor(private shimHost: LanguageServiceShimHost) {
|
||||
// if shimHost is a COM object then property check will become method call with no arguments.
|
||||
// 'in' does not have this effect.
|
||||
// 'in' does not have this effect.
|
||||
if ("getModuleResolutionsForFile" in this.shimHost) {
|
||||
this.resolveModuleNames = (moduleNames: string[], containingFile: string) => {
|
||||
const resolutionsInFile = <Map<string>>JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile));
|
||||
@@ -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));
|
||||
}
|
||||
@@ -961,7 +966,7 @@ namespace ts {
|
||||
return this.forwardJSONCall(
|
||||
"getPreProcessedFileInfo('" + fileName + "')",
|
||||
() => {
|
||||
// for now treat files as JavaScript
|
||||
// for now treat files as JavaScript
|
||||
const result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
|
||||
return {
|
||||
referencedFiles: this.convertFileReferences(result.referencedFiles),
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
namespace ts.SignatureHelp {
|
||||
|
||||
// A partially written generic type expression is not guaranteed to have the correct syntax tree. the expression could be parsed as less than/greater than expression or a comma expression
|
||||
// or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference.
|
||||
// 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
|
||||
// or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference.
|
||||
// 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,7 +161,8 @@ namespace ts.SignatureHelp {
|
||||
|
||||
// // Did not find matching token
|
||||
// return null;
|
||||
//}
|
||||
// }
|
||||
|
||||
const emptyArray: any[] = [];
|
||||
|
||||
const enum ArgumentListKind {
|
||||
@@ -189,6 +190,7 @@ namespace ts.SignatureHelp {
|
||||
}
|
||||
|
||||
const argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile);
|
||||
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
// Semantic filtering of signature help
|
||||
@@ -202,7 +204,7 @@ namespace ts.SignatureHelp {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
if (!candidates.length) {
|
||||
// We didn't have any sig help items produced by the TS compiler. If this is a JS
|
||||
// We didn't have any sig help items produced by the TS compiler. If this is a JS
|
||||
// file, then see if we can figure out anything better.
|
||||
if (isSourceFileJavaScript(sourceFile)) {
|
||||
return createJavaScriptSignatureHelpItems(argumentInfo, program);
|
||||
@@ -316,6 +318,7 @@ namespace ts.SignatureHelp {
|
||||
argumentCount: argumentCount
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.NoSubstitutionTemplateLiteral && node.parent.kind === SyntaxKind.TaggedTemplateExpression) {
|
||||
// Check if we're actually inside the template;
|
||||
@@ -542,7 +545,7 @@ namespace ts.SignatureHelp {
|
||||
const isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments;
|
||||
|
||||
const invocation = argumentListInfo.invocation;
|
||||
const callTarget = getInvokedExpression(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 => {
|
||||
|
||||
+95
-61
@@ -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];
|
||||
}
|
||||
@@ -432,12 +457,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;
|
||||
@@ -448,7 +477,7 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
|
||||
// <div> {
|
||||
// <div> {
|
||||
// |
|
||||
// } < /div>
|
||||
if (token && token.kind === SyntaxKind.CloseBraceToken && token.parent.kind === SyntaxKind.JsxExpression) {
|
||||
@@ -464,7 +493,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);
|
||||
}
|
||||
|
||||
@@ -473,10 +502,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):
|
||||
@@ -500,10 +529,10 @@ 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);
|
||||
|
||||
@@ -533,11 +562,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -553,8 +583,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getNodeModifiers(node: Node): string {
|
||||
let flags = getCombinedNodeFlags(node);
|
||||
let result: string[] = [];
|
||||
const flags = getCombinedNodeFlags(node);
|
||||
const result: string[] = [];
|
||||
|
||||
if (flags & NodeFlags.Private) result.push(ScriptElementKindModifier.privateMemberModifier);
|
||||
if (flags & NodeFlags.Protected) result.push(ScriptElementKindModifier.protectedMemberModifier);
|
||||
@@ -626,7 +656,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 {
|
||||
|
||||
// [a, b, c] of
|
||||
// [x, [a, b, c] ] = someExpression
|
||||
// or
|
||||
// or
|
||||
// {x, a: {a, b, c} } = someExpression
|
||||
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.kind === SyntaxKind.PropertyAssignment ? node.parent.parent : node.parent)) {
|
||||
return true;
|
||||
@@ -679,7 +709,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;
|
||||
@@ -705,7 +735,7 @@ namespace ts {
|
||||
|
||||
function writeIndent() {
|
||||
if (lineStart) {
|
||||
let indentString = getIndentString(indent);
|
||||
const indentString = getIndentString(indent);
|
||||
if (indentString) {
|
||||
displayParts.push(displayPart(indentString, SymbolDisplayPartKind.space));
|
||||
}
|
||||
@@ -729,7 +759,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function resetWriter() {
|
||||
displayParts = []
|
||||
displayParts = [];
|
||||
lineStart = true;
|
||||
indent = 0;
|
||||
}
|
||||
@@ -739,7 +769,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;
|
||||
@@ -810,7 +840,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;
|
||||
}
|
||||
@@ -839,12 +869,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;
|
||||
}
|
||||
@@ -861,7 +895,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)) {
|
||||
|
||||
Reference in New Issue
Block a user