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 · 13h ago
golang.go · 118 lines · 3.9 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
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Package golang is everything about Turbo Go that is about *Go*: how the
// editor names itself, which language server it talks to, what a Go project's
// starter files say, and how Go source is coloured.
//
// Everything else the editor does lives in turbo-core, which knows nothing
// about Go. This package is the whole of the difference between Turbo Go and
// Turbo Rust, which is what makes a third editor a matter of writing one of
// these rather than forking anything.
//
//	golang.Register()                 // teach the library to colour Go
//	editor := app.New(screen, name, golang.Profile())
package golang

import (
	"os"
	"path/filepath"

	"rickub.com/turbo-editors/turbo-core/profile"
	"rickub.com/turbo-editors/turbo-core/syntax"
)

// Name and Slug are what the editor calls itself. The slug is also its binary,
// its project directory (as .turbo-go) and the stem of its environment
// variables (as TURBO_GO_…), so it is not free to change.
const (
	Name = "Turbo Go"
	Slug = "turbo-go"
)

// Language is the name Go is known by: the value LanguageOf returns for a .go
// file, and what a snippets file writes in its languages key.
const Language syntax.Language = "go"

// ServerCommand is the language server Turbo Go talks to, and InstallHint the
// single command that installs it.
const (
	ServerCommand = "gopls"
	InstallHint   = "go install golang.org/x/tools/gopls@latest"
)

// Profile returns the editor Turbo Go is.
//
// It is a function rather than a variable because Server.Dirs is worked out
// from the environment, and a variable would freeze whatever GOPATH said when
// the package was linked.
func Profile() profile.Profile {
	return profile.Profile{
		Name:     Name,
		Slug:     Slug,
		Language: "Go",
		// Go is fixed on the bar and takes G, which no other menu claims.
		ToolsMenu: "~G~o",
		// go.mod is the module's boundary, and the module is what gopls loads.
		RootMarkers: []string{"go.mod"},
		Server: profile.Server{
			Command: ServerCommand,
			// gopls needs a subcommand; most language servers do not.
			Args:        []string{"serve"},
			InstallHint: InstallHint,
			Dirs:        []string{BinDir()},
		},
		Templates: profile.Templates{
			Settings: settingsTemplate,
			Snippets: snippetsTemplate,
			Tools:    toolsTemplate,
			Agents:   agentsTemplate,
		},
	}
}

// Register teaches turbo-core to colour Go.
//
// It is called explicitly at start-up rather than from an init function so that
// "which languages does this editor know?" is answered by reading main, not by
// working out which packages were imported.
func Register() {
	syntax.Register(syntax.Definition{
		Language:   Language,
		Extensions: []string{".go"},
		Highlight:  highlight,
	})
}

// highlight colours Go source with go/scanner — the same lexer the Go toolchain
// uses, so the editor and the compiler agree about what a token is.
//
// It works in byte offsets rather than a line at a time, which is why it goes
// through syntax.LineIndex instead of syntax.LineScanner.
func highlight(src string) [][]syntax.Span {
	lines := syntax.NewLineIndex(src)
	out := make([][]syntax.Span, lines.Count())

	tokens := scanTokens(src)
	for i, class := range classify(tokens) {
		lines.AppendSpans(out, tokens[i].start, tokens[i].end, class)
	}
	return out
}

// BinDir returns where "go install" puts binaries: GOBIN when it is set,
// GOPATH/bin otherwise, and the conventional ~/go/bin when neither is.
//
// It is where gopls is looked for after PATH, because `go install` puts it
// somewhere that is very often not on PATH — which is the single most common
// reason completion is missing on a machine that has gopls.
func BinDir() string {
	if gobin := os.Getenv("GOBIN"); gobin != "" {
		return gobin
	}
	if gopath := os.Getenv("GOPATH"); gopath != "" {
		return filepath.Join(gopath, "bin")
	}
	home, err := os.UserHomeDir()
	if err != nil {
		return ""
	}
	return filepath.Join(home, "go", "bin")
}