-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Completion list show correct entry for function expression and class expression #3643
Changes from 4 commits
aab2100
9c9e298
34489fa
5467e1d
45182b8
553085f
389e446
744f640
514d054
605ab0b
7747ad4
3079607
2e0c390
06c9876
6da98ce
7c52aaa
b01e4d8
f4cd1ac
0148915
872fdcf
b5b1b7b
d0b8002
41bedd2
d0d1ee9
f16f9d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12170,20 +12170,20 @@ namespace ts { | |
case SyntaxKind.EnumDeclaration: | ||
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.EnumMember); | ||
break; | ||
case SyntaxKind.ClassExpression: | ||
if ((<ClassExpression>location).name) { | ||
copySymbol(location.symbol, meaning); | ||
} | ||
// Fall through | ||
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); | ||
case SyntaxKind.ClassExpression: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. class expression listed twice There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uh oh. It'd be good to have a feature that catches this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JsonFreeman it's being tracked in #2854 |
||
let name = (<FunctionExpression|ClassExpression>location).name; | ||
if (name) { | ||
let symbol = location.symbol; | ||
if (symbol.flags & meaning && !hasProperty(symbols, name.text)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i would add a comment why we are not using copySymbol.. cause i will forget :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a good point :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🌵 |
||
symbols[name.text] = symbol; | ||
} | ||
} | ||
break; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1423,6 +1423,9 @@ namespace ts { | |
// class X {} | ||
export const classElement = "class"; | ||
|
||
// var x = class X {} | ||
export const localClassElement = "local class"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we have a separate tag for this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be consistent with function expression which has kind of "local function":cactus: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure why we do it for either one then. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i agree. let's take them both out. |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note. you need to handle this on the managed side as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What you mean be handling in the managed side? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably best to discuss this in person with someone but I believe the idea is that VS needs to handle styles differently depending on what comes in to the managed side. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you see in navbar when you do this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🌵 file a bug |
||
// interface Y {} | ||
export const interfaceElement = "interface"; | ||
|
||
|
@@ -2807,6 +2810,22 @@ namespace ts { | |
} | ||
} | ||
|
||
// Special case for function expression and class expression because despite sometimes having a name, the binder | ||
// binds them to a symbol with the name "__function" and "__class" respectively. However, for completion entry, we want | ||
// to display its declared name rather than "__function" and "__class". | ||
// var x = function foo () { | ||
// fo$ <- completion list should contain local name "foo" | ||
// } | ||
// foo$ <- completion list should not contain "foo" | ||
if (displayName === "__function" || displayName === "__class") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Look into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @DanielRosenwasser should have a fix for this in a pending review. you should synchronize. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah right, it's in my PR. Speaking of which, can you guys review it? 😃 #3367There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should be clear to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🌹 yeah |
||
displayName = symbol.declarations[0].name.getText(); | ||
|
||
// At this point, we expect that all completion list entries have declared name including function expression | ||
// because when we gather all relevant symbols, we check that the function expression must have declared name | ||
// before adding the symbol into our symbols table. (see: getSymbolsInScope) | ||
Debug.assert(displayName !== undefined,"Expected this function expression to have declared name"); | ||
} | ||
|
||
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)) { | ||
|
@@ -3552,7 +3571,8 @@ namespace ts { | |
function getSymbolKind(symbol: Symbol, location: Node): string { | ||
let flags = symbol.getFlags(); | ||
|
||
if (flags & SymbolFlags.Class) return ScriptElementKind.classElement; | ||
if (flags & SymbolFlags.Class) return symbol.declarations[0].kind === SyntaxKind.ClassExpression ? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use getDeclarationOfKind There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🌵 |
||
ScriptElementKind.localClassElement : ScriptElementKind.classElement; | ||
if (flags & SymbolFlags.Enum) return ScriptElementKind.enumElement; | ||
if (flags & SymbolFlags.TypeAlias) return ScriptElementKind.typeElement; | ||
if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement; | ||
|
@@ -3761,7 +3781,16 @@ namespace ts { | |
} | ||
} | ||
if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo) { | ||
displayParts.push(keywordPart(SyntaxKind.ClassKeyword)); | ||
// Special case for class expressions because we would like to indicate that | ||
// the class name is local to the class body (similar to function expression) | ||
// (local class) class <className> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move into the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🌵 |
||
if (symbol.getName() === "__class") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🌵 |
||
pushTypePart(ScriptElementKind.localClassElement); | ||
} | ||
else { | ||
// Class declaration has name which is not local. | ||
displayParts.push(keywordPart(SyntaxKind.ClassKeyword)); | ||
} | ||
displayParts.push(spacePart()); | ||
addFullSymbolName(symbol); | ||
writeTypeParametersOfSymbol(symbol, sourceFile); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
///<reference path="fourslash.ts" /> | ||
|
||
//// var x = class myClass { | ||
//// getClassName (){ | ||
//// m/*0*/ | ||
//// } | ||
//// /*1*/ | ||
//// } | ||
|
||
goTo.marker("0"); | ||
verify.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); | ||
|
||
goTo.marker("1"); | ||
verify.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
///<reference path="fourslash.ts" /> | ||
|
||
//// class myClass { /*0*/ } | ||
//// /*1*/ | ||
//// var x = class myClass { | ||
//// getClassName (){ | ||
//// m/*2*/ | ||
//// } | ||
//// /*3*/ | ||
//// } | ||
//// var y = class { | ||
//// getSomeName() { | ||
//// /*4*/ | ||
//// } | ||
//// /*5*/ | ||
//// } | ||
|
||
goTo.marker("0"); | ||
verify.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); | ||
|
||
goTo.marker("1"); | ||
verify.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); | ||
|
||
goTo.marker("2"); | ||
verify.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); | ||
|
||
goTo.marker("3"); | ||
verify.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); | ||
|
||
goTo.marker("4"); | ||
verify.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); | ||
|
||
goTo.marker("5"); | ||
verify.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
///<reference path="fourslash.ts" /> | ||
|
||
//// var x = function foo() { | ||
//// /*1*/ | ||
//// } | ||
|
||
goTo.marker("1"); | ||
verify.completionListContains("foo", "(local function) foo(): void", /*documentation*/ undefined, "local function"); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
///<reference path="fourslash.ts" /> | ||
|
||
//// function foo() {} | ||
//// /*0*/ | ||
//// var x = function foo() { | ||
//// /*1*/ | ||
//// } | ||
//// var y = function () { | ||
//// /*2*/ | ||
//// } | ||
|
||
goTo.marker("0"); | ||
verify.completionListContains("foo", "function foo(): void", /*documentation*/ undefined, "function"); | ||
|
||
goTo.marker("1"); | ||
verify.completionListContains("foo", "(local function) foo(): void", /*documentation*/ undefined, "local function"); | ||
|
||
goTo.marker("2"); | ||
verify.completionListContains("foo", "function foo(): void", /*documentation*/ undefined, "function") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does line do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which line ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the logic below, GitHub doesn't show it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @JsonFreeman for explaining this. This is the logic so that if we didn't come from static member of class or interface, add the type parameters into the symbol table (type-parameters of class Declaration and interface are in member property of the symbol.
Note: that the memberFlags come from previous iteration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also note that for type aliases and signatures, the type parameters are in the locals, but for classes and interfaces, the type parameters are in the members.