#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<tab>' */
- 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);
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);
/* 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;
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)
{
* 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);
}
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 */
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.
#endif
};
+static struct {
+ guint count;
+ char *prefix; /* completion prefix like :, ? and / */
+ char *current; /* holds the current written input box content */
+} excomp;
+
extern VbCore vb;
*/
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();
}
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('['):
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);
+}