Skip to content
This repository was archived by the owner on Nov 18, 2022. It is now read-only.

Commit 8c67bbc

Browse files
committed
Allow specifying releaseTag for rust-analyzer to be used/downloaded
1 parent 10f1e95 commit 8c67bbc

File tree

4 files changed

+107
-40
lines changed

4 files changed

+107
-40
lines changed

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,16 @@
470470
"default": {},
471471
"description": "Settings passed down to rust-analyzer server",
472472
"scope": "resource"
473+
},
474+
"rust.rust-analyzer.releaseTag": {
475+
"type": "string",
476+
"default": "nightly",
477+
"description": "Which binary release to download and use"
478+
},
479+
"rust.rust-analyzer.path": {
480+
"type": ["string", "null"],
481+
"default": null,
482+
"description": "When specified, uses the rust-analyzer binary at a given path"
473483
}
474484
}
475485
}

src/configuration.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ export class RLSConfiguration {
8383
);
8484
}
8585

86+
public get rustAnalyzer(): { path?: string; releaseTag: string } {
87+
const cfg = this.configuration;
88+
const releaseTag = cfg.get('rust.rust-analyzer.releaseTag', 'nightly');
89+
const path = cfg.get<string>('rust.rust-analyzer.path');
90+
return { releaseTag, ...{ path } };
91+
}
92+
8693
public get revealOutputChannelOn(): RevealOutputChannelOn {
8794
return RLSConfiguration.readRevealOutputChannelOn(this.configuration);
8895
}

src/extension.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,6 @@ export async function activate(context: ExtensionContext): Promise<Api> {
7171
});
7272
}
7373

74-
if (config.get('rust-client.engine') === 'rust-analyzer') {
75-
await rustAnalyzer.getServer({
76-
askBeforeDownload: true,
77-
package: { releaseTag: '2020-05-04' },
78-
});
79-
}
80-
8174
return { activeWorkspace };
8275
}
8376

@@ -213,7 +206,7 @@ export class ClientWorkspace {
213206
disabled: this.config.rustupDisabled,
214207
},
215208
rls: { path: this.config.rlsPath },
216-
rustAnalyzer: { releaseTag: '2020-05-04' },
209+
rustAnalyzer: this.config.rustAnalyzer,
217210
});
218211

219212
client.onDidChangeState(({ newState }) => {

src/rustAnalyzer.ts

Lines changed: 89 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { Observable } from './utils/observable';
1313

1414
const stat = promisify(fs.stat);
1515
const mkdir = promisify(fs.mkdir);
16+
const readFile = promisify(fs.readFile);
17+
const writeFile = promisify(fs.writeFile);
1618

1719
const REQUIRED_COMPONENTS = ['rust-src'];
1820

@@ -43,18 +45,29 @@ function installDir(): string | undefined {
4345
return undefined;
4446
}
4547

46-
async function ensureInstallDir() {
47-
const dir = installDir();
48-
if (!dir) {
49-
return;
50-
}
51-
const exists = await stat(dir).then(
52-
() => true,
53-
() => false,
54-
);
55-
if (!exists) {
56-
await mkdir(dir);
48+
/** Returns a path where persistent data for rust-analyzer should be installed. */
49+
function metadataDir(): string | undefined {
50+
if (process.platform === 'linux' || process.platform === 'darwin') {
51+
// Prefer, in this order:
52+
// 1. $XDG_CONFIG_HOME/rust-analyzer
53+
// 2. $HOME/.config/rust-analyzer
54+
const { HOME, XDG_CONFIG_HOME } = process.env;
55+
const baseDir = XDG_CONFIG_HOME || (HOME && path.join(HOME, '.config'));
56+
57+
return baseDir && path.resolve(path.join(baseDir, 'rust-analyzer'));
58+
} else if (process.platform === 'win32') {
59+
// %LocalAppData%\rust-analyzer\
60+
const { LocalAppData } = process.env;
61+
return (
62+
LocalAppData && path.resolve(path.join(LocalAppData, 'rust-analyzer'))
63+
);
5764
}
65+
66+
return undefined;
67+
}
68+
69+
function ensureDir(path: string) {
70+
return !!path && stat(path).catch(() => mkdir(path, { recursive: true }));
5871
}
5972

6073
interface RustAnalyzerConfig {
@@ -64,9 +77,44 @@ interface RustAnalyzerConfig {
6477
};
6578
}
6679

67-
export async function getServer(
68-
config: RustAnalyzerConfig,
69-
): Promise<string | undefined> {
80+
interface Metadata {
81+
releaseTag: string;
82+
}
83+
84+
async function readMetadata(): Promise<Metadata | {}> {
85+
const stateDir = metadataDir();
86+
if (!stateDir) {
87+
return { kind: 'error', code: 'NotSupported' };
88+
}
89+
90+
const filePath = path.join(stateDir, 'metadata.json');
91+
if (!(await stat(filePath).catch(() => false))) {
92+
return { kind: 'error', code: 'FileMissing' };
93+
}
94+
95+
const contents = await readFile(filePath, 'utf8');
96+
const obj = JSON.parse(contents);
97+
return typeof obj === 'object' ? obj : {};
98+
}
99+
100+
async function writeMetadata(config: Metadata) {
101+
const stateDir = metadataDir();
102+
if (!stateDir) {
103+
return false;
104+
}
105+
106+
if (!(await ensureDir(stateDir))) {
107+
return false;
108+
}
109+
110+
const filePath = path.join(stateDir, 'metadata.json');
111+
return writeFile(filePath, JSON.stringify(config)).then(() => true);
112+
}
113+
114+
export async function getServer({
115+
askBeforeDownload,
116+
package: pkg,
117+
}: RustAnalyzerConfig): Promise<string | undefined> {
70118
let binaryName: string | undefined;
71119
if (process.arch === 'x64' || process.arch === 'ia32') {
72120
if (process.platform === 'linux') {
@@ -95,19 +143,24 @@ export async function getServer(
95143
if (!dir) {
96144
return;
97145
}
98-
await ensureInstallDir();
146+
await ensureDir(dir);
147+
148+
const metadata: Partial<Metadata> = await readMetadata().catch(() => ({}));
149+
99150
const dest = path.join(dir, binaryName);
100-
const exists = await stat(dest).then(
101-
() => true,
102-
() => false,
103-
);
104-
if (exists) {
151+
const exists = await stat(dest).catch(() => false);
152+
if (exists && metadata.releaseTag === pkg.releaseTag) {
105153
return dest;
106154
}
107155

108-
if (config.askBeforeDownload) {
156+
if (askBeforeDownload) {
109157
const userResponse = await vs.window.showInformationMessage(
110-
`Language server release ${config.package.releaseTag} for rust-analyzer is not installed.\n
158+
`${
159+
metadata.releaseTag && metadata.releaseTag !== pkg.releaseTag
160+
? `You seem to have installed release \`${metadata.releaseTag}\` but requested a different one.`
161+
: ''
162+
}
163+
Release \`${pkg.releaseTag}\` of rust-analyzer is not installed.\n
111164
Install to ${dir}?`,
112165
'Download',
113166
);
@@ -119,7 +172,7 @@ export async function getServer(
119172
const release = await fetchRelease(
120173
'rust-analyzer',
121174
'rust-analyzer',
122-
config.package.releaseTag,
175+
pkg.releaseTag,
123176
);
124177
const artifact = release.assets.find(asset => asset.name === binaryName);
125178
if (!artifact) {
@@ -133,6 +186,10 @@ export async function getServer(
133186
{ mode: 0o755 },
134187
);
135188

189+
await writeMetadata({ releaseTag: pkg.releaseTag }).catch(() => {
190+
vs.window.showWarningMessage(`Couldn't save rust-analyzer metadata`);
191+
});
192+
136193
return dest;
137194
}
138195

@@ -156,27 +213,27 @@ export async function createLanguageClient(
156213
revealOutputChannelOn?: lc.RevealOutputChannelOn;
157214
logToFile?: boolean;
158215
rustup: { disabled: boolean; path: string; channel: string };
159-
rustAnalyzer?: { path?: string; releaseTag?: string };
216+
rustAnalyzer: { path?: string; releaseTag: string };
160217
},
161218
): Promise<lc.LanguageClient> {
162219
await rustup.ensureToolchain(config.rustup);
163220
await rustup.ensureComponents(config.rustup, REQUIRED_COMPONENTS);
164-
await getServer({
165-
askBeforeDownload: true,
166-
package: { releaseTag: config.rustAnalyzer?.releaseTag || '2020-05-04' },
167-
});
221+
if (!config.rustAnalyzer.path) {
222+
await getServer({
223+
askBeforeDownload: true,
224+
package: { releaseTag: config.rustAnalyzer.releaseTag },
225+
});
226+
}
168227

169228
if (INSTANCE) {
170229
return INSTANCE;
171230
}
172231

173232
const serverOptions: lc.ServerOptions = async () => {
174233
const binPath =
175-
config.rustAnalyzer?.path ||
234+
config.rustAnalyzer.path ||
176235
(await getServer({
177-
package: {
178-
releaseTag: config.rustAnalyzer?.releaseTag || '2020-05-04',
179-
},
236+
package: { releaseTag: config.rustAnalyzer.releaseTag },
180237
}));
181238

182239
if (!binPath) {

0 commit comments

Comments
 (0)