[gnome-builder/wip/gtk4-port: 48/1774] search: add IdePatternSpec




commit 1046cc4a56d192f9d6e973433a152710931f0020
Author: Christian Hergert <chergert redhat com>
Date:   Fri Sep 24 11:20:49 2021 -0700

    search: add IdePatternSpec
    
    This originated in Builder and then was pushed out to Dazzle. We can bring
    it back now that Builder is basically it's primary user for search stuff.

 src/libide/search/ide-pattern-spec.c | 206 +++++++++++++++++++++++++++++++++++
 src/libide/search/ide-pattern-spec.h |  45 ++++++++
 src/libide/search/libide-search.h    |   2 +-
 src/libide/search/meson.build        |   2 +
 4 files changed, 254 insertions(+), 1 deletion(-)
---
diff --git a/src/libide/search/ide-pattern-spec.c b/src/libide/search/ide-pattern-spec.c
new file mode 100644
index 000000000..c9c7621ec
--- /dev/null
+++ b/src/libide/search/ide-pattern-spec.c
@@ -0,0 +1,206 @@
+/* ide-pattern-spec.c
+ *
+ * Copyright (C) 2015-2021 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-pattern-spec"
+
+#include "config.h"
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <string.h>
+
+#include "ide-pattern-spec.h"
+
+G_DEFINE_BOXED_TYPE (IdePatternSpec, ide_pattern_spec, ide_pattern_spec_ref, ide_pattern_spec_unref)
+
+/**
+ * SECTION:ide-pattern-spec
+ * @title: IdePatternSpec
+ * @short_description: Simple glob-like searching
+ *
+ * This works similar to #GPatternSpec except the query syntax is different.
+ * It tries to match word boundaries, but with matching partial words up
+ * to those boundaries. For example, "gtk widg" would match "gtk_widget_show".
+ * Word boundaries include '_' and ' '. If any character is uppercase, then
+ * case sensitivity is used.
+ */
+
+struct _IdePatternSpec
+{
+  volatile int    ref_count;
+  char           *needle;
+  char          **parts;
+  guint           case_sensitive : 1;
+};
+
+#ifdef G_OS_WIN32
+/* A fallback for missing strcasestr() on Windows. This is not in any way
+ * optimized, but at least it supports something resembling UTF-8.
+ */
+static char *
+strcasestr (const char *haystack,
+            const char *needle)
+{
+  g_autofree char *haystack_folded = g_utf8_casefold (haystack, -1);
+  g_autofree char *needle_folded = g_utf8_casefold (needle, -1);
+  const char *pos;
+  gsize n_chars = 0;
+
+  pos = strstr (haystack_folded, needle_folded);
+
+  if (pos == NULL)
+    return NULL;
+
+  for (const char *iter = haystack_folded;
+       *iter != '\0';
+       iter = g_utf8_next_char (iter))
+    {
+      if (iter >= pos)
+        break;
+      n_chars++;
+    }
+
+  return g_utf8_offset_to_pointer (haystack, n_chars);
+}
+#endif
+
+IdePatternSpec *
+ide_pattern_spec_new (const char *needle)
+{
+  IdePatternSpec *self;
+  const char *tmp;
+
+  if (needle == NULL)
+    needle = "";
+
+  self = g_slice_new0 (IdePatternSpec);
+  self->ref_count = 1;
+  self->needle = g_strdup (needle);
+  self->parts = g_strsplit (needle, " ", 0);
+  self->case_sensitive = FALSE;
+
+  for (tmp = needle; *tmp; tmp = g_utf8_next_char (tmp))
+    {
+      if (g_unichar_isupper (g_utf8_get_char (tmp)))
+        {
+          self->case_sensitive = TRUE;
+          break;
+        }
+    }
+
+  return self;
+}
+
+const char *
+ide_pattern_spec_get_text (IdePatternSpec *self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return self->needle;
+}
+
+static void
+ide_pattern_spec_free (IdePatternSpec *self)
+{
+  g_clear_pointer (&self->parts, g_strfreev);
+  g_clear_pointer (&self->needle, g_free);
+  g_slice_free (IdePatternSpec, self);
+}
+
+static inline gboolean
+is_word_break (gunichar ch)
+{
+  return (ch == ' ' || ch == '_' || ch == '-' || ch == '.');
+}
+
+static const char *
+next_word_start (const char *haystack)
+{
+  for (; *haystack; haystack = g_utf8_next_char (haystack))
+    {
+      gunichar ch = g_utf8_get_char (haystack);
+
+      if (is_word_break (ch))
+        break;
+    }
+
+  for (; *haystack; haystack = g_utf8_next_char (haystack))
+    {
+      gunichar ch = g_utf8_get_char (haystack);
+
+      if (is_word_break (ch))
+        continue;
+
+      break;
+    }
+
+  g_return_val_if_fail (*haystack == '\0' || !is_word_break (*haystack), NULL);
+
+  return haystack;
+}
+
+gboolean
+ide_pattern_spec_match (IdePatternSpec *self,
+                        const char     *haystack)
+{
+  gsize i;
+
+  if (self == NULL || haystack == NULL)
+    return FALSE;
+
+  for (i = 0; (haystack != NULL) && self->parts [i]; i++)
+    {
+      if (self->parts [i][0] == '\0')
+        continue;
+
+      if (self->case_sensitive)
+        haystack = strstr (haystack, self->parts [i]);
+      else
+        haystack = strcasestr (haystack, self->parts [i]);
+
+      if (haystack == NULL)
+        return FALSE;
+
+      if (self->parts [i + 1] != NULL)
+        haystack = next_word_start (haystack + strlen (self->parts [i]));
+    }
+
+  return TRUE;
+}
+
+IdePatternSpec *
+ide_pattern_spec_ref (IdePatternSpec *self)
+{
+  g_return_val_if_fail (self, NULL);
+  g_return_val_if_fail (self->ref_count > 0, NULL);
+
+  g_atomic_int_inc (&self->ref_count);
+
+  return self;
+}
+
+void
+ide_pattern_spec_unref (IdePatternSpec *self)
+{
+  g_return_if_fail (self);
+  g_return_if_fail (self->ref_count > 0);
+
+  if (g_atomic_int_dec_and_test (&self->ref_count))
+    ide_pattern_spec_free (self);
+}
diff --git a/src/libide/search/ide-pattern-spec.h b/src/libide/search/ide-pattern-spec.h
new file mode 100644
index 000000000..cc786dbb8
--- /dev/null
+++ b/src/libide/search/ide-pattern-spec.h
@@ -0,0 +1,45 @@
+/* ide-pattern-spec.h
+ *
+ * Copyright (C) 2015-2021 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdePatternSpec IdePatternSpec;
+
+#define IDE_TYPE_PATTERN_SPEC (ide_pattern_spec_get_type())
+
+IDE_AVAILABLE_IN_ALL
+GType           ide_pattern_spec_get_type (void);
+IDE_AVAILABLE_IN_ALL
+IdePatternSpec *ide_pattern_spec_new      (const gchar    *keywords);
+IDE_AVAILABLE_IN_ALL
+IdePatternSpec *ide_pattern_spec_ref      (IdePatternSpec *self);
+IDE_AVAILABLE_IN_ALL
+void            ide_pattern_spec_unref    (IdePatternSpec *self);
+IDE_AVAILABLE_IN_ALL
+gboolean        ide_pattern_spec_match    (IdePatternSpec *self,
+                                           const gchar     *haystack);
+IDE_AVAILABLE_IN_ALL
+const gchar    *ide_pattern_spec_get_text (IdePatternSpec *self);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (IdePatternSpec, ide_pattern_spec_unref)
+
+G_END_DECLS
diff --git a/src/libide/search/libide-search.h b/src/libide/search/libide-search.h
index e0c0df866..591358d5a 100644
--- a/src/libide/search/libide-search.h
+++ b/src/libide/search/libide-search.h
@@ -20,12 +20,12 @@
 
 #pragma once
 
-#include <dazzle.h>
 #include <libide-core.h>
 #include <libide-threading.h>
 
 #define IDE_SEARCH_INSIDE
 
+#include "ide-pattern-spec.h"
 #include "ide-search-engine.h"
 #include "ide-search-provider.h"
 #include "ide-search-reducer.h"
diff --git a/src/libide/search/meson.build b/src/libide/search/meson.build
index ef39c4da2..6cd6750ed 100644
--- a/src/libide/search/meson.build
+++ b/src/libide/search/meson.build
@@ -7,6 +7,7 @@ libide_include_directories += include_directories('.')
 #
 
 libide_search_public_headers = [
+  'ide-pattern-spec.h',
   'ide-search-engine.h',
   'ide-search-provider.h',
   'ide-search-reducer.h',
@@ -21,6 +22,7 @@ install_headers(libide_search_public_headers, subdir: libide_search_header_subdi
 #
 
 libide_search_public_sources = [
+  'ide-pattern-spec.c',
   'ide-search-engine.c',
   'ide-search-provider.c',
   'ide-search-reducer.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]