@@ -118,6 +118,7 @@ export class LanguageClientsManager {
118
118
public readonly outputChannels : Map < string , vscode . OutputChannel > = new Map ( ) ;
119
119
120
120
private _disposables : vscode . Disposable ;
121
+ public _pythonCanceledPythonAndRobotEnv = new WeakMap < vscode . WorkspaceFolder , boolean > ( ) ;
121
122
public _pythonValidPythonAndRobotEnv = new WeakMap < vscode . WorkspaceFolder , boolean > ( ) ;
122
123
private _workspaceFolderDiscoverInfo = new WeakMap < vscode . WorkspaceFolder , DiscoverInfoResult > ( ) ;
123
124
@@ -188,15 +189,17 @@ export class LanguageClientsManager {
188
189
189
190
this . pythonManager . onActivePythonEnvironmentChanged ( async ( event ) => {
190
191
if ( event . resource !== undefined ) {
191
- let needsRestart = false ;
192
- if ( event . resource !== undefined ) {
193
- this . _workspaceFolderDiscoverInfo . delete ( event . resource ) ;
194
- needsRestart = this . _pythonValidPythonAndRobotEnv . has ( event . resource ) ;
195
- if ( needsRestart ) this . _pythonValidPythonAndRobotEnv . delete ( event . resource ) ;
196
- }
197
- await this . refresh ( event . resource . uri , needsRestart ) ;
192
+ this . inShowErrorWithSelectPythonInterpreter = true ;
193
+
194
+ this . _workspaceFolderDiscoverInfo . delete ( event . resource ) ;
195
+ this . _pythonValidPythonAndRobotEnv . delete ( event . resource ) ;
196
+ this . _pythonCanceledPythonAndRobotEnv . delete ( event . resource ) ;
197
+ this . showErrorWithSelectPythonInterpreterCancelTokenSource ?. cancel ( ) ;
198
+ await this . refresh ( event . resource . uri , true ) ;
198
199
} else {
199
200
this . _pythonValidPythonAndRobotEnv = new WeakMap < vscode . WorkspaceFolder , boolean > ( ) ;
201
+ this . _pythonCanceledPythonAndRobotEnv = new WeakMap < vscode . WorkspaceFolder , boolean > ( ) ;
202
+
200
203
await this . restart ( ) ;
201
204
}
202
205
} ) ,
@@ -278,101 +281,153 @@ export class LanguageClientsManager {
278
281
return serverOptions ;
279
282
}
280
283
281
- private async showErrorWithSelectPythonInterpreter ( msg : string , folder : vscode . WorkspaceFolder ) {
282
- this . outputChannel . appendLine ( msg ) ;
284
+ private inShowErrorWithSelectPythonInterpreter = false ;
285
+ private showErrorWithSelectPythonInterpreterCancelTokenSource : vscode . CancellationTokenSource | undefined ;
286
+
287
+ private async showErrorWithSelectPythonInterpreter ( title : string , _folder : vscode . WorkspaceFolder ) {
288
+ this . inShowErrorWithSelectPythonInterpreter = false ;
289
+ this . showErrorWithSelectPythonInterpreterCancelTokenSource = new vscode . CancellationTokenSource ( ) ;
283
290
284
- const item = await vscode . window . showErrorMessage (
285
- msg ,
286
- { title : "Select Python Interpreter" , id : "select" } ,
287
- { title : "Retry" , id : "retry" } ,
291
+ this . outputChannel . appendLine ( `${ title } ` ) ;
292
+
293
+ const item = await vscode . window . showQuickPick (
294
+ [
295
+ {
296
+ id : "select" ,
297
+ label : "Select Python Interpreter..." ,
298
+ detail :
299
+ "Choose a Python interpreter version 3.8 or newer that has `robotframework` version 4.1 or higher installed" ,
300
+ } ,
301
+ {
302
+ id : "create" ,
303
+ label : "Create Virtual Environment..." ,
304
+ detail : "Create a new virtual Python environment" ,
305
+ } ,
306
+ {
307
+ id : "retry" ,
308
+ label : "Retry" ,
309
+ detail :
310
+ "Install `robotframework` version 4.1 or higher manually in the current environment, then restart the language server" ,
311
+ } ,
312
+ { id : "ignore" , label : "Ignore" , detail : "Ignore this at the moment" } ,
313
+ ] ,
314
+ { title : title , placeHolder : "Choose an option..." , ignoreFocusOut : true } ,
315
+ this . showErrorWithSelectPythonInterpreterCancelTokenSource . token ,
288
316
) ;
289
317
290
- if ( item && item . id === "select" ) {
291
- await vscode . commands . executeCommand ( "python.setInterpreter" ) ;
292
- } else if ( item && item . id === "retry" ) {
293
- await this . restart ( folder . uri ) ;
318
+ switch ( item ?. id ) {
319
+ case "create" :
320
+ await vscode . commands . executeCommand ( "python.createEnvironment" ) ;
321
+ break ;
322
+ case "select" :
323
+ await vscode . commands . executeCommand ( "python.setInterpreter" ) ;
324
+ break ;
325
+ case "retry" :
326
+ return ;
327
+ }
328
+
329
+ if ( ! this . inShowErrorWithSelectPythonInterpreter ) {
330
+ this . _pythonCanceledPythonAndRobotEnv . set ( _folder , true ) ;
331
+
332
+ throw new Error ( `Select Python Interpreter for folder ${ _folder . name } canceled.` ) ;
294
333
}
295
334
}
296
335
297
336
public async isValidRobotEnvironmentInFolder (
298
337
folder : vscode . WorkspaceFolder ,
299
338
showDialogs ?: boolean ,
300
339
) : Promise < boolean > {
301
- return await this . _pythonValidPythonAndRobotEnvMutex . dispatch ( async ( ) => {
302
- if ( this . _pythonValidPythonAndRobotEnv . has ( folder ) ) {
303
- const r = this . _pythonValidPythonAndRobotEnv . get ( folder ) ?? false ;
304
- if ( r || ! showDialogs ) {
305
- return r ;
306
- }
340
+ return this . _pythonValidPythonAndRobotEnvMutex . dispatch ( async ( ) => {
341
+ if ( this . _pythonCanceledPythonAndRobotEnv . has ( folder ) ) {
342
+ return false ;
307
343
}
308
344
309
- const pythonCommand = await this . pythonManager . getPythonCommand ( folder ) ;
310
- if ( ! pythonCommand ) {
311
- this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
312
- if ( showDialogs ) {
313
- await this . showErrorWithSelectPythonInterpreter (
314
- `Can't find a valid python executable for workspace folder '${ folder . name } '. ` +
315
- "Check if python and the python extension is installed." ,
316
- folder ,
317
- ) ;
318
- }
345
+ let result = false ;
346
+ while ( ! result ) {
347
+ result = await this . _isValidRobotEnvironmentInFolder ( folder , showDialogs ) ;
348
+ if ( ! showDialogs ) break ;
349
+ }
319
350
320
- return false ;
351
+ return result ;
352
+ } ) ;
353
+ }
354
+
355
+ public async _isValidRobotEnvironmentInFolder (
356
+ folder : vscode . WorkspaceFolder ,
357
+ showDialogs ?: boolean ,
358
+ ) : Promise < boolean > {
359
+ if ( this . _pythonValidPythonAndRobotEnv . has ( folder ) ) {
360
+ const r = this . _pythonValidPythonAndRobotEnv . get ( folder ) ?? false ;
361
+ if ( r || ! showDialogs ) {
362
+ return r ;
321
363
}
364
+ }
322
365
323
- if ( ! this . pythonManager . checkPythonVersion ( pythonCommand ) ) {
324
- this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
325
- if ( showDialogs ) {
326
- await this . showErrorWithSelectPythonInterpreter (
327
- `Invalid python version for workspace folder ' ${ folder . name } '. Only python version >= 3.8 supported. ` +
328
- "Please update to a newer python version or select a valid python environment." ,
329
- folder ,
330
- ) ;
331
- }
366
+ const pythonCommand = await this . pythonManager . getPythonCommand ( folder ) ;
367
+ if ( ! pythonCommand ) {
368
+ this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
369
+ if ( showDialogs ) {
370
+ await this . showErrorWithSelectPythonInterpreter (
371
+ `Can't find a valid python executable for workspace folder ' ${ folder . name } '` ,
372
+ folder ,
373
+ ) ;
374
+ }
332
375
333
- return false ;
376
+ return false ;
377
+ }
378
+
379
+ if ( ! this . pythonManager . checkPythonVersion ( pythonCommand ) ) {
380
+ this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
381
+ if ( showDialogs ) {
382
+ await this . showErrorWithSelectPythonInterpreter (
383
+ `Invalid python version for workspace folder '${ folder . name } '` ,
384
+ folder ,
385
+ ) ;
334
386
}
335
387
336
- const robotCheck = this . pythonManager . checkRobotVersion ( pythonCommand ) ;
337
- if ( robotCheck === undefined ) {
338
- this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
388
+ return false ;
389
+ }
339
390
340
- if ( showDialogs ) {
341
- await this . showErrorWithSelectPythonInterpreter (
342
- `Robot Framework package not found in workspace folder '${ folder . name } '. ` +
343
- "Please install Robot Framework >= version 4.1 to the current python environment or select a valid python environment." ,
344
- folder ,
345
- ) ;
346
- }
391
+ const robotCheck = this . pythonManager . checkRobotVersion ( pythonCommand ) ;
392
+ if ( robotCheck === undefined ) {
393
+ this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
347
394
348
- return false ;
395
+ if ( showDialogs ) {
396
+ await this . showErrorWithSelectPythonInterpreter (
397
+ `Robot Framework package not found in workspace folder '${ folder . name } '.` ,
398
+ folder ,
399
+ ) ;
349
400
}
350
401
351
- if ( robotCheck === false ) {
352
- this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
402
+ return false ;
403
+ }
353
404
354
- if ( showDialogs ) {
355
- await this . showErrorWithSelectPythonInterpreter (
356
- `Robot Framework version in workspace folder '${ folder . name } ' not supported. Only Robot Framework version >= 4.1 supported. ` +
357
- "Please install or update to Robot Framework >= version 4.1 to the current python environment or select a valid python environment." ,
358
- folder ,
359
- ) ;
360
- }
405
+ if ( robotCheck === false ) {
406
+ this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
361
407
362
- return false ;
408
+ if ( showDialogs ) {
409
+ await this . showErrorWithSelectPythonInterpreter (
410
+ `Robot Framework version in workspace folder '${ folder . name } ' not supported.` ,
411
+ folder ,
412
+ ) ;
363
413
}
364
414
365
- this . _pythonValidPythonAndRobotEnv . set ( folder , true ) ;
366
- return true ;
367
- } ) ;
415
+ return false ;
416
+ }
417
+
418
+ this . _pythonValidPythonAndRobotEnv . set ( folder , true ) ;
419
+
420
+ return true ;
368
421
}
369
422
370
423
private async getServerOptions ( folder : vscode . WorkspaceFolder , mode : string ) : Promise < ServerOptions | undefined > {
371
424
const config = vscode . workspace . getConfiguration ( CONFIG_SECTION , folder ) ;
372
425
373
- const envOk = await this . isValidRobotEnvironmentInFolder ( folder , true ) ;
374
- if ( envOk === false ) return undefined ;
375
-
426
+ try {
427
+ if ( ! ( await this . isValidRobotEnvironmentInFolder ( folder , true ) ) ) return undefined ;
428
+ } catch ( e ) {
429
+ return undefined ;
430
+ }
376
431
const pythonCommand = await this . pythonManager . getPythonCommand ( folder ) ;
377
432
if ( ! pythonCommand ) return undefined ;
378
433
0 commit comments