From dbb814e7e76ae421474a109d549172302b5ea98a Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 21 Jul 2022 15:17:40 -0700 Subject: [PATCH 1/3] Add failing test case --- tests/cases/fourslash/issue49310.ts | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/cases/fourslash/issue49310.ts diff --git a/tests/cases/fourslash/issue49310.ts b/tests/cases/fourslash/issue49310.ts new file mode 100644 index 0000000000000..f75efd648f80d --- /dev/null +++ b/tests/cases/fourslash/issue49310.ts @@ -0,0 +1,46 @@ +/// + +// @filename: index.esm.d.ts +//// export declare class Chart { +//// constructor(config: ChartConfiguration); +//// } +//// +//// export interface ChartConfiguration { +//// options?: Partial; +//// } +//// +//// export interface TickOptions { +//// callback: (this: Scale, tickValue: number | string) => string | string[] | number | number[] | null | undefined; +//// } +//// +//// export interface CoreScaleOptions { +//// opt: boolean; +//// } +//// +//// export interface Scale { +//// opts: O; +//// getLabelForValue(value: number): string; +//// } + +// @filename: options.ts +//// import { Chart } from './index.esm'; +//// +//// const chart = new Chart({ +//// options: { +//// callback(tickValue) { +//// /*a*/const value = this.getLabelForValue(tickValue as number);/*b*/ +//// return '$' + value; +//// } +//// } +//// }); + +goTo.file("options.ts"); +verify.noErrors(); +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Extract Symbol", + actionName: "function_scope_0", + actionDescription: "Extract to inner function in method 'callback'", + newContent: +`TODO` +}); From cad26666515ef62b6fa9f7d2bcecd13e6cd358de Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 21 Jul 2022 16:52:17 -0700 Subject: [PATCH 2/3] Don't attempt to skipToEndOf a synthetic node during formatting --- src/services/formatting/formatting.ts | 4 ++-- ...s => extractFunctionWithSyntheticNodes.ts} | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) rename tests/cases/fourslash/{issue49310.ts => extractFunctionWithSyntheticNodes.ts} (64%) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 609246d0d7253..8c47cd6f8588b 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -716,7 +716,7 @@ namespace ts.formatting { } // child node is outside the target range - do not dive inside - if (!rangeOverlapsWithStartEnd(originalRange, child.pos, child.end)) { + if (!nodeIsSynthesized(child) && !rangeOverlapsWithStartEnd(originalRange, child.pos, child.end)) { if (child.end < originalRange.pos) { formattingScanner.skipToEndOf(child); } @@ -784,7 +784,7 @@ namespace ts.formatting { let listDynamicIndentation = parentDynamicIndentation; let startLine = parentStartLine; // node range is outside the target range - do not dive inside - if (!rangeOverlapsWithStartEnd(originalRange, nodes.pos, nodes.end)) { + if (!nodeIsSynthesized(nodes) && !rangeOverlapsWithStartEnd(originalRange, nodes.pos, nodes.end)) { if (nodes.end < originalRange.pos) { formattingScanner.skipToEndOf(nodes); } diff --git a/tests/cases/fourslash/issue49310.ts b/tests/cases/fourslash/extractFunctionWithSyntheticNodes.ts similarity index 64% rename from tests/cases/fourslash/issue49310.ts rename to tests/cases/fourslash/extractFunctionWithSyntheticNodes.ts index f75efd648f80d..ef3081d375998 100644 --- a/tests/cases/fourslash/issue49310.ts +++ b/tests/cases/fourslash/extractFunctionWithSyntheticNodes.ts @@ -1,6 +1,9 @@ /// -// @filename: index.esm.d.ts +// @filename: /project/tsconfig.json +//// {} + +// @filename: /project/index.esm.d.ts //// export declare class Chart { //// constructor(config: ChartConfiguration); //// } @@ -22,7 +25,7 @@ //// getLabelForValue(value: number): string; //// } -// @filename: options.ts +// @filename: /project/options.ts //// import { Chart } from './index.esm'; //// //// const chart = new Chart({ @@ -34,7 +37,7 @@ //// } //// }); -goTo.file("options.ts"); +goTo.file("/project/options.ts"); verify.noErrors(); goTo.select("a", "b"); edit.applyRefactor({ @@ -42,5 +45,18 @@ edit.applyRefactor({ actionName: "function_scope_0", actionDescription: "Extract to inner function in method 'callback'", newContent: -`TODO` +`import { Chart } from './index.esm'; + +const chart = new Chart({ + options: { + callback(tickValue) { + const value = /*RENAME*/newFunction.call(this); + return '$' + value; + + function newFunction(this: import("/project/index.esm").Scale) { + return this.getLabelForValue(tickValue as number); + } + } + } +});` }); From 06b06229450ab58306bbc8571a8d407d79ad2636 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 22 Jul 2022 16:35:52 -0700 Subject: [PATCH 3/3] Remove uses of visitNodes and visitNode in visitEachChild --- src/compiler/visitorPublic.ts | 12 ++++++------ src/services/formatting/formatting.ts | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 619348685efbc..372eb47c50307 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -616,7 +616,7 @@ namespace ts { nodeVisitor(node.argument, visitor, isTypeNode), nodeVisitor(node.assertions, visitor, isNode), nodeVisitor(node.qualifier, visitor, isEntityName), - visitNodes(node.typeArguments, visitor, isTypeNode), + nodesVisitor(node.typeArguments, visitor, isTypeNode), node.isTypeOf ); @@ -630,10 +630,10 @@ namespace ts { case SyntaxKind.NamedTupleMember: Debug.type(node); return factory.updateNamedTupleMember(node, - visitNode(node.dotDotDotToken, visitor, isDotDotDotToken), - visitNode(node.name, visitor, isIdentifier), - visitNode(node.questionToken, visitor, isQuestionToken), - visitNode(node.type, visitor, isTypeNode), + nodeVisitor(node.dotDotDotToken, visitor, isDotDotDotToken), + nodeVisitor(node.name, visitor, isIdentifier), + nodeVisitor(node.questionToken, visitor, isQuestionToken), + nodeVisitor(node.type, visitor, isTypeNode), ); case SyntaxKind.ParenthesizedType: @@ -761,7 +761,7 @@ namespace ts { Debug.type(node); return factory.updateTaggedTemplateExpression(node, nodeVisitor(node.tag, visitor, isExpression), - visitNodes(node.typeArguments, visitor, isTypeNode), + nodesVisitor(node.typeArguments, visitor, isTypeNode), nodeVisitor(node.template, visitor, isTemplateLiteral)); case SyntaxKind.TypeAssertionExpression: diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 8c47cd6f8588b..3f81d4d374da8 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -691,6 +691,7 @@ namespace ts.formatting { undecoratedParentStartLine: number, isListItem: boolean, isFirstListItem?: boolean): number { + Debug.assert(!nodeIsSynthesized(child)); if (nodeIsMissing(child)) { return inheritedIndentation; @@ -716,7 +717,7 @@ namespace ts.formatting { } // child node is outside the target range - do not dive inside - if (!nodeIsSynthesized(child) && !rangeOverlapsWithStartEnd(originalRange, child.pos, child.end)) { + if (!rangeOverlapsWithStartEnd(originalRange, child.pos, child.end)) { if (child.end < originalRange.pos) { formattingScanner.skipToEndOf(child); } @@ -778,13 +779,14 @@ namespace ts.formatting { parentStartLine: number, parentDynamicIndentation: DynamicIndentation): void { Debug.assert(isNodeArray(nodes)); + Debug.assert(!nodeIsSynthesized(nodes)); const listStartToken = getOpenTokenForList(parent, nodes); let listDynamicIndentation = parentDynamicIndentation; let startLine = parentStartLine; // node range is outside the target range - do not dive inside - if (!nodeIsSynthesized(nodes) && !rangeOverlapsWithStartEnd(originalRange, nodes.pos, nodes.end)) { + if (!rangeOverlapsWithStartEnd(originalRange, nodes.pos, nodes.end)) { if (nodes.end < originalRange.pos) { formattingScanner.skipToEndOf(nodes); }