From 56fc595d8e284b10252f947867ec31c089b3b54d Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 25 Dec 2025 08:33:22 +0800 Subject: [PATCH] Allow to use Jedi language server in system environment or virtual environments As this extension uses an internal Jedi LSP, it is hard for the users to change the jedi setting to adapt various requirements. And in some cases, the user require a specific version of the Jedi, which is hard to change as it is embedded. Add a new configuration property to allow the users to use Jedi LSP in their system environment or virtual environments, and auto fallback to the internal version if the external version is not found. Signed-off-by: Inochi Amaoto --- package.json | 6 ++++++ package.nls.json | 1 + python_files/run-jedi-language-server.py | 20 ++++++++++++++----- .../activation/jedi/languageClientFactory.ts | 6 ++++-- .../languageServer/jediLSExtensionManager.ts | 2 +- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 286e80baa2df..690bff39b3dc 100644 --- a/package.json +++ b/package.json @@ -536,6 +536,12 @@ "scope": "window", "type": "string" }, + "python.languageServer.useJediInEnvPath": { + "default": false, + "description": "%python.languageServer.useJediInEnvPath.description%", + "scope": "window", + "type": "boolean" + }, "python.interpreter.infoVisibility": { "default": "onPythonRelated", "description": "%python.interpreter.infoVisibility.description%", diff --git a/package.nls.json b/package.nls.json index 57f2ed95b2c0..a9a128221761 100644 --- a/package.nls.json +++ b/package.nls.json @@ -56,6 +56,7 @@ "python.languageServer.jediDescription": "Use Jedi behind the Language Server Protocol (LSP) as a language server.", "python.languageServer.pylanceDescription": "Use Pylance as a language server.", "python.languageServer.noneDescription": "Disable language server capabilities.", + "python.languageServer.useJediInEnvPath.description": "Use Jedi in system environment or virtual environments.", "python.interpreter.infoVisibility.description": "Controls when to display information of selected interpreter in the status bar.", "python.interpreter.infoVisibility.never.description": "Never display information.", "python.interpreter.infoVisibility.onPythonRelated.description": "Only display information if Python-related files are opened.", diff --git a/python_files/run-jedi-language-server.py b/python_files/run-jedi-language-server.py index 47bf503d596c..9b445e6d7030 100644 --- a/python_files/run-jedi-language-server.py +++ b/python_files/run-jedi-language-server.py @@ -2,11 +2,21 @@ import pathlib import sys -# Add the lib path to our sys path so jedi_language_server can find its references -extension_dir = pathlib.Path(__file__).parent.parent -EXTENSION_ROOT = os.fsdecode(extension_dir) -sys.path.insert(0, os.fsdecode(extension_dir / "python_files" / "lib" / "jedilsp")) -del extension_dir +use_external_jedi = sys.argv[-1] == "external" +sys.argv = sys.argv[:-1] + +if use_external_jedi: + try: + import jedi_language_server + except Exception: + use_external_jedi = False + +if not use_external_jedi: + # Add the lib path to our sys path so jedi_language_server can find its references + extension_dir = pathlib.Path(__file__).parent.parent + EXTENSION_ROOT = os.fsdecode(extension_dir) + sys.path.insert(0, os.fsdecode(extension_dir / "python_files" / "lib" / "jedilsp")) + del extension_dir from jedi_language_server.cli import cli # noqa: E402 diff --git a/src/client/activation/jedi/languageClientFactory.ts b/src/client/activation/jedi/languageClientFactory.ts index 70bd65da8d0d..c77a6b3b3db4 100644 --- a/src/client/activation/jedi/languageClientFactory.ts +++ b/src/client/activation/jedi/languageClientFactory.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import * as path from 'path'; +import { WorkspaceConfiguration } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node'; import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../../common/constants'; @@ -13,7 +14,7 @@ import { ILanguageClientFactory } from '../types'; const languageClientName = 'Python Jedi'; export class JediLanguageClientFactory implements ILanguageClientFactory { - constructor(private interpreterService: IInterpreterService) {} + constructor(private interpreterService: IInterpreterService, private readonly workspaceConfiguration: WorkspaceConfiguration) {} public async createLanguageClient( resource: Resource, @@ -23,9 +24,10 @@ export class JediLanguageClientFactory implements ILanguageClientFactory { // Just run the language server using a module const lsScriptPath = path.join(EXTENSION_ROOT_DIR, 'python_files', 'run-jedi-language-server.py'); const interpreter = await this.interpreterService.getActiveInterpreter(resource); + const useJediInEnv = this.workspaceConfiguration.get("languageServer.useJediInEnvPath") === true const serverOptions: ServerOptions = { command: interpreter ? interpreter.path : 'python', - args: [lsScriptPath], + args: [lsScriptPath, useJediInEnv ? "external": "internal"], }; return new LanguageClient(PYTHON_LANGUAGE, languageClientName, serverOptions, clientOptions); diff --git a/src/client/languageServer/jediLSExtensionManager.ts b/src/client/languageServer/jediLSExtensionManager.ts index 4cbfb6f33466..f8ef1e034cf5 100644 --- a/src/client/languageServer/jediLSExtensionManager.ts +++ b/src/client/languageServer/jediLSExtensionManager.ts @@ -47,7 +47,7 @@ export class JediLSExtensionManager implements IDisposable, ILanguageServerExten configurationService, workspaceService, ); - this.clientFactory = new JediLanguageClientFactory(interpreterService); + this.clientFactory = new JediLanguageClientFactory(interpreterService, workspaceService.getConfiguration("python")); this.serverProxy = new JediLanguageServerProxy(this.clientFactory); this.serverManager = new JediLanguageServerManager( serviceContainer,