diff --git a/configure.ac b/configure.ac index 6e2bc486f..ed567493e 100644 --- a/configure.ac +++ b/configure.ac @@ -648,6 +648,8 @@ tests/lib/vfs/Makefile tests/lib/widget/Makefile tests/src/Makefile tests/src/filemanager/Makefile +tests/src/editor/Makefile +tests/src/editor/test-data.txt ]) fi diff --git a/src/editor/editcmd.c b/src/editor/editcmd.c index 61e141cc1..c2425591a 100644 --- a/src/editor/editcmd.c +++ b/src/editor/editcmd.c @@ -1495,6 +1495,32 @@ edit_redraw_page_cb (void *data, void *user_data) ((WEdit *) data)->force |= REDRAW_PAGE; } +/* --------------------------------------------------------------------------------------------- */ +/** + * Insert autocompleted word into editor. + * + * @param edit editor object + * @param completion word for completion + * @param word_len offset from begining 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 ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ @@ -3266,8 +3292,7 @@ edit_complete_word_cmd (WEdit * edit) { gsize i, max_len, word_len = 0, num_compl = 0; off_t word_start = 0; - unsigned char *bufpos; - char *match_expr; + GString *match_expr; GString *compl[MAX_WORD_COMPLETIONS]; /* completions */ /* search start of word to be completed */ @@ -3275,43 +3300,46 @@ edit_complete_word_cmd (WEdit * edit) return; /* prepare match expression */ - bufpos = &edit->buffers1[word_start >> S_EDIT_BUF_SIZE][word_start & M_EDIT_BUF_SIZE]; - /* match_expr = g_strdup_printf ("\\b%.*s[a-zA-Z_0-9]+", word_len, bufpos); */ - match_expr = - g_strdup_printf - ("(^|\\s+|\\b)%.*s[^\\s\\.=\\+\\[\\]\\(\\)\\,\\;\\:\\\"\\'\\-\\?\\/\\|\\\\\\{\\}\\*\\&\\^\\%%\\$#@\\!]+", - (int) word_len, bufpos); + match_expr = g_string_new ("(^|\\s+|\\b)"); + for (i = 0; i < word_len; i++) + g_string_append_c (match_expr, edit_get_byte (edit, word_start + i)); + g_string_append (match_expr, "[^\\s\\.=\\+\\[\\]\\(\\)\\,\\;\\:\\\"\\'\\-\\?\\/\\|\\\\\\{\\}\\*\\&\\^\\%%\\$#@\\!]+"); /* collect the possible completions */ /* start search from begin to end of file */ max_len = - edit_collect_completions (edit, word_start, word_len, match_expr, (GString **) & compl, + edit_collect_completions (edit, word_start, word_len, match_expr->str, (GString **) & compl, &num_compl); if (num_compl > 0) { /* insert completed word if there is only one match */ if (num_compl == 1) - { - for (i = word_len; i < compl[0]->len; i++) - edit_insert (edit, *(compl[0]->str + i)); - } + edit_complete_word_insert_recoded_completion (edit, compl[0]->str, word_len); /* more than one possible completion => ask the user */ else { + char *curr_compl; + /* !!! usually only a beep is expected and when is !!! */ /* !!! pressed again the selection dialog pops up, but that !!! */ /* !!! seems to require a further internal state !!! */ /*tty_beep (); */ /* let the user select the preferred completion */ - editcmd_dialog_completion_show (edit, max_len, word_len, (GString **) & compl, - num_compl); + curr_compl = editcmd_dialog_completion_show (edit, max_len, + (GString **) & compl, num_compl); + + if (curr_compl != NULL) + { + edit_complete_word_insert_recoded_completion (edit, curr_compl, word_len); + g_free (curr_compl); + } } } - g_free (match_expr); + g_string_free (match_expr, TRUE); /* release memory before return */ for (i = 0; i < num_compl; i++) g_string_free (compl[i], TRUE); @@ -3432,10 +3460,9 @@ edit_get_match_keyword_cmd (WEdit * edit) { gsize word_len = 0, max_len = 0; int num_def = 0; - int i; + gsize i; off_t word_start = 0; - unsigned char *bufpos; - char *match_expr; + GString *match_expr; char *path = NULL; char *ptr = NULL; char *tagfile = NULL; @@ -3452,8 +3479,9 @@ edit_get_match_keyword_cmd (WEdit * edit) return; /* prepare match expression */ - bufpos = &edit->buffers1[word_start >> S_EDIT_BUF_SIZE][word_start & M_EDIT_BUF_SIZE]; - match_expr = g_strdup_printf ("%.*s", (int) word_len, bufpos); + match_expr = g_string_sized_new (word_len); + for (i = 0; i < word_len; i++) + g_string_append_c (match_expr, edit_get_byte (edit, word_start + i)); ptr = g_get_current_dir (); path = g_strconcat (ptr, G_DIR_SEPARATOR_S, (char *) NULL); @@ -3475,7 +3503,7 @@ edit_get_match_keyword_cmd (WEdit * edit) if (tagfile) { num_def = - etags_set_definition_hash (tagfile, path, match_expr, (etags_hash_t *) & def_hash); + etags_set_definition_hash (tagfile, path, match_expr->str, (etags_hash_t *) & def_hash); g_free (tagfile); } g_free (path); @@ -3484,10 +3512,10 @@ edit_get_match_keyword_cmd (WEdit * edit) word_len = 0; if (num_def > 0) { - editcmd_dialog_select_definition_show (edit, match_expr, max_len, word_len, + editcmd_dialog_select_definition_show (edit, match_expr->str, max_len, word_len, (etags_hash_t *) & def_hash, num_def); } - g_free (match_expr); + g_string_free (match_expr, TRUE); } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/editor/editcmd_dialogs.c b/src/editor/editcmd_dialogs.c index 1443c07e1..559e4c357 100644 --- a/src/editor/editcmd_dialogs.c +++ b/src/editor/editcmd_dialogs.c @@ -336,11 +336,10 @@ editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean c /* --------------------------------------------------------------------------------------------- */ /* let the user select its preferred completion */ -void -editcmd_dialog_completion_show (WEdit * edit, int max_len, int word_len, - GString ** compl, int num_compl) +char * +editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** compl, int num_compl) { - + const Widget *we = WIDGET (edit); int start_x, start_y, offset, i; char *curr = NULL; WDialog *compl_dlg; @@ -351,12 +350,15 @@ editcmd_dialog_completion_show (WEdit * edit, int max_len, int word_len, /* calculate the dialog metrics */ compl_dlg_h = num_compl + 2; compl_dlg_w = max_len + 4; - start_x = edit->curs_col + edit->start_col - (compl_dlg_w / 2) + - EDIT_TEXT_HORIZONTAL_OFFSET + (edit->fullscreen ? 0 : 1) + option_line_state_width; - start_y = edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + (edit->fullscreen ? 0 : 1) + 1; + 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) @@ -367,7 +369,7 @@ editcmd_dialog_completion_show (WEdit * edit, int max_len, int word_len, start_x -= offset; offset = start_y + compl_dlg_h - LINES; if (offset > 0) - start_y -= (offset + 1); + start_y -= offset; /* create the dialog */ compl_dlg = @@ -388,35 +390,13 @@ editcmd_dialog_completion_show (WEdit * edit, int max_len, int word_len, if (run_dlg (compl_dlg) == B_ENTER) { listbox_get_current (compl_list, &curr, NULL); - if (curr) - { -#ifdef HAVE_CHARSET - GString *temp, *temp2; - temp = g_string_new (""); - for (curr += word_len; *curr; curr++) - g_string_append_c (temp, *curr); - - temp2 = str_convert_to_input (temp->str); - - if (temp2 && temp2->len) - { - g_string_free (temp, TRUE); - temp = temp2; - } - else - g_string_free (temp2, TRUE); - for (curr = temp->str; *curr; curr++) - edit_insert (edit, *curr); - g_string_free (temp, TRUE); -#else - for (curr += word_len; *curr; curr++) - edit_insert (edit, *curr); -#endif - } + curr = g_strdup (curr); } /* destroy dialog before return */ destroy_dlg (compl_dlg); + + return curr; } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/editor/editcmd_dialogs.h b/src/editor/editcmd_dialogs.h index 24b9008fd..f691c857e 100644 --- a/src/editor/editcmd_dialogs.h +++ b/src/editor/editcmd_dialogs.h @@ -25,7 +25,8 @@ gboolean editcmd_dialog_search_show (WEdit * edit); int editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean cancel); -void editcmd_dialog_completion_show (WEdit *, int, int, GString **, int); +char *editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** compl, + int num_compl); void editcmd_dialog_select_definition_show (WEdit *, char *, int, int, struct etags_hash_struct *, int); diff --git a/tests/src/Makefile.am b/tests/src/Makefile.am index d42885062..92b045c30 100644 --- a/tests/src/Makefile.am +++ b/tests/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . filemanager +SUBDIRS = . editor filemanager AM_CPPFLAGS = \ $(GLIB_CFLAGS) \ diff --git a/tests/src/editor/Makefile.am b/tests/src/editor/Makefile.am new file mode 100644 index 000000000..c0ee705c2 --- /dev/null +++ b/tests/src/editor/Makefile.am @@ -0,0 +1,27 @@ +AM_CPPFLAGS = \ + -DTEST_SHARE_DIR=\"$(abs_srcdir)\" \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir) \ + @CHECK_CFLAGS@ + +AM_LDFLAGS = @TESTS_LDFLAGS@ + +LIBS=@CHECK_LIBS@ \ + $(top_builddir)/src/libinternal.la \ + $(top_builddir)/lib/libmc.la + +if ENABLE_VFS_SMB +# this is a hack for linking with own samba library in simple way +LIBS += $(top_builddir)/src/vfs/smbfs/helpers/libsamba.a +endif + +EXTRA_DIST = mc.charsets test-data.txt.in + +TESTS = \ + editcmd__edit_complete_word_cmd + +check_PROGRAMS = $(TESTS) + +editcmd__edit_complete_word_cmd_SOURCES = \ + editcmd__edit_complete_word_cmd.c + diff --git a/tests/src/editor/editcmd__edit_complete_word_cmd.c b/tests/src/editor/editcmd__edit_complete_word_cmd.c new file mode 100644 index 000000000..2efbf7565 --- /dev/null +++ b/tests/src/editor/editcmd__edit_complete_word_cmd.c @@ -0,0 +1,384 @@ +/* + src/editor - tests for edit_complete_word_cmd() function + + Copyright (C) 2013 + The Free Software Foundation, Inc. + + Written by: + Slava Zanko , 2013 + + 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 . + */ + +#define TEST_SUITE_NAME "/src/editor" + +#include "tests/mctest.h" + +#include + +#ifdef HAVE_CHARSET +#include "lib/charsets.h" +#endif +#include "lib/strutil.h" + +#include "src/vfs/local/local.c" +#ifdef HAVE_CHARSET +#include "src/selcodepage.h" +#endif +#include "src/editor/editwidget.h" +#include "src/editor/editcmd_dialogs.h" + + +static WEdit *test_edit; + +/* --------------------------------------------------------------------------------------------- */ +/* @Mock */ +void +edit_load_syntax (WEdit * _edit, char ***_pnames, const char *_type) +{ + (void) _edit; + (void) _pnames; + (void) _type; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @Mock */ +int +edit_get_syntax_color (WEdit * _edit, off_t _byte_index) +{ + (void) _edit; + (void) _byte_index; + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @Mock */ +gboolean +edit_load_macro_cmd (WEdit * _edit) +{ + (void) _edit; + + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @CapturedValue */ +static const WEdit *editcmd_dialog_completion_show__edit; +/* @CapturedValue */ +static int editcmd_dialog_completion_show__max_len; +/* @CapturedValue */ +static GString **editcmd_dialog_completion_show__compl; +/* @CapturedValue */ +static int editcmd_dialog_completion_show__num_compl; + +/* @ThenReturnValue */ +static char *editcmd_dialog_completion_show__return_value; + +/* @Mock */ +char * +editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** compl, int num_compl) +{ + + editcmd_dialog_completion_show__edit = edit; + editcmd_dialog_completion_show__max_len = max_len; + editcmd_dialog_completion_show__num_compl = num_compl; + + { + int iterator; + + editcmd_dialog_completion_show__compl = g_new0 (GString *, num_compl); + + for (iterator = 0; iterator < editcmd_dialog_completion_show__num_compl; iterator++) + editcmd_dialog_completion_show__compl[iterator] = + g_string_new_len (compl[iterator]->str, compl[iterator]->len); + } + + return editcmd_dialog_completion_show__return_value; +} + +static void +editcmd_dialog_completion_show__init (void) +{ + editcmd_dialog_completion_show__edit = NULL; + editcmd_dialog_completion_show__max_len = 0; + editcmd_dialog_completion_show__compl = NULL; + editcmd_dialog_completion_show__num_compl = 0; + editcmd_dialog_completion_show__return_value = NULL; +} + +static void +editcmd_dialog_completion_show__deinit (void) +{ + if (editcmd_dialog_completion_show__compl != NULL) + { + int iterator; + + for (iterator = 0; iterator < editcmd_dialog_completion_show__num_compl; iterator++) + g_string_free (editcmd_dialog_completion_show__compl[iterator], TRUE); + + g_free (editcmd_dialog_completion_show__compl); + } +} + + +/* --------------------------------------------------------------------------------------------- */ + +/* @Before */ +static void +my_setup (void) +{ + str_init_strings (NULL); + + vfs_init (); + init_localfs (); + vfs_setup_work_dir (); + +#ifdef HAVE_CHARSET + mc_global.sysconfig_dir = (char *) TEST_SHARE_DIR; + load_codepages_list (); +#endif /* HAVE_CHARSET */ + + test_edit = edit_init (NULL, 0, 0, 24, 80, vfs_path_from_str ("test-data.txt"), 1); + editcmd_dialog_completion_show__init (); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @After */ +static void +my_teardown (void) +{ + editcmd_dialog_completion_show__deinit (); + edit_clean (test_edit); + g_free (test_edit); + +#ifdef HAVE_CHARSET + free_codepages_list (); +#endif /* HAVE_CHARSET */ + + vfs_shut (); + + str_uninit_strings (); +} + +/* --------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_CHARSET +/* @DataSource("test_autocomplete_ds") */ +/* *INDENT-OFF* */ +static const struct test_autocomplete_ds +{ + off_t input_position; + const char *input_system_code_page; + int input_source_codepage_id; + const char *input_editor_code_page; + int input_display_codepage_id; + const char *input_completed_word; + + int expected_max_len; + int expected_compl_word_count; + int input_completed_word_start_pos; + const char *expected_completed_word; +} test_autocomplete_ds[] = +{ + { /* 0. */ + 111, + "KOI8-R", + 0, + "UTF-8", + 1, + "эъйцукен", + + 16, + 2, + 107, + "эъйцукен" + }, + { /* 1. */ + 147, + "UTF-8", + 1, + "KOI8-R", + 0, + "", + + 8, + 2, + 145, + "" + }, +}; +/* *INDENT-ON* */ + +/* @Test(dataSource = "test_autocomplete_ds") */ +/* *INDENT-OFF* */ +START_PARAMETRIZED_TEST (test_autocomplete, test_autocomplete_ds) +/* *INDENT-ON* */ +{ + /* given */ + editcmd_dialog_completion_show__return_value = g_strdup (data->input_completed_word); + + + mc_global.source_codepage = data->input_source_codepage_id; + mc_global.display_codepage = data->input_display_codepage_id; + cp_source = data->input_editor_code_page; + cp_display = data->input_system_code_page; + + do_set_codepage (0); + edit_set_codeset (test_edit); + + /* when */ + edit_cursor_move (test_edit, data->input_position); + edit_complete_word_cmd (test_edit); + + /* then */ + mctest_assert_ptr_eq (editcmd_dialog_completion_show__edit, test_edit); + mctest_assert_int_eq (editcmd_dialog_completion_show__num_compl, + data->expected_compl_word_count); + mctest_assert_int_eq (editcmd_dialog_completion_show__max_len, data->expected_max_len); + + { + off_t i = 0; + GString *actual_completed_str; + + actual_completed_str = g_string_new (""); + + while (TRUE) + { + int chr; + + chr = edit_get_byte (test_edit, data->input_completed_word_start_pos + i++); + if (isspace (chr)) + break; + g_string_append_c (actual_completed_str, chr); + } + mctest_assert_str_eq (actual_completed_str->str, data->expected_completed_word); + g_string_free (actual_completed_str, TRUE); + } +} +/* *INDENT-OFF* */ +END_PARAMETRIZED_TEST +/* *INDENT-ON* */ + +/* --------------------------------------------------------------------------------------------- */ + +/* @DataSource("test_autocomplete_single_ds") */ +/* *INDENT-OFF* */ +static const struct test_autocomplete_single_ds +{ + off_t input_position; + const char *input_system_code_page; + int input_source_codepage_id; + const char *input_editor_code_page; + int input_display_codepage_id; + + int input_completed_word_start_pos; + + const char *expected_completed_word; +} test_autocomplete_single_ds[] = +{ + { /* 0. */ + 155, + "UTF-8", + 1, + "KOI8-R", + 0, + + 154, + "" + }, +}; +/* *INDENT-ON* */ + +/* @Test(dataSource = "test_autocomplete_single_ds") */ +/* *INDENT-OFF* */ +START_PARAMETRIZED_TEST (test_autocomplete_single, test_autocomplete_single_ds) +/* *INDENT-ON* */ +{ + /* given */ + mc_global.source_codepage = data->input_source_codepage_id; + mc_global.display_codepage = data->input_display_codepage_id; + cp_source = data->input_editor_code_page; + cp_display = data->input_system_code_page; + + do_set_codepage (0); + edit_set_codeset (test_edit); + + /* when */ + edit_cursor_move (test_edit, data->input_position); + edit_complete_word_cmd (test_edit); + + /* then */ + { + off_t i = 0; + GString *actual_completed_str; + + actual_completed_str = g_string_new (""); + + while (TRUE) + { + int chr; + + chr = edit_get_byte (test_edit, data->input_completed_word_start_pos + i++); + if (isspace (chr)) + break; + g_string_append_c (actual_completed_str, chr); + } + mctest_assert_str_eq (actual_completed_str->str, data->expected_completed_word); + g_string_free (actual_completed_str, TRUE); + } +} +/* *INDENT-OFF* */ +END_PARAMETRIZED_TEST +/* *INDENT-ON* */ + + +#endif /* HAVE_CHARSET */ + +/* --------------------------------------------------------------------------------------------- */ + +int +main (void) +{ + int number_failed; + + Suite *s = suite_create (TEST_SUITE_NAME); + TCase *tc_core = tcase_create ("Core"); + SRunner *sr; + + tcase_add_checked_fixture (tc_core, my_setup, my_teardown); + + /* Add new tests here: *************** */ +#ifdef HAVE_CHARSET + mctest_add_parameterized_test (tc_core, test_autocomplete, test_autocomplete_ds); + mctest_add_parameterized_test (tc_core, test_autocomplete_single, test_autocomplete_single_ds); +#endif /* HAVE_CHARSET */ + /* *********************************** */ + + suite_add_tcase (s, tc_core); + sr = srunner_create (s); + srunner_set_log (sr, "edit_complete_word_cmd.log"); + srunner_run_all (sr, CK_NORMAL); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? 0 : 1; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/tests/src/editor/mc.charsets b/tests/src/editor/mc.charsets new file mode 100644 index 000000000..7b27d6182 --- /dev/null +++ b/tests/src/editor/mc.charsets @@ -0,0 +1,2 @@ +KOI8-R KOI8-R +UTF-8 UTF-8 diff --git a/tests/src/editor/test-data.txt.in b/tests/src/editor/test-data.txt.in new file mode 100644 index 000000000..10fcfb19e --- /dev/null +++ b/tests/src/editor/test-data.txt.in @@ -0,0 +1,20 @@ +****************** START:editcmd__edit_complete_word_cmd.c + +--- UTF-8: + +эъйцукен +эъфывапр + +эъ + +--- KOI8-R: + + + + + + + + + +****************** END:editcmd__edit_complete_word_cmd.c