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,
+ }
+ },
+);