From 5bdee45300486ab3facc55b3f82b5e508345a7e3 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Thu, 9 Dec 2021 19:51:32 -0800
Subject: [PATCH 1/6] Overhaul detection of JSX attributes and tag names
---
src/services/completions.ts | 2 +-
src/services/symbolDisplay.ts | 64 ++++++++++++++++-
src/services/types.ts | 2 +
src/services/utilities.ts | 10 +--
.../reference/api/tsserverlibrary.d.ts | 1 +
tests/baselines/reference/api/typescript.d.ts | 1 +
.../jsxTagNameDottedAttributeSnippet.ts | 69 +++++++++++++++++++
.../jsxTagNameDottedAttributeSnippetClosed.ts | 68 ++++++++++++++++++
.../fourslash/jsxTagNameDottedNoSnippet.ts | 53 ++++++++++++++
.../jsxTagNameDottedNoSnippetClosed.ts | 53 ++++++++++++++
tests/cases/fourslash/jsxTagNameNoSnippet.ts | 37 ++++++++++
.../cases/fourslash/tsxFindAllReferences11.ts | 2 +-
.../cases/fourslash/tsxFindAllReferences6.ts | 2 +-
13 files changed, 353 insertions(+), 11 deletions(-)
create mode 100644 tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts
create mode 100644 tests/cases/fourslash/jsxTagNameDottedAttributeSnippetClosed.ts
create mode 100644 tests/cases/fourslash/jsxTagNameDottedNoSnippet.ts
create mode 100644 tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts
create mode 100644 tests/cases/fourslash/jsxTagNameNoSnippet.ts
diff --git a/src/services/completions.ts b/src/services/completions.ts
index ca265f268f035..53f059b1e77eb 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -739,7 +739,7 @@ namespace ts.Completions {
}
}
- const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, location);
+ const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, location, contextToken);
if (kind === ScriptElementKind.jsxAttribute && preferences.includeCompletionsWithSnippetText && preferences.jsxAttributeCompletionStyle && preferences.jsxAttributeCompletionStyle !== "none") {
let useBraces = preferences.jsxAttributeCompletionStyle === "braces";
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, location);
diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts
index f1447b30cd9b5..3151f4ce8eb59 100644
--- a/src/services/symbolDisplay.ts
+++ b/src/services/symbolDisplay.ts
@@ -3,8 +3,8 @@ namespace ts.SymbolDisplay {
const symbolDisplayNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
// TODO(drosen): use contextual SemanticMeaning.
- export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind {
- const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location);
+ export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node, contextToken?: Node): ScriptElementKind {
+ const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location, contextToken);
if (result !== ScriptElementKind.unknown) {
return result;
}
@@ -25,7 +25,7 @@ namespace ts.SymbolDisplay {
return result;
}
- function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind {
+ function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, location: Node, contextToken?: Node): ScriptElementKind {
const roots = typeChecker.getRootSymbols(symbol);
// If this is a method from a mapped type, leave as a method so long as it still has a call signature.
if (roots.length === 1
@@ -83,6 +83,20 @@ namespace ts.SymbolDisplay {
}
return unionPropertyKind;
}
+
+ if (contextToken) {
+ const result = getSymbolKindOfJsxTagNameOrAttribute(location, contextToken);
+ if (result !== ScriptElementKind.unknown) {
+ return result;
+ }
+ }
+
+ if (isJsxAttribute(location) || location.parent && isJsxAttribute(location.parent) && location.parent.name === location) {
+ return ScriptElementKind.jsxAttribute;
+ }
+
+ // TODO(jakebailey): Delete the below code, once the edge cases it handles are handled above.
+
// If we requested completions after `x.` at the top-level, we may be at a source file location.
switch (location.parent && location.parent.kind) {
// If we've typed a character of the attribute name, will be 'JsxAttribute', else will be 'JsxOpeningElement'.
@@ -100,6 +114,50 @@ namespace ts.SymbolDisplay {
return ScriptElementKind.unknown;
}
+ function getSymbolKindOfJsxTagNameOrAttribute(location: Node, contextToken: Node): ScriptElementKind {
+ const symbolKindFromContext = forEachAncestor(contextToken, (n) => {
+ if (isJsxAttributeLike(n)) {
+ return ScriptElementKind.jsxAttribute;
+ }
+
+ if (isJsxFragment(n) || isJsxOpeningFragment(n) || isJsxClosingFragment(n)) {
+ return "quit";
+ }
+
+ if (isJsxOpeningElement(n) || isJsxSelfClosingElement(n) || isJsxClosingElement(n)) {
+ if (contextToken.getEnd() <= n.tagName.getFullStart()) {
+ // Definitely completing part of the tag name.
+ return ScriptElementKind.jsxTagName;
+ }
+
+ if (rangeContainsRange(n.tagName, contextToken)) {
+ // We are to the right of the tag name, as the context is there.
+ // figure out where we are based on where the location is.
+
+ // TODO(jakebailey): This seems hacky.
+ if (contextToken.kind === SyntaxKind.DotToken || contextToken.kind === SyntaxKind.QuestionDotToken) {
+ // Unfinished dotted tag name.
+ return ScriptElementKind.jsxTagName;
+ }
+
+ if (!rangeContainsRange(n, location)) {
+ // Unclosed JSX element; location is entirely outside the element.
+ return ScriptElementKind.jsxAttribute;
+ }
+
+ if (n.tagName.getEnd() <= location.getFullStart()) {
+ // After existing attributes, so is another attribute.
+ return ScriptElementKind.jsxAttribute;
+ }
+ }
+
+ return "quit";
+ }
+ });
+
+ return symbolKindFromContext || ScriptElementKind.unknown;
+ }
+
function getNormalizedSymbolModifiers(symbol: Symbol) {
if (symbol.declarations && symbol.declarations.length) {
const [declaration, ...declarations] = symbol.declarations;
diff --git a/src/services/types.ts b/src/services/types.ts
index b9bd9f2177d5b..7df40b22c8349 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -1457,6 +1457,8 @@ namespace ts {
/**
*
*/
+ jsxTagName = "JSX tag name",
+
jsxAttribute = "JSX attribute",
/** String literal */
diff --git a/src/services/utilities.ts b/src/services/utilities.ts
index 593cbc73913f7..ec8b7f95df903 100644
--- a/src/services/utilities.ts
+++ b/src/services/utilities.ts
@@ -1106,16 +1106,16 @@ namespace ts {
* If position === end, returns the preceding token if includeItemAtEndPosition(previousToken) === true
*/
export function getTouchingToken(sourceFile: SourceFile, position: number, includePrecedingTokenAtEndPosition?: (n: Node) => boolean): Node {
- return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includePrecedingTokenAtEndPosition, /*includeEndPosition*/ false);
+ return getTokenAtPositionWorker(sourceFile, position, () => false, includePrecedingTokenAtEndPosition, /*includeEndPosition*/ false);
}
/** Returns a token if position is in [start-of-leading-trivia, end) */
export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node {
- return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includePrecedingTokenAtEndPosition*/ undefined, /*includeEndPosition*/ false);
+ return getTokenAtPositionWorker(sourceFile, position, () => true, /*includePrecedingTokenAtEndPosition*/ undefined, /*includeEndPosition*/ false);
}
/** Get the token whose text contains the position */
- function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includePrecedingTokenAtEndPosition: ((n: Node) => boolean) | undefined, includeEndPosition: boolean): Node {
+ function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: (n: Node) => boolean, includePrecedingTokenAtEndPosition: ((n: Node) => boolean) | undefined, includeEndPosition: boolean): Node {
let current: Node = sourceFile;
let foundToken: Node | undefined;
outer: while (true) {
@@ -1145,7 +1145,7 @@ namespace ts {
// position and whose end is greater than the position.
- const start = allowPositionInLeadingTrivia ? children[middle].getFullStart() : children[middle].getStart(sourceFile, /*includeJsDoc*/ true);
+ const start = allowPositionInLeadingTrivia(children[middle]) ? children[middle].getFullStart() : children[middle].getStart(sourceFile, /*includeJsDoc*/ true);
if (start > position) {
return Comparison.GreaterThan;
}
@@ -1180,7 +1180,7 @@ namespace ts {
}
function nodeContainsPosition(node: Node) {
- const start = allowPositionInLeadingTrivia ? node.getFullStart() : node.getStart(sourceFile, /*includeJsDoc*/ true);
+ const start = allowPositionInLeadingTrivia(node) ? node.getFullStart() : node.getStart(sourceFile, /*includeJsDoc*/ true);
if (start > position) {
// If this child begins after position, then all subsequent children will as well.
return false;
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index 4df9e6328ecbd..b620c68b3cce1 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -6594,6 +6594,7 @@ declare namespace ts {
/**
*
*/
+ jsxTagName = "JSX tag name",
jsxAttribute = "JSX attribute",
/** String literal */
string = "string",
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index fdd55dab1f2d9..11d25bd2ab52d 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -6594,6 +6594,7 @@ declare namespace ts {
/**
*
*/
+ jsxTagName = "JSX tag name",
jsxAttribute = "JSX attribute",
/** String literal */
string = "string",
diff --git a/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts b/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts
new file mode 100644
index 0000000000000..e94e05d1684a0
--- /dev/null
+++ b/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts
@@ -0,0 +1,69 @@
+///
+//@Filename: file.tsx
+////interface NestedInterface {
+//// Foo: NestedInterface;
+//// (props: {className?: string}): any;
+////}
+////
+////declare const Foo: NestedInterface;
+////
+////function fn1() {
+//// return
+////
+////}
+////function fn2() {
+//// return
+////
+////}
+////function fn3() {
+//// return
+////
+////}
+////function fn4() {
+//// return
+////
+////}
+////function fn5() {
+//// return
+////
+////}
+////function fn6() {
+//// return
+////
+////}
+////function fn7() {
+//// return
+//@Filename: file.tsx
+////interface NestedInterface {
+//// Foo: NestedInterface;
+//// (props: {className?: string}): any;
+////}
+////
+////declare const Foo: NestedInterface;
+////
+////function fn1() {
+//// return
+////
+////
+////}
+////function fn2() {
+//// return
+////
+////
+////}
+////function fn3() {
+//// return
+////
+////
+////}
+////function fn4() {
+//// return
+////
+////
+////}
+////function fn5() {
+//// return
+////
+////
+////}
+////function fn6() {
+//// return
+////
+////
+////}
+////function fn7() {
+//// return
+////}
+////function fn8() {
+//// return
+////}
+////function fn9() {
+//// return
+////}
+////function fn10() {
+//// return
+////}
+////function fn11() {
+//// return
+////}
+
+verify.completions(
+ {
+ marker: test.markers(),
+ includes: [
+ { name: "className", insertText: 'className={$1}', isSnippet: true, sortText: completion.SortText.OptionalMember }
+ ],
+ preferences: {
+ jsxAttributeCompletionStyle: "braces",
+ includeCompletionsWithSnippetText: true,
+ includeCompletionsWithInsertText: true,
+ },
+ }
+)
diff --git a/tests/cases/fourslash/jsxTagNameDottedNoSnippet.ts b/tests/cases/fourslash/jsxTagNameDottedNoSnippet.ts
new file mode 100644
index 0000000000000..6b9b589a1bf41
--- /dev/null
+++ b/tests/cases/fourslash/jsxTagNameDottedNoSnippet.ts
@@ -0,0 +1,53 @@
+///
+//@Filename: file.tsx
+////interface NestedInterface {
+//// Foo: NestedInterface;
+//// (props: {}): any;
+////}
+////
+////declare const Foo: NestedInterface;
+////
+////function fn1() {
+//// return
+//// *1*/
+////
+////}
+////function fn2() {
+//// return
+////
+////}
+////function fn3() {
+//// return
+////
+////}
+////function fn4() {
+//// return
+////
+////}
+////function fn5() {
+//// return
+////
+////}
+////function fn6() {
+//// return
+////
+////}
+
+verify.completions(
+ {
+ marker: test.markers(),
+ includes: [
+ { name: "Foo", insertText: undefined, isSnippet: undefined }
+ ],
+ preferences: {
+ jsxAttributeCompletionStyle: "braces",
+ includeCompletionsWithSnippetText: true,
+ includeCompletionsWithInsertText: true,
+ },
+ }
+)
diff --git a/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts b/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts
new file mode 100644
index 0000000000000..6d1af353626b7
--- /dev/null
+++ b/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts
@@ -0,0 +1,53 @@
+///
+//@Filename: file.tsx
+////interface NestedInterface {
+//// Foo: NestedInterface;
+//// (props: {}): any;
+////}
+////
+////declare const Foo: NestedInterface;
+////
+////function fn1() {
+//// return
+//// *1*/ />
+////
+////}
+////function fn2() {
+//// return
+////
+////
+////}
+////function fn3() {
+//// return
+////
+////
+////}
+////function fn4() {
+//// return
+////
+////
+////}
+////function fn5() {
+//// return
+////
+////
+////}
+////function fn6() {
+//// return
+////
+////
+////}
+
+verify.completions(
+ {
+ marker: test.markers(),
+ includes: [
+ { name: "Foo", insertText: undefined, isSnippet: undefined }
+ ],
+ preferences: {
+ jsxAttributeCompletionStyle: "braces",
+ includeCompletionsWithSnippetText: true,
+ includeCompletionsWithInsertText: true,
+ },
+ }
+)
diff --git a/tests/cases/fourslash/jsxTagNameNoSnippet.ts b/tests/cases/fourslash/jsxTagNameNoSnippet.ts
new file mode 100644
index 0000000000000..0f6c07b0d87a4
--- /dev/null
+++ b/tests/cases/fourslash/jsxTagNameNoSnippet.ts
@@ -0,0 +1,37 @@
+///
+//@Filename: file.tsx
+////declare namespace JSX {
+//// interface IntrinsicElements {
+//// button: any;
+//// div: any;
+//// }
+////}
+////function fn() {
+//// return <>
+//// ;
+////}
+////function fn2() {
+//// return <>
+//// preceding junk ;
+////}
+////function fn3() {
+//// return <>
+//// ;
+////}
+
+verify.completions(
+ {
+ marker: test.markers(),
+ includes: [
+ { name: "button", insertText: undefined, isSnippet: undefined }
+ ],
+ preferences: {
+ jsxAttributeCompletionStyle: "braces",
+ includeCompletionsWithSnippetText: true,
+ includeCompletionsWithInsertText: true,
+ }
+ }
+);
diff --git a/tests/cases/fourslash/tsxFindAllReferences11.ts b/tests/cases/fourslash/tsxFindAllReferences11.ts
index 8c7e7a4690937..b965b87d8f831 100644
--- a/tests/cases/fourslash/tsxFindAllReferences11.ts
+++ b/tests/cases/fourslash/tsxFindAllReferences11.ts
@@ -25,4 +25,4 @@
//// declare function MainButton(props: ButtonProps | LinkProps): JSX.Element;
//// let opt = ;
-verify.singleReferenceGroup("(property) wrong: true");
+verify.singleReferenceGroup("(JSX attribute) wrong: true");
diff --git a/tests/cases/fourslash/tsxFindAllReferences6.ts b/tests/cases/fourslash/tsxFindAllReferences6.ts
index c0e119a4e456e..8d54ddd9537e1 100644
--- a/tests/cases/fourslash/tsxFindAllReferences6.ts
+++ b/tests/cases/fourslash/tsxFindAllReferences6.ts
@@ -19,4 +19,4 @@
//// declare function Opt(attributes: OptionPropBag): JSX.Element;
//// let opt = ;
-verify.singleReferenceGroup("(property) wrong: true");
+verify.singleReferenceGroup("(JSX attribute) wrong: true");
From 6a7c27b883f41dbb4ea14742539c935cb2ec7931 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Tue, 11 Jan 2022 23:53:07 -0800
Subject: [PATCH 2/6] Undo leftover change
---
src/services/utilities.ts | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/services/utilities.ts b/src/services/utilities.ts
index ec8b7f95df903..593cbc73913f7 100644
--- a/src/services/utilities.ts
+++ b/src/services/utilities.ts
@@ -1106,16 +1106,16 @@ namespace ts {
* If position === end, returns the preceding token if includeItemAtEndPosition(previousToken) === true
*/
export function getTouchingToken(sourceFile: SourceFile, position: number, includePrecedingTokenAtEndPosition?: (n: Node) => boolean): Node {
- return getTokenAtPositionWorker(sourceFile, position, () => false, includePrecedingTokenAtEndPosition, /*includeEndPosition*/ false);
+ return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includePrecedingTokenAtEndPosition, /*includeEndPosition*/ false);
}
/** Returns a token if position is in [start-of-leading-trivia, end) */
export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node {
- return getTokenAtPositionWorker(sourceFile, position, () => true, /*includePrecedingTokenAtEndPosition*/ undefined, /*includeEndPosition*/ false);
+ return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includePrecedingTokenAtEndPosition*/ undefined, /*includeEndPosition*/ false);
}
/** Get the token whose text contains the position */
- function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: (n: Node) => boolean, includePrecedingTokenAtEndPosition: ((n: Node) => boolean) | undefined, includeEndPosition: boolean): Node {
+ function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includePrecedingTokenAtEndPosition: ((n: Node) => boolean) | undefined, includeEndPosition: boolean): Node {
let current: Node = sourceFile;
let foundToken: Node | undefined;
outer: while (true) {
@@ -1145,7 +1145,7 @@ namespace ts {
// position and whose end is greater than the position.
- const start = allowPositionInLeadingTrivia(children[middle]) ? children[middle].getFullStart() : children[middle].getStart(sourceFile, /*includeJsDoc*/ true);
+ const start = allowPositionInLeadingTrivia ? children[middle].getFullStart() : children[middle].getStart(sourceFile, /*includeJsDoc*/ true);
if (start > position) {
return Comparison.GreaterThan;
}
@@ -1180,7 +1180,7 @@ namespace ts {
}
function nodeContainsPosition(node: Node) {
- const start = allowPositionInLeadingTrivia(node) ? node.getFullStart() : node.getStart(sourceFile, /*includeJsDoc*/ true);
+ const start = allowPositionInLeadingTrivia ? node.getFullStart() : node.getStart(sourceFile, /*includeJsDoc*/ true);
if (start > position) {
// If this child begins after position, then all subsequent children will as well.
return false;
From 2d0a7a3bfb49a33e761742fde99c4d3508860c8c Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Wed, 12 Jan 2022 10:43:11 -0800
Subject: [PATCH 3/6] Fix symbol display in other places
---
src/services/completions.ts | 6 ++---
src/services/symbolDisplay.ts | 8 ++++---
.../jsxTagNameDottedAttributeSnippet.ts | 9 ++++++--
.../jsxTagNameDottedAttributeSnippetClosed.ts | 8 ++++++-
.../fourslash/jsxTagNameDottedNoSnippet.ts | 23 ++++++++++---------
.../jsxTagNameDottedNoSnippetClosed.ts | 23 ++++++++++---------
tests/cases/fourslash/jsxTagNameNoSnippet.ts | 22 ++++++++----------
7 files changed, 56 insertions(+), 43 deletions(-)
diff --git a/src/services/completions.ts b/src/services/completions.ts
index 53f059b1e77eb..ce031739fd010 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -1402,7 +1402,7 @@ namespace ts.Completions {
case "symbol": {
const { symbol, location, contextToken, origin, previousToken } = symbolCompletion;
const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(name, location, contextToken, origin, symbol, program, host, compilerOptions, sourceFile, position, previousToken, formatContext, preferences, data, source);
- return createCompletionDetailsForSymbol(symbol, typeChecker, sourceFile, location, cancellationToken, codeActions, sourceDisplay); // TODO: GH#18217
+ return createCompletionDetailsForSymbol(symbol, typeChecker, sourceFile, location, cancellationToken, codeActions, sourceDisplay, contextToken); // TODO: GH#18217
}
case "literal": {
const { literal } = symbolCompletion;
@@ -1420,10 +1420,10 @@ namespace ts.Completions {
return createCompletionDetails(name, ScriptElementKindModifier.none, kind, [displayPart(name, kind2)]);
}
- export function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails {
+ export function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[], contextToken?: Node): CompletionEntryDetails {
const { displayParts, documentation, symbolKind, tags } =
checker.runWithCancellationToken(cancellationToken, checker =>
- SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All)
+ SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All, /*alias*/ undefined, contextToken)
);
return createCompletionDetails(symbol.name, SymbolDisplay.getSymbolModifiers(checker, symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay);
}
diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts
index 3151f4ce8eb59..7ae1d6367601b 100644
--- a/src/services/symbolDisplay.ts
+++ b/src/services/symbolDisplay.ts
@@ -202,12 +202,12 @@ namespace ts.SymbolDisplay {
// TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location
export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: TypeChecker, symbol: Symbol, sourceFile: SourceFile, enclosingDeclaration: Node | undefined,
- location: Node, semanticMeaning = getMeaningFromLocation(location), alias?: Symbol): SymbolDisplayPartsDocumentationAndSymbolKind {
+ location: Node, semanticMeaning = getMeaningFromLocation(location), alias?: Symbol, contextToken?: Node): SymbolDisplayPartsDocumentationAndSymbolKind {
const displayParts: SymbolDisplayPart[] = [];
let documentation: SymbolDisplayPart[] = [];
let tags: JSDocTagInfo[] = [];
const symbolFlags = getCombinedLocalAndExportSymbolFlags(symbol);
- let symbolKind = semanticMeaning & SemanticMeaning.Value ? getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location) : ScriptElementKind.unknown;
+ let symbolKind = semanticMeaning & SemanticMeaning.Value ? getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location, contextToken) : ScriptElementKind.unknown;
let hasAddedSymbolInfo = false;
const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isInExpressionContext(location);
let type: Type | undefined;
@@ -289,6 +289,7 @@ namespace ts.SymbolDisplay {
switch (symbolKind) {
case ScriptElementKind.jsxAttribute:
+ case ScriptElementKind.jsxTagName:
case ScriptElementKind.memberVariableElement:
case ScriptElementKind.variableElement:
case ScriptElementKind.constElement:
@@ -561,6 +562,7 @@ namespace ts.SymbolDisplay {
// For properties, variables and local vars: show the type
if (symbolKind === ScriptElementKind.memberVariableElement ||
symbolKind === ScriptElementKind.jsxAttribute ||
+ symbolKind === ScriptElementKind.jsxTagName ||
symbolFlags & SymbolFlags.Variable ||
symbolKind === ScriptElementKind.localVariableElement ||
isThisExpression) {
@@ -601,7 +603,7 @@ namespace ts.SymbolDisplay {
}
}
else {
- symbolKind = getSymbolKind(typeChecker, symbol, location);
+ symbolKind = getSymbolKind(typeChecker, symbol, location, contextToken);
}
}
diff --git a/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts b/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts
index e94e05d1684a0..1264ccf79ad5f 100644
--- a/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts
+++ b/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts
@@ -53,12 +53,17 @@
//// return
////}
+var preferences: FourSlashInterface.UserPreferences = {
+ jsxAttributeCompletionStyle: "braces",
+ includeCompletionsWithSnippetText: true,
+ includeCompletionsWithInsertText: true,
+};
+
verify.completions(
- {
- marker: test.markers(),
- includes: [
- { name: "Foo", insertText: undefined, isSnippet: undefined }
- ],
- preferences: {
- jsxAttributeCompletionStyle: "braces",
- includeCompletionsWithSnippetText: true,
- includeCompletionsWithInsertText: true,
- },
- }
+ { marker: "1", preferences, includes: { name: "Foo", text: "const Foo: NestedInterface" } },
+ { marker: "2", preferences, includes: { name: "Foo", text: "const Foo: NestedInterface" } },
+ { marker: "3", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
+ { marker: "4", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
+ { marker: "5", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
+ { marker: "6", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
)
diff --git a/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts b/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts
index 6d1af353626b7..fed65eb73f618 100644
--- a/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts
+++ b/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts
@@ -38,16 +38,17 @@
////
////}
+var preferences: FourSlashInterface.UserPreferences = {
+ jsxAttributeCompletionStyle: "braces",
+ includeCompletionsWithSnippetText: true,
+ includeCompletionsWithInsertText: true,
+};
+
verify.completions(
- {
- marker: test.markers(),
- includes: [
- { name: "Foo", insertText: undefined, isSnippet: undefined }
- ],
- preferences: {
- jsxAttributeCompletionStyle: "braces",
- includeCompletionsWithSnippetText: true,
- includeCompletionsWithInsertText: true,
- },
- }
+ { marker: "1", preferences, includes: { name: "Foo", text: "const Foo: NestedInterface" } },
+ { marker: "2", preferences, includes: { name: "Foo", text: "const Foo: NestedInterface" } },
+ { marker: "3", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
+ { marker: "4", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
+ { marker: "5", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
+ { marker: "6", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
)
diff --git a/tests/cases/fourslash/jsxTagNameNoSnippet.ts b/tests/cases/fourslash/jsxTagNameNoSnippet.ts
index 0f6c07b0d87a4..c79b359b1eb30 100644
--- a/tests/cases/fourslash/jsxTagNameNoSnippet.ts
+++ b/tests/cases/fourslash/jsxTagNameNoSnippet.ts
@@ -22,16 +22,14 @@
//// >;
////}
+var preferences: FourSlashInterface.UserPreferences = {
+ jsxAttributeCompletionStyle: "braces",
+ includeCompletionsWithSnippetText: true,
+ includeCompletionsWithInsertText: true,
+};
+
verify.completions(
- {
- marker: test.markers(),
- includes: [
- { name: "button", insertText: undefined, isSnippet: undefined }
- ],
- preferences: {
- jsxAttributeCompletionStyle: "braces",
- includeCompletionsWithSnippetText: true,
- includeCompletionsWithInsertText: true,
- }
- }
-);
+ { marker: "1", preferences, includes: { name: "button", text: "(JSX tag name) JSX.IntrinsicElements.button: any" } },
+ { marker: "2", preferences, includes: { name: "button", text: "(JSX tag name) JSX.IntrinsicElements.button: any" } },
+ { marker: "3", preferences, includes: { name: "button", text: "(JSX tag name) JSX.IntrinsicElements.button: any" } },
+)
From c4a7bb1b4e0ae2e19b4590a4c5c1929567070bfe Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Wed, 12 Jan 2022 11:51:07 -0800
Subject: [PATCH 4/6] Completely eliminate, deprecate jsxAttribute usage
---
src/services/completions.ts | 51 +++++++++--
src/services/symbolDisplay.ts | 84 ++-----------------
src/services/types.ts | 3 +-
.../reference/api/tsserverlibrary.d.ts | 2 +-
tests/baselines/reference/api/typescript.d.ts | 2 +-
tests/cases/fourslash/completionsInJsxTag.ts | 8 +-
.../fourslash/completionsJsxAttribute.ts | 4 +-
.../completionsJsxAttributeGeneric.ts | 2 +-
.../completionsJsxAttributeInitializer.ts | 4 +-
tests/cases/fourslash/jsxGenericQuickInfo.tsx | 4 +-
.../jsxTagNameDottedAttributeSnippet.ts | 2 +-
.../jsxTagNameDottedAttributeSnippetClosed.ts | 2 +-
.../fourslash/jsxTagNameDottedNoSnippet.ts | 8 +-
.../jsxTagNameDottedNoSnippetClosed.ts | 8 +-
tests/cases/fourslash/jsxTagNameNoSnippet.ts | 6 +-
tests/cases/fourslash/tsxCompletion12.ts | 4 +-
tests/cases/fourslash/tsxCompletion13.ts | 10 +--
tests/cases/fourslash/tsxCompletion7.ts | 4 +-
.../cases/fourslash/tsxFindAllReferences11.ts | 2 +-
.../cases/fourslash/tsxFindAllReferences6.ts | 2 +-
tests/cases/fourslash/tsxQuickInfo3.ts | 4 +-
tests/cases/fourslash/tsxQuickInfo4.ts | 4 +-
tests/cases/fourslash/tsxQuickInfo5.ts | 4 +-
23 files changed, 96 insertions(+), 128 deletions(-)
diff --git a/src/services/completions.ts b/src/services/completions.ts
index ce031739fd010..290fb8c5a5422 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -739,8 +739,47 @@ namespace ts.Completions {
}
}
- const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, location, contextToken);
- if (kind === ScriptElementKind.jsxAttribute && preferences.includeCompletionsWithSnippetText && preferences.jsxAttributeCompletionStyle && preferences.jsxAttributeCompletionStyle !== "none") {
+ const isJSXAttributeCompletion = contextToken && forEachAncestor(contextToken, (n) => {
+ if (isJsxAttributeLike(n)) {
+ return true;
+ }
+
+ if (isJsxFragment(n) || isJsxOpeningFragment(n) || isJsxClosingFragment(n)) {
+ return false;
+ }
+
+ if (isJsxOpeningElement(n) || isJsxSelfClosingElement(n) || isJsxClosingElement(n)) {
+ if (contextToken.getEnd() <= n.tagName.getFullStart()) {
+ // Definitely completing part of the tag name.
+ return false;
+ }
+
+ if (rangeContainsRange(n.tagName, contextToken)) {
+ // We are to the right of the tag name, as the context is there.
+ // figure out where we are based on where the location is.
+
+ // TODO(jakebailey): This seems hacky.
+ if (contextToken.kind === SyntaxKind.DotToken || contextToken.kind === SyntaxKind.QuestionDotToken) {
+ // Unfinished dotted tag name.
+ return false;
+ }
+
+ if (!rangeContainsRange(n, location)) {
+ // Unclosed JSX element; location is entirely outside the element.
+ return true;
+ }
+
+ if (n.tagName.getEnd() <= location.getFullStart()) {
+ // After existing attributes, so is another attribute.
+ return true;
+ }
+ }
+
+ return false;
+ }
+ });
+
+ if (isJSXAttributeCompletion && preferences.includeCompletionsWithSnippetText && preferences.jsxAttributeCompletionStyle && preferences.jsxAttributeCompletionStyle !== "none") {
let useBraces = preferences.jsxAttributeCompletionStyle === "braces";
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, location);
@@ -785,7 +824,7 @@ namespace ts.Completions {
// entries (like JavaScript identifier entries).
return {
name,
- kind,
+ kind: SymbolDisplay.getSymbolKind(typeChecker, symbol, location),
kindModifiers: SymbolDisplay.getSymbolModifiers(typeChecker, symbol),
sortText,
source,
@@ -1402,7 +1441,7 @@ namespace ts.Completions {
case "symbol": {
const { symbol, location, contextToken, origin, previousToken } = symbolCompletion;
const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(name, location, contextToken, origin, symbol, program, host, compilerOptions, sourceFile, position, previousToken, formatContext, preferences, data, source);
- return createCompletionDetailsForSymbol(symbol, typeChecker, sourceFile, location, cancellationToken, codeActions, sourceDisplay, contextToken); // TODO: GH#18217
+ return createCompletionDetailsForSymbol(symbol, typeChecker, sourceFile, location, cancellationToken, codeActions, sourceDisplay); // TODO: GH#18217
}
case "literal": {
const { literal } = symbolCompletion;
@@ -1420,10 +1459,10 @@ namespace ts.Completions {
return createCompletionDetails(name, ScriptElementKindModifier.none, kind, [displayPart(name, kind2)]);
}
- export function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[], contextToken?: Node): CompletionEntryDetails {
+ export function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails {
const { displayParts, documentation, symbolKind, tags } =
checker.runWithCancellationToken(cancellationToken, checker =>
- SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All, /*alias*/ undefined, contextToken)
+ SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All, /*alias*/ undefined)
);
return createCompletionDetails(symbol.name, SymbolDisplay.getSymbolModifiers(checker, symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay);
}
diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts
index 7ae1d6367601b..c7ee58ff28e63 100644
--- a/src/services/symbolDisplay.ts
+++ b/src/services/symbolDisplay.ts
@@ -3,8 +3,8 @@ namespace ts.SymbolDisplay {
const symbolDisplayNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
// TODO(drosen): use contextual SemanticMeaning.
- export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node, contextToken?: Node): ScriptElementKind {
- const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location, contextToken);
+ export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind {
+ const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location);
if (result !== ScriptElementKind.unknown) {
return result;
}
@@ -25,7 +25,7 @@ namespace ts.SymbolDisplay {
return result;
}
- function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, location: Node, contextToken?: Node): ScriptElementKind {
+ function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind {
const roots = typeChecker.getRootSymbols(symbol);
// If this is a method from a mapped type, leave as a method so long as it still has a call signature.
if (roots.length === 1
@@ -84,80 +84,12 @@ namespace ts.SymbolDisplay {
return unionPropertyKind;
}
- if (contextToken) {
- const result = getSymbolKindOfJsxTagNameOrAttribute(location, contextToken);
- if (result !== ScriptElementKind.unknown) {
- return result;
- }
- }
-
- if (isJsxAttribute(location) || location.parent && isJsxAttribute(location.parent) && location.parent.name === location) {
- return ScriptElementKind.jsxAttribute;
- }
-
- // TODO(jakebailey): Delete the below code, once the edge cases it handles are handled above.
-
- // If we requested completions after `x.` at the top-level, we may be at a source file location.
- switch (location.parent && location.parent.kind) {
- // If we've typed a character of the attribute name, will be 'JsxAttribute', else will be 'JsxOpeningElement'.
- case SyntaxKind.JsxOpeningElement:
- case SyntaxKind.JsxElement:
- case SyntaxKind.JsxSelfClosingElement:
- return location.kind === SyntaxKind.Identifier ? ScriptElementKind.memberVariableElement : ScriptElementKind.jsxAttribute;
- case SyntaxKind.JsxAttribute:
- return ScriptElementKind.jsxAttribute;
- default:
- return ScriptElementKind.memberVariableElement;
- }
+ return ScriptElementKind.memberVariableElement;
}
return ScriptElementKind.unknown;
}
- function getSymbolKindOfJsxTagNameOrAttribute(location: Node, contextToken: Node): ScriptElementKind {
- const symbolKindFromContext = forEachAncestor(contextToken, (n) => {
- if (isJsxAttributeLike(n)) {
- return ScriptElementKind.jsxAttribute;
- }
-
- if (isJsxFragment(n) || isJsxOpeningFragment(n) || isJsxClosingFragment(n)) {
- return "quit";
- }
-
- if (isJsxOpeningElement(n) || isJsxSelfClosingElement(n) || isJsxClosingElement(n)) {
- if (contextToken.getEnd() <= n.tagName.getFullStart()) {
- // Definitely completing part of the tag name.
- return ScriptElementKind.jsxTagName;
- }
-
- if (rangeContainsRange(n.tagName, contextToken)) {
- // We are to the right of the tag name, as the context is there.
- // figure out where we are based on where the location is.
-
- // TODO(jakebailey): This seems hacky.
- if (contextToken.kind === SyntaxKind.DotToken || contextToken.kind === SyntaxKind.QuestionDotToken) {
- // Unfinished dotted tag name.
- return ScriptElementKind.jsxTagName;
- }
-
- if (!rangeContainsRange(n, location)) {
- // Unclosed JSX element; location is entirely outside the element.
- return ScriptElementKind.jsxAttribute;
- }
-
- if (n.tagName.getEnd() <= location.getFullStart()) {
- // After existing attributes, so is another attribute.
- return ScriptElementKind.jsxAttribute;
- }
- }
-
- return "quit";
- }
- });
-
- return symbolKindFromContext || ScriptElementKind.unknown;
- }
-
function getNormalizedSymbolModifiers(symbol: Symbol) {
if (symbol.declarations && symbol.declarations.length) {
const [declaration, ...declarations] = symbol.declarations;
@@ -202,12 +134,12 @@ namespace ts.SymbolDisplay {
// TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location
export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: TypeChecker, symbol: Symbol, sourceFile: SourceFile, enclosingDeclaration: Node | undefined,
- location: Node, semanticMeaning = getMeaningFromLocation(location), alias?: Symbol, contextToken?: Node): SymbolDisplayPartsDocumentationAndSymbolKind {
+ location: Node, semanticMeaning = getMeaningFromLocation(location), alias?: Symbol): SymbolDisplayPartsDocumentationAndSymbolKind {
const displayParts: SymbolDisplayPart[] = [];
let documentation: SymbolDisplayPart[] = [];
let tags: JSDocTagInfo[] = [];
const symbolFlags = getCombinedLocalAndExportSymbolFlags(symbol);
- let symbolKind = semanticMeaning & SemanticMeaning.Value ? getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location, contextToken) : ScriptElementKind.unknown;
+ let symbolKind = semanticMeaning & SemanticMeaning.Value ? getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location) : ScriptElementKind.unknown;
let hasAddedSymbolInfo = false;
const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isInExpressionContext(location);
let type: Type | undefined;
@@ -289,7 +221,6 @@ namespace ts.SymbolDisplay {
switch (symbolKind) {
case ScriptElementKind.jsxAttribute:
- case ScriptElementKind.jsxTagName:
case ScriptElementKind.memberVariableElement:
case ScriptElementKind.variableElement:
case ScriptElementKind.constElement:
@@ -562,7 +493,6 @@ namespace ts.SymbolDisplay {
// For properties, variables and local vars: show the type
if (symbolKind === ScriptElementKind.memberVariableElement ||
symbolKind === ScriptElementKind.jsxAttribute ||
- symbolKind === ScriptElementKind.jsxTagName ||
symbolFlags & SymbolFlags.Variable ||
symbolKind === ScriptElementKind.localVariableElement ||
isThisExpression) {
@@ -603,7 +533,7 @@ namespace ts.SymbolDisplay {
}
}
else {
- symbolKind = getSymbolKind(typeChecker, symbol, location, contextToken);
+ symbolKind = getSymbolKind(typeChecker, symbol, location);
}
}
diff --git a/src/services/types.ts b/src/services/types.ts
index 7df40b22c8349..b30583bb0f4e8 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -1456,9 +1456,8 @@ namespace ts {
/**
*
+ * @deprecated
*/
- jsxTagName = "JSX tag name",
-
jsxAttribute = "JSX attribute",
/** String literal */
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index b620c68b3cce1..cb1d033b1578c 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -6593,8 +6593,8 @@ declare namespace ts {
externalModuleName = "external module name",
/**
*
+ * @deprecated
*/
- jsxTagName = "JSX tag name",
jsxAttribute = "JSX attribute",
/** String literal */
string = "string",
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index 11d25bd2ab52d..5985a23e8b361 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -6593,8 +6593,8 @@ declare namespace ts {
externalModuleName = "external module name",
/**
*
+ * @deprecated
*/
- jsxTagName = "JSX tag name",
jsxAttribute = "JSX attribute",
/** String literal */
string = "string",
diff --git a/tests/cases/fourslash/completionsInJsxTag.ts b/tests/cases/fourslash/completionsInJsxTag.ts
index 4f5862e820885..8fd2fcfe4cf5a 100644
--- a/tests/cases/fourslash/completionsInJsxTag.ts
+++ b/tests/cases/fourslash/completionsInJsxTag.ts
@@ -26,16 +26,16 @@ verify.completions({
exact: [
{
name: "aria-label",
- text: "(JSX attribute) \"aria-label\": string",
+ text: "(property) \"aria-label\": string",
documentation: "Label docs",
- kind: "JSX attribute",
+ kind: "property",
kindModifiers: "declare",
},
{
name: "foo",
- text: "(JSX attribute) foo: string",
+ text: "(property) foo: string",
documentation: "Doc",
- kind: "JSX attribute",
+ kind: "property",
kindModifiers: "declare",
},
],
diff --git a/tests/cases/fourslash/completionsJsxAttribute.ts b/tests/cases/fourslash/completionsJsxAttribute.ts
index 592c2c8ed3c72..3dcf49d487108 100644
--- a/tests/cases/fourslash/completionsJsxAttribute.ts
+++ b/tests/cases/fourslash/completionsJsxAttribute.ts
@@ -17,8 +17,8 @@
////;
const exact: ReadonlyArray = [
- { name: "bar", kind: "JSX attribute", kindModifiers: "declare", text: "(JSX attribute) bar: string" },
- { name: "foo", kind: "JSX attribute", kindModifiers: "declare", text: "(JSX attribute) foo: boolean", documentation: "Doc" },
+ { name: "bar", kind: "property", kindModifiers: "declare", text: "(property) bar: string" },
+ { name: "foo", kind: "property", kindModifiers: "declare", text: "(property) foo: boolean", documentation: "Doc" },
];
verify.completions({ marker: "", exact });
edit.insert("f");
diff --git a/tests/cases/fourslash/completionsJsxAttributeGeneric.ts b/tests/cases/fourslash/completionsJsxAttributeGeneric.ts
index 9180c691f09f4..2e50a8a895e8c 100644
--- a/tests/cases/fourslash/completionsJsxAttributeGeneric.ts
+++ b/tests/cases/fourslash/completionsJsxAttributeGeneric.ts
@@ -11,7 +11,7 @@
marker,
exact: [{
name: 'name',
- kind: 'JSX attribute',
+ kind: 'property',
kindModifiers: 'declare'
}]
})
diff --git a/tests/cases/fourslash/completionsJsxAttributeInitializer.ts b/tests/cases/fourslash/completionsJsxAttributeInitializer.ts
index 2076c6588f4d7..f383d52375912 100644
--- a/tests/cases/fourslash/completionsJsxAttributeInitializer.ts
+++ b/tests/cases/fourslash/completionsJsxAttributeInitializer.ts
@@ -9,8 +9,8 @@ verify.completions({
marker: "",
includes: [
{ name: "x", text: "(parameter) x: number", kind: "parameter", insertText: "{x}" },
- { name: "p", text: "(JSX attribute) p: number", kind: "JSX attribute", insertText: "{this.p}", sortText: completion.SortText.SuggestedClassMembers, source: completion.CompletionSource.ThisProperty },
- { name: "a b", text: '(JSX attribute) "a b": number', kind: "JSX attribute", insertText: '{this["a b"]}', sortText: completion.SortText.SuggestedClassMembers, source: completion.CompletionSource.ThisProperty },
+ { name: "p", text: "(property) p: number", kind: "property", insertText: "{this.p}", sortText: completion.SortText.SuggestedClassMembers, source: completion.CompletionSource.ThisProperty },
+ { name: "a b", text: '(property) "a b": number', kind: "property", insertText: '{this["a b"]}', sortText: completion.SortText.SuggestedClassMembers, source: completion.CompletionSource.ThisProperty },
],
preferences: {
includeInsertTextCompletions: true,
diff --git a/tests/cases/fourslash/jsxGenericQuickInfo.tsx b/tests/cases/fourslash/jsxGenericQuickInfo.tsx
index 7566193737651..9e1ff3b84b083 100644
--- a/tests/cases/fourslash/jsxGenericQuickInfo.tsx
+++ b/tests/cases/fourslash/jsxGenericQuickInfo.tsx
@@ -29,6 +29,6 @@
verify.quickInfoAt("0", "(property) PropsA.renderItem: (item: number) => string");
verify.quickInfoAt("1", "(parameter) item: number");
verify.quickInfoAt("2", `(property) PropsA.name: "A"`, 'comments for A');
-verify.quickInfoAt("3", "(JSX attribute) PropsA.renderItem: (item: number) => string");
+verify.quickInfoAt("3", "(property) PropsA.renderItem: (item: number) => string");
verify.quickInfoAt("4", "(parameter) item: number");
-verify.quickInfoAt("5", `(JSX attribute) PropsA.name: "A"`, 'comments for A');
+verify.quickInfoAt("5", `(property) PropsA.name: "A"`, 'comments for A');
diff --git a/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts b/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts
index 1264ccf79ad5f..98d4c92e20493 100644
--- a/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts
+++ b/tests/cases/fourslash/jsxTagNameDottedAttributeSnippet.ts
@@ -62,7 +62,7 @@ verify.completions(
insertText: "className={$1}",
isSnippet: true,
sortText: completion.SortText.OptionalMember,
- text: "(JSX attribute) className?: string"
+ text: "(property) className?: string"
}
],
preferences: {
diff --git a/tests/cases/fourslash/jsxTagNameDottedAttributeSnippetClosed.ts b/tests/cases/fourslash/jsxTagNameDottedAttributeSnippetClosed.ts
index 1a4cfe73886ec..b10279315292f 100644
--- a/tests/cases/fourslash/jsxTagNameDottedAttributeSnippetClosed.ts
+++ b/tests/cases/fourslash/jsxTagNameDottedAttributeSnippetClosed.ts
@@ -62,7 +62,7 @@ verify.completions(
insertText: "className={$1}",
isSnippet: true,
sortText: completion.SortText.OptionalMember,
- text: "(JSX attribute) className?: string"
+ text: "(property) className?: string"
}
],
preferences: {
diff --git a/tests/cases/fourslash/jsxTagNameDottedNoSnippet.ts b/tests/cases/fourslash/jsxTagNameDottedNoSnippet.ts
index 520021d73dad7..515c631c75ec0 100644
--- a/tests/cases/fourslash/jsxTagNameDottedNoSnippet.ts
+++ b/tests/cases/fourslash/jsxTagNameDottedNoSnippet.ts
@@ -47,8 +47,8 @@ var preferences: FourSlashInterface.UserPreferences = {
verify.completions(
{ marker: "1", preferences, includes: { name: "Foo", text: "const Foo: NestedInterface" } },
{ marker: "2", preferences, includes: { name: "Foo", text: "const Foo: NestedInterface" } },
- { marker: "3", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
- { marker: "4", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
- { marker: "5", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
- { marker: "6", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
+ { marker: "3", preferences, includes: { name: "Foo", text: "(property) NestedInterface.Foo: NestedInterface" } },
+ { marker: "4", preferences, includes: { name: "Foo", text: "(property) NestedInterface.Foo: NestedInterface" } },
+ { marker: "5", preferences, includes: { name: "Foo", text: "(property) NestedInterface.Foo: NestedInterface" } },
+ { marker: "6", preferences, includes: { name: "Foo", text: "(property) NestedInterface.Foo: NestedInterface" } },
)
diff --git a/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts b/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts
index fed65eb73f618..144c53ba60654 100644
--- a/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts
+++ b/tests/cases/fourslash/jsxTagNameDottedNoSnippetClosed.ts
@@ -47,8 +47,8 @@ var preferences: FourSlashInterface.UserPreferences = {
verify.completions(
{ marker: "1", preferences, includes: { name: "Foo", text: "const Foo: NestedInterface" } },
{ marker: "2", preferences, includes: { name: "Foo", text: "const Foo: NestedInterface" } },
- { marker: "3", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
- { marker: "4", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
- { marker: "5", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
- { marker: "6", preferences, includes: { name: "Foo", text: "(JSX tag name) NestedInterface.Foo: NestedInterface" } },
+ { marker: "3", preferences, includes: { name: "Foo", text: "(property) NestedInterface.Foo: NestedInterface" } },
+ { marker: "4", preferences, includes: { name: "Foo", text: "(property) NestedInterface.Foo: NestedInterface" } },
+ { marker: "5", preferences, includes: { name: "Foo", text: "(property) NestedInterface.Foo: NestedInterface" } },
+ { marker: "6", preferences, includes: { name: "Foo", text: "(property) NestedInterface.Foo: NestedInterface" } },
)
diff --git a/tests/cases/fourslash/jsxTagNameNoSnippet.ts b/tests/cases/fourslash/jsxTagNameNoSnippet.ts
index c79b359b1eb30..db243b1102558 100644
--- a/tests/cases/fourslash/jsxTagNameNoSnippet.ts
+++ b/tests/cases/fourslash/jsxTagNameNoSnippet.ts
@@ -29,7 +29,7 @@ var preferences: FourSlashInterface.UserPreferences = {
};
verify.completions(
- { marker: "1", preferences, includes: { name: "button", text: "(JSX tag name) JSX.IntrinsicElements.button: any" } },
- { marker: "2", preferences, includes: { name: "button", text: "(JSX tag name) JSX.IntrinsicElements.button: any" } },
- { marker: "3", preferences, includes: { name: "button", text: "(JSX tag name) JSX.IntrinsicElements.button: any" } },
+ { marker: "1", preferences, includes: { name: "button", text: "(property) JSX.IntrinsicElements.button: any" } },
+ { marker: "2", preferences, includes: { name: "button", text: "(property) JSX.IntrinsicElements.button: any" } },
+ { marker: "3", preferences, includes: { name: "button", text: "(property) JSX.IntrinsicElements.button: any" } },
)
diff --git a/tests/cases/fourslash/tsxCompletion12.ts b/tests/cases/fourslash/tsxCompletion12.ts
index 4b0b01c2241d0..c4feb1ae51ae7 100644
--- a/tests/cases/fourslash/tsxCompletion12.ts
+++ b/tests/cases/fourslash/tsxCompletion12.ts
@@ -28,14 +28,14 @@ verify.completions(
exact: [
"propString",
"propx",
- { name: "optional", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
+ { name: "optional", kind: "property", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
]
},
{
marker: "3",
exact: [
"propString",
- { name: "optional", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
+ { name: "optional", kind: "property", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
]
},
{ marker: "4", exact: "propString" },
diff --git a/tests/cases/fourslash/tsxCompletion13.ts b/tests/cases/fourslash/tsxCompletion13.ts
index b3dc913783a40..5b8a8d7e18b41 100644
--- a/tests/cases/fourslash/tsxCompletion13.ts
+++ b/tests/cases/fourslash/tsxCompletion13.ts
@@ -36,8 +36,8 @@ verify.completions(
exact: [
"goTo",
"onClick",
- { name: "children", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
- { name: "className", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
+ { name: "children", kind: "property", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
+ { name: "className", kind: "property", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
]
},
{
@@ -45,14 +45,14 @@ verify.completions(
exact: [
"goTo",
"onClick",
- { name: "className", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
+ { name: "className", kind: "property", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
]
},
{
marker: ["3", "4", "5"],
exact: [
- { name: "children", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
- { name: "className", kind: "JSX attribute", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
+ { name: "children", kind: "property", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
+ { name: "className", kind: "property", kindModifiers: "optional", sortText: completion.SortText.OptionalMember },
]
},
);
diff --git a/tests/cases/fourslash/tsxCompletion7.ts b/tests/cases/fourslash/tsxCompletion7.ts
index 90a98c4f5a0d0..1d31441ca1b94 100644
--- a/tests/cases/fourslash/tsxCompletion7.ts
+++ b/tests/cases/fourslash/tsxCompletion7.ts
@@ -13,7 +13,7 @@
verify.completions({
marker: "",
exact: [
- { name: "TWO", kind: "JSX attribute", kindModifiers: "declare", sortText: completion.SortText.LocationPriority },
- { name: "ONE", kind: "JSX attribute", kindModifiers: "declare", sortText: completion.SortText.MemberDeclaredBySpreadAssignment },
+ { name: "TWO", kind: "property", kindModifiers: "declare", sortText: completion.SortText.LocationPriority },
+ { name: "ONE", kind: "property", kindModifiers: "declare", sortText: completion.SortText.MemberDeclaredBySpreadAssignment },
]
});
diff --git a/tests/cases/fourslash/tsxFindAllReferences11.ts b/tests/cases/fourslash/tsxFindAllReferences11.ts
index b965b87d8f831..8c7e7a4690937 100644
--- a/tests/cases/fourslash/tsxFindAllReferences11.ts
+++ b/tests/cases/fourslash/tsxFindAllReferences11.ts
@@ -25,4 +25,4 @@
//// declare function MainButton(props: ButtonProps | LinkProps): JSX.Element;
//// let opt = ;
-verify.singleReferenceGroup("(JSX attribute) wrong: true");
+verify.singleReferenceGroup("(property) wrong: true");
diff --git a/tests/cases/fourslash/tsxFindAllReferences6.ts b/tests/cases/fourslash/tsxFindAllReferences6.ts
index 8d54ddd9537e1..c0e119a4e456e 100644
--- a/tests/cases/fourslash/tsxFindAllReferences6.ts
+++ b/tests/cases/fourslash/tsxFindAllReferences6.ts
@@ -19,4 +19,4 @@
//// declare function Opt(attributes: OptionPropBag): JSX.Element;
//// let opt = ;
-verify.singleReferenceGroup("(JSX attribute) wrong: true");
+verify.singleReferenceGroup("(property) wrong: true");
diff --git a/tests/cases/fourslash/tsxQuickInfo3.ts b/tests/cases/fourslash/tsxQuickInfo3.ts
index 614ffa878ef09..068fcab19f51f 100644
--- a/tests/cases/fourslash/tsxQuickInfo3.ts
+++ b/tests/cases/fourslash/tsxQuickInfo3.ts
@@ -24,7 +24,7 @@
verify.quickInfos({
1: "class Opt",
- 2: "(JSX attribute) propx: number",
+ 2: "(property) propx: number",
3: "const obj1: OptionProp",
- 4: "(JSX attribute) propx: true"
+ 4: "(property) propx: true"
});
diff --git a/tests/cases/fourslash/tsxQuickInfo4.ts b/tests/cases/fourslash/tsxQuickInfo4.ts
index 7e1f8e2bc7ba0..f66d4dc4afd9a 100644
--- a/tests/cases/fourslash/tsxQuickInfo4.ts
+++ b/tests/cases/fourslash/tsxQuickInfo4.ts
@@ -48,8 +48,8 @@
verify.quickInfos({
1: "function MainButton(linkProps: LinkProps): JSX.Element (+1 overload)",
- 2: "(JSX attribute) LinkProps.to: string",
+ 2: "(property) LinkProps.to: string",
3: "function MainButton(buttonProps: ButtonProps): JSX.Element (+1 overload)",
4: "(method) ButtonProps.onClick(event?: React.MouseEvent): void",
- 5: "(JSX attribute) extra-prop: true"
+ 5: "(property) extra-prop: true"
});
diff --git a/tests/cases/fourslash/tsxQuickInfo5.ts b/tests/cases/fourslash/tsxQuickInfo5.ts
index 3ebf1bd0bdd04..35f821fe6b28d 100644
--- a/tests/cases/fourslash/tsxQuickInfo5.ts
+++ b/tests/cases/fourslash/tsxQuickInfo5.ts
@@ -13,6 +13,6 @@
verify.quickInfos({
1: "function ComponentWithTwoAttributes(l: {\n key1: T;\n value: U;\n}): JSX.Element",
- 2: "(JSX attribute) key1: T",
- 3: "(JSX attribute) value: U",
+ 2: "(property) key1: T",
+ 3: "(property) value: U",
});
From e2a958d5c152f11ab9b4a22b6dfe9bc04dbea694 Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Wed, 12 Jan 2022 12:18:04 -0800
Subject: [PATCH 5/6] Remove leftover
---
src/services/completions.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/services/completions.ts b/src/services/completions.ts
index 290fb8c5a5422..2dbee0ce1dc2d 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -1462,7 +1462,7 @@ namespace ts.Completions {
export function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails {
const { displayParts, documentation, symbolKind, tags } =
checker.runWithCancellationToken(cancellationToken, checker =>
- SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All, /*alias*/ undefined)
+ SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All)
);
return createCompletionDetails(symbol.name, SymbolDisplay.getSymbolModifiers(checker, symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay);
}
From 73426dd8bec85a3b4c57bdf62f80b0c18542b4aa Mon Sep 17 00:00:00 2001
From: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Date: Wed, 12 Jan 2022 12:21:54 -0800
Subject: [PATCH 6/6] Remove todo
---
src/services/completions.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/services/completions.ts b/src/services/completions.ts
index 2dbee0ce1dc2d..e2d56f77dd810 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -758,7 +758,6 @@ namespace ts.Completions {
// We are to the right of the tag name, as the context is there.
// figure out where we are based on where the location is.
- // TODO(jakebailey): This seems hacky.
if (contextToken.kind === SyntaxKind.DotToken || contextToken.kind === SyntaxKind.QuestionDotToken) {
// Unfinished dotted tag name.
return false;