fix(tables): cycle Tab into the next row instead of inserting one
Pressing <Tab> past the last cell unconditionally appended a new row below, even when there was already a next row to jump into. Tabbing through a header → separator → body table from the header row inserted a blank row between header and separator instead of landing in the first body cell. After the final cell, walk forward from the current row to the end of the table, skip any separator row, and jump to the first cell of the next data row. Only insert a fresh row when the cursor is on the table's last row (or only separator rows follow). Applied to both the Vim (autoload/nuwiki/commands.vim) and Neovim (lua/nuwiki/ commands.lua) sides so the two harnesses stay in sync; covered by new keymap tests in scripts/test-keymaps-vim.vim and test-keymaps.lua. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1025,10 +1025,22 @@ function! nuwiki#commands#smart_tab() abort
|
|||||||
if l:target_idx < len(l:starts)
|
if l:target_idx < len(l:starts)
|
||||||
return "\<Cmd>call nuwiki#commands#_table_jump_to_cell(" . line('.') . ", " . l:target_idx . ")\<CR>"
|
return "\<Cmd>call nuwiki#commands#_table_jump_to_cell(" . line('.') . ", " . l:target_idx . ")\<CR>"
|
||||||
endif
|
endif
|
||||||
" Past the final cell → add a fresh row below, cursor in its first cell.
|
" Past the final cell. Prefer cycling into the next existing row (skipping
|
||||||
|
" any separator row); only insert a fresh row when we're already on the
|
||||||
|
" table's last row.
|
||||||
|
let l:row = line('.')
|
||||||
|
let [l:s_row, l:e_row] = s:find_table_range(l:row)
|
||||||
|
let l:next = l:row + 1
|
||||||
|
while l:next <= l:e_row
|
||||||
|
let [l:_, l:next_cells] = s:parse_table_row(getline(l:next))
|
||||||
|
if !s:is_sep_row(l:next_cells)
|
||||||
|
return "\<Cmd>call nuwiki#commands#_table_jump_to_cell(" . l:next . ", 1)\<CR>"
|
||||||
|
endif
|
||||||
|
let l:next += 1
|
||||||
|
endwhile
|
||||||
let l:indent = matchstr(l:line, '^\s*')
|
let l:indent = matchstr(l:line, '^\s*')
|
||||||
let l:cursor_col = len(l:indent) + 2
|
let l:cursor_col = len(l:indent) + 2
|
||||||
return "\<Cmd>call nuwiki#commands#_table_insert_row_below(" . line('.') . ", " . l:cursor_col . ")\<CR>"
|
return "\<Cmd>call nuwiki#commands#_table_insert_row_below(" . l:row . ", " . l:cursor_col . ")\<CR>"
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! nuwiki#commands#smart_shift_tab() abort
|
function! nuwiki#commands#smart_shift_tab() abort
|
||||||
|
|||||||
+22
-6
@@ -826,18 +826,34 @@ function M.smart_tab()
|
|||||||
if start <= col + 1 then current_idx = i end
|
if start <= col + 1 then current_idx = i end
|
||||||
end
|
end
|
||||||
local target_idx = current_idx + 1
|
local target_idx = current_idx + 1
|
||||||
if target_idx >= #cell_starts then
|
if target_idx < #cell_starts then
|
||||||
return string.format(
|
|
||||||
'<Cmd>lua require("nuwiki.commands")._table_insert_row_below(%d)<CR>',
|
|
||||||
row
|
|
||||||
)
|
|
||||||
end
|
|
||||||
return string.format(
|
return string.format(
|
||||||
'<Cmd>lua require("nuwiki.commands")._table_jump_to_cell(%d, %d)<CR>',
|
'<Cmd>lua require("nuwiki.commands")._table_jump_to_cell(%d, %d)<CR>',
|
||||||
row,
|
row,
|
||||||
target_idx
|
target_idx
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
-- Past the final cell. Prefer cycling into the next existing row
|
||||||
|
-- (skipping any separator); only insert a fresh row when we're on the
|
||||||
|
-- table's last row.
|
||||||
|
local _, e_row = find_table_range(row)
|
||||||
|
local next_row = row + 1
|
||||||
|
while next_row <= e_row do
|
||||||
|
local _, next_cells = parse_table_row(vim.fn.getline(next_row))
|
||||||
|
if not is_separator_row(next_cells) then
|
||||||
|
return string.format(
|
||||||
|
'<Cmd>lua require("nuwiki.commands")._table_jump_to_cell(%d, %d)<CR>',
|
||||||
|
next_row,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
end
|
||||||
|
next_row = next_row + 1
|
||||||
|
end
|
||||||
|
return string.format(
|
||||||
|
'<Cmd>lua require("nuwiki.commands")._table_insert_row_below(%d)<CR>',
|
||||||
|
row
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
function M.smart_shift_tab()
|
function M.smart_shift_tab()
|
||||||
local line = vim.api.nvim_get_current_line()
|
local line = vim.api.nvim_get_current_line()
|
||||||
|
|||||||
@@ -197,6 +197,18 @@ call s:run('tab.next_cell_in_table', {
|
|||||||
\ 'keys': "A\<Tab>x\<Esc>",
|
\ 'keys': "A\<Tab>x\<Esc>",
|
||||||
\ 'expect_lines': ['| a | b |', '|x | |'],
|
\ 'expect_lines': ['| a | b |', '|x | |'],
|
||||||
\ })
|
\ })
|
||||||
|
call s:run('tab.cycles_to_next_row_when_not_last', {
|
||||||
|
\ 'lines': ['| a | b |', '|---|---|', '| c | d |'],
|
||||||
|
\ 'cursor': [1, 7],
|
||||||
|
\ 'keys': "i\<Tab>x\<Esc>",
|
||||||
|
\ 'expect_lines': ['| a | b |', '|---|---|', '|x c | d |'],
|
||||||
|
\ })
|
||||||
|
call s:run('tab.inserts_row_only_when_on_last_row', {
|
||||||
|
\ 'lines': ['| a | b |', '|---|---|', '| c | d |'],
|
||||||
|
\ 'cursor': [3, 7],
|
||||||
|
\ 'keys': "i\<Tab>x\<Esc>",
|
||||||
|
\ 'expect_lines': ['| a | b |', '|---|---|', '| c | d |', '|x | |'],
|
||||||
|
\ })
|
||||||
call s:run('tab.from_first_cell_moves_to_second', {
|
call s:run('tab.from_first_cell_moves_to_second', {
|
||||||
\ 'lines': ['| a | b |'],
|
\ 'lines': ['| a | b |'],
|
||||||
\ 'cursor': [1, 3],
|
\ 'cursor': [1, 3],
|
||||||
|
|||||||
@@ -454,6 +454,21 @@ vim.defer_fn(function()
|
|||||||
expect_lines = { '| a |x b |' },
|
expect_lines = { '| a |x b |' },
|
||||||
wait_ms = 100,
|
wait_ms = 100,
|
||||||
})
|
})
|
||||||
|
run('tab.cycles_to_next_row_when_not_last', {
|
||||||
|
lines = { '| a | b |', '|---|---|', '| c | d |' },
|
||||||
|
cursor = { 1, 6 }, -- inside last cell of header row
|
||||||
|
keys = 'i<Tab>x<Esc>',
|
||||||
|
-- Past the final cell of row 1: skip separator, land in first cell of row 3.
|
||||||
|
expect_lines = { '| a | b |', '|---|---|', '|x c | d |' },
|
||||||
|
wait_ms = 100,
|
||||||
|
})
|
||||||
|
run('tab.inserts_row_only_when_on_last_row', {
|
||||||
|
lines = { '| a | b |', '|---|---|', '| c | d |' },
|
||||||
|
cursor = { 3, 6 }, -- inside last cell of last data row
|
||||||
|
keys = 'i<Tab>x<Esc>',
|
||||||
|
expect_lines = { '| a | b |', '|---|---|', '| c | d |', '|x | |' },
|
||||||
|
wait_ms = 100,
|
||||||
|
})
|
||||||
run('shift_tab.prev_cell_in_table', {
|
run('shift_tab.prev_cell_in_table', {
|
||||||
lines = { '| a | b |' },
|
lines = { '| a | b |' },
|
||||||
cursor = { 1, 6 }, -- inside cell `b`
|
cursor = { 1, 6 }, -- inside cell `b`
|
||||||
|
|||||||
Reference in New Issue
Block a user