From a9b5140c613460827ecf4a6d65c95eef59f851a8 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Wed, 9 Dec 2020 16:35:27 -0800 Subject: [PATCH 01/20] add hierarchical refactoring strings --- src/server/protocol.ts | 3 +- src/server/session.ts | 2 +- src/services/refactorProvider.ts | 9 +++++- .../addOrRemoveBracesToArrowFunction.ts | 22 ++++++++++---- ...onvertArrowFunctionOrFunctionExpression.ts | 11 ++++++- src/services/refactors/convertExport.ts | 17 +++++++++-- src/services/refactors/convertImport.ts | 22 ++++++++++---- .../convertOverloadListToSingleSignature.ts | 6 ++-- .../convertParamsToDestructuredObject.ts | 6 ++-- .../convertStringOrTemplateLiteral.ts | 5 ++-- .../convertToOptionalChainExpression.ts | 9 ++++-- src/services/refactors/extractSymbol.ts | 30 ++++++++++++++----- src/services/refactors/extractType.ts | 23 ++++++++++---- .../generateGetAccessorAndSetAccessor.ts | 9 ++++-- .../refactors/inferFunctionReturnType.ts | 4 ++- src/services/refactors/moveToNewFile.ts | 3 ++ src/services/services.ts | 7 +++-- src/services/types.ts | 11 ++++++- .../reference/api/tsserverlibrary.d.ts | 7 ++++- tests/baselines/reference/api/typescript.d.ts | 6 +++- 20 files changed, 163 insertions(+), 49 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 1f77bbfd7eb45..d80aced54229a 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -563,7 +563,8 @@ namespace ts.server.protocol { arguments: GetApplicableRefactorsRequestArgs; } export type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs & { - triggerReason?: RefactorTriggerReason + triggerReason?: RefactorTriggerReason; + refactorKind?: string; }; export type RefactorTriggerReason = "implicit" | "invoked"; diff --git a/src/server/session.ts b/src/server/session.ts index c23656dc3b1b3..a605e621ca4c6 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -2100,7 +2100,7 @@ namespace ts.server { private getApplicableRefactors(args: protocol.GetApplicableRefactorsRequestArgs): protocol.ApplicableRefactorInfo[] { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file)!; - return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason); + return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason, args.refactorKind); } private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo { diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 42bda231be6f2..3398225854376 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -11,11 +11,18 @@ namespace ts.refactor { export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] { return arrayFrom(flatMapIterator(refactors.values(), refactor => - context.cancellationToken && context.cancellationToken.isCancellationRequested() ? undefined : refactor.getAvailableActions(context))); + context.cancellationToken && context.cancellationToken.isCancellationRequested() ? + undefined : context.refactorKind && !hasMatchingRefactorKind(refactor.refactorKinds, context.refactorKind) ? + undefined : refactor.getAvailableActions(context))); } export function getEditsForRefactor(context: RefactorContext, refactorName: string, actionName: string): RefactorEditInfo | undefined { const refactor = refactors.get(refactorName); return refactor && refactor.getEditsForAction(context, actionName); } + + export function hasMatchingRefactorKind(refactorKinds: string[] | undefined, match: string | undefined): boolean { + if (!refactorKinds || !match) return false; + return refactorKinds.some(refactorKind => refactorKind.substr(0, match.length) === match); + } } diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index a2a14152da738..1afd4740de51d 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -6,7 +6,15 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { const removeBracesActionName = "Remove braces from arrow function"; const addBracesActionDescription = Diagnostics.Add_braces_to_arrow_function.message; const removeBracesActionDescription = Diagnostics.Remove_braces_from_arrow_function.message; - registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); + + const rewriteArrowBracesRemoveKind = "refactor.rewrite.arrow.braces.remove"; + const rewriteArrowBracesAddKind = "refactor.rewrite.arrow.braces.add"; + const refactorKinds = [ + rewriteArrowBracesAddKind, + rewriteArrowBracesRemoveKind + ]; + + registerRefactor(refactorName, { refactorKinds, getEditsForAction, getAvailableActions }); interface Info { func: ArrowFunction; @@ -36,10 +44,12 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { info.info.addBraces ? { name: addBracesActionName, - description: addBracesActionDescription + description: addBracesActionDescription, + refactorKind: rewriteArrowBracesAddKind } : { name: removeBracesActionName, - description: removeBracesActionDescription + description: removeBracesActionDescription, + refactorKind: rewriteArrowBracesRemoveKind } ] }]; @@ -52,11 +62,13 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { actions: [{ name: addBracesActionName, description: addBracesActionDescription, - notApplicableReason: info.error + notApplicableReason: info.error, + refactorKind: rewriteArrowBracesAddKind }, { name: removeBracesActionName, description: removeBracesActionDescription, - notApplicableReason: info.error + notApplicableReason: info.error, + refactorKind: rewriteArrowBracesRemoveKind }] }]; } diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index c9ef923534f7d..366aff3f8701c 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -11,7 +11,16 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const toNamedFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_named_function); const toArrowFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_arrow_function); - registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); + const rewriteFunctionAnonymousKind = "refactor.rewrite.function.anonymous"; + const rewriteFunctionArrowKind = "refactor.rewite.function.arrow"; + const rewriteFunctioNamedKind = "refactor.rewite.function.named"; + const refactorKinds = [ + rewriteFunctionAnonymousKind, + rewriteFunctionArrowKind, + rewriteFunctioNamedKind, + ]; + + registerRefactor(refactorName, { refactorKinds, getEditsForAction, getAvailableActions }); interface FunctionInfo { readonly selectedVariableDeclaration: boolean; diff --git a/src/services/refactors/convertExport.ts b/src/services/refactors/convertExport.ts index 6912979261a87..7866fd844c996 100644 --- a/src/services/refactors/convertExport.ts +++ b/src/services/refactors/convertExport.ts @@ -4,7 +4,15 @@ namespace ts.refactor { const actionNameDefaultToNamed = "Convert default export to named export"; const actionNameNamedToDefault = "Convert named export to default export"; + const rewriteExportToNamedKind = "refactor.rewrite.export.named"; + const rewriteExportToDefaultKind = "refactor.rewrite.export.default"; + const refactorKinds = [ + rewriteExportToDefaultKind, + rewriteExportToNamedKind + ]; + registerRefactor(refactorName, { + refactorKinds, getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getInfo(context, context.triggerReason === "invoked"); if (!info) return emptyArray; @@ -12,13 +20,16 @@ namespace ts.refactor { if (info.error === undefined) { const description = info.info.wasDefault ? Diagnostics.Convert_default_export_to_named_export.message : Diagnostics.Convert_named_export_to_default_export.message; const actionName = info.info.wasDefault ? actionNameDefaultToNamed : actionNameNamedToDefault; - return [{ name: refactorName, description, actions: [{ name: actionName, description }] }]; + const refactorKind = info.info.wasDefault ? rewriteExportToNamedKind : rewriteExportToDefaultKind; + return [{ name: refactorName, description, actions: [{ name: actionName, description, refactorKind }] }]; } if (context.preferences.provideRefactorNotApplicableReason) { return [ - { name: refactorName, description: Diagnostics.Convert_default_export_to_named_export.message, actions: [{ name: actionNameDefaultToNamed, description: Diagnostics.Convert_default_export_to_named_export.message, notApplicableReason: info.error }] }, - { name: refactorName, description: Diagnostics.Convert_named_export_to_default_export.message, actions: [{ name: actionNameNamedToDefault, description: Diagnostics.Convert_named_export_to_default_export.message, notApplicableReason: info.error }] }, + { name: refactorName, description: Diagnostics.Convert_default_export_to_named_export.message, + actions: [{ name: actionNameDefaultToNamed, description: Diagnostics.Convert_default_export_to_named_export.message, notApplicableReason: info.error, refactorKind: rewriteExportToNamedKind }] }, + { name: refactorName, description: Diagnostics.Convert_named_export_to_default_export.message, + actions: [{ name: actionNameNamedToDefault, description: Diagnostics.Convert_named_export_to_default_export.message, notApplicableReason: info.error, refactorKind: rewriteExportToDefaultKind }] }, ]; } diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index 5dec7fcf7368f..644b9d0c01936 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -4,6 +4,13 @@ namespace ts.refactor { const actionNameNamespaceToNamed = "Convert namespace import to named imports"; const actionNameNamedToNamespace = "Convert named imports to namespace import"; + const rewriteImportToNamedKind = "refactor.rewrite.import.named"; + const rewriteImportToNamespaceKind = "refactor.rewrite.import.namespace"; + const refactorKinds = [ + rewriteImportToNamedKind, + rewriteImportToNamespaceKind + ]; + type NamedImportBindingsOrError = { info: NamedImportBindings, error?: never @@ -13,20 +20,25 @@ namespace ts.refactor { }; registerRefactor(refactorName, { + refactorKinds, getAvailableActions(context): readonly ApplicableRefactorInfo[] { const i = getImportToConvert(context, context.triggerReason === "invoked"); if (!i) return emptyArray; if (i.error === undefined) { - const description = i.info.kind === SyntaxKind.NamespaceImport ? Diagnostics.Convert_namespace_import_to_named_imports.message : Diagnostics.Convert_named_imports_to_namespace_import.message; - const actionName = i.info.kind === SyntaxKind.NamespaceImport ? actionNameNamespaceToNamed : actionNameNamedToNamespace; - return [{ name: refactorName, description, actions: [{ name: actionName, description }] }]; + const namespaceImport = i.info.kind === SyntaxKind.NamespaceImport; + const description = namespaceImport ? Diagnostics.Convert_namespace_import_to_named_imports.message : Diagnostics.Convert_named_imports_to_namespace_import.message; + const actionName = namespaceImport ? actionNameNamespaceToNamed : actionNameNamedToNamespace; + const refactorKind = namespaceImport ? rewriteImportToNamedKind : rewriteImportToNamespaceKind; + return [{ name: refactorName, description, actions: [{ name: actionName, description, refactorKind }] }]; } if (context.preferences.provideRefactorNotApplicableReason) { return [ - { name: refactorName, description: Diagnostics.Convert_namespace_import_to_named_imports.message, actions: [{ name: actionNameNamespaceToNamed, description: Diagnostics.Convert_namespace_import_to_named_imports.message, notApplicableReason: i.error }] }, - { name: refactorName, description: Diagnostics.Convert_named_imports_to_namespace_import.message, actions: [{ name: actionNameNamedToNamespace, description: Diagnostics.Convert_named_imports_to_namespace_import.message, notApplicableReason: i.error }] } + { name: refactorName, description: Diagnostics.Convert_namespace_import_to_named_imports.message, + actions: [{ name: actionNameNamespaceToNamed, description: Diagnostics.Convert_namespace_import_to_named_imports.message, notApplicableReason: i.error, refactorKind: rewriteImportToNamedKind }] }, + { name: refactorName, description: Diagnostics.Convert_named_imports_to_namespace_import.message, + actions: [{ name: actionNameNamedToNamespace, description: Diagnostics.Convert_named_imports_to_namespace_import.message, notApplicableReason: i.error, refactorKind: rewriteImportToNamespaceKind }] } ]; } diff --git a/src/services/refactors/convertOverloadListToSingleSignature.ts b/src/services/refactors/convertOverloadListToSingleSignature.ts index fbcb6fece0cb4..a48b932d8ad9f 100644 --- a/src/services/refactors/convertOverloadListToSingleSignature.ts +++ b/src/services/refactors/convertOverloadListToSingleSignature.ts @@ -2,8 +2,9 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { const refactorName = "Convert overload list to single signature"; const refactorDescription = Diagnostics.Convert_overload_list_to_single_signature.message; - registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); + const rewriteFunctionOverloadListKind = "refactor.rewrite.function.overloadList"; + registerRefactor(refactorName, { refactorKinds: [rewriteFunctionOverloadListKind], getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition, program } = context; @@ -15,7 +16,8 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { description: refactorDescription, actions: [{ name: refactorName, - description: refactorDescription + description: refactorDescription, + refactorKind: rewriteFunctionOverloadListKind }] }]; } diff --git a/src/services/refactors/convertParamsToDestructuredObject.ts b/src/services/refactors/convertParamsToDestructuredObject.ts index ec2277b5da8a7..07ebfbc7c29da 100644 --- a/src/services/refactors/convertParamsToDestructuredObject.ts +++ b/src/services/refactors/convertParamsToDestructuredObject.ts @@ -2,8 +2,9 @@ namespace ts.refactor.convertParamsToDestructuredObject { const refactorName = "Convert parameters to destructured object"; const minimumParameterLength = 2; - registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); + const rewriteParametersToDestructuredKind = "refactor.rewrite.parameters.toDestructured"; + registerRefactor(refactorName, { refactorKinds: [rewriteParametersToDestructuredKind], getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition } = context; @@ -18,7 +19,8 @@ namespace ts.refactor.convertParamsToDestructuredObject { description, actions: [{ name: refactorName, - description + description, + refactorKind: rewriteParametersToDestructuredKind }] }]; } diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index 7641987153d63..025f7d16980ec 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -2,8 +2,9 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const refactorName = "Convert to template string"; const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_template_string); + const rewriteStringKind = "refactor.rewrite.string"; - registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { refactorKinds: [rewriteStringKind], getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition } = context; @@ -12,7 +13,7 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const refactorInfo: ApplicableRefactorInfo = { name: refactorName, description: refactorDescription, actions: [] }; if (isBinaryExpression(maybeBinary) && isStringConcatenationValid(maybeBinary)) { - refactorInfo.actions.push({ name: refactorName, description: refactorDescription }); + refactorInfo.actions.push({ name: refactorName, description: refactorDescription, refactorKind: rewriteStringKind }); return [refactorInfo]; } return emptyArray; diff --git a/src/services/refactors/convertToOptionalChainExpression.ts b/src/services/refactors/convertToOptionalChainExpression.ts index 52365fc0ba597..5a84fa5712672 100644 --- a/src/services/refactors/convertToOptionalChainExpression.ts +++ b/src/services/refactors/convertToOptionalChainExpression.ts @@ -2,8 +2,9 @@ namespace ts.refactor.convertToOptionalChainExpression { const refactorName = "Convert to optional chain expression"; const convertToOptionalChainExpressionMessage = getLocaleSpecificMessage(Diagnostics.Convert_to_optional_chain_expression); + const rewriteExpressionOptionalChainKind = "refactor.rewrite.expression.optionalChain"; - registerRefactor(refactorName, { getAvailableActions, getEditsForAction }); + registerRefactor(refactorName, { refactorKinds: [rewriteExpressionOptionalChainKind], getAvailableActions, getEditsForAction }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const info = getInfo(context, context.triggerReason === "invoked"); @@ -15,7 +16,8 @@ namespace ts.refactor.convertToOptionalChainExpression { description: convertToOptionalChainExpressionMessage, actions: [{ name: refactorName, - description: convertToOptionalChainExpressionMessage + description: convertToOptionalChainExpressionMessage, + refactorKind: rewriteExpressionOptionalChainKind }] }]; } @@ -27,7 +29,8 @@ namespace ts.refactor.convertToOptionalChainExpression { actions: [{ name: refactorName, description: convertToOptionalChainExpressionMessage, - notApplicableReason: info.error + notApplicableReason: info.error, + refactorKind: rewriteExpressionOptionalChainKind }] }]; } diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 73c8ca01e4cdc..dca9fbdd7d3ee 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1,7 +1,15 @@ /* @internal */ namespace ts.refactor.extractSymbol { const refactorName = "Extract Symbol"; - registerRefactor(refactorName, { getAvailableActions, getEditsForAction }); + + const extractConstantKind = "refactor.extract.constant"; + const extractFunctionKind = "refactor.extract.function"; + const refactorKinds = [ + extractConstantKind, + extractFunctionKind, + ]; + + registerRefactor(refactorName, { refactorKinds, getAvailableActions, getEditsForAction }); /** * Compute the associated code actions @@ -22,7 +30,8 @@ namespace ts.refactor.extractSymbol { actions: [{ description: getLocaleSpecificMessage(Diagnostics.Extract_function), name: "function_extract_error", - notApplicableReason: getStringError(rangeToExtract.errors) + notApplicableReason: getStringError(rangeToExtract.errors), + refactorKind: extractFunctionKind }] }, { @@ -31,7 +40,8 @@ namespace ts.refactor.extractSymbol { actions: [{ description: getLocaleSpecificMessage(Diagnostics.Extract_constant), name: "constant_extract_error", - notApplicableReason: getStringError(rangeToExtract.errors) + notApplicableReason: getStringError(rangeToExtract.errors), + refactorKind: extractConstantKind }] }]; } @@ -61,7 +71,8 @@ namespace ts.refactor.extractSymbol { usedFunctionNames.set(description, true); functionActions.push({ description, - name: `function_scope_${i}` + name: `function_scope_${i}`, + refactorKind: extractFunctionKind }); } } @@ -69,7 +80,8 @@ namespace ts.refactor.extractSymbol { innermostErrorFunctionAction = { description, name: `function_scope_${i}`, - notApplicableReason: getStringError(functionExtraction.errors) + notApplicableReason: getStringError(functionExtraction.errors), + refactorKind: extractFunctionKind }; } @@ -83,7 +95,8 @@ namespace ts.refactor.extractSymbol { usedConstantNames.set(description, true); constantActions.push({ description, - name: `constant_scope_${i}` + name: `constant_scope_${i}`, + refactorKind: extractConstantKind }); } } @@ -91,7 +104,8 @@ namespace ts.refactor.extractSymbol { innermostErrorConstantAction = { description, name: `constant_scope_${i}`, - notApplicableReason: getStringError(constantExtraction.errors) + notApplicableReason: getStringError(constantExtraction.errors), + refactorKind: extractConstantKind }; } @@ -106,7 +120,7 @@ namespace ts.refactor.extractSymbol { infos.push({ name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_function), - actions: functionActions + actions: functionActions, }); } else if (context.preferences.provideRefactorNotApplicableReason && innermostErrorFunctionAction) { diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts index 7f528ae245d4f..eb27156112592 100644 --- a/src/services/refactors/extractType.ts +++ b/src/services/refactors/extractType.ts @@ -4,7 +4,18 @@ namespace ts.refactor { const extractToTypeAlias = "Extract to type alias"; const extractToInterface = "Extract to interface"; const extractToTypeDef = "Extract to typedef"; + + const extractToTypeKind = "refactor.extract.type"; + const extractToInterfaceKind = "refactor.extract.interface"; + const extractToTypeDefKind = "refactor.extract.typedef"; + const refactorKinds = [ + extractToTypeKind, + extractToInterfaceKind, + extractToTypeDefKind, + ]; + registerRefactor(refactorName, { + refactorKinds, getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getRangeToExtract(context, context.triggerReason === "invoked"); if (!info) return emptyArray; @@ -14,11 +25,11 @@ namespace ts.refactor { name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_type), actions: info.info.isJS ? [{ - name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef) + name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef), refactorKind: extractToTypeDefKind }] : append([{ - name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias) + name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias), refactorKind: extractToTypeKind }], info.info.typeElements && { - name: extractToInterface, description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface) + name: extractToInterface, description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface), refactorKind: extractToInterfaceKind }) }]; } @@ -28,9 +39,9 @@ namespace ts.refactor { name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_type), actions: [ - { name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef), notApplicableReason: info.error }, - { name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias), notApplicableReason: info.error }, - { name: extractToInterface, description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface), notApplicableReason: info.error }, + { name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef), notApplicableReason: info.error, refactorKind: extractToTypeDefKind }, + { name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias), notApplicableReason: info.error, refactorKind: extractToTypeKind }, + { name: extractToInterface, description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface), notApplicableReason: info.error, refactorKind: extractToInterfaceKind }, ] }]; } diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index 160f4f1b5ae73..055979d8d91c1 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -2,7 +2,10 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { const actionName = "Generate 'get' and 'set' accessors"; const actionDescription = Diagnostics.Generate_get_and_set_accessors.message; + const rewritePropertyGenerateAccessors = "refactor.rewrite.property.generateAccessors"; + registerRefactor(actionName, { + refactorKinds: [rewritePropertyGenerateAccessors], getEditsForAction(context, actionName) { if (!context.endPosition) return undefined; const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition); @@ -29,7 +32,8 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { actions: [ { name: actionName, - description: actionDescription + description: actionDescription, + refactorKind: rewritePropertyGenerateAccessors } ] }]; @@ -42,7 +46,8 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { actions: [{ name: actionName, description: actionDescription, - notApplicableReason: info.error + notApplicableReason: info.error, + refactorKind: rewritePropertyGenerateAccessors }] }]; } diff --git a/src/services/refactors/inferFunctionReturnType.ts b/src/services/refactors/inferFunctionReturnType.ts index 3f46c0edb4ccc..d30e81d251f0c 100644 --- a/src/services/refactors/inferFunctionReturnType.ts +++ b/src/services/refactors/inferFunctionReturnType.ts @@ -2,7 +2,9 @@ namespace ts.refactor.inferFunctionReturnType { const refactorName = "Infer function return type"; const refactorDescription = Diagnostics.Infer_function_return_type.message; - registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); + const rewriteFunctionReturnType = "refactor.rewrite.function.returnType"; + + registerRefactor(refactorName, { refactorKinds: [rewriteFunctionReturnType], getEditsForAction, getAvailableActions }); function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined { const info = getInfo(context); diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index bf5609620e6e1..947ce5ab9fe1e 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -1,7 +1,10 @@ /* @internal */ namespace ts.refactor { const refactorName = "Move to a new file"; + const refactorMoveNewFile = "refactor.move.newFile"; + registerRefactor(refactorName, { + refactorKinds: [refactorMoveNewFile], getAvailableActions(context): readonly ApplicableRefactorInfo[] { if (!context.preferences.allowTextChangesInNewFiles || getStatementsToMove(context) === undefined) return emptyArray; const description = getLocaleSpecificMessage(Diagnostics.Move_to_a_new_file); diff --git a/src/services/services.ts b/src/services/services.ts index a891c2297d6d7..5a3de870d8255 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2446,7 +2446,7 @@ namespace ts { return Rename.getRenameInfo(program, getValidSourceFile(fileName), position, options); } - function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings, triggerReason?: RefactorTriggerReason): RefactorContext { + function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings, triggerReason?: RefactorTriggerReason, refactorKind?: string): RefactorContext { const [startPosition, endPosition] = typeof positionOrRange === "number" ? [positionOrRange, undefined] : [positionOrRange.pos, positionOrRange.end]; return { file, @@ -2458,6 +2458,7 @@ namespace ts { cancellationToken, preferences, triggerReason, + refactorKind }; } @@ -2465,10 +2466,10 @@ namespace ts { return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName)); } - function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason): ApplicableRefactorInfo[] { + function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, refactorKind: string): ApplicableRefactorInfo[] { synchronizeHostData(); const file = getValidSourceFile(fileName); - return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason)); + return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, refactorKind)); } function getEditsForRefactor( diff --git a/src/services/types.ts b/src/services/types.ts index bc7d7328a70ae..db38f00f68837 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -519,7 +519,7 @@ namespace ts { /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason): ApplicableRefactorInfo[]; + getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, refactorKind?: string): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; @@ -791,6 +791,11 @@ namespace ts { * the current context. */ notApplicableReason?: string; + + /** + * The hierarchical dotted name of the refactor action. + */ + refactorKind?: string; } /** @@ -1463,6 +1468,9 @@ namespace ts { /** @internal */ export interface Refactor { + /** list of hierarchical refactors as dotted strings */ + refactorKinds?: string[]; + /** Compute the associated code actions */ getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined; @@ -1479,5 +1487,6 @@ namespace ts { cancellationToken?: CancellationToken; preferences: UserPreferences; triggerReason?: RefactorTriggerReason; + refactorKind?: string; } } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 50b2adf403cfc..037f9ebc12652 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5566,7 +5566,7 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason): ApplicableRefactorInfo[]; + getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, refactorKind?: string): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; @@ -5792,6 +5792,10 @@ declare namespace ts { * the current context. */ notApplicableReason?: string; + /** + * The hierarchical dotted name of the refactor action. + */ + refactorKind?: string; } /** * A set of edits to make in response to a refactor action, plus an optional @@ -6921,6 +6925,7 @@ declare namespace ts.server.protocol { } type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs & { triggerReason?: RefactorTriggerReason; + refactorKind?: string; }; type RefactorTriggerReason = "implicit" | "invoked"; /** diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 141feee127961..3d95d01eb5838 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5566,7 +5566,7 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason): ApplicableRefactorInfo[]; + getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, refactorKind?: string): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; @@ -5792,6 +5792,10 @@ declare namespace ts { * the current context. */ notApplicableReason?: string; + /** + * The hierarchical dotted name of the refactor action. + */ + refactorKind?: string; } /** * A set of edits to make in response to a refactor action, plus an optional From 222762795cbda3a8b754a317ad963fbbf97dda0f Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Thu, 10 Dec 2020 13:05:37 -0800 Subject: [PATCH 02/20] fourslash tests --- src/harness/fourslashImpl.ts | 18 ++++++++++++------ src/harness/fourslashInterfaceImpl.ts | 4 ++++ tests/cases/fourslash/fourslash.ts | 1 + .../refactorKindsGeneralToSpecific.ts | 9 +++++++++ .../refactorKindsSpecificToSpecific.ts | 8 ++++++++ 5 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 tests/cases/fourslash/refactorKindsGeneralToSpecific.ts create mode 100644 tests/cases/fourslash/refactorKindsSpecificToSpecific.ts diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 4b285a749aa74..4b9a6da8bb90f 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3372,6 +3372,12 @@ namespace FourSlash { } } + public verifyRefactorKindsAvailable(refactorKind: string, expected: string[]) { + const refactors = this.getApplicableRefactorsAtSelection("invoked", refactorKind); + const availableKinds = refactors.reduce((a, b) => [...a, ...b.actions.map((action) => action.refactorKind)], []); + assert.deepEqual(availableKinds.sort(), expected.sort(), `Expected refactorKinds to be equal`); + } + public verifyRefactorsAvailable(names: readonly string[]): void { assert.deepEqual(unique(this.getApplicableRefactorsAtSelection(), r => r.name), names); } @@ -3785,14 +3791,14 @@ namespace FourSlash { test(renameKeys(newFileContents, key => pathUpdater(key) || key), "with file moved"); } - private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit") { - return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, ts.emptyOptions, triggerReason); + private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit", refactorKind?: string) { + return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, ts.emptyOptions, triggerReason, refactorKind); } - private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit"): readonly ts.ApplicableRefactorInfo[] { - return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason); // eslint-disable-line no-in-operator + private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit", refactorKind?: string): readonly ts.ApplicableRefactorInfo[] { + return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason, refactorKind); // eslint-disable-line no-in-operator } - private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason): readonly ts.ApplicableRefactorInfo[] { - return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason) || ts.emptyArray; + private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason, refactorKind?: string): readonly ts.ApplicableRefactorInfo[] { + return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, refactorKind) || ts.emptyArray; } public configurePlugin(pluginName: string, configuration: any): void { diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index e96641c2dbb88..7a0bccf510c56 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -215,6 +215,10 @@ namespace FourSlashInterface { this.state.verifyRefactorAvailable(this.negative, triggerReason, name, actionName); } + public refactorKindAvailable(refactorKind: string, expected: string[]) { + this.state.verifyRefactorKindsAvailable(refactorKind, expected); + } + public toggleLineComment(newFileContent: string) { this.state.toggleLineComment(newFileContent); } diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 98298bbfe5f21..5b4df26e77451 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -244,6 +244,7 @@ declare namespace FourSlashInterface { refactorAvailable(name: string, actionName?: string): void; refactorAvailableForTriggerReason(triggerReason: RefactorTriggerReason, name: string, action?: string): void; + refactorKindAvailable(refactorKind: string, expected: string[]): void; } class verify extends verifyNegatable { assertHasRanges(ranges: Range[]): void; diff --git a/tests/cases/fourslash/refactorKindsGeneralToSpecific.ts b/tests/cases/fourslash/refactorKindsGeneralToSpecific.ts new file mode 100644 index 0000000000000..2a76a6dff7662 --- /dev/null +++ b/tests/cases/fourslash/refactorKindsGeneralToSpecific.ts @@ -0,0 +1,9 @@ +/// + +//// const a = /*a*/1/*b*/; + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.extract", [ + "refactor.extract.constant", + "refactor.extract.function" +]); diff --git a/tests/cases/fourslash/refactorKindsSpecificToSpecific.ts b/tests/cases/fourslash/refactorKindsSpecificToSpecific.ts new file mode 100644 index 0000000000000..543178b39dae3 --- /dev/null +++ b/tests/cases/fourslash/refactorKindsSpecificToSpecific.ts @@ -0,0 +1,8 @@ +/// + +//// const a = /*a*/1/*b*/; + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.extract.constant", [ + "refactor.extract.constant" +]); From f331c1b3010dc31c7b406af852ecf9698970290a Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Thu, 10 Dec 2020 13:06:17 -0800 Subject: [PATCH 03/20] extractSymbol filters returned actions --- src/services/refactors/extractSymbol.ts | 124 ++++++++++++++---------- 1 file changed, 72 insertions(+), 52 deletions(-) diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index dca9fbdd7d3ee..05716d4d77a35 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -9,6 +9,15 @@ namespace ts.refactor.extractSymbol { extractFunctionKind, ]; + /** + * Checks if string "first" begins with string "second". + * Used to match requested refactorKinds with a known refactorKind. + */ + function refactorKindBeginsWith(first: string, second: string | undefined): boolean { + if(!second) return true; + return first.substr(0, second.length) === second; + } + registerRefactor(refactorName, { refactorKinds, getAvailableActions, getEditsForAction }); /** @@ -16,34 +25,41 @@ namespace ts.refactor.extractSymbol { * Exported for tests. */ export function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { + const requestedRefactor = context.refactorKind; const rangeToExtract = getRangeToExtract(context.file, getRefactorContextSpan(context), context.triggerReason === "invoked"); - const targetRange = rangeToExtract.targetRange; + if (targetRange === undefined) { if (!rangeToExtract.errors || rangeToExtract.errors.length === 0 || !context.preferences.provideRefactorNotApplicableReason) { return emptyArray; } - return [{ - name: refactorName, - description: getLocaleSpecificMessage(Diagnostics.Extract_function), - actions: [{ + const errors = []; + if (refactorKindBeginsWith(extractFunctionKind, requestedRefactor)) { + errors.push({ + name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_function), - name: "function_extract_error", - notApplicableReason: getStringError(rangeToExtract.errors), - refactorKind: extractFunctionKind - }] - }, - { - name: refactorName, - description: getLocaleSpecificMessage(Diagnostics.Extract_constant), - actions: [{ + actions: [{ + description: getLocaleSpecificMessage(Diagnostics.Extract_function), + name: "function_extract_error", + notApplicableReason: getStringError(rangeToExtract.errors), + refactorKind: extractFunctionKind + }] + }); + } + if (refactorKindBeginsWith(extractConstantKind, requestedRefactor)) { + errors.push({ + name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_constant), - name: "constant_extract_error", - notApplicableReason: getStringError(rangeToExtract.errors), - refactorKind: extractConstantKind - }] - }]; + actions: [{ + description: getLocaleSpecificMessage(Diagnostics.Extract_constant), + name: "constant_extract_error", + notApplicableReason: getStringError(rangeToExtract.errors), + refactorKind: extractConstantKind + }] + }); + } + return errors; } const extractions = getPossibleExtractions(targetRange, context); @@ -63,51 +79,55 @@ namespace ts.refactor.extractSymbol { let i = 0; for (const { functionExtraction, constantExtraction } of extractions) { 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 - // preferentially go into nearer scopes - if (!usedFunctionNames.has(description)) { - usedFunctionNames.set(description, true); - functionActions.push({ + if(refactorKindBeginsWith(extractFunctionKind, requestedRefactor)){ + if (functionExtraction.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 + if (!usedFunctionNames.has(description)) { + usedFunctionNames.set(description, true); + functionActions.push({ + description, + name: `function_scope_${i}`, + refactorKind: extractFunctionKind + }); + } + } + else if (!innermostErrorFunctionAction) { + innermostErrorFunctionAction = { description, name: `function_scope_${i}`, + notApplicableReason: getStringError(functionExtraction.errors), refactorKind: extractFunctionKind - }); + }; } } - else if (!innermostErrorFunctionAction) { - innermostErrorFunctionAction = { - description, - name: `function_scope_${i}`, - notApplicableReason: getStringError(functionExtraction.errors), - refactorKind: extractFunctionKind - }; - } // Skip these since we don't have a way to report errors yet - 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({ + if(refactorKindBeginsWith(extractConstantKind, requestedRefactor)) { + 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({ + description, + name: `constant_scope_${i}`, + refactorKind: extractConstantKind + }); + } + } + else if (!innermostErrorConstantAction) { + innermostErrorConstantAction = { description, name: `constant_scope_${i}`, + notApplicableReason: getStringError(constantExtraction.errors), refactorKind: extractConstantKind - }); + }; } } - else if (!innermostErrorConstantAction) { - innermostErrorConstantAction = { - description, - name: `constant_scope_${i}`, - notApplicableReason: getStringError(constantExtraction.errors), - refactorKind: extractConstantKind - }; - } // *do* increment i anyway because we'll look for the i-th scope // later when actually doing the refactoring if the user requests it From ca91afd787d4433e4da3d6b74fc56a0dc878a352 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Thu, 10 Dec 2020 13:16:18 -0800 Subject: [PATCH 04/20] move refactorKind check to utilities --- src/services/refactorProvider.ts | 7 +------ src/services/refactors/extractSymbol.ts | 9 --------- src/services/utilities.ts | 9 +++++++++ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 3398225854376..66ba0b6ea4211 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -12,7 +12,7 @@ namespace ts.refactor { export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] { return arrayFrom(flatMapIterator(refactors.values(), refactor => context.cancellationToken && context.cancellationToken.isCancellationRequested() ? - undefined : context.refactorKind && !hasMatchingRefactorKind(refactor.refactorKinds, context.refactorKind) ? + undefined : !refactor.refactorKinds?.some(kind => refactorKindBeginsWith(kind, context.refactorKind)) ? undefined : refactor.getAvailableActions(context))); } @@ -20,9 +20,4 @@ namespace ts.refactor { const refactor = refactors.get(refactorName); return refactor && refactor.getEditsForAction(context, actionName); } - - export function hasMatchingRefactorKind(refactorKinds: string[] | undefined, match: string | undefined): boolean { - if (!refactorKinds || !match) return false; - return refactorKinds.some(refactorKind => refactorKind.substr(0, match.length) === match); - } } diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 05716d4d77a35..42aef48c974c4 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -9,15 +9,6 @@ namespace ts.refactor.extractSymbol { extractFunctionKind, ]; - /** - * Checks if string "first" begins with string "second". - * Used to match requested refactorKinds with a known refactorKind. - */ - function refactorKindBeginsWith(first: string, second: string | undefined): boolean { - if(!second) return true; - return first.substr(0, second.length) === second; - } - registerRefactor(refactorName, { refactorKinds, getAvailableActions, getEditsForAction }); /** diff --git a/src/services/utilities.ts b/src/services/utilities.ts index bcfe1f8aff435..c1f8012cb975e 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2923,5 +2923,14 @@ namespace ts { return isInJSFile(declaration) || !findAncestor(declaration, isGlobalScopeAugmentation); } + /** + * Checks if string "first" begins with string "second". + * Used to match requested refactorKinds with a known refactorKind. + */ + export function refactorKindBeginsWith(first: string, second: string | undefined): boolean { + if(!second) return true; + return first.substr(0, second.length) === second; + } + // #endregion } From 265ae45f6a80a7dbbfa7af6519b7318efac1efdf Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Thu, 10 Dec 2020 15:50:07 -0800 Subject: [PATCH 05/20] rename parameters --- src/services/utilities.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index c1f8012cb975e..4cc6f0fd9a47f 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2924,12 +2924,12 @@ namespace ts { } /** - * Checks if string "first" begins with string "second". + * Checks if string "known" begins with string "requested". * Used to match requested refactorKinds with a known refactorKind. */ - export function refactorKindBeginsWith(first: string, second: string | undefined): boolean { - if(!second) return true; - return first.substr(0, second.length) === second; + export function refactorKindBeginsWith(known: string, requested: string | undefined): boolean { + if(!requested) return true; + return known.substr(0, requested.length) === requested; } // #endregion From 86d99c9bc98c4b96d444b3c5f89a060144d7ec07 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Thu, 10 Dec 2020 15:51:06 -0800 Subject: [PATCH 06/20] messaging for addOrRemoveBracesToArrowFunction --- src/compiler/diagnosticMessages.json | 8 +++++ .../addOrRemoveBracesToArrowFunction.ts | 35 ++++++++----------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b859d28d8a6f9..d940fa6fc8426 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6139,6 +6139,14 @@ "category": "Message", "code": 95148 }, + "Function body must be a braceless expression": { + "category": "Message", + "code": 95149 + }, + "Function body must be a one line return statement": { + "category": "Message", + "code": 95150 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index 1afd4740de51d..768deb14ee73b 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -110,7 +110,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return { renameFilename: undefined, renameLocation: undefined, edits }; } - function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number, considerFunctionBodies = true): InfoOrError | undefined { + function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number, considerFunctionBodies = true, refactorKind?: string): InfoOrError | undefined { const node = getTokenAtPosition(file, startPosition); const func = getContainingFunction(node); @@ -130,27 +130,22 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return undefined; } - if (isExpression(func.body)) { - return { - info: { - func, - addBraces: true, - expression: func.body + if (refactorKindBeginsWith(rewriteArrowBracesAddKind, refactorKind)) { + if (isExpression(func.body)) { + return { info: { func, addBraces: true, expression: func.body } }; + } else { + return { error: getLocaleSpecificMessage(Diagnostics.Function_body_must_be_a_braceless_expression) } + } + } else if (refactorKindBeginsWith(rewriteArrowBracesRemoveKind, refactorKind)) { + if (isBlock(func.body) && func.body.statements.length === 1) { + const firstStatement = first(func.body.statements); + if (isReturnStatement(firstStatement)) { + return { + info: { func, addBraces: false, expression: firstStatement.expression, returnStatement: firstStatement } + }; } - }; - } - else if (func.body.statements.length === 1) { - const firstStatement = first(func.body.statements); - if (isReturnStatement(firstStatement)) { - return { - info: { - func, - addBraces: false, - expression: firstStatement.expression, - returnStatement: firstStatement - } - }; } + return { error: getLocaleSpecificMessage(Diagnostics.Function_body_must_be_a_one_line_return_statement) } } return undefined; } From 029a5340a38c128b60bfb0fb4e2cffc792dd607e Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Sun, 13 Dec 2020 19:03:51 -0800 Subject: [PATCH 07/20] fix up inferFunctionReturnType --- src/compiler/diagnosticMessages.json | 8 ++++ .../refactors/inferFunctionReturnType.ts | 37 +++++++++++++++---- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d940fa6fc8426..52546af88e37b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6147,6 +6147,14 @@ "category": "Message", "code": 95150 }, + "Return type must be inferred from a function": { + "category": "Message", + "code": 95151 + }, + "Could not determine function return type": { + "category": "Message", + "code": 95152 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/refactors/inferFunctionReturnType.ts b/src/services/refactors/inferFunctionReturnType.ts index d30e81d251f0c..ce817edffb52b 100644 --- a/src/services/refactors/inferFunctionReturnType.ts +++ b/src/services/refactors/inferFunctionReturnType.ts @@ -8,7 +8,7 @@ namespace ts.refactor.inferFunctionReturnType { function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined { const info = getInfo(context); - if (info) { + if (info && "declaration" in info) { const edits = textChanges.ChangeTracker.with(context, t => t.tryInsertTypeAnnotation(context.file, info.declaration, info.returnTypeNode)); return { renameFilename: undefined, renameLocation: undefined, edits }; @@ -18,13 +18,26 @@ namespace ts.refactor.inferFunctionReturnType { function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const info = getInfo(context); - if (info) { + if (info && "declaration" in info) { return [{ name: refactorName, description: refactorDescription, actions: [{ name: refactorName, - description: refactorDescription + description: refactorDescription, + refactorKind: rewriteFunctionReturnType + }] + }]; + } + if (info && "error" in info && context.preferences.provideRefactorNotApplicableReason) { + return [{ + name: refactorName, + description: refactorDescription, + actions: [{ + name: refactorName, + description: refactorDescription, + refactorKind: rewriteFunctionReturnType, + notApplicableReason: info.error }] }]; } @@ -37,21 +50,31 @@ namespace ts.refactor.inferFunctionReturnType { | ArrowFunction | MethodDeclaration; - interface Info { + type Info = FunctionInfo | Error; + + interface FunctionInfo { declaration: ConvertibleDeclaration; returnTypeNode: TypeNode; } + interface Error { + error: string + } + function getInfo(context: RefactorContext): Info | undefined { - if (isInJSFile(context.file)) return; + if (isInJSFile(context.file) || !refactorKindBeginsWith(rewriteFunctionReturnType, context.refactorKind)) return; const token = getTokenAtPosition(context.file, context.startPosition); const declaration = findAncestor(token, isConvertibleDeclaration); - if (!declaration || !declaration.body || declaration.type) return; + if (!declaration || !declaration.body || declaration.type) { + return { error: getLocaleSpecificMessage(Diagnostics.Return_type_must_be_inferred_from_a_function) }; + } const typeChecker = context.program.getTypeChecker(); const returnType = tryGetReturnType(typeChecker, declaration); - if (!returnType) return; + if (!returnType) { + return { error: getLocaleSpecificMessage(Diagnostics.Could_not_determine_function_return_type) }; + }; const returnTypeNode = typeChecker.typeToTypeNode(returnType, declaration, NodeBuilderFlags.NoTruncation); if (returnTypeNode) { From 92776d47f7438b72341244ace0af6aa789302857 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Sun, 13 Dec 2020 19:38:18 -0800 Subject: [PATCH 08/20] fix up convertArrowFunctionOrFunctionExpression --- src/compiler/diagnosticMessages.json | 12 ++++ ...onvertArrowFunctionOrFunctionExpression.ts | 63 +++++++++++++------ 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 52546af88e37b..9f7631675ab19 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6155,6 +6155,18 @@ "category": "Message", "code": 95152 }, + "Could not convert to arrow function": { + "category": "Message", + "code": 95153 + }, + "Could not convert to named function": { + "category": "Message", + "code": 95154 + }, + "Could not convert to anonymous function": { + "category": "Message", + "code": 95155 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 366aff3f8701c..86a3965e0489e 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -12,12 +12,12 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const toArrowFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_arrow_function); const rewriteFunctionAnonymousKind = "refactor.rewrite.function.anonymous"; - const rewriteFunctionArrowKind = "refactor.rewite.function.arrow"; - const rewriteFunctioNamedKind = "refactor.rewite.function.named"; + const rewriteFunctionArrowKind = "refactor.rewrite.function.arrow"; + const rewriteFunctionNamedKind = "refactor.rewrite.function.named"; const refactorKinds = [ rewriteFunctionAnonymousKind, rewriteFunctionArrowKind, - rewriteFunctioNamedKind, + rewriteFunctionNamedKind, ]; registerRefactor(refactorName, { refactorKinds, getEditsForAction, getAvailableActions }); @@ -35,38 +35,65 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { } function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { - const { file, startPosition, program } = context; + const { file, startPosition, program, refactorKind } = context; const info = getFunctionInfo(file, startPosition, program); if (!info) return emptyArray; const { selectedVariableDeclaration, func } = info; const possibleActions: RefactorActionInfo[] = []; - - if (selectedVariableDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent))) { - possibleActions.push({ + const errors: RefactorActionInfo[] = []; + if (refactorKindBeginsWith(rewriteFunctionNamedKind, refactorKind)) { + const error = selectedVariableDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent)) ? + undefined : getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_named_function); + const action = { name: toNamedFunctionActionName, - description: toNamedFunctionActionDescription - }); + description: toNamedFunctionActionDescription, + notApplicableReason: error, + refactorKind: rewriteFunctionNamedKind, + } + if (error) { + errors.push(action); + } else { + possibleActions.push(action); + } } - if (!selectedVariableDeclaration && isArrowFunction(func)) { - possibleActions.push({ + if (refactorKindBeginsWith(rewriteFunctionAnonymousKind, refactorKind)) { + const error = !selectedVariableDeclaration && isArrowFunction(func) ? + undefined: getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_anonymous_function); + const action = { name: toAnonymousFunctionActionName, - description: toAnonymousFunctionActionDescription - }); + description: toAnonymousFunctionActionDescription, + notApplicableReason: error, + refactorKind: rewriteFunctionAnonymousKind + } + if (error) { + errors.push(action); + } else { + possibleActions.push(action); + } } - if (isFunctionExpression(func)) { - possibleActions.push({ + if (refactorKindBeginsWith(rewriteFunctionArrowKind, refactorKind)) { + const error = isFunctionExpression(func) ? undefined : getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_arrow_function); + const action = { name: toArrowFunctionActionName, - description: toArrowFunctionActionDescription - }); + description: toArrowFunctionActionDescription, + notApplicableReason: error, + refactorKind: rewriteFunctionArrowKind + } + if (error) { + errors.push(action); + } else { + possibleActions.push(action); + } } return [{ name: refactorName, description: refactorDescription, - actions: possibleActions + actions: possibleActions.length === 0 && context.preferences.provideRefactorNotApplicableReason ? + errors : possibleActions }]; } From d149afc09aec28930256978c0b0936d82724b88c Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Sun, 13 Dec 2020 22:24:37 -0800 Subject: [PATCH 09/20] add preferences to fourslash method --- src/harness/fourslashImpl.ts | 8 ++++---- src/harness/fourslashInterfaceImpl.ts | 4 ++-- tests/cases/fourslash/fourslash.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 4b9a6da8bb90f..73db76b391004 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3372,8 +3372,8 @@ namespace FourSlash { } } - public verifyRefactorKindsAvailable(refactorKind: string, expected: string[]) { - const refactors = this.getApplicableRefactorsAtSelection("invoked", refactorKind); + public verifyRefactorKindsAvailable(refactorKind: string, expected: string[], preferences = ts.emptyOptions) { + const refactors = this.getApplicableRefactorsAtSelection("invoked", refactorKind, preferences); const availableKinds = refactors.reduce((a, b) => [...a, ...b.actions.map((action) => action.refactorKind)], []); assert.deepEqual(availableKinds.sort(), expected.sort(), `Expected refactorKinds to be equal`); } @@ -3791,8 +3791,8 @@ namespace FourSlash { test(renameKeys(newFileContents, key => pathUpdater(key) || key), "with file moved"); } - private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit", refactorKind?: string) { - return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, ts.emptyOptions, triggerReason, refactorKind); + private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit", refactorKind?: string, preferences = ts.emptyOptions) { + return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, preferences, triggerReason, refactorKind); } private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit", refactorKind?: string): readonly ts.ApplicableRefactorInfo[] { return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason, refactorKind); // eslint-disable-line no-in-operator diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 7a0bccf510c56..e2089ee007caa 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -215,8 +215,8 @@ namespace FourSlashInterface { this.state.verifyRefactorAvailable(this.negative, triggerReason, name, actionName); } - public refactorKindAvailable(refactorKind: string, expected: string[]) { - this.state.verifyRefactorKindsAvailable(refactorKind, expected); + public refactorKindAvailable(refactorKind: string, expected: string[], preferences = ts.emptyOptions) { + this.state.verifyRefactorKindsAvailable(refactorKind, expected, preferences); } public toggleLineComment(newFileContent: string) { diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 5b4df26e77451..87639a34ced27 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -244,7 +244,7 @@ declare namespace FourSlashInterface { refactorAvailable(name: string, actionName?: string): void; refactorAvailableForTriggerReason(triggerReason: RefactorTriggerReason, name: string, action?: string): void; - refactorKindAvailable(refactorKind: string, expected: string[]): void; + refactorKindAvailable(refactorKind: string, expected: string[], preferences?: {}): void; } class verify extends verifyNegatable { assertHasRanges(ranges: Range[]): void; From a23296fb3c36a524cab87556e25fdbdd1d0427b0 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Sun, 13 Dec 2020 22:25:17 -0800 Subject: [PATCH 10/20] fix up convert string --- src/compiler/diagnosticMessages.json | 4 ++++ src/services/refactors/convertStringOrTemplateLiteral.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 9f7631675ab19..21e36c48e6d90 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6167,6 +6167,10 @@ "category": "Message", "code": 95155 }, + "Can only convert string concatenation": { + "category": "Message", + "code": 95156 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index 025f7d16980ec..63b3114627869 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -15,6 +15,10 @@ namespace ts.refactor.convertStringOrTemplateLiteral { if (isBinaryExpression(maybeBinary) && isStringConcatenationValid(maybeBinary)) { refactorInfo.actions.push({ name: refactorName, description: refactorDescription, refactorKind: rewriteStringKind }); return [refactorInfo]; + } else if (context.preferences.provideRefactorNotApplicableReason) { + refactorInfo.actions.push({ name: refactorName, description: refactorDescription, refactorKind: rewriteStringKind, + notApplicableReason: getLocaleSpecificMessage(Diagnostics.Can_only_convert_string_concatenation) }); + return [refactorInfo]; } return emptyArray; } From 7f471266007233c7d3ffaafde2765b522b06fab8 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Sun, 13 Dec 2020 23:12:58 -0800 Subject: [PATCH 11/20] fix up moveToNewFile --- src/compiler/diagnosticMessages.json | 4 ++++ src/services/refactors/moveToNewFile.ts | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 21e36c48e6d90..876cc6171a3f0 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6171,6 +6171,10 @@ "category": "Message", "code": 95156 }, + "Selection is not a valid statement or statements": { + "category": "Message", + "code": 95157 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index 947ce5ab9fe1e..328e2bd9e3a1f 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -6,9 +6,16 @@ namespace ts.refactor { registerRefactor(refactorName, { refactorKinds: [refactorMoveNewFile], getAvailableActions(context): readonly ApplicableRefactorInfo[] { - if (!context.preferences.allowTextChangesInNewFiles || getStatementsToMove(context) === undefined) return emptyArray; const description = getLocaleSpecificMessage(Diagnostics.Move_to_a_new_file); - return [{ name: refactorName, description, actions: [{ name: refactorName, description }] }]; + const statements = getStatementsToMove(context); + if (statements && context.preferences.allowTextChangesInNewFiles) { + return [{ name: refactorName, description, actions: [{ name: refactorName, description, refactorKind: refactorMoveNewFile }] }]; + } + if (context.preferences.provideRefactorNotApplicableReason) { + return [{ name: refactorName, description, actions: [{ name: refactorName, description, refactorKind: refactorMoveNewFile, + notApplicableReason: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_statement_or_statements) }] }]; + } + return emptyArray; }, getEditsForAction(context, actionName): RefactorEditInfo { Debug.assert(actionName === refactorName, "Wrong refactor invoked"); From ec565e0822de5319b032b23f9ab3a5bd22afd1fd Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Sun, 13 Dec 2020 23:31:24 -0800 Subject: [PATCH 12/20] fix lint errors --- .../addOrRemoveBracesToArrowFunction.ts | 10 +++++---- ...onvertArrowFunctionOrFunctionExpression.ts | 15 ++++++++----- .../convertStringOrTemplateLiteral.ts | 3 ++- .../refactors/inferFunctionReturnType.ts | 22 ++++++++----------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index 768deb14ee73b..5cddfa34a64ff 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -133,10 +133,12 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { if (refactorKindBeginsWith(rewriteArrowBracesAddKind, refactorKind)) { if (isExpression(func.body)) { return { info: { func, addBraces: true, expression: func.body } }; - } else { - return { error: getLocaleSpecificMessage(Diagnostics.Function_body_must_be_a_braceless_expression) } } - } else if (refactorKindBeginsWith(rewriteArrowBracesRemoveKind, refactorKind)) { + else { + return { error: getLocaleSpecificMessage(Diagnostics.Function_body_must_be_a_braceless_expression) }; + } + } + else if (refactorKindBeginsWith(rewriteArrowBracesRemoveKind, refactorKind)) { if (isBlock(func.body) && func.body.statements.length === 1) { const firstStatement = first(func.body.statements); if (isReturnStatement(firstStatement)) { @@ -145,7 +147,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { }; } } - return { error: getLocaleSpecificMessage(Diagnostics.Function_body_must_be_a_one_line_return_statement) } + return { error: getLocaleSpecificMessage(Diagnostics.Function_body_must_be_a_one_line_return_statement) }; } return undefined; } diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 86a3965e0489e..73aaff5f7297c 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -50,10 +50,11 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { description: toNamedFunctionActionDescription, notApplicableReason: error, refactorKind: rewriteFunctionNamedKind, - } + }; if (error) { errors.push(action); - } else { + } + else { possibleActions.push(action); } } @@ -66,10 +67,11 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { description: toAnonymousFunctionActionDescription, notApplicableReason: error, refactorKind: rewriteFunctionAnonymousKind - } + }; if (error) { errors.push(action); - } else { + } + else { possibleActions.push(action); } } @@ -81,10 +83,11 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { description: toArrowFunctionActionDescription, notApplicableReason: error, refactorKind: rewriteFunctionArrowKind - } + }; if (error) { errors.push(action); - } else { + } + else { possibleActions.push(action); } } diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index 63b3114627869..7c47f0bb28d3c 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -15,7 +15,8 @@ namespace ts.refactor.convertStringOrTemplateLiteral { if (isBinaryExpression(maybeBinary) && isStringConcatenationValid(maybeBinary)) { refactorInfo.actions.push({ name: refactorName, description: refactorDescription, refactorKind: rewriteStringKind }); return [refactorInfo]; - } else if (context.preferences.provideRefactorNotApplicableReason) { + } + else if (context.preferences.provideRefactorNotApplicableReason) { refactorInfo.actions.push({ name: refactorName, description: refactorDescription, refactorKind: rewriteStringKind, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Can_only_convert_string_concatenation) }); return [refactorInfo]; diff --git a/src/services/refactors/inferFunctionReturnType.ts b/src/services/refactors/inferFunctionReturnType.ts index ce817edffb52b..9659aa736653d 100644 --- a/src/services/refactors/inferFunctionReturnType.ts +++ b/src/services/refactors/inferFunctionReturnType.ts @@ -8,7 +8,7 @@ namespace ts.refactor.inferFunctionReturnType { function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined { const info = getInfo(context); - if (info && "declaration" in info) { + if (info && typeof info !== "string") { const edits = textChanges.ChangeTracker.with(context, t => t.tryInsertTypeAnnotation(context.file, info.declaration, info.returnTypeNode)); return { renameFilename: undefined, renameLocation: undefined, edits }; @@ -18,7 +18,7 @@ namespace ts.refactor.inferFunctionReturnType { function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const info = getInfo(context); - if (info && "declaration" in info) { + if (info && typeof info !== "string") { return [{ name: refactorName, description: refactorDescription, @@ -29,7 +29,7 @@ namespace ts.refactor.inferFunctionReturnType { }] }]; } - if (info && "error" in info && context.preferences.provideRefactorNotApplicableReason) { + if (info && typeof info === "string" && context.preferences.provideRefactorNotApplicableReason) { return [{ name: refactorName, description: refactorDescription, @@ -37,7 +37,7 @@ namespace ts.refactor.inferFunctionReturnType { name: refactorName, description: refactorDescription, refactorKind: rewriteFunctionReturnType, - notApplicableReason: info.error + notApplicableReason: info }] }]; } @@ -50,30 +50,26 @@ namespace ts.refactor.inferFunctionReturnType { | ArrowFunction | MethodDeclaration; - type Info = FunctionInfo | Error; - + type InfoOrError = FunctionInfo | string; + interface FunctionInfo { declaration: ConvertibleDeclaration; returnTypeNode: TypeNode; } - interface Error { - error: string - } - - function getInfo(context: RefactorContext): Info | undefined { + function getInfo(context: RefactorContext): InfoOrError | undefined { if (isInJSFile(context.file) || !refactorKindBeginsWith(rewriteFunctionReturnType, context.refactorKind)) return; const token = getTokenAtPosition(context.file, context.startPosition); const declaration = findAncestor(token, isConvertibleDeclaration); if (!declaration || !declaration.body || declaration.type) { - return { error: getLocaleSpecificMessage(Diagnostics.Return_type_must_be_inferred_from_a_function) }; + return getLocaleSpecificMessage(Diagnostics.Return_type_must_be_inferred_from_a_function); } const typeChecker = context.program.getTypeChecker(); const returnType = tryGetReturnType(typeChecker, declaration); if (!returnType) { - return { error: getLocaleSpecificMessage(Diagnostics.Could_not_determine_function_return_type) }; + return getLocaleSpecificMessage(Diagnostics.Could_not_determine_function_return_type); }; const returnTypeNode = typeChecker.typeToTypeNode(returnType, declaration, NodeBuilderFlags.NoTruncation); From 8397605010baf3cc96795cb367aa96013a0241f8 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Sun, 13 Dec 2020 23:45:13 -0800 Subject: [PATCH 13/20] remove extra arrow braces diagnostics --- src/compiler/diagnosticMessages.json | 22 ++++++------------- .../addOrRemoveBracesToArrowFunction.ts | 22 +++++-------------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 876cc6171a3f0..9504ad829d6c7 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6139,41 +6139,33 @@ "category": "Message", "code": 95148 }, - "Function body must be a braceless expression": { - "category": "Message", - "code": 95149 - }, - "Function body must be a one line return statement": { - "category": "Message", - "code": 95150 - }, "Return type must be inferred from a function": { "category": "Message", - "code": 95151 + "code": 95149 }, "Could not determine function return type": { "category": "Message", - "code": 95152 + "code": 95150 }, "Could not convert to arrow function": { "category": "Message", - "code": 95153 + "code": 95151 }, "Could not convert to named function": { "category": "Message", - "code": 95154 + "code": 95152 }, "Could not convert to anonymous function": { "category": "Message", - "code": 95155 + "code": 95153 }, "Can only convert string concatenation": { "category": "Message", - "code": 95156 + "code": 95154 }, "Selection is not a valid statement or statements": { "category": "Message", - "code": 95157 + "code": 95155 }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index 5cddfa34a64ff..f79c3204ce5b5 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -130,24 +130,14 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return undefined; } - if (refactorKindBeginsWith(rewriteArrowBracesAddKind, refactorKind)) { - if (isExpression(func.body)) { - return { info: { func, addBraces: true, expression: func.body } }; - } - else { - return { error: getLocaleSpecificMessage(Diagnostics.Function_body_must_be_a_braceless_expression) }; - } + if (refactorKindBeginsWith(rewriteArrowBracesAddKind, refactorKind) && isExpression(func.body)) { + return { info: { func, addBraces: true, expression: func.body } }; } - else if (refactorKindBeginsWith(rewriteArrowBracesRemoveKind, refactorKind)) { - if (isBlock(func.body) && func.body.statements.length === 1) { - const firstStatement = first(func.body.statements); - if (isReturnStatement(firstStatement)) { - return { - info: { func, addBraces: false, expression: firstStatement.expression, returnStatement: firstStatement } - }; - } + else if (refactorKindBeginsWith(rewriteArrowBracesRemoveKind, refactorKind) && isBlock(func.body) && func.body.statements.length === 1) { + const firstStatement = first(func.body.statements); + if (isReturnStatement(firstStatement)) { + return { info: { func, addBraces: false, expression: firstStatement.expression, returnStatement: firstStatement } }; } - return { error: getLocaleSpecificMessage(Diagnostics.Function_body_must_be_a_one_line_return_statement) }; } return undefined; } From 9c390e13babb47eda820cab5e927f81264a89026 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Mon, 14 Dec 2020 16:21:44 -0800 Subject: [PATCH 14/20] break out tests --- tests/cases/fourslash/refactorKind_extract.ts | 16 ++++++++++++++++ .../refactorKind_generateGetAndSetAccessor.ts | 11 +++++++++++ .../fourslash/refactorKind_moveToNewFile.ts | 12 ++++++++++++ .../fourslash/refactorKind_rewriteExport.ts | 9 +++++++++ .../fourslash/refactorKind_rewriteFunction.ts | 12 ++++++++++++ .../refactorKind_rewriteFunctionOverloadList.ts | 10 ++++++++++ .../fourslash/refactorKind_rewriteImport.ts | 9 +++++++++ .../refactorKind_rewriteOptionalChain.ts | 10 ++++++++++ ...factorKind_rewriteParametersToDestructured.ts | 9 +++++++++ .../fourslash/refactorKind_rewriteString.ts | 9 +++++++++ .../fourslash/refactorKindsGeneralToSpecific.ts | 9 --------- .../fourslash/refactorKindsSpecificToSpecific.ts | 8 -------- 12 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 tests/cases/fourslash/refactorKind_extract.ts create mode 100644 tests/cases/fourslash/refactorKind_generateGetAndSetAccessor.ts create mode 100644 tests/cases/fourslash/refactorKind_moveToNewFile.ts create mode 100644 tests/cases/fourslash/refactorKind_rewriteExport.ts create mode 100644 tests/cases/fourslash/refactorKind_rewriteFunction.ts create mode 100644 tests/cases/fourslash/refactorKind_rewriteFunctionOverloadList.ts create mode 100644 tests/cases/fourslash/refactorKind_rewriteImport.ts create mode 100644 tests/cases/fourslash/refactorKind_rewriteOptionalChain.ts create mode 100644 tests/cases/fourslash/refactorKind_rewriteParametersToDestructured.ts create mode 100644 tests/cases/fourslash/refactorKind_rewriteString.ts delete mode 100644 tests/cases/fourslash/refactorKindsGeneralToSpecific.ts delete mode 100644 tests/cases/fourslash/refactorKindsSpecificToSpecific.ts diff --git a/tests/cases/fourslash/refactorKind_extract.ts b/tests/cases/fourslash/refactorKind_extract.ts new file mode 100644 index 0000000000000..2162cb8dd0ea9 --- /dev/null +++ b/tests/cases/fourslash/refactorKind_extract.ts @@ -0,0 +1,16 @@ +/// + +//// const foo: /*a*/string/*b*/ = /*c*/1/*d*/; + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.extract", +[ + "refactor.extract.type" +]); + +goTo.select("c", "d"); +verify.refactorKindAvailable("refactor.extract", +[ + "refactor.extract.constant", + "refactor.extract.function" +]); diff --git a/tests/cases/fourslash/refactorKind_generateGetAndSetAccessor.ts b/tests/cases/fourslash/refactorKind_generateGetAndSetAccessor.ts new file mode 100644 index 0000000000000..52799947c9ca2 --- /dev/null +++ b/tests/cases/fourslash/refactorKind_generateGetAndSetAccessor.ts @@ -0,0 +1,11 @@ +/// + +//// class A { +//// /*a*/public a: string;/*b*/ +//// } + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.rewrite.property", +[ + "refactor.rewrite.property.generateAccessors" +]); diff --git a/tests/cases/fourslash/refactorKind_moveToNewFile.ts b/tests/cases/fourslash/refactorKind_moveToNewFile.ts new file mode 100644 index 0000000000000..d9b280f565222 --- /dev/null +++ b/tests/cases/fourslash/refactorKind_moveToNewFile.ts @@ -0,0 +1,12 @@ +/// + +//// /*a*/const moveMe = 1;/*b*/ + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.move", +[ + "refactor.move.newFile" +], +{ + allowTextChangesInNewFiles: true +}); diff --git a/tests/cases/fourslash/refactorKind_rewriteExport.ts b/tests/cases/fourslash/refactorKind_rewriteExport.ts new file mode 100644 index 0000000000000..90c484209a0c5 --- /dev/null +++ b/tests/cases/fourslash/refactorKind_rewriteExport.ts @@ -0,0 +1,9 @@ +/// + +//// /*a*/export function f() {}/*b*/ + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.rewrite.export", +[ + "refactor.rewrite.export.default" +]); diff --git a/tests/cases/fourslash/refactorKind_rewriteFunction.ts b/tests/cases/fourslash/refactorKind_rewriteFunction.ts new file mode 100644 index 0000000000000..56fe42cd9a449 --- /dev/null +++ b/tests/cases/fourslash/refactorKind_rewriteFunction.ts @@ -0,0 +1,12 @@ +/// + +//// const arrow = () /*a*/=>/*b*/ 1; + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.rewrite", +[ + "refactor.rewrite.arrow.braces.add", + "refactor.rewrite.function.named", + "refactor.rewrite.function.anonymous", + "refactor.rewrite.function.returnType" +]); diff --git a/tests/cases/fourslash/refactorKind_rewriteFunctionOverloadList.ts b/tests/cases/fourslash/refactorKind_rewriteFunctionOverloadList.ts new file mode 100644 index 0000000000000..068e9830d9da0 --- /dev/null +++ b/tests/cases/fourslash/refactorKind_rewriteFunctionOverloadList.ts @@ -0,0 +1,10 @@ +/// + +//// /*a*/declare function foo(): void; +//// declare function foo(a: string): void;/*b*/ + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.rewrite", +[ + "refactor.rewrite.function.overloadList" +]); diff --git a/tests/cases/fourslash/refactorKind_rewriteImport.ts b/tests/cases/fourslash/refactorKind_rewriteImport.ts new file mode 100644 index 0000000000000..7c78d1c3949bb --- /dev/null +++ b/tests/cases/fourslash/refactorKind_rewriteImport.ts @@ -0,0 +1,9 @@ +/// + +//// /*a*/import * as m from "m";/*b*/ + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.rewrite", +[ + "refactor.rewrite.import.named" +]); diff --git a/tests/cases/fourslash/refactorKind_rewriteOptionalChain.ts b/tests/cases/fourslash/refactorKind_rewriteOptionalChain.ts new file mode 100644 index 0000000000000..5661056447be8 --- /dev/null +++ b/tests/cases/fourslash/refactorKind_rewriteOptionalChain.ts @@ -0,0 +1,10 @@ +/// + +//// /*a*/foo && foo.bar/*b*/ + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.rewrite", +[ + "refactor.rewrite.expression.optionalChain" +]); + diff --git a/tests/cases/fourslash/refactorKind_rewriteParametersToDestructured.ts b/tests/cases/fourslash/refactorKind_rewriteParametersToDestructured.ts new file mode 100644 index 0000000000000..86597ce7b2e8b --- /dev/null +++ b/tests/cases/fourslash/refactorKind_rewriteParametersToDestructured.ts @@ -0,0 +1,9 @@ +/// + +//// function(/*a*/a: number, b: number/*b*/): number {} + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.rewrite", +[ + "refactor.rewrite.parameters.toDestructured" +]); diff --git a/tests/cases/fourslash/refactorKind_rewriteString.ts b/tests/cases/fourslash/refactorKind_rewriteString.ts new file mode 100644 index 0000000000000..ed9222b0aa1fc --- /dev/null +++ b/tests/cases/fourslash/refactorKind_rewriteString.ts @@ -0,0 +1,9 @@ +/// + +//// const foo = /*a*/"a" + bar/*b*/; + +goTo.select("a", "b"); +verify.refactorKindAvailable("refactor.rewrite", +[ + "refactor.rewrite.string" +]); diff --git a/tests/cases/fourslash/refactorKindsGeneralToSpecific.ts b/tests/cases/fourslash/refactorKindsGeneralToSpecific.ts deleted file mode 100644 index 2a76a6dff7662..0000000000000 --- a/tests/cases/fourslash/refactorKindsGeneralToSpecific.ts +++ /dev/null @@ -1,9 +0,0 @@ -/// - -//// const a = /*a*/1/*b*/; - -goTo.select("a", "b"); -verify.refactorKindAvailable("refactor.extract", [ - "refactor.extract.constant", - "refactor.extract.function" -]); diff --git a/tests/cases/fourslash/refactorKindsSpecificToSpecific.ts b/tests/cases/fourslash/refactorKindsSpecificToSpecific.ts deleted file mode 100644 index 543178b39dae3..0000000000000 --- a/tests/cases/fourslash/refactorKindsSpecificToSpecific.ts +++ /dev/null @@ -1,8 +0,0 @@ -/// - -//// const a = /*a*/1/*b*/; - -goTo.select("a", "b"); -verify.refactorKindAvailable("refactor.extract.constant", [ - "refactor.extract.constant" -]); From 4d2da96b0aa5a857042131ffdb493c283fa7588c Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Mon, 14 Dec 2020 19:36:04 -0800 Subject: [PATCH 15/20] add refactor helpers --- src/services/refactors/helpers.ts | 25 +++++++++++++++++++++++++ src/services/tsconfig.json | 1 + src/services/utilities.ts | 9 --------- 3 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 src/services/refactors/helpers.ts diff --git a/src/services/refactors/helpers.ts b/src/services/refactors/helpers.ts new file mode 100644 index 0000000000000..4c8e3a7c84628 --- /dev/null +++ b/src/services/refactors/helpers.ts @@ -0,0 +1,25 @@ +/* @internal */ +namespace ts.refactor { + /** + * Returned by refactor funtions when some error message needs to be surfaced to users. + */ + export interface RefactorErrorInfo { + error: string; + }; + + /** + * Checks if some refactor info has refactor error info. + */ + export function isRefactorErrorInfo(info: unknown): info is RefactorErrorInfo { + return (info as RefactorErrorInfo).error !== undefined; + } + + /** + * Checks if string "known" begins with string "requested". + * Used to match requested refactorKinds with a known refactorKind. + */ + export function refactorKindBeginsWith(known: string, requested: string | undefined): boolean { + if(!requested) return true; + return known.substr(0, requested.length) === requested; + } +} \ No newline at end of file diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 079b8e6ed4f73..0140929340340 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -116,6 +116,7 @@ "refactors/extractSymbol.ts", "refactors/extractType.ts", "refactors/generateGetAccessorAndSetAccessor.ts", + "refactors/helpers.ts", "refactors/moveToNewFile.ts", "refactors/addOrRemoveBracesToArrowFunction.ts", "refactors/convertParamsToDestructuredObject.ts", diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 4cc6f0fd9a47f..bcfe1f8aff435 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2923,14 +2923,5 @@ namespace ts { return isInJSFile(declaration) || !findAncestor(declaration, isGlobalScopeAugmentation); } - /** - * Checks if string "known" begins with string "requested". - * Used to match requested refactorKinds with a known refactorKind. - */ - export function refactorKindBeginsWith(known: string, requested: string | undefined): boolean { - if(!requested) return true; - return known.substr(0, requested.length) === requested; - } - // #endregion } From 6372062accce0bec35e96bbd2c5f27ebf963c7dd Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Mon, 14 Dec 2020 23:56:51 -0800 Subject: [PATCH 16/20] refactor refactors --- src/services/codefixes/generateAccessors.ts | 37 ++++----- .../addOrRemoveBracesToArrowFunction.ts | 78 +++++++------------ ...onvertArrowFunctionOrFunctionExpression.ts | 75 ++++++++---------- src/services/refactors/convertExport.ts | 60 +++++++------- src/services/refactors/convertImport.ts | 60 +++++++------- .../convertOverloadListToSingleSignature.ts | 14 ++-- .../convertParamsToDestructuredObject.ts | 18 ++--- .../convertStringOrTemplateLiteral.ts | 15 ++-- .../convertToOptionalChainExpression.ts | 51 +++++------- src/services/refactors/extractType.ts | 59 +++++++------- .../generateGetAccessorAndSetAccessor.ts | 31 +++----- .../refactors/inferFunctionReturnType.ts | 38 ++++----- src/services/refactors/moveToNewFile.ts | 17 ++-- 13 files changed, 250 insertions(+), 303 deletions(-) diff --git a/src/services/codefixes/generateAccessors.ts b/src/services/codefixes/generateAccessors.ts index f2b397cca3ec5..45f27ff08e185 100644 --- a/src/services/codefixes/generateAccessors.ts +++ b/src/services/codefixes/generateAccessors.ts @@ -4,7 +4,8 @@ namespace ts.codefix { type AcceptedNameType = Identifier | StringLiteral; type ContainerDeclaration = ClassLikeDeclaration | ObjectLiteralExpression; - interface Info { + type Info = AccessorInfo | refactor.RefactorErrorInfo; + interface AccessorInfo { readonly container: ContainerDeclaration; readonly isStatic: boolean; readonly isReadonly: boolean; @@ -16,20 +17,12 @@ namespace ts.codefix { readonly renameAccessor: boolean; } - type InfoOrError = { - info: Info, - error?: never - } | { - info?: never, - error: string - }; - export function generateAccessorFromProperty(file: SourceFile, program: Program, start: number, end: number, context: textChanges.TextChangesContext, _actionName: string): FileTextChanges[] | undefined { const fieldInfo = getAccessorConvertiblePropertyAtPosition(file, program, start, end); - if (!fieldInfo || !fieldInfo.info) return undefined; + if (!fieldInfo || refactor.isRefactorErrorInfo(fieldInfo)) return undefined; const changeTracker = textChanges.ChangeTracker.fromContext(context); - const { isStatic, isReadonly, fieldName, accessorName, originalName, type, container, declaration } = fieldInfo.info; + const { isStatic, isReadonly, fieldName, accessorName, originalName, type, container, declaration } = fieldInfo; suppressLeadingAndTrailingTrivia(fieldName); suppressLeadingAndTrailingTrivia(accessorName); @@ -112,7 +105,7 @@ namespace ts.codefix { return modifierFlags; } - export function getAccessorConvertiblePropertyAtPosition(file: SourceFile, program: Program, start: number, end: number, considerEmptySpans = true): InfoOrError | undefined { + export function getAccessorConvertiblePropertyAtPosition(file: SourceFile, program: Program, start: number, end: number, considerEmptySpans = true): AccessorInfo | Info | undefined { const node = getTokenAtPosition(file, start); const cursorRequest = start === end && considerEmptySpans; const declaration = findAncestor(node.parent, isAcceptedDeclaration); @@ -142,17 +135,15 @@ namespace ts.codefix { const fieldName = createPropertyName(startWithUnderscore ? name : getUniqueName(`_${name}`, file), declaration.name); const accessorName = createPropertyName(startWithUnderscore ? getUniqueName(name.substring(1), file) : name, declaration.name); return { - info: { - isStatic: hasStaticModifier(declaration), - isReadonly: hasEffectiveReadonlyModifier(declaration), - type: getDeclarationType(declaration, program), - container: declaration.kind === SyntaxKind.Parameter ? declaration.parent.parent : declaration.parent, - originalName: (declaration.name).text, - declaration, - fieldName, - accessorName, - renameAccessor: startWithUnderscore - } + isStatic: hasStaticModifier(declaration), + isReadonly: hasEffectiveReadonlyModifier(declaration), + type: getDeclarationType(declaration, program), + container: declaration.kind === SyntaxKind.Parameter ? declaration.parent.parent : declaration.parent, + originalName: (declaration.name).text, + declaration, + fieldName, + accessorName, + renameAccessor: startWithUnderscore }; } diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index f79c3204ce5b5..a2febc0fdffa1 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -2,55 +2,44 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { const refactorName = "Add or remove braces in an arrow function"; const refactorDescription = Diagnostics.Add_or_remove_braces_in_an_arrow_function.message; - const addBracesActionName = "Add braces to arrow function"; - const removeBracesActionName = "Remove braces from arrow function"; - const addBracesActionDescription = Diagnostics.Add_braces_to_arrow_function.message; - const removeBracesActionDescription = Diagnostics.Remove_braces_from_arrow_function.message; - const rewriteArrowBracesRemoveKind = "refactor.rewrite.arrow.braces.remove"; - const rewriteArrowBracesAddKind = "refactor.rewrite.arrow.braces.add"; + const addBracesAction = { + name: "Add braces to arrow function", + description: Diagnostics.Add_braces_to_arrow_function.message, + refactorKind: "refactor.rewrite.arrow.braces.add", + }; + + const removeBracesAction = { + name: "Remove braces from arrow function", + description: Diagnostics.Remove_braces_from_arrow_function.message, + refactorKind: "refactor.rewrite.arrow.braces.remove" + }; + const refactorKinds = [ - rewriteArrowBracesAddKind, - rewriteArrowBracesRemoveKind + addBracesAction.refactorKind, + removeBracesAction.refactorKind ]; registerRefactor(refactorName, { refactorKinds, getEditsForAction, getAvailableActions }); - interface Info { + interface FunctionBracesInfo { func: ArrowFunction; expression: Expression | undefined; returnStatement?: ReturnStatement; addBraces: boolean; } - type InfoOrError = { - info: Info, - error?: never - } | { - info?: never, - error: string - }; - function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition, triggerReason } = context; const info = getConvertibleArrowFunctionAtPosition(file, startPosition, triggerReason === "invoked"); if (!info) return emptyArray; - if (info.error === undefined) { + if (!isRefactorErrorInfo(info)) { return [{ name: refactorName, description: refactorDescription, actions: [ - info.info.addBraces ? - { - name: addBracesActionName, - description: addBracesActionDescription, - refactorKind: rewriteArrowBracesAddKind - } : { - name: removeBracesActionName, - description: removeBracesActionDescription, - refactorKind: rewriteArrowBracesRemoveKind - } + info.addBraces ? addBracesAction : removeBracesAction ] }]; } @@ -59,17 +48,10 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return [{ name: refactorName, description: refactorDescription, - actions: [{ - name: addBracesActionName, - description: addBracesActionDescription, - notApplicableReason: info.error, - refactorKind: rewriteArrowBracesAddKind - }, { - name: removeBracesActionName, - description: removeBracesActionDescription, - notApplicableReason: info.error, - refactorKind: rewriteArrowBracesRemoveKind - }] + actions: [ + { ...addBracesAction, notApplicableReason: info.error }, + { ...removeBracesAction, notApplicableReason: info.error }, + ] }]; } @@ -79,19 +61,19 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { const { file, startPosition } = context; const info = getConvertibleArrowFunctionAtPosition(file, startPosition); - if (!info || !info.info) return undefined; + Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info"); - const { expression, returnStatement, func } = info.info; + const { expression, returnStatement, func } = info; let body: ConciseBody; - if (actionName === addBracesActionName) { + if (actionName === addBracesAction.name) { const returnStatement = factory.createReturnStatement(expression); body = factory.createBlock([returnStatement], /* multiLine */ true); suppressLeadingAndTrailingTrivia(body); copyLeadingComments(expression!, returnStatement, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ true); } - else if (actionName === removeBracesActionName && returnStatement) { + else if (actionName === removeBracesAction.name && returnStatement) { const actualExpression = expression || factory.createVoidZero(); body = needsParentheses(actualExpression) ? factory.createParenthesizedExpression(actualExpression) : actualExpression; suppressLeadingAndTrailingTrivia(body); @@ -110,7 +92,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return { renameFilename: undefined, renameLocation: undefined, edits }; } - function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number, considerFunctionBodies = true, refactorKind?: string): InfoOrError | undefined { + function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number, considerFunctionBodies = true, refactorKind?: string): FunctionBracesInfo | RefactorErrorInfo | undefined { const node = getTokenAtPosition(file, startPosition); const func = getContainingFunction(node); @@ -130,13 +112,13 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return undefined; } - if (refactorKindBeginsWith(rewriteArrowBracesAddKind, refactorKind) && isExpression(func.body)) { - return { info: { func, addBraces: true, expression: func.body } }; + if (refactorKindBeginsWith(addBracesAction.refactorKind, refactorKind) && isExpression(func.body)) { + return { func, addBraces: true, expression: func.body }; } - else if (refactorKindBeginsWith(rewriteArrowBracesRemoveKind, refactorKind) && isBlock(func.body) && func.body.statements.length === 1) { + else if (refactorKindBeginsWith(removeBracesAction.refactorKind, refactorKind) && isBlock(func.body) && func.body.statements.length === 1) { const firstStatement = first(func.body.statements); if (isReturnStatement(firstStatement)) { - return { info: { func, addBraces: false, expression: firstStatement.expression, returnStatement: firstStatement } }; + return { func, addBraces: false, expression: firstStatement.expression, returnStatement: firstStatement }; } } return undefined; diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 73aaff5f7297c..bbf829856b7aa 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -3,21 +3,28 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const refactorName = "Convert arrow function or function expression"; const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_arrow_function_or_function_expression); - const toAnonymousFunctionActionName = "Convert to anonymous function"; - const toNamedFunctionActionName = "Convert to named function"; - const toArrowFunctionActionName = "Convert to arrow function"; + const toAnonymousFunctionAction = { + name: "Convert to anonymous function", + description: getLocaleSpecificMessage(Diagnostics.Convert_to_anonymous_function), + refactorKind: "refactor.rewrite.function.anonymous", + }; + + const toNamedFunctionAction = { + name: "Convert to named function", + description: getLocaleSpecificMessage(Diagnostics.Convert_to_named_function), + refactorKind: "refactor.rewrite.function.named", + }; + + const toArrowFunctionAction = { + name: "Convert to arrow function", + description: getLocaleSpecificMessage(Diagnostics.Convert_to_arrow_function), + refactorKind: "refactor.rewrite.function.arrow", + }; - const toAnonymousFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_anonymous_function); - const toNamedFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_named_function); - const toArrowFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_arrow_function); - - const rewriteFunctionAnonymousKind = "refactor.rewrite.function.anonymous"; - const rewriteFunctionArrowKind = "refactor.rewrite.function.arrow"; - const rewriteFunctionNamedKind = "refactor.rewrite.function.named"; const refactorKinds = [ - rewriteFunctionAnonymousKind, - rewriteFunctionArrowKind, - rewriteFunctionNamedKind, + toAnonymousFunctionAction.refactorKind, + toNamedFunctionAction.refactorKind, + toArrowFunctionAction.refactorKind, ]; registerRefactor(refactorName, { refactorKinds, getEditsForAction, getAvailableActions }); @@ -42,53 +49,35 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const { selectedVariableDeclaration, func } = info; const possibleActions: RefactorActionInfo[] = []; const errors: RefactorActionInfo[] = []; - if (refactorKindBeginsWith(rewriteFunctionNamedKind, refactorKind)) { + if (refactorKindBeginsWith(toNamedFunctionAction.refactorKind, refactorKind)) { const error = selectedVariableDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent)) ? undefined : getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_named_function); - const action = { - name: toNamedFunctionActionName, - description: toNamedFunctionActionDescription, - notApplicableReason: error, - refactorKind: rewriteFunctionNamedKind, - }; if (error) { - errors.push(action); + errors.push({ ...toNamedFunctionAction, notApplicableReason: error }); } else { - possibleActions.push(action); + possibleActions.push(toNamedFunctionAction); } } - if (refactorKindBeginsWith(rewriteFunctionAnonymousKind, refactorKind)) { + if (refactorKindBeginsWith(toAnonymousFunctionAction.refactorKind, refactorKind)) { const error = !selectedVariableDeclaration && isArrowFunction(func) ? undefined: getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_anonymous_function); - const action = { - name: toAnonymousFunctionActionName, - description: toAnonymousFunctionActionDescription, - notApplicableReason: error, - refactorKind: rewriteFunctionAnonymousKind - }; if (error) { - errors.push(action); + errors.push({ ...toAnonymousFunctionAction, notApplicableReason: error }); } else { - possibleActions.push(action); + possibleActions.push(toAnonymousFunctionAction); } } - if (refactorKindBeginsWith(rewriteFunctionArrowKind, refactorKind)) { + if (refactorKindBeginsWith(toArrowFunctionAction.refactorKind, refactorKind)) { const error = isFunctionExpression(func) ? undefined : getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_arrow_function); - const action = { - name: toArrowFunctionActionName, - description: toArrowFunctionActionDescription, - notApplicableReason: error, - refactorKind: rewriteFunctionArrowKind - }; if (error) { - errors.push(action); + errors.push({ ...toArrowFunctionAction, notApplicableReason: error }); } else { - possibleActions.push(action); + possibleActions.push(toArrowFunctionAction); } } @@ -109,18 +98,18 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const edits: FileTextChanges[] = []; switch (actionName) { - case toAnonymousFunctionActionName: + case toAnonymousFunctionAction.name: edits.push(...getEditInfoForConvertToAnonymousFunction(context, func)); break; - case toNamedFunctionActionName: + case toNamedFunctionAction.name: const variableInfo = getVariableInfo(func); if (!variableInfo) return undefined; edits.push(...getEditInfoForConvertToNamedFunction(context, func, variableInfo)); break; - case toArrowFunctionActionName: + case toArrowFunctionAction.name: if (!isFunctionExpression(func)) return undefined; edits.push(...getEditInfoForConvertToArrowFunction(context, func)); break; diff --git a/src/services/refactors/convertExport.ts b/src/services/refactors/convertExport.ts index 7866fd844c996..c2d096a71d353 100644 --- a/src/services/refactors/convertExport.ts +++ b/src/services/refactors/convertExport.ts @@ -1,11 +1,21 @@ /* @internal */ namespace ts.refactor { const refactorName = "Convert export"; - const actionNameDefaultToNamed = "Convert default export to named export"; - const actionNameNamedToDefault = "Convert named export to default export"; - const rewriteExportToNamedKind = "refactor.rewrite.export.named"; const rewriteExportToDefaultKind = "refactor.rewrite.export.default"; + + const defaultToNamedAction = { + name: "Convert default export to named export", + description: Diagnostics.Convert_default_export_to_named_export.message, + refactorKind: rewriteExportToNamedKind + }; + + const namedToDefaultAction = { + name: "Convert named export to default export", + description: Diagnostics.Convert_named_export_to_default_export.message, + refactorKind: rewriteExportToDefaultKind + }; + const refactorKinds = [ rewriteExportToDefaultKind, rewriteExportToNamedKind @@ -17,49 +27,41 @@ namespace ts.refactor { const info = getInfo(context, context.triggerReason === "invoked"); if (!info) return emptyArray; - if (info.error === undefined) { - const description = info.info.wasDefault ? Diagnostics.Convert_default_export_to_named_export.message : Diagnostics.Convert_named_export_to_default_export.message; - const actionName = info.info.wasDefault ? actionNameDefaultToNamed : actionNameNamedToDefault; - const refactorKind = info.info.wasDefault ? rewriteExportToNamedKind : rewriteExportToDefaultKind; - return [{ name: refactorName, description, actions: [{ name: actionName, description, refactorKind }] }]; + if (!isRefactorErrorInfo(info)) { + const action = info.wasDefault ? defaultToNamedAction : namedToDefaultAction; + return [{ name: refactorName, description: action.description, actions: [action] }]; } if (context.preferences.provideRefactorNotApplicableReason) { return [ - { name: refactorName, description: Diagnostics.Convert_default_export_to_named_export.message, - actions: [{ name: actionNameDefaultToNamed, description: Diagnostics.Convert_default_export_to_named_export.message, notApplicableReason: info.error, refactorKind: rewriteExportToNamedKind }] }, - { name: refactorName, description: Diagnostics.Convert_named_export_to_default_export.message, - actions: [{ name: actionNameNamedToDefault, description: Diagnostics.Convert_named_export_to_default_export.message, notApplicableReason: info.error, refactorKind: rewriteExportToDefaultKind }] }, + { name: refactorName, description: Diagnostics.Convert_default_export_to_named_export.message, actions: [ + { ...defaultToNamedAction, notApplicableReason: info.error }, + { ...namedToDefaultAction, notApplicableReason: info.error }, + ]} ]; } return emptyArray; }, getEditsForAction(context, actionName): RefactorEditInfo { - Debug.assert(actionName === actionNameDefaultToNamed || actionName === actionNameNamedToDefault, "Unexpected action name"); - const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, Debug.checkDefined(getInfo(context)?.info, "context must have info"), t, context.cancellationToken)); + Debug.assert(actionName === defaultToNamedAction.name || actionName === namedToDefaultAction.name, "Unexpected action name"); + const info = getInfo(context); + Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info"); + const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, info, t, context.cancellationToken)); return { edits, renameFilename: undefined, renameLocation: undefined }; }, }); // If a VariableStatement, will have exactly one VariableDeclaration, with an Identifier for a name. type ExportToConvert = FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | NamespaceDeclaration | TypeAliasDeclaration | VariableStatement; - interface Info { + interface ExportInfo { readonly exportNode: ExportToConvert; readonly exportName: Identifier; // This is exportNode.name except for VariableStatement_s. readonly wasDefault: boolean; readonly exportingModuleSymbol: Symbol; - } - - type InfoOrError = { - info: Info, - error?: never - } | { - info?: never, - error: string }; - function getInfo(context: RefactorContext, considerPartialSpans = true): InfoOrError | undefined { + function getInfo(context: RefactorContext, considerPartialSpans = true): ExportInfo | RefactorErrorInfo | undefined { const { file } = context; const span = getRefactorContextSpan(context); const token = getTokenAtPosition(file, span.start); @@ -85,7 +87,7 @@ namespace ts.refactor { case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.ModuleDeclaration: { const node = exportNode as FunctionDeclaration | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | TypeAliasDeclaration | NamespaceDeclaration; - return node.name && isIdentifier(node.name) ? { info: { exportNode: node, exportName: node.name, wasDefault, exportingModuleSymbol } } : undefined; + return node.name && isIdentifier(node.name) ? { exportNode: node, exportName: node.name, wasDefault, exportingModuleSymbol } : undefined; } case SyntaxKind.VariableStatement: { const vs = exportNode as VariableStatement; @@ -96,19 +98,19 @@ namespace ts.refactor { const decl = first(vs.declarationList.declarations); if (!decl.initializer) return undefined; Debug.assert(!wasDefault, "Can't have a default flag here"); - return isIdentifier(decl.name) ? { info: { exportNode: vs, exportName: decl.name, wasDefault, exportingModuleSymbol } } : undefined; + return isIdentifier(decl.name) ? { exportNode: vs, exportName: decl.name, wasDefault, exportingModuleSymbol } : undefined; } default: return undefined; } } - function doChange(exportingSourceFile: SourceFile, program: Program, info: Info, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void { + function doChange(exportingSourceFile: SourceFile, program: Program, info: ExportInfo, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void { changeExport(exportingSourceFile, info, changes, program.getTypeChecker()); changeImports(program, info, changes, cancellationToken); } - function changeExport(exportingSourceFile: SourceFile, { wasDefault, exportNode, exportName }: Info, changes: textChanges.ChangeTracker, checker: TypeChecker): void { + function changeExport(exportingSourceFile: SourceFile, { wasDefault, exportNode, exportName }: ExportInfo, changes: textChanges.ChangeTracker, checker: TypeChecker): void { if (wasDefault) { changes.delete(exportingSourceFile, Debug.checkDefined(findModifier(exportNode, SyntaxKind.DefaultKeyword), "Should find a default keyword in modifier list")); } @@ -142,7 +144,7 @@ namespace ts.refactor { } } - function changeImports(program: Program, { wasDefault, exportName, exportingModuleSymbol }: Info, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void { + function changeImports(program: Program, { wasDefault, exportName, exportingModuleSymbol }: ExportInfo, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void { const checker = program.getTypeChecker(); const exportSymbol = Debug.checkDefined(checker.getSymbolAtLocation(exportName), "Export name should resolve to a symbol"); FindAllReferences.Core.eachExportReference(program.getSourceFiles(), checker, cancellationToken, exportSymbol, exportingModuleSymbol, exportName.text, wasDefault, ref => { diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index 644b9d0c01936..b9ffd238b074c 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -1,58 +1,56 @@ /* @internal */ namespace ts.refactor { const refactorName = "Convert import"; - const actionNameNamespaceToNamed = "Convert namespace import to named imports"; - const actionNameNamedToNamespace = "Convert named imports to namespace import"; - const rewriteImportToNamedKind = "refactor.rewrite.import.named"; - const rewriteImportToNamespaceKind = "refactor.rewrite.import.namespace"; + const namespaceToNamedAction = { + name: "Convert namespace import to named imports", + description: Diagnostics.Convert_namespace_import_to_named_imports.message, + refactorKind: "refactor.rewrite.import.named", + }; + const namedToNamespaceAction = { + name: "Convert named imports to namespace import", + description: Diagnostics.Convert_named_imports_to_namespace_import.message, + refactorKind: "refactor.rewrite.import.namespace", + }; const refactorKinds = [ - rewriteImportToNamedKind, - rewriteImportToNamespaceKind + namespaceToNamedAction.refactorKind, + namedToNamespaceAction.refactorKind ]; - type NamedImportBindingsOrError = { - info: NamedImportBindings, - error?: never - } | { - info?: never, - error: string - }; - registerRefactor(refactorName, { refactorKinds, getAvailableActions(context): readonly ApplicableRefactorInfo[] { - const i = getImportToConvert(context, context.triggerReason === "invoked"); - if (!i) return emptyArray; - - if (i.error === undefined) { - const namespaceImport = i.info.kind === SyntaxKind.NamespaceImport; - const description = namespaceImport ? Diagnostics.Convert_namespace_import_to_named_imports.message : Diagnostics.Convert_named_imports_to_namespace_import.message; - const actionName = namespaceImport ? actionNameNamespaceToNamed : actionNameNamedToNamespace; - const refactorKind = namespaceImport ? rewriteImportToNamedKind : rewriteImportToNamespaceKind; - return [{ name: refactorName, description, actions: [{ name: actionName, description, refactorKind }] }]; + const info = getImportToConvert(context, context.triggerReason === "invoked"); + if (!info) return emptyArray; + + if (!isRefactorErrorInfo(info)) { + const namespaceImport = info.kind === SyntaxKind.NamespaceImport; + const action = namespaceImport ? namespaceToNamedAction : namedToNamespaceAction; + return [{ name: refactorName, description: action.description, actions: [action] }]; } if (context.preferences.provideRefactorNotApplicableReason) { return [ - { name: refactorName, description: Diagnostics.Convert_namespace_import_to_named_imports.message, - actions: [{ name: actionNameNamespaceToNamed, description: Diagnostics.Convert_namespace_import_to_named_imports.message, notApplicableReason: i.error, refactorKind: rewriteImportToNamedKind }] }, - { name: refactorName, description: Diagnostics.Convert_named_imports_to_namespace_import.message, - actions: [{ name: actionNameNamedToNamespace, description: Diagnostics.Convert_named_imports_to_namespace_import.message, notApplicableReason: i.error, refactorKind: rewriteImportToNamespaceKind }] } + { name: refactorName, description: namespaceToNamedAction.description, + actions: [{ ...namespaceToNamedAction, notApplicableReason: info.error }] }, + { name: refactorName, description: namedToNamespaceAction.description, + actions: [{ ...namedToNamespaceAction, notApplicableReason: info.error }] } ]; } return emptyArray; }, getEditsForAction(context, actionName): RefactorEditInfo { - Debug.assert(actionName === actionNameNamespaceToNamed || actionName === actionNameNamedToNamespace, "Unexpected action name"); - const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, t, Debug.checkDefined(getImportToConvert(context)?.info, "Context must provide an import to convert"))); + Debug.assert(actionName === namespaceToNamedAction.name || actionName === namedToNamespaceAction.name, "Unexpected action name"); + const info = getImportToConvert(context); + Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info"); + const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, t, info)); return { edits, renameFilename: undefined, renameLocation: undefined }; } }); // Can convert imports of the form `import * as m from "m";` or `import d, { x, y } from "m";`. - function getImportToConvert(context: RefactorContext, considerPartialSpans = true): NamedImportBindingsOrError | undefined { + function getImportToConvert(context: RefactorContext, considerPartialSpans = true): NamedImportBindings | RefactorErrorInfo | undefined { const { file } = context; const span = getRefactorContextSpan(context); const token = getTokenAtPosition(file, span.start); @@ -69,7 +67,7 @@ namespace ts.refactor { return { error: getLocaleSpecificMessage(Diagnostics.Could_not_find_namespace_import_or_named_imports) }; } - return { info: importClause.namedBindings }; + return importClause.namedBindings; } function doChange(sourceFile: SourceFile, program: Program, changes: textChanges.ChangeTracker, toConvert: NamedImportBindings): void { diff --git a/src/services/refactors/convertOverloadListToSingleSignature.ts b/src/services/refactors/convertOverloadListToSingleSignature.ts index a48b932d8ad9f..c49cf198c9756 100644 --- a/src/services/refactors/convertOverloadListToSingleSignature.ts +++ b/src/services/refactors/convertOverloadListToSingleSignature.ts @@ -2,9 +2,13 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { const refactorName = "Convert overload list to single signature"; const refactorDescription = Diagnostics.Convert_overload_list_to_single_signature.message; - const rewriteFunctionOverloadListKind = "refactor.rewrite.function.overloadList"; + const functionOverloadAction = { + name: refactorName, + description: refactorDescription, + refactorKind: "refactor.rewrite.function.overloadList", + }; - registerRefactor(refactorName, { refactorKinds: [rewriteFunctionOverloadListKind], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { refactorKinds: [functionOverloadAction.refactorKind], getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition, program } = context; @@ -14,11 +18,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return [{ name: refactorName, description: refactorDescription, - actions: [{ - name: refactorName, - description: refactorDescription, - refactorKind: rewriteFunctionOverloadListKind - }] + actions: [functionOverloadAction] }]; } diff --git a/src/services/refactors/convertParamsToDestructuredObject.ts b/src/services/refactors/convertParamsToDestructuredObject.ts index 07ebfbc7c29da..9fdcf486fdae7 100644 --- a/src/services/refactors/convertParamsToDestructuredObject.ts +++ b/src/services/refactors/convertParamsToDestructuredObject.ts @@ -2,9 +2,14 @@ namespace ts.refactor.convertParamsToDestructuredObject { const refactorName = "Convert parameters to destructured object"; const minimumParameterLength = 2; - const rewriteParametersToDestructuredKind = "refactor.rewrite.parameters.toDestructured"; + const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_parameters_to_destructured_object); - registerRefactor(refactorName, { refactorKinds: [rewriteParametersToDestructuredKind], getEditsForAction, getAvailableActions }); + const toDestructuredAction = { + name: refactorName, + description: refactorDescription, + refactorKind: "refactor.rewrite.parameters.toDestructured" + }; + registerRefactor(refactorName, { refactorKinds: [toDestructuredAction.refactorKind], getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition } = context; @@ -13,15 +18,10 @@ namespace ts.refactor.convertParamsToDestructuredObject { const functionDeclaration = getFunctionDeclarationAtPosition(file, startPosition, context.program.getTypeChecker()); if (!functionDeclaration) return emptyArray; - const description = getLocaleSpecificMessage(Diagnostics.Convert_parameters_to_destructured_object); return [{ name: refactorName, - description, - actions: [{ - name: refactorName, - description, - refactorKind: rewriteParametersToDestructuredKind - }] + description: refactorDescription, + actions: [toDestructuredAction] }]; } diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index 7c47f0bb28d3c..9d63969bed0c1 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -2,9 +2,13 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const refactorName = "Convert to template string"; const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_template_string); - const rewriteStringKind = "refactor.rewrite.string"; - registerRefactor(refactorName, { refactorKinds: [rewriteStringKind], getEditsForAction, getAvailableActions }); + const convertStringAction = { + name: refactorName, + description: refactorDescription, + refactorKind: "refactor.rewrite.string" + }; + registerRefactor(refactorName, { refactorKinds: [convertStringAction.refactorKind], getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition } = context; @@ -13,12 +17,13 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const refactorInfo: ApplicableRefactorInfo = { name: refactorName, description: refactorDescription, actions: [] }; if (isBinaryExpression(maybeBinary) && isStringConcatenationValid(maybeBinary)) { - refactorInfo.actions.push({ name: refactorName, description: refactorDescription, refactorKind: rewriteStringKind }); + refactorInfo.actions.push(convertStringAction); return [refactorInfo]; } else if (context.preferences.provideRefactorNotApplicableReason) { - refactorInfo.actions.push({ name: refactorName, description: refactorDescription, refactorKind: rewriteStringKind, - notApplicableReason: getLocaleSpecificMessage(Diagnostics.Can_only_convert_string_concatenation) }); + refactorInfo.actions.push({ ...convertStringAction, + notApplicableReason: getLocaleSpecificMessage(Diagnostics.Can_only_convert_string_concatenation) + }); return [refactorInfo]; } return emptyArray; diff --git a/src/services/refactors/convertToOptionalChainExpression.ts b/src/services/refactors/convertToOptionalChainExpression.ts index 5a84fa5712672..d9a1001a57ee5 100644 --- a/src/services/refactors/convertToOptionalChainExpression.ts +++ b/src/services/refactors/convertToOptionalChainExpression.ts @@ -2,23 +2,23 @@ namespace ts.refactor.convertToOptionalChainExpression { const refactorName = "Convert to optional chain expression"; const convertToOptionalChainExpressionMessage = getLocaleSpecificMessage(Diagnostics.Convert_to_optional_chain_expression); - const rewriteExpressionOptionalChainKind = "refactor.rewrite.expression.optionalChain"; - registerRefactor(refactorName, { refactorKinds: [rewriteExpressionOptionalChainKind], getAvailableActions, getEditsForAction }); + const toOptionalChainAction = { + name: refactorName, + description: convertToOptionalChainExpressionMessage, + refactorKind: "refactor.rewrite.expression.optionalChain", + }; + registerRefactor(refactorName, { refactorKinds: [toOptionalChainAction.refactorKind], getAvailableActions, getEditsForAction }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const info = getInfo(context, context.triggerReason === "invoked"); if (!info) return emptyArray; - if (!info.error) { + if (!isRefactorErrorInfo(info)) { return [{ name: refactorName, description: convertToOptionalChainExpressionMessage, - actions: [{ - name: refactorName, - description: convertToOptionalChainExpressionMessage, - refactorKind: rewriteExpressionOptionalChainKind - }] + actions: [toOptionalChainAction] }]; } @@ -26,12 +26,7 @@ namespace ts.refactor.convertToOptionalChainExpression { return [{ name: refactorName, description: convertToOptionalChainExpressionMessage, - actions: [{ - name: refactorName, - description: convertToOptionalChainExpressionMessage, - notApplicableReason: info.error, - refactorKind: rewriteExpressionOptionalChainKind - }] + actions: [{ ...toOptionalChainAction, notApplicableReason: info.error }] }]; } return emptyArray; @@ -39,24 +34,16 @@ namespace ts.refactor.convertToOptionalChainExpression { function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { const info = getInfo(context); - if (!info || !info.info) return undefined; + Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info"); const edits = textChanges.ChangeTracker.with(context, t => - doChange(context.file, context.program.getTypeChecker(), t, Debug.checkDefined(info.info, "context must have info"), actionName) + doChange(context.file, context.program.getTypeChecker(), t, info, actionName) ); return { edits, renameFilename: undefined, renameLocation: undefined }; } - type InfoOrError = { - info: Info, - error?: never; - } | { - info?: never, - error: string; - }; - type Occurrence = PropertyAccessExpression | ElementAccessExpression | Identifier; - interface Info { + interface OptionalChainInfo { finalExpression: PropertyAccessExpression | ElementAccessExpression | CallExpression, occurrences: Occurrence[], expression: ValidExpression, @@ -86,7 +73,7 @@ namespace ts.refactor.convertToOptionalChainExpression { return isValidExpression(node) || isValidStatement(node); } - function getInfo(context: RefactorContext, considerEmptySpans = true): InfoOrError | undefined { + function getInfo(context: RefactorContext, considerEmptySpans = true): OptionalChainInfo | RefactorErrorInfo | undefined { const { file, program } = context; const span = getRefactorContextSpan(context); @@ -106,7 +93,7 @@ namespace ts.refactor.convertToOptionalChainExpression { return isConditionalExpression(expression) ? getConditionalInfo(expression, checker) : getBinaryInfo(expression); } - function getConditionalInfo(expression: ConditionalExpression, checker: TypeChecker): InfoOrError | undefined { + function getConditionalInfo(expression: ConditionalExpression, checker: TypeChecker): OptionalChainInfo | RefactorErrorInfo | undefined { const condition = expression.condition; const finalExpression = getFinalExpressionInChain(expression.whenTrue); @@ -116,16 +103,16 @@ namespace ts.refactor.convertToOptionalChainExpression { if ((isPropertyAccessExpression(condition) || isIdentifier(condition)) && getMatchingStart(condition, finalExpression.expression)) { - return { info: { finalExpression, occurrences: [condition], expression } }; + return { finalExpression, occurrences: [condition], expression }; } else if (isBinaryExpression(condition)) { const occurrences = getOccurrencesInExpression(finalExpression.expression, condition); - return occurrences ? { info: { finalExpression, occurrences, expression } } : + return occurrences ? { finalExpression, occurrences, expression } : { error: getLocaleSpecificMessage(Diagnostics.Could_not_find_matching_access_expressions) }; } } - function getBinaryInfo(expression: BinaryExpression): InfoOrError | undefined { + function getBinaryInfo(expression: BinaryExpression): OptionalChainInfo | RefactorErrorInfo | undefined { if (expression.operatorToken.kind !== SyntaxKind.AmpersandAmpersandToken) { return { error: getLocaleSpecificMessage(Diagnostics.Can_only_convert_logical_AND_access_chains) }; }; @@ -134,7 +121,7 @@ namespace ts.refactor.convertToOptionalChainExpression { if (!finalExpression) return { error: getLocaleSpecificMessage(Diagnostics.Could_not_find_convertible_access_expression) }; const occurrences = getOccurrencesInExpression(finalExpression.expression, expression.left); - return occurrences ? { info: { finalExpression, occurrences, expression } } : + return occurrences ? { finalExpression, occurrences, expression } : { error: getLocaleSpecificMessage(Diagnostics.Could_not_find_matching_access_expressions) }; } @@ -291,7 +278,7 @@ namespace ts.refactor.convertToOptionalChainExpression { return toConvert; } - function doChange(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, info: Info, _actionName: string): void { + function doChange(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, info: OptionalChainInfo, _actionName: string): void { const { finalExpression, occurrences, expression } = info; const firstOccurrence = occurrences[occurrences.length - 1]; const convertedChain = convertOccurrences(checker, finalExpression, occurrences); diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts index eb27156112592..01fbd708a121c 100644 --- a/src/services/refactors/extractType.ts +++ b/src/services/refactors/extractType.ts @@ -5,13 +5,25 @@ namespace ts.refactor { const extractToInterface = "Extract to interface"; const extractToTypeDef = "Extract to typedef"; - const extractToTypeKind = "refactor.extract.type"; - const extractToInterfaceKind = "refactor.extract.interface"; - const extractToTypeDefKind = "refactor.extract.typedef"; + const extractToTypeAliasAction = { + name: "Extract to type alias", + description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias), + refactorKind: "refactor.extract.type", + }; + const extractToInterfaceAction = { + name: "Extract to interface", + description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface), + refactorKind: "refactor.extract.interface", + }; + const extractToTypeDefAction = { + name: "Extract to typedef", + description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef), + refactorKind: "refactor.extract.typedef" + }; const refactorKinds = [ - extractToTypeKind, - extractToInterfaceKind, - extractToTypeDefKind, + extractToTypeAliasAction.refactorKind, + extractToInterfaceAction.refactorKind, + extractToTypeDefAction.refactorKind, ]; registerRefactor(refactorName, { @@ -20,17 +32,12 @@ namespace ts.refactor { const info = getRangeToExtract(context, context.triggerReason === "invoked"); if (!info) return emptyArray; - if (info.error === undefined) { + if (!isRefactorErrorInfo(info)) { return [{ name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_type), - actions: info.info.isJS ? [{ - name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef), refactorKind: extractToTypeDefKind - }] : append([{ - name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias), refactorKind: extractToTypeKind - }], info.info.typeElements && { - name: extractToInterface, description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface), refactorKind: extractToInterfaceKind - }) + actions: info.isJS ? + [extractToTypeDefAction] : append([extractToTypeAliasAction], info.typeElements && extractToInterfaceAction) }]; } @@ -39,9 +46,9 @@ namespace ts.refactor { name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_type), actions: [ - { name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef), notApplicableReason: info.error, refactorKind: extractToTypeDefKind }, - { name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias), notApplicableReason: info.error, refactorKind: extractToTypeKind }, - { name: extractToInterface, description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface), notApplicableReason: info.error, refactorKind: extractToInterfaceKind }, + { ...extractToTypeDefAction, notApplicableReason: info.error }, + { ...extractToTypeAliasAction, notApplicableReason: info.error }, + { ...extractToInterfaceAction, notApplicableReason: info.error }, ] }]; } @@ -50,7 +57,8 @@ namespace ts.refactor { }, getEditsForAction(context, actionName): RefactorEditInfo { const { file, } = context; - const info = Debug.checkDefined(getRangeToExtract(context)?.info, "Expected to find a range to extract"); + const info = getRangeToExtract(context); + Debug.assert(info && !isRefactorErrorInfo(info), "Expected to find a range to extract"); const name = getUniqueName("NewType", file); const edits = textChanges.ChangeTracker.with(context, changes => { @@ -83,16 +91,9 @@ namespace ts.refactor { isJS: boolean; selection: TypeNode; firstStatement: Statement; typeParameters: readonly TypeParameterDeclaration[]; typeElements: readonly TypeElement[]; } - type Info = TypeAliasInfo | InterfaceInfo; - type InfoOrError = { - info: Info, - error?: never - } | { - info?: never, - error: string - }; + type ExtractInfo = TypeAliasInfo | InterfaceInfo; - function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): InfoOrError | undefined { + function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): ExtractInfo | RefactorErrorInfo | undefined { const { file, startPosition } = context; const isJS = isSourceFileJS(file); const current = getTokenAtPosition(file, startPosition); @@ -109,7 +110,7 @@ namespace ts.refactor { if (!typeParameters) return { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) }; const typeElements = flattenTypeLiteralNodeReference(checker, selection); - return { info: { isJS, selection, firstStatement, typeParameters, typeElements } }; + return { isJS, selection, firstStatement, typeParameters, typeElements }; } function flattenTypeLiteralNodeReference(checker: TypeChecker, node: TypeNode | undefined): readonly TypeElement[] | undefined { @@ -220,7 +221,7 @@ namespace ts.refactor { changes.replaceNode(file, selection, factory.createTypeReferenceNode(name, typeParameters.map(id => factory.createTypeReferenceNode(id.name, /* typeArguments */ undefined))), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.ExcludeWhitespace }); } - function doTypedefChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, info: Info) { + function doTypedefChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, info: ExtractInfo) { const { firstStatement, selection, typeParameters } = info; const node = factory.createJSDocTypedefTag( diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index 055979d8d91c1..4ec9cd192db83 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -2,21 +2,25 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { const actionName = "Generate 'get' and 'set' accessors"; const actionDescription = Diagnostics.Generate_get_and_set_accessors.message; - const rewritePropertyGenerateAccessors = "refactor.rewrite.property.generateAccessors"; + const generateGetSetAction = { + name: actionName, + description: actionDescription, + refactorKind: "refactor.rewrite.property.generateAccessors", + }; registerRefactor(actionName, { - refactorKinds: [rewritePropertyGenerateAccessors], + refactorKinds: [generateGetSetAction.refactorKind], getEditsForAction(context, actionName) { if (!context.endPosition) return undefined; const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition); - if (!info || !info.info) return undefined; + Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info"); const edits = codefix.generateAccessorFromProperty(context.file, context.program, context.startPosition, context.endPosition, context, actionName); if (!edits) return undefined; const renameFilename = context.file.fileName; - const nameNeedRename = info.info.renameAccessor ? info.info.accessorName : info.info.fieldName; + const nameNeedRename = info.renameAccessor ? info.accessorName : info.fieldName; const renameLocationOffset = isIdentifier(nameNeedRename) ? 0 : -1; - const renameLocation = renameLocationOffset + getRenameLocation(edits, renameFilename, nameNeedRename.text, /*preferLastLocation*/ isParameter(info.info.declaration)); + const renameLocation = renameLocationOffset + getRenameLocation(edits, renameFilename, nameNeedRename.text, /*preferLastLocation*/ isParameter(info.declaration)); return { renameFilename, renameLocation, edits }; }, @@ -25,17 +29,11 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition, context.triggerReason === "invoked"); if (!info) return emptyArray; - if (!info.error) { + if (!isRefactorErrorInfo(info)) { return [{ name: actionName, description: actionDescription, - actions: [ - { - name: actionName, - description: actionDescription, - refactorKind: rewritePropertyGenerateAccessors - } - ] + actions: [generateGetSetAction], }]; } @@ -43,12 +41,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { return [{ name: actionName, description: actionDescription, - actions: [{ - name: actionName, - description: actionDescription, - notApplicableReason: info.error, - refactorKind: rewritePropertyGenerateAccessors - }] + actions: [{ ...generateGetSetAction, notApplicableReason: info.error }], }]; } diff --git a/src/services/refactors/inferFunctionReturnType.ts b/src/services/refactors/inferFunctionReturnType.ts index 9659aa736653d..9aee141d42423 100644 --- a/src/services/refactors/inferFunctionReturnType.ts +++ b/src/services/refactors/inferFunctionReturnType.ts @@ -2,13 +2,17 @@ namespace ts.refactor.inferFunctionReturnType { const refactorName = "Infer function return type"; const refactorDescription = Diagnostics.Infer_function_return_type.message; - const rewriteFunctionReturnType = "refactor.rewrite.function.returnType"; - registerRefactor(refactorName, { refactorKinds: [rewriteFunctionReturnType], getEditsForAction, getAvailableActions }); + const inferReturnTypeAction = { + name: refactorName, + description: refactorDescription, + refactorKind: "refactor.rewrite.function.returnType" + }; + registerRefactor(refactorName, { refactorKinds: [inferReturnTypeAction.refactorKind], getEditsForAction, getAvailableActions }); function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined { const info = getInfo(context); - if (info && typeof info !== "string") { + if (info && !isRefactorErrorInfo(info)) { const edits = textChanges.ChangeTracker.with(context, t => t.tryInsertTypeAnnotation(context.file, info.declaration, info.returnTypeNode)); return { renameFilename: undefined, renameLocation: undefined, edits }; @@ -18,27 +22,19 @@ namespace ts.refactor.inferFunctionReturnType { function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const info = getInfo(context); - if (info && typeof info !== "string") { + if (!info) return emptyArray; + if (!isRefactorErrorInfo(info)) { return [{ name: refactorName, description: refactorDescription, - actions: [{ - name: refactorName, - description: refactorDescription, - refactorKind: rewriteFunctionReturnType - }] + actions: [inferReturnTypeAction] }]; } - if (info && typeof info === "string" && context.preferences.provideRefactorNotApplicableReason) { + if (context.preferences.provideRefactorNotApplicableReason) { return [{ name: refactorName, description: refactorDescription, - actions: [{ - name: refactorName, - description: refactorDescription, - refactorKind: rewriteFunctionReturnType, - notApplicableReason: info - }] + actions: [{ ...inferReturnTypeAction, notApplicableReason: info.error }] }]; } return emptyArray; @@ -50,26 +46,24 @@ namespace ts.refactor.inferFunctionReturnType { | ArrowFunction | MethodDeclaration; - type InfoOrError = FunctionInfo | string; - interface FunctionInfo { declaration: ConvertibleDeclaration; returnTypeNode: TypeNode; } - function getInfo(context: RefactorContext): InfoOrError | undefined { - if (isInJSFile(context.file) || !refactorKindBeginsWith(rewriteFunctionReturnType, context.refactorKind)) return; + function getInfo(context: RefactorContext): FunctionInfo | RefactorErrorInfo | undefined { + if (isInJSFile(context.file) || !refactorKindBeginsWith(inferReturnTypeAction.refactorKind, context.refactorKind)) return; const token = getTokenAtPosition(context.file, context.startPosition); const declaration = findAncestor(token, isConvertibleDeclaration); if (!declaration || !declaration.body || declaration.type) { - return getLocaleSpecificMessage(Diagnostics.Return_type_must_be_inferred_from_a_function); + return { error: getLocaleSpecificMessage(Diagnostics.Return_type_must_be_inferred_from_a_function) }; } const typeChecker = context.program.getTypeChecker(); const returnType = tryGetReturnType(typeChecker, declaration); if (!returnType) { - return getLocaleSpecificMessage(Diagnostics.Could_not_determine_function_return_type); + return { error: getLocaleSpecificMessage(Diagnostics.Could_not_determine_function_return_type) }; }; const returnTypeNode = typeChecker.typeToTypeNode(returnType, declaration, NodeBuilderFlags.NoTruncation); diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index 328e2bd9e3a1f..79433d8d07070 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -1,19 +1,24 @@ /* @internal */ namespace ts.refactor { const refactorName = "Move to a new file"; - const refactorMoveNewFile = "refactor.move.newFile"; + const description = getLocaleSpecificMessage(Diagnostics.Move_to_a_new_file); + const moveToNewFileAction = { + name: refactorName, + description, + refactorKind: "refactor.move.newFile", + }; registerRefactor(refactorName, { - refactorKinds: [refactorMoveNewFile], + refactorKinds: [moveToNewFileAction.refactorKind], getAvailableActions(context): readonly ApplicableRefactorInfo[] { - const description = getLocaleSpecificMessage(Diagnostics.Move_to_a_new_file); const statements = getStatementsToMove(context); if (statements && context.preferences.allowTextChangesInNewFiles) { - return [{ name: refactorName, description, actions: [{ name: refactorName, description, refactorKind: refactorMoveNewFile }] }]; + return [{ name: refactorName, description, actions: [moveToNewFileAction] }]; } if (context.preferences.provideRefactorNotApplicableReason) { - return [{ name: refactorName, description, actions: [{ name: refactorName, description, refactorKind: refactorMoveNewFile, - notApplicableReason: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_statement_or_statements) }] }]; + return [{ name: refactorName, description, actions: + [{ ...moveToNewFileAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_statement_or_statements) }] + }]; } return emptyArray; }, From 90d9c431e206a7e4726e97cc43bf8a5c1ee5f0ba Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Tue, 15 Dec 2020 00:49:51 -0800 Subject: [PATCH 17/20] keep list of actions --- src/services/refactorProvider.ts | 2 +- .../addOrRemoveBracesToArrowFunction.ts | 7 +-- ...onvertArrowFunctionOrFunctionExpression.ts | 12 ++-- src/services/refactors/convertExport.ts | 10 ++-- src/services/refactors/convertImport.ts | 9 ++- .../convertOverloadListToSingleSignature.ts | 2 +- .../convertParamsToDestructuredObject.ts | 2 +- .../convertStringOrTemplateLiteral.ts | 2 +- .../convertToOptionalChainExpression.ts | 6 +- src/services/refactors/extractSymbol.ts | 57 +++++++++---------- src/services/refactors/extractType.ts | 11 ++-- .../generateGetAccessorAndSetAccessor.ts | 2 +- .../refactors/inferFunctionReturnType.ts | 2 +- src/services/refactors/moveToNewFile.ts | 2 +- src/services/types.ts | 4 +- 15 files changed, 58 insertions(+), 72 deletions(-) diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 66ba0b6ea4211..84e8b5e1d9179 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -12,7 +12,7 @@ namespace ts.refactor { export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] { return arrayFrom(flatMapIterator(refactors.values(), refactor => context.cancellationToken && context.cancellationToken.isCancellationRequested() ? - undefined : !refactor.refactorKinds?.some(kind => refactorKindBeginsWith(kind, context.refactorKind)) ? + undefined : !refactor.actions?.some(kind => kind.refactorKind && refactorKindBeginsWith(kind.refactorKind, context.refactorKind)) ? undefined : refactor.getAvailableActions(context))); } diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index a2febc0fdffa1..f924504ed9a0a 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -15,12 +15,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { refactorKind: "refactor.rewrite.arrow.braces.remove" }; - const refactorKinds = [ - addBracesAction.refactorKind, - removeBracesAction.refactorKind - ]; - - registerRefactor(refactorName, { refactorKinds, getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { actions: [removeBracesAction], getEditsForAction, getAvailableActions }); interface FunctionBracesInfo { func: ArrowFunction; diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index bbf829856b7aa..224de5011120c 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -21,13 +21,11 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { refactorKind: "refactor.rewrite.function.arrow", }; - const refactorKinds = [ - toAnonymousFunctionAction.refactorKind, - toNamedFunctionAction.refactorKind, - toArrowFunctionAction.refactorKind, - ]; - - registerRefactor(refactorName, { refactorKinds, getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { + actions: [toAnonymousFunctionAction, toNamedFunctionAction, toArrowFunctionAction], + getEditsForAction, + getAvailableActions + }); interface FunctionInfo { readonly selectedVariableDeclaration: boolean; diff --git a/src/services/refactors/convertExport.ts b/src/services/refactors/convertExport.ts index c2d096a71d353..eb48a47c74417 100644 --- a/src/services/refactors/convertExport.ts +++ b/src/services/refactors/convertExport.ts @@ -16,13 +16,11 @@ namespace ts.refactor { refactorKind: rewriteExportToDefaultKind }; - const refactorKinds = [ - rewriteExportToDefaultKind, - rewriteExportToNamedKind - ]; - registerRefactor(refactorName, { - refactorKinds, + actions: [ + defaultToNamedAction, + namedToDefaultAction + ], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getInfo(context, context.triggerReason === "invoked"); if (!info) return emptyArray; diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index b9ffd238b074c..e53d00b458854 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -12,13 +12,12 @@ namespace ts.refactor { description: Diagnostics.Convert_named_imports_to_namespace_import.message, refactorKind: "refactor.rewrite.import.namespace", }; - const refactorKinds = [ - namespaceToNamedAction.refactorKind, - namedToNamespaceAction.refactorKind - ]; registerRefactor(refactorName, { - refactorKinds, + actions: [ + namespaceToNamedAction, + namedToNamespaceAction + ], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getImportToConvert(context, context.triggerReason === "invoked"); if (!info) return emptyArray; diff --git a/src/services/refactors/convertOverloadListToSingleSignature.ts b/src/services/refactors/convertOverloadListToSingleSignature.ts index c49cf198c9756..8c11bbfb4f002 100644 --- a/src/services/refactors/convertOverloadListToSingleSignature.ts +++ b/src/services/refactors/convertOverloadListToSingleSignature.ts @@ -8,7 +8,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { refactorKind: "refactor.rewrite.function.overloadList", }; - registerRefactor(refactorName, { refactorKinds: [functionOverloadAction.refactorKind], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { actions: [functionOverloadAction], getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition, program } = context; diff --git a/src/services/refactors/convertParamsToDestructuredObject.ts b/src/services/refactors/convertParamsToDestructuredObject.ts index 9fdcf486fdae7..616bc99750363 100644 --- a/src/services/refactors/convertParamsToDestructuredObject.ts +++ b/src/services/refactors/convertParamsToDestructuredObject.ts @@ -9,7 +9,7 @@ namespace ts.refactor.convertParamsToDestructuredObject { description: refactorDescription, refactorKind: "refactor.rewrite.parameters.toDestructured" }; - registerRefactor(refactorName, { refactorKinds: [toDestructuredAction.refactorKind], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { actions: [toDestructuredAction], getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition } = context; diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index 9d63969bed0c1..b4d2d194ebaa5 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -8,7 +8,7 @@ namespace ts.refactor.convertStringOrTemplateLiteral { description: refactorDescription, refactorKind: "refactor.rewrite.string" }; - registerRefactor(refactorName, { refactorKinds: [convertStringAction.refactorKind], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { actions: [convertStringAction], getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition } = context; diff --git a/src/services/refactors/convertToOptionalChainExpression.ts b/src/services/refactors/convertToOptionalChainExpression.ts index d9a1001a57ee5..44969c087a6c6 100644 --- a/src/services/refactors/convertToOptionalChainExpression.ts +++ b/src/services/refactors/convertToOptionalChainExpression.ts @@ -8,7 +8,7 @@ namespace ts.refactor.convertToOptionalChainExpression { description: convertToOptionalChainExpressionMessage, refactorKind: "refactor.rewrite.expression.optionalChain", }; - registerRefactor(refactorName, { refactorKinds: [toOptionalChainAction.refactorKind], getAvailableActions, getEditsForAction }); + registerRefactor(refactorName, { actions: [toOptionalChainAction], getAvailableActions, getEditsForAction }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const info = getInfo(context, context.triggerReason === "invoked"); @@ -18,7 +18,7 @@ namespace ts.refactor.convertToOptionalChainExpression { return [{ name: refactorName, description: convertToOptionalChainExpressionMessage, - actions: [toOptionalChainAction] + actions: [toOptionalChainAction], }]; } @@ -26,7 +26,7 @@ namespace ts.refactor.convertToOptionalChainExpression { return [{ name: refactorName, description: convertToOptionalChainExpressionMessage, - actions: [{ ...toOptionalChainAction, notApplicableReason: info.error }] + actions: [{ ...toOptionalChainAction, notApplicableReason: info.error }], }]; } return emptyArray; diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 42aef48c974c4..df03d39dcc619 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -2,14 +2,21 @@ namespace ts.refactor.extractSymbol { const refactorName = "Extract Symbol"; - const extractConstantKind = "refactor.extract.constant"; - const extractFunctionKind = "refactor.extract.function"; - const refactorKinds = [ - extractConstantKind, - extractFunctionKind, - ]; - - registerRefactor(refactorName, { refactorKinds, getAvailableActions, getEditsForAction }); + const extractConstantAction = { + name: "Extract Constant", + description: getLocaleSpecificMessage(Diagnostics.Extract_constant), + refactorKind: "refactor.extract.constant", + }; + const extractFunctionAction = { + name: "Extract Function", + description: getLocaleSpecificMessage(Diagnostics.Extract_function), + refactorKind: "refactor.extract.function", + }; + registerRefactor(refactorName, { + actions: [extractConstantAction, extractFunctionAction], + getAvailableActions, + getEditsForAction + }); /** * Compute the associated code actions @@ -26,28 +33,18 @@ namespace ts.refactor.extractSymbol { } const errors = []; - if (refactorKindBeginsWith(extractFunctionKind, requestedRefactor)) { + if (refactorKindBeginsWith(extractFunctionAction.refactorKind, requestedRefactor)) { errors.push({ name: refactorName, - description: getLocaleSpecificMessage(Diagnostics.Extract_function), - actions: [{ - description: getLocaleSpecificMessage(Diagnostics.Extract_function), - name: "function_extract_error", - notApplicableReason: getStringError(rangeToExtract.errors), - refactorKind: extractFunctionKind - }] + description: extractFunctionAction.description, + actions: [{ ...extractFunctionAction, notApplicableReason: getStringError(rangeToExtract.errors) }] }); } - if (refactorKindBeginsWith(extractConstantKind, requestedRefactor)) { + if (refactorKindBeginsWith(extractConstantAction.refactorKind, requestedRefactor)) { errors.push({ name: refactorName, - description: getLocaleSpecificMessage(Diagnostics.Extract_constant), - actions: [{ - description: getLocaleSpecificMessage(Diagnostics.Extract_constant), - name: "constant_extract_error", - notApplicableReason: getStringError(rangeToExtract.errors), - refactorKind: extractConstantKind - }] + description: extractConstantAction.description, + actions: [{ ...extractConstantAction, notApplicableReason: getStringError(rangeToExtract.errors) }] }); } return errors; @@ -70,7 +67,7 @@ namespace ts.refactor.extractSymbol { let i = 0; for (const { functionExtraction, constantExtraction } of extractions) { const description = functionExtraction.description; - if(refactorKindBeginsWith(extractFunctionKind, requestedRefactor)){ + if(refactorKindBeginsWith(extractFunctionAction.refactorKind, requestedRefactor)){ if (functionExtraction.errors.length === 0) { // Don't issue refactorings with duplicated names. // Scopes come back in "innermost first" order, so extractions will @@ -80,7 +77,7 @@ namespace ts.refactor.extractSymbol { functionActions.push({ description, name: `function_scope_${i}`, - refactorKind: extractFunctionKind + refactorKind: extractFunctionAction.refactorKind }); } } @@ -89,13 +86,13 @@ namespace ts.refactor.extractSymbol { description, name: `function_scope_${i}`, notApplicableReason: getStringError(functionExtraction.errors), - refactorKind: extractFunctionKind + refactorKind: extractFunctionAction.refactorKind }; } } // Skip these since we don't have a way to report errors yet - if(refactorKindBeginsWith(extractConstantKind, requestedRefactor)) { + if(refactorKindBeginsWith(extractConstantAction.refactorKind, requestedRefactor)) { if (constantExtraction.errors.length === 0) { // Don't issue refactorings with duplicated names. // Scopes come back in "innermost first" order, so extractions will @@ -106,7 +103,7 @@ namespace ts.refactor.extractSymbol { constantActions.push({ description, name: `constant_scope_${i}`, - refactorKind: extractConstantKind + refactorKind: extractConstantAction.refactorKind }); } } @@ -115,7 +112,7 @@ namespace ts.refactor.extractSymbol { description, name: `constant_scope_${i}`, notApplicableReason: getStringError(constantExtraction.errors), - refactorKind: extractConstantKind + refactorKind: extractConstantAction.refactorKind }; } } diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts index 01fbd708a121c..fc61d08e66286 100644 --- a/src/services/refactors/extractType.ts +++ b/src/services/refactors/extractType.ts @@ -20,14 +20,13 @@ namespace ts.refactor { description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef), refactorKind: "refactor.extract.typedef" }; - const refactorKinds = [ - extractToTypeAliasAction.refactorKind, - extractToInterfaceAction.refactorKind, - extractToTypeDefAction.refactorKind, - ]; registerRefactor(refactorName, { - refactorKinds, + actions: [ + extractToTypeAliasAction, + extractToInterfaceAction, + extractToTypeDefAction + ], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getRangeToExtract(context, context.triggerReason === "invoked"); if (!info) return emptyArray; diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index 4ec9cd192db83..4e9d459c32c54 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -9,7 +9,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { refactorKind: "refactor.rewrite.property.generateAccessors", }; registerRefactor(actionName, { - refactorKinds: [generateGetSetAction.refactorKind], + actions: [generateGetSetAction], getEditsForAction(context, actionName) { if (!context.endPosition) return undefined; const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition); diff --git a/src/services/refactors/inferFunctionReturnType.ts b/src/services/refactors/inferFunctionReturnType.ts index 9aee141d42423..6dfbcd8f90641 100644 --- a/src/services/refactors/inferFunctionReturnType.ts +++ b/src/services/refactors/inferFunctionReturnType.ts @@ -8,7 +8,7 @@ namespace ts.refactor.inferFunctionReturnType { description: refactorDescription, refactorKind: "refactor.rewrite.function.returnType" }; - registerRefactor(refactorName, { refactorKinds: [inferReturnTypeAction.refactorKind], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { actions: [inferReturnTypeAction], getEditsForAction, getAvailableActions }); function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined { const info = getInfo(context); diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index 79433d8d07070..1aab681f29e9a 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -9,7 +9,7 @@ namespace ts.refactor { refactorKind: "refactor.move.newFile", }; registerRefactor(refactorName, { - refactorKinds: [moveToNewFileAction.refactorKind], + actions: [moveToNewFileAction], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const statements = getStatementsToMove(context); if (statements && context.preferences.allowTextChangesInNewFiles) { diff --git a/src/services/types.ts b/src/services/types.ts index db38f00f68837..70990a4dda38d 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -1468,8 +1468,8 @@ namespace ts { /** @internal */ export interface Refactor { - /** list of hierarchical refactors as dotted strings */ - refactorKinds?: string[]; + /** list of actions a refactor can provide */ + actions?: RefactorActionInfo[]; /** Compute the associated code actions */ getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined; From 762841bd16ea3fd78e64f09eb153db9177053435 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Tue, 15 Dec 2020 14:56:36 -0800 Subject: [PATCH 18/20] address PR comments --- src/harness/fourslashImpl.ts | 2 +- src/services/codefixes/generateAccessors.ts | 2 +- src/services/refactorProvider.ts | 2 +- .../addOrRemoveBracesToArrowFunction.ts | 7 ++++--- .../convertArrowFunctionOrFunctionExpression.ts | 9 +++++---- src/services/refactors/convertExport.ts | 13 +++++-------- src/services/refactors/convertImport.ts | 6 +++--- .../convertOverloadListToSingleSignature.ts | 8 ++++++-- .../convertParamsToDestructuredObject.ts | 6 +++++- .../refactors/convertStringOrTemplateLiteral.ts | 6 +++++- .../convertToOptionalChainExpression.ts | 6 +++++- src/services/refactors/extractSymbol.ts | 5 ++++- src/services/refactors/extractType.ts | 17 +++++++---------- .../generateGetAccessorAndSetAccessor.ts | 2 +- .../refactors/inferFunctionReturnType.ts | 6 +++++- src/services/refactors/moveToNewFile.ts | 2 +- src/services/types.ts | 5 +++-- 17 files changed, 62 insertions(+), 42 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 73db76b391004..10267937b4006 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3374,7 +3374,7 @@ namespace FourSlash { public verifyRefactorKindsAvailable(refactorKind: string, expected: string[], preferences = ts.emptyOptions) { const refactors = this.getApplicableRefactorsAtSelection("invoked", refactorKind, preferences); - const availableKinds = refactors.reduce((a, b) => [...a, ...b.actions.map((action) => action.refactorKind)], []); + const availableKinds = ts.flatMap(refactors, refactor => refactor.actions).map(action => action.refactorKind); assert.deepEqual(availableKinds.sort(), expected.sort(), `Expected refactorKinds to be equal`); } diff --git a/src/services/codefixes/generateAccessors.ts b/src/services/codefixes/generateAccessors.ts index 45f27ff08e185..99bd53e29343f 100644 --- a/src/services/codefixes/generateAccessors.ts +++ b/src/services/codefixes/generateAccessors.ts @@ -105,7 +105,7 @@ namespace ts.codefix { return modifierFlags; } - export function getAccessorConvertiblePropertyAtPosition(file: SourceFile, program: Program, start: number, end: number, considerEmptySpans = true): AccessorInfo | Info | undefined { + export function getAccessorConvertiblePropertyAtPosition(file: SourceFile, program: Program, start: number, end: number, considerEmptySpans = true): Info | undefined { const node = getTokenAtPosition(file, start); const cursorRequest = start === end && considerEmptySpans; const declaration = findAncestor(node.parent, isAcceptedDeclaration); diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 84e8b5e1d9179..66ba0b6ea4211 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -12,7 +12,7 @@ namespace ts.refactor { export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] { return arrayFrom(flatMapIterator(refactors.values(), refactor => context.cancellationToken && context.cancellationToken.isCancellationRequested() ? - undefined : !refactor.actions?.some(kind => kind.refactorKind && refactorKindBeginsWith(kind.refactorKind, context.refactorKind)) ? + undefined : !refactor.refactorKinds?.some(kind => refactorKindBeginsWith(kind, context.refactorKind)) ? undefined : refactor.getAvailableActions(context))); } diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index f924504ed9a0a..9caa2ad35f0a5 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -8,14 +8,15 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { description: Diagnostics.Add_braces_to_arrow_function.message, refactorKind: "refactor.rewrite.arrow.braces.add", }; - const removeBracesAction = { name: "Remove braces from arrow function", description: Diagnostics.Remove_braces_from_arrow_function.message, refactorKind: "refactor.rewrite.arrow.braces.remove" }; - - registerRefactor(refactorName, { actions: [removeBracesAction], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { + refactorKinds: [removeBracesAction.refactorKind], + getEditsForAction, + getAvailableActions }); interface FunctionBracesInfo { func: ArrowFunction; diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 224de5011120c..98eab25febd27 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -8,21 +8,22 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { description: getLocaleSpecificMessage(Diagnostics.Convert_to_anonymous_function), refactorKind: "refactor.rewrite.function.anonymous", }; - const toNamedFunctionAction = { name: "Convert to named function", description: getLocaleSpecificMessage(Diagnostics.Convert_to_named_function), refactorKind: "refactor.rewrite.function.named", }; - const toArrowFunctionAction = { name: "Convert to arrow function", description: getLocaleSpecificMessage(Diagnostics.Convert_to_arrow_function), refactorKind: "refactor.rewrite.function.arrow", }; - registerRefactor(refactorName, { - actions: [toAnonymousFunctionAction, toNamedFunctionAction, toArrowFunctionAction], + refactorKinds: [ + toAnonymousFunctionAction.refactorKind, + toNamedFunctionAction.refactorKind, + toArrowFunctionAction.refactorKind + ], getEditsForAction, getAvailableActions }); diff --git a/src/services/refactors/convertExport.ts b/src/services/refactors/convertExport.ts index eb48a47c74417..32bf029fbe601 100644 --- a/src/services/refactors/convertExport.ts +++ b/src/services/refactors/convertExport.ts @@ -1,25 +1,22 @@ /* @internal */ namespace ts.refactor { const refactorName = "Convert export"; - const rewriteExportToNamedKind = "refactor.rewrite.export.named"; - const rewriteExportToDefaultKind = "refactor.rewrite.export.default"; const defaultToNamedAction = { name: "Convert default export to named export", description: Diagnostics.Convert_default_export_to_named_export.message, - refactorKind: rewriteExportToNamedKind + refactorKind: "refactor.rewrite.export.named" }; - const namedToDefaultAction = { name: "Convert named export to default export", description: Diagnostics.Convert_named_export_to_default_export.message, - refactorKind: rewriteExportToDefaultKind + refactorKind: "refactor.rewrite.export.default" }; registerRefactor(refactorName, { - actions: [ - defaultToNamedAction, - namedToDefaultAction + refactorKinds: [ + defaultToNamedAction.refactorKind, + namedToDefaultAction.refactorKind ], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getInfo(context, context.triggerReason === "invoked"); diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index e53d00b458854..9c6e40b94ca2e 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -14,9 +14,9 @@ namespace ts.refactor { }; registerRefactor(refactorName, { - actions: [ - namespaceToNamedAction, - namedToNamespaceAction + refactorKinds: [ + namespaceToNamedAction.refactorKind, + namedToNamespaceAction.refactorKind ], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getImportToConvert(context, context.triggerReason === "invoked"); diff --git a/src/services/refactors/convertOverloadListToSingleSignature.ts b/src/services/refactors/convertOverloadListToSingleSignature.ts index 8c11bbfb4f002..c559605e917d5 100644 --- a/src/services/refactors/convertOverloadListToSingleSignature.ts +++ b/src/services/refactors/convertOverloadListToSingleSignature.ts @@ -2,13 +2,17 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { const refactorName = "Convert overload list to single signature"; const refactorDescription = Diagnostics.Convert_overload_list_to_single_signature.message; + const functionOverloadAction = { name: refactorName, description: refactorDescription, refactorKind: "refactor.rewrite.function.overloadList", }; - - registerRefactor(refactorName, { actions: [functionOverloadAction], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { + refactorKinds: [functionOverloadAction.refactorKind], + getEditsForAction, + getAvailableActions + }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition, program } = context; diff --git a/src/services/refactors/convertParamsToDestructuredObject.ts b/src/services/refactors/convertParamsToDestructuredObject.ts index 616bc99750363..93378b5a872c8 100644 --- a/src/services/refactors/convertParamsToDestructuredObject.ts +++ b/src/services/refactors/convertParamsToDestructuredObject.ts @@ -9,7 +9,11 @@ namespace ts.refactor.convertParamsToDestructuredObject { description: refactorDescription, refactorKind: "refactor.rewrite.parameters.toDestructured" }; - registerRefactor(refactorName, { actions: [toDestructuredAction], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { + refactorKinds: [toDestructuredAction.refactorKind], + getEditsForAction, + getAvailableActions + }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition } = context; diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index b4d2d194ebaa5..b475d125dccb2 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -8,7 +8,11 @@ namespace ts.refactor.convertStringOrTemplateLiteral { description: refactorDescription, refactorKind: "refactor.rewrite.string" }; - registerRefactor(refactorName, { actions: [convertStringAction], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { + refactorKinds: [convertStringAction.refactorKind], + getEditsForAction, + getAvailableActions + }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const { file, startPosition } = context; diff --git a/src/services/refactors/convertToOptionalChainExpression.ts b/src/services/refactors/convertToOptionalChainExpression.ts index 44969c087a6c6..0a64f1619e934 100644 --- a/src/services/refactors/convertToOptionalChainExpression.ts +++ b/src/services/refactors/convertToOptionalChainExpression.ts @@ -8,7 +8,11 @@ namespace ts.refactor.convertToOptionalChainExpression { description: convertToOptionalChainExpressionMessage, refactorKind: "refactor.rewrite.expression.optionalChain", }; - registerRefactor(refactorName, { actions: [toOptionalChainAction], getAvailableActions, getEditsForAction }); + registerRefactor(refactorName, { + refactorKinds: [toOptionalChainAction.refactorKind], + getAvailableActions, + getEditsForAction + }); function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { const info = getInfo(context, context.triggerReason === "invoked"); diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index df03d39dcc619..2da74063a88f2 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -13,7 +13,10 @@ namespace ts.refactor.extractSymbol { refactorKind: "refactor.extract.function", }; registerRefactor(refactorName, { - actions: [extractConstantAction, extractFunctionAction], + refactorKinds: [ + extractConstantAction.refactorKind, + extractFunctionAction.refactorKind + ], getAvailableActions, getEditsForAction }); diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts index fc61d08e66286..e7a72f53a7b55 100644 --- a/src/services/refactors/extractType.ts +++ b/src/services/refactors/extractType.ts @@ -1,9 +1,6 @@ /* @internal */ namespace ts.refactor { const refactorName = "Extract type"; - const extractToTypeAlias = "Extract to type alias"; - const extractToInterface = "Extract to interface"; - const extractToTypeDef = "Extract to typedef"; const extractToTypeAliasAction = { name: "Extract to type alias", @@ -22,10 +19,10 @@ namespace ts.refactor { }; registerRefactor(refactorName, { - actions: [ - extractToTypeAliasAction, - extractToInterfaceAction, - extractToTypeDefAction + refactorKinds: [ + extractToTypeAliasAction.refactorKind, + extractToInterfaceAction.refactorKind, + extractToTypeDefAction.refactorKind ], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getRangeToExtract(context, context.triggerReason === "invoked"); @@ -62,13 +59,13 @@ namespace ts.refactor { const name = getUniqueName("NewType", file); const edits = textChanges.ChangeTracker.with(context, changes => { switch (actionName) { - case extractToTypeAlias: + case extractToTypeAliasAction.name: Debug.assert(!info.isJS, "Invalid actionName/JS combo"); return doTypeAliasChange(changes, file, name, info); - case extractToTypeDef: + case extractToTypeDefAction.name: Debug.assert(info.isJS, "Invalid actionName/JS combo"); return doTypedefChange(changes, file, name, info); - case extractToInterface: + case extractToInterfaceAction.name: Debug.assert(!info.isJS && !!info.typeElements, "Invalid actionName/JS combo"); return doInterfaceChange(changes, file, name, info as InterfaceInfo); default: diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index 4e9d459c32c54..4ec9cd192db83 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -9,7 +9,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { refactorKind: "refactor.rewrite.property.generateAccessors", }; registerRefactor(actionName, { - actions: [generateGetSetAction], + refactorKinds: [generateGetSetAction.refactorKind], getEditsForAction(context, actionName) { if (!context.endPosition) return undefined; const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition); diff --git a/src/services/refactors/inferFunctionReturnType.ts b/src/services/refactors/inferFunctionReturnType.ts index 6dfbcd8f90641..dd07390cb304a 100644 --- a/src/services/refactors/inferFunctionReturnType.ts +++ b/src/services/refactors/inferFunctionReturnType.ts @@ -8,7 +8,11 @@ namespace ts.refactor.inferFunctionReturnType { description: refactorDescription, refactorKind: "refactor.rewrite.function.returnType" }; - registerRefactor(refactorName, { actions: [inferReturnTypeAction], getEditsForAction, getAvailableActions }); + registerRefactor(refactorName, { + refactorKinds: [inferReturnTypeAction.refactorKind], + getEditsForAction, + getAvailableActions + }); function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined { const info = getInfo(context); diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index 1aab681f29e9a..79433d8d07070 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -9,7 +9,7 @@ namespace ts.refactor { refactorKind: "refactor.move.newFile", }; registerRefactor(refactorName, { - actions: [moveToNewFileAction], + refactorKinds: [moveToNewFileAction.refactorKind], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const statements = getStatementsToMove(context); if (statements && context.preferences.allowTextChangesInNewFiles) { diff --git a/src/services/types.ts b/src/services/types.ts index 70990a4dda38d..c20d045c00f9f 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -1468,8 +1468,9 @@ namespace ts { /** @internal */ export interface Refactor { - /** list of actions a refactor can provide */ - actions?: RefactorActionInfo[]; + /** List of action refactorKinds a refactor can provide. + * Used to skip unnecessary calculation when specific refactors are requested. */ + refactorKinds?: string[]; /** Compute the associated code actions */ getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined; From d8de6c8cab494d98656b3026f4dcad6594fca7fb Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Wed, 16 Dec 2020 12:37:59 -0800 Subject: [PATCH 19/20] response protocol --- src/server/protocol.ts | 5 +++++ tests/baselines/reference/api/tsserverlibrary.d.ts | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index d80aced54229a..61ddb040ed2cf 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -624,6 +624,11 @@ namespace ts.server.protocol { * the current context. */ notApplicableReason?: string; + + /** + * The hierarchical dotted name of the refactor action. + */ + refactorKind?: string; } export interface GetEditsForRefactorRequest extends Request { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 037f9ebc12652..9b5521bd2ac26 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -6978,6 +6978,10 @@ declare namespace ts.server.protocol { * the current context. */ notApplicableReason?: string; + /** + * The hierarchical dotted name of the refactor action. + */ + refactorKind?: string; } interface GetEditsForRefactorRequest extends Request { command: CommandTypes.GetEditsForRefactor; From 647db269b51f295642f74439d328fa3dc9366e86 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Tue, 22 Dec 2020 13:57:40 -0800 Subject: [PATCH 20/20] address more comments --- src/harness/fourslashImpl.ts | 20 ++++++------- src/harness/fourslashInterfaceImpl.ts | 4 +-- src/server/protocol.ts | 4 +-- src/server/session.ts | 2 +- src/services/refactorProvider.ts | 6 ++-- .../addOrRemoveBracesToArrowFunction.ts | 12 ++++---- ...onvertArrowFunctionOrFunctionExpression.ts | 22 +++++++-------- src/services/refactors/convertExport.ts | 10 +++---- src/services/refactors/convertImport.ts | 10 +++---- .../convertOverloadListToSingleSignature.ts | 4 +-- .../convertParamsToDestructuredObject.ts | 4 +-- .../convertStringOrTemplateLiteral.ts | 4 +-- .../convertToOptionalChainExpression.ts | 4 +-- src/services/refactors/extractSymbol.ts | 28 +++++++++---------- src/services/refactors/extractType.ts | 14 +++++----- .../generateGetAccessorAndSetAccessor.ts | 4 +-- src/services/refactors/helpers.ts | 2 +- .../refactors/inferFunctionReturnType.ts | 6 ++-- src/services/refactors/moveToNewFile.ts | 6 ++-- src/services/services.ts | 8 +++--- src/services/types.ts | 10 +++---- .../reference/api/tsserverlibrary.d.ts | 8 +++--- tests/baselines/reference/api/typescript.d.ts | 4 +-- 23 files changed, 98 insertions(+), 98 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 10267937b4006..c299c4ce3697b 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3372,10 +3372,10 @@ namespace FourSlash { } } - public verifyRefactorKindsAvailable(refactorKind: string, expected: string[], preferences = ts.emptyOptions) { - const refactors = this.getApplicableRefactorsAtSelection("invoked", refactorKind, preferences); - const availableKinds = ts.flatMap(refactors, refactor => refactor.actions).map(action => action.refactorKind); - assert.deepEqual(availableKinds.sort(), expected.sort(), `Expected refactorKinds to be equal`); + public verifyRefactorKindsAvailable(kind: string, expected: string[], preferences = ts.emptyOptions) { + const refactors = this.getApplicableRefactorsAtSelection("invoked", kind, preferences); + const availableKinds = ts.flatMap(refactors, refactor => refactor.actions).map(action => action.kind); + assert.deepEqual(availableKinds.sort(), expected.sort(), `Expected kinds to be equal`); } public verifyRefactorsAvailable(names: readonly string[]): void { @@ -3791,14 +3791,14 @@ namespace FourSlash { test(renameKeys(newFileContents, key => pathUpdater(key) || key), "with file moved"); } - private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit", refactorKind?: string, preferences = ts.emptyOptions) { - return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, preferences, triggerReason, refactorKind); + private getApplicableRefactorsAtSelection(triggerReason: ts.RefactorTriggerReason = "implicit", kind?: string, preferences = ts.emptyOptions) { + return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName, preferences, triggerReason, kind); } - private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit", refactorKind?: string): readonly ts.ApplicableRefactorInfo[] { - return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason, refactorKind); // eslint-disable-line no-in-operator + private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason = "implicit", kind?: string): readonly ts.ApplicableRefactorInfo[] { + return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences, triggerReason, kind); // eslint-disable-line no-in-operator } - private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason, refactorKind?: string): readonly ts.ApplicableRefactorInfo[] { - return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, refactorKind) || ts.emptyArray; + private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions, triggerReason: ts.RefactorTriggerReason, kind?: string): readonly ts.ApplicableRefactorInfo[] { + return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, kind) || ts.emptyArray; } public configurePlugin(pluginName: string, configuration: any): void { diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index e2089ee007caa..5daea88008964 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -215,8 +215,8 @@ namespace FourSlashInterface { this.state.verifyRefactorAvailable(this.negative, triggerReason, name, actionName); } - public refactorKindAvailable(refactorKind: string, expected: string[], preferences = ts.emptyOptions) { - this.state.verifyRefactorKindsAvailable(refactorKind, expected, preferences); + public refactorKindAvailable(kind: string, expected: string[], preferences = ts.emptyOptions) { + this.state.verifyRefactorKindsAvailable(kind, expected, preferences); } public toggleLineComment(newFileContent: string) { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 61ddb040ed2cf..5cf5c2569aaa4 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -564,7 +564,7 @@ namespace ts.server.protocol { } export type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs & { triggerReason?: RefactorTriggerReason; - refactorKind?: string; + kind?: string; }; export type RefactorTriggerReason = "implicit" | "invoked"; @@ -628,7 +628,7 @@ namespace ts.server.protocol { /** * The hierarchical dotted name of the refactor action. */ - refactorKind?: string; + kind?: string; } export interface GetEditsForRefactorRequest extends Request { diff --git a/src/server/session.ts b/src/server/session.ts index a605e621ca4c6..ed872c0f3466e 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -2100,7 +2100,7 @@ namespace ts.server { private getApplicableRefactors(args: protocol.GetApplicableRefactorsRequestArgs): protocol.ApplicableRefactorInfo[] { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file)!; - return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason, args.refactorKind); + return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason, args.kind); } private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo { diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 66ba0b6ea4211..ccc6f81d2924d 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -11,9 +11,9 @@ namespace ts.refactor { export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] { return arrayFrom(flatMapIterator(refactors.values(), refactor => - context.cancellationToken && context.cancellationToken.isCancellationRequested() ? - undefined : !refactor.refactorKinds?.some(kind => refactorKindBeginsWith(kind, context.refactorKind)) ? - undefined : refactor.getAvailableActions(context))); + context.cancellationToken && context.cancellationToken.isCancellationRequested() || + !refactor.kinds?.some(kind => refactorKindBeginsWith(kind, context.kind)) ? undefined : + refactor.getAvailableActions(context))); } export function getEditsForRefactor(context: RefactorContext, refactorName: string, actionName: string): RefactorEditInfo | undefined { diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index 9caa2ad35f0a5..3cd8e7dfb346b 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -6,15 +6,15 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { const addBracesAction = { name: "Add braces to arrow function", description: Diagnostics.Add_braces_to_arrow_function.message, - refactorKind: "refactor.rewrite.arrow.braces.add", + kind: "refactor.rewrite.arrow.braces.add", }; const removeBracesAction = { name: "Remove braces from arrow function", description: Diagnostics.Remove_braces_from_arrow_function.message, - refactorKind: "refactor.rewrite.arrow.braces.remove" + kind: "refactor.rewrite.arrow.braces.remove" }; registerRefactor(refactorName, { - refactorKinds: [removeBracesAction.refactorKind], + kinds: [removeBracesAction.kind], getEditsForAction, getAvailableActions }); @@ -88,7 +88,7 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return { renameFilename: undefined, renameLocation: undefined, edits }; } - function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number, considerFunctionBodies = true, refactorKind?: string): FunctionBracesInfo | RefactorErrorInfo | undefined { + function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number, considerFunctionBodies = true, kind?: string): FunctionBracesInfo | RefactorErrorInfo | undefined { const node = getTokenAtPosition(file, startPosition); const func = getContainingFunction(node); @@ -108,10 +108,10 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return undefined; } - if (refactorKindBeginsWith(addBracesAction.refactorKind, refactorKind) && isExpression(func.body)) { + if (refactorKindBeginsWith(addBracesAction.kind, kind) && isExpression(func.body)) { return { func, addBraces: true, expression: func.body }; } - else if (refactorKindBeginsWith(removeBracesAction.refactorKind, refactorKind) && isBlock(func.body) && func.body.statements.length === 1) { + else if (refactorKindBeginsWith(removeBracesAction.kind, kind) && isBlock(func.body) && func.body.statements.length === 1) { const firstStatement = first(func.body.statements); if (isReturnStatement(firstStatement)) { return { func, addBraces: false, expression: firstStatement.expression, returnStatement: firstStatement }; diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 98eab25febd27..dc5948c89874a 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -6,23 +6,23 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const toAnonymousFunctionAction = { name: "Convert to anonymous function", description: getLocaleSpecificMessage(Diagnostics.Convert_to_anonymous_function), - refactorKind: "refactor.rewrite.function.anonymous", + kind: "refactor.rewrite.function.anonymous", }; const toNamedFunctionAction = { name: "Convert to named function", description: getLocaleSpecificMessage(Diagnostics.Convert_to_named_function), - refactorKind: "refactor.rewrite.function.named", + kind: "refactor.rewrite.function.named", }; const toArrowFunctionAction = { name: "Convert to arrow function", description: getLocaleSpecificMessage(Diagnostics.Convert_to_arrow_function), - refactorKind: "refactor.rewrite.function.arrow", + kind: "refactor.rewrite.function.arrow", }; registerRefactor(refactorName, { - refactorKinds: [ - toAnonymousFunctionAction.refactorKind, - toNamedFunctionAction.refactorKind, - toArrowFunctionAction.refactorKind + kinds: [ + toAnonymousFunctionAction.kind, + toNamedFunctionAction.kind, + toArrowFunctionAction.kind ], getEditsForAction, getAvailableActions @@ -41,14 +41,14 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { } function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { - const { file, startPosition, program, refactorKind } = context; + const { file, startPosition, program, kind } = context; const info = getFunctionInfo(file, startPosition, program); if (!info) return emptyArray; const { selectedVariableDeclaration, func } = info; const possibleActions: RefactorActionInfo[] = []; const errors: RefactorActionInfo[] = []; - if (refactorKindBeginsWith(toNamedFunctionAction.refactorKind, refactorKind)) { + if (refactorKindBeginsWith(toNamedFunctionAction.kind, kind)) { const error = selectedVariableDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent)) ? undefined : getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_named_function); if (error) { @@ -59,7 +59,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { } } - if (refactorKindBeginsWith(toAnonymousFunctionAction.refactorKind, refactorKind)) { + if (refactorKindBeginsWith(toAnonymousFunctionAction.kind, kind)) { const error = !selectedVariableDeclaration && isArrowFunction(func) ? undefined: getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_anonymous_function); if (error) { @@ -70,7 +70,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { } } - if (refactorKindBeginsWith(toArrowFunctionAction.refactorKind, refactorKind)) { + if (refactorKindBeginsWith(toArrowFunctionAction.kind, kind)) { const error = isFunctionExpression(func) ? undefined : getLocaleSpecificMessage(Diagnostics.Could_not_convert_to_arrow_function); if (error) { errors.push({ ...toArrowFunctionAction, notApplicableReason: error }); diff --git a/src/services/refactors/convertExport.ts b/src/services/refactors/convertExport.ts index 32bf029fbe601..43096a4d08134 100644 --- a/src/services/refactors/convertExport.ts +++ b/src/services/refactors/convertExport.ts @@ -5,18 +5,18 @@ namespace ts.refactor { const defaultToNamedAction = { name: "Convert default export to named export", description: Diagnostics.Convert_default_export_to_named_export.message, - refactorKind: "refactor.rewrite.export.named" + kind: "refactor.rewrite.export.named" }; const namedToDefaultAction = { name: "Convert named export to default export", description: Diagnostics.Convert_named_export_to_default_export.message, - refactorKind: "refactor.rewrite.export.default" + kind: "refactor.rewrite.export.default" }; registerRefactor(refactorName, { - refactorKinds: [ - defaultToNamedAction.refactorKind, - namedToDefaultAction.refactorKind + kinds: [ + defaultToNamedAction.kind, + namedToDefaultAction.kind ], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getInfo(context, context.triggerReason === "invoked"); diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index 9c6e40b94ca2e..e84cb4ef668c7 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -5,18 +5,18 @@ namespace ts.refactor { const namespaceToNamedAction = { name: "Convert namespace import to named imports", description: Diagnostics.Convert_namespace_import_to_named_imports.message, - refactorKind: "refactor.rewrite.import.named", + kind: "refactor.rewrite.import.named", }; const namedToNamespaceAction = { name: "Convert named imports to namespace import", description: Diagnostics.Convert_named_imports_to_namespace_import.message, - refactorKind: "refactor.rewrite.import.namespace", + kind: "refactor.rewrite.import.namespace", }; registerRefactor(refactorName, { - refactorKinds: [ - namespaceToNamedAction.refactorKind, - namedToNamespaceAction.refactorKind + kinds: [ + namespaceToNamedAction.kind, + namedToNamespaceAction.kind ], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getImportToConvert(context, context.triggerReason === "invoked"); diff --git a/src/services/refactors/convertOverloadListToSingleSignature.ts b/src/services/refactors/convertOverloadListToSingleSignature.ts index c559605e917d5..cd0e4d9cd8ef4 100644 --- a/src/services/refactors/convertOverloadListToSingleSignature.ts +++ b/src/services/refactors/convertOverloadListToSingleSignature.ts @@ -6,10 +6,10 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { const functionOverloadAction = { name: refactorName, description: refactorDescription, - refactorKind: "refactor.rewrite.function.overloadList", + kind: "refactor.rewrite.function.overloadList", }; registerRefactor(refactorName, { - refactorKinds: [functionOverloadAction.refactorKind], + kinds: [functionOverloadAction.kind], getEditsForAction, getAvailableActions }); diff --git a/src/services/refactors/convertParamsToDestructuredObject.ts b/src/services/refactors/convertParamsToDestructuredObject.ts index 93378b5a872c8..feef5606b113c 100644 --- a/src/services/refactors/convertParamsToDestructuredObject.ts +++ b/src/services/refactors/convertParamsToDestructuredObject.ts @@ -7,10 +7,10 @@ namespace ts.refactor.convertParamsToDestructuredObject { const toDestructuredAction = { name: refactorName, description: refactorDescription, - refactorKind: "refactor.rewrite.parameters.toDestructured" + kind: "refactor.rewrite.parameters.toDestructured" }; registerRefactor(refactorName, { - refactorKinds: [toDestructuredAction.refactorKind], + kinds: [toDestructuredAction.kind], getEditsForAction, getAvailableActions }); diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index b475d125dccb2..4238f96e02c27 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -6,10 +6,10 @@ namespace ts.refactor.convertStringOrTemplateLiteral { const convertStringAction = { name: refactorName, description: refactorDescription, - refactorKind: "refactor.rewrite.string" + kind: "refactor.rewrite.string" }; registerRefactor(refactorName, { - refactorKinds: [convertStringAction.refactorKind], + kinds: [convertStringAction.kind], getEditsForAction, getAvailableActions }); diff --git a/src/services/refactors/convertToOptionalChainExpression.ts b/src/services/refactors/convertToOptionalChainExpression.ts index 0a64f1619e934..8bfb222c8bf0f 100644 --- a/src/services/refactors/convertToOptionalChainExpression.ts +++ b/src/services/refactors/convertToOptionalChainExpression.ts @@ -6,10 +6,10 @@ namespace ts.refactor.convertToOptionalChainExpression { const toOptionalChainAction = { name: refactorName, description: convertToOptionalChainExpressionMessage, - refactorKind: "refactor.rewrite.expression.optionalChain", + kind: "refactor.rewrite.expression.optionalChain", }; registerRefactor(refactorName, { - refactorKinds: [toOptionalChainAction.refactorKind], + kinds: [toOptionalChainAction.kind], getAvailableActions, getEditsForAction }); diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 2da74063a88f2..6478ad24d5ba5 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -5,17 +5,17 @@ namespace ts.refactor.extractSymbol { const extractConstantAction = { name: "Extract Constant", description: getLocaleSpecificMessage(Diagnostics.Extract_constant), - refactorKind: "refactor.extract.constant", + kind: "refactor.extract.constant", }; const extractFunctionAction = { name: "Extract Function", description: getLocaleSpecificMessage(Diagnostics.Extract_function), - refactorKind: "refactor.extract.function", + kind: "refactor.extract.function", }; registerRefactor(refactorName, { - refactorKinds: [ - extractConstantAction.refactorKind, - extractFunctionAction.refactorKind + kinds: [ + extractConstantAction.kind, + extractFunctionAction.kind ], getAvailableActions, getEditsForAction @@ -26,7 +26,7 @@ namespace ts.refactor.extractSymbol { * Exported for tests. */ export function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { - const requestedRefactor = context.refactorKind; + const requestedRefactor = context.kind; const rangeToExtract = getRangeToExtract(context.file, getRefactorContextSpan(context), context.triggerReason === "invoked"); const targetRange = rangeToExtract.targetRange; @@ -36,14 +36,14 @@ namespace ts.refactor.extractSymbol { } const errors = []; - if (refactorKindBeginsWith(extractFunctionAction.refactorKind, requestedRefactor)) { + if (refactorKindBeginsWith(extractFunctionAction.kind, requestedRefactor)) { errors.push({ name: refactorName, description: extractFunctionAction.description, actions: [{ ...extractFunctionAction, notApplicableReason: getStringError(rangeToExtract.errors) }] }); } - if (refactorKindBeginsWith(extractConstantAction.refactorKind, requestedRefactor)) { + if (refactorKindBeginsWith(extractConstantAction.kind, requestedRefactor)) { errors.push({ name: refactorName, description: extractConstantAction.description, @@ -70,7 +70,7 @@ namespace ts.refactor.extractSymbol { let i = 0; for (const { functionExtraction, constantExtraction } of extractions) { const description = functionExtraction.description; - if(refactorKindBeginsWith(extractFunctionAction.refactorKind, requestedRefactor)){ + if(refactorKindBeginsWith(extractFunctionAction.kind, requestedRefactor)){ if (functionExtraction.errors.length === 0) { // Don't issue refactorings with duplicated names. // Scopes come back in "innermost first" order, so extractions will @@ -80,7 +80,7 @@ namespace ts.refactor.extractSymbol { functionActions.push({ description, name: `function_scope_${i}`, - refactorKind: extractFunctionAction.refactorKind + kind: extractFunctionAction.kind }); } } @@ -89,13 +89,13 @@ namespace ts.refactor.extractSymbol { description, name: `function_scope_${i}`, notApplicableReason: getStringError(functionExtraction.errors), - refactorKind: extractFunctionAction.refactorKind + kind: extractFunctionAction.kind }; } } // Skip these since we don't have a way to report errors yet - if(refactorKindBeginsWith(extractConstantAction.refactorKind, requestedRefactor)) { + if(refactorKindBeginsWith(extractConstantAction.kind, requestedRefactor)) { if (constantExtraction.errors.length === 0) { // Don't issue refactorings with duplicated names. // Scopes come back in "innermost first" order, so extractions will @@ -106,7 +106,7 @@ namespace ts.refactor.extractSymbol { constantActions.push({ description, name: `constant_scope_${i}`, - refactorKind: extractConstantAction.refactorKind + kind: extractConstantAction.kind }); } } @@ -115,7 +115,7 @@ namespace ts.refactor.extractSymbol { description, name: `constant_scope_${i}`, notApplicableReason: getStringError(constantExtraction.errors), - refactorKind: extractConstantAction.refactorKind + kind: extractConstantAction.kind }; } } diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts index e7a72f53a7b55..ab85bb198cc62 100644 --- a/src/services/refactors/extractType.ts +++ b/src/services/refactors/extractType.ts @@ -5,24 +5,24 @@ namespace ts.refactor { const extractToTypeAliasAction = { name: "Extract to type alias", description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias), - refactorKind: "refactor.extract.type", + kind: "refactor.extract.type", }; const extractToInterfaceAction = { name: "Extract to interface", description: getLocaleSpecificMessage(Diagnostics.Extract_to_interface), - refactorKind: "refactor.extract.interface", + kind: "refactor.extract.interface", }; const extractToTypeDefAction = { name: "Extract to typedef", description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef), - refactorKind: "refactor.extract.typedef" + kind: "refactor.extract.typedef" }; registerRefactor(refactorName, { - refactorKinds: [ - extractToTypeAliasAction.refactorKind, - extractToInterfaceAction.refactorKind, - extractToTypeDefAction.refactorKind + kinds: [ + extractToTypeAliasAction.kind, + extractToInterfaceAction.kind, + extractToTypeDefAction.kind ], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const info = getRangeToExtract(context, context.triggerReason === "invoked"); diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index 4ec9cd192db83..76de5a7f4eaff 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -6,10 +6,10 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { const generateGetSetAction = { name: actionName, description: actionDescription, - refactorKind: "refactor.rewrite.property.generateAccessors", + kind: "refactor.rewrite.property.generateAccessors", }; registerRefactor(actionName, { - refactorKinds: [generateGetSetAction.refactorKind], + kinds: [generateGetSetAction.kind], getEditsForAction(context, actionName) { if (!context.endPosition) return undefined; const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition); diff --git a/src/services/refactors/helpers.ts b/src/services/refactors/helpers.ts index 4c8e3a7c84628..59d9c085f2fc9 100644 --- a/src/services/refactors/helpers.ts +++ b/src/services/refactors/helpers.ts @@ -16,7 +16,7 @@ namespace ts.refactor { /** * Checks if string "known" begins with string "requested". - * Used to match requested refactorKinds with a known refactorKind. + * Used to match requested kinds with a known kind. */ export function refactorKindBeginsWith(known: string, requested: string | undefined): boolean { if(!requested) return true; diff --git a/src/services/refactors/inferFunctionReturnType.ts b/src/services/refactors/inferFunctionReturnType.ts index dd07390cb304a..4fad099cad029 100644 --- a/src/services/refactors/inferFunctionReturnType.ts +++ b/src/services/refactors/inferFunctionReturnType.ts @@ -6,10 +6,10 @@ namespace ts.refactor.inferFunctionReturnType { const inferReturnTypeAction = { name: refactorName, description: refactorDescription, - refactorKind: "refactor.rewrite.function.returnType" + kind: "refactor.rewrite.function.returnType" }; registerRefactor(refactorName, { - refactorKinds: [inferReturnTypeAction.refactorKind], + kinds: [inferReturnTypeAction.kind], getEditsForAction, getAvailableActions }); @@ -56,7 +56,7 @@ namespace ts.refactor.inferFunctionReturnType { } function getInfo(context: RefactorContext): FunctionInfo | RefactorErrorInfo | undefined { - if (isInJSFile(context.file) || !refactorKindBeginsWith(inferReturnTypeAction.refactorKind, context.refactorKind)) return; + if (isInJSFile(context.file) || !refactorKindBeginsWith(inferReturnTypeAction.kind, context.kind)) return; const token = getTokenAtPosition(context.file, context.startPosition); const declaration = findAncestor(token, isConvertibleDeclaration); diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index 79433d8d07070..be93b513244e1 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -6,13 +6,13 @@ namespace ts.refactor { const moveToNewFileAction = { name: refactorName, description, - refactorKind: "refactor.move.newFile", + kind: "refactor.move.newFile", }; registerRefactor(refactorName, { - refactorKinds: [moveToNewFileAction.refactorKind], + kinds: [moveToNewFileAction.kind], getAvailableActions(context): readonly ApplicableRefactorInfo[] { const statements = getStatementsToMove(context); - if (statements && context.preferences.allowTextChangesInNewFiles) { + if (context.preferences.allowTextChangesInNewFiles && statements) { return [{ name: refactorName, description, actions: [moveToNewFileAction] }]; } if (context.preferences.provideRefactorNotApplicableReason) { diff --git a/src/services/services.ts b/src/services/services.ts index 5a3de870d8255..57845e193f3c9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2446,7 +2446,7 @@ namespace ts { return Rename.getRenameInfo(program, getValidSourceFile(fileName), position, options); } - function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings, triggerReason?: RefactorTriggerReason, refactorKind?: string): RefactorContext { + function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings, triggerReason?: RefactorTriggerReason, kind?: string): RefactorContext { const [startPosition, endPosition] = typeof positionOrRange === "number" ? [positionOrRange, undefined] : [positionOrRange.pos, positionOrRange.end]; return { file, @@ -2458,7 +2458,7 @@ namespace ts { cancellationToken, preferences, triggerReason, - refactorKind + kind }; } @@ -2466,10 +2466,10 @@ namespace ts { return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName)); } - function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, refactorKind: string): ApplicableRefactorInfo[] { + function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, kind: string): ApplicableRefactorInfo[] { synchronizeHostData(); const file = getValidSourceFile(fileName); - return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, refactorKind)); + return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, kind)); } function getEditsForRefactor( diff --git a/src/services/types.ts b/src/services/types.ts index c20d045c00f9f..8292897003cef 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -519,7 +519,7 @@ namespace ts { /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, refactorKind?: string): ApplicableRefactorInfo[]; + getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; @@ -795,7 +795,7 @@ namespace ts { /** * The hierarchical dotted name of the refactor action. */ - refactorKind?: string; + kind?: string; } /** @@ -1468,9 +1468,9 @@ namespace ts { /** @internal */ export interface Refactor { - /** List of action refactorKinds a refactor can provide. + /** List of action kinds a refactor can provide. * Used to skip unnecessary calculation when specific refactors are requested. */ - refactorKinds?: string[]; + kinds?: string[]; /** Compute the associated code actions */ getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined; @@ -1488,6 +1488,6 @@ namespace ts { cancellationToken?: CancellationToken; preferences: UserPreferences; triggerReason?: RefactorTriggerReason; - refactorKind?: string; + kind?: string; } } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 9b5521bd2ac26..da388d97217d8 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5566,7 +5566,7 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, refactorKind?: string): ApplicableRefactorInfo[]; + getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; @@ -5795,7 +5795,7 @@ declare namespace ts { /** * The hierarchical dotted name of the refactor action. */ - refactorKind?: string; + kind?: string; } /** * A set of edits to make in response to a refactor action, plus an optional @@ -6925,7 +6925,7 @@ declare namespace ts.server.protocol { } type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs & { triggerReason?: RefactorTriggerReason; - refactorKind?: string; + kind?: string; }; type RefactorTriggerReason = "implicit" | "invoked"; /** @@ -6981,7 +6981,7 @@ declare namespace ts.server.protocol { /** * The hierarchical dotted name of the refactor action. */ - refactorKind?: string; + kind?: string; } interface GetEditsForRefactorRequest extends Request { command: CommandTypes.GetEditsForRefactor; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3d95d01eb5838..91dc23d07dbc0 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5566,7 +5566,7 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, refactorKind?: string): ApplicableRefactorInfo[]; + getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; @@ -5795,7 +5795,7 @@ declare namespace ts { /** * The hierarchical dotted name of the refactor action. */ - refactorKind?: string; + kind?: string; } /** * A set of edits to make in response to a refactor action, plus an optional