<commit_before>﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using BenchmarkDotNet.ConsoleArguments;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Portability;
using JetBrains.Annotations;

namespace BenchmarkDotNet.Running
{
    internal class UserInteraction : IUserInteraction
    {
        private static bool consoleCancelKeyPressed;

        static UserInteraction() => Console.CancelKeyPress += (_, __) => consoleCancelKeyPressed = true;

        public void PrintNoBenchmarksError(ILogger logger)
        {
            logger.WriteError("No benchmarks to choose from. Make sure you provided public non-sealed non-static types with public [Benchmark] methods.");
        }

        [NotNull]
        public IReadOnlyList<Type> AskUser([NotNull] IReadOnlyList<Type> allTypes, ILogger logger)
        {
            var selectedTypes = new List<Type>();
            string benchmarkCaptionExample = allTypes.First().GetDisplayName();

            while (selectedTypes.Count == 0 && !consoleCancelKeyPressed)
            {
                PrintAvailable(allTypes, logger);

                if (consoleCancelKeyPressed)
                    break;

                string filterExample = "--filter " + UserInteractionHelper.EscapeCommandExample($"*{benchmarkCaptionExample}*");
                logger.WriteLineHelp($"You should select the target benchmark(s). Please, print a number of a benchmark (e.g. `0`) or a contained benchmark caption (e.g. `{benchmarkCaptionExample}`).");
                logger.WriteLineHelp("If you want to select few, please separate them with space ` ` (e.g. `1 2 3`).");
                logger.WriteLineHelp($"You can also provide the class name in console arguments by using --filter. (e.g. `{filterExample}`).");

                string userInput = Console.ReadLine() ?? "";

                selectedTypes.AddRange(GetMatching(allTypes, userInput.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)));
                logger.WriteLine();
            }

            return selectedTypes;
        }

        public void PrintWrongFilterInfo(IReadOnlyList<Type> allTypes, ILogger logger, [NotNull] string[] userFilters)
        {
            var correctionSuggester = new CorrectionsSuggester(allTypes);

            var filterToNames = userFilters
                .Select(userFilter => (userFilter: userFilter, suggestedBenchmarkNames: correctionSuggester.SuggestFor(userFilter)))
                .ToArray();

            foreach ((string userFilter, var suggestedBenchmarkNames) in filterToNames)<commit_msg>UserInteraction: don't loop when reaching the input end. (#1870)<commit_after>﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using BenchmarkDotNet.ConsoleArguments;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Portability;
using JetBrains.Annotations;

namespace BenchmarkDotNet.Running
{
    internal class UserInteraction : IUserInteraction
    {
        private static bool consoleCancelKeyPressed;

        static UserInteraction() => Console.CancelKeyPress += (_, __) => consoleCancelKeyPressed = true;

        public void PrintNoBenchmarksError(ILogger logger)
        {
            logger.WriteError("No benchmarks to choose from. Make sure you provided public non-sealed non-static types with public [Benchmark] methods.");
        }

        [NotNull]
        public IReadOnlyList<Type> AskUser([NotNull] IReadOnlyList<Type> allTypes, ILogger logger)
        {
            var selectedTypes = new List<Type>();
            string benchmarkCaptionExample = allTypes.First().GetDisplayName();

            while (selectedTypes.Count == 0 && !consoleCancelKeyPressed)
            {
                PrintAvailable(allTypes, logger);

                if (consoleCancelKeyPressed)
                    break;

                string filterExample = "--filter " + UserInteractionHelper.EscapeCommandExample($"*{benchmarkCaptionExample}*");
                logger.WriteLineHelp($"You should select the target benchmark(s). Please, print a number of a benchmark (e.g. `0`) or a contained benchmark caption (e.g. `{benchmarkCaptionExample}`).");
                logger.WriteLineHelp("If you want to select few, please separate them with space ` ` (e.g. `1 2 3`).");
                logger.WriteLineHelp($"You can also provide the class name in console arguments by using --filter. (e.g. `{filterExample}`).");

                string userInput = Console.ReadLine();
                if (userInput == null)
                {
                    break;
                }

                selectedTypes.AddRange(GetMatching(allTypes, userInput.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)));
                logger.WriteLine();
            }

            return selectedTypes;
        }

        public void PrintWrongFilterInfo(IReadOnlyList<Type> allTypes, ILogger logger, [NotNull] string[] userFilters)
        {
            var correctionSuggester = new CorrectionsSuggester(allTypes);

            var filterToNames = userFilters
                .Select(userFilter => (userFilter: userFilter, suggestedBenchmarkNames: correctionSuggester.SuggestFor(userFilter)))
                .ToArray();

            foreach ((string userFilter, var suggestedBenchmarkNames) in filterToNames)
{'fraction_non_alphanumeric': 0.09312921450701658, 'fraction_numerical': 0.0025514853289593585, 'mean_word_length': 2.7926744989633723, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 17, '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': '7478910', 'n_tokens_mistral': 1438, 'n_tokens_neox': 1355, 'n_words': 359}