192 lines
6.5 KiB
JavaScript
192 lines
6.5 KiB
JavaScript
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
const {
|
|
createMessageConnection,
|
|
StreamMessageReader,
|
|
StreamMessageWriter,
|
|
} = require("vscode-jsonrpc/node");
|
|
const path = require("path");
|
|
const { spawn } = require("child_process");
|
|
const { PassThrough } = require("stream");
|
|
|
|
const PET_EXE = "../target/debug/pet";
|
|
|
|
const environments = [];
|
|
|
|
async function start() {
|
|
const readable = new PassThrough();
|
|
const writable = new PassThrough();
|
|
const proc = spawn(PET_EXE, ["server"], {
|
|
env: process.env,
|
|
});
|
|
proc.stdout.pipe(readable, { end: false });
|
|
proc.stderr.on("data", (data) => console.error(data.toString()));
|
|
writable.pipe(proc.stdin, { end: false });
|
|
const connection = createMessageConnection(
|
|
new StreamMessageReader(readable),
|
|
new StreamMessageWriter(writable)
|
|
);
|
|
connection.onError((ex) => console.error("Connection Error:", ex));
|
|
connection.onClose(() => proc.kill());
|
|
handleLogMessages(connection);
|
|
handleDiscoveryMessages(connection);
|
|
connection.listen();
|
|
return connection;
|
|
}
|
|
|
|
/**
|
|
* @param {import("vscode-jsonrpc").MessageConnection} connection
|
|
*/
|
|
function handleLogMessages(connection) {
|
|
connection.onNotification("log", (data) => {
|
|
switch (data.level) {
|
|
case "info":
|
|
// console.info("PET: ", data.message);
|
|
break;
|
|
case "warning":
|
|
console.warn("PET: ", data.message);
|
|
break;
|
|
case "error":
|
|
console.error("PET: ", data.message);
|
|
break;
|
|
case "debug":
|
|
// console.debug('PET: ', data.message);
|
|
break;
|
|
default:
|
|
console.log("PET: ", data.message);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {import("vscode-jsonrpc").MessageConnection} connection
|
|
*/
|
|
function handleDiscoveryMessages(connection) {
|
|
connection.onNotification("manager", (mgr) =>
|
|
console.log(`Discovered Manager (${mgr.tool}) ${mgr.executable}`)
|
|
);
|
|
connection.onNotification("environment", (env) => {
|
|
environments.push(env);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Configurating the server.
|
|
*
|
|
* @param {import("vscode-jsonrpc").MessageConnection} connection
|
|
*/
|
|
async function configure(connection) {
|
|
// Cache directory to store information about the environments.
|
|
// This is optional, but recommended (e.g. spawning conda can be very slow, sometimes upto 30s).
|
|
const cacheDir = path.join(
|
|
process.env.TMPDIR || process.env.TEMP || path.join(process.cwd(), "temp"),
|
|
"cache"
|
|
);
|
|
|
|
const configuration = {
|
|
// List of fully qualified paths to look for Environments,
|
|
// Generally this maps to workspace folders opened in the client, such as VS Code.
|
|
workspaceDirectories: [process.cwd()],
|
|
// List of fully qualified paths to look for virtual environments.
|
|
// Leave empty, if not applicable.
|
|
// In VS Code, users can configure custom locations where virtual environments are created.
|
|
environmentDirectories: [],
|
|
// Cache directory to store information about the environments.
|
|
cacheDirectory: path.join(process.cwd(), "temp/cache"),
|
|
};
|
|
// This must always be the first request to the server.
|
|
// There's no need to send this every time, unless the configuration changes.
|
|
await connection.sendRequest("configure", configuration);
|
|
}
|
|
|
|
/**
|
|
* Refresh the environment
|
|
*
|
|
* @param {import("vscode-jsonrpc").MessageConnection} connection
|
|
* @param {undefined | { searchKind?: string } | { searchPaths?: string[] } } search Defaults to searching for all environments on the current machine.
|
|
* Have a look at the JSONRPC.md file for more information.
|
|
*/
|
|
async function refresh(connection, search) {
|
|
environments.length = 0;
|
|
const { duration } = await connection.sendRequest("refresh", search);
|
|
const scope = search
|
|
? ` (in ${JSON.stringify(search)})`
|
|
: "(in machine scope)";
|
|
console.log(
|
|
`Found ${environments.length} environments in ${duration}ms ${scope}`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Clear the cache
|
|
*
|
|
* @param {import("vscode-jsonrpc").MessageConnection} connection
|
|
*/
|
|
async function clear(connection) {
|
|
await connection.sendRequest("clear");
|
|
}
|
|
|
|
/**
|
|
* Gets all possible information about the Python executable provided.
|
|
* This will spawn the Python executable (if not already done in the past).
|
|
* This must be used only if some of the information already avaialble is not sufficient.
|
|
*
|
|
* E.g. if a Python env was discovered and the version information is not know,
|
|
* but is requried, then call this method.
|
|
* If on the other hand, all of the information is already available, then there's no need to call this method.
|
|
* In fact it would be better to avoid calling this method, as it will spawn a new process & consume resouces.
|
|
*
|
|
* @param {import("vscode-jsonrpc").MessageConnection} connection
|
|
* @param {String} executable Fully qualified path to the Python executable.
|
|
*/
|
|
async function resolve(connection, executable) {
|
|
try {
|
|
const environment = await connection.sendRequest("resolve", { executable });
|
|
console.log(
|
|
`Resolved (${environment.kind}, ${environment.version}) ${environment.executable}`
|
|
);
|
|
return environment;
|
|
} catch (ex) {
|
|
console.error(`Failed to resolve executable ${executable}`, ex.message);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const connection = await start();
|
|
|
|
// First request to the server, to configure the server.
|
|
await configure(connection);
|
|
|
|
await refresh(connection);
|
|
|
|
// Search for environments in the specified folders.
|
|
// This could be a folder thats not part of the workspace and not in any known location
|
|
// I.e. it could contain environments that have not been discovered (due to the fact that its not a common/known location).
|
|
await refresh(connection, {
|
|
searchPaths: [
|
|
"/Users/user_name/temp",
|
|
"/Users/user_name/demo/.venv",
|
|
"/Users/user_name/demo/.venv/bin/python",
|
|
],
|
|
});
|
|
// Search for environments in the specified python environment directory.
|
|
await refresh(connection, {
|
|
searchPaths: ["/Users/user_name/demo/.venv/bin", "/usr/local/bin/python3"],
|
|
});
|
|
// Search for environments of a particular kind.
|
|
await refresh(connection, { searchKind: "Conda" });
|
|
|
|
// Possible this env was discovered, and the version or prefix information is not known.
|
|
await resolve(connection, "/usr/local/bin/python3");
|
|
await resolve(connection, "/usr/local/bin/python3"); // With cache directory provided, the Python exe will be spawned only once and cached info will be used.
|
|
|
|
// Possible we have an enviornment that was never discovered and we need information about that.
|
|
await resolve(connection, "/Users/user_name/demo/.venv/bin/python");
|
|
|
|
connection.end();
|
|
process.exit(0);
|
|
}
|
|
|
|
main();
|