Support for [[, ]], [], ][, [m, ]m, ]M, [M of ftplugins
Closed this issue · 30 comments
More and more ftplugins in $VIMRUNTIME or third-party sources add ft-specific vertical motion mappings [[, ]], [], ][, [m, ]m, ]M, [M
.
The builtin mappings work for curly bracket based languages such as java and c++:
// cpp written in K&R style
// - curly braces for namespaces, classes and funcs on separate lines,
// - but 'egyptian style braces' for everything else such as if, while, switch, etc.
class Scalar
{
friend Scalar operator*(const Scalar&, const Scalar&);
}; // []
class Matrix
{ // [[
friend Vector operator-(const Vector&, const Vector&)
{
// ...
} // [M
friend Vector operator*(const Matrix&, const Vector&)
{ // [m
while x>0 do { // [{
// CURSOR <--------------------------------------
} // ]}
} // ]M
friend Vector operator+(const Vector&, const Vector&)
{ // ]m
// ...
}
}; // ][
class Vector
{ // ]]
friend Vector operator*(const Matrix&, const Vector&);
};
However, if you do not follow certain coding style, they won't work as expected. Also your cursor is not located on the actual start of the block. See for example the suggestion in :h section
, the c++ plugin lh-cpp or the discussion for c# on stackoverflow.
For javascript there exists the plugin https://github.com/okcompute/vim-javascript-motions which does not follow the conventions of the builtin square bracket commands and defines a different set (overwriting some other builtin mappings such as [I,]I
& [D,]D
and uses ]],[[
with a different meaning).
Consider following languages with ftplugins which have overwritten the builtin ones:
$VIMRUNTIME/ftplugin/*
python
([[, ]], [], ][, [m, ]m, ]M, [M
) https://github.com/vim/vim/blob/master/runtime/ftplugin/python.vim#L35-L60ruby
([[, ]], [], ][, [m, ]m, ]M, [M
) https://github.com/vim/vim/blob/master/runtime/ftplugin/ruby.vim#L165-L181vim
([[, ]], [], ][
)
https://github.com/vim/vim/blob/master/runtime/ftplugin/vim.vim#L40-L47eiffel
([[, ]], [m, ]m
) https://github.com/vim/vim/blob/master/runtime/ftplugin/eiffel.vim#L58-L77rust
([[
,]]
)
https://github.com/vim/vim/blob/master/runtime/ftplugin/rust.vim#L107-L112
Third Party
-
erlang
([[, ]], [], ][, [m, ]m, ]M, [M
)
https://github.com/edkolev/erlang-motions.vim -
vimtex
([[, ]], [], ][
)[],][
are used differently not for end but move before the line withsection
specifier.
https://github.com/lervag/vimtex -
markdown
([[, ]], [], ][
)
https://github.com/plasticboy/vim-markdown/blob/master/ftplugin/markdown.vim#L624-L627 -
perl
([m
,[M
,]m
and]M
)
https://github.com/catalinciurea/perl-nextmethod -
vim
([m
,[M
,]m
and]M
)This is duplicated from $VIMRUNTIME/ftplugin/vim.vim, using the (IMHO) correct mappings
"move to method", not "move to paragraph") -
go
([[
,]]
)
https://github.com/fatih/vim-go/blob/master/ftplugin/go.vim#L50-L58
Is it possible to make them work together with repmo-vim
? IMHO the pinky finger action is the weakness of these mappings.
I've looked at the python script.
I can tell you, it can be done, but it's cumbersome. It's less than ideal to use the mapping definitions as-is. Ideally, mappings come in form of <Plug>
mappings.
Problem:
I would like to enhance the mappings outside of the original scripts, but this requires the <SID>
to be known.
Anyway ...
Basically, one can just make the original right-hand-sides arguments of repmo#Key() (quoted with string()):
" original mappings from the Python ft-plugin (as of Vim 7.4.2251):
" nnoremap <silent> <buffer> ]] :call <SID>Python_jump('n', '\v%$\|^(class\|def)>', 'W')<cr>
" nnoremap <silent> <buffer> [[ :call <SID>Python_jump('n', '\v^(class\|def)>', 'Wb')<cr>
" repmo mappings (<silent> omitted, but could be added):
nnoremap <expr><buffer> ]] repmo#Key(':<C-U>call <SID>Python_jump(''n'', ''\v%$\|^(class\|def)>'', ''W'')<cr>', ':<C-U>call <SID>Python_jump(''n'', ''\v^(class\|def)>'', ''Wb'')<cr>')
nnoremap <expr><buffer> [[ repmo#Key(':<C-U>call <SID>Python_jump(''n'', ''\v^(class\|def)>'', ''Wb'')<cr>', ':<C-U>call <SID>Python_jump(''n'', ''\v%$\|^(class\|def)>'', ''W'')<cr>')
" script-local function from ftplugin\python.vim copied for convience (with bug fix):
fun! <SID>Python_jump(mode, motion, flags) range
let cnt = v:count1
if a:mode == 'x'
normal! gv
endif
normal! 0
mark '
while cnt > 0
call search(a:motion, a:flags)
let cnt = cnt - 1
endwhile
normal! ^
endfun
Also I've added <C-U>
to eat the count.
The Python script I used for my example contained a bug: the v:count1
is always 1
, because a previous :normal
resets the count (happens to be the same in the current version). Looks like people don't use the count much -- but repmo doesn't take action at all without given count.
Thanks for your response.
vimtex
The plugin vimtex uses <plug>
mappings
https://github.com/lervag/vimtex/blob/master/autoload/vimtex/motion.vim#L21-L36
https://github.com/lervag/vimtex/blob/master/autoload/vimtex.vim#L405-L416
and therefore, if I got it right, following setup for latex files should be fine:
" repeat the last [count]motion or the last zap-key:
map <expr> ; repmo#LastKey(';')|sunmap ;
map <expr> , repmo#LastRevKey(',')|sunmap ,
" add these mappings when repeating with `;' or `,':
noremap <expr> f repmo#ZapKey('f')|sunmap f
noremap <expr> F repmo#ZapKey('F')|sunmap F
noremap <expr> t repmo#ZapKey('t')|sunmap t
noremap <expr> T repmo#ZapKey('T')|sunmap T
noremap <expr> <C-E> repmo#SelfKey('<C-E>', '<C-Y>')
noremap <expr> <C-Y> repmo#SelfKey('<C-Y>', '<C-E>')
nnoremap <expr><buffer> ]] repmo#Key('<plug>(vimtex-]])', '<plug>(vimtex-[[)')
nnoremap <expr><buffer> [[ repmo#Key('<plug>(vimtex-[[)', '<plug>(vimtex-]])')
nnoremap <expr><buffer> ][ repmo#Key('<plug>(vimtex-][)', '<plug>(vimtex-[])')
nnoremap <expr><buffer> [] repmo#Key('<plug>(vimtex-[])', '<plug>(vimtex-][)')
\documentclass{article}
\begin{document}
\chapter{section 1)
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
no sea takimata sanctus est Lorem ipsum dolor sit amet.
\chapter{section 2)
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
no sea takimata sanctus est Lorem ipsum dolor sit amet.
\chapter{section 3)
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
no sea takimata sanctus est Lorem ipsum dolor sit amet.
\chapter{section 4)
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
no sea takimata sanctus est Lorem ipsum dolor sit amet.
\chapter{section 5)
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
no sea takimata sanctus est Lorem ipsum dolor sit amet.
\end{document}
However, repeating ]]
with ;
and ,
does not work. What do I still miss?
Python
I guess I would hope to convince the new maintainer of the python script to use <plug>
mappings. Unfortunately, all ftplugin files are affected by this:
/Applications/MacVim.app/Contents/Resources/vim/runtime/ftplugin
❯ rg '<buffer> ]]'
abaqus.vim
70:noremap <silent><buffer> ]] /^\*\a<CR>:nohlsearch<CR>
85:let b:undo_ftplugin .= "|unmap <buffer> [[|unmap <buffer> ]]"
cobol.vim
64: \ " | sil! exe 'unmap <buffer> ]]'" .
84: noremap <silent> <buffer> ]] m':call search('\c^\%(\s*\<Bar>.\{6\}\s\+\)\zs[A-Za-z0-9-]\+\s\+\%(division\<Bar>section\)\.','W')<CR>
context.vim
63:nnoremap <silent><buffer> ]] :<C-U>call <SID>move_around(v:count1, "beginsection", "W", v:false) <CR>
64:vnoremap <silent><buffer> ]] :<C-U>call <SID>move_around(v:count1, "beginsection", "W", v:true) <CR>
eiffel.vim
58: nnoremap <silent> <buffer> ]] :<C-U>call <SID>DoMotion(sections, v:count1, 'W')<CR>
59: xnoremap <silent> <buffer> ]] :<C-U>exe "normal! gv"<Bar>call <SID>DoMotion(sections, v:count1, 'W')<CR>
88: \ "| silent! execute 'unmap <buffer> [[' | silent! execute 'unmap <buffer> ]]'" .
hamster.vim
38:noremap <silent><buffer> ]] :call search('^\s*sub\>', "W")<CR>
j.vim
49:sunmap <buffer> ]]
60:let b:undo_ftplugin .= ' | silent! execute "unmap <buffer> ]]"'
mf.vim
45:nnoremap <silent><buffer> ]] :<C-U>call <SID>move_around(v:count1, "beginsection", "W", v:false) <CR>
46:vnoremap <silent><buffer> ]] :<C-U>call <SID>move_around(v:count1, "beginsection", "W", v:true) <CR>
mp.vim
54:nnoremap <silent><buffer> ]] :<C-U>call <SID>move_around(v:count1, "beginsection", "W", v:false) <CR>
55:vnoremap <silent><buffer> ]] :<C-U>call <SID>move_around(v:count1, "beginsection", "W", v:true) <CR>
python.vim
35:execute "nnoremap <silent> <buffer> ]] :call <SID>Python_jump('n', '". b:next_toplevel."', 'W')<cr>"
44:execute "onoremap <silent> <buffer> ]] :call <SID>Python_jump('o', '". b:next_toplevel."', 'W')<cr>"
53:execute "xnoremap <silent> <buffer> ]] :call <SID>Python_jump('x', '". b:next_toplevel."', 'W')<cr>"
ruby.vim
175: nnoremap <silent> <buffer> ]] :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>','rubyModule\<Bar>rubyClass','','n')<CR>
179: xnoremap <silent> <buffer> ]] :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>','rubyModule\<Bar>rubyClass','','v')<CR>
184: \."| sil! exe 'unmap <buffer> [[' | sil! exe 'unmap <buffer> ]]' | sil! exe 'unmap <buffer> []' | sil! exe 'unmap <buffer> ]['"
rust.vim
108:nnoremap <silent> <buffer> ]] :call rust#Jump('n', 'Forward')<CR>
110:xnoremap <silent> <buffer> ]] :call rust#Jump('v', 'Forward')<CR>
112:onoremap <silent> <buffer> ]] :call rust#Jump('o', 'Forward')<CR>
177: \|nunmap <buffer> ]]
179: \|xunmap <buffer> ]]
181: \|ounmap <buffer> ]]
vim.vim
42:nnoremap <silent><buffer> ]] m':call search('^\s*fu\%[nction]\>', "W")<CR>
43:vnoremap <silent><buffer> ]] m':<C-U>exe "normal! gv"<Bar>call search('^\s*fu\%[nction]\>', "W")<CR>
zimbu.vim
139:nnoremap <silent> <buffer> ]] m`:call ZimbuGoEndBlock()<CR>
<Plug>
mappings require enabled remapping:
nmap <expr><buffer> ]] repmo#Key('<plug>(vimtex-]])', '<plug>(vimtex-[[)')
Thanks for the help. However, it is still not working. I have also moved the nmap
into ~/.vim/after/ftplugins/tex.vim
:
nmap <expr><buffer> ]] repmo#Key('<plug>(vimtex-]])', '<plug>(vimtex-[[)')
nmap <expr><buffer> [[ repmo#Key('<plug>(vimtex-[[)', '<plug>(vimtex-]])')
nmap <expr><buffer> ][ repmo#Key('<plug>(vimtex-][)', '<plug>(vimtex-[])')
nmap <expr><buffer> [] repmo#Key('<plug>(vimtex-[])', '<plug>(vimtex-][)')
And running :verbose nmap ]]
within the tex file indicates the correct definition
n ]] @repmo#Key('<Plug>(vimtex-]])', '<Plug>(vimtex-[[)')
Last set from ~/.vim/after/ftplugin/tex.vim
and :verbose nmap ;
returns
nox; repmo#LastKey(';')
Last set from ~/.vim/vimrc
But the movement with ;
and ,
does not work.
Maybe check if a count works in the first place?
The implementation functions of (vimtex-]]) and (vimtex-[[) don't make use of the count.
Or: if a count is given, it's used incorrectly.
Indeed, using 1]]
and then ;
and ,
works!
So vimtex must be fixed. Can you describe what the author of vimtex should do to fix it?
1st: also use :<C-U>
with Normal mode mappings and/or define functions with range
.
2nd: do something with the count.
... make sure to ask for v:count
or v:count1
before the first :normal
.
Btw: actually, it doesn't make sense to define <Plug>
mappings with <buffer>
. Because: <Plug>
mappings should have distinct names (and usually have) and when there are many ft=tex
buffers, then these can share the same global <Plug>
mappings. Also makes it slightly easier to redefine the mappings for debugging (no reloading of all buffers necessary). But this is just my opinion.
To the original issue: when it's ok to have repmo mappings inside the original ftplugin script, then I'd prefer <SID>
mappings over <Plug>
mappings (because the <Plug>
mappings are of no use outside).
I guess not everyone wants to use the plugin repmo
and therefore the original ftplugin scripts should ideally written in a way that one can facilitate <plug>
mappings in the vimrc of a vim user.
There is one thing I do not like: when all original ftplugin scripts are updated to use mappings with support for counts, the user has to define nmap <expr><buffer> ]] repmo#Key('...
for every language individually. Is there any chance to generalize this? So the user can enable this for all languages which have those mappings or will get those mappings.
Then I'd consider to have a global mapping
nmap <expr> ]] repmo#Key('<Plug>(motion-]])')
and then let each ftplugin define a different nnoremap <buffer> <Plug>(motion-]]) ...
.
Bonus: the user can define a global fallback motion for <Plug>(motion-]])
.
Variant: The original (maybe global) <Plug>
mapping could even be kept intact by making <Plug>(motion-]])
refer to it.
I haven't yet managed to get your python suggestion to work.
I have changed $VIMRUNTIME/ftplugin/python.vim
as following:
diff --git a/python.vim.bk b/python.vim
index d52a338..a1c275f 100644
--- a/python.vim.bk
+++ b/python.vim
@@ -32,56 +32,44 @@ let b:prev='\v^\s*(class\|def\|async def)>'
let b:next_end='\v\S\n*(%$\|^\s*(class\|def\|async def)\|^\S)'
let b:prev_end='\v\S\n*(^\s*(class\|def\|async def)\|^\S)'
-execute "nnoremap <silent> <buffer> ]] :call <SID>Python_jump('n', '". b:next_toplevel."', 'W')<cr>"
-execute "nnoremap <silent> <buffer> [[ :call <SID>Python_jump('n', '". b:prev_toplevel."', 'Wb')<cr>"
-execute "nnoremap <silent> <buffer> ][ :call <SID>Python_jump('n', '". b:next_endtoplevel."', 'W', 0)<cr>"
-execute "nnoremap <silent> <buffer> [] :call <SID>Python_jump('n', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
-execute "nnoremap <silent> <buffer> ]m :call <SID>Python_jump('n', '". b:next."', 'W')<cr>"
-execute "nnoremap <silent> <buffer> [m :call <SID>Python_jump('n', '". b:prev."', 'Wb')<cr>"
-execute "nnoremap <silent> <buffer> ]M :call <SID>Python_jump('n', '". b:next_end."', 'W', 0)<cr>"
-execute "nnoremap <silent> <buffer> [M :call <SID>Python_jump('n', '". b:prev_end."', 'Wb', 0)<cr>"
-
-execute "onoremap <silent> <buffer> ]] :call <SID>Python_jump('o', '". b:next_toplevel."', 'W')<cr>"
-execute "onoremap <silent> <buffer> [[ :call <SID>Python_jump('o', '". b:prev_toplevel."', 'Wb')<cr>"
-execute "onoremap <silent> <buffer> ][ :call <SID>Python_jump('n', '". b:next_endtoplevel."', 'W', 0)<cr>"
-execute "onoremap <silent> <buffer> [] :call <SID>Python_jump('n', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
-execute "onoremap <silent> <buffer> ]m :call <SID>Python_jump('o', '". b:next."', 'W')<cr>"
-execute "onoremap <silent> <buffer> [m :call <SID>Python_jump('o', '". b:prev."', 'Wb')<cr>"
-execute "onoremap <silent> <buffer> ]M :call <SID>Python_jump('o', '". b:next_end."', 'W', 0)<cr>"
-execute "onoremap <silent> <buffer> [M :call <SID>Python_jump('o', '". b:prev_end."', 'Wb', 0)<cr>"
-
-execute "xnoremap <silent> <buffer> ]] :call <SID>Python_jump('x', '". b:next_toplevel."', 'W')<cr>"
-execute "xnoremap <silent> <buffer> [[ :call <SID>Python_jump('x', '". b:prev_toplevel."', 'Wb')<cr>"
-execute "xnoremap <silent> <buffer> ][ :call <SID>Python_jump('n', '". b:next_endtoplevel."', 'W', 0)<cr>"
-execute "xnoremap <silent> <buffer> [] :call <SID>Python_jump('n', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
-execute "xnoremap <silent> <buffer> ]m :call <SID>Python_jump('x', '". b:next."', 'W')<cr>"
-execute "xnoremap <silent> <buffer> [m :call <SID>Python_jump('x', '". b:prev."', 'Wb')<cr>"
-execute "xnoremap <silent> <buffer> ]M :call <SID>Python_jump('x', '". b:next_end."', 'W', 0)<cr>"
-execute "xnoremap <silent> <buffer> [M :call <SID>Python_jump('x', '". b:prev_end."', 'Wb', 0)<cr>"
-
if !exists('*<SID>Python_jump')
- fun! <SID>Python_jump(mode, motion, flags, ...) range
- let l:startofline = (a:0 >= 1) ? a:1 : 1
-
- if a:mode == 'x'
- normal! gv
- endif
-
- if l:startofline == 1
- normal! 0
- endif
-
- let cnt = v:count1
- mark '
- while cnt > 0
- call search(a:motion, a:flags)
- let cnt = cnt - 1
- endwhile
+ function! <SID>Python_jump(mode, motion, flags) range
+ let cnt = v:count1
+ if a:mode == 'x'
+ normal! gv
+ endif
+ normal! 0
+ mark '
+ while cnt > 0
+ call search(a:motion, a:flags)
+ let cnt = cnt - 1
+ endwhile
+ normal! ^
+ endfunction
+endif
- if l:startofline == 1
- normal! ^
+for mode in ['n', 'o', 'x']
+ execute mode."noremap <silent> <plug>[M-motion :<C-U>call <SID>Python_jump('".mode."', '". b:prev_end."', 'Wb', 0)<cr>"
+ execute mode."noremap <silent> <plug>[[-motion :<C-U>call <SID>Python_jump('".mode."', '". b:prev_toplevel."', 'Wb')<cr>"
+ execute mode."noremap <silent> <plug>[]-motion :<C-U>call <SID>Python_jump('".mode."', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
+ execute mode."noremap <silent> <plug>[m-motion :<C-U>call <SID>Python_jump('".mode."', '". b:prev."', 'Wb')<cr>"
+ execute mode."noremap <silent> <plug>]M-motion :<C-U>call <SID>Python_jump('".mode."', '". b:next_end."', 'W', 0)<cr>"
+ execute mode."noremap <silent> <plug>][-motion :<C-U>call <SID>Python_jump('".mode."', '". b:next_endtoplevel."', 'W', 0)<cr>"
+ execute mode."noremap <silent> <plug>]]-motion :<C-U>call <SID>Python_jump('".mode."', '". b:next_toplevel."', 'W')<cr>"
+ execute mode."noremap <silent> <plug>]m-motion :<C-U>call <SID>Python_jump('".mode."', '". b:next."', 'W')<cr>"
+endfor
+
+if !exists("no_plugin_maps") && !exists("no_python_maps")
+ for key1 in ['[', ']']
+ for key2 in ['M', '[', ']', 'm']
+ let keys = key1.key2
+ if !hasmapto('<Plug>'.keys.'-motion')
+ for mode in ['n', 'o', 'x']
+ execute mode."map ".keys." <plug>".keys."-motion"
+ endfor
endif
- endfun
+ endfor
+ endfor
endif
if has("browsefilter") && !exists("b:browsefilter")
My ~/.vim/after/ftplugin/python.vim
looks like
for mode in ['n', 'o', 'x']
execute mode."map <expr><buffer> ]] repmo#SelfKey('<plug>]]-motion', '<plug>[[-motion')"
execute mode."map <expr><buffer> [[ repmo#SelfKey('<plug>[[-motion', '<plug>]]-motion')"
execute mode."map <expr><buffer> ][ repmo#SelfKey('<plug>][-motion', '<plug>[]-motion')"
execute mode."map <expr><buffer> [] repmo#SelfKey('<plug>[]-motion', '<plug>][-motion')"
execute mode."map <expr><buffer> ]m repmo#SelfKey('<plug>]m-motion', '<plug>[m-motion')"
execute mode."map <expr><buffer> [m repmo#SelfKey('<plug>[m-motion', '<plug>]m-motion')"
execute mode."map <expr><buffer> ]M repmo#SelfKey('<plug>]M-motion', '<plug>[M-motion')"
execute mode."map <expr><buffer> [M repmo#SelfKey('<plug>[M-motion', '<plug>]M-motion')"
endfor
the working setup for tex files with 1]]
, and then ;
and ,
in ~/.vim/after/ftplugin/tex.vim
nmap <expr><buffer> ]] repmo#Key('<plug>(vimtex-]])', '<plug>(vimtex-[[)')
nmap <expr><buffer> [[ repmo#Key('<plug>(vimtex-[[)', '<plug>(vimtex-]])')
nmap <expr><buffer> ][ repmo#Key('<plug>(vimtex-][)', '<plug>(vimtex-[])')
nmap <expr><buffer> [] repmo#Key('<plug>(vimtex-[])', '<plug>(vimtex-][)')
and finally my vimrc:
map <expr> ; repmo#LastKey(';')|sunmap ;
map <expr> , repmo#LastRevKey(',')|sunmap ,
" add these mappings when repeating with `;' or `,':
noremap <expr> f repmo#ZapKey('f')|sunmap f
noremap <expr> F repmo#ZapKey('F')|sunmap F
noremap <expr> t repmo#ZapKey('t')|sunmap t
noremap <expr> T repmo#ZapKey('T')|sunmap T
noremap <expr> <C-E> repmo#SelfKey('<C-E>', '<C-Y>')
noremap <expr> <C-Y> repmo#SelfKey('<C-Y>', '<C-E>')
Verifying the setup in a python file:
x ]] @repmo#SelfKey('<Plug>]]-motion', '<Plug>[[-motion')
Last set from ~/.vim/after/ftplugin/python.vim
o ]] @repmo#SelfKey('<Plug>]]-motion', '<Plug>[[-motion')
Last set from ~/.vim/after/ftplugin/python.vim
n ]] @repmo#SelfKey('<Plug>]]-motion', '<Plug>[[-motion')
Last set from ~/.vim/after/ftplugin/python.vim
n ]] <Plug>]]-motion
Last set from /Applications/MacVim.app/Contents/Resources/vim/runtime/ftplugin/python.vim
nox; repmo#LastKey(';')
Last set from ~/.vim/vimrc
And counts also work! The remaining issue seems to be getting repmo to work.
Yep, it's not yet resembling the idea. The idea was (here only for [[
in Normal mode):
One global, uniquely named Python specific <Plug>
mapping, eg :nnoremap <Plug>(python-[[) :<C-U>call PythonNormalMotion('[[')<CR>
(the right-hand-side is some dummy implementation). It's what we have right now in ftplugin scripts. (It almost doesn't matter if it's global or buffer-local, but all the buffer-local copies are wasted imho). And instead of a <Plug>
mapping, this could also be an <SID>
mapping (eg <SID>[[
), as the interface is <Plug>[[-motion
below.
One buffer-local <Plug>
mapping, canonically named <Plug>[[-motion
(I even like that name for now, it cannot be confused with a filetype mapping): :nmap <buffer> <Plug>[[-motion <Plug>(python-[[)
. Each forecoming ftplugin is nice enough to define this mapping in a similar way. repmo is not involved yet.
The user then has the following options for his vimrc:
Map :nmap [[ <Plug>[[-motion
. This will make [[
do the right thing if supported by the ftplugin. He might also want to define a global fallback mapping: :nnoremap <Plug>[[-motion [[
.
When the user wants to use repmo he can define :nmap <expr> [[ repmo#Key('<Plug>[[-motion', '<Plug>]]-motion')
.
Btw: do not use :nmap <expr> [[ repmo#SelfKey('<Plug>[[-motion', '<Plug>]]-motion')
. SelfKey is only for :nnoremap <expr> [[ repmo#SelfKey('[[', ']]')
, ie when the left-hand-side occurs in the argument (maybe it wasn't clear enough from the readme example).
Modified ftplugin/python.vim, this also introduces g:no_bracket_maps
.
User's supporting vimrc settings
Thank you very much!
I can repeat now 1]]
with ;
and ,
in a python file. Afterwards I can switch to 1]m
and repeat.
Also fFtT
work with ;
and ,
.
However, I was hoping I do not need to add a count of 1
.
Also your suggested mappings for <C-E/Y>
in the README.md cannot be repeated:
noremap <expr> <C-E> repmo#SelfKey('<C-E>', '<C-Y>')
noremap <expr> <C-Y> repmo#SelfKey('<C-Y>', '<C-E>')
Hi,
I could resolve my issue when providing no counts by changing v:count
to v:count1
in autoload/repmo.vim
:
diff --git a/repmo.vim.bk b/repmo.vim
index 7923657..bb40ac1 100644
--- a/repmo.vim.bk
+++ b/repmo.vim
@@ -11,15 +11,15 @@ if !exists("s:last")
endif
func! repmo#Key(key, revkey) "{{{
- if v:count >= 1
- call extend(s:last, {'repmo': 1, 'key': a:key, 'revkey': a:revkey, 'count': v:count, 'remap': 1})
+ if v:count1 >= 1
+ call extend(s:last, {'repmo': 1, 'key': a:key, 'revkey': a:revkey, 'count': v:count1, 'remap': 1})
endif
return a:key
endfunc "}}}
func! repmo#SelfKey(key, revkey) "{{{
- if v:count >= 1
- call extend(s:last, {'repmo': 1, 'key': a:key, 'revkey': a:revkey, 'count': v:count, 'remap': 0})
+ if v:count1 >= 1
+ call extend(s:last, {'repmo': 1, 'key': a:key, 'revkey': a:revkey, 'count': v:count1, 'remap': 0})
exec "noremap <Plug>(repmo-lastkey) \<C-V>". a:key
exec "noremap <Plug>(repmo-lastrevkey) \<C-V>". a:revkey
endif
@@ -32,8 +32,8 @@ func! repmo#LastKey(zaprepkey) "{{{
return a:zaprepkey
endif
let lastkey = get(s:last, 'remap', 1) ? get(s:last, 'key', '') : "\<Plug>(repmo-lastkey)"
- if v:count >= 1
- let s:last.count = v:count
+ if v:count1 >= 1
+ let s:last.count = v:count1
return lastkey
else
return get(s:last, 'count', ''). lastkey
@@ -46,8 +46,8 @@ func! repmo#LastRevKey(zaprepkey) "{{{
return a:zaprepkey
endif
let lastrevkey = get(s:last, 'remap', 1) ? get(s:last, 'revkey', '') : "\<Plug>(repmo-lastrevkey)"
- if v:count >= 1
- let s:last.count = v:count
+ if v:count1 >= 1
+ let s:last.count = v:count1
return lastrevkey
else
return get(s:last, 'count', ''). lastrevkey
However, this means if I use counts e.g. 2fe
, the count is forgotten when repeating.
I did intentionally put "... for which a count was given" in the description line.
Any chance that you make this optional?
First thing I'd try is (in the ftplugin) (untested) map <buffer><expr><script> <Plug>[m-motion v:count==0 ? '1<SID>([m)' : '<SID>([m)'
. (not working)
I think it will cause trouble. I will not attempt to change repmo now to support this.
Too bad. I would have appreciated it. To be honest I use rarely counts and prefer to repeat the motion.
Also I am not entirely convinced that your decision fits into the idea of the plugin repmo itself. I am a bit surprised that it seems to be rather difficult to implement this. Because from a naive point of view you just have to set the variable, which keeps track of the count, to one if none is given.
Weird, I remembered problems, but they somehow vanished.
Ok, then put :let g:repmo_require_count = 0
in your vimrc and enjoy the update.
Thanks for your reconsideration. If this works reliable, I see no reason to make this the default behavior. But I don't mind adding a line to my vimrc to disable requiring a count.
I just noticed that your suggested python file given in the gist misses for the normal mode commands <C-U>
:
- execute "nnoremap <silent> <SID>(]]) :call <SID>Python_jump('n', '". b:next_toplevel."', 'W')<cr>"
- execute "nnoremap <silent> <SID>([[) :call <SID>Python_jump('n', '". b:prev_toplevel."', 'Wb')<cr>"
- execute "nnoremap <silent> <SID>(][) :call <SID>Python_jump('n', '". b:next_endtoplevel."', 'W', 0)<cr>"
- execute "nnoremap <silent> <SID>([]) :call <SID>Python_jump('n', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
- execute "nnoremap <silent> <SID>(]m) :call <SID>Python_jump('n', '". b:next."', 'W')<cr>"
- execute "nnoremap <silent> <SID>([m) :call <SID>Python_jump('n', '". b:prev."', 'Wb')<cr>"
- execute "nnoremap <silent> <SID>(]M) :call <SID>Python_jump('n', '". b:next_end."', 'W', 0)<cr>"
- execute "nnoremap <silent> <SID>([M) :call <SID>Python_jump('n', '". b:prev_end."', 'Wb', 0)<cr>"
+ execute "nnoremap <silent> <SID>(]]) :<C-U>call <SID>Python_jump('n', '". b:next_toplevel."', 'W')<cr>"
+ execute "nnoremap <silent> <SID>([[) :<C-U>call <SID>Python_jump('n', '". b:prev_toplevel."', 'Wb')<cr>"
+ execute "nnoremap <silent> <SID>(][) :<C-U>call <SID>Python_jump('n', '". b:next_endtoplevel."', 'W', 0)<cr>"
+ execute "nnoremap <silent> <SID>([]) :<C-U>call <SID>Python_jump('n', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
+ execute "nnoremap <silent> <SID>(]m) :<C-U>call <SID>Python_jump('n', '". b:next."', 'W')<cr>"
+ execute "nnoremap <silent> <SID>([m) :<C-U>call <SID>Python_jump('n', '". b:prev."', 'Wb')<cr>"
+ execute "nnoremap <silent> <SID>(]M) :<C-U>call <SID>Python_jump('n', '". b:next_end."', 'W', 0)<cr>"
+ execute "nnoremap <silent> <SID>([M) :<C-U>call <SID>Python_jump('n', '". b:prev_end."', 'Wb', 0)<cr>"
Also b:undo_ftplugin
should actually be prepended:
- let b:undo_ftplugin = (exists('b:undo_ftplugin') ? '|' : '').
+ let b:undo_ftplugin = (exists('b:undo_ftplugin') ? b:undo_ftplugin.'|' : '').
Also there were minor mistakes in the original file:
- execute "onoremap <silent> <SID>(][) :call <SID>Python_jump('n', '". b:next_endtoplevel."', 'W', 0)<cr>"
- execute "onoremap <silent> <SID>([]) :call <SID>Python_jump('n', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
+ execute "onoremap <silent> <SID>(][) :call <SID>Python_jump('o', '". b:next_endtoplevel."', 'W', 0)<cr>"
+ execute "onoremap <silent> <SID>([]) :call <SID>Python_jump('o', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
- execute "xnoremap <silent> <SID>(][) :call <SID>Python_jump('n', '". b:next_endtoplevel."', 'W', 0)<cr>"
- execute "xnoremap <silent> <SID>([]) :call <SID>Python_jump('n', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
+ execute "xnoremap <silent> <SID>(][) :call <SID>Python_jump('x', '". b:next_endtoplevel."', 'W', 0)<cr>"
+ execute "xnoremap <silent> <SID>([]) :call <SID>Python_jump('x', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
I've tried to keep the code block more compact by using a few for loops. My version of the new code lines in $VIMRUNTIME/ftplugin/python.vim look like.
if !exists('*<SID>Python_jump')
function! <SID>Python_jump(mode, motion, flags) range
let cnt = v:count1
if a:mode == 'x'
normal! gv
endif
normal! 0
mark '
while cnt > 0
call search(a:motion, a:flags)
let cnt = cnt - 1
endwhile
normal! ^
endfunction
endif
for mode in ['n', 'o', 'x']
if mode == 'n'
let ctrlu='<C-U>'
else
let ctrlu=''
endif
execute mode.'nnoremap <silent> <SID>(]]) :'.ctrlu."call <SID>Python_jump('".mode."', '". b:next_toplevel."', 'W')<cr>"
execute mode.'nnoremap <silent> <SID>([[) :'.ctrlu."call <SID>Python_jump('".mode."', '". b:prev_toplevel."', 'Wb')<cr>"
execute mode.'nnoremap <silent> <SID>(][) :'.ctrlu."call <SID>Python_jump('".mode."', '". b:next_endtoplevel."', 'W', 0)<cr>"
execute mode.'nnoremap <silent> <SID>([]) :'.ctrlu."call <SID>Python_jump('".mode."', '". b:prev_endtoplevel."', 'Wb', 0)<cr>"
execute mode.'nnoremap <silent> <SID>(]m) :'.ctrlu."call <SID>Python_jump('".mode."', '". b:next."', 'W')<cr>"
execute mode.'nnoremap <silent> <SID>([m) :'.ctrlu."call <SID>Python_jump('".mode."', '". b:prev."', 'Wb')<cr>"
execute mode.'nnoremap <silent> <SID>(]M) :'.ctrlu."call <SID>Python_jump('".mode."', '". b:next_end."', 'W', 0)<cr>"
execute mode.'nnoremap <silent> <SID>([M) :'.ctrlu."call <SID>Python_jump('".mode."', '". b:prev_end."', 'Wb', 0)<cr>"
endfor
for keys in ['[M', ']M', '[m', ']m', '[[', ']]', '[]', '][']
execute 'map <buffer> <Plug>'.keys.'-motion <SID>('.keys.')'
endfor
if !exists('no_plugin_maps') && !exists('no_python_maps')
for keys in ['[M', ']M', '[m', ']m', '[[', ']]', '[]', '][']
if !hasmapto('<plug>'.keys.'-motion')
execute 'map <buffer> '.keys.' <plug>'.keys.'-motion|sunmap <buffer> '.keys
let b:undo_ftplugin = (exists('b:undo_ftplugin') ? b:undo_ftplugin.'|' : '').'unmap <buffer> '.keys
endif
endfor
endif
If you think this is ok, I would send this to the current maintainer to get the changes into upstream.
Yes, we can work on a separate repo to modify the default and selected third-party ftplugins so that they can be repeated by repmo.
I just want to list here a few additional plugin/ftplugin motions which haven't been mentioned yet:
matchit
$VIMRUNTIME/pack/dist/opt/matchit/plugin/matchit.vim
adds following motions
nnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'n') <CR>
nnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'n') <CR>
vnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'v') <CR>m'gv``
vnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'v') <CR>m'gv``
onoremap <silent> % v:<C-U>call <SID>Match_wrapper('',1,'o') <CR>
onoremap <silent> g% v:<C-U>call <SID>Match_wrapper('',0,'o') <CR>
nnoremap <silent> [% :<C-U>call <SID>MultiMatch("bW", "n") <CR>
nnoremap <silent> ]% :<C-U>call <SID>MultiMatch("W", "n") <CR>
vmap [% <Esc>[%m'gv``
vmap ]% <Esc>]%m'gv``
onoremap <silent> [% v:<C-U>call <SID>MultiMatch("bW", "o") <CR>
onoremap <silent> ]% v:<C-U>call <SID>MultiMatch("W", "o") <CR>
They are useful to cycle through b:match_words
, e.g. if, elseif, else, endif
in vimscript:
if
...
elseif
...
elseif
...
else
...
endif
They also have to be modified.
matchup
Note, there is a new plugin called matchup which offers the same motion commands as matchit
which are written as <plug>
mappings, so you can already set them up to work together with repmo:
for keys in [ ['%', 'g%'], ['[%', ']%'] ]
execute 'map <expr> '.keys[0]." repmo#Key('<plug>(matchup-".keys[0].")', '<plug>(matchup-".keys[1].")')|sunmap ".keys[0]
execute 'map <expr> '.keys[1]." repmo#Key('<plug>(matchup-".keys[1].")', '<plug>(matchup-".keys[0].")')|sunmap ".keys[1]
endfor
Move to next comment ]"
(sql/vim), ]#
(hamster), ]-
(eiffel)
Inspired by the builtin motions ]*
, ]/
which move the cursor to the next c style comment block, a few ftplugins add similar motions to easily jump to the next comment block in the respective language.
The builtin mappings ]*
, ]/
can be made repeatable by:
for keys in [ ['[*', ']*'], ['[/', ']/'], ['[#', ']#'] ]
" [#, ]# c preprocessor (couldn't this be done with '%,g%,[%,]%' ?)
" [*, ]* or [/, ]/ block comment used in c,c++,c#,Css,D,Go,Java,JavaScript,Objective-c,PHP,Rust,Sql,Swift,...
execute 'noremap <expr> '.keys[0]." repmo#SelfKey('".keys[0]."', '".keys[1]."')|sunmap ".keys[0]
execute 'noremap <expr> '.keys[1]." repmo#SelfKey('".keys[1]."', '".keys[0]."')|sunmap ".keys[1]
endfor
However, the following have to be modified
$VIMRUNTIME/ftplugin/vim.vim
nnoremap <silent><buffer> ]" :call search('^\(\s*".*\n\)\@<!\(\s*"\)', "W")<CR>
vnoremap <silent><buffer> ]" :<C-U>exe "normal! gv"<Bar>call search('^\(\s*".*\n\)\@<!\(\s*"\)', "W")<CR>
nnoremap <silent><buffer> [" :call search('\%(^\s*".*\n\)\%(^\s*"\)\@!', "bW")<CR>
vnoremap <silent><buffer> [" :<C-U>exe "normal! gv"<Bar>call search('\%(^\s*".*\n\)\%(^\s*"\)\@!', "bW")<CR>
$VIMRUNTIME/ftplugin/sql.vim
exec 'nnoremap <silent><buffer> ]" :call search('."'".b:comment_start."'".', "W" )<CR>'
exec 'nnoremap <silent><buffer> [" :call search('."'".b:comment_end."'".', "W" )<CR>'
exec 'xnoremap <silent><buffer> ]" :<C-U>exec "normal! gv"<Bar>call search('."'".b:comment_start."'".', "W" )<CR>'
exec 'xnoremap <silent><buffer> [" :<C-U>exec "normal! gv"<Bar>call search('."'".b:comment_end."'".', "W" )<CR>'
$VIMRUNTIME/ftplugin/hamster.vim
noremap <silent><buffer> ]# :call search('^\s*#\@!', "W")<CR>
noremap <silent><buffer> [# :call search('^\s*#\@!', "bW")<CR>
$VIMRUNTIME/ftplugin/eiffel.vim
nnoremap <silent> <buffer> ]- :<C-U>call <SID>DoMotion(comment_block_start, 1, 'W')<CR>
xnoremap <silent> <buffer> ]- :<C-U>exe "normal! gv"<Bar>call <SID>DoMotion(comment_block_start, 1, 'W')<CR>
nnoremap <silent> <buffer> [- :<C-U>call <SID>DoMotion(comment_block_end, 1, 'Wb')<CR>
xnoremap <silent> <buffer> [- :<C-U>exe "normal! gv"<Bar>call <SID>DoMotion(comment_block_end, 1, 'Wb')<CR>
Ft-specific [{
and ]}
Repeat builtin with
for keys in [ ['[{', ']}'], ['[(', '])'] ]
execute 'noremap <expr> '.keys[0]." repmo#SelfKey('".keys[0]."', '".keys[1]."')|sunmap ".keys[0]
execute 'noremap <expr> '.keys[1]." repmo#SelfKey('".keys[1]."', '".keys[0]."')|sunmap ".keys[1]
endfor
The only language in $VIMRUNTIME which defines them is currently sql. Again they are not <plug>
mappings and have to be modified.
$VIMRUNTIME/ftplugin/sql.vim
exec "nnoremap <buffer> <silent> ]} :call search('".s:ftplugin_sql_objects."', 'W')<CR>"
exec "nnoremap <buffer> <silent> [{ :call search('".s:ftplugin_sql_objects."', 'bW')<CR>"
exec 'xnoremap <buffer> <silent> ]} /'.s:ftplugin_sql_objects.'<CR>'
exec 'xnoremap <buffer> <silent> [{ ?'.s:ftplugin_sql_objects.'<CR>'
Unimpaired
No change necessary: they are already <plug>
motions. Following works:
nmap <expr> ]q repmo#Key('<plug>unimpairedQNext', '<plug>unimpairedQPrevious')
nmap <expr> ]q repmo#Key('<plug>unimpairedQNext', '<plug>unimpairedQPrevious')
nmap <expr> [q repmo#Key('<plug>unimpairedQPrevious', '<plug>unimpairedQNext')
nmap <expr> ]l repmo#Key('<plug>unimpairedLNext', '<plug>unimpairedLPrevious')
nmap <expr> [l repmo#Key('<plug>unimpairedLPrevious', '<plug>unimpairedLNext')
nmap <expr> ]n repmo#Key('<plug>unimpairedContextNext', '<plug>unimpairedContextPrevious')
nmap <expr> [n repmo#Key('<plug>unimpairedContextPrevious', '<plug>unimpairedContextNext')
omap <expr> ]n repmo#Key('<plug>unimpairedContextNext', '<plug>unimpairedContextPrevious')
omap <expr> [n repmo#Key('<plug>unimpairedContextPrevious', '<plug>unimpairedContextNext')