how do I cancel visual mode programmatically in vim - vim

I'm running a bit of code in Vim and having a problem (I think) with not being in the correct mode. I'm trying to get the text between either '< >' or '[ ]' into a register. At point code starts below I know the cursor is between a pair of brackets, but I don't know what kind the brackets are:
" put text in x reg if I'm in <> brackets
normal! vi<"xy
" put in x reg if I'm in [] brackets
if len(#x) < 7
normal! vi["xy
endif
[. .. . more code follows making use of
value in x register]
If I'm clicking between <> brackets the rest of the code works okay, alhough I may not be in normal mode. If I'm clicking between the [] brackets then it doesn't work at all, and the literal text ["xy gets inserted at cursor position.
I've tried a couple different workarounds, e.g., trying to put into the keystrings (or a separate normal command) or using 'visual!' command after each visual part. Can't get things going with and the Visual! command avoids the character inserts on second part but but also seems to screw up getting the visual match into the x register.
I'm a little confused and not sure what mode I'm in exactly at all parts of script: normal, visual, or insert. I run through things manually and it seems to work fine but if I do it programmatically it behaves unexpectedly.
Is there some simple thing I'm missing? I could do what I want another way, but this method (at first look) seemed like easiest and most straightforward. Is it?
Thanks for any help.

Okay, I figured out the problem. The mode was left in visual mode when first inner bracket command found no match. Needed to have an extra 'v' to bring back to normal mode before pressing the keys again. Like this:
" put text in x reg if I'm in <> brackets
normal! vi<"xy
" put in x reg if I'm in [] brackets
if len(#x) < 7
" EXTRA V IN LINE BELOW
normal! vvi["xy
endif
[. .. . more code follows making use of
value in x register]

Related

Change inside brackets not working in vim [duplicate]

we all know what ci" ci' ci( ci[ ... does. Very handy in everyday's editing. I found something strange, and checked the help, didn't find out why.
say, I have a file:
foo "target"
foo 'target'
foo (target)
foo {target}
foo [target]
foo <target>
if my cursor at the beginning of each line, (on the 'f'), then I type ci", ci', ci(...
the cix works only with quotes (single or double), doesn't work for brackets. why do they behave differently?
(dix, vix the same)
tested with --noplugin, vim 7.3
thank you.
Update
thanks #romainl for the answer. I still have doubt regarding the "pair processing in vim"
check this example:
foo "targ\"eti\" some\"thing else "
if I have a line like above, I type ci", no matter cursor is at beginning or between quotes, it works perfectly, it seems that vim does have the idea of "pair"?
and this maybe what you meant about the pairing ?
foo "target x some"thing else "
foo (target x some(thing else )
I have above two lines, if (cursor at x) I type ci" and ci(, nothing happened to 2nd line, but first line changed into:
foo "I"thing else " (I is cursor)
ci( is consistent with ci[, ci{ and cit and all the other <action>i<something>. Only ci' and ci" work like they do. The outliers are the quotes, here, not the brackets.
Vim doesn't consider that quotes come in pairs while brackets do. It has an internal logic for matching pairs that works with actual pairs but not with quotes hence the difference in behavior.
You are not the first to complain about that discrepancy: this is one solution, maybe you can find others.
edit
I don't have a deep knowledge of Vim's internals, unfortunately, so I can only make assumptions, here.
If you ask Vim to do ci" it does its best to find a pair of double quotes but double quotes don't go by pairs: there's no way to tell if a " is the closing one or the opening one contrary to brackets. Because of that, Vim must make some choices. IMO, the choice that would make the most sense considering how the other members of the family work, would be to assume that the cursor is between the quotes and select from the first one to the right to the first one to the left. I can only assume that this method proved wrong in some way or didn't work for some reason and that the other method (the current one) prevailed.
Another explanation could be that the i<something> mechanism is somehow tied to a specific subsystem (maybe the same as showmatch?) that is unable to deal correctly with quotes.
Anyway, just like you, I find this discrepancy weird and I've somehow internalized it and aligned my usage of <action>i" to how the others work. To the point of actually doing 2t"ci" or some variant instead of ci"!! Inefficient, I know.
Did you read :h a'? I completely forgot where I got my "limited understanding" of the issue but it was there! It says:
"Only works within one line. When the cursor starts on a quote, Vim will figure out which quote pairs form a string by searching from the start of the line."
What I get from that is this: for some reasons unknown to us, Vim uses another mechanism for matching quotes than for the other pairs and that is why ci" is different from ciband friends. The underlying cause is not clear at all but I'm fairly certain that the big picture looks a lot like what I imagine.
To me, it looks a lot like a bug or a limitation disguised as a feature.
If you are still curious, I'd suggest you ask any further question on vim-dev.
make use of %
" nnoremap cb cib
nnoremap cb %cib
" nnoremap vb vib
nnoremap vb %vib
nnoremap yb %yib
nnoremap db %dab
to enhance %:
https://github.com/andymass/vim-matchup#tocbar-dopjfd
b can match ( [ {,
Want to use ' to match both ' and "?
nnoremap c' :call DoubleAsSingleC()<CR>
func! DoubleAsSingleC()
" When [!] is added, error messages will also be skipped,
" and commands and mappings will not be aborted
" when an error is detected. |v:errmsg| is still set.
let v:errmsg = ""
silent! :s#\"\([^"]*\)\"#'\1'#g
if (v:errmsg == "")
echo "双变单"
endif
exec "normal ci'"
endfunc
Similar for:
d'
y'
v'

vim - surround text with function call

I want to wrap some code :
myObj.text;
with a function call where the code is passed as an argument.
console.log(myObj.text);
I've thought about using surround.vim to do that but didn't manage to do it.
Any idea if it's possible ? I
With Surround in normal mode:
ysiwfconsole.log<CR>
With Surround in visual mode:
Sfconsole.log<CR>
Without Surround in normal mode:
ciwconsole.log(<C-r>")<Esc>
Without Surround in visual mode:
cconsole.log(<C-r>")<Esc>
But that's not very scalable. A mapping would certainly be more useful since you will almost certainly need to do it often:
xnoremap <key> cconsole.log(<C-r>")<Esc>
nnoremap <key> ciwconsole.log(<C-r>")<Esc>
which brings us back to Surround, which already does that—and more—very elegantly.
I know and use two different ways to accomplish this:
Variant 1:
Select the text you want to wrap in visual mode (hit v followed by whatever movements are appropriate).
Replace that text by hitting c, then type your function call console.log(). (The old text is not gone, it's just moved into a register, from where it will be promptly retrieved in step 3.) Hit <esc> while you are behind the closing parenthese, that should leave you on the ) character.
Paste the replaced text into the parentheses by hitting P (this inserts before the character you are currently on, so right between the ( and the )).
The entire sequence is v<movement>c<functionName>()<esc>P.
Variant 2:
Alternatively to leaving insert mode and pasting from normal mode, you can just as well paste directly from insertion mode by hitting <ctrl>R followed by ".
The entire sequence is v<movement>c<functionName>(<ctrl>R")<esc>.
You can use substitution instruction combined with visual mode
To change bar to foo(bar):
press v and select text you want (plus one more character) to surround with function call (^v$ will select whole text on current line including the newline character at the end)
type :s/\%V.*\%V/foo\(&\)/<CR>
Explanation:
s/a/b/g means 'substitute first match of a with b on current line'
\%V.*\%V matches visual selection without last character
& means 'matched text' (bar in this case)
foo\(&\) gives 'matched text surrounded with foo(...) '
<CR> means 'press enter'
Notes
For this to work you have to visually select also next character after bar (^v$ selects also the newline character at the end, so it's fine)
might be some problems with multiline selections, haven't checked it yet
when I press : in visual mode, it puts '<,'> in command line, but that doesn't interfere with rest of the command (it even prevents substitution, when selected text appears also somewhere earlier on current line) - :'<,'>s/... still works

Exiting exe mode in a macro

I had a large file I was trying to reformat which involved removing the 2nd to nth repeating sets on 2 to 100 lines per duplicate.
The data looked like
element1.element2.element...field.comment
I wanted to remove the repetition in elements after the first instance so of course I went complicated :) and did a macro something like
In a macro Yanked first element on current line to register p and then processed lines yanking the first element into register o and then doing, still in the macro
:if (#p=!#o)|:.s/paste register p//g|else|:norm! j|endif
Now this worked OK except when it got to a line where #p<>#o the :norm! j part stayed in : mode until I manually escaped once or twice then executed the :norm! j command.
I solved the problem an easier way but would like to know why it was only on the else portion that it wouldn't leave :ex mode.
From :help norm
:norm[al][!] {commands} *:norm* *:normal*
...
This command cannot be followed by another command,
since any '|' is considered part of the command.
...
An alternative is to use |:execute|, which uses an
expression as argument. This allows the use of
printable characters to represent special characters.
Example: >
:exe "normal \<c-w>\<c-w>"
So this would do the trick:
:if (#p=!#o)|:.s/paste register p//g|else|:exe "norm j"|endif

delete from end of lines using block select in vim

I'm getting an unusual behavior when I try to delete from end of lines using block selection in vim.
So let's say I have a text as such:
delete this char:x
and this:x
also this:x
and then this:x
lastly this:x
If I want to append y to every line I can:
start block selection with C-v
select all the lines with 4j
go to ends of lines with $
start appending with A
type the desired text y
in order to get:
delete this char:xy
and this:xy
also this:xy
and then this:xy
lastly this:xy
but if I try to delete x in the last step instead of appending I would expect to get:
delete this char:
and this:
also this:
and then this:
lastly this:
although I end up with:
delete this char:
and this:x:
also this:x:
and then this:x:
lastly this:x:
As far as I understand it appends the last char in the first line to all other lines (in this case :) rather than deleting the missing ones (in this case x).
I can do this with macros or substitutes but I don't quite understand the rationale behind such behavior. Is there a way I can do this with block selection?
Have you tried :{range}normal? This should work:
:'<,'>normal $x
(The '<,'> bit is filled in for you when you type :.)
$ C-v 4j x
go to end of line with $
toggle visual block C-v
go down (in your case 4x) 4j
delete that stuff with x
Edit: (reacting on your comment for arbitrary indentation)
That can be done with simple macro. Macros are not so hard as you can think:
start recording a macro, we will name it 'a', so qa
go to the end of line $
delete one character x
go down by one line with j
end our macro q
Now apply our macro: 20#a - will do the same you did while you was recording the macro, 20x.
If I have a small number of lines I typically do Abackspaceesc. Then repeatedly do j. until done. Not the fastest way but easy to remember.
For a large amount of lines I typically visually select the lines via V then do a substitution or a normal command on the range.
:'<,'>s/.$//
:'<,'>norm $x
Note: you do not have to type '<,'>. It will be inserted automatically when you start a command when some text is visually selected.
The substitution command is pretty simple, match the last character (.$) and then replace it with nothing.
The normal command is just how you would delete the last character in normal mode via $x for a single line except it will apply it to each line in the range.
For more help see:
:h range
:h :s
:h :norm
as you said yourself, to achieve your goal, there are other ways, in fact better ways to go. :s or q(macro) or :g/.../norm $x. :s/.$//g is pretty straightforward.
Ctrl-V is not suitable for this job. As for its name: Visual BLOCK. You want to remove the last x, and they (only x) are not in a block.
However if you really want to stick with ctrl-v, you have to do some extra work, to make those 'x' in a block. If you have Align plugin installed, you could :
Select (V) all lines you want to do the trick,
<leader>t:
then your text looks like:
delete this char : x
and this : x
also this : x
and then this : x
lastly this : x
Ctrl-V to remove x, you should know how to do it.
then
:%s/ *:/:/g
to remove padded spaces before ':'
However I don't think it is a good way to go.

Command to surround a character with spaces in vim

I am trying to use vim properly - to aid me I've mapped my arrow keys to "" so that I am forced to use {hjlk} to move around.
This is causing me a problem when I want to just surround a character with spaces, eg:
"2+3" is better formatted "2 + 3"
Previously I would have put my cursor over the + and typed:
i[space][arrow-right][space][Esc]
That's 5 presses.
To do this without the arrow I seem to need to put the cursor over the + and go:
i[space][Esc]lli[space][Esc]
That's 8 presses.
I can convert the "li" into an "a" which reduces it to 7 presses:
i[space][Esc]la[space][Esc]
Short of writing this into a macro is there a better way of doing it? Is there some magic vim command which will allow me to do it in less than even 5 presses - and some way to generalise it so that I can do it to entire words or symbols, eg if I want to convert 3==4 to 3 == 4?
Personally, I think it makes most sense to destroy what you want to surround, and then repaste it.
c w "" ESC P
Obviously, you can replace both the object and the quotes with whatever you like. To change just one character + to be [space]+[space], you would do
s [space] [space] ESC P
on the +
The first thing that jumps to mind after reading just the title is surround.vim which is an excellent script to do all kinds of useful things along the lines of what you've described.
To solve your specific problem, I would probably position the cursor on the + and:
s[space]+[space][esc]
To change 3==4 into 3 == 4, I might position the cursor on the first =, and:
i[space][esc]ww.
i have been wondering about this as well. i tried with surround.vim, but the naive approach
S<space>
(after making a visual selection) does not work since the space is already taken up as a modifier for adding space to other surrounding character pairs. S<space><cr> adds a ^M in the output. Ss almost works but inserts a space only before.
after asking at tpope/surround.vim on github:
S<space><space>
in visual mode works. alternatively, from normal mode, ysl<space><space> works for a single character
Hah! I've been trying to figure out how to surround a block in spaces for quite a while and I finally found the right combination.
Using surround.vim you say surround selector space space.
So for this specific case I would use visual mode (a good trick for operating on single characters under the cursor BTW) thus: "vs " <- four key presses!
I also have a habit of typing things like argument lists without spaces. With this technique you can just navigate to the second argument using w and say "vws " to visually select a word and surround with spaces.
I prefer visual select mode generally. Also the alternate surround syntax "ysw " excludes the word final comma that is caught by "vw".
You could create a macro with one of the described actions and call it everytime you need it (Like amphetamachine proposed while I was writing) or you could simply search & replace:
:%s/\(\d\)\(+\|-\)\(\d\)/\1 \2 \3/g
You probably have to execute this command two times because it will only find every second occurence of +/-.
EDIT:
This will replace everything without the need to be called twice:
:%s/\d\#<=+\|-\d\#=/ \0 /g
Try positioning your cursor over the '+' and typing this:
q1i[space][right arrow][space][left arrow][esc]q
This will record a quick macro in slot 1 that you can re-use whenever you feel like it, that will surround the character under the cursor with spaces. You can re-call it with #1.
There is also the more versatile one:
q1ea[space][esc]bi[space][right arrow][esc]q
Which will surround the word under the cursor ("==" counts as a word) with spaces when you hit #1.
You could set up a mapping like this (press enter in visual mode to wrap spaces):
:vnoremap <CR> <ESC>`<i<SPACE><ESC>`>la<SPACE><ESC>h
This method allows you to use . to repeat the command at the next +.
Put your cursor over the + and type:
s[SPACE][CTRL-R]"[SPACE][ESC]
I know this is and old thread, but this might be useful to someone. I've found that the map (map it to anything else you want!)
noremap <leader>ss diwi<SPACE><C-R>"<SPACE><ESC>B
works ok both for turning 'a+b' into 'a + b' (when used over the '+' char) and for turning 'a==b' into 'a == b' (when used over either the first or the second '=' sign).
I hope it's useful to someone.

Resources