From c552a4bf827cf56680d5cb1510dd948a3787ea38 Mon Sep 17 00:00:00 2001 From: Zak Miller Date: Thu, 22 Apr 2021 18:12:05 -0400 Subject: [PATCH] fix(42829) ignore preceeding jsx whitespace (#43452) --- src/services/refactors/extractSymbol.ts | 2 +- src/services/utilities.ts | 14 ++++++++ .../extract-method_jsxPreceedingWhitespace.ts | 36 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/extract-method_jsxPreceedingWhitespace.ts diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 62ba653961f..b5167124512 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -275,7 +275,7 @@ namespace ts.refactor.extractSymbol { } const cursorRequest = length === 0 && invoked; - const startToken = getTokenAtPosition(sourceFile, span.start); + const startToken = findFirstNonJsxWhitespaceToken(sourceFile, span.start); const endToken = findTokenOnLeftOfPosition(sourceFile, textSpanEnd(span)); /* If the refactoring command is invoked through a keyboard action it's safe to assume that the user is actively looking for refactoring actions at the span location. As they may not know the exact range that will trigger a refactoring, we expand the diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 6b4330e3155..7516da5e8de 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1141,6 +1141,20 @@ namespace ts { } } + /** + * Returns the first token where position is in [start, end), + * excluding `JsxText` tokens containing only whitespace. + */ + export function findFirstNonJsxWhitespaceToken(sourceFile: SourceFile, position: number): Node | undefined { + let tokenAtPosition = getTokenAtPosition(sourceFile, position); + while (isWhiteSpaceOnlyJsxText(tokenAtPosition)) { + const nextToken = findNextToken(tokenAtPosition, tokenAtPosition.parent, sourceFile); + if (!nextToken) return; + tokenAtPosition = nextToken; + } + return tokenAtPosition; + } + /** * The token on the left of the position is the token that strictly includes the position * or sits to the left of the cursor if it is on a boundary. For example diff --git a/tests/cases/fourslash/extract-method_jsxPreceedingWhitespace.ts b/tests/cases/fourslash/extract-method_jsxPreceedingWhitespace.ts new file mode 100644 index 00000000000..388a6eea7ab --- /dev/null +++ b/tests/cases/fourslash/extract-method_jsxPreceedingWhitespace.ts @@ -0,0 +1,36 @@ +/// + +// Repro https://github.com/Microsoft/TypeScript/issues/42829 + +// @jsx: preserve +// @filename: a.tsx +////export default function ComponentThatExhibitsIssue() { +//// return
+//// /*a*/
+//// hello from my nested component +////
+//// +//// /*b*/ +////
+ +goTo.file("a.tsx"); +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Extract Symbol", + actionName: "function_scope_1", + actionDescription: "Extract to function in module scope", + newContent: +`export default function ComponentThatExhibitsIssue() { + return
+ {newFunction()} + + +
+ +function /*RENAME*/newFunction() { + return
+ hello from my nested component +
; + } +` +});