-
Notifications
You must be signed in to change notification settings - Fork 195
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
Add mapped edits helper #11146
Add mapped edits helper #11146
Conversation
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 love the tests in general, but I wonder if there might be false positives from manually supplying the mappings. Is it possible to create a test that gets a real set of changes from Roslyn when renaming a using in a C# file that is the real razor compiler output, and use those changes and mappings?
I didn't have the closest look at the actual RazorEditHelper algorithm, so not approving yet, but left a few comments for how far I've gotten so far. Nothing worrying though :)
src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/LinePositionExtensions.cs
Outdated
Show resolved
Hide resolved
...r/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RazorEditHelper.TextChangeBuilder.cs
Outdated
Show resolved
Hide resolved
continue; | ||
} | ||
|
||
if (RazorSyntaxFacts.IsInUsingDirective(node)) |
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.
Not for this PR obviously, and perhaps never if this isn't a problem, but I'm 99% sure that the Razor compiler always outputs all using directives in a contiguous block in C#, regardless of where they come from (ie, which block of usings, _Imports files, etc.). It might be worth having the compiler expose the start and end line, in the C# document, of the using directives and then just compare the edit against those two line numbers, rather than repeatedly digging through the Razor tree here.
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.
ooo that's interesting. As long as that holds true it would simplify this a bit.
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.
This looks to be true, but brings about a point related to #6155 in that we're not correctly handling changes that should go into _imports files. Let me think more on it....
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 would be fine with v1 not handling _Imports
files, as I don't think the compiler maps them at all. Doing so would need a filename added to SourceMapping
or SourceSpan
, and updating everywhere to check that property, and I don't think that is in the realms of possibility right now. In future (cohosting) we can use the #line
mappings to handle this.
...r/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RazorEditHelper.TextChangeBuilder.cs
Outdated
Show resolved
Hide resolved
...r/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RazorEditHelper.TextChangeBuilder.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorSyntaxFacts.cs
Outdated
Show resolved
Hide resolved
...icrosoft.VisualStudio.LanguageServices.Razor/DynamicFiles/DefaultDynamicDocumentContainer.cs
Outdated
Show resolved
Hide resolved
...c/Microsoft.VisualStudio.LanguageServices.Razor/DynamicFiles/RazorDynamicFileInfoProvider.cs
Outdated
Show resolved
Hide resolved
...icrosoft.AspNetCore.Razor.LanguageServer.Test/Mapping/RazorMapToDocumentEditsEndpointTest.cs
Outdated
Show resolved
Hide resolved
...icrosoft.AspNetCore.Razor.LanguageServer.Test/Mapping/RazorMapToDocumentEditsEndpointTest.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.
All of my feedback is relatively minor. Happy to drop a green check when tests are passing
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RangComparer.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RangComparer.cs
Outdated
Show resolved
Hide resolved
...r/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RazorEditHelper.TextChangeBuilder.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RangComparer.cs
Outdated
Show resolved
Hide resolved
...r/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RazorEditHelper.TextChangeBuilder.cs
Outdated
Show resolved
Hide resolved
...r/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RazorEditHelper.TextChangeBuilder.cs
Outdated
Show resolved
Hide resolved
...rosoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorLSPMappingServiceTest.cs
Outdated
Show resolved
Hide resolved
...rosoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorLSPMappingServiceTest.cs
Outdated
Show resolved
Hide resolved
...rosoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorLSPMappingServiceTest.cs
Show resolved
Hide resolved
cancellationToken); | ||
} | ||
|
||
public async Task<ImmutableArray<RazorMappedEditoResult>> MapTextChangesAsync( |
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.
Amusingly, RazorMappedEditResult
is misspelled in the EA. 😁
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.
My worst version of spanglish to date
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.
Too funny. 🤣
I'd like to add one of these. Other than an integration test is it possible to fully set up a test workspace for this and unit test it? I was having trouble figuring it out but it's also quite late so brain may not be top tier functioning |
Not sure we have anything outside of cohosting that does the whole thing, but we have the bones of it. For getting a real generated C# document, with real mappings, yes. Anything that inherits from I wonder if you could use that, and put a test in If not, I guess just poke around at what the |
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.
Love the tests, and I don't think any of my comments are blocking. A real world test could be a pain so I don't think its a problem to follow up. Despite my comments about having the bones of it, an integration test might just be easier :)
I do wonder which tests fail, and in what way, if the AddComplexUsingsChanges
method is removed, and we just always call AddNewUsingChanges
and AddRemoveUsingsChanges
...zor/src/Microsoft.AspNetCore.Razor.LanguageServer/Mapping/RazorMapToDocumentEditsEndpoint.cs
Outdated
Show resolved
Hide resolved
...Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/RazorEditHelper.TextChangeBuilder.cs
Outdated
Show resolved
Hide resolved
return; | ||
} | ||
|
||
AddComplexUsingsChanges(codeDocument, addedUsings, removedUsings, 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.
I'm not sure I understand why we need separate logic to have additions and removals. Can't we just get the add changes, then then the remove changes, and then sort them to make sure they're in order?
src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/RazorEditHelper.cs
Show resolved
Hide resolved
src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/RazorEditHelper.cs
Outdated
Show resolved
Hide resolved
.Select(u => u.ToString()["using ".Length..^1]); | ||
.Select(u => u.Name) | ||
.WhereNotNull() | ||
.SelectAsArray(n => n.ToString()); |
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.
This is definitely a lot easier than the code I had before, but it does worry me ever so slightly.
Right now if a Razor file has:
@using System;
@using X = System.DateTime;
@using static System.Math;
they will be output into the generated C# verbatim (DotNetInternals), and just accessing the Name
property will pull them out as ["System", "System.DateTime", "System.Math"]
(SharpLab)
I admit, anything but a simple using statement is unlikely, but we do know that some users have using aliases, as they've reported bugs, and I think this change would mean those would still break under rename. Not sure if we need to worry about this, or wait for feedback, but at the same time I don't think there is harm in just putting back the old code. Or does it not work with the new edit helper?
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.
Okay, I think this is your follow up, #11168. I'll leave the comment though, because that issue mentions the Razor tree being inconsistent, but I'm pretty sure which ever solution we come up with for that would also have to be consistent with this.
Perhaps the answer is just to use the string representation of the entire directive, sans @
in Razor and ;
in C#
private readonly IDocumentMappingService _documentMappingService = new DocumentMappingService(loggerFactory); | ||
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<RazorMappingService>(); | ||
|
||
public async Task<ImmutableArray<RazorMappedSpanResult>> MapSpansAsync(Document document, IEnumerable<TextSpan> spans, CancellationToken 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.
I wonder if RazorMappingService
should inherit from RazorSpanMappingService
so we don't have to re-implement this method, and TryGetMappedSpans
. Unless they're different?
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 thought about that, but also am just considering removing the RazorSpanMappingService
. I'll consider this a follow up cleanup
src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Mapping/RazorEditHelperTest.cs
Outdated
Show resolved
Hide resolved
{|map1:public int Counter { get; set; }|} | ||
} | ||
""", | ||
newCSharpSource: |
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.
Okay this is the pettiest of nits, but perhaps it would be easier to rearrange the parameters so its C#, new C#, Razor, new Razor. Would make it easier to tell what has changed between new and old I think
...Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/RazorEditHelper.TextChangeBuilder.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.
This change LGTM
…Mapping/UsingsNodeComparer.cs Co-authored-by: David Wengier <[email protected]>
…Mapping/RazorEditHelper.cs Co-authored-by: David Wengier <[email protected]>
I'm planning on getting this in today with the follow up items I filed. If you see anything else that seems off please let me know! It's less complicated than my last attempt but I think there's probably more work to simplify even further. @davidwengier especially has a good comment on #11146 (comment) where I want to follow up and make sure assumptions I had before are true. 3 of the current tests fail but that doesn't mean there can't be further simplifications. |
Would be curious to know in what fashion they fail too, and whether it's blocking. You're essentially implementing an algorithm that does both sort usings, group system usings first, and honours a users arbitrary grouping. An admirable goal, but perhaps something we don't need to hit. |
Razor side of implementing
IRazorMappingService
.Suggested file by file. Bulk of the implementation is in RazorEditHelper. Commits don't really tell a story but a lot of the changes are mechanical to implement new service + endpoint. The process is generally as follows:
Call LSP endpoint to map edits from
IRazorMappingService
. LSP endpoints cassRazorEditHelper.MapCSharpEditsAsync
. This usesRazorEditHelper.TextChangeBuilder
to get the initial changes the normalizes them since there may be overlapping changes.Tests are in in
RazorMapToDocumentEditsEndpointTest.cs
and should generally describe functionality fairly well. I'm still flushing out more scenarios.