mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into js-object-literal-assignments-as-declarations
This commit is contained in:
@@ -14,18 +14,22 @@ namespace ts.codefix {
|
||||
return changes.length === 0 ? undefined : [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), changes, fixId }];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
addMissingMembers(getClass(diag.file!, diag.start!), context.sourceFile, context.program.getTypeChecker(), changes);
|
||||
}),
|
||||
getAllCodeActions: context => {
|
||||
const seenClassDeclarations = createMap<true>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const classDeclaration = getClass(diag.file!, diag.start!);
|
||||
if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) {
|
||||
addMissingMembers(classDeclaration, context.sourceFile, context.program.getTypeChecker(), changes);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function getClass(sourceFile: SourceFile, pos: number): ClassLikeDeclaration {
|
||||
// This is the identifier in the case of a class declaration
|
||||
// Token is the identifier in the case of a class declaration
|
||||
// or the class keyword token in the case of a class expression.
|
||||
const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false);
|
||||
const classDeclaration = token.parent;
|
||||
Debug.assert(isClassLike(classDeclaration));
|
||||
return classDeclaration as ClassLikeDeclaration;
|
||||
return cast(token.parent, isClassLike);
|
||||
}
|
||||
|
||||
function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, changeTracker: textChanges.ChangeTracker): void {
|
||||
|
||||
@@ -31,9 +31,7 @@ namespace ts.codefix {
|
||||
});
|
||||
|
||||
function getClass(sourceFile: SourceFile, pos: number): ClassLikeDeclaration {
|
||||
const classDeclaration = getContainingClass(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false));
|
||||
Debug.assert(!!classDeclaration);
|
||||
return classDeclaration!;
|
||||
return Debug.assertDefined(getContainingClass(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false)));
|
||||
}
|
||||
|
||||
function addMissingDeclarations(
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
const fixIdAddDefiniteAssignmentAssertions = "addMissingPropertyDefiniteAssignmentAssertions";
|
||||
const fixIdAddUndefinedType = "addMissingPropertyUndefinedType";
|
||||
const fixIdAddInitializer = "addMissingPropertyInitializer";
|
||||
const errorCodes = [Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor.code];
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions: (context) => {
|
||||
const propertyDeclaration = getPropertyDeclaration(context.sourceFile, context.span.start);
|
||||
if (!propertyDeclaration) return;
|
||||
|
||||
const newLineCharacter = getNewLineOrDefaultFromHost(context.host, context.formatContext.options);
|
||||
const result = [
|
||||
getActionForAddMissingUndefinedType(context, propertyDeclaration),
|
||||
getActionForAddMissingDefiniteAssignmentAssertion(context, propertyDeclaration, newLineCharacter)
|
||||
];
|
||||
|
||||
append(result, getActionForAddMissingInitializer(context, propertyDeclaration, newLineCharacter));
|
||||
|
||||
return result;
|
||||
},
|
||||
fixIds: [fixIdAddDefiniteAssignmentAssertions, fixIdAddUndefinedType, fixIdAddInitializer],
|
||||
getAllCodeActions: context => {
|
||||
const newLineCharacter = getNewLineOrDefaultFromHost(context.host, context.formatContext.options);
|
||||
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const propertyDeclaration = getPropertyDeclaration(diag.file, diag.start);
|
||||
if (!propertyDeclaration) return;
|
||||
|
||||
switch (context.fixId) {
|
||||
case fixIdAddDefiniteAssignmentAssertions:
|
||||
addDefiniteAssignmentAssertion(changes, diag.file, propertyDeclaration, newLineCharacter);
|
||||
break;
|
||||
case fixIdAddUndefinedType:
|
||||
addUndefinedType(changes, diag.file, propertyDeclaration);
|
||||
break;
|
||||
case fixIdAddInitializer:
|
||||
const checker = context.program.getTypeChecker();
|
||||
const initializer = getInitializer(checker, propertyDeclaration);
|
||||
if (!initializer) return;
|
||||
|
||||
addInitializer(changes, diag.file, propertyDeclaration, initializer, newLineCharacter);
|
||||
break;
|
||||
default:
|
||||
Debug.fail(JSON.stringify(context.fixId));
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function getPropertyDeclaration (sourceFile: SourceFile, pos: number): PropertyDeclaration | undefined {
|
||||
const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false);
|
||||
return isIdentifier(token) ? cast(token.parent, isPropertyDeclaration) : undefined;
|
||||
}
|
||||
|
||||
function getActionForAddMissingDefiniteAssignmentAssertion (context: CodeFixContext, propertyDeclaration: PropertyDeclaration, newLineCharacter: string): CodeFixAction {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_definite_assignment_assertion_to_property_0), [propertyDeclaration.getText()]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addDefiniteAssignmentAssertion(t, context.sourceFile, propertyDeclaration, newLineCharacter));
|
||||
return { description, changes, fixId: fixIdAddDefiniteAssignmentAssertions };
|
||||
}
|
||||
|
||||
function addDefiniteAssignmentAssertion(changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration, newLineCharacter: string): void {
|
||||
const property = updateProperty(
|
||||
propertyDeclaration,
|
||||
propertyDeclaration.decorators,
|
||||
propertyDeclaration.modifiers,
|
||||
propertyDeclaration.name,
|
||||
createToken(SyntaxKind.ExclamationToken),
|
||||
propertyDeclaration.type,
|
||||
propertyDeclaration.initializer
|
||||
);
|
||||
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration, property, { suffix: newLineCharacter });
|
||||
}
|
||||
|
||||
function getActionForAddMissingUndefinedType (context: CodeFixContext, propertyDeclaration: PropertyDeclaration): CodeFixAction {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_undefined_type_to_property_0), [propertyDeclaration.name.getText()]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addUndefinedType(t, context.sourceFile, propertyDeclaration));
|
||||
return { description, changes, fixId: fixIdAddUndefinedType };
|
||||
}
|
||||
|
||||
function addUndefinedType(changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration): void {
|
||||
const undefinedTypeNode = createKeywordTypeNode(SyntaxKind.UndefinedKeyword);
|
||||
const types = isUnionTypeNode(propertyDeclaration.type) ? propertyDeclaration.type.types.concat(undefinedTypeNode) : [propertyDeclaration.type, undefinedTypeNode];
|
||||
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration.type, createUnionTypeNode(types));
|
||||
}
|
||||
|
||||
function getActionForAddMissingInitializer (context: CodeFixContext, propertyDeclaration: PropertyDeclaration, newLineCharacter: string): CodeFixAction | undefined {
|
||||
const checker = context.program.getTypeChecker();
|
||||
const initializer = getInitializer(checker, propertyDeclaration);
|
||||
if (!initializer) return undefined;
|
||||
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_initializer_to_property_0), [propertyDeclaration.name.getText()]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addInitializer(t, context.sourceFile, propertyDeclaration, initializer, newLineCharacter));
|
||||
return { description, changes, fixId: fixIdAddInitializer };
|
||||
}
|
||||
|
||||
function addInitializer (changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration, initializer: Expression, newLineCharacter: string): void {
|
||||
const property = updateProperty(
|
||||
propertyDeclaration,
|
||||
propertyDeclaration.decorators,
|
||||
propertyDeclaration.modifiers,
|
||||
propertyDeclaration.name,
|
||||
propertyDeclaration.questionToken,
|
||||
propertyDeclaration.type,
|
||||
initializer
|
||||
);
|
||||
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration, property, { suffix: newLineCharacter });
|
||||
}
|
||||
|
||||
function getInitializer(checker: TypeChecker, propertyDeclaration: PropertyDeclaration): Expression | undefined {
|
||||
return getDefaultValueFromType(checker, checker.getTypeFromTypeNode(propertyDeclaration.type));
|
||||
}
|
||||
|
||||
function getDefaultValueFromType (checker: TypeChecker, type: Type): Expression | undefined {
|
||||
if (type.flags & TypeFlags.String) {
|
||||
return createLiteral("");
|
||||
}
|
||||
else if (type.flags & TypeFlags.Number) {
|
||||
return createNumericLiteral("0");
|
||||
}
|
||||
else if (type.flags & TypeFlags.Boolean) {
|
||||
return createFalse();
|
||||
}
|
||||
else if (type.flags & TypeFlags.Literal) {
|
||||
return createLiteral((<LiteralType>type).value);
|
||||
}
|
||||
else if (type.flags & TypeFlags.Union) {
|
||||
return firstDefined((<UnionType>type).types, t => getDefaultValueFromType(checker, t));
|
||||
}
|
||||
else if (getObjectFlags(type) & ObjectFlags.Class) {
|
||||
const classDeclaration = getClassLikeDeclarationOfSymbol(type.symbol);
|
||||
if (!classDeclaration || hasModifier(classDeclaration, ModifierFlags.Abstract)) return undefined;
|
||||
|
||||
const constructorDeclaration = find<ClassElement, ConstructorDeclaration>(classDeclaration.members, (m): m is ConstructorDeclaration => isConstructorDeclaration(m) && !!m.body)!;
|
||||
if (constructorDeclaration && constructorDeclaration.parameters.length) return undefined;
|
||||
|
||||
return createNew(createIdentifier(type.symbol.name), /*typeArguments*/ undefined, /*argumentsArray*/ undefined);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -17,4 +17,4 @@
|
||||
/// <reference path='helpers.ts' />
|
||||
/// <reference path='inferFromUsage.ts' />
|
||||
/// <reference path="fixInvalidImportSyntax.ts" />
|
||||
|
||||
/// <reference path="fixStrictClassInitialization.ts" />
|
||||
|
||||
+10
-11
@@ -106,7 +106,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, includeInsertTextCompletions: boolean): CompletionInfo {
|
||||
const { symbols, completionKind, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer } = completionData;
|
||||
const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer } = completionData;
|
||||
|
||||
if (sourceFile.languageVariant === LanguageVariant.JSX && location && location.parent && isJsxClosingElement(location.parent)) {
|
||||
// In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag,
|
||||
@@ -148,7 +148,7 @@ namespace ts.Completions {
|
||||
addRange(entries, getKeywordCompletions(keywordFilters));
|
||||
}
|
||||
|
||||
return { isGlobalCompletion: completionKind === CompletionKind.Global, isMemberCompletion, isNewIdentifierLocation, entries };
|
||||
return { isGlobalCompletion: isInSnippetScope, isMemberCompletion, isNewIdentifierLocation, entries };
|
||||
}
|
||||
|
||||
function isMemberCompletionKind(kind: CompletionKind): boolean {
|
||||
@@ -635,6 +635,7 @@ namespace ts.Completions {
|
||||
readonly kind: CompletionDataKind.Data;
|
||||
readonly symbols: ReadonlyArray<Symbol>;
|
||||
readonly completionKind: CompletionKind;
|
||||
readonly isInSnippetScope: boolean;
|
||||
/** Note that the presence of this alone doesn't mean that we need a conversion. Only do that if the completion is not an ordinary identifier. */
|
||||
readonly propertyAccessToConvert: PropertyAccessExpression | undefined;
|
||||
readonly isNewIdentifierLocation: boolean;
|
||||
@@ -649,7 +650,6 @@ namespace ts.Completions {
|
||||
|
||||
const enum CompletionKind {
|
||||
ObjectPropertyDeclaration,
|
||||
/** Note that sometimes we access completions from global scope, but use "None" instead of this. See isGlobalCompletionScope. */
|
||||
Global,
|
||||
PropertyAccess,
|
||||
MemberLike,
|
||||
@@ -753,6 +753,7 @@ namespace ts.Completions {
|
||||
log("getCompletionData: Is inside comment: " + (timestamp() - start));
|
||||
|
||||
let insideJsDocTagTypeExpression = false;
|
||||
let isInSnippetScope = false;
|
||||
if (insideComment) {
|
||||
if (hasDocComment(sourceFile, position)) {
|
||||
if (sourceFile.text.charCodeAt(position - 1) === CharacterCodes.at) {
|
||||
@@ -971,7 +972,7 @@ namespace ts.Completions {
|
||||
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
|
||||
|
||||
const recommendedCompletion = previousToken && getRecommendedCompletion(previousToken, position, sourceFile, typeChecker);
|
||||
return { kind: CompletionDataKind.Data, symbols, completionKind, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken, isJsxInitializer };
|
||||
return { kind: CompletionDataKind.Data, symbols, completionKind, isInSnippetScope, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken, isJsxInitializer };
|
||||
|
||||
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
|
||||
|
||||
@@ -1098,7 +1099,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
// Get all entities in the current scope.
|
||||
completionKind = CompletionKind.None;
|
||||
completionKind = CompletionKind.Global;
|
||||
isNewIdentifierLocation = isNewIdentifierDefinitionLocation(contextToken);
|
||||
|
||||
if (previousToken !== contextToken) {
|
||||
@@ -1134,9 +1135,7 @@ namespace ts.Completions {
|
||||
position;
|
||||
|
||||
const scopeNode = getScopeNode(contextToken, adjustedPosition, sourceFile) || sourceFile;
|
||||
if (isGlobalCompletionScope(scopeNode)) {
|
||||
completionKind = CompletionKind.Global;
|
||||
}
|
||||
isInSnippetScope = isSnippetScope(scopeNode);
|
||||
|
||||
const symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Alias;
|
||||
|
||||
@@ -1161,7 +1160,7 @@ namespace ts.Completions {
|
||||
return true;
|
||||
}
|
||||
|
||||
function isGlobalCompletionScope(scopeNode: Node): boolean {
|
||||
function isSnippetScope(scopeNode: Node): boolean {
|
||||
switch (scopeNode.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
case SyntaxKind.TemplateExpression:
|
||||
@@ -1999,7 +1998,7 @@ namespace ts.Completions {
|
||||
// NOTE: if one only performs this step when m.name is an identifier,
|
||||
// things like '__proto__' are not filtered out.
|
||||
const name = getNameOfDeclaration(m);
|
||||
existingName = getEscapedTextOfIdentifierOrLiteral(name as (Identifier | LiteralExpression));
|
||||
existingName = isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
|
||||
}
|
||||
|
||||
existingMemberNames.set(existingName, true);
|
||||
@@ -2128,10 +2127,10 @@ namespace ts.Completions {
|
||||
// TODO: GH#18169
|
||||
return { name: JSON.stringify(name), needsConvertPropertyAccess: false };
|
||||
case CompletionKind.PropertyAccess:
|
||||
case CompletionKind.None:
|
||||
case CompletionKind.Global:
|
||||
// Don't add a completion for a name starting with a space. See https://github.com/Microsoft/TypeScript/pull/20547
|
||||
return name.charCodeAt(0) === CharacterCodes.space ? undefined : { name, needsConvertPropertyAccess: true };
|
||||
case CompletionKind.None:
|
||||
case CompletionKind.String:
|
||||
return validIdentiferResult;
|
||||
default:
|
||||
|
||||
@@ -21,23 +21,11 @@ namespace ts.DocumentHighlights {
|
||||
};
|
||||
}
|
||||
|
||||
function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: ReadonlyArray<SourceFile>): DocumentHighlights[] {
|
||||
function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: ReadonlyArray<SourceFile>): DocumentHighlights[] | undefined {
|
||||
const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken);
|
||||
return referenceEntries && convertReferencedSymbols(referenceEntries);
|
||||
}
|
||||
|
||||
function convertReferencedSymbols(referenceEntries: ReadonlyArray<FindAllReferences.Entry>): DocumentHighlights[] {
|
||||
const fileNameToDocumentHighlights = createMap<HighlightSpan[]>();
|
||||
for (const entry of referenceEntries) {
|
||||
const { fileName, span } = FindAllReferences.toHighlightSpan(entry);
|
||||
let highlightSpans = fileNameToDocumentHighlights.get(fileName);
|
||||
if (!highlightSpans) {
|
||||
fileNameToDocumentHighlights.set(fileName, highlightSpans = []);
|
||||
}
|
||||
highlightSpans.push(span);
|
||||
}
|
||||
|
||||
return arrayFrom(fileNameToDocumentHighlights.entries(), ([fileName, highlightSpans ]) => ({ fileName, highlightSpans }));
|
||||
if (!referenceEntries) return undefined;
|
||||
const map = arrayToMultiMap(referenceEntries.map(FindAllReferences.toHighlightSpan), e => e.fileName, e => e.span);
|
||||
return arrayFrom(map.entries(), ([fileName, highlightSpans]) => ({ fileName, highlightSpans }));
|
||||
}
|
||||
|
||||
function getSyntacticDocumentHighlights(node: Node, sourceFile: SourceFile): DocumentHighlights[] {
|
||||
|
||||
@@ -330,17 +330,15 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
// Labels
|
||||
if (isLabelName(node)) {
|
||||
if (isJumpStatementTarget(node)) {
|
||||
const labelDefinition = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
|
||||
// if we have a label definition, look within its statement for references, if not, then
|
||||
// the label is undefined and we have no results..
|
||||
return labelDefinition && getLabelReferencesInNode(labelDefinition.parent, labelDefinition);
|
||||
}
|
||||
else {
|
||||
// it is a label definition and not a target, search within the parent labeledStatement
|
||||
return getLabelReferencesInNode(node.parent, <Identifier>node);
|
||||
}
|
||||
if (isJumpStatementTarget(node)) {
|
||||
const labelDefinition = getTargetLabel(node.parent, node.text);
|
||||
// if we have a label definition, look within its statement for references, if not, then
|
||||
// the label is undefined and we have no results..
|
||||
return labelDefinition && getLabelReferencesInNode(labelDefinition.parent, labelDefinition);
|
||||
}
|
||||
else if (isLabelOfLabeledStatement(node)) {
|
||||
// it is a label definition and not a target, search within the parent labeledStatement
|
||||
return getLabelReferencesInNode(node.parent, node);
|
||||
}
|
||||
|
||||
if (isThis(node)) {
|
||||
@@ -709,6 +707,16 @@ namespace ts.FindAllReferences.Core {
|
||||
return exposedByParent ? scope.getSourceFile() : scope;
|
||||
}
|
||||
|
||||
/** Used as a quick check for whether a symbol is used at all in a file (besides its definition). */
|
||||
export function isSymbolReferencedInFile(definition: Identifier, checker: TypeChecker, sourceFile: SourceFile) {
|
||||
const symbol = checker.getSymbolAtLocation(definition);
|
||||
if (!symbol) return true; // Be lenient with invalid code.
|
||||
return getPossibleSymbolReferencePositions(sourceFile, symbol.name).some(position => {
|
||||
const token = tryCast(getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true), isIdentifier);
|
||||
return token && token !== definition && token.escapedText === definition.escapedText && checker.getSymbolAtLocation(token) === symbol;
|
||||
});
|
||||
}
|
||||
|
||||
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): ReadonlyArray<number> {
|
||||
const positions: number[] = [];
|
||||
|
||||
@@ -745,18 +753,13 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
function getLabelReferencesInNode(container: Node, targetLabel: Identifier): SymbolAndEntries[] {
|
||||
const references: Entry[] = [];
|
||||
const sourceFile = container.getSourceFile();
|
||||
const labelName = targetLabel.text;
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container);
|
||||
for (const position of possiblePositions) {
|
||||
const references = mapDefined(getPossibleSymbolReferencePositions(sourceFile, labelName, container), position => {
|
||||
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false);
|
||||
// Only pick labels that are either the target label, or have a target that is the target label
|
||||
if (node && (node === targetLabel || (isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel))) {
|
||||
references.push(nodeEntry(node));
|
||||
}
|
||||
}
|
||||
|
||||
return node && (node === targetLabel || (isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel)) ? nodeEntry(node) : undefined;
|
||||
});
|
||||
return [{ definition: { type: "label", node: targetLabel }, references }];
|
||||
}
|
||||
|
||||
@@ -782,24 +785,16 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
function getAllReferencesForKeyword(sourceFiles: ReadonlyArray<SourceFile>, keywordKind: ts.SyntaxKind, cancellationToken: CancellationToken): SymbolAndEntries[] {
|
||||
const references: NodeEntry[] = [];
|
||||
for (const sourceFile of sourceFiles) {
|
||||
const references = flatMap(sourceFiles, sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
addReferencesForKeywordInFile(sourceFile, keywordKind, tokenToString(keywordKind), references);
|
||||
}
|
||||
return mapDefined(getPossibleSymbolReferencePositions(sourceFile, tokenToString(keywordKind), sourceFile), position => {
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
return referenceLocation.kind === keywordKind ? nodeEntry(referenceLocation) : undefined;
|
||||
});
|
||||
});
|
||||
return references.length ? [{ definition: { type: "keyword", node: references[0].node }, references }] : undefined;
|
||||
}
|
||||
|
||||
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, references: Push<NodeEntry>): void {
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile);
|
||||
for (const position of possiblePositions) {
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
if (referenceLocation.kind === kind) {
|
||||
references.push(nodeEntry(referenceLocation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencesInSourceFile(sourceFile: ts.SourceFile, search: Search, state: State): void {
|
||||
state.cancellationToken.throwIfCancellationRequested();
|
||||
return getReferencesInContainer(sourceFile, sourceFile, search, state);
|
||||
@@ -1290,15 +1285,11 @@ namespace ts.FindAllReferences.Core {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const references: Entry[] = [];
|
||||
|
||||
const sourceFile = searchSpaceNode.getSourceFile();
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode);
|
||||
for (const position of possiblePositions) {
|
||||
const references = mapDefined(getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode), position => {
|
||||
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false);
|
||||
|
||||
if (!node || node.kind !== SyntaxKind.SuperKeyword) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
const container = getSuperContainer(node, /*stopOnFunctions*/ false);
|
||||
@@ -1306,10 +1297,8 @@ namespace ts.FindAllReferences.Core {
|
||||
// If we have a 'super' container, we must have an enclosing class.
|
||||
// Now make sure the owning class is the same as the search-space
|
||||
// and has the same static qualifier as the original 'super's owner.
|
||||
if (container && (ModifierFlags.Static & getModifierFlags(container)) === staticFlag && container.parent.symbol === searchSpaceNode.symbol) {
|
||||
references.push(nodeEntry(node));
|
||||
}
|
||||
}
|
||||
return container && (ModifierFlags.Static & getModifierFlags(container)) === staticFlag && container.parent.symbol === searchSpaceNode.symbol ? nodeEntry(node) : undefined;
|
||||
});
|
||||
|
||||
return [{ definition: { type: "symbol", symbol: searchSpaceNode.symbol, node: superKeyword }, references }];
|
||||
}
|
||||
@@ -1350,19 +1339,10 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
const references: Entry[] = [];
|
||||
|
||||
let possiblePositions: ReadonlyArray<number>;
|
||||
if (searchSpaceNode.kind === SyntaxKind.SourceFile) {
|
||||
forEach(sourceFiles, sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this");
|
||||
getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, staticFlag, references);
|
||||
});
|
||||
}
|
||||
else {
|
||||
const sourceFile = searchSpaceNode.getSourceFile();
|
||||
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode);
|
||||
getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, staticFlag, references);
|
||||
for (const sourceFile of searchSpaceNode.kind === SyntaxKind.SourceFile ? sourceFiles : [searchSpaceNode.getSourceFile()]) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
const positions = getPossibleSymbolReferencePositions(sourceFile, "this", isSourceFile(searchSpaceNode) ? sourceFile : searchSpaceNode);
|
||||
getThisReferencesInFile(sourceFile, searchSpaceNode.kind === SyntaxKind.SourceFile ? sourceFile : searchSpaceNode, positions, staticFlag, references);
|
||||
}
|
||||
|
||||
return [{
|
||||
@@ -1411,27 +1391,18 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
function getReferencesForStringLiteral(node: StringLiteral, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken): SymbolAndEntries[] {
|
||||
const references: NodeEntry[] = [];
|
||||
|
||||
for (const sourceFile of sourceFiles) {
|
||||
const references = flatMap(sourceFiles, sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, node.text);
|
||||
getReferencesForStringLiteralInFile(sourceFile, node.text, possiblePositions, references);
|
||||
}
|
||||
return mapDefined(getPossibleSymbolReferencePositions(sourceFile, node.text), position => {
|
||||
const ref = tryCast(getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false), isStringLiteral);
|
||||
return ref && ref.text === node.text ? nodeEntry(ref, /*isInString*/ true) : undefined;
|
||||
});
|
||||
});
|
||||
|
||||
return [{
|
||||
definition: { type: "string", node },
|
||||
references
|
||||
}];
|
||||
|
||||
function getReferencesForStringLiteralInFile(sourceFile: SourceFile, searchText: string, possiblePositions: ReadonlyArray<number>, references: Push<NodeEntry>): void {
|
||||
for (const position of possiblePositions) {
|
||||
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false);
|
||||
if (node && node.kind === SyntaxKind.StringLiteral && (node as StringLiteral).text === searchText) {
|
||||
references.push(nodeEntry(node, /*isInString*/ true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For certain symbol kinds, we need to include other symbols in the search set.
|
||||
|
||||
@@ -13,9 +13,8 @@ namespace ts.GoToDefinition {
|
||||
|
||||
// Labels
|
||||
if (isJumpStatementTarget(node)) {
|
||||
const labelName = (<Identifier>node).text;
|
||||
const label = getTargetLabel((<BreakOrContinueStatement>node.parent), labelName);
|
||||
return label ? [createDefinitionInfoFromName(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined;
|
||||
const label = getTargetLabel(node.parent, node.text);
|
||||
return label ? [createDefinitionInfoFromName(label, ScriptElementKind.label, node.text, /*containerName*/ undefined)] : undefined;
|
||||
}
|
||||
|
||||
const typeChecker = program.getTypeChecker();
|
||||
|
||||
@@ -109,7 +109,9 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
else if (isDefaultImport(direct)) {
|
||||
const sourceFileLike = getSourceFileLikeForImportDeclaration(direct);
|
||||
addIndirectUser(sourceFileLike); // Add a check for indirect uses to handle synthetic default imports
|
||||
if (!isAvailableThroughGlobal) {
|
||||
addIndirectUser(sourceFileLike); // Add a check for indirect uses to handle synthetic default imports
|
||||
}
|
||||
directImports.push(direct);
|
||||
}
|
||||
else {
|
||||
@@ -651,8 +653,8 @@ namespace ts.FindAllReferences {
|
||||
if (parent.kind === SyntaxKind.SourceFile) {
|
||||
return parent as SourceFile;
|
||||
}
|
||||
Debug.assert(parent.kind === SyntaxKind.ModuleBlock && isAmbientModuleDeclaration(parent.parent));
|
||||
return parent.parent as AmbientModuleDeclaration;
|
||||
Debug.assert(parent.kind === SyntaxKind.ModuleBlock);
|
||||
return cast(parent.parent, isAmbientModuleDeclaration);
|
||||
}
|
||||
|
||||
function isAmbientModuleDeclaration(node: Node): node is AmbientModuleDeclaration {
|
||||
|
||||
+16
-24
@@ -89,45 +89,37 @@ namespace ts.NavigateTo {
|
||||
}
|
||||
|
||||
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]): boolean {
|
||||
if (declaration) {
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name) {
|
||||
const text = getTextOfIdentifierOrLiteral(name as (Identifier | LiteralExpression));
|
||||
if (text !== undefined) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
else if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return tryAddComputedPropertyName(name.expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
else {
|
||||
// Don't know how to add this.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name && isPropertyNameLiteral(name)) {
|
||||
containers.unshift(getTextOfIdentifierOrLiteral(name));
|
||||
return true;
|
||||
}
|
||||
else if (name && name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return tryAddComputedPropertyName(name.expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
else {
|
||||
// Don't know how to add this.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only added the names of computed properties if they're simple dotted expressions, like:
|
||||
//
|
||||
// [X.Y.Z]() { }
|
||||
function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean {
|
||||
const text = getTextOfIdentifierOrLiteral(expression as LiteralExpression);
|
||||
if (text !== undefined) {
|
||||
if (isPropertyNameLiteral(expression)) {
|
||||
const text = getTextOfIdentifierOrLiteral(expression);
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expression.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
const propertyAccess = <PropertyAccessExpression>expression;
|
||||
if (isPropertyAccessExpression(expression)) {
|
||||
if (includeLastPortion) {
|
||||
containers.unshift(propertyAccess.name.text);
|
||||
containers.unshift(expression.name.text);
|
||||
}
|
||||
|
||||
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion*/ true);
|
||||
return tryAddComputedPropertyName(expression.expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
+101
-36
@@ -1,9 +1,17 @@
|
||||
/* @internal */
|
||||
namespace ts.OrganizeImports {
|
||||
|
||||
/**
|
||||
* Organize imports by:
|
||||
* 1) Removing unused imports
|
||||
* 2) Coalescing imports from the same module
|
||||
* 3) Sorting imports
|
||||
*/
|
||||
export function organizeImports(
|
||||
sourceFile: SourceFile,
|
||||
formatContext: formatting.FormatContext,
|
||||
host: LanguageServiceHost) {
|
||||
host: LanguageServiceHost,
|
||||
program: Program) {
|
||||
|
||||
// TODO (https://github.com/Microsoft/TypeScript/issues/10020): sort *within* ambient modules (find using isAmbientModule)
|
||||
|
||||
@@ -21,7 +29,7 @@ namespace ts.OrganizeImports {
|
||||
|
||||
const newImportDecls = flatMap(sortedImportGroups, importGroup =>
|
||||
getExternalModuleName(importGroup[0].moduleSpecifier)
|
||||
? coalesceImports(removeUnusedImports(importGroup))
|
||||
? coalesceImports(removeUnusedImports(importGroup, sourceFile, program))
|
||||
: importGroup);
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext });
|
||||
@@ -47,8 +55,58 @@ namespace ts.OrganizeImports {
|
||||
return changeTracker.getChanges();
|
||||
}
|
||||
|
||||
function removeUnusedImports(oldImports: ReadonlyArray<ImportDeclaration>) {
|
||||
return oldImports; // TODO (https://github.com/Microsoft/TypeScript/issues/10020)
|
||||
function removeUnusedImports(oldImports: ReadonlyArray<ImportDeclaration>, sourceFile: SourceFile, program: Program) {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
const jsxNamespace = typeChecker.getJsxNamespace();
|
||||
const jsxContext = sourceFile.languageVariant === LanguageVariant.JSX && program.getCompilerOptions().jsx;
|
||||
|
||||
const usedImports: ImportDeclaration[] = [];
|
||||
|
||||
for (const importDecl of oldImports) {
|
||||
const {importClause} = importDecl;
|
||||
|
||||
if (!importClause) {
|
||||
// Imports without import clauses are assumed to be included for their side effects and are not removed.
|
||||
usedImports.push(importDecl);
|
||||
continue;
|
||||
}
|
||||
|
||||
let { name, namedBindings } = importClause;
|
||||
|
||||
// Default import
|
||||
if (name && !isDeclarationUsed(name)) {
|
||||
name = undefined;
|
||||
}
|
||||
|
||||
if (namedBindings) {
|
||||
if (isNamespaceImport(namedBindings)) {
|
||||
// Namespace import
|
||||
if (!isDeclarationUsed(namedBindings.name)) {
|
||||
namedBindings = undefined;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// List of named imports
|
||||
const newElements = namedBindings.elements.filter(e => isDeclarationUsed(e.propertyName || e.name));
|
||||
if (newElements.length < namedBindings.elements.length) {
|
||||
namedBindings = newElements.length
|
||||
? updateNamedImports(namedBindings, newElements)
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name || namedBindings) {
|
||||
usedImports.push(updateImportDeclarationAndClause(importDecl, name, namedBindings));
|
||||
}
|
||||
}
|
||||
|
||||
return usedImports;
|
||||
|
||||
function isDeclarationUsed(identifier: Identifier) {
|
||||
// The JSX factory symbol is always used.
|
||||
return jsxContext && (identifier.text === jsxNamespace) || FindAllReferences.Core.isSymbolReferencedInFile(identifier, typeChecker, sourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
function getExternalModuleName(specifier: Expression) {
|
||||
@@ -66,7 +124,7 @@ namespace ts.OrganizeImports {
|
||||
return importGroup;
|
||||
}
|
||||
|
||||
const { importWithoutClause, defaultImports, namespaceImports, namedImports } = getImportParts(importGroup);
|
||||
const { importWithoutClause, defaultImports, namespaceImports, namedImports } = getCategorizedImports(importGroup);
|
||||
|
||||
const coalescedImports: ImportDeclaration[] = [];
|
||||
|
||||
@@ -78,19 +136,20 @@ namespace ts.OrganizeImports {
|
||||
// produce two import declarations in this special case.
|
||||
if (defaultImports.length === 1 && namespaceImports.length === 1 && namedImports.length === 0) {
|
||||
// Add the namespace import to the existing default ImportDeclaration.
|
||||
const defaultImportClause = defaultImports[0].parent as ImportClause;
|
||||
const defaultImport = defaultImports[0];
|
||||
coalescedImports.push(
|
||||
updateImportDeclarationAndClause(defaultImportClause, defaultImportClause.name, namespaceImports[0]));
|
||||
updateImportDeclarationAndClause(defaultImport, defaultImport.importClause.name, namespaceImports[0].importClause.namedBindings));
|
||||
|
||||
return coalescedImports;
|
||||
}
|
||||
|
||||
const sortedNamespaceImports = stableSort(namespaceImports, (n1, n2) => compareIdentifiers(n1.name, n2.name));
|
||||
const sortedNamespaceImports = stableSort(namespaceImports, (i1, i2) =>
|
||||
compareIdentifiers((i1.importClause.namedBindings as NamespaceImport).name, (i2.importClause.namedBindings as NamespaceImport).name));
|
||||
|
||||
for (const namespaceImport of sortedNamespaceImports) {
|
||||
// Drop the name, if any
|
||||
coalescedImports.push(
|
||||
updateImportDeclarationAndClause(namespaceImport.parent, /*name*/ undefined, namespaceImport));
|
||||
updateImportDeclarationAndClause(namespaceImport, /*name*/ undefined, namespaceImport.importClause.namedBindings));
|
||||
}
|
||||
|
||||
if (defaultImports.length === 0 && namedImports.length === 0) {
|
||||
@@ -100,41 +159,48 @@ namespace ts.OrganizeImports {
|
||||
let newDefaultImport: Identifier | undefined;
|
||||
const newImportSpecifiers: ImportSpecifier[] = [];
|
||||
if (defaultImports.length === 1) {
|
||||
newDefaultImport = defaultImports[0];
|
||||
newDefaultImport = defaultImports[0].importClause.name;
|
||||
}
|
||||
else {
|
||||
for (const defaultImport of defaultImports) {
|
||||
newImportSpecifiers.push(
|
||||
createImportSpecifier(createIdentifier("default"), defaultImport));
|
||||
createImportSpecifier(createIdentifier("default"), defaultImport.importClause.name));
|
||||
}
|
||||
}
|
||||
|
||||
newImportSpecifiers.push(...flatMap(namedImports, n => n.elements));
|
||||
newImportSpecifiers.push(...flatMap(namedImports, i => (i.importClause.namedBindings as NamedImports).elements));
|
||||
|
||||
const sortedImportSpecifiers = stableSort(newImportSpecifiers, (s1, s2) =>
|
||||
compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name) ||
|
||||
compareIdentifiers(s1.name, s2.name));
|
||||
|
||||
const importClause = defaultImports.length > 0
|
||||
? defaultImports[0].parent as ImportClause
|
||||
: namedImports[0].parent;
|
||||
const importDecl = defaultImports.length > 0
|
||||
? defaultImports[0]
|
||||
: namedImports[0];
|
||||
|
||||
const newNamedImports = sortedImportSpecifiers.length === 0
|
||||
? undefined
|
||||
: namedImports.length === 0
|
||||
? createNamedImports(sortedImportSpecifiers)
|
||||
: updateNamedImports(namedImports[0], sortedImportSpecifiers);
|
||||
: updateNamedImports(namedImports[0].importClause.namedBindings as NamedImports, sortedImportSpecifiers);
|
||||
|
||||
coalescedImports.push(
|
||||
updateImportDeclarationAndClause(importClause, newDefaultImport, newNamedImports));
|
||||
updateImportDeclarationAndClause(importDecl, newDefaultImport, newNamedImports));
|
||||
|
||||
return coalescedImports;
|
||||
|
||||
function getImportParts(importGroup: ReadonlyArray<ImportDeclaration>) {
|
||||
/*
|
||||
* Returns entire import declarations because they may already have been rewritten and
|
||||
* may lack parent pointers. The desired parts can easily be recovered based on the
|
||||
* categorization.
|
||||
*
|
||||
* NB: There may be overlap between `defaultImports` and `namespaceImports`/`namedImports`.
|
||||
*/
|
||||
function getCategorizedImports(importGroup: ReadonlyArray<ImportDeclaration>) {
|
||||
let importWithoutClause: ImportDeclaration | undefined;
|
||||
const defaultImports: Identifier[] = [];
|
||||
const namespaceImports: NamespaceImport[] = [];
|
||||
const namedImports: NamedImports[] = [];
|
||||
const defaultImports: ImportDeclaration[] = [];
|
||||
const namespaceImports: ImportDeclaration[] = [];
|
||||
const namedImports: ImportDeclaration[] = [];
|
||||
|
||||
for (const importDeclaration of importGroup) {
|
||||
if (importDeclaration.importClause === undefined) {
|
||||
@@ -147,15 +213,15 @@ namespace ts.OrganizeImports {
|
||||
const { name, namedBindings } = importDeclaration.importClause;
|
||||
|
||||
if (name) {
|
||||
defaultImports.push(name);
|
||||
defaultImports.push(importDeclaration);
|
||||
}
|
||||
|
||||
if (namedBindings) {
|
||||
if (isNamespaceImport(namedBindings)) {
|
||||
namespaceImports.push(namedBindings);
|
||||
namespaceImports.push(importDeclaration);
|
||||
}
|
||||
else {
|
||||
namedImports.push(namedBindings);
|
||||
namedImports.push(importDeclaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,20 +237,19 @@ namespace ts.OrganizeImports {
|
||||
function compareIdentifiers(s1: Identifier, s2: Identifier) {
|
||||
return compareStringsCaseSensitive(s1.text, s2.text);
|
||||
}
|
||||
}
|
||||
|
||||
function updateImportDeclarationAndClause(
|
||||
importClause: ImportClause,
|
||||
name: Identifier | undefined,
|
||||
namedBindings: NamedImportBindings | undefined) {
|
||||
function updateImportDeclarationAndClause(
|
||||
importDeclaration: ImportDeclaration,
|
||||
name: Identifier | undefined,
|
||||
namedBindings: NamedImportBindings | undefined) {
|
||||
|
||||
const importDeclaration = importClause.parent;
|
||||
return updateImportDeclaration(
|
||||
importDeclaration,
|
||||
importDeclaration.decorators,
|
||||
importDeclaration.modifiers,
|
||||
updateImportClause(importClause, name, namedBindings),
|
||||
importDeclaration.moduleSpecifier);
|
||||
}
|
||||
return updateImportDeclaration(
|
||||
importDeclaration,
|
||||
importDeclaration.decorators,
|
||||
importDeclaration.modifiers,
|
||||
updateImportClause(importDeclaration.importClause, name, namedBindings),
|
||||
importDeclaration.moduleSpecifier);
|
||||
}
|
||||
|
||||
/* internal */ // Exported for testing
|
||||
|
||||
+5
-13
@@ -24,25 +24,19 @@ namespace ts.Rename {
|
||||
|
||||
// Only allow a symbol to be renamed if it actually has at least one declaration.
|
||||
if (symbol) {
|
||||
const declarations = symbol.getDeclarations();
|
||||
const { declarations } = symbol;
|
||||
if (declarations && declarations.length > 0) {
|
||||
// Disallow rename for elements that are defined in the standard TypeScript library.
|
||||
if (some(declarations, isDefinedInLibraryFile)) {
|
||||
if (declarations.some(isDefinedInLibraryFile)) {
|
||||
return getRenameInfoError(Diagnostics.You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library);
|
||||
}
|
||||
|
||||
// Cannot rename `default` as in `import { default as foo } from "./someModule";
|
||||
if (node.kind === SyntaxKind.Identifier &&
|
||||
(node as Identifier).originalKeywordKind === SyntaxKind.DefaultKeyword &&
|
||||
symbol.parent.flags & ts.SymbolFlags.Module) {
|
||||
if (isIdentifier(node) && node.originalKeywordKind === SyntaxKind.DefaultKeyword && symbol.parent.flags & SymbolFlags.Module) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, node);
|
||||
if (!kind) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const specifierName = (isImportOrExportSpecifierName(node) || isStringOrNumericLiteral(node) && node.parent.kind === SyntaxKind.ComputedPropertyName)
|
||||
? stripQuotes(getTextOfIdentifierOrLiteral(node))
|
||||
: undefined;
|
||||
@@ -51,13 +45,11 @@ namespace ts.Rename {
|
||||
return getRenameInfoSuccess(displayName, fullDisplayName, kind, SymbolDisplay.getSymbolModifiers(symbol), node, sourceFile);
|
||||
}
|
||||
}
|
||||
else if (node.kind === SyntaxKind.StringLiteral) {
|
||||
else if (isStringLiteral(node)) {
|
||||
if (isDefinedInLibraryFile(node)) {
|
||||
return getRenameInfoError(Diagnostics.You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library);
|
||||
}
|
||||
|
||||
const displayName = stripQuotes((node as StringLiteral).text);
|
||||
return getRenameInfoSuccess(displayName, displayName, ScriptElementKind.variableElement, ScriptElementKindModifier.none, node, sourceFile);
|
||||
return getRenameInfoSuccess(node.text, node.text, ScriptElementKind.variableElement, ScriptElementKindModifier.none, node, sourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-43
@@ -721,23 +721,8 @@ namespace ts {
|
||||
|
||||
function getDeclarationName(declaration: Declaration) {
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name) {
|
||||
const result = getTextOfIdentifierOrLiteral(name as (Identifier | LiteralExpression));
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
const expr = name.expression;
|
||||
if (expr.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
return (<PropertyAccessExpression>expr).name.text;
|
||||
}
|
||||
|
||||
return getTextOfIdentifierOrLiteral(expr as (Identifier | LiteralExpression));
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return name && (isPropertyNameLiteral(name) ? getTextOfIdentifierOrLiteral(name) :
|
||||
name.kind === SyntaxKind.ComputedPropertyName && isPropertyAccessExpression(name.expression) ? name.expression.name.text : undefined);
|
||||
}
|
||||
|
||||
function visit(node: Node): void {
|
||||
@@ -1482,10 +1467,7 @@ namespace ts {
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
if (node === sourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isLabelName(node)) {
|
||||
// Avoid giving quickInfo for the sourceFile as a whole.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1496,6 +1478,11 @@ namespace ts {
|
||||
// Try getting just type at this position and show
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
if (isLabelName(node)) {
|
||||
// Type here will be 'any', avoid displaying this.
|
||||
return undefined;
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.QualifiedName:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
@@ -1503,29 +1490,27 @@ namespace ts {
|
||||
case SyntaxKind.SuperKeyword:
|
||||
// For the identifiers/this/super etc get the type at position
|
||||
const type = typeChecker.getTypeAtLocation(node);
|
||||
if (type) {
|
||||
return {
|
||||
kind: ScriptElementKind.unknown,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
textSpan: createTextSpan(node.getStart(), node.getWidth()),
|
||||
displayParts: typeToDisplayParts(typeChecker, type, getContainerNode(node)),
|
||||
documentation: type.symbol ? type.symbol.getDocumentationComment(typeChecker) : undefined,
|
||||
tags: type.symbol ? type.symbol.getJsDocTags() : undefined
|
||||
};
|
||||
}
|
||||
return type && {
|
||||
kind: ScriptElementKind.unknown,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
textSpan: createTextSpanFromNode(node, sourceFile),
|
||||
displayParts: typeToDisplayParts(typeChecker, type, getContainerNode(node)),
|
||||
documentation: type.symbol ? type.symbol.getDocumentationComment(typeChecker) : undefined,
|
||||
tags: type.symbol ? type.symbol.getJsDocTags() : undefined
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const displayPartsDocumentationsAndKind = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(node), node);
|
||||
const { symbolKind, displayParts, documentation, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(node), node);
|
||||
return {
|
||||
kind: displayPartsDocumentationsAndKind.symbolKind,
|
||||
kind: symbolKind,
|
||||
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
|
||||
textSpan: createTextSpan(node.getStart(), node.getWidth()),
|
||||
displayParts: displayPartsDocumentationsAndKind.displayParts,
|
||||
documentation: displayPartsDocumentationsAndKind.documentation,
|
||||
tags: displayPartsDocumentationsAndKind.tags
|
||||
textSpan: createTextSpanFromNode(node, sourceFile),
|
||||
displayParts,
|
||||
documentation,
|
||||
tags,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1534,11 +1519,9 @@ namespace ts {
|
||||
&& isPropertyAssignment(node.parent)
|
||||
&& node.parent.name === node) {
|
||||
const type = checker.getContextualType(node.parent.parent);
|
||||
if (type) {
|
||||
const property = checker.getPropertyOfType(type, getTextOfIdentifierOrLiteral(node));
|
||||
if (property) {
|
||||
return property;
|
||||
}
|
||||
const property = type && checker.getPropertyOfType(type, getTextOfIdentifierOrLiteral(node));
|
||||
if (property) {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
return checker.getSymbolAtLocation(node);
|
||||
@@ -1855,7 +1838,7 @@ namespace ts {
|
||||
const sourceFile = getValidSourceFile(scope.fileName);
|
||||
const formatContext = formatting.getFormatContext(formatOptions);
|
||||
|
||||
return OrganizeImports.organizeImports(sourceFile, formatContext, host);
|
||||
return OrganizeImports.organizeImports(sourceFile, formatContext, host, program);
|
||||
}
|
||||
|
||||
function applyCodeActionCommand(action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
|
||||
|
||||
@@ -727,7 +727,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface CompletionInfo {
|
||||
/** Not true for all glboal completions. This will be true if the enclosing scope matches a few syntax kinds. See `isGlobalCompletionScope`. */
|
||||
/** Not true for all glboal completions. This will be true if the enclosing scope matches a few syntax kinds. See `isSnippetScope`. */
|
||||
isGlobalCompletion: boolean;
|
||||
isMemberCompletion: boolean;
|
||||
|
||||
|
||||
+10
-27
@@ -92,7 +92,7 @@ namespace ts {
|
||||
return SemanticMeaning.All;
|
||||
}
|
||||
else if (isInRightSideOfInternalImportEqualsDeclaration(node)) {
|
||||
return getMeaningFromRightHandSideOfImportEquals(node);
|
||||
return getMeaningFromRightHandSideOfImportEquals(node as Identifier);
|
||||
}
|
||||
else if (isDeclarationName(node)) {
|
||||
return getMeaningFromDeclaration(node.parent);
|
||||
@@ -112,19 +112,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getMeaningFromRightHandSideOfImportEquals(node: Node) {
|
||||
Debug.assert(node.kind === SyntaxKind.Identifier);
|
||||
|
||||
function getMeaningFromRightHandSideOfImportEquals(node: Node): SemanticMeaning {
|
||||
// import a = |b|; // Namespace
|
||||
// import a = |b.c|; // Value, type, namespace
|
||||
// import a = |b.c|.d; // Namespace
|
||||
|
||||
if (node.parent.kind === SyntaxKind.QualifiedName &&
|
||||
(<QualifiedName>node.parent).right === node &&
|
||||
node.parent.parent.kind === SyntaxKind.ImportEqualsDeclaration) {
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
}
|
||||
return SemanticMeaning.Namespace;
|
||||
const name = node.kind === SyntaxKind.QualifiedName ? node : isQualifiedName(node.parent) && node.parent.right === node ? node.parent : undefined;
|
||||
return name && name.parent.kind === SyntaxKind.ImportEqualsDeclaration ? SemanticMeaning.All : SemanticMeaning.Namespace;
|
||||
}
|
||||
|
||||
export function isInRightSideOfInternalImportEqualsDeclaration(node: Node) {
|
||||
@@ -221,16 +214,12 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isJumpStatementTarget(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.Identifier &&
|
||||
(node.parent.kind === SyntaxKind.BreakStatement || node.parent.kind === SyntaxKind.ContinueStatement) &&
|
||||
(<BreakOrContinueStatement>node.parent).label === node;
|
||||
}
|
||||
export function isJumpStatementTarget(node: Node): node is Identifier & { parent: BreakOrContinueStatement } {
|
||||
return node.kind === SyntaxKind.Identifier && isBreakOrContinueStatement(node.parent) && node.parent.label === node;
|
||||
}
|
||||
|
||||
function isLabelOfLabeledStatement(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.Identifier &&
|
||||
node.parent.kind === SyntaxKind.LabeledStatement &&
|
||||
(<LabeledStatement>node.parent).label === node;
|
||||
export function isLabelOfLabeledStatement(node: Node): node is Identifier {
|
||||
return node.kind === SyntaxKind.Identifier && isLabeledStatement(node.parent) && node.parent.label === node;
|
||||
}
|
||||
|
||||
export function isLabelName(node: Node): boolean {
|
||||
@@ -632,13 +621,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).
|
||||
const syntaxList = forEach(node.parent.getChildren(), c => {
|
||||
// find syntax list that covers the span of the node
|
||||
if (isSyntaxList(c) && c.pos <= node.pos && c.end >= node.end) {
|
||||
return c;
|
||||
}
|
||||
});
|
||||
|
||||
const syntaxList = find(node.parent.getChildren(), (c): c is SyntaxList => isSyntaxList(c) && rangeContainsRange(c, node));
|
||||
// Either we didn't find an appropriate list, or the list must contain us.
|
||||
Debug.assert(!syntaxList || contains(syntaxList.getChildren(), node));
|
||||
return syntaxList;
|
||||
|
||||
Reference in New Issue
Block a user