Closed barrett-ruth closed 1 year ago
Hi there!
All custom textobjects are defined inside config.custom_textobjects
table. Here are some examples. So no need to call textobj#user#plugin
, only define inside require('mini.ai').setup()
call.
Here is a help section for full specification capabilities.
Here are some examples of user setups.
I once quickly wrote a version of textobject for whole buffer in this Reddit comment.
Hope this helps.
I've been planning to do a wiki page with various setups of custom textobjects for quite a while now. Maybe in the near future. I'll then take a look at how vim-textobj-xmlattr
can be replaced.
Ok, most of that was what I expected, thanks for the clarification. By the way the documentation actually has an implementation of the "global" text object shorter than the one in the reddit comment here.
So, my last question is let's say I want to transcribe this plugin to mini.ai. It looks like I should essentially copy all of the logic into a custom textobject and return the from
and two
properties. However the above example has differing logic for the "around" and "inside" verbs on the text object. How can I communicate these things to mini.ai in order to maintain their functionality?
Your two paragraphs are interestingly interconnected :)
Yes, there is a whole buffer textobject implementation in documentation, but it is less flexible than the one from Reddit post. The difference being that the former doesn't differ between a
and i
textobjects, while the latter does. The way it is done is by utilizing its input arguments: all callable custom textobject specification are called with arguments same as MiniAi.find_textobject()
. There is a note about it in specs docs but I realized it is not reflected in examples.
So the shorter answer is: use first ai_type
argument of callable specification to condition the returned region(s).
Sorry for the prod @echasnovski ? At this point I have constructed two regexes to simulate one vim-textobj plugin I use: one for "a," and one for "i." Is there an easy way to translate that into mini/ai?
Sorry for the prod @echasnovski ? At this point I have constructed two regexes to simulate one vim-textobj plugin I use: one for "a," and one for "i." Is there an easy way to translate that into mini/ai?
Depends on regexes, unfortunately.
What are those textobjects?
The regexex here, which correlate to:
a: \s\+\(\([a-zA-Z0-9\-_:@.]\+\)\(=\(\(".\{-}"\)\|\(\w\+\)\)\)\=\)
i: \(\([a-zA-Z0-9\-_:@.]\+\)\(=\(\(".\{-}"\)\|\(\w\+\)\)\)\=\)
The regexex here, which correlate to:
a:
\s\+\(\([a-zA-Z0-9\-_:@.]\+\)\(=\(\(".\{-}"\)\|\(\w\+\)\)\)\=\)
i:\(\([a-zA-Z0-9\-_:@.]\+\)\(=\(\(".\{-}"\)\|\(\w\+\)\)\)\=\)
That is a scary looking regex...
Do you actually want to implement xml attributes, i.e. match anything similar inside to these entries: <div class="box" id=footer data-updatable data-content="everything"></div>
?
This can be implemented with composed Lua pattern (as described in textobject specification. Its complexity depends on how precise you want it to be.
I'd settle for simpler patterns with something like this:
require('mini.ai').setup({
custom_textobjects = {
x = { '%s+[^%s<>=]+=[^%s<>]+', '^%s+().*()$' },
}
})
Little breakdown of what it does:
=
+ group of allowed characters. Allowed characters here are anything other than whitespace, "<", or ">". This will match most of examples, but won't match "data-updatable" (I'd suggest using built-in aW
and iW
for these cases).a
textobject, trim initial whitespace to have i
textobject.Thank you so much, my poor understanding of lua patterns is what is holding me back (although not an excuse!).
I'll look back at the documentation and try to make out a solution for this and variable-segment in the future. My two last pesky vim plugins!
Thank you so much, my poor understanding of lua patterns is what is holding me back (although not an excuse!).
I'll look back at the documentation and try to make out a solution for this and variable-segment in the future. My two last pesky vim plugins!
Lua pattern are initially design to be not as complicated as regular regexes. If you at least somewhat familiar with regexes, it shouldn't be much of a problem. I mostly learned them from here while experimenting using built-in string
module. One important detail, though, is that LuaJit in Neovim also contains a so called "frontier pattern" - very useful to ensure that match is as wide as necessary.
Here is a MVP for 'variable-segment'. It doesn't really work in edge cases, so you can tweak it further. And speaking of frontier pattern, it also has is a slight update for xml attributes which includes maximum whitespace to the left of it:
require('mini.ai').setup({
custom_textobjects = {
x = { '%f[%s]%s+[^%s<>=]+=[^%s<>]+', '^%s+().*()$' },
v = { { '%f[%w]()%w+()_', '%f[%u]()()%w*[%l%d]()()%u' } },
}
})
Thanks so much!
Hi. Just wanted to ask if there is some kind of documentation/collection of mini.ai textobjects as a replacement for vim-textobj-user and nvim-various-textobjs? I would like to migrate to mini.ai from targets.vim and this two textobj-libraries but having first to reimplement every textobject seems kind of daunting.
Hi. Just wanted to ask if there is some kind of documentation/collection of mini.ai textobjects as a replacement for vim-textobj-user and nvim-various-textobjs? I would like to migrate to mini.ai from targets.vim and this two textobj-libraries but having first to reimplement every textobject seems kind of daunting.
Hello :wave:
Since this issue was created, there is a 'mini.extra' module which provides (among others) some 'mini.ai' specifications.
Also 'mini.ai' has most of 'targets.vim' textobjects working out of the box.
Other then that, you'd have to write own specifications, I am afraid. What textobjects exactly do you miss not covered with what is described above?
Thanks for letting me know about the textobjects in mini.extra. I didn't find any reference in the mini.ai-readme or docs. Maybe it should be mentioned somewhere for new users. indent-textobject is one of the most important for me. Some other objects are:
Of course there can be many more as can be seen in nvim-various-textobjs and vim-textobj-user. Maybe except of word-column the objects aren't that difficult to implement by oneself but I think it would be nice to have the most common textobjects-implementatons either be mentioned in a wiki or imo even better be shipped with a separate module like mini.textobjects. Together mini.ai and mini.textobjects could replace both targets.vim and other textobject-plugins like nvim-various-textobjs and vim-textobj-user and help new users to make the shift to exclusively using mini.ai.
What I find so appealing about mini.ai is that it unifies targets.vim and other textobject-plugins in a coherent way and makes it possible to use in/an, il/al, g] and g[ on all the textobjects defined by mini.ai which is not possible with 3rd-party-textobject-plugins. Moving from targets.vim to mini.ai is easy. From textobject-plugins not that much depending on how many textobjects the user is depending on.
PS: Thanks for the great plugins you are delivering. Although they are supposed to be 'mini' many of them seem to be equal or better than the old established 'big' plugins.
I didn't find any reference in the mini.ai-readme or docs. Maybe it should be mentioned somewhere for new users.
Indeed, thanks for noticing. There is a mention now.
- word-column
Yeah, might be tricky to implement in full. As it seems to have an extra logic for what to consider a "column".
- variable-segment
Should be possible, but frankly I don't really like the idea. Being more explicit during editing tends to be more robust (at least for me).
- key-value
- url
Indeed, both seem to be possible.
... or imo even better be shipped with a separate module like mini.textobjects.
I don't think this will happen. If some textobject is a combination of being non-trivial, popular, and having concise but not trivial implementation, it might be a candidate for MiniExtra.gen_ai_spec
entry. This is a case by case decision, which I went through based on my editing experience to land on its current entries.
I am open to suggestions. Yet, none of those four seem to fit in those criteria. I don't see concise implementation for first three and "url" seems to be rather trivial in terms of textobjects (as in "it usually can be replicated with other textobject like iW
, i)
, etc.).
Yeah, might be tricky to implement in full. As it seems to have an extra logic for what to consider a "column".
Do you mean it wouldn't be possible to implement word-column even if one would try because of mini.ai limitations?
I don't see concise implementation for first three
Regarding variable-segment/subword why do you think it couldn't be concise if it would simply deal with camelCase. For underscores and other separators in most cases ci_
or da_
should be enough except for the outer subwords. Assuming we want to delete a subword with *
being the cursor here are the cases:
cam*elCaseVar
-> caseVar
Cam*elCaseVar
-> CaseVar
camelCa*seVar
-> camelVar
camelCaseV*ar
-> camelCase
I think just covering the camelCase scenario should be concise, popular and not to trivial.
For the non-camelcase scenarios an outer-operator
combined with ci_
and da_
would make more sense. Like do_
would be:
und*er_score_var
-> score_var
under_sc*ore_var
-> under_score
(because do_
would search for the next outer _)under_score_v*ar
-> under_score
dol_
and under_score_v*ar
-> score_var
What do you think about a last-paste-object? Mapped to g;
by nvim-various-textobjs. My main-usecase is >g;
to indent the last pasted code.
I found some weird behavior with mini.extra indent-object. In the following case vai
and vii
both only select from line 2 to 5. Shouldn't vai
also include 1 and 6?
1 line
2 *line
3 line
4 line
5 line
6 line
In the next case vai
and vii
both also select lines 2 to 5. Shouldn't vii
select 3 and 4?
1 line
2 line
3 *line
4 line
5 line
6 line
Yeah, might be tricky to implement in full. As it seems to have an extra logic for what to consider a "column".
Do you mean it wouldn't be possible to implement word-column even if one would try because of mini.ai limitations?
I mean that it would most certainly require a callable textobject specification with some ad hoc logic about what should be considered a "column". Definitely out of scope for 'mini.extra'.
Regarding variable-segment/subword why do you think it couldn't be concise if it would simply deal with camelCase.
There is already an example for camel case words. It looks scary, but it works for all four examples you listed.
For the non-camelcase scenarios an
outer-operator
combined withci_
andda_
would make more sense. Likedo_
would be:
The 'mini.ai' is about a
(around) and i
(inside) textobjects. Adding new one is out of scope. That said, tweaking _
textobject to also accept space as edge should be doable via nested patterns.
What do you think about a last-paste-object? Mapped to
g;
by nvim-various-textobjs. My main-usecase is>g;
to indent the last pasted code.
Judging by its source code, it is straight up area between [
and ]
marks. Making it work in most cases is fairly straightforward with the following:
local select_last_change = function()
local mode = vim.fn.mode()
local is_visual = mode == 'v' or mode == 'V' or mode == '\22'
vim.api.nvim_win_set_cursor(0, vim.api.nvim_buf_get_mark(0, '['))
vim.cmd('normal! ' .. (is_visual and 'o' or 'v'))
vim.api.nvim_win_set_cursor(0, vim.api.nvim_buf_get_mark(0, ']'))
end
vim.keymap.set({ 'x', 'o' }, 'g;', select_last_change)
I found some weird behavior with mini.extra indent-object. In the following case
vai
andvii
both only select from line 2 to 5. Shouldn'tvai
also include 1 and 6?
At first glance, yes it should be from 1 to 6. I'll take a look later.
I have also recently migrated from vim-textobj-user
. Can you provide any example that could tackle the following please? @echasnovski
| The 'mini.ai' is about a (around) and i (inside) textobjects. Adding new one is out of scope. That said, tweaking _ textobject to also accept space as edge should be doable via nested patterns.
I'm struggling to achieve the following
da_
or a user-defined 'daX` pattern to
und*er_score_var
-> *score_var
under_sc*ore_var
-> under_*var
under_score_v*ar
-> under_score*
di_
or a user-defined diX
pattern to
und*er_score_var
-> *_score_var
under_sc*ore_var
-> under_*_var
under_score_v*ar
-> under_score_*
Many thanks in advance!
I'm struggling to achieve the following
Here is an example:
-- A composed pattern which will select either inside `_` and/or ` `
local underscore_with_space = {
-- Wrapping in curly brackets means that "unwrapping" composed pattern will
-- result in those particular nested patterns.
{
-- Cases like ` a*aa_bbb_ccc` and `aaa_b*bb_ccc`
{ '[ _][^ _]+_', '^.()().*().()$' },
-- Cases like `a*aa_bbb_ccc` at start of line
{ '^[^ _]+_', '^()().*().()$' },
-- Cases like `aaa_bbb_c*cc `
{ '_[^ _]+ ', '^().().*()().$' },
-- Cases like `aaa_bbb_c*cc` at the end of line
{ '_[^ _]+\n', '^().().*()().$' },
},
}
require('mini.ai').setup({
custom_textobjects = { ['-'] = underscore_with_space },
})
This might look scary, but hopefully comments make it manageable. I'd suggest reading this with attention to examples (this is how I right now remembered how to do this :sweat_smile: ).
I'm struggling to achieve the following
Here is an example:
-- A composed pattern which will select either inside `_` and/or ` ` local underscore_with_space = { -- Wrapping in curly brackets means that "unwrapping" composed pattern will -- result in those particular nested patterns. { -- Cases like ` a*aa_bbb_ccc` and `aaa_b*bb_ccc` { '[ _][^ _]+_', '^.()().*().()$' }, -- Cases like `a*aa_bbb_ccc` at start of line { '^[^ _]+_', '^()().*().()$' }, -- Cases like `aaa_bbb_c*cc ` { '_[^ _]+ ', '^().().*()().$' }, -- Cases like `aaa_bbb_c*cc` at the end of line { '_[^ _]+\n', '^().().*()().$' }, }, } require('mini.ai').setup({ custom_textobjects = { ['-'] = underscore_with_space }, })
This might look scary, but hopefully comments make it manageable. I'd suggest reading this with attention to examples (this is how I right now remembered how to do this 😅 ).
Thanks so much on the above examples. Those are working perfectly 99% of the time!
What I realized is we also need to handle cases like "
, '
, (
among others on both sides (when the cursor is on one of those signs), any good way to do it in a more "dry" way other than chaining those signs together in the pattern?
. Really appreciate your help as always!
What I realized is we also need to handle cases like
"
,'
,(
among others on both sides (when the cursor is on one of those signs), any good way to do it in a more "dry" way other than chaining those signs together in the pattern?
Maybe an automated approach which creates a necessary specification table in a loop. Or use [ %p]
instead of
(single space) as left and right delimiter.
I'd leave it you to actually iron out from 99% to 100%.
FWIW, below is what I settled with
v = {
{
-- a*aa_bbb
{ "[^%w]()()[%w]+()_()" },
-- a*aa_bb
{ "^()()[%w]+()_()" },
-- aaa_bbb*_ccc
{ "_()()[%w]+()_()" },
-- bbb_cc*cc
{ "()_()[%w]+()()%f[%W]" },
-- bbb_cc*c at the end of the line
{ "()_()[%w]+()()$" },
},
},
I can't get my head's around when setting up ()
separately :)
I can't get my head's around when setting up
()
separately :)
Here is a description of what it does. It is basically a way to define how a
and i
textobjects will be extracted from the match.
I can't get my head's around when setting up
()
separately :)Here is a description of what it does. It is basically a way to define how
a
andi
textobjects will be extracted from the match.
yes I read it so I came up with { "_()()[%w]+()_()" }
etc...but I haven't figured out how to transform it into { '_[^ _]+ ', '^().().*()().$' },
format.
Separately, as to the next step, I'm currently struggling with combining https://github.com/echasnovski/mini.nvim/issues/151#issuecomment-2336970229 with CamelCases. I was using this before starting using mini.ai
and it is neat to handle both CamelCase and under_score_case in one TextObj.
Any help / suggestion will be well appreciated!
... but I haven't figured out how to transform it into
{ '_[^ _]+ ', '^().().*()().$' },
format.
If it works like that, then I suggest to use it like that. No need to follow a specific format. The nested pattern format was mostly a result of my previous attempts to combine several patterns at once, until it was apparent that handling them separately looked like the best choice.
Separately, as to the next step, I'm currently struggling with combining #151 (comment) with CamelCases. I was using this before starting using
mini.ai
and it is neat to handle both CamelCase and under_score_case in one TextObj.Any help / suggestion will be well appreciated!
I'd start by adding new patterns to what you already got. If this doesn't work, textobject specification can be callable/function (either fully or instead of a single pattern) to provide vastly more flexibility (at the cost of more verbosity). In the end, it possible that 'mini.ai' nested pattern approach is not built for this kind of complex/flexible textobject specification.
Personally, it seems to be a better idea to have separate textobjects for snake and camel cases, though.
Contributing guidelines
Module(s)
mini.ai
Description
Currently trying to convert my setup to the mini ecosystem. I have several vim-textobj-user based plugins, such as this one or this.
These plugins straightforwardly define their text objet regexes in one file, but I just don't know how to convert the call to
textobj#user#plugin
to mini.ai.Any help would be appreciated.