From dd92326db7b1cc149cbbedc737e0daed018ce3cf Mon Sep 17 00:00:00 2001 From: OGAWA Keiji <70509917+kibi2@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:35:55 +0900 Subject: [PATCH 1/5] ci: add test case, motion/g-csv --- lua/tirenvi/config.lua | 3 +- lua/tirenvi/core/block.lua | 24 ++++- lua/tirenvi/core/blocks.lua | 31 +++--- lua/tirenvi/core/tir_vim.lua | 8 +- lua/tirenvi/editor/column.lua | 83 ++++++++++++++++ lua/tirenvi/editor/motion.lua | 2 +- lua/tirenvi/editor/textobj.lua | 4 + lua/tirenvi/init.lua | 6 +- tests/cases/motion/g-csv/out-expected.txt | 110 ++++++++++++++++++++++ tests/cases/motion/g-csv/run.vim | 24 +++++ tests/cases/textobj/val/out-expected.txt | 12 +-- tests/cases/textobj/val/run.vim | 4 +- tests/common.vim | 4 +- 13 files changed, 280 insertions(+), 35 deletions(-) create mode 100644 lua/tirenvi/editor/column.lua create mode 100644 tests/cases/motion/g-csv/out-expected.txt create mode 100644 tests/cases/motion/g-csv/run.vim diff --git a/lua/tirenvi/config.lua b/lua/tirenvi/config.lua index c3fecb1..34e1bef 100644 --- a/lua/tirenvi/config.lua +++ b/lua/tirenvi/config.lua @@ -39,7 +39,8 @@ local defaults = { probe = false, }, textobj = { - column = "l" + column = "l", + cell = "c", }, } diff --git a/lua/tirenvi/core/block.lua b/lua/tirenvi/core/block.lua index 0585cef..bfe9159 100644 --- a/lua/tirenvi/core/block.lua +++ b/lua/tirenvi/core/block.lua @@ -114,9 +114,8 @@ function M:add(record) end ---@self Block ----@param attr Attr -function M:set_attr(attr) - self.attr = attr +function M:reset_attr() + self.attr = Attr.new() end function M.plain.new() @@ -136,6 +135,8 @@ M.plain.normalize = nop M.plain.to_vim = nop M.plain.apply_replacements = nop M.plain.remove_padding = nop +M.plain.set_attr = nop +M.plain.set_attr_from_vi = nop ---@self Block_plain ---@return Block_grid @@ -205,4 +206,21 @@ function M.grid:to_grid() return self end +---@self Block +---@param attr Attr|nil +function M.grid:set_attr(attr) + if not attr or Attr.is_plain(attr) then + return + end + self.attr = attr +end + +---@self Block +function M.grid:set_attr_from_vi() + if #self.records == 0 then + return + end + self.attr = Attr.grid.new_from_record(self.records[1]) +end + return M diff --git a/lua/tirenvi/core/blocks.lua b/lua/tirenvi/core/blocks.lua index 8541cfc..8c0a62b 100644 --- a/lua/tirenvi/core/blocks.lua +++ b/lua/tirenvi/core/blocks.lua @@ -45,6 +45,13 @@ local function apply_replacements(self, replace) end end +---@self Blocks +local function set_attr(self) + for _, block in ipairs(self) do + Block[block.kind].set_attr_from_vi(block) + end +end + ---@self Blocks local function remove_padding(self) for _, block in ipairs(self) do @@ -122,7 +129,7 @@ local function apply_reference_attr_single(blocks, attr_prev, attr_next) return false, "grid in plain" end end - Block.set_attr(block, attr) + Block[block.kind].set_attr(block, attr) return true end @@ -142,16 +149,6 @@ local function insert_plain_block(self, attr_prev, attr_next) self[#self + 1] = Block.plain.new() end ----@param block Block ----@param attr Attr|nil -local function set_attr(block, attr) - if attr and not Attr.is_plain(attr) then - if block.kind == CONST.KIND.GRID then - Block.set_attr(block, attr) - end - end -end - ---@param self Blocks ---@param attr_prev Attr|nil ---@param attr_next Attr|nil @@ -159,8 +156,8 @@ local function attach_attr(self, attr_prev, attr_next) if #self == 0 then return end - set_attr(self[1], attr_prev) - set_attr(self[#self], attr_next) + Block[self[1].kind].set_attr(self[1], attr_prev) + Block[self[#self].kind].set_attr(self[#self], attr_next) end ---@param blocks Blocks @@ -218,10 +215,18 @@ end ---@return Blocks function M.new_from_vim(records) local self = build_blocks(records) + set_attr(self) remove_padding(self) return self end +---@self Blocks +function M:reset_attr() + for _, block in ipairs(self) do + Block.reset_attr(block) + end +end + ---@self Blocks ---@return Ndjson[] function M:serialize_to_flat() diff --git a/lua/tirenvi/core/tir_vim.lua b/lua/tirenvi/core/tir_vim.lua index 1e9c0b5..ca703a7 100644 --- a/lua/tirenvi/core/tir_vim.lua +++ b/lua/tirenvi/core/tir_vim.lua @@ -84,7 +84,7 @@ end ---@param line string ---@return integer[] -local function get_pipe_byte_position(line) +function M.get_pipe_byte_position(line) local indexes = {} local index = 1 while index <= #line do @@ -164,7 +164,7 @@ function M.get_block_range(lines, count, is_around, allow_plain) local irow, icol0 = unpack(vim.api.nvim_win_get_cursor(0)) local icol = icol0 + 1 local cline = vim.api.nvim_get_current_line() - local cbyte_pos = get_pipe_byte_position(cline) + local cbyte_pos = M.get_pipe_byte_position(cline) if #cbyte_pos == 0 then return nil end @@ -181,8 +181,8 @@ function M.get_block_range(lines, count, is_around, allow_plain) trow = 1 brow = #lines end - local tbyte_pos = get_pipe_byte_position(lines[trow]) - local bbyte_pos = get_pipe_byte_position(lines[brow]) + local tbyte_pos = M.get_pipe_byte_position(lines[trow]) + local bbyte_pos = M.get_pipe_byte_position(lines[brow]) local end_index = colIndex + count end_index = math.min(end_index, #bbyte_pos) return { diff --git a/lua/tirenvi/editor/column.lua b/lua/tirenvi/editor/column.lua new file mode 100644 index 0000000..0d2dd78 --- /dev/null +++ b/lua/tirenvi/editor/column.lua @@ -0,0 +1,83 @@ +local config = require("tirenvi.config") +local buffer = require("tirenvi.state.buffer") +local vim_parser = require("tirenvi.core.vim_parser") +local tir_vim = require("tirenvi.core.tir_vim") +local ui = require("tirenvi.ui") +local Attr = require("tirenvi.core.attr") +local log = require("tirenvi.util.log") + +local M = {} + +-- private helpers + +---@return integer|nil +---@return integer|nil +local function get_current_col() + local irow, icol0 = unpack(vim.api.nvim_win_get_cursor(0)) + local icol = icol0 + 1 + local cline = vim.api.nvim_get_current_line() + local cbyte_pos = tir_vim.get_pipe_byte_position(cline) + if #cbyte_pos == 0 then + return nil, nil + end + return irow, tir_vim.get_current_col_index(cbyte_pos, icol) +end + +---@param mode string +local function change_width(mode) + local bufnr = vim.api.nvim_get_current_buf() + log.probe("set_width called") + local width = vim.v.count1 + log.probe(width) + local irow, icol = get_current_col() + log.probe(irow) + log.probe(icol) + if not irow or not icol then + return + end + local lines = buffer.get_lines(bufnr, 0, -1, false) + local top = tir_vim.get_block_top_nrow(lines, irow) + local bottom = tir_vim.get_block_bottom_nrow(lines, irow) + log.probe(top) + log.probe(bottom) + local lines = buffer.get_lines(bufnr, top - 1, bottom, false) + local blocks = vim_parser.parse(lines) + local block = blocks[1] + log.probe(block) + assert(block.kind == "grid") + log.probe(block.attr) + log.probe(block.attr.columns) + local old_width = block.attr.columns[icol].width + if mode == "set" then + block.attr.columns[icol].width = width + elseif mode == "increase" then + block.attr.columns[icol].width = old_width + width + elseif mode == "decrease" then + block.attr.columns[icol].width = old_width - width + end + log.probe(block.attr.columns) + local vi_lines = vim_parser.unparse(blocks) + ui.set_lines(bufnr, top - 1, bottom, vi_lines) +end + +-- public API + +function M.increase_width() + change_width("increase") +end + +function M.decrease_width() + change_width("decrease") +end + +function M.set_width() + change_width("set") +end + +function M.setup() + vim.keymap.set("n", "=" .. config.textobj.cell, M.set_width, { desc = "set column width" }) + vim.keymap.set("n", ">" .. config.textobj.cell, M.increase_width, { desc = "increase column width" }) + vim.keymap.set("n", "<" .. config.textobj.cell, M.decrease_width, { desc = "decrease column width" }) +end + +return M diff --git a/lua/tirenvi/editor/motion.lua b/lua/tirenvi/editor/motion.lua index 50e8632..75e8ce6 100644 --- a/lua/tirenvi/editor/motion.lua +++ b/lua/tirenvi/editor/motion.lua @@ -37,7 +37,7 @@ function M.block_top() local top local parser = util.get_parser(bufnr) if not parser or not parser.allow_plain then - top = vim.api.nvim_buf_line_count(bufnr) + top = 1 else local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) top = tir_vim.get_block_top_nrow(lines, row) diff --git a/lua/tirenvi/editor/textobj.lua b/lua/tirenvi/editor/textobj.lua index 41b6ba1..9cb9512 100644 --- a/lua/tirenvi/editor/textobj.lua +++ b/lua/tirenvi/editor/textobj.lua @@ -6,6 +6,8 @@ local log = require("tirenvi.util.log") local M = {} +-- private helpers + ---@param is_around boolean|nil local function setup_vl(is_around) is_around = is_around or false @@ -22,6 +24,8 @@ local function setup_vl(is_around) vim.api.nvim_win_set_cursor(0, { pos.end_row, pos.end_col - 1, }) end +-- public API + function M.setup_vil() setup_vl() end diff --git a/lua/tirenvi/init.lua b/lua/tirenvi/init.lua index 75400ff..6fd883a 100644 --- a/lua/tirenvi/init.lua +++ b/lua/tirenvi/init.lua @@ -8,6 +8,7 @@ local buffer = require("tirenvi.state.buffer") local flat_parser = require("tirenvi.core.flat_parser") local vim_parser = require("tirenvi.core.vim_parser") local tir_vim = require("tirenvi.core.tir_vim") +local Blocks = require("tirenvi.core.blocks") local ui = require("tirenvi.ui") -- module @@ -61,6 +62,7 @@ function M.setup(opts) require("tirenvi.editor.autocmd").setup() require("tirenvi.editor.commands").setup() require("tirenvi.editor.textobj").setup() + require("tirenvi.editor.column").setup() require("tirenvi.ui").setup() end @@ -109,11 +111,13 @@ function M.restore_tir_vim(bufnr) from_flat(bufnr) end ----@param bufnr number Buffer number. +---@param bufnr number|nil Buffer number. ---@return nil function M.redraw(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() local old_lines = buffer.get_lines(bufnr, 0, -1, false) local blocks = vim_parser.parse(old_lines) + Blocks.reset_attr(blocks) local vi_lines = vim_parser.unparse(blocks) if table.concat(old_lines, "\n") ~= table.concat(vi_lines, "\n") then log.debug({ vi_lines[1], vi_lines[2] }) diff --git a/tests/cases/motion/g-csv/out-expected.txt b/tests/cases/motion/g-csv/out-expected.txt new file mode 100644 index 0000000..2df5d04 --- /dev/null +++ b/tests/cases/motion/g-csv/out-expected.txt @@ -0,0 +1,110 @@ +=== MESSAGE === + +=== DISPLAY === +│A1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│B22⠀⠀⠀⠀⠀⠀│C333⠀⠀⠀⠀⠀⠀│D4444⠀⠀⠀│E55555⠀⠀⠀│F666666│ +│ ===⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│- next -⠀│ line⠀⠀⠀⠀⠀│ has⠀⠀⠀│ no⠀⠀⠀⠀⠀⠀│ value │ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│x1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│y22⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│z333⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│===⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ - next -│ line⠀⠀⠀⠀⠀│ has⠀⠀⠀│linefeed⠀│ ⠀⠀⠀⠀⠀│ +│↲"1"2h", 34567890a│abc⠀⠀⠀⠀⠀⠀│DEF123⠀⠀⠀⠀│↲↲↲⠀⠀⠀⠀⠀│gh⠀⠀⠀⠀⠀⠀⠀│9⠀⠀⠀⠀⠀⠀│ +│a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│b↲⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│c⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│ ===⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│- next -⠀│ lines⠀⠀⠀⠀│ has⠀⠀⠀│tab⠀⠀⠀⠀⠀⠀│ ⠀⠀⠀⠀⠀│ +│⇥ l⇥ongtext01⠀⠀⠀⠀⠀│mi⇥⇥d2⠀⠀⠀│s⠀⠀⠀⠀⠀⠀⠀⠀⠀│tt⠀⠀⠀⠀⠀⠀│uuu⠀⠀⠀⠀⠀⠀│vvvv⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⇥⇥⇥⇥⇥⇥⇥⠀⠀⠀│⇥⇥⇥⇥⇥⇥⇥⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│R1C1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│R1C2⠀⠀⠀⠀⠀│R1C3⠀⠀⠀⠀⠀⠀│R1C4⠀⠀⠀⠀│R1C5⠀⠀⠀⠀⠀│R1C6⠀⠀⠀│ +│ ===⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│- next -⠀│ line⠀⠀⠀⠀⠀│ is⠀⠀⠀⠀│multibyte│ value │ +│aaあい⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│いろ2⠀⠀⠀⠀│ぼんさんが│へを⠀⠀⠀⠀│こいた⠀⠀⠀│.!...⠀│ +│g1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│h22⠀⠀⠀⠀⠀⠀│i333⠀⠀⠀⠀⠀⠀│j4444⠀⠀⠀│k55555⠀⠀⠀│l6⠀⠀⠀⠀⠀│ +│ ===⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│- next -⠀│ lines⠀⠀⠀⠀│ has⠀⠀⠀│linefeed+│tab ⠀⠀│ +│⇥⇥ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│↲⇥⠀⠀⠀⠀⠀⠀⠀│n⇥ ⠀⠀⠀⠀⠀⠀⠀│⇥⇥ o333⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│pqrst⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│uv⠀⠀⠀⠀⠀⠀⠀│wx1⠀⠀⠀⠀⠀⠀⠀│yz22⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│345⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│a1b2c3⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│XYZ⠀⠀⠀⠀⠀⠀⠀│7777⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│k9⠀⠀⠀⠀⠀│ +│short⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│longer12⠀│x⠀⠀⠀⠀⠀⠀⠀⠀⠀│yz⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│end⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│row15a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│row15b⠀⠀⠀│row15c⠀⠀⠀⠀│row15d⠀⠀│row15e⠀⠀⠀│row15f⠀│ +│1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│22⠀⠀⠀⠀⠀⠀⠀│333⠀⠀⠀⠀⠀⠀⠀│4444⠀⠀⠀⠀│55555⠀⠀⠀⠀│666666⠀│ +│alpha⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│beta⠀⠀⠀⠀⠀│gamma⠀⠀⠀⠀⠀│delta⠀⠀⠀│epsilon⠀⠀│zeta⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│mix1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│mix3⠀⠀⠀⠀⠀⠀│444⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│six6⠀⠀⠀│ +│abc123⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│def456⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│ghi789⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│z⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│y⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│x⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│t1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│t2⠀⠀⠀⠀⠀⠀⠀│t3⠀⠀⠀⠀⠀⠀⠀⠀│t4⠀⠀⠀⠀⠀⠀│t5⠀⠀⠀⠀⠀⠀⠀│t6⠀⠀⠀⠀⠀│ +│r1c1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r1c2⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r1c4⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│r1c6⠀⠀⠀│ +│data1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│data22⠀⠀⠀│data333⠀⠀⠀│data4444│data55555│data6⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│A⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│B⠀⠀⠀⠀⠀⠀⠀⠀│C⠀⠀⠀⠀⠀⠀⠀⠀⠀│D⠀⠀⠀⠀⠀⠀⠀│E⠀⠀⠀⠀⠀⠀⠀⠀│F⠀⠀⠀⠀⠀⠀│ +│abc⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│123⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│XYZ⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│one1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│two22⠀⠀⠀⠀│three3⠀⠀⠀⠀│four44⠀⠀│five5⠀⠀⠀⠀│six66⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│r30a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r30b⠀⠀⠀⠀⠀│r30c⠀⠀⠀⠀⠀⠀│r30d⠀⠀⠀⠀│r30e⠀⠀⠀⠀⠀│r30f⠀⠀⠀│ +│longvalue1⠀⠀⠀⠀⠀⠀⠀⠀│mid⠀⠀⠀⠀⠀⠀│short⠀⠀⠀⠀⠀│s⠀⠀⠀⠀⠀⠀⠀│tt⠀⠀⠀⠀⠀⠀⠀│uuu⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│x⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│y⠀⠀⠀⠀⠀⠀⠀⠀│z⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│end⠀⠀⠀⠀│ +│abcde⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│fghij⠀⠀⠀⠀│klmno⠀⠀⠀⠀⠀│pqrst⠀⠀⠀│uvwxy⠀⠀⠀⠀│z⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│cell1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│cell3⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│cell5⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│111⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│2222⠀⠀⠀⠀⠀│33333⠀⠀⠀⠀⠀│444444⠀⠀│55⠀⠀⠀⠀⠀⠀⠀│6⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│row40a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│row40b⠀⠀⠀│row40c⠀⠀⠀⠀│row40d⠀⠀│row40e⠀⠀⠀│row40f⠀│ +│a1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│a2⠀⠀⠀⠀⠀⠀⠀│a3⠀⠀⠀⠀⠀⠀⠀⠀│a4⠀⠀⠀⠀⠀⠀│a5⠀⠀⠀⠀⠀⠀⠀│a6⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│mixA⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│mixC⠀⠀⠀⠀⠀⠀│DDD⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│FFF⠀⠀⠀⠀│ +│123abc⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│456def⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│789ghi⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│000⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│r45a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r45b⠀⠀⠀⠀⠀│r45c⠀⠀⠀⠀⠀⠀│r45d⠀⠀⠀⠀│r45e⠀⠀⠀⠀⠀│r45f⠀⠀⠀│ +│alpha9⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│beta8⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│gamma7⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│A1B2C3⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│D4E5F6⠀⠀⠀│G7H8I9⠀⠀⠀⠀│J0⠀⠀⠀⠀⠀⠀│K1⠀⠀⠀⠀⠀⠀⠀│L2⠀⠀⠀⠀⠀│ +│short1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│short3⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│short5⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│r50a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r50b⠀⠀⠀⠀⠀│r50c⠀⠀⠀⠀⠀⠀│r50d⠀⠀⠀⠀│r50e⠀⠀⠀⠀⠀│r50f⠀⠀⠀│ +│x1y2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│z3⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│w4⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│v5⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│longertext⠀⠀⠀⠀⠀⠀⠀⠀│mid⠀⠀⠀⠀⠀⠀│small⠀⠀⠀⠀⠀│s1⠀⠀⠀⠀⠀⠀│s22⠀⠀⠀⠀⠀⠀│s333⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│r55a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r55b⠀⠀⠀⠀⠀│r55c⠀⠀⠀⠀⠀⠀│r55d⠀⠀⠀⠀│r55e⠀⠀⠀⠀⠀│r55f⠀⠀⠀│ +│abc⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│def⠀⠀⠀⠀⠀⠀│ghi⠀⠀⠀⠀⠀⠀⠀│jkl⠀⠀⠀⠀⠀│mno⠀⠀⠀⠀⠀⠀│pqr⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│1a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│2b⠀⠀⠀⠀⠀⠀⠀│3c⠀⠀⠀⠀⠀⠀⠀⠀│4d⠀⠀⠀⠀⠀⠀│5e⠀⠀⠀⠀⠀⠀⠀│6f⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│row60a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│row60b⠀⠀⠀│row60c⠀⠀⠀⠀│row60d⠀⠀│row60e⠀⠀⠀│row60f⠀│ +│value1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│value3⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│value5⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│aa⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│bb⠀⠀⠀⠀⠀⠀⠀│cc⠀⠀⠀⠀⠀⠀⠀⠀│dd⠀⠀⠀⠀⠀⠀│ee⠀⠀⠀⠀⠀⠀⠀│ff⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│r65a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r65b⠀⠀⠀⠀⠀│r65c⠀⠀⠀⠀⠀⠀│r65d⠀⠀⠀⠀│r65e⠀⠀⠀⠀⠀│r65f⠀⠀⠀│ +│x⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│y⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│z⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│alpha01⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│beta02⠀⠀⠀│gamma03⠀⠀⠀│delta04⠀│eps05⠀⠀⠀⠀│z6⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│row70a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│row70b⠀⠀⠀│row70c⠀⠀⠀⠀│row70d⠀⠀│row70e⠀⠀⠀│row70f⠀│ +│1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│3⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│5⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│abc1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│def2⠀⠀⠀⠀⠀│ghi3⠀⠀⠀⠀⠀⠀│jkl4⠀⠀⠀⠀│mno5⠀⠀⠀⠀⠀│pqr6⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│r75a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r75b⠀⠀⠀⠀⠀│r75c⠀⠀⠀⠀⠀⠀│r75d⠀⠀⠀⠀│r75e⠀⠀⠀⠀⠀│r75f⠀⠀⠀│ +│mix⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│mix2⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│mix3⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│b⠀⠀⠀⠀⠀⠀⠀⠀│c⠀⠀⠀⠀⠀⠀⠀⠀⠀│d⠀⠀⠀⠀⠀⠀⠀│e⠀⠀⠀⠀⠀⠀⠀⠀│f⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│row80a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│row80b⠀⠀⠀│row80c⠀⠀⠀⠀│row80d⠀⠀│row80e⠀⠀⠀│row80f⠀│ +│123⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│234⠀⠀⠀⠀⠀⠀│345⠀⠀⠀⠀⠀⠀⠀│456⠀⠀⠀⠀⠀│567⠀⠀⠀⠀⠀⠀│678⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│Aaa⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│Bbb⠀⠀⠀⠀⠀⠀│Ccc⠀⠀⠀⠀⠀⠀⠀│Ddd⠀⠀⠀⠀⠀│Eee⠀⠀⠀⠀⠀⠀│Fff⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│r85a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r85b⠀⠀⠀⠀⠀│r85c⠀⠀⠀⠀⠀⠀│r85d⠀⠀⠀⠀│r85e⠀⠀⠀⠀⠀│r85f⠀⠀⠀│ +│x1⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│x3⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│x5⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│abcde12345⠀⠀⠀⠀⠀⠀⠀⠀│f1⠀⠀⠀⠀⠀⠀⠀│g2⠀⠀⠀⠀⠀⠀⠀⠀│h3⠀⠀⠀⠀⠀⠀│i4⠀⠀⠀⠀⠀⠀⠀│j5⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│row90a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│row90b⠀⠀⠀│row90c⠀⠀⠀⠀│row90d⠀⠀│row90e⠀⠀⠀│row90f⠀│ +│1a2b3c⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│4d5e6f⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│7g8h9i⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│short⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│mid⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│longer⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│r95a⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│r95b⠀⠀⠀⠀⠀│r95c⠀⠀⠀⠀⠀⠀│r95d⠀⠀⠀⠀│r95e⠀⠀⠀⠀⠀│r95f⠀⠀⠀│ +│a1b⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│c2d⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│e3f⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ +│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀⠀⠀│⠀⠀⠀⠀⠀⠀⠀│ diff --git a/tests/cases/motion/g-csv/run.vim b/tests/cases/motion/g-csv/run.vim new file mode 100644 index 0000000..0e2ef74 --- /dev/null +++ b/tests/cases/motion/g-csv/run.vim @@ -0,0 +1,24 @@ +" Verify the screen display after executing the Tir redraw command. +" After executing a command that misaligns the border positions, the borders are aligned. + +source $TIRENVI_ROOT/tests/common.vim + +lua << EOF +vim.keymap.set({ 'n', 'o', 'x' }, 'gtf', require('tirenvi').motion.f, { expr = true, desc = '[T]irEnvi: f pipe' }) +vim.keymap.set({ 'n', 'o', 'x' }, 'gtF', require('tirenvi').motion.F, { expr = true, desc = '[T]irEnvi: F pipe' }) +vim.keymap.set({ 'n', 'o', 'x' }, 'gtt', require('tirenvi').motion.t, { expr = true, desc = '[T]irEnvi: t pipe' }) +vim.keymap.set({ 'n', 'o', 'x' }, 'gtT', require('tirenvi').motion.T, { expr = true, desc = '[T]irEnvi: T pipe' }) +vim.keymap.set('n', 'gtg', require('tirenvi').motion.block_top, { desc = '[T]irEnvi: block top' }) +vim.keymap.set('n', 'gtG', require('tirenvi').motion.block_bottom, { desc = '[T]irEnvi: block bottom' }) +EOF + +edit $TIRENVI_ROOT/tests/data/complex.csv +call cursor(2, 1) +call feedkeys("gtg", "x") +call feedkeys("j", "x") +execute "normal dd" +call feedkeys("gtG", "x") +call feedkeys("k", "x") +execute "normal dd" + +call RunTest({}) \ No newline at end of file diff --git a/tests/cases/textobj/val/out-expected.txt b/tests/cases/textobj/val/out-expected.txt index 4c6ec80..cecd673 100644 --- a/tests/cases/textobj/val/out-expected.txt +++ b/tests/cases/textobj/val/out-expected.txt @@ -1,11 +1,7 @@ === MESSAGE === -block of 5 lines yanked +block of 3 lines yanked === DISPLAY === -gfm -│Name⠀│Age│City⠀⠀│Age│ -│----⠀│---│----⠀⠀│---│ -│Alice│23⠀│Tokyo⠀│23⠀│ -│Bob⠀⠀│31⠀│Osaka⠀│31⠀│ -│Carol│27⠀│Nagoya│27⠀│ -markdown +│1a⠀│2 a│ 3a⠀│2 a│ +│1↲⠀│2b⠀│3↲b⇥│2b⠀│ +│↲↲↲│⠀⠀⠀│⇥c⠀⠀│⠀⠀⠀│ diff --git a/tests/cases/textobj/val/run.vim b/tests/cases/textobj/val/run.vim index acc69b1..79a0bd0 100644 --- a/tests/cases/textobj/val/run.vim +++ b/tests/cases/textobj/val/run.vim @@ -9,8 +9,8 @@ M.setup({ }) EOF -edit $TIRENVI_ROOT/tests/data/simple.md -call cursor(6, 12) +edit $TIRENVI_ROOT/tests/data/simple.csv +call cursor(2, 11) call feedkeys("vah", "x") execute "normal y" execute "normal $" diff --git a/tests/common.vim b/tests/common.vim index 7184973..2c22969 100644 --- a/tests/common.vim +++ b/tests/common.vim @@ -20,8 +20,8 @@ M.setup({ -- level = vim.log.levels.DEBUG, use_timestamp = false, monitor = false, - probe = false, - -- probe = true, + -- probe = false, + probe = true, output = "print", }, }) From bd9891f89ef12cc6dc2a37d7276e19c9a23be0be Mon Sep 17 00:00:00 2001 From: OGAWA Keiji <70509917+kibi2@users.noreply.github.com> Date: Sat, 4 Apr 2026 06:54:53 +0900 Subject: [PATCH 2/5] feat: make column width adjustment repeatable with "." --- lua/tirenvi/editor/column.lua | 56 ++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/lua/tirenvi/editor/column.lua b/lua/tirenvi/editor/column.lua index 0d2dd78..7a12037 100644 --- a/lua/tirenvi/editor/column.lua +++ b/lua/tirenvi/editor/column.lua @@ -24,60 +24,68 @@ local function get_current_col() end ---@param mode string -local function change_width(mode) +local function change_width(mode, count) local bufnr = vim.api.nvim_get_current_buf() - log.probe("set_width called") - local width = vim.v.count1 - log.probe(width) local irow, icol = get_current_col() - log.probe(irow) - log.probe(icol) if not irow or not icol then return end local lines = buffer.get_lines(bufnr, 0, -1, false) local top = tir_vim.get_block_top_nrow(lines, irow) local bottom = tir_vim.get_block_bottom_nrow(lines, irow) - log.probe(top) - log.probe(bottom) local lines = buffer.get_lines(bufnr, top - 1, bottom, false) local blocks = vim_parser.parse(lines) local block = blocks[1] - log.probe(block) assert(block.kind == "grid") - log.probe(block.attr) - log.probe(block.attr.columns) local old_width = block.attr.columns[icol].width if mode == "set" then - block.attr.columns[icol].width = width + block.attr.columns[icol].width = count elseif mode == "increase" then - block.attr.columns[icol].width = old_width + width + block.attr.columns[icol].width = old_width + count elseif mode == "decrease" then - block.attr.columns[icol].width = old_width - width + block.attr.columns[icol].width = old_width - count end - log.probe(block.attr.columns) local vi_lines = vim_parser.unparse(blocks) ui.set_lines(bufnr, top - 1, bottom, vi_lines) end +local function set_repeat(cmd) + pcall(vim.fn["repeat#set"], cmd) +end + -- public API -function M.increase_width() - change_width("increase") +function M.set_width(count) + change_width("set", count) + set_repeat(":" .. count .. "TirSetWidth\n") end -function M.decrease_width() - change_width("decrease") +function M.increase_width(count) + change_width("increase", count) + set_repeat(":" .. count .. "TirIncreaseWidth\n") end -function M.set_width() - change_width("set") +function M.decrease_width(count) + change_width("decrease", count) + set_repeat(":" .. count .. "TirDecreaseWidth\n") end function M.setup() - vim.keymap.set("n", "=" .. config.textobj.cell, M.set_width, { desc = "set column width" }) - vim.keymap.set("n", ">" .. config.textobj.cell, M.increase_width, { desc = "increase column width" }) - vim.keymap.set("n", "<" .. config.textobj.cell, M.decrease_width, { desc = "decrease column width" }) + vim.api.nvim_create_user_command("TirSetWidth", function(opts) + require("tirenvi.editor.column").set_width(opts.count) + end, { count = true }) + vim.api.nvim_create_user_command("TirIncreaseWidth", function(opts) + require("tirenvi.editor.column").increase_width(opts.count) + end, { count = true }) + vim.api.nvim_create_user_command("TirDecreaseWidth", function(opts) + require("tirenvi.editor.column").decrease_width(opts.count) + end, { count = true }) + vim.keymap.set("n", "=c", function() vim.cmd(vim.v.count1 .. "TirSetWidth ") end, + { desc = "set column width" }) + vim.keymap.set("n", ">c", function() vim.cmd(vim.v.count1 .. "TirIncreaseWidth ") end, + { desc = "increase column width" }) + vim.keymap.set("n", " Date: Sat, 4 Apr 2026 07:19:28 +0900 Subject: [PATCH 3/5] feat: add vim-repeat integration for '.' repeat - enable '.' repeat for =c, >c, Date: Sat, 4 Apr 2026 10:36:49 +0900 Subject: [PATCH 4/5] refactor: change keymap [=><]c -> t[=+-] for update column width --- lua/tirenvi/editor/column.lua | 100 -------------------------------- lua/tirenvi/editor/commands.lua | 36 ++++++++---- lua/tirenvi/init.lua | 73 ++++++++++++++++++++++- 3 files changed, 98 insertions(+), 111 deletions(-) delete mode 100644 lua/tirenvi/editor/column.lua diff --git a/lua/tirenvi/editor/column.lua b/lua/tirenvi/editor/column.lua deleted file mode 100644 index 5e6b66a..0000000 --- a/lua/tirenvi/editor/column.lua +++ /dev/null @@ -1,100 +0,0 @@ -local buffer = require("tirenvi.state.buffer") -local vim_parser = require("tirenvi.core.vim_parser") -local tir_vim = require("tirenvi.core.tir_vim") -local ui = require("tirenvi.ui") -local notify = require("tirenvi.util.notify") -local log = require("tirenvi.util.log") - -local M = {} - --- private helpers - ----@return integer|nil ----@return integer|nil -local function get_current_col() - local irow, icol0 = unpack(vim.api.nvim_win_get_cursor(0)) - local icol = icol0 + 1 - local cline = vim.api.nvim_get_current_line() - local cbyte_pos = tir_vim.get_pipe_byte_position(cline) - if #cbyte_pos == 0 then - return nil, nil - end - return irow, tir_vim.get_current_col_index(cbyte_pos, icol) -end - ----@param mode string -local function change_width(mode, count) - local bufnr = vim.api.nvim_get_current_buf() - local irow, icol = get_current_col() - if not irow or not icol then - return - end - local lines = buffer.get_lines(bufnr, 0, -1, false) - local top = tir_vim.get_block_top_nrow(lines, irow) - local bottom = tir_vim.get_block_bottom_nrow(lines, irow) - local lines = buffer.get_lines(bufnr, top - 1, bottom, false) - local blocks = vim_parser.parse(lines) - local block = blocks[1] - assert(block.kind == "grid") - local old_width = block.attr.columns[icol].width - if mode == "set" then - block.attr.columns[icol].width = count - elseif mode == "increase" then - block.attr.columns[icol].width = old_width + count - elseif mode == "decrease" then - block.attr.columns[icol].width = old_width - count - end - local vi_lines = vim_parser.unparse(blocks) - ui.set_lines(bufnr, top - 1, bottom, vi_lines) -end - -local warned = false - -local function set_repeat(cmd) - local ok = pcall(function() - vim.fn["repeat#set"](cmd) - end) - if not ok and not warned then - warned = true - notify.info( - "tirenvi: install 'tpope/vim-repeat' to enable '.' repeat" - ) - end -end - --- public API - -function M.set_width(count) - change_width("set", count) - set_repeat(":" .. count .. "TirSetWidth\n") -end - -function M.increase_width(count) - change_width("increase", count) - set_repeat(":" .. count .. "TirIncreaseWidth\n") -end - -function M.decrease_width(count) - change_width("decrease", count) - set_repeat(":" .. count .. "TirDecreaseWidth\n") -end - -function M.setup() - vim.api.nvim_create_user_command("TirSetWidth", function(opts) - require("tirenvi.editor.column").set_width(opts.count) - end, { count = true }) - vim.api.nvim_create_user_command("TirIncreaseWidth", function(opts) - require("tirenvi.editor.column").increase_width(opts.count) - end, { count = true }) - vim.api.nvim_create_user_command("TirDecreaseWidth", function(opts) - require("tirenvi.editor.column").decrease_width(opts.count) - end, { count = true }) - vim.keymap.set("n", "=c", function() vim.cmd(vim.v.count1 .. "TirSetWidth ") end, - { desc = "set column width" }) - vim.keymap.set("n", ">c", function() vim.cmd(vim.v.count1 .. "TirIncreaseWidth ") end, - { desc = "increase column width" }) - vim.keymap.set("n", " Date: Sat, 4 Apr 2026 11:42:53 +0900 Subject: [PATCH 5/5] fix: ensure :Tir width works with '.' repeat - use repeat#set for repeat support - escape to clear range - convert to termcodes for proper key replay --- lua/tirenvi/config.lua | 1 - lua/tirenvi/editor/commands.lua | 2 +- lua/tirenvi/init.lua | 21 ++++++++----- tests/cases/check/health-ng/out-expected.txt | 3 +- tests/cases/check/health/out-expected.txt | 3 +- tests/cases/textobj/width/out-expected.txt | 12 ++++++++ tests/cases/textobj/width/run.vim | 31 ++++++++++++++++++++ tests/cases/tir/on/run.vim | 1 + 8 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 tests/cases/textobj/width/out-expected.txt create mode 100644 tests/cases/textobj/width/run.vim diff --git a/lua/tirenvi/config.lua b/lua/tirenvi/config.lua index 34e1bef..0480055 100644 --- a/lua/tirenvi/config.lua +++ b/lua/tirenvi/config.lua @@ -40,7 +40,6 @@ local defaults = { }, textobj = { column = "l", - cell = "c", }, } diff --git a/lua/tirenvi/editor/commands.lua b/lua/tirenvi/editor/commands.lua index 963b82d..9ccccae 100644 --- a/lua/tirenvi/editor/commands.lua +++ b/lua/tirenvi/editor/commands.lua @@ -61,7 +61,7 @@ local function cmd_width(bufnr, opts) return end local operator, num = opts.args:match("^width%s*([=+-]?)(%d*)") - num = tonumber(num) or 1 + num = tonumber(num) or 0 init.width(bufnr, operator, num) end diff --git a/lua/tirenvi/init.lua b/lua/tirenvi/init.lua index d6708ba..ba80a29 100644 --- a/lua/tirenvi/init.lua +++ b/lua/tirenvi/init.lua @@ -52,14 +52,14 @@ end ---@return integer|nil ---@return integer|nil local function get_current_col() - local irow, icol0 = unpack(vim.api.nvim_win_get_cursor(0)) - local icol = icol0 + 1 + local irow, ibyte0 = unpack(vim.api.nvim_win_get_cursor(0)) + local ibyte = ibyte0 + 1 local cline = vim.api.nvim_get_current_line() - local cbyte_pos = tir_vim.get_pipe_byte_position(cline) - if #cbyte_pos == 0 then + local pipe_pos = tir_vim.get_pipe_byte_position(cline) + if #pipe_pos == 0 then return nil, nil end - return irow, tir_vim.get_current_col_index(cbyte_pos, icol) + return irow, tir_vim.get_current_col_index(pipe_pos, ibyte) end ---@param operator string @@ -99,9 +99,10 @@ end local warned = false -local function set_repeat(cmd) +---@param command string +local function set_repeat(command) local ok = pcall(function() - vim.fn["repeat#set"](cmd) + vim.fn["repeat#set"](command) end) if not ok and not warned then warned = true @@ -200,7 +201,11 @@ end ---@return nil function M.width(bufnr, operator, count) change_width(operator, count) - set_repeat(":Tir width" .. operator .. count .. "\n") + local command = vim.api.nvim_replace_termcodes( + ":Tir width " .. operator .. count .. "", + true, false, true + ) + set_repeat(command) end ---@param bufnr number diff --git a/tests/cases/check/health-ng/out-expected.txt b/tests/cases/check/health-ng/out-expected.txt index 2758bec..73d1622 100644 --- a/tests/cases/check/health-ng/out-expected.txt +++ b/tests/cases/check/health-ng/out-expected.txt @@ -4,6 +4,7 @@ - version: 0.2.0 - ⚠️ WARNING Could not parse grep version string. +- ⚠️ WARNING vim-repeat not found ('.' repeat disabled) - ✅ OK grep found - ✅ OK tir-csv found - ✅ OK tir-gfm-lite found @@ -14,4 +15,4 @@ === DISPLAY === ============================================================================== tirenvi ~ -tirenvi: 1 ⚠️ 4 ❌ +tirenvi: 2 ⚠️ 4 ❌ diff --git a/tests/cases/check/health/out-expected.txt b/tests/cases/check/health/out-expected.txt index 83487ad..1b7cecf 100644 --- a/tests/cases/check/health/out-expected.txt +++ b/tests/cases/check/health/out-expected.txt @@ -3,6 +3,7 @@ - version: 0.2.0 +- ⚠️ WARNING vim-repeat not found ('.' repeat disabled) - ✅ OK tir-csv found - ✅ OK tir-csv version 0.1.4 OK - ✅ OK tir-gfm-lite found @@ -12,4 +13,4 @@ === DISPLAY === ============================================================================== tirenvi ~ -tirenvi: ✅ +tirenvi: 1 ⚠️ diff --git a/tests/cases/textobj/width/out-expected.txt b/tests/cases/textobj/width/out-expected.txt new file mode 100644 index 0000000..bbd277b --- /dev/null +++ b/tests/cases/textobj/width/out-expected.txt @@ -0,0 +1,12 @@ +=== MESSAGE === +tirenvi: install 'tpope/vim-repeat' to enable '.' repeat +5 changes; before #6 0 seconds ago + +=== DISPLAY === +gfm +│Name│Age⠀⠀│City⠀⠀⠀⠀│ +│----│---⠀⠀│----⠀⠀⠀⠀│ +│Alice│23⠀⠀⠀│Tokyo⠀⠀⠀│ +│Bob│31⠀⠀⠀│Osaka⠀⠀⠀│ +│Carol│27⠀⠀⠀│Nagoya⠀⠀│ +markdown diff --git a/tests/cases/textobj/width/run.vim b/tests/cases/textobj/width/run.vim new file mode 100644 index 0000000..6066d35 --- /dev/null +++ b/tests/cases/textobj/width/run.vim @@ -0,0 +1,31 @@ +source $TIRENVI_ROOT/tests/common.vim + +lua << EOF +local M = require("tirenvi") +M.setup({ + textobj = { + column = "h" + }, +}) +EOF + +edit $TIRENVI_ROOT/tests/data/simple.md +call cursor(2, 22) +Tir width=8 +call cursor(3, 11) +Tir width=5 +call cursor(4, 1) +Tir width=9 +call cursor(5, 23) +Tir width+9 +Tir width+5 +Tir width+ +call feedkeys("u", "x") +call cursor(6, 20) +Tir width-10 +call cursor(2, 1) +Tir width-100 +call cursor(1, 1) +Tir width- + +call RunTest({}) \ No newline at end of file diff --git a/tests/cases/tir/on/run.vim b/tests/cases/tir/on/run.vim index 0247036..906ae50 100644 --- a/tests/cases/tir/on/run.vim +++ b/tests/cases/tir/on/run.vim @@ -7,5 +7,6 @@ let outfile = 'gen.csv' edit $TIRENVI_ROOT/tests/data/complex.csv Tir toggle Tir toggle +Tir hbar call RunTest({})