I'm trying to alter the ge motion's functionality when it's used with an operator. Instead of it operating on the last character of the target word, I'd like it to work exclusively only until the target word's last character similarly to w.
Example:
foo bar
^
Should lead to
fooar instead of foar when I use dge with the cursor at ^
I don't know anything about Vimscript and I'm pretty much relying on exe now. This quick attempt I wrote seems to be full of errors. The variables don't seem to be working right at all.
My leading idea is to escape when ge is typed in operator mode and call the function with hopefully the right arguments. Then set a mark on the starting position, move to the left one column (to ensure that it would work with repeated movements, even though this is operator mode only), move the set amount of ges and then move one column right (my main goal here) and delete till the set mark.
If anybody could point out the errors I've made, it would be greatly appreciated!
function! FixGE(count, operator)
exe 'normal m['
exe 'normal h'
exe count.'normal ge'
exe 'normal l'
exe operator.'normal `['
endfunction
onoremap ge <esc>:call FixGE(v:prevcount, v:operator)<cr>
This is a little closer (untested):
function! FixGE(count, operator)
exe 'normal m['
exe 'normal h'
exe 'normal ' . a:count . 'ge'
exe 'normal l'
exe 'normal ' . a:operator . '`['
endfunction
onoremap ge <esc>:call FixGE(v:prevcount, v:operator)<cr>
Function arguments have to use the :a prefix (or sigil, I think). I know that count is a reserved variable: :let count = 7 gives an error. I think it is OK to use it as a function argument, though.
I have also put :normal at the beginning of two lines, instead of in the middle.
Your example could be solved without changing the ge motion, by using the f and t operators:
dTo
dF (d + F + <space> )
If you still feels that you need to override the default behavior of ge you shoul check Ingo Karkat's plugin, CountJump : Create custom motions and text objects via repeated jumps:
DESCRIPTION
Though it is not difficult to write a custom movement (basically a :map
that executes some kind of search or jump) and a custom text-object (an
:omap that selects a range of text), this is too complex for a novice user
and often repetitive.
Related
Often when I'm using f to search for characters in the current line, I'll run into more occurrences of the character than I expected so highlighting each search match would be nice.
In the example below, let's say I'm starting at the beginning of the line and am trying to get to e in vowels. It would be helpful to highlight each of those occurrences so that I could get some context on the number of times to press ; after the initial search
# Here is a comment with a lot of vowels and I have passed it now
How does Vim's current implementation of f know how to wait for only a single character instead of a newline?
I would prefer to overwrite the builtin f functionality so I'm using a remap similar to this, but one of the problems is that it expects me to press enter at the end.
nnoremap f :call HighlightFSearches(input(''))<CR>
Currently have some issues with implementing my function HighlightFSearches as well, but one problem at a time.
Also, not really looking for a plugin and yes I know I can just do a search instead of using f but my brain seems to prefer going with f first in a lot of cases.
Update
Here's my final solution with much thanks to #filbranden below!
function! HighlightFSearches(cmd)
" Get extra character for the command.
let char = nr2char(getchar())
if char ==# ''
" Skip special keys: arrows, backspace...
return ''
endif
" Highlight 'char' on the current line.
let match_str = 'match Visual "\%' . line('.') . 'l' . char . '"'
execute match_str
" Finally, execute the original command with char appended to it
return a:cmd.char
endfunction
" highlight searches using 'f'
nnoremap <expr> f highlighting#HighlightFSearches('f')
nnoremap f<bs> <nop>
vnoremap <expr> f highlighting#HighlightFSearches('f')
vnoremap f<bs> <nop>
" highlight searches using 'F'
nnoremap <expr> F highlighting#HighlightFSearches('F')
nnoremap F<bs> <nop>
vnoremap <expr> F highlighting#HighlightFSearches('F')
vnoremap F<bs> <nop>
Note that I chose the Highlight Group used for visual selects. You could choose a different one or make your own too
The short answer is that you should use getchar() to get a single character from the user.
The long answer is that this gets somewhat complicated pretty quickly, since you need to deal with special keys and corner cases while handling getchar().
Note that getchar() may return a number (for a normal keypress, which you can convert to a character with nr2char()), or a string, starting with a special 0x80 byte for special keys (backspace, arrows, etc.)
A simplistic approach (but somewhat effective) is that running nr2char() on the strings returned for the special keys will return an empty string, so we can use that to skip those.
The next advice is that you can use <expr> in your mappings to return the new command as a string. That, together with non-recursive mappings, allow you to return the actual f command itself at the end of the function, so that part of emulating it is taken care of!
Finally, one more trick you might want to use is to create a "dummy" mapping for f followed by an invalid character. The fact that such a 2-character mapping exists makes it so that your f mapping won't trigger until a second character has been entered, and this will prevent Vim from moving the cursor to the last line while waiting for a character, making the f emulation more seamless.
Putting it all together:
function! HighlightFSearches(cmd)
" Get extra character for the command.
let char = nr2char(getchar())
if char ==# ''
" Skip special keys: arrows, backspace...
return ''
endif
" Here you'll want to highlight "char"
" on the current line.
" Finally, execute the original command.
return a:cmd.char
endfunction
nnoremap <expr> f HighlightFSearches('f')
nnoremap f<bs> <nop>
The function is written in a way that you can easily reuse it for F, t and T.
For highlighting the matches, you can either use :match (or :2match, :3match) or maybe you could set #/ and let 'hlsearch' do the highlighting...
You'll probably want to anchor the regexp on the current line, so only those matches are highlighted, see :help /\%l for what you can use for that.
Finally, you'll probably want to clear the highlighting if you move to a different line. Take a look at the CursorMoved event of autocmd for that purpose.
There are quite a few details to iron out, but hopefully this will clarify how to emulate the command part of getting the character to search for.
The short and sweet answer is to substitute input() for getchar()
Just another vim source code comment question here. I have this mapping for my python source code files:
map <C-C> <Home>i#<Esc>
imap <C-C> <Home>#<Esc> i
On Ctrl-C it puts # in the beginning if the line to comment it out. This improves productivity a lot. But when I want to uncomment lines, I have to do this manually, meaning going to the first character of each commented line and remove it. This is very annoying. At the first glance, I can just bind Home-x to some key, but I can occasionally remove an innocent space or something else in case I misshit and do this on line that has no # character at the beginning of it. I first try to do some replacement with :%s// for a single line, but that has an unwanted affect - it triggers a search and highlights 'pattern' in other lines. In case of a single # character it is a fail.
Can anybody suggest how to remove a specified character in the beginning of current line in case it present and do nothing, if not, without using pattern replacement?
I have created a simple function to Toggle comment in the line:
function! ToggleComment()
let l:pos = col('.')
if getline('.') =~ '\v(\s+|\t+)?#'
exec 'normal! _"_x'
let l:pos -= 1
else
exec 'normal! I#'
let l:pos += 1
endif
call cursor(line("."), l:pos)
endfunction
nnoremap <Leader>t :call ToggleComment()<CR>
inoremap <Leader>t <C-o>:call ToggleComment()<CR>
I recommend Tim Pope's plugin vim-commentary because is way more complete. But of course our idea gives you guys a glimpse how far we can get with vimscript.
Another approach, which does not need to save windowview and toggles comments in other languages can be seen here
Can anybody suggest how to remove a specified character in the beginning of current line in case it present and do nothing, if not, without using pattern replacement?
A solution would be (assuming your cursor is anywhere to the right of # when using the map):
map <c-c> mmF#x`m
A more general solution would be to use a substitution and histdel() to delete the last search pattern:
function! DelComment()
s/^\( *\)#/\1/
call histdel("search", -1)
let #/ = histget("search", -1)
endfunction
After executing the function (by selecting it and typing :#") you can map it to <c-c>:
map <silent> <c-c> mm:silent! call DelComment()<cr>`m
I like using marks around functions to retain the cursor position after executing the map. Feel free to remove mm and `m in the above map.
I sometime dream of a mark-selection feature in Vim, just like when you type:
ma
in normal mode, you can then hit, from anywhere:
`a
to get back to this place a.
I wish there were something like:
ma
in visual mode. This would save your visually selected area. And you would just type then, from any place in normal mode:
<someHeader>a
to enter visual mode back with this a selection.
Is there such a feature in Vim? Or an extension that looks like? Or an easy way to implement it?
I'm fairly certain there isn't any way to name selections similar to how marks or registers can be referenced.
The closest feature that I can think of is the gv command which enters Visual mode with the last previously selected block already re-selected. I find this to be a lot more convenient than having to manually re-selecting the same block of text more than once.
It should be possible to write a function using Vimscript that saves the start and ends of a visual block as marks that can be re-used to reselect a Visual block. I had a look to see if it was possible to use Vimscript to save the < and > marks as other (less ephemeral) marks but I didn't see anything.
I have written this which I believe is what you are asking for:
function! VisualMark()
call inputsave()
let registers = input("m")
call inputrestore()
exec "normal! `<m" . registers[0]
exec "normal! `>m" . registers[1]
endfun
function! GetVisualMark()
call inputsave()
let registers = input("`")
call inputrestore()
exec "normal! `" . registers[0]
if strlen(registers) > 1
exec "normal! v`" . registers[1]
endif
endfun
vnoremap m <esc>:call VisualMark()<cr>
nnoremap ` :call GetVisualMark()<cr>
they should behave in the same way as a regular mark except only in visual mode, and they both need two arguments.
First, to get a visually selected area saved, in visual mode with the block selected, hit the m key like you normally would. It will then let you type in some more text... here the function will expect 2 characters which are the two registers that the beginning and end marks will be saved to... so for example entering mab in visual mode and then pressing enter (I could not figure out a way to make it work without an enter), would create a mark in register a for the beginning of the block, and b would be the end of the block.
To reselect the block visually, go to visual mode and do the ` which is the same as a regular mark... this too will expect two characters
EDIT:
I have made the implementation better in my opinion. Before I had it so that to bring back the visually selected block you had to be in visual mode then hit the backtick (same button that you would use for a normal mark) button and then the 2 registers. Now you do it in normal mode... This means that the default use of the backtick now uses this function, but the function now checks how many registers you provide... so if you only use one register, it will only take you back to that one. if you provide 2 registers, then it will visually select them... if you provide more than 2, it will only use the first 2.
The enter key is still required to enter in your selection though. It is probably better this way with the new implementation anyways.
For those that want to see, or use the old implementation this is it:
function! VisualMark()
call inputsave()
let registers = input("m")
call inputrestore()
exec "normal! `<m" . registers[0]
exec "normal! `>m" . registers[1]
endfun
function! GetVisualMark()
call inputsave()
let registers = input("`")
call inputrestore()
exec "normal! `" . registers[0]
exec "normal! v`" . registers[1]
endfun
vnoremap m <esc>:call VisualMark()<cr>
vnoremap ` <esc>:call GetVisualMark()<cr>
Here is the kind solution of Steven Hall, along with some modifications I needed for the visual marks not to overwrite regular registers.
https://github.com/iago-lito/vim-visualMarks
It is a small vimScript allowing one to mark a visually selected area by typing, in visual mode:
ma
(for mark a)
Then retrieve it later from any place by typing, in normal mode:
<a
It does still need some basic improvements, but now anyone can try, improve and share. Thank you Steven for having launched the process :)
Is there a way to unfold code when going to a line number? For instance I type :35 where line 35 is folded, then I have to unfold that section manually to actually get to that line. I would like to type :35and have that code then unfolded automatically and my cursor put on line 35 without any further key presses.
If you use the 35G command instead of :35, you can achieve this with the following mapping:
"[count]G Also open fold under cursor when supplying [count] (i.e.
" jumping to a particular line, not the end of the
" buffer). Use [count]|gg| if you don't want this.
nnoremap <expr> G (v:count ? 'Gzv' : 'G')
For :35 itself, this would be hard to achieve. You would have to intercept the <CR> via a :cmap <expr>, check the typed command via getcmdtype() and getcmdline(), and, if it's a number, manipulate the command, i.e. append normal! zv to it; like this:
cmap <expr> <CR> getcmdtype() == ':' && getcmdline() =~ '^\d\+$' ? 'normal! zv<CR>' : '<CR>'
zv. From :help zv:
View cursor line: Open just enough folds to make the line in
which the cursor is located not folded.
While this command could probably be triggered automatically in some way, I have not come across it yet. Using the command as-is has served me well, though.
Define a new command mapping. In this example, I chose \gz:
:nmap \gz gg<Bar>zO
I'm using vim to maintain a weblog of what I do through the day (what commands I used to generate output, etc..), and sometimes I need to copy-paste strings that have special html characters in them. Is there a way to make an "html-paste" mode that will (for instance) convert < to <?
There are a few small functions here if someone feels like modifying them to accept a range, then provide a mapping which passes the [ and ] marks to act on the last pasted text.
Actually, after looking a bit, you don't need to modify the functions from the vim tip at all. If a function doesn't explicitly pass the range option, the function is called once for each line of the given range. This means that all you need to do is call the function with a range.
A couple useful examples are below. The first calls HtmlEscape() for each line in the last pasted text, the second does the same but for each line in a visually selected block. Add this to your .vimrc:
nnoremap <Leader>h :'[,']call HtmlEscape()<CR>
vnoremap <Leader>h :call HtmlEscape()<CR>
function HtmlEscape()
silent s/&/\&/eg
silent s/</\</eg
silent s/>/\>/eg
endfunction
Obviously if you want more things replaced you'd have to add them; there are many on the linked wiki page.
For simple XML encoding I use Tim Pope's unimpaired.vim.
[x followed by a motion
visually select the text then [x
[xx to encode the current line
To encode the just pasted text:
`[[x`]
Explanation:
`[ and `] are marks set by the boundaries of a change or a yank. `[ at the beginning and `] at the end.
First move to the start of the just pasted text with `[
Next execute [x to encode the following motion
By using `] the motion will be all the text between the current cursor location to the end of the pasted text.
Optionally you can wrap this up into a mapping
nmap <leader>x `[[x`]
More information:
:h mark-motion
:h `[
You can use a mapping that will convert register value and then paste it:
python import xmlrpclib, vim
function! EscapeHTML(str)
try " Force any error to be an exception "
let d={}
python vim.eval("extend(d, {'xml': '"+xmlrpclib.escape(vim.eval("a:str")).replace("'", "''")+"'})")
return d.xml
endtry
endfunction
function! s:PasteHTML()
return "\"=EscapeHTML(getreg(".string(v:register)."))\np"
endfunction
nnoremap <expr> ,p <SID>PasteHTML()
Requires vim with python support and python installed. xmlrpclib packages comes with python. If you don't want python, replace EscapeHTML function. With this script ,p will work just as p except for converting its input.