[mini.ai] The perfect mini-word textobject #1434
Replies: 5 comments 8 replies
-
|
I'd be interested to hear if anyone thinks the regex can be simplified. It took me quite a while to understand what it was doing (especially the %f pattern) but I could very likely be missing some opportunity for further improvement. |
Beta Was this translation helpful? Give feedback.
-
|
Nice job of "translating" Lua patterns into regular English :) As with any regex-like systems, first time encountering them can be daunting. It gets easier with time. The example was mostly added as a demo of capabilities for what looks like a popular textobject. I personally don't like using it as it feels "too magical". Regular Vim/Neovim operators textobjects together with |
Beta Was this translation helpful? Give feedback.
-
|
Thank you for this. I've been playing around with this and it does work great. One thing I cannot get to work: select multiple subwords. For example, given this: How would I make a selection: Here's what works for a single inner word for me: All of these don't work |
Beta Was this translation helpful? Give feedback.
-
WhyAdded support for around Tip I prefer left-handed underscores over right-handed ones. It can be useful when using subword motion plugins like chrisgrieser/nvim-spider. Use Around first (preferred)Note End subwords with numbers. New!custom_textobjects = {
e = function(ai_type, id, opts)
if ai_type == "a" then
return {
{
-- pattern, [^_]pattern_*
"%f[%a_%-]%l+%d*[_%-]*",
"%f[%w_%-]%d+[_%-]*",
"%f[%u_%-]%u%f[%A]%d*[_%-]*",
"%f[%u_%-]%u%l+%d*[_%-]*",
"%f[%u_%-]%u%u+%d*[_%-]*",
-- __pattern
"%f[_%-][_%-]+%l+%d*",
"%f[_%-][_%-]+%d+",
"%f[_%-][_%-]+%u%f[%A]%d*",
"%f[_%-][_%-]+%u%l+%d*",
"%f[_%-][_%-]+%u%u+%d*",
-- __pattern|__
"[_%-]()()%l+%d*[_%-]+()()",
"[_%-]()()%d+[_%-]+()()",
"[_%-]()()%u%f[%A]%d*[_%-]+()()",
"[_%-]()()%u%l+%d*[_%-]+()()",
"[_%-]()()%u%u+%d*[_%-]+()()",
},
}
end
if ai_type == "i" then
local reg = MiniAi.find_textobject("a", id, opts)
if reg then
local line = vim.fn.getline(reg.from.line)
local _, s = line:find("^[_%-]*.", reg.from.col)
local e = line:sub(1, reg.to.col):find(".[_%-]*$")
return vim.tbl_deep_extend("force", reg, { from = { col = s }, to = { col = e } })
end
end
end,
},HybridWarning Known issue: the separator isn’t recognized as part of a subword, so the cursor should not be placed on the separator in certain edge cases. Separate keymaplocal MiniAi = require 'mini.ai'
MiniAi.setup {
custom_textobjects = {
-- NOTE: Use any subword pattern you like, just ignore separators
e = {
{
"%f[%a]%l+%d*",
"%f[%w]%d+",
"%f[%u]%u%f[%A]%d*",
"%f[%u]%u%l+%d*",
"%f[%u]%u%u+%d*",
},
},
},
}
local around_subword = function(reg)
reg = vim.deepcopy(reg)
local SEP = "[_%-]+"
local line = vim.fn.getline(reg.from.line)
local left = line:sub(1, reg.from.col - 1):find(SEP .. "$")
local _, right = line:find("^" .. SEP, reg.to.col + 1)
if left then
reg.from.col = left
elseif right then
reg.to.col = right
end
return reg
end
vim.keymap.set({ "x", "o" }, "ae", function()
local reg = MiniAi.find_textobject("i", "e")
if reg then
MiniAi.config.custom_textobjects["Virt"] = around_subword(reg)
MiniAi.select_textobject("i", "Virt")
end
end)Pattern onlyCaution Pattern only approach has collisions, see #1434 (comment). Hide (bad)local ai = require 'mini.ai'
ai.setup {
custom_textobjects = {
e = {
{
-- __-1, __-U, __-l, __-1_, __-U_, __-l_
'[^_%-]()[_%-]+()%w()()[%s%p]',
'^()[_%-]+()%w()()[%s%p]',
-- __-123SNAKE
'[^_%-]()[_%-]+()%d+%u[%u%d]+()()',
'^()[_%-]+()%d+%u[%u%d]+()()',
-- __-123snake
'[^_%-]()[_%-]+()%d+%l[%l%d]+()()',
'^()[_%-]+()%d+%l[%l%d]+()()',
-- __-SNAKE, __-SNAKE123
'[^_%-]()[_%-]+()%u[%u%d]+()()',
'^()[_%-]+()%u[%u%d]+()()',
-- __-snake, __-Snake, __-snake123, __-Snake123
'[^_%-]()[_%-]+()%a[%l%d]+()()',
'^()[_%-]+()%a[%l%d]+()()',
-- UPPER, UPPER123, UPPER-__, UPPER123-__
-- No support: 123UPPER
'[^_%-%u]()()%u[%u%d]+()[_%-]*()',
'^()()%u[%u%d]+()[_%-]*()',
-- UPlower, UPlower123, UPlower-__, UPlower123-__
'%u%u()()[%l%d]+()[_%-]*()',
-- lower, lower123, lower-__, lower123-__
'[^_%-%w]()()[%l%d]+()[_%-]*()',
'^()()[%l%d]+()[_%-]*()',
-- Camel, Camel123, Camel-__, Camel123-__
'[^_%-%u]()()%u[%l%d]+()[_%-]*()',
'^()()%u[%l%d]+()[_%-]*()',
},
},
},
} |
Beta Was this translation helpful? Give feedback.
-
|
There’s only one small problem with defining patterns as in the provided example: the first matching pattern always wins, not necessarily the most relevant one. For example, my pattern groups collide on the string Example pattern: e = {
{
-- lower123
'[^%u%l]()%l%l+%d*()[^%l%d]',
-- UPPER123
'[^%u]()%u%u+%d*()%u%l',
'[^%u]()%u%u+%d*()[^%d]',
-- Camel123NAME4
'()%u%l+%d*()[^%d]',
-- U, l, 1
'[^%u]()%u()[^%u%l]',
'[^%l]()%l()[^%l]',
'[^%d]()%d()[^%d]',
-- 123|UPPER, 123|lower, 123|Camel
'[%s%p]()%d+()[^%d]',
},
}, |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
In the mini.ai help file, there's this custom textobject example:
I absolutely love this textobject and use it to rename variables/functions all the time. However, it has problems:
So, here's my updated version that fixes these issues with sufficient comments. It supports, as far as I can tell, all text cases and capitalizations:
Trust me, you will not regret this. This is one of the biggest quality-of-life improvements I've had with vim editing yet. Take a look:
perfect-textobj.mp4
Beta Was this translation helpful? Give feedback.
All reactions