diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 010a0ee8a..c15fda334 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -22,6 +22,7 @@ namespace Discord.Commands internal readonly bool _caseSensitive; internal readonly RunMode _defaultRunMode; + internal readonly char _splitCharacter; public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); @@ -60,6 +61,7 @@ namespace Discord.Commands _caseSensitive = config.CaseSensitiveCommands; _defaultRunMode = config.DefaultRunMode; + _splitCharacter = config.CommandSplitCharacter; } //Modules @@ -129,7 +131,7 @@ namespace Discord.Commands _moduleDefs.Add(module); foreach (var command in module.Commands) - _map.AddCommand(command); + _map.AddCommand(command, this); foreach (var submodule in module.Submodules) LoadModuleInternal(submodule); @@ -173,7 +175,7 @@ namespace Discord.Commands return false; foreach (var cmd in module.Commands) - _map.RemoveCommand(cmd); + _map.RemoveCommand(cmd, this); foreach (var submodule in module.Submodules) { @@ -214,7 +216,7 @@ namespace Discord.Commands public SearchResult Search(CommandContext context, string input) { string searchInput = _caseSensitive ? input : input.ToLowerInvariant(); - var matches = _map.GetCommands(searchInput).OrderByDescending(x => x.Priority).ToImmutableArray(); + var matches = _map.GetCommands(searchInput, this).OrderByDescending(x => x.Priority).ToImmutableArray(); if (matches.Length > 0) return SearchResult.FromSuccess(input, matches); diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 8377d4e60..7cba56515 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -6,5 +6,7 @@ public RunMode DefaultRunMode { get; set; } = RunMode.Sync; /// Should commands be case-sensitive? public bool CaseSensitiveCommands { get; set; } = false; + /// The character which splits commands + public char CommandSplitCharacter { get; set; } = ' '; } } diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index a6ac50005..1e41a0aa9 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -44,7 +44,7 @@ namespace Discord.Commands // both command and module provide aliases if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) - Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + " " + second : first).Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); + Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + service._splitCharacter + second : first).Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); // only module provides aliases else if (module.Aliases.Count > 0) Aliases = module.Aliases.Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index ab4f65713..65417e3fd 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -30,14 +30,14 @@ namespace Discord.Commands Remarks = builder.Remarks; Parent = parent; - Aliases = BuildAliases(builder).ToImmutableArray(); + Aliases = BuildAliases(builder, service).ToImmutableArray(); Commands = builder.Commands.Select(x => x.Build(this, service)); Preconditions = BuildPreconditions(builder).ToImmutableArray(); Submodules = BuildSubmodules(builder, service).ToImmutableArray(); } - private static IEnumerable BuildAliases(ModuleBuilder builder) + private static IEnumerable BuildAliases(ModuleBuilder builder, CommandService service) { IEnumerable result = null; @@ -60,9 +60,9 @@ namespace Discord.Commands result = level.Aliases.ToList(); //create a shallow copy so we don't overwrite the builder unexpectedly } else if (result.Count() > level.Aliases.Count) - result = result.Permutate(level.Aliases, (first, second) => first + " " + second); + result = result.Permutate(level.Aliases, (first, second) => first + service._splitCharacter + second); else - result = level.Aliases.Permutate(result, (second, first) => first + " " + second); + result = level.Aliases.Permutate(result, (second, first) => first + service._splitCharacter + second); } if (result == null) //there were no aliases; default to an empty list diff --git a/src/Discord.Net.Commands/Map/CommandMap.cs b/src/Discord.Net.Commands/Map/CommandMap.cs index 3a5239878..2478f488d 100644 --- a/src/Discord.Net.Commands/Map/CommandMap.cs +++ b/src/Discord.Net.Commands/Map/CommandMap.cs @@ -12,20 +12,20 @@ namespace Discord.Commands _root = new CommandMapNode(""); } - public void AddCommand(CommandInfo command) + public void AddCommand(CommandInfo command, CommandService service) { foreach (string text in GetAliases(command)) - _root.AddCommand(text, 0, command); + _root.AddCommand(service, text, 0, command); } - public void RemoveCommand(CommandInfo command) + public void RemoveCommand(CommandInfo command, CommandService service) { foreach (string text in GetAliases(command)) - _root.RemoveCommand(text, 0, command); + _root.RemoveCommand(service, text, 0, command); } - public IEnumerable GetCommands(string text) + public IEnumerable GetCommands(string text, CommandService service) { - return _root.GetCommands(text, 0); + return _root.GetCommands(service, text, 0); } private IReadOnlyList GetAliases(CommandInfo command) diff --git a/src/Discord.Net.Commands/Map/CommandMapNode.cs b/src/Discord.Net.Commands/Map/CommandMapNode.cs index a86c0643d..c1365f963 100644 --- a/src/Discord.Net.Commands/Map/CommandMapNode.cs +++ b/src/Discord.Net.Commands/Map/CommandMapNode.cs @@ -23,9 +23,9 @@ namespace Discord.Commands _commands = ImmutableArray.Create(); } - public void AddCommand(string text, int index, CommandInfo command) + public void AddCommand(CommandService service, string text, int index, CommandInfo command) { - int nextSpace = NextWhitespace(text, index); + int nextSpace = NextWhitespace(service, text, index); string name; lock (_lockObj) @@ -44,13 +44,13 @@ namespace Discord.Commands name = text.Substring(index, nextSpace - index); var nextNode = _nodes.GetOrAdd(name, x => new CommandMapNode(x)); - nextNode.AddCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + nextNode.AddCommand(service, nextSpace == -1 ? "" : text, nextSpace + 1, command); } } } - public void RemoveCommand(string text, int index, CommandInfo command) + public void RemoveCommand(CommandService service, string text, int index, CommandInfo command) { - int nextSpace = NextWhitespace(text, index); + int nextSpace = NextWhitespace(service, text, index); string name; lock (_lockObj) @@ -67,7 +67,7 @@ namespace Discord.Commands CommandMapNode nextNode; if (_nodes.TryGetValue(name, out nextNode)) { - nextNode.RemoveCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + nextNode.RemoveCommand(service, nextSpace == -1 ? "" : text, nextSpace + 1, command); if (nextNode.IsEmpty) _nodes.TryRemove(name, out nextNode); } @@ -75,32 +75,57 @@ namespace Discord.Commands } } - public IEnumerable GetCommands(string text, int index) + public IEnumerable GetCommands(CommandService service, string text, int index) { - int nextSpace = NextWhitespace(text, index); - string name; - - var commands = _commands; - for (int i = 0; i < commands.Length; i++) - yield return _commands[i]; + int nextCommand = NextCommandSegment(service, text, index); + string name = null; - if (text != "") + //got all command segments or base-level command + if (nextCommand == -1) { - if (nextSpace == -1) - name = text.Substring(index); - else + var commands = _commands; + for (int i = 0; i < commands.Length; i++) + yield return _commands[i]; + + //are we a base-level command? + int nextSpace = NextWhitespace(service, text, index); + if (nextSpace != -1) + { name = text.Substring(index, nextSpace - index); + } + else + { + name = text.Substring(index); + } + } + else + { + name = text.Substring(index, nextCommand - index); + } + if (name != null) + { CommandMapNode nextNode; if (_nodes.TryGetValue(name, out nextNode)) { - foreach (var cmd in nextNode.GetCommands(nextSpace == -1 ? "" : text, nextSpace + 1)) + foreach (var cmd in nextNode.GetCommands(service, text, nextCommand + 1)) yield return cmd; } } } - private static int NextWhitespace(string text, int startIndex) + private static int NextCommandSegment(CommandService service, string text, int startIndex) + { + int lowest = int.MaxValue; + + int index = text.IndexOf(service._splitCharacter, startIndex); + if (index != -1 && index < lowest) + lowest = index; + + return (lowest != int.MaxValue) ? lowest : -1; + } + + private static int NextWhitespace(CommandService service, string text, int startIndex) { int lowest = int.MaxValue; for (int i = 0; i < _whitespaceChars.Length; i++) @@ -109,6 +134,7 @@ namespace Discord.Commands if (index != -1 && index < lowest) lowest = index; } + return (lowest != int.MaxValue) ? lowest : -1; } }