|
2 | 2 |
|
3 | 3 | import { inject, injectable, named } from 'inversify'; |
4 | 4 | import * as os from 'os'; |
5 | | -import { OutputChannel, Uri } from 'vscode'; |
| 5 | +import { CancellationToken, OutputChannel, Uri } from 'vscode'; |
6 | 6 | import '../../common/extensions'; |
7 | 7 | import * as localize from '../../common/utils/localize'; |
8 | 8 | import { IServiceContainer } from '../../ioc/types'; |
@@ -40,7 +40,7 @@ export abstract class BaseInstaller { |
40 | 40 | this.productService = serviceContainer.get<IProductService>(IProductService); |
41 | 41 | } |
42 | 42 |
|
43 | | - public promptToInstall(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { |
| 43 | + public promptToInstall(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { |
44 | 44 | // If this method gets called twice, while previous promise has not been resolved, then return that same promise. |
45 | 45 | // E.g. previous promise is not resolved as a message has been displayed to the user, so no point displaying |
46 | 46 | // another message. |
@@ -49,15 +49,15 @@ export abstract class BaseInstaller { |
49 | 49 | if (BaseInstaller.PromptPromises.has(key)) { |
50 | 50 | return BaseInstaller.PromptPromises.get(key)!; |
51 | 51 | } |
52 | | - const promise = this.promptToInstallImplementation(product, resource); |
| 52 | + const promise = this.promptToInstallImplementation(product, resource, cancel); |
53 | 53 | BaseInstaller.PromptPromises.set(key, promise); |
54 | 54 | promise.then(() => BaseInstaller.PromptPromises.delete(key)).ignoreErrors(); |
55 | 55 | promise.catch(() => BaseInstaller.PromptPromises.delete(key)).ignoreErrors(); |
56 | 56 |
|
57 | 57 | return promise; |
58 | 58 | } |
59 | 59 |
|
60 | | - public async install(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { |
| 60 | + public async install(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { |
61 | 61 | if (product === Product.unittest) { |
62 | 62 | return InstallerResponse.Installed; |
63 | 63 | } |
@@ -70,7 +70,7 @@ export abstract class BaseInstaller { |
70 | 70 |
|
71 | 71 | const moduleName = translateProductToModule(product, ModuleNamePurpose.install); |
72 | 72 | const logger = this.serviceContainer.get<ILogger>(ILogger); |
73 | | - await installer.installModule(moduleName, resource) |
| 73 | + await installer.installModule(moduleName, resource, cancel) |
74 | 74 | .catch(logger.logError.bind(logger, `Error in installing the module '${moduleName}'`)); |
75 | 75 |
|
76 | 76 | return this.isInstalled(product, resource) |
@@ -98,7 +98,7 @@ export abstract class BaseInstaller { |
98 | 98 | } |
99 | 99 | } |
100 | 100 |
|
101 | | - protected abstract promptToInstallImplementation(product: Product, resource?: InterpreterUri): Promise<InstallerResponse>; |
| 101 | + protected abstract promptToInstallImplementation(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse>; |
102 | 102 | protected getExecutableNameFromSettings(product: Product, resource?: Uri): string { |
103 | 103 | const productType = this.productService.getProductType(product); |
104 | 104 | const productPathService = this.serviceContainer.get<IProductPathService>(IProductPathService, productType); |
@@ -132,14 +132,14 @@ export class CTagsInstaller extends BaseInstaller { |
132 | 132 | } |
133 | 133 | return InstallerResponse.Ignore; |
134 | 134 | } |
135 | | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { |
| 135 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, _cancel?: CancellationToken): Promise<InstallerResponse> { |
136 | 136 | const item = await this.appShell.showErrorMessage('Install CTags to enable Python workspace symbols?', 'Yes', 'No'); |
137 | 137 | return item === 'Yes' ? this.install(product, resource) : InstallerResponse.Ignore; |
138 | 138 | } |
139 | 139 | } |
140 | 140 |
|
141 | 141 | export class FormatterInstaller extends BaseInstaller { |
142 | | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { |
| 142 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, cancel?: CancellationToken): Promise<InstallerResponse> { |
143 | 143 | // Hard-coded on purpose because the UI won't necessarily work having |
144 | 144 | // another formatter. |
145 | 145 | const formatters = [Product.autopep8, Product.black, Product.yapf]; |
@@ -160,14 +160,14 @@ export class FormatterInstaller extends BaseInstaller { |
160 | 160 |
|
161 | 161 | const item = await this.appShell.showErrorMessage(message, ...options); |
162 | 162 | if (item === yesChoice) { |
163 | | - return this.install(product, resource); |
| 163 | + return this.install(product, resource, cancel); |
164 | 164 | } else if (typeof item === 'string') { |
165 | 165 | for (const formatter of formatters) { |
166 | 166 | const formatterName = ProductNames.get(formatter)!; |
167 | 167 |
|
168 | 168 | if (item.endsWith(formatterName)) { |
169 | 169 | await this.configService.updateSetting('formatting.provider', formatterName, resource); |
170 | | - return this.install(formatter, resource); |
| 170 | + return this.install(formatter, resource, cancel); |
171 | 171 | } |
172 | 172 | } |
173 | 173 | } |
@@ -177,7 +177,7 @@ export class FormatterInstaller extends BaseInstaller { |
177 | 177 | } |
178 | 178 |
|
179 | 179 | export class LinterInstaller extends BaseInstaller { |
180 | | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { |
| 180 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, cancel?: CancellationToken): Promise<InstallerResponse> { |
181 | 181 | const isPylint = product === Product.pylint; |
182 | 182 |
|
183 | 183 | const productName = ProductNames.get(product)!; |
@@ -202,7 +202,7 @@ export class LinterInstaller extends BaseInstaller { |
202 | 202 | const response = await this.appShell.showErrorMessage(message, ...options); |
203 | 203 | if (response === install) { |
204 | 204 | sendTelemetryEvent(EventName.LINTER_NOT_INSTALLED_PROMPT, undefined, { tool: productName as LinterId, action: 'install' }); |
205 | | - return this.install(product, resource); |
| 205 | + return this.install(product, resource, cancel); |
206 | 206 | } else if (response === disableInstallPrompt) { |
207 | 207 | await this.setStoredResponse(disableLinterInstallPromptKey, true); |
208 | 208 | sendTelemetryEvent(EventName.LINTER_NOT_INSTALLED_PROMPT, undefined, { tool: productName as LinterId, action: 'disablePrompt' }); |
@@ -250,7 +250,7 @@ export class LinterInstaller extends BaseInstaller { |
250 | 250 | } |
251 | 251 |
|
252 | 252 | export class TestFrameworkInstaller extends BaseInstaller { |
253 | | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { |
| 253 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, cancel?: CancellationToken): Promise<InstallerResponse> { |
254 | 254 | const productName = ProductNames.get(product)!; |
255 | 255 |
|
256 | 256 | const options: string[] = []; |
@@ -263,23 +263,23 @@ export class TestFrameworkInstaller extends BaseInstaller { |
263 | 263 | } |
264 | 264 |
|
265 | 265 | const item = await this.appShell.showErrorMessage(message, ...options); |
266 | | - return item === 'Yes' ? this.install(product, resource) : InstallerResponse.Ignore; |
| 266 | + return item === 'Yes' ? this.install(product, resource, cancel) : InstallerResponse.Ignore; |
267 | 267 | } |
268 | 268 | } |
269 | 269 |
|
270 | 270 | export class RefactoringLibraryInstaller extends BaseInstaller { |
271 | | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { |
| 271 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, cancel?: CancellationToken): Promise<InstallerResponse> { |
272 | 272 | const productName = ProductNames.get(product)!; |
273 | 273 | const item = await this.appShell.showErrorMessage(`Refactoring library ${productName} is not installed. Install?`, 'Yes', 'No'); |
274 | | - return item === 'Yes' ? this.install(product, resource) : InstallerResponse.Ignore; |
| 274 | + return item === 'Yes' ? this.install(product, resource, cancel) : InstallerResponse.Ignore; |
275 | 275 | } |
276 | 276 | } |
277 | 277 |
|
278 | 278 | export class DataScienceInstaller extends BaseInstaller { |
279 | | - protected async promptToInstallImplementation(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { |
| 279 | + protected async promptToInstallImplementation(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { |
280 | 280 | const productName = ProductNames.get(product)!; |
281 | 281 | const item = await this.appShell.showErrorMessage(localize.DataScience.libraryNotInstalled().format(productName), 'Yes', 'No'); |
282 | | - return item === 'Yes' ? this.install(product, resource) : InstallerResponse.Ignore; |
| 282 | + return item === 'Yes' ? this.install(product, resource, cancel) : InstallerResponse.Ignore; |
283 | 283 | } |
284 | 284 | } |
285 | 285 |
|
@@ -294,11 +294,11 @@ export class ProductInstaller implements IInstaller { |
294 | 294 |
|
295 | 295 | // tslint:disable-next-line:no-empty |
296 | 296 | public dispose() { } |
297 | | - public async promptToInstall(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { |
298 | | - return this.createInstaller(product).promptToInstall(product, resource); |
| 297 | + public async promptToInstall(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { |
| 298 | + return this.createInstaller(product).promptToInstall(product, resource, cancel); |
299 | 299 | } |
300 | | - public async install(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { |
301 | | - return this.createInstaller(product).install(product, resource); |
| 300 | + public async install(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { |
| 301 | + return this.createInstaller(product).install(product, resource, cancel); |
302 | 302 | } |
303 | 303 | public async isInstalled(product: Product, resource?: InterpreterUri): Promise<boolean | undefined> { |
304 | 304 | return this.createInstaller(product).isInstalled(product, resource); |
|