mcedit: move all completion-related stuff to editcomplete.[ch].

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
Andrew Borodin 2021-05-23 10:18:31 +03:00
parent 21eefb3e36
commit b77961a14d
11 changed files with 538 additions and 458 deletions

View File

@ -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 \

View File

@ -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);

View File

@ -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"

View File

@ -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)

View File

@ -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;
}
/* --------------------------------------------------------------------------------------------- */

View File

@ -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
View 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
View 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 */

View File

@ -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

View File

@ -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,
"ÆÙ×Á"
},
};

View File

@ -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