From 2bf0d54f9b50528fb60f906b40177b3429085866 Mon Sep 17 00:00:00 2001 From: Kirigaya <1193466151@qq.com> Date: Tue, 8 Oct 2024 14:30:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- l10n/bundle.l10n.en.json | 3 + l10n/bundle.l10n.zh-cn.json | 11 ++- l10n/bundle.l10n.zh-tw.json | 3 + src/extension.ts | 2 - src/function/lsp-client/cdn.ts | 56 ++++++++++++++ src/function/lsp-client/index.ts | 128 ++++++++++++++++++++++--------- 6 files changed, 162 insertions(+), 41 deletions(-) diff --git a/l10n/bundle.l10n.en.json b/l10n/bundle.l10n.en.json index f7ad156..3a21a12 100644 --- a/l10n/bundle.l10n.en.json +++ b/l10n/bundle.l10n.en.json @@ -8,6 +8,9 @@ "progress.initialization": "Initialization", "progress.build-module-tree": "Build Module Tree", "progress.download-digital-lsp": "Download Digital LSP", + "progress.choose-best-download-source": "Choose Best Download Source", + "progress.extract-digital-lsp": "Extract Digital LSP", + "error.download-digital-lsp": "Fail to download digital lsp server, check your network and reload vscode. You can also visit following site and download manually: https://github.com/Digital-EDA/Digital-IDE/releases/tag/", "fail.save-file": "fail to save file", "click.join-qq-group": "Click the link to join the QQ group" } \ No newline at end of file diff --git a/l10n/bundle.l10n.zh-cn.json b/l10n/bundle.l10n.zh-cn.json index ce8e08b..8b910d9 100644 --- a/l10n/bundle.l10n.zh-cn.json +++ b/l10n/bundle.l10n.zh-cn.json @@ -3,11 +3,14 @@ "welcome.title": "感谢使用 Digital-IDE ❤️。您的支持将是我们最大的动力!😊", "welcome.star": "支持", "welcome.refuse": "拒绝", - "progress.register-command": "(Digital IDE) 注册命令", - "progress.active-lsp-server": "Activate LSP Server", - "progress.initialization": "(Digital IDE) 初始化", + "progress.register-command": "Digital IDE 注册命令", + "progress.active-lsp-server": "Digital IDE 激活语言服务器", + "progress.initialization": "Digital IDE 初始化", "progress.build-module-tree": "构建模块树", - "progress.download-digital-lsp": "Download Digital LSP", + "progress.download-digital-lsp": "下载 Digital IDE 语言服务器", + "progress.choose-best-download-source": "选择最佳下载源", + "progress.extract-digital-lsp": "解压 Digital LSP 语言服务器", + "error.download-digital-lsp": "无法下载 Digital LSP 语言服务器,检查你的网络后重启 vscode,或者请手动去下方地址下载 https://github.com/Digital-EDA/Digital-IDE/releases/tag/", "fail.save-file": "保存文件失败", "click.join-qq-group": "点击链接加入QQ群一起讨论" } \ No newline at end of file diff --git a/l10n/bundle.l10n.zh-tw.json b/l10n/bundle.l10n.zh-tw.json index 7e2d4d8..88f1610 100644 --- a/l10n/bundle.l10n.zh-tw.json +++ b/l10n/bundle.l10n.zh-tw.json @@ -8,6 +8,9 @@ "progress.initialization": "(Digital IDE) 初始化", "progress.build-module-tree": "構建模塊樹", "progress.download-digital-lsp": "Download Digital LSP", + "progress.choose-best-download-source": "Choose Best Download Source", + "progress.extract-digital-lsp": "Extract Digital LSP", + "error.download-digital-lsp": "Fail to download digital lsp server, check your network and reload vscode", "fail.save-file": "保存文件失敗", "click.join-qq-group": "点击链接加入QQ群一起讨论" } \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 51d429e..80b3ffe 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,7 +12,6 @@ import * as lspClient from './function/lsp-client'; import { refreshArchTree } from './function/treeView'; - async function registerCommand(context: vscode.ExtensionContext) { func.registerFunctionCommands(context); func.registerLsp(context); @@ -44,7 +43,6 @@ async function launch(context: vscode.ExtensionContext) { title: t('progress.register-command') }, async () => { await registerCommand(context); - }); await lspClient.activate(context, versionString); diff --git a/src/function/lsp-client/cdn.ts b/src/function/lsp-client/cdn.ts index 199a092..012e7ed 100644 --- a/src/function/lsp-client/cdn.ts +++ b/src/function/lsp-client/cdn.ts @@ -1,5 +1,6 @@ import * as os from 'os'; import axios from 'axios'; +import { performance } from 'perf_hooks'; enum IPlatformSignature { x86Windows = 'windows_amd64', @@ -50,4 +51,59 @@ export function getGithubDownloadLink(signature: string, version: string): strin export function getGiteeDownloadLink(signature: string, version: string): string { return `https://gitee.com/Digital-IDE/Digital-IDE/releases/download/${version}/digital-lsp_${version}_${signature}.tar.gz`; +} + +function measureRequestTimecost(url: string, timeout: number = 5): Promise { + let s = performance.now(); + + let timer: NodeJS.Timeout; + + const responseP = axios.get(url, { responseType: 'stream' }); + + return new Promise((resolve, reject) => { + // 设置超时计时器 + timer = setTimeout(() => { + resolve(Infinity); + }, timeout); + + responseP.then(response => { + // 清除超时计时器 + clearTimeout(timer); + + const totalLength = response.headers['content-length']; + if (totalLength > 0) { + resolve(performance.now() - s); + } else { + resolve(Infinity); + } + }).catch(error => { + // 清除超时计时器 + clearTimeout(timer); + reject(error); + }); + }); +} + +export async function chooseBestDownloadSource(signature: string, version: string, timeout: number = 3000) { + const links = [ + getGiteeDownloadLink(signature, version), + getGithubDownloadLink(signature, version) + ]; + const pools: Promise[] = []; + for (const link of links) { + pools.push(measureRequestTimecost(link, timeout)); + } + let fastTc = Infinity; + let fastTcIndex = -1; + for (let i = 0; i < pools.length; ++ i) { + const tc = await pools[i]; + if (tc < fastTc) { + fastTc = tc; + fastTcIndex = i; + } + } + if (fastTcIndex == -1) { + return links[0]; + } + return links[fastTcIndex]; } \ No newline at end of file diff --git a/src/function/lsp-client/index.ts b/src/function/lsp-client/index.ts index 86035be..8384ed0 100644 --- a/src/function/lsp-client/index.ts +++ b/src/function/lsp-client/index.ts @@ -8,11 +8,13 @@ import { import * as vscode from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; +import * as zlib from 'zlib'; +import * as tar from 'tar'; import { platform } from "os"; import { IProgress, LspClient } from '../../global'; import axios, { AxiosResponse } from "axios"; -import { getGiteeDownloadLink, getPlatformPlatformSignature } from "./cdn"; - +import { chooseBestDownloadSource, getGiteeDownloadLink, getGithubDownloadLink, getPlatformPlatformSignature } from "./cdn"; +import { hdlDir } from "../../hdlFs"; function getLspServerExecutionName() { const osname = platform(); @@ -23,9 +25,54 @@ function getLspServerExecutionName() { return 'digital-lsp'; } -async function checkAndDownload(context: vscode.ExtensionContext, version: string): boolean { - const { t } = vscode.l10n; - +function extractTarGz(filePath: string, outputDir: string): Promise { + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } + + const inputStream = fs.createReadStream(filePath); + const gunzip = zlib.createGunzip(); + const extract = tar.extract({ + cwd: outputDir, // 解压到指定目录 + }); + + inputStream.pipe(gunzip).pipe(extract); + + return new Promise((resolve, reject) => { + extract.on('finish', resolve); + extract.on('error', reject); + }) +} + +function streamDownload(context: vscode.ExtensionContext, progress: vscode.Progress, response: AxiosResponse): Promise { + const totalLength = response.headers['content-length']; + const totalSize = parseInt(totalLength); + let downloadSize = 0; + const savePath = context.asAbsolutePath( + path.join('resources', 'dide-lsp', 'server', 'tmp.tar.gz') + ); + const fileStream = fs.createWriteStream(savePath); + response.data.pipe(fileStream); + + return new Promise((resolve, reject) => { + response.data.on('data', (chunk: Buffer) => { + downloadSize += chunk.length; + let precent = Math.ceil(downloadSize / totalSize * 100); + let increment = chunk.length / totalSize * 100; + progress.report({ message: `${precent}%`, increment }); + }); + + fileStream.on('finish', () => { + console.log('finish download'); + fileStream.close(); + resolve(savePath); + }); + + fileStream.on('error', reject); + }); +} + +async function checkAndDownload(context: vscode.ExtensionContext, version: string): Promise { const versionFolderPath = context.asAbsolutePath( path.join('resources', 'dide-lsp', 'server', version) ); @@ -35,41 +82,53 @@ async function checkAndDownload(context: vscode.ExtensionContext, version: strin return true; } - await vscode.window.withProgress({ + const serverFolder = context.asAbsolutePath( + path.join('resources', 'dide-lsp', 'server') + ); + hdlDir.mkdir(serverFolder); + + return await downloadLsp(context, version, versionFolderPath); +} + +export async function downloadLsp(context: vscode.ExtensionContext, version: string, versionFolderPath: string): Promise { + const { t } = vscode.l10n; + + const downloadLink = await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: t('progress.choose-best-download-source') + }, async (progress: vscode.Progress, token: vscode.CancellationToken) => { + let timeout = 3000; + let reportInterval = 500; + const intervalHandler = setInterval(() => { + progress.report({ increment: reportInterval / timeout * 100 }); + }, reportInterval); + + const signature = getPlatformPlatformSignature().toString(); + const downloadLink = await chooseBestDownloadSource(signature, version, timeout); + clearInterval(intervalHandler); + return downloadLink + }); + + const tarGzFilePath = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: t('progress.download-digital-lsp') }, async (progress: vscode.Progress, token: vscode.CancellationToken) => { progress.report({ increment: 0 }); - const signature = getPlatformPlatformSignature().toString(); - const downloadLink = getGiteeDownloadLink(signature, version); const response = await axios.get(downloadLink, { responseType: 'stream' }); - + return await streamDownload(context, progress, response); }); - function streamDownload(progress: vscode.Progress, response: AxiosResponse): Promise { - const totalLength = response.headers['content-length']; - const totalSize = parseInt(totalLength); - let downloadSize = 0; - const savePath = context.asAbsolutePath( - path.join('resources', 'dide-lsp', 'server', 'tmp.tar.gz') - ); - const fileStream = fs.createWriteStream(savePath); - - response.data.on('data', (chunk: Buffer) => { - downloadSize += chunk.length; - let increment = Math.ceil(downloadSize / totalSize * 100); - progress.report({ message: t('progress.download-digital-lsp'), increment }); - }); - return new Promise((resolve, reject) => { - fileStream.on('finish', () => { - resolve(); - }); - - fileStream.on('error', (error) => { - reject(error); - }); - }); - } + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: t('progress.extract-digital-lsp') + }, async (progress: vscode.Progress, token: vscode.CancellationToken) => { + if (fs.existsSync(tarGzFilePath)) { + console.log('check finish, begin to extract'); + await extractTarGz(tarGzFilePath, versionFolderPath); + } else { + vscode.window.showErrorMessage(t('error.download-digital-lsp') + version); + } + }); return false; } @@ -77,10 +136,9 @@ async function checkAndDownload(context: vscode.ExtensionContext, version: strin export async function activate(context: vscode.ExtensionContext, version: string) { await checkAndDownload(context, version); - const lspServerName = getLspServerExecutionName(); const lspServerPath = context.asAbsolutePath( - path.join('resources', 'dide-lsp', 'server', lspServerName) + path.join('resources', 'dide-lsp', 'server', version, lspServerName) ); if (fs.existsSync(lspServerPath)) {