Skip to content

Commit 8155463

Browse files
committed
implement better handling for switching python environments, no reload needed
1 parent 666df9c commit 8155463

File tree

4 files changed

+111
-60
lines changed

4 files changed

+111
-60
lines changed

robotcode/language_server/common/protocol.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ async def _initialize(
165165
raise
166166
except BaseException as e:
167167
raise JsonRPCErrorException(
168-
JsonRPCErrors.INTERNAL_ERROR, f"Can't start language server: {e}", InitializeError(retry=True)
168+
JsonRPCErrors.INTERNAL_ERROR, f"Can't start language server: {e}", InitializeError(retry=False)
169169
) from e
170170

171171
return InitializeResult(

robotcode/language_server/robotframework/protocol.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def check_robotframework() -> None:
3434
try:
3535
__import__("robot")
3636
except ImportError as e:
37-
raise Exception("RobotFramework not found, please install.") from e
37+
raise Exception("'robot' module not found, please install RobotFramework.") from e
3838

3939
if get_robot_version() < (4, 0):
4040
raise Exception("Wrong RobotFramework version. Expect version >= 4.0")

vscode-client/extension.ts

Lines changed: 83 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as net from "net";
22
import * as path from "path";
33
import * as vscode from "vscode";
44
import { LanguageClient, LanguageClientOptions, ServerOptions } from "vscode-languageclient/node";
5-
import { sleep } from "./utils";
5+
import { sleep, Mutex } from "./utils";
66

77
const LANGUAGE_SERVER_DEFAULT_TCP_PORT = 6610;
88
const LANGUAGE_SERVER_DEFAULT_HOST = "127.0.0.1";
@@ -40,8 +40,21 @@ function getPythonCommand(folder: vscode.WorkspaceFolder | undefined): string |
4040
return result;
4141
}
4242

43+
let clientsMutex = new Mutex();
4344
let clients: Map<string, LanguageClient> = new Map();
4445

46+
async function pythonExcetionDidChangeExecutionDetails(uri: vscode.Uri | undefined) {
47+
if (uri && clients.has(uri.toString())) {
48+
await clientsMutex.dispatch(async () => {
49+
let client = clients.get(uri.toString());
50+
clients.delete(uri.toString());
51+
await client?.stop();
52+
});
53+
54+
await getLanguageClientForResource(uri);
55+
}
56+
}
57+
4558
let _sortedWorkspaceFolders: string[] | undefined;
4659
function sortedWorkspaceFolders(): string[] {
4760
if (_sortedWorkspaceFolders === undefined) {
@@ -61,7 +74,6 @@ function sortedWorkspaceFolders(): string[] {
6174
}
6275
return _sortedWorkspaceFolders;
6376
}
64-
vscode.workspace.onDidChangeWorkspaceFolders(() => (_sortedWorkspaceFolders = undefined));
6577

6678
function getOuterMostWorkspaceFolder(folder: vscode.WorkspaceFolder): vscode.WorkspaceFolder {
6779
let sorted = sortedWorkspaceFolders();
@@ -84,64 +96,73 @@ async function getLanguageClientForDocument(document: vscode.TextDocument): Prom
8496
}
8597

8698
async function getLanguageClientForResource(resource: string | vscode.Uri): Promise<LanguageClient | undefined> {
87-
let uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource);
88-
let workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
99+
let client = await clientsMutex.dispatch(async () => {
100+
let uri = resource instanceof vscode.Uri ? resource : vscode.Uri.parse(resource);
101+
let workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
89102

90-
if (!workspaceFolder) {
91-
return;
92-
}
103+
if (!workspaceFolder) {
104+
return undefined;
105+
}
93106

94-
workspaceFolder = getOuterMostWorkspaceFolder(workspaceFolder);
107+
workspaceFolder = getOuterMostWorkspaceFolder(workspaceFolder);
95108

96-
var result = clients.get(workspaceFolder.uri.toString());
109+
var result = clients.get(workspaceFolder.uri.toString());
97110

98-
if (!result) {
99-
let config = vscode.workspace.getConfiguration(CONFIG_SECTION, uri);
111+
if (!result) {
112+
let config = vscode.workspace.getConfiguration(CONFIG_SECTION, uri);
100113

101-
let mode = config.get<string>("languageServer.mode", "stdio");
114+
let mode = config.get<string>("languageServer.mode", "stdio");
102115

103-
const serverOptions: ServerOptions =
104-
mode === "tcp" ? getServerOptionsTCP(workspaceFolder) : getServerOptionsStdIo(workspaceFolder);
105-
let name = `RobotCode Language Server mode=${mode} for workspace folder "${workspaceFolder.name}"`;
116+
const serverOptions: ServerOptions =
117+
mode === "tcp" ? getServerOptionsTCP(workspaceFolder) : getServerOptionsStdIo(workspaceFolder);
118+
let name = `RobotCode Language Server mode=${mode} for workspace folder "${workspaceFolder.name}"`;
106119

107-
let outputChannel = mode === "stdio" ? vscode.window.createOutputChannel(name) : undefined;
120+
let outputChannel = mode === "stdio" ? vscode.window.createOutputChannel(name) : undefined;
108121

109-
let clientOptions: LanguageClientOptions = {
110-
documentSelector: [
111-
{ scheme: "file", language: "robotframework", pattern: `${workspaceFolder.uri.fsPath}/**/*` },
112-
],
113-
synchronize: {
114-
configurationSection: [CONFIG_SECTION, "python"],
115-
},
116-
initializationOptions: {
117-
storageUri: extensionContext?.storageUri?.toString(),
118-
globalStorageUri: extensionContext?.globalStorageUri?.toString(),
119-
},
120-
diagnosticCollectionName: "robotcode",
121-
workspaceFolder: workspaceFolder,
122-
outputChannel: outputChannel,
123-
markdown: {
124-
isTrusted: true,
125-
},
126-
progressOnInitialization: true,
127-
};
122+
let clientOptions: LanguageClientOptions = {
123+
documentSelector: [
124+
{ scheme: "file", language: "robotframework", pattern: `${workspaceFolder.uri.fsPath}/**/*` },
125+
],
126+
synchronize: {
127+
configurationSection: [CONFIG_SECTION, "python"],
128+
},
129+
initializationOptions: {
130+
storageUri: extensionContext?.storageUri?.toString(),
131+
globalStorageUri: extensionContext?.globalStorageUri?.toString(),
132+
},
133+
diagnosticCollectionName: "robotcode",
134+
workspaceFolder: workspaceFolder,
135+
outputChannel: outputChannel,
136+
markdown: {
137+
isTrusted: true,
138+
},
139+
progressOnInitialization: true,
140+
};
128141

129-
OUTPUT_CHANNEL.appendLine(`start Language client: ${name}`);
130-
result = new LanguageClient(name, serverOptions, clientOptions);
131-
clients.set(workspaceFolder.uri.toString(), result);
142+
OUTPUT_CHANNEL.appendLine(`start Language client: ${name}`);
143+
result = new LanguageClient(name, serverOptions, clientOptions);
144+
clients.set(workspaceFolder.uri.toString(), result);
145+
}
146+
return result;
147+
});
132148

133-
result.start();
134-
}
149+
if (client) {
150+
if (client.needsStart()) {
151+
client.start();
152+
}
135153

136-
var counter = 0;
137-
while (!result.initializeResult && counter < 10_000) {
138-
await sleep(100);
139-
counter++;
140-
}
154+
var counter = 0;
155+
while (!client.initializeResult && counter < 10_000) {
156+
await sleep(100);
157+
counter++;
158+
}
141159

142-
await result.onReady();
160+
await client.onReady().catch((reason) => {
161+
OUTPUT_CHANNEL.appendLine("puhhh: " + reason);
162+
});
163+
}
143164

144-
return result;
165+
return client;
145166
}
146167

147168
function getServerOptionsTCP(folder: vscode.WorkspaceFolder) {
@@ -173,7 +194,7 @@ function getServerOptionsStdIo(folder: vscode.WorkspaceFolder) {
173194

174195
let pythonCommand = getPythonCommand(folder);
175196

176-
if (pythonCommand === undefined) {
197+
if (!pythonCommand) {
177198
throw new Error("Can't find a valid python executable.");
178199
}
179200

@@ -481,6 +502,7 @@ export async function activateAsync(context: vscode.ExtensionContext) {
481502
OUTPUT_CHANNEL.appendLine("Python Extension is active");
482503

483504
context.subscriptions.push(
505+
pythonExtension.exports.settings.onDidChangeExecutionDetails(pythonExcetionDidChangeExecutionDetails),
484506
vscode.commands.registerCommand("robotcode.runSuite", async (resource) => {
485507
return await debugSuiteOrTestcase(resource ?? vscode.window.activeTextEditor?.document.uri, undefined, {
486508
noDebug: true,
@@ -520,13 +542,17 @@ export async function activateAsync(context: vscode.ExtensionContext) {
520542
return await debugSuiteOrTestcase(res, realTest);
521543
}
522544
),
523-
vscode.workspace.onDidChangeWorkspaceFolders((event) => {
545+
vscode.workspace.onDidChangeWorkspaceFolders(async (event) => {
524546
for (let folder of event.removed) {
525-
let client = clients.get(folder.uri.toString());
526-
if (client) {
527-
clients.delete(folder.uri.toString());
528-
client.stop();
529-
}
547+
await clientsMutex.dispatch(async () => {
548+
_sortedWorkspaceFolders = undefined;
549+
550+
let client = clients.get(folder.uri.toString());
551+
if (client) {
552+
clients.delete(folder.uri.toString());
553+
client.stop();
554+
}
555+
});
530556
}
531557
}),
532558
vscode.debug.registerDebugConfigurationProvider("robotcode", new RobotCodeDebugConfigurationProvider()),
@@ -603,7 +629,7 @@ export async function activateAsync(context: vscode.ExtensionContext) {
603629

604630
vscode.workspace.onDidChangeConfiguration((event) => {
605631
for (let s of [
606-
"python.pythonPath",
632+
//"python.pythonPath",
607633
"robotcode.python",
608634
"robotcode.languageServer.mode",
609635
"robotcode.languageServer.tcpPort",
@@ -669,10 +695,10 @@ export async function activateAsync(context: vscode.ExtensionContext) {
669695
);
670696

671697
for (let document of vscode.workspace.textDocuments) {
672-
await getLanguageClientForDocument(document);
698+
getLanguageClientForDocument(document);
673699
}
674700

675-
await updateEditorContext();
701+
updateEditorContext();
676702
}
677703

678704
function displayProgress(promise: Promise<any>) {

vscode-client/utils.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,29 @@ export async function waitForPromise<T>(promise: Promise<T>, timeout: number): P
2020
reject(e);
2121
});
2222
});
23-
}
23+
}
24+
25+
export class Mutex {
26+
private mutex = Promise.resolve();
27+
28+
lock(): PromiseLike<() => void> {
29+
let begin: (unlock: () => void) => void = (unlock) => {};
30+
31+
this.mutex = this.mutex.then(() => {
32+
return new Promise(begin);
33+
});
34+
35+
return new Promise((res) => {
36+
begin = res;
37+
});
38+
}
39+
40+
async dispatch<T>(fn: (() => T) | (() => PromiseLike<T>)): Promise<T> {
41+
const unlock = await this.lock();
42+
try {
43+
return await Promise.resolve(fn()).finally(() => unlock());
44+
} finally {
45+
unlock();
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)