commit b77fb4ce4683b8901b4142f7595a46e4e5131daa Author: Matthias Heil Date: Wed Apr 15 16:05:46 2026 +0200 Added IEnumerableViewModel.cs Added IEnumerableVisualizer.cs Added IEnumerableVisualizerControl.cs Added IEnumerableVisualizerProvider.cs Added IEnumerableVisualizerSource.cs Added LatestDataAvailable.cs diff --git a/IEnumerableViewModel.cs b/IEnumerableViewModel.cs new file mode 100644 index 0000000..c37b35e --- /dev/null +++ b/IEnumerableViewModel.cs @@ -0,0 +1,527 @@ +// 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)))); + } +} diff --git a/IEnumerableVisualizer.cs b/IEnumerableVisualizer.cs new file mode 100644 index 0000000..7c1c09b --- /dev/null +++ b/IEnumerableVisualizer.cs @@ -0,0 +1,71 @@ +// Decompiled with JetBrains decompiler +// Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizer +// 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.DebuggerVisualizers; +using Microsoft.VisualStudio.DebuggerVisualizers.Common; +using Microsoft.VisualStudio.OutOfProcessVisualizers; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Utilities; +using System; +using System.Globalization; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Forms; + +#nullable enable +namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer; + +public class IEnumerableVisualizer : DialogDebuggerVisualizer +{ + public IEnumerableVisualizer() + : base((FormatterPolicy) 1) + { + } + + protected internal virtual void Show( + #nullable disable + IDialogVisualizerService windowService, + IVisualizerObjectProvider objectProvider) + { + if (!(windowService is IWin32Window win32Window)) + throw new ApplicationException("This debugger does not support modal visualizers"); + using (DpiAwareness.EnterDpiScope((DpiAwarenessContext) -4)) + { + IEnumerableViewModel viewModel = new IEnumerableViewModel((IAsyncVisualizerObjectProvider) objectProvider, VsTaskLibraryHelper.WithPriority(ThreadHelper.JoinableTaskFactory, (VsTaskRunContext) 6)); + viewModel.UINormalPriorityJTF.Run((Func) (async () => await viewModel.InitializeAsync())); + IEnumerableVisualizerControl visualizerControl1 = new IEnumerableVisualizerControl(); + visualizerControl1.DataContext = (object) viewModel; + visualizerControl1.Debugger = (IVsDebugger) null; + visualizerControl1.AreDataTipsEnabled = false; + IEnumerableVisualizerControl visualizerControl2 = visualizerControl1; + OutOfProcessVisualizerModalDialogViewModel modalDialogViewModel = new OutOfProcessVisualizerModalDialogViewModel((OutOfProcessVisualizer) null, string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resources.G_TitlebarCaption, (object) viewModel.DataObjectType.ToString()), Guid.Empty, string.Empty, string.Empty, viewModel.UINormalPriorityJTF); + ((OutOfProcessVisualizerViewModel) modalDialogViewModel).MainContent = (IDisposable) visualizerControl2; + ((OutOfProcessVisualizerViewModel) modalDialogViewModel).DialogState = (DialogState) 1; + ModalVisualizerDialog visualizerDialog = new ModalVisualizerDialog(); + ((FrameworkElement) visualizerDialog).DataContext = (object) modalDialogViewModel; + Window window = (Window) visualizerDialog; + try + { + if (viewModel.FatalExceptionCaught) + return; + Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizer.ShowWpfDialog(window, win32Window.Handle); + } + catch (Exception ex) + { + if (viewModel.FatalExceptionCaught) + return; + throw; + } + } + } + + private static void ShowWpfDialog(Window window, IntPtr hwndParent) + { + WindowExtensions.SetOwner(window, hwndParent); + window.ShowDialog(); + } +} diff --git a/IEnumerableVisualizerControl.cs b/IEnumerableVisualizerControl.cs new file mode 100644 index 0000000..debf8dd --- /dev/null +++ b/IEnumerableVisualizerControl.cs @@ -0,0 +1,525 @@ +// Decompiled with JetBrains decompiler +// Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizerControl +// 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.Internal.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Copilot; +using Microsoft.VisualStudio.Debugger.Interop; +using Microsoft.VisualStudio.Debugger.Interop.Internal; +using Microsoft.VisualStudio.Debugger.TabularDataUIShared; +using Microsoft.VisualStudio.Debugger.TabularDataUIShared.WPF; +using Microsoft.VisualStudio.DebuggerVisualizers; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.OutOfProcessVisualizers.CommonControls.TippingService; +using Microsoft.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Utilities; +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Markup; +using System.Windows.Media; + +#nullable enable +namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer; + +public class IEnumerableVisualizerControl : + UserControl, + IViewHelper, + IVsWindowSearch, + IDisposable, + IComponentConnector, + IStyleConnector +{ + private + #nullable disable + IEnumerableViewModel m_viewModel; + private readonly IVsWindowSearchHost m_windowSearchHost; + private readonly Guid m_searchCategory = new Guid("BA594F87-ABA8-4847-A2FA-5C16C2DB51F3"); + private const string IEnumerableVisualizerOpenedSettingsStorePath = "IsUserAwareOfIEnumerableVisualizer"; + private CancellationTokenSource m_hoverCancellationTokenSource = new CancellationTokenSource(); + private readonly Guid m_guidCSharpLanguage = new Guid("{3f5162f8-07c6-11d3-9053-00c04fa302a1}"); + private readonly Guid m_guidFSharpLanguage = new Guid("{ab4f38c9-b6e6-43ba-be3b-58080b2ccce3}"); + private readonly Guid m_guidVBLanguage = new Guid("{3a12d0b8-c26c-11d0-b442-00a0244a1dd2}"); + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + internal Border searchControlContainer; + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + internal Label labelTable; + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + internal TableComboBox comboBoxTable; + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + internal ExportButton exportButton; + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + internal SortableDataGrid emptyDataGridView; + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + internal SortableDataGrid dataGridView; + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + internal ProgressBar progressBar; + private bool _contentLoaded; + + public required IVsDebugger Debugger { get; init; } + + public required bool AreDataTipsEnabled { get; init; } + + public IEnumerableVisualizerControl() + { + ThreadHelper.ThrowIfNotOnUIThread(".ctor"); + this.InitializeComponent(); + this.comboBoxTable.SelectionChanged += new SelectionChangedEventHandler(this.ComboBoxTable_SelectionChanged); + this.DataContextChanged += new DependencyPropertyChangedEventHandler(this.OnDataContextChanged); + this.dataGridView.SelectedCellsChanged += new SelectedCellsChangedEventHandler(this.DataGridView_SelectedCellsChanged); + if (ServiceProvider.GlobalProvider.GetService(typeof (SVsWindowSearchHostFactory)) is IVsWindowSearchHostFactory service) + { + this.m_windowSearchHost = service.CreateWindowSearchHost((object) this.searchControlContainer, (IDropTarget) null); + this.m_windowSearchHost.SetupSearch((IVsWindowSearch) this); + SearchControl descendant = ExtensionMethods.FindDescendant((DependencyObject) this.searchControlContainer); + ((Control) descendant).TabIndex = 0; + ((UIElement) descendant).Focus(); + } + VisualizersTippingService.Instance?.RecordVisualizerOpened("IsUserAwareOfIEnumerableVisualizer"); + } + + private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + try + { + this.m_viewModel = (IEnumerableViewModel) e.NewValue; + this.comboBoxTable.TabularDataViewModel = (TabularDataViewModel) this.m_viewModel; + this.dataGridView.TabularDataViewModel = (TabularDataViewModel) this.m_viewModel; + this.emptyDataGridView.TabularDataViewModel = (TabularDataViewModel) this.m_viewModel; + this.dataGridView.SelectCellByIndex(0, 0, false); + this.m_viewModel.RefreshSelectedRowsStatusBarText(); + this.m_viewModel.ColumnsUpdateAttempted += new EventHandler(this.ColumnsUpdateAttempted); + this.m_viewModel.ContinuePausedOperation += new EventHandler(this.ResumeCancellableAsyncOperation); + this.m_viewModel.AsyncUserOperationStateChanged += new EventHandler(this.OnAsyncUserOperationStateChanged); + this.m_viewModel.SearchStarted += new EventHandler(this.OnSearchStarted); + this.m_viewModel.ShowErrorMessage += new EventHandler(this.ShowErrorMessageBox); + this.m_viewModel.UnderlyingDataChanged += new EventHandler(this.OnUnderlyingDataChanged); + if (this.m_viewModel.FatalExceptionCaught) + return; + VsTaskLibraryHelper.FileAndForget(this.m_viewModel.UINormalPriorityJTF.RunAsync((Func) (async () => await this.m_viewModel.StartFetchingRemainingRowsAsync(this.m_viewModel.CancellableAsyncOperationToken))), this.m_viewModel.VisualizerTelemetryPrefix + TelemetryConstants.FetchingRemainingRowsFileAndForgetEvent, (string) null, (Func) null); + } + catch (Exception ex) + { + if (!((sender is IEnumerableVisualizerControl visualizerControl ? visualizerControl.DataContext : (object) null) is TabularDataViewModel dataContext)) + return; + dataContext.HandleFatalException(ex.GetType().FullName, ex.Message, ex.StackTrace); + TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(dataContext, ex, nameof (OnDataContextChanged)); + } + } + + private void ResumeCancellableAsyncOperation(object sender, PausedOperationInfo operationInfo) + { + try + { + ThreadHelper.ThrowIfNotOnUIThread(nameof (ResumeCancellableAsyncOperation)); + if (!this.m_viewModel.IsCancellableAsyncOperationInProgress) + return; + switch (operationInfo.OperationType) + { + case AsyncOperationsTracker.AsyncOperationType.SORTING: + this.dataGridView.OnSorting((object) this, new DataGridSortingEventArgs(this.dataGridView.Columns[this.dataGridView.SortingColumnIndex])); + break; + case AsyncOperationsTracker.AsyncOperationType.EXPORTING: + this.exportButton.SelectedCommand.Command.Execute((object) this); + break; + case AsyncOperationsTracker.AsyncOperationType.FILTERING: + this.m_windowSearchHost.SearchAsync(this.m_viewModel.LastSearchQuery); + break; + case AsyncOperationsTracker.AsyncOperationType.COPYING: + this.dataGridView.ContextMenu_CopyAll((object) null, (EventArgs) null); + break; + case AsyncOperationsTracker.AsyncOperationType.COPYING_SELECTION: + this.dataGridView.ContextMenu_Copy((object) null, (EventArgs) null); + break; + } + } + catch (Exception ex) + { + if (!(sender is TabularDataViewModel viewModel)) + return; + TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(viewModel, ex, nameof (ResumeCancellableAsyncOperation)); + } + } + + private void OnSearchStarted(object sender, SearchStartedEventArgs args) + { + try + { + this.PerformFilterOperation(args.SearchText); + } + catch (Exception ex) + { + if (!(sender is TabularDataViewModel viewModel)) + return; + TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(viewModel, ex, nameof (OnSearchStarted)); + } + } + + private void PerformFilterOperation(string filter) + { + this.dataGridView.Filter(filter); + this.m_viewModel.RefreshSelectedRowsStatusBarText(); + } + + [SuppressMessage("Reliability", "VSSDK007:ThreadHelper.JoinableTaskFactory.RunAsync", Justification = "This is intentional. We are using FileAndForget to catch any issues that might appear down the road.")] + private void DataGridCell_MouseEnter(object sender, MouseEventArgs e) + { + ThreadHelper.ThrowIfNotOnUIThread(nameof (DataGridCell_MouseEnter)); + if (!this.AreDataTipsEnabled) + return; + DataGridCell dataGridCell = sender as DataGridCell; + if (dataGridCell == null) + return; + IDebuggerInternal debuggerInternal = this.Debugger as IDebuggerInternal; + if (debuggerInternal == null) + return; + IEnumerableColumnHeaderViewModel colHeaderViewModel = dataGridCell.Column.Header as IEnumerableColumnHeaderViewModel; + if (colHeaderViewModel == null) + return; + CancellationToken cancellationToken = this.m_hoverCancellationTokenSource.Token; + VsTaskLibraryHelper.FileAndForget(ThreadHelper.JoinableTaskFactory.RunAsync((Func) (async () => + { + await Task.Delay(350, cancellationToken); + if (!dataGridCell.IsMouseOver || !Utils.TryGetVisualizerExpressionContext(this.Debugger, out IDebugExpressionContext2 _) || !Utils.TryGetLanguageId(this.Debugger, out Guid _)) + return; + Window window = Window.GetWindow((DependencyObject) this); + Point position = e.GetPosition((IInputElement) window); + Point devicePoint = DpiAwareness.LogicalToDevicePoint((Visual) window, position); + POINT point = new POINT() + { + x = (int) devicePoint.X, + y = (int) devicePoint.Y + }; + Rect rect1 = dataGridCell.TransformToAncestor((Visual) window).TransformBounds(new Rect(0.0, 0.0, dataGridCell.ActualWidth, dataGridCell.ActualHeight)); + Rect deviceRect = DpiAwareness.LogicalToDeviceRect((Visual) window, rect1); + RECT rect2 = new RECT() + { + left = (int) deviceRect.Left, + top = (int) deviceRect.Top, + bottom = (int) deviceRect.Bottom, + right = (int) deviceRect.Right + }; + DataTipIdentity[] dataTipIdentityArray = new DataTipIdentity[1]; + dataTipIdentityArray[0].moniker = "IEnumerableVisualizerDataTip"; + dataTipIdentityArray[0].position = 1U; + IntPtr handle = new WindowInteropHelper(window).Handle; + string str = $"Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizerSource.GetObjectAt({this.m_viewModel.SelectedTableIndex},{((RowViewModel) dataGridCell.DataContext).RowIndex},{colHeaderViewModel.Index})"; + ErrorHandler.ThrowOnFailure(debuggerInternal.DataTipManager.ShowDataTipAt(point, 100, rect2, (ulong) (long) handle, str, dataTipIdentityArray)); + })), "VS/Diagnostics/Debugger/IEnumerableVisualizer/FileAndForget/ShowDataTip", (string) null, (Func) (ex => !(ex is OperationCanceledException))); + } + + private void DataGridCell_MouseLeave(object sender, MouseEventArgs e) + { + ThreadHelper.ThrowIfNotOnUIThread(nameof (DataGridCell_MouseLeave)); + this.m_hoverCancellationTokenSource.Cancel(); + this.m_hoverCancellationTokenSource = new CancellationTokenSource(); + } + + private void OnUnderlyingDataChanged(object sender, EventArgs e) + { + ThreadHelper.ThrowIfNotOnUIThread(nameof (OnUnderlyingDataChanged)); + this.RefreshTableSelection(this.comboBoxTable.SelectedItem, true); + if (((FrameworkElement) ExtensionMethods.FindDescendant((DependencyObject) this.searchControlContainer)).DataContext is IVsUIDataSource dataContext) + dataContext.SetValue(SearchControlDataSource.PropertyNames.SearchText, (IVsUIObject) BuiltInPropertyValue.Create(string.Empty)); + VsTaskLibraryHelper.FileAndForget(this.m_viewModel.UINormalPriorityJTF.RunAsync((Func) (async () => await this.m_viewModel.StartFetchingRemainingRowsAsync(this.m_viewModel.CancellableAsyncOperationToken))), this.m_viewModel.VisualizerTelemetryPrefix + TelemetryConstants.FetchingRemainingRowsFileAndForgetEvent, (string) null, (Func) null); + } + + private void ComboBoxTable_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + ThreadHelper.ThrowIfNotOnUIThread(nameof (ComboBoxTable_SelectionChanged)); + this.RefreshTableSelection(this.comboBoxTable.SelectedItem); + } + + private void RefreshTableSelection(object comboBoxSelectedItem, bool shouldRefreshInitialModels = false) + { + if (!this.comboBoxTable.IsEnabled) + return; + if (!(comboBoxSelectedItem is TableViewModel table)) + return; + try + { + this.UpdateColumns(table); + if (shouldRefreshInitialModels) + table.LoadInitialModels(); + this.dataGridView.UnselectAll(); + if (this.dataGridView.Items.Count > 0) + { + this.dataGridView.SelectCellByIndex(0, 0, false); + this.dataGridView.ScrollIntoView(this.dataGridView.Items[0]); + this.m_viewModel.RefreshSelectedRowsStatusBarText(); + } + this.PerformFilterOperation(this.m_viewModel.LastSearchQuery?.SearchString); + } + catch (Exception ex) + { + table.LoadRows(); + if (this.m_viewModel == null) + return; + TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry((TabularDataViewModel) this.m_viewModel, ex, nameof (RefreshTableSelection)); + } + } + + private void UpdateColumns(TableViewModel table) + { + this.dataGridView.ResetSortOrder(); + this.dataGridView.Columns.Clear(); + this.emptyDataGridView.Columns.Clear(); + if (table == null) + return; + table.LoadRows(); + SortableDataGrid sortableDataGrid = table.IsEmpty ? this.emptyDataGridView : this.dataGridView; + Style resource1 = sortableDataGrid.FindResource((object) "CellTextBlockStyle") as Style; + Style resource2 = sortableDataGrid.FindResource((object) "EditableCellTextBoxStyle") as Style; + int index = 0; + foreach (ColumnInfo column in (IEnumerable) table.Columns) + { + IEnumerableColumnHeaderViewModel columnHeaderViewModel = new IEnumerableColumnHeaderViewModel(column, table.Index, this.m_viewModel); + ObservableCollection columns = sortableDataGrid.Columns; + SortableDataGridColumn sortableDataGridColumn = new SortableDataGridColumn(); + sortableDataGridColumn.Binding = (BindingBase) new Binding(RowViewModel.IndexToBindingPath(index)) + { + Mode = BindingMode.TwoWay, + ValidatesOnNotifyDataErrors = true, + UpdateSourceTrigger = UpdateSourceTrigger.LostFocus + }; + sortableDataGridColumn.Header = (object) columnHeaderViewModel; + sortableDataGridColumn.IsReadOnly = column.ReadOnly || table.IsColumnLocked(column); + sortableDataGridColumn.ElementStyle = resource1; + sortableDataGridColumn.Width = new DataGridLength(1.0, DataGridLengthUnitType.Auto); + sortableDataGridColumn.MaxWidth = 200.0; + sortableDataGridColumn.EditingElementStyle = resource2; + sortableDataGridColumn.Visibility = columnHeaderViewModel.IsVisible ? Visibility.Visible : Visibility.Collapsed; + sortableDataGridColumn.CanSort = this.m_viewModel.IsSortingEnabled; + sortableDataGridColumn.IsSorted = false; + columns.Add((DataGridColumn) sortableDataGridColumn); + ++index; + } + } + + private void OnAsyncUserOperationStateChanged(object sender, AsyncOperationStatus _) + { + try + { + foreach (SortableDataGridColumn sortableDataGridColumn in this.dataGridView.Columns.Select((Func) (c => c as SortableDataGridColumn))) + sortableDataGridColumn.CanSort = this.m_viewModel.IsSortingEnabled; + } + catch (Exception ex) + { + if (!(sender is TabularDataViewModel viewModel)) + return; + TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(viewModel, ex, nameof (OnAsyncUserOperationStateChanged)); + } + } + + private void ColumnsUpdateAttempted(object sender, ContextMenuCommandArgs args) + { + try + { + if (args.WasSuccessful) + { + this.UpdateColumns(this.m_viewModel.SelectedTable); + } + else + { + string caption = string.Format((IFormatProvider) CultureInfo.InvariantCulture, Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_UpdateErrorCaption); + int num = (int) MessageBox.Show(args.ErrorMessage, caption, MessageBoxButton.OK, MessageBoxImage.Exclamation); + } + } + catch (Exception ex) + { + (this.comboBoxTable.SelectedItem as TableViewModel).LoadRows(); + if (!(sender is TabularDataViewModel viewModel)) + return; + TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(viewModel, ex, nameof (ColumnsUpdateAttempted)); + } + } + + private void ShowErrorMessageBox(object sender, string message) + { + string caption = string.Format((IFormatProvider) CultureInfo.InvariantCulture, Microsoft.VisualStudio.Debugger.TabularDataUIShared.Resources.G_FailedSort); + int num = (int) MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Exclamation); + } + + private void DataGridView_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e) + { + try + { + if (!(sender is DataGrid dataGrid)) + return; + this.m_viewModel.SelectedRows = dataGrid.SelectedCells.Select((Func) (cell => (RowViewModel) cell.Item)).Distinct(); + this.m_viewModel.RefreshSelectedRowsStatusBarText(); + } + catch (Exception ex) + { + if (!((sender is SortableDataGrid sortableDataGrid ? sortableDataGrid.DataContext : (object) null) is TabularDataViewModel dataContext)) + return; + TelemetryUtilities.LogVisualizerWpfHandlerErrorTelemetry(dataContext, ex, nameof (DataGridView_SelectedCellsChanged)); + } + } + + public bool PromptToKeepChanges(string message) + { + string updateErrorCaption = Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_UpdateErrorCaption; + return MessageBox.Show(message, updateErrorCaption, MessageBoxButton.OKCancel, MessageBoxImage.Exclamation) == MessageBoxResult.OK; + } + + public IVsSearchTask CreateSearch( + uint dwCookie, + IVsSearchQuery pSearchQuery, + IVsSearchCallback pSearchCallback) + { + return (IVsSearchTask) new SortableDataGridSearchTask((TabularDataViewModel) this.m_viewModel, dwCookie, pSearchQuery, pSearchCallback); + } + + public void ClearSearch() + { + if (this.m_viewModel.IsCancellableAsyncOperationInProgress && this.m_viewModel.AsyncOperationsTracker.LastUserAsyncOperation == AsyncOperationsTracker.AsyncOperationType.FILTERING) + this.m_viewModel.CancelButtonCommand.Execute((object) this); + this.m_viewModel.LastSearchQuery = (IVsSearchQuery) null; + this.PerformFilterOperation((string) null); + } + + public void ProvideSearchSettings(IVsUIDataSource pSearchSettings) + { + SearchSettingsDataSource settingsDataSource = pSearchSettings as SearchSettingsDataSource; + settingsDataSource.SearchStartType = (VSSEARCHSTARTTYPE) 2; + settingsDataSource.SearchProgressType = (VSSEARCHPROGRESSTYPE) 2; + settingsDataSource.ControlMaxWidth = uint.MaxValue; + string gFilter; + string str1 = gFilter = Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_Filter; + settingsDataSource.SearchStartTooltip = gFilter; + string str2; + string str3 = str2 = str1; + settingsDataSource.SearchTooltip = str2; + settingsDataSource.SearchWatermark = str3; + settingsDataSource.SearchClearTooltip = Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_ClearFilter; + settingsDataSource.SearchStopTooltip = Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.Resources.G_StopFilter; + settingsDataSource.UseDefaultThemeColors = false; + } + + public bool OnNavigationKeyDown(uint dwNavigationKey, uint dwModifiers) => false; + + public bool SearchEnabled => this.m_viewModel.IsSearchControlEnabled; + + public Guid Category => this.m_searchCategory; + + public IVsEnumWindowSearchFilters SearchFiltersEnum => (IVsEnumWindowSearchFilters) null; + + public IVsEnumWindowSearchOptions SearchOptionsEnum => (IVsEnumWindowSearchOptions) null; + + public void Dispose() + { + if (this.m_viewModel == null) + throw new InvalidOperationException("The IEnumerable Visualizer's view model should never be null."); + this.comboBoxTable.SelectionChanged -= new SelectionChangedEventHandler(this.ComboBoxTable_SelectionChanged); + this.DataContextChanged -= new DependencyPropertyChangedEventHandler(this.OnDataContextChanged); + this.dataGridView.SelectedCellsChanged -= new SelectedCellsChangedEventHandler(this.DataGridView_SelectedCellsChanged); + this.m_viewModel.ColumnsUpdateAttempted -= new EventHandler(this.ColumnsUpdateAttempted); + this.m_viewModel.ContinuePausedOperation -= new EventHandler(this.ResumeCancellableAsyncOperation); + this.m_viewModel.AsyncUserOperationStateChanged -= new EventHandler(this.OnAsyncUserOperationStateChanged); + this.m_viewModel.ShowErrorMessage -= new EventHandler(this.ShowErrorMessageBox); + this.m_viewModel.SearchStarted -= new EventHandler(this.OnSearchStarted); + this.m_viewModel.UnderlyingDataChanged -= new EventHandler(this.OnUnderlyingDataChanged); + this.m_viewModel.WindowClosing(); + IEnumerableCopilotSessionManager.Instance.ClearAllActiveSessions(); + foreach (BaseTableViewModel table in (Collection) this.m_viewModel.Tables) + { + table.TableRows?.Clear(); + table.ClearCache(); + } + } + + [DebuggerNonUserCode] + [GeneratedCode("PresentationBuildTasks", "4.0.0.0")] + public void InitializeComponent() + { + if (this._contentLoaded) + return; + this._contentLoaded = true; + Application.LoadComponent((object) this, new Uri("/IEnumerableVisualizer.UI;V18.0.0.0;component/ienumerablevisualizercontrol.xaml", UriKind.Relative)); + } + + [DebuggerNonUserCode] + [GeneratedCode("PresentationBuildTasks", "4.0.0.0")] + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Delegate _CreateDelegate(Type delegateType, string handler) + { + return Delegate.CreateDelegate(delegateType, (object) this, handler); + } + + [DebuggerNonUserCode] + [GeneratedCode("PresentationBuildTasks", "4.0.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + void IComponentConnector.Connect(int connectionId, object target) + { + switch (connectionId) + { + case 2: + this.searchControlContainer = (Border) target; + break; + case 3: + this.labelTable = (Label) target; + break; + case 4: + this.comboBoxTable = (TableComboBox) target; + break; + case 5: + this.exportButton = (ExportButton) target; + break; + case 6: + this.emptyDataGridView = (SortableDataGrid) target; + break; + case 7: + this.dataGridView = (SortableDataGrid) target; + break; + case 8: + this.progressBar = (ProgressBar) target; + break; + default: + this._contentLoaded = true; + break; + } + } + + [DebuggerNonUserCode] + [GeneratedCode("PresentationBuildTasks", "4.0.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + void IStyleConnector.Connect(int connectionId, object target) + { + if (connectionId != 1) + return; + ((Style) target).Setters.Add((SetterBase) new EventSetter() + { + Event = UIElement.MouseEnterEvent, + Handler = (Delegate) new MouseEventHandler(this.DataGridCell_MouseEnter) + }); + ((Style) target).Setters.Add((SetterBase) new EventSetter() + { + Event = UIElement.MouseLeaveEvent, + Handler = (Delegate) new MouseEventHandler(this.DataGridCell_MouseLeave) + }); + } +} diff --git a/IEnumerableVisualizerProvider.cs b/IEnumerableVisualizerProvider.cs new file mode 100644 index 0000000..4291a43 --- /dev/null +++ b/IEnumerableVisualizerProvider.cs @@ -0,0 +1,72 @@ +// Decompiled with JetBrains decompiler +// Type: Microsoft.VisualStudio.DebuggerVisualizers.ExtensionProviders.IEnumerableVisualizerProvider +// Assembly: Microsoft.VisualStudio.DebuggerVisualizers.ExtensionProviders, Version=18.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a +// MVID: 98A3FADF-73D8-4308-9C72-EE68F73E4141 +// Assembly location: C:\Program Files\Microsoft Visual Studio\18\Professional\Common7\IDE\CommonExtensions\Platform\Debugger\Visualizers\Microsoft.VisualStudio.DebuggerVisualizers.ExtensionProviders.dll + +using Microsoft.Internal.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Debugger.IEnumerableVisualizer; +using Microsoft.VisualStudio.Debugger.TabularDataUIShared.WPF; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers; +using Microsoft.VisualStudio.Extensibility.VSSdkCompatibility; +using Microsoft.VisualStudio.RpcContracts.DebuggerVisualizers; +using Microsoft.VisualStudio.RpcContracts.RemoteUI; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; + +#nullable enable +namespace Microsoft.VisualStudio.DebuggerVisualizers.ExtensionProviders; + +[VisualStudioContribution] +internal class IEnumerableVisualizerProvider : DebuggerVisualizerProvider +{ + public IEnumerableVisualizerProvider( + VisualStudioExtensibility extensibility, + TraceSource traceSource) + { + } + + public virtual DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration + { + get + { + return new DebuggerVisualizerProviderConfiguration("%IEnumerableVisualizer.DisplayName%", "System.Collections.Generic.IEnumerable`1, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") + { + VisualizerObjectSourceType = new VisualizerObjectSourceType(typeof (IEnumerableVisualizerSource)), + Style = (VisualizerStyle) 1 + }; + } + } + + public virtual async Task CreateVisualizerAsync(VisualizerTarget visualizerTarget,CancellationToken cancellationToken) + { + IEnumerableViewModel viewModel = new IEnumerableViewModel(visualizerTarget.ObjectSource, visualizerTarget.OriginalVisualizedExpression, VsTaskLibraryHelper.WithPriority(ThreadHelper.JoinableTaskFactory, (VsTaskRunContext) 6)); + visualizerTarget.VisualizedExpressionChanged += new Func(((TabularDataViewModel) viewModel).VisualizerTarget_VisualizedExpressionChangedAsync); + visualizerTarget.Changed += new Func(((TabularDataViewModel) viewModel).VisualizerTarget_StateChangedAsync); + await viewModel.InitializeAsync(); + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(new CancellationToken()); + IVsDebugger debugger = await ServiceExtensions.GetServiceAsync(AsyncServiceProvider.GlobalProvider, cancellationToken); + bool flag = (await ServiceExtensions.GetServiceAsync(AsyncServiceProvider.GlobalProvider, cancellationToken)).IsFeatureEnabled("Debugger.EnableVisualizerDataTips", true); + IEnumerableVisualizerControl visualizerControl1 = new IEnumerableVisualizerControl(); + visualizerControl1.DataContext = (object) viewModel; + visualizerControl1.Debugger = debugger; + visualizerControl1.AreDataTipsEnabled = flag; + IEnumerableVisualizerControl visualizerControl2 = visualizerControl1; + viewModel.ViewHelper = (IViewHelper) visualizerControl2; + IRemoteUserControl visualizerAsync = (IRemoteUserControl) new WpfControlWrapper((FrameworkElement) visualizerControl2); + viewModel = (IEnumerableViewModel) null; + debugger = (IVsDebugger) null; + return visualizerAsync; + } + + protected virtual Task InitializeAsync(CancellationToken cancellationToken) + { + return ((ExtensionPart) this).InitializeAsync(cancellationToken); + } +} diff --git a/IEnumerableVisualizerSource.cs b/IEnumerableVisualizerSource.cs new file mode 100644 index 0000000..9d037f9 --- /dev/null +++ b/IEnumerableVisualizerSource.cs @@ -0,0 +1,66 @@ +// Decompiled with JetBrains decompiler +// Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.IEnumerableVisualizerSource +// Assembly: IEnumerableVisualizer.DebuggeeSide, Version=18.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a +// MVID: CFD2027C-E4DB-4399-B4DA-641D6C32082D +// Assembly location: C:\Program Files\Microsoft Visual Studio\18\Professional\Common7\IDE\CommonExtensions\Platform\Debugger\Visualizers\net2.0\IEnumerableVisualizer.DebuggeeSide.dll + +using Microsoft.VisualStudio.DebuggerVisualizers; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer; + +public class IEnumerableVisualizerSource : VisualizerObjectSource +{ + private static readonly Dictionary DataObjectHandlerDictionary = new Dictionary(); + private static IEnumerableObjectHandler CurrentObjectHandler = (IEnumerableObjectHandler)null; + + public static object GetObjectAt(int tableIndex, int rowIndex, int columnIndex) + { + return CurrentObjectHandler?.GetObjectAt(tableIndex, rowIndex, columnIndex) ?? (object)null; + } + + public virtual void TransferData(object obj, Stream fromVisualizer, Stream toVisualizer) + { + IEnumerableObjectHandler dataObjectHandler; + if (DataObjectHandlerDictionary.ContainsKey(obj)) + { + dataObjectHandler = DataObjectHandlerDictionary[obj]; + dataObjectHandler.Refresh(); + } + else + { + dataObjectHandler = CreateDataObjectHandler(obj); + DataObjectHandlerDictionary[obj] = dataObjectHandler; + } + CurrentObjectHandler = dataObjectHandler; + fromVisualizer.Seek(0L, SeekOrigin.Begin); + IDeserializableObject deserializableObject = VisualizerObjectSource.GetDeserializableObject(fromVisualizer); + Type type = (Type)null; + if (!deserializableObject.IsBinaryFormat) + { + string stringPropertyValue = deserializableObject.GetJsonStringPropertyValue("TypeName"); + if (!Utils.SupportedCommands.TryGetValue(stringPropertyValue, out type)) + throw new ArgumentException($"Unknown Command: {stringPropertyValue}. Cannot send command to debuggee-side visualizer."); + } + ICommunicatorCommand communicatorCommand = (ICommunicatorCommand)deserializableObject.ToObject(type); + if (communicatorCommand.TypeName.Equals("GetTableListCommand", StringComparison.InvariantCulture)) + dataObjectHandler.InitializeDataSource(); + SendResult(communicatorCommand.HandleCommand(dataObjectHandler), toVisualizer); + } + + private IEnumerableObjectHandler CreateDataObjectHandler(object obj) + { + return new IEnumerableObjectHandler(obj); + } + + public virtual object CreateReplacementObject(object obj, Stream serializedObject) => throw new NotSupportedException(); + + public virtual void GetData(object obj, Stream dataStream) => throw new NotSupportedException(); + + private void SendResult(CommunicatorResult result, Stream toVisualizer) + { + VisualizerObjectSource.Serialize(toVisualizer, (object)result); + } +} diff --git a/LatestDataAvailable.cs b/LatestDataAvailable.cs new file mode 100644 index 0000000..351e0ea --- /dev/null +++ b/LatestDataAvailable.cs @@ -0,0 +1,18 @@ +// Decompiled with JetBrains decompiler +// Type: Microsoft.VisualStudio.Debugger.IEnumerableVisualizer.LatestDataAvailable +// 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 + +#nullable disable +namespace Microsoft.VisualStudio.Debugger.IEnumerableVisualizer; + +public class LatestDataAvailable +{ + public int AvailableDataCount { get; } + + public LatestDataAvailable(int availableDataCount) + { + this.AvailableDataCount = availableDataCount; + } +}