/// <inhericdoc />
        public List <PredictiveSuggestion> GetSuggestion(PredictionContext context, CancellationToken cancellationToken)
        {
            var localCancellationToken = Settings.ContinueOnTimeout ? CancellationToken.None : cancellationToken;

            IEnumerable <ValueTuple <string, string, PredictionSource> > suggestions = Enumerable.Empty <ValueTuple <string, string, PredictionSource> >();
            string maskedUserInput = string.Empty;
            // This is the list of records of the source suggestion and the prediction source.
            var telemetryData = new List <ValueTuple <string, PredictionSource> >();

            try
            {
                maskedUserInput = AzPredictor.MaskCommandLine(context.InputAst.FindAll((ast) => ast is CommandAst, true).LastOrDefault() as CommandAst);

                suggestions = _service.GetSuggestion(context.InputAst, _settings.SuggestionCount.Value, _settings.MaxAllowedCommandDuplicate.Value, localCancellationToken);

                localCancellationToken.ThrowIfCancellationRequested();

                var userAcceptedAndSuggestion = new Dictionary <string, string>();

                foreach (var s in suggestions)
                {
                    telemetryData.Add(ValueTuple.Create(s.Item2, s.Item3));
                    userAcceptedAndSuggestion[s.Item1] = s.Item2;
                }

                lock (_userAcceptedAndSuggestion)
                {
                    foreach (var u in userAcceptedAndSuggestion)
                    {
                        _userAcceptedAndSuggestion[u.Key] = u.Value;
                    }
                }

                localCancellationToken.ThrowIfCancellationRequested();

                var returnedValue = suggestions.Select((r, index) =>
                {
                    return(new PredictiveSuggestion(r.Item1));
                })
                                    .ToList();

                _telemetryClient.OnGetSuggestion(maskedUserInput,
                                                 telemetryData,
                                                 cancellationToken.IsCancellationRequested);

                return(returnedValue);
            }
            catch (Exception e) when(!(e is OperationCanceledException))
            {
                this._telemetryClient.OnGetSuggestionError(e);
            }

            return(new List <PredictiveSuggestion>());
        }
        /// <inhericdoc />
        public void StartEarlyProcessing(IReadOnlyList <string> history)
        {
            // The context only changes when the user executes the corresponding command.
            this._azContext?.UpdateContext();
            lock (_userAcceptedAndSuggestion)
            {
                _userAcceptedAndSuggestion.Clear();
            }

            if (history.Count > 0)
            {
                if (_lastTwoMaskedCommands.Any())
                {
                    _lastTwoMaskedCommands.Dequeue();
                }
                else
                {
                    // This is the first time we populate our record. Push the second to last command in history to the
                    // queue. If there is only one command in history, push the command placeholder.

                    if (history.Count() > 1)
                    {
                        string secondToLastLine    = history.TakeLast(AzPredictorConstants.CommandHistoryCountToProcess).First();
                        var    secondToLastCommand = GetAstAndMaskedCommandLine(secondToLastLine);
                        _lastTwoMaskedCommands.Enqueue(secondToLastCommand.Item2);
                        _service.RecordHistory(secondToLastCommand.Item1);
                    }
                    else
                    {
                        _lastTwoMaskedCommands.Enqueue(AzPredictorConstants.CommandPlaceholder);
                        // We only extract parameter values from the command line in _service.RecordHistory.
                        // So we don't need to do that for a placeholder.
                    }
                }

                string lastLine    = history.Last();
                var    lastCommand = GetAstAndMaskedCommandLine(lastLine);

                _lastTwoMaskedCommands.Enqueue(lastCommand.Item2);

                if ((lastCommand.Item2 != null) && !string.Equals(AzPredictorConstants.CommandPlaceholder, lastCommand.Item2, StringComparison.Ordinal))
                {
                    _service.RecordHistory(lastCommand.Item1);
                }

                _telemetryClient.OnHistory(lastCommand.Item2);
                _service.RequestPredictions(_lastTwoMaskedCommands);
            }

            ValueTuple <CommandAst, string> GetAstAndMaskedCommandLine(string commandLine)
            {
                var    asts              = Parser.ParseInput(commandLine, out _, out _);
                var    allNestedAsts     = asts?.FindAll((ast) => ast is CommandAst, true);
                var    commandAst        = allNestedAsts?.LastOrDefault() as CommandAst;
                string maskedCommandLine = null;

                var commandName = commandAst?.CommandElements?.FirstOrDefault().ToString();

                if (_service.IsSupportedCommand(commandName))
                {
                    maskedCommandLine = AzPredictor.MaskCommandLine(commandAst);
                }
                else
                {
                    maskedCommandLine = AzPredictorConstants.CommandPlaceholder;
                }

                return(ValueTuple.Create(commandAst, maskedCommandLine));
            }
        }