From 1d4ec40130bae10f4b955bd7672054e34cba8caa Mon Sep 17 00:00:00 2001 From: TypeScript Bot Date: Fri, 10 Dec 2021 15:47:23 -0800 Subject: [PATCH] Cherry-pick PR #47096 into release-4.5 (#47105) Component commits: 191aa682f8 Disable JSX attribute snippet if attribute is acutally the HTML tag 1786d5e278 Add more tests of text before and after 7ac5e596ce Big comment Co-authored-by: Jake Bailey <5341706+jakebailey@users.noreply.github.com> --- src/services/completions.ts | 19 +++++- .../jsxAttributeAsTagNameNoSnippet.ts | 61 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/jsxAttributeAsTagNameNoSnippet.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 9961a699945f7..b0b7075191462 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -713,8 +713,25 @@ namespace ts.Completions { } } + // Before offering up a JSX attribute snippet, ensure that we aren't potentially completing + // a tag name; this may appear as an attribute after the "<" when the tag has not yet been + // closed, as in: + // + // return <> + // foo + // + // We can detect this case by checking if both: + // + // 1. The location is "<", so we are completing immediately after it. + // 2. The "<" has the same position as its parent, so is not a binary expression. const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, location); - if (kind === ScriptElementKind.jsxAttribute && preferences.includeCompletionsWithSnippetText && preferences.jsxAttributeCompletionStyle && preferences.jsxAttributeCompletionStyle !== "none") { + if ( + kind === ScriptElementKind.jsxAttribute + && (location.kind !== SyntaxKind.LessThanToken || location.pos !== location.parent.pos) + && preferences.includeCompletionsWithSnippetText + && preferences.jsxAttributeCompletionStyle + && preferences.jsxAttributeCompletionStyle !== "none") { let useBraces = preferences.jsxAttributeCompletionStyle === "braces"; const type = typeChecker.getTypeOfSymbolAtLocation(symbol, location); diff --git a/tests/cases/fourslash/jsxAttributeAsTagNameNoSnippet.ts b/tests/cases/fourslash/jsxAttributeAsTagNameNoSnippet.ts new file mode 100644 index 0000000000000..40766cf294619 --- /dev/null +++ b/tests/cases/fourslash/jsxAttributeAsTagNameNoSnippet.ts @@ -0,0 +1,61 @@ +/// +//@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: "1", + includes: [ + { name: "button", insertText: undefined, isSnippet: undefined } + ], + preferences: { + jsxAttributeCompletionStyle: "braces", + includeCompletionsWithSnippetText: true, + includeCompletionsWithInsertText: true, + } + }, + { + marker: "2", + includes: [ + { name: "button", insertText: undefined, isSnippet: undefined } + ], + preferences: { + jsxAttributeCompletionStyle: "braces", + includeCompletionsWithSnippetText: true, + includeCompletionsWithInsertText: true, + } + }, + { + marker: "3", + includes: [ + { name: "button", insertText: undefined, isSnippet: undefined } + ], + preferences: { + jsxAttributeCompletionStyle: "braces", + includeCompletionsWithSnippetText: true, + includeCompletionsWithInsertText: true, + } + }, +);