From 9bffb94cc43b8ca8a14214cfc8d5d47dfb6a2cd3 Mon Sep 17 00:00:00 2001 From: Matthew Brush Date: Thu, 10 Nov 2011 19:18:02 -0800 Subject: [PATCH] Real-time type keyword highlighting * Add new function: document_update_tags(). * Refactor the various tag update functions into document_update_tags(). * Remove extra call to update the tags in document_new_file(). --- HACKING | 2 +- src/callbacks.c | 1 - src/document.c | 176 +++++++++++++++++++++------------------------- src/document.h | 4 +- src/keybindings.c | 2 +- 5 files changed, 83 insertions(+), 102 deletions(-) diff --git a/HACKING b/HACKING index df7bd1f24..d4992d628 100644 --- a/HACKING +++ b/HACKING @@ -486,7 +486,7 @@ For brace indentation, update lexer_has_braces() in editor.c; indentation after ':' is done from on_new_line_added(). If the Scintilla lexer supports user type keyword highlighting (e.g. -SCLEX_CPP), update document_update_type_keywords() in document.c. +SCLEX_CPP), update document_update_tags() in document.c. Adding a TagManager parser ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/callbacks.c b/src/callbacks.c index 5429e14f3..911681386 100644 --- a/src/callbacks.c +++ b/src/callbacks.c @@ -621,7 +621,6 @@ void on_notebook1_switch_page_after(GtkNotebook *notebook, GtkNotebookPage *page ui_document_show_hide(doc); /* update the document menu */ build_menu_update(doc); sidebar_update_tag_list(doc, FALSE); - document_update_type_keywords(doc); /* We delay the check to avoid weird fast, unintended switching of notebook pages when * the 'file has changed' dialog is shown while the switch event is not yet completely diff --git a/src/document.c b/src/document.c index 043ad727d..f97960331 100644 --- a/src/document.c +++ b/src/document.c @@ -103,7 +103,6 @@ typedef struct static void document_undo_clear(GeanyDocument *doc); static void document_redo_add(GeanyDocument *doc, guint type, gpointer data); -static gboolean update_tags_from_buffer(GeanyDocument *doc); static gboolean remove_page(guint page_num); @@ -718,11 +717,10 @@ GeanyDocument *document_new_file(const gchar *utf8_filename, GeanyFiletype *ft, if (ft == NULL && utf8_filename != NULL) /* guess the filetype from the filename if one is given */ ft = filetypes_detect_from_document(doc); - document_set_filetype(doc, ft); /* also clears taglist */ + document_set_filetype(doc, ft); /* also re-parses tags */ ui_set_window_title(doc); build_menu_update(doc); - document_update_tag_list(doc, FALSE); document_set_text_changed(doc, FALSE); ui_document_show_hide(doc); /* update the document menu */ @@ -2207,41 +2205,35 @@ gint document_replace_all(GeanyDocument *doc, const gchar *find_text, const gcha } -static gboolean update_tags_from_buffer(GeanyDocument *doc) +/* + * Parses or re-parses the document's buffer and updates the type + * keywords and symbol list. + * + * @param doc The document. + */ +void document_update_tags(GeanyDocument *doc) { guchar *buffer_ptr; gsize len; + GString *keywords_str; + gchar *keywords; + gint keyword_idx; - len = sci_get_length(doc->editor->sci); + g_return_if_fail(DOC_VALID(doc)); + g_return_if_fail(app->tm_workspace != NULL); - /* gets a direct character pointer from Scintilla. - * this is OK because tm_source_file_buffer_update() does not modify the - * buffer, it only requires that buffer doesn't change while it's running, - * which it won't since it runs in this thread (ie. synchronously). - * see tagmanager/read.c:bufferOpen */ - buffer_ptr = (guchar *) scintilla_send_message(doc->editor->sci, SCI_GETCHARACTERPOINTER, 0, 0); - - return tm_source_file_buffer_update(doc->tm_file, buffer_ptr, len, TRUE); -} - - -void document_update_tag_list(GeanyDocument *doc, gboolean update) -{ - /* We must call sidebar_update_tag_list() before returning, - * to ensure that the symbol list is always updated properly (e.g. - * when creating a new document with a partial filename set. */ - gboolean success = FALSE; - - /* if the filetype doesn't have a tag parser or it is a new file */ - if (doc == NULL || doc->file_type == NULL || app->tm_workspace == NULL || - ! filetype_has_tags(doc->file_type) || ! doc->file_name) + /* early out if it's a new file or doesn't support tags */ + if (! doc->file_name || ! doc->file_type || !filetype_has_tags(doc->file_type)) { - /* set the default (empty) tag list */ + /* We must call sidebar_update_tag_list() before returning, + * to ensure that the symbol list is always updated properly (e.g. + * when creating a new document with a partial filename set. */ sidebar_update_tag_list(doc, FALSE); return; } - if (doc->tm_file == NULL) + /* create a new TM file if there isn't one yet */ + if (! doc->tm_file) { gchar *locale_filename = utils_get_locale_from_utf8(doc->file_name); const gchar *name; @@ -2251,28 +2243,65 @@ void document_update_tag_list(GeanyDocument *doc, gboolean update) doc->tm_file = tm_source_file_new(locale_filename, FALSE, name); g_free(locale_filename); - if (doc->tm_file) + if (doc->tm_file && !tm_workspace_add_object(doc->tm_file)) { - if (!tm_workspace_add_object(doc->tm_file)) - { - tm_work_object_free(doc->tm_file); - doc->tm_file = NULL; - } - else - { - if (update) - update_tags_from_buffer(doc); - success = TRUE; - } + tm_work_object_free(doc->tm_file); + doc->tm_file = NULL; } } - else + + /* early out if there's no work object and we couldn't create one */ + if (doc->tm_file == NULL) { - success = update_tags_from_buffer(doc); - if (G_UNLIKELY(! success)) - geany_debug("tag list updating failed"); + /* We must call sidebar_update_tag_list() before returning, + * to ensure that the symbol list is always updated properly (e.g. + * when creating a new document with a partial filename set. */ + sidebar_update_tag_list(doc, FALSE); + return; + } + + /* Parse Scintilla's buffer directly using TagManager + * Note: this buffer *MUST NOT* be modified */ + len = sci_get_length(doc->editor->sci); + buffer_ptr = (guchar *) scintilla_send_message(doc->editor->sci, SCI_GETCHARACTERPOINTER, 0, 0); + tm_source_file_buffer_update(doc->tm_file, buffer_ptr, len, TRUE); + + sidebar_update_tag_list(doc, TRUE); + + /* some filetypes support type keywords (such as struct names), but not + * necessarily all filetypes for a particular scintilla lexer. this + * tells us whether the filetype supports keywords, and if so + * which index to use for the scintilla keywords set. */ + switch (doc->file_type->id) + { + case GEANY_FILETYPES_C: + case GEANY_FILETYPES_CPP: + case GEANY_FILETYPES_CS: + case GEANY_FILETYPES_D: + case GEANY_FILETYPES_JAVA: + case GEANY_FILETYPES_VALA: + { + + /* index of the keyword set in the Scintilla lexer, for + * example in LexCPP.cxx, see "cppWordLists" global array. + * TODO: this magic number should be a member of the filetype */ + keyword_idx = 3; + break; + } + default: + return; /* early out if type keywords are not supported */ + } + + /* get any type keywords and tell scintilla about them + * this will cause the type keywords to be colourized in scintilla */ + keywords_str = symbols_find_tags_as_string(app->tm_workspace->work_object.tags_array, + TM_GLOBAL_TYPE_MASK, doc->file_type->lang); + if (keywords_str) + { + keywords = g_string_free(keywords_str, FALSE); + sci_set_keywords(doc->editor->sci, keyword_idx, keywords); + g_free(keywords); } - sidebar_update_tag_list(doc, success); } @@ -2284,9 +2313,11 @@ static gboolean on_document_update_tag_list_idle(gpointer data) return FALSE; if (! main_status.quitting) - document_update_tag_list(doc, TRUE); + document_update_tags(doc); doc->priv->tag_list_update_source = 0; + + /* don't update the tags until another modification of the buffer */ return FALSE; } @@ -2296,59 +2327,15 @@ void document_update_tag_list_in_idle(GeanyDocument *doc) if (editor_prefs.autocompletion_update_freq <= 0 || ! filetype_has_tags(doc->file_type)) return; + /* prevent "stacking up" callback handlers, we only need one to run soon */ if (doc->priv->tag_list_update_source != 0) g_source_remove(doc->priv->tag_list_update_source); + doc->priv->tag_list_update_source = g_timeout_add_full(G_PRIORITY_LOW, editor_prefs.autocompletion_update_freq, on_document_update_tag_list_idle, doc, NULL); } -/* - * Updates the type keywords in the document's Scintilla widget. - * - * @param doc The document - */ -void document_update_type_keywords(GeanyDocument *doc) -{ - guint keyword_idx; - gchar *keywords; - GString *str; - GPtrArray *tags_array; - - g_return_if_fail(DOC_VALID(doc)); - g_return_if_fail(app->tm_workspace != NULL); - - switch (doc->file_type->id) - { - case GEANY_FILETYPES_C: - case GEANY_FILETYPES_CPP: - case GEANY_FILETYPES_CS: - case GEANY_FILETYPES_D: - case GEANY_FILETYPES_JAVA: - case GEANY_FILETYPES_VALA: - /* index of the keyword set in the Scintilla lexer, for - * example in LexCPP.cxx, see "cppWordLists" global array. */ - keyword_idx = 3; - break; - /* early out if user type keywords are not supported */ - default: - return; - } - - tags_array = app->tm_workspace->work_object.tags_array; - if (tags_array) - { - str = symbols_find_tags_as_string(tags_array, TM_GLOBAL_TYPE_MASK, doc->file_type->lang); - if (str) - { - keywords = g_string_free(str, FALSE); - sci_set_keywords(doc->editor->sci, keyword_idx, keywords); - g_free(keywords); - } - } -} - - static void document_load_config(GeanyDocument *doc, GeanyFiletype *type, gboolean filetype_changed) { @@ -2377,10 +2364,7 @@ static void document_load_config(GeanyDocument *doc, GeanyFiletype *type, doc->priv->symbol_list_sort_mode = type->priv->symbol_list_sort_mode; } - document_update_tag_list(doc, TRUE); - - /* Update session typename keywords. */ - document_update_type_keywords(doc); + document_update_tags(doc); } diff --git a/src/document.h b/src/document.h index 6642da08e..28d7bd39d 100644 --- a/src/document.h +++ b/src/document.h @@ -232,12 +232,10 @@ gint document_replace_all(GeanyDocument *doc, const gchar *find_text, const gcha void document_replace_sel(GeanyDocument *doc, const gchar *find_text, const gchar *replace_text, const gchar *original_find_text, const gchar *original_replace_text, gint flags); -void document_update_tag_list(GeanyDocument *doc, gboolean update); +void document_update_tags(GeanyDocument *doc); void document_update_tag_list_in_idle(GeanyDocument *doc); -void document_update_type_keywords(GeanyDocument *doc); - void document_set_encoding(GeanyDocument *doc, const gchar *new_encoding); gboolean document_check_disk_status(GeanyDocument *doc, gboolean force); diff --git a/src/keybindings.c b/src/keybindings.c index 022cc4a13..8d2e763f1 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -2544,7 +2544,7 @@ static gboolean cb_func_document_action(guint key_id) ui_document_show_hide(doc); break; case GEANY_KEYS_DOCUMENT_RELOADTAGLIST: - document_update_tag_list(doc, TRUE); + document_update_tags(doc); break; case GEANY_KEYS_DOCUMENT_FOLDALL: editor_fold_all(doc->editor);