<commit_before>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
/* gtksourcecompletion.c
 * This file is part of GtkSourceView
 *
 * Copyright (C) 2007 -2009 <NAME> <<EMAIL>>
 * Copyright (C) 2009 - <NAME> <<EMAIL>>
 *
 * GtkSourceView is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * GtkSourceView 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

/**
 * SECTION:completion
 * @title: GtkSourceCompletion
 * @short_description: Main Completion Object
 *
 * The completion system helps the user when he writes some text, such
 * as words, command names, functions, and suchlike. Proposals can be
 * shown, to complete the text the user is writing. Each proposal can
 * contain an additional piece of information, that is displayed when
 * the "Details" button is active.
 *
 * Proposals are created via a #GtkSourceCompletionProvider. There can
 * be for example a provider to complete words (see
 * #GtkSourceCompletionWords), another provider for the completion of
 * function's names, etc. To add a provider, call
 * gtk_source_completion_add_provider().
 *
 * When the completion is activated, a #GtkSourceCompletionContext object is
 * created. The providers are asked whether they match the context, with
 * gtk_source_completion_provider_match(). If a provider doesn't match the
 * context, it will not be visible in the completion window. On the
 * other hand, if the provider matches the context, its proposals will
 * be displayed.
 *
 * When several providers match, they are all shown in the completion
 * window, but one can switch between providers: see the
 * #GtkSourceCompletion::move-page signal. It is also possible to
 * activate the first proposals with key bindings, see the
 * #GtkSourceCompletion:accelerators property.
 *
 * The #GtkSourceCompletionProposal interface represents a proposal.
 * The #GtkSourceCompletionItem class is a simple implementation of this
 * interface.
 *
 * If a proposal contains extra information (see
 * gtk_source_completion_provider_get_info_widget()), it will be
 * displayed in a #GtkSourceCompletionInfo window, which appears when
 * the "Details" button is clicked.
 *
 * A #GtkSourceCompletionInfo window can also be used to display
 * calltips. When no proposals are available, it can be useful to
 * display extra information like a function's prototype (number of
 * parameters, types of parameters, etc).
 *
 * Each #GtkSourceView object is associated with a #GtkSourceCompletion
 * instance. This instance can be obtained with
 * gtk_source_view_get_completion(). The #GtkSourceView class contains also the
 * #GtkSourceView::show-completion signal.
 *
 * A same #GtkSourceCompletionProvider object can be used for several
 * #GtkSourceCompletion.
 */

#include "gtksourcecompletion.h"
#include "gtksourcecompletion-private.h"
#include "gtksourcecompletionutils.h"
#include "gtksourcecompletionmodel.h"
#include "gtksourcecompletioncontext.h"
#include "gtksourcecompletioninfo.h"
#include "gtksourcecompletionproposal.h"
#include "gtksourcecompletionprovider.h"
#include "gtksourcebuffer.h"
#include "gtksourceview-marshal.h"
#include "gtksourceview-i18n.h"

#define WINDOW_WIDTH 350
#define WINDOW_HEIGHT 200

#define GTK_SOURCE_COMPLETION_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object),\
						  GTK_SOURCE_TYPE_COMPLETION,           \
						  GtkSourceCompletionPrivate))

/*#define ENABLE_DEBUG*/
#undef ENABLE_DEBUG

#ifdef ENABLE_DEBUG
#define DEBUG(x) (x)
#else
#define DEBUG(x)
#endif

/* Signals */
enum
{
	SHOW,
	HIDE,
	POPULATE_CONTEXT,

	/* Actions */
	ACTIVATE_PROPOSAL,
	MOVE_CURSOR,
	MOVE_PAGE,

	LAST_SIGNAL
};

enum
{
	PROP_0,
	PROP_VIEW,
	PROP_REMEMBER_INFO_VISIBILITY,
	PROP_SELECT_ON_SHOW,
	PROP_SHOW_HEADERS,
	PROP_SHOW_ICONS,
	PROP_ACCELERATORS,

	PROP_AUTO_COMPLETE_DELAY,

	PROP_PROVIDER_PAGE_SIZE,
	PROP_PROPOSAL_PAGE_SIZE
};

enum
{
	TEXT_VIEW_KEY_PRESS,
	TEXT_VIEW_FOCUS_OUT,
	TEXT_VIEW_BUTTON_PRESS,
	TEXT_VIEW_EDITABLE,
	TEXT_BUFFER_BEGIN_SIGNALS,
	TEXT_BUFFER_DELETE_RANGE,
	TEXT_BUFFER_INSERT_TEXT,
	TEXT_BUFFER_MARK_SET,
	SOURCE_BUFFER_REDO,
	SOURCE_BUFFER_REDO_AFTER,
	SOURCE_BUFFER_UNDO,
	SOURCE_BUFFER_UNDO_AFTER,
	TEXT_BUFFER_PASTE_DONE,
	LAST_EXTERNAL_SIGNAL
};

struct _GtkSourceCompletionPrivate
{
	/**********
	 * Widgets
	 **********/

	GtkWidget *main_window;

	/* Image and label in the bottom bar, on the right, for showing which
	 * provider(s) are selected. */
	GtkWidget *selection_image;
	GtkWidget *selection_label;

	/* The CompletionInfo window */
	GtkWidget *info_window;

	/* The default widget (a GtkLabel) for the info window */
	GtkWidget *default_info;

	/* The "Details" button, for showing the info window */
	GtkWidget *info_button;

	/* List of proposals */
	GtkWidget *tree_view_proposals;
	GtkTreeViewColumn *tree_view_column_accelerator;
	GtkCellRenderer *cell_renderer_accelerator;
	GtkCellRenderer *cell_renderer_icon;

	/************************
	 * Completion management
	 ************************/

	GtkSourceCompletionModel *model_proposals;

	GList *providers;
	GList *interactive_providers;

	GtkSourceCompletionContext *context;
	GList *active_providers;
	GList *running_providers;

	guint show_timed_out_id;

	gint typing_line;
	gint typing_line_offset;

	gulong signals_ids[LAST_EXTERNAL_SIGNAL];

	gint min_auto_complete_delay;
	GList *auto_completion_selection;
	GtkSourceCompletionContext *auto_completion_context;

	/*************
	 * Properties
	 *************/

	GtkSourceView *view;
	guint num_accelerators;
	guint auto_complete_delay;
	guint proposal_page_size;
	guint provider_page_size;

	guint remember_info_visibility : 1;
	guint select_on_show : 1;
	guint show_headers : 1;
	guint show_icons : 1;
};

static guint signals[LAST_SIGNAL] = { 0 };

G_DEFINE_TYPE(GtkSourceCompletion, gtk_source_completion, G_TYPE_OBJECT)

static void update_completion (GtkSourceCompletion        *completion,
                               GList                      *providers,
                               GtkSourceCompletionContext *context);

static void show_info_cb (GtkWidget           *widget,
	                  GtkSourceCompletion *completion);

static gboolean
get_selected_proposal (GtkSourceCompletion          *completion,
                       GtkTreeIter                  *iter,
		       GtkSourceCompletionProvider **provider,
		       GtkSourceCompletionProposal **proposal)
{
	GtkTreeIter piter;
	GtkTreeSelection *selection;

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view_proposals));

	if (!gtk_tree_selection_get_selected (selection, NULL, &piter))
	{
		return FALSE;
	}

	if (gtk_source_completion_model_iter_is_header (completion->priv->model_proposals, &piter))
	{
		return FALSE;
	}

	if (iter != NULL)
	{
		*iter = piter;
	}

	if (provider != NULL)
	{
		gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->model_proposals), &piter,
				    GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROVIDER, provider,
				    -1);
	}

	if (proposal != NULL)
	{
		gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->model_proposals), &piter,
				    GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROPOSAL, proposal,
				    -1);
	}

	return TRUE;
}

static void
get_iter_at_insert (GtkSourceCompletion *completion,
                    GtkTextIter         *iter)
{
	GtkTextBuffer *buffer;

	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (completion->priv->view));
	gtk_text_buffer_get_iter_at_mark (buffer,
	                                  iter,
	                                  gtk_text_buffer_get_insert (buffer));
}

static void
completion_begin_block (GtkSourceCompletion *completion,
                        GtkSourceBuffer     *buffer)
{
	g_signal_handler_block (buffer, completion->priv->signals_ids[TEXT_BUFFER_INSERT_TEXT]);
	g_signal_handler_block (buffer, completion->priv->signals_ids[TEXT_BUFFER_DELETE_RANGE]);
}

static void
completion_end_block (GtkSourceCompletion *completion,
                      GtkSourceBuffer     *buffer)
{
	g_signal_handler_unblock (buffer, completion->priv->signals_ids[TEXT_BUFFER_INSERT_TEXT]);
	g_signal_handler_unblock (buffer, completion->priv->signals_ids[TEXT_BUFFER_DELETE_RANGE]);
}

static gboolean
activate_current_proposal (GtkSourceCompletion *completion)
{
	gboolean activated;
	GtkTreeIter iter;
	GtkTextIter titer;
	GtkSourceCompletionProposal *proposal = NULL;
	GtkSourceCompletionProvider *provider = NULL;
	GtkTextBuffer *buffer;
	gboolean has_start;
	GtkTextIter start;

	if (!get_selected_proposal (completion, &iter, &provider, &proposal))
	{
		DEBUG({
			g_print ("Hiding because activated without proposal\n");
		});

		gtk_source_completion_hide (completion);
		return TRUE;
	}

	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (completion->priv->view));
	gtk_text_buffer_get_start_iter (buffer, &start);

	has_start = gtk_source_completion_provider_get_start_iter (provider,
	                                                           completion->priv->context,
	                                                           proposal,
	                                                           &start);

	/* First hide the completion because the activation might actually
	   activate another one, which we don't want to hide */
	DEBUG({
		g_print ("Hiding just before proposal activation\n");
	});

	gtk_source_completion_hide (completion);

	/* Get insert iter */

	get_iter_at_insert (completion, &titer);

	completion_begin_block (completion, GTK_SOURCE_BUFFER (buffer));

	activated = gtk_source_completion_provider_activate_proposal (provider, proposal, &titer);

	if (!activated)
	{
		gchar *text = gtk_source_completion_proposal_get_text (proposal);

		if (has_start)
		{
			/* Replace from 'start' to 'titer' */
			gtk_text_buffer_begin_user_action (buffer);
			gtk_text_buffer_delete (buffer, &start, &titer);
			gtk_text_buffer_insert (buffer, &start, text, -1);
			gtk_text_buffer_end_user_action (buffer);
		}
		else
		{
			gtk_source_completion_utils_replace_current_word (GTK_SOURCE_BUFFER (buffer),
					                                  text,
					                                  -1);
		}
		g_free (text);
	}

	completion_end_block (completion, GTK_SOURCE_BUFFER (buffer));

	g_object_unref (provider);
	g_object_unref (proposal);

	return TRUE;
}

typedef gboolean (*ProposalSelector)(GtkSourceCompletion *completion,
                                     GtkTreeModel        *model,
                                     GtkTreeIter         *iter,
                                     gboolean             hasselection,
                                     gpointer             userdata);

static gboolean
select_proposal (GtkSourceCompletion *completion,
                 ProposalSelector     selector,
                 gpointer             userdata)
{
	GtkTreeSelection *selection;
	GtkTreeIter iter;
	GtkTreePath *path;
	GtkTreeModel *model;
	gboolean hasselection;

	if (!gtk_widget_get_visible (completion->priv->tree_view_proposals))
	{<commit_msg>Completion: little clean-up of activate_current_proposal()<commit_after>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
/* gtksourcecompletion.c
 * This file is part of GtkSourceView
 *
 * Copyright (C) 2007 -2009 <NAME> <<EMAIL>>
 * Copyright (C) 2009 - <NAME> <<EMAIL>>
 *
 * GtkSourceView is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * GtkSourceView 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

/**
 * SECTION:completion
 * @title: GtkSourceCompletion
 * @short_description: Main Completion Object
 *
 * The completion system helps the user when he writes some text, such
 * as words, command names, functions, and suchlike. Proposals can be
 * shown, to complete the text the user is writing. Each proposal can
 * contain an additional piece of information, that is displayed when
 * the "Details" button is active.
 *
 * Proposals are created via a #GtkSourceCompletionProvider. There can
 * be for example a provider to complete words (see
 * #GtkSourceCompletionWords), another provider for the completion of
 * function's names, etc. To add a provider, call
 * gtk_source_completion_add_provider().
 *
 * When the completion is activated, a #GtkSourceCompletionContext object is
 * created. The providers are asked whether they match the context, with
 * gtk_source_completion_provider_match(). If a provider doesn't match the
 * context, it will not be visible in the completion window. On the
 * other hand, if the provider matches the context, its proposals will
 * be displayed.
 *
 * When several providers match, they are all shown in the completion
 * window, but one can switch between providers: see the
 * #GtkSourceCompletion::move-page signal. It is also possible to
 * activate the first proposals with key bindings, see the
 * #GtkSourceCompletion:accelerators property.
 *
 * The #GtkSourceCompletionProposal interface represents a proposal.
 * The #GtkSourceCompletionItem class is a simple implementation of this
 * interface.
 *
 * If a proposal contains extra information (see
 * gtk_source_completion_provider_get_info_widget()), it will be
 * displayed in a #GtkSourceCompletionInfo window, which appears when
 * the "Details" button is clicked.
 *
 * A #GtkSourceCompletionInfo window can also be used to display
 * calltips. When no proposals are available, it can be useful to
 * display extra information like a function's prototype (number of
 * parameters, types of parameters, etc).
 *
 * Each #GtkSourceView object is associated with a #GtkSourceCompletion
 * instance. This instance can be obtained with
 * gtk_source_view_get_completion(). The #GtkSourceView class contains also the
 * #GtkSourceView::show-completion signal.
 *
 * A same #GtkSourceCompletionProvider object can be used for several
 * #GtkSourceCompletion.
 */

#include "gtksourcecompletion.h"
#include "gtksourcecompletion-private.h"
#include "gtksourcecompletionutils.h"
#include "gtksourcecompletionmodel.h"
#include "gtksourcecompletioncontext.h"
#include "gtksourcecompletioninfo.h"
#include "gtksourcecompletionproposal.h"
#include "gtksourcecompletionprovider.h"
#include "gtksourcebuffer.h"
#include "gtksourceview-marshal.h"
#include "gtksourceview-i18n.h"

#define WINDOW_WIDTH 350
#define WINDOW_HEIGHT 200

#define GTK_SOURCE_COMPLETION_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object),\
						  GTK_SOURCE_TYPE_COMPLETION,           \
						  GtkSourceCompletionPrivate))

/*#define ENABLE_DEBUG*/
#undef ENABLE_DEBUG

#ifdef ENABLE_DEBUG
#define DEBUG(x) (x)
#else
#define DEBUG(x)
#endif

/* Signals */
enum
{
	SHOW,
	HIDE,
	POPULATE_CONTEXT,

	/* Actions */
	ACTIVATE_PROPOSAL,
	MOVE_CURSOR,
	MOVE_PAGE,

	LAST_SIGNAL
};

enum
{
	PROP_0,
	PROP_VIEW,
	PROP_REMEMBER_INFO_VISIBILITY,
	PROP_SELECT_ON_SHOW,
	PROP_SHOW_HEADERS,
	PROP_SHOW_ICONS,
	PROP_ACCELERATORS,

	PROP_AUTO_COMPLETE_DELAY,

	PROP_PROVIDER_PAGE_SIZE,
	PROP_PROPOSAL_PAGE_SIZE
};

enum
{
	TEXT_VIEW_KEY_PRESS,
	TEXT_VIEW_FOCUS_OUT,
	TEXT_VIEW_BUTTON_PRESS,
	TEXT_VIEW_EDITABLE,
	TEXT_BUFFER_BEGIN_SIGNALS,
	TEXT_BUFFER_DELETE_RANGE,
	TEXT_BUFFER_INSERT_TEXT,
	TEXT_BUFFER_MARK_SET,
	SOURCE_BUFFER_REDO,
	SOURCE_BUFFER_REDO_AFTER,
	SOURCE_BUFFER_UNDO,
	SOURCE_BUFFER_UNDO_AFTER,
	TEXT_BUFFER_PASTE_DONE,
	LAST_EXTERNAL_SIGNAL
};

struct _GtkSourceCompletionPrivate
{
	/**********
	 * Widgets
	 **********/

	GtkWidget *main_window;

	/* Image and label in the bottom bar, on the right, for showing which
	 * provider(s) are selected. */
	GtkWidget *selection_image;
	GtkWidget *selection_label;

	/* The CompletionInfo window */
	GtkWidget *info_window;

	/* The default widget (a GtkLabel) for the info window */
	GtkWidget *default_info;

	/* The "Details" button, for showing the info window */
	GtkWidget *info_button;

	/* List of proposals */
	GtkWidget *tree_view_proposals;
	GtkTreeViewColumn *tree_view_column_accelerator;
	GtkCellRenderer *cell_renderer_accelerator;
	GtkCellRenderer *cell_renderer_icon;

	/************************
	 * Completion management
	 ************************/

	GtkSourceCompletionModel *model_proposals;

	GList *providers;
	GList *interactive_providers;

	GtkSourceCompletionContext *context;
	GList *active_providers;
	GList *running_providers;

	guint show_timed_out_id;

	gint typing_line;
	gint typing_line_offset;

	gulong signals_ids[LAST_EXTERNAL_SIGNAL];

	gint min_auto_complete_delay;
	GList *auto_completion_selection;
	GtkSourceCompletionContext *auto_completion_context;

	/*************
	 * Properties
	 *************/

	GtkSourceView *view;
	guint num_accelerators;
	guint auto_complete_delay;
	guint proposal_page_size;
	guint provider_page_size;

	guint remember_info_visibility : 1;
	guint select_on_show : 1;
	guint show_headers : 1;
	guint show_icons : 1;
};

static guint signals[LAST_SIGNAL] = { 0 };

G_DEFINE_TYPE(GtkSourceCompletion, gtk_source_completion, G_TYPE_OBJECT)

static void update_completion (GtkSourceCompletion        *completion,
                               GList                      *providers,
                               GtkSourceCompletionContext *context);

static void show_info_cb (GtkWidget           *widget,
	                  GtkSourceCompletion *completion);

/* Returns %TRUE if a proposal is selected.
 * Call g_object_unref() on @provider and @proposal when no longer needed.
 */
static gboolean
get_selected_proposal (GtkSourceCompletion          *completion,
                       GtkTreeIter                  *iter,
		       GtkSourceCompletionProvider **provider,
		       GtkSourceCompletionProposal **proposal)
{
	GtkTreeIter piter;
	GtkTreeSelection *selection;

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view_proposals));

	if (!gtk_tree_selection_get_selected (selection, NULL, &piter))
	{
		return FALSE;
	}

	if (gtk_source_completion_model_iter_is_header (completion->priv->model_proposals, &piter))
	{
		return FALSE;
	}

	if (iter != NULL)
	{
		*iter = piter;
	}

	if (provider != NULL)
	{
		gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->model_proposals), &piter,
				    GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROVIDER, provider,
				    -1);
	}

	if (proposal != NULL)
	{
		gtk_tree_model_get (GTK_TREE_MODEL (completion->priv->model_proposals), &piter,
				    GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROPOSAL, proposal,
				    -1);
	}

	return TRUE;
}

static void
get_iter_at_insert (GtkSourceCompletion *completion,
                    GtkTextIter         *iter)
{
	GtkTextBuffer *buffer;

	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (completion->priv->view));
	gtk_text_buffer_get_iter_at_mark (buffer,
	                                  iter,
	                                  gtk_text_buffer_get_insert (buffer));
}

static void
completion_begin_block (GtkSourceCompletion *completion,
                        GtkSourceBuffer     *buffer)
{
	g_signal_handler_block (buffer, completion->priv->signals_ids[TEXT_BUFFER_INSERT_TEXT]);
	g_signal_handler_block (buffer, completion->priv->signals_ids[TEXT_BUFFER_DELETE_RANGE]);
}

static void
completion_end_block (GtkSourceCompletion *completion,
                      GtkSourceBuffer     *buffer)
{
	g_signal_handler_unblock (buffer, completion->priv->signals_ids[TEXT_BUFFER_INSERT_TEXT]);
	g_signal_handler_unblock (buffer, completion->priv->signals_ids[TEXT_BUFFER_DELETE_RANGE]);
}

static void
activate_current_proposal (GtkSourceCompletion *completion)
{
	GtkSourceCompletionProvider *provider = NULL;
	GtkSourceCompletionProposal *proposal = NULL;
	GtkTextBuffer *buffer;
	gboolean has_start;
	GtkTextIter start_iter;
	GtkTextIter insert_iter;
	gboolean activated;

	if (!get_selected_proposal (completion, NULL, &provider, &proposal))
	{
		g_return_if_reached ();
	}

	has_start = gtk_source_completion_provider_get_start_iter (provider,
	                                                           completion->priv->context,
	                                                           proposal,
	                                                           &start_iter);

	/* First hide the completion because the activation might actually
	   activate another one, which we don't want to hide */
	DEBUG({
		g_print ("Hiding just before proposal activation\n");
	});

	gtk_source_completion_hide (completion);

	get_iter_at_insert (completion, &insert_iter);

	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (completion->priv->view));

	completion_begin_block (completion, GTK_SOURCE_BUFFER (buffer));

	activated = gtk_source_completion_provider_activate_proposal (provider, proposal, &insert_iter);

	if (!activated)
	{
		gchar *text = gtk_source_completion_proposal_get_text (proposal);

		if (has_start)
		{
			gtk_text_buffer_begin_user_action (buffer);
			gtk_text_buffer_delete (buffer, &start_iter, &insert_iter);
			gtk_text_buffer_insert (buffer, &start_iter, text, -1);
			gtk_text_buffer_end_user_action (buffer);
		}
		else
		{
			gtk_source_completion_utils_replace_current_word (GTK_SOURCE_BUFFER (buffer),
					                                  text,
					                                  -1);
		}

		g_free (text);
	}

	completion_end_block (completion, GTK_SOURCE_BUFFER (buffer));

	g_object_unref (provider);
	g_object_unref (proposal);
}

typedef gboolean (*ProposalSelector)(GtkSourceCompletion *completion,
                                     GtkTreeModel        *model,
                                     GtkTreeIter         *iter,
                                     gboolean             hasselection,
                                     gpointer             userdata);

static gboolean
select_proposal (GtkSourceCompletion *completion,
                 ProposalSelector     selector,
                 gpointer             userdata)
{
	GtkTreeSelection *selection;
	GtkTreeIter iter;
	GtkTreePath *path;
	GtkTreeModel *model;
	gboolean hasselection;

	if (!gtk_widget_get_visible (completion->priv->tree_view_proposals))
	{
{'fraction_non_alphanumeric': 0.08424148071267946, 'fraction_numerical': 0.0039785504238021106, 'mean_word_length': 3.734848484848485, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2213947', 'n_tokens_mistral': 7448, 'n_tokens_neox': 6969, 'n_words': 1844}