From 37ee095f4a81dfc4b092acb8029621c4745699ed Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 20 Aug 2020 17:47:36 +0100 Subject: [PATCH] Add basic console tab completion --- src/console_gui.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/console_gui.cpp b/src/console_gui.cpp index 50ce46b342..34f6d1041a 100644 --- a/src/console_gui.cpp +++ b/src/console_gui.cpp @@ -154,6 +154,7 @@ static inline void IConsoleResetHistoryPos() static const char *IConsoleHistoryAdd(const char *cmd); static void IConsoleHistoryNavigate(int direction); +static void IConsoleTabCompletion(); static const struct NWidgetPart _nested_console_window_widgets[] = { NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_BACKGROUND), SetResize(1, 1), @@ -299,6 +300,10 @@ struct IConsoleWindow : Window IConsoleCmdExec("clear"); break; + case WKC_TAB: + IConsoleTabCompletion(); + break; + default: if (_iconsole_cmdline.HandleKeyPress(key, keycode) != HKPR_NOT_HANDLED) { IConsoleWindow::scroll = 0; @@ -494,6 +499,69 @@ static void IConsoleHistoryNavigate(int direction) } } +static void IConsoleTabCompletion() +{ + const char *input = _iconsole_cmdline.buf; + + /* Strip all spaces at the beginning */ + while (IsWhitespace(*input)) input++; + + /* Don't do tab completion for no input */ + if (StrEmpty(input)) return; + + const char *cmdptr = input; + for (; *cmdptr != '\0'; cmdptr++) { + switch (*cmdptr) { + case ' ': + case '"': + case '\\': + // Give up + return; + } + } + size_t length = cmdptr - input; + char *prefix = (char*)alloca(length + 1); + strecpy(prefix, input, prefix + length); + RemoveUnderscores(prefix); + size_t prefix_length = strlen(prefix); + + if (prefix_length == 0) return; + + char buffer[4096]; + char *b = buffer; + uint matches = 0; + std::string common_prefix; + for (const IConsoleCmd *cmd = _iconsole_cmds; cmd != nullptr; cmd = cmd->next) { + if (strncmp(cmd->name, prefix, prefix_length) == 0) { + if ((_settings_client.gui.console_show_unlisted || !cmd->unlisted) && (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE)) { + if (matches == 0) { + common_prefix = cmd->name; + } else { + const char *cp = common_prefix.c_str(); + const char *cmdp = cmd->name; + while (true) { + const char *end = cmdp; + WChar a = Utf8Consume(cp); + WChar b = Utf8Consume(cmdp); + if (a == 0 || b == 0 || a != b) { + common_prefix.resize(end - cmd->name); + break; + } + } + } + matches++; + b += seprintf(b, lastof(buffer), "%s ", cmd->name); + } + } + } + if (matches > 0) { + _iconsole_cmdline.Assign(common_prefix.c_str()); + if (matches > 1) { + IConsolePrint(CC_WHITE, buffer); + } + } +} + /** * Handle the printing of text entered into the console or redirected there * by any other means. Text can be redirected to other clients in a network game