From 73826bdb7b921e1bf497f97e95ef9ce5e01e5857 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 3 Oct 2017 15:39:12 -0700 Subject: [PATCH] Allow Extract Constant into enclosing scope in spite of RangeFacts.UsesThis --- src/harness/unittests/extractConstants.ts | 24 +++++++++++++++++ src/services/refactors/extractSymbol.ts | 5 +++- .../extractConstant_This_Constructor.js | 16 ++++++++++++ .../extractConstant_This_Constructor.ts | 26 +++++++++++++++++++ .../extractConstant_This_Method.js | 16 ++++++++++++ .../extractConstant_This_Method.ts | 26 +++++++++++++++++++ .../extractConstant_This_Property.ts | 18 +++++++++++++ tests/cases/fourslash/extract-method20.ts | 5 ++-- 8 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/extractConstant/extractConstant_This_Constructor.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_This_Constructor.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_This_Method.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_This_Method.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_This_Property.ts diff --git a/src/harness/unittests/extractConstants.ts b/src/harness/unittests/extractConstants.ts index c5ddc18fea4..09f8db34f31 100644 --- a/src/harness/unittests/extractConstants.ts +++ b/src/harness/unittests/extractConstants.ts @@ -230,6 +230,30 @@ function f(): void { } testExtractConstantFailed("extractConstant_Never", ` function f(): never { } [#|f();|]`); + + testExtractConstant("extractConstant_This_Constructor", ` +class C { + constructor() { + [#|this.m2()|]; + } + m2() { return 1; } +}`); + + testExtractConstant("extractConstant_This_Method", ` +class C { + m1() { + [#|this.m2()|]; + } + m2() { return 1; } +}`); + + testExtractConstant("extractConstant_This_Property", ` +namespace N { // Force this test to be TS-only + class C { + x = 1; + y = [#|this.x|]; + } +}`); }); function testExtractConstant(caption: string, text: string) { diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index a03bb843600..9dd148e5420 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -476,7 +476,10 @@ namespace ts.refactor.extractSymbol { // if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class const containingClass = getContainingClass(current); if (containingClass) { - return [containingClass]; + const containingFunction = findAncestor(current, isFunctionLikeDeclaration); + return containingFunction + ? [containingFunction, containingClass] + : [containingClass]; } } diff --git a/tests/baselines/reference/extractConstant/extractConstant_This_Constructor.js b/tests/baselines/reference/extractConstant/extractConstant_This_Constructor.js new file mode 100644 index 00000000000..cf45ab2cd3f --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_This_Constructor.js @@ -0,0 +1,16 @@ +// ==ORIGINAL== + +class C { + constructor() { + /*[#|*/this.m2()/*|]*/; + } + m2() { return 1; } +} +// ==SCOPE::Extract to constant in enclosing scope== + +class C { + constructor() { + const /*RENAME*/newLocal = this.m2(); + } + m2() { return 1; } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_This_Constructor.ts b/tests/baselines/reference/extractConstant/extractConstant_This_Constructor.ts new file mode 100644 index 00000000000..d36d1a6fa21 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_This_Constructor.ts @@ -0,0 +1,26 @@ +// ==ORIGINAL== + +class C { + constructor() { + /*[#|*/this.m2()/*|]*/; + } + m2() { return 1; } +} +// ==SCOPE::Extract to constant in enclosing scope== + +class C { + constructor() { + const /*RENAME*/newLocal = this.m2(); + } + m2() { return 1; } +} +// ==SCOPE::Extract to readonly field in class 'C'== + +class C { + private readonly newProperty = this.m2(); + + constructor() { + this./*RENAME*/newProperty; + } + m2() { return 1; } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_This_Method.js b/tests/baselines/reference/extractConstant/extractConstant_This_Method.js new file mode 100644 index 00000000000..fd703868e9f --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_This_Method.js @@ -0,0 +1,16 @@ +// ==ORIGINAL== + +class C { + m1() { + /*[#|*/this.m2()/*|]*/; + } + m2() { return 1; } +} +// ==SCOPE::Extract to constant in enclosing scope== + +class C { + m1() { + const /*RENAME*/newLocal = this.m2(); + } + m2() { return 1; } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_This_Method.ts b/tests/baselines/reference/extractConstant/extractConstant_This_Method.ts new file mode 100644 index 00000000000..0dbaa4372d4 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_This_Method.ts @@ -0,0 +1,26 @@ +// ==ORIGINAL== + +class C { + m1() { + /*[#|*/this.m2()/*|]*/; + } + m2() { return 1; } +} +// ==SCOPE::Extract to constant in enclosing scope== + +class C { + m1() { + const /*RENAME*/newLocal = this.m2(); + } + m2() { return 1; } +} +// ==SCOPE::Extract to readonly field in class 'C'== + +class C { + private readonly newProperty = this.m2(); + + m1() { + this./*RENAME*/newProperty; + } + m2() { return 1; } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_This_Property.ts b/tests/baselines/reference/extractConstant/extractConstant_This_Property.ts new file mode 100644 index 00000000000..04b3b50da1b --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_This_Property.ts @@ -0,0 +1,18 @@ +// ==ORIGINAL== + +namespace N { // Force this test to be TS-only + class C { + x = 1; + y = /*[#|*/this.x/*|]*/; + } +} +// ==SCOPE::Extract to readonly field in class 'C'== + +namespace N { // Force this test to be TS-only + class C { + x = 1; + private readonly newProperty = this.x; + + y = this./*RENAME*/newProperty; + } +} \ No newline at end of file diff --git a/tests/cases/fourslash/extract-method20.ts b/tests/cases/fourslash/extract-method20.ts index 75927f0fd5c..bd137c55d19 100644 --- a/tests/cases/fourslash/extract-method20.ts +++ b/tests/cases/fourslash/extract-method20.ts @@ -10,5 +10,6 @@ //// } goTo.select('a', 'b') -verify.refactorAvailable('Extract Symbol', 'function_scope_0'); -verify.not.refactorAvailable('Extract Symbol', 'function_scope_1'); +verify.not.refactorAvailable('Extract Symbol', 'function_scope_0'); +verify.refactorAvailable('Extract Symbol', 'function_scope_1'); +verify.not.refactorAvailable('Extract Symbol', 'function_scope_2');