Running staticcheck with eglot and gopls

Another neat finding in Go’s language server. Basically, I wanted to include some sort of way to run some static analyser with my language server. I remember golanci-lint was long the “de facto” tool for this, but seems that staticcheck has grown a lot in popularity. So I wanted to integrate that with my gopls.

Naturally, the first step was installing the tool itself. Fortunately, that can be done super easily with just:

$ go install honnef.co/go/tools/cmd/staticcheck@latest

Next I needed to enable this somehow with gopls. Again fortunately, all the possible settings for gopls can be found here. Including the simple variable for staticcheck. To pass in this setting to eglot, I need to configure variable eglot-workspace-configuration, which basically allows you to configure LSP servers specifically for a given project and given LSP.

To pass in the setting to the gopls, we need to pass in a plist with the configurations we want:

(use-package eglot
  :custom
  (eglot-workspace-configuration '((:gopls . ((staticcheck . t))))))

;; OR

(setq-default eglot-workspace-configuration '((:gopls . ((staticcheck . t)))))

Naturally, you can pass any setting you desire that is available for the language server this way.


Generics-aware gopls in Emacs

I needed to write some funky Go code using relatively new Go generics in it, just to quickly notice that my LSP client in Emacs didn’t recognise those like you would expect. Fixing gopls was relatively straight-forward, since my issue seemed to be just an old version. So I just installed gopls while having Go 1.18+ installed. In my case, I had already generics-aware Go version installed but seemed like that I had installed my gopls version before the generics update, so I had to just reinstall it with:

$ go install golang.org/x/tools/gopls@latest

After that gopls was pleased, or at least my eglot didn’t complain about syntactical errors in my code. Pretty much immediately I realised that now my goimports was broken, so it didn’t organise my imports accordingly. I knew that gopls was able to do this stuff instead of using goimports, but I just never was eager to fix something that was already working. But since now it was broken, I decided to find a way to fix it.

What I did was just uninstall goimports from my machine and started relying on gopls for organising my imports. Previously, I had set my Emacs so that when I saved my files, it always ran goimports (when working on Go files, of course). Setting my eglot to do that was relatively simple, but then I noticed that it only formats my code, it doesn’t automatically import the libraries like how goimports does.

Like I mentioned earlier, gopls should be able to work exactly like goimports in this case, so I had to start digging on how I can make my eglot to do this. Basically, how gopls does this, is it uses source.organizeImports action for it. So I needed to run that somehow on save.

Fortunately, eglot exports all these code actions that the LSP can do with a neat function called eglot-code-actions. After some tinkering, I was able to call that before the save:

(use-package go-mode
  :ensure t
  :preface
  (defun tok/gofmt-before-save ()
    (interactive)
    (gofmt-before-save)
    ;; Run `eglot-code-actions' only in buffers where `eglot' is active.
    (when (functionp 'eglot-code-actions)
      (eglot-code-actions nil nil "source.organizeImports" t))
  :hook (go-mode . (lambda ()
                     ;; Using depth -10 will put this before eglot's
                     ;; willSave notification so that the notification
                     ;; reports the actual contents that will be
                     ;; saved.
                     (add-hook 'before-save-hook 'tok/gofmt-before-save -10 t))))

I decided to use gofmt-before-save, which comes from go-mode here since I noticed that if you would just run eglot-format-buffer formatting doesn’t open a new buffer where it lists all the errors and instead prints them in the LSPs messages. You can probably dig them somehow from there and print in a new buffer, but I liked already the existing behaviour of running gofmt with go-mode so I decided to use that one.

Small fix, but a really good one. Happy generics-aware hacking.


Telemetry in Go

Last week, Russ Cox from Go team started a discussion about the possibility of starting to collect telemetry from Go usage.

How do software developers understand which parts of their software are being used and whether they are performing as expected? The modern answer is telemetry, which means software sending data to answer those questions back to a collection server.

I believe that open-source software projects need to explore new telemetry designs that help developers get the information they need to work efficiently and effectively, without collecting invasive traces of detailed user activity.

I have written a short series of blog posts about one such design, which I call transparent telemetry, because it collects as little as possible (kilobytes per year from each installation) and then publishes every bit that it collects, for public inspection and analysis.

I’d like to explore using transparent telemetry, or a system like it, in the Go toolchain, which I hope will help Go project developers and users alike. To be clear, I am only suggesting that the instrumentation be added to the Go command-line tools written and distributed by the Go team, such as the go command, the Go compiler, gopls, and govulncheck. I am not suggesting that instrumentation be added by the Go compiler to all Go programs in the world: that’s clearly inappropriate.

Cox also published three part introductory blog post about “transparent telemetry” that is worth a read:

So this whole discussion got me thinking about my feelings towards Go and, possibly, its future. First, I enjoy working with Go. As a language, it’s delightful to work with. It’s safe and fast, and I feel productive in it. But if the Go developers would introduce something like telemetry collecting your Go usage, I would have to re-evaluate the need/desire to use Go in current/new projects. There aren’t many developers that enjoy something like this in their toolchains. Or that would willingly allow something like that.

Sure, collecting telemetry in Visual Studio Code hasn’t affected too much in its popularity. But then again VSCodium is also quite popular, so clearly, some people hate this kind of telemetry, even in their favourite tool. Of course, I’m not yet even talking about the legality of collecting something like that. Because if something like this would be on by default, even if opt-out is offered, looking at GDPR, this can be considered illegal.

Of course, Go has had some “trust issues” for many due to it being language primarily developed, or at least funded, by Google. So naturally, people tend to have certain ideas and feelings about it even without touching it. Understandably so. Many people have already raised criticism in Go, for example, in their usage of Google run closed source Go module proxy mirror (proxy.golang.org), which is set on by default. This is also odd since Go’s import system was made to be decentralized from the get-go. Still, they decided to introduce something like this to increase reliability when importing libraries.

Considering all this, personally, I feel that if they were to introduce something like this to the Go toolchain, especially if it’s set on by default, it’d be a horrible thing for Go making the language to start fighting a big uphill battle, which may never end. I would still like to continue working in it, but if something like this were to happen, I feel that I couldn’t continue working in it if I had a choice.