Skip to content

Commit 95a7630

Browse files
committed
feat(vscode): detection of invalid robot environment now shows a quickpick instead an error message
In the case RobotCode will initalize the LanguageServer and detect that environment is not valid, means wrong python version, no robotframework installed, instead an error message pops up a quickpick pops up, where you can do different things, like select a differnt interpreter, create a new venv, ignore this for the moment. The advantage of this way is, that this also works with extensions like Pyhton Manger and so on closes #165
1 parent 79ca693 commit 95a7630

File tree

2 files changed

+126
-73
lines changed

2 files changed

+126
-73
lines changed

vscode-client/languageclientsmanger.ts

+126-71
Original file line numberDiff line numberDiff line change
@@ -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

vscode-client/testcontrollermanager.ts

-2
Original file line numberDiff line numberDiff line change
@@ -1062,8 +1062,6 @@ export class TestControllerManager {
10621062

10631063
if (isFileAndExists && vscode.workspace.textDocuments.find((d) => d.uri.toString() === uri?.toString())) return;
10641064

1065-
this.outputChannel.appendLine(`refresh uri ${uri ? uri.toString() : "unknown"} because: ${reason ?? "unknown"}`);
1066-
10671065
if (exists) {
10681066
const testItem = this.findTestItemByUri(uri.toString());
10691067
if (testItem) {

0 commit comments

Comments
 (0)