From 6cab191d962b96918e30c621dfd9d9ce0b234367 Mon Sep 17 00:00:00 2001 From: Jitendra Gundaniya Date: Wed, 18 Dec 2024 15:35:22 +0530 Subject: [PATCH] Bug fix and Refactor extension.js Signed-off-by: Jitendra Gundaniya --- src/common/activationHelper.ts | 167 +++++++++++++++++++++++++++++ src/common/commands.ts | 7 +- src/common/utilities.ts | 7 ++ src/extension.ts | 189 ++++++--------------------------- 4 files changed, 210 insertions(+), 160 deletions(-) create mode 100644 src/common/activationHelper.ts diff --git a/src/common/activationHelper.ts b/src/common/activationHelper.ts new file mode 100644 index 0000000..322c2bc --- /dev/null +++ b/src/common/activationHelper.ts @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { + selectEnvironment, + executeServerCommand, + executeServerDefinitionCommand, + setKedroProjectPath, +} from './commands'; + +import * as vscode from 'vscode'; +import { registerLogger, traceError, traceLog, traceVerbose } from './log/logging'; +import { checkVersion, getInterpreterDetails, onDidChangePythonInterpreter, resolveInterpreter } from './python'; +import { sendHeapEventWithMetadata } from './telemetry'; +import { restartServer } from './server'; +import { checkIfConfigurationChanged, getInterpreterFromSetting } from './settings'; +import { loadServerDefaults } from './setup'; +import { createStatusBar } from './status_bar'; +import { getLSClientTraceLevel, updateKedroVizPanel } from './utilities'; +import { createOutputChannel, onDidChangeConfiguration, registerCommand } from './vscodeapi'; +import KedroVizPanel from '../webview/vizWebView'; +import { handleKedroViz } from '../webview/createOrShowKedroVizPanel'; +import { LanguageClient } from 'vscode-languageclient/node'; + +/** + * Runs the language server based on current environment and interpreter settings. + * Returns the updated lsClient reference. + */ +export const runServer = async ( + lsClient: LanguageClient | undefined, + selectedEnvironment?: vscode.QuickPickItem, +): Promise => { + const serverInfo = loadServerDefaults(); + const serverName = serverInfo.name; + const serverId = serverInfo.module; + + const outputChannel = createOutputChannel(serverName); + const interpreter = getInterpreterFromSetting(serverId); + let env: string | undefined = selectedEnvironment?.label; + + if (interpreter && interpreter.length > 0) { + if (checkVersion(await resolveInterpreter(interpreter))) { + traceVerbose(`Using interpreter from ${serverInfo.module}.interpreter: ${interpreter.join(' ')}`); + lsClient = await restartServer(serverId, serverName, outputChannel, lsClient, env); + } + return lsClient; + } + + const interpreterDetails = await getInterpreterDetails(); + console.log('===============DEBUG============'); + console.log(interpreterDetails); + console.log('===============DEBUG============'); + + if (interpreterDetails.path) { + traceVerbose(`Using interpreter from Python extension: ${interpreterDetails.path.join(' ')}`); + lsClient = await restartServer(serverId, serverName, outputChannel, lsClient, env); + return lsClient; + } + + 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 3.8 or greater.', + ); + + return lsClient; +}; + +/** + * Registers commands, events, and sets up logging and status bar. + * Accepts get/set functions for lsClient so that whenever runServer updates it, + * we can update the stored reference as well. + */ +export const registerCommandsAndEvents = ( + context: vscode.ExtensionContext, + getLSClient: () => LanguageClient | undefined, + setLSClient: (client: LanguageClient | undefined) => void, +) => { + const serverInfo = loadServerDefaults(); + const serverName = serverInfo.name; + const serverId = serverInfo.module; + + const outputChannel = createOutputChannel(serverName); + + // List of commands + const CMD_RESTART_SERVER = `${serverId}.restart`; + const CMD_SELECT_ENV = `${serverId}.selectEnvironment`; + const CMD_RUN_KEDRO_VIZ = `${serverId}.runKedroViz`; + const CMD_DEFINITION_REQUEST = `${serverId}.sendDefinitionRequest`; + const CMD_SHOW_OUTPUT_CHANNEL = `${serverId}.showOutputChannel`; + const CMD_SET_PROJECT_PATH = `${serverId}.kedroProjectPath`; + + (async () => { + // Status Bar + const statusBarItem = await createStatusBar(CMD_SELECT_ENV, serverId); + context.subscriptions.push(statusBarItem); + + // Setup logging + context.subscriptions.push(outputChannel, registerLogger(outputChannel)); + + const changeLogLevel = async (c: vscode.LogLevel, g: vscode.LogLevel) => { + const level = getLSClientTraceLevel(c, g); + await getLSClient()?.setTrace(level); + }; + + context.subscriptions.push( + outputChannel.onDidChangeLogLevel(async (e) => { + await changeLogLevel(e, vscode.env.logLevel); + }), + vscode.env.onDidChangeLogLevel(async (e) => { + await changeLogLevel(outputChannel.logLevel, e); + }), + ); + + traceLog(`Name: ${serverInfo.name}`); + traceLog(`Module: ${serverInfo.module}`); + traceVerbose(`Full Server Info: ${JSON.stringify(serverInfo)}`); + + context.subscriptions.push( + onDidChangePythonInterpreter(async () => { + const newClient = await runServer(getLSClient()); + setLSClient(newClient); + }), + onDidChangeConfiguration(async (e: vscode.ConfigurationChangeEvent) => { + if (checkIfConfigurationChanged(e, serverId)) { + const newClient = await runServer(getLSClient()); + setLSClient(newClient); + } + }), + registerCommand(CMD_RESTART_SERVER, async () => { + const newClient = await runServer(getLSClient()); + setLSClient(newClient); + await sendHeapEventWithMetadata(CMD_RESTART_SERVER, context); + + // If KedroVizPanel is open, update the data on server restart + if (KedroVizPanel.currentPanel) { + updateKedroVizPanel(getLSClient()); + } + }), + registerCommand(CMD_SELECT_ENV, async () => { + const result = await selectEnvironment(); + const newClient = await runServer(getLSClient(), result); + setLSClient(newClient); + if (result) { + statusBarItem.text = `$(kedro-logo) base + ${result.label}`; + } + await sendHeapEventWithMetadata(CMD_SELECT_ENV, context); + }), + registerCommand('pygls.server.executeCommand', async () => { + await executeServerCommand(getLSClient()); + }), + registerCommand(CMD_DEFINITION_REQUEST, async (word) => { + await executeServerDefinitionCommand(getLSClient(), word); + await sendHeapEventWithMetadata(CMD_DEFINITION_REQUEST, context); + }), + registerCommand(CMD_RUN_KEDRO_VIZ, async () => { + await handleKedroViz(context, getLSClient()); + }), + registerCommand(CMD_SHOW_OUTPUT_CHANNEL, () => { + outputChannel.show(); + }), + registerCommand(CMD_SET_PROJECT_PATH, () => { + setKedroProjectPath(); + }), + ); + })(); +}; diff --git a/src/common/commands.ts b/src/common/commands.ts index 53a9744..6f0b705 100644 --- a/src/common/commands.ts +++ b/src/common/commands.ts @@ -1,14 +1,13 @@ import * as fs from 'fs'; import * as path from 'path'; -import { QuickPickItem, window, Uri } from 'vscode'; +import { QuickPickItem, window } from 'vscode'; import * as vscode from 'vscode'; import { getWorkspaceFolders } from './vscodeapi'; import { LanguageClient, State } from 'vscode-languageclient/node'; -import { isKedroProject } from './utilities'; +import { getKedroProjectPath, isKedroProject } from './utilities'; export async function selectEnvironment() { - const config = vscode.workspace.getConfiguration('kedro'); - let kedroProjectPath = config.get('kedroProjectPath'); + let kedroProjectPath = await getKedroProjectPath(); let kedroProjectRootDir: string | undefined = undefined; if (kedroProjectPath) { diff --git a/src/common/utilities.ts b/src/common/utilities.ts index 9fe7c57..0f55fca 100644 --- a/src/common/utilities.ts +++ b/src/common/utilities.ts @@ -13,6 +13,7 @@ import { DEPENDENCIES_INSTALLED, EXTENSION_ROOT_DIR, PROJECT_METADATA, TELEMETRY import { traceError, traceLog } from './log/logging'; import KedroVizPanel from '../webview/vizWebView'; import { executeGetProjectDataCommand } from './commands'; +import { getWorkspaceSettings } from './settings'; function logLevelToTrace(logLevel: LogLevel): Trace { switch (logLevel) { @@ -225,3 +226,9 @@ async function checkPyprojectToml(projectPath: string): Promise { } return false; } + +export async function getKedroProjectPath(): Promise { + const projectRoot = await getProjectRoot(); + const workspaceSetting = await getWorkspaceSettings('kedro', projectRoot); + return workspaceSetting.kedroProjectPath; +} diff --git a/src/extension.ts b/src/extension.ts index 35bcee2..09b7e50 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,86 +1,32 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { - selectEnvironment, - executeServerCommand, - executeServerDefinitionCommand, - setKedroProjectPath, -} from './common/commands'; - import * as vscode from 'vscode'; import { LanguageClient } from 'vscode-languageclient/node'; -import { registerLogger, traceError, traceLog, traceVerbose } from './common/log/logging'; -import { - checkVersion, - getInterpreterDetails, - initializePython, - onDidChangePythonInterpreter, - resolveInterpreter, -} from './common/python'; -import { sendHeapEventWithMetadata } from './common/telemetry'; -import { restartServer } from './common/server'; -import { checkIfConfigurationChanged, getInterpreterFromSetting } from './common/settings'; +import { traceError, traceLog, traceVerbose } from './common/log/logging'; +import { initializePython } from './common/python'; + +import { getInterpreterFromSetting } from './common/settings'; import { loadServerDefaults } from './common/setup'; -import { createStatusBar } from './common/status_bar'; + import { - getLSClientTraceLevel, - updateKedroVizPanel, checkKedroProjectConsent, installTelemetryDependenciesIfNeeded, isKedroProject, + getKedroProjectPath, } from './common/utilities'; -import { createOutputChannel, onDidChangeConfiguration, registerCommand } from './common/vscodeapi'; -import KedroVizPanel from './webview/vizWebView'; -import { handleKedroViz } from './webview/createOrShowKedroVizPanel'; + +import { runServer, registerCommandsAndEvents } from './common/activationHelper'; let lsClient: LanguageClient | undefined; +let isCommandsAndEventsRegistered = false; export async function activate(context: vscode.ExtensionContext): Promise { // This is required to get server name and module. This should be // the first thing that we do in this extension. const serverInfo = loadServerDefaults(); - const serverName = serverInfo.name; const serverId = serverInfo.module; - // Setup logging - const outputChannel = createOutputChannel(serverName); - - const config = vscode.workspace.getConfiguration('kedro'); - let kedroProjectPath = config.get('kedroProjectPath', ''); - - const runServer = async (selectedEnvironment?: vscode.QuickPickItem) => { - const interpreter = getInterpreterFromSetting(serverId); - let env = undefined; - if (selectedEnvironment) { - env = selectedEnvironment.label; - } - - if (interpreter && interpreter.length > 0) { - if (checkVersion(await resolveInterpreter(interpreter))) { - traceVerbose(`Using interpreter from ${serverInfo.module}.interpreter: ${interpreter.join(' ')}`); - lsClient = await restartServer(serverId, serverName, outputChannel, lsClient, env); - } - return; - } - - const interpreterDetails = await getInterpreterDetails(); - console.log('===============DEBUG============'); - console.log(interpreterDetails); - console.log('===============DEBUG============'); - - if (interpreterDetails.path) { - traceVerbose(`Using interpreter from Python extension: ${interpreterDetails.path.join(' ')}`); - lsClient = await restartServer(serverId, serverName, outputChannel, lsClient, env); - return; - } - - 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 3.8 or greater.', - ); - }; + let kedroProjectPath = await getKedroProjectPath(); if (kedroProjectPath && kedroProjectPath.trim() !== '') { // User provided a Kedro project path in settings @@ -108,21 +54,17 @@ export async function activate(context: vscode.ExtensionContext): Promise console.log('Kedro VSCode extension: No Kedro project detected and no project path set.'); traceLog('No Kedro project detected and no kedro.projectPath set. Extension deactivated.'); - context.subscriptions.push( - onDidChangeConfiguration(async (e: vscode.ConfigurationChangeEvent) => { - if (checkIfConfigurationChanged(e, serverId)) { - // Clean up existing - if (lsClient) { - await lsClient.stop(); - } - await runServer(); - } - }), - registerCommand('kedro.kedroProjectPath', async () => { - setKedroProjectPath(); - }), - ); - + if (!isCommandsAndEventsRegistered) { + // Register all commands and events + registerCommandsAndEvents( + context, + () => lsClient, + (newClient) => { + lsClient = newClient; + }, + ); + isCommandsAndEventsRegistered = true; + } return; } } @@ -137,82 +79,17 @@ export async function activate(context: vscode.ExtensionContext): Promise traceLog(`Module: ${serverInfo.module}`); traceVerbose(`Full Server Info: ${JSON.stringify(serverInfo)}`); - // List of commands - const CMD_RESTART_SERVER = `${serverId}.restart`; - const CMD_SELECT_ENV = `${serverId}.selectEnvironment`; - const CMD_RUN_KEDRO_VIZ = `${serverId}.runKedroViz`; - const CMD_DEFINITION_REQUEST = `${serverId}.sendDefinitionRequest`; - const CMD_SHOW_OUTPUT_CHANNEL = `${serverId}.showOutputChannel`; - const CMD_SET_PROJECT_PATH = `${serverId}.kedroProjectPath`; - - // Status Bar - const statusBarItem = await createStatusBar(CMD_SELECT_ENV, serverId); - context.subscriptions.push(statusBarItem); - - context.subscriptions.push(outputChannel, registerLogger(outputChannel)); - - const changeLogLevel = async (c: vscode.LogLevel, g: vscode.LogLevel) => { - const level = getLSClientTraceLevel(c, g); - await lsClient?.setTrace(level); - }; - - context.subscriptions.push( - outputChannel.onDidChangeLogLevel(async (e) => { - await changeLogLevel(e, vscode.env.logLevel); - }), - vscode.env.onDidChangeLogLevel(async (e) => { - await changeLogLevel(outputChannel.logLevel, e); - }), - ); - - // Log Server information - traceLog(`Name: ${serverInfo.name}`); - traceLog(`Module: ${serverInfo.module}`); - traceVerbose(`Full Server Info: ${JSON.stringify(serverInfo)}`); - - context.subscriptions.push( - onDidChangePythonInterpreter(async () => { - await runServer(); - }), - onDidChangeConfiguration(async (e: vscode.ConfigurationChangeEvent) => { - if (checkIfConfigurationChanged(e, serverId)) { - await runServer(); - } - }), - registerCommand(CMD_RESTART_SERVER, async () => { - await runServer(); - await sendHeapEventWithMetadata(CMD_RESTART_SERVER, context); - - // If KedroVizPanel is open, update the data on server restart - if (KedroVizPanel.currentPanel) { - updateKedroVizPanel(lsClient); - } - }), - registerCommand(CMD_SELECT_ENV, async () => { - const result = await selectEnvironment(); - runServer(result); - if (result) { - statusBarItem.text = `$(kedro-logo) base + ${result.label}`; - } - await sendHeapEventWithMetadata(CMD_SELECT_ENV, context); - }), - registerCommand('pygls.server.executeCommand', async () => { - await executeServerCommand(lsClient); - }), - registerCommand(CMD_DEFINITION_REQUEST, async (word) => { - await executeServerDefinitionCommand(lsClient, word); - await sendHeapEventWithMetadata(CMD_DEFINITION_REQUEST, context); - }), - registerCommand(CMD_RUN_KEDRO_VIZ, async () => { - await handleKedroViz(context, lsClient); - }), - registerCommand(CMD_SHOW_OUTPUT_CHANNEL, () => { - outputChannel.show(); - }), - registerCommand(CMD_SET_PROJECT_PATH, () => { - setKedroProjectPath(); - }), - ); + if (!isCommandsAndEventsRegistered) { + // Register all commands and events + registerCommandsAndEvents( + context, + () => lsClient, + (newClient) => { + lsClient = newClient; + }, + ); + isCommandsAndEventsRegistered = true; + } setImmediate(async () => { const interpreter = getInterpreterFromSetting(serverId); @@ -222,7 +99,7 @@ export async function activate(context: vscode.ExtensionContext): Promise await initializePython(context.subscriptions); traceLog(`Python extension loaded`); } else { - await runServer(); + lsClient = await runServer(lsClient); } }); }