aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src')
-rw-r--r--editors/code/src/main.ts25
-rw-r--r--editors/code/src/net.ts88
-rw-r--r--editors/code/src/util.ts1
3 files changed, 65 insertions, 49 deletions
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 670f2ebfd..12b4d0510 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -43,12 +43,16 @@ export async function activate(context: vscode.ExtensionContext) {
43 const config = new Config(context); 43 const config = new Config(context);
44 const state = new PersistentState(context.globalState); 44 const state = new PersistentState(context.globalState);
45 const serverPath = await bootstrap(config, state).catch(err => { 45 const serverPath = await bootstrap(config, state).catch(err => {
46 let message = "Failed to bootstrap rust-analyzer."; 46 let message = "bootstrap error. ";
47
47 if (err.code === "EBUSY" || err.code === "ETXTBSY") { 48 if (err.code === "EBUSY" || err.code === "ETXTBSY") {
48 message += " Other vscode windows might be using rust-analyzer, " + 49 message += "Other vscode windows might be using rust-analyzer, ";
49 "you should close them and reload this window to retry."; 50 message += "you should close them and reload this window to retry. ";
50 } 51 }
51 message += " Open \"Help > Toggle Developer Tools > Console\" to see the logs"; 52
53 message += 'Open "Help > Toggle Developer Tools > Console" to see the logs ';
54 message += '(enable verbose logs with "rust-analyzer.trace.extension")';
55
52 log.error("Bootstrap error", err); 56 log.error("Bootstrap error", err);
53 throw new Error(message); 57 throw new Error(message);
54 }); 58 });
@@ -178,7 +182,11 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
178 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 182 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
179 183
180 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); 184 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix");
181 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer extension"); 185 await download({
186 url: artifact.browser_download_url,
187 dest,
188 progressTitle: "Downloading rust-analyzer extension",
189 });
182 190
183 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); 191 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest));
184 await fs.unlink(dest); 192 await fs.unlink(dest);
@@ -299,7 +307,12 @@ async function getServer(config: Config, state: PersistentState): Promise<string
299 if (err.code !== "ENOENT") throw err; 307 if (err.code !== "ENOENT") throw err;
300 }); 308 });
301 309
302 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 }); 310 await download({
311 url: artifact.browser_download_url,
312 dest,
313 progressTitle: "Downloading rust-analyzer server",
314 mode: 0o755
315 });
303 316
304 // Patching executable if that's NixOS. 317 // Patching executable if that's NixOS.
305 if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) { 318 if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) {
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index 9debdc57b..e02fd6d4f 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -60,32 +60,40 @@ export interface GithubRelease {
60 }>; 60 }>;
61} 61}
62 62
63interface DownloadOpts {
64 progressTitle: string;
65 url: string;
66 dest: string;
67 mode?: number;
68}
63 69
64export async function download( 70export async function download(opts: DownloadOpts) {
65 downloadUrl: string, 71 // Put the artifact into a temporary folder to prevent partially downloaded files when user kills vscode
66 destinationPath: string, 72 await withTempDir(async tempDir => {
67 progressTitle: string, 73 const tempFile = path.join(tempDir, path.basename(opts.dest));
68 { mode }: { mode?: number } = {}, 74
69) { 75 await vscode.window.withProgress(
70 await vscode.window.withProgress( 76 {
71 { 77 location: vscode.ProgressLocation.Notification,
72 location: vscode.ProgressLocation.Notification, 78 cancellable: false,
73 cancellable: false, 79 title: opts.progressTitle
74 title: progressTitle 80 },
75 }, 81 async (progress, _cancellationToken) => {
76 async (progress, _cancellationToken) => { 82 let lastPercentage = 0;
77 let lastPercentage = 0; 83 await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => {
78 await downloadFile(downloadUrl, destinationPath, mode, (readBytes, totalBytes) => { 84 const newPercentage = (readBytes / totalBytes) * 100;
79 const newPercentage = (readBytes / totalBytes) * 100; 85 progress.report({
80 progress.report({ 86 message: newPercentage.toFixed(0) + "%",
81 message: newPercentage.toFixed(0) + "%", 87 increment: newPercentage - lastPercentage
82 increment: newPercentage - lastPercentage 88 });
89
90 lastPercentage = newPercentage;
83 }); 91 });
92 }
93 );
84 94
85 lastPercentage = newPercentage; 95 await moveFile(tempFile, opts.dest);
86 }); 96 });
87 }
88 );
89} 97}
90 98
91/** 99/**
@@ -114,28 +122,23 @@ async function downloadFile(
114 122
115 log.debug("Downloading file of", totalBytes, "bytes size from", url, "to", destFilePath); 123 log.debug("Downloading file of", totalBytes, "bytes size from", url, "to", destFilePath);
116 124
117 // Put the artifact into a temporary folder to prevent partially downloaded files when user kills vscode 125 let readBytes = 0;
118 await withTempFile(async tempFilePath => { 126 res.body.on("data", (chunk: Buffer) => {
119 const destFileStream = fs.createWriteStream(tempFilePath, { mode }); 127 readBytes += chunk.length;
120 128 onProgress(readBytes, totalBytes);
121 let readBytes = 0; 129 });
122 res.body.on("data", (chunk: Buffer) => {
123 readBytes += chunk.length;
124 onProgress(readBytes, totalBytes);
125 });
126 130
127 await pipeline(res.body, destFileStream); 131 const destFileStream = fs.createWriteStream(destFilePath, { mode });
128 await new Promise<void>(resolve => { 132 await pipeline(res.body, destFileStream);
129 destFileStream.on("close", resolve); 133 await new Promise<void>(resolve => {
130 destFileStream.destroy(); 134 destFileStream.on("close", resolve);
131 // This workaround is awaiting to be removed when vscode moves to newer nodejs version: 135 destFileStream.destroy();
132 // https://github.com/rust-analyzer/rust-analyzer/issues/3167 136 // This workaround is awaiting to be removed when vscode moves to newer nodejs version:
133 }); 137 // https://github.com/rust-analyzer/rust-analyzer/issues/3167
134 await moveFile(tempFilePath, destFilePath);
135 }); 138 });
136} 139}
137 140
138async function withTempFile(scope: (tempFilePath: string) => Promise<void>) { 141async function withTempDir(scope: (tempDirPath: string) => Promise<void>) {
139 // Based on the great article: https://advancedweb.hu/secure-tempfiles-in-nodejs-without-dependencies/ 142 // Based on the great article: https://advancedweb.hu/secure-tempfiles-in-nodejs-without-dependencies/
140 143
141 // `.realpath()` should handle the cases where os.tmpdir() contains symlinks 144 // `.realpath()` should handle the cases where os.tmpdir() contains symlinks
@@ -144,7 +147,7 @@ async function withTempFile(scope: (tempFilePath: string) => Promise<void>) {
144 const tempDir = await fs.promises.mkdtemp(path.join(osTempDir, "rust-analyzer")); 147 const tempDir = await fs.promises.mkdtemp(path.join(osTempDir, "rust-analyzer"));
145 148
146 try { 149 try {
147 return await scope(path.join(tempDir, "file")); 150 return await scope(tempDir);
148 } finally { 151 } finally {
149 // We are good citizens :D 152 // We are good citizens :D
150 void fs.promises.rmdir(tempDir, { recursive: true }).catch(log.error); 153 void fs.promises.rmdir(tempDir, { recursive: true }).catch(log.error);
@@ -161,6 +164,7 @@ async function moveFile(src: fs.PathLike, dest: fs.PathLike) {
161 await fs.promises.unlink(src); 164 await fs.promises.unlink(src);
162 } else { 165 } else {
163 log.error(`Failed to rename the file ${src} -> ${dest}`, err); 166 log.error(`Failed to rename the file ${src} -> ${dest}`, err);
167 throw err;
164 } 168 }
165 } 169 }
166} 170}
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index fe3fb71cd..fec4c3295 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -26,7 +26,6 @@ export const log = new class {
26 } 26 }
27 27
28 error(message?: any, ...optionalParams: any[]): void { 28 error(message?: any, ...optionalParams: any[]): void {
29 if (!log.enabled) return;
30 debugger; 29 debugger;
31 // eslint-disable-next-line no-console 30 // eslint-disable-next-line no-console
32 console.error(message, ...optionalParams); 31 console.error(message, ...optionalParams);