Support CodeClass2.Parts returning parts in source generated files #58241
Conversation
These can be used by things other than the project system tests.
1dee96e to
fbabebf
Compare
The unit tests were testing against a special mock of VisualStudioWorkspace, which meant the "real" implementation of GetFileCodeModel wasn't being used, but instead requests for code model would just go through a lookup into a dictionary. This commit moves the MockVisualStudioWorkspace to inherit from VisualStudioWorkspaceImpl where the actual implementation lies, and then generally cleans up this area to create things via MEF rather than via direct calls to constructors of some MEF-produced types.
This returns a ProjectItem from the project system for a file, but in the case of source generated files we won't be able to do that since they're not represented by anything the project system knows about. Coincidently, our unit tests always have been passing null the whole time so this is something more or less we need to deal with regardless.
Some designers look through the other partial parts of a class to look for methods that match event handler signatures. Here I'm adding limited support so you can get a FileCodeModel and CodeElement for things residing in source generated files. Unlike regular FileCodeModels that have certain lifetime guarantees and use our COM weak handle magic, this is keeping the implementation simple and just returning a new FileCodeModel any time we need one, under the hopes that the uses of this are rare. If that becomes a problem, we can refactor the CodeModel cache to deal with these, but there is some complexity around when we precisely know the document has gone away. Since CodeModel is very much a legacy API I don't want to spend time adding complexity until we know we need it. This also doesn't implement any support for raising change events if a generator output changes. Again, if that is needed we can update the diffing code accordingly.
fee5781 to
f89b4bf
Compare
| return WorkspaceAndCodeModel.workspace; | ||
| } | ||
|
|
||
| private VisualStudioWorkspace GetExtraWorkspaceToDisposeButNotUse() |
There was a problem hiding this comment.
The VisualStudioWorkspace instance is now created by the MEF container, which will dispose it when we dispose the container itself.
|
|
||
| var documentFilePath = GetFilePath(documentId); | ||
| if (documentFilePath == null) | ||
| var document = _threadingContext.JoinableTaskFactory.Run(() => CurrentSolution.GetDocumentAsync(documentId, includeSourceGenerated: true).AsTask()); |
There was a problem hiding this comment.
:(
is this on UI thread? does this mean we're now blocking hte UI thread on unbounded source-generator work?
There was a problem hiding this comment.
That's not new, in essence: any CodeModel operation is going to be asking for semantics, which will block.
There was a problem hiding this comment.
Chatting with @CyrusNajmabadi offline, we also realized that in many cases they only way you get here is already having ran generators. i.e. you won't have known there's a DocumentId for the source generated file in the first place without generators having ran.
| else if (_isSourceGeneratedOutput) | ||
| { | ||
| document = State.ThreadingContext.JoinableTaskFactory.Run( | ||
| () => Workspace.CurrentSolution.GetSourceGeneratedDocumentAsync(GetDocumentId(), CancellationToken.None).AsTask()); |
| { | ||
| // Unlike for "regular" documents, we make no effort to cache these between callers or hold them for longer lifetimes with | ||
| // events. | ||
| return FileCodeModel.Create(GetCodeModelCache().State, parent: null, sourceGeneratedDocument.Id, isSourceGeneratorOutput: true, new TextManagerAdapter()).Handle; |
There was a problem hiding this comment.
do you want to CWT this?
There was a problem hiding this comment.
I could, I didn't want to do any caching at all just to keep things simple.
Some designers look through the other partial parts of a class to look for methods that match event handler signatures. Here I'm adding limited support so you can get a FileCodeModel and CodeElement for things residing in source generated files.
Unlike regular FileCodeModels that have certain lifetime guarantees and use our COM weak handle magic, this is keeping the implementation simple and just returning a new FileCodeModel any time we need one, under the hopes that the uses of this are rare. If that becomes a problem, we can refactor the CodeModel cache to deal with these, but there is some complexity around when we precisely know the document has gone away. Since CodeModel is very much a legacy API I don't want to spend time adding complexity until we know we need it.
This also doesn't implement any support for raising change events if a generator output changes. Again, if that is needed we can update the diffing code accordingly.