Skip to content
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

WIP: Add IDocumentRefactoringService #40173

Closed
wants to merge 19 commits into from

Conversation

ryzngard
Copy link
Contributor

@ryzngard ryzngard commented Dec 5, 2019

IDocumentRefactoringService is intended to allow refactoring behavior
provided by roslyn on documents directly. Currently it has one method,
UpdateAfterInfoChangeAsync, which is intended to update named types
to sync to the document name and location.

Also modify the public rename surface so it tags symbols that are
renamed. This is consumed by VisualStudioWorkspace.TryApplyChanges
to call IVsRefactorNotify for symbols that changed

To do:

  • IDocumentRefactoringService tests
  • VisualStudioWorkspace test for refactor notify
  • Namespace modification

@ryzngard ryzngard added Area-IDE PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. labels Dec 5, 2019
IDocumentRefactoringService is intended to allow refactoring behavior
provided by roslyn on documents directly. Currently it has one method,
UpdateAfterInfoChangeAsync, which is intended to update named types
to sync to the document name and location.

Also modify the public rename surface so it tags symbols that are
renamed. This is consumed by VisualStudioWorkspace.TryApplyChanges
to call IVsRefactorNotify for symbols that changed
@ryzngard ryzngard force-pushed the prototype/post_rename_api branch from be6691a to 0875e09 Compare December 6, 2019 22:21
@ryzngard ryzngard removed the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Dec 6, 2019
@ryzngard ryzngard marked this pull request as ready for review December 6, 2019 23:57
@ryzngard ryzngard requested a review from a team as a code owner December 6, 2019 23:57
Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

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

Blocking primarily because it looks like this will break TryApplyChanges if there's an additional file or analyzer config file being updated. But many other questions. I think the high level design of the annotation informing TryApplyChanges seems really solid though, this is just drilling into details.

End If
Next

Assert.True(False, "Did not find any rename annotated nodes")
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Assert.True(False, "Did not find any rename annotated nodes")
throw new Exception("Did not find any rename annotated nodes")

This results in less head-scratching when somebody sees an "Expected: true, Actual: false" line which is otherwise pointless.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, a little rant about this: Assertions throw exceptions from xunit. We have AssertEx.Fail which also will throw an exception. Both will by caught in the Try/catch surrounding this code, which makes sad pandas like me when I'm trying to investigate why a test failed and it's because in the dispose method of something later an assertion failed while in the middle of catching an exception thrown by this code.

TLDR: AssertEx.Fail is probably more correct here? Not really sure. Either way, the message will not be what surfaces when a test fails

{
[Export(typeof(DocumentRefactoringService)), PartNotDiscoverable]
[Export(typeof(IDocumentRefactoringService))]
[ExportWorkspaceService(typeof(IDocumentRefactoringService)), Shared]
Copy link
Member

Choose a reason for hiding this comment

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

Does this need to be a Workspace service or can it be just a straight-up MEF service? I ask only because @tmat is beginning to do some audits of our workspace services for what type they are, and I'm not sure if he has guidance yet on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Honestly I'm not sure what the difference will be in accessing it. Straight service should be fine? Would it still be workspace.GetService?

.DescendantNodes()

Assert.True(typeDeclarationPairs.Any())
Dim matchingTypeDeclarationPair = typeDeclarationPairs.Where(Function(p) String.Equals(p.Item2, Path.GetFileNameWithoutExtension(document.FilePath), StringComparison.OrdinalIgnoreCase))
Copy link
Member

Choose a reason for hiding this comment

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

Are we worried this test is just doing a copy/paste of the same code in the actual feature? Is it actually testing anything then?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it be better to just do a markup test of before/after? Might make sense

Comment on lines +11 to +12
/// Updates the contents of a document after some document info change, such as file name or path. May
/// prompt a user for confirmation on the changes.
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, this is a data API -- it really shouldn't be prompting. (I wish you luck when this is on a server...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If it needs use input, this seems like the best place for that information to be obtained. How does this directly vary from CodeActionWithOptions or similar?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ryzngard ryzngard added the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Dec 19, 2019
@ryzngard
Copy link
Contributor Author

Marking for personal review while I rip out the old rename code action

@ryzngard ryzngard added the Need Design Review The end user experience design needs to be reviewed and approved. label Jan 9, 2020
@ryzngard
Copy link
Contributor Author

ryzngard commented Jan 9, 2020

Marking for design review of public interface

@ryzngard
Copy link
Contributor Author

ryzngard commented Jan 9, 2020

Corresponds to dotnet/project-system#5436

var editor = Editor.GetEditor(operationKind, (TService)this, state, suggestedFileNames.FirstOrDefault(), cancellationToken);
var suggestedFileName = operationKind switch
{
MoveTypeOperationKind.RenameType => Path.GetFileNameWithoutExtension(document.Name),
Copy link
Member

Choose a reason for hiding this comment

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

slightly confsued . if we're renaming the type... the suggested file name is the name the document already is? can you doc what is going on here?

var suggestedFileName = operationKind switch
{
MoveTypeOperationKind.RenameType => Path.GetFileNameWithoutExtension(document.Name),
MoveTypeOperationKind.MoveTypeNamespaceScope => document.Name,
Copy link
Member

Choose a reason for hiding this comment

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

also don't understand this. if we're moving the type... why do we just get he doc name, and why aren't ee trimming the extension like above? this needs commenting.

{
MoveTypeOperationKind.RenameType => Path.GetFileNameWithoutExtension(document.Name),
MoveTypeOperationKind.MoveTypeNamespaceScope => document.Name,
_ => GetSuggestedFileNames(
Copy link
Member

Choose a reason for hiding this comment

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

can we be explicit about what case this is for, instead of _

syntaxFacts.StringComparer);

if (defaultNamespaceFromProjects.Count != 1
|| defaultNamespaceFromProjects.First() == null)
Copy link
Member

Choose a reason for hiding this comment

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

the null check seems unnecessary.

var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
root = addImportService.AddImports(compilation, root, contextLocation, imports, placeSystemNamespaceFirst, cancellationToken);
document = document.WithSyntaxRoot(root);
}
Copy link
Member

Choose a reason for hiding this comment

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

i skipped the rest of this file. note: you could consider a separate PR to make all the nullable/GetRequired changes to make this easier to review.

@@ -51,5 +53,10 @@ internal interface IChangeNamespaceService : ILanguageService
/// a no-op and original solution will be returned.
/// </remarks>
Task<Solution> ChangeNamespaceAsync(Document document, SyntaxNode container, string targetNamespace, CancellationToken cancellationToken);

/// <summary>
/// Gets the target namespace for a given document by using the path and any root namespace
Copy link
Member

Choose a reason for hiding this comment

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

  1. can you give an example?
  2. can you doc when you would get null back and waht that means?

return solution;
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

skipped this file for now. i don't understand the design here.

{
if (refactorNotifyService.Value.TryOnBeforeGlobalSymbolRenamed(this, changedDocumentIds, oldSymbol, newSymbol.ToDisplayString(), throwOnFailure: false))
{
refactorNotifyService.Value.TryOnAfterGlobalSymbolRenamed(this, changedDocumentIds, oldSymbol, newSymbol.ToDisplayString(), throwOnFailure: false);
Copy link
Member

Choose a reason for hiding this comment

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

  1. same comment as in the test version. this logic seems bizarre to me. what's the point of Before/After when we're not actually doing anything in between?

return;
}

var changedSymbols = RenameSymbolAnnotation.GatherChangedSymbolsInDocumentsAsync(changedDocumentIds, CurrentSolution, oldSolution, cancellationToken).WaitAndGetResult(cancellationToken);
Copy link
Member

Choose a reason for hiding this comment

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

WaitAndGetResult here seems really bad.

namespace Microsoft.VisualStudio.LanguageServices.Implementation
{
/// <summary>
/// Handles dismissing the rename tracker when a symbol has changed
Copy link
Member

Choose a reason for hiding this comment

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

... i feel like this needs a ton more information. why woudl we need this at all?

@CyrusNajmabadi
Copy link
Member

Definitely need more info here. ..

@ryzngard
Copy link
Contributor Author

@CyrusNajmabadi this is still marked for personal review

@CyrusNajmabadi
Copy link
Member

Oh. Sorry :) could you potentially use WIP in the titel? github email notifications don't include this info :(

@ryzngard ryzngard changed the title Add IDocumentRefactoringService WIP: Add IDocumentRefactoringService Jan 10, 2020
@ryzngard
Copy link
Contributor Author

I've split this work into two PRs:

#41126 for the rename = refactor notify work
#41267 for the new API design

@ryzngard ryzngard closed this Jan 29, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE Need Design Review The end user experience design needs to be reviewed and approved. PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it.
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants