Skip to content

extractMethod: Support renameLocation #18050

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
11 commits merged into from
Sep 13, 2017
Merged

extractMethod: Support renameLocation #18050

11 commits merged into from
Sep 13, 2017

Conversation

ghost
Copy link

@ghost ghost commented Aug 25, 2017

Fixes #17852
Does not fix #18048
EDIT: Now fixes both

@ghost ghost requested a review from RyanCavanaugh August 25, 2017 21:01
@ghost ghost requested a review from amcasey August 25, 2017 21:01
const { scopes, readsAndWrites: { errorsPerScope } } = getPossibleExtractionsWorker(targetRange, context);
// Need the inner type annotation to avoid https://github.com/Microsoft/TypeScript/issues/7547
return scopes.map((scope, i): PossibleExtraction => {
const errors = errorsPerScope[i];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be inlined

@ghost
Copy link
Author

ghost commented Sep 8, 2017

@mhegazy @amcasey Could you review again?

Debug.assert(fileName === renameFilename);
for (const change of textChanges) {
const { span, newText } = change;
const index = newText.indexOf(functionNameText);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do not know if the name is really unique text wise. we only look if it was declared as an identifier, but not in a comment or string literal for instance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also relies on the use site being inserted in the file before the definition. we should add a comment to that meaning in case we change where the function declaration is added.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would definitely be preferable not to assume that the new declaration is inserted after the extracted range because that will almost certainly not be the case when we start extracting locals.

}
}
else {
// TODO: test editInfo.renameFilename too
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just assert that it's non-empty for now?

@@ -691,9 +691,12 @@ function test(x: number) {
data.push(`// ==ORIGINAL==`);
data.push(sourceFile.text);
for (const r of results) {
const changes = refactor.extractMethod.getPossibleExtractions(result.targetRange, context, results.indexOf(r))[0].changes;
const { renameLocation, edits } = refactor.extractMethod.getPossibleExtractionAtIndex(result.targetRange, context, results.indexOf(r));
Debug.assert(edits.length === 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use assert.equal?

@mhegazy
Copy link
Contributor

mhegazy commented Sep 8, 2017

Please port this to release-2.5

@@ -494,12 +481,32 @@ namespace ts.refactor.extractMethod {
return scopes;
}

// exported only for tests
export function getPossibleExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just "getExtractionAtIndex"?

@@ -582,34 +568,33 @@ namespace ts.refactor.extractMethod {
}
}

function getUniqueName(isNameOkay: (name: string) => boolean) {
function getUniqueName(fileText: string): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, somewhat ironically, if you were to add a comment //TODO newFunction_1 goes here, we would call the new function "newFunction_2"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be easier to use something temporary, like a GUID, until the replacement has been accomplished and then swap it for the name we would originally have generated?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That might be doable, but we might (might not) need the GUID to have an identical length. But you could still calculate them together inside getUniqueName.

isClassLike(scope) ? createPropertyAccess(range.facts & RangeFacts.InStaticRegion ? createIdentifier(scope.name.getText()) : createThis(), functionReference) : functionReference,
const called = getCalledExpression(scope, range, functionNameText);

let call: Expression = createCall(called,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing linebreak before argument?

Debug.assert(fileName === renameFilename);
for (const change of textChanges) {
const { span, newText } = change;
// Note: We are assuming that the call expression comes before the function declaration,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add TODO (acasey) (or TODO (17098))? This assumption will be incorrect as soon as we implement Extract Local.

usages: Map<UsageEntry>;
typeParameterUsages: Map<TypeParameter>; // Key is type ID
substitutions: Map<Node>;
readonly usages: Map<UsageEntry>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when a property of an exported type depends on a non-exported type?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Users can't reference the type itself, but they can get a variable of the type. Pretty annoying. But it looks like this doesn't actually need to be exported.

var i = 10;
var __return: any;
({ __return, i } = newFunction(i));
({ __return, i } = n/*RENAME*/ewFunction(i));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the location within the identifier?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added #18349

Copy link
Member

@amcasey amcasey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our method of coming up with a unique name seems slightly crazy to me (esp for large files), but this generally makes sense.

@ghost ghost mentioned this pull request Sep 8, 2017
@ghost ghost merged commit c3199c7 into master Sep 13, 2017
@ghost ghost deleted the extract_rename branch September 13, 2017 16:02
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide rename location for extractMethod in presence of writes API to tell editor to trigger rename after applying refactoring
3 participants