mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-04 16:13:38 -05:00
Merge branch 'master' into jsdoc-values-as-namespaces
This commit is contained in:
@@ -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}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user