Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

projectorganizer: Add popup panel for navigation #1341

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions po/POTFILES.in
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ pretty-printer/src/PluginEntry.c
pretty-printer/src/ConfigUI.c

# ProjectOrganizer
projectorganizer/src/prjorg-goto-anywhere.c
projectorganizer/src/prjorg-main.c
projectorganizer/src/prjorg-menu.c
projectorganizer/src/prjorg-project.c
Expand Down
6 changes: 6 additions & 0 deletions projectorganizer/README
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ Project Organizer adds some extra entries under the Project menu:
the properties, it opens a project file with the same base name (without extension)
matching header patterns (and vice versa). If the files are already open, it
just switches the document tabs. Nothing happens if no matching file is found.
* Go to anywhere, Go to document symbol, Go to workspace symbol, Go to line -
these items allow to perform jump to the entered destination. The popup window is
identical for all of these actions, the only difference is the pre-filled prefix
that determines the type of the go to. No prefix performs the search in open files,
@ performs the search in current document's symbols, # performs the search in
all workspace symbols, and : performs navigation to the specified line.

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

Expand Down
6 changes: 5 additions & 1 deletion projectorganizer/src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ projectorganizer_la_SOURCES = \
prjorg-utils.h \
prjorg-utils.c \
prjorg-menu.h \
prjorg-menu.c
prjorg-menu.c \
prjorg-goto-panel.h \
prjorg-goto-panel.c \
prjorg-goto-anywhere.h \
prjorg-goto-anywhere.c

projectorganizer_la_CPPFLAGS = $(AM_CPPFLAGS) \
-DG_LOG_DOMAIN=\"ProjectOrganizer\"
Expand Down
332 changes: 332 additions & 0 deletions projectorganizer/src/prjorg-goto-anywhere.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,332 @@
/*
* Copyright 2023 Jiri Techet <[email protected]>
*
* 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.
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "prjorg-goto-anywhere.h"
#include "prjorg-goto-panel.h"

#include <gtk/gtk.h>
#include <geanyplugin.h>


#define SSM(s, m, w, l) scintilla_send_message((s), (m), (w), (l))


typedef struct
{
GeanyDocument *doc;
gchar *query;
} DocQueryData;


extern GeanyData *geany_data;


static void goto_line(GeanyDocument *doc, const gchar *line_str)
{
GPtrArray *arr = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free);
gint lineno = atoi(line_str);
gint linenum = sci_get_line_count(doc->editor->sci);
guint i;

for (i = 0; i < 4; i++)
{
PrjorgGotoSymbol *sym = g_new0(PrjorgGotoSymbol, 1);

sym->file_name = utils_get_utf8_from_locale(doc->real_path);
sym->icon = _ICON_OTHER;

switch (i)
{
case 0:
sym->name = g_strdup(_("line typed above"));
techee marked this conversation as resolved.
Show resolved Hide resolved
if (lineno == 0)
sym->line = sci_get_current_line(doc->editor->sci) + 1;
else if (lineno > linenum)
sym->line = linenum;
else
sym->line = lineno;
break;

case 1:
sym->name = g_strdup(_("start"));
sym->line = 1;
break;

case 2:
sym->name = g_strdup(_("middle"));
sym->line = linenum / 2;
break;

case 3:
sym->name = g_strdup(_("end"));
sym->line = linenum;
break;
}

g_ptr_array_add(arr, sym);
}

prjorg_goto_panel_fill(arr);

g_ptr_array_free(arr, TRUE);
}


static void goto_file(const gchar *file_str)
{
GPtrArray *arr = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free);
GPtrArray *filtered;
guint i;

foreach_document(i)
{
GeanyDocument *doc = documents[i];
PrjorgGotoSymbol *sym;

if (!doc->real_path)
continue;

sym = g_new0(PrjorgGotoSymbol, 1);
sym->name = g_path_get_basename(doc->real_path);
sym->file_name = utils_get_utf8_from_locale(doc->real_path);
sym->icon = _ICON_OTHER;
g_ptr_array_add(arr, sym);
}

filtered = prjorg_goto_panel_filter(arr, file_str);
prjorg_goto_panel_fill(filtered);

g_ptr_array_free(filtered, TRUE);
g_ptr_array_free(arr, TRUE);
}


/* symplified hard-coded icons because we don't have access to Geany icon mappings */
static int get_icon(TMTagType type)
{
switch (type)
{
case tm_tag_class_t:
return _ICON_CLASS;
case tm_tag_macro_t:
case tm_tag_macro_with_arg_t:
case tm_tag_undef_t:
return _ICON_MACRO;
case tm_tag_enum_t:
case tm_tag_struct_t:
case tm_tag_typedef_t:
case tm_tag_union_t:
return _ICON_STRUCT;
case tm_tag_enumerator_t:
case tm_tag_field_t:
case tm_tag_member_t:
return _ICON_MEMBER;
case tm_tag_method_t:
case tm_tag_function_t:
case tm_tag_prototype_t:
return _ICON_METHOD;
case tm_tag_interface_t:
case tm_tag_namespace_t:
case tm_tag_package_t:
return _ICON_NAMESPACE;
case tm_tag_variable_t:
case tm_tag_externvar_t:
case tm_tag_local_var_t:
case tm_tag_include_t:
return _ICON_VAR;
case tm_tag_other_t:
return _ICON_OTHER;
default:
return _ICON_NONE;
}
}


/* stolen from Geany */
static gboolean langs_compatible(TMParserType lang, TMParserType other)
{
if (lang == other)
return TRUE;
/* Accept CPP tags for C lang and vice versa - we don't have acces to
* Geany's TM_PARSER_ constants so let's hard-code 0 and 1 here (not too
* terrible things will happen if Geany changes these to something else) */
else if (lang == 0 && other == 1)
return TRUE;
else if (lang == 1 && other == 0)
return TRUE;

return FALSE;
}


static void goto_tm_symbol(const gchar *query, GPtrArray *tags, TMParserType lang)
{
GPtrArray *converted = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free);
GPtrArray *filtered;
TMTag *tag;
guint i;

if (tags)
{
foreach_ptr_array(tag, i, tags)
{
if (tag->file && langs_compatible(tag->lang, lang) &&
!(tag->type & (tm_tag_include_t | tm_tag_local_var_t)))
{
PrjorgGotoSymbol *sym = g_new0(PrjorgGotoSymbol, 1);
sym->name = g_strdup(tag->name);
sym->file_name = utils_get_utf8_from_locale(tag->file->file_name);
sym->line = tag->line;
sym->icon = get_icon(tag->type);

g_ptr_array_add(converted, sym);
}
}
}

filtered = prjorg_goto_panel_filter(converted, query);
prjorg_goto_panel_fill(filtered);

g_ptr_array_free(filtered, TRUE);
g_ptr_array_free(converted, TRUE);
}


static void perform_lookup(const gchar *query)
{
GeanyDocument *doc = document_get_current();
const gchar *query_str = query ? query : "";

if (g_str_has_prefix(query_str, "#"))
{
// TODO: possibly improve performance by binary searching the start and the end point
goto_tm_symbol(query_str+1, geany_data->app->tm_workspace->tags_array, doc->file_type->lang);
}
else if (g_str_has_prefix(query_str, "@"))
{
GPtrArray *tags = doc->tm_file ? doc->tm_file->tags_array : g_ptr_array_new();
goto_tm_symbol(query_str+1, tags, doc->file_type->lang);
if (!doc->tm_file)
g_ptr_array_free(tags, TRUE);
}
else if (g_str_has_prefix(query_str, ":"))
goto_line(doc, query_str+1);
else
goto_file(query_str);
}


static gchar *get_current_iden(GeanyDocument *doc, gint current_pos)
{
//TODO: use configured wordchars (also change in Geany)
const gchar *wordchars = GEANY_WORDCHARS;
GeanyFiletypeID ft = doc->file_type->id;
ScintillaObject *sci = doc->editor->sci;
gint start_pos, end_pos, pos;

if (ft == GEANY_FILETYPES_LATEX)
wordchars = GEANY_WORDCHARS"\\"; /* add \ to word chars if we are in a LaTeX file */
else if (ft == GEANY_FILETYPES_CSS)
wordchars = GEANY_WORDCHARS"-"; /* add - because they are part of property names */

pos = current_pos;
while (TRUE)
{
gint new_pos = SSM(sci, SCI_POSITIONBEFORE, pos, 0);
if (new_pos == pos)
break;
if (pos - new_pos == 1)
{
gchar c = sci_get_char_at(sci, new_pos);
if (!strchr(wordchars, c))
break;
}
pos = new_pos;
}
start_pos = pos;

pos = current_pos;
while (TRUE)
{
gint new_pos = SSM(sci, SCI_POSITIONAFTER, pos, 0);
if (new_pos == pos)
break;
if (new_pos - pos == 1)
{
gchar c = sci_get_char_at(sci, pos);
if (!strchr(wordchars, c))
break;
}
pos = new_pos;
}
end_pos = pos;

if (start_pos == end_pos)
return NULL;

return sci_get_contents_range(sci, start_pos, end_pos);
}


static void goto_panel_query(const gchar *query_type, gboolean prefill)
{
GeanyDocument *doc = document_get_current();
gint pos = sci_get_current_position(doc->editor->sci);
techee marked this conversation as resolved.
Show resolved Hide resolved
gchar *query = NULL;

if (!doc)
return;

if (prefill)
query = get_current_iden(doc, pos);
if (!query)
query = g_strdup("");
SETPTR(query, g_strconcat(query_type, query, NULL));

prjorg_goto_panel_show(query, perform_lookup);

g_free(query);
}


void prjorg_goto_anywhere_for_workspace(void)
{
goto_panel_query("#", TRUE);
}


void prjorg_goto_anywhere_for_doc(void)
{
goto_panel_query("@", TRUE);
}


void prjorg_goto_anywhere_for_line(void)
{
goto_panel_query(":", FALSE);
}


void prjorg_goto_anywhere_for_file(void)
{
goto_panel_query("", FALSE);
}
28 changes: 28 additions & 0 deletions projectorganizer/src/prjorg-goto-anywhere.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2023 Jiri Techet <[email protected]>
*
* 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 PRJORG_GOTO_ANYWHERE_H
#define PRJORG_GOTO_ANYWHERE_H 1


void prjorg_goto_anywhere_for_workspace(void);
void prjorg_goto_anywhere_for_doc(void);
void prjorg_goto_anywhere_for_line(void);
void prjorg_goto_anywhere_for_file(void);

#endif /* PRJORG_GOTO_ANYWHERE_H */
Loading