From 6bd0d56e132512829fac60bbd487a0aed6188dcf Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 19 Apr 2015 15:00:10 +0100 Subject: [PATCH] elm_code: add multiline paste support. Creating and breaking out a few helper text methods at the same time. Test all that we can for this reasonably complex operation --- elm_code/src/lib/elm_code_line.c | 18 ++++++ elm_code/src/lib/elm_code_line.h | 14 ++++- elm_code/src/lib/elm_code_text.c | 42 +++++++++++-- elm_code/src/lib/elm_code_text.h | 4 ++ elm_code/src/lib/widget/elm_code_widget.c | 15 +---- .../lib/widget/elm_code_widget_selection.c | 59 ++++++++++++++++--- elm_code/src/tests/elm_code_test_line.c | 23 ++++++++ elm_code/src/tests/elm_code_test_text.c | 11 ++++ 8 files changed, 159 insertions(+), 27 deletions(-) diff --git a/elm_code/src/lib/elm_code_line.c b/elm_code/src/lib/elm_code_line.c index 5195972..f9d3b08 100644 --- a/elm_code/src/lib/elm_code_line.c +++ b/elm_code/src/lib/elm_code_line.c @@ -20,6 +20,24 @@ elm_code_line_free(Elm_Code_Line *line) free(line); } +EAPI void elm_code_line_split_at(Elm_Code_Line *line, unsigned int position) +{ + Elm_Code_Line *newline; + char *content; + unsigned int length; + + content = (char *) elm_code_line_text_get(line, &length); + content = strndup(content, length); + elm_code_file_line_insert(line->file, line->number + 1, "", 0, NULL); + newline = elm_code_file_line_get(line->file, line->number + 1); +// TODO we need to split tokens from these lines + + elm_code_line_text_set(newline, content + position, length - position); + elm_code_line_text_set(line, content, position); + + free(content); +} + EAPI void elm_code_line_status_set(Elm_Code_Line *line, Elm_Code_Status_Type status) { if (!line) diff --git a/elm_code/src/lib/elm_code_line.h b/elm_code/src/lib/elm_code_line.h index e3ad128..6a0f767 100644 --- a/elm_code/src/lib/elm_code_line.h +++ b/elm_code/src/lib/elm_code_line.h @@ -38,8 +38,20 @@ typedef struct _Elm_Code_Line EAPI void elm_code_line_free(Elm_Code_Line *line); /** + * @brief Line manipulation functions. + * @defgroup Content + * @{ + * + * Functions for changing the content of lines in an Elm_Code_File + */ + +EAPI void elm_code_line_split_at(Elm_Code_Line *line, unsigned int position); + +/** + * @} + * * @brief Line markup functions. - * @defgroup Line highlighting and status manipulation + * @defgroup Highlighting * * @{ * diff --git a/elm_code/src/lib/elm_code_text.c b/elm_code/src/lib/elm_code_text.c index 206a6cf..8b22f3a 100644 --- a/elm_code/src/lib/elm_code_text.c +++ b/elm_code/src/lib/elm_code_text.c @@ -42,14 +42,12 @@ elm_code_line_text_set(Elm_Code_Line *line, const char *chars, unsigned int leng } EAPI int -elm_code_line_text_strpos(Elm_Code_Line *line, const char *search, int offset) +elm_code_text_strnpos(const char *content, unsigned int length, const char *search, int offset) { - unsigned int length, searchlen, c; - const char *content; + unsigned int searchlen, c; char *ptr; searchlen = strlen(search); - content = elm_code_line_text_get(line, &length); ptr = (char *) content; if (searchlen > length) @@ -67,6 +65,16 @@ elm_code_line_text_strpos(Elm_Code_Line *line, const char *search, int offset) return ELM_CODE_TEXT_NOT_FOUND; } +EAPI int +elm_code_line_text_strpos(Elm_Code_Line *line, const char *search, int offset) +{ + unsigned int length; + const char *content; + + content = elm_code_line_text_get(line, &length); + return elm_code_text_strnpos(content, length, search, offset); +} + EAPI Eina_Bool elm_code_line_text_contains(Elm_Code_Line *line, const char *search) { @@ -201,12 +209,36 @@ elm_code_line_text_remove(Elm_Code_Line *line, unsigned int position, int length /* generic text functions */ -unsigned int +EAPI unsigned int elm_code_text_tabwidth_at_position(unsigned int position, unsigned int tabstop) { return tabstop - (position % tabstop); } +EAPI int +elm_code_text_newlinenpos(const char *text, unsigned int length) +{ + int lfpos, crpos; + int check; + + lfpos = elm_code_text_strnpos(text, length, "\n", 0); + check = length; + if (lfpos != ELM_CODE_TEXT_NOT_FOUND) + check = lfpos; + crpos = elm_code_text_strnpos(text, check, "\r", 0); + + if (lfpos == ELM_CODE_TEXT_NOT_FOUND && crpos == ELM_CODE_TEXT_NOT_FOUND) + return ELM_CODE_TEXT_NOT_FOUND; + + if (crpos == ELM_CODE_TEXT_NOT_FOUND) + return lfpos; + if (lfpos == ELM_CODE_TEXT_NOT_FOUND) + return crpos; + if (lfpos < crpos) + return lfpos; + return crpos; +} + EAPI unsigned int elm_code_line_text_column_width_to_position(Elm_Code_Line *line, unsigned int position, unsigned int tabstop) { diff --git a/elm_code/src/lib/elm_code_text.h b/elm_code/src/lib/elm_code_text.h index 17a47eb..15b236e 100644 --- a/elm_code/src/lib/elm_code_text.h +++ b/elm_code/src/lib/elm_code_text.h @@ -50,6 +50,10 @@ EAPI void elm_code_line_text_remove(Elm_Code_Line *line, unsigned int position, EAPI unsigned int elm_code_text_tabwidth_at_position(unsigned int position, unsigned int tabstop); +EAPI int elm_code_text_strnpos(const char *text, unsigned int length, const char *search, int offset); + +EAPI int elm_code_text_newlinenpos(const char *text, unsigned int length); + EAPI unsigned int elm_code_line_text_column_width_to_position(Elm_Code_Line *line, unsigned int length, unsigned int tabstop); EAPI unsigned int elm_code_line_text_column_width(Elm_Code_Line *line, unsigned int tabstop); diff --git a/elm_code/src/lib/widget/elm_code_widget.c b/elm_code/src/lib/widget/elm_code_widget.c index f641906..4c1edc4 100644 --- a/elm_code/src/lib/widget/elm_code_widget.c +++ b/elm_code/src/lib/widget/elm_code_widget.c @@ -859,10 +859,9 @@ static void _elm_code_widget_newline(Elm_Code_Widget *widget) { Elm_Code *code; - Elm_Code_Line *line, *newline; + Elm_Code_Line *line; Elm_Code_Widget_Data *pd; - unsigned int row, col, length, position; - char *content; + unsigned int row, col, position; pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS); _elm_code_widget_delete_selection(widget); @@ -871,17 +870,9 @@ _elm_code_widget_newline(Elm_Code_Widget *widget) elm_code_widget_cursor_position_get(&col, &row)); line = elm_code_file_line_get(code->file, row); - content = (char *) elm_code_line_text_get(line, &length); - content = strndup(content, length); - elm_code_file_line_insert(code->file, line->number + 1, "", 0, NULL); - newline = elm_code_file_line_get(code->file, line->number + 1); -// TODO we need to split tokens from these lines (move this to elm_code_line?) - position = elm_code_line_text_position_for_column_get(line, col - 1, pd->tabstop); - elm_code_line_text_set(newline, content + position, length - position); - elm_code_line_text_set(line, content, position); + elm_code_line_split_at(line, position); - free(content); eo_do(widget, elm_code_widget_cursor_position_set(1, row + 1), // TODO construct and pass a change object diff --git a/elm_code/src/lib/widget/elm_code_widget_selection.c b/elm_code/src/lib/widget/elm_code_widget_selection.c index a7a5c01..473d999 100644 --- a/elm_code/src/lib/widget/elm_code_widget_selection.c +++ b/elm_code/src/lib/widget/elm_code_widget_selection.c @@ -287,14 +287,58 @@ elm_code_widget_selection_copy(Evas_Object *widget) free(text); } +static void +_selection_paste_single(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd, Elm_Code *code, + unsigned int col, unsigned int row, const char *text, unsigned int len) +{ + Elm_Code_Line *line; + unsigned int position, newcol; + + line = elm_code_file_line_get(code->file, row); + position = elm_code_line_text_position_for_column_get(line, col - 1, pd->tabstop); + elm_code_line_text_insert(line, position + 1, text, len); + + newcol = elm_code_line_text_column_width_to_position(line, position + len, pd->tabstop); + eo_do(widget, + elm_code_widget_cursor_position_set(newcol + 1, row)); +} + +static void +_selection_paste_multi(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd, Elm_Code *code, + unsigned int col, unsigned int row, const char *text, unsigned int len) +{ + Elm_Code_Line *line; + unsigned int position, newrow; + int nlpos; + char *ptr; + + line = elm_code_file_line_get(code->file, row); + position = elm_code_line_text_position_for_column_get(line, col - 1, pd->tabstop); + elm_code_line_split_at(line, position); + + newrow = row; + ptr = (char *)text; + while ((nlpos = elm_code_text_newlinenpos(ptr, len)) != ELM_CODE_TEXT_NOT_FOUND) + { + if (newrow == row) + _selection_paste_single(widget, pd, code, col, row, text, nlpos); + else + elm_code_file_line_insert(code->file, newrow, ptr, nlpos, NULL); + + ptr += nlpos + 1; // TODO make this adapt to windows lengths (length param to newlinenpos) + newrow++; + } + + _selection_paste_single(widget, pd, code, 1, newrow, ptr, len - (ptr - text)); +} + static Eina_Bool _selection_paste_cb(void *data, Evas_Object *obj EINA_UNUSED, Elm_Selection_Data *ev) { Elm_Code *code; Elm_Code_Widget *widget; - Elm_Code_Line *line; Elm_Code_Widget_Data *pd; - unsigned int row, col, col_width, position; + unsigned int row, col; widget = (Elm_Code_Widget *)data; pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS); @@ -308,15 +352,12 @@ _selection_paste_cb(void *data, Evas_Object *obj EINA_UNUSED, Elm_Selection_Data eo_do(widget, code = elm_code_widget_code_get(), elm_code_widget_cursor_position_get(&col, &row)); - line = elm_code_file_line_get(code->file, row); - position = elm_code_line_text_position_for_column_get(line, col - 1, pd->tabstop); - elm_code_line_text_insert(line, position + 1, ev->data, ev->len - 1); + if (elm_code_text_newlinenpos(ev->data, ev->len) == ELM_CODE_TEXT_NOT_FOUND) + _selection_paste_single(widget, pd, code, col, row, ev->data, ev->len - 1); + else + _selection_paste_multi(widget, pd, code, col, row, ev->data, ev->len - 1); - col_width = elm_code_line_text_column_width_to_position(line, position + ev->len - 1, pd->tabstop) - - elm_code_line_text_column_width_to_position(line, position, pd->tabstop); - eo_do(widget, - elm_code_widget_cursor_position_set(col + col_width, row)); return EINA_TRUE; } diff --git a/elm_code/src/tests/elm_code_test_line.c b/elm_code/src/tests/elm_code_test_line.c index 5d26897..b19bf61 100644 --- a/elm_code/src/tests/elm_code_test_line.c +++ b/elm_code/src/tests/elm_code_test_line.c @@ -44,9 +44,32 @@ START_TEST (elm_code_line_token_count_test) } END_TEST +START_TEST (elm_code_line_split_test) +{ + Elm_Code *code; + Elm_Code_File *file; + Elm_Code_Line *line, *newline; + + code = elm_code_create(); + file = elm_code_file_new(code); + + elm_code_file_line_append(file, "line1line2", 10, NULL); + line = elm_code_file_line_get(file, 1); + ck_assert_int_eq(1, elm_code_file_lines_get(file)); + ck_assert_int_eq(10, line->length); + + elm_code_line_split_at(line, 5); + ck_assert_int_eq(2, elm_code_file_lines_get(file)); + newline = elm_code_file_line_get(file, 2); + ck_assert_int_eq(5, line->length); + ck_assert_int_eq(5, newline->length); +} +END_TEST + void elm_code_test_line(TCase *tc) { tcase_add_test(tc, elm_code_line_create_test); tcase_add_test(tc, elm_code_line_token_count_test); + tcase_add_test(tc, elm_code_line_split_test); } diff --git a/elm_code/src/tests/elm_code_test_text.c b/elm_code/src/tests/elm_code_test_text.c index f017835..1798433 100644 --- a/elm_code/src/tests/elm_code_test_text.c +++ b/elm_code/src/tests/elm_code_test_text.c @@ -81,10 +81,21 @@ START_TEST (elm_code_text_strpos_test) } END_TEST +START_TEST (elm_code_text_newline_position_test) +{ + const char *unixtext = "a test\nwith newline"; + const char *wintext = "a windows\r\nnewline"; + + ck_assert_int_eq(6, elm_code_text_newlinenpos(unixtext, strlen(unixtext))); + ck_assert_int_eq(9, elm_code_text_newlinenpos(wintext, strlen(wintext))); +} +END_TEST + void elm_code_test_text(TCase *tc) { tcase_add_test(tc, elm_code_text_get_test); tcase_add_test(tc, elm_code_text_insert_test); tcase_add_test(tc, elm_code_text_contains_test); tcase_add_test(tc, elm_code_text_strpos_test); + tcase_add_test(tc, elm_code_text_newline_position_test); }