feat(45010): handle unclosed fragment in getJsxClosingTagAtPosition
(#45532)
* feat(45010): handle unclosed fragment in `getJsxClosingTagAtPosition` * Update tests * Fix types of `JsxText.parent` and `JsxExpression.parent`
This commit is contained in:
parent
07fd7bce64
commit
617251f2e0
|
@ -2598,14 +2598,14 @@ namespace ts {
|
|||
|
||||
export interface JsxExpression extends Expression {
|
||||
readonly kind: SyntaxKind.JsxExpression;
|
||||
readonly parent: JsxElement | JsxAttributeLike;
|
||||
readonly parent: JsxElement | JsxFragment | JsxAttributeLike;
|
||||
readonly dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
|
||||
readonly expression?: Expression;
|
||||
}
|
||||
|
||||
export interface JsxText extends LiteralLikeNode {
|
||||
readonly kind: SyntaxKind.JsxText;
|
||||
readonly parent: JsxElement;
|
||||
readonly parent: JsxElement | JsxFragment;
|
||||
readonly containsOnlyTriviaWhiteSpaces: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -1180,7 +1180,7 @@ namespace ts.Completions {
|
|||
case SyntaxKind.CaseKeyword:
|
||||
return getSwitchedType(cast(parent, isCaseClause), checker);
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
return isJsxExpression(parent) && parent.parent.kind !== SyntaxKind.JsxElement ? checker.getContextualTypeForJsxAttribute(parent.parent) : undefined;
|
||||
return isJsxExpression(parent) && !isJsxElement(parent.parent) && !isJsxFragment(parent.parent) ? checker.getContextualTypeForJsxAttribute(parent.parent) : undefined;
|
||||
default:
|
||||
const argInfo = SignatureHelp.getArgumentInfoForCompletions(previousToken, position, sourceFile);
|
||||
return argInfo ?
|
||||
|
|
|
@ -2088,10 +2088,15 @@ namespace ts {
|
|||
const token = findPrecedingToken(position, sourceFile);
|
||||
if (!token) return undefined;
|
||||
const element = token.kind === SyntaxKind.GreaterThanToken && isJsxOpeningElement(token.parent) ? token.parent.parent
|
||||
: isJsxText(token) ? token.parent : undefined;
|
||||
: isJsxText(token) && isJsxElement(token.parent) ? token.parent : undefined;
|
||||
if (element && isUnclosedTag(element)) {
|
||||
return { newText: `</${element.openingElement.tagName.getText(sourceFile)}>` };
|
||||
}
|
||||
const fragment = token.kind === SyntaxKind.GreaterThanToken && isJsxOpeningFragment(token.parent) ? token.parent.parent
|
||||
: isJsxText(token) && isJsxFragment(token.parent) ? token.parent : undefined;
|
||||
if (fragment && isUnclosedFragment(fragment)) {
|
||||
return { newText: "</>" };
|
||||
}
|
||||
}
|
||||
|
||||
function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) {
|
||||
|
@ -2334,6 +2339,10 @@ namespace ts {
|
|||
isJsxElement(parent) && tagNamesAreEquivalent(openingElement.tagName, parent.openingElement.tagName) && isUnclosedTag(parent);
|
||||
}
|
||||
|
||||
function isUnclosedFragment({ closingFragment, parent }: JsxFragment): boolean {
|
||||
return !!(closingFragment.flags & NodeFlags.ThisNodeHasError) || (isJsxFragment(parent) && isUnclosedFragment(parent));
|
||||
}
|
||||
|
||||
function getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined {
|
||||
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
|
||||
const range = formatting.getRangeOfEnclosingComment(sourceFile, position);
|
||||
|
|
|
@ -1381,13 +1381,13 @@ declare namespace ts {
|
|||
}
|
||||
export interface JsxExpression extends Expression {
|
||||
readonly kind: SyntaxKind.JsxExpression;
|
||||
readonly parent: JsxElement | JsxAttributeLike;
|
||||
readonly parent: JsxElement | JsxFragment | JsxAttributeLike;
|
||||
readonly dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
|
||||
readonly expression?: Expression;
|
||||
}
|
||||
export interface JsxText extends LiteralLikeNode {
|
||||
readonly kind: SyntaxKind.JsxText;
|
||||
readonly parent: JsxElement;
|
||||
readonly parent: JsxElement | JsxFragment;
|
||||
readonly containsOnlyTriviaWhiteSpaces: boolean;
|
||||
}
|
||||
export type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment;
|
||||
|
|
|
@ -1381,13 +1381,13 @@ declare namespace ts {
|
|||
}
|
||||
export interface JsxExpression extends Expression {
|
||||
readonly kind: SyntaxKind.JsxExpression;
|
||||
readonly parent: JsxElement | JsxAttributeLike;
|
||||
readonly parent: JsxElement | JsxFragment | JsxAttributeLike;
|
||||
readonly dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
|
||||
readonly expression?: Expression;
|
||||
}
|
||||
export interface JsxText extends LiteralLikeNode {
|
||||
readonly kind: SyntaxKind.JsxText;
|
||||
readonly parent: JsxElement;
|
||||
readonly parent: JsxElement | JsxFragment;
|
||||
readonly containsOnlyTriviaWhiteSpaces: boolean;
|
||||
}
|
||||
export type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment;
|
||||
|
|
51
tests/cases/fourslash/autoCloseFragment.ts
Normal file
51
tests/cases/fourslash/autoCloseFragment.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// Using separate files for each example to avoid unclosed JSX tags affecting other tests.
|
||||
|
||||
// @Filename: /0.tsx
|
||||
////const x = <>/*0*/;
|
||||
|
||||
// @Filename: /1.tsx
|
||||
////const x = <> foo/*1*/ </>;
|
||||
|
||||
// @Filename: /2.tsx
|
||||
////const x = <></>/*2*/;
|
||||
|
||||
// @Filename: /3.tsx
|
||||
////const x = </>/*3*/;
|
||||
|
||||
// @Filename: /4.tsx
|
||||
////const x = <div>
|
||||
//// <>/*4*/
|
||||
//// </div>
|
||||
////</>;
|
||||
|
||||
// @Filename: /5.tsx
|
||||
////const x = <> text /*5*/;
|
||||
|
||||
// @Filename: /6.tsx
|
||||
////const x = <>
|
||||
//// <>/*6*/
|
||||
////</>;
|
||||
|
||||
// @Filename: /7.tsx
|
||||
////const x = <div>
|
||||
//// <>/*7*/
|
||||
////</div>;
|
||||
|
||||
// @Filename: /8.tsx
|
||||
////const x = <div>
|
||||
//// <>/*8*/</>
|
||||
////</div>;
|
||||
|
||||
verify.jsxClosingTag({
|
||||
0: { newText: "</>" },
|
||||
1: undefined,
|
||||
2: undefined,
|
||||
3: undefined,
|
||||
4: { newText: "</>" },
|
||||
5: { newText: "</>" },
|
||||
6: { newText: "</>" },
|
||||
7: { newText: "</>" },
|
||||
8: undefined,
|
||||
});
|
Loading…
Reference in a new issue