1

I want to craft a custom operator-pending motion if for functions in R language that look like this:

doSomething <- function(arg1, arg2, ...) {
    doStuff(...)
}

The idea is to be able to press yif while on the function body to copy it's contents doStuff(...) in this case. I have something that works:

onoremap if :<c-u>execute "normal! $?<-\\s*function\\s*(\r^/{\r%``j^v``kg_"<cr>

This does the following: searches for the <- function pattern backwards, then finds the first {, jumps to it's match with %, then goes back to the beginning, goes down one line, enters visual mode, jump to the end, goes up one line and finishes.

This works for me with one downside: it finds functions even when I am outside (below) the function body.

My question is: how to make it fail when the cursor is not on a function?

Any other help/tips are appreciated as well.

Update

Following advice from @Luc I wrote a simple function to check if the given line is inside the function:

function! IsInsideRFunction(lnum)
  let currentline = a:lnum
  let savedpos = getpos(".")
  execute "silent! normal! $" . '?<-\s*function\s*(' . "\<cr>"
  execute "silent! normal! " . '/{' . "\<cr>"
  let startline = line(".")
  execute "silent! normal! %"
  let endline = line(".")
  if currentline >=# startline && currentline <=# endline
    call setpos(".", savedpos)
    return 1
  endif
  call setpos(".", savedpos)
endfunction

The function works. However when I put it inside the expression map:

onoremap <expr> aF IsInsideRFunction(line(".")) ? ":echom 'yes'<cr>" : ":echom 'no'<cr>"

it stops working. More precisely - it always returns "1".

What am I doing wrong?

2

Have you looked at Quick search, limited to a C++ function all the techniques presented in this discussion shall apply to your question. I'm not sure my solution is checking the cursor is within a function definition.

To check for improper uses, you have to define a function. There, first record the current line, and then check the first and last line you detect do surround the initial line. If not, throw an error or do whatever you wish.

EDIT: For instance something like:

" ftplugin/r.vim
onoremap <buffer> <silent> if :<c-u>call <sid>Select_current_function()<cr>
xnoremap <buffer> <silent> if :<c-u>call <sid>Select_current_function()<cr><esc>gv

function! s:Select_current_function() abort
  if 0
    " V1 that doesn't work well when the cursor is on the function declaration
    " The regex needs to be fixed
    let lin = line('.')
    let start = searchpos('<-\s*function\s*(.\{-})\s*{', 'bcenWz') " may depend on your vim version
    if start[0] == 0 
      throw "Not within a function"
    endif
    call assert_true(start[0] <= lin) " may not exist with old vim versions
    let end = searchpairpos('{', '', '}', 'nc')
    if end[0] == 0 || end[0] < lin
      throw "Not within a function"
    endif
    call cursor(start)
    normal! v
    call cursor(end)
  else

    " or, V2 that supposes that function end is always 0 indented
    " I don't know if this is valid for R
    let p = getcurpos()
    normal! ][
    if getline('.')[col('.')-1] != '}'
      call setpos('.', p)
      throw "Not within a function 1"
    endif
    normal! v%
    if line('.') > p[1] 
      call setpos('.', p)
      throw "Not within a function"
    endif
  endif
endfunction
  • Thank you for the reply. I tried something similar to what you suggested and it still didn't work. Maybe you can take a quick look - what am I doing wrong? – Karolis Koncevičius Aug 31 '16 at 21:49
  • @KarolisKoncevičius I'd have used search() with the don't move the cursor option to search for function start and end. Otherwise, you've forgotten to return something in the second case. BTW, you don't need execute to run silent! normal % – Luc Hermitte Aug 31 '16 at 22:01
  • I am new to this, as you can probably tell.. Thank you for all the advice. Didn't know about search - that's a good match for what I am doing, will try using it. But not having an "else" inside the function in this case does not seem to be the problem. I verified that the function works otherwise (calin IsInsideFunction() from command line). It just stops working when inside that onoremap – Karolis Koncevičius Aug 31 '16 at 22:06
  • @KarolisKoncevičius, the thing is that not all paths of your function return something. You can't use your function result as it may be in an unspecified state. – Luc Hermitte Aug 31 '16 at 22:34
  • Hmm I really don't think that's the issue. I got this from here Learn Vim Script The Hard Way and it says "if a Vimscript function doesn't return a value, it implicitly returns 0". And I tried adding else - didn't help. Another thing - after restarting vim now the same onoremap is returning 0 everywhere (without changing anything else). Probably something is cached here... – Karolis Koncevičius Aug 31 '16 at 22:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.