Trigger a Vim custom g# command on a single line? - vim

In Vim 8.x, is there a way to specify a same-line action when using g# and :set opfunc to define a custom command? In other words, I know how to use map to:
noremap <silent> <a-z> :set opfunc=MyFuncOp<cr>g#
Let's say MyFuncOp does some custom type of yank or delete. It works perfectly to press alt-z then j and do that on two lines. But what if you need it on just one line... just like you would with repeated operators like dd or yy? Mapping g#g# doesn't work. How do you convey a single linewise (i.e., the current line) to a g# custom command?

It may depend on how your MyFuncOp() is implemented but, in general, you should only need to do <A-z>l, <A-z>h, or basically use any motion that stays on the current line: 0, $, etc. Just like with a real operator.
Showing us your MyFuncOp() would help.

No, it's not to be implemented by custom g# only. Basically, you have two options.
Either, (1) create also a custom motion (mapmode-o) that includes the whole current line. So, say, you have Y to perform custom "yank" and al to select current line in operator pending mode. Then typing Yal will do. There are many existing implementations of such "custom motion" out there. And it's also pretty easy to devise another one yourself.
Or (2), define special version of your mapping. That is, both Y to invoke g-at, and YY to perform custom operation on the current line without entering the operator pending mode. Proper use of functions and commands should help to avoid code duplication in this case.

Related

Vim Custom Replace Function

Note: I'm currently using Neovim v0.2.2 (But I believe this shouldn't change anything related this post)
I'm currently attempting to create a function within vim that allows for easily replacing text.
I understand I can create a shortcuts and macros and all that, but ideally I just want to give 2 args, and not think about what specifics go where as this can interupt my thought process.
So I decided to just have a simple wrapper disguised as a function (Which I will create a command wrapper for as well, once I figure out what I did wrong here)
function! VisualReplace(query, replacement)
" Example = '<,'>s/query\%V/replacement/g
'<,'>s/a:query\%V/a:replacement/g
endfunction
As you can see, it's a very simple function that just applies the args in it's respective position, Yet, this fails even when called as a function using : call VisualReplace('some_query', 'some_replacement'
Alternatively, if you simply use the Example I have commented out directly, there's no issue, So I was hoping someoen could enlighten me on a potential fix
If need be, I could possibly look into string building & build it incrementally
Error msg:
Pattern not found: a:query\%V
General theory
Vimscript is evaluated exactly like the Ex commands typed in the : command-line. There were no variables in ex, so there's no way to specify them. When typing a command interactively, you'd probably use <C-R>= to insert variable contents:
:sleep <C-R>=timetowait<CR>m<CR>
... but in a script, :execute must be used. All the literal parts of the Ex command must be quoted (single or double quotes), and then concatenated with the variables:
execute 'sleep' timetowait . 'm'
Your function
In order to get the a:query and a:replacement arguments into :substitute, use :execute and either string concatenation or printf():
function! VisualReplace(query, replacement)
execute "'<,'>s/" . a:query . '\%V/' . a:replacement . '/g'
endfunction
Additional critique
Passing a range to a function is so common, there's special syntactic sugar for it: The range attribute to :function, and a:firstline and a:lastline implicit arguments. Read more about it at :help function-range-example. While your use case here seems to be specifically for visual mode, in general it's useful to keep the scope of functions as broad as possible.
#Ingo Karkat answered perfectly. However, I feel like there might be some workflow alternatives which might help. (Assuming you aren't trying to script this behavior)
Visual Star
It looks like you are build a search based on a visual section. You may want to consider using a visual-star plugin to simplify the process. Here is a an example of a visual star mapping:
xnoremap * :<c-u>let #/=#"<cr>gvy:let [#/,#"]=[#",#/]<cr>/\V<c-r>=substitute(escape(#/,'/\'),'\n','\\n','g')<cr><cr>
This mapping will allow you to visually select text and then execute * to make it a search pattern. Similar to how * works in normal mode on the current word.
Search refining
I get the impression that you are trying to refine your search pattern. Vim has a nice way of doing this with q/ or pressing <c-f> while searching with /. See :h q/. This will bring up the command-line window which will allow you to edit the query/command-line with all your normal Vim keys.
Search and Replace with gn motion
Sometimes doing a substitution is just overkill or doesn't quite fit the situation right. You can mimic a search and replace by using the gn motion to operate on a search pattern. By using an operator and the gn motion together you can use the dot command, ., to repeat the action easily.
Example:
/foo
cgnbar<esc>
Now you can use . to repeat the foo -> bar replacement. Use n to skip. You can use other operators as well, e.g. gU to uppercase.
See :h gn and :h operator for more help.
Related Vimcasts episodes:
Refining search patterns with the command-line window
Operating on search matches using gn
Search for the selected text

jump over user defined text objects in vim

I am using the kana / vim-textobj-user for defining some custom user objects but the problem is I can't jump over them : case in point
let's say I am using the same indent text object which is mapped by ai and ii
I want to jump around the text in normal mode something like ]i and [i
currently I am using a very hacky way of selecting and exiting visual mode
So is there a simple way to do that and have some kind of mappings for all the other user text-objects as well .
Something like ]{text-object}
I am using the kana / vim-textobj-user for defining some custom user objects
[...]
let's say I am using the same indent text object which is mapped by ai and ii
I want to jump around the text in normal mode something like ]i and [i
Vim has a bunch of built-in commands like ]m, [M, etc. So I thought you meant ]i/[i to move the cursor to the next/previous text object. If so, vim-textobj-user supports both selecting and moving to a text object since its first release. But it's not automatic. At least you have to declare what keys (such as ]i/[i) to be used for the commands.
But I wonder about the following sesntence:
currently I am using a very hacky way of selecting and exiting visual mode
So you typed like vaio<Esc> and vai<Esc>? What you want to do is to move the cursor to the first/last line of the text object under the cursor? If so, vim-textobj-user currently doesn't provide API to define such commands.
In this case, it is probably possible to automate defining key mappings like nmap ]i vai<Esc>. But it seems to be fragile and overrides several built-in commands.
Text objects are only for applying a command (e.g. gU) or visually selecting an area of text. Motions over / to the next occurrence are highly related, but different commands. I think the vim-textobj-user plugin only provides the former, but not the latter.
My CountJump plugin is quite similar, and provides commands to set up both text objects and jumps based on regular expressions.

vim-7 with ctags

running VIM-7.0.237 on CentOS-5.6. I have a large C code base with tags generated with ctags-5.6, there are functions with the same name defined in several places and I remember back when I used vim-6.3, I could jump over those multiple definitions easily -- VIM used to suggest me what definition I want to jump at. Now with vim-7 it gives me only first.
Is there a way to have a old-style behavior? Thanks.
PS. I have a default VIM configuration.
You can either precede the command with a count to jump to a specific match or use
:ts {identifier}. It will list the tags available for the given identifier.
You may find the ctrlrctrlw command
(et similars) useful to insert the word under cursor. A map may help you with
it.
nnoremap \] :ts <c-r><c-w><CR>
Use g] (g$ on french azerty keyboards) to display a list of definitions.

vim: pass a char or word to your function

I know that when you define a function in vim, you can use the range keyword so that users can say:
:-5,+5call MyFunction()
And then your function gets a:firstline and a:lastline.
What I want is something similar, but more like the traditional vi way of combining a command with a movement, so that 'd ' deletes a space, 'dw' deletes a word, 'd2w' deletes two words, 'd2j' deletes three lines, etc. Assuming my function gets mapped to some input-mode character sequence, is there any way to make it accept similar variable-length inputs, and then modify that text?
Just to be a little more clear, suppose I want to define a function to wrap <b> tags around existing text. We'll say that function is mapped to ;b. I want users to be able to say ';bw' to bold one word, or ';bf.' to bold everything to the end of the sentence, or whatever, with all the flexibility that vim provides to built-in commands.
If I understand what you're asking, then all you do is include the char argument in your mapping. For example:
map d :call MyFunction('d')<cr>
map dw :call MyFunction('dw')<cr>
map d2w :call MyFunction('d2w')<cr>
" Of course these would be terrible mappings because they
" are already mapped to important Vim functions
The way mappings work is that if you "overspecify" a char like 'd' above so that it is usable either by itself or as a prefix for longer command, Vim will wait briefly (for timeoutlen)after you press 'd' to see if you're going to press another character. (This depends on thetimeout option being set to true, which is the default.) If you don't press another character, Vim will execute the 'd' mapping. If you do it will call the more complex mapping. See :h map-commands generally and :h map-typing for more.
Note: After your clarification I think what you want is to create a custom 'operator' function that you can use to operate on buffer areas defined by Vim motions. See :h map-operator for info on how to do this.

Vim - Search and replace the results

I'm getting more and more comfortable with Vim after a few months.
BUT, there is only one simple feature I can't get any answer from the web. That is "Search and replace the results". The problem is that I know:
:/keyword to search, and hit enter "keyword" will be highlighted (of course with set hlsearch)
n, or N to navigate
:% s/keyword/new_keyword/g to replace all occurences of keyword with new_keyword.
BUT, I would think that there must be a way to search, and replace the matched keyword (highlighted) with any new_keyword WITHOUT doing ":% s/keyword/new_keyword/g", which is a lot of typing considering search & replace is such a day-to-day feature.
Any answers/comments will be greatly appreciated!
If you've already done a search you can do a substitution for the same pattern by simply leaving out the pattern in the substitute command. eg:
/keyword
searchs for "keyword", and then:
:%s//new_keyword/g
will replace all occurrences of "keyword" with "new_keyword".
Searching and using the dot command (you didn't meantion you are using the dot command, that's why I highlight it) to repeat the last input action is my best bet here.
I use s///g for search and replace.
Well, since #keyword# and #new_keyword# account for most of the characters, and you need some way to differentiate between them (i.e., a character in vim, or tab between entry fields in dialog in a different editor), you're left with maybe four or five keystrokes beyond that.
So I think you're probably overestimating number of keystrokes and also forgetting that (1) it becomes very natural, and (2) working this way allows you also to naturally modify the action performed by specifying a different range or option flag.
But you can cut down on keystrokes. If you want you can map a key to automatically bring up the command line with '%s/' already in place. e.g.:
nmap s :%s/
The command above would remap 's' (I'm not recommending remapping to that key, but it gives the idea) and set you up to insert the keyword.
Also, you can set the 'gdefault' option to default to substituting multiple times per line. This lets you skip the ending '/g' in your keystrokes:
set gdefault
See ':h gdefault' for help section on that option.
In the end I would say just get used to the default way it works, because using it that way allows you to keep same basic operation when you want to specify different ranges or option flags, and creating a new special map is just another thing to remember. gdefault may be worth setting if you think you're going to want it majority of time, adding /g flag at end when gdefault is set has effect of turning /g off. . .
Move to the first highlighted word then record a macro for replacing the word and moving to the next one, e.g:
gg
n
qq
caw new_word^[
n
q
#q
##
##
...

Resources