From f7e64e9c6a7dd22d16ac15adf4f86714b5b993ef Mon Sep 17 00:00:00 2001 From: David Vazgenovich Shakaryan Date: Sun, 1 Feb 2026 01:51:20 -0800 Subject: add readline-style text manipulation --- main.lua | 30 ++++++++++++--- rt.lua | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ state.lua | 3 +- 3 files changed, 140 insertions(+), 19 deletions(-) diff --git a/main.lua b/main.lua index dd046de..6a7974a 100644 --- a/main.lua +++ b/main.lua @@ -176,16 +176,34 @@ input.define_mapping('MENU', { }, }) input.define_mapping('SEARCH', { - ['ANY_UNICODE'] = {rt.search_input_char, 'complex'}, - ['BS'] = {rt.search_input_bs, 'repeat'}, - ['DEL'] = {rt.search_input_del, 'repeat'}, - ['ENTER'] = {rt.end_search}, ['ESC'] = {rt.cancel_search}, ['Ctrl+c'] = {rt.cancel_search}, - ['LEFT'] = {rt.search_cursor_left, 'repeat'}, - ['RIGHT'] = {rt.search_cursor_right, 'repeat'}, + ['ANY_UNICODE'] = {rt.search_text_insert_char, 'complex'}, + ['BS'] = {rt.search_text_del_prev_char, 'repeat'}, + ['DEL'] = {rt.search_text_del_next_char, 'repeat'}, + ['Alt+BS'] = {rt.search_text_del_prev_word, 'repeat'}, + ['Alt+DEL'] = {rt.search_text_del_next_word, 'repeat'}, + ['Ctrl+h'] = {rt.search_text_del_prev_char, 'repeat'}, + ['Ctrl+d'] = {rt.search_text_del_next_char, 'repeat'}, + ['Ctrl+w'] = {rt.search_text_del_prev_word, 'repeat'}, + ['Alt+d'] = {rt.search_text_del_next_word, 'repeat'}, + ['Ctrl+u'] = {rt.search_text_del_to_start}, + ['Ctrl+k'] = {rt.search_text_del_to_end}, + ['Ctrl+t'] = {rt.search_text_transpose_chars, 'repeat'}, + ['Alt+t'] = {rt.search_text_transpose_words, 'repeat'}, + + ['LEFT'] = {rt.search_cursor_prev_char, 'repeat'}, + ['RIGHT'] = {rt.search_cursor_next_char, 'repeat'}, + ['Alt+LEFT'] = {rt.search_cursor_prev_word, 'repeat'}, + ['Alt+RIGHT'] = {rt.search_cursor_next_word, 'repeat'}, + ['Ctrl+b'] = {rt.search_cursor_prev_char, 'repeat'}, + ['Ctrl+f'] = {rt.search_cursor_next_char, 'repeat'}, + ['Alt+b'] = {rt.search_cursor_prev_word, 'repeat'}, + ['Alt+f'] = {rt.search_cursor_next_word, 'repeat'}, + ['HOME'] = {rt.search_cursor_start}, + ['END'] = {rt.search_cursor_end}, ['Ctrl+a'] = {rt.search_cursor_start}, ['Ctrl+e'] = {rt.search_cursor_end}, diff --git a/rt.lua b/rt.lua index 502cf83..da6c238 100644 --- a/rt.lua +++ b/rt.lua @@ -535,7 +535,7 @@ function rt.open_option_info(opt) end end -function rt.search_input_char(ev) +function rt.search_text_insert_char(ev) if ev.event ~= 'down' and ev.event ~= 'repeat' then return end @@ -543,13 +543,13 @@ function rt.search_input_char(ev) local menu = state:menu() menu:set_search_text( menu.search_text:sub(1, menu.search_cursor - 1) .. - ev.key_text .. - menu.search_text:sub(menu.search_cursor)) - menu:set_search_cursor(menu.search_cursor + #ev.key_text) + ev.key_text .. + menu.search_text:sub(menu.search_cursor), + menu.search_cursor + #ev.key_text) osd:dirty() end -function rt.search_input_bs() +function rt.search_text_del_prev_char() local menu = state:menu() if menu.search_cursor <= 1 then return @@ -558,12 +558,12 @@ function rt.search_input_bs() local pos = util.utf8_seek(menu.search_text, menu.search_cursor, -1) menu:set_search_text( menu.search_text:sub(1, pos - 1) .. - menu.search_text:sub(menu.search_cursor)) - menu:set_search_cursor(pos) + menu.search_text:sub(menu.search_cursor), + pos) osd:dirty() end -function rt.search_input_del() +function rt.search_text_del_next_char() local menu = state:menu() if menu.search_cursor > #menu.search_text then return @@ -571,8 +571,97 @@ function rt.search_input_del() menu:set_search_text( menu.search_text:sub(1, menu.search_cursor - 1) .. - menu.search_text:sub(util.utf8_seek( - menu.search_text, menu.search_cursor, 1))) + menu.search_text:sub(util.utf8_seek( + menu.search_text, menu.search_cursor, 1))) + osd:dirty() +end + +function rt.search_text_del_prev_word() + local menu = state:menu() + if menu.search_cursor <= 1 then + return + end + + local pos = menu.search_text:sub(1, menu.search_cursor - 1):match( + '()%S*%s*$') + menu:set_search_text( + menu.search_text:sub(1, pos - 1) .. + menu.search_text:sub(menu.search_cursor), + pos) + osd:dirty() +end + +function rt.search_text_del_next_word() + local menu = state:menu() + if menu.search_cursor > #menu.search_text then + return + end + + local pos = menu.search_text:match('%s*%S*()', menu.search_cursor) + menu:set_search_text( + menu.search_text:sub(1, menu.search_cursor - 1) .. + menu.search_text:sub(pos)) + osd:dirty() +end + +function rt.search_text_del_to_start() + local menu = state:menu() + if menu.search_cursor <= 1 then + return + end + + menu:set_search_text(menu.search_text:sub(menu.search_cursor), 1) + osd:dirty() +end + +function rt.search_text_del_to_end() + local menu = state:menu() + if menu.search_cursor > #menu.search_text then + return + end + + menu:set_search_text(menu.search_text:sub(1, menu.search_cursor - 1)) + osd:dirty() +end + +function rt.search_text_transpose_chars() + local menu = state:menu() + if menu.search_cursor <= 1 then + return + end + + local pos = util.utf8_seek(menu.search_text, menu.search_cursor, 1) + local cp2 = util.utf8_seek(menu.search_text, pos, -1) + if cp2 <= 1 then + return + end + local cp1 = util.utf8_seek(menu.search_text, cp2, -1) + + menu:set_search_text( + menu.search_text:sub(1, cp1 - 1) .. + menu.search_text:sub(cp2, pos - 1) .. + menu.search_text:sub(cp1, cp2 - 1) .. + menu.search_text:sub(pos), + pos) + osd:dirty() +end + +function rt.search_text_transpose_words() + local menu = state:menu() + if menu.search_cursor <= 1 then + return + end + + local pos = menu.search_text:match('%s*%S*()', menu.search_cursor) + local pre, w1, sp, w2 = menu.search_text:sub(1, pos - 1):match( + '^(.-)(%S+)(%s+)(%S+%s*)$') + if not pre then + return + end + + menu:set_search_text( + pre .. w2 .. sp .. w1 .. menu.search_text:sub(pos), + pos) osd:dirty() end @@ -582,18 +671,31 @@ local function set_search_cursor(pos) end end -function rt.search_cursor_left() +function rt.search_cursor_prev_char() local menu = state:menu() set_search_cursor(util.utf8_seek( menu.search_text, menu.search_cursor, -1)) end -function rt.search_cursor_right() +function rt.search_cursor_next_char() local menu = state:menu() set_search_cursor(util.utf8_seek( menu.search_text, menu.search_cursor, 1)) end +function rt.search_cursor_prev_word() + local menu = state:menu() + set_search_cursor( + menu.search_text:sub(1, menu.search_cursor - 1):match( + '()%S*%s*$')) +end + +function rt.search_cursor_next_word() + local menu = state:menu() + set_search_cursor( + menu.search_text:match('%s*%S*()', menu.search_cursor)) +end + function rt.search_cursor_start() set_search_cursor(1) end diff --git a/state.lua b/state.lua index 3abb892..b5e07f5 100644 --- a/state.lua +++ b/state.lua @@ -201,8 +201,9 @@ function menu_mt:set_search_cursor(pos) return true end -function menu_mt:set_search_text(str) +function menu_mt:set_search_text(str, pos) self.search_text = str + self:set_search_cursor(pos or self.search_cursor) self:update_search_matches() end -- cgit v1.2.3-70-g09d2