Navigation between functions and types in vim-go

Navigation between functions and types in vim-go

I’ve introduced a new tool called motion in my previous post (I recommend to read it before you continue) and explained new features (such as text-objects) that are implemented on top of it.

Text-objects are great to modify and change the function. But we also might want to move around functions. Suppose there are several function declarations in the current file and you want to jump to them directly? There is a function that starts with “New…” but you don’t remember what it is? Or you quickly want to move to the next/previous function?

With the today’s vim-go release you’ll be easily answer all these questions! Let me first start with by moving to next/previous functions.

Text motion objects

Vim-go has two new text object motions:

]]   forward to next function declaration
[[   backward to previous function declaration

Now you can easily move forward to the next function by hitting ]] . If you want to move backwards just use the opposite: **[[ **. The best part is that its a motion object just like w for word, p for paragraphs, etc.. And you even can prefix it with a count, for example the following jumps to the third function from your cursor:

3]]

Or the following deletes everything until next function:

d]]

As you see it’s up to you how you combine these new motion objects. For me it’s now much more easier if I want to jump forth and back between two near functions. I don’t have to think anymore about _how many lines _I have to jump forward or where the function is. It just works. Here is a video that shows it in action:

https://www.youtube.com/watch?v=ettn9rAcIL4

Under the hood it uses motion. So you might ask how we find the next function declaration for a given offset? The first thing to remember is that Go’s parser is really fast. So if you parse a Go file to retrieve the AST, it’ll be instantaneously.

So what happens if you type ]] to move to the function declaration?:

  • Motion parses the whole file and produces an AST
  • It traverses the AST and finds all function declarations and creates a list of those function declarations. During the traversing it also constructs the function signature and all necessary pieces.
  • After that it searches for a function whose offset is larger than the current one via sort.Search
  • Once found it returns the function in Vim’s own format. Note that we have a special vim package that marshal’s the result into a Vim compatible format. This package is based on Alan Donovan’s initial work, which I’ve improved by adding field tag support and fixed couple of bugs. The vim format will be obsoleted in the future, as the latest Vim versions have now native support for encoding/decoding JSON.

Now as you see all these steps are executed so fast you don’t even notice it. Someone asked me if I use a cache for these intermediate results. Go is so fast that we don’t need any kind of cache between the editor and the tool. It just works.

Jump to function/type declarations

What about jumping to a function whose name you know already in a given file? Suppose the file itself is over 500 lines and you barely know where the function is declared. However you know the function very well. Or suppose you have a package that is spread over multiple files, wouldn’t it be cool if you could jump from one to another just by giving a function name?

This questions are now covered with the following new commands:

:GoDecls [file]
:GoDeclsDir [dir]

This currently depends on the excellent ctrlp.vim plugin (support for unite and co will implemented in the future too). It’s because ctrlp has a great UI that let us filter a list of items and then open the selection in the current buffer, vertical/horizontal or even in a new tab. The commands above are therefore only available if you installed ctrlp, if not they will not show up.

So how to they work?

  • :GoDecls opens the current file and lists all available function declarations. When you continue to type anything it filters them out according to the input. You can also give a filename as an argument, which in that case lists the function for the given filename, i.e **:GoDecls filename.go. **You can then just hit enter to open it inside the current buffer, or open in another vertical split via _, _split via or in a new tab via
  • :GoDeclsDir is the same as :GoDecls, the only difference is it parses all Go files under the current directory. It’s a very powerful tool. What it means is, you can instantaneously jump from one file to another file just by typing a function name. I’m using it for several weeks and now I’m thinking how I was living without it. Just like _:GoDecls _you can also pass a directory name to it, i.e :GoDeclsDir /usr/local/go/src/bytes

It not only jumps to function declarations it also jumps to type declarations too! Here is a GIF that shows it in action:

GoDecls and :GoDeclsDir in action

As you see vim-go got much more powerful due the underlying tool (motion) and the implementation on top of it. It’s highly extendible and I’m looking forward to implementing even more features in the future (such as dealing with structs).

All these features are now available immediately. Go ahead and pull the latest changes from https://github.com/fatih/vim-go. If you find any bugs please open a new issue. As always, I’m more than happy to receive any kind of feedback.