From 6812ec14d38fb31aa475ccdf42fcd687c7a947ac Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 22 Feb 2022 14:59:05 -0800 Subject: [PATCH] Value Tracking: Remove inproc symbol usage (#59473) Remove symbol usage in the Value Tracking command handler, and use the IValueTrackingService instead to find the right symbol at the root. Fixes #59378 --- .../ValueTrackedTreeItemViewModel.cs | 5 +- .../ValueTrackingCommandHandler.cs | 108 ++++++------------ 2 files changed, 39 insertions(+), 74 deletions(-) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index 945ae9e206746..995dbe4e19bb5 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -83,6 +83,7 @@ private ValueTrackedTreeItemViewModel( internal static async ValueTask CreateAsync( Solution solution, ValueTrackedItem item, + ImmutableArray children, ValueTrackingTreeViewModel treeViewModel, IGlyphService glyphService, IValueTrackingService valueTrackingService, @@ -108,7 +109,7 @@ internal static async ValueTask CreateAsync( globalOptions, threadingContext, fileName, - children: ImmutableArray.Empty); + children); } private void CalculateChildren() @@ -173,7 +174,7 @@ private async Task> CalculateChildrenAsync(Can cancellationToken).ConfigureAwait(false); return await valueTrackedItems.SelectAsArrayAsync((item, cancellationToken) => - CreateAsync(_solution, item, TreeViewModel, _glyphService, _valueTrackingService, _globalOptions, ThreadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); + CreateAsync(_solution, item, children: ImmutableArray.Empty, TreeViewModel, _glyphService, _valueTrackingService, _globalOptions, ThreadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index 735d18a399119..a5c8066ba2ef7 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Immutable; using System.ComponentModel.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -90,101 +90,65 @@ public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecution _threadingContext.JoinableTaskFactory.RunAsync(async () => { - var selectedSymbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken).ConfigureAwait(false); - if (selectedSymbol is null) + var service = document.Project.Solution.Workspace.Services.GetRequiredService(); + var items = await service.TrackValueSourceAsync(textSpan, document, cancellationToken).ConfigureAwait(false); + if (items.Length == 0) { - // TODO: Show error dialog return; } - var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); - var location = Location.Create(syntaxTree, textSpan); - - await ShowToolWindowAsync(args.TextView, selectedSymbol, location, document.Project.Solution, cancellationToken).ConfigureAwait(false); + await ShowToolWindowAsync(args.TextView, document, items, cancellationToken).ConfigureAwait(false); }); return true; } - private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) - { - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var selectedNode = root.FindNode(textSpan); - if (selectedNode is null) - { - return null; - } - - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var selectedSymbol = - semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol - ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); - - if (selectedSymbol is null) - { - return null; - } - - return selectedSymbol switch - { - ILocalSymbol - or IPropertySymbol { SetMethod: not null } - or IFieldSymbol { IsReadOnly: false } - or IEventSymbol - or IParameterSymbol - => selectedSymbol, - - _ => null - }; - } - - private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbol, Location location, Solution solution, CancellationToken cancellationToken) + private async Task ShowToolWindowAsync(ITextView textView, Document document, ImmutableArray items, CancellationToken cancellationToken) { - var item = await ValueTrackedItem.TryCreateAsync(solution, location, selectedSymbol, cancellationToken: cancellationToken).ConfigureAwait(false); - if (item is null) - { - return; - } - var toolWindow = await GetOrCreateToolWindowAsync(textView, cancellationToken).ConfigureAwait(false); if (toolWindow?.ViewModel is null) { return; } - var valueTrackingService = solution.Workspace.Services.GetRequiredService(); var classificationFormatMap = _classificationFormatMapService.GetClassificationFormatMap(textView); + var solution = document.Project.Solution; + var valueTrackingService = solution.Workspace.Services.GetRequiredService(); + var rootItemMap = items.GroupBy(i => i.Parent, resultSelector: (key, items) => (parent: key, children: items)); - var childItems = await valueTrackingService.TrackValueSourceAsync(solution, item, cancellationToken).ConfigureAwait(false); - - var childViewModels = await childItems.SelectAsArrayAsync((item, cancellationToken) => - ValueTrackedTreeItemViewModel.CreateAsync(solution, item, toolWindow.ViewModel, _glyphService, valueTrackingService, _globalOptions, _threadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); - - RoslynDebug.AssertNotNull(location.SourceTree); - var document = solution.GetRequiredDocument(location.SourceTree); - var options = _globalOptions.GetClassificationOptions(document.Project.Language); + using var _ = CodeAnalysis.PooledObjects.ArrayBuilder.GetInstance(out var rootItems); - var sourceText = await location.SourceTree.GetTextAsync(cancellationToken).ConfigureAwait(false); - var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, options, cancellationToken).ConfigureAwait(false); - var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, options, cancellationToken).ConfigureAwait(false); + foreach (var (parent, children) in rootItemMap) + { + if (parent is null) + { + foreach (var child in children) + { + var root = await ValueTrackedTreeItemViewModel.CreateAsync(solution, child, children: ImmutableArray.Empty, toolWindow.ViewModel, _glyphService, valueTrackingService, _globalOptions, _threadingContext, cancellationToken).ConfigureAwait(false); + rootItems.Add(root); + } + } + else + { + using var _1 = CodeAnalysis.PooledObjects.ArrayBuilder.GetInstance(out var childItems); + foreach (var child in children) + { + var childViewModel = await ValueTrackedTreeItemViewModel.CreateAsync(solution, child, children: ImmutableArray.Empty, toolWindow.ViewModel, _glyphService, valueTrackingService, _globalOptions, _threadingContext, cancellationToken).ConfigureAwait(false); + childItems.Add(childViewModel); + } - var root = new TreeItemViewModel( - location.SourceSpan, - sourceText, - document.Id, - document.FilePath ?? document.Name, - selectedSymbol.GetGlyph(), - classificationResult.ClassifiedSpans, - toolWindow.ViewModel, - _glyphService, - _threadingContext, - solution.Workspace, - childViewModels); + var root = await ValueTrackedTreeItemViewModel.CreateAsync(solution, parent, childItems.ToImmutable(), toolWindow.ViewModel, _glyphService, valueTrackingService, _globalOptions, _threadingContext, cancellationToken).ConfigureAwait(false); + rootItems.Add(root); + } + } await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); toolWindow.ViewModel.Roots.Clear(); - toolWindow.ViewModel.Roots.Add(root); + foreach (var root in rootItems) + { + toolWindow.ViewModel.Roots.Add(root); + } await ShowToolWindowAsync(cancellationToken).ConfigureAwait(true); }