SystemVerilog setup for NeoVim: treesitter-textobjects
NeoVim provides a powerful plugin nvim-treesitter-textobjects. By default, it provides its own textobjects:
- Classes
- Functions
- Blocks
- Comments
- Conditionals
- etc.
The only issue is that most of these are not implemented in most languages, including SystemVerilog. This post shows how to add support for SystemVerilog textobjects.
Setup
If you’re only interested in code or an example, please check out my gist. Feel free to ask any questions.
Install nvim-treesitter-textobjects
If you’re using LazyVim, just add the following plugin entry:
-- treesitter.lua
return {
-- TreeSitter plugin
{
"nvim-treesitter/nvim-treesitter",
event = { "BufReadPost", "BufNewFile" },
opts = {
ensure_installed = {
"verilog" -- Add Verilog/SystemVerilog support
},
textobjects = {
... -- Your own config
}
},
config = function(_, opts)
require("nvim-treesitter.configs").setup(opts)
end
},
-- To Support textobjects
{
"nvim-treesitter/nvim-treesitter-textobjects",
dependencies = "nvim-treesitter/nvim-treesitter",
}
}
Now you can use builtin textobjects in SystemVerilog files. However, you need to add keymaps & additional textobjects first.
Configure treesitter-textobjects
Check out an official example and configuration.
Or take this as an example:
-- Inside of your treesitter.lua
textobjects = {
select = {
enable = true,
lookahead = true, -- You need this to achieve correct behaviour
include_surrounding_whitespace = function(a)
-- I highly recommend defining a function to check if surrounding whitespace is needed
if a.query_string == "@parameter.outer" then
return true
elseif a.query_string == "@loop.outer" then
return true
elseif a.query_string == "@conditional.outer" then
return true
elseif a.query_string == "@block.outer" then
return true
elseif a.query_string == "@assignment.outer" then
return true
elseif a.query_string == "@module.outer" then
return true
elseif a.query_string == "@macro.outer" then
return true
elseif a.query_string == "@call.outer" then
return true
elseif a.query_string == "@comment.outer" then
return true
elseif a.query_string == "@statement.outer" then
return true
elseif a.query_string == "@function.outer" then
return true
elseif a.query_string == "@class.outer" then
return true
else
return false
end
end,
}
keymaps = { -- These are my keymaps, customize to your needs
["af"] = "@function.outer",
["if"] = "@function.inner",
["ac"] = "@class.outer",
["ic"] = "@class.inner",
["aa"] = "@parameter.outer",
["ia"] = "@parameter.inner",
["al"] = "@loop.outer",
["il"] = "@loop.inner",
["ai"] = "@conditional.outer",
["ii"] = "@conditional.inner",
["av"] = "@field.outer",
["iv"] = "@field.inner",
["ab"] = "@block.outer",
["ib"] = "@block.inner",
["is"] = "@statement.inner",
["as"] = "@statement.outer",
["aq"] = "@call.outer",
["iq"] = "@call.inner",
["i/"] = "@comment.inner",
["a/"] = "@comment.outer",
["aA"] = "@assignment.outer",
["iA"] = "@assignment.inner",
["iH"] = "@assignment.lhs",
["iL"] = "@assignment.rhs",
["im"] = "@module.inner",
["am"] = "@module.outer",
["id"] = "@macro.inner",
["ad"] = "@macro.outer",
}
}
Optionally, you can add move motions (look at gist for full code):
-- Inside of your treesitter.lua
move = {
enable = true,
set_jumps = true,
goto_next_start = {
["]f"] = "@function.outer",
["]c"] = "@class.outer",
["]a"] = "@parameter.outer",
["]l"] = "@loop.outer",
["]i"] = "@conditional.outer",
["]v"] = "@field.outer",
["]b"] = "@block.outer",
... -- and so on
Overriding Verilog Textobjects
Create file <nvim-config>/queries/verilog/textobjects.scm with this code.
That’s it! Now you can use treesitter-textobjects in Verilog files.
- However, please note that each node defined affects overall performance, so make sure
to delete all the S-expressions that you don’t need. For example:
DELETE STARTING HERE ;; Macros HERE'S A BLOCK OF S-EXPRESSIONS ;; ====== ; DEFINE ; ------ ; Outer (text_macro_definition) @macro.outer HERE IS AN S-EXPRESSION ; Inner (text_macro_definition . (text_macro_name) . (macro_text) @macro.inner) DELETE ENDING HERE