<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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Common.Core;
using Microsoft.Common.Core.Services;
using Microsoft.Common.Core.Shell;
using Microsoft.Common.Core.Threading;
using Microsoft.R.Components.InteractiveWorkflow;
using Microsoft.R.Components.Settings;
using Microsoft.R.Host.Client;
using Microsoft.R.Host.Client.Host;
using static System.FormattableString;

namespace Microsoft.R.Editor.Functions {
    /// <summary>
    /// Represents R session(s) for intellisense and signatures. The interactive session is used
    /// to determine list of loaded packages and separate session is used to fetch RD data 
    /// for function descriptions and signatures.
    /// </summary>
    public sealed class IntelliSenseRSession : IIntellisenseRSession {
        private readonly IRSessionProvider _sessionProvider;
        private readonly IRInteractiveWorkflow _workflow;
        private readonly BinaryAsyncLock _lock = new BinaryAsyncLock();
        private readonly bool _unitTests;
        private volatile IEnumerable<string> _loadedPackages;
        private volatile Task _updateTask;

        public static IIntellisenseRSession CreateService(IServiceContainer services) => new IntelliSenseRSession(services);

        public IntelliSenseRSession(IServiceContainer services) {
            Services = services;
            _workflow = services.GetService<IRInteractiveWorkflowProvider>().GetOrCreate();
            _sessionProvider = _workflow.RSessions;
            _unitTests = Services.GetService<ICoreShell>().IsUnitTestEnvironment;
        }

        /// <summary>
        /// Timeout to allow R-Host to start. Typically only needs
        /// different value in tests or code coverage runs.
        /// </summary>
        public static int HostStartTimeout { get; set; } = 3000;

        public IServiceContainer Services { get; }

        public IRSession Session { get; private set; }

        public void Dispose() {
            _workflow.RSession.Mutated -= OnInteractiveSessionMutated;
            Session?.Dispose();
            Session = null;
        }

        /// <summary>
        /// Given function name returns package the function belongs to.
        /// The package is determined from the interactive R session since
        /// there may be functions with the same name but from different packages.
        /// Most recently loaded package typically wins.
        /// </summary>
        /// <param name="functionName">R function name</param>
        /// <returns>Function package or null if undefined</returns>
        public async Task<string> GetFunctionPackageNameAsync(string functionName) {
            var session = GetLoadedPackagesInspectionSession();
            string packageName = null;

            if (session != null && session.IsHostRunning) {
                try {
                    var candidate = await session.EvaluateAsync<string>(
                        Invariant(
                            $"as.list(find('{functionName}', mode='function')[1])[[1]]"
                        ), REvaluationKind.Normal);
                    if (candidate != null && candidate.StartsWithOrdinal("package:")) {
                        packageName = candidate.Substring(8);
                    }
                } catch (Exception) { }
            }

            return packageName;
        }

        /// <summary>
        /// Starts intellisense session.
        /// </summary>
        public async Task StartSessionAsync() {
            var token = await _lock.ResetAsync();
            try {
                if (!_sessionProvider.HasBroker) {
                    throw new RHostDisconnectedException();
                }

                if (Session == null) {
                    Session = _sessionProvider.GetOrCreate(SessionNames.Intellisense);
                }

                if (!Session.IsHostRunning) {
                    int timeout = _unitTests ? 10000 : 3000;
                    var settings = Services.GetService<IRSettings>();
                    await Session.EnsureHostStartedAsync(new RHostStartupInfo(settings.CranMirror, codePage: settings.RCodePage), null, timeout);
                    Session.Mutated += OnInteractiveSessionMutated;
                }
            } finally {
                token.Set();
            }
        }

        /// <summary>
        /// Retrieves names of packages loaded into the interactive session.
        /// Cached list of packages may not be up to date.
        /// </summary>
        public IEnumerable<string> LoadedPackageNames {
            get {
                if (_loadedPackages == null && _workflow.RSession != null) {
                    UpdateListOfLoadedPackagesAsync().DoNotWait();
                }
                return _loadedPackages ?? Enumerable.Empty<string>();
            }
        }

        /// <summary>
        /// Retrieves names of packages loaded into the interactive session.
        /// </summary>
        public async Task<IEnumerable<string>> GetLoadedPackageNamesAsync() {
            if (_loadedPackages == null && _updateTask == null) {
                _updateTask = UpdateListOfLoadedPackagesAsync();
            }
            await _updateTask;
            return _loadedPackages ?? Enumerable.Empty<string>();
        }<commit_msg>Fix loaded package update<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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Common.Core;
using Microsoft.Common.Core.Services;
using Microsoft.Common.Core.Shell;
using Microsoft.Common.Core.Threading;
using Microsoft.R.Components.InteractiveWorkflow;
using Microsoft.R.Components.Settings;
using Microsoft.R.Host.Client;
using Microsoft.R.Host.Client.Host;
using static System.FormattableString;

namespace Microsoft.R.Editor.Functions {
    /// <summary>
    /// Represents R session(s) for intellisense and signatures. The interactive session is used
    /// to determine list of loaded packages and separate session is used to fetch RD data 
    /// for function descriptions and signatures.
    /// </summary>
    public sealed class IntelliSenseRSession : IIntellisenseRSession {
        private readonly IRSessionProvider _sessionProvider;
        private readonly IRInteractiveWorkflow _workflow;
        private readonly BinaryAsyncLock _lock = new BinaryAsyncLock();
        private readonly bool _unitTests;
        private volatile IEnumerable<string> _loadedPackages;
        private volatile Task _updateTask;

        public static IIntellisenseRSession CreateService(IServiceContainer services) => new IntelliSenseRSession(services);

        public IntelliSenseRSession(IServiceContainer services) {
            Services = services;
            _workflow = services.GetService<IRInteractiveWorkflowProvider>().GetOrCreate();
            _workflow.RSession.Mutated += OnInteractiveSessionMutated;
            _sessionProvider = _workflow.RSessions;
            _unitTests = Services.GetService<ICoreShell>().IsUnitTestEnvironment;
        }

        /// <summary>
        /// Timeout to allow R-Host to start. Typically only needs
        /// different value in tests or code coverage runs.
        /// </summary>
        public static int HostStartTimeout { get; set; } = 3000;

        public IServiceContainer Services { get; }

        public IRSession Session { get; private set; }

        public void Dispose() {
            _workflow.RSession.Mutated -= OnInteractiveSessionMutated;
            Session?.Dispose();
            Session = null;
        }

        /// <summary>
        /// Given function name returns package the function belongs to.
        /// The package is determined from the interactive R session since
        /// there may be functions with the same name but from different packages.
        /// Most recently loaded package typically wins.
        /// </summary>
        /// <param name="functionName">R function name</param>
        /// <returns>Function package or null if undefined</returns>
        public async Task<string> GetFunctionPackageNameAsync(string functionName) {
            var session = GetLoadedPackagesInspectionSession();
            string packageName = null;

            if (session != null && session.IsHostRunning) {
                try {
                    var candidate = await session.EvaluateAsync<string>(
                        Invariant(
                            $"as.list(find('{functionName}', mode='function')[1])[[1]]"
                        ), REvaluationKind.Normal);
                    if (candidate != null && candidate.StartsWithOrdinal("package:")) {
                        packageName = candidate.Substring(8);
                    }
                } catch (Exception) { }
            }

            return packageName;
        }

        /// <summary>
        /// Starts intellisense session.
        /// </summary>
        public async Task StartSessionAsync() {
            var token = await _lock.ResetAsync();
            try {
                if (!_sessionProvider.HasBroker) {
                    throw new RHostDisconnectedException();
                }

                if (Session == null) {
                    Session = _sessionProvider.GetOrCreate(SessionNames.Intellisense);
                }

                if (!Session.IsHostRunning) {
                    int timeout = _unitTests ? 10000 : 3000;
                    var settings = Services.GetService<IRSettings>();
                    await Session.EnsureHostStartedAsync(new RHostStartupInfo(settings.CranMirror, codePage: settings.RCodePage), null, timeout);
                }
            } finally {
                token.Set();
            }
        }

        /// <summary>
        /// Retrieves names of packages loaded into the interactive session.
        /// Cached list of packages may not be up to date.
        /// </summary>
        public IEnumerable<string> LoadedPackageNames {
            get {
                if (_loadedPackages == null && _workflow.RSession != null) {
                    UpdateListOfLoadedPackagesAsync().DoNotWait();
                }
                return _loadedPackages ?? Enumerable.Empty<string>();
            }
        }

        /// <summary>
        /// Retrieves names of packages loaded into the interactive session.
        /// </summary>
        public async Task<IEnumerable<string>> GetLoadedPackageNamesAsync() {
            if (_loadedPackages == null && _updateTask == null) {
                _updateTask = UpdateListOfLoadedPackagesAsync();
            }
            await _updateTask;
            return _loadedPackages ?? Enumerable.Empty<string>();
        }
{'fraction_non_alphanumeric': 0.08857787810383747, 'fraction_numerical': 0.002889390519187359, 'mean_word_length': 2.055448275862069, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 59, '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': '130404', 'n_tokens_mistral': 2670, 'n_tokens_neox': 2559, 'n_words': 741}