diff --git a/data/core/command.lua b/data/core/command.lua index 513e0701..2e3b60dd 100644 --- a/data/core/command.lua +++ b/data/core/command.lua @@ -58,6 +58,18 @@ function command.perform(...) end +function command.perform_many(n, name) + local cmd = command.map[name] + if not cmd or not cmd.predicate() then return false end + pcall(function() + for i = 1, n do + cmd.perform() + end + end) + return true +end + + function command.add_defaults() local reg = { "core", "root", "command", "doc", "findreplace", "files" } for _, name in ipairs(reg) do diff --git a/data/core/commands/core.lua b/data/core/commands/core.lua index a13f760c..63c7114f 100644 --- a/data/core/commands/core.lua +++ b/data/core/commands/core.lua @@ -171,4 +171,16 @@ command.add(nil, { return common.home_encode_list(common.dir_list_suggest(text, dir_list)) end) end, + + ["core:set-insert-mode"] = function() + core.mode = 'insert' + end, + + ["core:set-command-mode"] = function() + core.mode = 'command' + end, + + ["core:set-standard-mode"] = function() + core.mode = 'standard' + end, }) diff --git a/data/core/init.lua b/data/core/init.lua index f8833df6..b3550eb0 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -394,6 +394,7 @@ function core.init() core.clip_rect_stack = {{ 0,0,0,0 }} core.log_items = {} core.docs = {} + core.mode = 'standard' core.threads = setmetatable({}, { __mode = "k" }) local project_dir_abs = system.absolute_path(project_dir) @@ -716,12 +717,12 @@ end function core.on_event(type, ...) local did_keymap = false - if type == "textinput" then + if type == "textinput" and (core.mode == 'insert' or core.mode == 'standard') then core.root_view:on_text_input(...) elseif type == "keypressed" then - did_keymap = keymap.on_key_pressed(...) + did_keymap = keymap.on_key_pressed(core.mode, ...) elseif type == "keyreleased" then - keymap.on_key_released(...) + keymap.on_key_released(core.mode, ...) elseif type == "mousemoved" then core.root_view:on_mouse_moved(...) elseif type == "mousepressed" then diff --git a/data/core/keymap.lua b/data/core/keymap.lua index 27c59dff..2f3b9e0f 100644 --- a/data/core/keymap.lua +++ b/data/core/keymap.lua @@ -1,3 +1,4 @@ +local core = require "core" local command = require "core.command" local keymap = {} @@ -14,6 +15,57 @@ local modkey_map = { ["right alt"] = "altgr", } +function keymap.reset_vim_command() + keymap.command_verb = '.' + keymap.command_mult = 1 +end + + +local function table_find(t, e) + for i = 1, #t do + if t[i] == e then return i end + end +end + +keymap.vim_verbs_obj = {'d', 'c'} +keymap.vim_verbs_imm = {'y', 'p', 'h', 'j', 'k', 'l', 'x', 'i'} +keymap.vim_objects = {'w', '$'} + +local vim_object_map = { + ['w'] = 'end-of-word', + ['$'] = 'end-of-line', +} + +function keymap.vim_execute(verb, mult, object) + if verb == '.' then + return command.perform_many(mult, 'doc:move-to-' .. vim_object_map[object]) + elseif verb == 'd' then + return command.perform_many(mult, 'doc:delete-to-' .. vim_object_map[object]) + elseif verb == 'c' then + command.perform_many(mult, 'doc:select-to-' .. vim_object_map[object]) + command.perform('doc:copy') + command.perform('doc:cut') + command.perform('core:set-insert-mode') + elseif verb == 'h' then + command.perform_many(mult, 'doc:move-to-previous-char') + elseif verb == 'j' then + command.perform_many(mult, 'doc:move-to-next-line') + elseif verb == 'k' then + command.perform_many(mult, 'doc:move-to-previous-line') + elseif verb == 'l' then + command.perform_many(mult, 'doc:move-to-next-char') + elseif verb == 'x' then + command.perform_many(mult, 'doc:delete') + elseif verb == 'i' then + command.perform('core:set-insert-mode') + else + return false + end + return true +end + +keymap.reset_vim_command() + local modkeys = { "ctrl", "alt", "altgr", "shift" } local function key_to_stroke(k) @@ -52,7 +104,7 @@ function keymap.get_binding(cmd) end -function keymap.on_key_pressed(k) +function keymap.on_key_pressed(editor_mode, k) local mk = modkey_map[k] if mk then keymap.modkeys[mk] = true @@ -62,6 +114,29 @@ function keymap.on_key_pressed(k) end else local stroke = key_to_stroke(k) + if editor_mode == 'command' then + if keymap.command_verb == '.' and table_find(keymap.vim_verbs_imm, stroke) then + keymap.vim_execute(stroke, keymap.command_mult) + keymap.reset_vim_command() + return true + elseif keymap.command_verb == '.' and table_find(keymap.vim_verbs_obj, stroke) then + keymap.command_verb = stroke + return true + elseif string.byte(stroke) >= string.byte(1) and string.byte(stroke) <= string.byte(9) then + keymap.command_mult = tonumber(stroke) + return true + elseif table_find(keymap.vim_objects, stroke) then + keymap.vim_execute(keymap.command_verb, keymap.command_mult, stroke) + keymap.reset_vim_command() + return true + end + elseif editor_mode == 'insert' then + if stroke == 'escape' then + core.mode = 'command' + return true + end + return false + end local commands = keymap.map[stroke] if commands then for _, cmd in ipairs(commands) do @@ -75,7 +150,7 @@ function keymap.on_key_pressed(k) end -function keymap.on_key_released(k) +function keymap.on_key_released(editor_mode, k) local mk = modkey_map[k] if mk then keymap.modkeys[mk] = false diff --git a/data/core/statusview.lua b/data/core/statusview.lua index 58421c31..dfefca74 100644 --- a/data/core/statusview.lua +++ b/data/core/statusview.lua @@ -124,7 +124,8 @@ function StatusView:get_items() self.separator, string.format("%d%%", line / #dv.doc.lines * 100), }, { - style.text, indent_label, indent_size, + style.caret, core.mode ~= 'standard' and string.upper(core.mode) or '', style.text, self.separator2, + indent_label, indent_size, style.dim, self.separator2, style.text, style.icon_font, "g", style.font, style.dim, self.separator2, style.text,