Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: templ lsp should print an error when gopls is not installed #967

Open
whmountains opened this issue Oct 25, 2024 · 4 comments
Open

Comments

@whmountains
Copy link

whmountains commented Oct 25, 2024

Steps to reproduce:

  1. Ensure gopls is NOT installed.
  2. Run templ lsp

Expected behavior: an error message is printed, prompting the user to run templ info for more details.

Actual behavior: nothing is printed at all

This was quite confusing to me as someone who is new to Go and Templ. Both Zed and VS Code printed cryptic error messages about a closed channel or something. I immediately checked the LSP logs for additional clues, but there was nothing. I then tried running templ lsp manually. When it silently exited I thought it had daemonized itself or something. It was only when I tried to kill the daemon that I realized it was not there and started running other cli commands out of desparation.

This is not a bug and I expect a careful reading of the documentation would have given me some clues. But it seems like an opportunity to make a trivial change that could improve the experience for a percentage of new users. I work with several less common languages like Elixir, where buggy LSP servers are the norm, so I was perhaps slower to blame myself than I should have been.

  • OS: MacOS
  • templ CLI version: v0.2.778
  • Go version: go1.23.2 darwin/amd64
  • gopls version: golang.org/x/tools/gopls v0.16.2
@tjbarber
Copy link

When I tried this I encountered this error:

image

If you're not getting any error message printed, it's likely that the LSP is running properly when you run it from the command line and your editor isn't configured properly.

You're saying that you ran templ lsp and it immediately quit as opposed to just running non-interactively?

@whmountains
Copy link
Author

whmountains commented Oct 25, 2024

Thanks for attempting to reproduce! That is very strange, because I can 100% confirm that templ lsp exits immediately for me with no error message when gopls is not installed. I uninstalled it and tested again just now to confirm.

SCR-20241025-karq

It exits with status 1 but no error message.

I happen to be using a less common shell called nushell, so I temporarily set zsh to be my login shell and tried again, unfortunately with the same result. Note that my ZSH is absolutely stock, with no .zshrc at all.

image

You're saying that you ran templ lsp and it immediately quit as opposed to just running non-interactively?

Yes! I don't blame you for double checking. Once I installed gopls, it still prints nothing but it stays running until I force it to exit by pressing Control-C. Also, my editor immediately started working correctly with autocomplete and format-on-save once I installed gopls and ran the command to restart the language server. I had repeatedly restarted the language server before installing gopls but it would always immediately error out.

This problem is stranger than I thought. It's not particularly important, but I do hope we can get to the bottom of what's causing the difference in behavior.

@joerdav
Copy link
Collaborator

joerdav commented Oct 28, 2024

Just one thought from me, are you sure there isn't another version of gopls hanging around somewhere? Try where gopls

@a-h
Copy link
Owner

a-h commented Oct 29, 2024

Can you run templ info? It will tell you where gopls is.

On my Mac, the output currently looks like this:

(✓) os [ goos=darwin goarch=arm64 ]
(✓) go [ location=/run/current-system/sw/bin/go version=go version go1.23.1 darwin/arm64 ]
(✓) gopls [ location=/Users/adrian/go/bin/gopls version=golang.org/x/tools/gopls v0.16.2 ]
(✓) templ [ location=/Users/adrian/go/bin/templ version=v0.2.781 ]

But let's also trace through the code just to make sure.

The entrypoint of the templ CLI is:

func main() {

The LSP command is a subcommand:

return lspCmd(stdin, stdout, stderr, args[2:])

So, it ends up running the public Run function of the lspcmd package:

err = lspcmd.Run(stdin, stdout, stderr, lspcmd.Arguments{

There's a bit of setup:

func Run(stdin io.Reader, stdout, stderr io.Writer, args Arguments) (err error) {

But then it ends up at

return run(ctx, log, templStream, args)

And we see where the code starts gopls:

log.Info("lsp: starting gopls...")
rwc, err := pls.NewGopls(ctx, log, pls.Options{
Log: args.GoplsLog,
RPCTrace: args.GoplsRPCTrace,
})
if err != nil {
log.Error("failed to start gopls", zap.Error(err))
os.Exit(1)
}

There's an info log item, there's a section at https://templ.guide/commands-and-tools/ide-support#troubleshooting-1 that mentions how to enable logs.

Note that if an error is returned from pls.NewGopls the program stops:

if err != nil {
log.Error("failed to start gopls", zap.Error(err))
os.Exit(1)
}

So, let's make sure that NewGopls does that:

func NewGopls(ctx context.Context, log *zap.Logger, opts Options) (rwc io.ReadWriteCloser, err error) {
location, err := FindGopls()
if err != nil {
return nil, err
}
cmd := exec.Command(location, opts.AsArguments()...)
return newProcessReadWriteCloser(log, cmd)
}

Here's the function that looks for gopls:

func FindGopls() (location string, err error) {
executableName := "gopls"
if runtime.GOOS == "windows" {
executableName = "gopls.exe"
}
pathLocation, err := exec.LookPath(executableName)
if err == nil {
// Found on the path.
return pathLocation, nil
}
// Unexpected error.
if !errors.Is(err, exec.ErrNotFound) {
return "", fmt.Errorf("unexpected error looking for gopls: %w", err)
}
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("unexpected error looking for gopls: %w", err)
}
// Probe standard locations.
locations := []string{
path.Join(home, "go", "bin", executableName),
path.Join(home, ".local", "bin", executableName),
}
for _, location := range locations {
_, err = os.Stat(location)
if err != nil {
continue
}
// Found in a standard location.
return location, nil
}
return "", fmt.Errorf("cannot find gopls on the path (%q), in $HOME/go/bin or $HOME/.local/bin/gopls. You can install gopls with `go install golang.org/x/tools/gopls@latest`", os.Getenv("PATH"))
}

I can't see a bug in there, but I guess there's a chance that you haven't set the executable bit on the gopls binary...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants