mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Use the same logic for completion entry details that we do for getting completion entries.
This commit is contained in:
+87
-47
@@ -10547,62 +10547,102 @@ module ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] {
|
||||
function getSymbolsInScope(location: Node, meaning: SymbolFlags, predicate?: (symbol: Symbol) => boolean): Symbol[] {
|
||||
let symbols: SymbolTable = {};
|
||||
let memberFlags: NodeFlags = 0;
|
||||
function copySymbol(symbol: Symbol, meaning: SymbolFlags) {
|
||||
if (symbol.flags & meaning) {
|
||||
let id = symbol.name;
|
||||
if (!isReservedMemberName(id) && !hasProperty(symbols, id)) {
|
||||
symbols[id] = symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
function copySymbols(source: SymbolTable, meaning: SymbolFlags) {
|
||||
if (meaning) {
|
||||
for (let id in source) {
|
||||
if (hasProperty(source, id)) {
|
||||
copySymbol(source[id], meaning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isInsideWithStatementBody(location)) {
|
||||
// We cannot answer semantic questions within a with block, do not proceed any further
|
||||
return [];
|
||||
}
|
||||
|
||||
while (location) {
|
||||
if (location.locals && !isGlobalSourceFile(location)) {
|
||||
copySymbols(location.locals, meaning);
|
||||
}
|
||||
switch (location.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
if (!isExternalModule(<SourceFile>location)) break;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember);
|
||||
break;
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.EnumMember);
|
||||
break;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
if (!(memberFlags & NodeFlags.Static)) {
|
||||
copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.FunctionExpression:
|
||||
if ((<FunctionExpression>location).name) {
|
||||
copySymbol(location.symbol, meaning);
|
||||
}
|
||||
break;
|
||||
}
|
||||
memberFlags = location.flags;
|
||||
location = location.parent;
|
||||
}
|
||||
copySymbols(globals, meaning);
|
||||
populateSymbols();
|
||||
|
||||
return mapToArray(symbols);
|
||||
|
||||
function populateSymbols() {
|
||||
while (location) {
|
||||
if (location.locals && !isGlobalSourceFile(location)) {
|
||||
if (copySymbols(location.locals, meaning)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (location.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
if (!isExternalModule(<SourceFile>location)) {
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if (copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
if (copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.EnumMember)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
if (!(memberFlags & NodeFlags.Static)) {
|
||||
if (copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.FunctionExpression:
|
||||
if ((<FunctionExpression>location).name) {
|
||||
if (copySymbol(location.symbol, meaning)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
memberFlags = location.flags;
|
||||
location = location.parent;
|
||||
}
|
||||
if (copySymbols(globals, meaning)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns 'true' if we should stop processing symbols.
|
||||
function copySymbol(symbol: Symbol, meaning: SymbolFlags): boolean {
|
||||
if (symbol.flags & meaning) {
|
||||
let id = symbol.name;
|
||||
if (!isReservedMemberName(id) && !hasProperty(symbols, id)) {
|
||||
if (predicate) {
|
||||
// If we were supplied a predicate function, then check if this symbol
|
||||
// matches with it. If so, we're done and can immediately return.
|
||||
// Otherwise, just ignore this symbol and keep going.
|
||||
if (predicate(symbol)) {
|
||||
symbols[id] = symbol;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If no predicate was supplied, then just add the symbol as is.
|
||||
symbols[id] = symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function copySymbols(source: SymbolTable, meaning: SymbolFlags): boolean {
|
||||
if (meaning) {
|
||||
for (let id in source) {
|
||||
if (hasProperty(source, id)) {
|
||||
if (copySymbol(source[id], meaning)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isTypeDeclarationName(name: Node): boolean {
|
||||
|
||||
@@ -1092,7 +1092,10 @@ module ts {
|
||||
getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
|
||||
getIndexTypeOfType(type: Type, kind: IndexKind): Type;
|
||||
getReturnTypeOfSignature(signature: Signature): Type;
|
||||
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
|
||||
|
||||
// If 'predicate' is supplied, then only the first symbol in scope matching the predicate
|
||||
// will be returned. Otherwise, all symbols in scope will be returned.
|
||||
getSymbolsInScope(location: Node, meaning: SymbolFlags, predicate?: (symbol: Symbol) => boolean): Symbol[];
|
||||
getSymbolAtLocation(node: Node): Symbol;
|
||||
getShorthandAssignmentValueSymbol(location: Node): Symbol;
|
||||
getTypeAtLocation(node: Node): Type;
|
||||
|
||||
+43
-32
@@ -2421,7 +2421,6 @@ module ts {
|
||||
isValid = isIdentifierPart(displayName.charCodeAt(i), target);
|
||||
}
|
||||
|
||||
|
||||
if (isValid) {
|
||||
return unescapeIdentifier(displayName);
|
||||
}
|
||||
@@ -2450,7 +2449,21 @@ module ts {
|
||||
};
|
||||
}
|
||||
|
||||
function getCompletionSymbols(fileName: string, position: number): { symbols: Symbol[], isMemberCompletion: boolean, isNewIdentifierLocation: boolean, location: Node }{
|
||||
function getCompletionSymbols(fileName: string, position: number, symbolName?: string): { symbols: Symbol[], isMemberCompletion: boolean, isNewIdentifierLocation: boolean, location: Node } {
|
||||
let result = getCompletionSymbolsWorker(fileName, position, symbolName);
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (result.symbols && symbolName) {
|
||||
var target = program.getCompilerOptions().target;
|
||||
result.symbols = filter(result.symbols, s => getValidCompletionEntryDisplayName(s, target) === symbolName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getCompletionSymbolsWorker(fileName: string, position: number, symbolName: string): { symbols: Symbol[], isMemberCompletion: boolean, isNewIdentifierLocation: boolean, location: Node }{
|
||||
let syntacticStart = new Date().getTime();
|
||||
let sourceFile = getValidSourceFile(fileName);
|
||||
|
||||
@@ -2502,7 +2515,8 @@ module ts {
|
||||
}
|
||||
|
||||
let location = getTouchingPropertyName(sourceFile, position);
|
||||
// Populate the completion list
|
||||
var target = program.getCompilerOptions().target;
|
||||
|
||||
let semanticStart = new Date().getTime();
|
||||
let isMemberCompletion: boolean;
|
||||
let isNewIdentifierLocation: boolean;
|
||||
@@ -2580,7 +2594,12 @@ module ts {
|
||||
/// TODO filter meaning based on the current context
|
||||
let scopeNode = getScopeNode(previousToken, position, sourceFile);
|
||||
let symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Alias;
|
||||
symbols = typeInfoResolver.getSymbolsInScope(scopeNode, symbolMeanings);
|
||||
|
||||
// Filter down to the symbol that matches the symbolName if we were given one.
|
||||
let predicate: (s: Symbol) => boolean = symbolName
|
||||
? s => getValidCompletionEntryDisplayName(s, target) === symbolName
|
||||
: undefined;
|
||||
symbols = typeInfoResolver.getSymbolsInScope(scopeNode, symbolMeanings, predicate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2938,37 +2957,27 @@ module ts {
|
||||
}
|
||||
|
||||
function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
|
||||
// Note: No need to call synchronizeHostData, as we have captured all the data we need
|
||||
// in the getCompletionsAtPosition earlier
|
||||
let sourceFile = getValidSourceFile(fileName);
|
||||
synchronizeHostData();
|
||||
|
||||
let session = activeCompletionSession;
|
||||
|
||||
// Ensure that the current active completion session is still valid for this request
|
||||
if (!session || session.fileName !== fileName || session.position !== position) {
|
||||
return undefined;
|
||||
// Look up a completion symbol with this name.
|
||||
let result = getCompletionSymbols(fileName, position, entryName);
|
||||
if (result) {
|
||||
let { symbols, isMemberCompletion, isNewIdentifierLocation, location } = result;
|
||||
if (symbols && symbols.length > 0) {
|
||||
let symbol = symbols[0];
|
||||
let displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getValidSourceFile(fileName), location, typeInfoResolver, location, SemanticMeaning.All);
|
||||
return {
|
||||
name: entryName,
|
||||
kind: displayPartsDocumentationsAndSymbolKind.symbolKind,
|
||||
kindModifiers: getSymbolModifiers(symbol),
|
||||
displayParts: displayPartsDocumentationsAndSymbolKind.displayParts,
|
||||
documentation: displayPartsDocumentationsAndSymbolKind.documentation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let symbol = lookUp(activeCompletionSession.symbols, escapeIdentifier(entryName));
|
||||
if (symbol) {
|
||||
let location = getTouchingPropertyName(sourceFile, position);
|
||||
let completionEntry = createCompletionEntry(symbol, session.typeChecker, location);
|
||||
// TODO(drosen): Right now we just permit *all* semantic meanings when calling 'getSymbolKind'
|
||||
// which is permissible given that it is backwards compatible; but really we should consider
|
||||
// passing the meaning for the node so that we don't report that a suggestion for a value is an interface.
|
||||
// We COULD also just do what 'getSymbolModifiers' does, which is to use the first declaration.
|
||||
Debug.assert(session.typeChecker.getTypeOfSymbolAtLocation(symbol, location) !== undefined, "Could not find type for symbol");
|
||||
let displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getValidSourceFile(fileName), location, session.typeChecker, location, SemanticMeaning.All);
|
||||
return {
|
||||
name: entryName,
|
||||
kind: displayPartsDocumentationsAndSymbolKind.symbolKind,
|
||||
kindModifiers: completionEntry.kindModifiers,
|
||||
displayParts: displayPartsDocumentationsAndSymbolKind.displayParts,
|
||||
documentation: displayPartsDocumentationsAndSymbolKind.documentation
|
||||
};
|
||||
}
|
||||
else {
|
||||
// No symbol, it is a keyword
|
||||
let keywordCompletion = filter(keywordCompletions, c => c.name === entryName);
|
||||
if (keywordCompletion) {
|
||||
return {
|
||||
name: entryName,
|
||||
kind: ScriptElementKind.keyword,
|
||||
@@ -2977,6 +2986,8 @@ module ts {
|
||||
documentation: undefined
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// TODO(drosen): use contextual SemanticMeaning.
|
||||
|
||||
Reference in New Issue
Block a user