From d412077fb60e369913bedb0e3be145f212587c9c Mon Sep 17 00:00:00 2001 From: Julien Vincent Date: Sun, 13 Oct 2024 21:11:57 +0100 Subject: [PATCH] Add treesitter queries and tests for commonlisp --- README.md | 11 ++- lua/nvim-paredit/defaults.lua | 2 +- lua/nvim-paredit/treesitter/forms.lua | 5 +- queries/commonlisp/paredit/forms.scm | 13 +++ tests/config.lua | 2 +- .../commonlisp/element_raise_spec.lua | 52 ++++++++++ .../commonlisp/form_raise_spec.lua | 50 ++++++++++ tests/nvim-paredit/commonlisp/motion_spec.lua | 47 +++++++++ tests/nvim-paredit/commonlisp/slurp_spec.lua | 96 +++++++++++++++++++ 9 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 queries/commonlisp/paredit/forms.scm create mode 100644 tests/nvim-paredit/commonlisp/element_raise_spec.lua create mode 100644 tests/nvim-paredit/commonlisp/form_raise_spec.lua create mode 100644 tests/nvim-paredit/commonlisp/motion_spec.lua create mode 100644 tests/nvim-paredit/commonlisp/slurp_spec.lua diff --git a/README.md b/README.md index 424d38c..9df6b27 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ paredit.setup({ -- for new filetypes. -- -- Defaults to all supported filetypes. - filetypes = { "clojure", "fennel", "scheme" }, + filetypes = { "clojure", "fennel", "scheme", "lisp" }, -- This is some language specific configuration. Right now this is just used for -- setting character lists that are considered whitespace. @@ -288,8 +288,13 @@ understand the AST. Right now `nvim-paredit` has built-in support for: - `clojure` -- `fennel` -- `scheme` +- `fennel` * +- `scheme` * +- `commonlisp` * + +> [!NOTE] +> +> Items marked with `*` only have partial test coverage. Not all language syntax is guaranteed to work. Take a look at the [Language Queries Spec](./docs/language-queries.md) if you are wanting to add support for languages not built-in to nvim-paredit, or you want to develop on the existing queries. diff --git a/lua/nvim-paredit/defaults.lua b/lua/nvim-paredit/defaults.lua index b6c2380..be0ff0b 100644 --- a/lua/nvim-paredit/defaults.lua +++ b/lua/nvim-paredit/defaults.lua @@ -119,7 +119,7 @@ M.defaults = { enabled = false, indentor = require("nvim-paredit.indentation.native").indentor, }, - filetypes = { "clojure", "fennel", "scheme" }, + filetypes = { "clojure", "fennel", "scheme", "lisp" }, languages = { clojure = { whitespace_chars = { " ", "," }, diff --git a/lua/nvim-paredit/treesitter/forms.lua b/lua/nvim-paredit/treesitter/forms.lua index 712fc29..2337be6 100644 --- a/lua/nvim-paredit/treesitter/forms.lua +++ b/lua/nvim-paredit/treesitter/forms.lua @@ -94,7 +94,7 @@ end -- (list_lit -- (kw_lit)) @form.inner) @form.outer -- ``` --- +-- -- Given the above AST we would want to extract the `list_lit` annotated in -- this example with @form.inner. function M.get_form_inner(form_node, opts) @@ -140,6 +140,9 @@ local function get_last_anon_child_of_form_head(node) return current end local child = node:child(i) + if not child then + return + end if child:named() then return current end diff --git a/queries/commonlisp/paredit/forms.scm b/queries/commonlisp/paredit/forms.scm new file mode 100644 index 0000000..6471c82 --- /dev/null +++ b/queries/commonlisp/paredit/forms.scm @@ -0,0 +1,13 @@ +;; We only consider a list_lit a form if it starts with a "(" anonymous node. +;; Some constructs like `(defun)` or `(loop)` are constructed as: +;; +;; (list_lit +;; (defun ...)) +;; +;; And in these cases we want to consider the `(defun)` the form inner and +;; 'ignore' the `list_lit` node +(list_lit + open: "(") @form + +(loop_macro) @form +(defun) @form diff --git a/tests/config.lua b/tests/config.lua index 37c0dd1..5ad8a9a 100644 --- a/tests/config.lua +++ b/tests/config.lua @@ -11,6 +11,6 @@ vim.bo.swapfile = false require("nvim-treesitter.configs").setup({ parser_install_dir = vim.fn.getcwd() .. "/.build/parsers", - ensure_installed = { "clojure", "fennel", "scheme" }, + ensure_installed = { "clojure", "fennel", "scheme", "commonlisp" }, sync_install = true, }) diff --git a/tests/nvim-paredit/commonlisp/element_raise_spec.lua b/tests/nvim-paredit/commonlisp/element_raise_spec.lua new file mode 100644 index 0000000..7339752 --- /dev/null +++ b/tests/nvim-paredit/commonlisp/element_raise_spec.lua @@ -0,0 +1,52 @@ +local paredit = require("nvim-paredit.api") + +local prepare_buffer = require("tests.nvim-paredit.utils").prepare_buffer +local expect = require("tests.nvim-paredit.utils").expect + +describe("element raising", function() + vim.api.nvim_set_option_value("filetype", "lisp", { + buf = 0, + }) + + it("should raise the element", function() + prepare_buffer({ + "(a (|b))", + }) + paredit.raise_element() + expect({ + "(a |b)", + }) + end) + + it("should raise form elements when cursor is placed on edge", function() + prepare_buffer({ + "(a |(b))", + }) + + paredit.raise_element() + expect({ + "|(b)", + }) + + prepare_buffer({ + "(a |#(b))", + }) + + paredit.raise_element() + expect({ + "|#(b)", + }) + end) + + it("should do nothing if it is a direct child of the document root", function() + prepare_buffer({ + "|a", + "b", + }) + paredit.raise_form() + expect({ + "|a", + "b", + }) + end) +end) diff --git a/tests/nvim-paredit/commonlisp/form_raise_spec.lua b/tests/nvim-paredit/commonlisp/form_raise_spec.lua new file mode 100644 index 0000000..203039f --- /dev/null +++ b/tests/nvim-paredit/commonlisp/form_raise_spec.lua @@ -0,0 +1,50 @@ +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("form raising", function() + vim.api.nvim_set_option_value("filetype", "lisp", { + buf = 0, + }) + + it("should raise the form", function() + expect_all(paredit.raise_form, { + { + "list", + { "(a (b |c))" }, + { "|(b c)" }, + }, + { + "list with reader", + { "(a #(|b c))" }, + { "|#(b c)" }, + }, + }) + end) + + it("should do nothing if it is a direct child of the document root", function() + prepare_buffer({ + "(|a)", + "b", + }) + paredit.raise_form() + expect({ + "(|a)", + "b", + }) + end) + + it("should do nothing if it is outside of a form", function() + prepare_buffer({ + "|a", + "b", + }) + paredit.raise_form() + expect({ + "|a", + "b", + }) + end) +end) diff --git a/tests/nvim-paredit/commonlisp/motion_spec.lua b/tests/nvim-paredit/commonlisp/motion_spec.lua new file mode 100644 index 0000000..5693e70 --- /dev/null +++ b/tests/nvim-paredit/commonlisp/motion_spec.lua @@ -0,0 +1,47 @@ +local paredit = require("nvim-paredit.api") + +local prepare_buffer = require("tests.nvim-paredit.utils").prepare_buffer +local expect = require("tests.nvim-paredit.utils").expect + +describe("motions", function() + vim.api.nvim_set_option_value("filetype", "lisp", { + buf = 0, + }) + + it("should jump to next element in form (tail)", function() + prepare_buffer({ + "(a|a (bb) #(cc))", + }) + + paredit.move_to_next_element_tail() + expect({ + "(aa (bb|) #(cc))", + }) + + paredit.move_to_next_element_tail() + expect({ + "(aa (bb) #(cc|))", + }) + + paredit.move_to_next_element_tail() + expect({ + "(aa (bb) #(cc|))", + }) + end) + + it("should jump to next element in form (head)", function() + prepare_buffer({ + "(a|a (bb) #(cc))", + }) + + paredit.move_to_next_element_head() + expect({ + "(aa |(bb) #(cc))", + }) + + paredit.move_to_next_element_head() + expect({ + "(aa (bb) |#(cc))", + }) + end) +end) diff --git a/tests/nvim-paredit/commonlisp/slurp_spec.lua b/tests/nvim-paredit/commonlisp/slurp_spec.lua new file mode 100644 index 0000000..cb704b8 --- /dev/null +++ b/tests/nvim-paredit/commonlisp/slurp_spec.lua @@ -0,0 +1,96 @@ +local paredit = require("nvim-paredit.api") + +local expect_all = require("tests.nvim-paredit.utils").expect_all +local expect = require("tests.nvim-paredit.utils").expect + +describe("commonlisp slurping ::", function() + vim.api.nvim_set_option_value("filetype", "lisp", { + buf = 0, + }) + + describe("slurping backwards", function() + it("should slurp different form types", function() + expect_all(paredit.slurp_backwards, { + { + "list", + { "a (|)" }, + { "(a |)" }, + }, + { + "vector", + { "a |#()" }, + { "#(|a )" }, + }, + }) + end) + + it("should skip comments", function() + expect( + { + "a", + ";; comment", + "|()", + }, + paredit.slurp_backwards, + { + "(a", + ";; comment", + "|)", + } + ) + end) + end) + + describe("slurping forward", function() + it("should slurp forward different form types", function() + expect_all(paredit.slurp_forwards, { + { + "list", + { "(|) a" }, + { "(| a)" }, + }, + { + "vector", + { "#|() a" }, + { "#|( a)" }, + }, + { + "lambda", + { "(l|ambda (a b)) a" }, + { "(l|ambda (a b) a)" }, + }, + { + "lambda params inner", + { "(lambda (a b|) a)" }, + { "(lambda (a b| a))" }, + }, + { + "lambda params outer", + { "(lambda (a b|)) a" }, + { "(lambda (a b|) a)" }, + }, + { + "loop", + { "(loop for i from 1 to 10 do", " (print |i)) a" }, + { "(loop for i from 1 to 10 do", " (print |i) a)" }, + }, + }) + end) + + it("should skip comments", function() + expect( + { + "(|)", + ";; comment", + "a", + }, + paredit.slurp_forwards, + { + "|(", + ";; comment", + "a)", + } + ) + end) + end) +end)