mirror of
https://github.com/MidnightCommander/mc
synced 2024-12-22 20:36:50 +03:00
mcedit: move all completion-related stuff to editcomplete.[ch].
Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
parent
21eefb3e36
commit
b77961a14d
@ -10,6 +10,7 @@ libedit_la_SOURCES = \
|
||||
bookmark.c \
|
||||
edit-impl.h \
|
||||
edit.c edit.h \
|
||||
editcomplete.c editcomplete.h \
|
||||
editbuffer.c editbuffer.h \
|
||||
editcmd.c \
|
||||
editcmd_dialogs.c editcmd_dialogs.h \
|
||||
|
@ -196,8 +196,6 @@ void edit_mark_current_line_cmd (WEdit * edit);
|
||||
void edit_set_markers (WEdit * edit, off_t m1, off_t m2, long c1, long c2);
|
||||
void edit_push_markers (WEdit * edit);
|
||||
|
||||
void edit_complete_word_cmd (WEdit * edit);
|
||||
|
||||
gboolean edit_save_block (WEdit * edit, const char *filename, off_t start, off_t finish);
|
||||
gboolean edit_save_block_cmd (WEdit * edit);
|
||||
gboolean edit_insert_file_cmd (WEdit * edit);
|
||||
|
@ -69,6 +69,7 @@
|
||||
#include "edit-impl.h"
|
||||
#include "editwidget.h"
|
||||
#include "editsearch.h"
|
||||
#include "editcomplete.h" /* edit_complete_word_cmd() */
|
||||
#include "etags.h" /* edit_get_match_keyword_cmd() */
|
||||
#ifdef HAVE_ASPELL
|
||||
#include "spell.h"
|
||||
|
@ -681,262 +681,6 @@ pipe_mail (const edit_buffer_t * buf, char *to, char *subject, char *cc)
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* Get current word under cursor
|
||||
*
|
||||
* @param esm status message window
|
||||
* @param srch mc_search object
|
||||
* @param word_start start word position
|
||||
*
|
||||
* @return newly allocated string or NULL if no any words under cursor
|
||||
*/
|
||||
|
||||
static GString *
|
||||
edit_collect_completions_get_current_word (edit_search_status_msg_t * esm, mc_search_t * srch,
|
||||
off_t word_start)
|
||||
{
|
||||
WEdit *edit = esm->edit;
|
||||
gsize len = 0;
|
||||
GString *temp = NULL;
|
||||
|
||||
if (mc_search_run (srch, (void *) esm, word_start, edit->buffer.size, &len))
|
||||
{
|
||||
off_t i;
|
||||
|
||||
for (i = 0; i < (off_t) len; i++)
|
||||
{
|
||||
int chr;
|
||||
|
||||
chr = edit_buffer_get_byte (&edit->buffer, word_start + i);
|
||||
if (!isspace (chr))
|
||||
{
|
||||
if (temp == NULL)
|
||||
temp = g_string_sized_new (len);
|
||||
|
||||
g_string_append_c (temp, chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
edit_completion_string_free (gpointer data)
|
||||
{
|
||||
g_string_free ((GString *) data, TRUE);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* collect the possible completions from one buffer
|
||||
*/
|
||||
|
||||
static void
|
||||
edit_collect_completion_from_one_buffer (gboolean active_buffer, GQueue ** compl,
|
||||
mc_search_t * srch, edit_search_status_msg_t * esm,
|
||||
off_t word_start, gsize word_len, off_t last_byte,
|
||||
GString * current_word, int *max_width)
|
||||
{
|
||||
GString *temp = NULL;
|
||||
gsize len = 0;
|
||||
off_t start = -1;
|
||||
|
||||
while (mc_search_run (srch, (void *) esm, start + 1, last_byte, &len))
|
||||
{
|
||||
gsize i;
|
||||
int width;
|
||||
|
||||
if (temp == NULL)
|
||||
temp = g_string_sized_new (8);
|
||||
else
|
||||
g_string_set_size (temp, 0);
|
||||
|
||||
start = srch->normal_offset;
|
||||
|
||||
/* add matched completion if not yet added */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
int ch;
|
||||
|
||||
ch = edit_buffer_get_byte (&esm->edit->buffer, start + i);
|
||||
if (isspace (ch))
|
||||
continue;
|
||||
|
||||
/* skip current word */
|
||||
if (start + (off_t) i == word_start)
|
||||
break;
|
||||
|
||||
g_string_append_c (temp, ch);
|
||||
}
|
||||
|
||||
if (temp->len == 0)
|
||||
continue;
|
||||
|
||||
if (current_word != NULL && g_string_equal (current_word, temp))
|
||||
continue;
|
||||
|
||||
if (*compl == NULL)
|
||||
*compl = g_queue_new ();
|
||||
else
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = g_queue_peek_head_link (*compl); l != NULL; l = g_list_next (l))
|
||||
{
|
||||
GString *s = (GString *) l->data;
|
||||
|
||||
/* skip if already added */
|
||||
if (strncmp (s->str + word_len, temp->str + word_len,
|
||||
MAX (len, s->len) - word_len) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (l != NULL)
|
||||
{
|
||||
/* resort completion in main buffer only:
|
||||
* these completions must be at the top of list in the completion dialog */
|
||||
if (!active_buffer && l != g_queue_peek_tail_link (*compl))
|
||||
{
|
||||
/* move to the end */
|
||||
g_queue_unlink (*compl, l);
|
||||
g_queue_push_tail_link (*compl, l);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_CHARSET
|
||||
{
|
||||
GString *recoded;
|
||||
|
||||
recoded = str_convert_to_display (temp->str);
|
||||
if (recoded->len != 0)
|
||||
mc_g_string_copy (temp, recoded);
|
||||
|
||||
g_string_free (recoded, TRUE);
|
||||
}
|
||||
#endif
|
||||
if (active_buffer)
|
||||
g_queue_push_tail (*compl, temp);
|
||||
else
|
||||
g_queue_push_head (*compl, temp);
|
||||
|
||||
start += len;
|
||||
|
||||
/* note the maximal length needed for the completion dialog */
|
||||
width = str_term_width1 (temp->str);
|
||||
*max_width = MAX (*max_width, width);
|
||||
|
||||
temp = NULL;
|
||||
}
|
||||
|
||||
if (temp != NULL)
|
||||
g_string_free (temp, TRUE);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* collect the possible completions from all buffers
|
||||
*/
|
||||
|
||||
static GQueue *
|
||||
edit_collect_completions (WEdit * edit, off_t word_start, gsize word_len,
|
||||
const char *match_expr, int *max_width)
|
||||
{
|
||||
GQueue *compl = NULL;
|
||||
mc_search_t *srch;
|
||||
off_t last_byte;
|
||||
GString *current_word;
|
||||
gboolean entire_file, all_files;
|
||||
edit_search_status_msg_t esm;
|
||||
|
||||
#ifdef HAVE_CHARSET
|
||||
srch = mc_search_new (match_expr, cp_source);
|
||||
#else
|
||||
srch = mc_search_new (match_expr, NULL);
|
||||
#endif
|
||||
if (srch == NULL)
|
||||
return NULL;
|
||||
|
||||
entire_file =
|
||||
mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION,
|
||||
"editor_wordcompletion_collect_entire_file", FALSE);
|
||||
|
||||
last_byte = entire_file ? edit->buffer.size : word_start;
|
||||
|
||||
srch->search_type = MC_SEARCH_T_REGEX;
|
||||
srch->is_case_sensitive = TRUE;
|
||||
srch->search_fn = edit_search_cmd_callback;
|
||||
srch->update_fn = edit_search_update_callback;
|
||||
|
||||
esm.first = TRUE;
|
||||
esm.edit = edit;
|
||||
esm.offset = entire_file ? 0 : word_start;
|
||||
|
||||
status_msg_init (STATUS_MSG (&esm), _("Collect completions"), 1.0, simple_status_msg_init_cb,
|
||||
edit_search_status_update_cb, NULL);
|
||||
|
||||
current_word = edit_collect_completions_get_current_word (&esm, srch, word_start);
|
||||
|
||||
*max_width = 0;
|
||||
|
||||
/* collect completions from current buffer at first */
|
||||
edit_collect_completion_from_one_buffer (TRUE, &compl, srch, &esm, word_start, word_len,
|
||||
last_byte, current_word, max_width);
|
||||
|
||||
/* collect completions from other buffers */
|
||||
all_files =
|
||||
mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION,
|
||||
"editor_wordcompletion_collect_all_files", TRUE);
|
||||
if (all_files)
|
||||
{
|
||||
const WGroup *owner = CONST_GROUP (CONST_WIDGET (edit)->owner);
|
||||
gboolean saved_verbose;
|
||||
GList *w;
|
||||
|
||||
/* don't show incorrect percentage in edit_search_status_update_cb() */
|
||||
saved_verbose = verbose;
|
||||
verbose = FALSE;
|
||||
|
||||
for (w = owner->widgets; w != NULL; w = g_list_next (w))
|
||||
{
|
||||
Widget *ww = WIDGET (w->data);
|
||||
WEdit *e;
|
||||
|
||||
if (!edit_widget_is_editor (ww))
|
||||
continue;
|
||||
|
||||
e = (WEdit *) ww;
|
||||
|
||||
if (e == edit)
|
||||
continue;
|
||||
|
||||
/* search in entire file */
|
||||
word_start = 0;
|
||||
last_byte = e->buffer.size;
|
||||
esm.edit = e;
|
||||
esm.offset = 0;
|
||||
|
||||
edit_collect_completion_from_one_buffer (FALSE, &compl, srch, &esm, word_start,
|
||||
word_len, last_byte, current_word, max_width);
|
||||
}
|
||||
|
||||
verbose = saved_verbose;
|
||||
}
|
||||
|
||||
status_msg_deinit (STATUS_MSG (&esm));
|
||||
mc_search_free (srch);
|
||||
if (current_word != NULL)
|
||||
g_string_free (current_word, TRUE);
|
||||
|
||||
return compl;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
@ -1099,32 +843,6 @@ edit_syntax_onoff_cb (void *data, void *user_data)
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* Insert autocompleted word into editor.
|
||||
*
|
||||
* @param edit editor object
|
||||
* @param completion word for completion
|
||||
* @param word_len offset from beginning for insert
|
||||
*/
|
||||
|
||||
static void
|
||||
edit_complete_word_insert_recoded_completion (WEdit * edit, char *completion, gsize word_len)
|
||||
{
|
||||
#ifdef HAVE_CHARSET
|
||||
GString *temp;
|
||||
|
||||
temp = str_convert_to_input (completion);
|
||||
|
||||
for (completion = temp->str + word_len; *completion != '\0'; completion++)
|
||||
edit_insert (edit, *completion);
|
||||
g_string_free (temp, TRUE);
|
||||
#else
|
||||
for (completion += word_len; *completion != '\0'; completion++)
|
||||
edit_insert (edit, *completion);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/*** public functions ****************************************************************************/
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
@ -2601,74 +2319,6 @@ edit_mail_dialog (WEdit * edit)
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
/*******************/
|
||||
/* Word Completion */
|
||||
/*******************/
|
||||
|
||||
/**
|
||||
* Complete current word using regular expression search
|
||||
* backwards beginning at the current cursor position.
|
||||
*/
|
||||
|
||||
void
|
||||
edit_complete_word_cmd (WEdit * edit)
|
||||
{
|
||||
off_t word_start = 0;
|
||||
gsize word_len = 0;
|
||||
GString *match_expr;
|
||||
gsize i;
|
||||
GQueue *compl; /* completions: list of GString* */
|
||||
int max_width;
|
||||
|
||||
/* 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_strdup_printf ("\\b%.*s[a-zA-Z_0-9]+", word_len, bufpos); */
|
||||
match_expr = g_string_new ("(^|\\s+|\\b)");
|
||||
for (i = 0; i < word_len; i++)
|
||||
g_string_append_c (match_expr, edit_buffer_get_byte (&edit->buffer, word_start + i));
|
||||
g_string_append (match_expr,
|
||||
"[^\\s\\.=\\+\\[\\]\\(\\)\\,\\;\\:\\\"\\'\\-\\?\\/\\|\\\\\\{\\}\\*\\&\\^\\%%\\$#@\\!]+");
|
||||
|
||||
/* collect possible completions */
|
||||
compl = edit_collect_completions (edit, word_start, word_len, match_expr->str, &max_width);
|
||||
|
||||
g_string_free (match_expr, TRUE);
|
||||
|
||||
if (compl == NULL)
|
||||
return;
|
||||
|
||||
if (g_queue_get_length (compl) == 1)
|
||||
{
|
||||
/* insert completed word if there is only one match */
|
||||
|
||||
GString *curr_compl;
|
||||
|
||||
curr_compl = (GString *) g_queue_peek_head (compl);
|
||||
edit_complete_word_insert_recoded_completion (edit, curr_compl->str, word_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* more than one possible completion => ask the user */
|
||||
|
||||
char *curr_compl;
|
||||
|
||||
/* let the user select the preferred completion */
|
||||
curr_compl = editcmd_dialog_completion_show (edit, compl, max_width);
|
||||
if (curr_compl != NULL)
|
||||
{
|
||||
edit_complete_word_insert_recoded_completion (edit, curr_compl, word_len);
|
||||
g_free (curr_compl);
|
||||
}
|
||||
}
|
||||
|
||||
g_queue_free_full (compl, edit_completion_string_free);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef HAVE_CHARSET
|
||||
void
|
||||
edit_select_codepage_cmd (WEdit * edit)
|
||||
|
@ -115,69 +115,3 @@ editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean c
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/* let the user select its preferred completion */
|
||||
|
||||
char *
|
||||
editcmd_dialog_completion_show (const WEdit * edit, GQueue * compl, int max_width)
|
||||
{
|
||||
const Widget *we = CONST_WIDGET (edit);
|
||||
int start_x, start_y, offset;
|
||||
char *curr = NULL;
|
||||
WDialog *compl_dlg;
|
||||
WListbox *compl_list;
|
||||
int compl_dlg_h; /* completion dialog height */
|
||||
int compl_dlg_w; /* completion dialog width */
|
||||
GList *i;
|
||||
|
||||
/* calculate the dialog metrics */
|
||||
compl_dlg_h = g_queue_get_length (compl) + 2;
|
||||
compl_dlg_w = max_width + 4;
|
||||
start_x = we->x + edit->curs_col + edit->start_col + EDIT_TEXT_HORIZONTAL_OFFSET +
|
||||
(edit->fullscreen ? 0 : 1) + option_line_state_width;
|
||||
start_y = we->y + edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + (edit->fullscreen ? 0 : 1) + 1;
|
||||
|
||||
if (start_x < 0)
|
||||
start_x = 0;
|
||||
if (start_x < we->x + 1)
|
||||
start_x = we->x + 1 + option_line_state_width;
|
||||
if (compl_dlg_w > COLS)
|
||||
compl_dlg_w = COLS;
|
||||
if (compl_dlg_h > LINES - 2)
|
||||
compl_dlg_h = LINES - 2;
|
||||
|
||||
offset = start_x + compl_dlg_w - COLS;
|
||||
if (offset > 0)
|
||||
start_x -= offset;
|
||||
offset = start_y + compl_dlg_h - LINES;
|
||||
if (offset > 0)
|
||||
start_y -= offset;
|
||||
|
||||
/* create the dialog */
|
||||
compl_dlg =
|
||||
dlg_create (TRUE, start_y, start_x, compl_dlg_h, compl_dlg_w, WPOS_KEEP_DEFAULT, TRUE,
|
||||
dialog_colors, NULL, NULL, "[Completion]", NULL);
|
||||
|
||||
/* create the listbox */
|
||||
compl_list = listbox_new (1, 1, compl_dlg_h - 2, compl_dlg_w - 2, FALSE, NULL);
|
||||
|
||||
/* fill the listbox with the completions in the reverse order */
|
||||
for (i = g_queue_peek_tail_link (compl); i != NULL; i = g_list_previous (i))
|
||||
listbox_add_item (compl_list, LISTBOX_APPEND_AT_END, 0, ((GString *) i->data)->str, NULL,
|
||||
FALSE);
|
||||
|
||||
group_add_widget (GROUP (compl_dlg), compl_list);
|
||||
|
||||
/* pop up the dialog and apply the chosen completion */
|
||||
if (dlg_run (compl_dlg) == B_ENTER)
|
||||
{
|
||||
listbox_get_current (compl_list, &curr, NULL);
|
||||
curr = g_strdup (curr);
|
||||
}
|
||||
|
||||
/* destroy dialog before return */
|
||||
widget_destroy (WIDGET (compl_dlg));
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
@ -14,7 +14,6 @@
|
||||
/*** declarations of public functions ************************************************************/
|
||||
|
||||
int editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean cancel);
|
||||
char *editcmd_dialog_completion_show (const WEdit * edit, GQueue * compl, int max_width);
|
||||
|
||||
/*** inline functions ****************************************************************************/
|
||||
|
||||
|
475
src/editor/editcomplete.c
Normal file
475
src/editor/editcomplete.c
Normal file
@ -0,0 +1,475 @@
|
||||
/*
|
||||
Editor word completion engine
|
||||
|
||||
Copyright (C) 2021
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
Written by:
|
||||
Andrew Borodin <aborodin@vmail.ru>, 2021
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <ctype.h> /* isspace() */
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/global.h"
|
||||
#include "lib/search.h"
|
||||
#include "lib/strutil.h"
|
||||
#ifdef HAVE_CHARSET
|
||||
#include "lib/charsets.h" /* str_convert_to_input() */
|
||||
#endif
|
||||
#include "lib/tty/tty.h" /* LINES, COLS */
|
||||
#include "lib/widget.h"
|
||||
|
||||
#include "src/setup.h" /* verbose */
|
||||
|
||||
#include "editwidget.h"
|
||||
#include "edit-impl.h"
|
||||
#include "editsearch.h"
|
||||
|
||||
#include "editcomplete.h"
|
||||
|
||||
/*** global variables ****************************************************************************/
|
||||
|
||||
/*** file scope macro definitions ****************************************************************/
|
||||
|
||||
/*** file scope type declarations ****************************************************************/
|
||||
|
||||
/*** file scope variables ************************************************************************/
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/*** file scope functions ************************************************************************/
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get current word under cursor
|
||||
*
|
||||
* @param esm status message window
|
||||
* @param srch mc_search object
|
||||
* @param word_start start word position
|
||||
*
|
||||
* @return newly allocated string or NULL if no any words under cursor
|
||||
*/
|
||||
|
||||
static GString *
|
||||
edit_collect_completions_get_current_word (edit_search_status_msg_t * esm, mc_search_t * srch,
|
||||
off_t word_start)
|
||||
{
|
||||
WEdit *edit = esm->edit;
|
||||
gsize len = 0;
|
||||
GString *temp = NULL;
|
||||
|
||||
if (mc_search_run (srch, (void *) esm, word_start, edit->buffer.size, &len))
|
||||
{
|
||||
off_t i;
|
||||
|
||||
for (i = 0; i < (off_t) len; i++)
|
||||
{
|
||||
int chr;
|
||||
|
||||
chr = edit_buffer_get_byte (&edit->buffer, word_start + i);
|
||||
if (!isspace (chr))
|
||||
{
|
||||
if (temp == NULL)
|
||||
temp = g_string_sized_new (len);
|
||||
|
||||
g_string_append_c (temp, chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* collect the possible completions from one buffer
|
||||
*/
|
||||
|
||||
static void
|
||||
edit_collect_completion_from_one_buffer (gboolean active_buffer, GQueue ** compl,
|
||||
mc_search_t * srch, edit_search_status_msg_t * esm,
|
||||
off_t word_start, gsize word_len, off_t last_byte,
|
||||
GString * current_word, int *max_width)
|
||||
{
|
||||
GString *temp = NULL;
|
||||
gsize len = 0;
|
||||
off_t start = -1;
|
||||
|
||||
while (mc_search_run (srch, (void *) esm, start + 1, last_byte, &len))
|
||||
{
|
||||
gsize i;
|
||||
int width;
|
||||
|
||||
if (temp == NULL)
|
||||
temp = g_string_sized_new (8);
|
||||
else
|
||||
g_string_set_size (temp, 0);
|
||||
|
||||
start = srch->normal_offset;
|
||||
|
||||
/* add matched completion if not yet added */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
int ch;
|
||||
|
||||
ch = edit_buffer_get_byte (&esm->edit->buffer, start + i);
|
||||
if (isspace (ch))
|
||||
continue;
|
||||
|
||||
/* skip current word */
|
||||
if (start + (off_t) i == word_start)
|
||||
break;
|
||||
|
||||
g_string_append_c (temp, ch);
|
||||
}
|
||||
|
||||
if (temp->len == 0)
|
||||
continue;
|
||||
|
||||
if (current_word != NULL && g_string_equal (current_word, temp))
|
||||
continue;
|
||||
|
||||
if (*compl == NULL)
|
||||
*compl = g_queue_new ();
|
||||
else
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = g_queue_peek_head_link (*compl); l != NULL; l = g_list_next (l))
|
||||
{
|
||||
GString *s = (GString *) l->data;
|
||||
|
||||
/* skip if already added */
|
||||
if (strncmp (s->str + word_len, temp->str + word_len,
|
||||
MAX (len, s->len) - word_len) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (l != NULL)
|
||||
{
|
||||
/* resort completion in main buffer only:
|
||||
* these completions must be at the top of list in the completion dialog */
|
||||
if (!active_buffer && l != g_queue_peek_tail_link (*compl))
|
||||
{
|
||||
/* move to the end */
|
||||
g_queue_unlink (*compl, l);
|
||||
g_queue_push_tail_link (*compl, l);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_CHARSET
|
||||
{
|
||||
GString *recoded;
|
||||
|
||||
recoded = str_convert_to_display (temp->str);
|
||||
if (recoded->len != 0)
|
||||
mc_g_string_copy (temp, recoded);
|
||||
|
||||
g_string_free (recoded, TRUE);
|
||||
}
|
||||
#endif
|
||||
if (active_buffer)
|
||||
g_queue_push_tail (*compl, temp);
|
||||
else
|
||||
g_queue_push_head (*compl, temp);
|
||||
|
||||
start += len;
|
||||
|
||||
/* note the maximal length needed for the completion dialog */
|
||||
width = str_term_width1 (temp->str);
|
||||
*max_width = MAX (*max_width, width);
|
||||
|
||||
temp = NULL;
|
||||
}
|
||||
|
||||
if (temp != NULL)
|
||||
g_string_free (temp, TRUE);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* collect the possible completions from all buffers
|
||||
*/
|
||||
|
||||
static GQueue *
|
||||
edit_collect_completions (WEdit * edit, off_t word_start, gsize word_len,
|
||||
const char *match_expr, int *max_width)
|
||||
{
|
||||
GQueue *compl = NULL;
|
||||
mc_search_t *srch;
|
||||
off_t last_byte;
|
||||
GString *current_word;
|
||||
gboolean entire_file, all_files;
|
||||
edit_search_status_msg_t esm;
|
||||
|
||||
#ifdef HAVE_CHARSET
|
||||
srch = mc_search_new (match_expr, cp_source);
|
||||
#else
|
||||
srch = mc_search_new (match_expr, NULL);
|
||||
#endif
|
||||
if (srch == NULL)
|
||||
return NULL;
|
||||
|
||||
entire_file =
|
||||
mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION,
|
||||
"editor_wordcompletion_collect_entire_file", FALSE);
|
||||
|
||||
last_byte = entire_file ? edit->buffer.size : word_start;
|
||||
|
||||
srch->search_type = MC_SEARCH_T_REGEX;
|
||||
srch->is_case_sensitive = TRUE;
|
||||
srch->search_fn = edit_search_cmd_callback;
|
||||
srch->update_fn = edit_search_update_callback;
|
||||
|
||||
esm.first = TRUE;
|
||||
esm.edit = edit;
|
||||
esm.offset = entire_file ? 0 : word_start;
|
||||
|
||||
status_msg_init (STATUS_MSG (&esm), _("Collect completions"), 1.0, simple_status_msg_init_cb,
|
||||
edit_search_status_update_cb, NULL);
|
||||
|
||||
current_word = edit_collect_completions_get_current_word (&esm, srch, word_start);
|
||||
|
||||
*max_width = 0;
|
||||
|
||||
/* collect completions from current buffer at first */
|
||||
edit_collect_completion_from_one_buffer (TRUE, &compl, srch, &esm, word_start, word_len,
|
||||
last_byte, current_word, max_width);
|
||||
|
||||
/* collect completions from other buffers */
|
||||
all_files =
|
||||
mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION,
|
||||
"editor_wordcompletion_collect_all_files", TRUE);
|
||||
if (all_files)
|
||||
{
|
||||
const WGroup *owner = CONST_GROUP (CONST_WIDGET (edit)->owner);
|
||||
gboolean saved_verbose;
|
||||
GList *w;
|
||||
|
||||
/* don't show incorrect percentage in edit_search_status_update_cb() */
|
||||
saved_verbose = verbose;
|
||||
verbose = FALSE;
|
||||
|
||||
for (w = owner->widgets; w != NULL; w = g_list_next (w))
|
||||
{
|
||||
Widget *ww = WIDGET (w->data);
|
||||
WEdit *e;
|
||||
|
||||
if (!edit_widget_is_editor (ww))
|
||||
continue;
|
||||
|
||||
e = (WEdit *) ww;
|
||||
|
||||
if (e == edit)
|
||||
continue;
|
||||
|
||||
/* search in entire file */
|
||||
word_start = 0;
|
||||
last_byte = e->buffer.size;
|
||||
esm.edit = e;
|
||||
esm.offset = 0;
|
||||
|
||||
edit_collect_completion_from_one_buffer (FALSE, &compl, srch, &esm, word_start,
|
||||
word_len, last_byte, current_word, max_width);
|
||||
}
|
||||
|
||||
verbose = saved_verbose;
|
||||
}
|
||||
|
||||
status_msg_deinit (STATUS_MSG (&esm));
|
||||
mc_search_free (srch);
|
||||
if (current_word != NULL)
|
||||
g_string_free (current_word, TRUE);
|
||||
|
||||
return compl;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Insert autocompleted word into editor.
|
||||
*
|
||||
* @param edit editor object
|
||||
* @param completion word for completion
|
||||
* @param word_len offset from beginning for insert
|
||||
*/
|
||||
|
||||
static void
|
||||
edit_complete_word_insert_recoded_completion (WEdit * edit, char *completion, gsize word_len)
|
||||
{
|
||||
#ifdef HAVE_CHARSET
|
||||
GString *temp;
|
||||
|
||||
temp = str_convert_to_input (completion);
|
||||
|
||||
for (completion = temp->str + word_len; *completion != '\0'; completion++)
|
||||
edit_insert (edit, *completion);
|
||||
g_string_free (temp, TRUE);
|
||||
#else
|
||||
for (completion += word_len; *completion != '\0'; completion++)
|
||||
edit_insert (edit, *completion);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
edit_completion_string_free (gpointer data)
|
||||
{
|
||||
g_string_free ((GString *) data, TRUE);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/*** public functions ****************************************************************************/
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/* let the user select its preferred completion */
|
||||
|
||||
/* Public function for unit tests */
|
||||
char *
|
||||
edit_completion_dialog_show (const WEdit * edit, GQueue * compl, int max_width)
|
||||
{
|
||||
const Widget *we = CONST_WIDGET (edit);
|
||||
int start_x, start_y, offset;
|
||||
char *curr = NULL;
|
||||
WDialog *compl_dlg;
|
||||
WListbox *compl_list;
|
||||
int compl_dlg_h; /* completion dialog height */
|
||||
int compl_dlg_w; /* completion dialog width */
|
||||
GList *i;
|
||||
|
||||
/* calculate the dialog metrics */
|
||||
compl_dlg_h = g_queue_get_length (compl) + 2;
|
||||
compl_dlg_w = max_width + 4;
|
||||
start_x = we->x + edit->curs_col + edit->start_col + EDIT_TEXT_HORIZONTAL_OFFSET +
|
||||
(edit->fullscreen ? 0 : 1) + option_line_state_width;
|
||||
start_y = we->y + edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + (edit->fullscreen ? 0 : 1) + 1;
|
||||
|
||||
if (start_x < 0)
|
||||
start_x = 0;
|
||||
if (start_x < we->x + 1)
|
||||
start_x = we->x + 1 + option_line_state_width;
|
||||
if (compl_dlg_w > COLS)
|
||||
compl_dlg_w = COLS;
|
||||
if (compl_dlg_h > LINES - 2)
|
||||
compl_dlg_h = LINES - 2;
|
||||
|
||||
offset = start_x + compl_dlg_w - COLS;
|
||||
if (offset > 0)
|
||||
start_x -= offset;
|
||||
offset = start_y + compl_dlg_h - LINES;
|
||||
if (offset > 0)
|
||||
start_y -= offset;
|
||||
|
||||
/* create the dialog */
|
||||
compl_dlg =
|
||||
dlg_create (TRUE, start_y, start_x, compl_dlg_h, compl_dlg_w, WPOS_KEEP_DEFAULT, TRUE,
|
||||
dialog_colors, NULL, NULL, "[Completion]", NULL);
|
||||
|
||||
/* create the listbox */
|
||||
compl_list = listbox_new (1, 1, compl_dlg_h - 2, compl_dlg_w - 2, FALSE, NULL);
|
||||
|
||||
/* fill the listbox with the completions in the reverse order */
|
||||
for (i = g_queue_peek_tail_link (compl); i != NULL; i = g_list_previous (i))
|
||||
listbox_add_item (compl_list, LISTBOX_APPEND_AT_END, 0, ((GString *) i->data)->str, NULL,
|
||||
FALSE);
|
||||
|
||||
group_add_widget (GROUP (compl_dlg), compl_list);
|
||||
|
||||
/* pop up the dialog and apply the chosen completion */
|
||||
if (dlg_run (compl_dlg) == B_ENTER)
|
||||
{
|
||||
listbox_get_current (compl_list, &curr, NULL);
|
||||
curr = g_strdup (curr);
|
||||
}
|
||||
|
||||
/* destroy dialog before return */
|
||||
widget_destroy (WIDGET (compl_dlg));
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Complete current word using regular expression search
|
||||
* backwards beginning at the current cursor position.
|
||||
*/
|
||||
|
||||
void
|
||||
edit_complete_word_cmd (WEdit * edit)
|
||||
{
|
||||
off_t word_start = 0;
|
||||
gsize word_len = 0;
|
||||
GString *match_expr;
|
||||
gsize i;
|
||||
GQueue *compl; /* completions: list of GString* */
|
||||
int max_width;
|
||||
|
||||
/* 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_strdup_printf ("\\b%.*s[a-zA-Z_0-9]+", word_len, bufpos); */
|
||||
match_expr = g_string_new ("(^|\\s+|\\b)");
|
||||
for (i = 0; i < word_len; i++)
|
||||
g_string_append_c (match_expr, edit_buffer_get_byte (&edit->buffer, word_start + i));
|
||||
g_string_append (match_expr,
|
||||
"[^\\s\\.=\\+\\[\\]\\(\\)\\,\\;\\:\\\"\\'\\-\\?\\/\\|\\\\\\{\\}\\*\\&\\^\\%%\\$#@\\!]+");
|
||||
|
||||
/* collect possible completions */
|
||||
compl = edit_collect_completions (edit, word_start, word_len, match_expr->str, &max_width);
|
||||
|
||||
g_string_free (match_expr, TRUE);
|
||||
|
||||
if (compl == NULL)
|
||||
return;
|
||||
|
||||
if (g_queue_get_length (compl) == 1)
|
||||
{
|
||||
/* insert completed word if there is only one match */
|
||||
|
||||
GString *curr_compl;
|
||||
|
||||
curr_compl = (GString *) g_queue_peek_head (compl);
|
||||
edit_complete_word_insert_recoded_completion (edit, curr_compl->str, word_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* more than one possible completion => ask the user */
|
||||
|
||||
char *curr_compl;
|
||||
|
||||
/* let the user select the preferred completion */
|
||||
curr_compl = edit_completion_dialog_show (edit, compl, max_width);
|
||||
if (curr_compl != NULL)
|
||||
{
|
||||
edit_complete_word_insert_recoded_completion (edit, curr_compl, word_len);
|
||||
g_free (curr_compl);
|
||||
}
|
||||
}
|
||||
|
||||
g_queue_free_full (compl, edit_completion_string_free);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
21
src/editor/editcomplete.h
Normal file
21
src/editor/editcomplete.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef MC__EDIT_COMPLETE_H
|
||||
#define MC__EDIT_COMPLETE_H
|
||||
|
||||
/*** typedefs(not structures) and defined constants **********************************************/
|
||||
|
||||
/*** enums ***************************************************************************************/
|
||||
|
||||
/*** structures declarations (and typedefs of structures)*****************************************/
|
||||
|
||||
/*** global variables defined in .c file *********************************************************/
|
||||
|
||||
/*** declarations of public functions ************************************************************/
|
||||
|
||||
/* Public function for unit tests */
|
||||
char *edit_completion_dialog_show (const WEdit * edit, GQueue * compl, int max_width);
|
||||
|
||||
void edit_complete_word_cmd (WEdit * edit);
|
||||
|
||||
/*** inline functions ****************************************************************************/
|
||||
|
||||
#endif /* MC__EDIT_COMPLETE_H */
|
@ -26,10 +26,10 @@ endif
|
||||
EXTRA_DIST = mc.charsets test-data.txt.in
|
||||
|
||||
TESTS = \
|
||||
editcmd__edit_complete_word_cmd
|
||||
edit_complete_word_cmd
|
||||
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
||||
editcmd__edit_complete_word_cmd_SOURCES = \
|
||||
editcmd__edit_complete_word_cmd.c
|
||||
edit_complete_word_cmd_SOURCES = \
|
||||
edit_complete_word_cmd.c
|
||||
|
||||
|
@ -40,7 +40,8 @@
|
||||
#include "src/selcodepage.h"
|
||||
#endif
|
||||
#include "src/editor/editwidget.h"
|
||||
#include "src/editor/editcmd_dialogs.h"
|
||||
#include "src/editor/editmacros.h" /* edit_load_macro_cmd() */
|
||||
#include "src/editor/editcomplete.h"
|
||||
|
||||
static WGroup owner;
|
||||
static WEdit *test_edit;
|
||||
@ -88,60 +89,60 @@ edit_load_macro_cmd (WEdit * _edit)
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
/* @CapturedValue */
|
||||
static const WEdit *editcmd_dialog_completion_show__edit;
|
||||
static const WEdit *edit_completion_dialog_show__edit;
|
||||
/* @CapturedValue */
|
||||
static int editcmd_dialog_completion_show__max_width;
|
||||
static int edit_completion_dialog_show__max_width;
|
||||
/* @CapturedValue */
|
||||
static GQueue *editcmd_dialog_completion_show__compl;
|
||||
static GQueue *edit_completion_dialog_show__compl;
|
||||
|
||||
/* @ThenReturnValue */
|
||||
static char *editcmd_dialog_completion_show__return_value;
|
||||
static char *edit_completion_dialog_show__return_value;
|
||||
|
||||
/* @Mock */
|
||||
char *
|
||||
editcmd_dialog_completion_show (const WEdit * edit, GQueue * compl, int max_width)
|
||||
edit_completion_dialog_show (const WEdit * edit, GQueue * compl, int max_width)
|
||||
{
|
||||
|
||||
editcmd_dialog_completion_show__edit = edit;
|
||||
editcmd_dialog_completion_show__max_width = max_width;
|
||||
edit_completion_dialog_show__edit = edit;
|
||||
edit_completion_dialog_show__max_width = max_width;
|
||||
|
||||
{
|
||||
GList *i;
|
||||
|
||||
editcmd_dialog_completion_show__compl = g_queue_new ();
|
||||
edit_completion_dialog_show__compl = g_queue_new ();
|
||||
|
||||
for (i = g_queue_peek_tail_link (compl); i != NULL; i = g_list_previous (i))
|
||||
{
|
||||
GString *s = (GString *) i->data;
|
||||
|
||||
g_queue_push_tail (editcmd_dialog_completion_show__compl, mc_g_string_dup (s));
|
||||
g_queue_push_tail (edit_completion_dialog_show__compl, mc_g_string_dup (s));
|
||||
}
|
||||
}
|
||||
|
||||
return editcmd_dialog_completion_show__return_value;
|
||||
return edit_completion_dialog_show__return_value;
|
||||
}
|
||||
|
||||
static void
|
||||
editcmd_dialog_completion_show__init (void)
|
||||
edit_completion_dialog_show__init (void)
|
||||
{
|
||||
editcmd_dialog_completion_show__edit = NULL;
|
||||
editcmd_dialog_completion_show__max_width = 0;
|
||||
editcmd_dialog_completion_show__compl = NULL;
|
||||
editcmd_dialog_completion_show__return_value = NULL;
|
||||
edit_completion_dialog_show__edit = NULL;
|
||||
edit_completion_dialog_show__max_width = 0;
|
||||
edit_completion_dialog_show__compl = NULL;
|
||||
edit_completion_dialog_show__return_value = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
editcmd_dialog_completion_show__string_free (gpointer data)
|
||||
edit_completion_dialog_show__string_free (gpointer data)
|
||||
{
|
||||
g_string_free ((GString *) data, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
editcmd_dialog_completion_show__deinit (void)
|
||||
edit_completion_dialog_show__deinit (void)
|
||||
{
|
||||
if (editcmd_dialog_completion_show__compl != NULL)
|
||||
g_queue_free_full (editcmd_dialog_completion_show__compl,
|
||||
editcmd_dialog_completion_show__string_free);
|
||||
if (edit_completion_dialog_show__compl != NULL)
|
||||
g_queue_free_full (edit_completion_dialog_show__compl,
|
||||
edit_completion_dialog_show__string_free);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
@ -161,7 +162,7 @@ my_setup (void)
|
||||
load_codepages_list ();
|
||||
#endif /* HAVE_CHARSET */
|
||||
|
||||
mc_global.main_config = mc_config_init ("editcmd__edit_complete_word_cmd.ini", FALSE);
|
||||
mc_global.main_config = mc_config_init ("edit_complete_word_cmd.ini", FALSE);
|
||||
mc_config_set_bool (mc_global.main_config, CONFIG_APP_SECTION,
|
||||
"editor_wordcompletion_collect_all_files", TRUE);
|
||||
|
||||
@ -170,7 +171,7 @@ my_setup (void)
|
||||
test_edit = edit_init (NULL, 0, 0, 24, 80, vfs_path_from_str ("test-data.txt"), 1);
|
||||
memset (&owner, 0, sizeof (owner));
|
||||
group_add_widget (&owner, WIDGET (test_edit));
|
||||
editcmd_dialog_completion_show__init ();
|
||||
edit_completion_dialog_show__init ();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
@ -179,7 +180,7 @@ my_setup (void)
|
||||
static void
|
||||
my_teardown (void)
|
||||
{
|
||||
editcmd_dialog_completion_show__deinit ();
|
||||
edit_completion_dialog_show__deinit ();
|
||||
edit_clean (test_edit);
|
||||
group_remove_widget (test_edit);
|
||||
g_free (test_edit);
|
||||
@ -216,7 +217,7 @@ static const struct test_autocomplete_ds
|
||||
} test_autocomplete_ds[] =
|
||||
{
|
||||
{ /* 0. */
|
||||
111,
|
||||
102,
|
||||
"KOI8-R",
|
||||
0,
|
||||
"UTF-8",
|
||||
@ -225,11 +226,11 @@ static const struct test_autocomplete_ds
|
||||
|
||||
16,
|
||||
2,
|
||||
107,
|
||||
98,
|
||||
"Ñ<EFBFBD>ъйцукен"
|
||||
},
|
||||
{ /* 1. */
|
||||
147,
|
||||
138,
|
||||
"UTF-8",
|
||||
1,
|
||||
"KOI8-R",
|
||||
@ -238,7 +239,7 @@ static const struct test_autocomplete_ds
|
||||
|
||||
8,
|
||||
2,
|
||||
145,
|
||||
136,
|
||||
"ÜßÊÃÕËÅÎ"
|
||||
},
|
||||
};
|
||||
@ -250,7 +251,7 @@ START_PARAMETRIZED_TEST (test_autocomplete, test_autocomplete_ds)
|
||||
/* *INDENT-ON* */
|
||||
{
|
||||
/* given */
|
||||
editcmd_dialog_completion_show__return_value = g_strdup (data->input_completed_word);
|
||||
edit_completion_dialog_show__return_value = g_strdup (data->input_completed_word);
|
||||
|
||||
|
||||
mc_global.source_codepage = data->input_source_codepage_id;
|
||||
@ -266,10 +267,10 @@ START_PARAMETRIZED_TEST (test_autocomplete, test_autocomplete_ds)
|
||||
edit_complete_word_cmd (test_edit);
|
||||
|
||||
/* then */
|
||||
mctest_assert_ptr_eq (editcmd_dialog_completion_show__edit, test_edit);
|
||||
mctest_assert_int_eq (g_queue_get_length (editcmd_dialog_completion_show__compl),
|
||||
mctest_assert_ptr_eq (edit_completion_dialog_show__edit, test_edit);
|
||||
mctest_assert_int_eq (g_queue_get_length (edit_completion_dialog_show__compl),
|
||||
data->expected_compl_word_count);
|
||||
mctest_assert_int_eq (editcmd_dialog_completion_show__max_width, data->expected_max_width);
|
||||
mctest_assert_int_eq (edit_completion_dialog_show__max_width, data->expected_max_width);
|
||||
|
||||
{
|
||||
off_t i = 0;
|
||||
@ -314,13 +315,13 @@ static const struct test_autocomplete_single_ds
|
||||
} test_autocomplete_single_ds[] =
|
||||
{
|
||||
{ /* 0. */
|
||||
155,
|
||||
146,
|
||||
"UTF-8",
|
||||
1,
|
||||
"KOI8-R",
|
||||
0,
|
||||
|
||||
154,
|
||||
145,
|
||||
"ÆÙ×Á"
|
||||
},
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
****************** START:editcmd__edit_complete_word_cmd.c
|
||||
****************** START:edit_complete_word_cmd.c
|
||||
|
||||
--- UTF-8:
|
||||
|
||||
@ -17,4 +17,4 @@
|
||||
ニルラチ
|
||||
ニ
|
||||
|
||||
****************** END:editcmd__edit_complete_word_cmd.c
|
||||
****************** END:edit_complete_word_cmd.c
|
||||
|
Loading…
Reference in New Issue
Block a user