0

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> :endfunction " Create the keybinding nnoremap <leader>gtoc :call GotoClass(input('Class name: '))<CR> 
2
  • 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. Commented Mar 8, 2023 at 19:35

2 Answers 2

3

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.

djump

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.

1

Let's start by removing those useless colons:

function! GotoClass(className) normal! 1G normal! /class\s\+ . a:className<CR> endfunction 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 endfunction 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 endfunction 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> 

search


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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.