From 3986233b5cd4a36b46c2eb46f97a4f0eba602464 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 2 Apr 2021 18:01:14 +0200 Subject: [PATCH] Implement vim edit inside delimiters --- data/core/doc/init.lua | 7 ++++++ data/core/doc/translate.lua | 41 +++++++++++++++++++++++++++++++ data/core/vim.lua | 49 ++++++++++++++++++++++++++++++------- 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index 6b63151a..01fd9cb0 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -3,6 +3,7 @@ local Highlighter = require "core.doc.highlighter" local syntax = require "core.syntax" local config = require "core.config" local common = require "core.common" +local translate = require "core.doc.translate" local Doc = Object:extend() @@ -399,4 +400,10 @@ function Doc:select_to(...) end +function Doc:select_with_delimiters(delims, outer) + local line, col = self:get_selection() + self:set_selection(self:position_offset(line, col, translate.inside_delimiters, delims, outer)) +end + + return Doc diff --git a/data/core/doc/translate.lua b/data/core/doc/translate.lua index 8973ede2..f79fb648 100644 --- a/data/core/doc/translate.lua +++ b/data/core/doc/translate.lua @@ -145,4 +145,45 @@ function translate.end_of_doc(doc, line, col) end +function translate.inside_delimiters(doc, line, col, delims, outer) + print('translate.inside_delimiters', delims[2], outer) + local line1, col1 = line, col + while true do + local lineb, colb = doc:position_offset(line1, col1, -1) + local char = doc:get_char(lineb, colb) + if char == delims[1] + or line1 == lineb and col1 == colb then + break + end + line1, col1 = lineb, colb + end + + if outer then + line1, col1 = doc:position_offset(line1, col1, -1) + end + + local line2, col2 = line, col + while true do + local linef, colf = doc:position_offset(line2, col2, 1) + local char = doc:get_char(line2, col2) + if char == delims[2] + or linef == line2 and colf == col2 then + break + end + line2, col2 = linef, colf + end + + if outer then + line2, col2 = doc:position_offset(line2, col2, 1) + while doc:get_char(line2, col2) == ' ' do + local nline2, ncol2 = doc:position_offset(line2, col2, 1) + if nline2 == line2 and ncol2 == col2 then break end + line2, col2 = nline2, ncol2 + end + end + + return line1, col1, line2, col2 +end + + return translate diff --git a/data/core/vim.lua b/data/core/vim.lua index f094b840..574737a0 100644 --- a/data/core/vim.lua +++ b/data/core/vim.lua @@ -3,11 +3,12 @@ local command = require "core.command" local vim = {} -local command_buffer = {verb = '.', mult_accu = ''} +local command_buffer = {verb = '.', mult_accu = '', inside = ''} function command_buffer:reset() self.verb = '.' self.mult_accu = '' + self.inside = '' end function command_buffer:mult() @@ -28,7 +29,7 @@ local verbs_obj = {'c', 'd', 'y'} local verbs_imm = {'a', 'h', 'i', 'j', 'k', 'l', 'o', 'p', 'u', 'v', 'x', 'O', 'left', 'right', 'up', 'down', 'escape'} -local vim_objects = {'b', 'd', 'e', 'w', 'y', '^', '0', '$'} +local vim_objects = {'a', 'b', 'd', 'e', 'i', 'w', 'y', '^', '0', '$'} local vim_object_map = { ['b'] = 'start-of-word', @@ -39,6 +40,15 @@ local vim_object_map = { ['0'] = 'start-of-line', } +local inside_delims = { + [')'] = {'(', ')'}, + [']'] = {'[', ']'}, + ['}'] = {'{', '}'}, + ['>'] = {'<', '>'}, + ['"'] = {'"', '"'}, + ["'"] = {"'", "'"}, +} + local function doc_command(action, command) return 'doc:' .. action .. '-' .. command end @@ -125,27 +135,48 @@ local function vim_execute(mode, verb, mult, object) return true end -function vim.on_text_input(mode, text, stroke) - text = text or stroke +function vim.on_text_input(mode, text_raw, stroke) + local text = text_raw or stroke if mode == 'command' or mode == 'visual' then - if command_buffer.verb == '.' and table_find(verbs_imm, text) then + if command_buffer.inside ~= '' and inside_delims[text] then + -- got character for inside delimiter edits + local view = core.active_view + local outer = command_buffer.inside == 'a' + view.doc:select_with_delimiters(inside_delims[text], outer) + command.perform('doc:delete') + if command_buffer.verb == 'c' then + view:set_editing_mode('insert') + end + command_buffer:reset() + return true + elseif command_buffer.verb == '.' and table_find(verbs_imm, text) then + -- execute immediate vim command vim_execute(mode, text, command_buffer:mult()) command_buffer:reset() return true elseif command_buffer.verb == '.' and table_find(verbs_obj, text) then + -- vim command that takes an object if mode == 'command' then + -- store the command without executing command_buffer.verb = text else + -- visual mode: execute the command vim_execute(mode, text, command_buffer:mult()) command_buffer:reset() end return true - elseif string.byte(text) >= string.byte('0') and string.byte(text) <= string.byte('9') then - command_buffer:add_mult_char(text) + elseif text_raw and string.byte(text_raw) >= string.byte('0') and string.byte(text_raw) <= string.byte('9') then + -- numeric command multiplier + command_buffer:add_mult_char(text_raw) return true elseif table_find(vim_objects, text) then - vim_execute(mode, command_buffer.verb, command_buffer:mult(), text) - command_buffer:reset() + -- object of a verb + if text == 'i' or text == 'a' then + command_buffer.inside = text + else + vim_execute(mode, command_buffer.verb, command_buffer:mult(), text) + command_buffer:reset() + end return true elseif stroke == 'escape' then core.active_view:set_editing_mode('command')