aboutsummaryrefslogtreecommitdiff
path: root/editors/code
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code')
-rw-r--r--editors/code/package.json2
-rw-r--r--editors/code/src/main.ts13
-rw-r--r--editors/code/src/net.ts77
-rw-r--r--editors/code/src/util.ts1
4 files changed, 53 insertions, 40 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index e6ceb235f..68484a370 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -426,7 +426,7 @@
426 "Full log" 426 "Full log"
427 ], 427 ],
428 "default": "off", 428 "default": "off",
429 "description": "Trace requests to the rust-analyzer" 429 "description": "Trace requests to the rust-analyzer (this is usually overly verbose and not recommended for regular users)"
430 }, 430 },
431 "rust-analyzer.trace.extension": { 431 "rust-analyzer.trace.extension": {
432 "description": "Enable logging of VS Code extensions itself", 432 "description": "Enable logging of VS Code extensions itself",
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 301754733..12b4d0510 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -182,7 +182,11 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
182 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 182 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
183 183
184 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); 184 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix");
185 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 });
186 190
187 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); 191 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest));
188 await fs.unlink(dest); 192 await fs.unlink(dest);
@@ -303,7 +307,12 @@ async function getServer(config: Config, state: PersistentState): Promise<string
303 if (err.code !== "ENOENT") throw err; 307 if (err.code !== "ENOENT") throw err;
304 }); 308 });
305 309
306 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 });
307 316
308 // Patching executable if that's NixOS. 317 // Patching executable if that's NixOS.
309 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 0e7dd29c2..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/**
@@ -120,21 +128,17 @@ async function downloadFile(
120 onProgress(readBytes, totalBytes); 128 onProgress(readBytes, totalBytes);
121 }); 129 });
122 130
123 // Put the artifact into a temporary folder to prevent partially downloaded files when user kills vscode 131 const destFileStream = fs.createWriteStream(destFilePath, { mode });
124 await withTempFile(async tempFilePath => { 132 await pipeline(res.body, destFileStream);
125 const destFileStream = fs.createWriteStream(tempFilePath, { mode }); 133 await new Promise<void>(resolve => {
126 await pipeline(res.body, destFileStream); 134 destFileStream.on("close", resolve);
127 await new Promise<void>(resolve => { 135 destFileStream.destroy();
128 destFileStream.on("close", resolve); 136 // This workaround is awaiting to be removed when vscode moves to newer nodejs version:
129 destFileStream.destroy(); 137 // https://github.com/rust-analyzer/rust-analyzer/issues/3167
130 // This workaround is awaiting to be removed when vscode moves to newer nodejs version:
131 // https://github.com/rust-analyzer/rust-analyzer/issues/3167
132 });
133 await moveFile(tempFilePath, destFilePath);
134 }); 138 });
135} 139}
136 140
137async function withTempFile(scope: (tempFilePath: string) => Promise<void>) { 141async function withTempDir(scope: (tempDirPath: string) => Promise<void>) {
138 // 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/
139 143
140 // `.realpath()` should handle the cases where os.tmpdir() contains symlinks 144 // `.realpath()` should handle the cases where os.tmpdir() contains symlinks
@@ -143,7 +147,7 @@ async function withTempFile(scope: (tempFilePath: string) => Promise<void>) {
143 const tempDir = await fs.promises.mkdtemp(path.join(osTempDir, "rust-analyzer")); 147 const tempDir = await fs.promises.mkdtemp(path.join(osTempDir, "rust-analyzer"));
144 148
145 try { 149 try {
146 return await scope(path.join(tempDir, "file")); 150 return await scope(tempDir);
147 } finally { 151 } finally {
148 // We are good citizens :D 152 // We are good citizens :D
149 void fs.promises.rmdir(tempDir, { recursive: true }).catch(log.error); 153 void fs.promises.rmdir(tempDir, { recursive: true }).catch(log.error);
@@ -160,6 +164,7 @@ async function moveFile(src: fs.PathLike, dest: fs.PathLike) {
160 await fs.promises.unlink(src); 164 await fs.promises.unlink(src);
161 } else { 165 } else {
162 log.error(`Failed to rename the file ${src} -> ${dest}`, err); 166 log.error(`Failed to rename the file ${src} -> ${dest}`, err);
167 throw err;
163 } 168 }
164 } 169 }
165} 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);