PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/utils/include/lilyengine/graphicalconsole/graphicalconsole_autocomplete.h

http://github.com/ExpiredPopsicle/Lily-Engine-Utils
C Header | 403 lines | 251 code | 81 blank | 71 comment | 56 complexity | 18cb2aa342feed35cbaf9641cf3a05ff MD5 | raw file
  1. // ---------------------------------------------------------------------------
  2. //
  3. // Lily Engine Utils
  4. //
  5. // Copyright (c) 2012-2018 Kiri Jolly
  6. // http://expiredpopsicle.com
  7. // expiredpopsicle@gmail.com
  8. //
  9. // ---------------------------------------------------------------------------
  10. //
  11. // This software is provided 'as-is', without any express or implied
  12. // warranty. In no event will the authors be held liable for any
  13. // damages arising from the use of this software.
  14. //
  15. // Permission is granted to anyone to use this software for any
  16. // purpose, including commercial applications, and to alter it and
  17. // redistribute it freely, subject to the following restrictions:
  18. //
  19. // 1. The origin of this software must not be misrepresented; you must
  20. // not claim that you wrote the original software. If you use this
  21. // software in a product, an acknowledgment in the product
  22. // documentation would be appreciated but is not required.
  23. //
  24. // 2. Altered source versions must be plainly marked as such, and must
  25. // not be misrepresented as being the original software.
  26. //
  27. // 3. This notice may not be removed or altered from any source
  28. // distribution.
  29. //
  30. // -------------------------- END HEADER -------------------------------------
  31. // GraphicalConsole internal implementation.
  32. // ----------------------------------------------------------------------
  33. // Implementation
  34. // ----------------------------------------------------------------------
  35. #pragma once
  36. namespace ExPop
  37. {
  38. inline std::vector<std::string> graphicalConsoleFileTreeGenerator(
  39. const std::string &path,
  40. const std::string &matchExtension = "",
  41. bool includeDirectories = false)
  42. {
  43. std::vector<std::string> ret;
  44. std::vector<std::string> subdirs;
  45. ExPop::FileSystem::getSubdirectories(path, subdirs);
  46. for(size_t i = 0; i < subdirs.size(); i++) {
  47. std::vector<std::string> subdirContents =
  48. graphicalConsoleFileTreeGenerator(
  49. path + "/" + subdirs[i],
  50. matchExtension,
  51. includeDirectories);
  52. if(includeDirectories) {
  53. ret.push_back(subdirs[i]);
  54. }
  55. for(size_t k = 0; k < subdirContents.size(); k++) {
  56. ret.push_back(subdirs[i] + "/" + subdirContents[k]);
  57. }
  58. }
  59. std::vector<std::string> files;
  60. ExPop::FileSystem::getNondirectories(path, files);
  61. for(size_t k = 0; k < files.size(); k++) {
  62. if(matchExtension.size() && !ExPop::stringEndsWith("." + matchExtension, files[k])) {
  63. continue;
  64. }
  65. ret.push_back(files[k]);
  66. }
  67. return ret;
  68. }
  69. inline std::function<std::vector<std::string>()> graphicalConsoleFileTreeGeneratorGenerator(
  70. const std::string &path,
  71. const std::string &matchExtension,
  72. bool includeDirectories)
  73. {
  74. return
  75. std::bind(
  76. &graphicalConsoleFileTreeGenerator,
  77. std::string(path),
  78. std::string(matchExtension),
  79. includeDirectories);
  80. }
  81. template<typename T>
  82. inline void GraphicalConsole::setContextualAutocompleteGenerator(
  83. const std::string &commandOrCvarName,
  84. size_t parameterNumber,
  85. std::function<std::vector<T>()> function)
  86. {
  87. std::basic_string<uint32_t> utf32Name =
  88. ExPop::stringUTF8ToUTF32(commandOrCvarName);
  89. ContextualAutocompleter_Base *&autoCompleterPtr =
  90. contextualAutocompleters[utf32Name][parameterNumber];
  91. if(autoCompleterPtr) {
  92. delete autoCompleterPtr;
  93. }
  94. ContextualAutocompleter<T> *realCompleter = new ContextualAutocompleter<T>;
  95. realCompleter->func = function;
  96. autoCompleterPtr = realCompleter;
  97. }
  98. inline void GraphicalConsole::clearContextualAutocompleteGenerator(
  99. const std::string &commandOrCvarName,
  100. size_t parameterNumber)
  101. {
  102. std::basic_string<uint32_t> utf32Name =
  103. ExPop::stringUTF8ToUTF32(commandOrCvarName);
  104. auto a = contextualAutocompleters.find(utf32Name);
  105. if(a != contextualAutocompleters.end()) {
  106. auto b = a->second.find(parameterNumber);
  107. if(b != a->second.end()) {
  108. delete b->second;
  109. a->second.erase(b);
  110. }
  111. if(a->second.size() == 0) {
  112. contextualAutocompleters.erase(a);
  113. }
  114. }
  115. }
  116. inline GraphicalConsole::ContextualAutocompleter_Base::~ContextualAutocompleter_Base()
  117. {
  118. }
  119. template<typename T>
  120. inline GraphicalConsole::ContextualAutocompleter<T>::~ContextualAutocompleter<T>()
  121. {
  122. }
  123. inline std::vector<std::string> GraphicalConsole::ContextualAutocompleter_Base::getResultsAsStrings()
  124. {
  125. return std::vector<std::string>();
  126. }
  127. template<typename T>
  128. inline std::vector<std::string> GraphicalConsole::ContextualAutocompleter<T>::getResultsAsStrings()
  129. {
  130. std::vector<T> realValues = func();
  131. std::vector<std::string> strings;
  132. for(size_t i = 0; i < realValues.size(); i++) {
  133. std::ostringstream ostr;
  134. ostr << realValues[i];
  135. strings.push_back(ostr.str());
  136. }
  137. return strings;
  138. }
  139. template<typename T>
  140. inline std::vector<std::basic_string<uint32_t> > graphicalConsoleFindCompletions(
  141. const std::map<std::basic_string<uint32_t>, T> &inputMap,
  142. const std::basic_string<uint32_t> &inputPartialString,
  143. std::basic_string<uint32_t> &outputLongestMatch)
  144. {
  145. std::vector<std::basic_string<uint32_t> > completionList;
  146. auto startOfRange = inputMap.lower_bound(inputPartialString);
  147. if(startOfRange == inputMap.end()) {
  148. // Nothing found.
  149. outputLongestMatch = inputPartialString;
  150. return completionList;
  151. }
  152. outputLongestMatch = startOfRange->first;
  153. // Check to see if our first entry is even something
  154. // completable from this.
  155. if(outputLongestMatch.substr(0, inputPartialString.size()) != inputPartialString) {
  156. outputLongestMatch.clear();
  157. }
  158. auto i = startOfRange;
  159. while(i != inputMap.end() &&
  160. i->first.substr(0, inputPartialString.size()) == inputPartialString)
  161. {
  162. size_t n;
  163. for(n = 0; n < i->first.size() && n < outputLongestMatch.size(); n++) {
  164. if(outputLongestMatch[n] != i->first[n]) {
  165. // Found a point where they're different. Chop off the
  166. // different part.
  167. outputLongestMatch = outputLongestMatch.substr(0, n);
  168. break;
  169. }
  170. }
  171. // Reached the end of the second one before the first one.
  172. // Better chop longestCommonSubstring down.
  173. if(n < outputLongestMatch.size()) {
  174. outputLongestMatch = outputLongestMatch.substr(0, n);
  175. }
  176. completionList.push_back(i->first);
  177. i++;
  178. }
  179. return completionList;
  180. }
  181. inline std::basic_string<uint32_t> graphicalConsoleQuoteAndEscapeIfNeeded(const std::basic_string<uint32_t> &in)
  182. {
  183. // If there's any whitespace in here, then we need quotes.
  184. bool needsQuotes = false;
  185. bool needsEscapes = false;
  186. for(size_t i = 0; i < in.size(); i++) {
  187. if(ExPop::isWhiteSpace(in[i])) {
  188. needsQuotes = true;
  189. }
  190. if(in[i] < ' ' || in[i] >= 127) {
  191. needsEscapes = true;
  192. }
  193. }
  194. std::basic_string<uint32_t> ret = in;
  195. if(needsEscapes || needsQuotes) {
  196. std::basic_string<uint32_t> quoteStr = ExPop::stringUTF8ToUTF32("\"");
  197. // FIXME: ExPop::stringEscape() is broken for UTF-32.
  198. ret = quoteStr + ExPop::stringUTF8ToUTF32(ExPop::stringEscape(ExPop::stringUTF32ToUTF8(ret))) + quoteStr;
  199. }
  200. return ret;
  201. }
  202. inline void GraphicalConsole::editAutocomplete()
  203. {
  204. std::basic_string<uint32_t> editLineSubPart = editLineBuffer.substr(0, editLineCursorLocation);
  205. std::basic_string<uint32_t> editLineRestOfStuff = editLineBuffer.substr(editLineCursorLocation);
  206. std::vector<std::string> splitParts =
  207. graphicalConsoleSplitLine(
  208. ExPop::stringUTF32ToUTF8(editLineSubPart));
  209. // If we end on a space, stick an empty slot in the end.
  210. if(editLineSubPart.size() && ExPop::isWhiteSpace(editLineSubPart[editLineSubPart.size() - 1])) {
  211. splitParts.push_back("");
  212. }
  213. // Extract the partially complete input.
  214. std::basic_string<uint32_t> inputPartial =
  215. splitParts.size() ?
  216. ExPop::stringUTF8ToUTF32(splitParts[splitParts.size() - 1]) :
  217. ExPop::stringUTF8ToUTF32("");
  218. std::basic_string<uint32_t> longestCommonSubstring = inputPartial;
  219. if(splitParts.size() <= 1) {
  220. // Special case for the first thing in the list. Complete
  221. // to a command or cvar.
  222. std::vector<std::basic_string<uint32_t> > completionList =
  223. graphicalConsoleFindCompletions(
  224. commandsAndCvars,
  225. inputPartial,
  226. longestCommonSubstring);
  227. // Output the list if we have many matches.
  228. if(completionList.size() > 1) {
  229. out << "Completions for \""
  230. << ExPop::stringUTF32ToUTF8(inputPartial)
  231. << "\":" << std::endl;
  232. for(size_t i = 0; i < completionList.size(); i++) {
  233. CommandOrVariableEntry_Base *entry = commandsAndCvars[completionList[i]];
  234. out << " "
  235. << ExPop::stringUTF32ToUTF8(completionList[i])
  236. << " "
  237. << (entry->type == CommandOrVariableEntry_Base::COMMAND ? "(command)" : "(cvar)")
  238. << std::endl;
  239. }
  240. }
  241. // Actually complete as much as we can.
  242. if(longestCommonSubstring.size() >= inputPartial.size()) {
  243. editLineBuffer = longestCommonSubstring;
  244. editLineCursorLocation = editLineBuffer.size();
  245. // If we only have a single thing matching, then just
  246. // add the space automatically.
  247. if(completionList.size() == 1 && !editLineRestOfStuff.size()) {
  248. editLineBuffer.append(1, ' ');
  249. editLineCursorLocation++;
  250. }
  251. // Append back whatever was in the rest of the line.
  252. editLineBuffer += editLineRestOfStuff;
  253. }
  254. } else {
  255. // Attempt to find an autocompleter for the command
  256. // specified at the beginning.
  257. auto autoCompleterItr1 = contextualAutocompleters.find(ExPop::stringUTF8ToUTF32(splitParts[0]));
  258. if(autoCompleterItr1 != contextualAutocompleters.end() && splitParts.size() >= 2) {
  259. // Find a completer for the specific parameter index.
  260. auto autoCompleterItr2 = autoCompleterItr1->second.find(splitParts.size() - 2);
  261. if(autoCompleterItr2 != autoCompleterItr1->second.end()) {
  262. // Run the auto completer function and shove everything
  263. // into a std::map.
  264. std::map<std::basic_string<uint32_t>, bool> completionMap;
  265. std::vector<std::string> completionVec =
  266. autoCompleterItr2->second->getResultsAsStrings();
  267. for(size_t i = 0; i < completionVec.size(); i++) {
  268. completionMap[ExPop::stringUTF8ToUTF32(completionVec[i])] = true;
  269. }
  270. // Build completion list from that map.
  271. std::vector<std::basic_string<uint32_t> > completionList =
  272. graphicalConsoleFindCompletions(
  273. completionMap,
  274. inputPartial,
  275. longestCommonSubstring);
  276. // Output the list if we have many matches.
  277. if(completionList.size() > 1) {
  278. out << "Completions for \""
  279. << ExPop::stringUTF32ToUTF8(inputPartial)
  280. << "\":" << std::endl;
  281. for(size_t i = 0; i < completionList.size(); i++) {
  282. out << " "
  283. << ExPop::stringUTF32ToUTF8(completionList[i])
  284. << std::endl;
  285. }
  286. }
  287. // Actually complete as much as we can.
  288. if(longestCommonSubstring.size() >= inputPartial.size()) {
  289. std::basic_ostringstream<uint32_t> lineStr;
  290. // Add the command first.
  291. if(splitParts.size() > 0) {
  292. lineStr << graphicalConsoleQuoteAndEscapeIfNeeded(
  293. ExPop::stringUTF8ToUTF32(splitParts[0]));
  294. }
  295. // Add all the parameters back.
  296. for(size_t i = 1; i < splitParts.size() - 1; i++) {
  297. lineStr << ExPop::stringUTF8ToUTF32(" ") << graphicalConsoleQuoteAndEscapeIfNeeded(
  298. ExPop::stringUTF8ToUTF32(splitParts[i]));
  299. }
  300. // Add our mostly-completed string.
  301. editLineBuffer = lineStr.str() + ExPop::stringUTF8ToUTF32(" ") + longestCommonSubstring;
  302. // Move the cursor to what is currently the
  303. // end of the line (so it appears that text
  304. // was inserted and the cursor moved).
  305. editLineCursorLocation = editLineBuffer.size();
  306. // If we only have a single thing matching,
  307. // then just add the space automatically to
  308. // separate it from the next parameter.
  309. if(completionList.size() == 1 && !editLineRestOfStuff.size()) {
  310. editLineBuffer.append(1, ' ');
  311. editLineCursorLocation++;
  312. }
  313. // Append back whatever was in the rest of the
  314. // line.
  315. editLineBuffer += editLineRestOfStuff;
  316. }
  317. }
  318. }
  319. }
  320. backBufferIsDirty = true;
  321. }
  322. }