From 698d988f588227204d5ea47f3597059ac30dc533 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 28 Sep 2013 23:53:05 +0200 Subject: [PATCH] Moved parts of completion logic to ex.c. This change makes it easier to control the found matches and it allowed to complete also abbreviated commands like ':o !tag'. --- src/completion.c | 167 +++++++++-------------------------------------- src/completion.h | 6 +- src/ex.c | 164 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 194 insertions(+), 143 deletions(-) diff --git a/src/completion.c b/src/completion.c index ea48a6d..a0479b4 100644 --- a/src/completion.c +++ b/src/completion.c @@ -20,152 +20,49 @@ #include "config.h" #include "main.h" #include "completion.h" -#include "util.h" -#include "history.h" -#include "bookmark.h" -#include "command.h" -#include "setting.h" -#include "ex.h" - -#define TAG_INDICATOR '!' extern VbCore vb; static struct { GtkWidget *win; GtkWidget *tree; - int count; /* command count before the completed content */ - char *prefix; /* prefix that marks the completion ':', '/', ':open', ... */ int active; /* number of the current active tree item */ - char *text; /* text of the current active tree item */ + CompletionSelectFunc selfunc; } comp; -static void init_completion(GtkTreeModel *model, const char *prefix); -static void show(gboolean back); -static void move_cursor(gboolean back); static gboolean tree_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean selected, gpointer data); -gboolean completion_complete(gboolean back) +gboolean completion_create(GtkTreeModel *model, CompletionSelectFunc selfunc, + gboolean back) { - VbInputType type; - char *input; - const char *prefix, *suffix; - GtkListStore *store = NULL; - GtkTreeModel *model; - gboolean res = false, sort = true; - - /* TODO give the type of completion to this function - because we have to - * handle also abreviated commands like ':op foo' */ - input = vb_get_input_text(); - type = vb_get_input_parts(input, VB_INPUT_ALL, &prefix, &suffix); - if (vb.mode->flags & FLAG_COMPLETION) { - if (comp.text && !strcmp(input, comp.text)) { - /* step through the next/prev completion item */ - move_cursor(back); - g_free(input); - return true; - } - /* if current input isn't the content of the completion item, stop - * completion and start it after that again */ - completion_clean(); - } - - /* create the list store model */ - store = gtk_list_store_new(COMPLETION_STORE_NUM, G_TYPE_STRING, G_TYPE_STRING); - if (type == VB_INPUT_SET) { - res = setting_fill_completion(store, suffix); - } else if (type == VB_INPUT_OPEN || type == VB_INPUT_TABOPEN) { - /* if search string begins with TAG_INDICATOR lookup the bookmarks */ - if (suffix && *suffix == TAG_INDICATOR) { - res = bookmark_fill_completion(store, suffix + 1); - } else { - res = history_fill_completion(store, HISTORY_URL, suffix); - } - sort = false; - } else if (type == VB_INPUT_COMMAND) { - char *command = NULL; - /* remove counts before command and save it to print it later in inputbox */ - comp.count = g_ascii_strtoll(suffix, &command, 10); - - res = ex_fill_completion(store, command); - /* we have a special sorting of the ex commands so we don't should - * reorder them for the completion */ - sort = false; - } else if (type == VB_INPUT_SEARCH_FORWARD || type == VB_INPUT_SEARCH_BACKWARD) { - res = history_fill_completion(store, HISTORY_SEARCH, suffix); - } else if (type == VB_INPUT_BOOKMARK_ADD) { - res = bookmark_fill_tag_completion(store, suffix); - } - - if (!res) { - g_free(input); - return false; - } + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkRequisition size; + GtkTreePath *path; + GtkTreeIter iter; + int height, width; - model = GTK_TREE_MODEL(store); /* if there is only one match - don't build the tree view */ if (gtk_tree_model_iter_n_children(model, NULL) == 1) { char *value; - GtkTreePath *path = gtk_tree_path_new_from_indices(0, -1); - GtkTreeIter iter; + path = gtk_tree_path_new_from_indices(0, -1); if (gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get(model, &iter, COMPLETION_STORE_FIRST, &value, -1); - if (comp.count) { - vb_echo_force(VB_MSG_NORMAL, false, "%s%d%s", prefix, comp.count, value); - } else { - vb_echo_force(VB_MSG_NORMAL, false, "%s%s", prefix, value); - } + /* call the select function */ + selfunc(value); + g_free(value); + g_object_unref(model); - g_object_unref(G_OBJECT(store)); - g_free(input); return false; } } - /* apply the default sorting to the first tree model comlumn */ - if (sort) { - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), COMPLETION_STORE_FIRST, GTK_SORT_ASCENDING); - } - - init_completion(GTK_TREE_MODEL(store), prefix); - show(back); - g_free(input); - - return true; -} - -void completion_clean(void) -{ - if (vb.mode->flags & FLAG_COMPLETION) { - /* remove completion flag from mode */ - vb.mode->flags &= ~FLAG_COMPLETION; - - if (comp.win) { - gtk_widget_destroy(comp.win); - comp.win = comp.tree = NULL; - } - OVERWRITE_STRING(comp.prefix, NULL); - OVERWRITE_STRING(comp.text, NULL); - comp.count = 0; - } -} - -static void init_completion(GtkTreeModel *model, const char *prefix) -{ - GtkCellRenderer *renderer; - GtkTreeSelection *selection; - GtkTreeViewColumn *column; - GtkRequisition size; - int height, width; - - /* set the submode flag */ - vb.mode->flags |= FLAG_COMPLETION; - - OVERWRITE_STRING(comp.prefix, prefix); + comp.selfunc = selfunc; /* prepare the tree view */ comp.win = gtk_scrolled_window_new(NULL, NULL); @@ -241,10 +138,7 @@ static void init_completion(GtkTreeModel *model, const char *prefix) gtk_widget_set_size_request(comp.win, -1, height); } #endif -} -static void show(gboolean back) -{ /* this prevents the first item to be placed out of view if the completion * is shown */ gtk_widget_show_all(comp.win); @@ -254,10 +148,12 @@ static void show(gboolean back) /* set to -1 to have the cursor on first or last item set in move_cursor */ comp.active = -1; - move_cursor(back); + completion_next(back); + + return true; } -static void move_cursor(gboolean back) +void completion_next(gboolean back) { int rows; GtkTreePath *path; @@ -282,6 +178,14 @@ static void move_cursor(gboolean back) gtk_tree_path_free(path); } +void completion_clean(void) +{ + if (comp.win) { + gtk_widget_destroy(comp.win); + comp.win = comp.tree = NULL; + } +} + static gboolean tree_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean selected, gpointer data) { @@ -292,18 +196,9 @@ static gboolean tree_selection_func(GtkTreeSelection *selection, * interested in */ if (!selected && gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get(model, &iter, COMPLETION_STORE_FIRST, &value, -1); - /* save the content of the selected item so wen can access it easy */ - if (comp.text) { - g_free(comp.text); - comp.text = NULL; - } - if (comp.count) { - comp.text = g_strdup_printf("%s%d%s", comp.prefix, comp.count, value); - } else { - comp.text = g_strconcat(comp.prefix, value, NULL); - } - /* print the text also into inputbox */ - vb_set_input_text(comp.text); + + comp.selfunc(value); + g_free(value); } diff --git a/src/completion.h b/src/completion.h index e628b39..ac8ec25 100644 --- a/src/completion.h +++ b/src/completion.h @@ -30,7 +30,11 @@ enum { COMPLETION_STORE_NUM }; +typedef void (*CompletionSelectFunc) (char *match); + +gboolean completion_create(GtkTreeModel *model, CompletionSelectFunc selfunc, + gboolean back); void completion_clean(void); -gboolean completion_complete(gboolean back); +void completion_next(gboolean back); #endif /* end of include guard: _COMPLETION_H */ diff --git a/src/ex.c b/src/ex.c index 8872de4..c207533 100644 --- a/src/ex.c +++ b/src/ex.c @@ -68,7 +68,7 @@ typedef struct { int count; /* commands count */ int idx; /* index in commands array */ const char *name; /* name of the command */ - ExCode code; /* id of the command */ + ExCode code; /* id of the command */ gboolean bang; /* if the command was called with a bang ! */ GString *lhs; /* left hand side of the command - single word */ GString *rhs; /* right hand side of the command - multiple words */ @@ -110,6 +110,9 @@ static gboolean ex_set(const ExArg *arg); static gboolean ex_shellcmd(const ExArg *arg); static gboolean ex_shortcut(const ExArg *arg); +static gboolean ex_complete(short direction); +static void ex_completion_select(char *match); + /* The order of following command names is significant. If there exists * ambiguous commands matching to the users input, the first defined will be * the prefered match. @@ -157,6 +160,12 @@ static ExInfo commands[] = { #endif }; +static struct { + guint count; + char *prefix; /* completion prefix like :, ? and / */ + char *current; /* holds the current written input box content */ +} excomp; + extern VbCore vb; @@ -174,8 +183,10 @@ void ex_enter(void) */ void ex_leave(void) { - /* TODO clean those only if they where active */ - completion_clean(); + if (vb.mode->flags & FLAG_COMPLETION) { + completion_clean(); + vb.mode->flags &= ~FLAG_COMPLETION; + } hints_clear(); } @@ -197,12 +208,11 @@ VbResult ex_keypress(unsigned int key) switch (key) { case CTRL('I'): /* Tab */ - /* mode will be set in completion_complete */ - completion_complete(false); + ex_complete(1); break; case CTRL('O'): /* S-Tab */ - completion_complete(true); + ex_complete(-1); break; case CTRL('['): @@ -817,3 +827,145 @@ static gboolean ex_shortcut(const ExArg *arg) return false; } } + +/** + * Manage the generation and stepping through completions. + * This function prepared some prefix and suffix string that are required to + * put hte matched data back to inputbox, and prepares the tree list store + * model containing matched values. + */ +static gboolean ex_complete(short direction) +{ + char prefix[2] = {0}; + char *input; /* input read from inputbox */ + const char *in; /* pointer to input that we move */ + gboolean found = false; + gboolean sort = false; + GtkListStore *store; + + /* if direction is 0 stop the completion */ + if (!direction) { + completion_clean(); + vb.mode->flags &= ~FLAG_COMPLETION; + + return true; + } + + input = vb_get_input_text(); + /* if completion was already started move to the next/prev item */ + if (vb.mode->flags & FLAG_COMPLETION) { + if (excomp.current && !strcmp(input, excomp.current)) { + /* step through the next/prev completion item */ + completion_next(direction < 0); + g_free(input); + + return true; + } + + /* if current input isn't the content of the completion item, stop + * completion and start it after that again */ + completion_clean(); + vb.mode->flags &= ~FLAG_COMPLETION; + } + + store = gtk_list_store_new(COMPLETION_STORE_NUM, G_TYPE_STRING, G_TYPE_STRING); + + in = (const char*)input; + if (*in == ':') { + const char *before_cmdname; + /* skipt the first : */ + in++; + + ExArg *arg = g_new0(ExArg, 1); + + skip_whitespace(&in); + parse_count(&in, arg); + + /* packup the current pointer so that we can restore the input pointer + * if tha command name parsing fails */ + before_cmdname = in; + + if (parse_command_name(&in, arg) && *in == ' ') { + skip_whitespace(&in); + if (arg->code == EX_SET) { + if (setting_fill_completion(store, in)) { + /* TODO calculate the prefix automatically */ + OVERWRITE_STRING(excomp.prefix, ":set "); + sort = true; + found = true; + } + } else if (arg->code == EX_OPEN || arg->code == EX_TABOPEN) { + OVERWRITE_STRING(excomp.prefix, arg->code == EX_OPEN ? ":open " : ":tabopen "); + if (*in == '!') { + if (bookmark_fill_completion(store, in + 1)) { + found = true; + } + } else { + if (history_fill_completion(store, HISTORY_URL, in)) { + found = true; + } + } + } else if (arg->code == EX_BMA) { + if (bookmark_fill_tag_completion(store, in)) { + OVERWRITE_STRING(excomp.prefix, ":bma "); + sort = true; + found = true; + } + } + } else { /* complete command names */ + /* restore the 'in' pointer after try to parse command name */ + in = before_cmdname; + + /* backup the parsed data so we can access them in + * ex_completion_select function */ + excomp.count = arg->count; + + if (ex_fill_completion(store, in)) { + OVERWRITE_STRING(excomp.prefix, ":"); + found = true; + } + } + free_cmdarg(arg); + } else if (*in == '/' || *in == '?') { + prefix[0] = *in; + + if (history_fill_completion(store, HISTORY_SEARCH, in + 1)) { + OVERWRITE_STRING(excomp.prefix, prefix); + sort = true; + found = true; + } + } + + /* if the input could be parsed and the tree view could be filled */ + if (sort) { + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(store), COMPLETION_STORE_FIRST, GTK_SORT_ASCENDING + ); + } + if (found + && completion_create(GTK_TREE_MODEL(store), ex_completion_select, direction < 0) + ) { + /* set the submode flag */ + vb.mode->flags |= FLAG_COMPLETION; + } + + g_free(input); + return true; +} + +/** + * Callback called from the completion if a item is selected to write the + * matche item accordings with previously saved prefix and command name to the + * inputbox. + */ +static void ex_completion_select(char *match) +{ + OVERWRITE_STRING(excomp.current, NULL); + + if (excomp.count) { + excomp.current = g_strdup_printf("%s%d%s", excomp.prefix, excomp.count, match); + } else { + excomp.current = g_strconcat(excomp.prefix, match, NULL); + } + vb_set_input_text(excomp.current); +} -- 2.20.1