From ffccd5b2b05243e7976db80f90f453dccfae9886 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 22:22:03 +0200 Subject: Adding upstream version 3:4.8.30. Signed-off-by: Daniel Baumann --- src/editor/etags.c | 468 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 src/editor/etags.c (limited to 'src/editor/etags.c') diff --git a/src/editor/etags.c b/src/editor/etags.c new file mode 100644 index 0000000..7b570d6 --- /dev/null +++ b/src/editor/etags.c @@ -0,0 +1,468 @@ +/* + Editor C-code navigation via tags. + make TAGS file via command: + $ find . -type f -name "*.[ch]" | etags -l c --declarations - + + or, if etags utility not installed: + $ find . -type f -name "*.[ch]" | ctags --c-kinds=+p --fields=+iaS --extra=+q -e -L- + + Copyright (C) 2009-2023 + Free Software Foundation, Inc. + + Written by: + Ilia Maslakov , 2009 + Slava Zanko , 2009 + Andrew Borodin , 2010-2022 + + This file is part of the Midnight Commander. + + The Midnight Commander 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 3 of the License, + or (at your option) any later version. + + The Midnight Commander 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, see . + */ + +#include + +#include +#include +#include +#include + +#include "lib/global.h" +#include "lib/fileloc.h" /* TAGS_NAME */ +#include "lib/tty/tty.h" /* LINES, COLS */ +#include "lib/strutil.h" +#include "lib/util.h" + +#include "editwidget.h" + +#include "etags.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static int def_max_width; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void +etags_hash_free (gpointer data) +{ + etags_hash_t *hash = (etags_hash_t *) data; + + g_free (hash->filename); + g_free (hash->fullpath); + g_free (hash->short_define); + g_free (hash); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +parse_define (const char *buf, char **long_name, char **short_name, long *line) +{ + /* *INDENT-OFF* */ + enum + { + in_longname, + in_shortname, + in_shortname_first_char, + in_line, + finish + } def_state = in_longname; + /* *INDENT-ON* */ + + GString *longdef = NULL; + GString *shortdef = NULL; + GString *linedef = NULL; + + char c = *buf; + + while (!(c == '\0' || c == '\n')) + { + switch (def_state) + { + case in_longname: + if (c == 0x01) + def_state = in_line; + else if (c == 0x7F) + def_state = in_shortname; + else + { + if (longdef == NULL) + longdef = g_string_sized_new (32); + + g_string_append_c (longdef, c); + } + break; + + case in_shortname_first_char: + if (isdigit (c)) + { + if (shortdef == NULL) + shortdef = g_string_sized_new (32); + else + g_string_set_size (shortdef, 0); + + buf--; + def_state = in_line; + } + else if (c == 0x01) + def_state = in_line; + else + { + if (shortdef == NULL) + shortdef = g_string_sized_new (32); + + g_string_append_c (shortdef, c); + def_state = in_shortname; + } + break; + + case in_shortname: + if (c == 0x01) + def_state = in_line; + else if (c == '\n') + def_state = finish; + else + { + if (shortdef == NULL) + shortdef = g_string_sized_new (32); + + g_string_append_c (shortdef, c); + } + break; + + case in_line: + if (c == ',' || c == '\n') + def_state = finish; + else if (isdigit (c)) + { + if (linedef == NULL) + linedef = g_string_sized_new (32); + + g_string_append_c (linedef, c); + } + break; + + case finish: + *long_name = longdef == NULL ? NULL : g_string_free (longdef, FALSE); + *short_name = shortdef == NULL ? NULL : g_string_free (shortdef, FALSE); + + if (linedef == NULL) + *line = 0; + else + { + *line = atol (linedef->str); + g_string_free (linedef, TRUE); + } + return TRUE; + + default: + break; + } + + buf++; + c = *buf; + } + + *long_name = NULL; + *short_name = NULL; + *line = 0; + + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static GPtrArray * +etags_set_definition_hash (const char *tagfile, const char *start_path, const char *match_func) +{ + /* *INDENT-OFF* */ + enum + { + start, + in_filename, + in_define + } state = start; + /* *INDENT-ON* */ + + FILE *f; + char buf[BUF_LARGE]; + char *filename = NULL; + GPtrArray *ret = NULL; + + if (match_func == NULL || tagfile == NULL) + return NULL; + + /* open file with positions */ + f = fopen (tagfile, "r"); + if (f == NULL) + return NULL; + + while (fgets (buf, sizeof (buf), f) != NULL) + switch (state) + { + case start: + if (buf[0] == 0x0C) + state = in_filename; + break; + + case in_filename: + { + size_t pos; + + pos = strcspn (buf, ","); + g_free (filename); + filename = g_strndup (buf, pos); + state = in_define; + break; + } + + case in_define: + if (buf[0] == 0x0C) + state = in_filename; + else + { + char *chekedstr; + + /* check if the filename matches the define pos */ + chekedstr = strstr (buf, match_func); + if (chekedstr != NULL) + { + char *longname = NULL; + char *shortname = NULL; + etags_hash_t *def_hash; + + def_hash = g_new (etags_hash_t, 1); + + def_hash->fullpath = mc_build_filename (start_path, filename, (char *) NULL); + def_hash->filename = g_strdup (filename); + + def_hash->line = 0; + + parse_define (chekedstr, &longname, &shortname, &def_hash->line); + + if (shortname != NULL && *shortname != '\0') + { + def_hash->short_define = shortname; + g_free (longname); + } + else + { + def_hash->short_define = longname; + g_free (shortname); + } + + if (ret == NULL) + ret = g_ptr_array_new_with_free_func (etags_hash_free); + + g_ptr_array_add (ret, def_hash); + } + } + break; + + default: + break; + } + + g_free (filename); + fclose (f); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +editcmd_dialog_select_definition_add (gpointer data, gpointer user_data) +{ + etags_hash_t *def_hash = (etags_hash_t *) data; + WListbox *def_list = (WListbox *) user_data; + char *label_def; + int def_width; + + label_def = + g_strdup_printf ("%s -> %s:%ld", def_hash->short_define, def_hash->filename, + def_hash->line); + listbox_add_item (def_list, LISTBOX_APPEND_AT_END, 0, label_def, def_hash, FALSE); + def_width = str_term_width1 (label_def); + g_free (label_def); + def_max_width = MAX (def_max_width, def_width); +} + +/* --------------------------------------------------------------------------------------------- */ +/* let the user select where function definition */ + +static void +editcmd_dialog_select_definition_show (WEdit * edit, char *match_expr, GPtrArray * def_hash) +{ + const WRect *w = &CONST_WIDGET (edit)->rect; + int start_x, start_y, offset; + char *curr = NULL; + WDialog *def_dlg; + WListbox *def_list; + int def_dlg_h; /* dialog height */ + int def_dlg_w; /* dialog width */ + + /* calculate the dialog metrics */ + def_dlg_h = def_hash->len + 2; + def_dlg_w = COLS - 2; /* will be clarified later */ + start_x = w->x + edit->curs_col + edit->start_col + EDIT_TEXT_HORIZONTAL_OFFSET + + (edit->fullscreen ? 0 : 1) + edit_options.line_state_width; + start_y = w->y + edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + (edit->fullscreen ? 0 : 1) + 1; + + if (start_x < 0) + start_x = 0; + if (start_x < w->x + 1) + start_x = w->x + 1 + edit_options.line_state_width; + + if (def_dlg_h > LINES - 2) + def_dlg_h = LINES - 2; + + offset = start_y + def_dlg_h - LINES; + if (offset > 0) + start_y -= (offset + 1); + + def_dlg = dlg_create (TRUE, start_y, start_x, def_dlg_h, def_dlg_w, WPOS_KEEP_DEFAULT, TRUE, + dialog_colors, NULL, NULL, "[Definitions]", match_expr); + def_list = listbox_new (1, 1, def_dlg_h - 2, def_dlg_w - 2, FALSE, NULL); + group_add_widget_autopos (GROUP (def_dlg), def_list, WPOS_KEEP_ALL, NULL); + + /* fill the listbox with the completions and get the maximum width */ + def_max_width = 0; + g_ptr_array_foreach (def_hash, editcmd_dialog_select_definition_add, def_list); + + /* adjust dialog width */ + def_dlg_w = def_max_width + 4; + offset = start_x + def_dlg_w - COLS; + if (offset > 0) + start_x -= offset; + + widget_set_size (WIDGET (def_dlg), start_y, start_x, def_dlg_h, def_dlg_w); + + /* pop up the dialog and apply the chosen completion */ + if (dlg_run (def_dlg) == B_ENTER) + { + etags_hash_t *curr_def = NULL; + gboolean do_moveto = FALSE; + + listbox_get_current (def_list, &curr, (void **) &curr_def); + + if (!edit->modified) + do_moveto = TRUE; + else if (!edit_query_dialog2 + (_("Warning"), + _("Current text was modified without a file save.\n" + "Continue discards these changes."), _("C&ontinue"), _("&Cancel"))) + { + edit->force |= REDRAW_COMPLETELY; + do_moveto = TRUE; + } + + if (curr != NULL && do_moveto && edit_stack_iterator + 1 < MAX_HISTORY_MOVETO) + { + vfs_path_free (edit_history_moveto[edit_stack_iterator].filename_vpath, TRUE); + + /* Is file path absolute? Prepend with dir_vpath if necessary */ + if (edit->filename_vpath != NULL && edit->filename_vpath->relative + && edit->dir_vpath != NULL) + edit_history_moveto[edit_stack_iterator].filename_vpath = + vfs_path_append_vpath_new (edit->dir_vpath, edit->filename_vpath, NULL); + else + edit_history_moveto[edit_stack_iterator].filename_vpath = + vfs_path_clone (edit->filename_vpath); + + edit_history_moveto[edit_stack_iterator].line = edit->start_line + edit->curs_row + 1; + edit_stack_iterator++; + vfs_path_free (edit_history_moveto[edit_stack_iterator].filename_vpath, TRUE); + edit_history_moveto[edit_stack_iterator].filename_vpath = + vfs_path_from_str ((char *) curr_def->fullpath); + edit_history_moveto[edit_stack_iterator].line = curr_def->line; + edit_reload_line (edit, edit_history_moveto[edit_stack_iterator].filename_vpath, + edit_history_moveto[edit_stack_iterator].line); + } + } + + /* destroy dialog before return */ + widget_destroy (WIDGET (def_dlg)); +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +edit_get_match_keyword_cmd (WEdit * edit) +{ + gsize word_len = 0; + gsize i; + off_t word_start = 0; + GString *match_expr; + char *path = NULL; + char *ptr = NULL; + char *tagfile = NULL; + GPtrArray *def_hash = NULL; + + /* search start of word to be completed */ + if (!edit_buffer_find_word_start (&edit->buffer, &word_start, &word_len)) + return; + + /* prepare match expression */ + match_expr = g_string_sized_new (word_len); + for (i = 0; i < word_len; i++) + g_string_append_c (match_expr, edit_buffer_get_byte (&edit->buffer, word_start + i)); + + ptr = g_get_current_dir (); + path = g_strconcat (ptr, PATH_SEP_STR, (char *) NULL); + g_free (ptr); + + /* Recursive search file 'TAGS' in parent dirs */ + do + { + ptr = g_path_get_dirname (path); + g_free (path); + path = ptr; + g_free (tagfile); + tagfile = mc_build_filename (path, TAGS_NAME, (char *) NULL); + if (tagfile != NULL && exist_file (tagfile)) + break; + } + while (strcmp (path, PATH_SEP_STR) != 0); + + if (tagfile != NULL) + { + def_hash = etags_set_definition_hash (tagfile, path, match_expr->str); + g_free (tagfile); + } + g_free (path); + + if (def_hash != NULL) + { + editcmd_dialog_select_definition_show (edit, match_expr->str, def_hash); + + g_ptr_array_free (def_hash, TRUE); + } + + g_string_free (match_expr, TRUE); +} + +/* --------------------------------------------------------------------------------------------- */ -- cgit v1.2.3