@@ -118,6 +118,7 @@ export class LanguageClientsManager {
118118 public readonly outputChannels : Map < string , vscode . OutputChannel > = new Map ( ) ;
119119
120120 private _disposables : vscode . Disposable ;
121+ public _pythonCanceledPythonAndRobotEnv = new WeakMap < vscode . WorkspaceFolder , boolean > ( ) ;
121122 public _pythonValidPythonAndRobotEnv = new WeakMap < vscode . WorkspaceFolder , boolean > ( ) ;
122123 private _workspaceFolderDiscoverInfo = new WeakMap < vscode . WorkspaceFolder , DiscoverInfoResult > ( ) ;
123124
@@ -188,15 +189,17 @@ export class LanguageClientsManager {
188189
189190 this . pythonManager . onActivePythonEnvironmentChanged ( async ( event ) => {
190191 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 ) ;
198199 } else {
199200 this . _pythonValidPythonAndRobotEnv = new WeakMap < vscode . WorkspaceFolder , boolean > ( ) ;
201+ this . _pythonCanceledPythonAndRobotEnv = new WeakMap < vscode . WorkspaceFolder , boolean > ( ) ;
202+
200203 await this . restart ( ) ;
201204 }
202205 } ) ,
@@ -278,101 +281,153 @@ export class LanguageClientsManager {
278281 return serverOptions ;
279282 }
280283
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 ( ) ;
283290
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 ,
288316 ) ;
289317
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.` ) ;
294333 }
295334 }
296335
297336 public async isValidRobotEnvironmentInFolder (
298337 folder : vscode . WorkspaceFolder ,
299338 showDialogs ?: boolean ,
300339 ) : 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 ;
307343 }
308344
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+ }
319350
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 ;
321363 }
364+ }
322365
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+ }
332375
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+ ) ;
334386 }
335387
336- const robotCheck = this . pythonManager . checkRobotVersion ( pythonCommand ) ;
337- if ( robotCheck === undefined ) {
338- this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
388+ return false ;
389+ }
339390
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 ) ;
347394
348- return false ;
395+ if ( showDialogs ) {
396+ await this . showErrorWithSelectPythonInterpreter (
397+ `Robot Framework package not found in workspace folder '${ folder . name } '.` ,
398+ folder ,
399+ ) ;
349400 }
350401
351- if ( robotCheck === false ) {
352- this . _pythonValidPythonAndRobotEnv . set ( folder , false ) ;
402+ return false ;
403+ }
353404
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 ) ;
361407
362- return false ;
408+ if ( showDialogs ) {
409+ await this . showErrorWithSelectPythonInterpreter (
410+ `Robot Framework version in workspace folder '${ folder . name } ' not supported.` ,
411+ folder ,
412+ ) ;
363413 }
364414
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 ;
368421 }
369422
370423 private async getServerOptions ( folder : vscode . WorkspaceFolder , mode : string ) : Promise < ServerOptions | undefined > {
371424 const config = vscode . workspace . getConfiguration ( CONFIG_SECTION , folder ) ;
372425
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+ }
376431 const pythonCommand = await this . pythonManager . getPythonCommand ( folder ) ;
377432 if ( ! pythonCommand ) return undefined ;
378433
0 commit comments