-
Notifications
You must be signed in to change notification settings - Fork 270
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
8 changed files
with
977 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")); | ||
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); | ||
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ |
Oops, something went wrong.