Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/harness/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
ApplicableInteractiveRefactorInfo,
ApplicableNonInteractiveRefactorInfo,
ApplicableRefactorInfo,
CallHierarchyIncomingCall,
CallHierarchyItem,
Expand Down Expand Up @@ -52,6 +54,7 @@ import {
Program,
QuickInfo,
RefactorEditInfo,
RefactorTriggerReason,
ReferencedSymbol,
ReferenceEntry,
RenameInfo,
Expand Down Expand Up @@ -785,10 +788,13 @@ export class SessionClient implements LanguageService {
return { file, line, offset, endLine, endOffset };
}

getApplicableRefactors(fileName: string, positionOrRange: number | TextRange): ApplicableRefactorInfo[] {
getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string): ApplicableNonInteractiveRefactorInfo[];
getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason: RefactorTriggerReason | undefined, kind: string | undefined, includeInteractive: true): ApplicableInteractiveRefactorInfo[];
getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences | undefined, triggerReason?: RefactorTriggerReason, kind?: string, includeInteractive?: boolean): ApplicableRefactorInfo[];
getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, _preferences: UserPreferences | undefined, _triggerReason?: RefactorTriggerReason, _kind?: string, includeInteractive?: boolean): ApplicableRefactorInfo[] {
const args = this.createFileLocationOrRangeRequestArgs(positionOrRange, fileName);

const request = this.processRequest<protocol.GetApplicableRefactorsRequest>(protocol.CommandTypes.GetApplicableRefactors, args);
const request = this.processRequest<protocol.GetApplicableRefactorsRequest>(protocol.CommandTypes.GetApplicableRefactors, { ...args, includeInteractive });
const response = this.processResponse<protocol.GetApplicableRefactorsResponse>(request);
return response.body!; // TODO: GH#18217
}
Expand Down
3 changes: 3 additions & 0 deletions src/harness/harnessLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,9 @@ class LanguageServiceShimProxy implements ts.LanguageService {
getEditsForRefactor(): ts.RefactorEditInfo {
throw new Error("Not supported on the shim.");
}
getApplicableRefactors(): ts.ApplicableNonInteractiveRefactorInfo[];
getApplicableRefactors(): ts.ApplicableInteractiveRefactorInfo[];
getApplicableRefactors(): ts.ApplicableRefactorInfo[];
getApplicableRefactors(): ts.ApplicableRefactorInfo[] {
throw new Error("Not supported on the shim.");
}
Expand Down
23 changes: 19 additions & 4 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import type {
EndOfLineState,
FileExtensionInfo,
HighlightSpanKind,
InteractiveRefactorName,
MapLike,
NonInteractiveRefactorName,
OutliningSpanKind,
OutputFile,
PluginImport,
ProjectReference,
RefactorName,
RenameLocation,
ScriptElementKind,
ScriptKind,
Expand Down Expand Up @@ -586,6 +589,7 @@ export interface GetApplicableRefactorsRequest extends Request {
export type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs & {
triggerReason?: RefactorTriggerReason;
kind?: string;
includeInteractive?: boolean;
};

export type RefactorTriggerReason = "implicit" | "invoked";
Expand All @@ -594,18 +598,18 @@ export type RefactorTriggerReason = "implicit" | "invoked";
* Response is a list of available refactorings.
* Each refactoring exposes one or more "Actions"; a user selects one action to invoke a refactoring
*/
export interface GetApplicableRefactorsResponse extends Response {
body?: ApplicableRefactorInfo[];
export interface GetApplicableRefactorsResponse<TRefactorInfo extends ApplicableRefactorInfo = ApplicableRefactorInfo> extends Response {
body?: TRefactorInfo[];
}

/**
* A set of one or more available refactoring actions, grouped under a parent refactoring.
*/
export interface ApplicableRefactorInfo {
export interface BaseApplicableRefactorInfo {
/**
* The programmatic name of the refactoring
*/
name: string;
name: RefactorName;
/**
* A description of this refactoring category to show to the user.
* If the refactoring gets inlined (see below), this text will not be visible.
Expand All @@ -623,6 +627,17 @@ export interface ApplicableRefactorInfo {
actions: RefactorActionInfo[];
}

export interface ApplicableNonInteractiveRefactorInfo extends BaseApplicableRefactorInfo {
name: NonInteractiveRefactorName;
}

export interface ApplicableInteractiveRefactorInfo extends BaseApplicableRefactorInfo {
name: InteractiveRefactorName;
}

export type ApplicableRefactorInfo = ApplicableNonInteractiveRefactorInfo | ApplicableInteractiveRefactorInfo;


/**
* Represents a single refactoring action - for example, the "Extract Method..." refactor might
* offer several actions, each corresponding to a surround class or closure to extract into.
Expand Down
21 changes: 15 additions & 6 deletions src/services/refactorProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,44 @@ import {
ApplicableRefactorInfo,
arrayFrom,
flatMapIterator,
InteractiveRefactor,
InteractiveRefactorName,
NonInteractiveRefactor,
NonInteractiveRefactorName,
Refactor,
RefactorContext,
RefactorEditInfo,
RefactorName,
} from "./_namespaces/ts";
import { refactorKindBeginsWith } from "./_namespaces/ts.refactor";

// A map with the refactor code as key, the refactor itself as value
// e.g. nonSuggestableRefactors[refactorCode] -> the refactor you want
const refactors = new Map<string, Refactor>();
const refactors = new Map<RefactorName, Refactor>();

/**
* @param name An unique code associated with each refactor. Does not have to be human-readable.
*
* @internal
*/
export function registerRefactor(name: string, refactor: Refactor) {
export function registerRefactor<Name extends InteractiveRefactorName>(name: Name, refactor: InteractiveRefactor<Name>): void;
/** @internal */
export function registerRefactor(name: NonInteractiveRefactorName, refactor: NonInteractiveRefactor): void;
/** @internal */
export function registerRefactor(name: RefactorName, refactor: Refactor) {
refactors.set(name, refactor);
}

/** @internal */
export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] {
export function getApplicableRefactors(context: RefactorContext, includeInteractive?: boolean): ApplicableRefactorInfo[] {
return arrayFrom(flatMapIterator(refactors.values(), refactor =>
context.cancellationToken && context.cancellationToken.isCancellationRequested() ||
!refactor.kinds?.some(kind => refactorKindBeginsWith(kind, context.kind)) ? undefined :
(!refactor.kinds?.some(kind => refactorKindBeginsWith(kind, context.kind)) || refactor.isInteractive && !includeInteractive) ? undefined :
refactor.getAvailableActions(context)));
}

/** @internal */
export function getEditsForRefactor(context: RefactorContext, refactorName: string, actionName: string): RefactorEditInfo | undefined {
export function getEditsForRefactor(context: RefactorContext, refactorName: RefactorName, actionName: string, ...args: unknown[]): RefactorEditInfo | undefined {
const refactor = refactors.get(refactorName);
return refactor && refactor.getEditsForAction(context, actionName);
return refactor && refactor.getEditsForAction(context, actionName, ...args);
}
3 changes: 2 additions & 1 deletion src/services/refactors/addOrRemoveBracesToArrowFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
isExpression,
isReturnStatement,
needsParentheses,
NonInteractiveRefactorName,
rangeContainsRange,
RefactorContext,
RefactorEditInfo,
Expand All @@ -34,7 +35,7 @@ import {
registerRefactor,
} from "../_namespaces/ts.refactor";

const refactorName = "Add or remove braces in an arrow function";
const refactorName = NonInteractiveRefactorName.AddOrRemoveBracesToArrowFunction;
const refactorDescription = Diagnostics.Add_or_remove_braces_in_an_arrow_function.message;

const addBracesAction = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
length,
ModifierFlags,
Node,
NonInteractiveRefactorName,
Program,
rangeContainsRange,
RefactorActionInfo,
Expand All @@ -58,7 +59,7 @@ import {
registerRefactor,
} from "../_namespaces/ts.refactor";

const refactorName = "Convert arrow function or function expression";
const refactorName = NonInteractiveRefactorName.ConvertArrowFunctionOrFunctionExpression;
const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_arrow_function_or_function_expression);

const toAnonymousFunctionAction = {
Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/convertExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
NamespaceDeclaration,
Node,
NodeFlags,
NonInteractiveRefactorName,
Program,
PropertyAccessExpression,
QuotePreference,
Expand All @@ -57,7 +58,7 @@ import {
registerRefactor,
} from "../_namespaces/ts.refactor";

const refactorName = "Convert export";
const refactorName = NonInteractiveRefactorName.ConvertExport;

const defaultToNamedAction = {
name: "Convert default export to named export",
Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/convertImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
isStringLiteral,
NamedImports,
NamespaceImport,
NonInteractiveRefactorName,
Program,
PropertyAccessExpression,
QualifiedName,
Expand All @@ -50,7 +51,7 @@ import {
registerRefactor,
} from "../_namespaces/ts.refactor";

const refactorName = "Convert import";
const refactorName = NonInteractiveRefactorName.ConvertImport;

const actions = {
[ImportKind.Named]: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
NamedTupleMember,
Node,
NodeArray,
NonInteractiveRefactorName,
ParameterDeclaration,
Program,
rangeContainsPosition,
Expand All @@ -41,7 +42,7 @@ import {
} from "../_namespaces/ts";
import { registerRefactor } from "../_namespaces/ts.refactor";

const refactorName = "Convert overload list to single signature";
const refactorName = NonInteractiveRefactorName.ConvertOverloadListToSingleSignature;
const refactorDescription = Diagnostics.Convert_overload_list_to_single_signature.message;

const functionOverloadAction = {
Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/convertParamsToDestructuredObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import {
NewExpression,
Node,
NodeArray,
NonInteractiveRefactorName,
ObjectLiteralElementLike,
ObjectLiteralExpression,
ParameterDeclaration,
Expand All @@ -106,7 +107,7 @@ import {
} from "../_namespaces/ts";
import { registerRefactor } from "../_namespaces/ts.refactor";

const refactorName = "Convert parameters to destructured object";
const refactorName = NonInteractiveRefactorName.ConvertParamsToDestructuredObject;
const minimumParameterLength = 1;
const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_parameters_to_destructured_object);

Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/convertStringOrTemplateLiteral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
isTemplateMiddle,
map,
Node,
NonInteractiveRefactorName,
ParenthesizedExpression,
RefactorContext,
RefactorEditInfo,
Expand All @@ -38,7 +39,7 @@ import {
} from "../_namespaces/ts";
import { registerRefactor } from "../_namespaces/ts.refactor";

const refactorName = "Convert to template string";
const refactorName = NonInteractiveRefactorName.ConvertStringOrTemplateLiteral;
const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_template_string);

const convertStringAction = {
Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/convertToOptionalChainExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
isStringOrNumericLiteralLike,
isVariableStatement,
Node,
NonInteractiveRefactorName,
PropertyAccessExpression,
RefactorContext,
RefactorEditInfo,
Expand All @@ -47,7 +48,7 @@ import {
registerRefactor,
} from "../_namespaces/ts.refactor";

const refactorName = "Convert to optional chain expression";
const refactorName = NonInteractiveRefactorName.ConvertToOptionalChainExpression;
const convertToOptionalChainExpressionMessage = getLocaleSpecificMessage(Diagnostics.Convert_to_optional_chain_expression);

const toOptionalChainAction = {
Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/extractSymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ import {
Node,
NodeBuilderFlags,
NodeFlags,
NonInteractiveRefactorName,
nullTransformationContext,
ObjectLiteralElementLike,
ParameterDeclaration,
Expand Down Expand Up @@ -165,7 +166,7 @@ import {
registerRefactor,
} from "../_namespaces/ts.refactor";

const refactorName = "Extract Symbol";
const refactorName = NonInteractiveRefactorName.ExtractSymbol;

const extractConstantAction = {
name: "Extract Constant",
Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/extractType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
JSDocTemplateTag,
Node,
nodeOverlapsWithStartEnd,
NonInteractiveRefactorName,
pushIfUnique,
rangeContainsStartEnd,
RefactorContext,
Expand All @@ -70,7 +71,7 @@ import {
registerRefactor,
} from "../_namespaces/ts.refactor";

const refactorName = "Extract type";
const refactorName = NonInteractiveRefactorName.ExtractType;

const extractToTypeAliasAction = {
name: "Extract to type alias",
Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/generateGetAccessorAndSetAccessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import {
getRenameLocation,
isIdentifier,
isParameter,
NonInteractiveRefactorName,
RefactorContext,
} from "../_namespaces/ts";
import {
isRefactorErrorInfo,
registerRefactor,
} from "../_namespaces/ts.refactor";

const actionName = "Generate 'get' and 'set' accessors";
const actionName = NonInteractiveRefactorName.GenerateGetAccessorAndSetAccessor;
const actionDescription = Diagnostics.Generate_get_and_set_accessors.message;

const generateGetSetAction = {
Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/inferFunctionReturnType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
MethodDeclaration,
Node,
NodeBuilderFlags,
NonInteractiveRefactorName,
RefactorContext,
RefactorEditInfo,
SourceFile,
Expand All @@ -34,7 +35,7 @@ import {
registerRefactor,
} from "../_namespaces/ts.refactor";

const refactorName = "Infer function return type";
const refactorName = NonInteractiveRefactorName.InferFunctionReturnType;
const refactorDescription = Diagnostics.Infer_function_return_type.message;

const inferReturnTypeAction = {
Expand Down
3 changes: 2 additions & 1 deletion src/services/refactors/moveToNewFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ import {
Node,
NodeFlags,
nodeSeenTracker,
NonInteractiveRefactorName,
normalizePath,
ObjectBindingElementWithoutPropertyName,
Program,
Expand Down Expand Up @@ -131,7 +132,7 @@ import {
} from "../_namespaces/ts";
import { registerRefactor } from "../_namespaces/ts.refactor";

const refactorName = "Move to a new file";
const refactorName = NonInteractiveRefactorName.MoveToNewFile;
const description = getLocaleSpecificMessage(Diagnostics.Move_to_a_new_file);

const moveToNewFileAction = {
Expand Down
Loading