Skip to content

Commit

Permalink
Fixed various situations where [m would hit places it shoudln't and m…
Browse files Browse the repository at this point in the history
…iss places it should hit. Also added mappings for [[ and ]].
  • Loading branch information
Rob authored and Rob committed Feb 16, 2020
1 parent 8cb81e6 commit 8ea9e8d
Show file tree
Hide file tree
Showing 4 changed files with 338 additions and 35 deletions.
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
# vim-jumpmethod
Add ]m/[m support for more languages
Add better `[[`, `]]`, `]m`, `[m` support for more languages

# About

Based on [Andrew Radev's awesome Stack Overflow
answer](https://stackoverflow.com/a/6855438/79125), this plugin improves
`]m`/`[m` support for C#, Java, and C++.
`]m` and `[m` support for C#, Java, and C++.

It also uses [bybor answer](https://stackoverflow.com/a/25521838/79125) to use
OmniSharp support, if available.


Turned into a plugin by @idbrii

Modified by @RobertCWebb in the following ways:

* Added Test.cs as a C# file to test on. Fixes listed below reference it
* Fixed: In Func1() there were false positives where `if` or `using` statements span multiple lines
* Fixed: Func2() failed to hit before due to the template definition
* Fixed: Func3() failed to hit because a comment contained the word "if"
* Fixed: Func4() failed to hit because function heading was split over multiple lines
* Fixed: Func5() failed to hit too, here the '(' is also on a new line
* Fixed: Func6() failed to hit due to comments getting in the way
* 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 '{' below
* Added mappings for `[[`, `]]`, `[]` and `][` which stop at class and property definitions in addition to functions. Their standard behaviour in vim is often useless for C# and C++. `[m` and `]m` still only stop at methods and functions
160 changes: 139 additions & 21 deletions autoload/jumpmethod.vim
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,170 @@
" TODO:
" * Extract keywords for better language support.

" Skip back over comment lines and blank lines
function! jumpmethod#SkipBackOverComments(current_line)
let current_line = a:current_line
while current_line > 1
let text = getline(current_line)

let pos = match(text, '\*/\s*$')
if (pos >= 0)
" End of a C-style comment, jump to other end
call cursor(current_line, pos + 1)
normal! %
let current_line = line('.')
let text = getline(current_line)
endif

" Skip back if start of C or C++ style comments and blank lines
if (text =~ '^\s*\(//\|/\*\)' || text =~ '^\s*$')
let current_line = current_line - 1
else
" Not a comment
call cursor(current_line, 0)
return
endif
endwhile
endfunction

" Uses keywords to determine which are curly braces are for methods.
function! jumpmethod#jump(char, flags, mode)
let original_cursor = getpos('.')
function! jumpmethod#jump(char, flags, mode, includeClassesAndProperties)
let original_cursor = getcurpos()

if a:mode == 'v'
normal! gv
elseif a:mode == 'o'
normal! v
endif

while search(a:char, a:flags) > 0
if a:char == '}'
" Passing 'f' rather than '{' or '}' makes it jump to the function name
" rather than the brace. Handy when searching backwards and you can't see
" what function you landed on because the '{' is at the top of the screen.
" However the way I've implemented things it tends to scroll down to expose
" the function name above anyway, so maybe not required.
let char = a:char
let goToFunctionName = 0
if (char == 'f')
let char = '{'
let goToFunctionName = 1
endif

while search(char, a:flags) > 0
let found = 0

if char == '}'
" jump to the opening one to analyze the definition
normal! %
endif

" Remember where we are, with cursor on the '{'
let openingBracePos = getcurpos()

let current_line = line('.')
let col = col('.')

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
" it's alone on the line, check the line above
let current_line = current_line - 1
endif

" Skip back over comment lines and blank lines
call jumpmethod#SkipBackOverComments(current_line)

let current_line = line('.')
let text = getline(current_line)

if (current_line == openingBracePos[1])
" Still on original line, ignore everything after opening brace
let text = strpart(text, 0, col - 1)
endif

let method_line_body = getline(method_line)
" Strip trailing comment
let text = substitute(text, '//.*', '', '')

if method_line_body =~ '\k\+\s*(.*)' && method_line_body !~ '\<\(for\|foreach\|if\|while\|switch\|using\|catch\|get\|set\)\>'
" it's probably a function call
" See if there's a closing ')'
let pos = match(text, ')\s*{\?\s*$')
if (pos >= 0)

if a:char == '}'
" we need to go back to the closing bracket
normal! %
" Found a closing ')', but function definition may span multiple lines,
" so find matching '(' at start.
call cursor(current_line, pos + 1)
normal! %
let current_line = line('.')

let text = getline(current_line)

if (text =~ '^\s*(')
" Go back an extra line if '(' was on a new line
" and skip back over comment lines and blank lines
call jumpmethod#SkipBackOverComments(current_line - 1)

let current_line = line('.')
let text = getline(current_line)

" Strip trailing comment
let text = substitute(text, '//.*', '', '')
else
" Strip everything from the '(' on. Note: cursor is currently on the '('
let text = strpart(text, 0, col('.') - 1)
endif

echo
return
else
if a:char == '}'
" we still need to go back to the closing bracket
normal! %
if text =~ '\(\k\|>\)\s*$' &&
\ text !~ '\<\(for\|foreach\|if\|while\|switch\|using\|catch\)\>\s*$'
" it's probably a function call
let found = 1

" Scroll up to the function name so we can see it.
exec 'normal! ' . current_line . 'G'
endif

elseif (a:includeClassesAndProperties)
" No closing ')'. Maybe worth stopping here anyway if it's a class
" definition or property.
if text !~ '\(\<\(else\|try\|finally\|get\|set\)\>\|=>\|;\|{\|}\)\s*{\?\s*$'
" Probably something of interest
let found = 1

" Scroll up to the class/property name so we can see it.
exec 'normal! ' . current_line . 'G'
endif
endif

" If we found what we want, return
if (found)
if (goToFunctionName)
normal! ^

if (a:flags !~ 'b')
" Make sure we didn't go backwards when we wanted to go forwards
let newPos = getcurpos()
if newPos[1] < original_cursor[1] ||
\ (newPos[1] == original_cursor[1] && newPos[2] <= original_cursor[1])
let found = 0
normal! $
endif
endif
else
call setpos('.', openingBracePos)
if char == '}'
" we need to go back to the closing bracket
normal! %
endif
endif

if (found)
call setpos("''", original_cursor)
return
endif
endif

call setpos('.', openingBracePos)
if char == '}'
" Go back to the closing bracket
normal! %
endif
endwhile

" if we're here, the search has failed, restore cursor position
echo
call setpos('.', original_cursor)
endfunction
48 changes: 36 additions & 12 deletions autoload/jumpmethod/plug.vim
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
" Source: https://stackoverflow.com/a/6855438/79125

function! jumpmethod#plug#setup_plug()
nnoremap <Plug>(jumpmethod-curly-tostart-fwd) :<c-u>call jumpmethod#jump('{', 'W', 'n')<cr>
nnoremap <Plug>(jumpmethod-curly-tostart-back) :<c-u>call jumpmethod#jump('{', 'Wb', 'n')<cr>
nnoremap <Plug>(jumpmethod-curly-toend-fwd) :<c-u>call jumpmethod#jump('}', 'W', 'n')<cr>
nnoremap <Plug>(jumpmethod-curly-toend-back) :<c-u>call jumpmethod#jump('}', 'Wb', 'n')<cr>
nnoremap <Plug>(jumpmethod-section-tostart-fwd) :<c-u>call jumpmethod#jump('{', 'W', 'n', 1)<cr>
nnoremap <Plug>(jumpmethod-section-tostart-back) :<c-u>call jumpmethod#jump('{', 'Wb', 'n', 1)<cr>
nnoremap <Plug>(jumpmethod-section-toend-fwd) :<c-u>call jumpmethod#jump('}', 'W', 'n', 1)<cr>
nnoremap <Plug>(jumpmethod-section-toend-back) :<c-u>call jumpmethod#jump('}', 'Wb', 'n', 1)<cr>
nnoremap <Plug>(jumpmethod-curly-tostart-fwd) :<c-u>call jumpmethod#jump('{', 'W', 'n', 0)<cr>
nnoremap <Plug>(jumpmethod-curly-tostart-back) :<c-u>call jumpmethod#jump('{', 'Wb', 'n', 0)<cr>
nnoremap <Plug>(jumpmethod-curly-toend-fwd) :<c-u>call jumpmethod#jump('}', 'W', 'n', 0)<cr>
nnoremap <Plug>(jumpmethod-curly-toend-back) :<c-u>call jumpmethod#jump('}', 'Wb', 'n', 0)<cr>
xnoremap <Plug>(jumpmethod-curly-tostart-fwd) :<c-u>call jumpmethod#jump('{', 'W', 'v')<cr>
xnoremap <Plug>(jumpmethod-curly-tostart-back) :<c-u>call jumpmethod#jump('{', 'Wb', 'v')<cr>
xnoremap <Plug>(jumpmethod-curly-toend-fwd) :<c-u>call jumpmethod#jump('}', 'W', 'v')<cr>
xnoremap <Plug>(jumpmethod-curly-toend-back) :<c-u>call jumpmethod#jump('}', 'Wb', 'v')<cr>
xnoremap <Plug>(jumpmethod-section-tostart-fwd) :<c-u>call jumpmethod#jump('{', 'W', 'n', 1)<cr>
xnoremap <Plug>(jumpmethod-section-tostart-back) :<c-u>call jumpmethod#jump('{', 'Wb', 'n', 1)<cr>
xnoremap <Plug>(jumpmethod-section-toend-fwd) :<c-u>call jumpmethod#jump('}', 'W', 'n', 1)<cr>
xnoremap <Plug>(jumpmethod-section-toend-back) :<c-u>call jumpmethod#jump('}', 'Wb', 'n', 1)<cr>
xnoremap <Plug>(jumpmethod-curly-tostart-fwd) :<c-u>call jumpmethod#jump('{', 'W', 'v', 0)<cr>
xnoremap <Plug>(jumpmethod-curly-tostart-back) :<c-u>call jumpmethod#jump('{', 'Wb', 'v', 0)<cr>
xnoremap <Plug>(jumpmethod-curly-toend-fwd) :<c-u>call jumpmethod#jump('}', 'W', 'v', 0)<cr>
xnoremap <Plug>(jumpmethod-curly-toend-back) :<c-u>call jumpmethod#jump('}', 'Wb', 'v', 0)<cr>
onoremap <Plug>(jumpmethod-curly-tostart-fwd) :<c-u>call jumpmethod#jump('{', 'W', 'o')<cr>
onoremap <Plug>(jumpmethod-curly-tostart-back) :<c-u>call jumpmethod#jump('{', 'Wb', 'o')<cr>
onoremap <Plug>(jumpmethod-curly-toend-fwd) :<c-u>call jumpmethod#jump('}', 'W', 'o')<cr>
onoremap <Plug>(jumpmethod-curly-toend-back) :<c-u>call jumpmethod#jump('}', 'Wb', 'o')<cr>
onoremap <Plug>(jumpmethod-section-tostart-fwd) :<c-u>call jumpmethod#jump('{', 'W', 'n', 1)<cr>
onoremap <Plug>(jumpmethod-section-tostart-back) :<c-u>call jumpmethod#jump('{', 'Wb', 'n', 1)<cr>
onoremap <Plug>(jumpmethod-section-toend-fwd) :<c-u>call jumpmethod#jump('}', 'W', 'n', 1)<cr>
onoremap <Plug>(jumpmethod-section-toend-back) :<c-u>call jumpmethod#jump('}', 'Wb', 'n', 1)<cr>
onoremap <Plug>(jumpmethod-curly-tostart-fwd) :<c-u>call jumpmethod#jump('{', 'W', 'o', 0)<cr>
onoremap <Plug>(jumpmethod-curly-tostart-back) :<c-u>call jumpmethod#jump('{', 'Wb', 'o', 0)<cr>
onoremap <Plug>(jumpmethod-curly-toend-fwd) :<c-u>call jumpmethod#jump('}', 'W', 'o', 0)<cr>
onoremap <Plug>(jumpmethod-curly-toend-back) :<c-u>call jumpmethod#jump('}', 'Wb', 'o', 0)<cr>
let s:setup_plug = 1
endf
Expand All @@ -24,16 +36,28 @@ function! jumpmethod#plug#map_to_plug_in_buffer()
call jumpmethod#plug#setup_plug()
endif

nmap <buffer> ]] <Plug>(jumpmethod-section-tostart-fwd)
nmap <buffer> [[ <Plug>(jumpmethod-section-tostart-back)
nmap <buffer> ][ <Plug>(jumpmethod-section-toend-fwd)
nmap <buffer> [] <Plug>(jumpmethod-section-toend-back)
nmap <buffer> ]m <Plug>(jumpmethod-curly-tostart-fwd)
nmap <buffer> [m <Plug>(jumpmethod-curly-tostart-back)
nmap <buffer> ]M <Plug>(jumpmethod-curly-toend-fwd)
nmap <buffer> [M <Plug>(jumpmethod-curly-toend-back)
xmap <buffer> ]] <Plug>(jumpmethod-section-tostart-fwd)
xmap <buffer> [[ <Plug>(jumpmethod-section-tostart-back)
xmap <buffer> ][ <Plug>(jumpmethod-section-toend-fwd)
xmap <buffer> [] <Plug>(jumpmethod-section-toend-back)
xmap <buffer> ]m <Plug>(jumpmethod-curly-tostart-fwd)
xmap <buffer> [m <Plug>(jumpmethod-curly-tostart-back)
xmap <buffer> ]M <Plug>(jumpmethod-curly-toend-fwd)
xmap <buffer> [M <Plug>(jumpmethod-curly-toend-back)
omap <buffer> ]] <Plug>(jumpmethod-section-tostart-fwd)
omap <buffer> [[ <Plug>(jumpmethod-section-tostart-back)
omap <buffer> ][ <Plug>(jumpmethod-section-toend-fwd)
omap <buffer> [] <Plug>(jumpmethod-section-toend-back)
omap <buffer> ]m <Plug>(jumpmethod-curly-tostart-fwd)
omap <buffer> [m <Plug>(jumpmethod-curly-tostart-back)
omap <buffer> ]M <Plug>(jumpmethod-curly-toend-fwd)
Expand Down
Loading

0 comments on commit 8ea9e8d

Please sign in to comment.