Skip to content
15 changes: 12 additions & 3 deletions src/harness/fourslashImpl.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

namespace FourSlash {
import ArrayOrSingle = FourSlashInterface.ArrayOrSingle;

Expand Down Expand Up @@ -3481,8 +3480,18 @@ namespace FourSlash {

public verifyRefactorAvailable(negative: boolean, triggerReason: ts.RefactorTriggerReason, name: string, actionName?: string, actionDescription?: string) {
let refactors = this.getApplicableRefactorsAtSelection(triggerReason);
refactors = refactors.filter(r =>
r.name === name && (actionName === undefined || r.actions.some(a => a.name === actionName)) && (actionDescription === undefined || r.actions.some(a => a.description === actionDescription)));
refactors = refactors.filter(r => r.name === name);

if (actionName !== undefined) {
refactors.forEach(r => r.actions = r.actions.filter(a => a.name === actionName));
}

if (actionDescription !== undefined) {
refactors.forEach(r => r.actions = r.actions.filter(a => a.description === actionDescription));
}

refactors = refactors.filter(r => r.actions.length > 0);

const isAvailable = refactors.length > 0;

if (negative) {
Expand Down
4 changes: 2 additions & 2 deletions src/services/refactors/extractSymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ namespace ts.refactor.extractSymbol {

let i = 0;
for (const { functionExtraction, constantExtraction } of extractions) {
const description = functionExtraction.description;
if (refactorKindBeginsWith(extractFunctionAction.kind, requestedRefactor)) {
const description = functionExtraction.description;
if (functionExtraction.errors.length === 0) {
// Don't issue refactorings with duplicated names.
// Scopes come back in "innermost first" order, so extractions will
Expand All @@ -95,11 +95,11 @@ namespace ts.refactor.extractSymbol {
}

if (refactorKindBeginsWith(extractConstantAction.kind, requestedRefactor)) {
const description = constantExtraction.description;
if (constantExtraction.errors.length === 0) {
// Don't issue refactorings with duplicated names.
// Scopes come back in "innermost first" order, so extractions will
// preferentially go into nearer scopes
const description = constantExtraction.description;
if (!usedConstantNames.has(description)) {
usedConstantNames.set(description, true);
constantActions.push({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// <reference path="../fourslash.ts" />

//// function foo(): void { /*x*/console.log('a');/*y*/ }

goTo.select("x","y");
verify.refactorAvailable("Extract Symbol", 'function_scope_0', "Extract to inner function in function 'foo'");
verify.refactorAvailable("Extract Symbol", 'function_scope_1', "Extract to function in global scope");

verify.not.refactorAvailable("Extract Symbol", 'constant_scope_0', "Extract to inner function in function 'foo'");

verify.refactorAvailable("Extract Symbol", 'constant_scope_0', "Extract to constant in enclosing scope");
verify.refactorAvailable("Extract Symbol", 'constant_scope_1', "Extract to constant in global scope");

verify.not.refactorAvailable("Extract Symbol", 'constant_scope_0', "Extract to constant in global scope");