// Decompiled with JetBrains decompiler // Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableViewModel // Assembly: IEnumerableVisualizer.UI, Version=18.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a // MVID: 8D605BCB-B575-4FCD-B6C8-446C06150F34 // Assembly location: C:\Program Files\Microsoft Visual Studio\18\Professional\Common7\IDE\CommonExtensions\Platform\Debugger\Visualizers\IEnumerableVisualizer.UI.dll using Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Copilot; using Microsoft.VisualStudio.Debugger.TabularDataUIShared; using Microsoft.VisualStudio.Debugger.TabularDataUIShared.WPF; using Microsoft.VisualStudio.DebuggerVisualizers; using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; #nullable enable namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer; internal class IEnumerableViewModel : TabularDataViewModel, IExpressionCopilotHandler { private bool isMaximized; public volatile int LoadedDebuggeeSideRowCount; private bool _showTables; private #nullable disable ObservableCollection _tables; internal object VisualizerObjectSource { get; } internal VisualizerCommunicator Communicator { get; } public override string VisualizerTelemetryPrefix { get => "VS/Diagnostics/Debugger/IEnumerableVisualizer/"; } public bool IsMaximized { get => this.isMaximized; set { if (this.isMaximized == value) return; this.isMaximized = value; this.OnPropertyChanged(nameof (IsMaximized)); } } protected override async Task ExportDataToCSVAsync(string filePath, Encoding fileEncoding) { bool csvHelperAsync; using (StreamWriter writer = new StreamWriter(filePath, false, fileEncoding)) csvHelperAsync = await this.ExportToCSVHelperAsync((TextWriter) writer, true); return csvHelperAsync; } protected override async Task CopyDataToClipboardAsync() { StringBuilder sb = new StringBuilder(); bool clipboardAsync; using (StringWriter writer = new StringWriter(sb)) { int num = await this.ExportToCSVHelperAsync((TextWriter) writer, false) ? 1 : 0; if (num != 0) Clipboard.SetText(sb.ToString()); clipboardAsync = num != 0; } sb = (StringBuilder) null; return clipboardAsync; } protected override async Task CopySelectedDataToClipboardAsync( ICollection> cells, int columnStartIndex, int columnEndIndex, int rowStartIndex, int rowEndIndex, bool isRectangularAndDense) { IEnumerableViewModel ienumerableViewModel = this; List list = ienumerableViewModel.SelectedTable.Columns.Where((Func) (c => c.IsVisible && c.Index >= columnStartIndex && c.Index <= columnEndIndex)).ToList(); StringBuilder sb = new StringBuilder(); using (StringWriter writer = new StringWriter(sb)) { CSVWriter csvWriter = new CSVWriter(list.Select((Func) (c => c.DisplayName)), (TextWriter) writer); IEnumerable columnIndiciesToExport = list.Select((Func) (c => c.Index)); List rowData = new List(columnEndIndex - columnStartIndex); for (int rowIndex = rowStartIndex; rowIndex <= rowEndIndex; ++rowIndex) { if (ienumerableViewModel.CancellableAsyncOperationToken.IsCancellationRequested || ienumerableViewModel.WindowClosingCancellationToken.IsCancellationRequested) return false; RowViewModel itemAt = (RowViewModel) ienumerableViewModel.SelectedTable.CollectionView.GetItemAt(rowIndex); foreach (int columnIndex in columnIndiciesToExport) { if (isRectangularAndDense || cells.Contains(Tuple.Create(rowIndex, columnIndex))) rowData.Add(itemAt[columnIndex]); else rowData.Add(string.Empty); } await csvWriter.WriteRowAsync((IEnumerable) rowData); rowData.Clear(); } csvWriter = (CSVWriter) null; columnIndiciesToExport = (IEnumerable) null; rowData = (List) null; } Clipboard.SetText(sb.ToString()); return true; } protected override async Task RefreshUnderlyingDataAsync(CancellationToken cancelToken) { IEnumerableViewModel iEnumerableViewModel = this; await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(new CancellationToken()); iEnumerableViewModel.LoadedDebuggeeSideRowCount = 0; iEnumerableViewModel.IsStillLoadingDebuggeeSideRows = true; iEnumerableViewModel.LastSearchQuery = (IVsSearchQuery) null; if (cancelToken.IsCancellationRequested) return false; List tableListAsync = await iEnumerableViewModel.Communicator.GetTableListAsync(cancelToken, true); int num = Math.Min(tableListAsync.Count, iEnumerableViewModel.Tables.Count); int count = iEnumerableViewModel.Tables.Count; for (int index = 0; index < num; ++index) { if (cancelToken.IsCancellationRequested) return false; TableDisplayCache tableModel = tableListAsync[index]; if (iEnumerableViewModel.Tables[index] is TableViewModel table) { IReadOnlyList columns = table.Columns; string name = table.Name; table.SetModel(tableModel); if (table.Name.Equals(name, StringComparison.Ordinal)) iEnumerableViewModel.RestoreColumnInfo(table, columns); table.LoadRows(); } else if (iEnumerableViewModel.Tables[index] is EmptyTableViewModel) { iEnumerableViewModel.Tables[index].ClearCache(); iEnumerableViewModel.Tables[index] = (BaseTableViewModel) new TableViewModel(iEnumerableViewModel, tableModel); } } if (count > tableListAsync.Count) { for (int index = count - 1; index >= tableListAsync.Count; --index) { iEnumerableViewModel.Tables[index].ClearCache(); iEnumerableViewModel.Tables.RemoveAt(index); } } else if (count < tableListAsync.Count) { for (int index = num; index < tableListAsync.Count; ++index) { TableDisplayCache tableModel = tableListAsync[index]; iEnumerableViewModel.Tables.Add((BaseTableViewModel) new TableViewModel(iEnumerableViewModel, tableModel)); } } await iEnumerableViewModel.InitializeVisualizerPropertiesAsync(); return true; } private void RestoreColumnInfo( TableViewModel updatedTableViewModel, IReadOnlyList originalColumnInfos) { foreach (ColumnInfo columnInfo in originalColumnInfos.Skip(1)) { if (columnInfo.Index >= updatedTableViewModel.Columns.Count) break; ColumnInfo column = updatedTableViewModel.Columns[columnInfo.Index]; if (column.DisplayName.Equals(columnInfo.DisplayName, StringComparison.Ordinal)) { bool flag = false; if (columnInfo.ChildrenIndices != null && column.IsExpandable) { this.Communicator.ExpandColumn(updatedTableViewModel.Index, columnInfo.Index); flag = true; } if (!columnInfo.IsVisible) { this.Communicator.HideColumn(updatedTableViewModel.Index, columnInfo.Index); flag = true; } if (flag) updatedTableViewModel.RefreshColumns(); } } } private async Task ExportToCSVHelperAsync(TextWriter writer, bool shouldWriteHeaders) { IEnumerableViewModel ienumerableViewModel = this; List columns = ienumerableViewModel.SelectedTable.Columns.Where((Func) (c => c.IsVisible)).ToList(); if (columns.Count == 0) { await writer.WriteLineAsync(); return true; } CSVWriter csvWriter = new CSVWriter(columns.Select((Func) (c => c.DisplayName)), writer); if (shouldWriteHeaders) await csvWriter.WriteHeaderAsync(); IEnumerable columnIndiciesToExport = (IEnumerable) columns.Select((Func) (c => c.Index)).ToList(); foreach (RowViewModel rowViewModel in (IEnumerable) ienumerableViewModel.SelectedTable.CollectionView) { CancellationToken cancellationToken = ienumerableViewModel.CancellableAsyncOperationToken; if (!cancellationToken.IsCancellationRequested) { cancellationToken = ienumerableViewModel.WindowClosingCancellationToken; if (!cancellationToken.IsCancellationRequested) { List rowData = new List(); foreach (int columnIndex in columnIndiciesToExport) rowData.Add(rowViewModel[columnIndex]); await csvWriter.WriteRowAsync((IEnumerable) rowData); continue; } } return false; } return true; } public IEnumerableViewModel( VisualizerObjectSourceClient visualizerObjectSource, string visualizedExpression, JoinableTaskFactory jtf) : base(visualizedExpression, jtf) { this.VisualizerObjectSource = (object) (visualizerObjectSource ?? throw new ArgumentNullException(nameof (visualizerObjectSource))); this.Tables = new ObservableCollection(); this.Communicator = new VisualizerCommunicator(this); } public IEnumerableViewModel( IAsyncVisualizerObjectProvider visualizerObjectSource, JoinableTaskFactory jtf) : base((string) null, jtf) { this.VisualizerObjectSource = (object) (visualizerObjectSource ?? throw new ArgumentNullException(nameof (visualizerObjectSource))); this.Tables = new ObservableCollection(); this.Communicator = new VisualizerCommunicator(this); } public event EventHandler ColumnsUpdateAttempted; public event EventHandler LatestDataAvailableUpdated; public event EventHandler ContinuePausedOperation; public IViewHelper ViewHelper { get; set; } internal UpdateTableRowResult UpdateTableRowResults( TableViewModel table, int rowIndex, string[] values) { return this.Communicator.UpdateTableRowResults(table.Index, rowIndex, values); } internal TableDisplayCache InvalidateTableModel(TableViewModel table, out bool succeeded) { return this.Communicator.InvalidateTableCache(table.Index, out succeeded); } internal bool IsColumnLocked(ColumnInfo columnInfo) { return this.Communicator.IsColumnLocked(columnInfo); } internal void ExpandColumn(int columnIndex) { this.ContextMenuCommandHelper(Resources.G_ExpandColumnError, (Func<(bool, bool)>) (() => { SuccessResult successResult = this.Communicator.ExpandColumn(this.SelectedTableIndex, columnIndex); TelemetryUtilities.LogVisualizerTelemetry((TabularDataViewModel) this, TelemetryConstants.ExpandColumnCompletedEvent, TelemetryConstants.ExpandColumnEvent, (IEnumerable<(string, string)>) new List<(string, string)>() { (TelemetryConstants.PropertyResult, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) successResult.WasSuccessful))) }); return (successResult.WasSuccessful, true); })); } internal void HideColumn(int columnIndex) { this.ContextMenuCommandHelper(Resources.G_HideColumnError, (Func<(bool, bool)>) (() => { SuccessResult successResult = this.Communicator.HideColumn(this.SelectedTableIndex, columnIndex); if (successResult.WasSuccessful) this.SelectedTable.Columns[columnIndex].IsVisible = false; TelemetryUtilities.LogVisualizerTelemetry((TabularDataViewModel) this, TelemetryConstants.HideColumnCompletedEvent, TelemetryConstants.HideColumnEvent, (IEnumerable<(string, string)>) new List<(string, string)>() { (TelemetryConstants.PropertyResult, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) successResult.WasSuccessful))) }); return (successResult.WasSuccessful, false); })); } internal void HideChildren(int columnIndex) { this.ContextMenuCommandHelper(Resources.G_HideColumnsError, (Func<(bool, bool)>) (() => { SuccessResult successResult = this.Communicator.HideChildren(this.SelectedTableIndex, columnIndex); if (successResult.WasSuccessful) { foreach (int childrenIndex in this.SelectedTable.Columns[columnIndex].ChildrenIndices) this.SelectedTable.Columns[childrenIndex].IsVisible = false; } TelemetryUtilities.LogVisualizerTelemetry((TabularDataViewModel) this, TelemetryConstants.HideChildrenCompletedEvent, TelemetryConstants.HideChildrenEvent, (IEnumerable<(string, string)>) new List<(string, string)>() { (TelemetryConstants.PropertyResult, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) successResult.WasSuccessful))) }); return (successResult.WasSuccessful, false); })); } private void ContextMenuCommandHelper(string errorMessage, Func<(bool, bool)> customOperation) { (bool wasSuccessful, bool flag) = customOperation(); if (wasSuccessful & flag) { this.SelectedTable.RefreshColumns(); this.SelectedTable.InvalidateTableModel(); } EventHandler columnsUpdateAttempted = this.ColumnsUpdateAttempted; if (columnsUpdateAttempted == null) return; columnsUpdateAttempted((object) this, new ContextMenuCommandArgs(wasSuccessful, errorMessage)); } public DataObjectTypeName DataObjectType { get; private set; } public bool ShowTables { get => this._showTables; set { if (this._showTables == value) return; this._showTables = value; this.OnPropertyChanged(nameof (ShowTables)); } } public ObservableCollection Tables { get => this._tables; set { if (this._tables == value) return; this._tables = value; this.OnPropertyChanged(nameof (Tables)); } } public TableViewModel SelectedTable => this.Tables[this.SelectedTableIndex] as TableViewModel; public override ITableViewModel SelectedSimplifiedTable => (ITableViewModel) this.SelectedTable; public async Task StartFetchingRemainingRowsAsync(CancellationToken cancellationToken) { IEnumerableViewModel sender = this; bool shouldExit = false; sender.IsStillLoadingDebuggeeSideRows = true; while (!shouldExit) { CancellationToken cancellationToken1 = sender.WindowClosingCancellationToken; if (cancellationToken1.IsCancellationRequested || cancellationToken.IsCancellationRequested) break; int newlyLoadedRowCount = (await sender.Communicator.AdvanceIEnumerableDataSourceAsync(5000, cancellationToken)).Value; if (newlyLoadedRowCount != 5000) shouldExit = true; await Task.Yield(); cancellationToken1 = sender.WindowClosingCancellationToken; if (!cancellationToken1.IsCancellationRequested && !cancellationToken.IsCancellationRequested) { TableViewModel selectedTable = sender.SelectedTable; sender.LoadedDebuggeeSideRowCount += newlyLoadedRowCount; if (sender.LoadedDebuggeeSideRowCount > 1000000) { sender.LoadedDebuggeeSideRowCount = 1000000; sender.StatusBarMessage = string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resources.G_MaxRowsReached); shouldExit = true; } if (shouldExit) { sender.IsStillLoadingDebuggeeSideRows = false; IEnumerableViewModel viewModel = sender; string debuggeeSideRowsEvent = TelemetryConstants.FinishedLoadingDebuggeeSideRowsEvent; List<(string, string)> valueTupleList1 = new List<(string, string)>(); valueTupleList1.Add((TelemetryConstants.PropertyLoadedDebuggeeSideRowCount, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) sender.LoadedDebuggeeSideRowCount)))); valueTupleList1.Add((TelemetryConstants.PropertyCurrentTableRowsCount, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) selectedTable?.TableRows?.Count.GetValueOrDefault())))); List<(string, string)> valueTupleList2 = valueTupleList1; string tableColumnsCount = TelemetryConstants.PropertyCurrentTableColumnsCount; object[] objArray = new object[1]; TableViewModel tableViewModel = selectedTable; objArray[0] = (object) (tableViewModel != null ? tableViewModel.ColumnCount : 0); string str = FormattableString.Invariant(FormattableStringFactory.Create("{0}", objArray)); (string, string) valueTuple = (tableColumnsCount, str); valueTupleList2.Add(valueTuple); List<(string, string)> properties = valueTupleList1; await TelemetryUtilities.LogVisualizerTelemetryAsync((TabularDataViewModel) viewModel, debuggeeSideRowsEvent, (IEnumerable<(string, string)>) properties); if (sender.IsCancellableAsyncOperationInProgress) { EventHandler continuePausedOperation = sender.ContinuePausedOperation; if (continuePausedOperation != null) continuePausedOperation((object) sender, new PausedOperationInfo(sender.AsyncOperationsTracker.LastUserAsyncOperation)); } } if (selectedTable?.TableRows != null && selectedTable.TableRows.Count < sender.LoadedDebuggeeSideRowCount) { int num = selectedTable.TableRows.Count + (sender.LoadedDebuggeeSideRowCount - selectedTable.TableRows.Count); for (int count = selectedTable.TableRows.Count; count < num; ++count) selectedTable.TableRows.Add(new RowViewModel((ITableViewModel) selectedTable, count)); } EventHandler availableUpdated = sender.LatestDataAvailableUpdated; if (availableUpdated != null) availableUpdated((object) sender, new LatestDataAvailable(sender.LoadedDebuggeeSideRowCount)); sender.RefreshSelectedRowsStatusBarText(); selectedTable = (TableViewModel) null; } } } public override void TableRowsHaveAvailableData( int rowIndex, int rowCount, bool hideProgressOnCompletion) { if (this.SelectedTable?.TableRows != null) { for (int index = 0; index < rowCount; ++index) { this.SelectedTable.TableRows[rowIndex + index].HasData = true; this.SelectedTable.TableRows[rowIndex + index].OnValuesChanged(); } } if (!hideProgressOnCompletion) return; this.AsyncOperationsTracker.SetAsyncOperationStatusFor(AsyncOperationsTracker.AsyncOperationType.FETCHING_PAGE, false); } public async Task InitializeAsync() { IEnumerableViewModel iEnumerableViewModel = this; try { foreach (TableDisplayCache tableModel in await iEnumerableViewModel.Communicator.GetTableListAsync(CancellationToken.None)) iEnumerableViewModel.Tables.Add((BaseTableViewModel) new TableViewModel(iEnumerableViewModel, tableModel)); await iEnumerableViewModel.InitializeVisualizerPropertiesAsync(); } catch (Exception ex) { iEnumerableViewModel.IsEnabled = false; } } private async Task InitializeVisualizerPropertiesAsync() { IEnumerableViewModel ienumerableViewModel = this; ienumerableViewModel.DataObjectType = ienumerableViewModel.Communicator.DataObjectType; ienumerableViewModel.ShowTables = ienumerableViewModel.Tables.Count > 1; ienumerableViewModel.IsEnabled = ienumerableViewModel.Tables.Any(); if (!ienumerableViewModel.IsEnabled) ienumerableViewModel.Tables.Add((BaseTableViewModel) new EmptyTableViewModel(ienumerableViewModel)); if (ienumerableViewModel.Tables.Count > 0) ienumerableViewModel.SelectedTableIndex = 0; await TelemetryUtilities.LogVisualizerTelemetryAsync((TabularDataViewModel) ienumerableViewModel, TelemetryConstants.InitializedEvent, (IEnumerable<(string, string)>) new List<(string, string)>() { (TelemetryConstants.PropertyTableCount, FormattableString.Invariant(FormattableStringFactory.Create("{0}", (object) ienumerableViewModel.Tables.Count))) }); } public async Task StartCopilotSessionAsync( string currentExpression, ExpressionReplaceCallback replaceCallback, CopilotSessionType sessionType, string additionalUserContext, CancellationToken cancellationToken) { TableViewModel selectedTable = this.SelectedTable; string type = selectedTable != null ? selectedTable.Columns.FirstOrDefault()?.Type : (string) null; return NullableHelpers.IsNullOrEmpty(type) || IEnumerableCopilotService.Instance == null ? IEnumerableCopilotService.FailedSessionStart : await IEnumerableCopilotService.Instance.StartCopilotAsync(sessionType, currentExpression, type, this.GetVisualizedExpressionTypeMembers(type), replaceCallback, additionalUserContext, cancellationToken); } public async Task UpdateSessionContextAsync( Guid sessionId, string updatedExpression, string additionalUserContext, CancellationToken cancellationToken) { TableViewModel selectedTable = this.SelectedTable; string type = selectedTable != null ? selectedTable.Columns.FirstOrDefault()?.Type : (string) null; return !NullableHelpers.IsNullOrEmpty(type) && IEnumerableCopilotService.Instance != null && await IEnumerableCopilotService.Instance.UpdateSessionContextAsync(sessionId, updatedExpression, type, this.GetVisualizedExpressionTypeMembers(type), additionalUserContext, cancellationToken); } public Task OpenSessionInChatAsync( Guid sessionId, CancellationToken cancellationToken) { return IEnumerableCopilotService.Instance == null ? Task.FromResult(new CopilotSessionStartResult(false, new Guid?(sessionId))) : IEnumerableCopilotService.Instance.OpenSessionInChatAsync(sessionId, cancellationToken); } public Task ShowCopilotChatToolWindowAsync(CancellationToken cancellationToken) { return IEnumerableCopilotService.ShowCopilotChatToolWindowAsync(cancellationToken); } private IEnumerable GetVisualizedExpressionTypeMembers( string expressionTypeName) { return this.SelectedTable.Columns.Skip(1).Select((Func) (c => new IEnumerableTypeDefinitionEntry(c.Type, PackageUtilities.TrimPrefix(c.DisplayName, expressionTypeName + ".", StringComparison.Ordinal)))); } }