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")
}
|