targets.vim icon indicating copy to clipboard operation
targets.vim copied to clipboard

Additional text objects?

Open gaving opened this issue 10 years ago • 30 comments

Just generating discussion but I'm a fan of the following additional text objects:-

  • [x] https://github.com/gaving/vim-textobj-argument
  • [ ] https://github.com/kana/vim-textobj-line
  • [ ] https://github.com/kana/vim-textobj-indent

I'm thinking the argument one is superseded by targets.vim's separator objects, but any chance of either line or indent making their way in?

gaving avatar Feb 11 '14 17:02 gaving

Thank you for your input! Let me tackle them in order:

  • https://github.com/gaving/vim-textobj-argument

To be fair, separator text objects can't target the first and last arguments because they are surrounded by different delimiters. I had many thoughts about some special argument text objects because it would be really useful in combination with next and last text objects. An easy way might be to introduce separator classes so that different characters could be used as separators for one text objects. For example dana could delete the next element in a list separated by either of these characters: ,(). But this would not handle nested function calls properly. Doing it right would be much more complicated.

Also: Should the list of separators be customizable or just a fixed set that we can agree on?

  • https://github.com/kana/vim-textobj-line

I thought about this too. Looking at the default bindings, al and il would conflict with my last text objects like al,. We could either change the l for last text objects to N as suggested here, or find a different mapping for line objects. Operations like d2anl certainly seem appealing.

  • https://github.com/kana/vim-textobj-indent

I'm a happy user of this text object plugin myself. Thinking of it, it might be interesting to target next indents. So dani could delete the block below the cursor that has a different (or only deeper?) indentation that the cursor line. A count on the current indent could expand to less indented areas.


In general I would say I'm not striving to combine all text objects into targets.vim. All of them can coexist independently and everybody can choose what might fit their needs. So instead of including and expanding them in targets.vim, it might be fair to give their maintainers the chance to expand them themselves.

That said, I do see benefits in expanding these text objects to support next and last text objects. Maybe there is even a way to just generalize this concept of next and last to make it easier to support that for existing text objects.

Is this what you had in mind? Or what benefits would you see in integrating them into targets.vim?

wellle avatar Feb 11 '14 18:02 wellle

Thanks for the quick response!

Also: Should the list of separators be customizable or just a fixed set that we can agree on?

Yeah, I'm biased to working mainly in C-like languages so a fixed set would be fine for myself. But then again, wouldn't be fussed if it was customizable either. Nested arguments seem such a fringe case that I doubt it'd be worth devoting the time and energy to getting right.

Looking at the default bindings, al and il would conflict with my last text objects like al,. We could either change the l for last text objects to N as suggested here, or find a different mapping for line objects. Operations like d2anl certainly seem appealing.

After re-examining, personally 'l' for last seems better than line so if it were to be incorporated I'd say the line object should be the one to change. It's also one of these objects that I use so rarely that 'dd' or 'cc' cater for that it's maybe not worth bothering about at all.

In general I would say I'm not striving to combine all text objects into targets.vim. All of them can coexist independently and everybody can choose what might fit their needs. So instead of including and expanding them in targets.vim, it might be fair to give their maintainers the chance to expand them themselves.

Yeah, I was concious this might be the case and sounds like a sensible strategy. I think the goal is just having a good generalised set of text objects for the masses.

Is this what you had in mind?

Agree with all your thoughts entirely, plugin is heading in the right direction and I'll be keeping my eye on it!

gaving avatar Feb 11 '14 18:02 gaving

I use https://github.com/kana/vim-textobj-lastpat regularly (/ text object matching last search pattern). I would really appreciate it if both targets.vim and vim-textobj-lastpat can coexist. Some suggestions:

  • option in targets.vim to disable some/all separator text objects
  • targets.vim could provide similar functionality to vim-textobj-lastpat

BTW thank you for the great plugin!

edkolev avatar Feb 12 '14 07:02 edkolev

@edkolev Depending on your Vim version you don't need that plugin anymore. I'm using gn and gN for that exact use case. They even accept counts: d2gn deletes the second match after the cursor and is perfectly repeatable.

So I don't see any reason to replicate that text object in targets.vim.

But I agree that it should be possible to disable certain mappings. Let's continue that discussion in #15.

wellle avatar Feb 12 '14 09:02 wellle

@edkolev You might be interested in yesterdays vimcast:

http://vimcasts.org/episodes/operating-on-search-matches-using-gn/

He even mentioned the patch I wrote that makes gUgn work.

wellle avatar Feb 12 '14 13:02 wellle

Nice! Thank you, for pointing this out and for providing the patch to vim :)

edkolev avatar Feb 12 '14 14:02 edkolev

How about allowing for = as a marker? Handy if you are dealing with equations.

Konfekt avatar Apr 10 '14 21:04 Konfekt

@EPNGH That's a good idea. Lets continue the discussion regarding = in #48.

wellle avatar Apr 10 '14 21:04 wellle

Is there any interest with regards to incorporating LaTeX environments and quotes into text objects?

`` LaTeX style quotes '' don't really work with quote selection,
\( inline math environments \) are also not supported
\[ neither are displayed equation environments \]

Currently I use https://github.com/rbonvall/vim-textobj-latex for these, however I'm fairly lazy and I really love targets.vim's ability to "search out" the text object you're trying to edit rather than having you have to be inside of it. That plus the 'nNlL' makes for a very fluid workflow that isn't available with textobj plugins (although I wish they were). It does lead me to ask whether these will be implemented, however. Or if there exists a way to add them myself.

ghost avatar Aug 11 '14 08:08 ghost

@thang1thang2 That's an interesting use case I haven't considered yet. I will think about it. Thank you!

wellle avatar Aug 11 '14 16:08 wellle

@gaving: Just letting you know that I just merged #79 that adds support for argument text objects. See the Readme for details.

Let me know if this works for you!

wellle avatar Aug 21 '14 20:08 wellle

@wellle: Awesome, looking forward to giving it a whirl!

gaving avatar Aug 22 '14 08:08 gaving

Chiming in... @wellle do you know https://github.com/justinmk/TextObjectify ? Your plugin is very similar, but each of you have exclusive features. I tell this to you, because I don't understand why you need the n in cin) for example. I'm already very accustomed to TextObjectify, which uses ci( for exactly the same change (yes, ( not ) with good reasoning behind it). An exclusive function of TextObjectify is, that you can make up any pair of characters on the fly. vix would select a range which is "delimited" by two x's. This works with every key, even Space (vi).

I think it could be a source of inspiration. You and the other contributors might have a lot to talk about ;-)

freeo avatar Aug 22 '14 11:08 freeo

Using ci( for cin) is all fine and good, but what would you use for ci(previous set of delimiter)? You can't use ), and so it breaks the mnemonic. Surround.vim gets around this because you only ever want (word) or ( word ). With Targets.vim, though, you want three options, normal, forward and backwards.

Sanity checks would be needed for the "any pair of characters on the fly", to ensure that you're checking only non-mapped characters. (e.g. vis would work, but cis would break and do nasty things unless you made sure no conflicts happened). It's a good idea, but I'm not sure that it's the best way to go about it. It might be best to consider something like ci\ or ci(backtick) or some other "weird" delimiter which then allows you to "input" a letter to act as the delimiter.

ghost avatar Aug 22 '14 12:08 ghost

@freeo: Yes I know TextObjectify. I agree that on the fly text objects sound useful, but I never felt like I'm missing a text object with targets.vim. And if I do, it's quite easy to add it with the supported customization.

One problem that I see with on the fly text objects, is that it's not sure whether it should behave like a quote text object (dax deletes both surrounding x) or a separator text object (dax deletes only one surrounding x). That's why they are explicitly defined in targets.vim.

Having ci( and ci) behave slightly different sounds interesting. As @thang1thang2 pointed out, the problem is that there are three different targets possible:

  • current ci( (seeks if needed)
  • next cin(
  • previous cil(

So to me it looks like we need some sort of additional modifier to allow all three cases.

wellle avatar Aug 25 '14 19:08 wellle

About words

I suggest the creation of a new kind of text object named "sequence text object", that, instead being defined by separator or delimitator characters, these objects are defined by a sequence of characters (or by a regular expression).

For example, a word object is a sequence of letters, digits and undercores; a WORD is a sequence of anything but whitespace characters, and so on...

Note¹: The user can change what is a word by changing the option iskeyword.

Note²: Vim uses the option isfname to define what is a filename, it can be used to create a new text object.


About paragraphs and sentences

A paragraph is an object whose separator is an empty line.

A sentence can be a special kind of "separator text object" that has several separators. Vim defines sentence as "ending at a ., ! or ?". So it can be a object whose separators are ., ! and ?.

But, for vim, sentences have special features:

  1. The ., ! and ? must be followed by either the end of a line, or by a space or tab.

    This prevents that words like "e.g.," and "i.e.," are considered as a sentence.

  2. Any number of closing ), ], " and ' characters may appear after the ., ! or ? and before the spaces, tabs or end of line.

    This only makes sense in a few languages (like english) where the closing quote or parentheses must appear after the period (as in I have "money."). In latin based languages the closing quote or parentheses appear before the period (as in I have "money".).

  3. A paragraph boundary is also a sentence boundary.

    A paragraph with just one sentence (or a paragraph with no sentence) is a sentence

If a sequence text object is an object defined by regular expressions, paragraphs and sentences can be defined by regexps.

Seninha avatar Dec 28 '14 02:12 Seninha

@Seninha: Thank you for your comment, that is a great suggestion!

I think sequence text objects and separator text objects ultimately lead to the same text objects. But I agree that some text objects (like words) could be easier defined by sequence than by separator. As you described WORDs yourself, they still might be easier defined by separator (any whitespace).

I will definitely keep this in mind when adding word text objects. If ever. Is there demand?

Some open questions:

  1. Should it be possible to create new text objects with a given regex?
  2. What would the setup in your vimrc look like?
  3. How should the next text object be selected if regex matches overlap? Similar to argument text objects?
  4. Will there be some adjustments needed to our whitespace handling? Should something different than whitespace be handled similarly to current whitespace?
  5. I guess growing a sequence text objects is not a valid operation?

wellle avatar Dec 28 '14 12:12 wellle

Or to put it another way: Should we add sequence text objects or match text objects?

As in: Should we allow only certain sets of characters to build sequences from, or should we allow full regexes?

The former would be much easier to reason about. The latter is more powerful, but probably much more complex to do right. There might be minuscule differences in expectations, similar to the difference between quote text objects and separator text objects that we might not see yet.

Anyway, I'll think about it :+1:

wellle avatar Dec 28 '14 14:12 wellle

I think just sequence text objects is necessary, since it can works together with options like iskeyword and is simpler to implement and to configure.

Also, complex objects like sentences could be best defined by separator expressions, i.e., by a regexp that matches to separators. I think using regexps to match separators is more useful than using them to match an entire object.

Seninha avatar Dec 29 '14 01:12 Seninha

This is starting to broach a much more meta topic that I've been privately musing about for a while, which is the broader diagramming of language as a whole. When one operates on text inside vim, they operate on an abstraction of text into groups that make logical sense to humans. In English, we commonly break text into the distinctions of letter, word, phrase, sentence, paragraph, page, chapter, and document. For programming, we have a different set of distinctions: word, letter, argument, line-of-code, function, function argument, "block" (e.g. not the entire function but the internals of it), and so on so forth. Obviously, programming is a much more difficult and rigidly defined abstraction of thought, but it has to be by nature.

The purpose of a text editor such as vim is very different than most other text editors. Most other text editors perform actions on manually selected areas of text, if possible, but otherwise are very blind and are simply containers in which you type out your text. Vim, on the other hand, aims to be intelligent of language structure so that you don't operate in text but rather operate on linguistic structures.

It was this revelation that revealed to me that Vim, flexible as it is, was actually incredibly restricting in how it approached damn near everything. It was written in a time where C reigned supreme as a language and where everyone who would ever use it spoke English as a first, and probably only, language. This lead to some short-sightedness in programming the editor (which is, of course, obvious only in hindsight).

What I would love to have an eventual goal of targets.vim be (heh, or maybe even something else entirely) would be to strip out all the 'object' code in vim and smash it with a hammer and re-write it in a way that's extremely flexible.

My main premises:

  1. Text as a whole is thought of in terms of abstractions that we tend to name 'word', 'sentence', 'paragraph', etc.
  2. What we mean by that, however, is entirely dependent on a few factors:
    1. What language we're working in.
    2. What type of word we're working on [see footnote 1].
    3. What environment we're working in (file type, etc).
  3. As such, the text editor should take into account far more factors than it currently does when dealing with text objects, and the (to me) antiquated concept of 'keywords' should be eradicated from the earth if at all possible (however, it's still useful...)

footnote 1: An example of the flexibility I envision. I would see ciw behave differently in each of the following cases:

  • this_is_my_beautiful_word
  • this,is,my,beautiful,word
  • this-is-my-beautiful-word
  • thisIsMyBeautifulWord
  • this is my beautiful word

Intuitively. One would expect diw to delete a word, without any of the surrounding stuff modified. So, if the cursor was on 'beautiful' you would have the result of

  • this_is_my__word
  • this,is,my,,word
  • this-is-my--word

and so on, so forth. And if you had the cursor on 'word' you would get the result:

  • this_is_my_beautiful_
  • this,is,my,beautiful,
  • this-is-my-beautiful-

and so on, so forth. And additionally, daw would result in this_is_my_word and this_is_my_beautiful respectively.

But this raises up another problem. What are you doing when you diw, you're deleting everything inside a text object. Which is simply an abstraction of a language. So if you wanted to truly generalize it correctly, you would need to have an additional level of customizable behavior

  • [action] - [location (inside/outside)] - [text object]
    • Where text object can be defined based on any amount of logic required (e.g. regular expressions as well as separators/sequences)
    • Where the, now defined, text objects then have customizable behavior based on context (like above example with 'word'). Possibly even with customizable key aliases (like parens for sentence) based on context/filetype.
    • Where this now customized behavior can be modified by the end user if necessary.

This would allow for all the 'text object' code to go away in vim. How I see it is you would just then have a file structure in vim like so:

  • Objects [ one line per 'name' of text object, e.g. word, sentence, paragraph, function argument... ]
    • Definitions (laid out like so):

      #textobj name: aliasChar (e.g. word: w) filetype: definition (be it a regexp or whatever)

      default: definition (fallback for non-defined filetype)

      #textobj2 name [etc etc]

  • Actions ( like delete, change, surround, etc) [ one line per 'name' of action ]
    • Definitions ( laid out like so):

      #action name: aliasChar (e.g. delete: d) textobj: definition (behavior of action when applied to that textobj)

      default: definition (fallback for non-defined textobj)

      #action2 name [etc etc]

  • Location ( like inside/outside/next/previous ) [ one line per 'name' of action ]
    • Definitions (laid lout like so):

      #location name: aliasChar (e.g. inside: i) textobj: definition (behavior of action when applied to that textobj)

      default: definition (fallback for non-defined textobj)

      #definition2 name [etc etc]

Since location is dependent on action which is dependent on textobj, location is also dependent on textobj. But, importantly, you don't need to define location to have an action and vice versa, this allows you to create a generic 'next' or 'inner' and have it work with everything, but customize behavior for tricky things like function arguments. Additionally, Objects would be defined in reference to filetypes, so a 'block' would be different in python than C than in Haskell. The 'Action' and 'Location' parts are what targets.vim currently hit and it does a pretty good job of it. But currently there's no way to have intuitive behavior outside of global ultimatums.

For example: you can't, currently, have vim treat a word as "text between underscores" while treating hyphenated words as a single 'word' in one filetype but have it do the opposite in another without massive pain with the 'iskeyword' stuff. Also, having vim see every single thing as a keyword makes navigating a huge pain because 'w' sees _ as its own word (which is what iskeyword does, really), so this_is_a_pain-to-move_through_with_w will take like 15 spams with 'w' but only one with W. Ideally you want 'w' to move from 't'his to 'i's, which it currently doesn't. Additionally, diw if the cursor was on 'to' (should) be able to be customized to delete pain-to-move since that's one "word" for our hypothetical filetype.

All in all, it's quite interesting thinking of how to truly diagram language in a way that it's extensible for all cases and programming languages without being complex to the end user or impossible to program for the programmer.

ghost avatar Dec 30 '14 10:12 ghost

@thang1thang2: Thanks for your huge comment :+1: I will probably need some time to fully grasp it.

wellle avatar Dec 30 '14 11:12 wellle

@thang1thang2

Wow! I beleave that Text Objects (or just Objects as you proposed, and I liked your proposal) must be reimplemented to a more natural, context-relative, filetype-relative and dynamic way. I hope the NeoVim project stuck to this approach.

I liked the way you proposed Objects to be defined in a more objective and structural way (as a truly object in any programming language):

  • Name the object;
  • Define the object due one logic (regexp, separator, sequences and any other logic the user wants to implement);
  • Define what is in and what is out;
  • Bind this object to a key;
  • Etc.

Well, this is only a hope I have that I hope to happen.

Seninha avatar Jan 04 '15 23:01 Seninha

In the meanwhile, https://github.com/Julian/vim-textobj-variable-segment provides a workaround for the iw example given by @thang1thang2 by an additional iv text object (and perhaps some inspiration, although it uses the vim-textobj library and targets.vim does not). Then depending on the filetype (Text = text,tex,markdown,... or Code = cpp,vim,...), iw calls Vim's iw or iv.

The plugin https://github.com/bkad/CamelCaseMotion addresses the w example. (Quite heavyweight to overwrite w.) Again, depending on the filetype, wcould be mapped to Vim's or CamelCaseMotion's w.

Making this patchwork more coherent by a word regex for each filetype (maybe the distinction Text or Code Filetype is already sufficient) would be nice.

Konfekt avatar Jan 05 '15 08:01 Konfekt

@Seninha

I would want to caution against the notion of binding something to a specific 'key' on a keyboard; such thinking is a very constrained idea. It would be better to bind the action to a user-definable key, where the binding could be anything from a single key on a keyboard, to a sound-file, to a chord (see: stenographic keyboard), etc. Binding actions to a sound-file would allow for organic voice commands being used to manipulate vim, which is sometimes necessary for blind people and otherwise essential for people with motor disabilities. Binding actions to chords would allow you to type a natural chord/syllable on a stenographic keyboard without having to set silly workaround bindings in a vimrc. Such is the type of forward-thinking I envision when I see the potential for vim's object-styled "composing" language.

Additionally, I would argue that objects could be defined in multiple types of logics, depending on how you need to define it, filetypes, etc

e.g. it might make sense for a plain text or *.md document to define a 'word' as a sequence of letters, symbols, etc. that are delimited by whitespace, periods, commas, colons, or semicolons (and a few other things, perhaps). In that way, a 'word' would almost have the same meaning as 'WORD'. In many programming language syntaxes, this would be a silly idea, and a word would have to be defined very differently than 'WORD' and with much more complex logic.

When in doubt, it is almost always possible to further generalize a concept. To which extent 'objects' are generalized depends mostly on what is possible to accomplish in vim. It may be that we would have to wait for a project like NeoVim to "finish" refactoring vim to a state where it would be possible to rip out most of the 'word' and other 'object' oriented code and rebuild it in a much more robust and extensible way.

My greatest inspiration for the restructuring is along the following train of thought: Humans speak in a verbal language, which is fuzzy and incomplete (and yet capable of expressing any idea). One of the most attractive principles of vim is the fact that you 'communicate' with vim in an almost human language. 'caw' -> "change around word" is a very human train of thought, and almost sounds like you're just speaking to the editor in your own words and telling it what to do. Unfortunately, if you completely subscribe to that, you quickly run into limitations in vim's ability to "communicate" with you, due to the constraints under which it was originally conceived. In studying how language argues and shapes things, and how grammatical structures are formed, you can see (dimly) vim's "language". Being able to expand this to a completely customizable, and extensible "grammatical/syntactical" system of using commands in context would allow the editor to become much more powerful and 'natural' to use over time, seemingly without limit.

ghost avatar Jan 06 '15 05:01 ghost

@thang1thang2: You might like VimSpeak.

wellle avatar Jan 06 '15 09:01 wellle

I was reading Steve Losh's dotfiles

And found a numbers text object, that would be a great feature to have in targets.vim

  • Number
  • Float
  • Digit

alx741 avatar Oct 25 '15 01:10 alx741

@alx741: Thanks, will consider.

wellle avatar Oct 25 '15 10:10 wellle

@wellle one of the disadvantages of vim-textobj-user and it's derivatives is that they're very slow loading wise. You mentioned that you use vim-textobj-indent, I'd wager that it's in the top 10 of your plugins that's slowing down your loading.

rgrinberg avatar Feb 19 '16 02:02 rgrinberg

@gaving: I'd like to keep this issue open as it still contains a lot of good ideas I wouldn't want to get lost. If you don't want to get updates about this issue you can click on the Unsubscribe button above. Thanks again for opening this issue in the first place, it has been very useful!

wellle avatar Jun 03 '18 21:06 wellle

@welle Ah no worries, I was just tidying up a load of ancient issues/requests I had open.

Cheers!

gaving avatar Jun 03 '18 21:06 gaving