Completion: use std::set<std::string> instead of GCompletion
authorMax Kellermann <max@musicpd.org>
Fri, 16 Mar 2018 19:27:18 +0000 (20:27 +0100)
committerMax Kellermann <max@musicpd.org>
Sat, 17 Mar 2018 07:22:32 +0000 (08:22 +0100)
GCompletion is deprecated.

src/Completion.cxx
src/Completion.hxx
src/db_completion.cxx
src/db_completion.hxx
src/save_playlist.cxx
src/screen_queue.cxx
src/screen_utils.cxx
src/screen_utils.hxx
src/wreadln.cxx

index 43e56eb..49d7b98 100644 (file)
 
 #include "Completion.hxx"
 
-#include <string.h>
+#include <assert.h>
 
-/**
- * Wrapper for strncmp().  We are not allowed to pass &strncmp to
- * g_completion_set_compare(), because strncmp() takes size_t where
- * g_completion_set_compare passes a gsize value.
- */
-static gint
-completion_strncmp(const gchar *s1, const gchar *s2, gsize n)
+static bool
+StartsWith(const std::string &haystack, const std::string &needle)
 {
-       return strncmp(s1, s2, n);
+       return haystack.length() >= needle.length() &&
+               std::equal(needle.begin(), needle.end(), haystack.begin());
 }
 
-Completion::Completion()
-       :gcmp(g_completion_new(nullptr))
+Completion::Result
+Completion::Complete(const std::string &prefix) const
 {
-       g_completion_set_compare(gcmp, completion_strncmp);
-}
+       auto lower = list.lower_bound(prefix);
+       if (lower == list.end() || !StartsWith(*lower, prefix))
+               return {std::string(), {lower, lower}};
 
-Completion::~Completion()
-{
-       g_completion_free(gcmp);
+       auto upper = list.upper_bound(prefix);
+       while (upper != list.end() && StartsWith(*upper, prefix))
+               ++upper;
+
+       assert(upper != lower);
+
+       auto m = std::mismatch(lower->begin(), lower->end(),
+                              std::prev(upper)->begin()).first;
+
+       return {{lower->begin(), m}, {lower, upper}};
 }
index d358e00..7e3d852 100644 (file)
 #ifndef COMPLETION_HXX
 #define COMPLETION_HXX
 
-#include <glib.h>
+#include <set>
+#include <string>
 
 class Completion {
 protected:
-       GCompletion *const gcmp;
+       using List = std::set<std::string>;
+       List list;
 
 public:
-       Completion();
-       ~Completion();
+       Completion() = default;
 
        Completion(const Completion &) = delete;
        Completion &operator=(const Completion &) = delete;
 
-       GList *Complete(const gchar *prefix, gchar **new_prefix) {
-               return g_completion_complete(gcmp, prefix, new_prefix);
+       bool empty() const {
+               return list.empty();
        }
 
+       void clear() {
+               list.clear();
+       }
+
+       template<typename T>
+       void emplace(T &&value) {
+               list.emplace(std::forward<T>(value));
+       }
+
+       template<typename T>
+       void remove(T &&value) {
+               auto i = list.find(std::forward<T>(value));
+               if (i != list.end())
+                       list.erase(i);
+       }
+
+       struct Range {
+               using const_iterator = List::const_iterator;
+               const_iterator _begin, _end;
+
+               bool operator==(const Range other) const {
+                       return _begin == other._begin && _end == other._end;
+               }
+
+               bool operator!=(const Range other) const {
+                       return !(*this == other);
+               }
+
+               const_iterator begin() const {
+                       return _begin;
+               }
+
+               const_iterator end() const {
+                       return _end;
+               }
+       };
+
+       struct Result {
+               std::string new_prefix;
+
+               Range range;
+       };
+
+       Result Complete(const std::string &prefix) const;
+
        virtual void Pre(const char *value) = 0;
-       virtual void Post(const char *value, GList *list) = 0;
+       virtual void Post(const char *value, Range range) = 0;
 };
 
 #endif
index d3f0c81..b240c98 100644 (file)
  */
 
 #include "db_completion.hxx"
+#include "Completion.hxx"
 #include "charset.hxx"
 #include "mpdclient.hxx"
 
 #include <glib.h>
 
-GList *
+void
 gcmp_list_from_path(struct mpdclient *c, const char *path,
-                   GList *list, int types)
+                   Completion &completion,
+                   int types)
 {
        struct mpd_connection *connection = mpdclient_get_connection(c);
        if (connection == nullptr)
-               return list;
+               return;
 
        mpd_send_list_meta(connection, path);
 
@@ -58,9 +60,7 @@ gcmp_list_from_path(struct mpdclient *c, const char *path,
                        continue;
                }
 
-               list = g_list_append(list, name);
+               completion.emplace(name);
                mpd_entity_free(entity);
        }
-
-       return list;
 }
index 7c0ff4a..fe74cd7 100644 (file)
@@ -20,8 +20,8 @@
 #ifndef DB_COMPLETION_H
 #define DB_COMPLETION_H
 
-typedef struct _GList GList;
 struct mpdclient;
+class Completion;
 
 #define GCMP_TYPE_DIR       (0x01 << 0)
 #define GCMP_TYPE_FILE      (0x01 << 1)
@@ -30,10 +30,11 @@ struct mpdclient;
 #define GCMP_TYPE_RPLAYLIST (GCMP_TYPE_DIR | GCMP_TYPE_PLAYLIST)
 
 /**
- * Create a list suitable for GCompletion from path.
+ * Create a list suitable for #Completion from path.
  */
-GList *
+void
 gcmp_list_from_path(struct mpdclient *c, const char *path,
-                   GList *list, int types);
+                   Completion &completion,
+                   int types);
 
 #endif
index bd3fb3c..e144948 100644 (file)
 
 class PlaylistNameCompletion final : public Completion {
        struct mpdclient &c;
-       GList *list = nullptr;
 
 public:
        explicit PlaylistNameCompletion(struct mpdclient &_c)
                :c(_c) {}
 
-       ~PlaylistNameCompletion() {
-               string_list_free(list);
-       }
-
 protected:
        /* virtual methods from class Completion */
        void Pre(const char *value) override;
-       void Post(const char *value, GList *items) override;
+       void Post(const char *value, Range range) override;
 };
 
 void
 PlaylistNameCompletion::Pre(gcc_unused const char *value)
 {
-       if (list == nullptr) {
+       if (empty()) {
                /* create completion list */
-               list = gcmp_list_from_path(&c, "", nullptr, GCMP_TYPE_PLAYLIST);
-               g_completion_add_items(gcmp, list);
+               gcmp_list_from_path(&c, "", *this, GCMP_TYPE_PLAYLIST);
        }
 }
 
 void
-PlaylistNameCompletion::Post(gcc_unused const char *value, GList *items)
+PlaylistNameCompletion::Post(gcc_unused const char *value, Range range)
 {
-       if (g_list_length(items) >= 1)
-               screen_display_completion_list(items);
+       if (range.begin() != range.end() &&
+           std::next(range.begin()) != range.end())
+               screen_display_completion_list(range);
 }
 
 #endif
index 98a2f14..0965dc4 100644 (file)
@@ -229,13 +229,11 @@ QueuePage::OnSongChange(const struct mpd_status *status)
 
 #ifndef NCMPC_MINI
 static void
-add_dir(GCompletion *gcmp, const char *dir,
-       GList **list, struct mpdclient *c)
+add_dir(Completion &completion, const char *dir,
+       struct mpdclient *c)
 {
-       g_completion_remove_items(gcmp, *list);
-       *list = string_list_remove(*list, dir);
-       *list = gcmp_list_from_path(c, dir, *list, GCMP_TYPE_RFILE);
-       g_completion_add_items(gcmp, *list);
+       completion.clear();
+       gcmp_list_from_path(c, dir, completion, GCMP_TYPE_RFILE);
 }
 
 class DatabaseCompletion final : public Completion {
@@ -254,7 +252,7 @@ public:
 protected:
        /* virtual methods from class Completion */
        void Pre(const char *value) override;
-       void Post(const char *value, GList *items) override;
+       void Post(const char *value, Range range) override;
 };
 
 void
@@ -262,27 +260,27 @@ DatabaseCompletion::Pre(const char *line)
 {
        if (list == nullptr) {
                /* create initial list */
-               list = gcmp_list_from_path(&c, "", nullptr, GCMP_TYPE_RFILE);
-               g_completion_add_items(gcmp, list);
+               gcmp_list_from_path(&c, "", *this, GCMP_TYPE_RFILE);
        } else if (line && line[0] && line[strlen(line) - 1] == '/') {
                auto i = dir_list.emplace(line);
                if (i.second)
                        /* add directory content to list */
-                       add_dir(gcmp, line, &list, &c);
+                       add_dir(*this, line, &c);
        }
 }
 
 void
-DatabaseCompletion::Post(const char *line, GList *items)
+DatabaseCompletion::Post(const char *line, Range range)
 {
-       if (g_list_length(items) >= 1)
-               screen_display_completion_list(items);
+       if (range.begin() != range.end() &&
+           std::next(range.begin()) != range.end())
+               screen_display_completion_list(range);
 
        if (line && line[0] && line[strlen(line) - 1] == '/') {
                /* add directory content to list */
                auto i = dir_list.emplace(line);
                if (i.second)
-                       add_dir(gcmp, line, &list, &c);
+                       add_dir(*this, line, &c);
        }
 }
 
index 6edda0b..3d76230 100644 (file)
@@ -152,34 +152,33 @@ CompletionDisplayString(const char *value)
 }
 
 void
-screen_display_completion_list(GList *list)
+screen_display_completion_list(Completion::Range range)
 {
-       static GList *prev_list = nullptr;
+       static Completion::Range prev_range;
        static guint prev_length = 0;
        static guint offset = 0;
        WINDOW *w = screen->main_window.w;
 
-       unsigned length = g_list_length(list);
-       if (list == prev_list && length == prev_length) {
+       unsigned length = std::distance(range.begin(), range.end());
+       if (range == prev_range && length == prev_length) {
                offset += screen->main_window.size.height;
                if (offset >= length)
                        offset = 0;
        } else {
-               prev_list = list;
+               prev_range = range;
                prev_length = length;
                offset = 0;
        }
 
        colors_use(w, COLOR_STATUS_ALERT);
 
-       GList *item = g_list_nth(list, offset);
-       for (unsigned y = 0; y < screen->main_window.size.height;
-            ++y, item = item->next) {
+       auto i = std::next(range.begin(), offset);
+       for (unsigned y = 0; y < screen->main_window.size.height; ++y, ++i) {
                wmove(w, y, 0);
-               if (item == nullptr)
+               if (i == range.end())
                        break;
 
-               const char *value = (const char *)item->data;
+               const char *value = i->c_str();
                waddstr(w, CompletionDisplayString(value));
                wclrtoeol(w);
        }
index 4db10e4..0eb034f 100644 (file)
@@ -23,8 +23,7 @@
 #include "config.h"
 #include "command.hxx"
 #include "History.hxx"
-
-#include <glib.h>
+#include "Completion.hxx"
 
 struct mpdclient;
 class Completion;
@@ -51,6 +50,7 @@ std::string
 screen_readln(const char *prompt, const char *value,
              History *history, Completion *completion);
 
-void screen_display_completion_list(GList *list);
+void
+screen_display_completion_list(Completion::Range range);
 
 #endif
index ec7373e..8106c7b 100644 (file)
@@ -424,19 +424,15 @@ _wreadln(WINDOW *w,
                case TAB:
 #ifndef NCMPC_MINI
                        if (completion != nullptr) {
-                               char *prefix = nullptr;
-                               GList *list;
-
                                completion->Pre(wr.value.c_str());
-                               list = completion->Complete(wr.value.c_str(), &prefix);
-                               if (prefix) {
-                                       wr.value = prefix;
+                               auto r = completion->Complete(wr.value.c_str());
+                               if (!r.new_prefix.empty()) {
+                                       wr.value = std::move(r.new_prefix);
                                        cursor_move_to_eol(&wr);
-                                       g_free(prefix);
                                } else
                                        screen_bell();
 
-                               completion->Post(wr.value.c_str(), list);
+                               completion->Post(wr.value.c_str(), r.range);
                        }
 #endif
                        break;