Merge branch 'master' into jsdoc-values-as-namespaces

This commit is contained in:
Nathan Shively-Sanders
2017-11-30 10:34:50 -08:00
150 changed files with 2120 additions and 969 deletions

View File

@@ -814,7 +814,6 @@ namespace ts.codefix {
lastCharWasValid = isValid;
}
// Need `|| "_"` to ensure result isn't empty.
const token = stringToToken(res);
return token === undefined || !isNonContextualKeyword(token) ? res || "_" : `_${res}`;
return !isStringANonContextualKeyword(res) ? res || "_" : `_${res}`;
}
}

View File

@@ -112,7 +112,7 @@ namespace ts.Completions {
}
const realName = unescapeLeadingUnderscores(name);
if (uniqueNames.has(realName)) {
if (uniqueNames.has(realName) || isStringANonContextualKeyword(realName)) {
return;
}
@@ -552,8 +552,6 @@ namespace ts.Completions {
options: GetCompletionsAtPositionOptions,
target: ScriptTarget,
): CompletionData | undefined {
const isJavaScriptFile = isSourceFileJavaScript(sourceFile);
let request: Request | undefined;
let start = timestamp();
@@ -832,23 +830,21 @@ namespace ts.Completions {
}
}
function addTypeProperties(type: Type) {
// Filter private properties
for (const symbol of type.getApparentProperties()) {
if (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.name)) {
symbols.push(symbol);
}
}
if (isJavaScriptFile && type.flags & TypeFlags.Union) {
function addTypeProperties(type: Type): void {
if (isSourceFileJavaScript(sourceFile)) {
// In javascript files, for union types, we don't just get the members that
// the individual types have in common, we also include all the members that
// each individual type has. This is because we're going to add all identifiers
// anyways. So we might as well elevate the members that were at least part
// each individual type has. This is because we're going to add all identifiers
// anyways. So we might as well elevate the members that were at least part
// of the individual types to a higher status since we know what they are.
const unionType = <UnionType>type;
for (const elementType of unionType.types) {
addTypeProperties(elementType);
symbols.push(...getPropertiesForCompletion(type, typeChecker, /*isForAccess*/ true));
}
else {
// Filter private properties
for (const symbol of type.getApparentProperties()) {
if (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.name)) {
symbols.push(symbol);
}
}
}
}
@@ -1239,7 +1235,7 @@ namespace ts.Completions {
isNewIdentifierLocation = true;
const typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
if (!typeForObject) return false;
typeMembers = getPropertiesForCompletion(typeForObject, typeChecker);
typeMembers = getPropertiesForCompletion(typeForObject, typeChecker, /*isForAccess*/ false);
existingMembers = (<ObjectLiteralExpression>objectLikeContainer).properties;
}
else {
@@ -1952,6 +1948,8 @@ namespace ts.Completions {
function getAllKeywordCompletions() {
const allKeywordsCompletions: CompletionEntry[] = [];
for (let i = SyntaxKind.FirstKeyword; i <= SyntaxKind.LastKeyword; i++) {
// "undefined" is a global variable, so don't need a keyword completion for it.
if (i === SyntaxKind.UndefinedKeyword) continue;
allKeywordsCompletions.push({
name: tokenToString(i),
kind: ScriptElementKind.keyword,
@@ -2050,14 +2048,15 @@ namespace ts.Completions {
* tries to only include those types which declare properties, not methods.
* This ensures that we don't try providing completions for all the methods on e.g. Array.
*/
function getPropertiesForCompletion(type: Type, checker: TypeChecker): Symbol[] {
function getPropertiesForCompletion(type: Type, checker: TypeChecker, isForAccess: boolean): Symbol[] {
if (!(type.flags & TypeFlags.Union)) {
return checker.getPropertiesOfType(type);
return type.getApparentProperties();
}
const { types } = type as UnionType;
const filteredTypes = types.filter(memberType => !(memberType.flags & TypeFlags.Primitive || checker.isArrayLikeType(memberType)));
// If there are no property-only types, just provide completions for every type as usual.
// If we're providing completions for an object literal, skip primitive, array-like, or callable types since those shouldn't be implemented by object literals.
const filteredTypes = isForAccess ? types : types.filter(memberType =>
!(memberType.flags & TypeFlags.Primitive || checker.isArrayLikeType(memberType) || typeHasCallOrConstructSignatures(memberType, checker)));
return checker.getAllPossiblePropertiesOfTypes(filteredTypes);
}
}

View File

@@ -336,46 +336,53 @@ namespace ts.NavigationBar {
nameToItems.set(name, [itemWithSameName, child]);
return true;
}
function tryMerge(a: NavigationBarNode, b: NavigationBarNode): boolean {
if (shouldReallyMerge(a.node, b.node)) {
merge(a, b);
return true;
}
return false;
}
});
}
/** a and b have the same name, but they may not be mergeable. */
function shouldReallyMerge(a: Node, b: Node): boolean {
return a.kind === b.kind && (a.kind !== SyntaxKind.ModuleDeclaration || areSameModule(<ModuleDeclaration>a, <ModuleDeclaration>b));
function tryMerge(a: NavigationBarNode, b: NavigationBarNode): boolean {
if (shouldReallyMerge(a.node, b.node)) {
merge(a, b);
return true;
}
return false;
}
// We use 1 NavNode to represent 'A.B.C', but there are multiple source nodes.
// Only merge module nodes that have the same chain. Don't merge 'A.B.C' with 'A'!
function areSameModule(a: ModuleDeclaration, b: ModuleDeclaration): boolean {
if (a.body.kind !== b.body.kind) {
return false;
}
if (a.body.kind !== SyntaxKind.ModuleDeclaration) {
return true;
}
return areSameModule(<ModuleDeclaration>a.body, <ModuleDeclaration>b.body);
}
/** a and b have the same name, but they may not be mergeable. */
function shouldReallyMerge(a: Node, b: Node): boolean {
if (a.kind !== b.kind) {
return false;
}
switch (a.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return hasModifier(a, ModifierFlags.Static) === hasModifier(b, ModifierFlags.Static);
case SyntaxKind.ModuleDeclaration:
return areSameModule(<ModuleDeclaration>a, <ModuleDeclaration>b);
default:
return true;
}
}
// We use 1 NavNode to represent 'A.B.C', but there are multiple source nodes.
// Only merge module nodes that have the same chain. Don't merge 'A.B.C' with 'A'!
function areSameModule(a: ModuleDeclaration, b: ModuleDeclaration): boolean {
return a.body.kind === b.body.kind && (a.body.kind !== SyntaxKind.ModuleDeclaration || areSameModule(<ModuleDeclaration>a.body, <ModuleDeclaration>b.body));
}
/** Merge source into target. Source should be thrown away after this is called. */
function merge(target: NavigationBarNode, source: NavigationBarNode): void {
target.additionalNodes = target.additionalNodes || [];
target.additionalNodes.push(source.node);
if (source.additionalNodes) {
target.additionalNodes.push(...source.additionalNodes);
}
/** Merge source into target. Source should be thrown away after this is called. */
function merge(target: NavigationBarNode, source: NavigationBarNode): void {
target.additionalNodes = target.additionalNodes || [];
target.additionalNodes.push(source.node);
if (source.additionalNodes) {
target.additionalNodes.push(...source.additionalNodes);
}
target.children = concatenate(target.children, source.children);
if (target.children) {
mergeChildren(target.children);
sortChildren(target.children);
}
target.children = concatenate(target.children, source.children);
if (target.children) {
mergeChildren(target.children);
sortChildren(target.children);
}
}

View File

@@ -138,39 +138,36 @@ namespace ts.Completions.PathCompletions {
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] {
const { baseUrl, paths } = compilerOptions;
let result: CompletionEntry[];
const result: CompletionEntry[] = [];
const fileExtensions = getSupportedExtensions(compilerOptions);
if (baseUrl) {
const projectDir = compilerOptions.project || host.getCurrentDirectory();
const absolute = isRootedDiskPath(baseUrl) ? baseUrl : combinePaths(projectDir, baseUrl);
result = getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/ false, span, host);
getCompletionEntriesForDirectoryFragment(fragment, normalizePath(absolute), fileExtensions, /*includeExtensions*/ false, span, host, /*exclude*/ undefined, result);
if (paths) {
for (const path in paths) {
if (paths.hasOwnProperty(path)) {
if (path === "*") {
if (paths[path]) {
for (const pattern of paths[path]) {
for (const match of getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions, host)) {
result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName, span));
}
}
}
}
else if (startsWith(path, fragment)) {
const entry = paths[path] && paths[path].length === 1 && paths[path][0];
if (entry) {
result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName, span));
}
for (const path in paths) {
if (!paths.hasOwnProperty(path)) continue;
const patterns = paths[path];
if (!patterns) continue;
if (path === "*") {
for (const pattern of patterns) {
for (const match of getModulesForPathsPattern(fragment, baseUrl, pattern, fileExtensions, host)) {
// Path mappings may provide a duplicate way to get to something we've already added, so don't add again.
if (result.some(entry => entry.name === match)) continue;
result.push(createCompletionEntryForModule(match, ScriptElementKind.externalModuleName, span));
}
}
}
else if (startsWith(path, fragment)) {
if (patterns.length === 1) {
if (result.some(entry => entry.name === path)) continue;
result.push(createCompletionEntryForModule(path, ScriptElementKind.externalModuleName, span));
}
}
}
}
else {
result = [];
}
if (compilerOptions.moduleResolution === ts.ModuleResolutionKind.NodeJs) {
forEachAncestorDirectory(scriptPath, ancestor => {

View File

@@ -46,9 +46,19 @@ namespace ts.refactor.installTypesForPackage {
function getAction(context: RefactorContext): CodeAction | undefined {
const { file, startPosition } = context;
const node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false);
if (isStringLiteral(node) && isModuleIdentifier(node) && getResolvedModule(file, node.text) === undefined) {
return codefix.tryGetCodeActionForInstallPackageTypes(context.host, file.fileName, node.text);
if (!isStringLiteral(node) || !isModuleIdentifier(node)) {
return undefined;
}
const resolvedTo = getResolvedModule(file, node.text);
// Still offer to install types if it resolved to e.g. a ".js" file.
// `tryGetCodeActionForInstallPackageTypes` will verify that we're looking for a valid package name,
// so the fix won't trigger for imports of ".js" files that couldn't be better replaced by typings.
if (resolvedTo && extensionIsTypeScript(resolvedTo.extension)) {
return undefined;
}
return codefix.tryGetCodeActionForInstallPackageTypes(context.host, file.fileName, node.text);
}
function isModuleIdentifier(node: StringLiteral): boolean {