Adopt code action ranges for refactorings (#57608)

This commit is contained in:
navya9singh 2024-03-18 14:34:44 -07:00 committed by GitHub
parent a46664ac64
commit c295531bfa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 846 additions and 52 deletions

View File

@ -2750,7 +2750,8 @@ export class Session<TMessage = string> implements EventSender {
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.kind, args.includeInteractiveActions);
const result = project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason, args.kind, args.includeInteractiveActions);
return result.map(result => ({ ...result, actions: result.actions.map(action => ({ ...action, range: action.range ? { start: convertToLocation({ line: action.range.start.line, character: action.range.start.offset }), end: convertToLocation({ line: action.range.end.line, character: action.range.end.offset }) } : undefined })) }));
}
private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo {

View File

@ -47,6 +47,7 @@ import {
getEffectiveTypeParameterDeclarations,
getEmitScriptTarget,
getEnclosingBlockScopeContainer,
getLineAndCharacterOfPosition,
getLocaleSpecificMessage,
getModifiers,
getNodeId,
@ -219,7 +220,7 @@ export function getRefactorActionsToExtractSymbol(context: RefactorContext): rea
return errors;
}
const extractions = getPossibleExtractions(targetRange, context);
const { affectedTextRange, extractions } = getPossibleExtractions(targetRange, context);
if (extractions === undefined) {
// No extractions possible
return emptyArray;
@ -247,6 +248,10 @@ export function getRefactorActionsToExtractSymbol(context: RefactorContext): rea
description,
name: `function_scope_${i}`,
kind: extractFunctionAction.kind,
range: {
start: { line: getLineAndCharacterOfPosition(context.file, affectedTextRange.pos).line, offset: getLineAndCharacterOfPosition(context.file, affectedTextRange.pos).character },
end: { line: getLineAndCharacterOfPosition(context.file, affectedTextRange.end).line, offset: getLineAndCharacterOfPosition(context.file, affectedTextRange.end).character },
},
});
}
}
@ -272,6 +277,10 @@ export function getRefactorActionsToExtractSymbol(context: RefactorContext): rea
description,
name: `constant_scope_${i}`,
kind: extractConstantAction.kind,
range: {
start: { line: getLineAndCharacterOfPosition(context.file, affectedTextRange.pos).line, offset: getLineAndCharacterOfPosition(context.file, affectedTextRange.pos).character },
end: { line: getLineAndCharacterOfPosition(context.file, affectedTextRange.end).line, offset: getLineAndCharacterOfPosition(context.file, affectedTextRange.end).character },
},
});
}
}
@ -909,8 +918,8 @@ interface ScopeExtractions {
* Each returned ExtractResultForScope corresponds to a possible target scope and is either a set of changes
* or an error explaining why we can't extract into that scope.
*/
function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): readonly ScopeExtractions[] | undefined {
const { scopes, readsAndWrites: { functionErrorsPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context);
function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): { readonly affectedTextRange: TextRange; readonly extractions: ScopeExtractions[] | undefined; } {
const { scopes, affectedTextRange, readsAndWrites: { functionErrorsPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context);
// Need the inner type annotation to avoid https://github.com/Microsoft/TypeScript/issues/7547
const extractions = scopes.map((scope, i): ScopeExtractions => {
const functionDescriptionPart = getDescriptionForFunctionInScope(scope);
@ -953,10 +962,10 @@ function getPossibleExtractions(targetRange: TargetRange, context: RefactorConte
},
};
});
return extractions;
return { affectedTextRange, extractions };
}
function getPossibleExtractionsWorker(targetRange: TargetRange, context: RefactorContext): { readonly scopes: Scope[]; readonly readsAndWrites: ReadsAndWrites; } {
function getPossibleExtractionsWorker(targetRange: TargetRange, context: RefactorContext): { readonly scopes: Scope[]; readonly affectedTextRange: TextRange; readonly readsAndWrites: ReadsAndWrites; } {
const { file: sourceFile } = context;
const scopes = collectEnclosingScopes(targetRange);
@ -969,7 +978,7 @@ function getPossibleExtractionsWorker(targetRange: TargetRange, context: Refacto
context.program.getTypeChecker(),
context.cancellationToken!,
);
return { scopes, readsAndWrites };
return { scopes, affectedTextRange: enclosingTextRange, readsAndWrites };
}
function getDescriptionForFunctionInScope(scope: Scope): string {

View File

@ -99,16 +99,27 @@ registerRefactor(refactorName, {
extractToTypeDefAction.kind,
],
getAvailableActions: function getRefactorActionsToExtractType(context): readonly ApplicableRefactorInfo[] {
const info = getRangeToExtract(context, context.triggerReason === "invoked");
const { info, affectedTextRange } = getRangeToExtract(context, context.triggerReason === "invoked");
if (!info) return emptyArray;
if (!isRefactorErrorInfo(info)) {
return [{
const refactorInfo: ApplicableRefactorInfo[] = [{
name: refactorName,
description: getLocaleSpecificMessage(Diagnostics.Extract_type),
actions: info.isJS ?
[extractToTypeDefAction] : append([extractToTypeAliasAction], info.typeElements && extractToInterfaceAction),
}];
return refactorInfo.map(info => ({
...info,
actions: info.actions.map(action => ({
...action,
range: affectedTextRange ? {
start: { line: getLineAndCharacterOfPosition(context.file, affectedTextRange.pos).line, offset: getLineAndCharacterOfPosition(context.file, affectedTextRange.pos).character },
end: { line: getLineAndCharacterOfPosition(context.file, affectedTextRange.end).line, offset: getLineAndCharacterOfPosition(context.file, affectedTextRange.end).character },
}
: undefined,
})),
}));
}
if (context.preferences.provideRefactorNotApplicableReason) {
@ -127,7 +138,7 @@ registerRefactor(refactorName, {
},
getEditsForAction: function getRefactorEditsToExtractType(context, actionName): RefactorEditInfo {
const { file } = context;
const info = getRangeToExtract(context);
const { info } = getRangeToExtract(context);
Debug.assert(info && !isRefactorErrorInfo(info), "Expected to find a range to extract");
const name = getUniqueName("NewType", file);
@ -171,20 +182,20 @@ interface InterfaceInfo {
type ExtractInfo = TypeAliasInfo | InterfaceInfo;
function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): ExtractInfo | RefactorErrorInfo | undefined {
function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): { info: ExtractInfo | RefactorErrorInfo | undefined; affectedTextRange?: TextRange; } {
const { file, startPosition } = context;
const isJS = isSourceFileJS(file);
const range = createTextRangeFromSpan(getRefactorContextSpan(context));
const isCursorRequest = range.pos === range.end && considerEmptySpans;
const firstType = getFirstTypeAt(file, startPosition, range, isCursorRequest);
if (!firstType || !isTypeNode(firstType)) return { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) };
if (!firstType || !isTypeNode(firstType)) return { info: { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) }, affectedTextRange: undefined };
const checker = context.program.getTypeChecker();
const enclosingNode = getEnclosingNode(firstType, isJS);
if (enclosingNode === undefined) return { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) };
if (enclosingNode === undefined) return { info: { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) }, affectedTextRange: undefined };
const expandedFirstType = getExpandedSelectionNode(firstType, enclosingNode);
if (!isTypeNode(expandedFirstType)) return { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) };
if (!isTypeNode(expandedFirstType)) return { info: { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) }, affectedTextRange: undefined };
const typeList: TypeNode[] = [];
if ((isUnionTypeNode(expandedFirstType.parent) || isIntersectionTypeNode(expandedFirstType.parent)) && range.end > firstType.end) {
@ -198,11 +209,11 @@ function getRangeToExtract(context: RefactorContext, considerEmptySpans = true):
}
const selection = typeList.length > 1 ? typeList : expandedFirstType;
const typeParameters = collectTypeParameters(checker, selection, enclosingNode, file);
if (!typeParameters) return { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) };
const { typeParameters, affectedTextRange } = collectTypeParameters(checker, selection, enclosingNode, file);
if (!typeParameters) return { info: { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) }, affectedTextRange: undefined };
const typeElements = flattenTypeLiteralNodeReference(checker, selection);
return { isJS, selection, enclosingNode, typeParameters, typeElements };
return { info: { isJS, selection, enclosingNode, typeParameters, typeElements }, affectedTextRange };
}
function getFirstTypeAt(file: SourceFile, startPosition: number, range: TextRange, isCursorRequest: boolean): Node | undefined {
@ -260,14 +271,14 @@ function rangeContainsSkipTrivia(r1: TextRange, node: TextRange, file: SourceFil
return rangeContainsStartEnd(r1, skipTrivia(file.text, node.pos), node.end);
}
function collectTypeParameters(checker: TypeChecker, selection: TypeNode | TypeNode[], enclosingNode: Node, file: SourceFile): TypeParameterDeclaration[] | undefined {
function collectTypeParameters(checker: TypeChecker, selection: TypeNode | TypeNode[], enclosingNode: Node, file: SourceFile): { typeParameters: TypeParameterDeclaration[] | undefined; affectedTextRange: TextRange | undefined; } {
const result: TypeParameterDeclaration[] = [];
const selectionArray = toArray(selection);
const selectionRange = { pos: selectionArray[0].pos, end: selectionArray[selectionArray.length - 1].end };
const selectionRange = { pos: selectionArray[0].getStart(file), end: selectionArray[selectionArray.length - 1].end };
for (const t of selectionArray) {
if (visitor(t)) return undefined;
if (visitor(t)) return { typeParameters: undefined, affectedTextRange: undefined };
}
return result;
return { typeParameters: result, affectedTextRange: selectionRange };
function visitor(node: Node): true | undefined {
if (isTypeReferenceNode(node)) {

View File

@ -50,6 +50,7 @@ import {
GetCanonicalFileName,
getDecorators,
getDirectoryPath,
getLineAndCharacterOfPosition,
getLocaleSpecificMessage,
getModifiers,
getPropertySymbolFromBindingElement,
@ -164,6 +165,7 @@ const moveToFileAction = {
registerRefactor(refactorNameForMoveToFile, {
kinds: [moveToFileAction.kind],
getAvailableActions: function getRefactorActionsToMoveToFile(context, interactiveRefactorArguments): readonly ApplicableRefactorInfo[] {
const file = context.file;
const statements = getStatementsToMove(context);
if (!interactiveRefactorArguments) {
return emptyArray;
@ -171,7 +173,6 @@ registerRefactor(refactorNameForMoveToFile, {
/** If the start/end nodes of the selection are inside a block like node do not show the `Move to file` code action
* This condition is used in order to show less often the `Move to file` code action */
if (context.endPosition !== undefined) {
const file = context.file;
const startNodeAncestor = findAncestor(getTokenAtPosition(file, context.startPosition), isBlockLike);
const endNodeAncestor = findAncestor(getTokenAtPosition(file, context.endPosition), isBlockLike);
if (startNodeAncestor && !isSourceFile(startNodeAncestor) && endNodeAncestor && !isSourceFile(endNodeAncestor)) {
@ -179,7 +180,11 @@ registerRefactor(refactorNameForMoveToFile, {
}
}
if (context.preferences.allowTextChangesInNewFiles && statements) {
return [{ name: refactorNameForMoveToFile, description, actions: [moveToFileAction] }];
const affectedTextRange = {
start: { line: getLineAndCharacterOfPosition(file, statements.all[0].getStart(file)).line, offset: getLineAndCharacterOfPosition(file, statements.all[0].getStart(file)).character },
end: { line: getLineAndCharacterOfPosition(file, last(statements.all).end).line, offset: getLineAndCharacterOfPosition(file, last(statements.all).end).character },
};
return [{ name: refactorNameForMoveToFile, description, actions: [{ ...moveToFileAction, range: affectedTextRange }] }];
}
if (context.preferences.provideRefactorNotApplicableReason) {
return [{ name: refactorNameForMoveToFile, description, actions: [{ ...moveToFileAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_statement_or_statements) }] }];

View File

@ -6,6 +6,7 @@ import {
emptyArray,
fileShouldUseJavaScriptRequire,
getBaseFileName,
getLineAndCharacterOfPosition,
getLocaleSpecificMessage,
getQuotePreference,
hasSyntacticModifier,
@ -14,6 +15,7 @@ import {
insertImports,
isPrologueDirective,
LanguageServiceHost,
last,
ModifierFlags,
nodeSeenTracker,
Program,
@ -64,7 +66,12 @@ registerRefactor(refactorName, {
getAvailableActions: function getRefactorActionsToMoveToNewFile(context): readonly ApplicableRefactorInfo[] {
const statements = getStatementsToMove(context);
if (context.preferences.allowTextChangesInNewFiles && statements) {
return [{ name: refactorName, description, actions: [moveToNewFileAction] }];
const file = context.file;
const affectedTextRange = {
start: { line: getLineAndCharacterOfPosition(file, statements.all[0].getStart(file)).line, offset: getLineAndCharacterOfPosition(file, statements.all[0].getStart(file)).character },
end: { line: getLineAndCharacterOfPosition(file, last(statements.all).end).line, offset: getLineAndCharacterOfPosition(file, last(statements.all).end).character },
};
return [{ name: refactorName, description, actions: [{ ...moveToNewFileAction, range: affectedTextRange }] }];
}
if (context.preferences.provideRefactorNotApplicableReason) {
return [{ name: refactorName, description, actions: [{ ...moveToNewFileAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_statement_or_statements) }] }];

View File

@ -1008,6 +1008,11 @@ export interface RefactorActionInfo {
* when calling `getEditsForRefactor`.
*/
isInteractive?: boolean;
/**
* Range of code the refactoring will be applied to.
*/
range?: { start: { line: number; offset: number; }; end: { line: number; offset: number; }; };
}
/**

View File

@ -21,4 +21,69 @@ describe("unittests:: tsserver:: getApplicableRefactors", () => {
});
baselineTsserverLogs("getApplicableRefactors", "works when taking position", session);
});
it("returns the affected range of text for extract symbol refactor", () => {
const file1: File = {
path: "/a.ts",
content: `class Foo {
someMethod(m: number) {
var x = m;
x = x * 3;
var y = 30;
var j = 10;
var z = y + j;
console.log(z);
var q = 10;
return q;
}
}`,
};
const host = createServerHost([file1]);
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetApplicableRefactorsRequest>({
command: ts.server.protocol.CommandTypes.GetApplicableRefactors,
arguments: { file: file1.path, startLine: 3, startOffset: 9, endLine: 5, endOffset: 20 },
});
baselineTsserverLogs("getApplicableRefactors", "returns the affected range of text for extract symbol refactor", session);
});
it("returns the affected range of text for extract type refactor", () => {
const file1: File = {
path: "/a.ts",
content: `type A<B, C, D = B> = Partial<C | string | D> & D | C;`,
};
const host = createServerHost([file1]);
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetApplicableRefactorsRequest>({
command: ts.server.protocol.CommandTypes.GetApplicableRefactors,
arguments: { file: file1.path, startLine: 1, startOffset: 26, endLine: 1, endOffset: 38 },
});
baselineTsserverLogs("getApplicableRefactors", "returns the affected range of text for extract type refactor", session);
});
it("returns the affected range of text for 'move to file' and 'move to new file' refactors", () => {
const file1: File = {
path: "/a.ts",
content: `const a = 1;
const b = 1;
function foo() { }`,
};
const host = createServerHost([file1]);
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: {
preferences: { allowTextChangesInNewFiles: true },
},
});
session.executeCommandSeq<ts.server.protocol.GetApplicableRefactorsRequest>({
command: ts.server.protocol.CommandTypes.GetApplicableRefactors,
arguments: { file: file1.path, startLine: 1, startOffset: 3, endLine: 2, endOffset: 3, includeInteractiveActions: true },
});
baselineTsserverLogs("getApplicableRefactors", "returns the affected range of text for 'move to file' and 'move to new file' refactors", session);
});
});

View File

@ -10352,6 +10352,19 @@ declare namespace ts {
* when calling `getEditsForRefactor`.
*/
isInteractive?: boolean;
/**
* Range of code the refactoring will be applied to.
*/
range?: {
start: {
line: number;
offset: number;
};
end: {
line: number;
offset: number;
};
};
}
/**
* A set of edits to make in response to a refactor action, plus an optional

View File

@ -156,12 +156,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to inner function in function 'foo'",
"name": "function_scope_0",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to function in global scope",
"name": "function_scope_1",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
},
@ -172,12 +192,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to constant in enclosing scope",
"name": "constant_scope_0",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to constant in global scope",
"name": "constant_scope_1",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
}
@ -246,12 +286,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to inner function in function 'foo'",
"name": "function_scope_0",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to function in global scope",
"name": "function_scope_1",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
},
@ -262,12 +322,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to constant in enclosing scope",
"name": "constant_scope_0",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to constant in global scope",
"name": "constant_scope_1",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
}
@ -336,12 +416,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to inner function in function 'foo'",
"name": "function_scope_0",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to function in global scope",
"name": "function_scope_1",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
},
@ -352,12 +452,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to constant in enclosing scope",
"name": "constant_scope_0",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to constant in global scope",
"name": "constant_scope_1",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
}
@ -426,12 +546,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to inner function in function 'foo'",
"name": "function_scope_0",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to function in global scope",
"name": "function_scope_1",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
},
@ -442,12 +582,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to constant in enclosing scope",
"name": "constant_scope_0",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to constant in global scope",
"name": "constant_scope_1",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
}
@ -516,12 +676,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to inner function in function 'foo'",
"name": "function_scope_0",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to function in global scope",
"name": "function_scope_1",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
},
@ -532,12 +712,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to constant in enclosing scope",
"name": "constant_scope_0",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to constant in global scope",
"name": "constant_scope_1",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
}
@ -606,12 +806,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to inner function in function 'foo'",
"name": "function_scope_0",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to function in global scope",
"name": "function_scope_1",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
},
@ -622,12 +842,32 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to constant in enclosing scope",
"name": "constant_scope_0",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
},
{
"description": "Extract to constant in global scope",
"name": "constant_scope_1",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 24
},
"end": {
"line": 1,
"offset": 41
}
}
}
]
}

View File

@ -248,7 +248,17 @@ Info seq [hh:mm:ss:mss] response:
{
"name": "Move to a new file",
"description": "Move to a new file",
"kind": "refactor.move.newFile"
"kind": "refactor.move.newFile",
"range": {
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 1,
"offset": 20
}
}
}
]
},
@ -259,7 +269,17 @@ Info seq [hh:mm:ss:mss] response:
{
"name": "Move to file",
"description": "Move to file",
"kind": "refactor.move.file"
"kind": "refactor.move.file",
"range": {
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 1,
"offset": 20
}
}
}
]
},
@ -270,7 +290,17 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to function in module scope",
"name": "function_scope_0",
"kind": "refactor.extract.function"
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 1,
"offset": 17
},
"end": {
"line": 1,
"offset": 19
}
}
}
]
},
@ -281,7 +311,17 @@ Info seq [hh:mm:ss:mss] response:
{
"description": "Extract to constant in enclosing scope",
"name": "constant_scope_0",
"kind": "refactor.extract.constant"
"kind": "refactor.extract.constant",
"range": {
"start": {
"line": 1,
"offset": 17
},
"end": {
"line": 1,
"offset": 19
}
}
}
]
}

View File

@ -0,0 +1,155 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist
Before request
//// [/a.ts]
const a = 1;
const b = 1;
function foo() { }
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/a.ts"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /
Info seq [hh:mm:ss:mss] For info: /a.ts :: No config files found.
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
/a.ts SVC-1-0 "const a = 1;\nconst b = 1;\nfunction foo() { }"
a.ts
Root file specified for compilation
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
Projects::
/dev/null/inferredProject1* (Inferred) *new*
projectStateVersion: 1
projectProgramVersion: 1
ScriptInfos::
/a.ts (Open) *new*
version: SVC-1-0
containingProjects: 1
/dev/null/inferredProject1* *default*
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "configure",
"arguments": {
"preferences": {
"allowTextChangesInNewFiles": true
}
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"seq": 0,
"type": "response",
"command": "configure",
"request_seq": 2,
"success": true,
"performanceData": {
"updateGraphDurationMs": *
}
}
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "getApplicableRefactors",
"arguments": {
"file": "/a.ts",
"startLine": 1,
"startOffset": 3,
"endLine": 2,
"endOffset": 3,
"includeInteractiveActions": true
},
"seq": 3,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"response": [
{
"name": "Move to a new file",
"description": "Move to a new file",
"actions": [
{
"name": "Move to a new file",
"description": "Move to a new file",
"kind": "refactor.move.newFile",
"range": {
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 2,
"offset": 13
}
}
}
]
},
{
"name": "Move to file",
"description": "Move to file",
"actions": [
{
"name": "Move to file",
"description": "Move to file",
"kind": "refactor.move.file",
"range": {
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 2,
"offset": 13
}
}
}
]
}
],
"responseRequired": true
}
After request

View File

@ -0,0 +1,142 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist
Before request
//// [/a.ts]
class Foo {
someMethod(m: number) {
var x = m;
x = x * 3;
var y = 30;
var j = 10;
var z = y + j;
console.log(z);
var q = 10;
return q;
}
}
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/a.ts"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /
Info seq [hh:mm:ss:mss] For info: /a.ts :: No config files found.
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
/a.ts SVC-1-0 "class Foo {\n someMethod(m: number) {\n var x = m;\n x = x * 3;\n var y = 30;\n var j = 10;\n var z = y + j;\n console.log(z);\n var q = 10;\n return q;\n }\n}"
a.ts
Root file specified for compilation
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
Projects::
/dev/null/inferredProject1* (Inferred) *new*
projectStateVersion: 1
projectProgramVersion: 1
ScriptInfos::
/a.ts (Open) *new*
version: SVC-1-0
containingProjects: 1
/dev/null/inferredProject1* *default*
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "getApplicableRefactors",
"arguments": {
"file": "/a.ts",
"startLine": 3,
"startOffset": 9,
"endLine": 5,
"endOffset": 20
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"response": [
{
"name": "Extract Symbol",
"description": "Extract function",
"actions": [
{
"description": "Extract to inner function in method 'someMethod'",
"name": "function_scope_0",
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 3,
"offset": 9
},
"end": {
"line": 5,
"offset": 20
}
}
},
{
"description": "Extract to method in class 'Foo'",
"name": "function_scope_1",
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 3,
"offset": 9
},
"end": {
"line": 5,
"offset": 20
}
}
},
{
"description": "Extract to function in global scope",
"name": "function_scope_2",
"kind": "refactor.extract.function",
"range": {
"start": {
"line": 3,
"offset": 9
},
"end": {
"line": 5,
"offset": 20
}
}
}
]
}
],
"responseRequired": true
}
After request

View File

@ -0,0 +1,101 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist
Before request
//// [/a.ts]
type A<B, C, D = B> = Partial<C | string | D> & D | C;
Info seq [hh:mm:ss:mss] request:
{
"command": "open",
"arguments": {
"file": "/a.ts"
},
"seq": 1,
"type": "request"
}
Info seq [hh:mm:ss:mss] Search path: /
Info seq [hh:mm:ss:mss] For info: /a.ts :: No config files found.
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
/a.ts SVC-1-0 "type A<B, C, D = B> = Partial<C | string | D> & D | C;"
a.ts
Root file specified for compilation
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred)
Info seq [hh:mm:ss:mss] Files (1)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1*
Info seq [hh:mm:ss:mss] response:
{
"responseRequired": false
}
After request
PolledWatches::
/a/lib/lib.d.ts: *new*
{"pollingInterval":500}
Projects::
/dev/null/inferredProject1* (Inferred) *new*
projectStateVersion: 1
projectProgramVersion: 1
ScriptInfos::
/a.ts (Open) *new*
version: SVC-1-0
containingProjects: 1
/dev/null/inferredProject1* *default*
Before request
Info seq [hh:mm:ss:mss] request:
{
"command": "getApplicableRefactors",
"arguments": {
"file": "/a.ts",
"startLine": 1,
"startOffset": 26,
"endLine": 1,
"endOffset": 38
},
"seq": 2,
"type": "request"
}
Info seq [hh:mm:ss:mss] response:
{
"response": [
{
"name": "Extract type",
"description": "Extract type",
"actions": [
{
"name": "Extract to type alias",
"description": "Extract to type alias",
"kind": "refactor.extract.type",
"range": {
"start": {
"line": 1,
"offset": 23
},
"end": {
"line": 1,
"offset": 46
}
}
}
]
}
],
"responseRequired": true
}
After request