Handle completions at name of namespace declaration (#25661)

* Handle completions at name of namespace declaration

* Handle namespace merging
This commit is contained in:
Andy 2018-07-19 10:26:18 -07:00 committed by GitHub
parent 37277e86e6
commit 46827f4a76
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 16 deletions

View file

@ -921,6 +921,9 @@ namespace ts.Completions {
case SyntaxKind.QualifiedName:
node = (parent as QualifiedName).left;
break;
case SyntaxKind.ModuleDeclaration:
node = (parent as ModuleDeclaration).name;
break;
case SyntaxKind.ImportType:
case SyntaxKind.MetaProperty:
node = parent;
@ -1062,6 +1065,8 @@ namespace ts.Completions {
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
const allowTypeOrValue = isRhsOfImportDeclaration || (!isTypeLocation && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker));
if (isEntityName(node) || isImportType) {
const isNamespaceName = isModuleDeclaration(node.parent);
if (isNamespaceName) isNewIdentifierLocation = true;
let symbol = typeChecker.getSymbolAtLocation(node);
if (symbol) {
symbol = skipAlias(symbol, typeChecker);
@ -1071,13 +1076,17 @@ namespace ts.Completions {
const exportedSymbols = Debug.assertEachDefined(typeChecker.getExportsOfModule(symbol), "getExportsOfModule() should all be defined");
const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(isImportType ? <ImportTypeNode>node : <PropertyAccessExpression>(node.parent), symbol.name);
const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol);
const isValidAccess = allowTypeOrValue ?
// Any kind is allowed when dotting off namespace in internal import equals declaration
(symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
isTypeLocation ? isValidTypeAccess : isValidValueAccess;
for (const symbol of exportedSymbols) {
if (isValidAccess(symbol)) {
symbols.push(symbol);
const isValidAccess: (symbol: Symbol) => boolean =
isNamespaceName
// At `namespace N.M/**/`, if this is the only declaration of `M`, don't include `M` as a completion.
? symbol => !!(symbol.flags & SymbolFlags.Namespace) && !symbol.declarations.every(d => d.parent === node.parent)
: allowTypeOrValue ?
// Any kind is allowed when dotting off namespace in internal import equals declaration
symbol => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
isTypeLocation ? isValidTypeAccess : isValidValueAccess;
for (const exportedSymbol of exportedSymbols) {
if (isValidAccess(exportedSymbol)) {
symbols.push(exportedSymbol);
}
}
@ -1461,7 +1470,8 @@ namespace ts.Completions {
function isNewIdentifierDefinitionLocation(previousToken: Node | undefined): boolean {
if (previousToken) {
const containingNodeKind = previousToken.parent.kind;
switch (previousToken.kind) {
// Previous token may have been a keyword that was converted to an identifier.
switch (keywordForNode(previousToken)) {
case SyntaxKind.CommaToken:
return containingNodeKind === SyntaxKind.CallExpression // func( a, |
|| containingNodeKind === SyntaxKind.Constructor // constructor( a, | /* public, protected, private keywords are allowed here, so show completion */
@ -1507,14 +1517,6 @@ namespace ts.Completions {
case SyntaxKind.ProtectedKeyword:
return containingNodeKind === SyntaxKind.PropertyDeclaration; // class A{ public |
}
// Previous token may have been a keyword that was converted to an identifier.
switch (keywordForNode(previousToken)) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PrivateKeyword:
return true;
}
}
return false;

View file

@ -0,0 +1,17 @@
/// <reference path='fourslash.ts'/>
////{ namespace /*0*/ }
////namespace N/*1*/ {}
////namespace N.M {}
////namespace N./*2*/
////
////namespace N1.M/*3*/ {}
////namespace N2.M {}
////namespace N2.M/*4*/
verify.completions(
{ marker: ["0", "1"], isNewIdentifierLocation: true },
{ marker: "2", exact: ["M"], isNewIdentifierLocation: true },
{ marker: "3", exact: undefined, isNewIdentifierLocation: true },
{ marker: "4", exact: "M", isNewIdentifierLocation: true },
);