Skip to content

Commit

Permalink
Use the python extension venv (#820)
Browse files Browse the repository at this point in the history
Summary:
Fixes #6
Fixes #645
Fixes #183

If [the VsCode Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python) is installed, use the Python interpreter that is selected in the extension to start the pyre language server. Also, subscribe to environment switches and restart the pyre language server with the new environments.

If the extension is not present, we retain the original behaviour of the extension.

This is very useful for people that may have multiple environments installed but is more useful for people that use the VsCode SSH extension and cannot benefit from [this workaround](#6 (comment)).

Pull Request resolved: #820

Test Plan: I ran the extension locally and it seems to be picking up the interpreter path.

Reviewed By: stroxler, inseokhwang

Differential Revision: D56205532

Pulled By: kinto0

fbshipit-source-id: dc0c00f5a41d37c5cb7d9f85a60ac0ba9230456a
  • Loading branch information
vthemelis authored and facebook-github-bot committed Jun 3, 2024
1 parent 3ef054e commit 8b9d60a
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 22 deletions.
3 changes: 3 additions & 0 deletions tools/ide_plugins/vscode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/out
**/.vscode-test
*.vsix
9 changes: 6 additions & 3 deletions tools/ide_plugins/vscode/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pyre-vscode",
"version": "0.0.1",
"version": "0.0.2",
"publisher": "fb-pyre-check",
"engines": {
"vscode": "^1.15.0"
Expand All @@ -26,15 +26,18 @@
"lint": "tslint --force -p ."
},
"dependencies": {
"vscode-languageclient": "^3.5.0"
"@vscode/python-extension": "^1.0.5",
"vscode-languageclient": "^3.5.0",
"which": "^4.0.0"
},
"devDependencies": {
"@types/mocha": "^2.2.44",
"@types/node": "^8.0.53",
"@types/which": "^3.0.4",
"cson": "^4.1.0",
"tslint": "^5.8.0",
"tslint-microsoft-contrib": "^5.0.1",
"typescript": "^2.6.1",
"typescript": "^3.7",
"vscode": "^1.1.7"
}
}
101 changes: 83 additions & 18 deletions tools/ide_plugins/vscode/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,60 @@

import * as vscode from 'vscode';
import { LanguageClient, LanguageClientOptions, DidChangeConfigurationNotification } from 'vscode-languageclient';
import { EnvironmentPath, PVSC_EXTENSION_ID, PythonExtension } from '@vscode/python-extension'
import { dirname, join } from 'path';
import { existsSync, statSync } from 'fs';
import which from 'which';

let languageClient: LanguageClient;
type LanguageClientState = {
languageClient: LanguageClient,
configListener: Promise<vscode.Disposable>
}

namespace Configuration {
// Extension state
let state : LanguageClientState | undefined;
let envListener: vscode.Disposable | undefined;

let configurationListener: vscode.Disposable;
let outputChannel = vscode.window.createOutputChannel("pyre");

export function initialize() {
configurationListener = vscode.workspace.onDidChangeConfiguration(() => {
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: null });
});
export async function activate(_: vscode.ExtensionContext) {

const pythonExtension = vscode.extensions.getExtension<PythonExtension>(PVSC_EXTENSION_ID);

if (!pythonExtension) {
outputChannel.appendLine("Python extension not found. Will use the default console environment.");
state = createLanguageClient('pyre');
outputChannel.appendLine("Done");
return;
}

export function dispose() {
if (configurationListener) {
configurationListener.dispose();
}
const activatedEnvPath = pythonExtension.exports.environments.getActiveEnvironmentPath();
const pyrePath = await findPyreCommand(activatedEnvPath);

if (pyrePath) {
state = createLanguageClient(pyrePath);
}

envListener = pythonExtension.exports.environments.onDidChangeActiveEnvironmentPath(async (e) => {
state?.languageClient?.stop();
state?.configListener.then((listener) => listener.dispose());
state = undefined;

const pyrePath = await findPyreCommand(e);
if (pyrePath) {
state = createLanguageClient(pyrePath);
}
});
}

export async function activate(_: vscode.ExtensionContext) {
function createLanguageClient(pyrePath: string) : LanguageClientState {

let serverOptions = {
command: "pyre",
const serverOptions = {
command: pyrePath,
args: ["persistent"]
};

let clientOptions: LanguageClientOptions = {
const clientOptions: LanguageClientOptions = {
documentSelector: [{scheme: 'file', language: 'python'}],
synchronize: {
// Notify the server about file changes to '.clientrc files contain in the workspace
Expand All @@ -54,12 +80,51 @@ export async function activate(_: vscode.ExtensionContext) {
)

languageClient.registerProposedFeatures();
languageClient.onReady().then(() => {
Configuration.initialize();

const configListener = languageClient.onReady().then(() => {
return vscode.workspace.onDidChangeConfiguration(() => {
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: null });
});
});

languageClient.start();

return {languageClient, configListener};
}

async function findPyreCommand(envPath: EnvironmentPath) : Promise<string | undefined> {

if (envPath.id === 'DEFAULT_PYTHON') {
outputChannel.appendLine(`Using the default python environment`);
return 'pyre';
}

const path = envPath.path;
const stat = statSync(path)

const pyrePath = stat.isFile()
? join(dirname(envPath.path), 'pyre')
: stat.isDirectory()
? join(path, 'bin', 'pyre')
: undefined;

if (pyrePath && existsSync(pyrePath) && statSync(pyrePath).isFile()) {
outputChannel.appendLine(`Using pyre path: ${pyrePath} from python environment: ${envPath.id} at ${envPath.path}`);
return pyrePath;
}

const pyreFromPathEnvVariable = await which('pyre', { nothrow: true });
if (pyreFromPathEnvVariable != null) {
outputChannel.appendLine(`Using pyre path: ${pyreFromPathEnvVariable} from PATH`);
return pyreFromPathEnvVariable;
}

outputChannel.appendLine(`Could not find pyre path from python environment: ${envPath.id} at ${envPath.path}`);
return undefined;
}

export function deactivate() {
Configuration.dispose();
state?.languageClient.stop();
state?.configListener.then((listener) => listener.dispose());
envListener?.dispose();
}
3 changes: 2 additions & 1 deletion tools/ide_plugins/vscode/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"noImplicitAny": false,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true
"noUnusedParameters": true,
"esModuleInterop": true
},
"exclude": [
"node_modules",
Expand Down

0 comments on commit 8b9d60a

Please sign in to comment.