Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export interface DataMappingRecord {
}

export interface GenerateTypesFromRecordRequest {
attachment?: Attachment[]
attachment: Attachment[]
}

export interface GenerateTypesFromRecordResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
*/

import { BallerinaPackagesParams, BallerinaProjectComponents, BallerinaSTParams, ComponentModels, ComponentModelsParams, ExecutorPositions, PartialST, PartialSTParams, ProjectDiagnosticsRequest, ProjectDiagnosticsResponse, STModifyParams, SymbolInfo, SymbolInfoParams, SyntaxTree, SyntaxTreeParams, TypeFromExpressionParams, TypeFromSymbolParams, TypesFromFnDefinitionParams } from "../../interfaces/extended-lang-client";
import { BallerinaVersionResponse, CompletionRequest, CompletionResponse, DiagnosticsResponse, CodeActionRequest, CodeActionResponse, RenameRequest, RenameResponse, DefinitionPositionRequest, UpdateFileContentRequest, UpdateFileContentResponse, DefinitionResponse, ExecutorPositionsRequest, DidCloseRequest, TypesFromExpressionResponse, TypesFromSymbolResponse, DidOpenRequest, DidChangeRequest } from "./interfaces";
import { BallerinaVersionResponse, CompletionRequest, CompletionResponse, DiagnosticsResponse, CodeActionRequest, CodeActionResponse, RenameRequest, RenameResponse, DefinitionPositionRequest, UpdateFileContentRequest, UpdateFileContentResponse, DefinitionResponse, ExecutorPositionsRequest, DidCloseRequest, TypesFromExpressionResponse, TypesFromSymbolResponse, DidOpenRequest, DidChangeRequest, SemanticVersion } from "./interfaces";

export interface LangClientAPI {
getSyntaxTree: () => Promise<SyntaxTree>;
getST: (params: SyntaxTreeParams) => Promise<SyntaxTree>;
getSTByRange: (params: BallerinaSTParams) => Promise<SyntaxTree>;
getBallerinaProjectComponents: (params: BallerinaPackagesParams) => Promise<BallerinaProjectComponents>;
getBallerinaVersion: () => Promise<BallerinaVersionResponse>;
isSupportedSLVersion: (params: SemanticVersion) => Promise<boolean>;
getCompletion: (params: CompletionRequest) => Promise<CompletionResponse>;
getDiagnostics: (params: SyntaxTreeParams) => Promise<DiagnosticsResponse>;
getProjectDiagnostics: (params: ProjectDiagnosticsRequest) => Promise<ProjectDiagnosticsResponse>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,10 @@ export interface TypesFromSymbolResponse {

export interface ExecutorPositionsResponse {
executorPositions?: ExecutorPosition[];
}
}

export interface SemanticVersion {
major: number;
minor: number;
patch: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* THIS FILE INCLUDES AUTO GENERATED CODE
*/
import { BallerinaPackagesParams, BallerinaProjectComponents, BallerinaSTParams, ComponentModels, ComponentModelsParams, ExecutorPositions, PartialST, PartialSTParams, ProjectDiagnosticsRequest, ProjectDiagnosticsResponse, STModifyParams, SymbolInfo, SymbolInfoParams, SyntaxTree, SyntaxTreeParams, TypeFromExpressionParams, TypeFromSymbolParams, TypesFromFnDefinitionParams } from "../../interfaces/extended-lang-client";
import { BallerinaVersionResponse, CompletionRequest, CompletionResponse, DiagnosticsResponse, CodeActionRequest, CodeActionResponse, RenameRequest, RenameResponse, DefinitionPositionRequest, UpdateFileContentRequest, UpdateFileContentResponse, DefinitionResponse, ExecutorPositionsRequest, DidCloseRequest, TypesFromExpressionResponse, TypesFromSymbolResponse, DidOpenRequest, DidChangeRequest } from "./interfaces";
import { BallerinaVersionResponse, CompletionRequest, CompletionResponse, DiagnosticsResponse, CodeActionRequest, CodeActionResponse, RenameRequest, RenameResponse, DefinitionPositionRequest, UpdateFileContentRequest, UpdateFileContentResponse, DefinitionResponse, ExecutorPositionsRequest, DidCloseRequest, TypesFromExpressionResponse, TypesFromSymbolResponse, DidOpenRequest, DidChangeRequest, SemanticVersion } from "./interfaces";
import { RequestType, NotificationType } from "vscode-messenger-common";

const _preFix = "lang-client";
Expand All @@ -27,6 +27,7 @@ export const getST: RequestType<SyntaxTreeParams, SyntaxTree> = { method: `${_pr
export const getSTByRange: RequestType<BallerinaSTParams, SyntaxTree> = { method: `${_preFix}/getSTByRange` };
export const getBallerinaProjectComponents: RequestType<BallerinaPackagesParams, BallerinaProjectComponents> = { method: `${_preFix}/getBallerinaProjectComponents` };
export const getBallerinaVersion: RequestType<void, BallerinaVersionResponse> = { method: `${_preFix}/getBallerinaVersion` };
export const isSupportedSLVersion: RequestType<SemanticVersion, boolean> = { method: `${_preFix}/isSupportedSLVersion` };
export const getCompletion: RequestType<CompletionRequest, CompletionResponse> = { method: `${_preFix}/getCompletion` };
export const getDiagnostics: RequestType<SyntaxTreeParams, DiagnosticsResponse> = { method: `${_preFix}/getDiagnostics` };
export const getProjectDiagnostics: RequestType<ProjectDiagnosticsRequest, ProjectDiagnosticsResponse> = { method: `${_preFix}/getProjectDiagnostics` };
Expand Down
2 changes: 1 addition & 1 deletion workspaces/ballerina/ballerina-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "ballerina",
"displayName": "Ballerina",
"description": "Ballerina Language support, debugging, graphical visualization, AI-based data-mapping and many more.",
"version": "5.6.0",
"version": "5.6.1",
"publisher": "wso2",
"icon": "resources/images/ballerina.png",
"homepage": "https://wso2.com/ballerina/vscode/docs",
Expand Down
26 changes: 20 additions & 6 deletions workspaces/ballerina/ballerina-extension/src/core/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,18 @@ import { exec, spawnSync, execSync } from 'child_process';
import { LanguageClientOptions, State as LS_STATE, RevealOutputChannelOn, ServerOptions } from "vscode-languageclient/node";
import { getServerOptions } from '../utils/server/server';
import { ExtendedLangClient } from './extended-language-client';
import { debug, log, getOutputChannel, outputChannel, isWindows, isWSL, isSupportedVersion, VERSION, isSupportedSLVersion } from '../utils';
import {
debug,
log,
getOutputChannel,
outputChannel,
isWindows,
isWSL,
isSupportedVersion,
VERSION,
isSupportedSLVersion,
createVersionNumber
} from '../utils';
import { AssertionError } from "assert";
import {
BALLERINA_HOME, ENABLE_ALL_CODELENS, ENABLE_TELEMETRY, ENABLE_SEMANTIC_HIGHLIGHTING, OVERRIDE_BALLERINA_HOME,
Expand Down Expand Up @@ -142,6 +153,7 @@ export class BallerinaExtension {
public ballerinaVersion: string;
public biSupported: boolean;
public isNPSupported: boolean;
public isWorkspaceSupported: boolean;
public extension: Extension<any>;
private clientOptions: LanguageClientOptions;
public langClient?: ExtendedLangClient;
Expand Down Expand Up @@ -172,6 +184,7 @@ export class BallerinaExtension {
this.ballerinaVersion = '';
this.biSupported = false;
this.isNPSupported = false;
this.isWorkspaceSupported = false;
this.isPersist = false;
this.ballerinaUserHomeName = '.ballerina';

Expand Down Expand Up @@ -442,9 +455,10 @@ export class BallerinaExtension {
}

try {
this.biSupported = isSupportedSLVersion(this, 2201123); // Minimum supported version for BI
this.isNPSupported = isSupportedSLVersion(this, 2201130) && this.enabledExperimentalFeatures(); // Minimum supported requirements for NP
debug(`[INIT] Feature support calculated - BI: ${this.biSupported}, NP: ${this.isNPSupported}`);
this.biSupported = isSupportedSLVersion(this, createVersionNumber(2201, 12, 3)); // Minimum supported version for BI: 2201.12.3
this.isNPSupported = isSupportedSLVersion(this, createVersionNumber(2201, 13, 0)) && this.enabledExperimentalFeatures(); // Minimum supported requirements for NP: 2201.13.0
this.isWorkspaceSupported = isSupportedSLVersion(this, createVersionNumber(2201, 13, 0)); // Minimum supported requirements for Workspace: 2201.13.0
debug(`[INIT] Feature support calculated - BI: ${this.biSupported}, NP: ${this.isNPSupported}, Workspace: ${this.isWorkspaceSupported}`);
} catch (error) {
debug(`[INIT] Error calculating feature support: ${error}`);
// Don't throw here, we can continue without these features
Expand All @@ -464,7 +478,7 @@ export class BallerinaExtension {
debug(`[INIT] Final Ballerina Home: ${this.ballerinaHome}`);
debug(`[INIT] Plugin Dev Mode: ${this.overrideBallerinaHome()}`);
debug(`[INIT] Debug Mode: ${this.enableLSDebug()}`);
debug(`[INIT] Feature flags - Experimental: ${this.enabledExperimentalFeatures()}, BI: ${this.biSupported}, NP: ${this.isNPSupported}`);
debug(`[INIT] Feature flags - Experimental: ${this.enabledExperimentalFeatures()}, BI: ${this.biSupported}, NP: ${this.isNPSupported}, Workspace: ${this.isWorkspaceSupported}`);

// Check version compatibility
try {
Expand Down Expand Up @@ -2283,7 +2297,7 @@ export class BallerinaExtension {
}

public enabledLiveReload(): boolean {
return isSupportedSLVersion(this, 2201100) && workspace.getConfiguration().get(ENABLE_LIVE_RELOAD);
return isSupportedSLVersion(this, createVersionNumber(2201, 10, 0)) && workspace.getConfiguration().get(ENABLE_LIVE_RELOAD);
}

public enabledPerformanceForecasting(): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,11 +477,10 @@ export async function generateMappings(
): Promise<AllDataMapperSourceRequest> {
const targetFilePath = metadataWithAttachments.metadata.codeData.lineRange.fileName || context.documentUri;

const optionalMappingInstructionFile = metadataWithAttachments.attachments && metadataWithAttachments.attachments.length > 0
? metadataWithAttachments.attachments[0]
: undefined;

const generatedMappings = await generateMappingExpressionsFromModel(metadataWithAttachments.metadata.mappingsModel as DMModel, optionalMappingInstructionFile);
const generatedMappings = await generateMappingExpressionsFromModel(
metadataWithAttachments.metadata.mappingsModel as DMModel,
metadataWithAttachments.attachments || []
);

const customFunctionMappings = generatedMappings.filter(mapping => mapping.isFunctionCall);
let customFunctionsFilePath: string | undefined;
Expand Down Expand Up @@ -1123,20 +1122,47 @@ export async function generateInlineMappingsSource(
// processContextTypeCreation - Functions for processing context type creation
// ================================================================================================

// Extract record types from Ballerina code
export function extractRecordTypes(typesCode: string): { name: string; code: string }[] {
const recordPattern = /\b(?:public|private)?\s*type\s+(\w+)\s+record\s+(?:{[|]?|[|]?{)[\s\S]*?;?\s*[}|]?;/g;
const matches = [...typesCode.matchAll(recordPattern)];
return matches.map((match) => ({
name: match[1],
code: match[0].trim(),
}));
// Extract record and enum types from syntax tree
export async function extractRecordTypesFromSyntaxTree(
langClient: ExtendedLangClient,
filePath: string
): Promise<{ records: string[]; enums: string[] }> {
const st = (await langClient.getSyntaxTree({
documentIdentifier: {
uri: Uri.file(filePath).toString(),
},
})) as SyntaxTree;

if (!st.syntaxTree) {
throw new Error("Failed to retrieve syntax tree for file: " + filePath);
}

const modulePart = st.syntaxTree as ModulePart;
const records: string[] = [];
const enums: string[] = [];

for (const member of modulePart.members) {
if (STKindChecker.isTypeDefinition(member)) {
const typeName = member.typeName?.value;
if (typeName) {
records.push(typeName);
}
} else if (STKindChecker.isEnumDeclaration(member)) {
const enumName = member.identifier?.value;
if (enumName) {
enums.push(enumName);
}
}
}

return { records, enums };
}

// Generate Ballerina record types from context attachments and validate against existing records
export async function generateTypesFromContext(
sourceAttachments: Attachment[],
projectComponents: ProjectComponentsResponse
projectComponents: ProjectComponentsResponse,
langClient: ExtendedLangClient
): Promise<TypesGenerationResult> {
if (!sourceAttachments || sourceAttachments.length === 0) {
throw new Error("Source attachments are required for type generation");
Expand All @@ -1163,31 +1189,61 @@ export async function generateTypesFromContext(
const typeFilePath = baseFilePath + typeComponent.filePath;
existingRecordTypesMap.set(typeComponent.name, { type: typeComponent.name, isArray: false, filePath: typeFilePath });
});
moduleSummary.enums.forEach((enumComponent: ComponentInfo) => {
const enumFilePath = baseFilePath + enumComponent.filePath;
existingRecordTypesMap.set(enumComponent.name, { type: enumComponent.name, isArray: false, filePath: enumFilePath });
});
});
});

// Generate type definitions from attachments
// Generate type definitions from all attachments together
const typeGenerationRequest: GenerateTypesFromRecordRequest = {
attachment: sourceAttachments
};

const typeGenerationResponse = await generateTypeCreation(typeGenerationRequest);
const generatedTypesCode = typeGenerationResponse.typesCode;

// Create temp directory and file to validate generated types
const tempDirectory = await createTempBallerinaDir();
const tempTypesFilePath = path.join(tempDirectory, outputFileName);

writeBallerinaFileDidOpenTemp(tempTypesFilePath, generatedTypesCode);

// Extract record and enum names from syntax tree
const { records: generatedRecords, enums: generatedEnums } = await extractRecordTypesFromSyntaxTree(langClient, tempTypesFilePath);

// Check for duplicate record names
for (const recordName of generatedRecords) {
if (existingRecordTypesMap.has(recordName)) {
throw new Error(`Record "${recordName}" already exists in the workspace`);
}
}

// Check for duplicate enum names
for (const enumName of generatedEnums) {
if (existingRecordTypesMap.has(enumName)) {
throw new Error(`Enum "${enumName}" already exists in the workspace`);
}
}

return {
typesCode: generatedTypesCode,
filePath: outputFileName,
recordMap: existingRecordTypesMap
};
}

// Generate Ballerina record type definitions from an attachment file
// Generate Ballerina record type definitions from attachment files
export async function generateTypeCreation(
typeGenerationRequest: GenerateTypesFromRecordRequest
): Promise<GenerateTypesFromRecordResponse> {
const sourceFile = typeGenerationRequest.attachment?.[0];
if (typeGenerationRequest.attachment.length === 0) {
throw new Error('No attachments provided for type generation');
}

const generatedTypeDefinitions = await extractRecordTypeDefinitionsFromFile(sourceFile);
// Process all attachments together to understand correlations
const generatedTypeDefinitions = await extractRecordTypeDefinitionsFromFile(typeGenerationRequest.attachment);
if (typeof generatedTypeDefinitions !== 'string') {
throw new Error(`Failed to generate types: ${JSON.stringify(generatedTypeDefinitions)}`);
}
Expand Down
Loading
Loading