ajwillia-ms pushed a commit to branch master. http://git.enlightenment.org/tools/edi.git/commit/?id=9d128bdb40e02cd172afb0203ce3aa22cc62e3e0
commit 9d128bdb40e02cd172afb0203ce3aa22cc62e3e0 Author: YeongJong Lee <clean...@naver.com> Date: Tue Dec 13 07:42:36 2016 +0000 autocomplete: Refresh candidate list when typing. Summary: if you start typing when candidate list is shown, the list refresh immediately. Test Plan: 1. Run edi. 2. Open project. 3. Open candidate list using <ctrl> + <space>. 4. Continue typing. 5. Check the candidate list is correct. Reviewers: bu5hm4n, ajwillia.ms Differential Revision: https://phab.enlightenment.org/D4478 --- src/bin/editor/edi_editor.c | 376 ++++++++++++++++++++++++++++++++------------ 1 file changed, 275 insertions(+), 101 deletions(-) diff --git a/src/bin/editor/edi_editor.c b/src/bin/editor/edi_editor.c index 6282b17..8948081 100644 --- a/src/bin/editor/edi_editor.c +++ b/src/bin/editor/edi_editor.c @@ -26,7 +26,7 @@ typedef struct Edi_Location end; } Edi_Range; -static Evas_Object *_clang_autocomplete_popup_bg; +static Evas_Object *_clang_autocomplete_popup_bg, *_clang_autocomplete_popup_genlist; void edi_editor_save(Edi_Editor *editor) @@ -68,14 +68,12 @@ _changed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUS #if HAVE_LIBCLANG static char* -_edi_editor_current_word_get(Edi_Editor *editor) +_edi_editor_current_word_get(Edi_Editor *editor, unsigned int row, unsigned int col) { Elm_Code *code; Elm_Code_Line *line; char *ptr, *curword, *curtext; - unsigned int curlen, col, row, wordlen; - - elm_obj_code_widget_cursor_position_get(editor->entry, &row, &col); + unsigned int curlen, wordlen; code = elm_code_widget_code_get(editor->entry); line = elm_code_file_line_get(code->file, row); @@ -98,62 +96,6 @@ _edi_editor_current_word_get(Edi_Editor *editor) return curword; } -static void -_autocomplete_list_cb_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED, - Evas_Object *obj, void *event_info) -{ - Edi_Mainview_Item *item; - Edi_Editor *editor; - Elm_Code *code; - Elm_Code_Line *line; - Evas_Event_Key_Down *ev = event_info; - char *word; - const char *list_word; - unsigned int wordlen, col, row; - - item = edi_mainview_item_current_get(); - - if (!item) - return; - - editor = (Edi_Editor *)evas_object_data_get(item->view, "editor"); - - elm_code_widget_cursor_position_get(editor->entry, &row, &col); - - code = elm_code_widget_code_get(editor->entry); - line = elm_code_file_line_get(code->file, row); - - if (!strcmp(ev->key, "Down") || !strcmp(ev->key, "Up")) - return; - else if (!strcmp(ev->key, "Return")) - { - Elm_Object_Item *it; - - word = _edi_editor_current_word_get(editor); - wordlen = strlen(word); - free(word); - - elm_code_line_text_remove(line, col - wordlen - 1, wordlen); - it = elm_genlist_selected_item_get(obj); - list_word = elm_object_item_data_get(it); - - elm_code_line_text_insert(line, col - wordlen - 1, - list_word, strlen(list_word)); - elm_code_widget_cursor_position_set(editor->entry, row, - col - wordlen + strlen(list_word)); - } - - evas_object_del(_clang_autocomplete_popup_bg); -} - -static void -_autocomplete_list_cb_focus(void *data EINA_UNUSED, - Evas_Object *obj EINA_UNUSED, void *event_info) -{ - Elm_Object_Item *it = event_info; - elm_genlist_item_selected_set(it, EINA_TRUE); -} - static Evas_Object * _autocomplete_list_content_get(void *data, Evas_Object *obj, const char *part) { @@ -192,14 +134,45 @@ _autocomplete_list_content_get(void *data, Evas_Object *obj, const char *part) } static void -_autocomplete_list_del(void *data, Evas_Object *obj EINA_UNUSED) +_autocomplete_list_update(char *word) { - char *auto_str = data; - free(auto_str); + Eina_List *list, *l; + Elm_Genlist_Item_Class *ic; + Elm_Object_Item *item; + char *auto_str; + + elm_genlist_clear(_clang_autocomplete_popup_genlist); + + list = (Eina_List *)evas_object_data_get(_clang_autocomplete_popup_genlist, + "autocomplete_list"); + ic = elm_genlist_item_class_new(); + ic->item_style = "full"; + ic->func.content_get = _autocomplete_list_content_get; + + EINA_LIST_FOREACH(list, l, auto_str) + { + if (eina_str_has_prefix(auto_str, word)) + { + elm_genlist_item_append(_clang_autocomplete_popup_genlist, + ic, + auto_str, + NULL, + ELM_GENLIST_ITEM_NONE, + NULL, + NULL); + } + } + elm_genlist_item_class_free(ic); + + item = elm_genlist_first_item_get(_clang_autocomplete_popup_genlist); + if (item) + elm_genlist_item_selected_set(item, EINA_TRUE); + else + evas_object_hide(_clang_autocomplete_popup_bg); } static void -_autocomplete_list_update(Evas_Object *genlist, Edi_Editor *editor) +_autocomplete_list_set(Edi_Editor *editor) { Elm_Code *code; CXIndex idx; @@ -208,19 +181,27 @@ _autocomplete_list_update(Evas_Object *genlist, Edi_Editor *editor) char *curword, **clang_argv; const char *path, *args; unsigned int clang_argc, row, col; + Eina_List *list = NULL; + + list = (Eina_List *)evas_object_data_get(_clang_autocomplete_popup_genlist, + "autocomplete_list"); + if (list) + { + char *temp_str; + EINA_LIST_FREE(list, temp_str) + free(temp_str); + list = NULL; + evas_object_data_del(_clang_autocomplete_popup_genlist, + "autocomplete_list"); + } - elm_obj_code_widget_cursor_position_get(editor->entry, &row, &col); + edi_editor_save(editor); + elm_code_widget_cursor_position_get(editor->entry, &row, &col); code = elm_code_widget_code_get(editor->entry); path = elm_code_file_path_get(code->file); - curword = _edi_editor_current_word_get(editor); - - //Genlist Item Class - Elm_Genlist_Item_Class *ic = elm_genlist_item_class_new(); - ic->item_style = "full"; - ic->func.content_get = _autocomplete_list_content_get; - ic->func.del = _autocomplete_list_del; + curword = _edi_editor_current_word_get(editor, row, col); //Initialize Clang args = "-I/usr/inclue/ " EFL_CFLAGS " " CLANG_INCLUDES " -Wall -Wextra"; @@ -251,64 +232,249 @@ _autocomplete_list_update(Evas_Object *genlist, Edi_Editor *editor) const CXString str_out = clang_getCompletionChunkText(str, j); char *auto_str = strdup(clang_getCString(str_out)); - if (eina_str_has_prefix(auto_str, curword)) - { - elm_genlist_item_append(genlist, - ic, - auto_str, - NULL, - ELM_GENLIST_ITEM_NONE, - NULL, - NULL); - } + list = eina_list_append(list, auto_str); } } - elm_genlist_item_class_free(ic); clang_disposeCodeCompleteResults(res); clang_disposeTranslationUnit(tx_unit); clang_disposeIndex(idx); + + evas_object_data_set(_clang_autocomplete_popup_genlist, + "autocomplete_list", list); + _autocomplete_list_update(curword); free(curword); } static void -_clang_autocomplete_popup(Edi_Editor *editor) +_autocomplete_bg_cb_hide(void *data EINA_UNUSED, Evas *e EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Eina_List *list = NULL; + + list = (Eina_List *)evas_object_data_get(_clang_autocomplete_popup_genlist, + "autocomplete_list"); + if (list) + { + char *temp_str; + EINA_LIST_FREE(list, temp_str) + free(temp_str); + list = NULL; + evas_object_data_del(_clang_autocomplete_popup_genlist, + "autocomplete_list"); + } + evas_object_key_ungrab(_clang_autocomplete_popup_genlist, "Return", 0, 0); + evas_object_key_ungrab(_clang_autocomplete_popup_genlist, "Up", 0, 0); + evas_object_key_ungrab(_clang_autocomplete_popup_genlist, "Down", 0, 0); +} + +static void +_autocomplete_list_cb_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED, + Evas_Object *obj, void *event_info) +{ + Edi_Mainview_Item *m_it; + Elm_Object_Item *it; + Edi_Editor *editor; + Evas_Object *genlist = obj; + Elm_Code *code; + Elm_Code_Line *line; + Evas_Event_Key_Down *ev = event_info; + char *word; + const char *list_word; + unsigned int wordlen, col, row; + + m_it = edi_mainview_item_current_get(); + + if (!m_it) + return; + + editor = (Edi_Editor *)evas_object_data_get(m_it->view, "editor"); + + elm_code_widget_cursor_position_get(editor->entry, &row, &col); + + code = elm_code_widget_code_get(editor->entry); + line = elm_code_file_line_get(code->file, row); + + if (!strcmp(ev->key, "Return")) + { + word = _edi_editor_current_word_get(editor, row, col); + wordlen = strlen(word); + free(word); + + elm_code_line_text_remove(line, col - wordlen - 1, wordlen); + it = elm_genlist_selected_item_get(genlist); + list_word = elm_object_item_data_get(it); + + elm_code_line_text_insert(line, col - wordlen - 1, + list_word, strlen(list_word)); + elm_code_widget_cursor_position_set(editor->entry, row, + col - wordlen + strlen(list_word)); + evas_object_hide(_clang_autocomplete_popup_bg); + } + else if (!strcmp(ev->key, "Up")) + { + it = elm_genlist_item_prev_get(elm_genlist_selected_item_get(genlist)); + if(!it) it = elm_genlist_last_item_get(genlist); + + elm_genlist_item_selected_set(it, EINA_TRUE); + elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_IN); + } + else if (!strcmp(ev->key, "Down")) + { + it = elm_genlist_item_next_get(elm_genlist_selected_item_get(genlist)); + if(!it) it = elm_genlist_first_item_get(genlist); + + elm_genlist_item_selected_set(it, EINA_TRUE); + elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_IN); + } +} + +static void +_autocomplete_list_cb_focus(void *data EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, void *event_info) +{ + Elm_Object_Item *it = event_info; + elm_genlist_item_selected_set(it, EINA_TRUE); +} + +static void +_clang_autocomplete_popup_show(Edi_Editor *editor) { unsigned int col, row; Evas_Coord cx, cy, cw, ch; - elm_obj_code_widget_cursor_position_get(editor->entry, &row, &col); + if (elm_genlist_items_count(_clang_autocomplete_popup_genlist) <= 0) + return; + + elm_code_widget_cursor_position_get(editor->entry, &row, &col); elm_code_widget_geometry_for_position_get(editor->entry, row, col, &cx, &cy, &cw, &ch); - edi_editor_save(editor); + evas_object_move(_clang_autocomplete_popup_bg, cx, cy); + evas_object_show(_clang_autocomplete_popup_bg); + + if (!evas_object_key_grab(_clang_autocomplete_popup_genlist, "Return", 0, 0, + EINA_TRUE)) + ERR("Failed to grab key - %s", "Return"); + if (!evas_object_key_grab(_clang_autocomplete_popup_genlist, "Up", 0, 0, + EINA_TRUE)) + ERR("Failed to grab key - %s", "Up"); + if (!evas_object_key_grab(_clang_autocomplete_popup_genlist, "Down", 0, 0, + EINA_TRUE)) + ERR("Failed to grab key - %s", "Down"); +} + +static void +_clang_autocomplete_popup_key_down_cb(Edi_Editor *editor, const char *key, + const char *string) +{ + Elm_Code *code; + Elm_Code_Line *line; + unsigned int col, row; + char *word; + + if (!evas_object_visible_get(_clang_autocomplete_popup_bg)) + return; + + elm_code_widget_cursor_position_get(editor->entry, &row, &col); + + code = elm_code_widget_code_get(editor->entry); + line = elm_code_file_line_get(code->file, row); + + if (!strcmp(key, "Left")) + { + if (col - 1 <= 0) + { + evas_object_hide(_clang_autocomplete_popup_bg); + return; + } + + word = _edi_editor_current_word_get(editor, row, col - 1); + if (!strcmp(word, "")) + evas_object_hide(_clang_autocomplete_popup_bg); + else + { + _autocomplete_list_update(word); + _clang_autocomplete_popup_show(editor); + } + free(word); + } + else if (!strcmp(key, "Right")) + { + if (line->length < col) + { + evas_object_hide(_clang_autocomplete_popup_bg); + return; + } + + word = _edi_editor_current_word_get(editor, row, col + 1); + if (!strcmp(word, "")) + evas_object_hide(_clang_autocomplete_popup_bg); + else + { + _autocomplete_list_update(word); + _clang_autocomplete_popup_show(editor); + } + free(word); + } + else if (!strcmp(key, "BackSpace")) + { + if (col - 1 <= 0) + { + evas_object_hide(_clang_autocomplete_popup_bg); + return; + } + + word = _edi_editor_current_word_get(editor, row, col - 1); + if (!strcmp(word, "")) + evas_object_hide(_clang_autocomplete_popup_bg); + else + { + _autocomplete_list_update(word); + _clang_autocomplete_popup_show(editor); + } + free(word); + } + else if (!strcmp(key, "Escape")) + { + evas_object_hide(_clang_autocomplete_popup_bg); + } + else if (!strcmp(key, "Delete")) + { + //Do nothing + } + else if (string && strlen(string) == 1) + { + word = _edi_editor_current_word_get(editor, row, col); + strncat(word, string, 1); + _autocomplete_list_update(word); + _clang_autocomplete_popup_show(editor); + free(word); + } +} + +static void +_clang_autocomplete_popup_init(Edi_Editor *editor) +{ //Popup bg Evas_Object *bg = elm_bubble_add(editor->entry); _clang_autocomplete_popup_bg = bg; evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(bg, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_event_callback_add(bg, EVAS_CALLBACK_HIDE, + _autocomplete_bg_cb_hide, NULL); evas_object_resize(bg, 350, 200); - evas_object_move(bg, cx, cy); - evas_object_show(bg); //Genlist Evas_Object *genlist = elm_genlist_add(editor->entry); + _clang_autocomplete_popup_genlist = genlist; + elm_object_focus_allow_set(genlist, EINA_FALSE); evas_object_event_callback_add(genlist, EVAS_CALLBACK_KEY_DOWN, _autocomplete_list_cb_key_down, NULL); evas_object_smart_callback_add(genlist, "item,focused", _autocomplete_list_cb_focus, NULL); elm_object_content_set(bg, genlist); evas_object_show(genlist); - - _autocomplete_list_update(genlist, editor); - - //Focus first item - Elm_Object_Item *item = elm_genlist_first_item_get(genlist); - if (item) - elm_object_item_focus_set(item, EINA_TRUE); - else - evas_object_del(bg); - } #endif @@ -357,10 +523,15 @@ _smart_cb_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED, #if HAVE_LIBCLANG else if (!strcmp(ev->key, "space")) { - _clang_autocomplete_popup(editor); + _autocomplete_list_set(editor); + _clang_autocomplete_popup_show(editor); } #endif } +#if HAVE_LIBCLANG + if ((!alt) && (!ctrl)) + _clang_autocomplete_popup_key_down_cb(editor, ev->key, ev->string); +#endif } static void @@ -739,7 +910,7 @@ _mouse_up_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, event = (Evas_Event_Mouse_Up *)event_info; if (_clang_autocomplete_popup_bg) - evas_object_del(_clang_autocomplete_popup_bg); + evas_object_hide(_clang_autocomplete_popup_bg); ctrl = evas_key_modifier_is_set(event->modifiers, "Control"); if (event->button != 3 || !ctrl) @@ -894,5 +1065,8 @@ edi_editor_add(Evas_Object *parent, Edi_Mainview_Item *item) ev_handler = ecore_event_handler_add(EDI_EVENT_CONFIG_CHANGED, _edi_editor_config_changed, widget); evas_object_event_callback_add(vbox, EVAS_CALLBACK_DEL, _editor_del_cb, ev_handler); +#if HAVE_LIBCLANG + _clang_autocomplete_popup_init(editor); +#endif return vbox; } --