I'm trying to write a vimscript function that will search for a ruby class definition. My function correctly goes to the top of the file to start the search, but then doesn't move to the given class definition. To simplify, I tried searching with:

normal! 1G
normal! /class<CR>

but this failed. It just moved to the top of the file.

My more complete code is

:function! GotoClass(className)
    " Move to the top of the current buffer
    normal! 1G
    normal! /class\s\+ . a:className<CR>

" Create the keybinding
nnoremap <leader>gtoc :call GotoClass(input('Class name: '))<CR>
  • 5
    please use the search() function. Commented Mar 8, 2023 at 18:25
  • 3
    The reason why your code is not working is that :normal! will not recognize <CR> as a "return" character. You can work around that by using :execute "normal! /class\<CR>" instead. In your actual example, you're trying to do some concatenation (with . a:className) so you need to use a string anyways, another reason to use :execute. But as Christian pointed out, search() is better. See :help :execute and :help search() for all the details.
    – filbranden
    Commented Mar 8, 2023 at 19:35

2 Answers 2


FWIW, this looks a lot like a job for the built-in :help definition-search but, for some reason, the default ftplugin for Ruby doesn't set :help 'define', which is crucial for this feature to work.

Let's fix that by creating after/ftplugin/ruby.vim and populating it with the following line:

setlocal define=\^\\s\*\\(class\\\|def\\\|module\\)

which essentially tells Vim to look for lines that start with either class, def, or module as cues when searching for "definitions".

Once that is done, we can simply leverage the built-in :help :djump:

:dj Annotation

or even:

:dj annotation

if :help 'ignorecase' is on.


And, if you really want a mapping (though :dj compares quite well to your <leader>gtoc), you can go with a very simple:

nnoremap <key> :djump<Space>

Bonus: you also get [D and friends.


Let's start by removing those useless colons:

function! GotoClass(className)
    normal! 1G
    normal! /class\s\+ . a:className<CR>
nnoremap <key> :call GotoClass(input('Class name: '))<CR>

Now, the most obvious issue is that :help :normal can't do concatenation.

The following line literally searches for class, followed by one or more whitespace characters, followed by a space, followed by any character except \n, followed by a space, followed by a:className<CR>:

normal! /class\s\+ . a:className<CR>

which is unlikely to find any match, which explains why your function only moves the cursor to the top of the buffer. Also, the <CR> is useless in this context.

A simple fix would be to use :help :execute, which does concatenation:

function! GotoClass(className)
    normal! 1G
    execute "normal! /class\s\+" . a:className
nnoremap <key> :call GotoClass(input('Class name: '))<CR>

But we can do better. Instead of abusing :normal, which doesn't really add any value here, let's use :help :/ to replace these two lines:

normal! 1G
execute "normal! /class\s\+" . a:className

with a single line:

function! GotoClass(className)
    execute "1/class\s\+" . a:className
nnoremap <key> :call GotoClass(input('Class name: '))<CR>

Let's not stop there! Is that custom function really necessary? We could use :help search() and get rid of it:

nnoremap <key> <Cmd>1call search('class\s\+' . input('Class name: '))<CR>


At this point I wanted to go even further with :help searchdecl(), which would have allowed me to get rid of concatenation, but it proved to be a lot less reliable than I expected so I'm afraid I reached the end of the line.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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