Merge branch 'master' into jsdoc

This commit is contained in:
Andy Hanson
2017-05-17 07:17:32 -07:00
104 changed files with 3234 additions and 757 deletions

View File

@@ -130,7 +130,7 @@ namespace ts.codefix {
}
function signatureToMethodDeclaration(signature: Signature, enclosingDeclaration: Node, body?: Block) {
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration);
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.SuppressAnyReturnType);
if (signatureDeclaration) {
signatureDeclaration.decorators = undefined;
signatureDeclaration.modifiers = modifiers;

View File

@@ -18,7 +18,7 @@ namespace ts.Completions {
return undefined;
}
const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag } = completionData;
const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords } = completionData;
if (requestJsDocTagName) {
// If the current position is a jsDoc tag name, only tag names should be provided for completion
@@ -52,7 +52,7 @@ namespace ts.Completions {
sortText: "0",
});
}
else {
else if (!hasFilteredClassMemberKeywords) {
return undefined;
}
}
@@ -60,8 +60,11 @@ namespace ts.Completions {
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log);
}
if (hasFilteredClassMemberKeywords) {
addRange(entries, classMemberKeywordCompletions);
}
// Add keywords if this is not a member completion list
if (!isMemberCompletion && !requestJsDocTag && !requestJsDocTagName) {
else if (!isMemberCompletion && !requestJsDocTag && !requestJsDocTagName) {
addRange(entries, keywordCompletions);
}
@@ -221,11 +224,12 @@ namespace ts.Completions {
function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo, typeChecker: TypeChecker): CompletionInfo | undefined {
const candidates: Signature[] = [];
const entries: CompletionEntry[] = [];
const uniques = createMap<true>();
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates);
for (const candidate of candidates) {
addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker);
addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker, uniques);
}
if (entries.length) {
@@ -258,7 +262,7 @@ namespace ts.Completions {
return undefined;
}
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>, typeChecker: TypeChecker): void {
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>, typeChecker: TypeChecker, uniques = createMap<true>()): void {
if (type && type.flags & TypeFlags.TypeParameter) {
type = typeChecker.getBaseConstraintOfType(type);
}
@@ -267,16 +271,20 @@ namespace ts.Completions {
}
if (type.flags & TypeFlags.Union) {
for (const t of (<UnionType>type).types) {
addStringLiteralCompletionsFromType(t, result, typeChecker);
addStringLiteralCompletionsFromType(t, result, typeChecker, uniques);
}
}
else if (type.flags & TypeFlags.StringLiteral) {
result.push({
name: (<LiteralType>type).text,
kindModifiers: ScriptElementKindModifier.none,
kind: ScriptElementKind.variableElement,
sortText: "0"
});
const name = (<LiteralType>type).text;
if (!uniques.has(name)) {
uniques.set(name, true);
result.push({
name,
kindModifiers: ScriptElementKindModifier.none,
kind: ScriptElementKind.variableElement,
sortText: "0"
});
}
}
}
@@ -351,7 +359,7 @@ namespace ts.Completions {
start = timestamp();
// Completion not allowed inside comments, bail out if this is the case
const insideComment = isInsideComment(sourceFile, currentToken, position);
const insideComment = isInComment(sourceFile, position, currentToken);
log("getCompletionData: Is inside comment: " + (timestamp() - start));
if (insideComment) {
@@ -406,7 +414,7 @@ namespace ts.Completions {
}
if (requestJsDocTagName || requestJsDocTag) {
return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, requestJsDocTagName, requestJsDocTag };
return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords: false };
}
if (!insideJsDocTagExpression) {
@@ -505,6 +513,7 @@ namespace ts.Completions {
let isGlobalCompletion = false;
let isMemberCompletion: boolean;
let isNewIdentifierLocation: boolean;
let hasFilteredClassMemberKeywords = false;
let symbols: Symbol[] = [];
if (isRightOfDot) {
@@ -542,7 +551,7 @@ namespace ts.Completions {
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), requestJsDocTagName, requestJsDocTag };
return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords };
function getTypeScriptMemberSymbols(): void {
// Right of dot member completion list
@@ -599,6 +608,7 @@ namespace ts.Completions {
function tryGetGlobalSymbols(): boolean {
let objectLikeContainer: ObjectLiteralExpression | BindingPattern;
let namedImportsOrExports: NamedImportsOrExports;
let classLikeContainer: ClassLikeDeclaration;
let jsxContainer: JsxOpeningLikeElement;
if (objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken)) {
@@ -611,6 +621,12 @@ namespace ts.Completions {
return tryGetImportOrExportClauseCompletionSymbols(namedImportsOrExports);
}
if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) {
// cursor inside class declaration
getGetClassLikeCompletionSymbols(classLikeContainer);
return true;
}
if (jsxContainer = tryGetContainingJsxElement(contextToken)) {
let attrsType: Type;
if ((jsxContainer.kind === SyntaxKind.JsxSelfClosingElement) || (jsxContainer.kind === SyntaxKind.JsxOpeningElement)) {
@@ -813,19 +829,16 @@ namespace ts.Completions {
// We're looking up possible property names from contextual/inferred/declared type.
isMemberCompletion = true;
let typeForObject: Type;
let typeMembers: Symbol[];
let existingMembers: Declaration[];
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
// We are completing on contextual types, but may also include properties
// other than those within the declared type.
isNewIdentifierLocation = true;
// If the object literal is being assigned to something of type 'null | { hello: string }',
// it clearly isn't trying to satisfy the 'null' type. So we grab the non-nullable type if possible.
typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
typeForObject = typeForObject && typeForObject.getNonNullableType();
const typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
if (!typeForObject) return false;
typeMembers = typeChecker.getAllPossiblePropertiesOfType(typeForObject);
existingMembers = (<ObjectLiteralExpression>objectLikeContainer).properties;
}
else if (objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern) {
@@ -849,7 +862,10 @@ namespace ts.Completions {
}
}
if (canGetType) {
typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
if (!typeForObject) return false;
// In a binding pattern, get only known properties. Everywhere else we will get all possible properties.
typeMembers = typeChecker.getPropertiesOfType(typeForObject);
existingMembers = (<ObjectBindingPattern>objectLikeContainer).elements;
}
}
@@ -861,11 +877,6 @@ namespace ts.Completions {
Debug.fail("Expected object literal or binding pattern, got " + objectLikeContainer.kind);
}
if (!typeForObject) {
return false;
}
const typeMembers = typeChecker.getPropertiesOfType(typeForObject);
if (typeMembers && typeMembers.length > 0) {
// Add filtered items to the completion list
symbols = filterObjectMembersList(typeMembers, existingMembers);
@@ -913,6 +924,62 @@ namespace ts.Completions {
return true;
}
/**
* Aggregates relevant symbols for completion in class declaration
* Relevant symbols are stored in the captured 'symbols' variable.
*/
function getGetClassLikeCompletionSymbols(classLikeDeclaration: ClassLikeDeclaration) {
// We're looking up possible property names from parent type.
isMemberCompletion = true;
// Declaring new property/method/accessor
isNewIdentifierLocation = true;
// Has keywords for class elements
hasFilteredClassMemberKeywords = true;
const baseTypeNode = getClassExtendsHeritageClauseElement(classLikeDeclaration);
const implementsTypeNodes = getClassImplementsHeritageClauseElements(classLikeDeclaration);
if (baseTypeNode || implementsTypeNodes) {
const classElement = contextToken.parent;
let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement);
// If this is context token is not something we are editing now, consider if this would lead to be modifier
if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
switch (contextToken.getText()) {
case "private":
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
break;
case "static":
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
break;
}
}
// No member list for private methods
if (!(classElementModifierFlags & ModifierFlags.Private)) {
let baseClassTypeToGetPropertiesFrom: Type;
if (baseTypeNode) {
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode);
if (classElementModifierFlags & ModifierFlags.Static) {
// Use static class to get property symbols from
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation(
baseClassTypeToGetPropertiesFrom.symbol, classLikeDeclaration);
}
}
const implementedInterfaceTypePropertySymbols = (classElementModifierFlags & ModifierFlags.Static) ?
undefined :
flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode)));
// List of property symbols of base type that are not private and already implemented
symbols = filterClassMembersList(
baseClassTypeToGetPropertiesFrom ?
typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) :
undefined,
implementedInterfaceTypePropertySymbols,
classLikeDeclaration.members,
classElementModifierFlags);
}
}
}
/**
* Returns the immediate owning object literal or binding pattern of a context token,
* on the condition that one exists and that the context implies completion should be given.
@@ -953,6 +1020,49 @@ namespace ts.Completions {
return undefined;
}
function isFromClassElementDeclaration(node: Node) {
return isClassElement(node.parent) && isClassLike(node.parent.parent);
}
/**
* Returns the immediate owning class declaration of a context token,
* on the condition that one exists and that the context implies completion should be given.
*/
function tryGetClassLikeCompletionContainer(contextToken: Node): ClassLikeDeclaration {
if (contextToken) {
switch (contextToken.kind) {
case SyntaxKind.OpenBraceToken: // class c { |
if (isClassLike(contextToken.parent)) {
return contextToken.parent;
}
break;
// class c {getValue(): number; | }
case SyntaxKind.CommaToken:
case SyntaxKind.SemicolonToken:
// class c { method() { } | }
case SyntaxKind.CloseBraceToken:
if (isClassLike(location)) {
return location;
}
break;
default:
if (isFromClassElementDeclaration(contextToken) &&
(isClassMemberCompletionKeyword(contextToken.kind) ||
isClassMemberCompletionKeywordText(contextToken.getText()))) {
return contextToken.parent.parent as ClassLikeDeclaration;
}
}
}
// class c { method() { } | method2() { } }
if (location && location.kind === SyntaxKind.SyntaxList && isClassLike(location.parent)) {
return location.parent;
}
return undefined;
}
function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement {
if (contextToken) {
const parent = contextToken.parent;
@@ -1081,7 +1191,7 @@ namespace ts.Completions {
isFunction(containingNodeKind);
case SyntaxKind.StaticKeyword:
return containingNodeKind === SyntaxKind.PropertyDeclaration;
return containingNodeKind === SyntaxKind.PropertyDeclaration && !isClassLike(contextToken.parent.parent);
case SyntaxKind.DotDotDotToken:
return containingNodeKind === SyntaxKind.Parameter ||
@@ -1098,13 +1208,17 @@ namespace ts.Completions {
containingNodeKind === SyntaxKind.ExportSpecifier ||
containingNodeKind === SyntaxKind.NamespaceImport;
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
if (isFromClassElementDeclaration(contextToken)) {
return false;
}
// falls through
case SyntaxKind.ClassKeyword:
case SyntaxKind.EnumKeyword:
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.FunctionKeyword:
case SyntaxKind.VarKeyword:
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
case SyntaxKind.ImportKeyword:
case SyntaxKind.LetKeyword:
case SyntaxKind.ConstKeyword:
@@ -1113,6 +1227,13 @@ namespace ts.Completions {
return true;
}
// If the previous token is keyword correspoding to class member completion keyword
// there will be completion available here
if (isClassMemberCompletionKeywordText(contextToken.getText()) &&
isFromClassElementDeclaration(contextToken)) {
return false;
}
// Previous token may have been a keyword that was converted to an identifier.
switch (contextToken.getText()) {
case "abstract":
@@ -1159,7 +1280,7 @@ namespace ts.Completions {
for (const element of namedImportsOrExports) {
// If this is the current item we are editing right now, do not filter it out
if (element.getStart() <= position && position <= element.getEnd()) {
if (isCurrentlyEditingNode(element)) {
continue;
}
@@ -1198,7 +1319,7 @@ namespace ts.Completions {
}
// If this is the current item we are editing right now, do not filter it out
if (m.getStart() <= position && position <= m.getEnd()) {
if (isCurrentlyEditingNode(m)) {
continue;
}
@@ -1223,6 +1344,58 @@ namespace ts.Completions {
return filter(contextualMemberSymbols, m => !existingMemberNames.get(m.name));
}
/**
* Filters out completion suggestions for class elements.
*
* @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
*/
function filterClassMembersList(baseSymbols: Symbol[], implementingTypeSymbols: Symbol[], existingMembers: ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] {
const existingMemberNames = createMap<boolean>();
for (const m of existingMembers) {
// Ignore omitted expressions for missing members
if (m.kind !== SyntaxKind.PropertyDeclaration &&
m.kind !== SyntaxKind.MethodDeclaration &&
m.kind !== SyntaxKind.GetAccessor &&
m.kind !== SyntaxKind.SetAccessor) {
continue;
}
// If this is the current item we are editing right now, do not filter it out
if (isCurrentlyEditingNode(m)) {
continue;
}
// Dont filter member even if the name matches if it is declared private in the list
if (hasModifier(m, ModifierFlags.Private)) {
continue;
}
// do not filter it out if the static presence doesnt match
const mIsStatic = hasModifier(m, ModifierFlags.Static);
const currentElementIsStatic = !!(currentClassElementModifierFlags & ModifierFlags.Static);
if ((mIsStatic && !currentElementIsStatic) ||
(!mIsStatic && currentElementIsStatic)) {
continue;
}
const existingName = getPropertyNameForPropertyNameNode(m.name);
if (existingName) {
existingMemberNames.set(existingName, true);
}
}
return concatenate(
filter(baseSymbols, baseProperty => isValidProperty(baseProperty, ModifierFlags.Private)),
filter(implementingTypeSymbols, implementingProperty => isValidProperty(implementingProperty, ModifierFlags.NonPublicAccessibilityModifier))
);
function isValidProperty(propertySymbol: Symbol, inValidModifierFlags: ModifierFlags) {
return !existingMemberNames.get(propertySymbol.name) &&
propertySymbol.getDeclarations() &&
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & inValidModifierFlags);
}
}
/**
* Filters out completion suggestions from 'symbols' according to existing JSX attributes.
*
@@ -1233,7 +1406,7 @@ namespace ts.Completions {
const seenNames = createMap<boolean>();
for (const attr of attributes) {
// If this is the current item we are editing right now, do not filter it out
if (attr.getStart() <= position && position <= attr.getEnd()) {
if (isCurrentlyEditingNode(attr)) {
continue;
}
@@ -1244,6 +1417,10 @@ namespace ts.Completions {
return filter(symbols, a => !seenNames.get(a.name));
}
function isCurrentlyEditingNode(node: Node): boolean {
return node.getStart() <= position && position <= node.getEnd();
}
}
/**
@@ -1306,6 +1483,29 @@ namespace ts.Completions {
});
}
function isClassMemberCompletionKeyword(kind: SyntaxKind) {
switch (kind) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.AbstractKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ConstructorKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
case SyntaxKind.AsyncKeyword:
return true;
}
}
function isClassMemberCompletionKeywordText(text: string) {
return isClassMemberCompletionKeyword(stringToToken(text));
}
const classMemberKeywordCompletions = filter(keywordCompletions, entry =>
isClassMemberCompletionKeywordText(entry.name));
function isEqualityExpression(node: Node): node is BinaryExpression {
return isBinaryExpression(node) && isEqualityOperatorKind(node.operatorToken.kind);
}

View File

@@ -554,7 +554,7 @@ namespace ts.FindAllReferences.Core {
}
function isObjectBindingPatternElementWithoutPropertyName(symbol: Symbol): boolean {
const bindingElement = <BindingElement>getDeclarationOfKind(symbol, SyntaxKind.BindingElement);
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
return bindingElement &&
bindingElement.parent.kind === SyntaxKind.ObjectBindingPattern &&
!bindingElement.propertyName;
@@ -562,7 +562,7 @@ namespace ts.FindAllReferences.Core {
function getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol: Symbol, checker: TypeChecker): Symbol | undefined {
if (isObjectBindingPatternElementWithoutPropertyName(symbol)) {
const bindingElement = <BindingElement>getDeclarationOfKind(symbol, SyntaxKind.BindingElement);
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent);
return typeOfPattern && checker.getPropertyOfType(typeOfPattern, (<Identifier>bindingElement.name).text);
}

View File

@@ -50,19 +50,10 @@ namespace ts.GoToDefinition {
// get the aliased symbol instead. This allows for goto def on an import e.g.
// import {A, B} from "mod";
// to jump to the implementation directly.
if (symbol.flags & SymbolFlags.Alias) {
const declaration = symbol.declarations[0];
// Go to the original declaration for cases:
//
// (1) when the aliased symbol was declared in the location(parent).
// (2) when the aliased symbol is originating from a named import.
//
if (node.kind === SyntaxKind.Identifier &&
(node.parent === declaration ||
(declaration.kind === SyntaxKind.ImportSpecifier && declaration.parent && declaration.parent.kind === SyntaxKind.NamedImports))) {
symbol = typeChecker.getAliasedSymbol(symbol);
if (symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) {
const aliased = typeChecker.getAliasedSymbol(symbol);
if (aliased.declarations) {
symbol = aliased;
}
}
@@ -136,6 +127,29 @@ namespace ts.GoToDefinition {
return getDefinitionFromSymbol(typeChecker, type.symbol, node);
}
// Go to the original declaration for cases:
//
// (1) when the aliased symbol was declared in the location(parent).
// (2) when the aliased symbol is originating from an import.
//
function shouldSkipAlias(node: Node, declaration: Node): boolean {
if (node.kind !== SyntaxKind.Identifier) {
return false;
}
if (node.parent === declaration) {
return true;
}
switch (declaration.kind) {
case SyntaxKind.ImportClause:
case SyntaxKind.ImportEqualsDeclaration:
return true;
case SyntaxKind.ImportSpecifier:
return declaration.parent.kind === SyntaxKind.NamedImports;
default:
return false;
}
}
function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo[] {
const result: DefinitionInfo[] = [];
const declarations = symbol.getDeclarations();

View File

@@ -526,17 +526,18 @@ namespace ts.FindAllReferences {
return isExternalModuleSymbol(exportingModuleSymbol) ? { exportingModuleSymbol, exportKind } : undefined;
}
function symbolName(symbol: Symbol): string {
function symbolName(symbol: Symbol): string | undefined {
if (symbol.name !== "default") {
return symbol.name;
}
const name = forEach(symbol.declarations, decl => {
return forEach(symbol.declarations, decl => {
if (isExportAssignment(decl)) {
return isIdentifier(decl.expression) ? decl.expression.text : undefined;
}
const name = getNameOfDeclaration(decl);
return name && name.kind === SyntaxKind.Identifier && name.text;
});
Debug.assert(!!name);
return name;
}
/** If at an export specifier, go to the symbol it refers to. */

View File

@@ -369,6 +369,7 @@ namespace ts {
_incrementExpressionBrand: any;
_unaryExpressionBrand: any;
_expressionBrand: any;
/*@internal*/typeArguments: NodeArray<TypeNode>;
constructor(_kind: SyntaxKind.Identifier, pos: number, end: number) {
super(pos, end);
}
@@ -1862,8 +1863,7 @@ namespace ts {
// OK, we have found a match in the file. This is only an acceptable match if
// it is contained within a comment.
const token = getTokenAtPosition(sourceFile, matchPosition, /*includeJsDocComment*/ false);
if (!isInsideComment(sourceFile, token, matchPosition)) {
if (!isInComment(sourceFile, matchPosition)) {
continue;
}

View File

@@ -200,27 +200,33 @@ namespace ts.SymbolDisplay {
(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.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
}
else {
signature = allSignatures[0];
}
// Use function declaration to write the signatures only if the symbol corresponding to this declaration
const locationIsSymbolDeclaration = findDeclaration(symbol, declaration =>
declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration));
if (functionDeclaration.kind === SyntaxKind.Constructor) {
// show (constructor) Type(...) signature
symbolKind = ScriptElementKind.constructorImplementationElement;
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
}
else {
// (function/method) symbol(..signature)
addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature &&
!(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind);
}
if (locationIsSymbolDeclaration) {
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
}
else {
signature = allSignatures[0];
}
addSignatureDisplayParts(signature, allSignatures);
hasAddedSymbolInfo = true;
if (functionDeclaration.kind === SyntaxKind.Constructor) {
// show (constructor) Type(...) signature
symbolKind = ScriptElementKind.constructorImplementationElement;
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
}
else {
// (function/method) symbol(..signature)
addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature &&
!(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind);
}
addSignatureDisplayParts(signature, allSignatures);
hasAddedSymbolInfo = true;
}
}
}
}
@@ -269,7 +275,7 @@ namespace ts.SymbolDisplay {
}
if (symbolFlags & SymbolFlags.Module) {
addNewLineIfDisplayPartsExist();
const declaration = <ModuleDeclaration>getDeclarationOfKind(symbol, SyntaxKind.ModuleDeclaration);
const declaration = getDeclarationOfKind<ModuleDeclaration>(symbol, SyntaxKind.ModuleDeclaration);
const isNamespace = declaration && declaration.name && declaration.name.kind === SyntaxKind.Identifier;
displayParts.push(keywordPart(isNamespace ? SyntaxKind.NamespaceKeyword : SyntaxKind.ModuleKeyword));
displayParts.push(spacePart());
@@ -290,9 +296,9 @@ namespace ts.SymbolDisplay {
}
else {
// Method/function type parameter
let declaration = <Node>getDeclarationOfKind(symbol, SyntaxKind.TypeParameter);
Debug.assert(declaration !== undefined);
declaration = declaration.parent;
const decl = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter);
Debug.assert(decl !== undefined);
const declaration = decl.parent;
if (declaration) {
if (isFunctionLikeKind(declaration.kind)) {

View File

@@ -497,8 +497,8 @@ namespace ts.textChanges {
readonly node: Node;
}
export function getNonformattedText(node: Node, sourceFile: SourceFile, newLine: NewLineKind): NonFormattedText {
const options = { newLine, target: sourceFile.languageVersion };
export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLine: NewLineKind): NonFormattedText {
const options = { newLine, target: sourceFile && sourceFile.languageVersion };
const writer = new Writer(getNewLineCharacter(options));
const printer = createPrinter(options, writer);
printer.writeNode(EmitHint.Unspecified, node, sourceFile, writer);
@@ -528,26 +528,6 @@ namespace ts.textChanges {
return skipTrivia(s, 0) === s.length;
}
const nullTransformationContext: TransformationContext = {
enableEmitNotification: noop,
enableSubstitution: noop,
endLexicalEnvironment: () => undefined,
getCompilerOptions: notImplemented,
getEmitHost: notImplemented,
getEmitResolver: notImplemented,
hoistFunctionDeclaration: noop,
hoistVariableDeclaration: noop,
isEmitNotificationEnabled: notImplemented,
isSubstitutionEnabled: notImplemented,
onEmitNode: noop,
onSubstituteNode: notImplemented,
readEmitHelpers: notImplemented,
requestEmitHelper: noop,
resumeLexicalEnvironment: noop,
startLexicalEnvironment: noop,
suspendLexicalEnvironment: noop
};
function assignPositionsToNode(node: Node): Node {
const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray, assignPositionsToNode);
// create proxy node for non synthesized nodes

View File

@@ -256,37 +256,6 @@ namespace ts {
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node;
}
/** Returns true if the position is within a comment */
export function isInsideComment(sourceFile: SourceFile, token: Node, position: number): boolean {
// The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment
return position <= token.getStart(sourceFile) &&
(isInsideCommentRange(getTrailingCommentRanges(sourceFile.text, token.getFullStart())) ||
isInsideCommentRange(getLeadingCommentRanges(sourceFile.text, token.getFullStart())));
function isInsideCommentRange(comments: CommentRange[]): boolean {
return forEach(comments, comment => {
// either we are 1. completely inside the comment, or 2. at the end of the comment
if (comment.pos < position && position < comment.end) {
return true;
}
else if (position === comment.end) {
const text = sourceFile.text;
const width = comment.end - comment.pos;
// is single line comment or just /*
if (width <= 2 || text.charCodeAt(comment.pos + 1) === CharacterCodes.slash) {
return true;
}
else {
// is unterminated multi-line comment
return !(text.charCodeAt(comment.end - 1) === CharacterCodes.slash &&
text.charCodeAt(comment.end - 2) === CharacterCodes.asterisk);
}
}
return false;
});
}
}
export function getContainerNode(node: Node): Declaration {
while (true) {
node = node.parent;
@@ -651,44 +620,26 @@ 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 (const child of current.getChildren()) {
// all jsDocComment nodes were already visited
if (isJSDocNode(child)) {
if (isJSDocNode(child) && !includeJsDocComment) {
continue;
}
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, includeJsDocComment);
if (start <= position) {
const end = child.getEnd();
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
current = child;
continue outer;
}
else if (includeItemAtEndPosition && end === position) {
const previousToken = findPrecedingToken(position, sourceFile, child);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
if (start > position) {
continue;
}
const end = child.getEnd();
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
current = child;
continue outer;
}
else if (includeItemAtEndPosition && end === position) {
const previousToken = findPrecedingToken(position, sourceFile, child);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
}
}
@@ -834,10 +785,6 @@ namespace ts {
return false;
}
export function isInComment(sourceFile: SourceFile, position: number) {
return isInCommentHelper(sourceFile, position, /*predicate*/ undefined);
}
/**
* returns true if the position is in between the open and close elements of an JSX expression.
*/
@@ -883,15 +830,30 @@ namespace ts {
}
/**
* Returns true if the cursor at position in sourceFile is within a comment that additionally
* satisfies predicate, and false otherwise.
* Returns true if the cursor at position in sourceFile is within a comment.
*
* @param tokenAtPosition Must equal `getTokenAtPosition(sourceFile, position)
* @param predicate Additional predicate to test on the comment range.
*/
function isInCommentHelper(sourceFile: SourceFile, position: number, predicate?: (c: CommentRange) => boolean): boolean {
const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false);
export function isInComment(
sourceFile: SourceFile,
position: number,
tokenAtPosition = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false),
predicate?: (c: CommentRange) => boolean): boolean {
return position <= tokenAtPosition.getStart(sourceFile) &&
(isInCommentRange(getLeadingCommentRanges(sourceFile.text, tokenAtPosition.pos)) ||
isInCommentRange(getTrailingCommentRanges(sourceFile.text, tokenAtPosition.pos)));
if (token && position <= token.getStart(sourceFile)) {
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
function isInCommentRange(commentRanges: CommentRange[]): boolean {
return forEach(commentRanges, c => isPositionInCommentRange(c, position, sourceFile.text) && (!predicate || predicate(c)));
}
}
function isPositionInCommentRange({ pos, end, kind }: ts.CommentRange, position: number, text: string): boolean {
if (pos < position && position < end) {
return true;
}
else if (position === end) {
// 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):
//
@@ -902,15 +864,13 @@ namespace ts {
// /* asdf */^
//
// Internally, we represent the end of the comment at the newline and closing '/', respectively.
return predicate ?
forEach(commentRanges, c => c.pos < position &&
(c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end) &&
predicate(c)) :
forEach(commentRanges, c => c.pos < position &&
(c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end));
return kind === SyntaxKind.SingleLineCommentTrivia ||
// true for unterminated multi-line comment
!(text.charCodeAt(end - 1) === CharacterCodes.slash && text.charCodeAt(end - 2) === CharacterCodes.asterisk);
}
else {
return false;
}
return false;
}
export function hasDocComment(sourceFile: SourceFile, position: number) {
@@ -1093,21 +1053,17 @@ namespace ts {
}
export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInCommentHelper(sourceFile, position, isReferenceComment);
function isReferenceComment(c: CommentRange): boolean {
return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => {
const commentText = sourceFile.text.substring(c.pos, c.end);
return tripleSlashDirectivePrefixRegex.test(commentText);
}
});
}
export function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean {
return isInCommentHelper(sourceFile, position, isNonReferenceComment);
function isNonReferenceComment(c: CommentRange): boolean {
return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => {
const commentText = sourceFile.text.substring(c.pos, c.end);
return !tripleSlashDirectivePrefixRegex.test(commentText);
}
});
}
export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan {