246 lines
6.3 KiB
C
246 lines
6.3 KiB
C
|
/* ide-highlight-index.c
|
||
|
*
|
||
|
* Copyright 2015-2019 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/>.
|
||
|
*
|
||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
*/
|
||
|
|
||
|
#define G_LOG_DOMAIN "ide-highlight-index"
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <dazzle.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "ide-highlight-index.h"
|
||
|
|
||
|
G_DEFINE_BOXED_TYPE (IdeHighlightIndex, ide_highlight_index,
|
||
|
ide_highlight_index_ref, ide_highlight_index_unref)
|
||
|
|
||
|
struct _IdeHighlightIndex
|
||
|
{
|
||
|
/* For debugging info */
|
||
|
guint count;
|
||
|
gsize chunk_size;
|
||
|
|
||
|
GStringChunk *strings;
|
||
|
GHashTable *index;
|
||
|
GVariant *variant;
|
||
|
};
|
||
|
|
||
|
IdeHighlightIndex *
|
||
|
ide_highlight_index_new (void)
|
||
|
{
|
||
|
IdeHighlightIndex *ret;
|
||
|
|
||
|
ret = g_atomic_rc_box_new0 (IdeHighlightIndex);
|
||
|
ret->strings = g_string_chunk_new (ide_get_system_page_size ());
|
||
|
ret->index = g_hash_table_new (g_str_hash, g_str_equal);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
IdeHighlightIndex *
|
||
|
ide_highlight_index_new_from_variant (GVariant *variant)
|
||
|
{
|
||
|
IdeHighlightIndex *self;
|
||
|
|
||
|
self = ide_highlight_index_new ();
|
||
|
|
||
|
if (variant != NULL)
|
||
|
{
|
||
|
g_autoptr(GVariant) unboxed = NULL;
|
||
|
|
||
|
self->variant = g_variant_ref_sink (variant);
|
||
|
|
||
|
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARIANT))
|
||
|
variant = unboxed = g_variant_get_variant (variant);
|
||
|
|
||
|
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT))
|
||
|
{
|
||
|
GVariantIter iter;
|
||
|
GVariant *value;
|
||
|
const gchar *tag;
|
||
|
|
||
|
g_variant_iter_init (&iter, variant);
|
||
|
|
||
|
while (g_variant_iter_loop (&iter, "{&sv}", &tag, &value))
|
||
|
{
|
||
|
if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
|
||
|
{
|
||
|
g_autofree const gchar **strv = NULL;
|
||
|
gsize len;
|
||
|
|
||
|
strv = g_variant_get_strv (value, &len);
|
||
|
|
||
|
for (gsize i = 0; i < len; i++)
|
||
|
{
|
||
|
const gchar *word = strv[i];
|
||
|
|
||
|
if (g_hash_table_contains (self->index, word))
|
||
|
continue;
|
||
|
|
||
|
/* word is guaranteed to be alive and valid inside our
|
||
|
* variant that we are storing. No need to add to the
|
||
|
* string chunk too.
|
||
|
*/
|
||
|
g_hash_table_insert (self->index, (gchar *)word, (gchar *)tag);
|
||
|
self->count++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ide_highlight_index_insert (IdeHighlightIndex *self,
|
||
|
const gchar *word,
|
||
|
gpointer tag)
|
||
|
{
|
||
|
gchar *key;
|
||
|
|
||
|
g_assert (self);
|
||
|
g_assert (tag != NULL);
|
||
|
|
||
|
if (word == NULL || word[0] == '\0')
|
||
|
return;
|
||
|
|
||
|
if (g_hash_table_contains (self->index, word))
|
||
|
return;
|
||
|
|
||
|
self->count++;
|
||
|
self->chunk_size += strlen (word) + 1;
|
||
|
|
||
|
key = g_string_chunk_insert (self->strings, word);
|
||
|
g_hash_table_insert (self->index, key, tag);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ide_highlight_index_lookup:
|
||
|
* @self: An #IdeHighlightIndex.
|
||
|
*
|
||
|
* Gets the pointer tag that was registered for @word, or %NULL. This can be
|
||
|
* any arbitrary value. Some highlight engines might use it to point at
|
||
|
* internal structures or strings they know about to optimize later work.
|
||
|
*
|
||
|
* Returns: (transfer none) (nullable): Highlighter specific tag.
|
||
|
*
|
||
|
* Since: 3.32
|
||
|
*/
|
||
|
gpointer
|
||
|
ide_highlight_index_lookup (IdeHighlightIndex *self,
|
||
|
const gchar *word)
|
||
|
{
|
||
|
g_assert (self);
|
||
|
g_assert (word);
|
||
|
|
||
|
return g_hash_table_lookup (self->index, word);
|
||
|
}
|
||
|
|
||
|
IdeHighlightIndex *
|
||
|
ide_highlight_index_ref (IdeHighlightIndex *self)
|
||
|
{
|
||
|
g_assert (self);
|
||
|
|
||
|
return g_atomic_rc_box_acquire (self);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ide_highlight_index_finalize (IdeHighlightIndex *self)
|
||
|
{
|
||
|
IDE_ENTRY;
|
||
|
|
||
|
g_clear_pointer (&self->strings, g_string_chunk_free);
|
||
|
g_clear_pointer (&self->index, g_hash_table_unref);
|
||
|
g_clear_pointer (&self->variant, g_variant_unref);
|
||
|
|
||
|
IDE_EXIT;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ide_highlight_index_unref (IdeHighlightIndex *self)
|
||
|
{
|
||
|
g_assert (self);
|
||
|
|
||
|
g_atomic_rc_box_release_full (self, (GDestroyNotify)ide_highlight_index_finalize);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ide_highlight_index_dump (IdeHighlightIndex *self)
|
||
|
{
|
||
|
g_autofree gchar *format = NULL;
|
||
|
|
||
|
g_assert (self);
|
||
|
|
||
|
format = g_format_size (self->chunk_size);
|
||
|
g_debug ("IdeHighlightIndex (%p) contains %u items and consumes %s.",
|
||
|
self, self->count, format);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ide_highlight_index_to_variant:
|
||
|
* @self: a #IdeHighlightIndex
|
||
|
*
|
||
|
* Creates a variant to represent the index. Useful to transport across IPC boundaries.
|
||
|
*
|
||
|
* Returns: (transfer full): a #GVariant
|
||
|
*
|
||
|
* Since: 3.32
|
||
|
*/
|
||
|
GVariant *
|
||
|
ide_highlight_index_to_variant (IdeHighlightIndex *self)
|
||
|
{
|
||
|
g_autoptr(GHashTable) arrays = NULL;
|
||
|
GHashTableIter iter;
|
||
|
const gchar *k, *v;
|
||
|
GPtrArray *ar;
|
||
|
GVariantDict dict;
|
||
|
|
||
|
g_return_val_if_fail (self != NULL, NULL);
|
||
|
|
||
|
arrays = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_ptr_array_unref);
|
||
|
|
||
|
g_hash_table_iter_init (&iter, self->index);
|
||
|
while (g_hash_table_iter_next (&iter, (gpointer *)&k, (gpointer *)&v))
|
||
|
{
|
||
|
if G_UNLIKELY (!(ar = g_hash_table_lookup (arrays, v)))
|
||
|
{
|
||
|
ar = g_ptr_array_new ();
|
||
|
g_hash_table_insert (arrays, (gchar *)v, ar);
|
||
|
}
|
||
|
|
||
|
g_ptr_array_add (ar, (gchar *)k);
|
||
|
}
|
||
|
|
||
|
g_variant_dict_init (&dict, NULL);
|
||
|
|
||
|
g_hash_table_iter_init (&iter, arrays);
|
||
|
while (g_hash_table_iter_next (&iter, (gpointer *)&k, (gpointer *)&ar))
|
||
|
{
|
||
|
GVariant *keys;
|
||
|
|
||
|
g_ptr_array_add (ar, NULL);
|
||
|
|
||
|
keys = g_variant_new_strv ((const gchar * const *)ar->pdata, ar->len - 1);
|
||
|
g_variant_dict_insert_value (&dict, k, g_steal_pointer (&keys));
|
||
|
}
|
||
|
|
||
|
return g_variant_take_ref (g_variant_dict_end (&dict));
|
||
|
}
|