Vim [m motion with c#
Vim provides very useful motion commands to jump to next start/end of a method: ]m, ]M, [m and ]m.
These works for Java or similar structured language. (as described in :help ]m and :help 29.3)
It seems to work considering the outermost开发者_运维技巧 pair of curly braces as class declaration and the next level of curly braces as method declarations.
These motion commands doesn't work when there is an outer pair of curly braces around class definition, which is somewhat common on languages as C#.
I was wondering if there is some trick to make those commands (alone and prefixed with operators, e.g., y[m, V]M) work on this code:
namespace ABC.DEF
{
class A
{
protected string strID;
public string PortID { get { return strID; } set { strID = value; } }
protected MyType x;
public MyType X
{
get { return x; }
set { x = value; if ( x != null ) func1(); }
}
int func1()
{
return 1;
}
int func2(int flag)
{
if (flag == 0)
return flag;
if (flag > 3)
{
return flag;
}
return 2;
}
int func3()
{
return 3;
}
}
}
I don't think the ]m
family of mappings can be customized. In such cases, the usual practice is to override it with custom logic. I came up with some vimscript that should do what you describe. Basically, it jumps through curly braces and looks at the relevant line to decide what to do. In this case, it just ignores "class" and "namespace" declarations.
nnoremap <buffer> ]m :<c-u>call <SID>JumpMethod('{', 'W', 'n')<cr>
nnoremap <buffer> [m :<c-u>call <SID>JumpMethod('{', 'Wb', 'n')<cr>
nnoremap <buffer> ]M :<c-u>call <SID>JumpMethod('}', 'W', 'n')<cr>
nnoremap <buffer> [M :<c-u>call <SID>JumpMethod('}', 'Wb', 'n')<cr>
xnoremap <buffer> ]m :<c-u>call <SID>JumpMethod('{', 'W', 'v')<cr>
xnoremap <buffer> [m :<c-u>call <SID>JumpMethod('{', 'Wb', 'v')<cr>
xnoremap <buffer> ]M :<c-u>call <SID>JumpMethod('}', 'W', 'v')<cr>
xnoremap <buffer> [M :<c-u>call <SID>JumpMethod('}', 'Wb', 'v')<cr>
onoremap <buffer> ]m :<c-u>call <SID>JumpMethod('{', 'W', 'o')<cr>
onoremap <buffer> [m :<c-u>call <SID>JumpMethod('{', 'Wb', 'o')<cr>
onoremap <buffer> ]M :<c-u>call <SID>JumpMethod('}', 'W', 'o')<cr>
onoremap <buffer> [M :<c-u>call <SID>JumpMethod('}', 'Wb', 'o')<cr>
function! s:JumpMethod(char, flags, mode)
let original_cursor = getpos('.')
if a:mode == 'v'
normal! gv
elseif a:mode == 'o'
normal! v
endif
while search(a:char, a:flags) > 0
if a:char == '}'
" jump to the opening one to analyze the definition
normal! %
endif
let current_line = line('.')
if getline(current_line) =~ '^\s*{'
" it's alone on the line, check the above one
let method_line = current_line - 1
else
let method_line = current_line
endif
let method_line_body = getline(method_line)
if method_line_body =~ '\k\+\s*(.*)' && method_line_body !~ '\<\(for\|foreach\|if\|while\|switch\|using\|catch\|get\|set\)\>'
" it's probably a function call
if a:char == '}'
" we need to go back to the closing bracket
normal! %
endif
echo
return
else
if a:char == '}'
" we still need to go back to the closing bracket
normal! %
endif
endif
endwhile
" if we're here, the search has failed, restore cursor position
echo
call setpos('.', original_cursor)
endfunction
Bear in mind that I don't really know a lot of C#, so it might not work properly in all cases, but if you give me examples that break, I might be able to figure something out.
To try it, you should put it somewhere under "ftplugin" in your vimfiles directory, as "cs.vim". Any other filename that starts with "cs" and ends in ".vim" is good too, if you already have a "cs.vim" file there.
OmniSharp now has :OmniSharpNavigateUp
and :OmniSharpNavigateDown
. It always goes to "start" (hence this is for ]m
and [m
mappings). I'm using these mappings (taken from another answer to this question) in ftplugin\cs.vim
nnoremap <buffer> ]m :OmniSharpNavigateDown<cr>
nnoremap <buffer> [m :OmniSharpNavigateUp<cr>
nnoremap <buffer> ]M :OmniSharpNavigateDown<cr>
nnoremap <buffer> [M :OmniSharpNavigateUp<cr>
xnoremap <buffer> ]m :OmniSharpNavigateDown<cr>
xnoremap <buffer> [m :OmniSharpNavigateUp<cr>
xnoremap <buffer> ]M :OmniSharpNavigateDown<cr>
xnoremap <buffer> [M :OmniSharpNavigateUp<cr>
onoremap <buffer> ]m :OmniSharpNavigateDown<cr>
onoremap <buffer> [m :OmniSharpNavigateUp<cr>
onoremap <buffer> ]M :OmniSharpNavigateDown<cr>
onoremap <buffer> [M :OmniSharpNavigateUp<cr>
A few weeks ago, a similar question has been asked on vim mailing list, but for C++. Here is the solution I came up with.
It relies on ctags and a few other plugins of mine: lh-dev, lh-tag, and lh-vim-lib. You can install lh-dev via vim-addon-manager, this will install lh-tag and lh-vim-lib in turn.
HTH,
Andrew Radev gave an answer which idbrii turned into a plugin.
I have modified that plugin here: https://github.com/RobertCWebb/vim-jumpmethod/
I fixed various issues and added improved mappings for [[ and ]] as well
- Fixed: failed to stop when function heading was split over multiple lines (this happens all the time for me)
- Fixed: stopped at multi-line if-statements when it shouldn't
- Fixed: failed to stop at template functions, like Func()
- Fixed: failed to stop at functions with comments containing words like "if" or "for"
- Fixed: failed to stop at functions with comment lines between the function name and the brace
- Fixed previous position marker not being set, ie after using [m or ]m you should be able to use '' to go back to previous line
- Also, when searching back, I made it scroll up a little so the function name is visible, even though the cursor still lands on the '{'
- Added mappings for [[, ]], [] and ][ which stop at class, enum and property definitions in addition to functions. Their standard behaviour in vim is often useless for C#. [m and ]m still only stop at methods and functions
Regarding the latter, I found it's more useful to stop at class definitions and properties too.
Properties in C# can contain a get and a set method, but I decided it was probably sufficient to just stop once at the definition of the property itself.
For classes, I noticed that I would jump from a method in one function to a method in another, without even realising I was in a different class. That seems dangerous to me, so it's nice to stop at the class definition too. The [[ and ]] mappings will do this.
You can use 9]}. It's a bit forced, but hey, it works.
精彩评论