<commit_before>﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.Common.Core;
using Microsoft.R.Debugger;
using Microsoft.R.Editor.Data;
using Microsoft.R.Host.Client;
using Microsoft.R.Host.Client.Session;
using Microsoft.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.R.Package.DataInspect.Office;
using Microsoft.VisualStudio.R.Package.Shell;
using static System.FormattableString;

namespace Microsoft.VisualStudio.R.Package.DataInspect {
    /// <summary>
    /// Model for variable tree grid, that provides UI customization of <see cref="DebugEvaluationResult"/>
    /// </summary>
    public sealed class VariableViewModel : RSessionDataObject, IIndexedItem {
        private readonly IObjectDetailsViewerAggregator _aggregator;
        private IObjectDetailsViewer _detailsViewer;
        private string _title;
        private bool _deleted;

        public VariableViewModel() { Index = -1; }

        /// <summary>
        /// Create new instance of <see cref="VariableViewModel"/>
        /// </summary>
        /// <param name="evaluation">R session's evaluation result</param>
        /// <param name="truncateChildren">true to truncate children returned by GetChildrenAsync</param>
        public VariableViewModel(DebugEvaluationResult evaluation, IObjectDetailsViewerAggregator aggregator, int index = -1, int? maxChildrenCount = null) :
            base(evaluation, maxChildrenCount) {
            _aggregator = aggregator;

            Index = index;
            var result = DebugEvaluation as DebugValueEvaluationResult;
            if (result != null) {
                SetViewButtonStatus(result);
            }
        }

        private void SetViewButtonStatus(DebugValueEvaluationResult result) {
            _detailsViewer = _aggregator.GetViewer(result);
            _title = result.Name;

            CanShowDetail = _detailsViewer != null;
            if (CanShowDetail) {
                ShowDetailCommand = new DelegateCommand(o => _detailsViewer.ViewAsync(result.Expression, _title).DoNotWait(), o => CanShowDetail);
                ShowDetailCommandTooltip = Resources.ShowDetailCommandTooltip;
            }

            var tableCaps = (ViewerCapabilities.Table | ViewerCapabilities.List);
            CanShowOpenCsv = CanShowDetail && (_detailsViewer.Capabilities & tableCaps) != 0 && result.Length > 0;
            if (CanShowOpenCsv) {
                OpenInCsvAppCommand = new DelegateCommand(OpenInCsvApp, o => CanShowOpenCsv);
                OpenCsvAppCommandTooltip = Resources.OpenCsvAppCommandTooltip;
            }
        }

        #region Ellipsis 

        private static Lazy<VariableViewModel> _ellipsis = Lazy.Create(() => {
            var instance = new VariableViewModel();
            instance.Name = string.Empty;
            instance.Value = Resources.VariableExplorer_Truncated;
            instance.HasChildren = false;
            return instance;
        });

        public static VariableViewModel Ellipsis {
            get { return _ellipsis.Value; }
        }

        #endregion

        private const string Repr = "rtvs:::make_repr_str(max_length = 100)";

        protected override async Task<IReadOnlyList<IRSessionDataObject>> GetChildrenAsyncInternal() {
            List<IRSessionDataObject> result = null;

            var valueEvaluation = DebugEvaluation as DebugValueEvaluationResult;
            if (valueEvaluation == null) {
                Debug.Assert(false, $"{nameof(VariableViewModel)} result type is not {typeof(DebugValueEvaluationResult)}");
                return result;
            }

            if (valueEvaluation.HasChildren) {
                await TaskUtilities.SwitchToBackgroundThread();

                const DebugEvaluationResultFields fields =
                    DebugEvaluationResultFields.Expression |
                    DebugEvaluationResultFields.Kind |
                    DebugEvaluationResultFields.TypeName |
                    DebugEvaluationResultFields.Classes |
                    DebugEvaluationResultFields.Length |
                    DebugEvaluationResultFields.SlotCount |
                    DebugEvaluationResultFields.AttrCount |
                    DebugEvaluationResultFields.Dim |
                    DebugEvaluationResultFields.Flags;
                IReadOnlyList<DebugEvaluationResult> children = await valueEvaluation.GetChildrenAsync(fields, MaxChildrenCount, Repr);

                result = new List<IRSessionDataObject>();
                var aggregator = VsAppShell.Current.ExportProvider.GetExportedValue<IObjectDetailsViewerAggregator>();
                for (int i = 0; i < children.Count; i++) {
                    result.Add(new VariableViewModel(children[i], aggregator, index: i, maxChildrenCount: GetMaxChildrenCount(children[i])));
                }

                // return children can be less than value's length in some cases e.g. missing parameter
                if (valueEvaluation.Length > result.Count
                    && (valueEvaluation.Length > MaxChildrenCount)) {
                    result.Add(VariableViewModel.Ellipsis); // insert dummy child to indicate truncation in UI
                }
            }

            return result;
        }

        #region IIndexedItem support

        /// <summary>
        /// Index returned from the evaluation provider. 
        /// DebugEvaluationResult returns in ascending order
        /// </summary>
        public int Index { get; }

        #endregion

        #region Variable Grid command
        public bool CanShowDetail { get; private set; }

        public ICommand ShowDetailCommand { get; private set; }
        public string ShowDetailCommandTooltip { get; private set; }

        public bool CanShowOpenCsv { get; private set; }

        public ICommand OpenInCsvAppCommand { get; private set; }
        public string OpenCsvAppCommandTooltip { get; private set; }

        private void OpenInCsvApp(object parameter) {
            CsvAppFileIO.OpenDataCsvApp(DebugEvaluation).DoNotWait();
        }

        /// <summary>
        /// Deletes variable represented by this mode
        /// </summary>
        public Task DeleteAsync() {
            if (!_deleted) {
                _deleted = true;
                var sessionProvider = VsAppShell.Current.ExportProvider.GetExportedValue<IRSessionProvider>();
                var session = sessionProvider.GetInteractiveWindowRSession();
                try {
                    return session.EvaluateAsync(Invariant($"rm({Name})"), REvaluationKind.Mutating);
                } catch (RException ex) {
                    VsAppShell.Current.ShowErrorMessage(
                        string.Format(CultureInfo.InvariantCulture, Resources.Error_UnableToDeleteVariable, ex.Message));
                } catch (MessageTransportException) { }
            }
            return Task.CompletedTask;
        }
        #endregion
    }
}
<commit_msg>#1726 Xaml binding error in variable explorer<commit_after>﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.Common.Core;
using Microsoft.R.Debugger;
using Microsoft.R.Editor.Data;
using Microsoft.R.Host.Client;
using Microsoft.R.Host.Client.Session;
using Microsoft.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.R.Package.DataInspect.Office;
using Microsoft.VisualStudio.R.Package.Shell;
using static System.FormattableString;

namespace Microsoft.VisualStudio.R.Package.DataInspect {
    /// <summary>
    /// Model for variable tree grid, that provides UI customization of <see cref="DebugEvaluationResult"/>
    /// </summary>
    public sealed class VariableViewModel : RSessionDataObject, IIndexedItem {
        private readonly IObjectDetailsViewerAggregator _aggregator;
        private IObjectDetailsViewer _detailsViewer;
        private string _title;
        private bool _deleted;

        public VariableViewModel() { Index = -1; }

        /// <summary>
        /// Create new instance of <see cref="VariableViewModel"/>
        /// </summary>
        /// <param name="evaluation">R session's evaluation result</param>
        /// <param name="truncateChildren">true to truncate children returned by GetChildrenAsync</param>
        public VariableViewModel(DebugEvaluationResult evaluation, IObjectDetailsViewerAggregator aggregator, int index = -1, int? maxChildrenCount = null) :
            base(evaluation, maxChildrenCount) {
            _aggregator = aggregator;

            Index = index;
            var result = DebugEvaluation as DebugValueEvaluationResult;
            if (result != null) {
                SetViewButtonStatus(result);
            }
        }

        private void SetViewButtonStatus(DebugValueEvaluationResult result) {
            _detailsViewer = _aggregator.GetViewer(result);
            _title = result.Name;

            CanShowDetail = _detailsViewer != null;
            if (CanShowDetail) {
                ShowDetailCommand = new DelegateCommand(o => _detailsViewer.ViewAsync(result.Expression, _title).DoNotWait(), o => CanShowDetail);
                ShowDetailCommandTooltip = Resources.ShowDetailCommandTooltip;
            }

            var tableCaps = (ViewerCapabilities.Table | ViewerCapabilities.List);
            CanShowOpenCsv = CanShowDetail && (_detailsViewer.Capabilities & tableCaps) != 0 && result.Length > 0;
            if (CanShowOpenCsv) {
                OpenInCsvAppCommand = new DelegateCommand(OpenInCsvApp, o => CanShowOpenCsv);
                OpenInCsvAppCommandTooltip = Resources.OpenCsvAppCommandTooltip;
            }
        }

        #region Ellipsis 

        private static Lazy<VariableViewModel> _ellipsis = Lazy.Create(() => {
            var instance = new VariableViewModel();
            instance.Name = string.Empty;
            instance.Value = Resources.VariableExplorer_Truncated;
            instance.HasChildren = false;
            return instance;
        });

        public static VariableViewModel Ellipsis {
            get { return _ellipsis.Value; }
        }

        #endregion

        private const string Repr = "rtvs:::make_repr_str(max_length = 100)";

        protected override async Task<IReadOnlyList<IRSessionDataObject>> GetChildrenAsyncInternal() {
            List<IRSessionDataObject> result = null;

            var valueEvaluation = DebugEvaluation as DebugValueEvaluationResult;
            if (valueEvaluation == null) {
                Debug.Assert(false, $"{nameof(VariableViewModel)} result type is not {typeof(DebugValueEvaluationResult)}");
                return result;
            }

            if (valueEvaluation.HasChildren) {
                await TaskUtilities.SwitchToBackgroundThread();

                const DebugEvaluationResultFields fields =
                    DebugEvaluationResultFields.Expression |
                    DebugEvaluationResultFields.Kind |
                    DebugEvaluationResultFields.TypeName |
                    DebugEvaluationResultFields.Classes |
                    DebugEvaluationResultFields.Length |
                    DebugEvaluationResultFields.SlotCount |
                    DebugEvaluationResultFields.AttrCount |
                    DebugEvaluationResultFields.Dim |
                    DebugEvaluationResultFields.Flags;
                IReadOnlyList<DebugEvaluationResult> children = await valueEvaluation.GetChildrenAsync(fields, MaxChildrenCount, Repr);

                result = new List<IRSessionDataObject>();
                var aggregator = VsAppShell.Current.ExportProvider.GetExportedValue<IObjectDetailsViewerAggregator>();
                for (int i = 0; i < children.Count; i++) {
                    result.Add(new VariableViewModel(children[i], aggregator, index: i, maxChildrenCount: GetMaxChildrenCount(children[i])));
                }

                // return children can be less than value's length in some cases e.g. missing parameter
                if (valueEvaluation.Length > result.Count
                    && (valueEvaluation.Length > MaxChildrenCount)) {
                    result.Add(VariableViewModel.Ellipsis); // insert dummy child to indicate truncation in UI
                }
            }

            return result;
        }

        #region IIndexedItem support

        /// <summary>
        /// Index returned from the evaluation provider. 
        /// DebugEvaluationResult returns in ascending order
        /// </summary>
        public int Index { get; }

        #endregion

        #region Variable Grid command
        public bool CanShowDetail { get; private set; }

        public ICommand ShowDetailCommand { get; private set; }
        public string ShowDetailCommandTooltip { get; private set; }

        public bool CanShowOpenCsv { get; private set; }

        public ICommand OpenInCsvAppCommand { get; private set; }
        public string OpenInCsvAppCommandTooltip { get; private set; }

        private void OpenInCsvApp(object parameter) {
            CsvAppFileIO.OpenDataCsvApp(DebugEvaluation).DoNotWait();
        }

        /// <summary>
        /// Deletes variable represented by this mode
        /// </summary>
        public Task DeleteAsync() {
            if (!_deleted) {
                _deleted = true;
                var sessionProvider = VsAppShell.Current.ExportProvider.GetExportedValue<IRSessionProvider>();
                var session = sessionProvider.GetInteractiveWindowRSession();
                try {
                    return session.EvaluateAsync(Invariant($"rm({Name})"), REvaluationKind.Mutating);
                } catch (RException ex) {
                    VsAppShell.Current.ShowErrorMessage(
                        string.Format(CultureInfo.InvariantCulture, Resources.Error_UnableToDeleteVariable, ex.Message));
                } catch (MessageTransportException) { }
            }
            return Task.CompletedTask;
        }
        #endregion
    }
}

{'fraction_non_alphanumeric': 0.08056708160442601, 'fraction_numerical': 0.0013831258644536654, 'mean_word_length': 2.311426608655828, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7052776', 'n_tokens_mistral': 3607, 'n_tokens_neox': 3371, 'n_words': 834}