diff options
author | Xin Li <delphij@google.com> | 2024-01-17 22:13:58 -0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2024-01-17 22:13:58 -0800 |
commit | 28d03a2a1cabbe01d7bcb6cf5166c10e50d3c2c6 (patch) | |
tree | c1643be8ab17fc607cea748a8bb1d621a5964873 /pw_ide/ts/pigweed-vscode/src/extension.ts | |
parent | ec2628a6ba2d0ecbe3ac10c8c772f6fc6acc345d (diff) | |
parent | f054515492af5132f685cb23fe11891ee77104c9 (diff) | |
download | pigweed-28d03a2a1cabbe01d7bcb6cf5166c10e50d3c2c6.tar.gz |
Merge Android 24Q1 Release (ab/11220357)temp_319669529
Bug: 319669529
Merged-In: Iba357b308a79d0c8b560acd4f72b5423c9c83294
Change-Id: Icdf552029fb97a34e83c6dd7799433fc473a2506
Diffstat (limited to 'pw_ide/ts/pigweed-vscode/src/extension.ts')
-rw-r--r-- | pw_ide/ts/pigweed-vscode/src/extension.ts | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/pw_ide/ts/pigweed-vscode/src/extension.ts b/pw_ide/ts/pigweed-vscode/src/extension.ts new file mode 100644 index 000000000..16e1ca51f --- /dev/null +++ b/pw_ide/ts/pigweed-vscode/src/extension.ts @@ -0,0 +1,242 @@ +// Copyright 2023 The Pigweed Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +import * as vscode from 'vscode'; + +import { getExtensionsJson } from './config'; + +/** + * Open the extensions sidebar and show the provided extensions. + * @param extensions - A list of extension IDs + */ +function showExtensions(extensions: string[]) { + vscode.commands.executeCommand( + 'workbench.extensions.search', + '@id:' + extensions.join(', @id:'), + ); +} + +/** + * Given a list of extensions, return the subset that are not installed or are + * disabled. + * @param extensions - A list of extension IDs + * @returns A list of extension IDs + */ +function getUnavailableExtensions(extensions: string[]): string[] { + const unavailableExtensions: string[] = []; + const available = vscode.extensions.all; + + // TODO(chadnorvell): Verify that this includes disabled extensions + extensions.map(async (extId) => { + const ext = available.find((ext) => ext.id == extId); + + if (!ext) { + unavailableExtensions.push(extId); + } + }); + + return unavailableExtensions; +} + +/** + * If there are recommended extensions that are not installed or enabled in the + * current workspace, prompt the user to install them. This is "sticky" in the + * sense that it will keep bugging the user to enable those extensions until + * they enable them all, or until they explicitly cancel. + * @param recs - A list of extension IDs + */ +async function installRecommendedExtensions(recs: string[]): Promise<void> { + let unavailableRecs = getUnavailableExtensions(recs); + const totalNumUnavailableRecs = unavailableRecs.length; + let numUnavailableRecs = totalNumUnavailableRecs; + + const update = () => { + unavailableRecs = getUnavailableExtensions(recs); + numUnavailableRecs = unavailableRecs.length; + }; + + const wait = async () => new Promise((resolve) => setTimeout(resolve, 2500)); + + const progressIncrement = (num: number) => + 1 - (num / totalNumUnavailableRecs) * 100; + + // All recommendations are installed; we're done. + if (totalNumUnavailableRecs == 0) { + console.log('User has all recommended extensions'); + + return; + } + + showExtensions(unavailableRecs); + + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + // TODO(chadnorvell): Make this look better + title: + 'Install these extensions! This Pigweed project needs these recommended extensions to be installed.', + cancellable: true, + }, + async (progress, token) => { + while (numUnavailableRecs > 0) { + // TODO(chadnorvell): Wait for vscode.extensions.onDidChange + await wait(); + update(); + + progress.report({ + increment: progressIncrement(numUnavailableRecs), + }); + + if (numUnavailableRecs > 0) { + console.log( + `User lacks ${numUnavailableRecs} recommended extensions`, + ); + + showExtensions(unavailableRecs); + } + + if (token.isCancellationRequested) { + console.log('User cancelled recommended extensions check'); + + break; + } + } + + console.log('All recommended extensions are enabled'); + progress.report({ increment: 100 }); + }, + ); +} + +/** + * Given a list of extensions, return the subset that are enabled. + * @param extensions - A list of extension IDs + * @returns A list of extension IDs + */ +function getEnabledExtensions(extensions: string[]): string[] { + const enabledExtensions: string[] = []; + const available = vscode.extensions.all; + + // TODO(chadnorvell): Verify that this excludes disabled extensions + extensions.map(async (extId) => { + const ext = available.find((ext) => ext.id == extId); + + if (ext) { + enabledExtensions.push(extId); + } + }); + + return enabledExtensions; +} + +/** + * If there are unwanted extensions that are enabled in the current workspace, + * prompt the user to disable them. This is "sticky" in the sense that it will + * keep bugging the user to disable those extensions until they disable them + * all, or until they explicitly cancel. + * @param recs - A list of extension IDs + */ +async function disableUnwantedExtensions(unwanted: string[]) { + let enabledUnwanted = getEnabledExtensions(unwanted); + const totalNumEnabledUnwanted = enabledUnwanted.length; + let numEnabledUnwanted = totalNumEnabledUnwanted; + + const update = () => { + enabledUnwanted = getEnabledExtensions(unwanted); + numEnabledUnwanted = enabledUnwanted.length; + }; + + const wait = async () => new Promise((resolve) => setTimeout(resolve, 2500)); + + const progressIncrement = (num: number) => + 1 - (num / totalNumEnabledUnwanted) * 100; + + // All unwanted are disabled; we're done. + if (totalNumEnabledUnwanted == 0) { + console.log('User has no unwanted extensions enabled'); + + return; + } + + showExtensions(enabledUnwanted); + + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + // TODO(chadnorvell): Make this look better + title: + 'Disable these extensions! This Pigweed project needs these extensions to be disabled.', + cancellable: true, + }, + async (progress, token) => { + while (numEnabledUnwanted > 0) { + // TODO(chadnorvell): Wait for vscode.extensions.onDidChange + await wait(); + update(); + + progress.report({ + increment: progressIncrement(numEnabledUnwanted), + }); + + if (numEnabledUnwanted > 0) { + console.log( + `User has ${numEnabledUnwanted} unwanted extensions enabled`, + ); + + showExtensions(enabledUnwanted); + } + + if (token.isCancellationRequested) { + console.log('User cancelled unwanted extensions check'); + + break; + } + } + + console.log('All unwanted extensions are disabled'); + progress.report({ increment: 100 }); + }, + ); +} + +async function checkExtensions() { + const extensions = await getExtensionsJson(); + + const num_recommendations = extensions.recommendations?.length ?? 0; + const num_unwanted = extensions.unwantedRecommendations?.length ?? 0; + + if (num_recommendations > 0) { + await installRecommendedExtensions(extensions.recommendations as string[]); + } + + if (num_unwanted > 0) { + await disableUnwantedExtensions( + extensions.unwantedRecommendations as string[], + ); + } +} + +export function activate(context: vscode.ExtensionContext) { + const pwCheckExtensions = vscode.commands.registerCommand( + 'pigweed.check-extensions', + () => checkExtensions(), + ); + + context.subscriptions.push(pwCheckExtensions); + checkExtensions(); +} + +export function deactivate() { + // Do nothing. +} |