File: StackTraceExplorer\StackTraceExplorerViewModel.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_ozsccwvc_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.StackTraceExplorer;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.VisualStudio.LanguageServices.Utilities;
using Microsoft.VisualStudio.Text.Classification;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.StackTraceExplorer;
 
internal class StackTraceExplorerViewModel : ViewModelBase
{
    private readonly IThreadingContext _threadingContext;
    private readonly Workspace _workspace;
    public ObservableCollection<FrameViewModel> Frames { get; } = [];
 
    private bool _isLoading;
    private readonly ClassificationTypeMap _classificationTypeMap;
    private readonly IClassificationFormatMap _formatMap;
 
    public bool IsLoading
    {
        get => _isLoading;
        set => SetProperty(ref _isLoading, value);
    }
 
    private FrameViewModel? _selection;
    public FrameViewModel? Selection
    {
        get => _selection;
        set => SetProperty(ref _selection, value);
    }
 
    public bool IsListVisible => Frames.Count > 0;
    public bool IsInstructionTextVisible => Frames.Count == 0;
 
    private int _id;
 
    internal void OnClear()
    {
        Frames.Clear();
    }
 
    public string InstructionText => ServicesVSResources.Paste_valid_stack_trace;
    public string StackTrace => ServicesVSResources.Stack_Trace;
 
    public StackTraceExplorerViewModel(IThreadingContext threadingContext, Workspace workspace, ClassificationTypeMap classificationTypeMap, IClassificationFormatMap formatMap)
    {
        _threadingContext = threadingContext;
        _workspace = workspace;
 
        workspace.WorkspaceChanged += Workspace_WorkspaceChanged;
        _classificationTypeMap = classificationTypeMap;
        _formatMap = formatMap;
 
        Frames.CollectionChanged += CallstackLines_CollectionChanged;
    }
 
    public bool Matches(string text) => text.GetHashCode() == _id;
 
    public void OnPaste_CallOnUIThread(string text)
    {
        IsLoading = true;
        Frames.Clear();
        var cancellationToken = _threadingContext.DisposalToken;
        System.Threading.Tasks.Task.Run(async () =>
        {
            try
            {
                var result = await StackTraceAnalyzer.AnalyzeAsync(text, cancellationToken).ConfigureAwait(false);
                await SetStackTraceResultAsync(result, text, cancellationToken).ConfigureAwait(false);
            }
            finally
            {
                await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync();
                IsLoading = false;
            }
        }, cancellationToken);
    }
 
    public async System.Threading.Tasks.Task SetStackTraceResultAsync(StackTraceAnalysisResult result, string originalText, System.Threading.CancellationToken cancellationToken)
    {
        _id = originalText.GetHashCode();
        var viewModels = result.ParsedFrames.Select(l => GetViewModel(l));
 
        await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
 
        Selection = null;
        Frames.Clear();
 
        foreach (var vm in viewModels)
        {
            Frames.Add(vm);
        }
    }
 
    private void CallstackLines_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        NotifyPropertyChanged(nameof(IsListVisible));
        NotifyPropertyChanged(nameof(IsInstructionTextVisible));
    }
 
    private void Workspace_WorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
    {
        if (e.Kind == WorkspaceChangeKind.SolutionChanged)
        {
            Selection = null;
            Frames.Clear();
        }
    }
 
    private FrameViewModel GetViewModel(ParsedFrame frame)
        => frame switch
        {
            IgnoredFrame ignoredFrame => new IgnoredFrameViewModel(ignoredFrame, _formatMap, _classificationTypeMap),
            ParsedStackFrame stackFrame => new StackFrameViewModel(stackFrame, _threadingContext, _workspace, _formatMap, _classificationTypeMap),
            _ => throw ExceptionUtilities.UnexpectedValue(frame)
        };
}