From 00d9357fe5f359a3a901cfc2dcc0f41d4536c6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 3 Oct 2023 18:24:30 +0200 Subject: [PATCH 1/9] Add API for LSP plugins This patch adds a simple API allowing LSP plugins to take control of certain Geany built-in features and disabling their implementation in Geany. Currently, autocompletion, calltips, and going to definition/declaration is supported. Plugins should define the Lsp struct, fill the pointers to the functions implementing the features they override, and register themselves using the lsp_register() call. Similarly, they should unregister themselves using lsp_unregister() when they don't want to provide the Lsp features any more, or, latest, when the plugin gets unloaded. Each of the currently supported features is implemented using two functions - one checking whether Lsp plugin will override Geany's behavior of this feature, and the other function implementing the feature. For instance for autocompletion: - autocomplete_available(doc) should return TRUE if the plugin performs autocompletion for the doc's filetype. This function is used to deactivate Geany's autocompletion when the Lsp plugin supports it. - autocomplete_perform(doc) calls the corresponding interface of the LSP server (which responds asynchronously so when autocomplete_perform() is executed, the results won't be available yet) The Lsp struct is padded with an array large enough to allow additions of many more functions to the struct and allowing binary compatibility with older LSP plugin versions not implementing all the functions. It's just mandatory that all plugins zero-initialize the allocated Lsp struct (either by creating it on the heap or by allocating it using g_new0() or similar function). NULL pointers in this struct are handled gracefully. The current implementation supports only a single LSP plugin. However, if in the future arises the need for more LSP plugins running in parallel (e.g. a specialized LSP plugin for certain language together with the current "generic" LSP plugin), the implementation could be extended to support a list of Lsp structs and trying to call the functions defined by them one by one. The current interface should already be sufficient to support such an implementation. --- meson.build | 3 ++ src/Makefile.am | 2 + src/editor.c | 32 +++++++++----- src/keybindings.c | 11 ++++- src/lsp.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++ src/lsp.h | 59 +++++++++++++++++++++++++ src/symbols.c | 9 ++++ 7 files changed, 211 insertions(+), 12 deletions(-) create mode 100644 src/lsp.c create mode 100644 src/lsp.h diff --git a/meson.build b/meson.build index 100d774772..30efc6fe23 100644 --- a/meson.build +++ b/meson.build @@ -780,6 +780,7 @@ install_headers( 'src/toolbar.h', 'src/ui_utils.h', 'src/utils.h', + 'src/lsp.h', subdir: 'geany' ) @@ -819,6 +820,8 @@ libgeany = shared_library('geany', 'src/keyfile.h', 'src/log.c', 'src/log.h', + 'src/lsp.c', + 'src/lsp.h', 'src/libmain.c', 'src/main.h', 'src/geany.h', diff --git a/src/Makefile.am b/src/Makefile.am index c94bcfbbba..58f0d06bf5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -47,6 +47,7 @@ geany_include_HEADERS = \ gtkcompat.h \ highlighting.h \ keybindings.h \ + lsp.h \ main.h \ msgwindow.h \ navqueue.h \ @@ -86,6 +87,7 @@ libgeany_la_SOURCES = \ keyfile.c keyfile.h \ log.c log.h \ libmain.c main.h geany.h \ + lsp.c lsp.h \ msgwindow.c msgwindow.h \ navqueue.c navqueue.h \ notebook.c notebook.h \ diff --git a/src/editor.c b/src/editor.c index 0dd1e0a62d..0fa137a11c 100644 --- a/src/editor.c +++ b/src/editor.c @@ -45,6 +45,7 @@ #include "geanyobject.h" #include "highlighting.h" #include "keybindings.h" +#include "lsp.h" #include "main.h" #include "prefs.h" #include "projectprivate.h" @@ -671,9 +672,13 @@ static gboolean reshow_calltip(gpointer data) g_return_val_if_fail(calltip.sci != NULL, FALSE); - SSM(calltip.sci, SCI_CALLTIPCANCEL, 0, 0); doc = document_get_current(); + if (lsp_calltips_available(doc)) + return FALSE; + + SSM(calltip.sci, SCI_CALLTIPCANCEL, 0, 0); + if (doc && doc->editor->sci == calltip.sci) { /* we use the position where the calltip was previously started as SCI_GETCURRENTPOS @@ -821,13 +826,15 @@ static void on_char_added(GeanyEditor *editor, SCNotification *nt) case '(': { auto_close_chars(sci, pos, nt->ch); - /* show calltips */ - editor_show_calltip(editor, --pos); + if (!lsp_calltips_available(editor->document)) + /* show calltips */ + editor_show_calltip(editor, --pos); break; } case ')': { /* hide calltips */ - if (SSM(sci, SCI_CALLTIPACTIVE, 0, 0)) + if (SSM(sci, SCI_CALLTIPACTIVE, 0, 0) && + !lsp_calltips_available(editor->document)) { SSM(sci, SCI_CALLTIPCANCEL, 0, 0); } @@ -857,13 +864,15 @@ static void on_char_added(GeanyEditor *editor, SCNotification *nt) case ':': /* C/C++ class:: syntax */ /* tag autocompletion */ default: -#if 0 - if (! editor_start_auto_complete(editor, pos, FALSE)) - request_reshowing_calltip(nt); -#else editor_start_auto_complete(editor, pos, FALSE); -#endif } + + if (lsp_calltips_available(editor->document)) + lsp_calltips_show(editor->document); + + if (lsp_autocomplete_available(editor->document)) + lsp_autocomplete_perform(editor->document); + check_line_breaking(editor, pos); } @@ -1171,7 +1180,7 @@ static gboolean on_editor_notify(G_GNUC_UNUSED GObject *object, GeanyEditor *edi break; case SCN_CALLTIPCLICK: - if (nt->position > 0) + if (!lsp_calltips_available(doc) && nt->position > 0) { switch (nt->position) { @@ -2222,6 +2231,9 @@ gboolean editor_start_auto_complete(GeanyEditor *editor, gint pos, gboolean forc g_return_val_if_fail(editor != NULL, FALSE); + if (lsp_autocomplete_available(editor->document)) + return FALSE; + if (! editor_prefs.auto_complete_symbols && ! force) return FALSE; diff --git a/src/keybindings.c b/src/keybindings.c index 5cf755ab57..d228c98118 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -39,6 +39,7 @@ #include "filetypes.h" #include "geanyobject.h" #include "keybindingsprivate.h" +#include "lsp.h" #include "main.h" #include "msgwindow.h" #include "navqueue.h" @@ -2151,10 +2152,16 @@ static gboolean cb_func_editor_action(guint key_id) sci_send_command(doc->editor->sci, SCI_LINETRANSPOSE); break; case GEANY_KEYS_EDITOR_AUTOCOMPLETE: - editor_start_auto_complete(doc->editor, sci_get_current_position(doc->editor->sci), TRUE); + if (lsp_autocomplete_available(doc)) + lsp_autocomplete_perform(doc); + else + editor_start_auto_complete(doc->editor, sci_get_current_position(doc->editor->sci), TRUE); break; case GEANY_KEYS_EDITOR_CALLTIP: - editor_show_calltip(doc->editor, -1); + if (lsp_calltips_available(doc)) + lsp_calltips_show(doc); + else + editor_show_calltip(doc->editor, -1); break; case GEANY_KEYS_EDITOR_CONTEXTACTION: if (check_current_word(doc, FALSE)) diff --git a/src/lsp.c b/src/lsp.c new file mode 100644 index 0000000000..e66fc9aa86 --- /dev/null +++ b/src/lsp.c @@ -0,0 +1,107 @@ +/* + * lsp.c - this file is part of Geany, a fast and lightweight IDE + * + * Copyright 2023 The Geany contributors + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "lsp.h" + + +gboolean func_return_false(GeanyDocument *doc) +{ + return FALSE; +} + + +void func_args_doc(GeanyDocument *doc) +{ +} + + +void func_args_doc_bool(GeanyDocument *doc, gboolean dummy) +{ +} + + +static Lsp dummy_lsp = { + .autocomplete_available = func_return_false, + .autocomplete_perform = func_args_doc, + + .calltips_available = func_return_false, + .calltips_show = func_args_doc, + + .goto_available = func_return_false, + .goto_perform = func_args_doc_bool, +}; + +static Lsp *current_lsp = &dummy_lsp; + + +GEANY_API_SYMBOL +void lsp_register(Lsp *lsp) +{ + /* possibly, in the future if there's a need for multiple LSP plugins, + * have a list of LSP clients and add/remove to/from the list */ + current_lsp = lsp; +} + + +GEANY_API_SYMBOL +void lsp_unregister(Lsp *lsp) +{ + current_lsp = &dummy_lsp; +} + + +/* allow LSP plugins not to implement all the functions (might happen if we + * add more in the future) and fall back to the dummy implementation */ +#define CALL_IF_EXISTS(f) (current_lsp->f ? current_lsp->f : dummy_lsp.f) + +gboolean lsp_autocomplete_available(GeanyDocument *doc) +{ + return CALL_IF_EXISTS(autocomplete_available)(doc); +} + + +void lsp_autocomplete_perform(GeanyDocument *doc) +{ + CALL_IF_EXISTS(autocomplete_perform)(doc); +} + + +gboolean lsp_calltips_available(GeanyDocument *doc) +{ + return CALL_IF_EXISTS(calltips_available)(doc); +} + + +void lsp_calltips_show(GeanyDocument *doc) +{ + CALL_IF_EXISTS(calltips_show)(doc); +} + + +gboolean lsp_goto_available(GeanyDocument *doc) +{ + return CALL_IF_EXISTS(goto_available)(doc); +} + + +void lsp_goto_perform(GeanyDocument *doc, gboolean definition) +{ + CALL_IF_EXISTS(goto_perform)(doc, definition); +} diff --git a/src/lsp.h b/src/lsp.h new file mode 100644 index 0000000000..9a20f4cecf --- /dev/null +++ b/src/lsp.h @@ -0,0 +1,59 @@ +/* + * lsp.h - this file is part of Geany, a fast and lightweight IDE + * + * Copyright 2023 The Geany contributors + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef GEANY_LSP_H +#define GEANY_LSP_H 1 + +#include "document.h" + +G_BEGIN_DECLS + +typedef struct { + gboolean (*autocomplete_available)(GeanyDocument *doc); + void (*autocomplete_perform)(GeanyDocument *doc); + + gboolean (*calltips_available)(GeanyDocument *doc); + void (*calltips_show)(GeanyDocument *doc); + + gboolean (*goto_available)(GeanyDocument *doc); + void (*goto_perform)(GeanyDocument *doc, gboolean definition); +} Lsp; + + +void lsp_register(Lsp *lsp); +void lsp_unregister(Lsp *lsp); + + +#ifdef GEANY_PRIVATE + +gboolean lsp_autocomplete_available(GeanyDocument *doc); +void lsp_autocomplete_perform(GeanyDocument *doc); + +gboolean lsp_calltips_available(GeanyDocument *doc); +void lsp_calltips_show(GeanyDocument *doc); + +gboolean lsp_goto_available(GeanyDocument *doc); +void lsp_goto_perform(GeanyDocument *doc, gboolean definition); + +#endif /* GEANY_PRIVATE */ + +G_END_DECLS + +#endif /* GEANY_LSP_H */ diff --git a/src/symbols.c b/src/symbols.c index 567a9f62ca..db94f3ab28 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -44,6 +44,7 @@ #include "filetypesprivate.h" #include "geanyobject.h" #include "highlighting.h" +#include "lsp.h" #include "main.h" #include "navqueue.h" #include "sciwrappers.h" @@ -1688,6 +1689,14 @@ static gboolean goto_tag(const gchar *name, gboolean definition) gboolean symbols_goto_tag(const gchar *name, gboolean definition) { + GeanyDocument *doc = document_get_current(); + + if (lsp_goto_available(doc)) + { + lsp_goto_perform(doc, definition); + return TRUE; + } + if (goto_tag(name, definition)) return TRUE; From 74f7d0b9900b43622c1a6caa9ae86a805efcc2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Thu, 2 Nov 2023 22:23:01 +0100 Subject: [PATCH 2/9] Add padding for possible future extensions --- src/lsp.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lsp.h b/src/lsp.h index 9a20f4cecf..4e57f3f685 100644 --- a/src/lsp.h +++ b/src/lsp.h @@ -34,6 +34,8 @@ typedef struct { gboolean (*goto_available)(GeanyDocument *doc); void (*goto_perform)(GeanyDocument *doc, gboolean definition); + + gchar _dummy[1024]; } Lsp; From 2ca122762d68cfcf1c4cc8dbe6f1404c3cbb64a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 24 Oct 2023 23:58:33 +0200 Subject: [PATCH 3/9] LSP interface for document symbol queries Since the whole code of the symbol tree is quite complex, the idea of the interface between Geany and LSP plugins is to reuse as much of the Geany symbol tree code as possible. This means using the TMTag structs by LSP plugins to pass the symbol data to Geany. The main interface consists of 3 functions: - lsp_doc_symbols_available(GeanyDocument *doc) - checking whether the plugin supports symbol tree queries for the document of the given file type - void lsp_doc_symbols_request(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data) - function performing asynchronous request to the LSP server to obtain symbols for the current document. Once available, callback is executed. - GPtrArray *lsp_doc_symbols_get_cached(GeanyDocument *doc) - this function returns (synchronously) the symbols obtained by the last call of lsp_doc_symbols_request(). The return value of this function is an array of TMTag structures which are filled by the plugin as specified below. TMTag: - name - the name of the symbol - type - the SymbolKind code as specified by LSP specification - line - the line of the symbol - arglist - the "detail" field of LSP document symbol (typically some form of function signature) - scope - scope obtained from serializing the children field of LSP document symbol with the scope separator valid for the given filetype, obtained by symbols_get_context_separator() In addition, there's a auxiliary function lsp_get_symbols_icon_id() that maps LSP symbol kind (passed inside TMTag type member) to a Geany symbol icon (TM_ICON_...). --- src/lsp.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lsp.h | 12 +++++++ 2 files changed, 114 insertions(+) diff --git a/src/lsp.c b/src/lsp.c index e66fc9aa86..56aec6abc5 100644 --- a/src/lsp.c +++ b/src/lsp.c @@ -21,12 +21,79 @@ #include "lsp.h" +typedef enum { + LspKindFile = 1, + LspKindModule, + LspKindNamespace, + LspKindPackage, + LspKindClass, + LspKindMethod, + LspKindProperty, + LspKindField, + LspKindConstructor, + LspKindEnum, + LspKindInterface, + LspKindFunction, + LspKindVariable, + LspKindConstant, + LspKindString, + LspKindNumber, + LspKindBoolean, + LspKindArray, + LspKindObject, + LspKindKey, + LspKindNull, + LspKindEnumMember, + LspKindStruct, + LspKindEvent, + LspKindOperator, + LspKindTypeParameter, + LSP_KIND_NUM = LspKindTypeParameter +} LspSymbolKind; /* note: enums different than in LspCompletionItemKind */ + + +static LspSymbolKind kind_icons[LSP_KIND_NUM] = { + TM_ICON_NAMESPACE, /* LspKindFile */ + TM_ICON_NAMESPACE, /* LspKindModule */ + TM_ICON_NAMESPACE, /* LspKindNamespace */ + TM_ICON_NAMESPACE, /* LspKindPackage */ + TM_ICON_CLASS, /* LspKindClass */ + TM_ICON_METHOD, /* LspKindMethod */ + TM_ICON_MEMBER, /* LspKindProperty */ + TM_ICON_MEMBER, /* LspKindField */ + TM_ICON_METHOD, /* LspKindConstructor */ + TM_ICON_STRUCT, /* LspKindEnum */ + TM_ICON_CLASS, /* LspKindInterface */ + TM_ICON_METHOD, /* LspKindFunction */ + TM_ICON_VAR, /* LspKindVariable */ + TM_ICON_MACRO, /* LspKindConstant */ + TM_ICON_OTHER, /* LspKindString */ + TM_ICON_OTHER, /* LspKindNumber */ + TM_ICON_OTHER, /* LspKindBoolean */ + TM_ICON_OTHER, /* LspKindArray */ + TM_ICON_OTHER, /* LspKindObject */ + TM_ICON_OTHER, /* LspKindKey */ + TM_ICON_OTHER, /* LspKindNull */ + TM_ICON_MEMBER, /* LspKindEnumMember */ + TM_ICON_STRUCT, /* LspKindStruct */ + TM_ICON_OTHER, /* LspKindEvent */ + TM_ICON_METHOD, /* LspKindOperator */ + TM_ICON_OTHER /* LspKindTypeParameter */ +}; + + gboolean func_return_false(GeanyDocument *doc) { return FALSE; } +GPtrArray *func_return_ptrarr(GeanyDocument *doc) +{ + return NULL; +} + + void func_args_doc(GeanyDocument *doc) { } @@ -37,6 +104,11 @@ void func_args_doc_bool(GeanyDocument *doc, gboolean dummy) } +void func_args_doc_symcallback_ptr(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data) +{ +} + + static Lsp dummy_lsp = { .autocomplete_available = func_return_false, .autocomplete_perform = func_args_doc, @@ -46,6 +118,10 @@ static Lsp dummy_lsp = { .goto_available = func_return_false, .goto_perform = func_args_doc_bool, + + .doc_symbols_available = func_return_false, + .doc_symbols_request = func_args_doc_symcallback_ptr, + .doc_symbols_get_cached = func_return_ptrarr }; static Lsp *current_lsp = &dummy_lsp; @@ -67,6 +143,14 @@ void lsp_unregister(Lsp *lsp) } +guint lsp_get_symbols_icon_id(guint kind) +{ + if (kind >= LspKindFile && kind <= LSP_KIND_NUM) + return kind_icons[kind - 1]; + return TM_ICON_STRUCT; +} + + /* allow LSP plugins not to implement all the functions (might happen if we * add more in the future) and fall back to the dummy implementation */ #define CALL_IF_EXISTS(f) (current_lsp->f ? current_lsp->f : dummy_lsp.f) @@ -105,3 +189,21 @@ void lsp_goto_perform(GeanyDocument *doc, gboolean definition) { CALL_IF_EXISTS(goto_perform)(doc, definition); } + + +gboolean lsp_doc_symbols_available(GeanyDocument *doc) +{ + return CALL_IF_EXISTS(doc_symbols_available)(doc); +} + + +void lsp_doc_symbols_request(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data) +{ + CALL_IF_EXISTS(doc_symbols_request)(doc, callback, user_data); +} + + +GPtrArray *lsp_doc_symbols_get_cached(GeanyDocument *doc) +{ + return CALL_IF_EXISTS(doc_symbols_get_cached)(doc); +} diff --git a/src/lsp.h b/src/lsp.h index 4e57f3f685..91f8d227d9 100644 --- a/src/lsp.h +++ b/src/lsp.h @@ -25,6 +25,8 @@ G_BEGIN_DECLS +typedef void (*LspSymbolRequestCallback) (gpointer user_data); + typedef struct { gboolean (*autocomplete_available)(GeanyDocument *doc); void (*autocomplete_perform)(GeanyDocument *doc); @@ -35,6 +37,10 @@ typedef struct { gboolean (*goto_available)(GeanyDocument *doc); void (*goto_perform)(GeanyDocument *doc, gboolean definition); + gboolean (*doc_symbols_available)(GeanyDocument *doc); + void (*doc_symbols_request)(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data); + GPtrArray *(*doc_symbols_get_cached)(GeanyDocument *doc); + gchar _dummy[1024]; } Lsp; @@ -45,6 +51,8 @@ void lsp_unregister(Lsp *lsp); #ifdef GEANY_PRIVATE +guint lsp_get_symbols_icon_id(guint kind); + gboolean lsp_autocomplete_available(GeanyDocument *doc); void lsp_autocomplete_perform(GeanyDocument *doc); @@ -54,6 +62,10 @@ void lsp_calltips_show(GeanyDocument *doc); gboolean lsp_goto_available(GeanyDocument *doc); void lsp_goto_perform(GeanyDocument *doc, gboolean definition); +gboolean lsp_doc_symbols_available(GeanyDocument *doc); +void lsp_doc_symbols_request(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data); +GPtrArray *lsp_doc_symbols_get_cached(GeanyDocument *doc); + #endif /* GEANY_PRIVATE */ G_END_DECLS From 576e4bb153086d90f098ad9a3231c9af87a93c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 25 Oct 2023 00:06:09 +0200 Subject: [PATCH 4/9] sidebar.c and symbols.c changes related to document symbols --- src/sidebar.c | 81 ++++++++++++++++++++++++-------------------- src/symbols.c | 94 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 102 insertions(+), 73 deletions(-) diff --git a/src/sidebar.c b/src/sidebar.c index 9d004f392f..21f118b5af 100644 --- a/src/sidebar.c +++ b/src/sidebar.c @@ -34,6 +34,7 @@ #include "filetypesprivate.h" #include "geanyobject.h" #include "keyfile.h" +#include "lsp.h" #include "navqueue.h" #include "stash.h" #include "support.h" @@ -172,11 +173,37 @@ static void create_default_tag_tree(void) } -/* update = rescan the tags for doc->filename */ -void sidebar_update_tag_list(GeanyDocument *doc, gboolean update) +/* changes the tree view to the given one, trying not to do useless changes */ +static void change_tree(GeanyDocument *doc, GtkWidget *new_child) { GtkWidget *child = gtk_bin_get_child(GTK_BIN(tag_window)); + /* only change the tag tree if it's actually not the same (to avoid flickering) and if + * it's the one of the current document (to avoid problems when e.g. reloading + * configuration files */ + if (child != new_child && doc == document_get_current()) + { + if (child) + gtk_container_remove(GTK_CONTAINER(tag_window), child); + gtk_container_add(GTK_CONTAINER(tag_window), new_child); + } +} + + +static void lsp_symbol_request_cb(gpointer user_data) +{ + GeanyDocument *doc = user_data; + + if (doc == document_get_current()) + doc->has_tags = symbols_recreate_tag_list(doc, SYMBOLS_SORT_USE_PREVIOUS); + + change_tree(doc, doc->has_tags ? doc->priv->tag_tree : tv.default_tag_tree); +} + + +/* update = rescan the tags for doc->filename */ +void sidebar_update_tag_list(GeanyDocument *doc, gboolean update) +{ g_return_if_fail(doc == NULL || doc->is_valid); if (update && doc != NULL) @@ -185,56 +212,36 @@ void sidebar_update_tag_list(GeanyDocument *doc, gboolean update) if (gtk_notebook_get_current_page(GTK_NOTEBOOK(main_widgets.sidebar_notebook)) != TREEVIEW_SYMBOL) return; /* don't bother updating symbol tree if we don't see it */ - /* changes the tree view to the given one, trying not to do useless changes */ - #define CHANGE_TREE(new_child) \ - G_STMT_START { \ - /* only change the tag tree if it's actually not the same (to avoid flickering) and if \ - * it's the one of the current document (to avoid problems when e.g. reloading \ - * configuration files */ \ - if (child != new_child && doc == document_get_current()) \ - { \ - if (child) \ - gtk_container_remove(GTK_CONTAINER(tag_window), child); \ - gtk_container_add(GTK_CONTAINER(tag_window), new_child); \ - } \ - } G_STMT_END - if (tv.default_tag_tree == NULL) create_default_tag_tree(); /* show default empty tag tree if there are no tags */ if (doc == NULL || doc->file_type == NULL || ! filetype_has_tags(doc->file_type)) { - CHANGE_TREE(tv.default_tag_tree); + change_tree(doc, tv.default_tag_tree); return; } + if (doc->priv->tag_tree == NULL) + { + doc->priv->tag_store = gtk_tree_store_new( + SYMBOLS_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, TM_TYPE_TAG, G_TYPE_STRING); + doc->priv->tag_tree = gtk_tree_view_new(); + prepare_taglist(doc->priv->tag_tree, doc->priv->tag_store); + gtk_widget_show(doc->priv->tag_tree); + g_object_ref((gpointer)doc->priv->tag_tree); /* to hold it after removing */ + } + if (doc->priv->tag_tree_dirty) - { /* updating the tag list in the left tag window */ - if (doc->priv->tag_tree == NULL) - { - doc->priv->tag_store = gtk_tree_store_new( - SYMBOLS_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, TM_TYPE_TAG, G_TYPE_STRING); - doc->priv->tag_tree = gtk_tree_view_new(); - prepare_taglist(doc->priv->tag_tree, doc->priv->tag_store); - gtk_widget_show(doc->priv->tag_tree); - g_object_ref((gpointer)doc->priv->tag_tree); /* to hold it after removing */ - } + { + if (lsp_doc_symbols_available(doc)) + lsp_doc_symbols_request(doc, lsp_symbol_request_cb, doc); doc->has_tags = symbols_recreate_tag_list(doc, SYMBOLS_SORT_USE_PREVIOUS); doc->priv->tag_tree_dirty = FALSE; } - if (doc->has_tags) - { - CHANGE_TREE(doc->priv->tag_tree); - } - else - { - CHANGE_TREE(tv.default_tag_tree); - } - - #undef CHANGE_TREE + change_tree(doc, doc->has_tags ? doc->priv->tag_tree : tv.default_tag_tree); } diff --git a/src/symbols.c b/src/symbols.c index db94f3ab28..e79a18815d 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -306,22 +306,20 @@ static gint compare_symbol_lines(gconstpointer a, gconstpointer b) } -static GList *get_tag_list(GeanyDocument *doc, TMTagType tag_types) +static GList *get_tag_list(GPtrArray *tags_array, gchar *tag_filter, TMTagType tag_types) { GList *tag_names = NULL; guint i; gchar **tf_strv; - g_return_val_if_fail(doc, NULL); - - if (! doc->tm_file || ! doc->tm_file->tags_array) + if (!tags_array) return NULL; - tf_strv = g_strsplit_set(doc->priv->tag_filter, " ", -1); + tf_strv = g_strsplit_set(tag_filter, " ", -1); - for (i = 0; i < doc->tm_file->tags_array->len; ++i) + for (i = 0; i < tags_array->len; ++i) { - TMTag *tag = TM_TAG(doc->tm_file->tags_array->pdata[i]); + TMTag *tag = TM_TAG(tags_array->pdata[i]); if (tag->type & tag_types) { @@ -487,17 +485,18 @@ static void hide_empty_rows(GtkTreeStore *store) } -static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboolean include_scope, - gboolean include_line) +static gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboolean include_scope, + gboolean include_line, gboolean use_lsp) { gchar *utf8_name; const gchar *scope = tag->scope; - static GString *buffer = NULL; /* buffer will be small so we can keep it for reuse */ + GString *buffer = NULL; gboolean doc_is_utf8 = FALSE; /* encodings_convert_to_utf8_from_charset() fails with charset "None", so skip conversion * for None at this point completely */ - if (utils_str_equal(doc->encoding, "UTF-8") || + if (use_lsp || + utils_str_equal(doc->encoding, "UTF-8") || utils_str_equal(doc->encoding, "None")) doc_is_utf8 = TRUE; else /* normally the tags will always be in UTF-8 since we parse from our buffer, but a @@ -513,10 +512,7 @@ static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboole if (utf8_name == NULL) return NULL; - if (! buffer) - buffer = g_string_new(NULL); - else - g_string_truncate(buffer, 0); + buffer = g_string_new(NULL); /* check first char of scope is a wordchar */ if (include_scope && scope && @@ -535,14 +531,24 @@ static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag, gboole if (include_line) g_string_append_printf(buffer, " [%lu]", tag->line); - return buffer->str; + return g_string_free(buffer, FALSE); } // Returns NULL if the tag is not a variable or callable -static gchar *get_symbol_tooltip(GeanyDocument *doc, const TMTag *tag, gboolean include_scope) +static gchar *get_symbol_tooltip(GeanyDocument *doc, const TMTag *tag, gboolean include_scope, + gboolean use_lsp) { - gchar *utf8_name = tm_parser_format_function(tag->lang, tag->name, + gchar *utf8_name; + + if (use_lsp) + { + if (tag->arglist) + return g_strdup(tag->arglist); + return get_symbol_name(doc, tag, include_scope, FALSE, TRUE); + } + + utf8_name = tm_parser_format_function(tag->lang, tag->name, tag->arglist, tag->var_type, tag->scope); if (!utf8_name && tag->var_type && @@ -889,6 +895,7 @@ static void tags_table_value_free(gpointer data) */ static void update_tree_tags(GeanyDocument *doc, GList **tags) { + gboolean use_lsp = lsp_doc_symbols_available(doc); GtkTreeStore *store = doc->priv->tag_store; GtkTreeModel *model = GTK_TREE_MODEL(store); GHashTable *parents_table; @@ -948,18 +955,18 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags) if (!tm_tags_equal(tag, found)) { - const gchar *name; - gchar *tooltip; + gchar *name, *tooltip; /* only update fields that (can) have changed (name that holds line * number, tooltip, and the tag itself) */ - name = get_symbol_name(doc, found, parent_name == NULL, TRUE); - tooltip = get_symbol_tooltip(doc, found, FALSE); + name = get_symbol_name(doc, found, parent_name == NULL, TRUE, use_lsp); + tooltip = get_symbol_tooltip(doc, found, FALSE, use_lsp); gtk_tree_store_set(store, &iter, SYMBOLS_COLUMN_NAME, name, SYMBOLS_COLUMN_TOOLTIP, tooltip, SYMBOLS_COLUMN_TAG, found, -1); + g_free(name); g_free(tooltip); } @@ -983,16 +990,20 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags) TMTag *tag = item->data; GtkTreeIter *parent, *parent_group; - parent_group = get_tag_type_iter(tag->lang, tag->type); + parent_group = use_lsp ? NULL : get_tag_type_iter(tag->lang, tag->type); /* tv_iters[0] is reserved for the "Symbols" group */ - parent = ui_prefs.symbols_group_by_type ? parent_group : &tv_iters[0]; - if (parent_group) + parent = (ui_prefs.symbols_group_by_type && !use_lsp) ? parent_group : &tv_iters[0]; + if (use_lsp || parent_group) { gboolean expand; - const gchar *name; + gchar *name, *tooltip; const gchar *parent_name; - gchar *tooltip; - GdkPixbuf *icon = get_child_icon(store, parent_group); + GdkPixbuf *icon; + + if (use_lsp) + icon = g_object_ref(symbols_icons[lsp_get_symbols_icon_id(tag->type)].pixbuf); + else + icon = get_child_icon(store, parent_group); parent_name = get_parent_name(tag); if (parent_name) @@ -1010,14 +1021,15 @@ static void update_tree_tags(GeanyDocument *doc, GList **tags) expand = ! gtk_tree_model_iter_has_child(model, parent); /* insert the new element */ - name = get_symbol_name(doc, tag, parent_name == NULL, TRUE); - tooltip = get_symbol_tooltip(doc, tag, FALSE); + name = get_symbol_name(doc, tag, parent_name == NULL, TRUE, use_lsp); + tooltip = get_symbol_tooltip(doc, tag, FALSE, use_lsp); gtk_tree_store_insert_with_values(store, &iter, parent, 0, SYMBOLS_COLUMN_NAME, name, SYMBOLS_COLUMN_TOOLTIP, tooltip, SYMBOLS_COLUMN_ICON, icon, SYMBOLS_COLUMN_TAG, tag, -1); + g_free(name); g_free(tooltip); if (G_LIKELY(icon)) g_object_unref(icon); @@ -1137,18 +1149,28 @@ static void sort_tree(GtkTreeStore *store, gboolean sort_by_name) gboolean symbols_recreate_tag_list(GeanyDocument *doc, gint sort_mode) { - GList *tags; + gboolean use_lsp; + GList *tags = NULL; g_return_val_if_fail(DOC_VALID(doc), FALSE); - tags = get_tag_list(doc, ~(tm_tag_local_var_t | tm_tag_include_t)); + use_lsp = lsp_doc_symbols_available(doc); + + if (use_lsp) + tags = get_tag_list(lsp_doc_symbols_get_cached(doc), doc->priv->tag_filter, tm_tag_max_t); + else + { + if (doc->tm_file && doc->tm_file->tags_array) + tags = get_tag_list(doc->tm_file->tags_array, doc->priv->tag_filter, + ~(tm_tag_local_var_t | tm_tag_include_t)); + } if (tags == NULL) return FALSE; - if (doc->priv->symbols_group_by_type != ui_prefs.symbols_group_by_type) + if (doc->priv->symbols_group_by_type != (ui_prefs.symbols_group_by_type && !use_lsp)) gtk_tree_store_clear(doc->priv->tag_store); - doc->priv->symbols_group_by_type = ui_prefs.symbols_group_by_type; + doc->priv->symbols_group_by_type = ui_prefs.symbols_group_by_type && !use_lsp; /* FIXME: Not sure why we detached the model here? */ @@ -1456,10 +1478,10 @@ static void show_goto_popup(GeanyDocument *doc, GPtrArray *tags, gboolean have_b gchar *fname = short_names[i]; gchar *text; gchar *tooltip; - gchar *sym = get_symbol_tooltip(doc, tmtag, TRUE); + gchar *sym = get_symbol_tooltip(doc, tmtag, TRUE, FALSE); if (!sym) - sym = g_strdup(get_symbol_name(doc, tmtag, TRUE, FALSE)); + sym = get_symbol_name(doc, tmtag, TRUE, FALSE, FALSE); if (!sym) sym = g_strdup(""); From 0d8e52bb54de3982e7adac191a34c622dec177dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sun, 29 Oct 2023 12:21:55 +0100 Subject: [PATCH 5/9] Add API for semantic token colorization The API is based on current Geany's colorization of typenames by allowing the change of type keywords and using Scintilla's SCI_SETKEYWORDS (supported by some lexers only). The API consists of 3 functions: - symbol_highlight_available(doc) - checking whether the LSP plugin provides highlighting capabilities for the given document's filetype. - symbol_highlight_request(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data) - this function performs asynchronous request for the typenames to highlight. Once obtained from the serrver, callback is invoked. - const gchar *symbol_highlight_get_cached(GeanyDocument *doc) returns synchronously a space separated list of typenames to be colorized (in a form that can be passed to SCI_SETKEYWORDS) Note that the LSP plugin is free to chose other way of colorization than using SCI_SETKEYWORDS - in this case, the plugin should always return "" from symbol_highlight_get_cached() and perform colorization by itself. --- src/lsp.c | 30 +++++++++++++++++++++++++++++- src/lsp.h | 8 ++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/lsp.c b/src/lsp.c index 56aec6abc5..ef6bae336f 100644 --- a/src/lsp.c +++ b/src/lsp.c @@ -94,6 +94,12 @@ GPtrArray *func_return_ptrarr(GeanyDocument *doc) } +const gchar *func_return_str(GeanyDocument *doc) +{ + return NULL; +} + + void func_args_doc(GeanyDocument *doc) { } @@ -121,7 +127,11 @@ static Lsp dummy_lsp = { .doc_symbols_available = func_return_false, .doc_symbols_request = func_args_doc_symcallback_ptr, - .doc_symbols_get_cached = func_return_ptrarr + .doc_symbols_get_cached = func_return_ptrarr, + + .symbol_highlight_available = func_return_false, + .symbol_highlight_request = func_args_doc_symcallback_ptr, + .symbol_highlight_get_cached = func_return_str }; static Lsp *current_lsp = &dummy_lsp; @@ -207,3 +217,21 @@ GPtrArray *lsp_doc_symbols_get_cached(GeanyDocument *doc) { return CALL_IF_EXISTS(doc_symbols_get_cached)(doc); } + + +gboolean lsp_symbol_highlight_available(GeanyDocument *doc) +{ + return CALL_IF_EXISTS(doc_symbols_available)(doc); +} + + +void lsp_symbol_highlight_request(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data) +{ + CALL_IF_EXISTS(symbol_highlight_request)(doc, callback, user_data); +} + + +const gchar *lsp_symbol_highlight_get_cached(GeanyDocument *doc) +{ + return CALL_IF_EXISTS(symbol_highlight_get_cached)(doc); +} diff --git a/src/lsp.h b/src/lsp.h index 91f8d227d9..c2692dbbed 100644 --- a/src/lsp.h +++ b/src/lsp.h @@ -41,6 +41,10 @@ typedef struct { void (*doc_symbols_request)(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data); GPtrArray *(*doc_symbols_get_cached)(GeanyDocument *doc); + gboolean (*symbol_highlight_available)(GeanyDocument *doc); + void (*symbol_highlight_request)(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data); + const gchar *(*symbol_highlight_get_cached)(GeanyDocument *doc); + gchar _dummy[1024]; } Lsp; @@ -66,6 +70,10 @@ gboolean lsp_doc_symbols_available(GeanyDocument *doc); void lsp_doc_symbols_request(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data); GPtrArray *lsp_doc_symbols_get_cached(GeanyDocument *doc); +gboolean lsp_symbol_highlight_available(GeanyDocument *doc); +void lsp_symbol_highlight_request(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data); +const gchar *lsp_symbol_highlight_get_cached(GeanyDocument *doc); + #endif /* GEANY_PRIVATE */ G_END_DECLS From 7ea99e1ff84d7f2a733eef48606a8992ab666707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sun, 29 Oct 2023 13:49:02 +0100 Subject: [PATCH 6/9] Support highlighting using LSP in document.c --- src/document.c | 72 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/src/document.c b/src/document.c index 4cf74105b1..81de8aab16 100644 --- a/src/document.c +++ b/src/document.c @@ -40,6 +40,7 @@ #include "geanyobject.h" #include "geanywraplabel.h" #include "highlighting.h" +#include "lsp.h" #include "main.h" #include "msgwindow.h" #include "navqueue.h" @@ -96,6 +97,15 @@ typedef struct * it contains the new value */ } undo_action; + +/* Data used for LSP highlighting request callback */ +typedef struct +{ + GeanyDocument *doc; + gint keyword_idx; +} LspHighlightData; + + /* Custom document info bar response IDs */ enum { @@ -2713,6 +2723,40 @@ void document_update_tags(GeanyDocument *doc) } +static void document_highlight_keywords(GeanyDocument *doc, const gchar *keywords, gint keyword_idx) +{ + guint hash = g_str_hash(keywords); + + if (hash != doc->priv->keyword_hash) + { + sci_set_keywords(doc->editor->sci, keyword_idx, keywords); + queue_colourise(doc); /* force re-highlighting the entire document */ + doc->priv->keyword_hash = hash; + } +} + + +static void lsp_highlight_cb(gpointer user_data) +{ + LspHighlightData *data = user_data; + guint i; + + foreach_document(i) + { + /* the document could have been closed before the callback fires, check + * if it's still valid */ + if (documents[i] == data->doc) + { + const gchar *keywords = lsp_symbol_highlight_get_cached(data->doc); + document_highlight_keywords(data->doc, keywords ? keywords : "", data->keyword_idx); + break; + } + } + + g_free(data); +} + + /* Re-highlights type keywords without re-parsing the whole document. */ void document_highlight_tags(GeanyDocument *doc) { @@ -2748,21 +2792,27 @@ void document_highlight_tags(GeanyDocument *doc) if (!app->tm_workspace->tags_array) return; - /* get any type keywords and tell scintilla about them - * this will cause the type keywords to be colourized in scintilla */ - keywords_str = symbols_find_typenames_as_string(doc->file_type->lang, FALSE); - if (keywords_str) + if (lsp_symbol_highlight_available(doc)) { - gchar *keywords = g_string_free(keywords_str, FALSE); - guint hash = g_str_hash(keywords); + LspHighlightData *data = g_new0(LspHighlightData, 1); + const gchar *keywords = lsp_symbol_highlight_get_cached(doc); - if (hash != doc->priv->keyword_hash) + data->doc = doc; + data->keyword_idx = keyword_idx; + document_highlight_keywords(doc, keywords ? keywords : "", keyword_idx); + lsp_symbol_highlight_request(doc, lsp_highlight_cb, data); + } + else + { + /* get any type keywords and tell scintilla about them + * this will cause the type keywords to be colourized in scintilla */ + keywords_str = symbols_find_typenames_as_string(doc->file_type->lang, FALSE); + if (keywords_str) { - sci_set_keywords(doc->editor->sci, keyword_idx, keywords); - queue_colourise(doc); /* force re-highlighting the entire document */ - doc->priv->keyword_hash = hash; + gchar *keywords = g_string_free(keywords_str, FALSE); + document_highlight_keywords(doc, keywords, keyword_idx); + g_free(keywords); } - g_free(keywords); } } From bccb605030dda0bafedb0b91e31e220c94285c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Mon, 13 Nov 2023 19:19:10 +0100 Subject: [PATCH 7/9] Modify the LSP interface to return position for the symbol goto The position may not be the current caret position when invoking the goto from the context menu - in this case we want the right-click position and the plugin has no other way of getting it. --- src/editor.c | 2 +- src/keybindings.c | 2 +- src/lsp.c | 8 ++++---- src/lsp.h | 4 ++-- src/symbols.c | 4 ++-- src/symbols.h | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/editor.c b/src/editor.c index 0fa137a11c..062f5e517d 100644 --- a/src/editor.c +++ b/src/editor.c @@ -320,7 +320,7 @@ static gboolean on_editor_button_press_event(GtkWidget *widget, GdkEventButton * editor_find_current_word(editor, editor_info.click_pos, current_word, sizeof current_word, NULL); if (*current_word) - return symbols_goto_tag(current_word, TRUE); + return symbols_goto_tag(current_word, editor_info.click_pos, TRUE); else keybindings_send_command(GEANY_KEY_GROUP_GOTO, GEANY_KEYS_GOTO_MATCHINGBRACE); return TRUE; diff --git a/src/keybindings.c b/src/keybindings.c index d228c98118..2e9a5fe29c 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -1975,7 +1975,7 @@ static void goto_tag(GeanyDocument *doc, gboolean definition) gchar *text = get_current_word_or_sel(doc, FALSE); if (text) - symbols_goto_tag(text, definition); + symbols_goto_tag(text, sci_get_current_position(doc->editor->sci), definition); else utils_beep(); diff --git a/src/lsp.c b/src/lsp.c index ef6bae336f..5b7db3aef5 100644 --- a/src/lsp.c +++ b/src/lsp.c @@ -105,7 +105,7 @@ void func_args_doc(GeanyDocument *doc) } -void func_args_doc_bool(GeanyDocument *doc, gboolean dummy) +void func_args_doc_int_bool(GeanyDocument *doc, gint dummy1, gboolean dummy2) { } @@ -123,7 +123,7 @@ static Lsp dummy_lsp = { .calltips_show = func_args_doc, .goto_available = func_return_false, - .goto_perform = func_args_doc_bool, + .goto_perform = func_args_doc_int_bool, .doc_symbols_available = func_return_false, .doc_symbols_request = func_args_doc_symcallback_ptr, @@ -195,9 +195,9 @@ gboolean lsp_goto_available(GeanyDocument *doc) } -void lsp_goto_perform(GeanyDocument *doc, gboolean definition) +void lsp_goto_perform(GeanyDocument *doc, gint pos, gboolean definition) { - CALL_IF_EXISTS(goto_perform)(doc, definition); + CALL_IF_EXISTS(goto_perform)(doc, pos, definition); } diff --git a/src/lsp.h b/src/lsp.h index c2692dbbed..981f20dac3 100644 --- a/src/lsp.h +++ b/src/lsp.h @@ -35,7 +35,7 @@ typedef struct { void (*calltips_show)(GeanyDocument *doc); gboolean (*goto_available)(GeanyDocument *doc); - void (*goto_perform)(GeanyDocument *doc, gboolean definition); + void (*goto_perform)(GeanyDocument *doc, gint pos, gboolean definition); gboolean (*doc_symbols_available)(GeanyDocument *doc); void (*doc_symbols_request)(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data); @@ -64,7 +64,7 @@ gboolean lsp_calltips_available(GeanyDocument *doc); void lsp_calltips_show(GeanyDocument *doc); gboolean lsp_goto_available(GeanyDocument *doc); -void lsp_goto_perform(GeanyDocument *doc, gboolean definition); +void lsp_goto_perform(GeanyDocument *doc, gint pos, gboolean definition); gboolean lsp_doc_symbols_available(GeanyDocument *doc); void lsp_doc_symbols_request(GeanyDocument *doc, LspSymbolRequestCallback callback, gpointer user_data); diff --git a/src/symbols.c b/src/symbols.c index e79a18815d..4c73cd0103 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -1709,13 +1709,13 @@ static gboolean goto_tag(const gchar *name, gboolean definition) } -gboolean symbols_goto_tag(const gchar *name, gboolean definition) +gboolean symbols_goto_tag(const gchar *name, gint pos, gboolean definition) { GeanyDocument *doc = document_get_current(); if (lsp_goto_available(doc)) { - lsp_goto_perform(doc, definition); + lsp_goto_perform(doc, pos, definition); return TRUE; } diff --git a/src/symbols.h b/src/symbols.h index d455d5c142..25a9d91db6 100644 --- a/src/symbols.h +++ b/src/symbols.h @@ -57,7 +57,7 @@ gint symbols_generate_global_tags(gint argc, gchar **argv, gboolean want_preproc void symbols_show_load_tags_dialog(void); -gboolean symbols_goto_tag(const gchar *name, gboolean definition); +gboolean symbols_goto_tag(const gchar *name, gint pos, gboolean definition); gint symbols_get_current_function(GeanyDocument *doc, const gchar **tagname); From 952c4bf5e2522aa73d810b3ae9523bf36fc8dcdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sun, 19 Nov 2023 23:33:34 +0100 Subject: [PATCH 8/9] Don't call sidebar_update_tag_list() and document_highlight_tags() on startup These methods get called in on_notebook1_switch_page_after() for the last activated tab and calling these before unnecessarily launches LSP servers for all the open documents. --- src/document.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/document.c b/src/document.c index 81de8aab16..16cc607339 100644 --- a/src/document.c +++ b/src/document.c @@ -2718,6 +2718,9 @@ void document_update_tags(GeanyDocument *doc) buffer_ptr = (guchar *) SSM(doc->editor->sci, SCI_GETCHARACTERPOINTER, 0, 0); tm_workspace_update_source_file_buffer(doc->tm_file, buffer_ptr, len); + if (G_UNLIKELY(main_status.opening_session_files || main_status.closing_all)) + return; + sidebar_update_tag_list(doc, TRUE); document_highlight_tags(doc); } From 28cd7c47860a04ce96a9c35c76e9e089213f4a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Thu, 30 Nov 2023 22:59:49 +0100 Subject: [PATCH 9/9] Revert "Don't call sidebar_update_tag_list() and document_highlight_tags() on startup" This detection based on the currently tab switch is imperfect as it doesn't happen if there's a single document for instance so this approach doesn't work and may lead to missed highlight requests. Instead, introduce a new signal detecting session file opening (in a separate PR) and use that one. This reverts commit 952c4bf5e2522aa73d810b3ae9523bf36fc8dcdf. --- src/document.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/document.c b/src/document.c index 16cc607339..81de8aab16 100644 --- a/src/document.c +++ b/src/document.c @@ -2718,9 +2718,6 @@ void document_update_tags(GeanyDocument *doc) buffer_ptr = (guchar *) SSM(doc->editor->sci, SCI_GETCHARACTERPOINTER, 0, 0); tm_workspace_update_source_file_buffer(doc->tm_file, buffer_ptr, len); - if (G_UNLIKELY(main_status.opening_session_files || main_status.closing_all)) - return; - sidebar_update_tag_list(doc, TRUE); document_highlight_tags(doc); }