turbo-editors/turbo-rustpublic Fork 0
v1.0.0
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-rust.git
git clone ssh://git@rickub.com/turbo-editors/turbo-rust.git

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

📦 Turbo Rust 713ea5c · on v1.0.0 · k33g · 11h ago
rustlang.go · 126 lines · 4.4 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
// Package rustlang is everything about Turbo Rust that is about *Rust*: how the
// editor names itself, which language server it talks to, what a Cargo
// project's starter files say, and how Rust source is coloured.
//
// Everything else the editor does lives in turbo-core, which knows nothing
// about Rust. This package is the whole of the difference between Turbo Rust
// and Turbo Go, which is what makes a third editor a matter of writing one of
// these rather than forking anything.
//
//	rustlang.Register()               // teach the library to colour Rust
//	editor := app.New(screen, name, rustlang.Profile())
package rustlang

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-rust) and the stem of its environment
// variables (as TURBO_RUST_…), so it is not free to change.
const (
	Name = "Turbo Rust"
	Slug = "turbo-rust"
)

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

// ServerCommand is the language server Turbo Rust talks to, and InstallHint the
// single command that installs it.
//
// rustup is the way nearly everybody has Rust, and `rustup component add` is
// the shortest true answer for them. Somebody who installed Rust from their
// distribution will have their own package for it; the hint has to fit on a
// status bar, so it names the common case.
const (
	ServerCommand = "rust-analyzer"
	InstallHint   = "rustup component add rust-analyzer"
)

// Profile returns the editor Turbo Rust is.
//
// It is a function rather than a variable because Server.Dirs is worked out
// from the environment, and a variable would freeze whatever CARGO_HOME said
// when the package was linked.
func Profile() profile.Profile {
	return profile.Profile{
		Name:     Name,
		Slug:     Slug,
		Language: "Rust",
		// R is Run's and S is Search's, so the hot key lands on the T. The
		// alternative was naming the menu Cargo, which would have taken C — but
		// the menu holds whatever the project put in its tools file, and that is
		// not always cargo.
		ToolsMenu: "Rus~t~",
		// Cargo.toml is the crate's boundary, and the crate is what
		// rust-analyzer loads. A workspace root has one too, and the nearest one
		// going up is the right answer for both.
		RootMarkers: []string{"Cargo.toml"},
		Server: profile.Server{
			Command: ServerCommand,
			// rust-analyzer takes no subcommand, unlike gopls.
			Args:        nil,
			InstallHint: InstallHint,
			Dirs:        []string{CargoBinDir(), RustupToolchainBinDir()},
		},
		Templates: profile.Templates{
			Settings: settingsTemplate,
			Snippets: snippetsTemplate,
			Tools:    toolsTemplate,
			Agents:   agentsTemplate,
		},
	}
}

// Register teaches turbo-core to colour Rust.
//
// 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{".rs"},
		Highlight:  Highlight,
	})
}

// CargoBinDir returns where cargo puts the binaries it installs: CARGO_HOME/bin
// when CARGO_HOME is set, and ~/.cargo/bin otherwise.
//
// It is one of the two places rust-analyzer is looked for after PATH.
func CargoBinDir() string {
	if home := os.Getenv("CARGO_HOME"); home != "" {
		return filepath.Join(home, "bin")
	}
	home, err := os.UserHomeDir()
	if err != nil {
		return ""
	}
	return filepath.Join(home, ".cargo", "bin")
}

// RustupToolchainBinDir returns rustup's own binary directory,
// RUSTUP_HOME/bin, defaulting to ~/.rustup/bin.
//
// `rustup component add rust-analyzer` — the command the install hint gives —
// puts a shim on PATH through ~/.cargo/bin, but a machine where the cargo
// directory was never added to PATH still has this one, and looking in both is
// cheaper than explaining the difference to somebody whose completion is
// silently missing.
func RustupToolchainBinDir() string {
	if home := os.Getenv("RUSTUP_HOME"); home != "" {
		return filepath.Join(home, "bin")
	}
	home, err := os.UserHomeDir()
	if err != nil {
		return ""
	}
	return filepath.Join(home, ".rustup", "bin")
}