| 📦 Turbo Go 3d7798b k33g 8h ago | 1 | // Package golang is everything about Turbo Go that is about *Go*: how the |
| 2 | // editor names itself, which language server it talks to, what a Go project's |
| 3 | // starter files say, and how Go source is coloured. |
| 4 | // |
| 5 | // Everything else the editor does lives in turbo-core, which knows nothing |
| 6 | // about Go. This package is the whole of the difference between Turbo Go and |
| 7 | // Turbo Rust, which is what makes a third editor a matter of writing one of |
| 8 | // these rather than forking anything. |
| 9 | // |
| 10 | // golang.Register() // teach the library to colour Go |
| 11 | // editor := app.New(screen, name, golang.Profile()) |
| 12 | package golang |
| 13 | |
| 14 | import ( |
| 15 | "os" |
| 16 | "path/filepath" |
| 17 | |
| 18 | "rickub.com/turbo-editors/turbo-core/profile" |
| 19 | "rickub.com/turbo-editors/turbo-core/syntax" |
| 20 | ) |
| 21 | |
| 22 | // Name and Slug are what the editor calls itself. The slug is also its binary, |
| 23 | // its project directory (as .turbo-go) and the stem of its environment |
| 24 | // variables (as TURBO_GO_…), so it is not free to change. |
| 25 | const ( |
| 26 | Name = "Turbo Go" |
| 27 | Slug = "turbo-go" |
| 28 | ) |
| 29 | |
| 30 | // Language is the name Go is known by: the value LanguageOf returns for a .go |
| 31 | // file, and what a snippets file writes in its languages key. |
| 32 | const Language syntax.Language = "go" |
| 33 | |
| 34 | // ServerCommand is the language server Turbo Go talks to, and InstallHint the |
| 35 | // single command that installs it. |
| 36 | const ( |
| 37 | ServerCommand = "gopls" |
| 38 | InstallHint = "go install golang.org/x/tools/gopls@latest" |
| 39 | ) |
| 40 | |
| 41 | // Profile returns the editor Turbo Go is. |
| 42 | // |
| 43 | // It is a function rather than a variable because Server.Dirs is worked out |
| 44 | // from the environment, and a variable would freeze whatever GOPATH said when |
| 45 | // the package was linked. |
| 46 | func Profile() profile.Profile { |
| 47 | return profile.Profile{ |
| 48 | Name: Name, |
| 49 | Slug: Slug, |
| 50 | Language: "Go", |
| 51 | // Go is fixed on the bar and takes G, which no other menu claims. |
| 52 | ToolsMenu: "~G~o", |
| 53 | // go.mod is the module's boundary, and the module is what gopls loads. |
| 54 | RootMarkers: []string{"go.mod"}, |
| 55 | Server: profile.Server{ |
| 56 | Command: ServerCommand, |
| 57 | // gopls needs a subcommand; most language servers do not. |
| 58 | Args: []string{"serve"}, |
| 59 | InstallHint: InstallHint, |
| 60 | Dirs: []string{BinDir()}, |
| 61 | }, |
| 62 | Templates: profile.Templates{ |
| 63 | Settings: settingsTemplate, |
| 64 | Snippets: snippetsTemplate, |
| 65 | Tools: toolsTemplate, |
| 66 | Agents: agentsTemplate, |
| 67 | }, |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | // Register teaches turbo-core to colour Go. |
| 72 | // |
| 73 | // It is called explicitly at start-up rather than from an init function so that |
| 74 | // "which languages does this editor know?" is answered by reading main, not by |
| 75 | // working out which packages were imported. |
| 76 | func Register() { |
| 77 | syntax.Register(syntax.Definition{ |
| 78 | Language: Language, |
| 79 | Extensions: []string{".go"}, |
| 80 | Highlight: highlight, |
| 81 | }) |
| 82 | } |
| 83 | |
| 84 | // highlight colours Go source with go/scanner — the same lexer the Go toolchain |
| 85 | // uses, so the editor and the compiler agree about what a token is. |
| 86 | // |
| 87 | // It works in byte offsets rather than a line at a time, which is why it goes |
| 88 | // through syntax.LineIndex instead of syntax.LineScanner. |
| 89 | func highlight(src string) [][]syntax.Span { |
| 90 | lines := syntax.NewLineIndex(src) |
| 91 | out := make([][]syntax.Span, lines.Count()) |
| 92 | |
| 93 | tokens := scanTokens(src) |
| 94 | for i, class := range classify(tokens) { |
| 95 | lines.AppendSpans(out, tokens[i].start, tokens[i].end, class) |
| 96 | } |
| 97 | return out |
| 98 | } |
| 99 | |
| 100 | // BinDir returns where "go install" puts binaries: GOBIN when it is set, |
| 101 | // GOPATH/bin otherwise, and the conventional ~/go/bin when neither is. |
| 102 | // |
| 103 | // It is where gopls is looked for after PATH, because `go install` puts it |
| 104 | // somewhere that is very often not on PATH — which is the single most common |
| 105 | // reason completion is missing on a machine that has gopls. |
| 106 | func BinDir() string { |
| 107 | if gobin := os.Getenv("GOBIN"); gobin != "" { |
| 108 | return gobin |
| 109 | } |
| 110 | if gopath := os.Getenv("GOPATH"); gopath != "" { |
| 111 | return filepath.Join(gopath, "bin") |
| 112 | } |
| 113 | home, err := os.UserHomeDir() |
| 114 | if err != nil { |
| 115 | return "" |
| 116 | } |
| 117 | return filepath.Join(home, "go", "bin") |
| 118 | } |