diff --git a/lua/nvim-paredit/indentation/native.lua b/lua/nvim-paredit/indentation/native.lua index 83e2dc8..53fd815 100644 --- a/lua/nvim-paredit/indentation/native.lua +++ b/lua/nvim-paredit/indentation/native.lua @@ -99,8 +99,17 @@ local function indent_barf(event) if parent:type() == "source" then delta = node_range[2] else - local form_edges = lang.get_form_edges(parent) - delta = node_range[2] - form_edges.left.range[2] - 1 + local row + local ref_node = utils.get_first_sibling_on_upper_line(node, { lang = lang }) + if ref_node then + local range = { ref_node:range() } + row = range[2] + else + local form_edges = lang.get_form_edges(parent) + row = form_edges.left.range[2] - 1 + end + + delta = node_range[2] - row end indent_lines(lines, delta * -1, { @@ -127,9 +136,18 @@ local function indent_slurp(event) end local lines = utils.find_affected_lines(child, utils.get_node_line_range(child_range)) - local form_edges = lang.get_form_edges(parent) - local delta = form_edges.left.range[4] - child_range[2] + local row + local ref_node = utils.get_first_sibling_on_upper_line(child, { lang = lang }) + if ref_node then + local range = { ref_node:range() } + row = range[2] + else + local form_edges = lang.get_form_edges(parent) + row = form_edges.left.range[4] + end + + local delta = row - child_range[2] indent_lines(lines, delta, { buf = event.buf, }) diff --git a/lua/nvim-paredit/indentation/utils.lua b/lua/nvim-paredit/indentation/utils.lua index 0afb09c..1c83cc9 100644 --- a/lua/nvim-paredit/indentation/utils.lua +++ b/lua/nvim-paredit/indentation/utils.lua @@ -61,4 +61,35 @@ function M.node_is_first_on_line(node, opts) return sibling_range[3] ~= node_range[1] end +-- This functions finds the closest sibling to a given `node` which is: +-- 1) Not on the same line (one line higher) +-- 2) Is the first node on the line +-- +-- This node can be used as an indentation reference point. +function M.get_first_sibling_on_upper_line(node, opts) + local node_range = { node:range() } + + local reference + local prev = node + + while prev do + prev = traversal.get_prev_sibling_ignoring_comments(prev, opts) + if not prev then + return reference + end + + local sibling_range = { prev:range() } + + if reference and reference:range() ~= sibling_range[1] then + return reference + end + + if sibling_range[1] ~= node_range[1] then + reference = prev + end + end + + return reference +end + return M diff --git a/tests/nvim-paredit/indentation_spec.lua b/tests/nvim-paredit/indentation_spec.lua index ed9adf7..197f9c1 100644 --- a/tests/nvim-paredit/indentation_spec.lua +++ b/tests/nvim-paredit/indentation_spec.lua @@ -66,6 +66,14 @@ describe("forward slurping indentation", function() after_content = { "(", ";; comment", " a)" }, after_cursor = { 1, 0 }, }, + + { + "should indent to the first relevant siblings indentation", + before_content = { "(def a []", " target sibling)", "child" }, + before_cursor = { 1, 1 }, + after_content = { "(def a []", " target sibling", " child)" }, + after_cursor = { 1, 1 }, + }, }) end) @@ -142,6 +150,14 @@ describe("forward barfing indentation", function() after_content = { "()", ";; comment", "a" }, after_cursor = { 1, 0 }, }, + + { + "should indent to the first relevant siblings indentation", + before_content = { "(def a []", " target (sibling", " child))" }, + before_cursor = { 2, 10 }, + after_content = { "(def a []", " target (sibling)", " child)" }, + after_cursor = { 2, 10 }, + }, }) end) @@ -166,5 +182,13 @@ describe("backward barfing indentation", function() after_content = { "(a", " (bc", " de))" }, after_cursor = { 2, 3 }, }, + + { + "should indent to the first relevant siblings indentation", + before_content = { "(def a []", " target (sibling", " child))" }, + before_cursor = { 3, 1 }, + after_content = { "(def a []", " target sibling", " (child))" }, + after_cursor = { 3, 2 }, + }, }) end)