Skip to content

Commit 60b4c81

Browse files
committed
projectorganizer: Add popup panel for navigation
This feature allows quick navigation do document/workspace symbols based on their names, open files, and line numbers. The panel's code is mostly stolen from the LSP plugin which in turn stole it from the Colomban Wendling's Commander plugin.
1 parent 644550b commit 60b4c81

8 files changed

+977
-6
lines changed

po/POTFILES.in

+1
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ pretty-printer/src/PluginEntry.c
205205
pretty-printer/src/ConfigUI.c
206206

207207
# ProjectOrganizer
208+
projectorganizer/src/prjorg-goto-anywhere.c
208209
projectorganizer/src/prjorg-main.c
209210
projectorganizer/src/prjorg-menu.c
210211
projectorganizer/src/prjorg-project.c

projectorganizer/README

+6
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ Project Organizer adds some extra entries under the Project menu:
156156
the properties, it opens a project file with the same base name (without extension)
157157
matching header patterns (and vice versa). If the files are already open, it
158158
just switches the document tabs. Nothing happens if no matching file is found.
159+
* Go to anywhere, Go to document symbol, Go to workspace symbol, Go to line -
160+
these items allow to perform jump to the entered destination. The popup window is
161+
identical for all of these actions, the only difference is the pre-filled prefix
162+
that determines the type of the go to. No prefix performs the search in open files,
163+
@ performs the search in current document's symbols, # performs the search in
164+
all workspace symbols, and : performs navigation to the specified line.
159165

160166
Each of these entries can be assigned a key binding under Edit->Preferences->Keybindings.
161167

projectorganizer/src/Makefile.am

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ projectorganizer_la_SOURCES = \
1212
prjorg-utils.h \
1313
prjorg-utils.c \
1414
prjorg-menu.h \
15-
prjorg-menu.c
15+
prjorg-menu.c \
16+
prjorg-goto-panel.h \
17+
prjorg-goto-panel.c \
18+
prjorg-goto-anywhere.h \
19+
prjorg-goto-anywhere.c
1620

1721
projectorganizer_la_CPPFLAGS = $(AM_CPPFLAGS) \
1822
-DG_LOG_DOMAIN=\"ProjectOrganizer\"
+332
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
/*
2+
* Copyright 2023 Jiri Techet <[email protected]>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
19+
#ifdef HAVE_CONFIG_H
20+
# include "config.h"
21+
#endif
22+
23+
#include "prjorg-goto-anywhere.h"
24+
#include "prjorg-goto-panel.h"
25+
26+
#include <gtk/gtk.h>
27+
#include <geanyplugin.h>
28+
29+
30+
#define SSM(s, m, w, l) scintilla_send_message((s), (m), (w), (l))
31+
32+
33+
typedef struct
34+
{
35+
GeanyDocument *doc;
36+
gchar *query;
37+
} DocQueryData;
38+
39+
40+
extern GeanyData *geany_data;
41+
42+
43+
static void goto_line(GeanyDocument *doc, const gchar *line_str)
44+
{
45+
GPtrArray *arr = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free);
46+
gint lineno = atoi(line_str);
47+
gint linenum = sci_get_line_count(doc->editor->sci);
48+
guint i;
49+
50+
for (i = 0; i < 4; i++)
51+
{
52+
PrjorgGotoSymbol *sym = g_new0(PrjorgGotoSymbol, 1);
53+
54+
sym->file_name = utils_get_utf8_from_locale(doc->real_path);
55+
sym->icon = _ICON_OTHER;
56+
57+
switch (i)
58+
{
59+
case 0:
60+
sym->name = g_strdup(_("line typed above"));
61+
if (lineno == 0)
62+
sym->line = sci_get_current_line(doc->editor->sci) + 1;
63+
else if (lineno > linenum)
64+
sym->line = linenum;
65+
else
66+
sym->line = lineno;
67+
break;
68+
69+
case 1:
70+
sym->name = g_strdup(_("start"));
71+
sym->line = 1;
72+
break;
73+
74+
case 2:
75+
sym->name = g_strdup(_("middle"));
76+
sym->line = linenum / 2;
77+
break;
78+
79+
case 3:
80+
sym->name = g_strdup(_("end"));
81+
sym->line = linenum;
82+
break;
83+
}
84+
85+
g_ptr_array_add(arr, sym);
86+
}
87+
88+
prjorg_goto_panel_fill(arr);
89+
90+
g_ptr_array_free(arr, TRUE);
91+
}
92+
93+
94+
static void goto_file(const gchar *file_str)
95+
{
96+
GPtrArray *arr = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free);
97+
GPtrArray *filtered;
98+
guint i;
99+
100+
foreach_document(i)
101+
{
102+
GeanyDocument *doc = documents[i];
103+
PrjorgGotoSymbol *sym;
104+
105+
if (!doc->real_path)
106+
continue;
107+
108+
sym = g_new0(PrjorgGotoSymbol, 1);
109+
sym->name = g_path_get_basename(doc->real_path);
110+
sym->file_name = utils_get_utf8_from_locale(doc->real_path);
111+
sym->icon = _ICON_OTHER;
112+
g_ptr_array_add(arr, sym);
113+
}
114+
115+
filtered = prjorg_goto_panel_filter(arr, file_str);
116+
prjorg_goto_panel_fill(filtered);
117+
118+
g_ptr_array_free(filtered, TRUE);
119+
g_ptr_array_free(arr, TRUE);
120+
}
121+
122+
123+
/* symplified hard-coded icons because we don't have access to Geany icon mappings */
124+
static int get_icon(TMTagType type)
125+
{
126+
switch (type)
127+
{
128+
case tm_tag_class_t:
129+
return _ICON_CLASS;
130+
case tm_tag_macro_t:
131+
case tm_tag_macro_with_arg_t:
132+
case tm_tag_undef_t:
133+
return _ICON_MACRO;
134+
case tm_tag_enum_t:
135+
case tm_tag_struct_t:
136+
case tm_tag_typedef_t:
137+
case tm_tag_union_t:
138+
return _ICON_STRUCT;
139+
case tm_tag_enumerator_t:
140+
case tm_tag_field_t:
141+
case tm_tag_member_t:
142+
return _ICON_MEMBER;
143+
case tm_tag_method_t:
144+
case tm_tag_function_t:
145+
case tm_tag_prototype_t:
146+
return _ICON_METHOD;
147+
case tm_tag_interface_t:
148+
case tm_tag_namespace_t:
149+
case tm_tag_package_t:
150+
return _ICON_NAMESPACE;
151+
case tm_tag_variable_t:
152+
case tm_tag_externvar_t:
153+
case tm_tag_local_var_t:
154+
case tm_tag_include_t:
155+
return _ICON_VAR;
156+
case tm_tag_other_t:
157+
return _ICON_OTHER;
158+
default:
159+
return _ICON_NONE;
160+
}
161+
}
162+
163+
164+
/* stolen from Geany */
165+
static gboolean langs_compatible(TMParserType lang, TMParserType other)
166+
{
167+
if (lang == other)
168+
return TRUE;
169+
/* Accept CPP tags for C lang and vice versa - we don't have acces to
170+
* Geany's TM_PARSER_ constants so let's hard-code 0 and 1 here (not too
171+
* terrible things will happen if Geany changes these to something else) */
172+
else if (lang == 0 && other == 1)
173+
return TRUE;
174+
else if (lang == 1 && other == 0)
175+
return TRUE;
176+
177+
return FALSE;
178+
}
179+
180+
181+
static void goto_tm_symbol(const gchar *query, GPtrArray *tags, TMParserType lang)
182+
{
183+
GPtrArray *converted = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free);
184+
GPtrArray *filtered;
185+
TMTag *tag;
186+
guint i;
187+
188+
if (tags)
189+
{
190+
foreach_ptr_array(tag, i, tags)
191+
{
192+
if (tag->file && langs_compatible(tag->lang, lang) &&
193+
!(tag->type & (tm_tag_include_t | tm_tag_local_var_t)))
194+
{
195+
PrjorgGotoSymbol *sym = g_new0(PrjorgGotoSymbol, 1);
196+
sym->name = g_strdup(tag->name);
197+
sym->file_name = utils_get_utf8_from_locale(tag->file->file_name);
198+
sym->line = tag->line;
199+
sym->icon = get_icon(tag->type);
200+
201+
g_ptr_array_add(converted, sym);
202+
}
203+
}
204+
}
205+
206+
filtered = prjorg_goto_panel_filter(converted, query);
207+
prjorg_goto_panel_fill(filtered);
208+
209+
g_ptr_array_free(filtered, TRUE);
210+
g_ptr_array_free(converted, TRUE);
211+
}
212+
213+
214+
static void perform_lookup(const gchar *query)
215+
{
216+
GeanyDocument *doc = document_get_current();
217+
const gchar *query_str = query ? query : "";
218+
219+
if (g_str_has_prefix(query_str, "#"))
220+
{
221+
// TODO: possibly improve performance by binary searching the start and the end point
222+
goto_tm_symbol(query_str+1, geany_data->app->tm_workspace->tags_array, doc->file_type->lang);
223+
}
224+
else if (g_str_has_prefix(query_str, "@"))
225+
{
226+
GPtrArray *tags = doc->tm_file ? doc->tm_file->tags_array : g_ptr_array_new();
227+
goto_tm_symbol(query_str+1, tags, doc->file_type->lang);
228+
if (!doc->tm_file)
229+
g_ptr_array_free(tags, TRUE);
230+
}
231+
else if (g_str_has_prefix(query_str, ":"))
232+
goto_line(doc, query_str+1);
233+
else
234+
goto_file(query_str);
235+
}
236+
237+
238+
static gchar *get_current_iden(GeanyDocument *doc, gint current_pos)
239+
{
240+
//TODO: use configured wordchars (also change in Geany)
241+
const gchar *wordchars = GEANY_WORDCHARS;
242+
GeanyFiletypeID ft = doc->file_type->id;
243+
ScintillaObject *sci = doc->editor->sci;
244+
gint start_pos, end_pos, pos;
245+
246+
if (ft == GEANY_FILETYPES_LATEX)
247+
wordchars = GEANY_WORDCHARS"\\"; /* add \ to word chars if we are in a LaTeX file */
248+
else if (ft == GEANY_FILETYPES_CSS)
249+
wordchars = GEANY_WORDCHARS"-"; /* add - because they are part of property names */
250+
251+
pos = current_pos;
252+
while (TRUE)
253+
{
254+
gint new_pos = SSM(sci, SCI_POSITIONBEFORE, pos, 0);
255+
if (new_pos == pos)
256+
break;
257+
if (pos - new_pos == 1)
258+
{
259+
gchar c = sci_get_char_at(sci, new_pos);
260+
if (!strchr(wordchars, c))
261+
break;
262+
}
263+
pos = new_pos;
264+
}
265+
start_pos = pos;
266+
267+
pos = current_pos;
268+
while (TRUE)
269+
{
270+
gint new_pos = SSM(sci, SCI_POSITIONAFTER, pos, 0);
271+
if (new_pos == pos)
272+
break;
273+
if (new_pos - pos == 1)
274+
{
275+
gchar c = sci_get_char_at(sci, pos);
276+
if (!strchr(wordchars, c))
277+
break;
278+
}
279+
pos = new_pos;
280+
}
281+
end_pos = pos;
282+
283+
if (start_pos == end_pos)
284+
return NULL;
285+
286+
return sci_get_contents_range(sci, start_pos, end_pos);
287+
}
288+
289+
290+
static void goto_panel_query(const gchar *query_type, gboolean prefill)
291+
{
292+
GeanyDocument *doc = document_get_current();
293+
gint pos = sci_get_current_position(doc->editor->sci);
294+
gchar *query = NULL;
295+
296+
if (!doc)
297+
return;
298+
299+
if (prefill)
300+
query = get_current_iden(doc, pos);
301+
if (!query)
302+
query = g_strdup("");
303+
SETPTR(query, g_strconcat(query_type, query, NULL));
304+
305+
prjorg_goto_panel_show(query, perform_lookup);
306+
307+
g_free(query);
308+
}
309+
310+
311+
void prjorg_goto_anywhere_for_workspace(void)
312+
{
313+
goto_panel_query("#", TRUE);
314+
}
315+
316+
317+
void prjorg_goto_anywhere_for_doc(void)
318+
{
319+
goto_panel_query("@", TRUE);
320+
}
321+
322+
323+
void prjorg_goto_anywhere_for_line(void)
324+
{
325+
goto_panel_query(":", FALSE);
326+
}
327+
328+
329+
void prjorg_goto_anywhere_for_file(void)
330+
{
331+
goto_panel_query("", FALSE);
332+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2023 Jiri Techet <[email protected]>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
19+
#ifndef PRJORG_GOTO_ANYWHERE_H
20+
#define PRJORG_GOTO_ANYWHERE_H 1
21+
22+
23+
void prjorg_goto_anywhere_for_workspace(void);
24+
void prjorg_goto_anywhere_for_doc(void);
25+
void prjorg_goto_anywhere_for_line(void);
26+
void prjorg_goto_anywhere_for_file(void);
27+
28+
#endif /* PRJORG_GOTO_ANYWHERE_H */

0 commit comments

Comments
 (0)