-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Conversation
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
be6691a
to
0875e09
Compare
src/EditorFeatures/CSharpTest/DocumentRefactoringService/DocumentRefactoringServiceTests.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
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
src/VisualStudio/Core/Def/Implementation/DocumentRefactoring/DocumentRefactoringService.cs
Outdated
Show resolved
Hide resolved
src/VisualStudio/Core/Def/Implementation/DocumentRefactoring/DocumentRefactoringService.cs
Show resolved
Hide resolved
{ | ||
[Export(typeof(DocumentRefactoringService)), PartNotDiscoverable] | ||
[Export(typeof(IDocumentRefactoringService))] | ||
[ExportWorkspaceService(typeof(IDocumentRefactoringService)), Shared] |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
?
src/VisualStudio/Core/Def/Implementation/DocumentRefactoring/DocumentRefactoringService.cs
Outdated
Show resolved
Hide resolved
.DescendantNodes() | ||
|
||
Assert.True(typeDeclarationPairs.Any()) | ||
Dim matchingTypeDeclarationPair = typeDeclarationPairs.Where(Function(p) String.Equals(p.Item2, Path.GetFileNameWithoutExtension(document.FilePath), StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
src/VisualStudio/Core/Test/DocumentRefactoring/DocumentRefactoringServiceTests.vb
Outdated
Show resolved
Hide resolved
/// 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. |
There was a problem hiding this comment.
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...)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping @jasonmalinowski
Marking for personal review while I rip out the old rename code action |
Marking for design review of public interface |
Corresponds to dotnet/project-system#5436 |
src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/CodeFixes/NamingStyle/NamingStyleCodeFixProvider.cs
Outdated
Show resolved
Hide resolved
var editor = Editor.GetEditor(operationKind, (TService)this, state, suggestedFileNames.FirstOrDefault(), cancellationToken); | ||
var suggestedFileName = operationKind switch | ||
{ | ||
MoveTypeOperationKind.RenameType => Path.GetFileNameWithoutExtension(document.Name), |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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); | ||
} |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- can you give an example?
- can you doc when you would get null back and waht that means?
return solution; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 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?
src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs
Outdated
Show resolved
Hide resolved
return; | ||
} | ||
|
||
var changedSymbols = RenameSymbolAnnotation.GatherChangedSymbolsInDocumentsAsync(changedDocumentIds, CurrentSolution, oldSolution, cancellationToken).WaitAndGetResult(cancellationToken); |
There was a problem hiding this comment.
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.
src/VisualStudio/Core/Def/Implementation/VisualStudioRenameTrackingDismissService.cs
Outdated
Show resolved
Hide resolved
src/VisualStudio/Core/Def/Implementation/VisualStudioRenameTrackingDismissService.cs
Outdated
Show resolved
Hide resolved
namespace Microsoft.VisualStudio.LanguageServices.Implementation | ||
{ | ||
/// <summary> | ||
/// Handles dismissing the rename tracker when a symbol has changed |
There was a problem hiding this comment.
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?
src/VisualStudio/Core/Def/Implementation/VisualStudioRenameTrackingDismissService.cs
Outdated
Show resolved
Hide resolved
src/VisualStudio/Core/Test/DocumentRefactoring/DocumentRefactoringServiceTests.vb
Outdated
Show resolved
Hide resolved
Definitely need more info here. .. |
@CyrusNajmabadi this is still marked for personal review |
Oh. Sorry :) could you potentially use WIP in the titel? github email notifications don't include this info :( |
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: