1
1
import { inject , injectable , named } from 'inversify' ;
2
2
import * as os from 'os' ;
3
3
import * as path from 'path' ;
4
- import { OutputChannel , Uri } from 'vscode' ;
5
4
import * as vscode from 'vscode' ;
6
5
import { IFormatterHelper } from '../../formatters/types' ;
7
6
import { IServiceContainer } from '../../ioc/types' ;
@@ -33,14 +32,14 @@ abstract class BaseInstaller {
33
32
protected appShell : IApplicationShell ;
34
33
protected configService : IConfigurationService ;
35
34
36
- constructor ( protected serviceContainer : IServiceContainer , protected outputChannel : OutputChannel ) {
35
+ constructor ( protected serviceContainer : IServiceContainer , protected outputChannel : vscode . OutputChannel ) {
37
36
this . appShell = serviceContainer . get < IApplicationShell > ( IApplicationShell ) ;
38
37
this . configService = serviceContainer . get < IConfigurationService > ( IConfigurationService ) ;
39
38
}
40
39
41
- public abstract promptToInstall ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > ;
40
+ public abstract promptToInstall ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > ;
42
41
43
- public async install ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > {
42
+ public async install ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > {
44
43
if ( product === Product . unittest ) {
45
44
return InstallerResponse . Installed ;
46
45
}
@@ -60,7 +59,7 @@ abstract class BaseInstaller {
60
59
. then ( isInstalled => isInstalled ? InstallerResponse . Installed : InstallerResponse . Ignore ) ;
61
60
}
62
61
63
- public async isInstalled ( product : Product , resource ?: Uri ) : Promise < boolean | undefined > {
62
+ public async isInstalled ( product : Product , resource ?: vscode . Uri ) : Promise < boolean | undefined > {
64
63
if ( product === Product . unittest ) {
65
64
return true ;
66
65
}
@@ -85,22 +84,22 @@ abstract class BaseInstaller {
85
84
}
86
85
}
87
86
88
- protected getExecutableNameFromSettings ( product : Product , resource ?: Uri ) : string {
87
+ protected getExecutableNameFromSettings ( product : Product , resource ?: vscode . Uri ) : string {
89
88
throw new Error ( 'getExecutableNameFromSettings is not supported on this object' ) ;
90
89
}
91
90
}
92
91
93
92
class CTagsInstaller extends BaseInstaller {
94
- constructor ( serviceContainer : IServiceContainer , outputChannel : OutputChannel ) {
93
+ constructor ( serviceContainer : IServiceContainer , outputChannel : vscode . OutputChannel ) {
95
94
super ( serviceContainer , outputChannel ) ;
96
95
}
97
96
98
- public async promptToInstall ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > {
97
+ public async promptToInstall ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > {
99
98
const item = await this . appShell . showErrorMessage ( 'Install CTags to enable Python workspace symbols?' , 'Yes' , 'No' ) ;
100
99
return item === 'Yes' ? this . install ( product , resource ) : InstallerResponse . Ignore ;
101
100
}
102
101
103
- public async install ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > {
102
+ public async install ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > {
104
103
if ( this . serviceContainer . get < IPlatformService > ( IPlatformService ) . isWindows ) {
105
104
this . outputChannel . appendLine ( 'Install Universal Ctags Win32 to enable support for Workspace Symbols' ) ;
106
105
this . outputChannel . appendLine ( 'Download the CTags binary from the Universal CTags site.' ) ;
@@ -117,32 +116,41 @@ class CTagsInstaller extends BaseInstaller {
117
116
return InstallerResponse . Ignore ;
118
117
}
119
118
120
- protected getExecutableNameFromSettings ( product : Product , resource ?: Uri ) : string {
119
+ protected getExecutableNameFromSettings ( product : Product , resource ?: vscode . Uri ) : string {
121
120
const settings = this . configService . getSettings ( resource ) ;
122
121
return settings . workspaceSymbols . ctagsPath ;
123
122
}
124
123
}
125
124
126
125
class FormatterInstaller extends BaseInstaller {
127
- public async promptToInstall ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > {
126
+ public async promptToInstall ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > {
127
+ // Hard-coded on purpose because the UI won't necessarily work having
128
+ // another formatter.
129
+ const formatters = [ Product . autopep8 , Product . black , Product . yapf ] ;
130
+ const formatterNames = formatters . map ( ( formatter ) => ProductNames . get ( formatter ) ! ) ;
128
131
const productName = ProductNames . get ( product ) ! ;
132
+ formatterNames . splice ( formatterNames . indexOf ( productName ) , 1 ) ;
133
+ const useOptions = formatterNames . map ( ( name ) => `Use ${ name } ` ) ;
134
+ const yesChoice = 'Yes' ;
129
135
130
- const installThis = `Install ${ productName } ` ;
131
- const alternateFormatter = product === Product . autopep8 ? 'yapf' : 'autopep8' ;
132
- const useOtherFormatter = `Use '${ alternateFormatter } ' formatter` ;
133
- const item = await this . appShell . showErrorMessage ( `Formatter ${ productName } is not installed.` , installThis , useOtherFormatter ) ;
134
-
135
- if ( item === installThis ) {
136
+ const item = await this . appShell . showErrorMessage ( `Formatter ${ productName } is not installed. Install?` , yesChoice , ...useOptions ) ;
137
+ if ( item === yesChoice ) {
136
138
return this . install ( product , resource ) ;
139
+ } else if ( typeof item === 'string' ) {
140
+ for ( const formatter of formatters ) {
141
+ const formatterName = ProductNames . get ( formatter ) ! ;
142
+
143
+ if ( item . endsWith ( formatterName ) ) {
144
+ await this . configService . updateSettingAsync ( 'formatting.provider' , formatterName , resource ) ;
145
+ return this . install ( formatter , resource ) ;
146
+ }
147
+ }
137
148
}
138
- if ( item === useOtherFormatter ) {
139
- await this . configService . updateSettingAsync ( 'formatting.provider' , alternateFormatter , resource ) ;
140
- return InstallerResponse . Installed ;
141
- }
149
+
142
150
return InstallerResponse . Ignore ;
143
151
}
144
152
145
- protected getExecutableNameFromSettings ( product : Product , resource ?: Uri ) : string {
153
+ protected getExecutableNameFromSettings ( product : Product , resource ?: vscode . Uri ) : string {
146
154
const settings = this . configService . getSettings ( resource ) ;
147
155
const formatHelper = this . serviceContainer . get < IFormatterHelper > ( IFormatterHelper ) ;
148
156
const settingsPropNames = formatHelper . getSettingsPropertyNames ( product ) ;
@@ -152,7 +160,7 @@ class FormatterInstaller extends BaseInstaller {
152
160
153
161
// tslint:disable-next-line:max-classes-per-file
154
162
class LinterInstaller extends BaseInstaller {
155
- public async promptToInstall ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > {
163
+ public async promptToInstall ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > {
156
164
const productName = ProductNames . get ( product ) ! ;
157
165
const install = 'Install' ;
158
166
const disableAllLinting = 'Disable linting' ;
@@ -173,21 +181,21 @@ class LinterInstaller extends BaseInstaller {
173
181
}
174
182
return InstallerResponse . Ignore ;
175
183
}
176
- protected getExecutableNameFromSettings ( product : Product , resource ?: Uri ) : string {
184
+ protected getExecutableNameFromSettings ( product : Product , resource ?: vscode . Uri ) : string {
177
185
const linterManager = this . serviceContainer . get < ILinterManager > ( ILinterManager ) ;
178
186
return linterManager . getLinterInfo ( product ) . pathName ( resource ) ;
179
187
}
180
188
}
181
189
182
190
// tslint:disable-next-line:max-classes-per-file
183
191
class TestFrameworkInstaller extends BaseInstaller {
184
- public async promptToInstall ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > {
192
+ public async promptToInstall ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > {
185
193
const productName = ProductNames . get ( product ) ! ;
186
194
const item = await this . appShell . showErrorMessage ( `Test framework ${ productName } is not installed. Install?` , 'Yes' , 'No' ) ;
187
195
return item === 'Yes' ? this . install ( product , resource ) : InstallerResponse . Ignore ;
188
196
}
189
197
190
- protected getExecutableNameFromSettings ( product : Product , resource ?: Uri ) : string {
198
+ protected getExecutableNameFromSettings ( product : Product , resource ?: vscode . Uri ) : string {
191
199
const testHelper = this . serviceContainer . get < ITestsHelper > ( ITestsHelper ) ;
192
200
const settingsPropNames = testHelper . getSettingsPropertyNames ( product ) ;
193
201
if ( ! settingsPropNames . pathName ) {
@@ -201,12 +209,12 @@ class TestFrameworkInstaller extends BaseInstaller {
201
209
202
210
// tslint:disable-next-line:max-classes-per-file
203
211
class RefactoringLibraryInstaller extends BaseInstaller {
204
- public async promptToInstall ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > {
212
+ public async promptToInstall ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > {
205
213
const productName = ProductNames . get ( product ) ! ;
206
214
const item = await this . appShell . showErrorMessage ( `Refactoring library ${ productName } is not installed. Install?` , 'Yes' , 'No' ) ;
207
215
return item === 'Yes' ? this . install ( product , resource ) : InstallerResponse . Ignore ;
208
216
}
209
- protected getExecutableNameFromSettings ( product : Product , resource ?: Uri ) : string {
217
+ protected getExecutableNameFromSettings ( product : Product , resource ?: vscode . Uri ) : string {
210
218
return translateProductToModule ( product , ModuleNamePurpose . run ) ;
211
219
}
212
220
}
@@ -230,19 +238,20 @@ export class ProductInstaller implements IInstaller {
230
238
this . ProductTypes . set ( Product . pytest , ProductType . TestFramework ) ;
231
239
this . ProductTypes . set ( Product . unittest , ProductType . TestFramework ) ;
232
240
this . ProductTypes . set ( Product . autopep8 , ProductType . Formatter ) ;
241
+ this . ProductTypes . set ( Product . black , ProductType . Formatter ) ;
233
242
this . ProductTypes . set ( Product . yapf , ProductType . Formatter ) ;
234
243
this . ProductTypes . set ( Product . rope , ProductType . RefactoringLibrary ) ;
235
244
}
236
245
237
246
// tslint:disable-next-line:no-empty
238
247
public dispose ( ) { }
239
- public async promptToInstall ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > {
248
+ public async promptToInstall ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > {
240
249
return this . createInstaller ( product ) . promptToInstall ( product , resource ) ;
241
250
}
242
- public async install ( product : Product , resource ?: Uri ) : Promise < InstallerResponse > {
251
+ public async install ( product : Product , resource ?: vscode . Uri ) : Promise < InstallerResponse > {
243
252
return this . createInstaller ( product ) . install ( product , resource ) ;
244
253
}
245
- public async isInstalled ( product : Product , resource ?: Uri ) : Promise < boolean | undefined > {
254
+ public async isInstalled ( product : Product , resource ?: vscode . Uri ) : Promise < boolean | undefined > {
246
255
return this . createInstaller ( product ) . isInstalled ( product , resource ) ;
247
256
}
248
257
public translateProductToModuleName ( product : Product , purpose : ModuleNamePurpose ) : string {
@@ -280,6 +289,7 @@ function translateProductToModule(product: Product, purpose: ModuleNamePurpose):
280
289
case Product . pylint : return 'pylint' ;
281
290
case Product . pytest : return 'pytest' ;
282
291
case Product . autopep8 : return 'autopep8' ;
292
+ case Product . black : return 'black' ;
283
293
case Product . pep8 : return 'pep8' ;
284
294
case Product . pydocstyle : return 'pydocstyle' ;
285
295
case Product . yapf : return 'yapf' ;
0 commit comments