private void DoSearchInternal(RichCommandLineContext parentContext, IConsoleProvider standaloneConsole, bool allowCancel)
        {
            if (parentContext == null && standaloneConsole == null)
            {
                throw new ArgumentException("You must specify either parentContext or standaloneConsole");
            }
            else if (parentContext != null && standaloneConsole != null)
            {
                throw new ArgumentException("You cannot specify both parentContext and standaloneConsole, you must choose one or the other");
            }

            this.parentReaderContext           = parentContext;
            this.console                       = parentContext != null ? parentContext.Console : standaloneConsole;
            this.menuWiper.Console             = this.console;
            this.resultsWiper.Console          = this.console;
            this.expireableAsyncRequestManager = new ExpireableAsyncRequestManager();
            SelectedValue                      = null;

            this.menuWiper.SetTopLeftFromConsole();

            if (allowCancel)
            {
                this.console.Write(new ConsoleString("Type to search. Use up/down/enter/escape to navigate/select/cancel: ", ConsoleColor.Cyan));
            }
            else
            {
                this.console.Write(new ConsoleString("Type to search. Use up/down/enter to navigate/select: ", ConsoleColor.Cyan));
            }

            this.resultsWiper.SetTopLeftFromConsole();
            this.resultsWiper.Left = 0;
            this.resultsWiper.Top += 2;

            this.searchReader = new RichTextCommandLineReader()
            {
                Console = this.console
            };
            this.searchReader.UnregisterHandler(ConsoleKey.UpArrow);
            this.searchReader.UnregisterHandler(ConsoleKey.DownArrow);
            this.searchReader.UnregisterHandler(ConsoleKey.Escape);
            this.searchReader.UnregisterHandler(ConsoleKey.Enter);

            this.searchReader.RegisterHandler(KeyHandler.FromAction((searchReaderContext) => { _MoveSelectedIndex(-1); searchReaderContext.Intercept = true; }, ConsoleKey.UpArrow));
            this.searchReader.RegisterHandler(KeyHandler.FromAction((_searchReaderContext) => { _searchReaderContext.Intercept = true; _MoveSelectedIndex(1); }, ConsoleKey.DownArrow));
            this.searchReader.RegisterHandler(KeyHandler.FromAction((searchReaderContext) =>
            {
                searchReaderContext.Intercept = true;
                if (allowCancel)
                {
                    throw new OperationCanceledException();
                }
            }, ConsoleKey.Escape));
            this.searchReader.RegisterHandler(KeyHandler.FromAction((searchReaderContext) => { _SearchReader_HandleEnterKey(searchReaderContext); }, ConsoleKey.Enter));
            this.searchReader.AfterReadKey += (searchReaderContext) => { _SearchReader_HandleKeyPressed(searchReaderContext); };

            try
            {
                this.DoSearch(string.Empty);
                this.searchReader.ReadLine();
            }
            finally
            {
                // This next lione makes sure that we ignore any in flight search calls that come back after we return control of the main
                // thread.  If we didn't do this then the results would get written to the screen even though the search assist code is no
                // longer running.
                this.expireableAsyncRequestManager.ExpireAll();
            }
        }