-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Simplify how frozen partial documents are produced. #72106
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
Changes from all commits
ec96fdf
e10f539
e779049
9474247
47d67dc
0ae7572
da1d505
33c13c9
0f14496
f0d8d5d
90d4413
d9cf134
85b289e
0d618a7
b04c2c3
b4acb04
907f5e1
f63f083
1abfc99
33dcff4
18075f8
e1549e5
196779b
c612602
3a242b5
e093933
c90b066
8632ff0
a3a51af
77fc5b7
752cfee
268acfe
06bcc73
5b5e9b5
ec4c22d
2d0994b
165c5c8
fd19961
edcda05
ef76245
6044e88
b2f88b3
8818c50
29af9c5
1ca0682
c87d1d7
cb4b4e8
01535b0
67a1797
ccf6878
d17921b
35f714e
5c10a29
8a58525
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -186,153 +186,6 @@ ImmutableList<TranslationAction> UpdatePendingTranslationActions( | |
| } | ||
| } | ||
|
|
||
| public ICompilationTracker FreezePartialState(CancellationToken cancellationToken) | ||
| { | ||
| var state = this.ReadState(); | ||
|
|
||
| // If we're already finalized then just return what we have, and with the frozen bit flipped so that | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all this work got effectively inlined into this method.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the diff is awful. i recommend not looking at it inlined.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok. because the diff was so awful, i moved this method much lower. so it just shows as a full add. |
||
| // any future forks keep things frozen. | ||
| if (state is FinalCompilationTrackerState finalState) | ||
| { | ||
| // If we're finalized and already frozen, we can just use ourselves. | ||
| return finalState.IsFrozen | ||
| ? this | ||
| : new CompilationTracker(this.ProjectState, finalState.WithIsFrozen(), this.SkeletonReferenceCache.Clone()); | ||
| } | ||
|
|
||
| Contract.ThrowIfFalse(state is null or InProgressState); | ||
|
|
||
| GetPartialCompilationState( | ||
| state, | ||
| out var inProgressProject, | ||
| out var compilationWithoutGeneratedDocuments, | ||
| out var compilationWithGeneratedDocuments, | ||
| out var generatorInfo, | ||
| cancellationToken); | ||
|
|
||
| // Transition us to a frozen in progress state. With the best compilations up to this point, but no | ||
| // more pending actions, and with the frozen bit flipped so that any future forks keep things | ||
| // frozen. | ||
| var inProgressState = InProgressState.Create( | ||
| isFrozen: true, | ||
| compilationWithoutGeneratedDocuments, | ||
| generatorInfo, | ||
| compilationWithGeneratedDocuments, | ||
| pendingTranslationActions: []); | ||
|
|
||
| return new CompilationTracker(inProgressProject, inProgressState, this.SkeletonReferenceCache.Clone()); | ||
| } | ||
|
|
||
| public ICompilationTracker FreezePartialStateWithDocument( | ||
| DocumentState docState, | ||
| CancellationToken cancellationToken) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no more freezing partial state with documents. instead, we just freeze the whole state, and then add/update the doc of interest back at the outer level |
||
| { | ||
| var state = this.ReadState(); | ||
|
|
||
| // If we're already finalized, and no change has been made to this document. Then just return what we | ||
| // have (just with the frozen bit flipped so that any future forks keep things frozen). | ||
| if (state is FinalCompilationTrackerState finalState && | ||
| this.ProjectState.DocumentStates.TryGetState(docState.Id, out var oldState) && | ||
| oldState == docState) | ||
| { | ||
| // If we're finalized, already frozen and have the document being asked for, we can just use ourselves. | ||
| return finalState.IsFrozen | ||
| ? this | ||
| : new CompilationTracker(this.ProjectState, finalState.WithIsFrozen(), this.SkeletonReferenceCache.Clone()); | ||
| } | ||
|
|
||
| // Otherwise, we're not finalized, or the document has changed. We'll create an in-progress state | ||
| // to represent the new state of the project, with all the necessary translation steps to | ||
| // incorporate the new document. | ||
|
|
||
| GetPartialCompilationState( | ||
| state, | ||
| out var oldProjectState, | ||
| out var compilationWithoutGeneratedDocuments, | ||
| out var compilationWithGeneratedDocuments, | ||
| out var generatorInfo, | ||
| cancellationToken); | ||
|
|
||
| TranslationAction pendingAction = oldProjectState.DocumentStates.TryGetState(docState.Id, out oldState) | ||
| // The document had been previously parsed and it's there, so we can update it with our current | ||
| // state. Note if no compilation existed GetPartialCompilationState would have produced an empty | ||
| // one, and removed any documents, so inProgressProject.DocumentStates would have been empty | ||
| // originally. | ||
| ? new TranslationAction.TouchDocumentAction(oldProjectState, oldProjectState.UpdateDocument(docState, contentChanged: true), oldState, docState) | ||
| // The document wasn't present in the original snapshot at all, and we just need to add the | ||
| // document. | ||
| : new TranslationAction.AddDocumentsAction(oldProjectState, oldProjectState.AddDocuments([docState]), [docState]); | ||
|
|
||
| // Transition us to a frozen in progress state. With the best compilations up to this point, and the | ||
| // pending actions we'll need to perform on it to get to the final state. | ||
| var inProgressState = InProgressState.Create( | ||
| isFrozen: true, | ||
| compilationWithoutGeneratedDocuments, | ||
| generatorInfo, | ||
| compilationWithGeneratedDocuments, | ||
| [pendingAction]); | ||
|
|
||
| return new CompilationTracker(pendingAction.NewProjectState, inProgressState, this.SkeletonReferenceCache.Clone()); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Tries to get the latest snapshot of the compilation without waiting for it to be fully built. This | ||
| /// method takes advantage of the progress side-effect produced during <see | ||
| /// cref="GetOrBuildFinalStateAsync"/>. It will either return the already built compilation, any in-progress | ||
| /// compilation or any known old compilation in that order of preference. The compilation state that is | ||
| /// returned will have a compilation that is retained so that it cannot disappear. | ||
| /// </summary> | ||
| private void GetPartialCompilationState( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this whole method got inlined. |
||
| CompilationTrackerState? state, | ||
| out ProjectState inProgressProject, | ||
| out Compilation compilationWithoutGeneratedDocuments, | ||
| out Compilation compilationWithGeneratedDocuments, | ||
| out CompilationTrackerGeneratorInfo generatorInfo, | ||
| CancellationToken cancellationToken) | ||
| { | ||
| if (state is null) | ||
| { | ||
| inProgressProject = this.ProjectState.RemoveAllDocuments(); | ||
| generatorInfo = CompilationTrackerGeneratorInfo.Empty; | ||
|
|
||
| compilationWithoutGeneratedDocuments = this.CreateEmptyCompilation(); | ||
| compilationWithGeneratedDocuments = compilationWithoutGeneratedDocuments; | ||
| } | ||
| else if (state is FinalCompilationTrackerState finalState) | ||
| { | ||
| inProgressProject = this.ProjectState; | ||
| generatorInfo = finalState.GeneratorInfo; | ||
|
|
||
| compilationWithoutGeneratedDocuments = finalState.CompilationWithoutGeneratedDocuments; | ||
| compilationWithGeneratedDocuments = finalState.FinalCompilationWithGeneratedDocuments; | ||
|
|
||
| } | ||
| else if (state is InProgressState inProgressState) | ||
| { | ||
| generatorInfo = inProgressState.GeneratorInfo; | ||
| inProgressProject = inProgressState is { PendingTranslationActions: [var translationAction, ..] } | ||
| ? translationAction.OldProjectState | ||
| : this.ProjectState; | ||
|
|
||
| compilationWithoutGeneratedDocuments = inProgressState.CompilationWithoutGeneratedDocuments; | ||
|
|
||
| // Parse the generated documents in parallel. | ||
| Parallel.ForEach( | ||
| generatorInfo.Documents.States.Values, | ||
| new ParallelOptions { CancellationToken = cancellationToken }, | ||
| state => state.GetSyntaxTree(cancellationToken)); | ||
| cancellationToken.ThrowIfCancellationRequested(); | ||
|
|
||
| // Retrieving the syntax trees will be free now that we computed them above. | ||
| compilationWithGeneratedDocuments = compilationWithoutGeneratedDocuments.AddSyntaxTrees( | ||
| generatorInfo.Documents.States.Values.Select(state => state.GetSyntaxTree(cancellationToken))); | ||
| } | ||
| else | ||
| { | ||
| throw ExceptionUtilities.UnexpectedValue(state.GetType()); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the final compilation if it is available. | ||
| /// </summary> | ||
|
|
@@ -780,6 +633,86 @@ private async Task<bool> HasSuccessfullyLoadedSlowAsync( | |
| return finalState.HasSuccessfullyLoaded; | ||
| } | ||
|
|
||
| public ICompilationTracker FreezePartialState(CancellationToken cancellationToken) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here's teh new method, in all its simplified and inlined glory. |
||
| { | ||
| var state = this.ReadState(); | ||
|
|
||
| // If we're finalized and already frozen, we can just use ourselves. | ||
| if (state is FinalCompilationTrackerState { IsFrozen: true } finalState) | ||
| return this; | ||
|
|
||
| var projectState = state switch | ||
| { | ||
| // If we don't have an existing state, then transition to a project state without any data. | ||
| null => this.ProjectState.RemoveAllDocuments(), | ||
|
|
||
| FinalCompilationTrackerState => this.ProjectState, | ||
|
|
||
| // If we have an in progress state with no steps, then we're just at the current project state. | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| InProgressState { PendingTranslationActions: [] } => this.ProjectState, | ||
|
|
||
| // Otherwise, reset us to whatever state the InProgressState had currently transitioned to. | ||
| InProgressState inProgressState => inProgressState.PendingTranslationActions.First().OldProjectState, | ||
|
|
||
| _ => throw ExceptionUtilities.UnexpectedValue(state.GetType()), | ||
| }; | ||
|
|
||
| var frozenState = GetFrozenCompilationState(); | ||
| Contract.ThrowIfFalse(frozenState.IsFrozen); | ||
| return new CompilationTracker(projectState, frozenState, this.SkeletonReferenceCache.Clone()); | ||
|
|
||
| CompilationTrackerState GetFrozenCompilationState() | ||
| { | ||
| if (state is FinalCompilationTrackerState finalState) | ||
| { | ||
| // Checked by caller. | ||
| Contract.ThrowIfTrue(finalState.IsFrozen); | ||
| // If we're already finalized then just return what we have, and with the frozen bit flipped so | ||
| // that any future forks keep things frozen. | ||
| return finalState.WithIsFrozen(); | ||
| } | ||
|
|
||
| // Non-final state currently. Produce an in-progress-state containing the forked change. Note: we | ||
| // transition to in-progress-state here (and not final-state) as we still want to leverage all the | ||
| // final-state-transition logic contained in FinalizeCompilationAsync (for example, properly setting | ||
| // up all references). | ||
| if (state is null) | ||
| { | ||
| // We have no data at all. Create a frozen empty project/compilation to represent this state. | ||
| var compilationWithoutGeneratedDocuments = this.CreateEmptyCompilation(); | ||
| var compilationWithGeneratedDocuments = compilationWithoutGeneratedDocuments; | ||
|
|
||
| return InProgressState.Create( | ||
| isFrozen: true, | ||
| compilationWithoutGeneratedDocuments, | ||
| CompilationTrackerGeneratorInfo.Empty, | ||
| compilationWithGeneratedDocuments, | ||
| pendingTranslationActions: []); | ||
| } | ||
| else if (state is InProgressState inProgressState) | ||
| { | ||
| // Grab whatever is in the in-progress-state so far, add any generated docs, and snap | ||
| // us to a frozen state with that information. | ||
| var generatorInfo = inProgressState.GeneratorInfo; | ||
|
|
||
| var compilationWithoutGeneratedDocuments = inProgressState.CompilationWithoutGeneratedDocuments; | ||
| var compilationWithGeneratedDocuments = compilationWithoutGeneratedDocuments.AddSyntaxTrees( | ||
| generatorInfo.Documents.States.Values.Select(state => state.GetSyntaxTree(cancellationToken))); | ||
|
|
||
| return InProgressState.Create( | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| isFrozen: true, | ||
| compilationWithoutGeneratedDocuments, | ||
| generatorInfo, | ||
| compilationWithGeneratedDocuments, | ||
| pendingTranslationActions: []); | ||
| } | ||
| else | ||
| { | ||
| throw ExceptionUtilities.UnexpectedValue(state.GetType()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public async ValueTask<TextDocumentStates<SourceGeneratedDocumentState>> GetSourceGeneratedDocumentStatesAsync( | ||
| SolutionCompilationState compilationState, CancellationToken cancellationToken) | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,7 +39,6 @@ private interface ICompilationTracker | |
| Task<Compilation> GetCompilationAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); | ||
|
|
||
| ICompilationTracker FreezePartialState(CancellationToken cancellationToken); | ||
| ICompilationTracker FreezePartialStateWithDocument(DocumentState docState, CancellationToken cancellationToken); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i def like ICompTracker getting simpler. And i think the semantics of 'Freeze the entire state' are much clearer than 'freeze the state, but ensure we have this docState in it'. |
||
|
|
||
| Task<VersionStamp> GetDependentVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); | ||
| Task<VersionStamp> GetDependentSemanticVersionAsync(SolutionCompilationState compilationState, 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.
Can this use your other helper? If this is coming in a queued PR then feel free to resolve this comment.
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.
will do!