diff --git a/lua/mini/snippets.lua b/lua/mini/snippets.lua index b37d3e7c..7c2531d5 100644 --- a/lua/mini/snippets.lua +++ b/lua/mini/snippets.lua @@ -2126,19 +2126,39 @@ end H.session_ensure_gravity = function(session) -- Ensure proper gravity relative to reference node (first node with current - -- tabstop): "left" before, "expand" at, "right" after. This should account - -- for typing in snippets like `$1$2$1$2$1` (in both 1 and 2). + -- tabstop): "left" before, "expand" at and all its parents, "right" after. + -- This accounts for typing in snippets like `$1$2$1$2$1` (in both 1 and 2) + -- and correct tracking of $2 in `${2:$1}` (should expand if typing in 1). local buf_id, cur_tabstop, base_gravity = session.buf_id, session.cur_tabstop, 'left' + local parent_extmarks = {} local ensure = function(n) local is_ref_node = n.tabstop == cur_tabstop and base_gravity == 'left' + if is_ref_node then + for _, extmark_id in ipairs(parent_extmarks) do + H.extmark_set_gravity(buf_id, extmark_id, 'expand') + end + -- Disable parent stack tracking, as reference node is accounted for + parent_extmarks = nil + end H.extmark_set_gravity(buf_id, n.extmark_id, is_ref_node and 'expand' or base_gravity) base_gravity = (is_ref_node or base_gravity == 'right') and 'right' or 'left' end - -- NOTE: This relies on `H.nodes_traverse` to first apply to the node and - -- only later (recursively) to placeholder nodes, which makes them all have - -- "right" gravity and thus being removable during replacing placeholder (as - -- they will not cover newly inserted text). - H.nodes_traverse(session.nodes, ensure) + + local ensure_in_nodes + ensure_in_nodes = function(nodes) + for _, n in ipairs(nodes) do + -- NOTE: apply first to the node and only later to placeholder nodes, + -- which makes them have "right" gravity and thus being removable during + -- replacing placeholder (as they will not cover newly inserted text). + ensure(n) + if n.placeholder ~= nil then + if parent_extmarks ~= nil then table.insert(parent_extmarks, n.extmark_id) end + ensure_in_nodes(n.placeholder) + if parent_extmarks ~= nil then parent_extmarks[#parent_extmarks] = nil end + end + end + end + ensure_in_nodes(session.nodes) end H.session_get_ref_node = function(session) diff --git a/tests/screenshots/tests-test_snippets.lua---Tricky-snippets---nested-empty-tabstops-004 b/tests/screenshots/tests-test_snippets.lua---Tricky-snippets---nested-empty-tabstops-004 index 75480d9b..5e10e5bf 100644 --- a/tests/screenshots/tests-test_snippets.lua---Tricky-snippets---nested-empty-tabstops-004 +++ b/tests/screenshots/tests-test_snippets.lua---Tricky-snippets---nested-empty-tabstops-004 @@ -1,19 +1,19 @@ -|---------|---------|---------|---------| -1|••a a a∎ +1|a a a∎ 2|~ 3|~ 4|~ 5|~ 6|~ -7|[No Name] [+] 1,2-4 All +7|[No Name] [+] 1,2 All 8|-- INSERT -- -|---------|---------|---------|---------| -1|0012121322222222222222222222222222222222 -2|4444444444444444444444444444444444444444 -3|4444444444444444444444444444444444444444 -4|4444444444444444444444444444444444444444 -5|4444444444444444444444444444444444444444 -6|4444444444444444444444444444444444444444 -7|5555555555555555555555555555555555555555 -8|6666666666667777777777777777777777777777 +1|0101021111111111111111111111111111111111 +2|3333333333333333333333333333333333333333 +3|3333333333333333333333333333333333333333 +4|3333333333333333333333333333333333333333 +5|3333333333333333333333333333333333333333 +6|3333333333333333333333333333333333333333 +7|4444444444444444444444444444444444444444 +8|5555555555556666666666666666666666666666 diff --git a/tests/screenshots/tests-test_snippets.lua---Tricky-snippets---nested-empty-tabstops-005 b/tests/screenshots/tests-test_snippets.lua---Tricky-snippets---nested-empty-tabstops-005 index 223c2c03..4f8b319e 100644 --- a/tests/screenshots/tests-test_snippets.lua---Tricky-snippets---nested-empty-tabstops-005 +++ b/tests/screenshots/tests-test_snippets.lua---Tricky-snippets---nested-empty-tabstops-005 @@ -1,15 +1,15 @@ -|---------|---------|---------|---------| -1|•b b a∎ +1|b b a∎ 2|~ 3|~ 4|~ 5|~ 6|~ -7|[No Name] [+] 1,2-3 All +7|[No Name] [+] 1,2 All 8|-- INSERT -- -|---------|---------|---------|---------| -1|0121203222222222222222222222222222222222 +1|0101231111111111111111111111111111111111 2|4444444444444444444444444444444444444444 3|4444444444444444444444444444444444444444 4|4444444444444444444444444444444444444444 diff --git a/tests/test_snippets.lua b/tests/test_snippets.lua index b561b6e5..51ca386e 100644 --- a/tests/test_snippets.lua +++ b/tests/test_snippets.lua @@ -3210,7 +3210,7 @@ T['Session']['preserves order of "squashed" empty tabstops'] = function() child.expect_screenshot() end -T['Session']['whole session tracking'] = function() +T['Session']['tracks whole session'] = function() local validate_session_range = function(ref_from, ref_to) local session = get() local data = @@ -3250,6 +3250,62 @@ T['Session']['whole session tracking'] = function() validate_session_range({ 0, 5 }, { 1, 7 }) end +T['Session']['tracks nodes in case of nested placeholders'] = function() + start_session('${1:$2} $1') + local ref_nodes = { + { + tabstop = '1', + extmark = { row = 0, col = 0, end_row = 0, end_col = 0 }, + placeholder = { + { + tabstop = '2', + extmark = { row = 0, col = 0, end_row = 0, end_col = 0 }, + placeholder = { { text = '', extmark = { row = 0, col = 0, end_row = 0, end_col = 0 } } }, + }, + }, + }, + { text = ' ' }, + { + tabstop = '1', + extmark = { row = 0, col = 1, end_row = 0, end_col = 1 }, + placeholder = { + { + tabstop = '2', + extmark = { row = 0, col = 1, end_row = 0, end_col = 1 }, + placeholder = { { text = '', extmark = { row = 0, col = 1, end_row = 0, end_col = 1 } } }, + }, + }, + }, + { tabstop = '0' }, + } + validate_session_nodes_partial(get(), ref_nodes) + + jump('next') + type_keys('xxx') + local ref_nodes_after = { + { + tabstop = '1', + -- Should expand parent tabstop's region + extmark = { row = 0, col = 0, end_row = 0, end_col = 3 }, + placeholder = { + -- Should correctly track region of reference node + { tabstop = '2', extmark = { row = 0, col = 0, end_row = 0, end_col = 3 } }, + }, + }, + { text = ' ' }, + -- Should expand region in linked tabstops also + { + tabstop = '1', + extmark = { row = 0, col = 4, end_row = 0, end_col = 7 }, + placeholder = { + { tabstop = '2', extmark = { row = 0, col = 4, end_row = 0, end_col = 7 } }, + }, + }, + { tabstop = '0' }, + } + validate_session_nodes_partial(get(), ref_nodes_after) +end + T['Session']['autostop'] = new_set() T['Session']['autostop']['works when text is typed with final tabstop being current'] = function() @@ -3840,7 +3896,7 @@ T['Session']['linked tabstops']['works for tabstops with different placeholders' end --stylua: ignore -T['Session']['linked tabstops']['result in proper extmark tracking'] = function() +T['Session']['linked tabstops']['have proper extmark tracking'] = function() local validate = function(ref_nodes) validate_session_nodes_partial(get(), ref_nodes) end -- As placeholder in another tabstop