diff --git a/workspaces/mi/mi-extension/src/debugger/debugHelper.ts b/workspaces/mi/mi-extension/src/debugger/debugHelper.ts index ffed46660cd..effaae44f82 100644 --- a/workspaces/mi/mi-extension/src/debugger/debugHelper.ts +++ b/workspaces/mi/mi-extension/src/debugger/debugHelper.ts @@ -38,6 +38,7 @@ import { serverLog, showServerOutputChannel } from '../util/serverLogger'; import { getJavaHomeFromConfig, getServerPathFromConfig } from '../util/onboardingUtils'; import * as crypto from 'crypto'; import { Uri, workspace } from "vscode"; +import { MILanguageClient } from '../lang-client/activator'; const child_process = require('child_process'); const findProcess = require('find-process'); @@ -422,7 +423,8 @@ export async function stopServer(projectUri: string, serverPath: string, isWindo export async function executeTasks(projectUri: string, serverPath: string, isDebug: boolean): Promise { const maxTimeout = 120000; return new Promise(async (resolve, reject) => { - const isTerminated = await getStateMachine(projectUri).context().langClient?.shutdownTryoutServer(); + const langClient = await MILanguageClient.getInstance(projectUri); + const isTerminated = await langClient.shutdownTryoutServer(); if (!isTerminated) { reject('Failed to terminate the tryout server. Kill the server manually and try again.'); } diff --git a/workspaces/mi/mi-extension/src/debugger/debugger.ts b/workspaces/mi/mi-extension/src/debugger/debugger.ts index b247fce15f5..665d0d7e6cf 100644 --- a/workspaces/mi/mi-extension/src/debugger/debugger.ts +++ b/workspaces/mi/mi-extension/src/debugger/debugger.ts @@ -27,6 +27,7 @@ import { webviews } from '../visualizer/webview'; import { extension } from '../MIExtensionContext'; import { reject } from 'lodash'; import { LogLevel, logDebug } from '../util/logger'; +import { MILanguageClient } from '../lang-client/activator'; export interface RuntimeBreakpoint { id: number; @@ -87,7 +88,7 @@ export class Debugger extends EventEmitter { return; } const projectUri = workspace.uri.fsPath; - const langClient = getStateMachine(projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const breakpointPerFile: RuntimeBreakpoint[] = []; // To maintain the valid and invalid breakpoints in the vscode const vscodeBreakpointsPerFile: RuntimeBreakpoint[] = []; @@ -191,7 +192,7 @@ export class Debugger extends EventEmitter { return; } const projectUri = workspace.uri.fsPath; - const langClient = getStateMachine(projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const stepOverBreakpoints: RuntimeBreakpoint[] = []; if (path) { // create BreakpointPosition array @@ -278,7 +279,7 @@ export class Debugger extends EventEmitter { throw new Error(`No workspace found for path: ${filePath}`); } const projectUri = workspace.uri.fsPath; - const langClient = getStateMachine(projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); // create BreakpointPosition[] array const breakpointPositions = breakpoints.map((breakpoint) => { return { line: breakpoint.line, column: breakpoint?.column }; @@ -296,7 +297,7 @@ export class Debugger extends EventEmitter { public async getNextMediatorBreakpoint(): Promise { try { - const langClient = getStateMachine(this.projectUri).context().langClient; + const langClient = await MILanguageClient.getInstance(this.projectUri); if (!langClient) { throw new Error('Language client is not initialized'); } diff --git a/workspaces/mi/mi-extension/src/extension.ts b/workspaces/mi/mi-extension/src/extension.ts index 1598808384f..16b0c474dc3 100644 --- a/workspaces/mi/mi-extension/src/extension.ts +++ b/workspaces/mi/mi-extension/src/extension.ts @@ -34,6 +34,7 @@ import { webviews } from './visualizer/webview'; import { v4 as uuidv4 } from 'uuid'; import path from 'path'; import { COMMANDS } from './constants'; +import { enableLS } from './util/workspace'; const os = require('os'); export async function activate(context: vscode.ExtensionContext) { @@ -92,12 +93,16 @@ export async function activate(context: vscode.ExtensionContext) { activateRuntimeService(context, firstProject); activateVisualizer(context, firstProject); activateAiPanel(context); + + workspace.workspaceFolders?.forEach(folder => { + context.subscriptions.push(...enableLS()); + }); } export async function deactivate(): Promise { const clients = await MILanguageClient.getAllInstances(); clients.forEach(async client => { - await client?.languageClient?.stop(); + await client?.stop(); }); // close all webviews diff --git a/workspaces/mi/mi-extension/src/lang-client/activator.ts b/workspaces/mi/mi-extension/src/lang-client/activator.ts index fca9269af51..e193334d44a 100644 --- a/workspaces/mi/mi-extension/src/lang-client/activator.ts +++ b/workspaces/mi/mi-extension/src/lang-client/activator.ts @@ -101,8 +101,11 @@ const versionRegex = /(\d+\.\d+\.?\d*)/g; export class MILanguageClient { private static _instances: Map = new Map(); - private static lsChannelCache: Map = new Map(); - public languageClient: ExtendedLanguageClient | undefined; + private static lsChannels: Map = new Map(); + private static stopTimers: Map = new Map(); + private static stoppingInstances: Set = new Set(); + private static readonly STOP_DEBOUNCE_MS = 30000; // 30 seconds + private languageClient: ExtendedLanguageClient | undefined; // eslint-disable-next-line @typescript-eslint/naming-convention private COMPATIBLE_JDK_VERSION = "11"; // Minimum JDK version required to run the language server @@ -110,21 +113,55 @@ export class MILanguageClient { constructor(private projectUri: string) { } - public static async getInstance(projectUri: string): Promise { + public static async getInstance(projectUri: string): Promise { + // Cancel any pending stop operation for this project + const existingTimer = this.stopTimers.get(projectUri); + if (existingTimer) { + clearTimeout(existingTimer); + this.stopTimers.delete(projectUri); + } + + // If instance is currently stopping, wait for it to complete and create a new one + if (this.stoppingInstances.has(projectUri)) { + // Wait a bit for the stop operation to complete + await new Promise(resolve => setTimeout(resolve, 100)); + this.stoppingInstances.delete(projectUri); + } + if (!this._instances.has(projectUri)) { const instance = new MILanguageClient(projectUri); await instance.launch(projectUri); this._instances.set(projectUri, instance); } - return this._instances.get(projectUri)!; + const languageClient = this._instances.get(projectUri)!.languageClient; + if (!languageClient) { + const errorMessage = "Language client failed to initialize"; + window.showErrorMessage(errorMessage); + throw new Error(errorMessage); + } + return languageClient; } public static async stopInstance(projectUri: string) { - const instance = this._instances.get(projectUri); - if (instance) { - await instance.stop(); - this._instances.delete(projectUri); + // Cancel any existing timer for this project + const existingTimer = this.stopTimers.get(projectUri); + if (existingTimer) { + clearTimeout(existingTimer); } + + // Schedule the stop operation with debounce + const timer = setTimeout(async () => { + this.stoppingInstances.add(projectUri); + const instance = this._instances.get(projectUri); + if (instance) { + await instance.stop(); + this._instances.delete(projectUri); + } + this.stopTimers.delete(projectUri); + this.stoppingInstances.delete(projectUri); + }, this.STOP_DEBOUNCE_MS); + + this.stopTimers.set(projectUri, timer); } public static async getAllInstances(): Promise { @@ -136,10 +173,10 @@ export class MILanguageClient { } public static getOrCreateOutputChannel(projectUri: string): vscode.OutputChannel { - let channel = this.lsChannelCache.get(projectUri); + let channel = this.lsChannels.get(projectUri); if (!channel) { channel = vscode.window.createOutputChannel(`Synapse Language Server - ${path.basename(projectUri)}`); - this.lsChannelCache.set(projectUri, channel); + this.lsChannels.set(projectUri, channel); } return channel; } diff --git a/workspaces/mi/mi-extension/src/project-explorer/activate.ts b/workspaces/mi/mi-extension/src/project-explorer/activate.ts index b121e0b1818..b43b8dc0c80 100644 --- a/workspaces/mi/mi-extension/src/project-explorer/activate.ts +++ b/workspaces/mi/mi-extension/src/project-explorer/activate.ts @@ -37,27 +37,28 @@ import { webviews } from '../visualizer/webview'; import { MILanguageClient } from '../lang-client/activator'; let isProjectExplorerInitialized = false; -export async function activateProjectExplorer(treeviewId: string, context: ExtensionContext, lsClient: ExtendedLanguageClient, isInWI: boolean) { +export async function activateProjectExplorer(treeviewId: string, context: ExtensionContext, projectUri: string, isInWI: boolean) { if (isProjectExplorerInitialized) { return; } isProjectExplorerInitialized = true; + const lsClient: ExtendedLanguageClient = await MILanguageClient.getInstance(projectUri); const projectExplorerDataProvider = new ProjectExplorerEntryProvider(context); await projectExplorerDataProvider.refresh(); let registryExplorerDataProvider; const projectTree = window.createTreeView(treeviewId, { treeDataProvider: projectExplorerDataProvider }); - const projectDetailsRes = await lsClient?.getProjectDetails(); + const projectDetailsRes = await lsClient.getProjectDetails(); const runtimeVersion = projectDetailsRes.primaryDetails.runtimeVersion.value; const isRegistrySupported = compareVersions(runtimeVersion, RUNTIME_VERSION_440) < 0; - commands.registerCommand(COMMANDS.REFRESH_COMMAND, () => { + commands.registerCommand(COMMANDS.REFRESH_COMMAND, () => { if (isInWI) { commands.executeCommand(COMMANDS.WI_PROJECT_EXPLORER_VIEW_REFRESH); return; } - return projectExplorerDataProvider.refresh(); + return projectExplorerDataProvider.refresh(); }); commands.registerCommand(COMMANDS.ADD_ARTIFACT_COMMAND, (entry: ProjectExplorerEntry) => { @@ -512,7 +513,7 @@ export async function activateProjectExplorer(treeviewId: string, context: Exten window.showErrorMessage('Cannot find workspace folder'); return; } - const langClient = getStateMachine(workspace.uri.fsPath).context().langClient; + const langClient = await MILanguageClient.getInstance(workspace.uri.fsPath); if (!langClient) { window.showErrorMessage('Language client not found.'); return; @@ -571,7 +572,7 @@ export async function activateProjectExplorer(treeviewId: string, context: Exten if (filePath !== "") { const fileName = path.basename(filePath); const langClient = await MILanguageClient.getInstance(workspace.uri.fsPath); - const fileUsageIdentifiers = await langClient?.languageClient?.getResourceUsages(filePath); + const fileUsageIdentifiers = await langClient.getResourceUsages(filePath); const fileUsageMessage = fileUsageIdentifiers?.length && fileUsageIdentifiers?.length > 0 ? "It is used in:\n" + fileUsageIdentifiers.join(", ") : "No usage found"; window.showInformationMessage("Do you want to delete : " + fileName + "\n\n" + fileUsageMessage, { modal: true }, "Yes") .then(async answer => { @@ -659,7 +660,7 @@ export async function activateProjectExplorer(treeviewId: string, context: Exten window.showErrorMessage('Cannot find workspace folder'); throw new Error('Cannot find workspace folder'); } - const langClient = getStateMachine(workspace.uri.fsPath).context().langClient; + const langClient = await MILanguageClient.getInstance(workspace.uri.fsPath); // Read the POM file const workspaceFolder = vscode.workspace.getWorkspaceFolder(Uri.file(filePath)); diff --git a/workspaces/mi/mi-extension/src/project-explorer/project-explorer-provider.ts b/workspaces/mi/mi-extension/src/project-explorer/project-explorer-provider.ts index 6161b15c4e5..c67e621e8ab 100644 --- a/workspaces/mi/mi-extension/src/project-explorer/project-explorer-provider.ts +++ b/workspaces/mi/mi-extension/src/project-explorer/project-explorer-provider.ts @@ -145,8 +145,8 @@ async function getProjectStructureData(): Promise { continue; } const langClient = await MILanguageClient.getInstance(rootPath); - const resp = await langClient?.languageClient?.getProjectExplorerModel(rootPath); - const projectDetailsRes = await langClient?.languageClient?.getProjectDetails(); + const resp = await langClient.getProjectExplorerModel(rootPath); + const projectDetailsRes = await langClient.getProjectDetails(); const runtimeVersion = projectDetailsRes.primaryDetails.runtimeVersion.value; const projectTree = await generateTreeData(workspace, resp, runtimeVersion); diff --git a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts index b27a86cab72..d76570a97d7 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/ai-panel/rpc-manager.ts @@ -50,6 +50,7 @@ import { codeDiagnostics } from "../../ai-panel/copilot/diagnostics/diagnostics" import { getLoginMethod } from '../../ai-panel/auth'; import { LoginMethod } from '@wso2/mi-core'; import { logInfo, logWarn, logError, logDebug } from '../../ai-panel/copilot/logger'; +import { MILanguageClient } from '../../lang-client/activator'; export class MIAIPanelRpcManager implements MIAIPanelAPI { private eventHandler: CopilotEventHandler; @@ -374,13 +375,7 @@ export class MIAIPanelRpcManager implements MIAIPanelAPI { this.eventHandler.handleCodeDiagnosticStart(xmlCodes); // Get diagnostics using existing RPC infrastructure - const { getStateMachine } = await import('../../stateMachine'); - const stateMachine = getStateMachine(this.projectUri); - if (!stateMachine) { - throw new Error('State machine not found for project'); - } - - const langClient = stateMachine.context().langClient; + const langClient = await MILanguageClient.getInstance(this.projectUri); if (!langClient) { throw new Error('Language client not available'); } diff --git a/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-manager.ts index e0986507e3c..81a1a8a475e 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-manager.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/mi-data-mapper/rpc-manager.ts @@ -57,6 +57,7 @@ import { DM_OPERATORS_FILE_NAME, DM_OPERATORS_IMPORT_NAME, READONLY_MAPPING_FUNC import { readTSFile, removeMapFunctionEntry, showMappingEndNotification } from "../../util/ai-datamapper-utils"; import { compareVersions } from "../../util/onboardingUtils"; import { mapDataMapper } from "../../ai-panel/copilot/data-mapper/mapper"; +import { MILanguageClient } from "../../lang-client/activator"; const undoRedoManager = new UndoRedoManager(); @@ -331,7 +332,7 @@ export class MiDataMapperRpcManager implements MIDataMapperAPI { const workspaceFolder = workspace.getWorkspaceFolder(Uri.file(filePath)); let miDiagramRpcManager: MiDiagramRpcManager = new MiDiagramRpcManager(this.projectUri); - const langClient = getStateMachine(this.projectUri).context().langClient; + const langClient = await MILanguageClient.getInstance(this.projectUri); const projectDetailsRes = await langClient?.getProjectDetails(); const runtimeVersion = projectDetailsRes.primaryDetails.runtimeVersion.value; const isResourceContentUsed = compareVersions(runtimeVersion, RUNTIME_VERSION_440) >= 0; diff --git a/workspaces/mi/mi-extension/src/rpc-managers/mi-debugger/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/mi-debugger/rpc-manager.ts index 8594322cf9f..1957ff755bd 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/mi-debugger/rpc-manager.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/mi-debugger/rpc-manager.ts @@ -33,13 +33,14 @@ import { } from "@wso2/mi-core"; import * as vscode from "vscode"; import { getStateMachine, refreshUI } from "../../stateMachine"; +import { MILanguageClient } from "../../lang-client/activator"; export class MiDebuggerRpcManager implements MiDebuggerAPI { constructor(private projectUri: string) { } async validateBreakpoints(params: ValidateBreakpointsRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const definition = await langClient.validateBreakpoints(params); resolve(definition); @@ -48,7 +49,7 @@ export class MiDebuggerRpcManager implements MiDebuggerAPI { async getBreakpointInfo(params: GetBreakpointInfoRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const breakpointInfo = await langClient.getBreakpointInfo(params); resolve(breakpointInfo); @@ -113,7 +114,7 @@ export class MiDebuggerRpcManager implements MiDebuggerAPI { async getStepOverBreakpoint(params: StepOverBreakpointRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const breakpointInfo = await langClient.getStepOverBreakpoint(params); resolve(breakpointInfo); diff --git a/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts index 1896e0c0236..d95b19e7f45 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts @@ -339,6 +339,7 @@ import { MiVisualizerRpcManager } from "../mi-visualizer/rpc-manager"; import { DebuggerConfig } from "../../debugger/config"; import { getKubernetesConfiguration, getKubernetesDataConfiguration } from "../../util/template-engine/mustach-templates/KubernetesConfiguration"; import { parseStringPromise, Builder } from "xml2js"; +import { MILanguageClient } from "../../lang-client/activator"; const AdmZip = require('adm-zip'); const { XMLParser, XMLBuilder } = require("fast-xml-parser"); @@ -473,7 +474,7 @@ export class MiDiagramRpcManager implements MiDiagramAPI { async tryOutMediator(params: MediatorTryOutRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.tryOutMediator(params); resolve(res); }); @@ -481,7 +482,7 @@ export class MiDiagramRpcManager implements MiDiagramAPI { async shutDownTryoutServer(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.shutdownTryoutServer(); resolve(res); }); @@ -499,7 +500,7 @@ export class MiDiagramRpcManager implements MiDiagramAPI { const payloadPath = path.join(this.projectUri, ".tryout", "input.json"); const payload = fs.readFileSync(payloadPath, "utf8"); params.inputPayload = payload - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getMediatorInputOutputSchema(params); resolve(res); }); @@ -526,7 +527,7 @@ export class MiDiagramRpcManager implements MiDiagramAPI { } return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getSyntaxTree({ documentIdentifier: { uri: documentUri @@ -636,7 +637,7 @@ export class MiDiagramRpcManager implements MiDiagramAPI { let fileName: string; let response: GenerateAPIResponse = { apiXml: "", endpointXml: "" }; if (!xmlData) { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const projectDetailsRes = await langClient?.getProjectDetails(); const runtimeVersion = projectDetailsRes.primaryDetails.runtimeVersion.value; const isRegistrySupported = compareVersions(runtimeVersion, RUNTIME_VERSION_440) < 0; @@ -1507,7 +1508,7 @@ export class MiDiagramRpcManager implements MiDiagramAPI { async getEndpointsAndSequences(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const resp = await langClient.getProjectStructure(this.projectUri); const artifacts = (resp.directoryMap as any).src.main.wso2mi.artifacts; @@ -1536,7 +1537,7 @@ export class MiDiagramRpcManager implements MiDiagramAPI { async getAllAPIcontexts(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const resp = await langClient.getProjectStructure(this.projectUri); const artifacts = (resp.directoryMap as any).src.main.wso2mi.artifacts; @@ -1552,7 +1553,7 @@ export class MiDiagramRpcManager implements MiDiagramAPI { async getTemplates(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const resp = await langClient.getProjectStructure(this.projectUri); const artifacts = (resp.directoryMap as any).src.main.wso2mi.artifacts; @@ -3123,7 +3124,7 @@ ${endpointAttributes} async getESBConfigs(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const resp = await langClient.getProjectStructure(this.projectUri); const ESBConfigs: string[] = []; @@ -3436,7 +3437,7 @@ ${endpointAttributes} async convertPdfToBase64Images(params: string): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const images = await langClient.pdfToImagesBase64(params) resolve(images); }); @@ -3874,7 +3875,7 @@ ${endpointAttributes} async getDefinition(params: GetDefinitionRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const definition = await langClient.getDefinition(params); resolve(definition); @@ -3896,11 +3897,11 @@ ${endpointAttributes} } async getDiagnostics(params: GetDiagnosticsReqeust): Promise { - return getStateMachine(this.projectUri).context().langClient!.getDiagnostics(params); + return (await MILanguageClient.getInstance(this.projectUri)).getDiagnostics(params); } async getAvailableResources(params: GetAvailableResourcesRequest): Promise { - return getStateMachine(this.projectUri).context().langClient!.getAvailableResources(params); + return (await MILanguageClient.getInstance(this.projectUri)).getAvailableResources(params); } async browseFile(params: BrowseFileRequest): Promise { @@ -4201,7 +4202,7 @@ ${endpointAttributes} async getAvailableConnectors(params: GetAvailableConnectorRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getAvailableConnectors({ documentUri: params.documentUri, connectorName: params.connectorName @@ -4213,7 +4214,7 @@ ${endpointAttributes} async updateConnectors(params: UpdateConnectorRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.updateConnectors({ documentUri: params.documentUri }); @@ -4351,7 +4352,7 @@ ${endpointAttributes} async saveInboundEPUischema(params: SaveInboundEPUischemaRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.saveInboundEPUischema({ connectorName: params.connectorName, uiSchema: params.uiSchema @@ -4363,7 +4364,7 @@ ${endpointAttributes} async getInboundEPUischema(params: GetInboundEPUischemaRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getInboundEPUischema({ connectorName: params.connectorName, documentPath: params.documentPath @@ -4576,7 +4577,7 @@ ${keyValuesXML}`; async getConnectorConnections(params: GetConnectorConnectionsRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getConnectorConnections({ documentUri: params.documentUri, connectorName: params.connectorName @@ -4616,7 +4617,7 @@ ${keyValuesXML}`; async getAllRegistryPaths(params: GetAllRegistryPathsRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getRegistryFiles(params.path); resolve({ registryPaths: res.map(element => element.split(path.sep).join("/")) }); }); @@ -4624,7 +4625,7 @@ ${keyValuesXML}`; async getAllResourcePaths(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getResourceFiles(); resolve({ resourcePaths: res }); }); @@ -4632,7 +4633,7 @@ ${keyValuesXML}`; async getConfigurableEntries(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getConfigurableEntries(); resolve({ configurableEntries: res }); }); @@ -4640,7 +4641,7 @@ ${keyValuesXML}`; async getAllArtifacts(params: GetAllArtifactsRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getArifactFiles(params.path); resolve({ artifacts: res }); }); @@ -4648,7 +4649,7 @@ ${keyValuesXML}`; async getArtifactType(params: GetArtifactTypeRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getArtifactType(params.filePath); resolve({ artifactType: res.artifactType, artifactFolder: res.artifactFolder }); }); @@ -4865,7 +4866,7 @@ ${keyValuesXML}`; extName: "Devant", }; - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); let integrationType: string | undefined; if (params.componentDir) { @@ -5041,7 +5042,7 @@ ${keyValuesXML}`; fs.mkdirSync(path.dirname(openAPISpecPath), { recursive: true }); }; - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const { swagger } = await langClient.swaggerFromAPI({ apiPath }); if (!fs.existsSync(openAPISpecPath)) { // Create the file if not exists @@ -5098,7 +5099,7 @@ ${keyValuesXML}`; return resolve({ swaggerExists: false }); } - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const { swagger: generatedSwagger } = await langClient.swaggerFromAPI({ apiPath: apiPath, swaggerPath: swaggerPath }); const swaggerContent = fs.readFileSync(swaggerPath, 'utf-8'); const isEqualSwagger = isEqualSwaggers({ @@ -5126,7 +5127,7 @@ ${keyValuesXML}`; let generatedSwagger = params.generatedSwagger; let existingSwagger = params.existingSwagger; if (!generatedSwagger || !existingSwagger) { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const response = await langClient.swaggerFromAPI({ apiPath: apiPath, ...(fs.existsSync(swaggerPath) && { swaggerPath: swaggerPath }) }); generatedSwagger = response.swagger; existingSwagger = fs.readFileSync(swaggerPath, 'utf-8'); @@ -5153,7 +5154,7 @@ ${keyValuesXML}`; let generatedSwagger = params.generatedSwagger; let existingSwagger = params.existingSwagger; if (!generatedSwagger || !existingSwagger) { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const response = await langClient.swaggerFromAPI({ apiPath }); generatedSwagger = response.swagger; existingSwagger = fs.readFileSync(swaggerPath, 'utf-8'); @@ -5272,7 +5273,7 @@ ${keyValuesXML}`; let range; if (!params.range) { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const st = await langClient.getSyntaxTree({ documentIdentifier: { uri: filePath @@ -5417,7 +5418,7 @@ ${keyValuesXML}`; async getOpenAPISpec(params: SwaggerTypeRequest): Promise { const swaggerPath = path.join(this.projectUri, SWAGGER_REL_DIR, `${path.basename(params.apiPath, ".xml")}.yaml`); - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); let response; if (params.isRuntimeService) { const versionedUrl = await exposeVersionedServices(this.projectUri); @@ -5542,7 +5543,7 @@ ${keyValuesXML}`; async testDbConnection(req: TestDbConnectionRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient; + const langClient = await MILanguageClient.getInstance(this.projectUri); const response = await langClient?.testDbConnection(req); resolve({ success: response ? response.success : false }); }); @@ -5608,7 +5609,7 @@ ${keyValuesXML}`; async checkDBDriver(className: string): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.checkDBDriver(className); resolve(res); }); @@ -5616,7 +5617,7 @@ ${keyValuesXML}`; async addDBDriver(params: AddDriverRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.addDBDriver(params); resolve(res); }); @@ -5624,7 +5625,7 @@ ${keyValuesXML}`; async removeDBDriver(params: AddDriverRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.removeDBDriver(params); resolve(res); }); @@ -5632,7 +5633,7 @@ ${keyValuesXML}`; async modifyDBDriver(params: AddDriverRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.modifyDBDriver(params); resolve(res); }); @@ -5641,7 +5642,7 @@ ${keyValuesXML}`; async generateDSSQueries(params: ExtendedDSSQueryGenRequest): Promise { const { documentUri, position, ...genQueryParams } = params; return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const xml = await langClient.generateQueries(genQueryParams); if (!xml) { @@ -5667,7 +5668,7 @@ ${keyValuesXML}`; async fetchDSSTables(params: DSSFetchTablesRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.fetchTables({ ...params, tableData: "", datasourceName: "" }); @@ -5677,7 +5678,7 @@ ${keyValuesXML}`; async getMediators(param: GetMediatorsRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); let response = await langClient.getMediators(param); resolve(response); }); @@ -5685,7 +5686,7 @@ ${keyValuesXML}`; async getMediator(param: GetMediatorRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); let response = await langClient.getMediator(param); resolve(response); }); @@ -5693,7 +5694,7 @@ ${keyValuesXML}`; async updateMediator(param: UpdateMediatorRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); let response = await langClient.generateSynapseConfig(param); if (response && response.textEdits) { let edits = response.textEdits; @@ -5717,7 +5718,7 @@ ${keyValuesXML}`; async getLocalInboundConnectors(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); let response = await langClient.getLocalInboundConnectors(); resolve(response); }); @@ -5725,7 +5726,7 @@ ${keyValuesXML}`; async getConnectionSchema(param: GetConnectionSchemaRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); let response = await langClient.getConnectionSchema(param); resolve(response); }); @@ -5734,7 +5735,7 @@ ${keyValuesXML}`; async getExpressionCompletions(params: ExpressionCompletionsRequest): Promise { return new Promise(async (resolve, reject) => { try { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getExpressionCompletions(params); if (!res.isIncomplete) { resolve(res); @@ -5751,7 +5752,7 @@ ${keyValuesXML}`; async getHelperPaneInfo(params: GetHelperPaneInfoRequest): Promise { return new Promise(async (resolve, reject) => { try { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); let response = await langClient.getHelperPaneInfo(params); resolve(response); } catch (error) { @@ -5763,7 +5764,7 @@ ${keyValuesXML}`; async testConnectorConnection(params: TestConnectorConnectionRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.testConnectorConnection(params); resolve(res); }); @@ -5887,7 +5888,7 @@ ${keyValuesXML}`; async getValueOfEnvVariable(variableName: string): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const response = await langClient.getConfigurableList(); const envVariable = response.find(variable => variable.key === variableName); if (envVariable && envVariable.value != null && envVariable.value !== "") { @@ -6122,7 +6123,7 @@ ${keyValuesXML}`; } async function exposeVersionedServices(projectUri: string): Promise { - const langClient = getStateMachine(projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(projectUri); const projectDetailsRes = await langClient?.getProjectDetails(); const isVersionedDeploymentEnabled = projectDetailsRes?.buildDetails?.versionedDeployment?.value; if (!isVersionedDeploymentEnabled) { diff --git a/workspaces/mi/mi-extension/src/rpc-managers/mi-visualizer/rpc-manager.ts b/workspaces/mi/mi-extension/src/rpc-managers/mi-visualizer/rpc-manager.ts index f36f8f72b44..e8e95286fd0 100644 --- a/workspaces/mi/mi-extension/src/rpc-managers/mi-visualizer/rpc-manager.ts +++ b/workspaces/mi/mi-extension/src/rpc-managers/mi-visualizer/rpc-manager.ts @@ -92,6 +92,7 @@ import { TextEdit } from "vscode-languageclient"; import { downloadJavaFromMI, downloadMI, getProjectSetupDetails, getSupportedMIVersionsHigherThan, setPathsInWorkSpace, updateRuntimeVersionsInPom, getMIVersionFromPom } from '../../util/onboardingUtils'; import { extractCAppDependenciesAsProjects } from "../../visualizer/activate"; import { findMultiModuleProjectsInWorkspaceDir } from "../../util/migrationUtils"; +import { MILanguageClient } from "../../lang-client/activator"; Mustache.escape = escapeXml; @@ -145,7 +146,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async getProjectStructure(params: ProjectStructureRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getProjectStructure(this.projectUri); resolve(res); @@ -154,7 +155,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async getProjectDetails(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getProjectDetails(); resolve(res); }); @@ -162,7 +163,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async setDeployPlugin(params: MavenDeployPluginDetails): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.setDeployPlugin(params); await this.updatePom([res.textEdit]); resolve(res); @@ -171,7 +172,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async getDeployPluginDetails(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getDeployPluginDetails(); resolve(res); }); @@ -179,7 +180,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async removeDeployPlugin(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.removeDeployPlugin(); if (res.range.start.line !== 0 && res.range.start.character !== 0) { await this.updatePom([res]); @@ -196,7 +197,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { */ async updateProperties(params: UpdatePropertiesRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.updateProperties(params); await this.updatePom(res.textEdits); resolve(true); @@ -212,7 +213,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async reloadDependencies(params?: ReloadDependenciesRequest): Promise { return new Promise(async (resolve) => { let reloadDependenciesResult = true; - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const updateDependenciesResult = await langClient?.updateConnectorDependencies(); if (!updateDependenciesResult.toLowerCase().startsWith("success")) { const connectorsNotDownloaded: string[] = []; @@ -324,7 +325,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async updateDependencies(params: UpdateDependenciesRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const projectDetails = await langClient.getProjectDetails(); const existingDependencies = projectDetails.dependencies || []; @@ -367,7 +368,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async getDependencyStatusList(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getDependencyStatusList(); resolve(res); }); @@ -423,7 +424,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async updateConnectorDependencies(): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.updateConnectorDependencies(); await extractCAppDependenciesAsProjects(this.projectUri); resolve(res); @@ -432,7 +433,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async updateDependenciesFromOverview(params: UpdateDependenciesRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.updateDependencies({ dependencies: params.dependencies }); await this.updatePom(res.textEdits); resolve(true); @@ -775,7 +776,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { } async getProjectOverview(params: ProjectStructureRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const res = await langClient.getOverviewModel(); resolve(res); }); @@ -861,7 +862,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async importOpenAPISpec(params: ImportOpenAPISpecRequest): Promise { const { filePath } = params; - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); if (filePath && filePath.length > 0) { const connectorGenRequest = { openAPIPath: filePath, @@ -903,7 +904,7 @@ export class MiVisualizerRpcManager implements MIVisualizerAPI { async updateAiDependencies(params: UpdateAiDependenciesRequest): Promise { return new Promise(async (resolve) => { - const langClient = getStateMachine(this.projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(this.projectUri); const projectDetails = await langClient.getProjectDetails(); const existingDependencies = projectDetails.dependencies || []; diff --git a/workspaces/mi/mi-extension/src/stateMachine.ts b/workspaces/mi/mi-extension/src/stateMachine.ts index b4ca482655c..7ef35f83499 100644 --- a/workspaces/mi/mi-extension/src/stateMachine.ts +++ b/workspaces/mi/mi-extension/src/stateMachine.ts @@ -14,7 +14,6 @@ import { VisualizerLocation, webviewReady } from '@wso2/mi-core'; -import { ExtendedLanguageClient } from './lang-client/ExtendedLanguageClient'; import { VisualizerWebview, webviews } from './visualizer/webview'; import { RPCLayer } from './RPCLayer'; import { history } from './history/activator'; @@ -34,7 +33,6 @@ import { containsMultiModuleNatureInProjectFile, containsMultiModuleNatureInPomF const fs = require('fs'); interface MachineContext extends VisualizerLocation { - langClient: ExtendedLanguageClient | null; dependenciesResolved?: boolean; isInWI: boolean; isLegacyRuntime?: boolean; @@ -46,7 +44,6 @@ const stateMachine = createMachine({ predictableActionArguments: true, context: { projectUri: "", - langClient: null, errors: [], view: MACHINE_VIEW.Welcome, dependenciesResolved: false, @@ -174,14 +171,12 @@ const stateMachine = createMachine({ target: 'ready', cond: (context, event) => context.displayOverview === true, actions: assign({ - langClient: (context, event) => event.data }) }, { target: 'ready.viewReady', cond: (context, event) => context.displayOverview === false, actions: assign({ - langClient: (context, event) => event.data, isLoading: (context, event) => false }) } @@ -397,12 +392,7 @@ const stateMachine = createMachine({ try { const treeViewId = context.isInWI ? WI_PROJECT_EXPLORER_VIEW_ID : MI_PROJECT_EXPLORER_VIEW_ID; vscode.commands.executeCommand(`${treeViewId}.focus`); - const instance = await MILanguageClient.getInstance(context.projectUri!); - const errors = instance.getErrors(); - if (errors.length) { - return reject(errors); - } - const ls = instance.languageClient; + const ls = await MILanguageClient.getInstance(context.projectUri!); vscode.commands.executeCommand('setContext', 'MI.status', 'projectLoaded'); resolve(ls); @@ -466,7 +456,7 @@ const stateMachine = createMachine({ }, findView: (context, event): Promise => { return new Promise(async (resolve, reject) => { - const langClient = context.langClient!; + const langClient = await MILanguageClient.getInstance(context.projectUri!); const viewLocation = context; if (context.view === MACHINE_VIEW.IdpConnectorSchemaGeneratorForm) { @@ -606,7 +596,7 @@ const stateMachine = createMachine({ } } if (viewLocation.view === MACHINE_VIEW.ResourceView) { - const res = await langClient!.getDiagnostics({ documentUri: context.documentUri! }); + const res = await langClient.getDiagnostics({ documentUri: context.documentUri! }); if (res.diagnostics && res.diagnostics.length > 0) { viewLocation.diagnostics = res.diagnostics; } @@ -658,9 +648,8 @@ const stateMachine = createMachine({ }, activateOtherFeatures: (context, event) => { return new Promise(async (resolve, reject) => { - const ls = await MILanguageClient.getInstance(context.projectUri!); const treeviewId = context.isInWI ? WI_PROJECT_EXPLORER_VIEW_ID : MI_PROJECT_EXPLORER_VIEW_ID; - await activateProjectExplorer(treeviewId, extension.context, ls.languageClient!, context.isInWI); + await activateProjectExplorer(treeviewId, extension.context, context.projectUri!, context.isInWI); await activateTestExplorer(extension.context); resolve(true); }); @@ -702,7 +691,6 @@ export const getStateMachine = (projectUri: string, context?: VisualizerLocation stateService = interpret(stateMachine.withContext({ projectUri: projectUri, - langClient: null, errors: [], view: MACHINE_VIEW.Overview, isInWI: vscode.extensions.getExtension(WI_EXTENSION_ID) ? true : false, diff --git a/workspaces/mi/mi-extension/src/test-explorer/activator.ts b/workspaces/mi/mi-extension/src/test-explorer/activator.ts index dfe4952ea6c..7b3d8c7cac0 100644 --- a/workspaces/mi/mi-extension/src/test-explorer/activator.ts +++ b/workspaces/mi/mi-extension/src/test-explorer/activator.ts @@ -97,7 +97,7 @@ export async function activateTestExplorer(extensionContext: ExtensionContext) { const fileUri = `${id.split('.xml/')[0]}.xml`; const testCaseName = id.split('.xml/')[1]; const langClient = await MILanguageClient.getInstance(getProjectRoot(Uri.parse(fileUri))!); - const st = await langClient?.languageClient?.getSyntaxTree({ + const st = await langClient.getSyntaxTree({ documentIdentifier: { uri: fileUri }, @@ -160,7 +160,7 @@ export async function activateTestExplorer(extensionContext: ExtensionContext) { try { const projectRoot = getProjectRoot(Uri.file(fileUri)); const langClient = await MILanguageClient.getInstance(projectRoot!); - const st = await langClient?.languageClient?.getSyntaxTree({ + const st = await langClient.getSyntaxTree({ documentIdentifier: { uri: fileUri }, @@ -271,7 +271,7 @@ export async function activateTestExplorer(extensionContext: ExtensionContext) { export async function createTests(uri: Uri) { const projectRoot = getProjectRoot(uri); const langClient = await MILanguageClient.getInstance(projectRoot!); - const projectDetails = await langClient?.languageClient?.getProjectDetails(); + const projectDetails = await langClient.getProjectDetails(); const projectName = projectDetails?.primaryDetails?.projectName?.value ?? getProjectName(uri); if (!testController || !projectRoot || !projectName) { @@ -351,7 +351,7 @@ async function getTestCaseNamesAndTestSuiteType(uri: Uri) { } const langClient = await MILanguageClient.getInstance(projectUri); - const st = await langClient?.languageClient?.getSyntaxTree({ + const st = await langClient.getSyntaxTree({ documentIdentifier: { uri: uri.fsPath }, @@ -363,7 +363,7 @@ async function getTestCaseNamesAndTestSuiteType(uri: Uri) { const unitTestST: UnitTest = st?.syntaxTree["unit-test"]; const testArtifact = unitTestST?.unitTestArtifacts?.testArtifact?.artifact?.textNode; - const projectStructure = await langClient.languageClient!.getProjectStructure(projectUri); + const projectStructure = await langClient!.getProjectStructure(projectUri); const artifacts = projectStructure.directoryMap.src?.main?.wso2mi?.artifacts; const apis = artifacts?.apis?.map((api: ProjectStructureArtifactResponse) => { return { name: api.name, path: api.path.split(projectUri)[1], type: "Api" } }); @@ -397,7 +397,7 @@ async function getTestCases(uri: Uri) { const projectRoot = getProjectRoot(uri); const langClient = await MILanguageClient.getInstance(projectRoot!); - const st = await langClient?.languageClient?.getSyntaxTree({ + const st = await langClient.getSyntaxTree({ documentIdentifier: { uri: uri.fsPath }, diff --git a/workspaces/mi/mi-extension/src/util/schemaBuilder.ts b/workspaces/mi/mi-extension/src/util/schemaBuilder.ts index 7b4c58d1a9f..b92175abdc0 100644 --- a/workspaces/mi/mi-extension/src/util/schemaBuilder.ts +++ b/workspaces/mi/mi-extension/src/util/schemaBuilder.ts @@ -19,6 +19,7 @@ import { JSONSchema3or4 } from "to-json-schema"; import { getStateMachine } from "../stateMachine"; import { IOType } from "@wso2/mi-core"; +import { MILanguageClient } from "../lang-client/activator"; export function convertToJSONSchema(fileContent: JSONSchema3or4): JSONSchema3or4 { let schema = JSON.parse(fileContent); @@ -26,7 +27,7 @@ export function convertToJSONSchema(fileContent: JSONSchema3or4): JSONSchema3or4 } export async function generateSchema(ioType: IOType, schemaType: string, filePath: string, projectUri: string): Promise { - const langClient = getStateMachine(projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(projectUri); const response = await langClient.generateSchema({ filePath: filePath, delimiter: "", @@ -41,7 +42,7 @@ export async function generateSchema(ioType: IOType, schemaType: string, filePat } export async function generateSchemaFromContent(projectUri: string, ioType: IOType, content: string, fileType: string, csvDelimiter?: string): Promise { - const langClient = getStateMachine(projectUri).context().langClient!; + const langClient = await MILanguageClient.getInstance(projectUri); const response = await langClient.generateSchemaFromContent({ fileContent: content, delimiter: csvDelimiter ?? "", diff --git a/workspaces/mi/mi-extension/src/util/swagger.ts b/workspaces/mi/mi-extension/src/util/swagger.ts index fbdb406de3b..66f893a72c2 100644 --- a/workspaces/mi/mi-extension/src/util/swagger.ts +++ b/workspaces/mi/mi-extension/src/util/swagger.ts @@ -24,6 +24,7 @@ import path from "path"; import * as vscode from 'vscode'; import { deleteRegistryResource } from "./fileOperations"; import { getStateMachine } from "../stateMachine"; +import { MILanguageClient } from "../lang-client/activator"; const fs = require('fs'); @@ -296,7 +297,7 @@ export function generateSwagger(apiPath: string): Promise { for (const wrkspace of workspace.workspaceFolders!) { const lsClient = await MILanguageClient.getInstance(wrkspace.uri.fsPath); if (lsClient) { - const projectDetails = await lsClient.languageClient?.getProjectDetails(); + const projectDetails = await lsClient.getProjectDetails(); if (projectDetails?.primaryDetails?.projectName?.value) { if (projects.has(projectDetails.primaryDetails.projectName.value)) { projects.set(wrkspace.uri.fsPath, wrkspace.uri.fsPath); @@ -91,12 +91,12 @@ export async function askForProject(): Promise { return projects.get(quickPick)!; } -export async function saveIdpSchemaToFile(folderPath: string, fileName:string, fileContent?: string,imageOrPdf?:string): Promise { - const documentUri = path.join(folderPath, fileName +".json"); +export async function saveIdpSchemaToFile(folderPath: string, fileName: string, fileContent?: string, imageOrPdf?: string): Promise { + const documentUri = path.join(folderPath, fileName + ".json"); if (!fs.existsSync(folderPath)) { fs.mkdirSync(folderPath, { recursive: true }); } - if(fileContent){ + if (fileContent) { fs.writeFileSync(documentUri, fileContent, 'utf-8'); } if (imageOrPdf) { @@ -110,27 +110,77 @@ export async function saveIdpSchemaToFile(folderPath: string, fileName:string, f } const mimeTypeMatch = imageOrPdf.match(/^data:(.*?);base64,/); if (mimeTypeMatch) { - const mimeType = mimeTypeMatch[1]; + const mimeType = mimeTypeMatch[1]; let extension = ""; if (mimeType === "application/pdf") { extension = ".pdf"; } else if (mimeType.startsWith("image/")) { - extension = mimeType.split("/")[1]; + extension = mimeType.split("/")[1]; extension = `.${extension}`; } else { console.error("Unsupported MIME type:", mimeType); - return false; + return false; } - const base64Data = imageOrPdf.replace(/^data:.*;base64,/, ""); + const base64Data = imageOrPdf.replace(/^data:.*;base64,/, ""); const binaryData = Buffer.from(base64Data, "base64"); const filePath = path.join(folderPath, fileName + extension); fs.writeFileSync(filePath, binaryData); } else { console.error("Invalid base64 string format."); - return false; + return false; } } commands.executeCommand(COMMANDS.REFRESH_COMMAND); return true; } +export function enableLS(): Disposable[] { + const disposables: Disposable[] = []; + + const disposable1 = window.onDidChangeActiveTextEditor(async (event) => { + if (!event) { + return; + } + const document = event.document; + const projectUri = workspace.getWorkspaceFolder(document.uri)?.uri.fsPath; + if (!projectUri) { + return; + } + const hasActiveDocument = hasOpenedDocumentInProject(projectUri); + + if (hasActiveDocument) { + await MILanguageClient.getInstance(projectUri); + } + }); + + const disposable2 = workspace.onDidCloseTextDocument(async (document) => { + const projectUri = workspace.getWorkspaceFolder(document.uri)?.uri.fsPath; + if (!projectUri) { + return; + } + const hasActiveWebview = webviews.has(projectUri); + + if (hasActiveWebview) { + return; + } + const hasActiveDocument = hasOpenedDocumentInProject(projectUri); + + if (!hasActiveDocument) { + await MILanguageClient.stopInstance(projectUri); + } + }); + disposables.push(disposable1, disposable2); + return disposables; +} + +export function hasOpenedDocumentInProject(projectUri: string): boolean { + const artifactsPath = path.join(projectUri, 'src', 'main', 'wso2mi', 'artifacts'); + for (const tabGroup of window.tabGroups.all) { + for (const tab of tabGroup.tabs) { + if (tab.input instanceof TabInputText && tab.input.uri.fsPath.startsWith(artifactsPath)) { + return true; + } + } + } + return false; +} diff --git a/workspaces/mi/mi-extension/src/visualizer/activate.ts b/workspaces/mi/mi-extension/src/visualizer/activate.ts index 5d56394e138..cfc9346def5 100644 --- a/workspaces/mi/mi-extension/src/visualizer/activate.ts +++ b/workspaces/mi/mi-extension/src/visualizer/activate.ts @@ -36,6 +36,7 @@ import { MiDiagramRpcManager } from '../rpc-managers/mi-diagram/rpc-manager'; import { log } from '../util/logger'; import { CACHED_FOLDER, INTEGRATION_PROJECT_DEPENDENCIES_DIR } from '../util/onboardingUtils'; import { getHash } from '../util/fileOperations'; +import { MILanguageClient } from '../lang-client/activator'; export function activateVisualizer(context: vscode.ExtensionContext, firstProject: string) { context.subscriptions.push( @@ -233,7 +234,7 @@ export function activateVisualizer(context: vscode.ExtensionContext, firstProjec if (e.document.uri.fsPath.endsWith('pom.xml')) { const projectUri = vscode.workspace.getWorkspaceFolder(e.document.uri)?.uri.fsPath; - const langClient = getStateMachine(projectUri!).context().langClient; + const langClient = await MILanguageClient.getInstance(projectUri!); const confirmUpdate = await vscode.window.showInformationMessage( 'The pom.xml file has been modified. Do you want to update the dependencies?', diff --git a/workspaces/mi/mi-extension/src/visualizer/webview.ts b/workspaces/mi/mi-extension/src/visualizer/webview.ts index 41805e0baa2..eac6d9bd21c 100644 --- a/workspaces/mi/mi-extension/src/visualizer/webview.ts +++ b/workspaces/mi/mi-extension/src/visualizer/webview.ts @@ -29,6 +29,7 @@ import { MACHINE_VIEW } from '@wso2/mi-core'; import { refreshDiagram } from './activate'; import { MILanguageClient } from '../lang-client/activator'; import { deletePopupStateMachine } from '../stateMachinePopup'; +import { hasOpenedDocumentInProject } from '../util/workspace'; export const webviews: Map = new Map(); export class VisualizerWebview { @@ -124,7 +125,7 @@ export class VisualizerWebview { // The JS file from the React build output const jsFiles = getComposerJSFiles(extension.context, 'Visualizer', webview); console.debug('JS files to be included:', jsFiles); - + const scriptUri = jsFiles.map(jsFile => { const scriptTag = ''; console.debug('Generated script tag:', scriptTag); @@ -277,7 +278,12 @@ export class VisualizerWebview { deleteStateMachine(this.projectUri); deletePopupStateMachine(this.projectUri); RPCLayer._messengers.delete(this.projectUri); - await MILanguageClient.stopInstance(this.projectUri); + const hasActiveDocument = hasOpenedDocumentInProject(this.projectUri); + + if (!hasActiveDocument) { + await MILanguageClient.stopInstance(this.projectUri); + } + this._panel?.dispose(); while (this._disposables.length) {