altering Vim's text input behavior - vim

[Edit: after reading comments I realize that the Surround plugin is adequate for my needs after all, so I'll leave this question for purely academic purposes to gain a better understanding of vimscript's inner workings]
I'd like to make adding/deleting tags, quotes, braces, and other symmetrical text structures easier to do in Vim, and I find the surround.vim plugin a little too quirky and specialized for my needs.
What I really need is more generally a "mirrored" input mode and "mirrored" deletion mode, whereby I could visually select a block of text, then type onto or delete from both ends of the selection at once. As an example workflow, I'd like to:
select the word hello
hit some keystroke combo to enter "mirror mode"
type "
my text now says "hello"
In this example I only typed one character at each end, but it's important that in step three I could have typed many characters, not just one, for instance I should be able to type <b> to produce <b>hello<b> (I still would need to manually add the / in the closing tag, which I'm OK doing).
So is this even possible in Vim? Could someone provide a broad outline of functions that would be involved in the solution? Specifically, I don't know how to intercept text as it's being inserted and then alter the location where it appears so that it's tacked onto the beginning and ending of the selection block instead of the cursor location. And ditto for deletion.

Well, the behavior you describe is exactly what Surround does:
select the word hello
hit S
type "
my text now says "hello"
The difference with what you ask is the "live updating" or "live mirroring" which I have no idea how to do. You could probably take a look at SnipMate or UltiSnips for that part.

Related

Is there a one-liner to tell vim/ctags autocompletion to search from the middle of a word?

In vim (in Insert mode, after running exuberant ctags), I am using ctrl-x followed by ctrl-] to bring up a dropdown of various possible words/tokens. It's a great feature.
The problem is that by default, this list starts with a bunch of numeric options and automatically inserts the first numeric option, and if I backspace to get rid of the numbers and start typing a part of a word fresh -- with the idea of searching from the middle of the word -- the autocompletion behavior exits entirely.
I know I could type the first letter of the word that I want, then go from there. But that assumes that I know the first letter of the word, which is not necessarily a given.
For example, if I'm working on a pair-programming project with a friend during a long weekend, I might not remember at any given moment whether he called his method promoteRecordStatus(), updateRecordStatus() or boostRecordStatus(). In this example, I would like to type RecordStatus and get the relevant result, which does not seem to be possible at a glance with the current behavior.
So with that scenario in mind: Is there a simple, vim-native way to tell the editor to start its autocompletion without any assumptions, then search all available tokens for my typed string in all parts of each token?
I will of course consider plugin suggestions helpful, but I would prefer a short, vim-native answer that doesn't require any plugins if possible. Ideally, the configuration could be set using just a line or two.
The built-in completions all require a match at the starting position. In some cases, you could drop separator characters from the 'iskeyword' option (e.g. in Vimscript, drop # to be able to complete individual components from foo#bar#BazFunction()), but this won't work for camelCaseWords at all.
Custom :help complete-functions can implement any completion search, though. To be based on the tags database, it would have to use taglist() as a source, and filter according to the completion base entered before triggering the completion. If you do not anchor this pattern match at the beginning, you have your desired completion.

Is there a better method for find and replace in Vim?

Edit: I moved this over to the Vi and Vim site: https://vi.stackexchange.com/questions/13689/how-to-find-and-replace-in-vim-without-having-to-type-the-original-word
I'd like to optimize my "find and replace" workflow in Vim. It's something I do often, as I'm sure most of you do too. Usually something along the lines of -- copy a block and change the name of a variable in a few places. I know, I know, that probably triggers your "why are you copying and pasting code" reflex, but let's not go down that road... There are plenty of valid use cases :)
I'm well aware of the search and replace commands: :s or :%s but I don't like them. It forces me to type out both the full variable name I'm searching for and what I'm changing it to. Maybe there is a better way fix the the amount of typing with :%s? I often use long descriptive variable names, so that is really a deal breaker for me. I also don't like how typing out a variable name from scratch is typo prone and can consume time and brainpower hunting down typos. I much prefer typing it once, and then copying and pasting to just avoid this entirely if possible.
My current workflow uses some combination of movement/yank/select/search/put to move around the file and replace one by one. It is not great but has the benefit of avoiding typing out full variable names. I might just need to type the first few letters with / or use another movement command (i.e. fx) depending on what's around and then hit ve to select the whole word. I also don't mind that I have to repeat for every instance. I never do a full find replace without confirming each change. But it would be much preferable if I could repeat the replacement action with a single keystroke (which I can't do with this method). each replacement is usually something like n then ve then p (or even worse "0p)
Is there a faster way?
My own workflow is similar to yours:
To start, get the cursor on one instance, possibly with / or by navigation.
Hit * to find the next instance of that word.
Change one instance with cw and then the new variable name.
Then it's fast: n/N to get to the next/previous instance, and . to repeat the last edit.
This workflow gives me the same advantage as yours, in that I can review each case before applying the change, but it's just two keystrokes for each additional change.
Hope this helps.
I like the "visual highlight then edit" approach.
shift + v to highlight the region that you want to modify.
then :s/old/new/r where old is what word you want to replace with new.
r changes the first instance of that word old.
Note* There are options other than r which modify its behavior how you want to replace the word.

Is there a less fantastically kludgy way to do one-off highlights in Vim?

i. The Problem
My goal is something like the following:
I have a line of text like
Who left the dead mouse in the fridge?
and I want to highlight the first the in green, just this one occurrence. That is, I don't want to syn match ThisMagicWord "\<the\>" or anything that will overzealously highlight other thes.
There is one other requirement, which is that if the user edits the other text on the line, say to
Who on earth left the delicious dead mouse in the fridge?
the highlighting will track with the word the, so long as the user doesn't edit that one particular word.
ii. The Kludge
Now, I have a solution to this. In fact, I am proud of my solution, because it was tricky to think up. But it is not, by any stretch of the imagination, a good solution.
It turns out that the Unicode character Combining Grapheme Joiner is effectively a no-op in Vim. It produces no glyph, and takes up no width. It is the only such character that I have discovered. So what I do is, I surreptitiously edit the line in question to be
Who left the<CGJ> dead mouse in the fridge?
and then define a rule
syn match ThisMagicWord "the<CGJ>"
I will additionally trigger on BufWritePre and BufWritePost to strip the CGJs out of the file on disk.
iii. The Questions
Is there a no-op character in Vim (or a way to produce one) other than CGJ? Ideally a non-combining character, since the<CGJ> will not match a search for /the, due to the way Vim regexes handle combining characters.
Is there a better way to get at the behavior that I want?
You're right that there's currently no good way to mark static matches and keep them up-to-date when edits are done nearby. My approach would have been worse than your kludge: Include the line / column information in the match (via the \%l and \%v special atoms), and attempting to update those with a combination of marks (works for line changes) and intra-line custom diffing.
Though your use of special Unicode characters is clever, it's (as you admit) a hack. I've asked you for uses in the comments, and am still not completely satisfied / convinced. If you can come up with good, real use cases and current pain points, please direct them to the vim_dev mailing list (best with a functional draft patch attached). The functionality to keep track of such text is basically there (in the Vim internals), it's just not yet tracked and exposed to users / Vimscript. Though Vim development has been (often frustratingly) slow, with a compelling argument on your side, new functionality can and does happen.
How about using marks?
Move the cursor to the word you want, set a lowercase letter mark (e.g. mz), then add highlighting for the word like \%'zthe

VIM Delete Standard Scripting Word Groups

How would you use VIM to delete a word group, which includes white space characters, but is a standard grouping you would want to access when scripting? Specifically, when you have your cursor over some part of the following text, how would delete help="initialize, lines, h2, derivs, tt, history", from below. Maybe one would need to create specific mappings. But on the other hand, it seems pretty natural to want to access text like this if you are using VIM to edit scripting programs.
parser = argparse.ArgumentParser()
parser.add_argument("task", help="initialize, lines, h2, derivs, tt, history", default='yes')
Vim has a variety of text objects built-in, e.g. da" deletes quoted text (including the quotes; di" keeps the quotes). See :help text-objects for more information.
There are some plugins, e.g. textobj-user - Support for user-defined text objects and my own CountJump plugin that make it easy to define your own, "special" text objects. Also, you'll find many such text objects on vim.org. Based on your example, argtextobj.vim - Text-object like motion for arguments may be exactly what you need here.
If you are inside the " you want to delete, I would use:
di"diW
If you were above help=, I would use something like:
d/defEnter
to remove everything until you encounter default, followed by a few x, and left-wise motion, to remove the remaining characters.
I don't really think a new mapping is needed, but your experience may vary.
What makes sense from Vim's perspective and according to its design goals is to provide small and generic elements and a few rules to combine them in order to achieve higher level tasks. It does quite a good job, I'd say, with its numerous text-objects and motions but we always have to repeat domain-specific tasks and that's exactly where Vim's extensibility comes into play. It is where users and plugin authors fill the gap with custom mappings/object/functions and… plugins.
It is fairly easy, for example, to record a macro and map it for later reuse. Or create a quick and dirty custom text-object…
The following snippet should work with your sample.
xnoremap aa /\v["'][,)]/e<CR>o?\v\s+\w+\=<CR>
onoremap aa :normal vaa<CR>
With it, you can do daa, caa, yaa and vaa from anywhere within that argument.
Obviously, this solution is extremely specific and making it more generic would most certainly involve a bit more thought but there are already relatively smart solutions floating around, as in Ingo's answer.

Word / Code Completion in VIM

I know that I can get word completion through CTRL+N & CTRL+P and code completion through omnifunc with CTRL+X CTRL+O. I additionally tried Supertab (very nice), because I'm used to TAB-completion. That all worked all right. I would like to see possible matches while I'm typing, so I also tried autocomplpop.vim, witch does just that.
What I like to accomplish though, would be a combination of both together with a little twist: I would like to see suggestions pop up as I type (just like with autocompop) but when I use TAB the word should be expanded only to the largest common match:
foo bar testor booze test baz teter
te<TAB>
After I type te in the 2nd line, the popup should suggest test, teter and testor.
When I press TAB, it should do nothing, because there is no more common ground than te. After I typed an additional s and press TAB, it should expand tes to test (because it is the smallest common ground) and to testor after a 2nd TAB.
Edit: I try to be more clear...
te<TAB>
should do nothing because "we" don't know if a 't' (teter) or an 's' (test, testor) should follow.
tes<TAB>
should expand to test (because that works for both - test and testor - and teter is no longer a possible match).
test<TAB>
should expand to testor (only possible match).
Well, the suggestions popup is just bonus, but I really would love to see the TAB behavior. Hope I don't get to esoteric here and you can help me out with some script-tricks or plugins to tame VIM to do just that.
:set completeopt=longest,menu,preview
maybe?
(I'm not sure if your statement
"when I press TAB, it should do nothing, because there is no more common ground than te"
conflicts with your later statement
"to testor after a 2nd TAB."
It seems to me that there's no more common ground than "test" at that point... Unless you mean the first tab goes to the longest common ground, and then the subsequent tabs cycle through the other matches, in which case you're after what I have above...)

Resources