From 7723130713a2fa88c9098adf7330bd145968d294 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 29 Jun 2016 07:03:44 -0300 Subject: [PATCH] Implemented new CommandMap --- src/Discord.Net.Commands/CommandService.cs | 50 ++-------- src/Discord.Net.Commands/Map/CommandMap.cs | 76 +++++++++++++++ .../Map/CommandMapNode.cs | 95 +++++++++++++++++++ 3 files changed, 178 insertions(+), 43 deletions(-) create mode 100644 src/Discord.Net.Commands/Map/CommandMap.cs create mode 100644 src/Discord.Net.Commands/Map/CommandMapNode.cs diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index a9e7085e0..794ab0863 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -14,8 +14,8 @@ namespace Discord.Commands { private readonly SemaphoreSlim _moduleLock; private readonly ConcurrentDictionary _modules; - private readonly ConcurrentDictionary> _map; private readonly ConcurrentDictionary _typeReaders; + private readonly CommandMap _map; public IEnumerable Modules => _modules.Select(x => x.Value); public IEnumerable Commands => _modules.SelectMany(x => x.Value.Commands); @@ -24,7 +24,7 @@ namespace Discord.Commands { _moduleLock = new SemaphoreSlim(1, 1); _modules = new ConcurrentDictionary(); - _map = new ConcurrentDictionary>(); + _map = new CommandMap(); _typeReaders = new ConcurrentDictionary { [typeof(string)] = new GenericTypeReader((m, s) => Task.FromResult(TypeReaderResult.FromSuccess(s))), @@ -160,11 +160,7 @@ namespace Discord.Commands _modules[moduleInstance] = loadedModule; foreach (var cmd in loadedModule.Commands) - { - var list = _map.GetOrAdd(cmd.Text, _ => new List()); - lock (list) - list.Add(cmd); - } + _map.AddCommand(cmd); return loadedModule; } @@ -222,14 +218,7 @@ namespace Discord.Commands if (_modules.TryRemove(module, out unloadedModule)) { foreach (var cmd in unloadedModule.Commands) - { - List list; - if (_map.TryGetValue(cmd.Text, out list)) - { - lock (list) - list.Remove(cmd); - } - } + _map.RemoveCommand(cmd); return true; } else @@ -240,35 +229,10 @@ namespace Discord.Commands public SearchResult Search(IMessage message, string input) { string lowerInput = input.ToLowerInvariant(); - - ImmutableArray.Builder matches = null; - List group; - int pos = -1; - - while (true) - { - pos = input.IndexOf(' ', pos + 1); - string cmdText = pos == -1 ? input : input.Substring(0, pos); - if (!_map.TryGetValue(cmdText, out group)) - break; - - lock (group) - { - if (matches == null) - matches = ImmutableArray.CreateBuilder(group.Count); - for (int i = 0; i < group.Count; i++) - matches.Add(group[i]); - } - - if (pos == -1) - { - pos = input.Length; - break; - } - } + var matches = _map.GetCommands(input).ToImmutableArray(); - if (matches != null) - return SearchResult.FromSuccess(input, matches.ToImmutable()); + if (matches.Length > 0) + return SearchResult.FromSuccess(input, matches); else return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command."); } diff --git a/src/Discord.Net.Commands/Map/CommandMap.cs b/src/Discord.Net.Commands/Map/CommandMap.cs new file mode 100644 index 000000000..0f719d56d --- /dev/null +++ b/src/Discord.Net.Commands/Map/CommandMap.cs @@ -0,0 +1,76 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +namespace Discord.Commands +{ + internal class CommandMap + { + private readonly ConcurrentDictionary _nodes; + + public CommandMap() + { + _nodes = new ConcurrentDictionary(); + } + + public void AddCommand(Command command) + { + string text = command.Text; + int nextSpace = text.IndexOf(' '); + string name; + + lock (this) + { + if (nextSpace == -1) + name = command.Text; + else + name = command.Text.Substring(0, nextSpace); + var nextNode = _nodes.GetOrAdd(name, x => new CommandMapNode(x)); + nextNode.AddCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + } + } + public void RemoveCommand(Command command) + { + string text = command.Text; + int nextSpace = text.IndexOf(' '); + string name; + + lock (this) + { + if (nextSpace == -1) + name = command.Text; + else + name = command.Text.Substring(0, nextSpace); + + CommandMapNode nextNode; + if (_nodes.TryGetValue(name, out nextNode)) + { + nextNode.AddCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + if (nextNode.IsEmpty) + _nodes.TryRemove(name, out nextNode); + } + } + } + + public IEnumerable GetCommands(string text) + { + int nextSpace = text.IndexOf(' '); + string name; + + lock (this) + { + if (nextSpace == -1) + name = text; + else + name = text.Substring(0, nextSpace); + + CommandMapNode nextNode; + if (_nodes.TryGetValue(name, out nextNode)) + return nextNode.GetCommands(text, nextSpace + 1); + else + return Enumerable.Empty(); + } + } + } +} diff --git a/src/Discord.Net.Commands/Map/CommandMapNode.cs b/src/Discord.Net.Commands/Map/CommandMapNode.cs new file mode 100644 index 000000000..1ce0b4724 --- /dev/null +++ b/src/Discord.Net.Commands/Map/CommandMapNode.cs @@ -0,0 +1,95 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Discord.Commands +{ + internal class CommandMapNode + { + private readonly ConcurrentDictionary _nodes; + private readonly string _name; + private ImmutableArray _commands; + + public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0; + + public CommandMapNode(string name) + { + _name = name; + _nodes = new ConcurrentDictionary(); + _commands = ImmutableArray.Create(); + } + + public void AddCommand(string text, int index, Command command) + { + int nextSpace = text.IndexOf(' ', index); + string name; + + lock (this) + { + if (text == "") + _commands = _commands.Add(command); + else + { + if (nextSpace == -1) + name = text.Substring(index); + else + name = text.Substring(index, nextSpace - index); + + var nextNode = _nodes.GetOrAdd(name, x => new CommandMapNode(x)); + nextNode.AddCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + } + } + } + public void RemoveCommand(string text, int index, Command command) + { + int nextSpace = text.IndexOf(' ', index); + string name; + + lock (this) + { + if (text == "") + _commands = _commands.Remove(command); + else + { + if (nextSpace == -1) + name = text.Substring(index); + else + name = text.Substring(index, nextSpace - index); + + CommandMapNode nextNode; + if (_nodes.TryGetValue(name, out nextNode)) + { + nextNode.RemoveCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + if (nextNode.IsEmpty) + _nodes.TryRemove(name, out nextNode); + } + } + } + } + + public IEnumerable GetCommands(string text, int index) + { + int nextSpace = text.IndexOf(' ', index); + string name; + + var commands = _commands; + for (int i = 0; i < commands.Length; i++) + yield return _commands[i]; + + if (text != "") + { + if (nextSpace == -1) + name = text.Substring(index); + else + name = text.Substring(index, nextSpace - index); + + CommandMapNode nextNode; + if (_nodes.TryGetValue(name, out nextNode)) + { + foreach (var cmd in nextNode.GetCommands(nextSpace == -1 ? "" : text, nextSpace + 1)) + yield return cmd; + } + } + } + } +}