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

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

🛟 Updated. 28d5985 · on v1.0.2 · k33g · 20h ago
profile.go · 184 lines · 6.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
 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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Package profile holds everything that makes one editor built on turbo-core
// differ from another: what it is called, which language it is for, which
// language server it talks to, and the starter files it writes.
//
// It is the seam the whole library turns on. Turbo Go, Turbo Rust, Turbo
// Python, Turbo MoonBit, Turbo Golo and Turbo JS are the same editor with six
// different values of this type, so anything that would otherwise be a
// hardcoded "turbo-go" or "gopls" is a field here instead.
//
// The package is deliberately data and nothing else — no filesystem beyond the
// user's configuration directory, no processes, no colours — so that every
// other package in the library can depend on it without acquiring a dependency
// of its own.
//
//	p := profile.Profile{
//		Name:     "Turbo Go",
//		Slug:     "turbo-go",
//		Language: "Go",
//	}
//	p.ProjectDir() // ".turbo-go"
package profile

import (
	"os"
	"path/filepath"
	"strings"
)

// Profile is one editor's identity and the language it is built for.
//
// The zero value is not useful: Slug is what every derived path is built from,
// and an editor without one would write its project files into a directory
// called ".".
type Profile struct {
	// Name is what the editor calls itself, in its window titles and its About
	// box: "Turbo Go", "Turbo Rust".
	Name string

	// Slug is its one-word name: "turbo-go". The binary, the project directory
	// (with a dot in front of it), the user's own configuration directory and
	// the environment variables that override them are all built from it.
	Slug string

	// Language names what the editor is for, in prose meant to be read: "Go",
	// "Rust". It appears in messages, not in file formats.
	Language string

	// ToolsMenu is the label of the menu the project's own commands appear in,
	// with its hot key marked: "~G~o", "Rus~t~". The plain name — what a tools
	// file writes in its menu key — is this with the markers taken out.
	ToolsMenu string

	// RootMarkers are the files that mark the root of a project, in the order
	// they are looked for: "go.mod", "Cargo.toml". The language server is
	// started in the first directory above the file being edited that holds
	// one of them.
	RootMarkers []string

	// Server is the language server this editor talks to.
	Server Server

	// Templates are the starter files the editor offers to write.
	Templates Templates
}

// Server describes the language server an editor talks to, in enough detail to
// find it, start it, and say what to do when it is missing.
type Server struct {
	// Command is the executable's name: "gopls", "rust-analyzer".
	Command string

	// Args are the arguments it is started with. gopls wants "serve";
	// rust-analyzer wants none.
	Args []string

	// InstallHint is a single command the user can copy when the server is not
	// installed. It is shown on the status bar, so it has to fit on a line.
	InstallHint string

	// Dirs are extra directories to look in when PATH has nothing, in the order
	// they are tried: GOPATH/bin for Go, ~/.cargo/bin for Rust. They are
	// resolved by the editor at start-up, because working them out means
	// reading environment variables this package deliberately does not know
	// about.
	Dirs []string
}

// Templates are the files the editor writes into a project's own directory
// when asked to, one per menu item that offers to create one.
//
// They are held here rather than compiled into the packages that write them
// because their contents are the one part of those files that is about a
// particular language: a Go project's tools are `go build ./...`, a Rust
// project's are `cargo build`.
type Templates struct {
	// Settings is the starter settings.toml. It is formatted with two
	// arguments, in this order: the theme name and the autosave delay, both
	// with %q.
	Settings string

	// Snippets is the starter snippets.toml. It is formatted with two
	// arguments, in this order: the name of the group an ungrouped snippet
	// falls into, and the path to the user's own snippets file, both with %s.
	Snippets string

	// Tools is the starter tools.toml. It takes no formatting arguments.
	Tools string

	// Agents is the starter acp.toml, listing the coding agents the editor can
	// open a window on. It is formatted with two arguments, in this order: the
	// editor's own project directory — ".turbo-go" — and the path to the
	// user's own agents file, both with %s.
	Agents string
}

// ProjectDir returns the directory this editor keeps a project's own files in:
// its settings, its snippets, its tools.
//
// It is the slug with a dot in front, which is the convention every other tool
// in a repository follows.
//
//	profile.Profile{Slug: "turbo-go"}.ProjectDir() // ".turbo-go"
func (p Profile) ProjectDir() string { return "." + p.Slug }

// UserDir returns the directory this editor keeps the user's own files in — the
// ones that follow them between projects — or "" when the system cannot say
// where that would be.
//
// The environment variable named by DirEnvVar wins when it is set, which is
// what lets a test point the editor at a directory of its own instead of at the
// real one.
//
//	profile.Profile{Slug: "turbo-go"}.UserDir() // "~/.config/turbo-go"
func (p Profile) UserDir() string {
	if dir := os.Getenv(p.DirEnvVar()); dir != "" {
		return dir
	}

	config, err := os.UserConfigDir()
	if err != nil {
		return ""
	}
	return filepath.Join(config, p.Slug)
}

// ThemeDir returns the directory the user's own themes are read from, or ""
// when there is nowhere to read them from.
//
// The environment variable named by ThemeDirEnvVar wins when it is set, and
// names the theme directory itself rather than the directory above it — a test
// wants to say exactly where the themes are.
func (p Profile) ThemeDir() string {
	if dir := os.Getenv(p.ThemeDirEnvVar()); dir != "" {
		return dir
	}

	user := p.UserDir()
	if user == "" {
		return ""
	}
	return filepath.Join(user, "themes")
}

// DirEnvVar names the environment variable that overrides UserDir.
//
//	profile.Profile{Slug: "turbo-go"}.DirEnvVar() // "TURBO_GO_DIR"
func (p Profile) DirEnvVar() string { return p.envPrefix() + "_DIR" }

// ThemeDirEnvVar names the environment variable that overrides ThemeDir.
//
//	profile.Profile{Slug: "turbo-go"}.ThemeDirEnvVar() // "TURBO_GO_THEME_DIR"
func (p Profile) ThemeDirEnvVar() string { return p.envPrefix() + "_THEME_DIR" }

// SnippetDirEnvVar names the environment variable that overrides where the
// user's own snippets are read from.
//
//	profile.Profile{Slug: "turbo-go"}.SnippetDirEnvVar() // "TURBO_GO_SNIPPET_DIR"
func (p Profile) SnippetDirEnvVar() string { return p.envPrefix() + "_SNIPPET_DIR" }

// envPrefix turns the slug into the shape an environment variable takes:
// upper case, with dashes as underscores.
func (p Profile) envPrefix() string {
	return strings.ToUpper(strings.ReplaceAll(p.Slug, "-", "_"))
}