diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lua_interface.c | 67 | ||||
-rw-r--r-- | src/textadept.c | 84 | ||||
-rw-r--r-- | src/textadept.h | 8 |
3 files changed, 142 insertions, 17 deletions
diff --git a/src/lua_interface.c b/src/lua_interface.c index 34376f75..86709a5a 100644 --- a/src/lua_interface.c +++ b/src/lua_interface.c @@ -31,7 +31,8 @@ LF l_buffer_mt_index(LS *lua), l_buffer_mt_newindex(LS *lua), l_view_mt_index(LS *lua), l_view_mt_newindex(LS *lua), l_ta_mt_index(LS *lua), l_ta_mt_newindex(LS *lua), l_pm_mt_index(LS *lua), l_pm_mt_newindex(LS *lua), - l_find_mt_index(LS *lua), l_find_mt_newindex(LS *lua); + l_find_mt_index(LS *lua), l_find_mt_newindex(LS *lua), + l_ce_mt_index(LS *lua), l_ce_mt_newindex(LS *lua); LF l_cf_ta_buffer_new(LS *lua), l_cf_buffer_delete(LS *lua), @@ -40,14 +41,14 @@ LF l_cf_ta_buffer_new(LS *lua), l_cf_view_focus(LS *lua), l_cf_view_split(LS *lua), l_cf_view_unsplit(LS *lua), l_cf_ta_get_split_table(LS *lua), - l_cf_ta_focus_command(LS *lua), l_cf_ta_goto_window(LS *lua), l_cf_view_goto_buffer(LS *lua), l_cf_ta_gtkmenu(LS *lua), l_cf_ta_popupmenu(LS *lua), l_cf_ta_reset(LS *lua), l_cf_pm_focus(LS *lua), l_cf_pm_clear(LS *lua), l_cf_pm_activate(LS *lua), - l_cf_find_focus(LS *lua); + l_cf_find_focus(LS *lua), + l_cf_ce_focus(LS *lua); const char *views_dne = "textadept.views doesn't exist or was overwritten.", @@ -95,10 +96,13 @@ void l_init(int argc, char **argv, bool reinit) { l_cfunc(lua, l_cf_find_focus, "focus"); l_mt(lua, "_find_mt", l_find_mt_index, l_find_mt_newindex); lua_setfield(lua, -2, "find"); + lua_newtable(lua); + l_cfunc(lua, l_cf_ce_focus, "focus"); + l_mt(lua, "_ce_mt", l_ce_mt_index, l_ce_mt_newindex); + lua_setfield(lua, -2, "command_entry"); l_cfunc(lua, l_cf_ta_buffer_new, "new_buffer"); l_cfunc(lua, l_cf_ta_goto_window, "goto_view"); l_cfunc(lua, l_cf_ta_get_split_table, "get_split_table"); - l_cfunc(lua, l_cf_ta_focus_command, "focus_command"); l_cfunc(lua, l_cf_ta_gtkmenu, "gtkmenu"); l_cfunc(lua, l_cf_ta_popupmenu, "popupmenu"); l_cfunc(lua, l_cf_ta_reset, "reset"); @@ -700,6 +704,40 @@ void l_ta_command(const char *command) { } else l_handle_error(lua, "Error executing command."); } +// Command Entry + +/** + * Requests completions for the Command Entry Completion. + * @param entry_text The text in the Command Entry. + * @see l_cec_populate + */ +bool l_cec_get_completions_for(const char *entry_text) { + if (!l_is_ta_table_function("command_entry", "get_completions_for")) + return false; + lua_pushstring(lua, entry_text); + return l_call_function(1, 1, true); +} + +/** + * Populates the Command Entry Completion with the contents of a Lua table at + * the stack top. + * @see l_cec_get_completions_for + */ +void l_cec_populate() { + GtkTreeIter iter; + if (!lua_istable(lua, -1)) + return warn("command_entry.get_completions_for return not a table."); + gtk_tree_store_clear(cec_store); + lua_pushnil(lua); + while (lua_next(lua, -2)) { + if (lua_type(lua, -1) == LUA_TSTRING) { + gtk_tree_store_append(cec_store, &iter, NULL); + gtk_tree_store_set(cec_store, &iter, 0, lua_tostring(lua, -1), -1); + } else warn("command_entry.get_completions_for: string value expected."); + lua_pop(lua, 1); // value + } lua_pop(lua, 1); // returned table +} + // Project Manager /** @@ -729,6 +767,7 @@ bool l_pm_get_contents_for(const char *entry_text, bool expanding) { * @param initial_iter The initial GtkTreeIter. If not NULL, it is a treenode * being expanded and the contents will be added to that expanding node. * Defaults to NULL. + * @see l_pm_get_contents_for */ void l_pm_populate(GtkTreeIter *initial_iter) { GtkTreeIter iter, child; @@ -1125,6 +1164,22 @@ LF l_find_mt_newindex(LS *lua) { return 0; } +LF l_ce_mt_index(LS *lua) { + const char *key = lua_tostring(lua, 2); + if (streq(key, "entry_text")) + lua_pushstring(lua, gtk_entry_get_text(GTK_ENTRY(command_entry))); + else lua_rawget(lua, 1); + return 1; +} + +LF l_ce_mt_newindex(LS *lua) { + const char *key = lua_tostring(lua, 2); + if (streq(key, "entry_text")) + gtk_entry_set_text(GTK_ENTRY(command_entry), lua_tostring(lua, 3)); + else lua_rawset(lua, 1); + return 0; +} + // Lua CFunctions. For documentation, consult the LuaDoc. LF l_cf_ta_buffer_new(LS *lua) { @@ -1234,8 +1289,6 @@ LF l_cf_ta_get_split_table(LS *lua) { return 1; } -LF l_cf_ta_focus_command(LS *) { command_toggle_focus(); return 0; } - LF l_cf_ta_goto_(LS *lua, GtkWidget *editor, bool buffer=true) { int n = static_cast<int>(luaL_checkinteger(lua, 1)); bool absolute = lua_gettop(lua) > 1 ? lua_toboolean(lua, 2) == 1 : true; @@ -1304,3 +1357,5 @@ LF l_cf_pm_activate(LS *) { g_signal_emit_by_name(G_OBJECT(pm_entry), "activate"); return 0; } LF l_cf_find_focus(LS *) { find_toggle_focus(); return 0; } + +LF l_cf_ce_focus(LS *) { ce_toggle_focus(); return 0; } diff --git a/src/textadept.c b/src/textadept.c index 084d616a..e3024456 100644 --- a/src/textadept.c +++ b/src/textadept.c @@ -8,9 +8,15 @@ GtkWidget *window, *focused_editor, *command_entry, *menubar, *statusbar, *docstatusbar; +GtkEntryCompletion *command_entry_completion; +GtkTreeStore *cec_store; static void c_activated(GtkWidget *widget, gpointer); static bool c_keypress(GtkWidget *widget, GdkEventKey *event, gpointer); +static int cec_match_func(GtkEntryCompletion *, const char *, GtkTreeIter *, + gpointer); +static bool cec_match_selected(GtkEntryCompletion *, GtkTreeModel *model, + GtkTreeIter *iter, gpointer); static void t_notification(GtkWidget*, gint, gpointer lParam, gpointer); static void t_command(GtkWidget *editor, gint wParam, gpointer, gpointer); static bool t_keypress(GtkWidget*, GdkEventKey *event, gpointer); @@ -85,36 +91,59 @@ void create_ui() { signal(window, "delete_event", w_exit); signal(window, "focus-in-event", w_focus); signal(window, "key_press_event", w_keypress); + GtkWidget *vbox = gtk_vbox_new(false, 0); gtk_container_add(GTK_CONTAINER(window), vbox); + menubar = gtk_menu_bar_new(); gtk_box_pack_start(GTK_BOX(vbox), menubar, false, false, 0); + GtkWidget *pane = gtk_hpaned_new(); gtk_box_pack_start(GTK_BOX(vbox), pane, true, true, 0); + GtkWidget *pm = pm_create_ui(); gtk_paned_add1(GTK_PANED(pane), pm); + GtkWidget *hbox = gtk_hbox_new(false, 0); gtk_paned_add2(GTK_PANED(pane), hbox); + GtkWidget *editor = new_scintilla_window(); gtk_box_pack_start(GTK_BOX(hbox), editor, true, true, 0); + GtkWidget *find = find_create_ui(); gtk_box_pack_start(GTK_BOX(vbox), find, false, false, 5); + GtkWidget *hboxs = gtk_hbox_new(false, 0); gtk_box_pack_start(GTK_BOX(vbox), hboxs, false, false, 0); + statusbar = gtk_statusbar_new(); gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, ""); gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(statusbar), false); gtk_box_pack_start(GTK_BOX(hboxs), statusbar, true, true, 0); + command_entry = gtk_entry_new(); gtk_widget_set_name(command_entry, "textadept-command-entry"); signal(command_entry, "activate", c_activated); signal(command_entry, "key_press_event", c_keypress); g_object_set(G_OBJECT(command_entry), "width-request", 200, NULL); gtk_box_pack_start(GTK_BOX(hboxs), command_entry, true, true, 0); + + command_entry_completion = gtk_entry_completion_new(); + signal(command_entry_completion, "match-selected", cec_match_selected); + gtk_entry_completion_set_match_func(command_entry_completion, cec_match_func, + NULL, NULL); + gtk_entry_completion_set_popup_set_width(command_entry_completion, false); + gtk_entry_completion_set_text_column(command_entry_completion, 0); + cec_store = gtk_tree_store_new(1, G_TYPE_STRING); + gtk_entry_completion_set_model(command_entry_completion, + GTK_TREE_MODEL(cec_store)); + gtk_entry_set_completion(GTK_ENTRY(command_entry), command_entry_completion); + docstatusbar = gtk_statusbar_new(); gtk_statusbar_push(GTK_STATUSBAR(docstatusbar), 0, ""); g_object_set(G_OBJECT(docstatusbar), "width-request", 400, NULL); gtk_box_pack_start(GTK_BOX(hboxs), docstatusbar, false, false, 0); + gtk_widget_show_all(window); gtk_widget_hide(menubar); // hide initially gtk_widget_hide(findbox); // hide initially @@ -348,7 +377,7 @@ void set_docstatusbar_text(const char *text) { * Toggles focus between a Scintilla window and the Lua command entry. * When the entry is visible, the statusbars are temporarily hidden. */ -void command_toggle_focus() { +void ce_toggle_focus() { if (!GTK_WIDGET_HAS_FOCUS(command_entry)) { gtk_widget_hide(statusbar); gtk_widget_hide(docstatusbar); gtk_widget_show(command_entry); @@ -368,34 +397,71 @@ void command_toggle_focus() { * Generates a 'hide_completions' event. */ static void c_activated(GtkWidget *widget, gpointer) { - l_handle_event("hide_completions"); l_ta_command(gtk_entry_get_text(GTK_ENTRY(widget))); - command_toggle_focus(); + ce_toggle_focus(); } /** * Signal for a keypress inside the Lua command entry. * Currently handled keypresses: * - Escape - Hide the completion buffer if it is open. - * - Tab - Show completion buffer. - * Generates a 'hide_completions' or 'show_completions' event as necessary. + * - Tab - Display possible completions. */ static bool c_keypress(GtkWidget *widget, GdkEventKey *event, gpointer) { if (event->state == 0) switch(event->keyval) { case 0xff1b: - l_handle_event("hide_completions"); - command_toggle_focus(); + ce_toggle_focus(); return true; case 0xff09: - l_handle_event("show_completions", - gtk_entry_get_text(GTK_ENTRY(widget))); + if (l_cec_get_completions_for(gtk_entry_get_text(GTK_ENTRY(widget)))) { + l_cec_populate(); + gtk_entry_completion_complete(command_entry_completion); + } return true; } return false; } /** + * Sets every item in the Command Entry Model to be a match. + * For each attempted completion, the Command Entry Model is filled with the + * results from a call to Lua to make a list of possible completions. Therefore, + * every item in the list is valid. + */ +static int cec_match_func(GtkEntryCompletion*, const char*, GtkTreeIter*, + gpointer) { + return true; +} + +/** + * Enters the requested completion text into the Command Entry. + * The last word at the cursor is replaced with the completion. A word consists + * of any alphanumeric character or underscore. + */ +static bool cec_match_selected(GtkEntryCompletion*, GtkTreeModel *model, + GtkTreeIter *iter, gpointer) { + const char *entry_text = gtk_entry_get_text(GTK_ENTRY(command_entry)); + const char *p = entry_text + strlen(entry_text) - 1; + while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || *p == '_') { + g_signal_emit_by_name(G_OBJECT(command_entry), "move-cursor", + GTK_MOVEMENT_VISUAL_POSITIONS, -1, true, 0); + p--; + } + if (p < entry_text + strlen(entry_text) - 1) + g_signal_emit_by_name(G_OBJECT(command_entry), "backspace", 0); + + char *text; + gtk_tree_model_get(model, iter, 0, &text, -1); + g_signal_emit_by_name(G_OBJECT(command_entry), "insert-at-cursor", text, 0); + g_free(text); + + gtk_tree_store_clear(cec_store); + return true; +} + +/** * Signal for a Scintilla notification. */ static void t_notification(GtkWidget*, gint, gpointer lParam, gpointer) { diff --git a/src/textadept.h b/src/textadept.h index 70f83447..617d5766 100644 --- a/src/textadept.h +++ b/src/textadept.h @@ -23,7 +23,8 @@ extern GtkWidget *window, *focused_editor, *command_entry, *pm_container, *pm_entry, *pm_view, *findbox, *find_entry, *replace_entry; -extern GtkTreeStore *pm_store; +extern GtkEntryCompletion *command_entry_completion; +extern GtkTreeStore *cec_store, *pm_store; extern lua_State *lua; static const char *textadept_home = "/usr/share/textadept/"; @@ -45,7 +46,7 @@ void resize_split(GtkWidget *editor, int pos, bool increment=true); void set_menubar(GtkWidget *menubar); void set_statusbar_text(const char *text); void set_docstatusbar_text(const char *text); -void command_toggle_focus(); +void ce_toggle_focus(); void set_default_editor_properties(ScintillaObject *sci); void set_default_buffer_properties(ScintillaObject *sci); @@ -69,6 +70,9 @@ bool l_handle_keypress(int keyval, bool shift, bool control, bool alt); void l_handle_scnnotification(SCNotification *n); void l_ta_command(const char *command); +bool l_cec_get_completions_for(const char *entry_text); +void l_cec_populate(); + bool l_pm_get_contents_for(const char *entry_text, bool expanding=false); void l_pm_populate(GtkTreeIter *initial_iter=NULL); void l_pm_get_full_path(GtkTreePath *path); |