>From 5bbfd931eebe06ac391b3de1b41cbcbff9c295cb Mon Sep 17 00:00:00 2001
From: Colomban Wendling <ban@herbesfolles.org>
Date: Mon, 26 Sep 2011 23:26:34 +0200
Subject: [PATCH 2/2] Tell the user if real-time reparsing is slow and let her
 disable it

---
 src/callbacks.c       |    2 +-
 src/document.c        |   83 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/document.h        |    2 +-
 src/documentprivate.h |    2 +
 src/editor.c          |    3 +-
 src/editor.h          |    2 +
 src/keyfile.c         |    4 ++
 7 files changed, 94 insertions(+), 4 deletions(-)

diff --git a/src/callbacks.c b/src/callbacks.c
index 65af4eb..937717e 100644
--- a/src/callbacks.c
+++ b/src/callbacks.c
@@ -2135,6 +2135,6 @@ void on_menu_real_time_symbols_activate(GtkCheckMenuItem *menuitem, gpointer use
 
 	if (doc != NULL)
 	{
-		document_set_auto_update_symbols(doc, gtk_check_menu_item_get_active(menuitem));
+		document_set_auto_update_symbols(doc, gtk_check_menu_item_get_active(menuitem), TRUE);
 	}
 }
diff --git a/src/document.c b/src/document.c
index e610112..4deb5c6 100644
--- a/src/document.c
+++ b/src/document.c
@@ -391,6 +391,8 @@ static void init_doc_struct(GeanyDocument *new_doc)
 	priv->line_count = 0;
 	priv->tag_list_update.enabled = editor_prefs.autocompletion_update_freq > 0;
 	priv->tag_list_update.source = 0;
+	priv->tag_list_update.exceedings = 0;
+	priv->tag_list_update.user_choice = FALSE;
 #ifndef USE_GIO_FILEMON
 	priv->last_check = time(NULL);
 #endif
@@ -593,8 +595,11 @@ static GeanyDocument *document_create(const gchar *utf8_filename)
 
 
 /* set whether real-time tag parsing is enabled for a document */
-void document_set_auto_update_symbols(GeanyDocument *doc, gboolean enable)
+void document_set_auto_update_symbols(GeanyDocument *doc, gboolean enable, gboolean user_choice)
 {
+	doc->priv->tag_list_update.user_choice = user_choice;
+	doc->priv->tag_list_update.exceedings = 0;
+
 	if (enable != doc->priv->tag_list_update.enabled)
 	{
 		doc->priv->tag_list_update.enabled = enable;
@@ -2326,6 +2331,49 @@ void document_update_tag_list(GeanyDocument *doc, gboolean update)
 }
 
 
+static void ask_user_for_slow_reparsing(GeanyDocument *doc)
+{
+	GtkWidget *dialog;
+	guint i;
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(main_widgets.window), 0,
+		GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
+		_("Do you want to disable symbols real-time updates?"));
+	gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
+	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
+		_("Real-time updates of the symbols in this document seems to be slow, "
+		  "do you want to disable them?"));
+	gtk_dialog_add_buttons(GTK_DIALOG(dialog),
+		GTK_STOCK_NO, GTK_RESPONSE_NO,
+		_("Disable for the Document"), 1,
+		_("Disable Globally"), 2,
+		NULL);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), 1);
+
+	/* TODO: use a GtkInfoBar-like, because:
+	 * - it is less annoying for the user, and this is not a vital decision
+	 * - since the user is likely to be typing, she might take a decision accidentally */
+
+	switch (gtk_dialog_run(GTK_DIALOG(dialog)))
+	{
+		case 1:
+			document_set_auto_update_symbols(doc, FALSE, TRUE);
+			break;
+
+		case 2:
+			editor_prefs.autocompletion_update_freq = 0;
+			foreach_document(i)
+			{
+				document_set_auto_update_symbols(documents[i], FALSE, TRUE);
+			}
+			break;
+	}
+	gtk_widget_destroy(dialog);
+
+	doc->priv->tag_list_update.user_choice = TRUE;
+}
+
+
 static gboolean on_document_update_tag_list_idle(gpointer data)
 {
 	GeanyDocument *doc = data;
@@ -2334,8 +2382,41 @@ static gboolean on_document_update_tag_list_idle(gpointer data)
 		return FALSE;
 
 	if (! main_status.quitting)
+	{
+		static GTimer *timer = NULL;
+
+		if (G_UNLIKELY (! timer))
+			timer = g_timer_new ();
+		else
+			g_timer_reset (timer);
+
 		document_update_tag_list(doc, TRUE);
 
+		/* if elapsed time is greater than the maximum one, consider disabling the live
+		 * updates.  but we don't do so the first time an exceeding happens because it might
+		 * be caused by an temporary problem (external heavy workload, etc.).
+		 * the user can configure this for it to fit her needs */
+		if (! doc->priv->tag_list_update.user_choice /* only ask it user haven't already chosen */ &&
+			editor_prefs.autocompletion_max_duration > 0 /* 0 means disabled */)
+		{
+			if ((g_timer_elapsed (timer, NULL) * 1000) >= editor_prefs.autocompletion_max_duration)
+			{
+				/* give more weight to exceedings */
+				doc->priv->tag_list_update.exceedings += 2;
+				if (doc->priv->tag_list_update.exceedings >=
+					(editor_prefs.autocompletion_max_exceedings * 2))
+				{
+					ask_user_for_slow_reparsing(doc);
+				}
+			}
+			else if (doc->priv->tag_list_update.exceedings > 0)
+			{
+				/* give less weight to success */
+				doc->priv->tag_list_update.exceedings -= 1;
+			}
+		}
+	}
+
 	doc->priv->tag_list_update.source = 0;
 	return FALSE;
 }
diff --git a/src/document.h b/src/document.h
index 3c9afc4..7b9049f 100644
--- a/src/document.h
+++ b/src/document.h
@@ -196,7 +196,7 @@ void document_init_doclist(void);
 
 void document_finalize(void);
 
-void document_set_auto_update_symbols(GeanyDocument *doc, gboolean enable);
+void document_set_auto_update_symbols(GeanyDocument *doc, gboolean enable, gboolean user_choice);
 
 gboolean document_remove_page(guint page_num);
 
diff --git a/src/documentprivate.h b/src/documentprivate.h
index 5f6f582..1327324 100644
--- a/src/documentprivate.h
+++ b/src/documentprivate.h
@@ -88,6 +88,8 @@ typedef struct GeanyDocumentPrivate
 	{
 		gboolean	 enabled;		/* whether to real-time update tags for this document */
 		guint		 source;		/* ID of the idle callback updating the tag list */
+		gint		 exceedings;	/* the number of times we took excessive time to reparse the tags */
+		gboolean	 user_choice;	/* whether the user made a choice for this particular document */
 	} tag_list_update;
 }
 GeanyDocumentPrivate;
diff --git a/src/editor.c b/src/editor.c
index 6605f82..fde303a 100644
--- a/src/editor.c
+++ b/src/editor.c
@@ -4941,7 +4941,8 @@ void editor_apply_update_prefs(GeanyEditor *editor)
 
 	/* real-time tag parsing */
 	/* FIXME: only change the setting for a document if it used the previous default? */
-	document_set_auto_update_symbols(editor->document, editor_prefs.autocompletion_update_freq > 0);
+	document_set_auto_update_symbols(editor->document,
+		editor_prefs.autocompletion_update_freq > 0, FALSE);
 }
 
 
diff --git a/src/editor.h b/src/editor.h
index 9da00c4..89f5922 100644
--- a/src/editor.h
+++ b/src/editor.h
@@ -148,6 +148,8 @@ typedef struct GeanyEditorPrefs
 	/* This setting may be overridden when a project is opened. Use @c editor_get_prefs(). */
 	gboolean	long_line_enabled;
 	gint		autocompletion_update_freq;
+	gint		autocompletion_max_duration; /* 0 means no check */
+	gint		autocompletion_max_exceedings;
 }
 GeanyEditorPrefs;
 
diff --git a/src/keyfile.c b/src/keyfile.c
index ace0091..166f6d9 100644
--- a/src/keyfile.c
+++ b/src/keyfile.c
@@ -209,6 +209,10 @@ static void init_pref_groups(void)
 		"use_gtk_word_boundaries", TRUE);
 	stash_group_add_boolean(group, &editor_prefs.complete_snippets_whilst_editing,
 		"complete_snippets_whilst_editing", FALSE);
+	stash_group_add_integer(group, &editor_prefs.autocompletion_max_duration,
+		"autocompletion_max_duration", 500);
+	stash_group_add_integer(group, &editor_prefs.autocompletion_max_exceedings,
+		"autocompletion_max_exceedings", 3);
 	stash_group_add_boolean(group, &file_prefs.use_safe_file_saving,
 		atomic_file_saving_key, FALSE);
 	stash_group_add_boolean(group, &file_prefs.gio_unsafe_save_backup,
-- 
1.7.6.3

