diff --git a/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts index 9e953006eaa..a1ada5a98bb 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/bi/activator.ts @@ -198,6 +198,9 @@ export function activate(context: BallerinaExtension) { function openBallerinaTomlFile(context: BallerinaExtension) { const projectPath = StateMachine.context().projectPath || StateMachine.context().workspacePath; + if (!projectPath) { + return; + } const ballerinaTomlFile = path.join(projectPath, "Ballerina.toml"); try { const content = readFileSync(ballerinaTomlFile, "utf8"); diff --git a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/activator.ts b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/activator.ts index 96d792c193b..036df018c82 100644 --- a/workspaces/ballerina/ballerina-extension/src/features/test-explorer/activator.ts +++ b/workspaces/ballerina/ballerina-extension/src/features/test-explorer/activator.ts @@ -17,17 +17,31 @@ * under the License. */ -import { tests, workspace, TestRunProfileKind, TestController } from "vscode"; +import { tests, workspace, TestRunProfileKind, TestController, Uri } from "vscode"; import { BallerinaExtension } from "../../core"; import { runHandler } from "./runner"; import { activateEditBiTest } from "./commands"; import { discoverTestFunctionsInProject, handleFileChange as handleTestFileUpdate, handleFileDelete as handleTestFileDelete } from "./discover"; +import { getCurrentBallerinaProject, getWorkspaceRoot } from "../../utils/project-utils"; +import { checkIsBallerinaPackage, checkIsBallerinaWorkspace } from "../../utils"; +import { PROJECT_TYPE } from "../project"; export let testController: TestController; export async function activate(ballerinaExtInstance: BallerinaExtension) { testController = tests.createTestController('ballerina-integrator-tests', 'WSO2 Integrator: BI Tests'); + const workspaceRoot = getWorkspaceRoot(); + + const isBallerinaWorkspace = await checkIsBallerinaWorkspace(Uri.file(workspaceRoot)); + const isBallerinaProject = !isBallerinaWorkspace && await checkIsBallerinaPackage(Uri.file(workspaceRoot)); + const currentProject = !isBallerinaWorkspace && !isBallerinaProject && await getCurrentBallerinaProject(); + const isSingleFile = currentProject && currentProject.kind === PROJECT_TYPE.SINGLE_FILE; + + if (!isBallerinaWorkspace && !isBallerinaProject && !isSingleFile) { + return; + } + // Create test profiles to display. testController.createRunProfile('Run Tests', TestRunProfileKind.Run, runHandler, true); testController.createRunProfile('Debug Tests', TestRunProfileKind.Debug, runHandler, true); diff --git a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts index 9c266e17ad1..c3003ca8d70 100644 --- a/workspaces/ballerina/ballerina-extension/src/stateMachine.ts +++ b/workspaces/ballerina/ballerina-extension/src/stateMachine.ts @@ -20,7 +20,8 @@ import { Type, dependencyPullProgress, BI_COMMANDS, - NodePosition + NodePosition, + ProjectInfo } from "@wso2/ballerina-core"; import { fetchAndCacheLibraryData } from './features/library-browser'; import { VisualizerWebview } from './views/visualizer/webview'; @@ -107,13 +108,14 @@ const stateMachine = createMachine( } ] }, - UPDATE_PROJECT_ROOT: { + UPDATE_PROJECT_ROOT_AND_INFO: { actions: [ assign({ - projectPath: (context, event) => event.projectPath + projectPath: (context, event) => event.projectPath, + projectInfo: (context, event) => event.projectInfo }), async (context, event) => { - await buildProjectsStructure(context.projectInfo, StateMachine.langClient(), true); + await buildProjectsStructure(event.projectInfo, StateMachine.langClient(), true); notifyCurrentWebview(); notifyTreeView(event.projectPath, context.documentUri, context.position, context.view); // Resolve the next pending promise waiting for project root update completion @@ -468,10 +470,13 @@ const stateMachine = createMachine( fetchProjectInfo: (context, event) => { return new Promise(async (resolve, reject) => { try { - const projectInfo = await context.langClient.getProjectInfo({ - projectPath: context.workspacePath || context.projectPath - }); - resolve({ projectInfo }); + const projectPath = context.workspacePath || context.projectPath; + if (!projectPath) { + resolve({ projectInfo: undefined }); + } else { + const projectInfo = await context.langClient.getProjectInfo({ projectPath }); + resolve({ projectInfo }); + } } catch (error) { throw new Error("Error occurred while fetching project info.", error); } @@ -799,10 +804,10 @@ export const StateMachine = { }, sendEvent: (eventType: EVENT_TYPE) => { stateService.send({ type: eventType }); }, updateProjectStructure: (payload: ProjectStructureResponse) => { stateService.send({ type: "UPDATE_PROJECT_STRUCTURE", payload }); }, - updateProjectRoot: (projectPath: string): Promise => { + updateProjectRootAndInfo: (projectPath: string, projectInfo: ProjectInfo): Promise => { return new Promise((resolve) => { pendingProjectRootUpdateResolvers.push(resolve); - stateService.send({ type: "UPDATE_PROJECT_ROOT", projectPath }); + stateService.send({ type: "UPDATE_PROJECT_ROOT_AND_INFO", projectPath, projectInfo }); }); }, refreshProjectInfo: () => { @@ -1026,12 +1031,21 @@ function notifyTreeView( position?: NodePosition, view?: MACHINE_VIEW ) { - commands.executeCommand(BI_COMMANDS.NOTIFY_PROJECT_EXPLORER, { - projectPath, - documentUri, - position, - view - }); + try { + const biExtension = extensions.getExtension('wso2.ballerina-integrator'); + if (biExtension && !biExtension.isActive) { + return; + } + + commands.executeCommand(BI_COMMANDS.NOTIFY_PROJECT_EXPLORER, { + projectPath, + documentUri, + position, + view + }); + } catch (error) { + console.error('Error notifying tree view:', error); + } } function setBIContext(isBI: boolean) { diff --git a/workspaces/ballerina/ballerina-extension/src/views/ai-panel/activate.ts b/workspaces/ballerina/ballerina-extension/src/views/ai-panel/activate.ts index df064c0e375..4dab56881ab 100644 --- a/workspaces/ballerina/ballerina-extension/src/views/ai-panel/activate.ts +++ b/workspaces/ballerina/ballerina-extension/src/views/ai-panel/activate.ts @@ -54,7 +54,6 @@ export function activateAiPanel(ballerinaExtInstance: BallerinaExtension) { return; } - // StateMachine.updateProjectRoot(selectedPackage); openView(EVENT_TYPE.OPEN_VIEW, { view: MACHINE_VIEW.PackageOverview, projectPath: selectedPackage }); } catch (error) { console.error("Error selecting package:", error); diff --git a/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts b/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts index ef92daa4f04..0a59af5ea2a 100644 --- a/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts +++ b/workspaces/ballerina/ballerina-extension/src/views/visualizer/activate.ts @@ -80,13 +80,27 @@ export function activateSubscriptions() { const projectRoot = await findBallerinaPackageRoot(documentPath); const isBallerinaWorkspace = !!StateMachine.context().workspacePath; - if (isBallerinaWorkspace && pathOrItem instanceof vscode.TreeItem) { + if (isBallerinaWorkspace) { + if (pathOrItem instanceof vscode.TreeItem) { + openView( + EVENT_TYPE.OPEN_VIEW, + { + projectPath: pathOrItem.resourceUri?.fsPath, + view: MACHINE_VIEW.PackageOverview + }, + true + ); + return; + } + const documentUri = documentPath || vscode.window.activeTextEditor?.document.uri.fsPath; openView( EVENT_TYPE.OPEN_VIEW, { - projectPath: pathOrItem.resourceUri?.fsPath, - view: MACHINE_VIEW.PackageOverview + projectPath: projectRoot, + documentUri: documentUri, + position: nodePosition }, + true ); return; } @@ -95,8 +109,8 @@ export function activateSubscriptions() { // Initialize project structure if not already set by finding and loading the Ballerina project root // Can happen when the user opens a directory containing multiple Ballerina projects if (projectRoot) { - // TODO: Need to create the project structure for the workspace - await StateMachine.updateProjectRoot(projectRoot); + const projectInfo = await StateMachine.langClient().getProjectInfo({ projectPath: projectRoot }); + await StateMachine.updateProjectRootAndInfo(projectRoot, projectInfo); } } diff --git a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx index b3d873410f8..aa133b51668 100644 --- a/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx +++ b/workspaces/ballerina/ballerina-visualizer/src/components/TopNavigationBar/index.tsx @@ -157,7 +157,8 @@ export function TopNavigationBar(props: TopNavigationBarProps) { "data mapper", "connection", "add project", - "bi add project skip" + "bi add project skip", + "welcome" ]; if (workspaceType?.type !== "BALLERINA_WORKSPACE") { diff --git a/workspaces/bi/bi-extension/src/project-explorer/activate.ts b/workspaces/bi/bi-extension/src/project-explorer/activate.ts index 2a644ab303c..5db56952cb2 100644 --- a/workspaces/bi/bi-extension/src/project-explorer/activate.ts +++ b/workspaces/bi/bi-extension/src/project-explorer/activate.ts @@ -41,6 +41,9 @@ export function activateProjectExplorer(config: ExplorerActivationConfig) { projectExplorerDataProvider.setTreeView(projectTree); + // Always register core commands so they're available to the Ballerina extension + registerCoreCommands(projectExplorerDataProvider); + if (isBallerinaPackage || isBallerinaWorkspace) { registerBallerinaCommands(projectExplorerDataProvider, isBI, isBallerinaWorkspace, isEmptyWorkspace); } @@ -63,13 +66,8 @@ function createProjectTree(dataProvider: ProjectExplorerEntryProvider) { return window.createTreeView(BI_COMMANDS.PROJECT_EXPLORER, { treeDataProvider: dataProvider }); } -function registerBallerinaCommands( - dataProvider: ProjectExplorerEntryProvider, - isBI: boolean, - isBallerinaWorkspace?: boolean, - isEmptyWorkspace?: boolean -) { - commands.registerCommand(BI_COMMANDS.REFRESH_COMMAND, () => dataProvider.refresh()); +function registerCoreCommands(dataProvider: ProjectExplorerEntryProvider) { + // Register the notify command that's called by the Ballerina extension commands.registerCommand( BI_COMMANDS.NOTIFY_PROJECT_EXPLORER, (event: { @@ -81,6 +79,17 @@ function registerBallerinaCommands( dataProvider.revealInTreeView(event.documentUri, event.projectPath, event.position, event.view); } ); + + // Register the refresh command + commands.registerCommand(BI_COMMANDS.REFRESH_COMMAND, () => dataProvider.refresh()); +} + +function registerBallerinaCommands( + dataProvider: ProjectExplorerEntryProvider, + isBI: boolean, + isBallerinaWorkspace?: boolean, + isEmptyWorkspace?: boolean +) { commands.executeCommand('setContext', 'BI.isWorkspaceSupported', extension.isWorkspaceSupported ?? false); if (isBallerinaWorkspace) { diff --git a/workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts b/workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts index ec8a4dce670..388514c5ee7 100644 --- a/workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts +++ b/workspaces/bi/bi-extension/src/project-explorer/project-explorer-provider.ts @@ -143,7 +143,7 @@ export class ProjectExplorerEntryProvider implements vscode.TreeDataProvider