diff options
author | mitchell <70453897+667e-11@users.noreply.github.com> | 2020-03-22 19:23:40 -0400 |
---|---|---|
committer | mitchell <70453897+667e-11@users.noreply.github.com> | 2020-03-22 19:23:40 -0400 |
commit | 9b07405cd5513f75b2f1a2ae1d402cde40742a17 (patch) | |
tree | 88e6d54650d34b60b0fef68e91f84db5e3263f6b /src/textadept.c | |
parent | 66cd64e293bd85bb8c24dbda78c00331879a8a5b (diff) |
More code cleanup, reformatting, refactoring, and bug fixing.
Diffstat (limited to 'src/textadept.c')
-rw-r--r-- | src/textadept.c | 1797 |
1 files changed, 882 insertions, 915 deletions
diff --git a/src/textadept.c b/src/textadept.c index 7d6cd457..c673f24b 100644 --- a/src/textadept.c +++ b/src/textadept.c @@ -9,18 +9,19 @@ #include <limits.h> // for MB_LEN_MAX #include <locale.h> #include <iconv.h> -#include <stdarg.h> -#include <stdio.h> +#include <math.h> // for fmax +//#include <stdarg.h> +//#include <stdio.h> #include <stdlib.h> #include <string.h> #if __linux__ -#include <unistd.h> +//#include <unistd.h> #elif _WIN32 #include <windows.h> -#include <fcntl.h> +#include <fcntl.h> // for _open_osfhandle, _O_RDONLY #define main main_ #elif __APPLE__ -#include <mach-o/dyld.h> +#include <mach-o/dyld.h> // for _NSGetExecutablePath #elif (__FreeBSD__ || __NetBSD__ || __OpenBSD__) #include <sys/types.h> #include <sys/sysctl.h> @@ -35,9 +36,9 @@ #if !_WIN32 #include <signal.h> #include <sys/ioctl.h> -#include <sys/select.h> +//#include <sys/select.h> #include <sys/time.h> -#include <termios.h> +//#include <termios.h> #else #undef main #endif @@ -46,7 +47,7 @@ // External dependency includes. #include "gtdialog.h" -#include "lua.h" +//#include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "Scintilla.h" @@ -58,7 +59,6 @@ #include "termkey.h" #endif -// GTK definitions and macros. #if GTK typedef GtkWidget Scintilla; // Translate GTK 2.x API to GTK 3.0 for compatibility. @@ -69,23 +69,23 @@ typedef GtkWidget Scintilla; #define gtk_combo_box_entry_set_text_column gtk_combo_box_set_entry_text_column #define GTK_COMBO_BOX_ENTRY GTK_COMBO_BOX #endif +#if !_WIN32 +#define ID "textadept.editor" +#else +#define ID "\\\\.\\pipe\\textadept.editor" // Win32 single-instance functionality. -#if _WIN32 #define g_application_command_line_get_arguments(_,__) \ - g_strsplit((char *)data, "\n", 0); argc = g_strv_length(argv); + g_strsplit((char *)userdata, "\n", 0); argc = g_strv_length(argv) #define g_application_command_line_get_cwd(_) argv[0] #define g_application_register(_,__,___) TRUE #define g_application_get_is_remote(_) \ - (WaitNamedPipe("\\\\.\\pipe\\textadept.editor", NMPWAIT_WAIT_FOREVER) != 0) -#define g_application_run(_,__,___) win32_application_run() + (WaitNamedPipe(ID, NMPWAIT_WAIT_FOREVER) != 0) #define gtk_main() \ HANDLE pipe = NULL, thread = NULL; \ - if (!g_application_get_is_remote(app)) { \ - pipe = CreateNamedPipe("\\\\.\\pipe\\textadept.editor", \ - PIPE_ACCESS_INBOUND, PIPE_WAIT, 1, 0, 0, INFINITE, \ - NULL); \ - thread = CreateThread(NULL, 0, &pipe_listener, pipe, 0, NULL); \ - } \ + if (!g_application_get_is_remote(app)) \ + pipe = CreateNamedPipe( \ + ID, PIPE_ACCESS_INBOUND, PIPE_WAIT, 1, 0, 0, INFINITE, NULL), \ + thread = CreateThread(NULL, 0, &pipe_listener, pipe, 0, NULL); \ gtk_main(); \ if (pipe && thread) \ TerminateThread(thread, 0), CloseHandle(thread), CloseHandle(pipe); @@ -93,23 +93,16 @@ typedef GtkWidget Scintilla; #elif CURSES typedef void Scintilla; #endif - -// Lua definitions and macros. -#define l_setglobalview(l, view) (l_pushview(l, view), lua_setglobal(l, "view")) -#define l_setglobaldoc(l, doc) (l_pushdoc(l, doc), lua_setglobal(l, "buffer")) -#define l_setcfunction(l, n, name, f) \ - (lua_pushcfunction(l, f), lua_setfield(l, (n > 0) ? n : n - 1, name)) -#define l_setmetatable(l, n, name, __index, __newindex) \ - if (luaL_newmetatable(l, name)) { \ - l_setcfunction(l, -1, "__index", __index); \ - l_setcfunction(l, -1, "__newindex", __newindex); \ - } \ - lua_setmetatable(l, (n > 0) ? n : n - 1); -#define lL_openlib(l, n) (luaL_requiref(l, #n, luaopen_##n, 1), lua_pop(l, 1)) +#define set_metatable(l, n, name, __index, __newindex) \ + if (luaL_newmetatable(l, name)) \ + lua_pushcfunction(l, __index), lua_setfield(l, -2, "__index"), \ + lua_pushcfunction(l, __newindex), lua_setfield(l, -2, "__newindex"); \ + lua_setmetatable(l, n > 0 ? n : n - 1); static char *textadept_home, *platform; -// User interface objects and related macros. +// User interface objects and related macros for GTK and curses +// interoperability. static Scintilla *focused_view, *dummy_view, *command_entry; #if GTK // GTK window. @@ -118,29 +111,27 @@ static GtkAccelGroup *accel; #if __APPLE__ static GtkosxApplication *osxapp; #endif +typedef GtkWidget Pane; #define SS(view, msg, w, l) scintilla_send_message(SCINTILLA(view), msg, w, l) -#define signal(w, sig, cb) g_signal_connect(G_OBJECT(w), sig, G_CALLBACK(cb), 0) -#define osx_signal(app, sig, cb) g_signal_connect(app, sig, G_CALLBACK(cb), 0) #define focus_view(view) gtk_widget_grab_focus(view) #define scintilla_delete(view) gtk_widget_destroy(view) -#define child(n, pane) gtk_paned_get_child##n(GTK_PANED(pane)) -#define event_mod(modifier) LUA_TBOOLEAN, event->state & GDK_##modifier##_MASK // GTK find & replace pane. -static GtkWidget *findbox, *find_entry, *replace_entry, *flabel, *rlabel; +static GtkWidget *findbox, *find_entry, *repl_entry, *find_label, *repl_label; #define find_text gtk_entry_get_text(GTK_ENTRY(find_entry)) -#define repl_text gtk_entry_get_text(GTK_ENTRY(replace_entry)) -typedef GtkWidget * FindButton; -static FindButton fnext_button, fprev_button, r_button, ra_button; +#define repl_text gtk_entry_get_text(GTK_ENTRY(repl_entry)) +#define set_entry_text(entry, text) gtk_entry_set_text( \ + GTK_ENTRY(entry == find_text ? find_entry : repl_entry), text) +typedef GtkWidget *FindButton; +static FindButton find_next, find_prev, replace, replace_all; static GtkWidget *match_case, *whole_word, *regex, *in_files; typedef GtkListStore ListStore; -static ListStore *find_store, *repl_store; +static ListStore *find_history, *repl_history; #define toggled(w) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)) #define toggle(w, on) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), on) #define set_label_text(l, t) gtk_label_set_text_with_mnemonic(GTK_LABEL(l), t) #define set_button_label(b, l) gtk_button_set_label(GTK_BUTTON(b), l) #define set_option_label(o, _, l) gtk_button_set_label(GTK_BUTTON(o), l) -#define attach(...) gtk_table_attach(GTK_TABLE(findbox), __VA_ARGS__) -#define FILL(option) (GtkAttachOptions)(GTK_FILL | GTK_##option) +// GTK command entry. #define command_entry_focused gtk_widget_has_focus(command_entry) #elif CURSES // curses window. @@ -152,59 +143,47 @@ typedef struct Pane { struct Pane *child1, *child2; // each pane in a split view } Pane; // Pane implementation based on code by Chris Emerson. static Pane *pane; -static int statusbar_length[2], command_entry_focused; TermKey *ta_tk; // global for CDK use #define SS(view, msg, w, l) scintilla_send_message(view, msg, w, l) -#define focus_view(view) \ - (focused_view ? SS(focused_view, SCI_SETFOCUS, 0, 0) : 0, \ - SS(view, SCI_SETFOCUS, 1, 0)) -#define refresh_all() do { \ - pane_refresh(pane); \ - if (command_entry_focused) scintilla_noutrefresh(command_entry); \ - refresh(); \ -} while (0) -#define flushch() (timeout(0), getch(), timeout(-1)) +#define focus_view(view) ( \ + focused_view ? SS(focused_view, SCI_SETFOCUS, 0, 0) : 0, \ + SS(view, SCI_SETFOCUS, 1, 0)) // curses find & replace pane. static CDKSCREEN *findbox; -static CDKENTRY *find_entry, *replace_entry, *focused_entry; -static char *find_text, *repl_text, *flabel, *rlabel; -typedef enum {fnext_button, r_button, fprev_button, ra_button} FindButton; +static CDKENTRY *find_entry, *repl_entry, *focused_entry; +static char *find_text, *repl_text, *find_label, *repl_label; +#define set_entry_text(entry, text) copyfree(&entry, text) +typedef enum {find_next, replace, find_prev, replace_all} FindButton; static int find_options[4]; static int *match_case = &find_options[0], *whole_word = &find_options[1], - *regex = &find_options[2], *in_files = &find_options[3]; + *regex = &find_options[2], *in_files = &find_options[3]; static char *button_labels[4], *option_labels[4]; -typedef char * ListStore; -static ListStore find_store[10], repl_store[10]; -#define max_(a, b) (((a) > (b)) ? (a) : (b)) -#define bind(k, d) (bindCDKObject(vENTRY, find_entry, k, entry_keypress, d), \ - bindCDKObject(vENTRY, replace_entry, k, entry_keypress, d)) +typedef char *ListStore; +static ListStore find_history[10], repl_history[10]; #define toggled(find_option) *find_option // Use pointer arithmetic to highlight/unhighlight options as necessary. #define toggle(o, on) do { \ if (*o != on) *o = on, option_labels[o - match_case] += *o ? -4 : 4; \ } while (0) -#define set_label_text(label, text) fcopy(&label, text) -#define set_button_label(button, label) fcopy(&button_labels[button], label) +#define set_label_text(label, text) copyfree(&label, text) +#define set_button_label(button, label) copyfree(&button_labels[button], label) // Prepend "</R>" to each option label because pointer arithmetic will be used // to make the "</R>" visible or invisible (thus highlighting or unhighlighting // the label) depending on whether or not the option is enabled by the user. #define set_option_label(option, i, label) do { \ lua_pushstring(L, "</R>"), lua_pushstring(L, label), lua_concat(L, 2); \ if (option_labels[i] && !*option) option_labels[i] -= 4; \ - fcopy(&option_labels[i], lua_tostring(L, -1)); \ + copyfree(&option_labels[i], lua_tostring(L, -1)); \ if (!*option) option_labels[i] += 4; \ } while (0) -#if _WIN32 -#define textadept_waitkey(tk, key) \ - (termkey_set_fd(tk, scintilla_get_window(view)), termkey_getkey(tk, key)) +// Curses command entry and statusbar. +static int command_entry_focused, statusbar_length[2]; #endif -#endif -#define set_clipboard(s) SS(focused_view, SCI_COPYTEXT, strlen(s), (sptr_t)s) // Lua objects. static lua_State *lua; #if CURSES -static int quit; +static int quitting; #endif static int initing, closing; static int show_tabs = TRUE, tab_sync; @@ -213,7 +192,7 @@ enum {SVOID, SINT, SLEN, SCOLOR, SBOOL, SKEYMOD, SSTRING, SSTRINGRET}; // Forward declarations. static void new_buffer(sptr_t); static Scintilla *new_view(sptr_t); -static int lL_init(lua_State *, int, char **, int); +static int init_lua(lua_State *, int, char **, int); LUALIB_API int luaopen_lpeg(lua_State *), luaopen_lfs(lua_State *); LUALIB_API int os_spawn_pushfds(lua_State *), os_spawn_readfds(lua_State *); @@ -229,52 +208,49 @@ LUALIB_API int os_spawn_pushfds(lua_State *), os_spawn_readfds(lua_State *); * @return TRUE or FALSE depending on the boolean value returned by the event * handler, if any. */ -static int lL_event(lua_State *L, const char *name, ...) { +static int emit(lua_State *L, const char *name, ...) { int ret = FALSE; - if (lua_getglobal(L, "events") == LUA_TTABLE) { - if (lua_getfield(L, -1, "emit") == LUA_TFUNCTION) { - lua_pushstring(L, name); - int n = 1, type; - va_list ap; - va_start(ap, name); - for (type = va_arg(ap, int); type != -1; type = va_arg(ap, int), n++) - if (type == LUA_TNIL) - lua_pushnil(L); - else if (type == LUA_TBOOLEAN) - lua_pushboolean(L, va_arg(ap, int)); - else if (type == LUA_TNUMBER) - lua_pushinteger(L, va_arg(ap, int)); - else if (type == LUA_TSTRING) - lua_pushstring(L, va_arg(ap, char *)); - else if (type == LUA_TLIGHTUSERDATA || type == LUA_TTABLE) { - sptr_t arg = va_arg(ap, sptr_t); - lua_rawgeti(L, LUA_REGISTRYINDEX, arg); - luaL_unref(L, LUA_REGISTRYINDEX, arg); - } - va_end(ap); - if (lua_pcall(L, n, 1, 0) == LUA_OK) - ret = lua_toboolean(L, -1); - else - lL_event(L, "error", LUA_TSTRING, lua_tostring(L, -1), -1); - lua_pop(L, 2); // result, events - } else lua_pop(L, 2); // non-function, events - } else lua_pop(L, 1); // non-table - return ret; + if (lua_getglobal(L, "events") != LUA_TTABLE) return (lua_pop(L, 1), ret); + if (lua_getfield(L, -1, "emit") != LUA_TFUNCTION) return (lua_pop(L, 2), ret); + lua_pushstring(L, name); + int n = 1; + va_list ap; + va_start(ap, name); + for (int type = va_arg(ap, int); type != -1; type = va_arg(ap, int), n++) + switch (type) { + case LUA_TBOOLEAN: lua_pushboolean(L, va_arg(ap, int)); break; + case LUA_TNUMBER: lua_pushinteger(L, va_arg(ap, int)); break; + case LUA_TSTRING: lua_pushstring(L, va_arg(ap, char *)); break; + case LUA_TLIGHTUSERDATA: case LUA_TTABLE: { + sptr_t arg = va_arg(ap, sptr_t); + lua_rawgeti(L, LUA_REGISTRYINDEX, arg); + luaL_unref(L, LUA_REGISTRYINDEX, arg); + break; + } default: lua_pushnil(L); + } + va_end(ap); + if (lua_pcall(L, n, 1, 0) != LUA_OK) { + emit(L, "error", LUA_TSTRING, lua_tostring(L, -1), -1); + return (lua_pop(L, 2), FALSE); // result, events + } + ret = lua_toboolean(L, -1); + return (lua_pop(L, 2), ret); // result, events } #if GTK /** Processes a remote Textadept's command line arguments. */ -static int a_command_line(GApplication*_, GApplicationCommandLine *cmdline, - void *data) { +static int a_command_line( + GApplication *_, GApplicationCommandLine *command_line, void *userdata) +{ if (!lua) return 0; // only process argv for secondary/remote instances int argc = 0; - char **argv = g_application_command_line_get_arguments(cmdline, &argc); + char **argv = g_application_command_line_get_arguments(command_line, &argc); if (argc > 1) { lua_newtable(lua); - const char *cwd = g_application_command_line_get_cwd(cmdline); - lua_pushstring(lua, cwd ? cwd : ""), lua_rawseti(lua, -2, -1); + lua_pushstring(lua, g_application_command_line_get_cwd(command_line)); + lua_rawseti(lua, -2, -1); while (--argc) lua_pushstring(lua, argv[argc]), lua_rawseti(lua, -2, argc); - lL_event(lua, "cmd_line", LUA_TTABLE, luaL_ref(lua, LUA_REGISTRYINDEX), -1); + emit(lua, "command_line", LUA_TTABLE, luaL_ref(lua, LUA_REGISTRYINDEX), -1); } g_strfreev(argv); return (gtk_window_present(GTK_WINDOW(window)), 0); @@ -283,13 +259,13 @@ static int a_command_line(GApplication*_, GApplicationCommandLine *cmdline, #if CURSES /** - * Frees the given string's current value, if any, and copies the given value to - * it. + * Copies the given value to the given string after freeing that string's + * existing value (if any). * The given string must be freed when finished. * @param s The address of the string to copy value to. - * @param value The new value to copy. It may be freed immediately. + * @param value String value to copy. It may be freed immediately. */ -static void fcopy(char **s, const char *value) { +static void copyfree(char **s, const char *value) { if (*s) free(*s); *s = strcpy(malloc(strlen(value) + 1), value); } @@ -297,10 +273,10 @@ static void fcopy(char **s, const char *value) { /** * Adds the given text to the find/replace history list if it is not at the top. - * @param text The text to add. * @param store The ListStore to add the text to. + * @param text The text to add. */ -static void find_add_to_history(const char *text, ListStore *store) { +static void add_to_history(ListStore* store, const char *text) { #if GTK // Note: GtkComboBoxEntry key navigation behaves contrary to command line // history navigation. Down cycles from newer to older, and up cycles from @@ -310,81 +286,96 @@ static void find_add_to_history(const char *text, ListStore *store) { GtkTreeIter iter; if (n > 10) gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, --n), - gtk_list_store_remove(store, &iter); // keep 10 items + gtk_list_store_remove(store, &iter); // keep 10 items char *last_text = NULL; if (n > 0) gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, n - 1), - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &last_text, -1); + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &last_text, -1); if (!last_text || strcmp(text, last_text) != 0) gtk_list_store_append(store, &iter), - gtk_list_store_set(store, &iter, 0, text, -1); + gtk_list_store_set(store, &iter, 0, text, -1); g_free(last_text); #elif CURSES if (!text || (store[0] && strcmp(text, store[0]) == 0)) return; if (store[9]) free(store[9]); for (int i = 9; i > 0; i--) store[i] = store[i - 1]; - store[0] = NULL, fcopy(&store[0], text); + store[0] = NULL, copyfree(&store[0], text); #endif } /** Signal for a find box button click. */ -static void f_clicked(FindButton button, void*_) { +static void find_clicked(FindButton button, void *userdata) { if (find_text && !*find_text) return; - if (button == fnext_button || button == fprev_button) { - find_add_to_history(find_text, find_store); - lL_event(lua, "find", LUA_TSTRING, find_text, LUA_TBOOLEAN, - button == fnext_button, -1); - } else { - find_add_to_history(repl_text, repl_store); - if (button == r_button) { - lL_event(lua, "replace", LUA_TSTRING, repl_text, -1); - lL_event(lua, "find", LUA_TSTRING, find_text, LUA_TBOOLEAN, 1, -1); - } else lL_event(lua, "replace_all", LUA_TSTRING, find_text, LUA_TSTRING, - repl_text, -1); - } + if (button == find_next || button == find_prev) + add_to_history(find_history, find_text); + else + add_to_history(repl_history, repl_text); + lua_State *L = (lua_State *)userdata; + if (button == find_next) + emit(L, "find", LUA_TSTRING, find_text, LUA_TBOOLEAN, TRUE, -1); + else if (button == find_prev) + emit(L, "find", LUA_TSTRING, find_text, LUA_TBOOLEAN, FALSE, -1); + else if (button == replace) { + emit(L, "replace", LUA_TSTRING, repl_text, -1); + emit(L, "find", LUA_TSTRING, find_text, LUA_TBOOLEAN, TRUE, -1); + } else if (button == replace_all) + emit(L, "replace_all", LUA_TSTRING, find_text, LUA_TSTRING, repl_text, -1); } /** `find.find_next()` Lua function. */ -static int lfind_next(lua_State *L) {return (f_clicked(fnext_button, NULL), 0);} +static int click_find_next(lua_State *L) { + return (find_clicked(find_next, L), 0); +} /** `find.find_prev()` Lua function. */ -static int lfind_prev(lua_State *L) {return (f_clicked(fprev_button, NULL), 0);} +static int click_find_prev(lua_State *L) { + return (find_clicked(find_prev, L), 0); +} + +/** `find.replace()` Lua function. */ +static int click_replace(lua_State *L) { + return (find_clicked(replace, L), 0); +} + +/** `find.replace_all()` Lua function. */ +static int click_replace_all(lua_State *L) { + return (find_clicked(replace_all, L), 0); +} #if CURSES /** - * Signal for a keypress in the Find/Replace Entry. + * Signal for Find/Replace entry keypress. * For tab keys, toggle through find/replace buttons. * For ^N and ^P keys, cycle through find/replace history. * For F1-F4 keys, toggle the respective search option. * For up and down keys, toggle entry focus. */ -static int entry_keypress(EObjectType _, void *object, void *data, chtype key) { +static int find_keypress(EObjectType _, void *object, void *data, chtype key) { if (key == KEY_TAB || key == KEY_BTAB) injectCDKButtonbox((CDKBUTTONBOX *)data, key); else if (key == CDK_PREV || key == CDK_NEXT) { CDKENTRY *entry = (CDKENTRY *)object; - ListStore *store = (entry == find_entry) ? find_store : repl_store; + ListStore *store = entry == find_entry ? find_history : repl_history; int i; for (i = 9; i >= 0; i--) if (store[i] && strcmp(store[i], getCDKEntryValue(entry)) == 0) break; - (key == CDK_PREV) ? i++ : i--; + key == CDK_PREV ? i++ : i--; if (i >= 0 && i <= 9 && store[i]) setCDKEntryValue(entry, store[i]), drawCDKEntry(entry, FALSE); } else if (key >= KEY_F(1) && key <= KEY_F(4)) { - int i = key - KEY_F(1), option = find_options[key - KEY_F(1)]; - // Use pointer arithmetic to highlight/unhighlight options as necessary. - find_options[i] = !option, option_labels[i] += !option ? -4 : 4; + toggle(&find_options[key - KEY_F(1)], !find_options[key - KEY_F(1)]); // Redraw the optionbox. CDKBUTTONBOX **optionbox = (CDKBUTTONBOX **)data; int width = (*optionbox)->boxWidth - 1; destroyCDKButtonbox(*optionbox); - *optionbox = newCDKButtonbox(findbox, RIGHT, TOP, 2, width, NULL, 2, 2, - option_labels, 4, A_NORMAL, FALSE, FALSE); + *optionbox = newCDKButtonbox( + findbox, RIGHT, TOP, 2, width, NULL, 2, 2, option_labels, 4, A_NORMAL, + FALSE, FALSE); drawCDKButtonbox(*optionbox, FALSE); } else if (key == KEY_UP || key == KEY_DOWN) { CDKENTRY *entry = (CDKENTRY *)object; - focused_entry = (entry == find_entry) ? replace_entry : find_entry; - injectCDKEntry(entry, KEY_ENTER); + focused_entry = entry == find_entry ? repl_entry : find_entry; + injectCDKEntry(entry, KEY_ENTER); // exit this entry } return TRUE; } @@ -402,24 +393,31 @@ static char *get_clipboard() { * Redraws an entire pane and its children. * @param pane The pane to redraw. */ -static void pane_refresh(Pane *pane) { +static void refresh_pane(Pane *pane) { if (pane->type == VSPLIT) { mvwvline(pane->win, 0, 0, 0, pane->rows), wrefresh(pane->win); - pane_refresh(pane->child1), pane_refresh(pane->child2); + refresh_pane(pane->child1), refresh_pane(pane->child2); } else if (pane->type == HSPLIT) { mvwhline(pane->win, 0, 0, 0, pane->cols), wrefresh(pane->win); - pane_refresh(pane->child1), pane_refresh(pane->child2); + refresh_pane(pane->child1), refresh_pane(pane->child2); } else scintilla_noutrefresh(pane->view); } + +/** Refreshes the entire screen. */ +static void refresh_all() { + refresh_pane(pane); + if (command_entry_focused) scintilla_noutrefresh(command_entry); + refresh(); +} #endif /** `find.focus()` Lua function. */ -static int lfind_focus(lua_State *L) { +static int focus_find(lua_State *L) { #if GTK if (!gtk_widget_has_focus(findbox)) { gtk_widget_show(findbox); gtk_widget_grab_focus(find_entry); - gtk_widget_grab_default(fnext_button); + gtk_widget_grab_default(find_next); } else { gtk_widget_grab_focus(focused_view); gtk_widget_hide(findbox); @@ -428,71 +426,64 @@ static int lfind_focus(lua_State *L) { if (findbox) return 0; // already active wresize(scintilla_get_window(focused_view), LINES - 4, COLS); findbox = initCDKScreen(newwin(2, 0, LINES - 3, 0)), eraseCDKScreen(findbox); - int b_width = max_(strlen(button_labels[0]), strlen(button_labels[1])) + - max_(strlen(button_labels[2]), strlen(button_labels[3])) + 3; - int o_width = max_(strlen(option_labels[0]), strlen(option_labels[1])) + - max_(strlen(option_labels[2]), strlen(option_labels[3])) + 3; - int l_width = max_(strlen(flabel), strlen(rlabel)); + int b_width = fmax(strlen(button_labels[0]), strlen(button_labels[1])) + + fmax(strlen(button_labels[2]), strlen(button_labels[3])) + 3; + int o_width = fmax(strlen(option_labels[0]), strlen(option_labels[1])) + + fmax(strlen(option_labels[2]), strlen(option_labels[3])) + 3; + int l_width = fmax(strlen(find_label), strlen(repl_label)); int e_width = COLS - o_width - b_width - l_width - 1; - find_entry = newCDKEntry(findbox, l_width - strlen(flabel), TOP, NULL, flabel, - A_NORMAL, '_', vMIXED, e_width, 0, 64, FALSE, FALSE); - replace_entry = newCDKEntry(findbox, l_width - strlen(rlabel), BOTTOM, NULL, - rlabel, A_NORMAL, '_', vMIXED, e_width, 0, 64, - FALSE, FALSE); + find_entry = newCDKEntry( + findbox, l_width - strlen(find_label), TOP, NULL, find_label, A_NORMAL, '_', + vMIXED, e_width, 0, 64, FALSE, FALSE); + repl_entry = newCDKEntry( + findbox, l_width - strlen(repl_label), BOTTOM, NULL, repl_label, A_NORMAL, + '_', vMIXED, e_width, 0, 64, FALSE, FALSE); CDKBUTTONBOX *buttonbox, *optionbox; - buttonbox = newCDKButtonbox(findbox, COLS - o_width - b_width, TOP, 2, - b_width, NULL, 2, 2, button_labels, 4, A_REVERSE, - FALSE, FALSE); - optionbox = newCDKButtonbox(findbox, RIGHT, TOP, 2, o_width, NULL, 2, 2, - option_labels, 4, A_NORMAL, FALSE, FALSE); + buttonbox = newCDKButtonbox( + findbox, COLS - o_width - b_width, TOP, 2, b_width, NULL, 2, 2, + button_labels, 4, A_REVERSE, FALSE, FALSE); + optionbox = newCDKButtonbox( + findbox, RIGHT, TOP, 2, o_width, NULL, 2, 2, option_labels, 4, A_NORMAL, + FALSE, FALSE); + // TODO: ideally no #define here. + #define bind(k, d) (bindCDKObject(vENTRY, find_entry, k, find_keypress, d), \ + bindCDKObject(vENTRY, repl_entry, k, find_keypress, d)) bind(KEY_TAB, buttonbox), bind(KEY_BTAB, buttonbox); bind(CDK_NEXT, NULL), bind(CDK_PREV, NULL); - bind(KEY_F(1), &optionbox), bind(KEY_F(2), &optionbox); - bind(KEY_F(3), &optionbox), bind(KEY_F(4), &optionbox); + for (int i = 1; i <= 4; i++) bind(KEY_F(i), &optionbox); bind(KEY_DOWN, NULL), bind(KEY_UP, NULL); setCDKEntryValue(find_entry, find_text); - setCDKEntryValue(replace_entry, repl_text); - // Draw these widgets manually since activateCDKEntry() only draws find_entry. - drawCDKEntry(replace_entry, FALSE); - drawCDKButtonbox(buttonbox, FALSE), drawCDKButtonbox(optionbox, FALSE); + setCDKEntryValue(repl_entry, repl_text); char *clipboard = get_clipboard(); GPasteBuffer = copyChar(clipboard); // set the CDK paste buffer curs_set(1); - activateCDKEntry(focused_entry = find_entry, NULL); + refreshCDKScreen(findbox), activateCDKEntry(focused_entry = find_entry, NULL); while (focused_entry->exitType == vNORMAL || focused_entry->exitType == vNEVER_ACTIVATED) { - fcopy(&find_text, getCDKEntryValue(find_entry)); - fcopy(&repl_text, getCDKEntryValue(replace_entry)); - if (focused_entry->exitType == vNORMAL) { - f_clicked(getCDKButtonboxCurrentButton(buttonbox), NULL); + copyfree(&find_text, getCDKEntryValue(find_entry)); + copyfree(&repl_text, getCDKEntryValue(repl_entry)); + if (focused_entry->exitType == vNORMAL) + find_clicked(getCDKButtonboxCurrentButton(buttonbox), L), refresh_all(); - if (toggled(in_files)) refreshCDKScreen(findbox); // splits cause trouble - } - find_entry->exitType = replace_entry->exitType = vNEVER_ACTIVATED; - activateCDKEntry(focused_entry, NULL); + find_entry->exitType = repl_entry->exitType = vNEVER_ACTIVATED; + refreshCDKScreen(findbox), activateCDKEntry(focused_entry, NULL); } curs_set(0); // Set Scintilla clipboard with new CDK paste buffer if necessary. - if (strcmp(clipboard, GPasteBuffer)) set_clipboard(GPasteBuffer); + if (strcmp(clipboard, GPasteBuffer) != 0) + SS(focused_view, SCI_COPYTEXT, strlen(GPasteBuffer), (sptr_t)GPasteBuffer); free(clipboard), free(GPasteBuffer), GPasteBuffer = NULL; - destroyCDKEntry(find_entry), destroyCDKEntry(replace_entry); + destroyCDKEntry(find_entry), destroyCDKEntry(repl_entry); destroyCDKButtonbox(buttonbox), destroyCDKButtonbox(optionbox); - delwin(findbox->window), destroyCDKScreen(findbox), findbox = NULL, flushch(); + delwin(findbox->window), destroyCDKScreen(findbox), findbox = NULL; + timeout(0), getch(), timeout(-1); // flush potential extra Escape wresize(scintilla_get_window(focused_view), LINES - 2, COLS); #endif return 0; } -/** `find.replace()` Lua function. */ -static int lfind_replace(lua_State *L) {return (f_clicked(r_button, NULL), 0);} - -/** `find.replace_all()` Lua function. */ -static int lfind_replace_all(lua_State *L) { - return (f_clicked(ra_button, NULL), 0); -} - /** `find.__index` Lua metamethod. */ -static int lfind__index(lua_State *L) { +static int find_index(lua_State *L) { const char *key = lua_tostring(L, 2); if (strcmp(key, "find_entry_text") == 0) lua_pushstring(L, find_text); @@ -511,21 +502,13 @@ static int lfind__index(lua_State *L) { return 1; } -/** `find.__newindex` Lua metatable. */ -static int lfind__newindex(lua_State *L) { +/** `find.__newindex` Lua metamethod. */ +static int find_newindex(lua_State *L) { const char *key = lua_tostring(L, 2); if (strcmp(key, "find_entry_text") == 0) -#if GTK - gtk_entry_set_text(GTK_ENTRY(find_entry), lua_tostring(L, 3)); -#elif CURSES - fcopy(&find_text, lua_tostring(L, 3)); -#endif + set_entry_text(find_text, lua_tostring(L, 3)); else if (strcmp(key, "replace_entry_text") == 0) -#if GTK - gtk_entry_set_text(GTK_ENTRY(replace_entry), lua_tostring(L, 3)); -#elif CURSES - fcopy(&repl_text, lua_tostring(L, 3)); -#endif + set_entry_text(repl_text, lua_tostring(L, 3)); else if (strcmp(key, "match_case") == 0) toggle(match_case, lua_toboolean(L, -1)); else if (strcmp(key, "whole_word") == 0) @@ -535,17 +518,17 @@ static int lfind__newindex(lua_State *L) { else if (strcmp(key, "in_files") == 0) toggle(in_files, lua_toboolean(L, -1)); else if (strcmp(key, "find_label_text") == 0) - set_label_text(flabel, lua_tostring(L, 3)); + set_label_text(find_label, lua_tostring(L, 3)); else if (strcmp(key, "replace_label_text") == 0) - set_label_text(rlabel, lua_tostring(L, 3)); + set_label_text(repl_label, lua_tostring(L, 3)); else if (strcmp(key, "find_next_button_text") == 0) - set_button_label(fnext_button, lua_tostring(L, 3)); + set_button_label(find_next, lua_tostring(L, 3)); else if (strcmp(key, "find_prev_button_text") == 0) - set_button_label(fprev_button, lua_tostring(L, 3)); + set_button_label(find_prev, lua_tostring(L, 3)); else if (strcmp(key, "replace_button_text") == 0) - set_button_label(r_button, lua_tostring(L, 3)); + set_button_label(replace, lua_tostring(L, 3)); else if (strcmp(key, "replace_all_button_text") == 0) - set_button_label(ra_button, lua_tostring(L, 3)); + set_button_label(replace_all, lua_tostring(L, 3)); else if (strcmp(key, "match_case_label_text") == 0) set_option_label(match_case, 0, lua_tostring(L, 3)); else if (strcmp(key, "whole_word_label_text") == 0) @@ -560,7 +543,7 @@ static int lfind__newindex(lua_State *L) { } /** `command_entry.focus()` Lua function. */ -static int lce_focus(lua_State *L) { +static int focus_command_entry(lua_State *L) { //if (closing) return 0; #if GTK if (!gtk_widget_get_visible(command_entry)) @@ -575,43 +558,39 @@ static int lce_focus(lua_State *L) { } /** Runs the work function passed to `ui.dialogs.progressbar()`. */ -static char *progressbar_cb() { - lua_getfield(lua, LUA_REGISTRYINDEX, "ta_progress"); - if (lua_pcall(lua, 0, 2, 0) == LUA_OK) { - if (lua_isnil(lua, -2)) return (lua_pop(lua, 2), NULL); - if (lua_isnil(lua, -1)) lua_pushliteral(lua, ""), lua_replace(lua, -2); - if (lua_isnumber(lua, -2)) { - lua_pushliteral(lua, " "), lua_insert(lua, -2), - lua_pushliteral(lua, "\n"); - lua_concat(lua, 4); // "num str\n" - char *s = strcpy(malloc(lua_rawlen(lua, -1) + 1), lua_tostring(lua, -1)); - return (lua_pop(lua, 1), s); // will be freed by gtdialog - } else lua_pop(lua, 2), lua_pushliteral(lua, "invalid return values"); +static char *work(void *userdata) { + lua_State *L = (lua_State *)userdata; + lua_getfield(L, LUA_REGISTRYINDEX, "ta_progress"); + if (lua_pcall(L, 0, 2, 0) == LUA_OK) { + if (lua_isnil(L, -2)) return (lua_pop(L, 2), NULL); // done + if (lua_isnil(L, -1)) lua_pushliteral(L, ""), lua_replace(L, -2); + if (lua_isnumber(L, -2) && lua_isstring(L, -1)) { + lua_pushliteral(L, " "), lua_insert(L, -2), lua_pushliteral(L, "\n"); + lua_concat(L, 4); // "num str\n" + char *input = strcpy(malloc(lua_rawlen(L, -1) + 1), lua_tostring(L, -1)); + return (lua_pop(L, 1), input); // will be freed by gtdialog + } else lua_pop(L, 2), lua_pushliteral(L, "invalid return values"); } - lL_event(lua, "error", LUA_TSTRING, lua_tostring(lua, -1), -1); - return (lua_pop(lua, 1), NULL); + emit(L, "error", LUA_TSTRING, lua_tostring(L, -1), -1); + return (lua_pop(L, 1), NULL); } /** `ui.dialog()` Lua function. */ -static int lui_dialog(lua_State *L) { +static int dialog(lua_State *L) { GTDialogType type = gtdialog_type(luaL_checkstring(L, 1)); - int i, j, k, n = lua_gettop(L) - 1, argc = n; - for (i = 2; i < n + 2; i++) + int n = lua_gettop(L) - 1, argc = n; + for (int i = 2; i < n + 2; i++) if (lua_istable(L, i)) argc += lua_rawlen(L, i) - 1; if (type == GTDIALOG_PROGRESSBAR) lua_pushnil(L), lua_setfield(L, LUA_REGISTRYINDEX, "ta_progress"), argc--; - const char *argv[argc + 1]; // not malloc since luaL_checklstring throws - for (i = 0, j = 2; j < n + 2; j++) - if (lua_istable(L, j)) { - int len = lua_rawlen(L, j); - for (k = 1; k <= len; k++) { - lua_rawgeti(L, j, k); - argv[i++] = luaL_checkstring(L, -1); - lua_pop(L, 1); - } - } else if (lua_isfunction(L, j) && type == GTDIALOG_PROGRESSBAR) { + const char *argv[argc + 1]; // not malloc since luaL_checkstring throws + for (int i = 0, j = 2; j < n + 2; j++) + if (lua_istable(L, j)) + for (int k = 1, len = lua_rawlen(L, j); k <= len; lua_pop(L, 1), k++) + argv[i++] = (lua_rawgeti(L, j, k), luaL_checkstring(L, -1)); // ^popped + else if (lua_isfunction(L, j) && type == GTDIALOG_PROGRESSBAR) { lua_pushvalue(L, j), lua_setfield(L, LUA_REGISTRYINDEX, "ta_progress"); - gtdialog_set_progressbar_callback(progressbar_cb, NULL); + gtdialog_set_progressbar_callback(work, L); } else argv[i++] = luaL_checkstring(L, j); argv[argc] = NULL; char *out = gtdialog(type, argc, argv); @@ -625,50 +604,52 @@ static int lui_dialog(lua_State *L) { /** * Pushes the Scintilla view onto the stack. - * The view must have previously been added with lL_addview. + * The view must have previously been added with add_view. * @param L The Lua state. * @param view The Scintilla view to push. - * @see lL_addview + * @see add_view */ -static void l_pushview(lua_State *L, Scintilla *view) { +static void lua_pushview(lua_State *L, Scintilla *view) { lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); lua_pushlightuserdata(L, view), lua_gettable(L, -2), lua_replace(L, -2); } +/** + * Pushes onto the stack a split view. + * @param L The Lua state. + * @param pane The pane of the split to push. + */ +static void lua_pushsplit(lua_State *L, Pane *pane) { #if GTK -static void l_pushsplittable(lua_State *L, GtkWidget *w) { - if (GTK_IS_PANED(w)) { + if (GTK_IS_PANED(pane)) { + GtkPaned *p = GTK_PANED(pane); lua_newtable(L); - l_pushsplittable(L, child(1, w)), lua_rawseti(L, -2, 1); - l_pushsplittable(L, child(2, w)), lua_rawseti(L, -2, 2); - lua_pushboolean(L, gtk_orientable_get_orientation(GTK_ORIENTABLE(w)) == - GTK_ORIENTATION_HORIZONTAL); - lua_setfield(L, -2, "vertical"); - lua_pushinteger(L, gtk_paned_get_position(GTK_PANED(w))); - lua_setfield(L, -2, "size"); - } else l_pushview(L, w); -} + lua_pushsplit(L, gtk_paned_get_child1(p)), lua_rawseti(L, -2, 1); + lua_pushsplit(L, gtk_paned_get_child2(p)), lua_rawseti(L, -2, 2); + lua_pushboolean( + L, gtk_orientable_get_orientation(GTK_ORIENTABLE(pane)) == + GTK_ORIENTATION_HORIZONTAL), lua_setfield(L, -2, "vertical"); + lua_pushinteger(L, gtk_paned_get_position(p)), lua_setfield(L, -2, "size"); + } else lua_pushview(L, pane); #elif CURSES -static void l_pushsplittable(lua_State *L, Pane *pane) { if (pane->type != SINGLE) { lua_newtable(L); - l_pushsplittable(L, pane->child1), lua_rawseti(L, -2, 1); - l_pushsplittable(L, pane->child2), lua_rawseti(L, -2, 2); + lua_pushsplit(L, pane->child1), lua_rawseti(L, -2, 1); + lua_pushsplit(L, pane->child2), lua_rawseti(L, -2, 2); lua_pushboolean(L, pane->type == VSPLIT), lua_setfield(L, -2, "vertical"); lua_pushinteger(L, pane->split_size), lua_setfield(L, -2, "size"); - } else l_pushview(L, pane->view); -} + } else lua_pushview(L, pane->view); #endif +} /** `ui.get_split_table()` Lua function. */ -static int lui_get_split_table(lua_State *L) { +static int get_split_table(lua_State *L) { #if GTK - GtkWidget *w = focused_view; - while (GTK_IS_PANED(gtk_widget_get_parent(w))) w = gtk_widget_get_parent(w); - return (l_pushsplittable(L, w), 1); -#elif CURSES - return (l_pushsplittable(L, pane), 1); + GtkWidget *pane = focused_view; + while (GTK_IS_PANED(gtk_widget_get_parent(pane))) + pane = gtk_widget_get_parent(pane); #endif + return (lua_pushsplit(L, pane), 1); } /** @@ -677,24 +658,23 @@ static int lui_get_split_table(lua_State *L) { * @param index Stack index of the view. * @return Scintilla view */ -static Scintilla *l_toview(lua_State *L, int index) { +static Scintilla *lua_toview(lua_State *L, int index) { lua_getfield(L, index, "widget_pointer"); Scintilla *view = (Scintilla *)lua_touserdata(L, -1); - lua_pop(L, 1); // widget pointer - return view; + return (lua_pop(L, 1), view); // widget pointer } /** * Pushes the Scintilla document onto the stack. - * The document must have previously been added with lL_adddoc. + * The document must have previously been added with add_doc. * @param L The Lua state. * @param doc The document to push. - * @see lL_adddoc + * @see add_doc */ -static void l_pushdoc(lua_State *L, sptr_t doc) { +static void lua_pushdoc(lua_State *L, sptr_t doc) { lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); - lua_pushlightuserdata(L, (sptr_t *)doc), lua_gettable(L, -2); - lua_replace(L, -2); + lua_pushlightuserdata(L, (sptr_t *)doc), lua_gettable(L, -2), + lua_replace(L, -2); } /** @@ -703,7 +683,7 @@ static void l_pushdoc(lua_State *L, sptr_t doc) { static void sync_tabbar() { #if GTK lua_getfield(lua, LUA_REGISTRYINDEX, "ta_buffers"); - l_pushdoc(lua, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)); + lua_pushdoc(lua, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)); int i = (lua_gettable(lua, -2), lua_tointeger(lua, -1) - 1); lua_pop(lua, 2); // index and buffers GtkNotebook *tabs = GTK_NOTEBOOK(tabbar); @@ -719,59 +699,57 @@ static void sync_tabbar() { * @param index The stack index of the value to test. * @param tname The name of the expected metatable. */ -static int lL_hasmetatable(lua_State *L, int index, const char *tname) { - luaL_getmetatable(L, tname); - lua_getmetatable(L, (index > 0) ? index : index - 1); +static int is_type(lua_State *L, int index, const char *tname) { + lua_getmetatable(L, index), luaL_getmetatable(L, tname); int has_metatable = lua_rawequal(L, -1, -2); - lua_pop(L, 2); // metatable, metatable - return has_metatable; + return (lua_pop(L, 2), has_metatable); // metatable, metatable } /** - * Checks whether the function argument narg is a Scintilla view and returns + * Checks whether the function argument arg is a Scintilla view and returns * this view cast to a Scintilla. * @param L The Lua state. * @param arg The stack index of the Scintilla view. * @return Scintilla view */ -static Scintilla *lL_checkview(lua_State *L, int arg) { - luaL_argcheck(L, lL_hasmetatable(L, arg, "ta_view"), arg, "View expected"); +static Scintilla *luaL_checkview(lua_State *L, int arg) { + luaL_argcheck(L, is_type(L, arg, "ta_view"), arg, "View expected"); lua_getfield(L, arg, "widget_pointer"); Scintilla *view = (Scintilla *)lua_touserdata(L, -1); - lua_pop(L, 1); // widget_pointer - return view; + return (lua_pop(L, 1), view); // widget_pointer } /** * Change focus to the given Scintilla view. * Generates 'view_before_switch' and 'view_after_switch' events. * @param view The Scintilla view to focus. + * @param L The Lua state. */ -static void goto_view(Scintilla *view) { - if (!initing && !closing) lL_event(lua, "view_before_switch", -1); - l_setglobalview(lua, focused_view = view), sync_tabbar(); - l_setglobaldoc(lua, SS(view, SCI_GETDOCPOINTER, 0, 0)); - if (!initing && !closing) lL_event(lua, "view_after_switch", -1); +static void view_focused(Scintilla *view, lua_State *L) { + if (!initing && !closing) emit(L, "view_before_switch", -1); + lua_pushview(L, focused_view = view), lua_setglobal(L, "view"), sync_tabbar(); + lua_pushdoc(L, SS(view, SCI_GETDOCPOINTER, 0, 0)), lua_setglobal(L, "buffer"); + if (!initing && !closing) emit(L, "view_after_switch", -1); } /** `ui.goto_view()` Lua function. */ -static int lui_goto_view(lua_State *L) { +static int goto_view(lua_State *L) { if (lua_isnumber(L, 1)) { lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); - l_pushview(L, focused_view), lua_gettable(L, -2); + lua_pushview(L, focused_view), lua_gettable(L, -2); int n = lua_tointeger(L, -1) + lua_tointeger(L, 1); if (n > (int)lua_rawlen(L, -2)) n = 1; else if (n < 1) n = lua_rawlen(L, -2); - lua_rawgeti(L, -2, n), lua_replace(L, 1); + lua_rawgeti(L, -2, n), lua_replace(L, 1); // index } - Scintilla *view = lL_checkview(L, 1); + Scintilla *view = luaL_checkview(L, 1); focus_view(view); #if GTK // ui.dialog() interferes with focus so gtk_widget_grab_focus() does not - // always work. If this is the case, ensure goto_view() is called. - if (!gtk_widget_has_focus(view)) goto_view(view); + // always work. If this is the case, ensure view_focused() is called. + if (!gtk_widget_has_focus(view)) view_focused(view, L); #endif return 0; } @@ -785,10 +763,9 @@ static int lui_goto_view(lua_State *L) { * @param n The index in the table to get. * @return integer */ -static int l_rawgetiint(lua_State *L, int index, int n) { +static int get_int_field(lua_State *L, int index, int n) { int i = (lua_rawgeti(L, index, n), lua_tointeger(L, -1)); - lua_pop(L, 1); // integer - return i; + return (lua_pop(L, 1), i); // integer } #if GTK @@ -801,91 +778,87 @@ static int l_rawgetiint(lua_State *L, int index, int n) { * item. * @param submenu Flag indicating whether or not this menu is a submenu. */ -static void l_pushmenu(lua_State *L, int index, GCallback callback, - int submenu) { - GtkWidget *menu = gtk_menu_new(), *menu_item = NULL, *submenu_root = NULL; - const char *label; - lua_pushvalue(L, index); // copy to stack top so relative indices can be used - if (lua_getfield(L, -1, "title") != LUA_TNIL || submenu) { // submenu title - label = !lua_isnil(L, -1) ? lua_tostring(L, -1) : "notitle"; +static void lua_pushmenu( + lua_State *L, int index, GCallback callback, int submenu) +{ + GtkWidget *menu = gtk_menu_new(), *submenu_root = NULL; + if (lua_getfield(L, index, "title") != LUA_TNIL || submenu) { // submenu title + const char *label = !lua_isnil(L, -1) ? lua_tostring(L, -1) : "notitle"; submenu_root = gtk_menu_item_new_with_mnemonic(label); gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu_root), menu); } lua_pop(L, 1); // title - for (size_t i = 1; i <= lua_rawlen(L, -1); i++) { - if (lua_rawgeti(L, -1, i) == LUA_TTABLE) { - int is_submenu = lua_getfield(L, -1, "title") != LUA_TNIL; - lua_pop(L, 1); // title - if (is_submenu) { - l_pushmenu(L, -1, callback, TRUE); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), - (GtkWidget *)lua_touserdata(L, -1)); - lua_pop(L, 1); // menu - } else { - lua_rawgeti(L, -1, 1), label = lua_tostring(L, -1), lua_pop(L, 1); - int menu_id = l_rawgetiint(L, -1, 2); - int key = l_rawgetiint(L, -1, 3), modifiers = l_rawgetiint(L, -1, 4); - if (label) { - menu_item = (*label) ? gtk_menu_item_new_with_mnemonic(label) - : gtk_separator_menu_item_new(); - if (*label && key > 0) - gtk_widget_add_accelerator(menu_item, "activate", accel, key, - modifiers, GTK_ACCEL_VISIBLE); - g_signal_connect(menu_item, "activate", callback, - GINT_TO_POINTER(menu_id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - } - } + for (size_t i = 1; i <= lua_rawlen(L, index); lua_pop(L, 1), i++) { + if (lua_rawgeti(L, -1, i) != LUA_TTABLE) continue; // popped on loop + int is_submenu = lua_getfield(L, -1, "title") != LUA_TNIL; + lua_pop(L, 1); // title + if (is_submenu) { + lua_pushmenu(L, -1, callback, TRUE); + gtk_menu_shell_append( + GTK_MENU_SHELL(menu), (GtkWidget *)lua_touserdata(L, -1)); + lua_pop(L, 1); // menu + continue; } - lua_pop(L, 1); // value + const char *label = (lua_rawgeti(L, -1, 1), lua_tostring(L, -1)); + lua_pop(L, 1); // label + if (!label) continue; + // Menu item table is of the form {label, id, key, modifiers}. + GtkWidget *menu_item = *label ? + gtk_menu_item_new_with_mnemonic(label) : gtk_separator_menu_item_new(); + if (*label && get_int_field(L, -1, 3) > 0) + gtk_widget_add_accelerator( + menu_item, "activate", accel, get_int_field(L, -1, 3), + get_int_field(L, -1, 4), GTK_ACCEL_VISIBLE); + g_signal_connect( + menu_item, "activate", callback, (void*)(long)get_int_field(L, -1, 2)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); } - lua_pop(L, 1); // table copy lua_pushlightuserdata(L, !submenu_root ? menu : submenu_root); } /** Signal for a menu item click. */ -static void m_clicked(GtkWidget*_, void *id) { - lL_event(lua, "menu_clicked", LUA_TNUMBER, GPOINTER_TO_INT(id), -1); +static void menu_clicked(GtkWidget *_, void *id) { + emit(lua, "menu_clicked", LUA_TNUMBER, (int)(long)id, -1); } #endif /** `ui.menu()` Lua function. */ -static int lui_menu(lua_State *L) { +static int menu(lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); #if GTK - return (l_pushmenu(L, -1, G_CALLBACK(m_clicked), FALSE), 1); + return (lua_pushmenu(L, -1, G_CALLBACK(menu_clicked), FALSE), 1); #elif CURSES return (lua_pushnil(L), 1); #endif } /** `ui.__index` Lua metamethod. */ -static int lui__index(lua_State *L) { +static int ui_index(lua_State *L) { const char *key = lua_tostring(L, 2); if (strcmp(key, "clipboard_text") == 0) { #if GTK char *text = gtk_clipboard_wait_for_text( - gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); + gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); lua_pushstring(L, text ? text : ""); if (text) free(text); #elif CURSES - char *text = get_clipboard(); + char *text = get_clipboard(); // may contain NUL bytes lua_pushlstring(L, text, scintilla_get_clipboard(focused_view, NULL)); free(text); #endif - } else if (strcmp(key, "maximized") == 0) + } else if (strcmp(key, "maximized") == 0) { #if GTK - lua_pushboolean(L, gdk_window_get_state(gtk_widget_get_window(window)) & - GDK_WINDOW_STATE_MAXIMIZED); + GdkWindowState state = gdk_window_get_state(gtk_widget_get_window(window)); + lua_pushboolean(L, state & GDK_WINDOW_STATE_MAXIMIZED); #elif CURSES lua_pushboolean(L, FALSE); #endif - else if (strcmp(key, "size") == 0) { -#if GTK + } else if (strcmp(key, "size") == 0) { int width, height; +#if GTK gtk_window_get_size(GTK_WINDOW(window), &width, &height); #elif CURSES - int width = COLS, height = LINES; + width = COLS, height = LINES; #endif lua_newtable(L); lua_pushinteger(L, width), lua_rawseti(L, -2, 1); @@ -901,17 +874,17 @@ static void set_statusbar_text(const char *text, int bar) { #if GTK if (statusbar[bar]) gtk_label_set_text(GTK_LABEL(statusbar[bar]), text); #elif CURSES - int start = (bar == 0) ? 0 : statusbar_length[0]; - int end = (bar == 0) ? COLS - statusbar_length[1] : COLS; + int start = bar == 0 ? 0 : statusbar_length[0]; + int end = bar == 0 ? COLS - statusbar_length[1] : COLS; for (int i = start; i < end; i++) mvaddch(LINES - 1, i, ' '); // clear int len = utf8strlen(text); - mvaddstr(LINES - 1, (bar == 0) ? 0 : COLS - len, text), refresh(); + mvaddstr(LINES - 1, bar == 0 ? 0 : COLS - len, text), refresh(); statusbar_length[bar] = len; #endif } /** `ui.__newindex` Lua metatable. */ -static int lui__newindex(lua_State *L) { +static int ui_newindex(lua_State *L) { const char *key = lua_tostring(L, 2); if (strcmp(key, "title") == 0) { #if GTK @@ -920,23 +893,25 @@ static int lui__newindex(lua_State *L) { for (int i = 0; i < COLS; i++) mvaddch(0, i, ' '); // clear titlebar mvaddstr(0, 0, lua_tostring(L, 3)), refresh(); #endif - } else if (strcmp(key, "clipboard_text") == 0) - set_clipboard(luaL_checkstring(L, 3)); - else if (strcmp(key, "statusbar_text") == 0) + } else if (strcmp(key, "clipboard_text") == 0) { + const char *text = luaL_checkstring(L, 3); + SS(focused_view, SCI_COPYTEXT, lua_rawlen(L, 3), (sptr_t)text); + } else if (strcmp(key, "statusbar_text") == 0) set_statusbar_text(lua_tostring(L, 3), 0); else if (strcmp(key, "bufstatusbar_text") == 0) set_statusbar_text(lua_tostring(L, 3), 1); else if (strcmp(key, "menubar") == 0) { #if GTK luaL_argcheck(L, lua_istable(L, 3), 3, "table of menus expected"); - GtkWidget *new_menubar = gtk_menu_bar_new(); // TODO: this leaks on error - for (size_t i = 1; i <= lua_rawlen(L, 3); i++) { - luaL_argcheck(L, lua_rawgeti(L, 3, i) == LUA_TLIGHTUSERDATA, 3, - "table of menus expected"); - GtkWidget *menu_item = (GtkWidget *)lua_touserdata(L, -1); - gtk_menu_shell_append(GTK_MENU_SHELL(new_menubar), menu_item); - lua_pop(L, 1); // value - } + for (size_t i = 1; i <= lua_rawlen(L, 3); lua_pop(L, 1), i++) + luaL_argcheck( + L, lua_rawgeti(L, 3, i) == LUA_TLIGHTUSERDATA, 3, + "table of menus expected"); // popped on loop + GtkWidget *new_menubar = gtk_menu_bar_new(); + for (size_t i = 1; i <= lua_rawlen(L, 3); lua_pop(L, 1), i++) + gtk_menu_shell_append( + GTK_MENU_SHELL(new_menubar), + (lua_rawgeti(L, 3, i), (GtkWidget *)lua_touserdata(L, -1))); // ^popped GtkWidget *vbox = gtk_widget_get_parent(menubar); gtk_container_remove(GTK_CONTAINER(vbox), menubar); gtk_box_pack_start(GTK_BOX(vbox), menubar = new_menubar, FALSE, FALSE, 0); @@ -944,26 +919,29 @@ static int lui__newindex(lua_State *L) { if (lua_rawlen(L, 3) > 0) gtk_widget_show_all(new_menubar); #if __APPLE__ gtkosx_application_set_menu_bar(osxapp, GTK_MENU_SHELL(new_menubar)); - gtk_widget_hide(new_menubar); + gtk_widget_hide(new_menubar); // hide in window #endif +//#elif CURSES + // TODO: menus #endif } else if (strcmp(key, "maximized") == 0) { #if GTK - lua_toboolean(L, 3) ? gtk_window_maximize(GTK_WINDOW(window)) - : gtk_window_unmaximize(GTK_WINDOW(window)); + lua_toboolean(L, 3) ? gtk_window_maximize( + GTK_WINDOW(window)) : gtk_window_unmaximize(GTK_WINDOW(window)); #endif } else if (strcmp(key, "size") == 0) { #if GTK - luaL_argcheck(L, lua_istable(L, 3) && lua_rawlen(L, 3) == 2, 3, - "{width, height} table expected"); - int w = l_rawgetiint(L, 3, 1), h = l_rawgetiint(L, 3, 2); + luaL_argcheck( + L, lua_istable(L, 3) && lua_rawlen(L, 3) == 2, 3, + "{width, height} table expected"); + int w = get_int_field(L, 3, 1), h = get_int_field(L, 3, 2); if (w > 0 && h > 0) gtk_window_resize(GTK_WINDOW(window), w, h); #endif } else if (strcmp(key, "tabs") == 0) { show_tabs = lua_toboolean(L, 3); #if GTK - gtk_widget_set_visible(tabbar, show_tabs && - gtk_notebook_get_n_pages(GTK_NOTEBOOK(tabbar)) > 1); + gtk_widget_set_visible( + tabbar, show_tabs && gtk_notebook_get_n_pages(GTK_NOTEBOOK(tabbar)) > 1); //#elif CURSES // TODO: tabs #endif @@ -977,39 +955,35 @@ static int lui__newindex(lua_State *L) { * @param index Stack index of the buffer. * @return Scintilla document */ -static sptr_t l_todoc(lua_State *L, int index) { +static sptr_t lua_todoc(lua_State *L, int index) { lua_getfield(L, index, "doc_pointer"); sptr_t doc = (sptr_t)lua_touserdata(L, -1); - lua_pop(L, 1); // doc_pointer - return doc; + return (lua_pop(L, 1), doc); // doc_pointer } /** - * Compares the Scintilla document at the given index with the global one and - * returns 0 if they are equivalent, -1 if that document belongs to the command - * entry, and any other value otherwise. - * In the last case, loads the document in `dummy_view` for non-global document - * use (unless it is already loaded). Raises and error if the value is not a - * Scintilla document or if the document no longer exists. + * Returns for the Scintilla document at the given index a suitable Scintilla + * view that can operate on it. + * For non-global, non-command entry documents, loads that document in + * `dummy_view` for non-global document use (unless it is already loaded). + * Raises and error if the value is not a Scintilla document or if the document + * no longer exists. * @param L The Lua state. * @param index The stack index of the Scintilla document. - * @return 0, -1, or the Scintilla document's pointer */ -static sptr_t l_globaldoccompare(lua_State *L, int index) { - luaL_argcheck(L, lL_hasmetatable(L, index, "ta_buffer"), index, - "Buffer expected"); - sptr_t doc = l_todoc(L, index); - if (doc != SS(focused_view, SCI_GETDOCPOINTER, 0, 0)) { - lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); - luaL_argcheck(L, (l_pushdoc(L, doc), lua_gettable(L, -2) != LUA_TNIL), - index, "this Buffer does not exist"); - lua_pop(L, 2); // buffer, ta_buffers - // TODO: technically, (uptr_t)-1 is a valid memory address, but in two's - // compliment, that is the absolute last byte, so unlikely to occur. - if (doc == SS(command_entry, SCI_GETDOCPOINTER, 0, 0)) return -1; - if (doc == SS(dummy_view, SCI_GETDOCPOINTER, 0, 0)) return doc; // keep - return (SS(dummy_view, SCI_SETDOCPOINTER, 0, doc), doc); - } else return 0; +static Scintilla *view_for_doc(lua_State *L, int index) { + luaL_argcheck( + L, is_type(L, index, "ta_buffer"), index, "Buffer expected"); + sptr_t doc = lua_todoc(L, index); + if (doc == SS(focused_view, SCI_GETDOCPOINTER, 0, 0)) return focused_view; + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + luaL_argcheck( + L, (lua_pushdoc(L, doc), lua_gettable(L, -2) != LUA_TNIL), index, + "this Buffer does not exist"); + lua_pop(L, 2); // buffer, ta_buffers + if (doc == SS(command_entry, SCI_GETDOCPOINTER, 0, 0)) return command_entry; + if (doc == SS(dummy_view, SCI_GETDOCPOINTER, 0, 0)) return dummy_view; + return (SS(dummy_view, SCI_SETDOCPOINTER, 0, doc), dummy_view); } /** @@ -1020,26 +994,22 @@ static sptr_t l_globaldoccompare(lua_State *L, int index) { * n of -1 represents the last document. * @param relative Flag indicating whether or not n is relative. */ -static void lL_gotodoc(lua_State *L, Scintilla *view, int n, int relative) { +static void goto_doc(lua_State *L, Scintilla *view, int n, int relative) { if (relative && n == 0) return; lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); if (relative) { - l_pushdoc(L, SS(view, SCI_GETDOCPOINTER, 0, 0)), lua_gettable(L, -2); + lua_pushdoc(L, SS(view, SCI_GETDOCPOINTER, 0, 0)), lua_gettable(L, -2); n = lua_tointeger(L, -1) + n; - lua_pop(L, 1); // index - if (n > (int)lua_rawlen(L, -1)) + if (n > (int)lua_rawlen(L, -2)) n = 1; else if (n < 1) - n = lua_rawlen(L, -1); - lua_rawgeti(L, -1, n); - } else { - luaL_argcheck(L, (n > 0 && n <= (int)lua_rawlen(L, -1)) || n == -1, 2, - "no Buffer exists at that index"); - lua_rawgeti(L, -1, (n > 0) ? n : (int)lua_rawlen(L, -1)); - } - sptr_t doc = l_todoc(L, -1); + n = lua_rawlen(L, -2); + lua_rawgeti(L, -2, n), lua_replace(L, -2); // index + } else lua_rawgeti(L, -1, n > 0 ? n : (int)lua_rawlen(L, -1)); + luaL_argcheck(L, !lua_isnil(L, -1), 2, "no Buffer exists at that index"); + sptr_t doc = lua_todoc(L, -1); SS(view, SCI_SETDOCPOINTER, 0, doc), sync_tabbar(); - l_setglobaldoc(L, doc); + lua_pushdoc(L, doc), lua_setglobal(L, "buffer"); lua_pop(L, 2); // buffer and buffers } @@ -1050,13 +1020,13 @@ static void lL_gotodoc(lua_State *L, Scintilla *view, int n, int relative) { static void register_command_entry_doc() { sptr_t doc = SS(command_entry, SCI_GETDOCPOINTER, 0, 0); lua_getfield(lua, LUA_REGISTRYINDEX, "ta_buffers"); - lua_getglobal(lua, "ui"); - lua_getfield(lua, -1, "command_entry"), lua_replace(lua, -2); - lua_pushstring(lua, "doc_pointer"); - lua_pushlightuserdata(lua, (sptr_t *)doc), lua_rawset(lua, -3); - // t[doc_pointer] = ce, t[0] = ce, t[ce] = 0 - lua_pushlightuserdata(lua, (sptr_t *)doc); - lua_pushvalue(lua, -2), lua_settable(lua, -4); + lua_getglobal(lua, "ui"), lua_getfield(lua, -1, "command_entry"), + lua_replace(lua, -2); + lua_pushstring(lua, "doc_pointer"), lua_pushlightuserdata(lua, (sptr_t *)doc), + lua_rawset(lua, -3); + // t[doc_pointer] = command_entry, t[0] = command_entry, t[command_entry] = 0 + lua_pushlightuserdata(lua, (sptr_t *)doc), lua_pushvalue(lua, -2), + lua_settable(lua, -4); lua_pushvalue(lua, -1), lua_rawseti(lua, -3, 0); lua_pushinteger(lua, 0), lua_settable(lua, -3); lua_pop(lua, 1); // buffers @@ -1064,30 +1034,28 @@ static void register_command_entry_doc() { /** * Removes the Scintilla document from the 'buffers' registry table. - * The document must have been previously added with lL_adddoc. + * The document must have been previously added with add_doc. * It is removed from any other views showing it first. Therefore, ensure the * length of 'buffers' is more than one unless quitting the application. * @param L The Lua state. * @param doc The Scintilla document to remove. - * @see lL_adddoc + * @see add_doc */ -static void lL_removedoc(lua_State *L, sptr_t doc) { +static void remove_doc(lua_State *L, sptr_t doc) { lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); - for (size_t i = 1; i <= lua_rawlen(L, -1); i++) { - lua_rawgeti(L, -1, i); - Scintilla *view = l_toview(L, -1); - if (doc == SS(view, SCI_GETDOCPOINTER, 0, 0)) lL_gotodoc(L, view, -1, TRUE); - lua_pop(L, 1); // value + for (size_t i = 1; i <= lua_rawlen(L, -1); lua_pop(L, 1), i++) { + Scintilla *view = (lua_rawgeti(L, -1, i), lua_toview(L, -1)); // ^popped + if (doc == SS(view, SCI_GETDOCPOINTER, 0, 0)) goto_doc(L, view, -1, TRUE); } lua_pop(L, 1); // views lua_newtable(L); lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); for (size_t i = 1; i <= lua_rawlen(L, -1); i++) - if (doc != (lua_rawgeti(L, -1, i), l_todoc(L, -1))) { - lua_getfield(L, -1, "doc_pointer"); + if (doc != (lua_rawgeti(L, -1, i), lua_todoc(L, -1))) { // t[doc_pointer] = buffer, t[#t + 1] = buffer, t[buffer] = #t - lua_pushvalue(L, -2), lua_rawseti(L, -5, lua_rawlen(L, -5) + 1); - lua_pushvalue(L, -2), lua_settable(L, -5); + lua_getfield(L, -1, "doc_pointer"), lua_pushvalue(L, -2), + lua_settable(L, -5); + lua_pushvalue(L, -1), lua_rawseti(L, -4, lua_rawlen(L, -4) + 1); lua_pushinteger(L, lua_rawlen(L, -3)), lua_settable(L, -4); } else { #if GTK @@ -1108,27 +1076,28 @@ static void lL_removedoc(lua_State *L, sptr_t doc) { /** * Removes the Scintilla buffer from the current Scintilla view. * @param doc The Scintilla document. - * @see lL_removedoc + * @see remove_doc */ static void delete_buffer(sptr_t doc) { - lL_removedoc(lua, doc), SS(dummy_view, SCI_SETDOCPOINTER, 0, 0); + remove_doc(lua, doc), SS(dummy_view, SCI_SETDOCPOINTER, 0, 0); SS(focused_view, SCI_RELEASEDOCUMENT, 0, doc); } /** `buffer.delete()` Lua function. */ -static int lbuffer_delete(lua_State *L) { - Scintilla *view = l_globaldoccompare(L, 1) == 0 ? focused_view : dummy_view; +static int delete_buffer_lua(lua_State *L) { + Scintilla *view = view_for_doc(L, 1); + luaL_argcheck(L, view != command_entry, 1, "cannot delete command entry"); sptr_t doc = SS(view, SCI_GETDOCPOINTER, 0, 0); lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); if (lua_rawlen(L, -1) == 1) new_buffer(0); - lL_gotodoc(L, focused_view, -1, TRUE); + goto_doc(L, focused_view, -1, TRUE); delete_buffer(doc); - lL_event(L, "buffer_after_switch", -1), lL_event(L, "buffer_deleted", -1); + emit(L, "buffer_after_switch", -1), emit(L, "buffer_deleted", -1); return 0; } /** `_G.buffer_new()` Lua function. */ -static int lbuffer_new(lua_State *L) { +static int new_buffer_lua(lua_State *L) { if (initing) luaL_error(L, "cannot create buffers during initialization"); new_buffer(0); lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); @@ -1143,7 +1112,7 @@ static int lbuffer_new(lua_State *L) { * @param type The Scintilla type to convert to. * @return Scintilla param */ -static sptr_t lL_checkscintillaparam(lua_State *L, int *arg, int type) { +static sptr_t luaL_checkscintilla(lua_State *L, int *arg, int type) { if (type == SSTRING) return (sptr_t)luaL_checkstring(L, (*arg)++); if (type == SBOOL) return lua_toboolean(L, (*arg)++); if (type >= SINT && type <= SKEYMOD) return luaL_checkinteger(L, (*arg)++); @@ -1162,27 +1131,29 @@ static sptr_t lL_checkscintillaparam(lua_State *L, int *arg, int type) { * @param arg The stack index of the first Scintilla parameter. Subsequent * elements will also be passed to Scintilla as needed. * @return number of results pushed onto the stack. - * @see lL_checkscintillaparam + * @see luaL_checkscintilla */ -static int l_callscintilla(lua_State *L, Scintilla *view, int msg, int wtype, - int ltype, int rtype, int arg) { +static int call_scintilla( + lua_State *L, Scintilla *view, int msg, int wtype, int ltype, int rtype, + int arg) +{ uptr_t wparam = 0; sptr_t lparam = 0, len = 0; - int params_needed = 2, string_return = FALSE; + int params_needed = 2, string_return = FALSE, nresults = 0; char *text = NULL; // Even though the SCI_PRIVATELEXERCALL interface has ltype int, the LPeg // lexer API uses different types depending on wparam. Modify ltype // appropriately. See the LPeg lexer API for more information. - if (msg == SCI_PRIVATELEXERCALL) { - ltype = SSTRINGRET; - int c = luaL_checkinteger(L, arg); - if (c == SCI_GETDIRECTFUNCTION || c == SCI_SETDOCPOINTER || - c == SCI_CHANGELEXERSTATE) - ltype = SINT; - else if (c == SCI_SETLEXERLANGUAGE || c == SCI_LOADLEXERLIBRARY) - ltype = SSTRING; - } + if (msg == SCI_PRIVATELEXERCALL) + switch (luaL_checkinteger(L, arg)) { + case SCI_GETDIRECTFUNCTION: case SCI_SETDOCPOINTER: + case SCI_CHANGELEXERSTATE: + ltype = SINT; break; + case SCI_SETLEXERLANGUAGE: case SCI_LOADLEXERLIBRARY: + ltype = SSTRING; break; + default: ltype = SSTRINGRET; + } // Set wParam and lParam appropriately for Scintilla based on wtype and ltype. if (wtype == SLEN && ltype == SSTRING) { @@ -1190,9 +1161,9 @@ static int l_callscintilla(lua_State *L, Scintilla *view, int msg, int wtype, lparam = (sptr_t)luaL_checkstring(L, arg); params_needed = 0; } else if (ltype == SSTRINGRET || rtype == SSTRINGRET) - string_return = TRUE, params_needed = (wtype == SLEN) ? 0 : 1; - if (params_needed > 0) wparam = lL_checkscintillaparam(L, &arg, wtype); - if (params_needed > 1) lparam = lL_checkscintillaparam(L, &arg, ltype); + string_return = TRUE, params_needed = wtype == SLEN ? 0 : 1; + if (params_needed > 0) wparam = luaL_checkscintilla(L, &arg, wtype); + if (params_needed > 1) lparam = luaL_checkscintilla(L, &arg, ltype); if (string_return) { // create a buffer for the return string len = SS(view, msg, wparam, 0); if (wtype == SLEN) wparam = len; @@ -1204,30 +1175,25 @@ static int l_callscintilla(lua_State *L, Scintilla *view, int msg, int wtype, // Send the message to Scintilla and return the appropriate values. sptr_t result = SS(view, msg, wparam, lparam); - arg = lua_gettop(L); - if (string_return) lua_pushlstring(L, text, len), free(text); + if (string_return) lua_pushlstring(L, text, len), nresults++, free(text); if (rtype > SVOID && rtype < SBOOL) - lua_pushinteger(L, result); + lua_pushinteger(L, result), nresults++; else if (rtype == SBOOL) - lua_pushboolean(L, result); - return lua_gettop(L) - arg; + lua_pushboolean(L, result), nresults++; + return nresults; } -static int lbuf_closure(lua_State *L) { - Scintilla *view = focused_view; +static int call_scintilla_lua(lua_State *L) { // If optional buffer/view argument is given, check it. - if (lua_istable(L, 1)) { - //if (lL_hasmetatable(L, 1, "ta_view")) - // lua_getfield(L, 1, "buffer"), lua_replace(L, 1); // use view.buffer - int result = l_globaldoccompare(L, 1); - if (result != 0) view = (result != -1) ? dummy_view : command_entry; - } + //if (is_type(L, 1, "ta_view")) + // lua_getfield(L, 1, "buffer"), lua_replace(L, 1); // use view.buffer // Interface table is of the form {msg, rtype, wtype, ltype}. - return l_callscintilla(L, view, l_rawgetiint(L, lua_upvalueindex(1), 1), - l_rawgetiint(L, lua_upvalueindex(1), 3), - l_rawgetiint(L, lua_upvalueindex(1), 4), - l_rawgetiint(L, lua_upvalueindex(1), 2), - lua_istable(L, 1) ? 2 : 1); + return call_scintilla( + L, lua_istable(L, 1) ? view_for_doc(L, 1) : focused_view, + get_int_field(L, lua_upvalueindex(1), 1), + get_int_field(L, lua_upvalueindex(1), 3), + get_int_field(L, lua_upvalueindex(1), 4), + get_int_field(L, lua_upvalueindex(1), 2), lua_istable(L, 1) ? 2 : 1); } #if GTK @@ -1237,44 +1203,47 @@ static int lbuf_closure(lua_State *L) { * @param event An optional GTK mouse button event. * @param k The ui table field that contains the context menu. */ -static void lL_showcontextmenu(lua_State *L, GdkEventButton *event, char *k) { +static void show_context_menu(lua_State *L, GdkEventButton *event, char *k) { if (lua_getglobal(L, "ui") == LUA_TTABLE) { if (lua_getfield(L, -1, k) == LUA_TLIGHTUSERDATA) { GtkWidget *menu = (GtkWidget *)lua_touserdata(L, -1); gtk_widget_show_all(menu); - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, - event ? event->button : 0, - gdk_event_get_time((GdkEvent *)event)); + gtk_menu_popup( + GTK_MENU(menu), NULL, NULL, NULL, NULL, event ? event->button : 0, + gdk_event_get_time((GdkEvent *)event)); } - lua_pop(L, 1); // ui context menu field + lua_pop(L, 2); // ui context menu field, ui } else lua_pop(L, 1); // non-table } /** Signal for a tab label mouse click. */ -static int t_tabbuttonpress(GtkWidget *label, GdkEventButton *event, void*__) { +static int tab_clicked(GtkWidget *label, GdkEventButton *event, void *__) { GtkNotebook *tabs = GTK_NOTEBOOK(tabbar); for (int i = 0; i < gtk_notebook_get_n_pages(tabs); i++) { GtkWidget *page = gtk_notebook_get_nth_page(tabs, i); if (label != gtk_notebook_get_tab_label(tabs, page)) continue; - lL_event(lua, "tab_clicked", LUA_TNUMBER, i + 1, LUA_TNUMBER, event->button, - event_mod(SHIFT), event_mod(CONTROL), event_mod(MOD1), - event_mod(META), -1); - if (event->button == 3) lL_showcontextmenu(lua, event, "tab_context_menu"); + emit( + lua, "tab_clicked", LUA_TNUMBER, i + 1, LUA_TNUMBER, event->button, + LUA_TBOOLEAN, event->state & GDK_SHIFT_MASK, + LUA_TBOOLEAN, event->state & GDK_CONTROL_MASK, + LUA_TBOOLEAN, event->state & GDK_MOD1_MASK, + LUA_TBOOLEAN, event->state & GDK_META_MASK, -1); + if (event->button == 3) show_context_menu(lua, event, "tab_context_menu"); } return TRUE; } #endif /** `buffer.__index` and `buffer.__newindex` Lua metamethods. */ -static int lbuf_property(lua_State *L) { - int newindex = (lua_gettop(L) == 3); - int is_buffer = lL_hasmetatable(L, 1, "ta_buffer"); // ta_buffer or ta_bufferp +static int buffer_metamethod(lua_State *L) { + int newindex = lua_gettop(L) == 3; + int is_buffer = is_type(L, 1, "ta_buffer"); // ta_buffer or ta_bufferp // If the key is a Scintilla function, return a callable closure. if (is_buffer && !newindex) { lua_getfield(L, LUA_REGISTRYINDEX, "ta_functions"); if (lua_pushvalue(L, 2), lua_gettable(L, -2) == LUA_TTABLE) - return (lua_pushcclosure(L, lbuf_closure, 1), 1); + return (lua_pushcclosure(L, call_scintilla_lua, 1), 1); lua_pop(L, 2); // non-table, ta_functions } @@ -1286,61 +1255,58 @@ static int lbuf_property(lua_State *L) { // indexible property. is_buffer ? lua_pushvalue(L, 2) : (void)lua_getfield(L, 1, "property"); if (lua_gettable(L, -2) == LUA_TTABLE) { - Scintilla *view = focused_view; // Interface table is of the form {get_id, set_id, rtype, wtype}. if (!is_buffer) lua_getfield(L, 1, "buffer"); - int result = l_globaldoccompare(L, is_buffer ? 1 : -1); - if (result != 0) view = (result != -1) ? dummy_view : command_entry; + Scintilla *view = view_for_doc(L, is_buffer ? 1 : -1); if (!is_buffer) lua_pop(L, 1); - if (is_buffer && l_rawgetiint(L, -1, 4) != SVOID) { // indexible property + if (is_buffer && get_int_field(L, -1, 4) != SVOID) { // indexible property lua_newtable(L); lua_pushvalue(L, 2), lua_setfield(L, -2, "property"); lua_pushvalue(L, 1), lua_setfield(L, -2, "buffer"); - l_setmetatable(L, -1, "ta_bufferp", lbuf_property, lbuf_property); + set_metatable(L, -1, "ta_bufferp", buffer_metamethod, buffer_metamethod); return 1; } - int msg = l_rawgetiint(L, -1, !newindex ? 1 : 2); - int wtype = l_rawgetiint(L, -1, !newindex ? 4 : 3); - int ltype = !newindex ? SVOID : l_rawgetiint(L, -1, 4); - int rtype = !newindex ? l_rawgetiint(L, -1, 3) : SVOID; + int msg = get_int_field(L, -1, !newindex ? 1 : 2); + int wtype = get_int_field(L, -1, !newindex ? 4 : 3); + int ltype = !newindex ? SVOID : get_int_field(L, -1, 4); + int rtype = !newindex ? get_int_field(L, -1, 3) : SVOID; if (newindex && (ltype != SVOID || wtype == SSTRING || wtype == SSTRINGRET || - msg == SCI_SETMARGINLEFT || msg == SCI_SETMARGINRIGHT)) { - int temp = (wtype != SSTRINGRET) ? wtype : SSTRING; + msg == SCI_SETMARGINLEFT || msg == SCI_SETMARGINRIGHT)) { + int temp = wtype != SSTRINGRET ? wtype : SSTRING; wtype = ltype, ltype = temp; } - luaL_argcheck(L, msg != 0, !newindex ? 2 : 3, - !newindex ? "write-only property" : "read-only property"); - return l_callscintilla(L, view, msg, wtype, ltype, rtype, - (!is_buffer || !newindex) ? 2 : 3); + luaL_argcheck( + L, msg != 0, !newindex ? 2 : 3, + !newindex ? "write-only property" : "read-only property"); + return call_scintilla( + L, view, msg, wtype, ltype, rtype, !is_buffer || !newindex ? 2 : 3); } else lua_pop(L, 2); // non-table, ta_properties if (strcmp(lua_tostring(L, 2), "tab_label") == 0 && - l_todoc(L, 1) != SS(command_entry, SCI_GETDOCPOINTER, 0, 0)) { + lua_todoc(L, 1) != SS(command_entry, SCI_GETDOCPOINTER, 0, 0)) { // Return or update the buffer's tab label. lua_getfield(L, 1, "tab_pointer"); #if GTK GtkNotebook *tabs = GTK_NOTEBOOK(tabbar); GtkWidget *tab = (GtkWidget *)lua_touserdata(L, -1); lua_pushstring(L, gtk_notebook_get_tab_label_text(tabs, tab)); - if (newindex) { - GtkWidget *box = gtk_event_box_new(); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(box), FALSE); // for WIN32 - GtkWidget *label = gtk_label_new(luaL_checkstring(L, 3)); - gtk_container_add(GTK_CONTAINER(box), label), gtk_widget_show(label); - gtk_notebook_set_tab_label(tabs, tab, box); - signal(box, "button-press-event", t_tabbuttonpress); - } + if (!newindex) return 1; + GtkWidget *box = gtk_event_box_new(); + gtk_event_box_set_visible_window(GTK_EVENT_BOX(box), FALSE); + GtkWidget *label = gtk_label_new(luaL_checkstring(L, 3)); + gtk_container_add(GTK_CONTAINER(box), label), gtk_widget_show(label); + gtk_notebook_set_tab_label(tabs, tab, box); + g_signal_connect(box, "button-press-event", G_CALLBACK(tab_clicked), NULL); //#elif CURSES // TODO: tabs #endif - return !newindex ? 1 : 0; + return 0; } else if (strcmp(lua_tostring(L, 2), "height") == 0 && - l_todoc(L, 1) == SS(command_entry, SCI_GETDOCPOINTER, 0, 0)) { + lua_todoc(L, 1) == SS(command_entry, SCI_GETDOCPOINTER, 0, 0)) { // Return or set the command entry's pixel height. - int height = luaL_optinteger(L, 3, 0); - int min_height = SS(command_entry, SCI_TEXTHEIGHT, 0, 0); - if (height < min_height) height = min_height; + int height = fmax( + luaL_optinteger(L, 3, 0), SS(command_entry, SCI_TEXTHEIGHT, 0, 0)); #if GTK GtkWidget *paned = gtk_widget_get_parent(command_entry); GtkAllocation allocation; @@ -1371,7 +1337,7 @@ static int lbuf_property(lua_State *L) { * @param L The Lua state. * @param doc The Scintilla document to add. */ -static void lL_adddoc(lua_State *L, sptr_t doc) { +static void add_doc(lua_State *L, sptr_t doc) { lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); lua_newtable(L); lua_pushlightuserdata(L, (sptr_t *)doc); // TODO: can this fail? @@ -1382,9 +1348,9 @@ static void lL_adddoc(lua_State *L, sptr_t doc) { //#elif CURSES // TODO: tabs #endif - l_setcfunction(L, -2, "delete", lbuffer_delete); - l_setcfunction(L, -2, "new", lbuffer_new); - l_setmetatable(L, -2, "ta_buffer", lbuf_property, lbuf_property); + lua_pushcfunction(L, delete_buffer_lua), lua_setfield(L, -3, "delete"); + lua_pushcfunction(L, new_buffer_lua) , lua_setfield(L, -3, "new"); + set_metatable(L, -2, "ta_buffer", buffer_metamethod, buffer_metamethod); // t[doc_pointer] = buffer, t[#t + 1] = buffer, t[buffer] = #t lua_pushvalue(L, -2), lua_settable(L, -4); lua_pushvalue(L, -1), lua_rawseti(L, -3, lua_rawlen(L, -3) + 1); @@ -1398,18 +1364,17 @@ static void lL_adddoc(lua_State *L, sptr_t doc) { * @param doc Almost always zero, except for the first Scintilla view created, * in which its doc pointer would be given here since it already has a * pre-created buffer. - * @see lL_adddoc + * @see add_doc */ static void new_buffer(sptr_t doc) { if (!doc) { - lL_event(lua, "buffer_before_switch", -1); - doc = SS(focused_view, SCI_CREATEDOCUMENT, 0, 0); // create the new document - lL_adddoc(lua, doc); - lL_gotodoc(lua, focused_view, -1, FALSE); - } else lL_adddoc(lua, doc), SS(focused_view, SCI_ADDREFDOCUMENT, 0, doc); + emit(lua, "buffer_before_switch", -1); + add_doc(lua, doc = SS(focused_view, SCI_CREATEDOCUMENT, 0, 0)); + goto_doc(lua, focused_view, -1, FALSE); + } else add_doc(lua, doc), SS(focused_view, SCI_ADDREFDOCUMENT, 0, doc); #if GTK // Add a tab to the tabbar. - l_pushdoc(lua, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)); + lua_pushdoc(lua, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)); lua_getfield(lua, -1, "tab_pointer"); GtkWidget *tab = (GtkWidget *)lua_touserdata(lua, -1); tab_sync = TRUE; @@ -1421,43 +1386,36 @@ static void new_buffer(sptr_t doc) { //#elif CURSES // TODO: tabs #endif - l_setglobaldoc(lua, doc); - if (!initing) lL_event(lua, "buffer_new", -1); + lua_pushdoc(lua, doc), lua_setglobal(lua, "buffer"); + if (!initing) emit(lua, "buffer_new", -1); } /** `_G.quit()` Lua function. */ -static int lquit(lua_State *L) { +static int quit(lua_State *L) { #if GTK GdkEventAny event = {GDK_DELETE, gtk_widget_get_window(window), TRUE}; gdk_event_put((GdkEvent *)&event); #elif CURSES - quit = TRUE; + quitting = !emit(lua, "quit", -1); #endif return 0; } -// stpcpy() does not exist on _WIN32, but exists on other platforms with or -// without feature test macros. In order to minimize confusion, just define it. -char *stpcpy_(char *dest, const char *src) { - return (strcpy(dest, src), dest + strlen(src)); -} - /** * Loads and runs the given file. * @param L The Lua state. * @param filename The file name relative to textadept_home. * @return 1 if there are no errors or 0 in case of errors. */ -static int lL_dofile(lua_State *L, const char *filename) { +static int run_file(lua_State *L, const char *filename) { char *file = malloc(strlen(textadept_home) + 1 + strlen(filename) + 1); - stpcpy_(stpcpy_(stpcpy_(file, textadept_home), "/"), filename); + sprintf(file, "%s/%s", textadept_home, filename); int ok = (luaL_dofile(L, file) == LUA_OK); if (!ok) { const char *argv[] = { - "--title", "Initialization Error", "--informative-text", - lua_tostring(L, -1), "--no-cancel", "--icon", "gtk-dialog-error" + "--title", "Initialization Error", "--text", lua_tostring(L, -1) }; - free(gtdialog(GTDIALOG_OK_MSGBOX, 7, argv)); + free(gtdialog(GTDIALOG_TEXTBOX, 7, argv)); lua_settop(L, 0); } free(file); @@ -1465,22 +1423,24 @@ static int lL_dofile(lua_State *L, const char *filename) { } /** `_G.reset()` Lua function. */ -static int lreset(lua_State *L) { +static int reset(lua_State *L) { int persist_ref = (lua_newtable(L), luaL_ref(L, LUA_REGISTRYINDEX)); - lua_rawgeti(L, LUA_REGISTRYINDEX, persist_ref); // lL_event will unref - lL_event(L, "reset_before", LUA_TTABLE, luaL_ref(L, LUA_REGISTRYINDEX), -1); - lL_init(L, 0, NULL, TRUE); - l_setglobalview(L, focused_view); - l_setglobaldoc(L, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)); + lua_rawgeti(L, LUA_REGISTRYINDEX, persist_ref); // emit will unref + emit(L, "reset_before", LUA_TTABLE, luaL_ref(L, LUA_REGISTRYINDEX), -1); + init_lua(L, 0, NULL, TRUE); + lua_pushview(L, focused_view), lua_setglobal(L, "view"); + lua_pushdoc(L, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)), + lua_setglobal(L, "buffer"); lua_pushnil(L), lua_setglobal(L, "arg"); - lL_dofile(L, "init.lua"), lL_event(L, "initialized", -1); + run_file(L, "init.lua"), emit(L, "initialized", -1); lua_getfield(L, LUA_REGISTRYINDEX, "ta_arg"), lua_setglobal(L, "arg"); - lL_event(L, "reset_after", LUA_TTABLE, persist_ref, -1); + emit(L, "reset_after", LUA_TTABLE, persist_ref, -1); return 0; } -static int emit_timeout(void *data) { - int *refs = (int *)data; +/** Runs the timeout function passed to `_G.timeout()`. */ +static int timed_out(void *userdata) { + int *refs = (int *)userdata; lua_rawgeti(lua, LUA_REGISTRYINDEX, refs[0]); // function int nargs = 0, repeat = TRUE; while (refs[++nargs]) lua_rawgeti(lua, LUA_REGISTRYINDEX, refs[nargs]); @@ -1488,65 +1448,53 @@ static int emit_timeout(void *data) { if (!ok || !lua_toboolean(lua, -1)) { while (--nargs >= 0) luaL_unref(lua, LUA_REGISTRYINDEX, refs[nargs]); repeat = FALSE; - if (!ok) lL_event(lua, "error", LUA_TSTRING, lua_tostring(lua, -1), -1); + if (!ok) emit(lua, "error", LUA_TSTRING, lua_tostring(lua, -1), -1); } - lua_pop(lua, 1); // result - return repeat; + return (lua_pop(lua, 1), repeat); // result } /** `_G.timeout()` Lua function. */ -static int ltimeout(lua_State *L) { +static int add_timeout(lua_State *L) { #if GTK - double timeout = luaL_checknumber(L, 1); - luaL_argcheck(L, timeout > 0, 1, "timeout must be > 0"); + double interval = luaL_checknumber(L, 1); + luaL_argcheck(L, interval > 0, 1, "interval must be > 0"); luaL_argcheck(L, lua_isfunction(L, 2), 2, "function expected"); int n = lua_gettop(L), *refs = (int *)calloc(n, sizeof(int)); for (int i = 2; i <= n; i++) lua_pushvalue(L, i), refs[i - 2] = luaL_ref(L, LUA_REGISTRYINDEX); - return (g_timeout_add(timeout * 1000, emit_timeout, (void *)refs), 0); + return (g_timeout_add(interval * 1000, timed_out, (void *)refs), 0); #elif CURSES return luaL_error(L, "not implemented in this environment"); #endif } /** `string.iconv()` Lua function. */ -static int lstring_iconv(lua_State *L) { +static int iconv_lua(lua_State *L) { size_t inbytesleft = 0; char *inbuf = (char *)luaL_checklstring(L, 1, &inbytesleft); const char *to = luaL_checkstring(L, 2), *from = luaL_checkstring(L, 3); iconv_t cd = iconv_open(to, from); - if (cd != (iconv_t)-1) { - // Ensure the minimum buffer size can hold a potential output BOM and one - // multibyte character. - size_t bufsiz = 4 + ((inbytesleft > MB_LEN_MAX) ? inbytesleft : MB_LEN_MAX); - char *outbuf = malloc(bufsiz + 1), *p = outbuf; - size_t outbytesleft = bufsiz; - int n = 1; // concat this many converted strings - while (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) == (size_t)-1) - if (errno == E2BIG && p - outbuf > 0) { - // Buffer was too small to store converted string. Push the partially - // converted string for later concatenation. - lua_checkstack(L, 2), lua_pushlstring(L, outbuf, p - outbuf), n++; - p = outbuf, outbytesleft = bufsiz; - } else free(outbuf), iconv_close(cd), luaL_error(L, "conversion failed"); - lua_pushlstring(L, outbuf, p - outbuf); - lua_concat(L, n); - free(outbuf), iconv_close(cd); - } else luaL_error(L, "invalid encoding(s)"); + if (cd == (iconv_t)-1) luaL_error(L, "invalid encoding(s)"); + // Ensure the minimum buffer size can hold a potential output BOM and one + // multibyte character. + size_t bufsiz = 4 + (inbytesleft > MB_LEN_MAX ? inbytesleft : MB_LEN_MAX); + char *outbuf = malloc(bufsiz + 1), *p = outbuf; + size_t outbytesleft = bufsiz; + int n = 1; // concat this many converted strings + while (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) == (size_t)-1) + if (errno == E2BIG && p - outbuf > 0) { + // Buffer was too small to store converted string. Push the partially + // converted string for later concatenation. + lua_checkstack(L, 2), lua_pushlstring(L, outbuf, p - outbuf), n++; + p = outbuf, outbytesleft = bufsiz; + } else free(outbuf), iconv_close(cd), luaL_error(L, "conversion failed"); + lua_pushlstring(L, outbuf, p - outbuf); + lua_concat(L, n); + free(outbuf), iconv_close(cd); return 1; } /** - * Clears a table at the given valid index by setting all of its keys to nil. - * @param L The Lua state. - * @param index The absolute stack index of the table. - */ -static void lL_cleartable(lua_State *L, int index) { - while (lua_pushnil(L), lua_next(L, index)) - lua_pushnil(L), lua_replace(L, -2), lua_rawset(L, index); -} - -/** * Initializes or re-initializes the Lua state. * Populates the state with global variables and functions, then runs the * 'core/init.lua' script. @@ -1556,7 +1504,7 @@ static void lL_cleartable(lua_State *L, int index) { * @param reinit Flag indicating whether or not to reinitialize the Lua state. * @return TRUE on success, FALSE otherwise. */ -static int lL_init(lua_State *L, int argc, char **argv, int reinit) { +static int init_lua(lua_State *L, int argc, char **argv, int reinit) { if (!reinit) { lua_newtable(L); for (int i = 0; i < argc; i++) @@ -1566,49 +1514,50 @@ static int lL_init(lua_State *L, int argc, char **argv, int reinit) { lua_newtable(L), lua_setfield(L, LUA_REGISTRYINDEX, "ta_views"); } else { // clear package.loaded and _G lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); - lL_cleartable(L, lua_gettop(L)); - lua_pop(L, 1); // package.loaded + while (lua_pushnil(L), lua_next(L, -2)) + lua_pushnil(L), lua_replace(L, -2), lua_rawset(L, -3); // clear lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); - lL_cleartable(L, lua_gettop(L)); - lua_pop(L, 1); // _G + while (lua_pushnil(L), lua_next(L, -2)) + lua_pushnil(L), lua_replace(L, -2), lua_rawset(L, -3); // clear + lua_pop(L, 2); // package.loaded, _G } - lua_pushinteger(L, (sptr_t)L), lua_setglobal(L, "_LUA"); + lua_pushinteger(L, (sptr_t)L), lua_setglobal(L, "_LUA"); // for LPeg lexer luaL_openlibs(L); - lL_openlib(L, lpeg), lL_openlib(L, lfs); + luaL_requiref(L, "lpeg", luaopen_lpeg, 1), lua_pop(L, 1); + luaL_requiref(L, "lfs", luaopen_lfs, 1), lua_pop(L, 1); lua_newtable(L); lua_newtable(L); - l_setcfunction(L, -1, "find_next", lfind_next); - l_setcfunction(L, -1, "find_prev", lfind_prev); - l_setcfunction(L, -1, "focus", lfind_focus); - l_setcfunction(L, -1, "replace", lfind_replace); - l_setcfunction(L, -1, "replace_all", lfind_replace_all); - l_setmetatable(L, -1, "ta_find", lfind__index, lfind__newindex); + lua_pushcfunction(L, click_find_next), lua_setfield(L, -2, "find_next"); + lua_pushcfunction(L, click_find_prev), lua_setfield(L, -2, "find_prev"); + lua_pushcfunction(L, click_replace), lua_setfield(L, -2, "replace"); + lua_pushcfunction(L, click_replace_all), lua_setfield(L, -2, "replace_all"); + lua_pushcfunction(L, focus_find), lua_setfield(L, -2, "focus"); + set_metatable(L, -1, "ta_find", find_index, find_newindex); lua_setfield(L, -2, "find"); if (!reinit) { lua_newtable(L); - l_setcfunction(L, -1, "focus", lce_focus); - l_setmetatable(L, -1, "ta_buffer", lbuf_property, lbuf_property); - } else { - lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); - lua_rawgeti(L, -1, 0), lua_replace(L, -2); // _BUFFERS[0] - } + lua_pushcfunction(L, focus_command_entry), lua_setfield(L, -2, "focus"); + set_metatable(L, -1, "ta_buffer", buffer_metamethod, buffer_metamethod); + } else + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"), lua_rawgeti(L, -1, 0), + lua_replace(L, -2); // _BUFFERS[0] == command_entry lua_setfield(L, -2, "command_entry"); - l_setcfunction(L, -1, "dialog", lui_dialog); - l_setcfunction(L, -1, "get_split_table", lui_get_split_table); - l_setcfunction(L, -1, "goto_view", lui_goto_view); - l_setcfunction(L, -1, "menu", lui_menu); - l_setmetatable(L, -1, "ta_ui", lui__index, lui__newindex); + lua_pushcfunction(L, dialog), lua_setfield(L, -2, "dialog"); + lua_pushcfunction(L, get_split_table), lua_setfield(L, -2, "get_split_table"); + lua_pushcfunction(L, goto_view), lua_setfield(L, -2, "goto_view"); + lua_pushcfunction(L, menu), lua_setfield(L, -2, "menu"); + set_metatable(L, -1, "ta_ui", ui_index, ui_newindex); lua_setglobal(L, "ui"); lua_getglobal(L, "_G"); - l_setcfunction(L, -1, "quit", lquit); - l_setcfunction(L, -1, "reset", lreset); - l_setcfunction(L, -1, "timeout", ltimeout); + lua_pushcfunction(L, quit), lua_setfield(L, -2, "quit"); + lua_pushcfunction(L, reset), lua_setfield(L, -2, "reset"); + lua_pushcfunction(L, add_timeout), lua_setfield(L, -2, "timeout"); lua_pop(L, 1); // _G lua_getglobal(L, "string"); - l_setcfunction(L, -1, "iconv", lstring_iconv); + lua_pushcfunction(L, iconv_lua), lua_setfield(L, -2, "iconv"); lua_pop(L, 1); // string lua_getfield(L, LUA_REGISTRYINDEX, "ta_arg"), lua_setglobal(L, "arg"); @@ -1637,29 +1586,29 @@ static int lL_init(lua_State *L, int argc, char **argv, int reinit) { #endif lua_pushstring(L, charset), lua_setglobal(L, "_CHARSET"); - if (!lL_dofile(L, "core/init.lua")) return (lua_close(L), FALSE); + if (!run_file(L, "core/init.lua")) return (lua_close(L), FALSE); lua_getglobal(L, "_SCINTILLA"); - lua_getfield(L, -1, "constants"); - lua_setfield(L, LUA_REGISTRYINDEX, "ta_constants"); - lua_getfield(L, -1, "functions"); - lua_setfield(L, LUA_REGISTRYINDEX, "ta_functions"); - lua_getfield(L, -1, "properties"); - lua_setfield(L, LUA_REGISTRYINDEX, "ta_properties"); + lua_getfield(L, -1, "constants"), + lua_setfield(L, LUA_REGISTRYINDEX, "ta_constants"); + lua_getfield(L, -1, "functions"), + lua_setfield(L, LUA_REGISTRYINDEX, "ta_functions"); + lua_getfield(L, -1, "properties"), + lua_setfield(L, LUA_REGISTRYINDEX, "ta_properties"); lua_pop(L, 1); // _SCINTILLA return TRUE; } #if GTK /** Signal for a Textadept window focus change. */ -static int w_focus(GtkWidget*_, GdkEventFocus*__, void*___) { +static int window_focused(GtkWidget *_, GdkEventFocus *__, void *___) { if (command_entry_focused) return FALSE; // keep command entry focused if (focused_view && !gtk_widget_has_focus(focused_view)) gtk_widget_grab_focus(focused_view); - return (lL_event(lua, "focus", -1), FALSE); + return (emit(lua, "focus", -1), FALSE); } /** Signal for a Textadept keypress. */ -static int w_keypress(GtkWidget*_, GdkEventKey *event, void*__) { +static int window_keypress(GtkWidget *_, GdkEventKey *event, void *__) { if (event->keyval == GDK_Escape && gtk_widget_get_visible(findbox) && !gtk_widget_has_focus(command_entry)) { gtk_widget_hide(findbox); @@ -1671,20 +1620,20 @@ static int w_keypress(GtkWidget*_, GdkEventKey *event, void*__) { /** * Removes the Scintilla view from the 'views' registry table. - * The view must have been previously added with lL_addview. + * The view must have been previously added with add_view. * @param L The Lua state. * @param view The Scintilla view to remove. - * @see lL_addview + * @see add_view */ -static void lL_removeview(lua_State *L, Scintilla *view) { +static void remove_view(lua_State *L, Scintilla *view) { lua_newtable(L); lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); for (size_t i = 1; i <= lua_rawlen(L, -1); i++) { - if (view != (lua_rawgeti(L, -1, i), l_toview(L, -1))) { - lua_getfield(L, -1, "widget_pointer"); - // vs[userdata] = v, vs[#vs + 1] = v, vs[v] = #vs - lua_pushvalue(L, -2), lua_rawseti(L, -5, lua_rawlen(L, -5) + 1); - lua_pushvalue(L, -2), lua_settable(L, -5); + if (view != (lua_rawgeti(L, -1, i), lua_toview(L, -1))) { + // t[widget_pointer] = view, t[#t + 1] = view, t[view] = #t + lua_getfield(L, -1, "widget_pointer"), lua_pushvalue(L, -2), + lua_settable(L, -5); + lua_pushvalue(L, -1), lua_rawseti(L, -4, lua_rawlen(L, -4) + 1); lua_pushinteger(L, lua_rawlen(L, -3)), lua_settable(L, -4); } else lua_pop(L, 1); // view } @@ -1696,25 +1645,12 @@ static void lL_removeview(lua_State *L, Scintilla *view) { /** * Removes a Scintilla view. * @param view The Scintilla view to remove. - * @see lL_removeview + * @see remove_view */ static void delete_view(Scintilla *view) { - lL_removeview(lua, view); - scintilla_delete(view); + remove_view(lua, view), scintilla_delete(view); } -#if GTK -/** - * Removes all Scintilla views from the given pane and deletes them. - * @param pane The GTK pane to remove Scintilla views from. - * @see delete_view - */ -static void remove_views_from_pane(GtkWidget *pane) { - GtkWidget *child1 = child(1, pane), *child2 = child(2, pane); - GTK_IS_PANED(child1) ? remove_views_from_pane(child1) : delete_view(child1); - GTK_IS_PANED(child2) ? remove_views_from_pane(child2) : delete_view(child2); -} -#elif CURSES /** * Removes all Scintilla views from the given pane and deletes them along with * the child panes themselves. @@ -1722,13 +1658,21 @@ static void remove_views_from_pane(GtkWidget *pane) { * @see delete_view */ static void remove_views_from_pane(Pane *pane) { +#if GTK + GtkWidget *child1 = gtk_paned_get_child1(GTK_PANED(pane)); + GtkWidget *child2 = gtk_paned_get_child2(GTK_PANED(pane)); + GTK_IS_PANED(child1) ? remove_views_from_pane(child1) : delete_view(child1); + GTK_IS_PANED(child2) ? remove_views_from_pane(child2) : delete_view(child2); +#elif CURSES if (pane->type == VSPLIT || pane->type == HSPLIT) { remove_views_from_pane(pane->child1), remove_views_from_pane(pane->child2); delwin(pane->win), pane->win = NULL; // delete split bar } else delete_view(pane->view); free(pane); +#endif } +#if CURSES /** * Resizes and repositions a pane. * @param pane the pane to resize and move. @@ -1737,20 +1681,20 @@ static void remove_views_from_pane(Pane *pane) { * @param y The y-coordinate to place the pane at. * @param x The x-coordinate to place the pane at. */ -static void pane_resize(Pane *pane, int rows, int cols, int y, int x) { +static void resize_pane(Pane *pane, int rows, int cols, int y, int x) { if (pane->type == VSPLIT) { - int ssize = pane->split_size * cols / max_(pane->cols, 1); - if (ssize < 1 || ssize >= cols - 1) ssize = (ssize < 1) ? 1 : cols - 2; + int ssize = pane->split_size * cols / fmax(pane->cols, 1); + if (ssize < 1 || ssize >= cols - 1) ssize = ssize < 1 ? 1 : cols - 2; pane->split_size = ssize; - pane_resize(pane->child1, rows, ssize, y, x); - pane_resize(pane->child2, rows, cols - ssize - 1, y, x + ssize + 1); + resize_pane(pane->child1, rows, ssize, y, x); + resize_pane(pane->child2, rows, cols - ssize - 1, y, x + ssize + 1); wresize(pane->win, rows, 1), mvwin(pane->win, y, x + ssize); // split bar } else if (pane->type == HSPLIT) { - int ssize = pane->split_size * rows / max_(pane->rows, 1); - if (ssize < 1 || ssize >= rows - 1) ssize = (ssize < 1) ? 1 : rows - 2; + int ssize = pane->split_size * rows / fmax(pane->rows, 1); + if (ssize < 1 || ssize >= rows - 1) ssize = ssize < 1 ? 1 : rows - 2; pane->split_size = ssize; - pane_resize(pane->child1, ssize, cols, y, x); - pane_resize(pane->child2, rows - ssize - 1, cols, y + ssize + 1, x); + resize_pane(pane->child1, ssize, cols, y, x); + resize_pane(pane->child2, rows - ssize - 1, cols, y + ssize + 1, x); wresize(pane->win, 1, cols), mvwin(pane->win, y + ssize, x); // split bar } else wresize(pane->win, rows, cols), mvwin(pane->win, y, x); pane->rows = rows, pane->cols = cols, pane->y = y, pane->x = x; @@ -1761,24 +1705,25 @@ static void pane_resize(Pane *pane, int rows, int cols, int y, int x) { * @param pane The pane that contains the view to unsplit. * @param view The view to unsplit. * @param parent The parent of pane. Used recursively. + * @return TRUE if the view can be split and was; FALSE otherwise * @see unsplit_view */ -static int pane_unsplit_view(Pane *pane, Scintilla *view, Pane *parent) { - if (pane->type == SINGLE) { - if (pane->view != view) return FALSE; - remove_views_from_pane((pane == parent->child1) ? parent->child2 - : parent->child1); - delwin(parent->win); // delete split bar - // Inherit child's properties. - parent->type = pane->type, parent->split_size = pane->split_size; - parent->win = pane->win, parent->view = pane->view; - parent->child1 = pane->child1, parent->child2 = pane->child2; - free(pane); - // Update. - pane_resize(parent, parent->rows, parent->cols, parent->y, parent->x); - return TRUE; - } else return pane_unsplit_view(pane->child1, view, pane) || - pane_unsplit_view(pane->child2, view, pane); +static int unsplit_pane(Pane *pane, Scintilla *view, Pane *parent) { + if (pane->type != SINGLE) + return unsplit_pane(pane->child1, view, pane) || + unsplit_pane(pane->child2, view, pane); + if (pane->view != view) return FALSE; + remove_views_from_pane( + pane == parent->child1 ? parent->child2 : parent->child1); + delwin(parent->win); // delete split bar + // Inherit child's properties. + parent->type = pane->type, parent->split_size = pane->split_size; + parent->win = pane->win, parent->view = pane->view; + parent->child1 = pane->child1, parent->child2 = pane->child2; + free(pane); + // Update. + resize_pane(parent, parent->rows, parent->cols, parent->y, parent->x); + return TRUE; } #endif @@ -1786,6 +1731,7 @@ static int pane_unsplit_view(Pane *pane, Scintilla *view, Pane *parent) { * Unsplits the pane the given Scintilla view is in and keeps the view. * All views in the other pane are deleted. * @param view The Scintilla view to keep when unsplitting. + * @return TRUE if the view was split; FALSE otherwise * @see remove_views_from_pane * @see delete_view */ @@ -1793,7 +1739,9 @@ static int unsplit_view(Scintilla *view) { #if GTK GtkWidget *pane = gtk_widget_get_parent(view); if (!GTK_IS_PANED(pane)) return FALSE; - GtkWidget *other = (child(1, pane) != view) ? child(1, pane) : child(2, pane); + GtkWidget *other = gtk_paned_get_child1(GTK_PANED(pane)) != view ? + gtk_paned_get_child1(GTK_PANED(pane)) : + gtk_paned_get_child2(GTK_PANED(pane)); g_object_ref(view), g_object_ref(other); gtk_container_remove(GTK_CONTAINER(pane), view); gtk_container_remove(GTK_CONTAINER(pane), other); @@ -1801,8 +1749,9 @@ static int unsplit_view(Scintilla *view) { GtkWidget *parent = gtk_widget_get_parent(pane); gtk_container_remove(GTK_CONTAINER(parent), pane); if (GTK_IS_PANED(parent)) - !child(1, parent) ? gtk_paned_add1(GTK_PANED(parent), view) - : gtk_paned_add2(GTK_PANED(parent), view); + !gtk_paned_get_child1(GTK_PANED(parent)) ? + gtk_paned_add1(GTK_PANED(parent), view) : + gtk_paned_add2(GTK_PANED(parent), view); else gtk_container_add(GTK_CONTAINER(parent), view); //gtk_widget_show_all(parent); @@ -1810,7 +1759,7 @@ static int unsplit_view(Scintilla *view) { g_object_unref(view), g_object_unref(other); #elif CURSES if (pane->type == SINGLE) return FALSE; - pane_unsplit_view(pane, view, NULL), scintilla_noutrefresh(view); + unsplit_pane(pane, view, NULL), scintilla_noutrefresh(view); #endif return TRUE; } @@ -1821,12 +1770,12 @@ static int unsplit_view(Scintilla *view) { * documents, before closing the state. * @param L The Lua state. */ -static void l_close(lua_State *L) { +static void close_lua(lua_State *L) { closing = TRUE; while (unsplit_view(focused_view)) ; // need space to fix compiler warning lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); for (size_t i = 1; i <= lua_rawlen(L, -1); i++) - lua_rawgeti(L, -1, i), delete_buffer(l_todoc(L, -1)), lua_pop(L, 1); + lua_rawgeti(L, -1, i), delete_buffer(lua_todoc(L, -1)), lua_pop(L, 1); lua_pop(L, 1); // buffers scintilla_delete(focused_view), scintilla_delete(dummy_view); scintilla_delete(command_entry); @@ -1838,11 +1787,11 @@ static void l_close(lua_State *L) { * Signal for exiting Textadept. * Generates a 'quit' event. * Closes the Lua state and releases resources. - * @see l_close + * @see close_lua */ -static int w_exit(GtkWidget*_, GdkEventAny*__, void*___) { - if (lL_event(lua, "quit", -1)) return TRUE; - l_close(lua); +static int exiting(GtkWidget *_, GdkEventAny *__, void *___) { + if (emit(lua, "quit", -1)) return TRUE; // halt + close_lua(lua); scintilla_release_resources(); return (gtk_main_quit(), FALSE); } @@ -1852,25 +1801,25 @@ static int w_exit(GtkWidget*_, GdkEventAny*__, void*___) { * Signal for opening files from OSX. * Generates an 'appleevent_odoc' event for each document sent. */ -static int w_open_osx(GtkosxApplication*_, char *path, void*__) { - return (lL_event(lua, "appleevent_odoc", LUA_TSTRING, path, -1), TRUE); +static int open_file(GtkosxApplication*_, char *path, void*__) { + return (emit(lua, "appleevent_odoc", LUA_TSTRING, path, -1), TRUE); } /** * Signal for block terminating Textadept from OSX. * Generates a 'quit' event. */ -static int w_exit_osx(GtkosxApplication*_, void*__) { - return lL_event(lua, "quit", -1); +static int terminating(GtkosxApplication *_, void *__) { + return emit(lua, "quit", -1); } /** * Signal for terminating Textadept from OSX. * Closes the Lua state and releases resources. - * @see l_close + * @see close_lua */ -static void w_quit_osx(GtkosxApplication*_, void*__) { - l_close(lua); +static void terminate(GtkosxApplication *_, void *__) { + close_lua(lua); scintilla_release_resources(); g_object_unref(osxapp); gtk_main_quit(); @@ -1883,9 +1832,9 @@ static void w_quit_osx(GtkosxApplication*_, void*__) { * the specified buffer. * Generates 'buffer_before_switch' and 'buffer_after_switch' events. */ -static void t_tabchange(GtkNotebook*_, GtkWidget*__, int page_num, void*___) { - if (tab_sync) return; - lL_event(lua, "tab_clicked", LUA_TNUMBER, page_num + 1, LUA_TNUMBER, 1, -1); +static void tab_changed(GtkNotebook *_, GtkWidget *__, int tab_num, void *___) { + if (!tab_sync) + emit(lua, "tab_clicked", LUA_TNUMBER, tab_num + 1, LUA_TNUMBER, 1, -1); } #endif // if GTK @@ -1893,16 +1842,16 @@ static void t_tabchange(GtkNotebook*_, GtkWidget*__, int page_num, void*___) { * Emits a Scintilla notification event. * @param L The Lua state. * @param n The Scintilla notification struct. - * @see lL_event + * @see emit */ -static void lL_notify(lua_State *L, struct SCNotification *n) { +static void emit_notification(lua_State *L, SCNotification *n) { lua_newtable(L); lua_pushinteger(L, n->nmhdr.code), lua_setfield(L, -2, "code"); lua_pushinteger(L, n->position), lua_setfield(L, -2, "position"); lua_pushinteger(L, n->ch), lua_setfield(L, -2, "ch"); lua_pushinteger(L, n->modifiers), lua_setfield(L, -2, "modifiers"); - lua_pushinteger(L, n->modificationType); - lua_setfield(L, -2, "modification_type"); + lua_pushinteger(L, n->modificationType), + lua_setfield(L, -2, "modification_type"); if (n->text && n->length) lua_pushlstring(L, n->text, n->length), lua_setfield(L, -2, "text"); else @@ -1914,63 +1863,66 @@ static void lL_notify(lua_State *L, struct SCNotification *n) { //lua_pushinteger(L, n->lParam), lua_setfield(L, -2, "lParam"); lua_pushinteger(L, n->line), lua_setfield(L, -2, "line"); //lua_pushinteger(L, n->foldLevelNow), lua_setfield(L, -2, "fold_level_now"); - //lua_pushinteger(L, n->foldLevelPrev); - //lua_setfield(L, -2, "fold_level_prev"); + //lua_pushinteger(L, n->foldLevelPrev), + // lua_setfield(L, -2, "fold_level_prev"); lua_pushinteger(L, n->margin), lua_setfield(L, -2, "margin"); lua_pushinteger(L, n->x), lua_setfield(L, -2, "x"); lua_pushinteger(L, n->y), lua_setfield(L, -2, "y"); //lua_pushinteger(L, n->token), lua_setfield(L, -2, "token"); - //lua_pushinteger(L, n->annotationLinesAdded); - //lua_setfield(L, -2, "annotation_lines_added"); + //lua_pushinteger(L, n->annotationLinesAdded), + // lua_setfield(L, -2, "annotation_lines_added"); lua_pushinteger(L, n->updated), lua_setfield(L, -2, "updated"); - //lua_pushinteger(L, n->listCompletionMethod); - //lua_setfield(L, -2, "list_completion_method"); - lL_event(L, "SCN", LUA_TTABLE, luaL_ref(L, LUA_REGISTRYINDEX), -1); + //lua_pushinteger(L, n->listCompletionMethod), + // lua_setfield(L, -2, "list_completion_method"); + emit(L, "SCN", LUA_TTABLE, luaL_ref(L, LUA_REGISTRYINDEX), -1); } /** Signal for a Scintilla notification. */ -static void s_notify(Scintilla *view, int _, void *lParam, void*__) { - struct SCNotification *n = (struct SCNotification *)lParam; +static void notified( + Scintilla *view, int _, SCNotification *n, void *userdata) +{ if (focused_view == view || n->nmhdr.code == SCN_URIDROPPED) { - if (focused_view != view) goto_view(view); - lL_notify(lua, n); + if (focused_view != view) view_focused(view, (lua_State *)userdata); + emit_notification((lua_State *)userdata, n); } else if (n->nmhdr.code == SCN_FOCUSIN) - goto_view(view); + view_focused(view, (lua_State *)userdata); } #if GTK /** Signal for a Scintilla keypress. */ -static int s_keypress(GtkWidget*_, GdkEventKey *event, void*__) { - return lL_event(lua, "keypress", LUA_TNUMBER, event->keyval, event_mod(SHIFT), - event_mod(CONTROL), event_mod(MOD1), event_mod(META), - event_mod(LOCK), -1); +static int keypress(GtkWidget *_, GdkEventKey *event, void *__) { + return emit( + lua, "keypress", LUA_TNUMBER, event->keyval, + LUA_TBOOLEAN, event->state & GDK_SHIFT_MASK, + LUA_TBOOLEAN, event->state & GDK_CONTROL_MASK, + LUA_TBOOLEAN, event->state & GDK_MOD1_MASK, + LUA_TBOOLEAN, event->state & GDK_META_MASK, + LUA_TBOOLEAN, event->state & GDK_LOCK_MASK, -1); } /** Signal for a Scintilla mouse click. */ -static int s_buttonpress(GtkWidget*_, GdkEventButton *event, void*__) { - if (event->type == GDK_BUTTON_PRESS && event->button == 3) - return (lL_showcontextmenu(lua, event, "context_menu"), TRUE); - return FALSE; +static int mouse_clicked(GtkWidget*_, GdkEventButton *event, void*__) { + if (event->type != GDK_BUTTON_PRESS || event->button != 3) return FALSE; + return (show_context_menu(lua, event, "context_menu"), TRUE); } #endif /** `view.goto_buffer()` Lua function. */ -static int lview_goto_buffer(lua_State *L) { - Scintilla *view = lL_checkview(L, 1), *prev_view = focused_view; - luaL_argcheck(L, lua_istable(L, 2) || lua_isnumber(L, 2), 2, - "Buffer or relative index expected"); +static int goto_doc_lua(lua_State *L) { + Scintilla *view = luaL_checkview(L, 1), *prev_view = focused_view; int relative = lua_isnumber(L, 2); if (!relative) { lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); lua_pushvalue(L, 2), lua_gettable(L, -2), lua_replace(L, 2); - luaL_argcheck(L, lua_isnumber(L, 2), 2, "Buffer expected"); + luaL_argcheck( + L, lua_isnumber(L, 2), 2, "Buffer or relative index expected"); } // If the indexed view is not currently focused, temporarily focus it so // `_G.buffer` in handlers is accurate. if (view != focused_view) focus_view(view); - if (!initing) lL_event(L, "buffer_before_switch", -1); - lL_gotodoc(L, view, lua_tointeger(L, 2), relative); - if (!initing) lL_event(L, "buffer_after_switch", -1); + if (!initing) emit(L, "buffer_before_switch", -1); + goto_doc(L, view, lua_tointeger(L, 2), relative); + if (!initing) emit(L, "buffer_after_switch", -1); if (focused_view != prev_view) focus_view(prev_view); return 0; } @@ -1980,7 +1932,7 @@ static int lview_goto_buffer(lua_State *L) { * Creates a new pane that contains a Scintilla view. * @param view The Scintilla view to place in the pane. */ -static Pane *pane_new(Scintilla *view) { +static Pane *new_pane(Scintilla *view) { Pane *p = (Pane *)calloc(1, sizeof(Pane)); p->type = SINGLE, p->win = scintilla_get_window(view), p->view = view; return p; @@ -1995,31 +1947,34 @@ static Pane *pane_new(Scintilla *view) { * @param view2 The second Scintilla view to place in the split view. * @see split_view */ -static int pane_split_view(Pane *pane, int vertical, Scintilla *view, - Scintilla *view2) { - if (pane->type == SINGLE) { - if (view != pane->view) return FALSE; - Pane *child1 = pane_new(view), *child2 = pane_new(view2); - pane->type = vertical ? VSPLIT : HSPLIT; - pane->child1 = child1, pane->child2 = child2, pane->view = NULL; - // Resize children and create a split bar. - if (vertical) { - pane->split_size = pane->cols / 2; - pane_resize(child1, pane->rows, pane->split_size, pane->y, pane->x); - pane_resize(child2, pane->rows, pane->cols - pane->split_size - 1, - pane->y, pane->x + pane->split_size + 1); - pane->win = newwin(pane->rows, 1, pane->y, pane->x + pane->split_size); - } else { - pane->split_size = pane->rows / 2; - pane_resize(child1, pane->split_size, pane->cols, pane->y, pane->x); - pane_resize(child2, pane->rows - pane->split_size - 1, pane->cols, - pane->y + pane->split_size + 1, pane->x); - pane->win = newwin(1, pane->cols, pane->y + pane->split_size, pane->x); - } - pane_refresh(pane); - return TRUE; - } else return pane_split_view(pane->child1, vertical, view, view2) || - pane_split_view(pane->child2, vertical, view, view2); +static int split_pane( + Pane *pane, int vertical, Scintilla *view, Scintilla *view2) +{ + if (pane->type != SINGLE) + return split_pane(pane->child1, vertical, view, view2) || + split_pane(pane->child2, vertical, view, view2); + if (view != pane->view) return FALSE; + Pane *child1 = new_pane(view), *child2 = new_pane(view2); + pane->type = vertical ? VSPLIT : HSPLIT; + pane->child1 = child1, pane->child2 = child2, pane->view = NULL; + // Resize children and create a split bar. + if (vertical) { + pane->split_size = pane->cols / 2; + resize_pane(child1, pane->rows, pane->split_size, pane->y, pane->x); + resize_pane( + child2, pane->rows, pane->cols - pane->split_size - 1, pane->y, + pane->x + pane->split_size + 1); + pane->win = newwin(pane->rows, 1, pane->y, pane->x + pane->split_size); + } else { + pane->split_size = pane->rows / 2; + resize_pane(child1, pane->split_size, pane->cols, pane->y, pane->x); + resize_pane( + child2, pane->rows - pane->split_size - 1, pane->cols, + pane->y + pane->split_size + 1, pane->x); + pane->win = newwin(1, pane->cols, pane->y + pane->split_size, pane->x); + } + refresh_pane(pane); + return TRUE; } #endif @@ -2033,6 +1988,7 @@ static int pane_split_view(Pane *pane, int vertical, Scintilla *view, static void split_view(Scintilla *view, int vertical) { sptr_t curdoc = SS(view, SCI_GETDOCPOINTER, 0, 0); int first_line = SS(view, SCI_GETFIRSTVISIBLELINE, 0, 0); + int x_offset = SS(view, SCI_GETXOFFSET, 0, 0); int current_pos = SS(view, SCI_GETCURRENTPOS, 0, 0); int anchor = SS(view, SCI_GETANCHOR, 0, 0); @@ -2056,24 +2012,24 @@ static void split_view(Scintilla *view, int vertical) { while (gtk_events_pending()) gtk_main_iteration(); // ensure view2 is painted #elif CURSES Scintilla *view2 = new_view(curdoc); - pane_split_view(pane, vertical, view, view2); + split_pane(pane, vertical, view, view2); #endif focus_view(view2); SS(view2, SCI_SETSEL, anchor, current_pos); int new_first_line = SS(view2, SCI_GETFIRSTVISIBLELINE, 0, 0); SS(view2, SCI_LINESCROLL, first_line - new_first_line, 0); + SS(view2, SCI_SETXOFFSET, x_offset, 0); } /** `view.split()` Lua function. */ -static int lview_split(lua_State *L) { - split_view(lL_checkview(L, 1), lua_toboolean(L, 2)); - // Return old view, new view. - return (lua_pushvalue(L, 1), lua_getglobal(L, "view"), 2); +static int split_view_lua(lua_State *L) { + split_view(luaL_checkview(L, 1), lua_toboolean(L, 2)); + return (lua_pushvalue(L, 1), lua_getglobal(L, "view"), 2); // old, new view } /** `view.unsplit()` Lua function. */ -static int lview_unsplit(lua_State *L) { - return (lua_pushboolean(L, unsplit_view(lL_checkview(L, 1))), 1); +static int unsplit_view_lua(lua_State *L) { + return (lua_pushboolean(L, unsplit_view(luaL_checkview(L, 1))), 1); } #if CURSES @@ -2082,38 +2038,36 @@ static int lview_unsplit(lua_State *L) { * @param pane The pane that contains the desired view. * @param view The view to get the parent pane of. */ -static Pane *pane_get_parent(Pane *pane, Scintilla *view) { +static Pane *get_parent_pane(Pane *pane, Scintilla *view) { if (pane->type == SINGLE) return NULL; if (pane->child1->view == view || pane->child2->view == view) return pane; - Pane *parent = pane_get_parent(pane->child1, view); - return parent ? parent : pane_get_parent(pane->child2, view); + Pane *parent = get_parent_pane(pane->child1, view); + return parent ? parent : get_parent_pane(pane->child2, view); } #endif /** `view.__index` and `view.__newindex` Lua metamethods. */ -static int lview_property(lua_State *L) { +static int view_metamethod(lua_State *L) { int newindex = (lua_gettop(L) == 3); - Scintilla *view = lL_checkview(L, 1); + Scintilla *view = luaL_checkview(L, 1); if (strcmp(lua_tostring(L, 2), "buffer") == 0) { luaL_argcheck(L, !newindex, 2, "read-only property"); - return (l_pushdoc(L, SS(view, SCI_GETDOCPOINTER, 0, 0)), 1); + return (lua_pushdoc(L, SS(view, SCI_GETDOCPOINTER, 0, 0)), 1); } else if (strcmp(lua_tostring(L, 2), "size") == 0) { - int size = newindex ? luaL_checkinteger(L, 3) : 0; - if (size < 0) size = 0; #if GTK GtkWidget *pane = gtk_widget_get_parent(view); - if (GTK_IS_PANED(pane) && !newindex) - lua_pushinteger(L, gtk_paned_get_position(GTK_PANED(pane))); + GtkPaned *p = GTK_IS_PANED(pane) ? GTK_PANED(pane) : NULL; + if (p && newindex) + gtk_paned_set_position(p, fmax(luaL_checkinteger(L, 3), 0)); else if (!newindex) - lua_pushnil(L); - else if (GTK_IS_PANED(pane) && newindex) - gtk_paned_set_position(GTK_PANED(pane), size); + p ? lua_pushinteger(L, gtk_paned_get_position(p)) : lua_pushnil(L); #elif CURSES - Pane *p = pane_get_parent(pane, view); - if (!newindex) + Pane *p = get_parent_pane(pane, view); + if (p && newindex) { + p->split_size = fmax(luaL_checkinteger(L, 3), 0); + resize_pane(p, p->rows, p->cols, p->y, p->x); + } else if (!newindex) p ? lua_pushinteger(L, p->split_size) : lua_pushnil(L); - else if (p) - p->split_size = size, pane_resize(p, p->rows, p->cols, p->y, p->x); #endif return !newindex ? 1 : 0; //} else { @@ -2123,12 +2077,12 @@ static int lview_property(lua_State *L) { // lua_getfield(L, LUA_REGISTRYINDEX, "ta_functions"); // lua_getfield(L, LUA_REGISTRYINDEX, "ta_constants"); // if ((lua_pushvalue(L, 2), lua_gettable(L, -4) == LUA_TTABLE) || - // (!newindex && - // ((lua_pushvalue(L, 2), lua_gettable(L, -4) == LUA_TTABLE) || - // (lua_pushvalue(L, 2), lua_gettable(L, -4) == LUA_TNUMBER)))) { + // (!newindex && + // ((lua_pushvalue(L, 2), lua_gettable(L, -4) == LUA_TTABLE) || + // (lua_pushvalue(L, 2), lua_gettable(L, -4) == LUA_TNUMBER)))) { // lua_settop(L, !newindex ? 2 : 3); - // l_pushdoc(L, SS(view, SCI_GETDOCPOINTER, 0, 0)), lua_replace(L, 1); - // return lbuf_property(L); + // lua_pushdoc(L, SS(view, SCI_GETDOCPOINTER, 0, 0)), lua_replace(L, 1); + // return buffer_metamethod(L); // } else lua_settop(L, !newindex ? 2 : 3); } return !newindex ? (lua_rawget(L, 1), 1) : (lua_rawset(L, 1), 0); @@ -2139,16 +2093,16 @@ static int lview_property(lua_State *L) { * @param L The Lua state. * @param view The Scintilla view to add. */ -static void lL_addview(lua_State *L, Scintilla *view) { +static void add_view(lua_State *L, Scintilla *view) { lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); lua_newtable(L); lua_pushlightuserdata(L, view); lua_pushvalue(L, -1), lua_setfield(L, -3, "widget_pointer"); - l_setcfunction(L, -2, "goto_buffer", lview_goto_buffer); - l_setcfunction(L, -2, "split", lview_split); - l_setcfunction(L, -2, "unsplit", lview_unsplit); - l_setmetatable(L, -2, "ta_view", lview_property, lview_property); - // vs[userdata] = v, vs[#vs + 1] = v, vs[v] = #vs + lua_pushcfunction(L, goto_doc_lua), lua_setfield(L, -3, "goto_buffer"); + lua_pushcfunction(L, split_view_lua), lua_setfield(L, -3, "split"); + lua_pushcfunction(L, unsplit_view_lua), lua_setfield(L, -3, "unsplit"); + set_metatable(L, -2, "ta_view", view_metamethod, view_metamethod); + // t[widget_pointer] = view, t[#t + 1] = view, t[view] = #t lua_pushvalue(L, -2), lua_settable(L, -4); lua_pushvalue(L, -1), lua_rawseti(L, -3, lua_rawlen(L, -3) + 1); lua_pushinteger(L, lua_rawlen(L, -2)), lua_settable(L, -3); @@ -2161,25 +2115,25 @@ static void lL_addview(lua_State *L, Scintilla *view) { * @param doc The document to load in the new view. Almost never zero, except * for the first Scintilla view created, in which there is no doc pointer. * @return Scintilla view - * @see lL_addview + * @see add_view */ static Scintilla *new_view(sptr_t doc) { #if GTK Scintilla *view = scintilla_new(); gtk_widget_set_size_request(view, 1, 1); // minimum size - signal(view, SCINTILLA_NOTIFY, s_notify); - signal(view, "key-press-event", s_keypress); - signal(view, "button-press-event", s_buttonpress); + g_signal_connect(view, SCINTILLA_NOTIFY, G_CALLBACK(notified), lua); + g_signal_connect(view, "key-press-event", G_CALLBACK(keypress), NULL); + g_signal_connect(view, "button-press-event", G_CALLBACK(mouse_clicked), NULL); #elif CURSES - Scintilla *view = scintilla_new(s_notify); + Scintilla *view = scintilla_new(notified, lua); #endif SS(view, SCI_USEPOPUP, SC_POPUP_NEVER, 0); - lL_addview(lua, view); - l_setglobalview(lua, view); + add_view(lua, view); + lua_pushview(lua, view), lua_setglobal(lua, "view"); if (doc) SS(view, SCI_SETDOCPOINTER, 0, doc); focus_view(view), focused_view = view; if (!doc) new_buffer(SS(view, SCI_GETDOCPOINTER, 0, 0)); - if (!initing) lL_event(lua, "view_new", -1); + if (!initing) emit(lua, "view_new", -1); return view; } @@ -2187,59 +2141,60 @@ static Scintilla *new_view(sptr_t doc) { /** Creates the Find box. */ static GtkWidget *new_findbox() { findbox = gtk_table_new(2, 6, FALSE); - find_store = gtk_list_store_new(1, G_TYPE_STRING); - repl_store = gtk_list_store_new(1, G_TYPE_STRING); + find_history = gtk_list_store_new(1, G_TYPE_STRING); + repl_history = gtk_list_store_new(1, G_TYPE_STRING); - flabel = gtk_label_new_with_mnemonic("_Find:"); - rlabel = gtk_label_new_with_mnemonic("R_eplace:"); + // Note: label text is set later via Lua for localization purposes. + find_label = gtk_label_new(""), repl_label = gtk_label_new(""); GtkWidget *find_combo = gtk_combo_box_entry_new_with_model( - GTK_TREE_MODEL(find_store), 0); + GTK_TREE_MODEL(find_history), 0); gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(find_combo), 0); gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(find_combo), FALSE); find_entry = gtk_bin_get_child(GTK_BIN(find_combo)); gtk_entry_set_activates_default(GTK_ENTRY(find_entry), TRUE); GtkWidget *replace_combo = gtk_combo_box_entry_new_with_model( - GTK_TREE_MODEL(repl_store), 0); + GTK_TREE_MODEL(repl_history), 0); gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(replace_combo), 0); gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(replace_combo), FALSE); - replace_entry = gtk_bin_get_child(GTK_BIN(replace_combo)); - gtk_entry_set_activates_default(GTK_ENTRY(replace_entry), TRUE); - g_object_unref(find_store), g_object_unref(repl_store); - fnext_button = gtk_button_new_with_mnemonic("Find _Next"); - fprev_button = gtk_button_new_with_mnemonic("Find _Prev"); - r_button = gtk_button_new_with_mnemonic("_Replace"); - ra_button = gtk_button_new_with_mnemonic("Replace _All"); - match_case = gtk_check_button_new_with_mnemonic("_Match case"); - whole_word = gtk_check_button_new_with_mnemonic("_Whole word"); - regex = gtk_check_button_new_with_mnemonic("Rege_x"); - in_files = gtk_check_button_new_with_mnemonic("_In files"); - - gtk_label_set_mnemonic_widget(GTK_LABEL(flabel), find_entry); - gtk_label_set_mnemonic_widget(GTK_LABEL(rlabel), replace_entry); - - attach(find_combo, 1, 2, 0, 1, FILL(EXPAND), FILL(SHRINK), 5, 0); - attach(replace_combo, 1, 2, 1, 2, FILL(EXPAND), FILL(SHRINK), 5, 0); - attach(flabel, 0, 1, 0, 1, FILL(SHRINK), FILL(SHRINK), 5, 0); - attach(rlabel, 0, 1, 1, 2, FILL(SHRINK), FILL(SHRINK), 5, 0); - attach(fnext_button, 2, 3, 0, 1, FILL(SHRINK), FILL(SHRINK), 0, 0); - attach(fprev_button, 3, 4, 0, 1, FILL(SHRINK), FILL(SHRINK), 0, 0); - attach(r_button, 2, 3, 1, 2, FILL(SHRINK), FILL(SHRINK), 0, 0); - attach(ra_button, 3, 4, 1, 2, FILL(SHRINK), FILL(SHRINK), 0, 0); - attach(match_case, 4, 5, 0, 1, FILL(SHRINK), FILL(SHRINK), 5, 0); - attach(whole_word, 4, 5, 1, 2, FILL(SHRINK), FILL(SHRINK), 5, 0); - attach(regex, 5, 6, 0, 1, FILL(SHRINK), FILL(SHRINK), 5, 0); - attach(in_files, 5, 6, 1, 2, FILL(SHRINK), FILL(SHRINK), 5, 0); - - signal(fnext_button, "clicked", f_clicked); - signal(fprev_button, "clicked", f_clicked); - signal(r_button, "clicked", f_clicked); - signal(ra_button, "clicked", f_clicked); - - gtk_widget_set_can_default(fnext_button, TRUE); - gtk_widget_set_can_focus(fnext_button, FALSE); - gtk_widget_set_can_focus(fprev_button, FALSE); - gtk_widget_set_can_focus(r_button, FALSE); - gtk_widget_set_can_focus(ra_button, FALSE); + repl_entry = gtk_bin_get_child(GTK_BIN(replace_combo)); + gtk_entry_set_activates_default(GTK_ENTRY(repl_entry), TRUE); + g_object_unref(find_history), g_object_unref(repl_history); + // TODO: ideally no #define here. + #define button gtk_button_new_with_mnemonic + #define checkbutton gtk_check_button_new_with_mnemonic + find_next = button(""), find_prev = button(""); + replace = button(""), replace_all = button(""); + match_case = checkbutton(""), whole_word = checkbutton(""); + regex = checkbutton(""), in_files = checkbutton(""); + + gtk_label_set_mnemonic_widget(GTK_LABEL(find_label), find_entry); + gtk_label_set_mnemonic_widget(GTK_LABEL(repl_label), repl_entry); + + GtkTable *table = GTK_TABLE(findbox); + int expand = GTK_FILL | GTK_EXPAND, shrink = GTK_FILL | GTK_SHRINK; + gtk_table_attach(table, find_combo, 1, 2, 0, 1, expand, shrink, 5, 0); + gtk_table_attach(table, replace_combo, 1, 2, 1, 2, expand, shrink, 5, 0); + gtk_table_attach(table, find_label, 0, 1, 0, 1, shrink, shrink, 5, 0); + gtk_table_attach(table, repl_label, 0, 1, 1, 2, shrink, shrink, 5, 0); + gtk_table_attach(table, find_next, 2, 3, 0, 1, shrink, shrink, 0, 0); + gtk_table_attach(table, find_prev, 3, 4, 0, 1, shrink, shrink, 0, 0); + gtk_table_attach(table, replace, 2, 3, 1, 2, shrink, shrink, 0, 0); + gtk_table_attach(table, replace_all, 3, 4, 1, 2, shrink, shrink, 0, 0); + gtk_table_attach(table, match_case, 4, 5, 0, 1, shrink, shrink, 5, 0); + gtk_table_attach(table, whole_word, 4, 5, 1, 2, shrink, shrink, 5, 0); + gtk_table_attach(table, regex, 5, 6, 0, 1, shrink, shrink, 5, 0); + gtk_table_attach(table, in_files, 5, 6, 1, 2, shrink, shrink, 5, 0); + + g_signal_connect(find_next, "clicked", G_CALLBACK(find_clicked), lua); + g_signal_connect(find_prev, "clicked", G_CALLBACK(find_clicked), lua); + g_signal_connect(replace, "clicked", G_CALLBACK(find_clicked), lua); + g_signal_connect(replace_all, "clicked", G_CALLBACK(find_clicked), lua); + + gtk_widget_set_can_default(find_next, TRUE); + gtk_widget_set_can_focus(find_next, FALSE); + gtk_widget_set_can_focus(find_prev, FALSE); + gtk_widget_set_can_focus(replace, FALSE); + gtk_widget_set_can_focus(replace_all, FALSE); gtk_widget_set_can_focus(match_case, FALSE); gtk_widget_set_can_focus(whole_word, FALSE); gtk_widget_set_can_focus(regex, FALSE); @@ -2250,13 +2205,12 @@ static GtkWidget *new_findbox() { /** * Emit "Escape" key for the command entry on focus lost unless the window is - * losing focus. + * losing focus or the application is quitting. */ -static int wc_focusout(GtkWidget *widget, GdkEvent*_, void*__) { - if (widget == window && command_entry_focused) return TRUE; - if (widget == command_entry && !closing) - lL_event(lua, "keypress", LUA_TNUMBER, GDK_Escape, -1); - return FALSE; +static int focus_lost(GtkWidget *widget, GdkEvent *_, void *__) { + if (widget == window && command_entry_focused) return TRUE; // halt + if (widget != command_entry || closing) return FALSE; + return (emit(lua, "keypress", LUA_TNUMBER, GDK_Escape, -1), FALSE); } #endif // if GTK @@ -2271,11 +2225,10 @@ static void new_window() { GList *icon_list = NULL; const char *icons[] = {"16x16", "32x32", "48x48", "64x64", "128x128"}; for (int i = 0; i < 5; i++) { - char *icon_file = g_strconcat(textadept_home, "/core/images/ta_", icons[i], - ".png", NULL); + char icon_file[strlen(textadept_home) + 30]; + sprintf(icon_file, "%s/core/images/ta_%s.png", textadept_home, icons[i]); GdkPixbuf *pb = gdk_pixbuf_new_from_file(icon_file, NULL); if (pb) icon_list = g_list_prepend(icon_list, pb); - g_free(icon_file); } gtk_window_set_default_icon_list(icon_list); g_list_foreach(icon_list, (GFunc)g_object_unref, NULL); @@ -2284,18 +2237,22 @@ static void new_window() { window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_set_name(window, "textadept"); gtk_window_set_default_size(GTK_WINDOW(window), 1000, 600); - signal(window, "delete-event", w_exit); - signal(window, "focus-in-event", w_focus); - signal(window, "focus-out-event", wc_focusout); - signal(window, "key-press-event", w_keypress); + g_signal_connect(window, "delete-event", G_CALLBACK(exiting), NULL); + g_signal_connect(window, "focus-in-event", G_CALLBACK(window_focused), NULL); + g_signal_connect(window, "focus-out-event", G_CALLBACK(focus_lost), NULL); + g_signal_connect( + window, "key-press-event", G_CALLBACK(window_keypress), NULL); gtdialog_set_parent(GTK_WINDOW(window)); accel = gtk_accel_group_new(); #if (__APPLE__ && !CURSES) gtkosx_application_set_use_quartz_accelerators(osxapp, FALSE); - osx_signal(osxapp, "NSApplicationOpenFile", w_open_osx); - osx_signal(osxapp, "NSApplicationBlockTermination", w_exit_osx); - osx_signal(osxapp, "NSApplicationWillTerminate", w_quit_osx); + g_signal_connect( + osxapp, "NSApplicationOpenFile", G_CALLBACK(open_file), NULL); + g_signal_connect( + osxapp, "NSApplicationBlockTermination", G_CALLBACK(terminating), NULL); + g_signal_connect( + osxapp, "NSApplicationWillTerminate", G_CALLBACK(terminate), NULL); #endif GtkWidget *vbox = gtk_vbox_new(FALSE, 0); @@ -2305,7 +2262,7 @@ static void new_window() { gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); tabbar = gtk_notebook_new(); - signal(tabbar, "switch-page", t_tabchange); + g_signal_connect(tabbar, "switch-page", G_CALLBACK(tab_changed), NULL); gtk_notebook_set_scrollable(GTK_NOTEBOOK(tabbar), TRUE); gtk_box_pack_start(GTK_BOX(vbox), tabbar, FALSE, FALSE, 0); gtk_widget_set_can_focus(tabbar, FALSE); @@ -2325,11 +2282,13 @@ static void new_window() { command_entry = scintilla_new(); gtk_widget_set_size_request(command_entry, 1, 1); - signal(command_entry, "key-press-event", s_keypress); - signal(command_entry, "focus-out-event", wc_focusout); + g_signal_connect( + command_entry, "key-press-event", G_CALLBACK(keypress), NULL); + g_signal_connect( + command_entry, "focus-out-event", G_CALLBACK(focus_lost), NULL); gtk_paned_add2(GTK_PANED(paned), command_entry); - gtk_container_child_set(GTK_CONTAINER(paned), command_entry, "shrink", FALSE, - NULL); + gtk_container_child_set( + GTK_CONTAINER(paned), command_entry, "shrink", FALSE, NULL); GtkWidget *hboxs = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), hboxs, FALSE, FALSE, 1); @@ -2346,18 +2305,18 @@ static void new_window() { dummy_view = scintilla_new(); #elif CURSES - pane = pane_new(new_view(0)), pane_resize(pane, LINES - 2, COLS, 1, 0); - command_entry = scintilla_new(NULL); + pane = new_pane(new_view(0)), resize_pane(pane, LINES - 2, COLS, 1, 0); + command_entry = scintilla_new(NULL, NULL); wresize(scintilla_get_window(command_entry), 1, COLS); mvwin(scintilla_get_window(command_entry), LINES - 2, 0); - dummy_view = scintilla_new(NULL); + dummy_view = scintilla_new(NULL, NULL); #endif register_command_entry_doc(); } #if GTK && _WIN32 /** Reads and processes a remote Textadept's command line arguments. */ -static int pipe_read(GIOChannel *source, GIOCondition _, HANDLE pipe) { +static int read_pipe(GIOChannel *source, GIOCondition _, HANDLE pipe) { char *buf; size_t len; g_io_channel_read_to_end(source, &buf, &len, NULL); @@ -2373,27 +2332,28 @@ static DWORD WINAPI pipe_listener(HANDLE pipe) { if (pipe != INVALID_HANDLE_VALUE && ConnectNamedPipe(pipe, NULL)) { int fd = _open_osfhandle((intptr_t)pipe, _O_RDONLY); GIOChannel *channel = g_io_channel_win32_new_fd(fd); - g_io_add_watch(channel, G_IO_IN, pipe_read, pipe); - g_io_channel_unref(channel); + g_io_add_watch(channel, G_IO_IN, read_pipe, pipe), + g_io_channel_unref(channel); } return 0; } /** Replacement for `g_application_run()` that handles multiple instances. */ -static void win32_application_run() { - HANDLE pipe = CreateFile("\\\\.\\pipe\\textadept.editor", GENERIC_WRITE, 0, - NULL, OPEN_EXISTING, 0, NULL); - char cwd[FILENAME_MAX + 1]; +int g_application_run(GApplication *_, int __, char **___) { + HANDLE pipe = CreateFile(ID, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + char cwd[FILENAME_MAX + 1]; // TODO: is this big enough? GetCurrentDirectory(FILENAME_MAX + 1, cwd); DWORD len_written; WriteFile(pipe, cwd, strlen(cwd) + 1, &len_written, NULL); for (int i = 1; i < __argc; i++) WriteFile(pipe, __argv[i], strlen(__argv[i]) + 1, &len_written, NULL); CloseHandle(pipe); + return 0; } #endif -#if (CURSES && !_WIN32) +#if CURSES +#if !_WIN32 /** * Signal for a terminal suspend, continue, and resize. * libtermkey has been patched to enable suspend as well as enable/disable mouse @@ -2404,23 +2364,25 @@ static void t_signal(int signal) { if (signal == SIGCONT) termkey_start(ta_tk); struct winsize w; ioctl(0, TIOCGWINSZ, &w); - resizeterm(w.ws_row, w.ws_col), pane_resize(pane, LINES - 2, COLS, 1, 0); - WINDOW *ce_win = scintilla_get_window(command_entry); - wresize(ce_win, 1, COLS), mvwin(ce_win, LINES - 1 - getmaxy(ce_win), 0); - if (signal == SIGCONT) lL_event(lua, "resume", -1); - lL_event(lua, "update_ui", -1); - } else if (!lL_event(lua, "suspend", -1)) + resizeterm(w.ws_row, w.ws_col), resize_pane(pane, LINES - 2, COLS, 1, 0); + WINDOW *win = scintilla_get_window(command_entry); + wresize(win, 1, COLS), mvwin(win, LINES - 1 - getmaxy(win), 0); + if (signal == SIGCONT) emit(lua, "resume", -1); + emit(lua, "update_ui", -1); + } else if (!emit(lua, "suspend", -1)) endwin(), termkey_stop(ta_tk), kill(0, SIGSTOP); refresh_all(); } +#endif /** Replacement for `termkey_waitkey()` that handles asynchronous I/O. */ static TermKeyResult textadept_waitkey(TermKey *tk, TermKeyKey *key) { +#if !_WIN32 int force = FALSE; struct timeval timeout = {0, termkey_get_waittime(tk)}; while (1) { - TermKeyResult res = !force ? termkey_getkey(tk, key) - : termkey_getkey_force(tk, key); + TermKeyResult res = !force ? + termkey_getkey(tk, key) : termkey_getkey_force(tk, key); if (res != TERMKEY_RES_AGAIN && res != TERMKEY_RES_NONE) return res; if (res == TERMKEY_RES_AGAIN) force = TRUE; // Wait for input. @@ -2433,6 +2395,9 @@ static TermKeyResult textadept_waitkey(TermKey *tk, TermKeyKey *key) { } lua_pop(lua, 1); // fd_set } +#else + return termkey_getkey(tk, key); +#endif } #endif @@ -2460,19 +2425,19 @@ int main(int argc, char **argv) { char *last_slash = NULL; #if __linux__ - textadept_home = malloc(FILENAME_MAX); - int len = readlink("/proc/self/exe", textadept_home, FILENAME_MAX); + textadept_home = malloc(FILENAME_MAX + 1); + int len = readlink("/proc/self/exe", textadept_home, FILENAME_MAX + 1); textadept_home[len] = '\0'; if ((last_slash = strrchr(textadept_home, '/'))) *last_slash = '\0'; platform = "LINUX"; #elif _WIN32 - textadept_home = malloc(FILENAME_MAX); - GetModuleFileName(NULL, textadept_home, FILENAME_MAX); + textadept_home = malloc(FILENAME_MAX + 1); + GetModuleFileName(NULL, textadept_home, FILENAME_MAX + 1); if ((last_slash = strrchr(textadept_home, '\\'))) *last_slash = '\0'; platform = "WIN32"; #elif __APPLE__ - char *path = malloc(FILENAME_MAX), *p = NULL; - uint32_t size = FILENAME_MAX; + char *path = malloc(FILENAME_MAX + 1), *p = NULL; + uint32_t size = FILENAME_MAX + 1; _NSGetExecutablePath(path, &size); textadept_home = realpath(path, NULL); p = strstr(textadept_home, "MacOS"), strcpy(p, "Resources\0"); @@ -2482,9 +2447,9 @@ int main(int argc, char **argv) { platform = "OSX"; // OSX is only set for GUI version #endif #elif (__FreeBSD__ || __NetBSD__ || __OpenBSD__) - textadept_home = malloc(FILENAME_MAX); + textadept_home = malloc(FILENAME_MAX + 1); int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; - size_t cb = FILENAME_MAX; + size_t cb = FILENAME_MAX + 1; sysctl(mib, 4, textadept_home, &cb, NULL, 0); if ((last_slash = strrchr(textadept_home, '/'))) *last_slash = '\0'; platform = "BSD"; @@ -2497,21 +2462,22 @@ int main(int argc, char **argv) { force = TRUE; break; } - GApplication *app = g_application_new("textadept.editor", - G_APPLICATION_HANDLES_COMMAND_LINE); - g_signal_connect(app, "command-line", G_CALLBACK(a_command_line), 0); + GApplication *app = g_application_new(ID, G_APPLICATION_HANDLES_COMMAND_LINE); + g_signal_connect(app, "command-line", G_CALLBACK(a_command_line), NULL); int registered = g_application_register(app, NULL, NULL); if (!registered || !g_application_get_is_remote(app) || force) { #endif setlocale(LC_COLLATE, "C"), setlocale(LC_NUMERIC, "C"); // for Lua - if (lua = luaL_newstate(), !lL_init(lua, argc, argv, FALSE)) return 1; - initing = TRUE, new_window(), lL_dofile(lua, "init.lua"), initing = FALSE; - lL_event(lua, "buffer_new", -1), lL_event(lua, "view_new", -1); // first ones - l_setglobaldoc(lua, SS(command_entry, SCI_GETDOCPOINTER, 0, 0)); - lL_event(lua, "buffer_new", -1), lL_event(lua, "view_new", -1); // cmd entry - l_setglobaldoc(lua, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)); - lL_event(lua, "initialized", -1); // ready + if (lua = luaL_newstate(), !init_lua(lua, argc, argv, FALSE)) return 1; + initing = TRUE, new_window(), run_file(lua, "init.lua"), initing = FALSE; + emit(lua, "buffer_new", -1), emit(lua, "view_new", -1); // first ones + lua_pushdoc(lua, SS(command_entry, SCI_GETDOCPOINTER, 0, 0)), + lua_setglobal(lua, "buffer"); + emit(lua, "buffer_new", -1), emit(lua, "view_new", -1); // cmd entry + lua_pushdoc(lua, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)), + lua_setglobal(lua, "buffer"); + emit(lua, "initialized", -1); // ready #if (__APPLE__ && !CURSES) gtkosx_application_ready(osxapp); #endif @@ -2530,13 +2496,10 @@ int main(int argc, char **argv) { struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = t_signal, sigfillset(&act.sa_mask); - sigaction(SIGTSTP, &act, NULL), sigaction(SIGCONT, &act, NULL); - sigaction(SIGWINCH, &act, NULL); + sigaction(SIGTSTP, &act, NULL), sigaction(SIGCONT, &act, NULL), + sigaction(SIGWINCH, &act, NULL); #else - freopen("NUL", "w", stderr); // redirect stderr -#if CURSES - freopen("NUL", "w", stdout); // redirect stdout -#endif + freopen("NUL", "w", stdout), freopen("NUL", "w", stderr); // redirect #endif Scintilla *view = focused_view; @@ -2561,11 +2524,11 @@ int main(int argc, char **argv) { lua_newtable(lua); for (size_t i = 0; i < nargs; i++) lua_pushinteger(lua, args[i]), lua_rawseti(lua, -2, i + 1); - lL_event(lua, "csi", LUA_TNUMBER, cmd, LUA_TTABLE, - luaL_ref(lua, LUA_REGISTRYINDEX), -1); + emit(lua, "csi", LUA_TNUMBER, cmd, LUA_TTABLE, luaL_ref( + lua, LUA_REGISTRYINDEX), -1); } else if (key.type == TERMKEY_TYPE_MOUSE) { - termkey_interpret_mouse(ta_tk, &key, (TermKeyMouseEvent*)&event, &button, - &y, &x), y--, x--; + termkey_interpret_mouse( + ta_tk, &key, (TermKeyMouseEvent*)&event, &button, &y, &x), y--, x--; #if !_WIN32 struct timeval time = {0, 0}; gettimeofday(&time, NULL); @@ -2581,34 +2544,38 @@ int main(int argc, char **argv) { int shift = key.modifiers & TERMKEY_KEYMOD_SHIFT; int ctrl = key.modifiers & TERMKEY_KEYMOD_CTRL; int alt = key.modifiers & TERMKEY_KEYMOD_ALT; - if (ch && !lL_event(lua, "keypress", LUA_TNUMBER, ch, LUA_TBOOLEAN, shift, - LUA_TBOOLEAN, ctrl, LUA_TBOOLEAN, alt, -1)) + if (ch && !emit( + lua, "keypress", LUA_TNUMBER, ch, LUA_TBOOLEAN, shift, + LUA_TBOOLEAN, ctrl, LUA_TBOOLEAN, alt, -1)) scintilla_send_key(view, ch, shift, ctrl, alt); - else if (!ch && !scintilla_send_mouse(view, event, millis, button, y, x, - shift, ctrl, alt) && - !lL_event(lua, "mouse", LUA_TNUMBER, event, LUA_TNUMBER, button, - LUA_TNUMBER, y, LUA_TNUMBER, x, LUA_TBOOLEAN, shift, - LUA_TBOOLEAN, ctrl, LUA_TBOOLEAN, alt, -1)) - scintilla_send_mouse(focused_view, event, millis, button, y, x, shift, - ctrl, alt); // try again with possibly another view - if (quit && !lL_event(lua, "quit", -1)) { - l_close(lua); + else if (!ch && !scintilla_send_mouse( + view, event, millis, button, y, x, shift, ctrl, alt) && + !emit( + lua, "mouse", LUA_TNUMBER, event, LUA_TNUMBER, button, + LUA_TNUMBER, y, LUA_TNUMBER, x, LUA_TBOOLEAN, shift, + LUA_TBOOLEAN, ctrl, LUA_TBOOLEAN, alt, -1)) + // Try again with possibly another view. + scintilla_send_mouse( + focused_view, event, millis, button, y, x, shift, ctrl, alt); + if (quitting) { + close_lua(lua); // Free some memory. - free(pane), free(flabel), free(rlabel); + free(pane), free(find_label), free(repl_label); if (find_text) free(find_text); if (repl_text) free(repl_text); for (int i = 0; i < 10; i++) { - if (find_store[i]) free(find_store[i]); - if (repl_store[i]) free(repl_store[i]); + if (find_history[i]) free(find_history[i]); + if (repl_history[i]) free(repl_history[i]); if (i > 3) continue; free(find_options[i] ? option_labels[i] : option_labels[i] - 4); free(button_labels[i]); } break; - } else quit = FALSE; + } refresh_all(); view = !command_entry_focused ? focused_view : command_entry; -#if (_WIN32 && CURSES) +#if _WIN32 + termkey_set_fd(ta_tk, scintilla_get_window(view)); mouse_set(ALL_MOUSE_EVENTS); // _popen() and system() change console mode #endif } |