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

Commit bc3e29a

Browse files
committed
Implement nightly updates for rust-analyzer
Also make the binary detection more robust, i.e. wrt. "rls.restart" commands.
1 parent 5a4dde0 commit bc3e29a

File tree

2 files changed

+70
-57
lines changed

2 files changed

+70
-57
lines changed

src/rust-analyzer/persistent_state.ts

+14-17
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import * as vscode from 'vscode';
22

3+
export interface Release {
4+
/**
5+
* ID of a release. Used to disambiguate between different releases under *moving* tags.
6+
*/
7+
id: number;
8+
tag: string;
9+
}
10+
311
export class PersistentState {
412
constructor(private readonly globalState: vscode.Memento) {
5-
const { lastCheck, releaseId, releaseTag: serverVersion } = this;
6-
console.info('PersistentState:', { lastCheck, releaseId, serverVersion });
13+
const { lastCheck, installedRelease } = this;
14+
console.info('PersistentState:', { lastCheck, installedRelease });
715
}
816

917
/**
@@ -16,25 +24,14 @@ export class PersistentState {
1624
await this.globalState.update('lastCheck', value);
1725
}
1826

19-
/**
20-
* Release id of the *nightly* extension.
21-
* Used to check if we should update.
22-
*/
23-
get releaseId(): number | undefined {
24-
return this.globalState.get('releaseId');
25-
}
26-
async updateReleaseId(value: number) {
27-
await this.globalState.update('releaseId', value);
28-
}
29-
3027
/**
3128
* Release tag of the installed server.
3229
* Used to check if we need to update the server.
3330
*/
34-
get releaseTag(): string | undefined {
35-
return this.globalState.get('releaseTag');
31+
get installedRelease(): Release | undefined {
32+
return this.globalState.get('installedRelease');
3633
}
37-
async updateReleaseTag(value: string | undefined) {
38-
await this.globalState.update('releaseTag', value);
34+
async updateInstalledRelease(value: Release | undefined) {
35+
return this.globalState.update('installedRelease', value);
3936
}
4037
}

src/rust-analyzer/rustAnalyzer.ts

+56-40
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ function installDir(): string | undefined {
4444
return undefined;
4545
}
4646

47-
function ensureDir(path: string) {
48-
return !!path && stat(path).catch(() => mkdir(path, { recursive: true }));
49-
}
50-
5147
interface RustAnalyzerConfig {
5248
askBeforeDownload?: boolean;
5349
package: {
@@ -80,38 +76,39 @@ export async function getServer(
8076
'about that [here](https://github.com/rust-analyzer/rust-analyzer/issues) and we ' +
8177
'will consider it.',
8278
);
83-
return undefined;
79+
return;
8480
}
8581

8682
const dir = installDir();
8783
if (!dir) {
8884
return;
85+
} else {
86+
await stat(dir).catch(() => mkdir(dir, { recursive: true }));
8987
}
90-
await ensureDir(dir);
9188

9289
const dest = path.join(dir, binaryName);
9390
const exists = await stat(dest).catch(() => false);
9491

9592
if (!exists) {
96-
await state.updateReleaseTag(undefined);
97-
} else if (state.releaseTag === config.package.releaseTag) {
98-
return dest;
93+
await state.updateInstalledRelease(undefined);
9994
}
10095

101-
if (config.askBeforeDownload) {
102-
const userResponse = await vs.window.showInformationMessage(
103-
`${
104-
state.releaseTag && state.releaseTag !== config.package.releaseTag
105-
? `You seem to have installed release \`${state.releaseTag}\` but requested a different one.`
106-
: ''
107-
}
108-
Release \`${
109-
config.package.releaseTag
110-
}\` of rust-analyzer is not installed.\n
111-
Install to ${dir}?`,
112-
'Download',
113-
);
114-
if (userResponse !== 'Download') {
96+
const now = Date.now();
97+
if (state.installedRelease?.tag == config.package.releaseTag) {
98+
// Release tags that are *moving* - these are expected to point to different
99+
// commits and update as the time goes on. Make sure to poll the GitHub API
100+
// (at most once per hour) to see if we need to update.
101+
const MOVING_TAGS = ['nightly'];
102+
const POLL_INTERVAL = 60 * 60 * 1000;
103+
104+
const shouldCheckForNewRelease = MOVING_TAGS.includes(
105+
config.package.releaseTag,
106+
)
107+
? state.installedRelease === undefined ||
108+
now - (state.lastCheck ?? 0) > POLL_INTERVAL
109+
: false;
110+
111+
if (!shouldCheckForNewRelease) {
115112
return dest;
116113
}
117114
}
@@ -121,19 +118,47 @@ export async function getServer(
121118
'rust-analyzer',
122119
config.package.releaseTag,
123120
);
121+
122+
if (state.installedRelease?.id === release.id) {
123+
return dest;
124+
}
125+
124126
const artifact = release.assets.find(asset => asset.name === binaryName);
125127
if (!artifact) {
126128
throw new Error(`Bad release: ${JSON.stringify(release)}`);
127129
}
128130

131+
if (config.askBeforeDownload) {
132+
const userResponse = await vs.window.showInformationMessage(
133+
`${
134+
state.installedRelease &&
135+
state.installedRelease.tag !== config.package.releaseTag
136+
? `You seem to have installed release \`${state.installedRelease?.tag}\` but requested a different one.`
137+
: ''
138+
}
139+
Release \`${config.package.releaseTag}\` of rust-analyzer ${
140+
!state.installedRelease ? 'is not installed' : 'can be updated'
141+
}.\n
142+
Install to ${dir}?`,
143+
'Download',
144+
);
145+
if (userResponse !== 'Download') {
146+
return exists ? dest : undefined;
147+
}
148+
}
149+
129150
await download({
130151
url: artifact.browser_download_url,
131152
dest,
132153
progressTitle: 'Downloading rust-analyzer server',
133154
mode: 0o755,
134155
});
135156

136-
await state.updateReleaseTag(config.package.releaseTag);
157+
await state.updateLastCheck(now);
158+
await state.updateInstalledRelease({
159+
id: release.id,
160+
tag: config.package.releaseTag,
161+
});
137162

138163
return dest;
139164
}
@@ -167,34 +192,25 @@ export async function createLanguageClient(
167192
await rustup.ensureComponents(config.rustup, REQUIRED_COMPONENTS);
168193
}
169194

170-
if (!config.rustAnalyzer.path) {
171-
await getServer(
195+
const binPath =
196+
config.rustAnalyzer.path ||
197+
(await getServer(
172198
{
173199
askBeforeDownload: true,
174200
package: { releaseTag: config.rustAnalyzer.releaseTag },
175201
},
176202
new PersistentState(state),
177-
);
203+
));
204+
205+
if (!binPath) {
206+
throw new Error("Couldn't fetch Rust Analyzer binary");
178207
}
179208

180209
if (INSTANCE) {
181210
return INSTANCE;
182211
}
183212

184213
const serverOptions: lc.ServerOptions = async () => {
185-
const binPath =
186-
config.rustAnalyzer.path ||
187-
(await getServer(
188-
{
189-
package: { releaseTag: config.rustAnalyzer.releaseTag },
190-
},
191-
new PersistentState(state),
192-
));
193-
194-
if (!binPath) {
195-
throw new Error("Couldn't fetch Rust Analyzer binary");
196-
}
197-
198214
const childProcess = child_process.exec(binPath);
199215
if (config.logToFile) {
200216
const logPath = path.join(folder.uri.fsPath, `ra-${Date.now()}.log`);

0 commit comments

Comments
 (0)