turbo-editors/turbo-gopublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-go.git
git clone ssh://git@rickub.com/turbo-editors/turbo-go.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

📦 Turbo Go 3d7798b · on v1.0.2 · k33g · 12h ago
gopls_test.go · 93 lines · 2.8 KBGo Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package golang_test

import (
	"context"
	"errors"
	"path/filepath"
	"strings"
	"testing"

	"rickub.com/turbo-editors/turbo-core/lsp"

	"rickub.com/turbo-editors/turbo-go/internal/golang"
)

// TestAgainstRealGopls drives turbo-core's LSP client against an actual
// language server: process, pipes, handshake, completion and shutdown.
//
// It lives here rather than in turbo-core because gopls is Turbo Go's server;
// the library has no language server of its own to test against.
//
// The original comment follows.
//
// It drives the whole client against an actual language
// server: process, pipes, handshake, completion and shutdown.
//
// It is skipped when gopls is not installed and under -short, so a checkout
// with no language server still has a green suite — which is exactly the
// situation the editor itself is built to cope with.
func TestAgainstRealGopls(t *testing.T) {
	if testing.Short() {
		t.Skip("-short: not starting a language server")
	}
	if _, err := lsp.FindServer(golang.Profile().Server); errors.Is(err, lsp.ErrServerNotFound) {
		t.Skipf("%s is not installed; %s", golang.ServerCommand, golang.InstallHint)
	}

	root := t.TempDir()
	writeFile(t, filepath.Join(root, "go.mod"), "module example.test\n\ngo 1.24\n")
	source := "package main\n\nimport \"strings\"\n\nfunc main() {\n\tstrings.\n}\n"
	path := filepath.Join(root, "main.go")
	writeFile(t, path, source)

	server, err := lsp.StartServer(t.Context(), golang.Profile().Server, root, golang.Name)
	if err != nil {
		t.Fatalf("StartServer() error = %v", err)
	}
	client := server.Client()
	t.Cleanup(func() {
		// Not t.Context(): that one is already cancelled by the time cleanups
		// run, and shutting a server down needs a context that is still alive.
		if err := server.Stop(context.Background()); err != nil {
			t.Errorf("Stop() error = %v", err)
		}
	})

	if !client.Ready() {
		t.Fatal("the client is not ready after StartServer returned")
	}
	if err := client.DidOpen(path, source); err != nil {
		t.Fatalf("DidOpen() error = %v", err)
	}

	// Line 5, just after "strings." — the column counts the leading tab as one
	// rune, as the editor does everywhere.
	items, err := client.Complete(t.Context(), path, 5, 9, "\tstrings.")
	if err != nil {
		t.Fatalf("Complete() error = %v", err)
	}
	if len(items) == 0 {
		t.Fatal("gopls offered no completions after \"strings.\"")
	}

	if !containsLabel(items, "Contains") {
		t.Errorf("the completions do not include strings.Contains: %v", labelsOf(items))
	}
}

func containsLabel(items []lsp.CompletionItem, want string) bool {
	for _, item := range items {
		if strings.HasPrefix(item.Label, want) {
			return true
		}
	}
	return false
}

func labelsOf(items []lsp.CompletionItem) []string {
	labels := make([]string, 0, min(len(items), 10))
	for _, item := range items[:min(len(items), 10)] {
		labels = append(labels, item.Label)
	}
	return labels
}