using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Robust.Shared; using Robust.Shared.Console; using Robust.Shared.Network.Messages; namespace Robust.Client.Console; internal partial class ClientConsoleHost { private readonly Dictionary _completionsPending = new(); private int _completionSeq; public async Task GetCompletions(List args, string argStr, CancellationToken cancel) { // Last element is the command currently being typed. May be empty. // _logger.Debug($"Running completions: {string.Join(", ", args)}"); var delay = _cfg.GetCVar(CVars.ConCompletionDelay); if (delay > 0) await Task.Delay((int)(delay * 1000), cancel); return await CalcCompletions(args, argStr, cancel); } private Task CalcCompletions(List args, string argStr, CancellationToken cancel) { if (args.Count == 1) { // Typing out command name, handle this ourselves. var cmdOptions = CompletionResult.FromOptions( AvailableCommands.Values .Where(c => CanExecute(c.Command) && !IsCommandHidden(c)) .OrderBy(c => c.Command) .Select(c => new CompletionOption(c.Command, c.Description))); return Task.FromResult(cmdOptions); } if (!AvailableCommands.TryGetValue(args[0], out var cmd)) return Task.FromResult(CompletionResult.Empty); return cmd.GetCompletionAsync(LocalShell, args.ToArray()[1..], argStr, cancel).AsTask(); } private Task DoServerCompletions(List args, string argStr, CancellationToken cancel) { var tcs = new TaskCompletionSource(); var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel); var seq = _completionSeq++; var pending = new PendingCompletion { Cts = cts, Tcs = tcs }; var msg = new MsgConCompletion { Args = args.ToArray(), ArgString = argStr, Seq = seq }; cts.Token.Register(() => { tcs.SetCanceled(cts.Token); cts.Dispose(); _completionsPending.Remove(seq); }, true); NetManager.ClientSendMessage(msg); _completionsPending.Add(seq, pending); return tcs.Task; } private void ProcessCompletionResp(MsgConCompletionResp message) { if (!_completionsPending.TryGetValue(message.Seq, out var pending)) return; pending.Cts.Dispose(); pending.Tcs.SetResult(message.Result); _completionsPending.Remove(message.Seq); } private struct PendingCompletion { public TaskCompletionSource Tcs; public CancellationTokenSource Cts; } }