|
| 1 | +// Copyright 2024 The MathWorks, Inc. |
| 2 | + |
| 3 | +import * as vscode from 'vscode' |
| 4 | +import { LanguageClient } from 'vscode-languageclient/node' |
| 5 | +import Notification from './Notifications' |
| 6 | + |
| 7 | +enum DeprecationType { |
| 8 | + NEVER_SUPPORTED = 1, |
| 9 | + DEPRECATED = 2, |
| 10 | + TO_BE_DEPRECATED = 3 |
| 11 | +} |
| 12 | + |
| 13 | +interface PopupInfo { |
| 14 | + message: string |
| 15 | + options: string[] |
| 16 | +} |
| 17 | + |
| 18 | +interface MessageData { |
| 19 | + deprecationType: DeprecationType |
| 20 | + deprecationInfo: DeprecationInfo |
| 21 | +} |
| 22 | + |
| 23 | +interface DeprecationInfo { |
| 24 | + /** |
| 25 | + * The version of MATLAB which has been launched |
| 26 | + */ |
| 27 | + matlabVersion: string |
| 28 | + |
| 29 | + /** |
| 30 | + * The minimum supported release of MATLAB (support may be ending soon) |
| 31 | + */ |
| 32 | + minVersion: string |
| 33 | + |
| 34 | + /** |
| 35 | + * The future minimum supported release of MATLAB. This may be greater |
| 36 | + * than the `minVersion` if support for `minVersion` is planned to end. |
| 37 | + */ |
| 38 | + futureMinVersion: string |
| 39 | +} |
| 40 | + |
| 41 | +interface StoredPopupConfig { |
| 42 | + /** |
| 43 | + * Contains saved config for when the popup should not be shown. |
| 44 | + */ |
| 45 | + hidePopupFor: { |
| 46 | + /** |
| 47 | + * The deprecated releases of MATLAB for which the user has |
| 48 | + * chosen "Do not show again for <release>" |
| 49 | + */ |
| 50 | + deprecated: string[] |
| 51 | + |
| 52 | + /** |
| 53 | + * The to-be-deprecated releases of MATLAB for which the user has |
| 54 | + * chosen "Do not show again for <release>" |
| 55 | + */ |
| 56 | + toBeDeprecated: string[] |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +const GLOBAL_STORAGE_KEY = 'matlab.deprecation.popup' |
| 61 | + |
| 62 | +/** |
| 63 | + * Service to handle notifications from the MATLAB language server when a |
| 64 | + * launched MATLAB version is no longer supported (or is on a deprecation |
| 65 | + * track) by the language server. |
| 66 | + */ |
| 67 | +export default class DeprecationPopupService { |
| 68 | + constructor (private readonly context: vscode.ExtensionContext) {} |
| 69 | + |
| 70 | + /** |
| 71 | + * Initializes the service to listen to notifications from the language server. |
| 72 | + * |
| 73 | + * @param client The language server client |
| 74 | + */ |
| 75 | + initialize (client: LanguageClient): void { |
| 76 | + this.context.subscriptions.push( |
| 77 | + client.onNotification(Notification.MatlabVersionDeprecation, (data: MessageData) => { |
| 78 | + this.showDeprecationPopup(data.deprecationType, data.deprecationInfo) |
| 79 | + }), |
| 80 | + vscode.commands.registerCommand('matlab.resetDeprecationPopups', () => { |
| 81 | + this.resetDeprecationPopupConfig() |
| 82 | + }) |
| 83 | + ) |
| 84 | + } |
| 85 | + |
| 86 | + /** |
| 87 | + * Attempts to display a deprecation notification popup. |
| 88 | + * |
| 89 | + * @see {@link shouldShowDeprecationPopup} for details on when the popup is shown. |
| 90 | + * |
| 91 | + * @param deprecationType The type of depreceation message (i.e. Deprecated, |
| 92 | + * Never Supported, or To-Be-Deprecated) |
| 93 | + * @param deprecationInfo Contains info about the current MATLAB version as well as |
| 94 | + * what versions are supported. |
| 95 | + */ |
| 96 | + private showDeprecationPopup (deprecationType: DeprecationType, deprecationInfo: DeprecationInfo): void { |
| 97 | + if (!this.shouldShowDeprecationPopup(deprecationType, deprecationInfo)) { |
| 98 | + return |
| 99 | + } |
| 100 | + |
| 101 | + const { message, options } = this.getPopupInfo(deprecationType, deprecationInfo) |
| 102 | + |
| 103 | + switch (deprecationType) { |
| 104 | + case DeprecationType.TO_BE_DEPRECATED: |
| 105 | + void vscode.window.showWarningMessage(message, ...options).then(choice => { |
| 106 | + this.handleChoiceClicked(deprecationType, deprecationInfo, choice) |
| 107 | + }) |
| 108 | + break |
| 109 | + case DeprecationType.NEVER_SUPPORTED: |
| 110 | + case DeprecationType.DEPRECATED: |
| 111 | + void vscode.window.showErrorMessage(message, ...options).then(choice => { |
| 112 | + this.handleChoiceClicked(deprecationType, deprecationInfo, choice) |
| 113 | + }) |
| 114 | + break |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + /** |
| 119 | + * Determines whether the deprecation popup should be shown. By default, the popup will |
| 120 | + * be shown. However, if the "Do not show again" option is clicked, the popup will not |
| 121 | + * be shown again in the future for this combination of deprecation type AND MATLAB version. |
| 122 | + * |
| 123 | + * @param deprecationType The type of depreceation message (i.e. Deprecated, |
| 124 | + * Never Supported, or To-Be-Deprecated) |
| 125 | + * @param deprecationInfo Contains info about the current MATLAB version as well as |
| 126 | + * what versions are supported. |
| 127 | + */ |
| 128 | + private shouldShowDeprecationPopup (deprecationType: DeprecationType, deprecationInfo: DeprecationInfo): boolean { |
| 129 | + const config: StoredPopupConfig | undefined = this.context.globalState.get(GLOBAL_STORAGE_KEY) |
| 130 | + |
| 131 | + if (config === undefined) { |
| 132 | + // No saved config - show popup |
| 133 | + return true |
| 134 | + } else { |
| 135 | + const { matlabVersion } = deprecationInfo |
| 136 | + const isInDeprecated = config.hidePopupFor.deprecated.includes(matlabVersion) |
| 137 | + const isInToBeDeprecated = config.hidePopupFor.toBeDeprecated.includes(matlabVersion) |
| 138 | + |
| 139 | + if (deprecationType === DeprecationType.DEPRECATED && isInDeprecated) { |
| 140 | + return false |
| 141 | + } else if (deprecationType === DeprecationType.TO_BE_DEPRECATED && isInToBeDeprecated) { |
| 142 | + return false |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + return true |
| 147 | + } |
| 148 | + |
| 149 | + /** |
| 150 | + * Gets the message and options to display on the popup. |
| 151 | + * |
| 152 | + * @param deprecationType The type of depreceation message (i.e. Deprecated, |
| 153 | + * Never Supported, or To-Be-Deprecated) |
| 154 | + * @param deprecationInfo Contains info about the current MATLAB version as well as |
| 155 | + * what versions are supported. |
| 156 | + */ |
| 157 | + private getPopupInfo (deprecationType: DeprecationType, deprecationInfo: DeprecationInfo): PopupInfo { |
| 158 | + let message = '' |
| 159 | + const options = ['Dismiss'] |
| 160 | + |
| 161 | + if (deprecationType === DeprecationType.NEVER_SUPPORTED) { |
| 162 | + message = `This extension does not support MATLAB ${deprecationInfo.matlabVersion}. To make use of the advanced features of the extension or run MATLAB code, you must have MATLAB ${deprecationInfo.minVersion} or later installed.` |
| 163 | + } else if (deprecationType === DeprecationType.DEPRECATED) { |
| 164 | + message = `This extension no longer supports MATLAB ${deprecationInfo.matlabVersion}. To make use of the advanced features of the extension or run MATLAB code, you must have MATLAB ${deprecationInfo.minVersion} or later installed.` |
| 165 | + options.push(`Do not show again for ${deprecationInfo.matlabVersion}`) |
| 166 | + } else if (deprecationType === DeprecationType.TO_BE_DEPRECATED) { |
| 167 | + message = `This extension will no longer support MATLAB ${deprecationInfo.matlabVersion} in a future release. To make use of the advanced features of the extension or run MATLAB code, you will need to have MATLAB ${deprecationInfo.futureMinVersion} or later installed.` |
| 168 | + options.push(`Do not show again for ${deprecationInfo.matlabVersion}`) |
| 169 | + } |
| 170 | + |
| 171 | + return { |
| 172 | + message, |
| 173 | + options |
| 174 | + } |
| 175 | + } |
| 176 | + |
| 177 | + /** |
| 178 | + * Handles the user's choice within the popup. |
| 179 | + * |
| 180 | + * @param deprecationType The type of depreceation message (i.e. Deprecated, |
| 181 | + * Never Supported, or To-Be-Deprecated) |
| 182 | + * @param deprecationInfo Contains info about the current MATLAB version as well as |
| 183 | + * what versions are supported. |
| 184 | + * @param choice the user's choice, or undefined if the user dismissed the popup without selecting an option |
| 185 | + */ |
| 186 | + private handleChoiceClicked (deprecationType: DeprecationType, deprecationInfo: DeprecationInfo, choice?: string): void { |
| 187 | + if (choice == null) { |
| 188 | + return |
| 189 | + } |
| 190 | + |
| 191 | + if (choice?.startsWith('Do not show again for')) { |
| 192 | + // Need to update the stored config to hide this popup for this scenario |
| 193 | + let config: StoredPopupConfig | undefined = this.context.globalState.get(GLOBAL_STORAGE_KEY) |
| 194 | + |
| 195 | + if (config === undefined) { |
| 196 | + // No current config - need to create config object |
| 197 | + config = { |
| 198 | + hidePopupFor: { |
| 199 | + deprecated: [], |
| 200 | + toBeDeprecated: [] |
| 201 | + } |
| 202 | + } |
| 203 | + } |
| 204 | + |
| 205 | + if (deprecationType === DeprecationType.DEPRECATED) { |
| 206 | + config.hidePopupFor.deprecated.push(deprecationInfo.matlabVersion) |
| 207 | + } else if (deprecationType === DeprecationType.TO_BE_DEPRECATED) { |
| 208 | + config.hidePopupFor.toBeDeprecated.push(deprecationInfo.matlabVersion) |
| 209 | + } |
| 210 | + |
| 211 | + void this.context.globalState.update(GLOBAL_STORAGE_KEY, config) |
| 212 | + } |
| 213 | + } |
| 214 | + |
| 215 | + /** |
| 216 | + * Resets the stored configuration for the deprecation popups, allowing |
| 217 | + * them to appear again. |
| 218 | + */ |
| 219 | + private resetDeprecationPopupConfig (): void { |
| 220 | + void this.context.globalState.update(GLOBAL_STORAGE_KEY, undefined) |
| 221 | + } |
| 222 | +} |
0 commit comments