<commit_before>﻿using System;
using System.Collections.Generic;
using Microsoft.Languages.Editor.Shell;
using Microsoft.R.Editor.Document;
using Microsoft.R.Editor.Formatting;
using Microsoft.VisualStudio.InteractiveWindow.Shell;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.R.Package.Shell;
using Microsoft.VisualStudio.R.Packages.R;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods;
using Task = System.Threading.Tasks.Task;

namespace Microsoft.VisualStudio.R.Package.Repl {
    /// <summary>
    /// Tracks most recently active REPL window
    /// </summary>
    internal sealed class ReplWindow : IVsWindowFrameEvents, IDisposable {
        private uint _windowFrameEventsCookie;
        private IVsInteractiveWindow _lastUsedReplWindow;
        private static readonly Lazy<ReplWindow> _instance = new Lazy<ReplWindow>(() => new ReplWindow());
        LinkedList<PendingSubmission> _pendingInputs = new LinkedList<PendingSubmission>();

        class PendingSubmission {
            public string Code;
            public bool AddNewLine;
        }

        public ReplWindow() {
            IVsUIShell7 shell = AppShell.Current.GetGlobalService<IVsUIShell7>(typeof(SVsUIShell));
            _windowFrameEventsCookie = shell.AdviseWindowFrameEvents(this);
        }

        public static ReplWindow Current => _instance.Value;

        public bool IsActive {
            get {
                IVsWindowFrame frame = ReplWindow.Current.GetToolWindow();
                if (frame != null) {
                    int onScreen;
                    frame.IsOnScreen(out onScreen);
                    return onScreen != 0;
                }
                return false;
            }
        }

        public IVsWindowFrame GetToolWindow() {
            IVsWindowFrame frame = null;
            IVsUIShell shell = AppShell.Current.GetGlobalService<IVsUIShell>(typeof(SVsUIShell));
            Guid persistenceSlot = RGuidList.ReplInteractiveWindowProviderGuid;

            // First just find. If it exists, use it. 
            shell.FindToolWindow((int)__VSFINDTOOLWIN.FTW_fFindFirst, ref persistenceSlot, out frame);
            return frame;
        }

        private void ProcessQueuedInput() {
            IVsInteractiveWindow interactive = _instance.Value.GetInteractiveWindow();
            if (interactive != null) {
                var window = interactive.InteractiveWindow;

                // Process all of our pending inputs until we get a complete statement
                while (_pendingInputs.Count != 0) {
                    var cur = _pendingInputs.First.Value;
                    _pendingInputs.RemoveFirst();

                    window.InsertCode(cur.Code);
                    string fullCode = window.CurrentLanguageBuffer.CurrentSnapshot.GetText();

                    if (window.Evaluator.CanExecuteCode(fullCode)) {
                        // the code is complete, execute it now
                        window.Operations.ExecuteInput();
                        break;
                    } else if (cur.AddNewLine) {
                        // We want a new line after non-complete inputs, e.g. the user ctrl-entered on
                        // function() {
                        window.InsertCode(window.TextView.Options.GetNewLineCharacter());
                    }
                }
            }
        }

        /// <summary>
        /// Enqueues the provided code for execution.  If there's no current execution the code is
        /// inserted at the caret position.  Otherwise the code is stored for when the current
        /// execution is completed.
        /// 
        /// If the current input becomes complete after inserting the code then the input is executed.  
        /// 
        /// If the code is not complete and addNewLine is true then a new line character is appended 
        /// to the end of the input.
        /// </summary>
        /// <param name="code">The code to be inserted</param>
        /// <param name="addNewLine">True to add a new line on non-complete inputs.</param>
        public void EnqueueCode(string code, bool addNewLine) {
            IVsInteractiveWindow current = _instance.Value.GetInteractiveWindow();
            if (current != null) {
                if (current.InteractiveWindow.IsResetting) {
                    return;
                }

                // add the input to our queue...
                _pendingInputs.AddLast(new PendingSubmission() { Code = code, AddNewLine = addNewLine });

                if (!current.InteractiveWindow.IsRunning) {
                    // and process the queue if we weren't currently running
                    ProcessQueuedInput();
                }
            }
        }

        public void ExecuteCode(string code) {
            IVsInteractiveWindow current = _instance.Value.GetInteractiveWindow();
            if (current != null && !string.IsNullOrWhiteSpace(code)) {
                current.InteractiveWindow.AddInput(code);
                current.InteractiveWindow.Operations.ExecuteInput();
            }
        }

        public void ExecuteCurrentExpression(ITextView textView) {
            ICompletionBroker broker = EditorShell.Current.ExportProvider.GetExport<ICompletionBroker>().Value;
            broker.DismissAllSessions(textView);

            IVsInteractiveWindow current = _instance.Value.GetInteractiveWindow();
            if (current != null) {
                SnapshotPoint? documentPoint = REditorDocument.MapCaretPositionFromView(textView);
                if (!documentPoint.HasValue ||
                    documentPoint.Value == documentPoint.Value.Snapshot.Length ||
                    documentPoint.Value.Snapshot.Length == 0) {
                    // Let the repl try and execute the code if the user presses enter at the
                    // end of the buffer.
                    current.InteractiveWindow.Operations.Return();
                } else {
                    // Otherwise insert a line break in the middle of an input
                    current.InteractiveWindow.Operations.BreakLine();
                    var document = REditorDocument.TryFromTextBuffer(current.InteractiveWindow.CurrentLanguageBuffer);
                    if (document != null) {
                        var tree = document.EditorTree;
                        tree.EnsureTreeReady();

                        AutoFormat.HandleAutoFormat(
                            current.InteractiveWindow.TextView,
                            current.InteractiveWindow.CurrentLanguageBuffer,
                            tree.AstRoot,
                            '\n'<commit_msg>Bug #287: Don't process enter if we're actually running<commit_after>﻿using System;
using System.Collections.Generic;
using Microsoft.Languages.Editor.Shell;
using Microsoft.R.Editor.Document;
using Microsoft.R.Editor.Formatting;
using Microsoft.VisualStudio.InteractiveWindow.Shell;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.R.Package.Shell;
using Microsoft.VisualStudio.R.Packages.R;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods;
using Task = System.Threading.Tasks.Task;

namespace Microsoft.VisualStudio.R.Package.Repl {
    /// <summary>
    /// Tracks most recently active REPL window
    /// </summary>
    internal sealed class ReplWindow : IVsWindowFrameEvents, IDisposable {
        private uint _windowFrameEventsCookie;
        private IVsInteractiveWindow _lastUsedReplWindow;
        private static readonly Lazy<ReplWindow> _instance = new Lazy<ReplWindow>(() => new ReplWindow());
        LinkedList<PendingSubmission> _pendingInputs = new LinkedList<PendingSubmission>();

        class PendingSubmission {
            public string Code;
            public bool AddNewLine;
        }

        public ReplWindow() {
            IVsUIShell7 shell = AppShell.Current.GetGlobalService<IVsUIShell7>(typeof(SVsUIShell));
            _windowFrameEventsCookie = shell.AdviseWindowFrameEvents(this);
        }

        public static ReplWindow Current => _instance.Value;

        public bool IsActive {
            get {
                IVsWindowFrame frame = ReplWindow.Current.GetToolWindow();
                if (frame != null) {
                    int onScreen;
                    frame.IsOnScreen(out onScreen);
                    return onScreen != 0;
                }
                return false;
            }
        }

        public IVsWindowFrame GetToolWindow() {
            IVsWindowFrame frame = null;
            IVsUIShell shell = AppShell.Current.GetGlobalService<IVsUIShell>(typeof(SVsUIShell));
            Guid persistenceSlot = RGuidList.ReplInteractiveWindowProviderGuid;

            // First just find. If it exists, use it. 
            shell.FindToolWindow((int)__VSFINDTOOLWIN.FTW_fFindFirst, ref persistenceSlot, out frame);
            return frame;
        }

        private void ProcessQueuedInput() {
            IVsInteractiveWindow interactive = _instance.Value.GetInteractiveWindow();
            if (interactive != null) {
                var window = interactive.InteractiveWindow;

                // Process all of our pending inputs until we get a complete statement
                while (_pendingInputs.Count != 0) {
                    var cur = _pendingInputs.First.Value;
                    _pendingInputs.RemoveFirst();

                    window.InsertCode(cur.Code);
                    string fullCode = window.CurrentLanguageBuffer.CurrentSnapshot.GetText();

                    if (window.Evaluator.CanExecuteCode(fullCode)) {
                        // the code is complete, execute it now
                        window.Operations.ExecuteInput();
                        break;
                    } else if (cur.AddNewLine) {
                        // We want a new line after non-complete inputs, e.g. the user ctrl-entered on
                        // function() {
                        window.InsertCode(window.TextView.Options.GetNewLineCharacter());
                    }
                }
            }
        }

        /// <summary>
        /// Enqueues the provided code for execution.  If there's no current execution the code is
        /// inserted at the caret position.  Otherwise the code is stored for when the current
        /// execution is completed.
        /// 
        /// If the current input becomes complete after inserting the code then the input is executed.  
        /// 
        /// If the code is not complete and addNewLine is true then a new line character is appended 
        /// to the end of the input.
        /// </summary>
        /// <param name="code">The code to be inserted</param>
        /// <param name="addNewLine">True to add a new line on non-complete inputs.</param>
        public void EnqueueCode(string code, bool addNewLine) {
            IVsInteractiveWindow current = _instance.Value.GetInteractiveWindow();
            if (current != null) {
                if (current.InteractiveWindow.IsResetting) {
                    return;
                }

                // add the input to our queue...
                _pendingInputs.AddLast(new PendingSubmission() { Code = code, AddNewLine = addNewLine });

                if (!current.InteractiveWindow.IsRunning) {
                    // and process the queue if we weren't currently running
                    ProcessQueuedInput();
                }
            }
        }

        public void ExecuteCode(string code) {
            IVsInteractiveWindow current = _instance.Value.GetInteractiveWindow();
            if (current != null && !string.IsNullOrWhiteSpace(code)) {
                current.InteractiveWindow.AddInput(code);
                current.InteractiveWindow.Operations.ExecuteInput();
            }
        }

        public void ExecuteCurrentExpression(ITextView textView) {
            ICompletionBroker broker = EditorShell.Current.ExportProvider.GetExport<ICompletionBroker>().Value;
            broker.DismissAllSessions(textView);

            IVsInteractiveWindow current = _instance.Value.GetInteractiveWindow();
            if (current != null && !current.InteractiveWindow.IsRunning) {
                SnapshotPoint? documentPoint = REditorDocument.MapCaretPositionFromView(textView);
                if (!documentPoint.HasValue ||
                    documentPoint.Value == documentPoint.Value.Snapshot.Length ||
                    documentPoint.Value.Snapshot.Length == 0) {
                    // Let the repl try and execute the code if the user presses enter at the
                    // end of the buffer.
                    current.InteractiveWindow.Operations.Return();
                } else {
                    // Otherwise insert a line break in the middle of an input
                    current.InteractiveWindow.Operations.BreakLine();
                    var document = REditorDocument.TryFromTextBuffer(current.InteractiveWindow.CurrentLanguageBuffer);
                    if (document != null) {
                        var tree = document.EditorTree;
                        tree.EnsureTreeReady();

                        AutoFormat.HandleAutoFormat(
                            current.InteractiveWindow.TextView,
                            current.InteractiveWindow.CurrentLanguageBuffer,
                            tree.AstRoot,
                            '\n'
{'fraction_non_alphanumeric': 0.07949760418179178, 'fraction_numerical': 0.0009438071729345143, 'mean_word_length': 1.865612648221344, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 37, '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': '956073', 'n_tokens_mistral': 3344, 'n_tokens_neox': 3176, 'n_words': 820}