Skip to content

Commit

Permalink
feature: show Metals' release notes directly in vscode
Browse files Browse the repository at this point in the history
  • Loading branch information
kpodsiad committed Jun 9, 2022
1 parent e259a29 commit d66b766
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 1 deletion.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,8 @@
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.1",
"@types/node": "17.0.27",
"@types/remarkable": "^2.0.3",
"@types/semver": "^7.3.9",
"@types/vscode": "1.59.0",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
Expand All @@ -902,6 +904,7 @@
"mocha": "^10.0.0",
"ovsx": "0.3.0",
"prettier": "2.6.2",
"remarkable": "^2.0.1",
"rimraf": "^3.0.2",
"ts-mocha": "^10.0.0",
"typescript": "4.7.3",
Expand All @@ -911,6 +914,7 @@
"ansicolor": "^1.1.100",
"metals-languageclient": "0.5.15",
"promisify-child-process": "4.1.1",
"semver": "^7.3.7",
"vscode-languageclient": "7.0.0"
},
"extensionPack": [
Expand Down
4 changes: 3 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import * as workbenchCommands from "./workbenchCommands";
import { getServerVersion } from "./getServerVersion";
import { getCoursierMirrorPath } from "./mirrors";
import { DoctorProvider } from "./doctor";
import { showReleaseNotesIfNeeded } from "./releaseNotesProvider";

const outputChannel = window.createOutputChannel("Metals");
const openSettingsAction = "Open settings";
Expand Down Expand Up @@ -130,7 +131,8 @@ export async function activate(context: ExtensionContext): Promise<void> {
commands.executeCommand("setContext", "metals:enabled", true);
try {
const javaHome = await getJavaHome(getJavaHomeFromConfig());
return fetchAndLaunchMetals(context, javaHome, serverVersion);
await fetchAndLaunchMetals(context, javaHome, serverVersion);
await showReleaseNotesIfNeeded(context, serverVersion, outputChannel);
} catch (err) {
outputChannel.appendLine(`${err}`);
showMissingJavaMessage();
Expand Down
180 changes: 180 additions & 0 deletions src/releaseNotesProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { env, ExtensionContext } from "vscode";
import * as vscode from "vscode";
import * as semver from "semver";
import { Remarkable } from "remarkable";
import http from "https";

const versionKey = "metals-server-version";

// prettier-ignore
const releaseNotesMarkdownUrl: Record<string, string> = {
"0.11.5": "https://raw.githubusercontent.com/scalameta/metals/main/website/blog/2022-04-28-aluminium.md",
"0.11.6": "https://raw.githubusercontent.com/scalameta/metals/main/website/blog/2022-06-03-aluminium.md",
};

/**
* Show release notes if possible, swallow errors since its not a crucial feature.
* Treats snapshot versions like 0.11.6+67-926ec9a3-SNAPSHOT as a 0.11.6.
*/
export async function showReleaseNotesIfNeeded(
context: ExtensionContext,
serverVersion: string,
outputChannel: vscode.OutputChannel
) {
try {
await showReleaseNotes(context, serverVersion);
} catch (error) {
outputChannel.appendLine(
`Couldn't show release notes for Metals ${serverVersion}`
);
outputChannel.appendLine(`${error}`);
}
}

async function showReleaseNotes(
context: ExtensionContext,
currentVersion: string
): Promise<void> {
const state = context.globalState;

const version = getVersion();
const releaseNotesUrl = version
? releaseNotesMarkdownUrl[version]
: undefined;
if (isNotRemote() && version && releaseNotesUrl) {
await showPanel(version, releaseNotesUrl);
}

async function showPanel(version: string, releaseNotesUrl: string) {
const releaseNotes = await getReleaseNotesMarkdown(releaseNotesUrl);

if (typeof releaseNotes === "string") {
const panel = vscode.window.createWebviewPanel(
`scalameta.metals.whatsNew`,
`Metals ${version} release notes`,
vscode.ViewColumn.One
);

panel.webview.html = releaseNotes;
panel.reveal();

// set wrong value for local development
// const newVersion = "0.11.5";

const newVersion = version;

// Update current device's latest server version when there's no value or it was a older one.
// Then sync this value across other devices.
state.update(versionKey, newVersion);
state.setKeysForSync([versionKey]);

context.subscriptions.push(panel);
}
}

/**
* Don't show panel for remote environment because it installs extension on every time.
* TODO: what about wsl?
*/
function isNotRemote(): boolean {
const isNotRemote = env.remoteName == null;
// const isWsl = env.remoteName === "wsl";
return isNotRemote;
}

/**
* Return version for which release notes should be displayed
* or
* undefined if notes shouldn't be displayed
*/
function getVersion(): string | undefined {
const previousVersion: string | undefined = state.get(versionKey);
// strip version to 'major.minor.patch'
const cleanVersion = semver.clean(currentVersion);
if (cleanVersion) {
if (previousVersion) {
const compare = semver.compare(cleanVersion, previousVersion);
const diff = semver.diff(cleanVersion, previousVersion);

// take into account only major, minor and patch, ignore snapshot releases
const isNewerVersion =
compare === 1 &&
(diff === "major" || diff === "minor" || diff === "patch");

return isNewerVersion ? cleanVersion : undefined;
}

// if there was no previous version then show release notes for current cleaned version
return currentVersion;
}
}
}

async function getReleaseNotesMarkdown(
releaseNotesUrl: string
): Promise<string | { error: unknown }> {
const ps = new Promise<string>((resolve, reject) => {
http.get(releaseNotesUrl, (resp) => {
let body = "";
resp.on("data", (chunk) => (body += chunk));
resp.on("end", () => resolve(body));
resp.on("error", (e) => reject(e));
});
});

const text = await ps;
// every release notes starts with that
const beginning = "We're happy to announce the release of";
const notesStartIdx = text.indexOf(beginning);
const releaseNotes = text.substring(notesStartIdx);

// cut metadata yaml from release notes, it start with --- and ends with ---
const metadata = text
.substring(0, notesStartIdx - 1)
.replace("---", "")
.replace("---", "")
.trim()
.split("\n");
const author = metadata[0].slice("author: ".length);
const title = metadata[1].slice("title: ".length);
const authorUrl = metadata[2].slice("authorURL: ".length);

const md = new Remarkable({ html: true });
const renderedNotes = md.render(releaseNotes);

const notes = getHtmlContent(renderedNotes, author, title, authorUrl);
return notes;
}

function getHtmlContent(
renderedNotes: string,
author: string,
title: string,
authorURL: string
): string {
return `
<!DOCTYPE html>
<html lang="en" style="height: 100%; width: 100%;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>${title}</h1>
<hr>
<p>
Showing Metals' release notes embedded in vscode is an experimental feature, in case of any issues report them at
<a href="https://github.com/scalameta/metals-vscode">https://github.com/scalameta/metals-vscode</a>.
</p>
<hr>
<p>
<a href="${authorURL}" target="_blank" itemprop="url">
<span itemprop="name">${author}</span>
</a>
</p>
<hr>
${renderedNotes}
</body>
</html>
`;
}
55 changes: 55 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
"@types/minimatch" "*"
"@types/node" "*"

"@types/highlight.js@^9.7.0":
version "9.12.4"
resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.4.tgz#8c3496bd1b50cc04aeefd691140aa571d4dbfa34"
integrity sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww==

"@types/json-schema@^7.0.9":
version "7.0.11"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
Expand All @@ -90,6 +95,19 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.27.tgz#f4df3981ae8268c066e8f49995639f855469081e"
integrity sha512-4/Ke7bbWOasuT3kceBZFGakP1dYN2XFd8v2l9bqF2LNWrmeU07JLpp56aEeG6+Q3olqO5TvXpW0yaiYnZJ5CXg==

"@types/remarkable@^2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/remarkable/-/remarkable-2.0.3.tgz#ba2e5edada8f0fe64c658beb2127e06ac0b9c1c8"
integrity sha512-QQUBeYApuHCNl9Br6ZoI3PlKmwZ69JHrlJktJXnjxobia9liZgsI70fm8PnCqVFAcefYK+9PGzR5L/hzCslNYQ==
dependencies:
"@types/highlight.js" "^9.7.0"
highlight.js "^9.7.0"

"@types/semver@^7.3.9":
version "7.3.9"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc"
integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==

"@types/vscode@1.59.0":
version "1.59.0"
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.59.0.tgz#11c93f5016926126bf30b47b9ece3bd617eeef31"
Expand Down Expand Up @@ -280,6 +298,13 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"

argparse@^1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"

argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
Expand All @@ -300,6 +325,13 @@ async@^3.2.2:
resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==

autolinker@^3.11.0:
version "3.15.0"
resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-3.15.0.tgz#03956088648f236642a5783612f9ca16adbbed38"
integrity sha512-N/5Dk5AZnqL9k6kkHdFIGLm/0/rRuSnJwqYYhLCJjU7ZtiaJwCBzNTvjzy1zzJADngv/wvtHYcrPHytPnASeFA==
dependencies:
tslib "^2.3.0"

azure-devops-node-api@^11.0.1:
version "11.1.0"
resolved "https://registry.yarnpkg.com/azure-devops-node-api/-/azure-devops-node-api-11.1.0.tgz#ea3ca49de8583b0366d000f3c3f8a75b8104055f"
Expand Down Expand Up @@ -1102,6 +1134,11 @@ he@1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==

highlight.js@^9.7.0:
version "9.18.5"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825"
integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA==

hosted-git-info@^4.0.2:
version "4.1.0"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224"
Expand Down Expand Up @@ -1758,6 +1795,14 @@ regexpp@^3.2.0:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==

remarkable@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-2.0.1.tgz#280ae6627384dfb13d98ee3995627ca550a12f31"
integrity sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==
dependencies:
argparse "^1.0.10"
autolinker "^3.11.0"

require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
Expand Down Expand Up @@ -1901,6 +1946,11 @@ source-map@^0.6.0:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==

sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=

string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
Expand Down Expand Up @@ -2071,6 +2121,11 @@ tslib@^2.2.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==

tslib@^2.3.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==

tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
Expand Down

0 comments on commit d66b766

Please sign in to comment.