diff --git a/README.md b/README.md
index e04282a..3d4a2af 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,8 @@
-The goal of `nvim-paredit` is to provide a comparable s-expression editing experience in Neovim to that provided by Emacs. This is what is provided:
+The goal of `nvim-paredit` is to provide a comparable s-expression editing experience in Neovim to that provided by
+Emacs. This is what is provided:
- Treesitter based lisp structural editing, cursor motions and text object selections
- Dot-repeatable keybindings
@@ -24,9 +25,11 @@ The goal of `nvim-paredit` is to provide a comparable s-expression editing exper
## Project Status
-This is currently **beta software**. It works well in the workflows of the current maintainers but has not been thoroughly tested with many users.
+This is currently **beta software**. It works well in the workflows of the current maintainers but has not been
+thoroughly tested with many users.
-It currently only has first-class support for the `clojure` language and has a focus on supporting the fundamental paredit operations and motions.
+It currently only has first-class support for the `clojure` language and has a focus on supporting the fundamental
+paredit operations and motions.
## Installation
@@ -62,6 +65,13 @@ paredit.setup({
-- this case it will place the cursor on the moved edge
cursor_behaviour = "auto", -- remain, follow, auto
+ dragging = {
+ -- If set to `true` paredit will attempt to infer if an element being
+ -- dragged is part of a 'paired' form like as a map. If so then the element
+ -- will be dragged along with it's pair.
+ auto_drag_pairs = true,
+ },
+
indent = {
-- This controls how nvim-paredit handles indentation when performing operations which
-- should change the indentation of the form (such as when slurping or barfing).
@@ -85,6 +95,9 @@ paredit.setup({
[">e"] = { paredit.api.drag_element_forwards, "Drag element right" },
["p"] = { api.drag_pair_forwards, "Drag element pairs right" },
+ ["f"] = { paredit.api.drag_form_forwards, "Drag form right" },
["
vim.lsp.buf.format
- Below is a reference implementation for using `vim.lsp.buf.format` to replace the native implementation. This implementation won't be nearly as performant but it will be more correct.
-
- ```lua
- local function lsp_indent(event, opts)
- local traversal = require("nvim-paredit.utils.traversal")
- local utils = require("nvim-paredit.indentation.utils")
- local langs = require("nvim-paredit.lang")
-
- local lang = langs.get_language_api()
-
- local parent = event.parent
-
- local child
- if event.type == "slurp-forwards" then
- child = parent:named_child(parent:named_child_count() - 1)
- elseif event.type == "slurp-backwards" then
- child = parent:named_child(1)
- elseif event.type == "barf-forwards" then
- child = traversal.get_next_sibling_ignoring_comments(event.parent, { lang = lang })
- elseif event.type == "barf-backwards" then
- child = event.parent
- else
- return
- end
-
- local child_range = { child:range() }
- local lines = utils.find_affected_lines(child, utils.get_node_line_range(child_range))
-
- vim.lsp.buf.format({
- bufnr = opts.buf or 0,
- range = {
- ["start"] = { lines[1] + 1, 0 },
- ["end"] = { lines[#lines] + 1, 0 },
- },
- })
+Below is a reference implementation for using `vim.lsp.buf.format` to replace the native implementation. This
+implementation won't be nearly as performant but it will be more correct.
+
+```lua
+local function lsp_indent(event, opts)
+ local traversal = require("nvim-paredit.utils.traversal")
+ local utils = require("nvim-paredit.indentation.utils")
+ local langs = require("nvim-paredit.lang")
+
+ local lang = langs.get_language_api()
+
+ local parent = event.parent
+
+ local child
+ if event.type == "slurp-forwards" then
+ child = parent:named_child(parent:named_child_count() - 1)
+ elseif event.type == "slurp-backwards" then
+ child = parent:named_child(1)
+ elseif event.type == "barf-forwards" then
+ child = traversal.get_next_sibling_ignoring_comments(event.parent, { lang = lang })
+ elseif event.type == "barf-backwards" then
+ child = event.parent
+ else
+ return
end
- require("nvim-paredit").setup({
- indent = {
- enabled = true,
- indentor = lsp_indent
- }
+ local child_range = { child:range() }
+ local lines = utils.find_affected_lines(child, utils.get_node_line_range(child_range))
+
+ vim.lsp.buf.format({
+ bufnr = opts.buf or 0,
+ range = {
+ ["start"] = { lines[1] + 1, 0 },
+ ["end"] = { lines[#lines] + 1, 0 },
+ },
})
- ```
+end
+
+local child_range = { child:range() }
+local lines = utils.find_affected_lines(child, utils.get_node_line_range(child_range))
+
+vim.lsp.buf.format({
+ bufnr = opts.buf or 0,
+ range = {
+ ["start"] = { lines[1] + 1, 0 },
+ ["end"] = { lines[#lines] + 1, 0 },
+ },
+})
+end
+
+require("nvim-paredit").setup({
+ indent = {
+ enabled = true,
+ indentor = lsp_indent,
+ },
+})
+```
+
+## Pairwise Dragging
+
+Nvim-paredit has support for dragging elements pairwise. If an element being dragged is within a form that contains
+pairs of elements (such as a clojure `map`) then the element will be dragged along with it's pair.
+
+For example:
+
+```clojure
+{:a 1
+ |:b 2}
+;; Drag backwards
+{|:b 2
+ :a 1}
+```
+
+This is enabled by default and can be disabled by setting `dragging.auto_drag_pairs = false`.
+
+Pairwise dragging works using treesitter queries to identify element pairs within some localized node. This means you
+can very easily extend the paredit pairwise implementation by simply adding new treesitter queries to your nvim
+configuration.
+
+You might want to extend if:
+
+1) You are a language extension author and want to add pairwise dragging support to your extension.
+2) You want to add support for some syntax not supported by nvim-paredit.
+
+This is especially useful if you have your own clojure macros that you want to enable pairwise dragging on.
+
+All you need to do to extend is to add a new file called `queries//paredit/pairwise.scm` in your nvim config
+directory. Make sure to include the `;; extends` directive to the file or you will overwrite any pre-existing queries
+defined by nvim-paredit or other language extensions.
+
+As an example if you want to add support for the following clojure macro:
+
+```clojure
+(defmacro my-custom-bindings [bindings & body]
+ ...)
+
+(my-custom-bindings [a 1
+ b 2]
+ (println a b))
+```
+
+You can add the following TS query
+
+```scm
+;; extends
+
+(list_lit
+ (sym_lit) @fn-name
+ (vec_lit
+ (_) @pair)
+ (#eq? @fn-name "my-custom-bindings"))
+```
+
## Language Support
-As this is built using Treesitter it requires that you have the relevant Treesitter grammar installed for your language of choice. Additionally `nvim-paredit` will need explicit support for the treesitter grammar as the node names and metadata of nodes vary between languages.
+As this is built using Treesitter it requires that you have the relevant Treesitter grammar installed for your language
+of choice. Additionally `nvim-paredit` will need explicit support for the treesitter grammar as the node names and
+metadata of nodes vary between languages.
-Right now `nvim-paredit` only has built in support for `clojure` but exposes an extension API for adding support for other lisp dialects. This API is considered **very alpha** and may change without warning to properly account for other languages when attempts are made to add support.
+Right now `nvim-paredit` only has built in support for `clojure` but exposes an extension API for adding support for
+other lisp dialects. This API is considered **very alpha** and may change without warning to properly account for other
+languages when attempts are made to add support.
Extensions can either be added as config when calling `setup`:
@@ -247,22 +340,18 @@ require("nvim-paredit").setup({
-- The node at cursor in the below example is `()` or 'list_lit':
-- '(|)
-- But the node root is `'()` or 'quoting_lit'
- get_node_root = function(node)
- end,
+ get_node_root = function(node) end,
-- This is the inverse of `get_node_root` for forms and should find the inner node for which
-- the forms elements are direct children.
--
-- For example given the node `'()` or 'quoting_lit', this function should return `()` or 'list_lit'.
- unwrap_form = function(node)
- end,
+ unwrap_form = function(node) end,
-- Accepts a Treesitter node and should return true or false depending on whether the given node
-- can be considered a 'form'
- node_is_form = function(node)
- end,
+ node_is_form = function(node) end,
-- Accepts a Treesitter node and should return true or false depending on whether the given node
-- can be considered a 'comment'
- node_is_comment = function(node)
- end,
+ node_is_comment = function(node) end,
-- Accepts a Treesitter node representing a form and should return the 'edges' of the node. This
-- includes the node text and the range covered by the node
get_form_edges = function(node)
@@ -271,12 +360,13 @@ require("nvim-paredit").setup({
right = { text = "}", range = { 0, 5, 0, 6 } },
}
end,
- }
- }
+ },
+ },
})
```
-Or by calling the `add_language_extension` API directly before the setup. This would be the recommended approach for extension plugin authors.
+Or by calling the `add_language_extension` API directly before the setup. This would be the recommended approach for
+extension plugin authors.
```lua
require("nvim-paredit").extension.add_language_extension("commonlisp", { ... }).
@@ -284,12 +374,13 @@ require("nvim-paredit").extension.add_language_extension("commonlisp", { ... }).
### Existing Language Extensions
-+ [fennel](https://github.com/julienvincent/nvim-paredit-fennel)
-+ [scheme](https://github.com/ekaitz-zarraga/nvim-paredit-scheme)
+- [fennel](https://github.com/julienvincent/nvim-paredit-fennel)
+- [scheme](https://github.com/ekaitz-zarraga/nvim-paredit-scheme)
---
-As no attempt has been made to add support for other grammars I have no idea if the language extension API's are actually sufficient for adding additional languages. They will evolve as attempts are made.
+As no attempt has been made to add support for other grammars I have no idea if the language extension API's are
+actually sufficient for adding additional languages. They will evolve as attempts are made.
## API
@@ -306,6 +397,8 @@ paredit.api.slurp_forwards()
- **`barf_backwards`**
- **`drag_element_forwards`**
- **`drag_element_backwards`**
+- **`drag_pair_forwards`**
+- **`drag_pair_backwards`**
- **`drag_form_forwards`**
- **`drag_form_backwards`**
- **`raise_element`**
@@ -334,10 +427,13 @@ Cursor api `paredit.cursor`
### `vim-sexp` wrap form (head/tail) replication
Require api module:
+
```lua
local paredit = require("nvim-paredit")
```
+
Add following keybindings to config:
+
```lua
["w"] = {
function()
@@ -383,13 +479,16 @@ Add following keybindings to config:
"Wrap form insert tail",
}
```
+
Same approach can be used for other `vim-sexp` keybindings (e.g. `e[`) with cursor placement or without.
## Prior Art
### [vim-sexp](https://github.com/guns/vim-sexp)
-Currently the de-facto s-expression editing plugin with the most extensive set of available editing operations. If you are looking for a more complete plugin with a wider range of supported languages then you might want to look into using this instead.
+Currently the de-facto s-expression editing plugin with the most extensive set of available editing operations. If you
+are looking for a more complete plugin with a wider range of supported languages then you might want to look into using
+this instead.
The main reasons you might want to consider `nvim-paredit` instead are:
@@ -401,4 +500,5 @@ The main reasons you might want to consider `nvim-paredit` instead are:
### [vim-sexp-mappings-for-regular-people](https://github.com/tpope/vim-sexp-mappings-for-regular-people)
-A companion to `vim-sexp` which configures `vim-sexp` with better mappings. The default mappings for `nvim-paredit` were derived from here.
+A companion to `vim-sexp` which configures `vim-sexp` with better mappings. The default mappings for `nvim-paredit` were
+derived from here.
diff --git a/lua/nvim-paredit/api/dragging.lua b/lua/nvim-paredit/api/dragging.lua
index 39d1ea3..1685edb 100644
--- a/lua/nvim-paredit/api/dragging.lua
+++ b/lua/nvim-paredit/api/dragging.lua
@@ -1,5 +1,8 @@
local traversal = require("nvim-paredit.utils.traversal")
+local common = require("nvim-paredit.utils.common")
+local ts_utils = require("nvim-paredit.utils.ts")
local ts = require("nvim-treesitter.ts_utils")
+local config = require("nvim-paredit.config")
local langs = require("nvim-paredit.lang")
local M = {}
@@ -44,24 +47,84 @@ function M.drag_form_backwards()
ts.swap_nodes(root, sibling, buf, true)
end
-function M.drag_element_forwards()
- local lang = langs.get_language_api()
- local current_node = lang.get_node_root(ts.get_node_at_cursor())
+local function find_current_pair(pairs, current_node)
+ for i, pair in ipairs(pairs) do
+ for _, node in ipairs(pair) do
+ if node:equal(current_node) then
+ return i, pair
+ end
+ end
+ end
+end
- local sibling = current_node:next_named_sibling()
- if not sibling then
+local function drag_node_in_pair(current_node, nodes, opts)
+ local direction = 1
+ if opts.reversed then
+ direction = -1
+ end
+
+ local pairs = common.chunk_table(nodes, 2)
+ local chunk_index, pair = find_current_pair(pairs, current_node)
+
+ local corresponding_pair = pairs[chunk_index + direction]
+ if not corresponding_pair then
return
end
local buf = vim.api.nvim_get_current_buf()
- ts.swap_nodes(current_node, sibling, buf, true)
+ if pair[2] and corresponding_pair[2] then
+ ts.swap_nodes(pair[2], corresponding_pair[2], buf, true)
+ end
+ if pair[1] and corresponding_pair[1] then
+ ts.swap_nodes(pair[1], corresponding_pair[1], buf, true)
+ end
+end
+
+local function drag_pair(opts)
+ local lang = langs.get_language_api()
+ local current_node = lang.get_node_root(ts.get_node_at_cursor())
+ if not current_node then
+ return
+ end
+
+ local pairwise_nodes = ts_utils.find_pairwise_nodes(
+ current_node,
+ vim.tbl_deep_extend("force", opts, {
+ lang = lang,
+ })
+ )
+ if not pairwise_nodes then
+ local parent = current_node:parent()
+ if not parent then
+ return
+ end
+
+ pairwise_nodes = traversal.get_children_ignoring_comments(parent, {
+ lang = lang,
+ })
+ end
+
+ drag_node_in_pair(current_node, pairwise_nodes, opts)
end
-function M.drag_element_backwards()
+local function drag_element(opts)
local lang = langs.get_language_api()
local current_node = lang.get_node_root(ts.get_node_at_cursor())
- local sibling = current_node:prev_named_sibling()
+ if opts.dragging.auto_drag_pairs then
+ local pairwise_nodes = ts_utils.find_pairwise_nodes(current_node, { lang = lang })
+ if pairwise_nodes then
+ return drag_node_in_pair(current_node, pairwise_nodes, opts)
+ end
+ end
+
+ local sibling
+ if opts.reversed then
+ sibling = current_node:prev_named_sibling()
+ else
+ sibling = current_node:next_named_sibling()
+ end
+
if not sibling then
return
end
@@ -70,4 +133,44 @@ function M.drag_element_backwards()
ts.swap_nodes(current_node, sibling, buf, true)
end
+function M.drag_element_forwards(opts)
+ local drag_opts = vim.tbl_deep_extend(
+ "force",
+ {
+ dragging = config.config.dragging or {},
+ },
+ opts or {},
+ {
+ reversed = false,
+ }
+ )
+ drag_element(drag_opts)
+end
+
+function M.drag_element_backwards(opts)
+ local drag_opts = vim.tbl_deep_extend(
+ "force",
+ {
+ dragging = config.config.dragging or {},
+ },
+ opts or {},
+ {
+ reversed = true,
+ }
+ )
+ drag_element(drag_opts)
+end
+
+function M.drag_pair_forwards()
+ drag_pair({
+ reversed = false,
+ })
+end
+
+function M.drag_pair_backwards()
+ drag_pair({
+ reversed = true,
+ })
+end
+
return M
diff --git a/lua/nvim-paredit/api/init.lua b/lua/nvim-paredit/api/init.lua
index df573fa..efac43e 100644
--- a/lua/nvim-paredit/api/init.lua
+++ b/lua/nvim-paredit/api/init.lua
@@ -15,6 +15,10 @@ local M = {
drag_element_forwards = dragging.drag_element_forwards,
drag_element_backwards = dragging.drag_element_backwards,
+
+ drag_pair_forwards = dragging.drag_pair_forwards,
+ drag_pair_backwards = dragging.drag_pair_backwards,
+
drag_form_forwards = dragging.drag_form_forwards,
drag_form_backwards = dragging.drag_form_backwards,
diff --git a/lua/nvim-paredit/api/selections.lua b/lua/nvim-paredit/api/selections.lua
index 1531801..3f9f058 100644
--- a/lua/nvim-paredit/api/selections.lua
+++ b/lua/nvim-paredit/api/selections.lua
@@ -41,7 +41,7 @@ function M.get_range_around_form()
end
function M.get_range_around_top_level_form()
- return get_range_around_form_impl(traversal.get_top_level_node_below_document)
+ return get_range_around_form_impl(traversal.find_local_root)
end
local function select_around_form_impl(range)
@@ -93,7 +93,7 @@ function M.get_range_in_form()
end
function M.get_range_in_top_level_form()
- return get_range_in_form_impl(traversal.get_top_level_node_below_document)
+ return get_range_in_form_impl(traversal.find_local_root)
end
local function select_in_form_impl(range)
diff --git a/lua/nvim-paredit/defaults.lua b/lua/nvim-paredit/defaults.lua
index 03eb727..cf8caa1 100644
--- a/lua/nvim-paredit/defaults.lua
+++ b/lua/nvim-paredit/defaults.lua
@@ -4,7 +4,7 @@ local unwrap = require("nvim-paredit.api.unwrap")
local M = {}
M.default_keys = {
- ["@"] = { unwrap.unwrap_form_under_cursor, "Splice sexp", },
+ ["@"] = { unwrap.unwrap_form_under_cursor, "Splice sexp" },
[">)"] = { api.slurp_forwards, "Slurp forwards" },
[">("] = { api.barf_backwards, "Barf backwards" },
@@ -15,6 +15,9 @@ M.default_keys = {
[">e"] = { api.drag_element_forwards, "Drag element right" },
["p"] = { api.drag_pair_forwards, "Drag element pairs right" },
+ ["f"] = { api.drag_form_forwards, "Drag form right" },
["
function M.get_language_api()
for l in string.gmatch(vim.bo.filetype, "[^.]+") do
if langs[l] ~= nil then
return langs[l]
end
end
- return nil
+ error("Could not find language extension for filetype " .. vim.bo.filetype, vim.log.levels.ERROR)
end
function M.add_language_extension(filetype, api)
diff --git a/lua/nvim-paredit/utils/common.lua b/lua/nvim-paredit/utils/common.lua
index 93b7c22..68c9825 100644
--- a/lua/nvim-paredit/utils/common.lua
+++ b/lua/nvim-paredit/utils/common.lua
@@ -9,6 +9,20 @@ function M.included_in_table(table, item)
return false
end
+function M.chunk_table(tbl, chunk_size)
+ local result = {}
+ for i = 1, #tbl, chunk_size do
+ local chunk = {}
+ for j = 0, chunk_size - 1 do
+ if tbl[i + j] then
+ table.insert(chunk, tbl[i + j])
+ end
+ end
+ table.insert(result, chunk)
+ end
+ return result
+end
+
-- Compares the two given { col, row } position tuples and returns -1/0/1 depending
-- on whether `a` is less than, equal to or greater than `b`
--
diff --git a/lua/nvim-paredit/utils/traversal.lua b/lua/nvim-paredit/utils/traversal.lua
index b8857ed..72d89d7 100644
--- a/lua/nvim-paredit/utils/traversal.lua
+++ b/lua/nvim-paredit/utils/traversal.lua
@@ -16,6 +16,22 @@ function M.find_nearest_form(current_node, opts)
end
end
+function M.get_children_ignoring_comments(node, opts)
+ local children = {}
+
+ local index = 0
+ local child = node:named_child(index)
+ while child do
+ if not child:extra() and not opts.lang.node_is_comment(child) then
+ table.insert(children, child)
+ end
+ index = index + 1
+ child = node:named_child(index)
+ end
+
+ return children
+end
+
local function get_child_ignoring_comments(node, index, opts)
if index < 0 or index >= node:named_child_count() then
return
@@ -139,34 +155,18 @@ function M.find_root_element_relative_to(root, child)
return M.find_root_element_relative_to(root, parent)
end
-function M.get_top_level_node_below_document(node)
- -- Document
- -- - Branch A
- -- -- Node X
- -- --- Sub-node 1
- -- - Branch B
- -- -- Node Y
- -- --- Sub-node 2
- -- --- Sub-node 3
-
- -- If we call this function on "Sub-node 2" we expect "Branch B" to be
- -- returned, the top level one below the document itself. We know which
- -- node is the document because it lacks a parent, just like Batman.
-
- local parent = node:parent()
-
- -- Does the node have a parent? If so, we might be at the right level.
- -- If not, we should just return the node right away, we're already too high.
- if parent then
- -- If the parent _also_ has a parent then we still need to go higher, recur.
- if parent:parent() then
- return M.get_top_level_node_below_document(parent)
+-- Find the root node of the tree `node` is a member of, excluding the root
+-- 'source' document.
+function M.find_local_root(node)
+ local current = node
+ while true do
+ local next = current:parent()
+ if not next or next:type() == "source" then
+ break
end
+ current = next
end
-
- -- As soon as we don't have a grandparent or parent, return the node
- -- we're on because it means we're one step below the top level document node.
- return node
+ return current
end
return M
diff --git a/lua/nvim-paredit/utils/ts.lua b/lua/nvim-paredit/utils/ts.lua
new file mode 100644
index 0000000..606e4ad
--- /dev/null
+++ b/lua/nvim-paredit/utils/ts.lua
@@ -0,0 +1,42 @@
+local traversal = require("nvim-paredit.utils.traversal")
+
+local M = {}
+
+-- Use a 'paredit/pairwise' treesitter query to find all nodes within a local
+-- branch that are labeled as @pair.
+--
+-- If any of these labeled nodes match the given target node then return all
+-- matched nodes.
+function M.find_pairwise_nodes(target_node, opts)
+ local root_node = traversal.find_local_root(target_node)
+
+ local bufnr = vim.api.nvim_get_current_buf()
+ local lang = vim.treesitter.language.get_lang(vim.bo.filetype)
+
+ local query = vim.treesitter.query.get(lang, "paredit/pairwise")
+ if not query then
+ return
+ end
+
+ local captures = query:iter_captures(root_node, bufnr)
+ local pairwise_nodes = {}
+ local found = false
+ for id, node in captures do
+ if query.captures[id] == "pair" then
+ if not node:extra() and not opts.lang.node_is_comment(node) then
+ table.insert(pairwise_nodes, node)
+ if node:equal(target_node) then
+ found = true
+ end
+ end
+ end
+ end
+
+ if not found then
+ return
+ end
+
+ return pairwise_nodes
+end
+
+return M
diff --git a/plugin/nvim-paredit.vim b/plugin/nvim-paredit.vim
deleted file mode 100644
index 4d2dbff..0000000
--- a/plugin/nvim-paredit.vim
+++ /dev/null
@@ -1,4 +0,0 @@
-if exists("g:loaded_paredit")
- finish
-endif
-let g:loaded_paredit = 1
diff --git a/queries/clojure/paredit/pairwise.scm b/queries/clojure/paredit/pairwise.scm
new file mode 100644
index 0000000..78a71a2
--- /dev/null
+++ b/queries/clojure/paredit/pairwise.scm
@@ -0,0 +1,26 @@
+(list_lit
+ (sym_lit) @fn-name
+ (vec_lit
+ (_) @pair)
+ (#any-of? @fn-name "let" "loop" "binding" "with-open" "with-redefs"))
+
+(map_lit
+ (_) @pair)
+
+(list_lit
+ (sym_lit) @fn-name
+ (_)
+ (_) @pair
+ (#eq? @fn-name "case"))
+
+(list_lit
+ (sym_lit) @fn-name
+ (_) @pair
+ (#eq? @fn-name "cond"))
+
+(list_lit
+ (sym_lit) @fn-name
+ (_)
+ (_)
+ (_) @pair
+ (#eq? @fn-name "condp"))
diff --git a/tests/nvim-paredit/pair_drag_spec.lua b/tests/nvim-paredit/pair_drag_spec.lua
new file mode 100644
index 0000000..139f7df
--- /dev/null
+++ b/tests/nvim-paredit/pair_drag_spec.lua
@@ -0,0 +1,86 @@
+local paredit = require("nvim-paredit.api")
+
+local prepare_buffer = require("tests.nvim-paredit.utils").prepare_buffer
+local expect_all = require("tests.nvim-paredit.utils").expect_all
+local expect = require("tests.nvim-paredit.utils").expect
+
+describe("paired-element-auto-dragging", function()
+ vim.api.nvim_buf_set_option(0, "filetype", "clojure")
+ it("should drag map pairs forward", function()
+ prepare_buffer({
+ content = "{:a 1 :b 2}",
+ cursor = { 1, 1 },
+ })
+
+ paredit.drag_element_forwards({
+ dragging = {
+ auto_drag_pairs = true,
+ },
+ })
+ expect({
+ content = "{:b 2 :a 1}",
+ cursor = { 1, 6 },
+ })
+ end)
+
+ it("should drag map pairs backwards", function()
+ prepare_buffer({
+ content = "{:a 1 :b 2}",
+ cursor = { 1, 9 },
+ })
+
+ paredit.drag_element_backwards({
+ dragging = {
+ auto_drag_pairs = true,
+ },
+ })
+ expect({
+ content = "{:b 2 :a 1}",
+ cursor = { 1, 1 },
+ })
+ end)
+
+ it("should detect various types", function()
+ expect_all(function()
+ paredit.drag_element_forwards({ dragging = { auto_drag_pairs = true } })
+ end, {
+ {
+ "let binding",
+ before_content = "(let [a b c d])",
+ before_cursor = { 1, 6 },
+ after_content = "(let [c d a b])",
+ after_cursor = { 1, 10 },
+ },
+ {
+ "loop binding",
+ before_content = "(loop [a b c d])",
+ before_cursor = { 1, 7 },
+ after_content = "(loop [c d a b])",
+ after_cursor = { 1, 11 },
+ },
+ {
+ "case",
+ before_content = "(case a :a 1 :b 2)",
+ before_cursor = { 1, 8 },
+ after_content = "(case a :b 2 :a 1)",
+ after_cursor = { 1, 13 },
+ },
+ })
+ end)
+end)
+
+describe("paired-element-dragging", function()
+ vim.api.nvim_buf_set_option(0, "filetype", "clojure")
+ it("should drag vector elements forwards", function()
+ prepare_buffer({
+ content = "'[a b c d]",
+ cursor = { 1, 2 },
+ })
+
+ paredit.drag_pair_forwards()
+ expect({
+ content = "'[c d a b]",
+ cursor = { 1, 6 },
+ })
+ end)
+end)