forked from enlightenment/edi
autocomplete: Add code autocomplete using clang
Summary: Pressing shortcut(<ctrl> + <space>) while editing the code show the list of suggestions. This support variable, function, struct member.. etc. It is simple version, so it show just function name without parameter, return type. Test Plan: 1. Run edi. 2. Open project. 3. Press shortcut(<ctrl> + <space>) while editing the code. 4. Check that the list of suggestions are correct. Reviewers: ajwillia.ms Reviewed By: ajwillia.ms Subscribers: bu5hm4n Differential Revision: https://phab.enlightenment.org/D4476
This commit is contained in:
parent
886b6356bd
commit
ebc07e85e0
|
@ -26,6 +26,8 @@ typedef struct
|
|||
Edi_Location end;
|
||||
} Edi_Range;
|
||||
|
||||
static Evas_Object *_clang_autocomplete_popup_bg;
|
||||
|
||||
void
|
||||
edi_editor_save(Edi_Editor *editor)
|
||||
{
|
||||
|
@ -51,8 +53,6 @@ _edi_editor_autosave_cb(void *data)
|
|||
return ECORE_CALLBACK_CANCEL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
_changed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
{
|
||||
|
@ -66,6 +66,252 @@ _changed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUS
|
|||
editor->save_timer = ecore_timer_add(EDI_CONTENT_SAVE_TIMEOUT, _edi_editor_autosave_cb, editor);
|
||||
}
|
||||
|
||||
#if HAVE_LIBCLANG
|
||||
static char*
|
||||
_edi_editor_currnet_word_get(Edi_Editor *editor)
|
||||
{
|
||||
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, &col, &row);
|
||||
|
||||
code = elm_code_widget_code_get(editor->entry);
|
||||
line = elm_code_file_line_get(code->file, row);
|
||||
|
||||
curtext = (char *)elm_code_line_text_get(line, &curlen);
|
||||
ptr = curtext + col - 1;
|
||||
|
||||
while (ptr != curtext &&
|
||||
((*(ptr - 1) >= 'a' && *(ptr - 1) <= 'z') ||
|
||||
(*(ptr - 1) >= 'A' && *(ptr - 1) <= 'Z') ||
|
||||
(*(ptr - 1) >= '0' && *(ptr - 1) <= '9') ||
|
||||
*(ptr - 1) == '_'))
|
||||
ptr--;
|
||||
|
||||
wordlen = col - (ptr - curtext) - 1;
|
||||
curword = malloc(sizeof(char) * (wordlen + 1));
|
||||
strncpy(curword, ptr, wordlen);
|
||||
curword[wordlen] = '\0';
|
||||
|
||||
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, &col, &row);
|
||||
|
||||
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_currnet_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,
|
||||
col - wordlen + strlen(list_word), row);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Edi_Editor *editor;
|
||||
Edi_Mainview_Item *item;
|
||||
Evas_Object *label;
|
||||
char *format, *display, *auto_str = data;
|
||||
const char *font;
|
||||
int font_size, displen;
|
||||
|
||||
if (strcmp(part, "elm.swallow.content"))
|
||||
return NULL;
|
||||
|
||||
item = edi_mainview_item_current_get();
|
||||
|
||||
if (!item)
|
||||
return NULL;
|
||||
|
||||
editor = (Edi_Editor *)evas_object_data_get(item->view, "editor");
|
||||
elm_code_widget_font_get(editor->entry, &font, &font_size);
|
||||
|
||||
format = "<align=left><font=%s><font_size=%d> %s</font_size></font></align>";
|
||||
displen = strlen(auto_str) + strlen(format) + strlen(font);
|
||||
display = malloc(sizeof(char) * displen);
|
||||
snprintf(display, displen, format, font, font_size, auto_str);
|
||||
|
||||
label = elm_label_add(obj);
|
||||
elm_object_text_set(label, display);
|
||||
evas_object_color_set(label, 255, 255, 255, 255);
|
||||
evas_object_size_hint_weight_set(label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
|
||||
evas_object_size_hint_align_set(label, EVAS_HINT_FILL, EVAS_HINT_FILL);
|
||||
evas_object_show(label);
|
||||
free(display);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
static void
|
||||
_autocomplete_list_del(void *data, Evas_Object *obj EINA_UNUSED)
|
||||
{
|
||||
char *auto_str = data;
|
||||
free(auto_str);
|
||||
}
|
||||
|
||||
static void
|
||||
_autocomplete_list_update(Evas_Object *genlist, Edi_Editor *editor)
|
||||
{
|
||||
Elm_Code *code;
|
||||
CXIndex idx;
|
||||
CXTranslationUnit tx_unit;
|
||||
CXCodeCompleteResults *res;
|
||||
char *curword, **clang_argv;
|
||||
const char *path, *args;
|
||||
unsigned int clang_argc, row, col;
|
||||
|
||||
elm_obj_code_widget_cursor_position_get(editor->entry, &col, &row);
|
||||
|
||||
code = elm_code_widget_code_get(editor->entry);
|
||||
path = elm_code_file_path_get(code->file);
|
||||
|
||||
curword = _edi_editor_currnet_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;
|
||||
|
||||
//Initialize Clang
|
||||
args = "-I/usr/inclue/ " EFL_CFLAGS " " CLANG_INCLUDES " -Wall -Wextra";
|
||||
clang_argv = eina_str_split_full(args, " ", 0, &clang_argc);
|
||||
|
||||
idx = clang_createIndex(0, 0);
|
||||
/* FIXME: Possibly activate more options? */
|
||||
tx_unit = clang_parseTranslationUnit(idx, path,
|
||||
(const char *const *)clang_argv,
|
||||
(int)clang_argc, NULL, 0,
|
||||
CXTranslationUnit_PrecompiledPreamble);
|
||||
clang_reparseTranslationUnit(tx_unit, 0, 0, 0);
|
||||
res = clang_codeCompleteAt(tx_unit, path, row, col - strlen(curword), NULL, 0,
|
||||
CXCodeComplete_IncludeMacros |
|
||||
CXCodeComplete_IncludeCodePatterns);
|
||||
|
||||
clang_sortCodeCompletionResults(res->Results, res->NumResults);
|
||||
|
||||
for (unsigned int i = 0; i < res->NumResults; i++)
|
||||
{
|
||||
const CXCompletionString str = res->Results[i].CompletionString;
|
||||
for (unsigned int j = 0; j < clang_getNumCompletionChunks(str); j++)
|
||||
{
|
||||
if (clang_getCompletionChunkKind(str, j) !=
|
||||
CXCompletionChunk_TypedText)
|
||||
continue;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
elm_genlist_item_class_free(ic);
|
||||
|
||||
clang_disposeCodeCompleteResults(res);
|
||||
clang_disposeTranslationUnit(tx_unit);
|
||||
clang_disposeIndex(idx);
|
||||
free(curword);
|
||||
}
|
||||
|
||||
static void
|
||||
_clang_autocomplete_popup(Edi_Editor *editor)
|
||||
{
|
||||
unsigned int col, row;
|
||||
Evas_Coord cx, cy, cw, ch;
|
||||
|
||||
elm_obj_code_widget_cursor_position_get(editor->entry, &col, &row);
|
||||
elm_code_widget_geometry_for_position_get(editor->entry, row, col,
|
||||
&cx, &cy, &cw, &ch);
|
||||
edi_editor_save(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_resize(bg, 350, 200);
|
||||
evas_object_move(bg, cx, cy);
|
||||
evas_object_show(bg);
|
||||
|
||||
//Genlist
|
||||
Evas_Object *genlist = elm_genlist_add(editor->entry);
|
||||
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
|
||||
|
||||
static void
|
||||
_smart_cb_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED,
|
||||
Evas_Object *obj EINA_UNUSED, void *event)
|
||||
|
@ -108,6 +354,12 @@ _smart_cb_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED,
|
|||
{
|
||||
edi_mainview_goto_popup_show();
|
||||
}
|
||||
#if HAVE_LIBCLANG
|
||||
else if (!strcmp(ev->key, "space"))
|
||||
{
|
||||
_clang_autocomplete_popup(editor);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,6 +738,9 @@ _mouse_up_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED,
|
|||
widget = (Elm_Code_Widget *)data;
|
||||
event = (Evas_Event_Mouse_Up *)event_info;
|
||||
|
||||
if (_clang_autocomplete_popup_bg)
|
||||
evas_object_del(_clang_autocomplete_popup_bg);
|
||||
|
||||
ctrl = evas_key_modifier_is_set(event->modifiers, "Control");
|
||||
if (event->button != 3 || !ctrl)
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue