Skip to content

Commit 988d2d3

Browse files
committed
Adds quick info on modifiers and declaration keywords
1 parent 64a0dd4 commit 988d2d3

File tree

8 files changed

+118
-30
lines changed

8 files changed

+118
-30
lines changed

src/compiler/binder.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ module ts {
166166
else {
167167
symbol = createSymbol(0, "__missing");
168168
}
169+
if (!node.name) {
170+
symbol.isAnonymous = true;
171+
}
169172
addDeclarationToSymbol(symbol, node, includes);
170173
symbol.parent = parent;
171174

@@ -385,6 +388,9 @@ module ts {
385388

386389
function bindAnonymousDeclaration(node: Declaration, symbolKind: SymbolFlags, name: string, isBlockScopeContainer: boolean) {
387390
let symbol = createSymbol(symbolKind, name);
391+
if (!node.name) {
392+
symbol.isAnonymous = true;
393+
}
388394
addDeclarationToSymbol(symbol, node, symbolKind);
389395
bindChildren(node, symbolKind, isBlockScopeContainer);
390396
}
@@ -424,7 +430,7 @@ module ts {
424430

425431
function bind(node: Node) {
426432
node.parent = parent;
427-
433+
428434
switch (node.kind) {
429435
case SyntaxKind.TypeParameter:
430436
bindDeclaration(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false);

src/compiler/checker.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ module ts {
5757
getReturnTypeOfSignature,
5858
getSymbolsInScope,
5959
getSymbolAtLocation,
60+
getSymbolFromDeclarationKeyword,
61+
getContextualSignature,
6062
getShorthandAssignmentValueSymbol,
6163
getTypeAtLocation,
6264
typeToString,
@@ -11378,6 +11380,33 @@ module ts {
1137811380
return undefined;
1137911381
}
1138011382

11383+
function getSymbolFromDeclarationKeyword(node: Node): Symbol {
11384+
switch(node.kind) {
11385+
case SyntaxKind.ClassKeyword:
11386+
case SyntaxKind.EnumKeyword:
11387+
case SyntaxKind.FunctionKeyword:
11388+
case SyntaxKind.InterfaceKeyword:
11389+
case SyntaxKind.ModuleKeyword:
11390+
case SyntaxKind.NamespaceKeyword:
11391+
if (node.parent.symbol) {
11392+
return node.parent.symbol;
11393+
}
11394+
return undefined;
11395+
11396+
case SyntaxKind.ConstKeyword:
11397+
case SyntaxKind.LetKeyword:
11398+
case SyntaxKind.VarKeyword:
11399+
if (node.parent.kind === SyntaxKind.VariableDeclarationList) {
11400+
// We only want to show quick info for declaration list of length 1 and on a non-binding pattern.
11401+
if ((<VariableDeclarationList>node.parent).declarations.length === 1 &&
11402+
(<VariableDeclarationList>node.parent).declarations[0].name.kind === SyntaxKind.Identifier) {
11403+
return getSymbolOfEntityNameOrPropertyAccessExpression(<Identifier>(<VariableDeclarationList>node.parent).declarations[0].name);
11404+
}
11405+
}
11406+
return undefined;
11407+
}
11408+
}
11409+
1138111410
function getSymbolInfo(node: Node) {
1138211411
if (isInsideWithStatementBody(node)) {
1138311412
// We cannot answer semantic questions within a with block, do not proceed any further

src/compiler/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,11 +1148,13 @@ module ts {
11481148

11491149
getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
11501150
getSymbolAtLocation(node: Node): Symbol;
1151+
getSymbolFromDeclarationKeyword(node: Node): Symbol;
11511152
getShorthandAssignmentValueSymbol(location: Node): Symbol;
11521153
getTypeAtLocation(node: Node): Type;
11531154
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
11541155
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
11551156
getSymbolDisplayBuilder(): SymbolDisplayBuilder;
1157+
getContextualSignature(node: FunctionExpression | MethodDeclaration): Signature;
11561158
getFullyQualifiedName(symbol: Symbol): string;
11571159
getAugmentedPropertiesOfType(type: Type): Symbol[];
11581160
getRootSymbols(symbol: Symbol): Symbol[];
@@ -1377,6 +1379,7 @@ module ts {
13771379
/* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
13781380
valueDeclaration?: Declaration; // First value declaration of the symbol
13791381
/* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums
1382+
isAnonymous?: boolean; // True if declaration is anonymous
13801383
}
13811384

13821385
/* @internal */
@@ -1455,7 +1458,7 @@ module ts {
14551458
/* @internal */
14561459
ContainsUndefinedOrNull = 0x00040000, // Type is or contains Undefined or Null type
14571460
/* @internal */
1458-
ContainsObjectLiteral = 0x00080000, // Type is or contains object literal type
1461+
ContainsObjectLiteral = 0x00080000, // Type is or contains object literal type
14591462
ESSymbol = 0x00100000, // Type of symbol primitive introduced in ES6
14601463

14611464
/* @internal */

src/compiler/utilities.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -917,8 +917,8 @@ module ts {
917917
return false;
918918
}
919919

920-
export function isStatement(n: Node): boolean {
921-
switch (n.kind) {
920+
export function isStatement(node: Node): boolean {
921+
switch (node.kind) {
922922
case SyntaxKind.BreakStatement:
923923
case SyntaxKind.ContinueStatement:
924924
case SyntaxKind.DebuggerStatement:
@@ -944,8 +944,8 @@ module ts {
944944
}
945945
}
946946

947-
export function isClassElement(n: Node): boolean {
948-
switch (n.kind) {
947+
export function isClassElement(node: Node): boolean {
948+
switch (node.kind) {
949949
case SyntaxKind.Constructor:
950950
case SyntaxKind.PropertyDeclaration:
951951
case SyntaxKind.MethodDeclaration:
@@ -959,6 +959,30 @@ module ts {
959959
}
960960
}
961961

962+
export function isHeritageKeyword(node: Node): boolean {
963+
return node.kind === SyntaxKind.ImplementsKeyword || node.kind === SyntaxKind.ExtendsKeyword;
964+
}
965+
966+
export function isDeclarationKeyword(node: Node): boolean {
967+
switch (node.kind) {
968+
case SyntaxKind.ClassKeyword:
969+
case SyntaxKind.ConstKeyword:
970+
case SyntaxKind.DefaultKeyword:
971+
case SyntaxKind.EnumKeyword:
972+
case SyntaxKind.ExtendsKeyword:
973+
case SyntaxKind.FunctionKeyword:
974+
case SyntaxKind.InterfaceKeyword:
975+
case SyntaxKind.ImplementsKeyword:
976+
case SyntaxKind.LetKeyword:
977+
case SyntaxKind.ModuleKeyword:
978+
case SyntaxKind.NamespaceKeyword:
979+
case SyntaxKind.SetKeyword:
980+
case SyntaxKind.VarKeyword:
981+
return true;
982+
}
983+
return false;
984+
}
985+
962986
// True if the given identifier, string literal, or number literal is the name of a declaration node
963987
export function isDeclarationName(name: Node): boolean {
964988
if (name.kind !== SyntaxKind.Identifier && name.kind !== SyntaxKind.StringLiteral && name.kind !== SyntaxKind.NumericLiteral) {

src/services/formatting/formatting.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ module ts.formatting {
603603
return inheritedIndentation;
604604
}
605605

606-
if (isToken(child)) {
606+
if (isReservedWord(child)) {
607607
// if child node is a token, it does not impact indentation, proceed it using parent indentation scope rules
608608
let tokenInfo = formattingScanner.readTokenInfo(child);
609609
Debug.assert(tokenInfo.token.end === child.end);

src/services/formatting/formattingScanner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ module ts.formatting {
234234
// then kind needs to be fixed. This might happen in cases
235235
// when parser interprets token differently, i.e keyword treated as identifier
236236
function fixTokenKind(tokenInfo: TokenInfo, container: Node): TokenInfo {
237-
if (isToken(container) && tokenInfo.token.kind !== container.kind) {
237+
if (isReservedWord(container) && tokenInfo.token.kind !== container.kind) {
238238
tokenInfo.token.kind = container.kind;
239239
}
240240
return tokenInfo;

src/services/services.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,6 @@ module ts {
197197
let list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, NodeFlags.Synthetic, this);
198198
list._children = [];
199199
let pos = nodes.pos;
200-
201-
202200

203201
for (let node of nodes) {
204202
if (pos < node.pos) {
@@ -3672,7 +3670,7 @@ module ts {
36723670
addFullSymbolName(symbol);
36733671
writeTypeParametersOfSymbol(symbol, sourceFile);
36743672
}
3675-
if ((symbolFlags & SymbolFlags.Interface) && (semanticMeaning & SemanticMeaning.Type)) {
3673+
if (symbolFlags & SymbolFlags.Interface) {
36763674
addNewLineIfDisplayPartsExist();
36773675
displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword));
36783676
displayParts.push(spacePart());
@@ -3839,7 +3837,9 @@ module ts {
38393837
if (symbolKind) {
38403838
pushTypePart(symbolKind);
38413839
displayParts.push(spacePart());
3842-
addFullSymbolName(symbol);
3840+
if (!symbol.isAnonymous) {
3841+
addFullSymbolName(symbol);
3842+
}
38433843
}
38443844
}
38453845

@@ -3852,6 +3852,9 @@ module ts {
38523852
case ScriptElementKind.constructorImplementationElement:
38533853
displayParts.push(textOrKeywordPart(symbolKind));
38543854
return;
3855+
case ScriptElementKind.localFunctionElement:
3856+
displayParts.push(textOrKeywordPart(ScriptElementKind.functionElement));
3857+
return;
38553858
default:
38563859
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
38573860
displayParts.push(textOrKeywordPart(symbolKind));
@@ -3882,11 +3885,22 @@ module ts {
38823885
}
38833886
}
38843887

3888+
function getQuickInfoNodeBySkippingModifiersAndHeritageKeywords(sourceFile: SourceFile, position: number, beignWithLastTokenPath = false): Node {
3889+
let node = getTouchingPropertyName(sourceFile, position, beignWithLastTokenPath);
3890+
if (!node) {
3891+
return undefined;
3892+
}
3893+
else if (isModifier(node.kind) || isHeritageKeyword(node)) {
3894+
node = getQuickInfoNodeBySkippingModifiersAndHeritageKeywords(sourceFile, node.end + 1, /*beignWithLastTokenPath*/ true);
3895+
}
3896+
return node;
3897+
}
3898+
38853899
function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {
38863900
synchronizeHostData();
38873901

38883902
let sourceFile = getValidSourceFile(fileName);
3889-
let node = getTouchingPropertyName(sourceFile, position);
3903+
let node = getQuickInfoNodeBySkippingModifiersAndHeritageKeywords(sourceFile, position);
38903904
if (!node) {
38913905
return undefined;
38923906
}
@@ -3897,6 +3911,9 @@ module ts {
38973911

38983912
let typeChecker = program.getTypeChecker();
38993913
let symbol = typeChecker.getSymbolAtLocation(node);
3914+
if(!symbol && isDeclarationKeyword(node)) {
3915+
symbol = typeChecker.getSymbolFromDeclarationKeyword(node);
3916+
}
39003917

39013918
if (!symbol) {
39023919
// Try getting just type at this position and show
@@ -3906,14 +3923,15 @@ module ts {
39063923
case SyntaxKind.QualifiedName:
39073924
case SyntaxKind.ThisKeyword:
39083925
case SyntaxKind.SuperKeyword:
3909-
// For the identifiers/this/super etc get the type at position
3926+
case SyntaxKind.FunctionKeyword:
39103927
let type = typeChecker.getTypeAtLocation(node);
39113928
if (type) {
3929+
let displayParts = typeToDisplayParts(typeChecker, type, getContainerNode(node));
39123930
return {
39133931
kind: ScriptElementKind.unknown,
39143932
kindModifiers: ScriptElementKindModifier.none,
39153933
textSpan: createTextSpan(node.getStart(), node.getWidth()),
3916-
displayParts: typeToDisplayParts(typeChecker, type, getContainerNode(node)),
3934+
displayParts: displayParts,
39173935
documentation: type.symbol ? type.symbol.getDocumentationComment() : undefined
39183936
};
39193937
}
@@ -6228,7 +6246,7 @@ module ts {
62286246
if (textSpanIntersectsWith(span, element.getFullStart(), element.getFullWidth())) {
62296247
let children = element.getChildren();
62306248
for (let child of children) {
6231-
if (isToken(child)) {
6249+
if (isReservedWord(child)) {
62326250
classifyToken(child);
62336251
}
62346252
else {

src/services/utilities.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -266,37 +266,45 @@ module ts {
266266
/* Gets the token whose text has range [start, end) and position >= start
267267
* and (position < end or (position === end && token is keyword or identifier or numeric\string litera))
268268
*/
269-
export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node {
270-
return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind));
269+
export function getTouchingPropertyName(sourceFile: SourceFile, position: number, beignWithLastTokenPath = false): Node {
270+
return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind), /*beignWithLastTokenPath*/ beignWithLastTokenPath);
271271
}
272272

273273
/** Returns the token if position is in [start, end) or if position === end and includeItemAtEndPosition(token) === true */
274-
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean): Node {
275-
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition);
274+
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean, beignWithLastTokenPath = false): Node {
275+
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition, /*beignWithLastTokenPath*/ beignWithLastTokenPath);
276276
}
277277

278278
/** Returns a token if position is in [start-of-leading-trivia, end) */
279279
export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node {
280280
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined);
281281
}
282+
283+
export let lastTokenPath: { [depth: number]: number } = {};
282284

283285
/** Get the token whose text contains the position */
284-
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean): Node {
286+
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean, beignWithLastTokenPath = false): Node {
285287
let current: Node = sourceFile;
288+
let depthLevel = 0;
289+
if(!beignWithLastTokenPath) {
290+
lastTokenPath = {};
291+
}
286292
outer: while (true) {
287-
if (isToken(current)) {
288-
// exit early
293+
if (isReservedWord(current)) {
289294
return current;
290295
}
291-
292-
// find the child that contains 'position'
296+
297+
// Find the child that contains 'position' and it will begin with the old token path if specified.
298+
let start = beignWithLastTokenPath && lastTokenPath[depthLevel] ? lastTokenPath[depthLevel] : 0;
293299
for (let i = 0, n = current.getChildCount(sourceFile); i < n; i++) {
294300
let child = current.getChildAt(i);
295301
let start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile);
296302
if (start <= position) {
297303
let end = child.getEnd();
298304
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
299305
current = child;
306+
lastTokenPath[depthLevel] = i;
307+
depthLevel++;
300308
continue outer;
301309
}
302310
else if (includeItemAtEndPosition && end === position) {
@@ -323,7 +331,7 @@ module ts {
323331
// Ideally, getTokenAtPosition should return a token. However, it is currently
324332
// broken, so we do a check to make sure the result was indeed a token.
325333
let tokenAtPosition = getTokenAtPosition(file, position);
326-
if (isToken(tokenAtPosition) && position > tokenAtPosition.getStart(file) && position < tokenAtPosition.getEnd()) {
334+
if (isReservedWord(tokenAtPosition) && position > tokenAtPosition.getStart(file) && position < tokenAtPosition.getEnd()) {
327335
return tokenAtPosition;
328336
}
329337

@@ -334,7 +342,7 @@ module ts {
334342
return find(parent);
335343

336344
function find(n: Node): Node {
337-
if (isToken(n) && n.pos === previousToken.end) {
345+
if (isReservedWord(n) && n.pos === previousToken.end) {
338346
// this is token that starts at the end of previous token - return it
339347
return n;
340348
}
@@ -360,7 +368,7 @@ module ts {
360368
return find(startNode || sourceFile);
361369

362370
function findRightmostToken(n: Node): Node {
363-
if (isToken(n)) {
371+
if (isReservedWord(n)) {
364372
return n;
365373
}
366374

@@ -371,7 +379,7 @@ module ts {
371379
}
372380

373381
function find(n: Node): Node {
374-
if (isToken(n)) {
382+
if (isReservedWord(n)) {
375383
return n;
376384
}
377385

@@ -447,7 +455,7 @@ module ts {
447455
return undefined;
448456
}
449457

450-
export function isToken(n: Node): boolean {
458+
export function isReservedWord(n: Node): boolean {
451459
return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken;
452460
}
453461

0 commit comments

Comments
 (0)