Series Index
Why and What
Projects, Dependencies and Gopls
Minimal Version Selection
Mirrors, Checksums and Athens
Gopls Improvements
Vendoring
Prelude
This is a guest post written by Rohan Challa, a member of the Go team working on gopls.
This document is a follow up to Bill Kennedy’s post on projects, dependencies and gopls
. His post showed that gopls
did not work well with modules, particularly when adding and removing dependencies from a project inside of your editor. Over the last 3 months, my work on the Go tools team has involved improving support for modules in gopls
.
Introduction
When the go
command needs to download a new dependency to resolve an import, the project’s go.mod
file may be updated as part of running the command. Since many editors run go
commands in the background, these changes occur without the developer’s knowledge. We believe this had lead to an editor experience that is confusing and unintuitive.
To address this issue, Go 1.14 supports a new flag: -modfile
. This flag instructs the go
command to read from and write to an alternate go.mod
file instead of the one associated with the project. gopls
has been updated to use this new flag so that the editor can provide suggestions, instead of directly changing the project’s go.mod
without the developer’s knowledge. This way, gopls
gives the developer the information they need to make the best decision for the project.
In this post, I will show you the new features in version 0.4.0 of gopls
that will improve the editor experience with modules.
Walkthrough
This post will serve as a companion to Bill’s post and will use the same example code. It will follow a similar workflow, demonstrating how the gopls
team has improved the editor experience of adding and removing dependencies. These features work for any editor that supports the Language Server Protocol. In this case, I will be using VS Code with the Go extension.
First, it’s important to confirm that your environment is properly configured.
Listing 1
// Check Go plugin version : >= v0.13.0
$ code --list-extensions --show-versions
// Check gopls version : >= v0.4.0
$ gopls version
// Check Go Version : >= v1.14
$ go version
Listing 1 shows the commands to verify the versions of the VS Code Go extension, gopls
, and Go you are using. It’s important to make sure you are using at least version 0.13.0 of the VS Code Go extension, version 0.4.0 of gopls
, and version 1.14 of go
.
As of the writing of this post, version 0.4.0 is the most recent version of gopls
.
Listing 2
// To install the latest version of gopls.
$ cd $HOME
$ GO111MODULE=on go get golang.org/x/tools/gopls@latest
Listing 2 shows how to install the latest version of gopls
. Run this command from your $HOME
directory.
Module Cache
As Bill did, I am going to clear my module cache before I begin so I have a clean working environment. This will allow me to show you how to handle situations when the module you need hasn’t been downloaded yet to your local module cache.
Listing 3
Listing 3 shows the call to go clean
with the -modcache
flag. This is not something you should need to do under normal workflows.
New Project
To start, I am going to create a new project. For more information about creating a new Go project using modules, you can look at this documentation.
Listing 4
https://play.golang.org/p/4zDoHbGT4Mz
$ cd $HOME
$ mkdir modtest
$ cd modtest
$ touch main.go
$ go mod init modtest
The commands in listing 4 create a new project folder containing a source code file: main.go
as well as a go.mod
file which creates a module.
Listing 5
Running the command in listing 5 will start VS Code and open the project. This will also automatically start a gopls
server in the background to service VS Code for this project.
Application Code
Listing 6
https://play.golang.org/p/fUha75miwFB
01 package main
02
03 import (
04 "log"
05 )
06
07 func main() {
08 log.Println("This is package main")
09 }
To start, copy the code from listing 6 into the main.go
file.
I want the project to use a function from github.com/ardanlabs/conf like Bill did in his post.
Listing 7
https://play.golang.org/p/QioJFbiXGye
07 func main() {
08 var cfg struct {
09 Web struct {
10 APIHost string `conf:"default:0.0.0.0:3000"`
11 DebugHost string `conf:"default:0.0.0.0:4000"`
12 ReadTimeout time.Duration `conf:"default:5s"`
13 WriteTimeout time.Duration `conf:"default:5s"`
14 ShutdownTimeout time.Duration `conf:"default:5s"`
15 }
16 }
17
18 if err := conf.Parse(os.Args[1:], "SALES", &cfg); err != nil {
19 log.Fatal("parsing config : %w", err)
20 }
21 log.Println("This is package main")
22 }
Add the code from listing 7 into the main
function and save the file.
Figure 1
Once you save the file, figure 1 shows you the error you should see about the use of conf.Parse
. Since there is no information about the conf
package in the module cache (remember, I cleared the module cache before I started) gopls
can’t direct VS Code to add an import for the conf
package. gopls
also can’t find any information about that package.
Adding Dependencies
You are responsible for adding this import manually since gopls
only knows about packages that are in the standard library or in your local module cache. Once a package exists in your local cache, gopls
can add imports for it automatically in all of your projects.
Listing 8
03 import (
04 "log"
05 "os"
06 "time"
07
08 "github.com/ardanlabs/conf"
09 )
Add an import for the conf
package inside the import section of the main.go
file as shown on line 08 of listing 8 and hit save. Adding this import will direct gopls
to download the module to your local module cache. Once this is done, the reference to conf.Parse
on line 22 can be resolved. Be aware that you need to wait for the download to complete before the editor can resolve the reference and provide any information about the module.
At this point in Bill’s post, the old version of gopls
surfaced a diagnostic with a message about the import which stated, undeclared name: conf. Now, the latest version of gopls
doesn’t provide this vague error message, but provides help instead.
Figure 2
You should see a squiggly line under the import to indicate a warning exists. When you hover over the import, you should see the warning as shown in figure 2. This message indicates that the conf
module is not listed as a dependency in the project’s go.mod
file and that you need to add the new dependency to the go.mod
file to satisfy the import. Fortunately, the warning comes with a Quick Fix
link.
Figure 3
If you look closely at figure 3, you will see the Quick Fix
link. Clicking this link will bring up an option to add the module to the go.mod
file. When you select the option to add, the go.mod
file is updated but left unsaved so that you can decide to keep the change or not.
Figure 4
The unsaved go.mod
file should look like the image in figure 4 with the conf
module listed. The version might vary since gopls
downloads the latest version of the module. Save the file to keep the changes. At this point, the module with a selected version is recorded in go.mod
and the squiggly line with the warning message on the import disappears.
Removing Dependencies
Now, what happens if you change your mind and no longer want to use the conf
package?
Listing 9
https://play.golang.org/p/RwC0aWzXf3F
11 func main() {
12 log.Println("This is package main")
13 }
Change the code in the main
function as provided in listing 9, which will remove the dependency on the conf
package. Once you hit save, gopls
will organize the imports and remove any unused dependencies.
Listing 10
https://play.golang.org/p/fUha75miwFB
01 package main
02
03 import (
04 "log"
05 )
06
07 func main() {
08 log.Println("This is package main")
09 }
Listing 10 shows what the main.go
file looks like after the imports are cleaned up when you save the file. Since the code is no longer using the conf
dependency, it should be removed from the project’s go.mod
file.
In Bill’s previous post, he needed to leave VS Code and run go mod tidy
to clean up the projects go.mod
file. Now, you can tidy the go.mod
file from within VS Code.
Figure 5
Navigate to the go.mod
file, it should look like figure 5. You should see a squiggly line on line 5 related to the conf
module.
Figure 6
When you hover over the module, you will see the warning in figure 6. You are also provided a Quick Fix
link, which when clicked will provide the option to remove the dependency from the go.mod
file.
Figure 7
Figure 7 shows how after clicking the Quick Fix
option the module is removed. Don’t forget to save the go.mod
file after this operation.
Upgrading Dependencies
Another new feature is the ability to upgrade dependencies to their latest version from within VS Code. This feature works with any version of Go that supports modules. To show this, I will put the code that requires the conf
module back in the main
function.
Listing 11
https://play.golang.org/p/NB6CMA52Zyf
01 package main
02
03 import (
04 "log"
05 "os"
06 "time"
07
08 "github.com/ardanlabs/conf"
09 )
10
11 func main() {
12 var cfg struct {
13 Web struct {
14 APIHost string `conf:"default:0.0.0.0:3000"`
15 DebugHost string `conf:"default:0.0.0.0:4000"`
16 ReadTimeout time.Duration `conf:"default:5s"`
17 WriteTimeout time.Duration `conf:"default:5s"`
18 ShutdownTimeout time.Duration `conf:"default:5s"`
19 }
20 }
21
22 if err := conf.Parse(os.Args[1:], "SALES", &cfg); err != nil {
23 log.Fatal("parsing config : %w", err)
24 }
25 log.Println("This is package main")
26 }
Listing 11 shows the main.go
file again using the conf
module. Next, I will intentionally use an older version of the conf
module in the go.mod
file.
Listing 12
01 module modtest
02
03 go 1.14
04
05 require github.com/ardanlabs/conf v1.2.0
Listing 12 shows the go.mod
file after I manually changed the module from using the latest version to version 1.2.0. When I save that change, I get a suggestion about upgrading the version.
Figure 8
In figure 8, you see a suggestion link to upgrade the conf
module from version 1.2.0 to version 1.2.1. Version 1.2.1 is the latest greatest version at the time of writing this post. When you click on this suggestion link, gopls
will upgrade the dependency to the version specified. You also see a suggestion link at the top of the go.mod
file. This suggestion link will upgrade all the modules in the files that have an individual suggestion.
Figure 9
After clicking either suggestion link, your go.mod
file will list the latest greatest version for the conf
module like the image in figure 9.
Conclusion
The gopls
team would like to thank Bill for providing such a detailed post explaining the pain points of using gopls
with Go modules. The steps outlined in the post helped us test and better understand user interactions with modules. Experience reports are very helpful, and we value your feedback.
The features described in this post are available once you upgrade your development environment to Go 1.14 and gopls
to the latest version (v0.4.0), pre-releases included.
To see these new features in action, take a look at this screencast, which demonstrates the features outlined in this post.
Issues with gopls
can be filed on the issue tracker.
Any questions can be asked on Slack inside of the #gopls channel, use the invite app for access.