Implement configurable command node separatorstags/1.0-rc
| @@ -22,6 +22,7 @@ namespace Discord.Commands | |||
| internal readonly bool _caseSensitive; | |||
| internal readonly RunMode _defaultRunMode; | |||
| internal readonly char _splitCharacter; | |||
| public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x); | |||
| public IEnumerable<CommandInfo> 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); | |||
| @@ -6,5 +6,7 @@ | |||
| public RunMode DefaultRunMode { get; set; } = RunMode.Sync; | |||
| /// <summary> Should commands be case-sensitive? </summary> | |||
| public bool CaseSensitiveCommands { get; set; } = false; | |||
| /// <summary> The character which splits commands </summary> | |||
| public char CommandSplitCharacter { get; set; } = ' '; | |||
| } | |||
| } | |||
| @@ -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(); | |||
| @@ -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<string> BuildAliases(ModuleBuilder builder) | |||
| private static IEnumerable<string> BuildAliases(ModuleBuilder builder, CommandService service) | |||
| { | |||
| IEnumerable<string> 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 | |||
| @@ -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<CommandInfo> GetCommands(string text) | |||
| public IEnumerable<CommandInfo> GetCommands(string text, CommandService service) | |||
| { | |||
| return _root.GetCommands(text, 0); | |||
| return _root.GetCommands(service, text, 0); | |||
| } | |||
| private IReadOnlyList<string> GetAliases(CommandInfo command) | |||
| @@ -23,9 +23,9 @@ namespace Discord.Commands | |||
| _commands = ImmutableArray.Create<CommandInfo>(); | |||
| } | |||
| 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<CommandInfo> GetCommands(string text, int index) | |||
| public IEnumerable<CommandInfo> 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; | |||
| } | |||
| } | |||