You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

CommandMapNode.cs 5.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Collections.Immutable;
  5. namespace Discord.Commands
  6. {
  7. internal class CommandMapNode
  8. {
  9. private static readonly char[] WhitespaceChars = { ' ', '\r', '\n' };
  10. private readonly ConcurrentDictionary<string, CommandMapNode> _nodes;
  11. private readonly string _name;
  12. private readonly object _lockObj = new object();
  13. private ImmutableArray<CommandInfo> _commands;
  14. public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0;
  15. public CommandMapNode(string name)
  16. {
  17. _name = name;
  18. _nodes = new ConcurrentDictionary<string, CommandMapNode>();
  19. _commands = ImmutableArray.Create<CommandInfo>();
  20. }
  21. public void AddCommand(CommandService service, string text, int index, CommandInfo command)
  22. {
  23. int nextSegment = NextSegment(text, index, service._separatorChar);
  24. string name;
  25. lock (_lockObj)
  26. {
  27. if (text == "")
  28. {
  29. if (_name == "")
  30. throw new InvalidOperationException("Cannot add commands to the root node.");
  31. _commands = _commands.Add(command);
  32. }
  33. else
  34. {
  35. if (nextSegment == -1)
  36. name = text.Substring(index);
  37. else
  38. name = text.Substring(index, nextSegment - index);
  39. string fullName = _name == "" ? name : _name + service._separatorChar + name;
  40. var nextNode = _nodes.GetOrAdd(name, x => new CommandMapNode(fullName));
  41. nextNode.AddCommand(service, nextSegment == -1 ? "" : text, nextSegment + 1, command);
  42. }
  43. }
  44. }
  45. public void RemoveCommand(CommandService service, string text, int index, CommandInfo command)
  46. {
  47. int nextSegment = NextSegment(text, index, service._separatorChar);
  48. lock (_lockObj)
  49. {
  50. if (text == "")
  51. _commands = _commands.Remove(command);
  52. else
  53. {
  54. string name;
  55. if (nextSegment == -1)
  56. name = text.Substring(index);
  57. else
  58. name = text.Substring(index, nextSegment - index);
  59. if (_nodes.TryGetValue(name, out var nextNode))
  60. {
  61. nextNode.RemoveCommand(service, nextSegment == -1 ? "" : text, nextSegment + 1, command);
  62. if (nextNode.IsEmpty)
  63. _nodes.TryRemove(name, out nextNode);
  64. }
  65. }
  66. }
  67. }
  68. public IEnumerable<CommandMatch> GetCommands(CommandService service, string text, int index, bool visitChildren = true)
  69. {
  70. var commands = _commands;
  71. for (int i = 0; i < commands.Length; i++)
  72. yield return new CommandMatch(_commands[i], _name);
  73. if (visitChildren)
  74. {
  75. string name;
  76. CommandMapNode nextNode;
  77. //Search for next segment
  78. int nextSegment = NextSegment(text, index, service._separatorChar);
  79. if (nextSegment == -1)
  80. name = text.Substring(index);
  81. else
  82. name = text.Substring(index, nextSegment - index);
  83. if (_nodes.TryGetValue(name, out nextNode))
  84. {
  85. foreach (var cmd in nextNode.GetCommands(service, nextSegment == -1 ? "" : text, nextSegment + 1, true))
  86. yield return cmd;
  87. }
  88. //Check if this is the last command segment before args
  89. nextSegment = NextSegment(text, index, WhitespaceChars, service._separatorChar);
  90. if (nextSegment != -1)
  91. {
  92. name = text.Substring(index, nextSegment - index);
  93. if (_nodes.TryGetValue(name, out nextNode))
  94. {
  95. foreach (var cmd in nextNode.GetCommands(service, nextSegment == -1 ? "" : text, nextSegment + 1, false))
  96. yield return cmd;
  97. }
  98. }
  99. }
  100. }
  101. private static int NextSegment(string text, int startIndex, char separator)
  102. {
  103. return text.IndexOf(separator, startIndex);
  104. }
  105. private static int NextSegment(string text, int startIndex, char[] separators, char except)
  106. {
  107. int lowest = int.MaxValue;
  108. for (int i = 0; i < separators.Length; i++)
  109. {
  110. if (separators[i] != except)
  111. {
  112. int index = text.IndexOf(separators[i], startIndex);
  113. if (index != -1 && index < lowest)
  114. lowest = index;
  115. }
  116. }
  117. return (lowest != int.MaxValue) ? lowest : -1;
  118. }
  119. }
  120. }