Treating Go types as objects in Vim

Fatih Arslan
Mar 14, 2016 · 4 min read

Vim users know that they can edit text in a very different way, with what we call as “text objects” or “text motions”. For example words are defined with the character “w” or paragraphs with “p”. Instead of selecting words manually and change them (or delete) you select “words” and operate on them. So you can replace words, delete them, copy them and so on. These actions are called operators in Vim and there are many of them, such as “d” for delete, “c” for change, “y” for yank, etc… How do they work?

For example this simple action below means “delete a word”:

dw

Or the following action means “copy four words”:

4yw

This is a very powerful concept that enables Vim users to edit their content in a very different mindset.

So I thought about these “text objects” and “text motions” a little bit. It’s very useful to have the “w” object that defines how to move forward to a “word”. Combining this with an operator changes what we want to do with the word. But there are many other objects that are not useful when you edit a Go source code. Such as “p” for paragraphs, or “s” for sentences, “t” for XML tags, and so on.

What if we could define Go specific text objects? For example what if we define a function with the character “f” ? Or what if we could move to the third function in our source code? These are questions that opens a whole different mindset of editing your source code.

Current Problems

This approach (based on regex) was very flawed and had problems:

  1. It doesn’t select function literals
  2. It doesn’t know what a function is (no knowledge of the AST).
  3. It’s based on regex, maintaining it is not easy and also not worth it. It’s fundamentally broken because it doesn’t has any kind of knowledge what the source code is.
  4. Using regex meant also that we can’t use more advanced cases, such as selecting one-line function declarations from the same line, selecting function declarations from the comment itself, etc…

Regex is not the solution.

Fortunately Go has very fundamental and great packages (the parser family: go/{parser,token,scanner,ast}) that enables us to parse Go code and get a sense how a Go code is constructed. These packages are already used as a foundation to many other tools. Why don’t we use this information?

Introducing Motion

motion is the tool which is doing the heavy lifting. It’s a CLI tool that can be run in multiple modes and outputs different outputs based on the given mode (just like guru)

Under the hood it uses the go parser family to parse the given file or director and then returns a consumable output. Currently it outputs in vim and json format. The output changes based on the given mode. For example one of the modes is called enclosing, and this mode returns the enclosed function information for a given offset. What does the information contains? Anything we might be interested! Below is an example output:

{
"mode": "enclosing",
"func": {
"sig": {
"full": "func Bar() (string, error)",
"recv": "",
"name": "Bar",
"in": "",
"out": "string, error"
},
"func": {
"filename": "testdata/main.go",
"offset": 174,
"line": 15,
"col": 1
},
"lbrace": {
"filename": "testdata/main.go",
"offset": 201,
"line": 15,
"col": 28
},
"rbrace": {
"filename": "testdata/main.go",
"offset": 225,
"line": 17,
"col": 1
}
}
}

This information is just a simple representation of a given function declaration. For example suppose we have the information above. Now for any given cursor position, we can easily know if the cursor is inside a function, is on top of a comment documentation or is outside a function.

The new implementation in vim-go is using motion to retrieve the necessary information and then using that information to perform the operator actions on the if and af objects.

Here is a video of showing how powerful these objects are now:

Next generation vim-go text objects explained

There are many details that are now better compared to the regex implementation:

  • Anonymous functions are supported
  • One liner functions can be selected easier
  • Cursor position can be anywhere as long as it makes sense
  • Comments are treated as a part of the function declaration

Relying on the AST opens a whole another world of ways implementing new features. This is only one part, in my next blog post (I’ve posted it, go ahead and read it!) I’m going to show another new feature in vim-go, which is used to jump to a generic declaration both on file and package level.

Text object support will be released soon with other additional improvements and new features.

It’s still in alpha mode, but if you have any feedback and ideas how we can even improve it even further, please don’t hesitate! I’m looking forward to improve it and extend it with possible other features.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store