protected virtual async Task Run(IMenu menu, IEnumerable <IMenu> path)
        {
            if (menu == null)
            {
                throw new ArgumentNullException("menu");
            }

            var pageNumber = 0;

            await menu.Enter(new Context(this, path));

            var pages = new PaginatedAsyncSequence <IMenuItem>(menu.Items);

            async Task ExecuteMenuItemAndRefresh(IMenuItem item)
            {
                var context = new Context(this, path);

                await ExecuteItem(item, context);

                if (context.MenuInvalidated)
                {
                    await pages.DisposeAsync();

                    pages = new PaginatedAsyncSequence <IMenuItem>(menu.Items);
                }
            }

            try
            {
                do
                {
                    options = CreateOptions();

                    var progressCancellation = new CancellationTokenSource();
                    var loaderCancellation   = new CancellationTokenSource();
                    var progressTask         = Task.Run(async() =>
                    {
                        try
                        {
                            await Task.Delay(100, progressCancellation.Token);
                            if (!progressCancellation.Token.IsCancellationRequested)
                            {
                                using (var progress = ((IMenuUserInterface)this).StartProgress("Loading... Press [Esc] to cancel"))
                                {
                                    progress.SetIndeterminate();
                                    var result = await((IMenuUserInterface)this).Select(null, r => (r.Type == PromptType.Cancel, r.Type), progressCancellation.Token);
                                    if (result == PromptType.Cancel)
                                    {
                                        loaderCancellation.Cancel();
                                    }
                                }
                            }
                        }
                        catch (TaskCanceledException) { }
                    });

                    Page <IMenuItem> page;
                    try
                    {
                        page = await pages.GetPage(pageNumber, options.Count, loaderCancellation.Token);
                    }
                    finally
                    {
                        progressCancellation.Cancel();
                        await progressTask;
                    }

                    if (menu.ExecuteIfSingleItem && page.IsFirstPage && page.Count == 1)
                    {
                        await ExecuteMenuItemAndRefresh(page[0]);

                        break;
                    }

                    Render(menu, page, null, path);

                    var(choice, item) = await Prompt(page);

                    if (choice == MenuChoice.Cancel)
                    {
                        if (await menu.CanExit(new Context(this, path)))
                        {
                            break;
                        }
                        else
                        {
                            continue;
                        }
                    }

                    if (choice == MenuChoice.PreviousPage)
                    {
                        --pageNumber;
                        continue;
                    }

                    if (choice == MenuChoice.NextPage)
                    {
                        ++pageNumber;
                        continue;
                    }

                    if (choice == MenuChoice.Item)
                    {
                        Render(menu, page, item, path);

                        Cons.WriteLine();
                        Cons.WriteLine("Running...");
                        Cons.WriteLine();

                        await ExecuteMenuItemAndRefresh(item);
                    }
                }while (!menu.ShouldExit);
            }
            finally
            {
                await pages.DisposeAsync();
            }
        }