-
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
Allow server to pass along a specific project-scope it wants to sync data for. #70342
Changes from all commits
2b6b842
f72b32e
1fab9f7
c2b8de8
528a01b
eb5f197
ab6e43d
bf4badd
afe7f7a
0103f81
66dc991
d174bb8
4b619d7
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 |
---|---|---|
|
@@ -72,6 +72,7 @@ public static SolutionStateChecksums Deserialize(ObjectReader reader) | |
|
||
public async Task FindAsync( | ||
SolutionState state, | ||
ProjectId? hintProject, | ||
HashSet<Checksum> searchingChecksumsLeft, | ||
Dictionary<Checksum, object> result, | ||
CancellationToken cancellationToken) | ||
|
@@ -99,18 +100,42 @@ public async Task FindAsync( | |
result[FrozenSourceGeneratedDocumentText] = await SerializableSourceText.FromTextDocumentStateAsync(state.FrozenSourceGeneratedDocumentState, cancellationToken).ConfigureAwait(false); | ||
} | ||
|
||
foreach (var (_, projectState) in state.ProjectStates) | ||
ChecksumCollection.Find(state.AnalyzerReferences, AnalyzerReferences, searchingChecksumsLeft, result, cancellationToken); | ||
|
||
// Before doing a depth-first-search *into* each project, first run across all the project at their top level. | ||
// This ensures that when we are trying to sync the projects referenced by a SolutionStateChecksums' instance | ||
// that we don't unnecessarily walk all documents looking just for those. | ||
|
||
foreach (var (projectId, projectState) in state.ProjectStates) | ||
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. 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 changes in a followup pr. i use the hint project to just index directly into state.ProjectStates. |
||
{ | ||
if (searchingChecksumsLeft.Count == 0) | ||
break; | ||
|
||
if (hintProject != null && hintProject != projectId) | ||
continue; | ||
|
||
if (projectState.TryGetStateChecksums(out var projectStateChecksums) && | ||
searchingChecksumsLeft.Remove(projectStateChecksums.Checksum)) | ||
{ | ||
result[projectStateChecksums.Checksum] = projectStateChecksums; | ||
} | ||
} | ||
|
||
// Now actually do the depth first search into each project. | ||
|
||
foreach (var (projectId, projectState) in state.ProjectStates) | ||
{ | ||
if (searchingChecksumsLeft.Count == 0) | ||
break; | ||
|
||
if (hintProject != null && hintProject != projectId) | ||
continue; | ||
|
||
// It's possible not all all our projects have checksums. Specifically, we may have only been | ||
// asked to compute the checksum tree for a subset of projects that were all that a feature needed. | ||
if (projectState.TryGetStateChecksums(out var projectStateChecksums)) | ||
await projectStateChecksums.FindAsync(projectState, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); | ||
} | ||
|
||
ChecksumCollection.Find(state.AnalyzerReferences, AnalyzerReferences, searchingChecksumsLeft, result, cancellationToken); | ||
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. by moving this above the projects, we don't bother searching into documents when someone is just trying to sync the solution-checksum-info in isolation. |
||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,28 +19,29 @@ internal abstract class AbstractAssetProvider | |
/// <summary> | ||
/// return data of type T whose checksum is the given checksum | ||
/// </summary> | ||
public abstract ValueTask<T> GetAssetAsync<T>(Checksum checksum, CancellationToken cancellationToken); | ||
public abstract ValueTask<T> GetAssetAsync<T>(ProjectId? hintProject, Checksum checksum, CancellationToken cancellationToken); | ||
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. 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. that happens in the followup :) |
||
|
||
public async Task<SolutionInfo> CreateSolutionInfoAsync(Checksum solutionChecksum, CancellationToken cancellationToken) | ||
{ | ||
var solutionChecksums = await GetAssetAsync<SolutionStateChecksums>(solutionChecksum, cancellationToken).ConfigureAwait(false); | ||
var solutionAttributes = await GetAssetAsync<SolutionInfo.SolutionAttributes>(solutionChecksums.Attributes, cancellationToken).ConfigureAwait(false); | ||
var solutionChecksums = await GetAssetAsync<SolutionStateChecksums>(hintProject: null, solutionChecksum, cancellationToken).ConfigureAwait(false); | ||
var solutionAttributes = await GetAssetAsync<SolutionInfo.SolutionAttributes>(hintProject: null, solutionChecksums.Attributes, cancellationToken).ConfigureAwait(false); | ||
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 hints here (because this is solution info, not project info). However, with teh reordering we did in the solution-finding code, this will all be cheap. that's because we search the direct (non-project-child) info of the solution first, before recursing. So we'll always find this data in O(1) time, instead of O(solution) time. |
||
|
||
using var _ = ArrayBuilder<ProjectInfo>.GetInstance(solutionChecksums.Projects.Count, out var projects); | ||
foreach (var projectChecksum in solutionChecksums.Projects) | ||
projects.AddIfNotNull(await CreateProjectInfoAsync(projectChecksum, cancellationToken).ConfigureAwait(false)); | ||
|
||
var analyzerReferences = await CreateCollectionAsync<AnalyzerReference>(solutionChecksums.AnalyzerReferences, cancellationToken).ConfigureAwait(false); | ||
var analyzerReferences = await CreateCollectionAsync<AnalyzerReference>(hintProject: null, solutionChecksums.AnalyzerReferences, cancellationToken).ConfigureAwait(false); | ||
|
||
return SolutionInfo.Create( | ||
solutionAttributes.Id, solutionAttributes.Version, solutionAttributes.FilePath, projects.ToImmutableAndClear(), analyzerReferences).WithTelemetryId(solutionAttributes.TelemetryId); | ||
} | ||
|
||
public async Task<ProjectInfo?> CreateProjectInfoAsync(Checksum projectChecksum, CancellationToken cancellationToken) | ||
{ | ||
var projectChecksums = await GetAssetAsync<ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false); | ||
var projectChecksums = await GetAssetAsync<ProjectStateChecksums>(hintProject: null, projectChecksum, cancellationToken).ConfigureAwait(false); | ||
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. note: this is a place where passing a project-id would be better. a followup PR accomplishes this. |
||
|
||
var attributes = await GetAssetAsync<ProjectInfo.ProjectAttributes>(projectChecksums.Info, cancellationToken).ConfigureAwait(false); | ||
var projectId = projectChecksums.ProjectId; | ||
var attributes = await GetAssetAsync<ProjectInfo.ProjectAttributes>(hintProject: projectId, projectChecksums.Info, cancellationToken).ConfigureAwait(false); | ||
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. in this PR, we don't know the projectID without this roundtrip. A future PR fixes that, so we have that info up front. |
||
if (!RemoteSupportedLanguages.IsSupported(attributes.Language)) | ||
{ | ||
// only add project our workspace supports. | ||
|
@@ -49,17 +50,16 @@ public async Task<SolutionInfo> CreateSolutionInfoAsync(Checksum solutionChecksu | |
} | ||
|
||
var compilationOptions = attributes.FixUpCompilationOptions( | ||
await GetAssetAsync<CompilationOptions>(projectChecksums.CompilationOptions, cancellationToken).ConfigureAwait(false)); | ||
await GetAssetAsync<CompilationOptions>(hintProject: projectId, projectChecksums.CompilationOptions, cancellationToken).ConfigureAwait(false)); | ||
var parseOptions = await GetAssetAsync<ParseOptions>(hintProject: projectId, projectChecksums.ParseOptions, cancellationToken).ConfigureAwait(false); | ||
|
||
var parseOptions = await GetAssetAsync<ParseOptions>(projectChecksums.ParseOptions, cancellationToken).ConfigureAwait(false); | ||
var projectReferences = await CreateCollectionAsync<ProjectReference>(hintProject: projectId, projectChecksums.ProjectReferences, cancellationToken).ConfigureAwait(false); | ||
var metadataReferences = await CreateCollectionAsync<MetadataReference>(hintProject: projectId, projectChecksums.MetadataReferences, cancellationToken).ConfigureAwait(false); | ||
var analyzerReferences = await CreateCollectionAsync<AnalyzerReference>(hintProject: projectId, projectChecksums.AnalyzerReferences, cancellationToken).ConfigureAwait(false); | ||
|
||
var projectReferences = await CreateCollectionAsync<ProjectReference>(projectChecksums.ProjectReferences, cancellationToken).ConfigureAwait(false); | ||
var metadataReferences = await CreateCollectionAsync<MetadataReference>(projectChecksums.MetadataReferences, cancellationToken).ConfigureAwait(false); | ||
var analyzerReferences = await CreateCollectionAsync<AnalyzerReference>(projectChecksums.AnalyzerReferences, cancellationToken).ConfigureAwait(false); | ||
|
||
var documentInfos = await CreateDocumentInfosAsync(projectChecksums.Documents, cancellationToken).ConfigureAwait(false); | ||
var additionalDocumentInfos = await CreateDocumentInfosAsync(projectChecksums.AdditionalDocuments, cancellationToken).ConfigureAwait(false); | ||
var analyzerConfigDocumentInfos = await CreateDocumentInfosAsync(projectChecksums.AnalyzerConfigDocuments, cancellationToken).ConfigureAwait(false); | ||
var documentInfos = await CreateDocumentInfosAsync(projectChecksums.Documents).ConfigureAwait(false); | ||
var additionalDocumentInfos = await CreateDocumentInfosAsync(projectChecksums.AdditionalDocuments).ConfigureAwait(false); | ||
var analyzerConfigDocumentInfos = await CreateDocumentInfosAsync(projectChecksums.AnalyzerConfigDocuments).ConfigureAwait(false); | ||
|
||
return ProjectInfo.Create( | ||
attributes, | ||
|
@@ -72,13 +72,27 @@ public async Task<SolutionInfo> CreateSolutionInfoAsync(Checksum solutionChecksu | |
additionalDocumentInfos, | ||
analyzerConfigDocumentInfos, | ||
hostObjectType: null); // TODO: https://github.com/dotnet/roslyn/issues/62804 | ||
|
||
async Task<ImmutableArray<DocumentInfo>> CreateDocumentInfosAsync(ChecksumCollection documentChecksums) | ||
{ | ||
using var _ = ArrayBuilder<DocumentInfo>.GetInstance(documentChecksums.Count, out var documentInfos); | ||
|
||
foreach (var documentChecksum in documentChecksums) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
documentInfos.Add(await CreateDocumentInfoAsync(projectId, documentChecksum, cancellationToken).ConfigureAwait(false)); | ||
} | ||
|
||
return documentInfos.ToImmutableAndClear(); | ||
} | ||
} | ||
|
||
public async Task<DocumentInfo> CreateDocumentInfoAsync(Checksum documentChecksum, CancellationToken cancellationToken) | ||
public async Task<DocumentInfo> CreateDocumentInfoAsync( | ||
ProjectId projectId, Checksum documentChecksum, CancellationToken cancellationToken) | ||
{ | ||
var documentSnapshot = await GetAssetAsync<DocumentStateChecksums>(documentChecksum, cancellationToken).ConfigureAwait(false); | ||
var attributes = await GetAssetAsync<DocumentInfo.DocumentAttributes>(documentSnapshot.Info, cancellationToken).ConfigureAwait(false); | ||
var serializableSourceText = await GetAssetAsync<SerializableSourceText>(documentSnapshot.Text, cancellationToken).ConfigureAwait(false); | ||
var documentSnapshot = await GetAssetAsync<DocumentStateChecksums>(hintProject: projectId, documentChecksum, cancellationToken).ConfigureAwait(false); | ||
var attributes = await GetAssetAsync<DocumentInfo.DocumentAttributes>(hintProject: projectId, documentSnapshot.Info, cancellationToken).ConfigureAwait(false); | ||
var serializableSourceText = await GetAssetAsync<SerializableSourceText>(hintProject: projectId, documentSnapshot.Text, cancellationToken).ConfigureAwait(false); | ||
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 scopes us down to a particular project to find this data. but we still need to search the whole project to find the document info being asked for. a followup PR addresses this by allowing the doc-id to be passed along as well to narrow the search even further. |
||
|
||
var text = await serializableSourceText.GetTextAsync(cancellationToken).ConfigureAwait(false); | ||
var textLoader = TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create(), attributes.FilePath)); | ||
|
@@ -87,27 +101,15 @@ public async Task<DocumentInfo> CreateDocumentInfoAsync(Checksum documentChecksu | |
return new DocumentInfo(attributes, textLoader, documentServiceProvider: null); | ||
} | ||
|
||
private async Task<ImmutableArray<DocumentInfo>> CreateDocumentInfosAsync(ChecksumCollection documentChecksums, CancellationToken cancellationToken) | ||
{ | ||
using var _ = ArrayBuilder<DocumentInfo>.GetInstance(documentChecksums.Count, out var documentInfos); | ||
|
||
foreach (var documentChecksum in documentChecksums) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
documentInfos.Add(await CreateDocumentInfoAsync(documentChecksum, cancellationToken).ConfigureAwait(false)); | ||
} | ||
|
||
return documentInfos.ToImmutableAndClear(); | ||
} | ||
|
||
public async Task<ImmutableArray<T>> CreateCollectionAsync<T>(ChecksumCollection checksums, CancellationToken cancellationToken) where T : class | ||
public async Task<ImmutableArray<T>> CreateCollectionAsync<T>( | ||
ProjectId? hintProject, ChecksumCollection checksums, CancellationToken cancellationToken) where T : class | ||
{ | ||
using var _ = ArrayBuilder<T>.GetInstance(checksums.Count, out var assets); | ||
|
||
foreach (var checksum in checksums) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
assets.Add(await GetAssetAsync<T>(checksum, cancellationToken).ConfigureAwait(false)); | ||
assets.Add(await GetAssetAsync<T>(hintProject, checksum, cancellationToken).ConfigureAwait(false)); | ||
} | ||
|
||
return assets.ToImmutableAndClear(); | ||
|
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 pass this 'hint' along to tell the host where to search, isntead of searching everything.
Note: in a followup PR thsi becomes more generic, so we can also scope down to the document level as well (so it's trivial to get data for things like a document, without having to do a full solution sweep).