-
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
Change RazorProjectInfo to use HostProject and HostDocument #11030
base: main
Are you sure you want to change the base?
Conversation
HostDocument is a record and now it correctly implements equality
It turns out that DocumentSnapshotHandle and HostDocument are essentially identical, so we can unify on HostDocument.
We should check the version before verifying the array header.
Correct. This is not a breaking change for VS but will block VS Code until a dual insertion. I'll ping you offline for timelines |
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'd like to get #11015 in before this goes in to facilitate making the snap for prerelease this week in VS Code.
|
||
var version = reader.ReadInt32(); | ||
reader.ReadArrayHeaderAndVerify(4); |
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.
Why not have this above the verify, and just have the reader move in VerifyVersionNumber
since it's already passed by ref?
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 guess because that felt a bit more awkward to me when I wrote this. The problem is that we need to verify the array header length after the version check. Otherwise, we may never get to the version check before we blow up because the array size changed if a new version happens to add or remove RazorProjectInfo
field. I felt that it was better to have a separate method to verify the version by peeking ahead and then leaving this method to do the real data read.
since it's already passed by ref?
MessagePackReader
really should be passed by ref in some way. It's a ref struct and a ref has already been taken for it. I'll update VerifyVersionNumber
to ref readonly
.
{ | ||
return null; | ||
} | ||
|
||
var projectPath = Path.GetDirectoryName(project.FilePath); | ||
if (projectPath is 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.
is it possible for this to be null if the project.FilePath
is checked above?
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 don't believe so, but it's annotated to be string?
on .NET without a [NotNullIfNotNull]
attribute.
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.
Actually, it turns out that it is possible for this to return null if project.FilePath
is not null. Project.GetDirectoryName(...)
returns null if the file path pass to it is null OR if it is a root without a directory name, such as /
or C:
.
defaultNamespace = rootNamespace ?? "ASP"; // TODO: Source generator does this. Do we want it? | ||
|
||
return razorConfiguration; | ||
return (configuration, rootNamespace ?? "ASP"); // TODO: Source generator does this. Do we want 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 think we can just remove the TODO
from this. It's what we've been doing for a while
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.
Or honestly just leave it 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.
Or honestly just leave it null
This is how subtle bugs creep in! 😛
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.
Yeah, I'm not comfortable making an intentional behavior change in this method when I was just making a small refactoring.
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.
Fair enough. Can we remove the null check on line 83 at least?
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.
and change the return tuple to (RazorConfiguration configuration, string rootNamespace)
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.
For sure! I had marked string rootNamespace
as nullable before I even looked at the implementation, since I know that HostProject.RootNamespace
is nullable. Clearly it can't be null though.
{ | ||
var normalizedPath = FilePathNormalizer.Normalize(filePath); |
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 worries me. If previous RazorProjectService
would ensure normalized paths, but HostProject
does not, I think we'll end up [re-]introducing subtle bugs. We've definitely seen scenarios where CPS and Roslyn don't agree on what a path looks like, with both systems even changing for a specific project during load.
The simplest fix here would be to have HostProject
normalize the path in its constructor. That seems to fit well with the rest of the PR, centralizing our APIs on a single type that has all the necessary logic.
The other option would be to have HostProject
use the normalizing comparer, and doing a pass through everything that uses HostProject.FilePath
or IProjectSnapshot.FilePath
for comparisons and making them use the normalizing comparer too.
As an example, I think this could now break:
https://github.com/dotnet/razor/blob/main/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshotManager.cs#L160
That gets called from our CPS data flow receiver, and if it has a project in it that comes from the fallback project manager via Roslyn, it might not see it as the same project.
I am aware that "always call the file path normalizer" is a little bit of cargo cult programming, but I'm not sure I trust our tests to provide coverage and confidence that we can move away from it. Thankfully the Good Ship Cohosting should save us :D
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 that you spotted this! It was the only place that I was aware of removing a call to FilePathNormalizer.Normalize(...)
. I fully agree that the whole file-normalization business is super error prone, and I think HostProject
is great place to unify some of that. I'll take a closer look at that for this pull request.
Thankfully the Good Ship Cohosting should save us :D
goodshipcohosting.com appears to be available for purchase.
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.
FWIW, as I've started looking at this, I'm finding that it's unclear whether HostProject.FIlePath
should be normalized or not. Currently, it looks like the VS ProjectSnapshotManager
does not contain normalized project paths but the language server ProjectSnapshotManager
does. Since CPS only accesses the VS ProjectSnapshotManager
, I can't see how it would be affected by the language server's ProjectSnapshotManager
suddenly not having normalized file paths. This looks to me like a mess and I'm not sure that I know how it's supposed to work.
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 VS side not having normalized paths is not too surprising I guess, since it doesn't have a RazorProjectService, so perhaps my worry about that specific location wasn't valid (or is already broken 😁). I still worry about changing the language server side to not be normalized, but perhaps its worth rolling the die for a preview 1?
On the other hand, I don't think normalizing everywhere is likely to introduce issues, the only downside really is reinforcing the cargo cult.
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.
perhaps its worth rolling the die for a preview 1
That doesn't feel good to me. I want to get to the bottom of this. 😄
defaultNamespace = rootNamespace ?? "ASP"; // TODO: Source generator does this. Do we want it? | ||
|
||
return razorConfiguration; | ||
return (configuration, rootNamespace ?? "ASP"); // TODO: Source generator does this. Do we want 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.
Or honestly just leave it null
This is how subtle bugs creep in! 😛
src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TestRazorProjectService.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.
btw, why these files live in Shared? they are not used by the razor compiler, are they?
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.
Before we switched to message pack, these were compiled into both VS and the Razor language server. Today, I think there's a desire for the compiler to use them rather than a different TagHelperDescriptor JSON converter in tests. Seems reasonable to share that code someday.
This pushes the
HostProject
andHostDocument
types down to MS.ANC.Razor.ProjectEngineHost and uses them inRazorProjectInfo
.RazorProjectInfo
tracks all of the same information as aHostProject
, so it makes sense to bring them together. In addition,RazorProjectInfo
exposed anImmutableArray<DocumentSnapshotHandle>
butDocumentSnapshotHandle
is identical toHostDocument
. So, that's been unified as well. In addition, I've gone ahead and simplified some of the code inRazorProjectService
to take advantage ofHostProject
andHostDocument
.These changes require an increment to the serialization format versions. So, this will need to make its way to VS Code as well. (Although, since we don't save bin files to disk anymore, is that strictly necessary for VS Code?)