-
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
Add "Active file" analysis scope for background analysis in the IDE #39699
Conversation
Replaces the existing "Full Solution Analysis" option, with a background analysis scope to allow users to control the analysis scope for all solution crawler based background analysis. Fixes dotnet#38429. Active file scope minimizes all the background analysis to the active files, saving on CPU consumption from background analysis on remaining files in the solution. I have also updated up the low virtual memory listener which detects low VM and used to turn off just the full solution analysis to now set the background analysis scope to "Active file" for the current session.
if (incrementalAnalyzer.GetType().FullName == "Microsoft.CodeAnalysis.UnitTesting.SourceBasedTestDiscovery.SourceBasedTestDiscoveryIncrementalAnalyzer") | ||
{ | ||
return BackgroundAnalysisScope.FullSolution; | ||
} |
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.
@Cosifne After we merge the change to consume the unit testing IVTs, we should remove the above block of code. Perhaps this can be done as part of the second cleanup PR you were planning for the wrappers...
Overall, i like your OP! A few questions:
Is there any notification to the user that this has happened? perhaps a yellow bar stating that low-resource conditions were encountered and some services have been throttled to help out?
What doe that mean overall? Say i'm opening a project for the first time and tehre are designable items in it... how does VS learn about this? What's the flow here? (Feel free to provide gif if that will be clearer). Thanks!
Out of curiosity, does this at least run OOP? |
src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentIncrementalAnalyzer.cs
Outdated
Show resolved
Hide resolved
src/EditorFeatures/Core/Implementation/TodoComment/TodoCommentIncrementalAnalyzer.cs
Show resolved
Hide resolved
src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs
Show resolved
Hide resolved
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs
Outdated
Show resolved
Hide resolved
@@ -170,12 +247,12 @@ private void ReportPendingWorkItemCount() | |||
var reasons = workItem.InvocationReasons; | |||
if (workItem.MustRefresh || reasons.Contains(PredefinedInvocationReasons.SyntaxChanged)) | |||
{ | |||
await RunAnalyzersAsync(analyzers, document, (a, d, c) => a.AnalyzeSyntaxAsync(d, reasons, c), cancellationToken).ConfigureAwait(false); | |||
await RunAnalyzersAsync(analyzers, document, workItem, (a, d, c) => a.AnalyzeSyntaxAsync(d, reasons, c), cancellationToken).ConfigureAwait(false); |
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.
nit: we can calculate applicable analyzers up front and reuse it for calls to RunAnalyzersAsync
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.
Yes, I thought about it, but then felt it is much cleaner and reliable to keep the computation of applicable analyzers at one place just prior to the core execution. If we see any perf issues, I will follow your suggestion.
src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs
Show resolved
Hide resolved
{ | ||
get | ||
{ | ||
lock (_gate) |
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 the lock necessary for the read? i.e. are we protecting read from write, or just concurrent reads (which can trigger initialization)?
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 copied most of this from existing code in AbstractPriorityProcessor. I will keep it the same way and be defensive and consistent.
// Now update the task list if there are any changes from the previously computed TODO comments, if any. | ||
// NOTE: We don't check for cancellation here to ensure the task list view and our latest | ||
// computed state are identical. | ||
if (existingData == null || existingData.Items.Length != newData.Items.Length) |
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 existingData and newData has different items but same length?
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.
Good point. However, this hasn't change as part of this PR, I am just refactoring the code in this PR. I will play around a bit with this and see if I can get a repro to drive this change.
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 change to the core logic looks good to me, other than a few minor comments. But I skipped the UI part and didn't pay very close attention to the parts involves old/new options.
Marking as blocked on testing signoff from the unit testing team. |
…tch the error list terms in its scope filter combo box `Active file` -> `Current Document` `Open files and projects -> `Open Documents and Projects` `Full Solution` -> `Entire Solution`
I didn't read whole PR so it might be already handled. just curious since it looks like it reuse options for diagnostics to general background work.. so when user move to different tab (editor), do you enqueue reanalysis to old doc to remove errors? or you just leave them as they are? asking since for "open files", when user close file, we explicitly remove errors for that file. (except errors from build) it was done that way since, at the beginning, option said "open files only" and some people wanted things to work literally as option said. so we actively removed errors that don't belong to open files when that option is on. now, we re-worded the option to describe scope in general sense rather than what it does literally. so I am +1 on not touching errors already reported (even for open files case), but want to let you know the implication of that. if user fixes a semantic data, (unknown name "A"), you open new file, create new type "A", the error - unknown name "A" in other file, will not go away until user switch back to that file. my opinion is that is fine since that is the behavior of "build error" and with "active file or open file" scope, one is basically mixing "live and build" error behavior and get "build error" behavior for out of scope errors? |
another approach is putting high priority on removing errors but not on adding errors. basically, always consider any file that has errors as [files to analyze], and always analyze those when snapshot changed. that will make errors go away without users opening a file, but it won't make errors show up unless it is opened or active. doesn't matter for full solution analysis. it will show up at some point. |
I tried to repro this on latest 16.5 builds (prior to my change) and this behavior seems to have changed. Closing the files does not remove its diagnostics from the error list. Lets see if anyone complains about it, and if so we can first try to ask them to change their error list scope combo box to open files. If they still disagree, we can fix it to get the old behavior back.
Yes, I do enqueue a work item with "OnDocumentClosed" invocation reasons for previous active document, but that does not seem to be removing the diagnostics for that document. I presume it will also start working if we fix the open files case above.
Yes, that is true for active file scope, and confirms the reduced background analysis scope. I think that is acceptable behavior for this scope.
That is a good suggestion. I will wait for some more customer feedback on whether stale diagnostics in non-active file bother folks. I am also thinking on programmatically syncing the error list scope combo box when user switches their background scope in Tools Option. That might avoid us to add any additional removal logic. |
…kground analysis scope in Tools Options.
…cted background analysis scope in Tools Options." This reverts commit be5bd27.
… test team has moved to IUnitTestingIncrementalAnalyzer
Removing the "Blocked" label as @shyamnamboodiripad verified the test explorer functionality is unchanged on this branch. Thanks Shyam! |
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've verified that unit test discovery is not impacted by this change. Thanks for addressing all the concerns around unit test discovery!
public static BackgroundAnalysisScope GetOverriddenBackgroundAnalysisScope(this IIncrementalAnalyzer incrementalAnalyzer, OptionSet options, BackgroundAnalysisScope defaultBackgroundAnalysisScope) | ||
{ | ||
// Unit testing analyzer has special semantics for analysis scope. | ||
if (incrementalAnalyzer is UnitTestingIncrementalAnalyzer unitTestingAnalyzer) |
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.
@mavasani As I'd mentioned offline, I think it would be better if this were controlled in a way that does not require special casing the unit testing analyzer.
One thought is that perhaps a setting could be added via IncrementalAnalyzerProviderMetadata
(similar to HighPriorityForActiveFile
). The metadata can also be provided via MEF if required (ExportIncrementalAnalyzerProviderAttribute
). However, the unit testing analyzer is not exposed via MEF - so if you wanted to limit this to just the unit testing analyzer, you could make the opt out setting only available to analyzers that register via ISolutionCrawlerRegistrationService
and supply IncrementalAnalyzerProviderMetadata
...
Not urgent - but hopefully we can look into this at some point in the future...
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.
Thanks @shyamnamboodiripad. I will try to get to this in a follow-up PR.
…ak for TS and F# Recent PR dotnet#39699 introduced a breaking API and functionality change for TypeScript and F# as they are using our internal option ClosedFileDiagnostics for controlling their full solution analysis experience. This PR restores the ClosedFileDiagnostics option and functionality for non C#/VB languages. Additionally, the newly added option SolutionCrawlerOptions.BackgroundAnalysisScope has been made a per-language option so that all languages can have different UI/setting for this option.
Replaces the existing "Full Solution Analysis" option, with a background analysis scope to allow users to control the analysis scope for all solution crawler based background analysis (except for Test explorer's unit testing incremental analyzer, which will always execute for full solution).
Fixes #38429. Active file scope minimizes all the background analysis to the active files, saving on CPU consumption from background analysis on remaining files in the solution.
I have also updated up the low virtual memory listener which detects low VM and used to turn off just the full solution analysis to now set the background analysis scope to "Active file" for the current session.
TODOs:
Add unit tests and integration tests for new analysis scopeUpdate DesignerAttributeIncrementalAnalyzer to work at single file level for active file analysis.