Skip to content

Type surroundings #5

@cathyprime

Description

@cathyprime

The code snippets will require replacing the mini.surround plugin:

dotconf/nvim/init.lua

Lines 876 to 891 in 829ea31

require("mini.surround").setup({
mappings = {
add = "ys",
delete = "ds",
find = "",
find_left = "",
highlight = "",
replace = "cs",
update_n_lines = "",
-- Add this only if you don't want to use extended mappings
suffix_last = "",
suffix_next = "",
},
search_method = "cover_or_next",
})
with nvim-surround might not be actually needed here, since it might be doable with mini.surround, but that is an exercise for the reader

The idea is kind of complicated, so there will be more code snippets, but I'll try to provide explanations for how I did it.
nvim-surround has support for treesitter, which we will make heavy use of. First component would be getting familiar with nvim-surround's configuration :h nvim-surround.config.surrounds, there we can see how we can add/modify new surrounding types, this is main thing required to understand what is happening, I heavily encourage the reader to pause now and read that section now.

Another thing to read would be the source code of nvim-surround and how the function call surround is done, important part being is that we can call on treesitter to get a selection of text. Now it would be useful to learn at least basic stuff about the treesitter. Armed with this knowledge we can begin.

so the plan is now:

  • write a query that will select the type
  • create the surround functions that will work on those selections

from the neovim's docs:

Nvim looks for queries as *.scm files in a queries directory under
runtimepath, where each file contains queries for a specific language and
purpose, e.g., queries/lua/highlights.scm for highlighting Lua files.
By default, the first query on runtimepath is used (which usually implies
that user config takes precedence over plugins, which take precedence over
queries bundled with Nvim). If a query should extend other queries instead
of replacing them, use treesitter-query-modeline-extends.

this means if we want to create a textobject 'type' for the rust language, we need to create queries/rust/textobjects.scm and this is what we put inside it:

;; extends

(generic_type
  type: (_)) @type.outer

(generic_type
 type_arguments: (type_arguments
     (_) @_start
     (_)? @_end
     (#make-range! "type.inner" @_start @_end)))

in lisp languages comments are made with ; but I've found that people usually use ;;, both work
extends means that we want to extend the default rust textobjects, if we ever install a plugin that adds rust specific textobjects this will prevent that plugin from straight up breaking

this query searches for nodes generic_type that have any type, and then we mark the generic_type nodes as @type.outer, while to get the @type.inner we have a bit more complex query, which basically gets every arguments separately and joins them together as a single capture, this allows us to precisely select only the arguments without the '<' and '>' (edit: query changed, which now allows the used of MiniSurround instead!)

I would also recommend setting this up for cpp and other languages having similar constructs

now we can move to the main code, the code should be readable by itself, but some additional notes are needed:

  • the lua patterns are just copied from the source code for function calls, but replace the escaped parenthesis with angle brackets
  • those mappings won't fall through if something is not defined, so if you want to change or delete you gotta add those too
    basically that's it :3

edit:
this was the original query (this is worse):

(generic_type
  type_arguments: (_) @type.inner) @type.outer

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions