mle icon indicating copy to clipboard operation
mle copied to clipboard

support elastic tabstops

Open dicktyr opened this issue 11 months ago • 2 comments

elastic tabstops are certainly useful and essential for working with tabular data

dicktyr avatar Jan 15 '25 19:01 dicktyr

Hi, thanks for the idea. If we expose an API function to set vcol, you could write this as a Lua plugin. However, I have some heavy refactoring planned for 1.8.0 that relates to how characters are stored and rendered (including vcol), so I will wait until then.

In the mean time, here's an untested proof-of-concept for a such a function.

diff --git a/uscript.inc.c b/uscript.inc.c
index 2bf55a1..bfdabf4 100644
--- a/uscript.inc.c
+++ b/uscript.inc.c
@@ -2529,6 +2529,36 @@ static int _uscript_func_mark_swap(lua_State *L) {
     return 1;
 }
 
+static int _uscript_func_mark_set_char_vwidth(lua_State *L) {
+    mark_t *mark = (mark_t *)luaL_checkpointer(L, 1);
+    bint_t new_vwidth = (bint_t)luaL_checkinteger(L, 2);
+    int rv;
+    if (new_vwidth >= 1) {
+        bline_t *bline = mark->bline;
+        bint_t cur_vwidth;
+        if (mark->col < bline->char_count - 1) {
+            cur_vwidth = bline->chars[mark->col + 1].vcol - bline->chars[mark->col].vcol;
+            bint_t col;
+            for (col = mark->col + 1; col < bline->char_count; col++) {
+                bline->chars[col].vcol += new_vwidth - cur_vwidth;
+            }
+        } else if (mark->col < bline->char_count) {
+            cur_vwidth = bline->char_vwidth - bline->chars[mark->col].vcol;
+        }
+        bline->char_vwidth += new_vwidth - cur_vwidth;
+        rv = MLE_OK;
+    } else {
+        rv = MLE_ERR;
+    }
+
+    lua_createtable(L, 0, 1);
+    lua_pushstring(L, "rv");
+    lua_pushinteger(L, (lua_Integer)rv);
+    lua_settable(L, -3);
+    lua_pushvalue(L, -1);
+    return 1;
+}
+
 // static int _uscript_func_util_escape_shell_arg(lua_State *L) {
 // }
 
@@ -2691,5 +2721,6 @@ static const struct luaL_Reg mle_lib[] = {
     { "mark_swap", _uscript_func_mark_swap },
     { "util_escape_shell_arg", _uscript_func_util_escape_shell_arg },
     { "util_shell_exec", _uscript_func_util_shell_exec },
+    { "mark_set_char_vwidth", _uscript_func_mark_set_char_vwidth },
     { NULL, NULL }
 };

And here's a contrived example that sets all tab widths equal to line_index + 1. You could use this as a starting point to implement elastic tabs.

-- vcol.lua
mle.editor_register_observer("buffer:baction", function (baction)
    local buffer = baction["buffer"]
    local sl = baction["start_line_index"]
    local el = baction["maybe_end_line_index"]
    if el < sl then el = sl end
    local mark = mle.buffer_add_mark(buffer, "0x0", 0)["rv"]
    local eol = mle.buffer_add_mark(buffer, "0x0", 0)["rv"]
    for line_index = sl, el do
        mle.mark_move_to(mark, line_index, 0)
        mle.mark_move_to(eol, line_index, 0)
        mle.mark_move_bol(mark)
        mle.mark_move_eol(eol)
        while mle.mark_move_next_str(mark, "\t", 1)["rv"] == 0 and mle.mark_is_lt(mark, eol)["rv"] > 0 do
            mle.mark_set_char_vwidth(mark, line_index + 1)
            mle.mark_move_by(mark, 1)
        end
    end
    mle.mark_destroy(mark)
    mle.mark_destroy(eol)
end)

To test it:

$ { for i in $(seq 1 9); do printf "$i\ta\tb\n"; done; } | ./mle -N -H0 -a0 -x vcol.lua

adsr avatar Jan 20 '25 21:01 adsr

thanks for your kind reply and code

I'm not (yet?) a regular user of mle but discovered it recently while looking for basic terminal text editors

in any case I'm glad you appreciate elastic tabstops (odd that they still remain so unknown)

dicktyr avatar Jan 21 '25 05:01 dicktyr