// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Repl.ConsoleHandling; using Microsoft.Repl.Parsing; namespace Microsoft.Repl.Commanding { public static class DefaultCommandDispatcher { public static DefaultCommandDispatcher Create(Func getPrompt, TProgramState programState) { return new DefaultCommandDispatcher(getPrompt, programState); } public static DefaultCommandDispatcher Create(Action onReady, TProgramState programState) { return new DefaultCommandDispatcher(onReady, programState); } public static DefaultCommandDispatcher Create(Func getPrompt, TProgramState programState, IParser parser) where TParseResult : ICoreParseResult { return new DefaultCommandDispatcher(getPrompt, programState, parser); } public static DefaultCommandDispatcher Create(Action onReady, TProgramState programState, IParser parser) where TParseResult : ICoreParseResult { return new DefaultCommandDispatcher(onReady, programState, parser); } } public class DefaultCommandDispatcher : DefaultCommandDispatcher { public DefaultCommandDispatcher(Func getPrompt, TProgramState programState) : base(getPrompt, programState, new CoreParser()) { } public DefaultCommandDispatcher(Action onReady, TProgramState programState) : base(onReady, programState, new CoreParser()) { } } public class DefaultCommandDispatcher : ICommandDispatcher where TParseResult : ICoreParseResult { private readonly Action _onReady; private readonly TProgramState _programState; private readonly IParser _parser; private readonly HashSet> _commands = new HashSet>(); private bool _isReady; public DefaultCommandDispatcher(Func getPrompt, TProgramState programState, IParser parser) : this(s => s.ConsoleManager.Write(getPrompt()), programState, parser) { } public DefaultCommandDispatcher(Action onReady, TProgramState programState, IParser parser) { _onReady = onReady; _programState = programState; _parser = parser; } public void AddCommand(ICommand command) { _commands.Add(command); } public IEnumerable> Commands => _commands; public IParser Parser => _parser; public IReadOnlyList CollectSuggesetions(IShellState shellState) { string line = shellState.InputManager.GetCurrentBuffer(); TParseResult parseResult = _parser.Parse(line, shellState.ConsoleManager.CaretPosition); HashSet suggestions = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (ICommand command in _commands) { IEnumerable commandSuggestions = command.Suggest(shellState, _programState, parseResult); if (commandSuggestions != null) { suggestions.UnionWith(commandSuggestions); } } return suggestions.OrderBy(x => x, StringComparer.OrdinalIgnoreCase).ToList(); } public async Task ExecuteCommandAsync(IShellState shellState, CancellationToken cancellationToken) { _isReady = false; shellState.ConsoleManager.WriteLine(); string commandText = shellState.InputManager.GetCurrentBuffer(); if (!string.IsNullOrWhiteSpace(commandText)) { shellState.CommandHistory.AddCommand(shellState.InputManager.GetCurrentBuffer()); try { await ExecuteCommandInternalAsync(shellState, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { shellState.ConsoleManager.Error.WriteLine(ex.ToString().Bold().Red()); } if (cancellationToken.IsCancellationRequested) { shellState.ConsoleManager.Error.WriteLine("Execution was cancelled".Bold().Red()); } } if (!_isReady) { shellState.ConsoleManager.WriteLine(); OnReady(shellState); } shellState.InputManager.ResetInput(); } private async Task ExecuteCommandInternalAsync(IShellState shellState, CancellationToken cancellationToken) { string line = shellState.InputManager.GetCurrentBuffer(); TParseResult parseResult = _parser.Parse(line, shellState.ConsoleManager.CaretPosition); if (!string.IsNullOrWhiteSpace(parseResult.CommandText)) { foreach (ICommand command in _commands) { bool? result = command.CanHandle(shellState, _programState, parseResult); if (result.HasValue) { if (result.Value) { await command.ExecuteAsync(shellState, _programState, parseResult, cancellationToken); } //If the handler returned non-null, the input would be directed to it, but it's not valid input return; } } shellState.ConsoleManager.Error.WriteLine("No matching command found".Red().Bold()); } } public void OnReady(IShellState shellState) { if (!_isReady) { _onReady(shellState); shellState.ConsoleManager.ResetCommandStart(); _isReady = true; } } } }