From fd6620a950ebac0794a5eddbc38ce13b6bfc00ae Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 6 Apr 2017 23:36:50 +0100 Subject: [PATCH] Add basic help search --- src/command/cmd_ac.c | 1 + src/command/cmd_defs.c | 81 ++++++++++++++++++++++++++++++++++++-- src/command/cmd_defs.h | 2 + src/command/cmd_funcs.c | 87 ++++++++++++++++++++++++++--------------- src/ui/console.c | 2 +- 5 files changed, 137 insertions(+), 36 deletions(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 9d0ee08b..60c2762b 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -204,6 +204,7 @@ cmd_ac_init(void) help_ac = autocomplete_new(); autocomplete_add(help_ac, "commands"); autocomplete_add(help_ac, "navigation"); + autocomplete_add(help_ac, "search"); help_commands_ac = autocomplete_new(); autocomplete_add(help_commands_ac, "chat"); diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index d5eba7c8..9c320a4f 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -113,12 +113,12 @@ static gboolean _cmd_has_tag(Command *pcmd, const char *const tag); static struct cmd_t command_defs[] = { { "/help", - parse_args, 0, 2, NULL, + parse_args_with_freetext, 0, 2, NULL, CMD_NOSUBFUNCS CMD_MAINFUNC(cmd_help) CMD_NOTAGS CMD_SYN( - "/help [|]") + "/help [||search] []") CMD_DESC( "Help on using Profanity. Passing no arguments list help areas. " "For command help, optional arguments are shown using square brackets, " @@ -127,8 +127,10 @@ static struct cmd_t command_defs[] = "e.g. val1|val2|val3.") CMD_ARGS( { "", "Summary help for commands in a certain area of functionality." }, - { "", "Full help for a specific command, for example '/help connect'." }) + { "", "Full help for a specific command, for example '/help connect'." }, + { "search ", "Search commands for search_term." }) CMD_EXAMPLES( + "/help search presence show", "/help commands", "/help presence", "/help who") @@ -2264,9 +2266,75 @@ static struct cmd_t command_defs[] = CMD_EXAMPLES( "/export /path/to/output.csv", "/export ~/contacts.csv") - }, + } }; +static GHashTable *search_index; + +char* +_cmd_index(Command *cmd) { + GString *index_source = g_string_new(""); + index_source = g_string_append(index_source, cmd->cmd); + index_source = g_string_append(index_source, " "); + index_source = g_string_append(index_source, cmd->help.desc); + index_source = g_string_append(index_source, " "); + + int len = g_strv_length(cmd->help.tags); + int i = 0; + for (i = 0; i < len; i++) { + index_source = g_string_append(index_source, cmd->help.tags[i]); + index_source = g_string_append(index_source, " "); + } + len = g_strv_length(cmd->help.synopsis); + for (i = 0; i < len; i++) { + index_source = g_string_append(index_source, cmd->help.synopsis[i]); + index_source = g_string_append(index_source, " "); + } + for (i = 0; cmd->help.args[i][0] != NULL; i++) { + index_source = g_string_append(index_source, cmd->help.args[i][0]); + index_source = g_string_append(index_source, " "); + index_source = g_string_append(index_source, cmd->help.args[i][1]); + index_source = g_string_append(index_source, " "); + } + + gchar **tokens = g_str_tokenize_and_fold(index_source->str, NULL, NULL); + + GString *index = g_string_new(""); + i = 0; + for (i = 0; i < g_strv_length(tokens); i++) { + index = g_string_append(index, tokens[i]); + index = g_string_append(index, " "); + } + + char *res = index->str; + g_string_free(index, FALSE); + + return res; +} + +GList* +cmd_search_index(char *term) +{ + GList *results = NULL; + + gchar **processed_terms = g_str_tokenize_and_fold(term, NULL, NULL); + int terms_len = g_strv_length(processed_terms); + + int i = 0; + for (i = 0; i < terms_len; i++) { + GList *index_keys = g_hash_table_get_keys(search_index); + GList *curr = index_keys; + while (curr) { + char *index_entry = g_hash_table_lookup(search_index, curr->data); + if (g_str_match_string(processed_terms[i], index_entry, FALSE)) { + results = g_list_append(results, curr->data); + } + curr = g_list_next(curr); + } + } + + return results; +} /* * Initialise command autocompleter and history @@ -2278,6 +2346,8 @@ cmd_init(void) cmd_ac_init(); + search_index = g_hash_table_new_full(g_str_hash, g_str_equal, free, free); + // load command defs into hash table commands = g_hash_table_new(g_str_hash, g_str_equal); unsigned int i; @@ -2287,6 +2357,9 @@ cmd_init(void) // add to hash g_hash_table_insert(commands, pcmd->cmd, pcmd); + // add to search index + g_hash_table_insert(search_index, strdup(pcmd->cmd), strdup(_cmd_index(pcmd))); + // add to commands and help autocompleters cmd_ac_add_cmd(pcmd); } diff --git a/src/command/cmd_defs.h b/src/command/cmd_defs.h index 074970c9..286bad0d 100644 --- a/src/command/cmd_defs.h +++ b/src/command/cmd_defs.h @@ -49,4 +49,6 @@ gboolean cmd_valid_tag(const char *const str); void command_docgen(void); +GList* cmd_search_index(char *term); + #endif diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index a2d2571f..fea2d55b 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -1484,6 +1484,41 @@ cmd_win(ProfWin *window, const char *const command, gchar **args) return TRUE; } +static void +_cmd_list_commands(GList *commands) { + int maxlen = 0; + GList *curr = commands; + while (curr) { + gchar *cmd = curr->data; + int len = strlen(cmd); + if (len > maxlen) maxlen = len; + curr = g_list_next(curr); + } + + GString *cmds = g_string_new(""); + curr = commands; + int count = 0; + while (curr) { + gchar *cmd = curr->data; + if (count == 5) { + cons_show(cmds->str); + g_string_free(cmds, TRUE); + cmds = g_string_new(""); + count = 0; + } + g_string_append_printf(cmds, "%-*s", maxlen + 1, cmd); + curr = g_list_next(curr); + count++; + } + cons_show(cmds->str); + g_string_free(cmds, TRUE); + g_list_free(curr); + + cons_show(""); + cons_show("Use /help [command] without the leading slash, for help on a specific command"); + cons_show(""); +} + static void _cmd_help_cmd_list(const char *const tag) { @@ -1520,38 +1555,8 @@ _cmd_help_cmd_list(const char *const tag) } } - int maxlen = 0; - GList *curr = ordered_commands; - while (curr) { - gchar *cmd = curr->data; - int len = strlen(cmd); - if (len > maxlen) maxlen = len; - curr = g_list_next(curr); - } - - GString *cmds = g_string_new(""); - curr = ordered_commands; - int count = 0; - while (curr) { - gchar *cmd = curr->data; - if (count == 5) { - cons_show(cmds->str); - g_string_free(cmds, TRUE); - cmds = g_string_new(""); - count = 0; - } - g_string_append_printf(cmds, "%-*s", maxlen + 1, cmd); - curr = g_list_next(curr); - count++; - } - cons_show(cmds->str); - g_string_free(cmds, TRUE); + _cmd_list_commands(ordered_commands); g_list_free(ordered_commands); - g_list_free(curr); - - cons_show(""); - cons_show("Use /help [command] without the leading slash, for help on a specific command"); - cons_show(""); } gboolean @@ -1560,6 +1565,26 @@ cmd_help(ProfWin *window, const char *const command, gchar **args) int num_args = g_strv_length(args); if (num_args == 0) { cons_help(); + } else if (strcmp(args[0], "search") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + } else { + GList *cmds = cmd_search_index(args[1]); + if (cmds == NULL) { + cons_show("No commands found."); + } else { + GList *curr = cmds; + GList *results = NULL; + while (curr) { + results = g_list_insert_sorted(results, curr->data, (GCompareFunc)g_strcmp0); + curr = g_list_next(curr); + } + cons_show("Search results:"); + _cmd_list_commands(results); + g_list_free(results); + } + g_list_free(cmds); + } } else if (strcmp(args[0], "commands") == 0) { if (args[1]) { if (!cmd_valid_tag(args[1])) { diff --git a/src/ui/console.c b/src/ui/console.c index 3b904c62..cb5a10b0 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -142,7 +142,7 @@ cons_show_help(const char *const cmd, CommandHelp *help) if (g_strv_length((gchar**)help->examples) > 0) { cons_show(""); - win_println(console, THEME_HELP_HEADER, '-', "Arguments"); + win_println(console, THEME_HELP_HEADER, '-', "Examples"); ui_show_lines(console, help->examples); } }