Show bookmarked uri also in completion list (#4).
authorDaniel Carl <danielcarl@gmx.de>
Sat, 6 Apr 2013 17:38:29 +0000 (19:38 +0200)
committerDaniel Carl <danielcarl@gmx.de>
Sat, 6 Apr 2013 17:38:29 +0000 (19:38 +0200)
doc/vimb.1.txt
src/bookmark.c
src/bookmark.h
src/completion.c

index 1f6af08..5583aa3 100644 (file)
@@ -217,7 +217,23 @@ Remove a Search Mode keybinding.
 .TP
 .B complete
 Start a completion according to the current first chars in the inputbox. If
-complation is already started, switch to the next completion item.
+completion is already started, switch to the next completion item.
+
+Following completions are available:
+.RS
+.nr step 1 1
+.IP \n[step] 3
+Commands `:'
+.IP \n+[step]
+Settings `:set '
+.IP \n+[step]
+Url-History and Bookmarks `:open \fIQUERY\fP' or `:tabopen \fIQUERY\fP'.
+If \fIQUERY\fP is given, this will be used to fetch matching urls from history
+file, or to find bookmarks from bookmark file. The urls from history will only
+be matched by their url, the bookmarks only by their tags. If multiple tags
+are given to get the bookmarks, only those bookmarks will be returned, that
+matches all the given tags or that don't have any tag set.
+.RE
 .TP
 .B complete-back
 If complation is already started, switch to the previous completion item.
index 793ce57..59b86f5 100644 (file)
 
 extern VbCore vb;
 
+typedef struct {
+    char *uri;
+    char **argv;
+    int  argc;
+} Bookmark;
+
+static GList *load(const char *file);
+static gboolean contains_all_args(char **argv_src, unsigned int s, char **argv_query, unsigned int q);
+static void free_bookmark(Bookmark *bm);
+
 /**
  * Write a new bookmark entry to the end of bookmark file.
  */
@@ -55,3 +65,115 @@ void bookmark_add(const char *uri, const char *tags)
         g_string_free(string, true);
     }
 }
+
+/**
+ * Retrieves all bookmark uri matching the given space separated tags string.
+ * Don't forget to free the returned list.
+ */
+GList *bookmark_get_by_tags(const char *tags)
+{
+    GList *res = NULL;
+    GList *src = load(vb.files[FILES_BOOKMARK]);
+    char **parts;
+    unsigned int len;
+
+    parts = g_strsplit(tags, " ", 0);
+    len   = g_strv_length(parts);
+
+    for (GList *l = src; l; l = l->next) {
+        Bookmark *bm = (Bookmark*)l->data;
+        if (contains_all_args(bm->argv, bm->argc, parts, len)) {
+            res = g_list_prepend(res, g_strdup(bm->uri));
+        }
+    }
+
+    g_list_free_full(src, (GDestroyNotify)free_bookmark);
+    g_strfreev(parts);
+
+    return res;
+}
+
+static GList *load(const char *file)
+{
+    /* read the items from file */
+    GList *list   = NULL;
+    char buf[512] = {0}, **argv = NULL;
+    int argc      = 0;
+    FILE *f;
+
+    if (!(f = fopen(file, "r"))) {
+        return list;
+    }
+
+    file_lock_set(fileno(f), F_RDLCK);
+    while (fgets(buf, sizeof(buf), f)) {
+        char *p;
+        Bookmark *bm;
+
+        g_strstrip(buf);
+        /* skip empty lines */
+        if (!*buf) {
+            continue;
+        }
+
+        /* create bookmark */
+        bm = g_new(Bookmark, 1);
+        if ((p = strchr(buf, ' '))) {
+            *p = '\0';
+            /* parse tags */
+            if (g_shell_parse_argv(p + 1, &argc, &argv, NULL)) {
+                bm->uri  = g_strdup(buf);
+                bm->argv = argv;
+                bm->argc = argc;
+            } else {
+                continue;
+            }
+        } else {
+            bm->uri  = g_strdup(buf);
+            bm->argv = NULL;
+            bm->argc = 0;
+        }
+
+        list = g_list_prepend(list, bm);
+    }
+    file_lock_set(fileno(f), F_UNLCK);
+    fclose(f);
+
+    return list;
+}
+
+/**
+ * Checks if the given source array of pointer contains all those entries
+ * given as array of search strings.
+ */
+static gboolean contains_all_args(char **argv_src, unsigned int s, char **argv_query, unsigned int q)
+{
+    unsigned int i, n;
+
+    if (!s || !q) {
+        return true;
+    }
+
+    /* iterate over all query parts */
+    for (i = 0; i < q; i++) {
+        gboolean found = false;
+        for (n = 0; n < s; n++) {
+            if (!strcmp(argv_query[i], argv_src[n])) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static void free_bookmark(Bookmark *bm)
+{
+    g_free(bm->uri);
+    g_strfreev(bm->argv);
+    g_free(bm);
+}
index 6dac270..20f1538 100644 (file)
@@ -21,5 +21,6 @@
 #define _BOOKMARK_H
 
 void bookmark_add(const char *uri, const char *tags);
+GList *bookmark_get_by_tags(const char *tags);
 
 #endif /* end of include guard: _BOOKMARK_H */
index d106c58..ad2b7e3 100644 (file)
@@ -20,6 +20,7 @@
 #include "completion.h"
 #include "util.h"
 #include "history.h"
+#include "bookmark.h"
 
 extern VbCore vb;
 
@@ -32,6 +33,8 @@ typedef struct {
 
 static GList *init_completion(GList *target, GList *source,
     Comp_Func func, const char *input, const char *prefix);
+static GList *prepend_bookmark_completion(GList *target, GList *source, const char *prefix);
+static void pack_boxes(GList *list);
 static GList *update(GList *completion, GList *active, gboolean back);
 static void show(gboolean back);
 static void set_entry_text(Completion *completion);
@@ -71,6 +74,8 @@ gboolean completion_complete(gboolean back)
     gtk_box_pack_start(GTK_BOX(vb.gui.box), vb.gui.compbox, false, false, 0);
 
     /* TODO move these decision to a more generic place */
+    /* TODO simplify this logic - seperate the list preparation, filtering and
+     * gtk box creation from another */
     if (!strncmp(input, ":set ", 5)) {
         source = g_hash_table_get_keys(vb.settings);
         source = g_list_sort(source, (GCompareFunc)g_strcmp0);
@@ -84,12 +89,26 @@ gboolean completion_complete(gboolean back)
             vb.comps.completions, source, (Comp_Func)util_strcasestr, &input[6], ":open "
         );
         history_list_free(&source);
+
+        /* prepend the bookmark items */
+        source = bookmark_get_by_tags(&input[6]);
+        vb.comps.completions = prepend_bookmark_completion(
+            vb.comps.completions, source, ":open "
+        );
+        history_list_free(&source);
     } else if (!strncmp(input, ":tabopen ", 9)) {
         source = history_get_all(HISTORY_URL);
         vb.comps.completions = init_completion(
             vb.comps.completions, source, (Comp_Func)util_strcasestr, &input[9], ":tabopen "
         );
         history_list_free(&source);
+
+        /* prepend the bookmark items */
+        source = bookmark_get_by_tags(&input[9]);
+        vb.comps.completions = prepend_bookmark_completion(
+            vb.comps.completions, source, ":tabopen "
+        );
+        history_list_free(&source);
     } else {
         source = g_hash_table_get_keys(vb.behave.commands);
         source = g_list_sort(source, (GCompareFunc)g_strcmp0);
@@ -102,6 +121,7 @@ gboolean completion_complete(gboolean back)
     if (!vb.comps.completions) {
         return false;
     }
+    pack_boxes(vb.comps.completions);
     show(back);
 
     return TRUE;
@@ -152,10 +172,8 @@ static GList *init_completion(GList *target, GList *source,
             }
         }
         if (match) {
-            Completion *completion = get_new(data, prefix);
-            gtk_box_pack_start(GTK_BOX(vb.gui.compbox), completion->event, TRUE, TRUE, 0);
             /* use prepend because that faster */
-            target = g_list_prepend(target, completion);
+            target = g_list_prepend(target, get_new(data, prefix));
         }
     }
 
@@ -165,6 +183,23 @@ static GList *init_completion(GList *target, GList *source,
     return target;
 }
 
+static GList *prepend_bookmark_completion(GList *target, GList *source, const char *prefix)
+{
+    for (GList *l = source; l; l = l->next) {
+        target = g_list_prepend(target, get_new(l->data, prefix));
+    }
+
+    return target;
+}
+
+static void pack_boxes(GList *list)
+{
+    for (GList *l = list; l; l = l->next) {
+        Completion *c = (Completion*)l->data;
+        gtk_box_pack_start(GTK_BOX(vb.gui.compbox), c->event, TRUE, TRUE, 0);
+    }
+}
+
 static GList *update(GList *completion, GList *active, gboolean back)
 {
     GList *old, *new;