diff --git a/src/common/constants.ts b/src/common/constants.ts index 402e017..6d47e1d 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -12,3 +12,4 @@ export const DEBUG_SERVER_SCRIPT_PATH = path.join(BUNDLED_PYTHON_SCRIPTS_DIR, 't export const PYTHON_MAJOR = 3; export const PYTHON_MINOR = 8; export const PYTHON_VERSION = `${PYTHON_MAJOR}.${PYTHON_MINOR}`; +export const LS_SERVER_RESTART_DELAY = 1000; diff --git a/src/common/python.ts b/src/common/python.ts index d9450df..e1b62a6 100644 --- a/src/common/python.ts +++ b/src/common/python.ts @@ -6,14 +6,15 @@ import { commands, Disposable, Event, EventEmitter, Uri } from 'vscode'; import { traceError, traceLog } from './logging'; import { PythonExtension, ResolvedEnvironment } from '@vscode/python-extension'; import { PYTHON_MAJOR, PYTHON_MINOR, PYTHON_VERSION } from './constants'; +import { getProjectRoot } from './utilities'; export interface IInterpreterDetails { path?: string[]; resource?: Uri; } -const onDidChangePythonInterpreterEvent = new EventEmitter(); -export const onDidChangePythonInterpreter: Event = onDidChangePythonInterpreterEvent.event; +const onDidChangePythonInterpreterEvent = new EventEmitter(); +export const onDidChangePythonInterpreter: Event = onDidChangePythonInterpreterEvent.event; let _api: PythonExtension | undefined; async function getPythonExtensionAPI(): Promise { @@ -24,19 +25,56 @@ async function getPythonExtensionAPI(): Promise { return _api; } +function sameInterpreter(a: string[], b: string[]): boolean { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} + +let serverPython: string[] | undefined; +function checkAndFireEvent(interpreter: string[] | undefined): void { + if (interpreter === undefined) { + if (serverPython) { + // Python was reset for this uri + serverPython = undefined; + onDidChangePythonInterpreterEvent.fire(); + return; + } else { + return; // No change in interpreter + } + } + + if (!serverPython || !sameInterpreter(serverPython, interpreter)) { + serverPython = interpreter; + onDidChangePythonInterpreterEvent.fire(); + } +} + +async function refreshServerPython(): Promise { + const projectRoot = await getProjectRoot(); + const interpreter = await getInterpreterDetails(projectRoot?.uri); + checkAndFireEvent(interpreter.path); +} + export async function initializePython(disposables: Disposable[]): Promise { try { const api = await getPythonExtensionAPI(); if (api) { disposables.push( - api.environments.onDidChangeActiveEnvironmentPath((e) => { - onDidChangePythonInterpreterEvent.fire({ path: [e.path], resource: e.resource?.uri }); + api.environments.onDidChangeActiveEnvironmentPath(async () => { + await refreshServerPython(); }), ); traceLog('Waiting for interpreter from Python extension.'); - onDidChangePythonInterpreterEvent.fire(await getInterpreterDetails()); + await refreshServerPython(); } } catch (error) { traceError('Error initializing Python: ', error); diff --git a/src/common/server.ts b/src/common/server.ts index ec99b24..afe5dc9 100644 --- a/src/common/server.ts +++ b/src/common/server.ts @@ -78,12 +78,12 @@ export async function restartServer( serverId: string, serverName: string, outputChannel: LogOutputChannel, - lsClient?: LanguageClient, + oldLsClient?: LanguageClient, ): Promise { - if (lsClient) { + if (oldLsClient) { traceInfo(`Server: Stop requested`); try { - await lsClient.stop(); + await oldLsClient.stop(); } catch (ex) { traceError(`Server: Stop failed: ${ex}`); } diff --git a/src/common/settings.ts b/src/common/settings.ts index 93fd29d..b5a78d6 100644 --- a/src/common/settings.ts +++ b/src/common/settings.ts @@ -5,6 +5,7 @@ import { ConfigurationChangeEvent, ConfigurationScope, WorkspaceConfiguration, W import { traceLog, traceWarn } from './logging'; import { getInterpreterDetails } from './python'; import { getConfiguration, getWorkspaceFolders } from './vscodeapi'; +import { getInterpreterFromSetting } from './utilities'; /* eslint-disable @typescript-eslint/naming-convention */ const DEFAULT_SEVERITY: Record = { @@ -80,11 +81,6 @@ function getCwd(config: WorkspaceConfiguration, workspace: WorkspaceFolder): str return resolveVariables([cwd], workspace)[0]; } -export function getInterpreterFromSetting(namespace: string, scope?: ConfigurationScope) { - const config = getConfiguration(namespace, scope); - return config.get('interpreter'); -} - export async function getWorkspaceSettings( namespace: string, workspace: WorkspaceFolder, diff --git a/src/common/utilities.ts b/src/common/utilities.ts index 7d5ff61..e3115bd 100644 --- a/src/common/utilities.ts +++ b/src/common/utilities.ts @@ -3,9 +3,9 @@ import * as fs from 'fs-extra'; import * as path from 'path'; -import { LogLevel, Uri, WorkspaceFolder } from 'vscode'; +import { ConfigurationScope, LogLevel, Uri, WorkspaceFolder } from 'vscode'; import { Trace } from 'vscode-jsonrpc/node'; -import { getWorkspaceFolders, isVirtualWorkspace } from './vscodeapi'; +import { getConfiguration, getWorkspaceFolders, isVirtualWorkspace } from './vscodeapi'; import { DocumentSelector } from 'vscode-languageclient'; function logLevelToTrace(logLevel: LogLevel): Trace { @@ -36,6 +36,11 @@ export function getLSClientTraceLevel(channelLogLevel: LogLevel, globalLogLevel: return level; } +export function getInterpreterFromSetting(namespace: string, scope?: ConfigurationScope) { + const config = getConfiguration(namespace, scope); + return config.get('interpreter'); +} + export async function getProjectRoot(): Promise { const workspaces: readonly WorkspaceFolder[] = getWorkspaceFolders(); if (workspaces.length === 0) { diff --git a/src/extension.ts b/src/extension.ts index 99806d8..73d36f6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,17 +6,12 @@ import { LanguageClient } from 'vscode-languageclient/node'; import { registerLogger, traceError, traceLog, traceVerbose } from './common/logging'; import { initializePython, onDidChangePythonInterpreter } from './common/python'; import { restartServer } from './common/server'; -import { - checkIfConfigurationChanged, - getInterpreterFromSetting, - getWorkspaceSettings, - logLegacySettings, -} from './common/settings'; +import { checkIfConfigurationChanged, getWorkspaceSettings, logLegacySettings } from './common/settings'; import { loadServerDefaults } from './common/setup'; -import { getLSClientTraceLevel, getProjectRoot } from './common/utilities'; +import { getInterpreterFromSetting, getLSClientTraceLevel, getProjectRoot } from './common/utilities'; import { createOutputChannel, onDidChangeConfiguration, registerCommand } from './common/vscodeapi'; import { registerLanguageStatusItem, updateStatus } from './common/status'; -import { PYTHON_VERSION } from './common/constants'; +import { LS_SERVER_RESTART_DELAY, PYTHON_VERSION } from './common/constants'; let lsClient: LanguageClient | undefined; export async function activate(context: vscode.ExtensionContext): Promise { @@ -48,19 +43,33 @@ export async function activate(context: vscode.ExtensionContext): Promise traceLog(`Module: ${serverInfo.module}`); traceVerbose(`Configuration: ${JSON.stringify(serverInfo)}`); + let isRestarting = false; + let restartTimer: NodeJS.Timeout | undefined; const runServer = async () => { - const projectRoot = await getProjectRoot(); - const workspaceSetting = await getWorkspaceSettings(serverId, projectRoot, true); - if (workspaceSetting.interpreter.length === 0) { - updateStatus(vscode.l10n.t('Please select a Python interpreter.'), vscode.LanguageStatusSeverity.Error); - traceError( - 'Python interpreter missing:\r\n' + - '[Option 1] Select python interpreter using the ms-python.python.\r\n' + - `[Option 2] Set an interpreter using "${serverId}.interpreter" setting.\r\n`, - `Please use Python ${PYTHON_VERSION} or greater.`, - ); - } else { - lsClient = await restartServer(workspaceSetting, serverId, serverName, outputChannel, lsClient); + if (isRestarting) { + if (restartTimer) { + clearTimeout(restartTimer); + } + restartTimer = setTimeout(runServer, LS_SERVER_RESTART_DELAY); + return; + } + isRestarting = true; + try { + const projectRoot = await getProjectRoot(); + const workspaceSetting = await getWorkspaceSettings(serverId, projectRoot, true); + if (workspaceSetting.interpreter.length === 0) { + updateStatus(vscode.l10n.t('Please select a Python interpreter.'), vscode.LanguageStatusSeverity.Error); + traceError( + 'Python interpreter missing:\r\n' + + '[Option 1] Select python interpreter using the ms-python.python (select interpreter command).\r\n' + + `[Option 2] Set an interpreter using "${serverId}.interpreter" setting.\r\n`, + `Please use Python ${PYTHON_VERSION} or greater.`, + ); + } else { + lsClient = await restartServer(workspaceSetting, serverId, serverName, outputChannel, lsClient); + } + } finally { + isRestarting = false; } };