mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #2475 from Microsoft/completionEntryDetails
Share code between getCompletionsAtPosition and getCompletionEntryDetails.
This commit is contained in:
+51
-38
@@ -10587,7 +10587,55 @@ module ts {
|
||||
function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] {
|
||||
let symbols: SymbolTable = {};
|
||||
let memberFlags: NodeFlags = 0;
|
||||
function copySymbol(symbol: Symbol, meaning: SymbolFlags) {
|
||||
|
||||
if (isInsideWithStatementBody(location)) {
|
||||
// We cannot answer semantic questions within a with block, do not proceed any further
|
||||
return [];
|
||||
}
|
||||
|
||||
populateSymbols();
|
||||
|
||||
return mapToArray(symbols);
|
||||
|
||||
function populateSymbols() {
|
||||
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);
|
||||
}
|
||||
|
||||
// Returns 'true' if we should stop processing symbols.
|
||||
function copySymbol(symbol: Symbol, meaning: SymbolFlags): void {
|
||||
if (symbol.flags & meaning) {
|
||||
let id = symbol.name;
|
||||
if (!isReservedMemberName(id) && !hasProperty(symbols, id)) {
|
||||
@@ -10595,7 +10643,8 @@ module ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
function copySymbols(source: SymbolTable, meaning: SymbolFlags) {
|
||||
|
||||
function copySymbols(source: SymbolTable, meaning: SymbolFlags): void {
|
||||
if (meaning) {
|
||||
for (let id in source) {
|
||||
if (hasProperty(source, id)) {
|
||||
@@ -10604,42 +10653,6 @@ module ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
return mapToArray(symbols);
|
||||
}
|
||||
|
||||
function isTypeDeclarationName(name: Node): boolean {
|
||||
|
||||
@@ -256,6 +256,11 @@ module ts {
|
||||
return tokenStrings[t];
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function stringToToken(s: string): SyntaxKind {
|
||||
return textToToken[s];
|
||||
}
|
||||
|
||||
export function computeLineStarts(text: string): number[] {
|
||||
let result: number[] = new Array();
|
||||
let pos = 0;
|
||||
|
||||
@@ -1092,6 +1092,9 @@ module ts {
|
||||
getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
|
||||
getIndexTypeOfType(type: Type, kind: IndexKind): Type;
|
||||
getReturnTypeOfSignature(signature: Signature): Type;
|
||||
|
||||
// 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): Symbol[];
|
||||
getSymbolAtLocation(node: Node): Symbol;
|
||||
getShorthandAssignmentValueSymbol(location: Node): Symbol;
|
||||
|
||||
+146
-130
@@ -418,7 +418,10 @@ module ts {
|
||||
|
||||
function pushDocCommentLineText(docComments: SymbolDisplayPart[], text: string, blankLineCount: number) {
|
||||
// Add the empty lines in between texts
|
||||
while (blankLineCount--) docComments.push(textPart(""));
|
||||
while (blankLineCount--) {
|
||||
docComments.push(textPart(""));
|
||||
}
|
||||
|
||||
docComments.push(textPart(text));
|
||||
}
|
||||
|
||||
@@ -1415,14 +1418,6 @@ module ts {
|
||||
|
||||
/// Language Service
|
||||
|
||||
interface CompletionSession {
|
||||
fileName: string; // the file where the completion was requested
|
||||
position: number; // position in the file where the completion was requested
|
||||
entries: CompletionEntry[]; // entries for this completion
|
||||
symbols: Map<Symbol>; // symbols by entry name map
|
||||
typeChecker: TypeChecker; // the typeChecker used to generate this completion
|
||||
}
|
||||
|
||||
interface FormattingOptions {
|
||||
useTabs: boolean;
|
||||
spacesPerTab: number;
|
||||
@@ -2186,7 +2181,6 @@ module ts {
|
||||
let typeInfoResolver: TypeChecker;
|
||||
let useCaseSensitivefileNames = false;
|
||||
let cancellationToken = new CancellationTokenObject(host.getCancellationToken && host.getCancellationToken());
|
||||
let activeCompletionSession: CompletionSession; // The current active completion session, used to get the completion entry details
|
||||
|
||||
// Check if the localized messages json is set, otherwise query the host for it
|
||||
if (!localizedDiagnosticMessages && host.getLocalizedDiagnosticMessages) {
|
||||
@@ -2404,43 +2398,51 @@ module ts {
|
||||
}
|
||||
|
||||
/// Completion
|
||||
function getValidCompletionEntryDisplayName(symbol: Symbol, target: ScriptTarget): string {
|
||||
function getCompletionEntryDisplayName(symbol: Symbol, target: ScriptTarget, performCharacterChecks: boolean): string {
|
||||
let displayName = symbol.getName();
|
||||
if (displayName && displayName.length > 0) {
|
||||
let firstCharCode = displayName.charCodeAt(0);
|
||||
// First check of the displayName is not external module; if it is an external module, it is not valid entry
|
||||
if ((symbol.flags & SymbolFlags.Namespace) && (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote)) {
|
||||
// If the symbol is external module, don't show it in the completion list
|
||||
// (i.e declare module "http" { let x; } | // <= request completion here, "http" should not be there)
|
||||
if (!displayName) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let firstCharCode = displayName.charCodeAt(0);
|
||||
// First check of the displayName is not external module; if it is an external module, it is not valid entry
|
||||
if ((symbol.flags & SymbolFlags.Namespace) && (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote)) {
|
||||
// If the symbol is external module, don't show it in the completion list
|
||||
// (i.e declare module "http" { let x; } | // <= request completion here, "http" should not be there)
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (displayName && displayName.length >= 2 && firstCharCode === displayName.charCodeAt(displayName.length - 1) &&
|
||||
(firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote)) {
|
||||
// If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an
|
||||
// invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name.
|
||||
displayName = displayName.substring(1, displayName.length - 1);
|
||||
}
|
||||
|
||||
if (!displayName) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (performCharacterChecks) {
|
||||
if (!isIdentifierStart(displayName.charCodeAt(0), target)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (displayName && displayName.length >= 2 && firstCharCode === displayName.charCodeAt(displayName.length - 1) &&
|
||||
(firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote)) {
|
||||
// If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an
|
||||
// invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name.
|
||||
displayName = displayName.substring(1, displayName.length - 1);
|
||||
}
|
||||
|
||||
let isValid = isIdentifierStart(displayName.charCodeAt(0), target);
|
||||
for (let i = 1, n = displayName.length; isValid && i < n; i++) {
|
||||
isValid = isIdentifierPart(displayName.charCodeAt(i), target);
|
||||
}
|
||||
|
||||
|
||||
if (isValid) {
|
||||
return unescapeIdentifier(displayName);
|
||||
for (let i = 1, n = displayName.length; i < n; i++) {
|
||||
if (!isIdentifierPart(displayName.charCodeAt(i), target)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return unescapeIdentifier(displayName);
|
||||
}
|
||||
|
||||
function createCompletionEntry(symbol: Symbol, typeChecker: TypeChecker, location: Node): CompletionEntry {
|
||||
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
|
||||
// We would like to only show things that can be added after a dot, so for instance numeric properties can
|
||||
// not be accessed with a dot (a.1 <- invalid)
|
||||
let displayName = getValidCompletionEntryDisplayName(symbol, program.getCompilerOptions().target);
|
||||
let displayName = getCompletionEntryDisplayName(symbol, program.getCompilerOptions().target, /*performCharacterChecks:*/ true);
|
||||
if (!displayName) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -2456,20 +2458,18 @@ module ts {
|
||||
};
|
||||
}
|
||||
|
||||
function getCompletionsAtPosition(fileName: string, position: number) {
|
||||
synchronizeHostData();
|
||||
|
||||
function getCompletionData(fileName: string, position: number) {
|
||||
let syntacticStart = new Date().getTime();
|
||||
let sourceFile = getValidSourceFile(fileName);
|
||||
|
||||
let start = new Date().getTime();
|
||||
let currentToken = getTokenAtPosition(sourceFile, position);
|
||||
log("getCompletionsAtPosition: Get current token: " + (new Date().getTime() - start));
|
||||
log("getCompletionData: Get current token: " + (new Date().getTime() - start));
|
||||
|
||||
start = new Date().getTime();
|
||||
// Completion not allowed inside comments, bail out if this is the case
|
||||
let insideComment = isInsideComment(sourceFile, currentToken, position);
|
||||
log("getCompletionsAtPosition: Is inside comment: " + (new Date().getTime() - start));
|
||||
log("getCompletionData: Is inside comment: " + (new Date().getTime() - start));
|
||||
|
||||
if (insideComment) {
|
||||
log("Returning an empty list because completion was inside a comment.");
|
||||
@@ -2480,14 +2480,14 @@ module ts {
|
||||
// Note: previousToken can be undefined if we are the beginning of the file
|
||||
start = new Date().getTime();
|
||||
let previousToken = findPrecedingToken(position, sourceFile);
|
||||
log("getCompletionsAtPosition: Get previous token 1: " + (new Date().getTime() - start));
|
||||
log("getCompletionData: Get previous token 1: " + (new Date().getTime() - start));
|
||||
|
||||
// The caret is at the end of an identifier; this is a partial identifier that we want to complete: e.g. a.toS|
|
||||
// Skip this partial identifier to the previous token
|
||||
if (previousToken && position <= previousToken.end && previousToken.kind === SyntaxKind.Identifier) {
|
||||
let start = new Date().getTime();
|
||||
previousToken = findPrecedingToken(previousToken.pos, sourceFile);
|
||||
log("getCompletionsAtPosition: Get previous token 2: " + (new Date().getTime() - start));
|
||||
log("getCompletionData: Get previous token 2: " + (new Date().getTime() - start));
|
||||
}
|
||||
|
||||
// Check if this is a valid completion location
|
||||
@@ -2498,8 +2498,8 @@ module ts {
|
||||
|
||||
// Find the node where completion is requested on, in the case of a completion after a dot, it is the member access expression
|
||||
// other wise, it is a request for all visible symbols in the scope, and the node is the current location
|
||||
let node: Node;
|
||||
let isRightOfDot: boolean;
|
||||
let node = currentToken;
|
||||
let isRightOfDot = false;
|
||||
if (previousToken && previousToken.kind === SyntaxKind.DotToken && previousToken.parent.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
node = (<PropertyAccessExpression>previousToken.parent).expression;
|
||||
isRightOfDot = true;
|
||||
@@ -2508,30 +2508,18 @@ module ts {
|
||||
node = (<QualifiedName>previousToken.parent).left;
|
||||
isRightOfDot = true;
|
||||
}
|
||||
else {
|
||||
node = currentToken;
|
||||
isRightOfDot = false;
|
||||
}
|
||||
|
||||
// Clear the current activeCompletionSession for this session
|
||||
activeCompletionSession = {
|
||||
fileName: fileName,
|
||||
position: position,
|
||||
entries: [],
|
||||
symbols: {},
|
||||
typeChecker: typeInfoResolver
|
||||
};
|
||||
log("getCompletionsAtPosition: Syntactic work: " + (new Date().getTime() - syntacticStart));
|
||||
|
||||
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;
|
||||
let symbols: Symbol[];
|
||||
|
||||
if (isRightOfDot) {
|
||||
// Right of dot member completion list
|
||||
let symbols: Symbol[] = [];
|
||||
symbols = [];
|
||||
isMemberCompletion = true;
|
||||
isNewIdentifierLocation = false;
|
||||
|
||||
@@ -2562,8 +2550,6 @@ module ts {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getCompletionEntriesFromSymbols(symbols, activeCompletionSession);
|
||||
}
|
||||
else {
|
||||
let containingObjectLiteral = getContainingObjectLiteralApplicableForCompletion(previousToken);
|
||||
@@ -2580,8 +2566,7 @@ module ts {
|
||||
let contextualTypeMembers = typeInfoResolver.getPropertiesOfType(contextualType);
|
||||
if (contextualTypeMembers && contextualTypeMembers.length > 0) {
|
||||
// Add filtered items to the completion list
|
||||
let filteredMembers = filterContextualMembersList(contextualTypeMembers, containingObjectLiteral.properties);
|
||||
getCompletionEntriesFromSymbols(filteredMembers, activeCompletionSession);
|
||||
symbols = filterContextualMembersList(contextualTypeMembers, containingObjectLiteral.properties);
|
||||
}
|
||||
}
|
||||
else if (getAncestor(previousToken, SyntaxKind.ImportClause)) {
|
||||
@@ -2593,8 +2578,7 @@ module ts {
|
||||
let importDeclaration = <ImportDeclaration>getAncestor(previousToken, SyntaxKind.ImportDeclaration);
|
||||
Debug.assert(importDeclaration !== undefined);
|
||||
let exports = typeInfoResolver.getExportsOfExternalModule(importDeclaration);
|
||||
let filteredExports = filterModuleExports(exports, importDeclaration);
|
||||
getCompletionEntriesFromSymbols(filteredExports, activeCompletionSession);
|
||||
symbols = filterModuleExports(exports, importDeclaration);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -2605,24 +2589,14 @@ 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;
|
||||
let symbols = typeInfoResolver.getSymbolsInScope(scopeNode, symbolMeanings);
|
||||
|
||||
getCompletionEntriesFromSymbols(symbols, activeCompletionSession);
|
||||
|
||||
symbols = typeInfoResolver.getSymbolsInScope(scopeNode, symbolMeanings);
|
||||
}
|
||||
}
|
||||
|
||||
// Add keywords if this is not a member completion list
|
||||
if (!isMemberCompletion) {
|
||||
Array.prototype.push.apply(activeCompletionSession.entries, keywordCompletions);
|
||||
}
|
||||
log("getCompletionsAtPosition: Semantic work: " + (new Date().getTime() - semanticStart));
|
||||
log("getCompletionData: Semantic work: " + (new Date().getTime() - semanticStart));
|
||||
|
||||
return {
|
||||
isMemberCompletion,
|
||||
isNewIdentifierLocation,
|
||||
isBuilder: isNewIdentifierDefinitionLocation, // temporary property used to match VS implementation
|
||||
entries: activeCompletionSession.entries
|
||||
};
|
||||
return { symbols, isMemberCompletion, isNewIdentifierLocation, location };
|
||||
|
||||
/**
|
||||
* Finds the first node that "embraces" the position, so that one may
|
||||
@@ -2636,21 +2610,6 @@ module ts {
|
||||
return scope;
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromSymbols(symbols: Symbol[], session: CompletionSession): void {
|
||||
let start = new Date().getTime();
|
||||
forEach(symbols, symbol => {
|
||||
let entry = createCompletionEntry(symbol, session.typeChecker, location);
|
||||
if (entry) {
|
||||
let id = escapeIdentifier(entry.name);
|
||||
if (!lookUp(session.symbols, id)) {
|
||||
session.entries.push(entry);
|
||||
session.symbols[id] = symbol;
|
||||
}
|
||||
}
|
||||
});
|
||||
log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (new Date().getTime() - start));
|
||||
}
|
||||
|
||||
function isCompletionListBlocker(previousToken: Node): boolean {
|
||||
let start = new Date().getTime();
|
||||
let result = isInStringOrRegularExpressionOrTemplateLiteral(previousToken) ||
|
||||
@@ -2704,7 +2663,7 @@ module ts {
|
||||
|
||||
case SyntaxKind.EqualsToken:
|
||||
return containingNodeKind === SyntaxKind.VariableDeclaration // let x = a|
|
||||
|| containingNodeKind === SyntaxKind.BinaryExpression; // x = a|
|
||||
|| containingNodeKind === SyntaxKind.BinaryExpression; // x = a|
|
||||
|
||||
case SyntaxKind.TemplateHead:
|
||||
return containingNodeKind === SyntaxKind.TemplateExpression; // `aa ${|
|
||||
@@ -2821,7 +2780,7 @@ module ts {
|
||||
case SyntaxKind.SemicolonToken:
|
||||
return containingNodeKind === SyntaxKind.PropertySignature &&
|
||||
(previousToken.parent.parent.kind === SyntaxKind.InterfaceDeclaration || // interface a { f; |
|
||||
previousToken.parent.parent.kind === SyntaxKind.TypeLiteral); // let x : { a; |
|
||||
previousToken.parent.parent.kind === SyntaxKind.TypeLiteral); // let x : { a; |
|
||||
|
||||
case SyntaxKind.LessThanToken:
|
||||
return containingNodeKind === SyntaxKind.ClassDeclaration || // class A< |
|
||||
@@ -2937,38 +2896,78 @@ 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);
|
||||
|
||||
let session = activeCompletionSession;
|
||||
|
||||
// Ensure that the current active completion session is still valid for this request
|
||||
if (!session || session.fileName !== fileName || session.position !== position) {
|
||||
function getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
|
||||
synchronizeHostData();
|
||||
|
||||
let completionData = getCompletionData(fileName, position);
|
||||
if (!completionData) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
let { symbols, isMemberCompletion, isNewIdentifierLocation, location } = completionData;
|
||||
if (!symbols || symbols.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
// No symbol, it is a keyword
|
||||
|
||||
var entries = getCompletionEntriesFromSymbols(symbols);
|
||||
|
||||
// Add keywords if this is not a member completion list
|
||||
if (!isMemberCompletion) {
|
||||
addRange(entries, keywordCompletions);
|
||||
}
|
||||
|
||||
return { isMemberCompletion, isNewIdentifierLocation, entries };
|
||||
|
||||
function getCompletionEntriesFromSymbols(symbols: Symbol[]): CompletionEntry[] {
|
||||
let start = new Date().getTime();
|
||||
var entries: CompletionEntry[] = [];
|
||||
var nameToSymbol: Map<Symbol> = {};
|
||||
|
||||
for (let symbol of symbols) {
|
||||
let entry = createCompletionEntry(symbol, typeInfoResolver, location);
|
||||
if (entry) {
|
||||
let id = escapeIdentifier(entry.name);
|
||||
if (!lookUp(nameToSymbol, id)) {
|
||||
entries.push(entry);
|
||||
nameToSymbol[id] = symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (new Date().getTime() - start));
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
|
||||
synchronizeHostData();
|
||||
|
||||
// Compute all the completion symbols again.
|
||||
let completionData = getCompletionData(fileName, position);
|
||||
if (completionData) {
|
||||
let { symbols, location } = completionData;
|
||||
|
||||
// Find the symbol with the matching entry name.
|
||||
let target = program.getCompilerOptions().target;
|
||||
// We don't need to perform character checks here because we're only comparing the
|
||||
// name against 'entryName' (which is known to be good), not building a new
|
||||
// completion entry.
|
||||
let symbol = forEach(symbols, s => getCompletionEntryDisplayName(s, target, /*performCharacterChecks:*/ false) === entryName ? s : undefined);
|
||||
|
||||
if (symbol) {
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find a symbol with this name. See if we can find a keyword instead.
|
||||
let keywordCompletion = forEach(keywordCompletions, c => c.name === entryName);
|
||||
if (keywordCompletion) {
|
||||
return {
|
||||
name: entryName,
|
||||
kind: ScriptElementKind.keyword,
|
||||
@@ -2977,6 +2976,8 @@ module ts {
|
||||
documentation: undefined
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// TODO(drosen): use contextual SemanticMeaning.
|
||||
@@ -3075,12 +3076,14 @@ module ts {
|
||||
typeResolver: TypeChecker, location: Node,
|
||||
// TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location
|
||||
semanticMeaning = getMeaningFromLocation(location)) {
|
||||
|
||||
let displayParts: SymbolDisplayPart[] = [];
|
||||
let documentation: SymbolDisplayPart[];
|
||||
let symbolFlags = symbol.flags;
|
||||
let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(symbol, symbolFlags, typeResolver, location);
|
||||
let hasAddedSymbolInfo: boolean;
|
||||
let type: Type;
|
||||
|
||||
// Class at constructor site need to be shown as constructor apart from property,method, vars
|
||||
if (symbolKind !== ScriptElementKind.unknown || symbolFlags & SymbolFlags.Class || symbolFlags & SymbolFlags.Alias) {
|
||||
// If it is accessor they are allowed only if location is at name of the accessor
|
||||
@@ -3132,9 +3135,7 @@ module ts {
|
||||
}
|
||||
else if (symbolFlags & SymbolFlags.Alias) {
|
||||
symbolKind = ScriptElementKind.alias;
|
||||
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
displayParts.push(textPart(symbolKind));
|
||||
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
pushTypePart(symbolKind);
|
||||
displayParts.push(spacePart());
|
||||
if (useConstructSignatures) {
|
||||
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
|
||||
@@ -3370,14 +3371,29 @@ module ts {
|
||||
function addPrefixForAnyFunctionOrVar(symbol: Symbol, symbolKind: string) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
if (symbolKind) {
|
||||
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
displayParts.push(textPart(symbolKind));
|
||||
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
pushTypePart(symbolKind);
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
function pushTypePart(symbolKind: string) {
|
||||
switch (symbolKind) {
|
||||
case ScriptElementKind.variableElement:
|
||||
case ScriptElementKind.functionElement:
|
||||
case ScriptElementKind.letElement:
|
||||
case ScriptElementKind.constElement:
|
||||
case ScriptElementKind.constructorImplementationElement:
|
||||
displayParts.push(textOrKeywordPart(symbolKind));
|
||||
return;
|
||||
default:
|
||||
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
|
||||
displayParts.push(textOrKeywordPart(symbolKind));
|
||||
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function addSignatureDisplayParts(signature: Signature, allSignatures: Signature[], flags?: TypeFormatFlags) {
|
||||
displayParts.push.apply(displayParts, signatureToDisplayParts(typeResolver, signature, enclosingDeclaration, flags | TypeFormatFlags.WriteTypeArgumentsOfSignature));
|
||||
if (allSignatures.length > 1) {
|
||||
|
||||
@@ -611,6 +611,13 @@ module ts {
|
||||
return displayPart(tokenToString(kind), SymbolDisplayPartKind.operator);
|
||||
}
|
||||
|
||||
export function textOrKeywordPart(text: string) {
|
||||
var kind = stringToToken(text);
|
||||
return kind === undefined
|
||||
? textPart(text)
|
||||
: keywordPart(kind);
|
||||
}
|
||||
|
||||
export function textPart(text: string) {
|
||||
return displayPart(text, SymbolDisplayPartKind.text);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user