aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar mitchell <70453897+orbitalquark@users.noreply.github.com>2022-03-18 21:55:42 -0400
committerGravatar mitchell <70453897+orbitalquark@users.noreply.github.com>2022-03-18 21:55:42 -0400
commit092d5984e16f2d8f844dc88a417ed6f9defdf42c (patch)
tree1ddb8e897e06f9c85f85429e03e355c50ae298a6
parent962e738d191bc526e43eb06943280cb24ef5f82c (diff)
Added `move_buffer()` function and made tabs rearrangeable via drag and drop.
-rw-r--r--core/init.lua8
-rw-r--r--docs/api.md10
-rw-r--r--modules/lua/ta_api1
-rw-r--r--modules/lua/ta_tags1
-rw-r--r--src/textadept.c55
-rw-r--r--test/test.lua31
6 files changed, 103 insertions, 3 deletions
diff --git a/core/init.lua b/core/init.lua
index 4290c901..56b7519f 100644
--- a/core/init.lua
+++ b/core/init.lua
@@ -134,6 +134,14 @@ local view
-- The functions below are Lua C functions.
---
+-- Moves the buffer at index *from* to index *to*, shifting other buffers as necessary.
+-- @param from Index of the buffer to move.
+-- @param to Index to move the buffer to.
+-- @class function
+-- @name move_buffer
+local move_buffer
+
+---
-- Emits a `QUIT` event, and unless any handler returns `false`, quits Textadept.
-- @see events.QUIT
-- @class function
diff --git a/docs/api.md b/docs/api.md
index f2c7239c..2c98084c 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -98,6 +98,16 @@ The path to the user's *~/.textadept/* directory, where all preferences and user
### Functions defined by `_G`
+<a id="move_buffer"></a>
+#### `move_buffer`(*from, to*)
+
+Moves the buffer at index *from* to index *to*, shifting other buffers as necessary.
+
+Parameters:
+
+* *`from`*: Index of the buffer to move.
+* *`to`*: Index to move the buffer to.
+
<a id="quit"></a>
#### `quit`()
diff --git a/modules/lua/ta_api b/modules/lua/ta_api
index f3d6de54..000ca551 100644
--- a/modules/lua/ta_api
+++ b/modules/lua/ta_api
@@ -728,6 +728,7 @@ modify buffer.modify (bool, Read-only)\nWhether or not the buffer has unsaved ch
modify_rule lexer.modify_rule(lexer, id, rule)\nReplaces in lexer *lexer* the existing rule identified by string *id* with pattern *rule*.\n@param lexer The lexer to modify.\n@param id The id associated with this rule.\n@param rule The LPeg pattern of the rule.
mouse_dwell_time view.mouse_dwell_time (number)\nThe number of milliseconds the mouse must idle before generating a `DWELL_START` event. A\ntime of `view.TIME_FOREVER` will never generate one.
mouse_selection_rectangular_switch view.mouse_selection_rectangular_switch (bool)\nWhether or not pressing `view.rectangular_selection_modifier` when selecting text\nnormally with the mouse turns on rectangular selection.\nThe default value is `false`.
+move_buffer _G.move_buffer(from, to)\nMoves the buffer at index *from* to index *to*, shifting other buffers as necessary.\n@param from Index of the buffer to move.\n@param to Index to move the buffer to.
move_caret_inside_view buffer.move_caret_inside_view(buffer)\nMoves the caret into view if it is not already, removing any selections.\n@param buffer A buffer.
move_extends_selection buffer.move_extends_selection (bool, Read-only)\nWhether or not regular caret movement alters the selected text.\n`buffer.selection_mode` dictates this property.
move_selected_lines_down buffer.move_selected_lines_down(buffer)\nShifts the selected lines down one line.\n@param buffer A buffer.
diff --git a/modules/lua/ta_tags b/modules/lua/ta_tags
index 7443c621..6a448c2e 100644
--- a/modules/lua/ta_tags
+++ b/modules/lua/ta_tags
@@ -730,6 +730,7 @@ modify _HOME/core/.buffer.luadoc /^module('buffer')$/;" F class:buffer
modify_rule _HOME/lexers/lexer.lua /^function M.modify_rule(lexer, id, rule)$/;" f class:lexer
mouse_dwell_time _HOME/core/.view.luadoc /^module('view')$/;" F class:view
mouse_selection_rectangular_switch _HOME/core/.view.luadoc /^module('view')$/;" F class:view
+move_buffer _HOME/core/init.lua /^local move_buffer$/;" f
move_caret_inside_view _HOME/core/.buffer.luadoc /^function move_caret_inside_view(buffer) end$/;" f class:buffer
move_extends_selection _HOME/core/.buffer.luadoc /^module('buffer')$/;" F class:buffer
move_selected_lines_down _HOME/core/.buffer.luadoc /^function move_selected_lines_down(buffer) end$/;" f class:buffer
diff --git a/src/textadept.c b/src/textadept.c
index 022f4604..40e6c641 100644
--- a/src/textadept.c
+++ b/src/textadept.c
@@ -1038,7 +1038,7 @@ static void register_command_entry_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); lua_pop(L, 1), i++) {
- Scintilla *view = (lua_rawgeti(L, -1, i), lua_toview(L, -1)); // ^popped
+ Scintilla *view = (lua_rawgeti(L, -1, i), lua_toview(L, -1)); // popped on loop
if (doc == SS(view, SCI_GETDOCPOINTER, 0, 0)) goto_doc(L, view, -1, true);
}
lua_pop(L, 1); // views
@@ -1412,6 +1412,7 @@ static void new_buffer(sptr_t doc) {
tab_sync = true;
int i = gtk_notebook_append_page(GTK_NOTEBOOK(tabbar), tab, NULL);
gtk_widget_show(tab), gtk_widget_set_visible(tabbar, show_tabs(i > 0));
+ gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(tabbar), tab, true);
gtk_notebook_set_current_page(GTK_NOTEBOOK(tabbar), i);
tab_sync = false;
lua_pop(lua, 2); // tab_pointer and buffer
@@ -1423,6 +1424,42 @@ static void new_buffer(sptr_t doc) {
if (!initing) emit(lua, "buffer_new", -1);
}
+/**
+ * Moves the buffer from the given index to another index, shifting other buffers as necessary.
+ * @param from Index of the buffer to move.
+ * @param to Index to move the buffer to.
+ * @reorder_tabs Flag indicating whether or not to reorder tabs in the GUI. This is `false`
+ * when responding to a GUI reordering event and `true` when calling from Lua.
+ */
+static void move_buffer(int from, int to, bool reorder_tabs) {
+ lua_getglobal(lua, "table"), lua_getfield(lua, -1, "insert"), lua_replace(lua, -2);
+ lua_getfield(lua, LUA_REGISTRYINDEX, "ta_buffers"), lua_pushinteger(lua, to);
+ lua_getglobal(lua, "table"), lua_getfield(lua, -1, "remove"), lua_replace(lua, -2);
+ lua_getfield(lua, LUA_REGISTRYINDEX, "ta_buffers"), lua_pushinteger(lua, from),
+ lua_call(lua, 2, 1); // table.remove(_BUFFERS, from) --> buf
+ lua_call(lua, 3, 0); // table.insert(_BUFFERS, to, buf)
+ lua_getfield(lua, LUA_REGISTRYINDEX, "ta_buffers");
+ for (int i = 1; i <= lua_rawlen(lua, -1); i++)
+ lua_rawgeti(lua, -1, i), lua_pushinteger(lua, i), lua_settable(lua, -3); // t[buffer] = i
+ lua_pop(lua, 1);
+ if (!reorder_tabs) return;
+#if GTK
+ gtk_notebook_reorder_child(
+ GTK_NOTEBOOK(tabbar), gtk_notebook_get_nth_page(GTK_NOTEBOOK(tabbar), from - 1), to - 1);
+//#elif CURSES
+// TODO: tabs
+#endif
+}
+
+/** `_G.move_buffer` Lua function. */
+static int move_buffer_lua(lua_State *L) {
+ lua_getfield(lua, LUA_REGISTRYINDEX, "ta_buffers");
+ int from = luaL_checkinteger(L, 1), to = luaL_checkinteger(L, 2);
+ luaL_argcheck(L, from >= 1 && from <= lua_rawlen(L, -1), 1, "position out of bounds");
+ luaL_argcheck(L, to >= 1 && to <= lua_rawlen(L, -1), 2, "position out of bounds");
+ return (lua_pop(L, 1), move_buffer(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), true), 0);
+}
+
/** `_G.quit()` Lua function. */
static int quit(lua_State *L) {
#if GTK
@@ -1575,6 +1612,7 @@ static bool init_lua(lua_State *L, int argc, char **argv, bool reinit) {
set_metatable(L, -1, "ta_ui", ui_index, ui_newindex);
lua_setglobal(L, "ui");
+ lua_pushcfunction(L, move_buffer_lua), lua_setglobal(L, "move_buffer");
lua_pushcfunction(L, quit), lua_setglobal(L, "quit");
lua_pushcfunction(L, reset), lua_setglobal(L, "reset");
lua_pushcfunction(L, add_timeout), lua_setglobal(L, "timeout");
@@ -1785,8 +1823,8 @@ static void close_lua(lua_State *L) {
closing = true;
while (unsplit_view(focused_view)) {}
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(lua_todoc(L, -1)), lua_pop(L, 1);
+ for (size_t i = 1; i <= lua_rawlen(L, -1); lua_pop(L, 1), i++)
+ lua_rawgeti(L, -1, i), delete_buffer(lua_todoc(L, -1)); // popped on loop
lua_pop(L, 1); // buffers
scintilla_delete(focused_view), scintilla_delete(dummy_view);
scintilla_delete(command_entry);
@@ -1844,6 +1882,16 @@ static void terminate(GtkosxApplication *_, void *L) {
static void tab_changed(GtkNotebook *_, GtkWidget *__, int tab_num, void *L) {
if (!tab_sync) emit(L, "tab_clicked", LUA_TNUMBER, tab_num + 1, LUA_TNUMBER, 1, -1);
}
+
+/** Signal for reordering tabs. */
+static void tab_reordered(GtkNotebook *_, GtkWidget *tab, int to, void *L) {
+ lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers");
+ for (size_t i = 1; i <= lua_rawlen(L, -1); lua_pop(L, 2), i++)
+ if (tab == (lua_rawgeti(L, -1, i), lua_getfield(L, -1, "tab_pointer"), lua_touserdata(L, -1))) {
+ lua_pop(L, 3), move_buffer(i, to + 1, false);
+ break;
+ }
+}
#endif // if GTK
/**
@@ -2281,6 +2329,7 @@ static void new_window() {
tabbar = gtk_notebook_new();
g_signal_connect(tabbar, "switch-page", G_CALLBACK(tab_changed), lua);
+ g_signal_connect(tabbar, "page-reordered", G_CALLBACK(tab_reordered), lua);
gtk_notebook_set_scrollable(GTK_NOTEBOOK(tabbar), true);
gtk_widget_set_can_focus(tabbar, false);
gtk_box_pack_start(GTK_BOX(vbox), tabbar, false, false, 0);
diff --git a/test/test.lua b/test/test.lua
index c3c7a695..ed8fd3f5 100644
--- a/test/test.lua
+++ b/test/test.lua
@@ -4021,6 +4021,37 @@ function test_ui_restore_view_state()
buffer:close()
end
+function test_move_buffer()
+ local buffer1 = buffer.new()
+ buffer1:set_text('1')
+ local buffer2 = buffer.new()
+ buffer2:set_text('2')
+ local buffer3 = buffer.new()
+ buffer3:set_text('3')
+ local buffer4 = buffer.new()
+ buffer4:set_text('4')
+ move_buffer(_BUFFERS[buffer4], _BUFFERS[buffer1])
+ assert(_BUFFERS[buffer4] < _BUFFERS[buffer1], 'buffer4 not before buffer1')
+ assert(_BUFFERS[buffer1] < _BUFFERS[buffer2], 'buffer1 not before buffer2')
+ assert(_BUFFERS[buffer2] < _BUFFERS[buffer3], 'buffer2 not before buffer3')
+ move_buffer(_BUFFERS[buffer2], _BUFFERS[buffer3])
+ assert(_BUFFERS[buffer4] < _BUFFERS[buffer1], 'buffer4 not before buffer1')
+ assert(_BUFFERS[buffer1] < _BUFFERS[buffer3], 'buffer1 not before buffer3')
+ assert(_BUFFERS[buffer3] < _BUFFERS[buffer2], 'buffer3 not before buffer2')
+
+ assert_raises(function() move_buffer('') end, 'number expected')
+ assert_raises(function() move_buffer(1) end, 'number expected')
+ assert_raises(function() move_buffer(1, true) end, 'number expected')
+ assert_raises(function() move_buffer(1, 10) end, 'out of bounds')
+ assert_raises(function() move_buffer(1, -1) end, 'out of bounds')
+ assert_raises(function() move_buffer(10, 1) end, 'out of bounds')
+ assert_raises(function() move_buffer(-1, 1) end, 'out of bounds')
+ buffer1:close(true)
+ buffer2:close(true)
+ buffer3:close(true)
+ buffer4:close(true)
+end
+
function test_reset()
local _persist
_G.foo = 'bar'