Implemented new CommandMap
This commit is contained in:
@@ -14,8 +14,8 @@ namespace Discord.Commands
|
||||
{
|
||||
private readonly SemaphoreSlim _moduleLock;
|
||||
private readonly ConcurrentDictionary<object, Module> _modules;
|
||||
private readonly ConcurrentDictionary<string, List<Command>> _map;
|
||||
private readonly ConcurrentDictionary<Type, TypeReader> _typeReaders;
|
||||
private readonly CommandMap _map;
|
||||
|
||||
public IEnumerable<Module> Modules => _modules.Select(x => x.Value);
|
||||
public IEnumerable<Command> Commands => _modules.SelectMany(x => x.Value.Commands);
|
||||
@@ -24,7 +24,7 @@ namespace Discord.Commands
|
||||
{
|
||||
_moduleLock = new SemaphoreSlim(1, 1);
|
||||
_modules = new ConcurrentDictionary<object, Module>();
|
||||
_map = new ConcurrentDictionary<string, List<Command>>();
|
||||
_map = new CommandMap();
|
||||
_typeReaders = new ConcurrentDictionary<Type, TypeReader>
|
||||
{
|
||||
[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<Command>());
|
||||
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<Command> 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<Command>.Builder matches = null;
|
||||
List<Command> 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<Command>(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.");
|
||||
}
|
||||
|
||||
76
src/Discord.Net.Commands/Map/CommandMap.cs
Normal file
76
src/Discord.Net.Commands/Map/CommandMap.cs
Normal file
@@ -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<string, CommandMapNode> _nodes;
|
||||
|
||||
public CommandMap()
|
||||
{
|
||||
_nodes = new ConcurrentDictionary<string, CommandMapNode>();
|
||||
}
|
||||
|
||||
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<Command> 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<Command>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
src/Discord.Net.Commands/Map/CommandMapNode.cs
Normal file
95
src/Discord.Net.Commands/Map/CommandMapNode.cs
Normal file
@@ -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<string, CommandMapNode> _nodes;
|
||||
private readonly string _name;
|
||||
private ImmutableArray<Command> _commands;
|
||||
|
||||
public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0;
|
||||
|
||||
public CommandMapNode(string name)
|
||||
{
|
||||
_name = name;
|
||||
_nodes = new ConcurrentDictionary<string, CommandMapNode>();
|
||||
_commands = ImmutableArray.Create<Command>();
|
||||
}
|
||||
|
||||
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<Command> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user