Bundle of more than a dozen new text objects for Neovim.
textobj | description | inner / outer | forward-seeking | default keymaps | filetypes (for default keymaps) |
---|---|---|---|---|---|
indentation | lines with same amount of indentation | see overview from vim-indent-object | no | ii , ai , (aI , iI ) |
all |
value | value of key-value pair, or right side of a variable assignment (inside one line) | outer includes trailing commas or semicolons | yes | iv , av |
all |
key | key of key-value pair, or left side of a variable assignment | outer includes the = or : |
yes | ik , ak |
all |
number | numbers, similar to <C-a> |
inner: only pure digits, outer: number including minus sign and decimal point | yes | in , an |
all |
diagnostic | LSP diagnostic (requires built-in LSP) | - | yes | ! |
all |
nearEoL | from cursor position to end of line, minus one character | - | no | n |
all |
mdlink | markdown link like [title](url) |
inner is only the link title (between the [] ) |
yes | il , al |
markdown, toml |
mdFencedCodeBlock | markdown fenced code (enclosed by three backticks) | outer includes the enclosing backticks | yes | iC , aC |
markdown |
cssSelector | class in CSS, like .my-class |
outer includes trailing comma and space | yes | ic , ac |
css, scss |
jsRegex | JavaScript regex pattern | outer includes the slashes and any flags | yes | i/ , a/ |
javascript, typescript |
doubleSquareBrackets | text enclosed by [[]] |
outer includes the four square brackets | yes | iD , aD |
lua, shell, neorg, markdown |
column | column down until indent or shorter line. Accepts {count} for multiple columns. |
- | no | | (pipe char) |
all |
restOfParagraph | like } , but linewise |
- | no | r |
all |
subword | like iw , but treating - , _ or . as word delimiters and only part of camelcase. |
outer includes trailing _ or - |
yes | iS , aS |
all |
entireBuffer | entire buffer as one text object | - | - | gG |
all |
url | link beginning with "http" | - | yes | L |
all |
shellPipe | command stdout is piped to | outer includes the front pipe character | yes | iP /aP |
bash, zsh, fish, sh |
-- packer
use {
"chrisgrieser/nvim-various-textobjs",
config = function ()
require("various-textobjs").setup({ useDefaultKeymaps = true })
end,
}
-- lazy.nvim
{
"chrisgrieser/nvim-various-textobjs",
config = function ()
require("various-textobjs").setup({ useDefaultKeymaps = true })
end,
},
The .setup()
call is optional if you are fine with the defaults below. (Note that the default is to not set any keymaps by this plugin.)
-- default config
require("various-textobjs").setup {
lookForwardLines = 5, -- Set to 0 to only look in the current line.
useDefaultKeymaps = false, -- Use suggested keymaps (see README).
}
If you want to set your own keybindings yourself, you can do so by calling the respective function:
true
always meaning "inner," and false
meaning "outer."-- example: `?` for diagnostic textobj
vim.keymap.set({"o", "x"}, "?", function () require("various-textobjs").diagnostic() end)
-- example: `an` for outer number, `in` for inner number
vim.keymap.set({"o", "x"}, "an", function () require("various-textobjs").number(false) end)
vim.keymap.set({"o", "x"}, "in", function () require("various-textobjs").number(true) end)
-- exception: indentation textobj requires two parameters, first for exclusion of the
-- starting border, second for the exclusion of ending border
vim.keymap.set({"o", "x"}, "ii", function () require("various-textobjs").indentation(true, true) end)
vim.keymap.set({"o", "x"}, "ai", function () require("various-textobjs").indentation(false, true) end)
You can also use the text objects as input for small snippets by yanking them and using getreg()
. The following example uses the outer regex text object to retrieve pattern, flags, and replacement value of the next regex, and opens regex101 prefilled with them:
vim.keymap.set("n", "gR", function()
require("various-textobjs").jsRegex(false) -- set visual selection to outer regex
vim.cmd.normal { '"zy', bang = true } -- retrieve regex with "z as intermediary
local regex = vim.fn.getreg("z")
local pattern = regex:match("/(.*)/")
local flags = regex:match("/.*/(.*)")
local replacement = fn.getline("."):match('replace ?%(/.*/.*, ?"(.-)"')
-- https://github.com/firasdib/Regex101/wiki/FAQ#how-to-prefill-the-fields-on-the-interface-via-url
local url = "https://regex101.com/?regex=" .. pattern .. "&flags=" .. flags
if replacement then url = url .. "&subst=" .. replacement end
local opener
if vim.fn.has("macunix") then
opener = "open"
elseif vim.fn.has("unix") then
opener = "xdg-open"
elseif vim.fn.has("win64") or fn.has("win32") then
opener = "start"
end
os.execute(opener .. "'" .. url .. "'")
end, { desc = "Open next js regex in regex101" })
gx
Using the URL textobj, you can also write a small snippet for a smarter gx
. The code below retrieves the next URL (within the amount of lines configured in the setup
call), and opens it in your browser. While this is already an improvement to vim's built-in gx
, which requires the cursor to be standing on a URL to work, you can even go one step further. If no URL has been found within the next few lines, the :UrlView
command from urlview.nvim is triggered, searching the entire buffer for URLs from which you can choose which to open.
vim.keymap.set("n", "gx", function ()
require("various-textobjs").url() -- select URL
local foundURL = fn.mode():find("v") -- only switches to visual mode if found
local url
if foundURL then
vim.cmd.normal { '"zy', bang = true } -- retrieve URL with "z as intermediary
url = fn.getreg("z")
local opener
if vim.fn.has("macunix") then
opener = "open"
elseif vim.fn.has("unix") then
opener = "xdg-open"
elseif vim.fn.has("win64") or fn.has("win32") then
opener = "start"
end
os.execute(opener .. "'" .. url .. "'")
else
-- if not found in proximity, search whole buffer via urlview.nvim instead
cmd.UrlView("buffer")
end
end, {desc = "Smart URL Opener"})
Thanks
Kudos to the Valuable Dev for their blog post on how to get started with creating custom text objects.
About Me
In my day job, I am a sociologist studying the social mechanisms underlying the digital economy. For my PhD project, I investigate the governance of the app economy and how software ecosystems manage the tension between innovation and compatibility. If you are interested in this subject, feel free to get in touch.
Profiles