Ejemplo n.º 1
0
        private void ProcessMessageAtSessionInitialization(string line)
        {
            // Handle incoming messages at session startup.
            if (line.Contains("MOTD:"))
            {
                // Let's display the message-of-the-day from PowerShell Azure Shell.
                Console.WriteLine("\n" + line);
                // Also, seeing this message means we are now in pwsh.
                _codeExecutedTaskSource?.SetResult(null);
            }
            else if (line.Contains("VERBOSE: "))
            {
                // Let's show the verbose message generated from the profile.
                Console.Write(line);
            }
            else if (line.Contains(CommandToSetPrompt))
            {
                // pwsh will echo the command passed to it.
                // It's okay to show incoming messages after this very first command is echoed back.
                _sessionInitialized = true;

                string color = VTColorUtils.CombineColorSequences(ConsoleColor.Green, VTColorUtils.DefaultConsoleColor);
                Console.WriteLine($"\n{color}Welcome to Azure Cloud Shell!{VTColorUtils.ResetColor}");
                Console.WriteLine($"{color}Submitted code will run in the Azure Cloud Shell, type 'exit' to quit.{VTColorUtils.ResetColor}");
            }
        }
Ejemplo n.º 2
0
        internal async Task ExitSession()
        {
            await SendCommand(_exitSessionCommand, waitForExecutionCompletion : false);

            _tokenRenewTimer.Stop();

            string color = VTColorUtils.CombineColorSequences(ConsoleColor.Green, VTColorUtils.DefaultConsoleColor);

            Console.Write($"{color}Azure Cloud Shell session ended.{VTColorUtils.ResetColor}\n");
            Console.Write($"{color}Submitted code will run in the local PowerShell sub kernel.{VTColorUtils.ResetColor}\n");
        }
Ejemplo n.º 3
0
        private void ForceRender()
        {
            var defaultColor = VTColorUtils.MapColorToEscapeSequence(_console.ForegroundColor, isBackground: false) +
                               VTColorUtils.MapColorToEscapeSequence(_console.BackgroundColor, isBackground: true);

            // Geneate a sequence of logical lines with escape sequences for coloring.
            int logicalLineCount = GenerateRender(defaultColor);

            // Now write that out (and remember what we did so we can clear previous renders
            // and minimize writing more than necessary on the next render.)

            var renderLines = new RenderedLineData[logicalLineCount];
            var renderData  = new RenderData {
                lines = renderLines
            };

            for (var i = 0; i < logicalLineCount; i++)
            {
                var line = _consoleBufferLines[i].ToString();
                renderLines[i].line    = line;
                renderLines[i].columns = LengthInBufferCells(line);
            }

            // And then do the real work of writing to the screen.
            // Rendering data is in reused
            ReallyRender(renderData, defaultColor);

            // Cleanup some excess buffers, saving a few because we know we'll use them.
            var bufferCount   = _consoleBufferLines.Count;
            var excessBuffers = bufferCount - renderLines.Length;

            if (excessBuffers > 5)
            {
                _consoleBufferLines.RemoveRange(renderLines.Length, excessBuffers);
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Generates an array of strings representing as much of the outstanding progress activities as possible within the given
        /// space.  As more outstanding activities are collected, nodes are "compressed" (i.e. rendered in an increasing terse
        /// fashion) in order to display as many as possible.  Ultimately, some nodes may be compressed to the point of
        /// invisibility. The oldest nodes are compressed first.
        /// </summary>
        /// <param name="maxWidth">
        /// The maximum width (in BufferCells) that the rendering may consume.
        /// </param>
        /// <param name="maxHeight">
        /// The maximum height (in BufferCells) that the rendering may consume.
        /// </param>
        /// <param name="ui">
        /// The PSHostRawUserInterface used to gauge string widths in the rendering.
        /// </param>
        /// <returns>
        /// An array of strings containing the textual representation of the outstanding progress activities.
        /// </returns>
        internal List <string> Render(int maxWidth, int maxHeight, PSKernelHostUserInterface ui)
        {
            if (_topLevelNodes == null || _topLevelNodes.Count == 0)
            {
                // we have nothing to render.
                return(null);
            }

            int invisible = 0;
            PSHostRawUserInterface rawUI = ui.RawUI;

            if (TallyHeight(rawUI, maxHeight, maxWidth) > maxHeight)
            {
                // This will smash down nodes until the tree will fit into the alloted number of lines.  If in the
                // process some nodes were made invisible, we will add a line to the display to say so.
                invisible = CompressToFit(rawUI, maxHeight, maxWidth);
            }

            var    result = new List <string>(capacity: 5);
            string border = StringUtil.Padding(maxWidth);
            string vtSeqs = VTColorUtils.CombineColorSequences(ui.ProgressForegroundColor, ui.ProgressBackgroundColor);

            result.Add(string.IsNullOrEmpty(vtSeqs) ? border : vtSeqs + border);
            RenderHelper(result, _topLevelNodes, indentation: 0, maxWidth, rawUI);
            if (invisible == 1)
            {
                result.Add(" 1 activity not shown...");
            }
            else if (invisible > 1)
            {
                result.Add(StringUtil.Format(" {0} activities not shown...", invisible));
            }

            result.Add(string.IsNullOrEmpty(vtSeqs) ? border : border + VTColorUtils.ResetColor);
            return(result);
        }
Ejemplo n.º 5
0
 private static string MakeCombinedColor(ConsoleColor fg, ConsoleColor bg)
 => VTColorUtils.AsEscapeSequence(fg) + VTColorUtils.AsEscapeSequence(bg, isBackground: true);
Ejemplo n.º 6
0
        private void MenuCompleteImpl(Menu menu, CommandCompletion completions)
        {
            var menuStack = new Stack <Menu>(10);

            menuStack.Push(null);  // Used to help detect excess backspaces

            RemoveEditsAfterUndo();
            var undoPoint = _edits.Count;

            bool undo = false;

            int savedUserMark = _mark;

            _visualSelectionCommandCount++;

            // Get the unambiguous prefix, possibly removing the first quote
            var userCompletionText = GetUnambiguousPrefix(menu.MenuItems, out var ambiguous);

            if (userCompletionText.Length > 0)
            {
                var c = userCompletionText[0];
                if (IsSingleQuote(c) || IsDoubleQuote(c))
                {
                    userCompletionText = userCompletionText.Substring(1);
                }
            }

            var userInitialCompletionLength = userCompletionText.Length;

            completions.CurrentMatchIndex = 0;
            menu.DrawMenu(null);

            bool processingKeys    = true;
            int  previousSelection = -1;

            while (processingKeys)
            {
                if (menu.CurrentSelection != previousSelection)
                {
                    var currentMenuItem = menu.CurrentMenuItem;
                    int curPos          = FindUserCompletionTextPosition(currentMenuItem, userCompletionText);
                    if (userCompletionText.Length == 0 &&
                        (IsSingleQuote(currentMenuItem.CompletionText[0]) || IsDoubleQuote(currentMenuItem.CompletionText[0])))
                    {
                        curPos++;
                    }

                    // set mark to the end of UserCompletion but in real completion (because of .\ and so on)
                    _mark = completions.ReplacementIndex + curPos + userCompletionText.Length;
                    DoReplacementForCompletion(currentMenuItem, completions);

                    ExchangePointAndMark();

                    // After replacement, the menu might be misplaced from the command line
                    // getting shorter or longer.
                    var endOfCommandLine = ConvertOffsetToPoint(_buffer.Length);
                    var topAdjustment    = (endOfCommandLine.Y + 1) - menu.Top;

                    if (topAdjustment != 0)
                    {
                        menu.Top += topAdjustment;
                        menu.DrawMenu(null);
                    }
                    if (topAdjustment > 0)
                    {
                        // Render did not clear the rest of the command line which flowed
                        // into the menu, so we must do that here.
                        _console.SaveCursor();
                        _console.SetCursorPosition(endOfCommandLine.X, endOfCommandLine.Y);
                        _console.Write(Spaces(_console.BufferWidth - endOfCommandLine.X));
                        _console.RestoreCursor();
                    }

                    if (previousSelection != -1)
                    {
                        if (menu.ToolTipLines > 0)
                        {
                            // Erase previous tooltip, taking into account if the menu moved up/down.
                            WriteBlankLines(menu.Top + menu.Rows, -topAdjustment + menu.ToolTipLines);
                        }
                        menu.UpdateMenuSelection(previousSelection, /*select*/ false,
                                                 /*showToolTips*/ false, VTColorUtils.AsEscapeSequence(Options.EmphasisColor));
                    }
                    menu.UpdateMenuSelection(menu.CurrentSelection, /*select*/ true,
                                             Options.ShowToolTips, VTColorUtils.AsEscapeSequence(Options.EmphasisColor));

                    previousSelection = menu.CurrentSelection;
                }

                var nextKey = ReadKey();
                if (nextKey.EqualsNormalized(Keys.RightArrow))
                {
                    menu.MoveRight();
                }
                else if (nextKey.EqualsNormalized(Keys.LeftArrow))
                {
                    menu.MoveLeft();
                }
                else if (nextKey.EqualsNormalized(Keys.DownArrow))
                {
                    menu.MoveDown();
                }
                else if (nextKey.EqualsNormalized(Keys.UpArrow))
                {
                    menu.MoveUp();
                }
                else if (nextKey.EqualsNormalized(Keys.PageDown))
                {
                    menu.MovePageDown();
                }
                else if (nextKey.EqualsNormalized(Keys.PageUp))
                {
                    menu.MovePageUp();
                }
                else if (nextKey.EqualsNormalized(Keys.Tab))
                {
                    // Search for possible unambiguous common prefix.
                    string unAmbiguousText = GetUnambiguousPrefix(menu.MenuItems, out ambiguous);
                    int    userComplPos    = unAmbiguousText.IndexOf(userCompletionText, StringComparison.OrdinalIgnoreCase);

                    // ... If found - advance IncrementalCompletion ...
                    if (unAmbiguousText.Length > 0 && userComplPos >= 0 &&
                        unAmbiguousText.Length > (userComplPos + userCompletionText.Length))
                    {
                        userCompletionText = unAmbiguousText.Substring(userComplPos);
                        _current           = completions.ReplacementIndex +
                                             FindUserCompletionTextPosition(menu.MenuItems[menu.CurrentSelection], userCompletionText) +
                                             userCompletionText.Length;
                        Render();
                        Ding();
                    }
                    // ... if no - usual Tab behaviour
                    else
                    {
                        menu.MoveN(1);
                    }
                }
                else if (nextKey.EqualsNormalized(Keys.ShiftTab))
                {
                    menu.MoveN(-1);
                }
                else if (nextKey.EqualsNormalized(Keys.CtrlG) ||
                         nextKey.EqualsNormalized(Keys.Escape))
                {
                    undo           = true;
                    processingKeys = false;
                    _visualSelectionCommandCount = 0;
                    _mark = savedUserMark;
                }
                else if (nextKey.EqualsNormalized(Keys.Backspace))
                {
                    // TODO: Shift + Backspace does not fail here?
                    if (menuStack.Count > 1)
                    {
                        var newMenu = menuStack.Pop();

                        newMenu.DrawMenu(menu);
                        previousSelection = -1;

                        menu = newMenu;

                        userCompletionText = userCompletionText.Substring(0, userCompletionText.Length - 1);
                    }
                    else if (menuStack.Count == 1)
                    {
                        Ding();

                        Debug.Assert(menuStack.Peek() == null, "sentinel value expected");
                        // Pop so the next backspace sends us to the else block and out of the loop.
                        menuStack.Pop();
                    }
                    else
                    {
                        processingKeys = false;
                        undo           = true;
                        _visualSelectionCommandCount = 0;
                        _mark = savedUserMark;
                        PrependQueuedKeys(nextKey);
                    }
                }
                else
                {
                    bool prependNextKey            = false;
                    int  cursorAdjustment          = 0;
                    bool truncateCurrentCompletion = false;
                    bool keepSelection             = false;

                    var currentMenuItem = menu.CurrentMenuItem;
                    if (IsDoneWithCompletions(currentMenuItem, nextKey))
                    {
                        processingKeys = false;
                        ExchangePointAndMark(); // cursor to the end of Completion
                        if (!nextKey.EqualsNormalized(Keys.Enter))
                        {
                            if (currentMenuItem.ResultType == CompletionResultType.ProviderContainer)
                            {
                                userCompletionText = GetUnquotedText(
                                    GetReplacementTextForDirectory(currentMenuItem.CompletionText, ref cursorAdjustment),
                                    consistentQuoting: false);
                            }
                            else
                            {
                                userCompletionText = GetUnquotedText(currentMenuItem, consistentQuoting: false);
                            }

                            // do not append the same char as last char in CompletionText (works for for '(', '\')
                            prependNextKey = userCompletionText[userCompletionText.Length - 1] != nextKey.KeyChar;
                        }
                    }
                    else if (nextKey.KeyChar > 0 && !char.IsControl(nextKey.KeyChar))
                    {
                        userCompletionText += nextKey.KeyChar;
                        // filter out matches and redraw menu
                        var newMatches = FilterCompletions(completions, userCompletionText);
                        if (newMatches.Count > 0)
                        {
                            var newMenu = CreateCompletionMenu(newMatches);

                            newMenu.DrawMenu(menu);
                            previousSelection = -1;

                            // Remember the current menu for when we see Backspace.
                            menu.ToolTipLines = 0;
                            if (menuStack.Count == 0)
                            {
                                // The user hit backspace before there were any items on the stack
                                // and we removed the sentinel - so put it back now.
                                menuStack.Push(null);
                            }

                            menuStack.Push(menu);
                            menu = newMenu;
                        }
                        else
                        {
                            processingKeys = false;
                            prependNextKey = true;
                            // we exit loop with current completion up to cursor
                            truncateCurrentCompletion = true;
                            if (userInitialCompletionLength == 0)
                            {
                                undo = true;
                            }
                        }
                    }
                    else // exit with any other Key chord
                    {
                        processingKeys = false;
                        prependNextKey = true;

                        // without this branch experience doesnt look naturally
                        if (_dispatchTable.TryGetValue(nextKey, out var handler) &&
                            (
                                handler.Action == CopyOrCancelLine ||
                                handler.Action == Cut ||
                                handler.Action == DeleteChar ||
                                handler.Action == Paste
                            )
                            )
                        {
                            keepSelection = true;
                        }
                    }

                    if (!processingKeys) // time to exit loop
                    {
                        if (truncateCurrentCompletion && !undo)
                        {
                            CompletionResult r = new CompletionResult(currentMenuItem
                                                                      .CompletionText.Substring(0, _current - completions.ReplacementIndex));
                            DoReplacementForCompletion(r, completions);
                        }
                        if (keepSelection)
                        {
                            _visualSelectionCommandCount = 1;
                        }
                        else
                        {
                            _visualSelectionCommandCount = 0;
                            // if mark was set after cursor, it restored in uninspected position, because text before mark now longer
                            // should we correct it ? I think not, beause any other text insertion does not correct it
                            _mark = savedUserMark;
                        }
                        // without render all key chords that just move cursor leave selection visible, but it can be wrong
                        if (!undo && !keepSelection)
                        {
                            Render();
                        }
                        if (prependNextKey)
                        {
                            _current -= cursorAdjustment;
                            PrependQueuedKeys(nextKey);
                        }
                    }
                }
            }

            menu.Clear();

            var lastInsert = ((GroupedEdit)_edits[_edits.Count - 1])._groupedEditItems[1];

            Debug.Assert(lastInsert is EditItemInsertString, "The only edits possible here are pairs of Delete/Insert");
            var firstDelete = ((GroupedEdit)_edits[undoPoint])._groupedEditItems[0];

            Debug.Assert(firstDelete is EditItemDelete, "The only edits possible here are pairs of Delete/Insert");

            var groupEditCount = _edits.Count - undoPoint;

            _edits.RemoveRange(undoPoint, groupEditCount);
            _undoEditIndex = undoPoint;

            if (undo)
            {
                // Pretend it never happened.
                lastInsert.Undo();
                firstDelete.Undo();
                Render();
            }
            else
            {
                // Leave one edit instead of possibly many to undo
                SaveEditItem(GroupedEdit.Create(new List <EditItem> {
                    firstDelete, lastInsert
                }));
            }
        }