Learn to use text-object in Vim&Neovim
Intro
You probably do not know what the text-object is in Vim/Neovim. However, you may use it in your daily life. For instance, When you are writing code, you may want to change the arguments of a function. Take the following code as an example, let’s say you want to change the function call to bar(3, 2, 1)
, and the cursor currently stays on the ,
def foo():
...
res = bar(1, 2, 3)
# ^
# The position of your cursor(on `,`)
...
How will you achieve this goal? If you are familiar with Vim/Neovim, you will likely instinctively type ci)
or ci(
, and then you can quickly type in the new arguments. The i(
or i)
here are so-called text-object.
F(
to jump to previous (
and then use x
to delete char one by oneBuilt-in text-object
:h text-objects
for more informationSimply put, text-object is a textual entity corresponding to a certain area of text, and this area of text contains structure within it. Therefore, they can be treated as a single text-object, which brings convenience to many operations.
The built-in text-object is represented as two-letter combinations:
- The first letter is either
i
ora
i
refers to text-object itself, anda
also contains the next/previous whitespace or paired symbols(e.g.{}, (), ...
)- Mnemonic:
i = (i)nside
,a = (a)round
- The second letter has two possibilities
- One of the paired symbols
- Paired symbols:
{}, (), [], '', "", <tag></tag>
. Note that<tag></tag>
is represented ast
. For example, if you want to delete thefoobar
in<tag>foobar</tag>
, you can put the cursor within the tag and typedit
- e.g.
a{
=(around) {
, it contains all the text within{}
(inclusive)
- Paired symbols:
- (w)ord, (s)entences, (p)aragraphs
- Each one of them can be used with
i
ora
-iw, aw, is, as, ip, ap
- Each one of them can be used with
- One of the paired symbols
Syntax-aware text-object
Previously, we mentioned that the text-object is structured in some way. It reminds me of the code, which is highly structured text.
Occasionally, we want to delete the entire function body and rewrite it. If the function body is enclosed in {}
, then typically we can use ci{
to accomplish this task. However, care must be taken regarding the cursor’s position to ensure the {}
of the function body is closet to the cursor, which can be somewhat annoying.
The function body in Rust is enclosed in {}
. To delete the entire function body and rewrite it, we can place the cursor on the i
in if
, and then type ci{
fn fib(n: i32) -> i32 {
// The position of your cursor(on `i`)
// v
if n == 1 || n == 2 {
1
} else {
fib(n - 1) + fib(n-2)
}
}
But what if the function body is not enclosed in {}
? For example, the Python code uses indentation to recognize the function body.
def fib(n: int):
if n == 1 or n == 2:
return 1
return fib(n - 1) + fib(n - 2)
Is there a special text-object that corresponds to the function body considering that the code is highly structured? That is what the magic plugin - nvim-treesitter-textobjects does for us. By analyzing the syntax of code, it defines various text-object such as functions, classes, loops, etc.
Check my commit to see the changes and how I organize my configuration files.
Your file structure may not be the same, but you get the idea :)
If you also use lazy.nvim, put the followinglines in your plugin list.
require("lazy").setup({
...
{
"nvim-treesitter/nvim-treesitter-textobjects",
dependencies = "nvim-treesitter/nvim-treesitter",
config = function()
require("config.nvim-treesitter-textobjects")
end,
},
...
})
Create a new file nvim-treesitter-textobjects.lua
and edit(Most of the code has been omitted by me, the complete file is located here):
local is_ok, configs = pcall(require, "nvim-treesitter.configs")
if not is_ok then
return
end
configs.setup({
textobjects = {
select = {
...
keymaps = {
-- outer: outer part
-- inner: inner part
["af"] = "@function.outer",
["if"] = "@function.inner",
["ac"] = "@class.outer",
["ic"] = "@class.inner",
["al"] = "@loop.outer",
["il"] = "@loop.inner",
},
include_surrounding_whitespace = true,
},
},
...
})
Based on my personal needs, I add 6 text-object(af, if, ac, ic, al, il
) to operate on function, class, and loop. You can find syntax-aware text-object in the official README.md file.
Now we can use dif
to delete the entire function body in any position within the function body :)
Wrap-up
The text-object organizes the text into objects, allowing us to operate them conveniently. With the help of nvim-treesitter-textobjects, we can operate on various programming language constructs🍺