From 3a93f78902bc95a65135350a124359f67ab51c48 Mon Sep 17 00:00:00 2001 From: dulaj Date: Mon, 13 Oct 2025 05:27:39 +0000 Subject: [PATCH] Fix data mapping function detection in FunctionSearchCommand --- .../core/search/FunctionSearchCommand.java | 17 +- .../core/search/SearchCommand.java | 7 +- .../extension/FlowModelGeneratorService.java | 3 +- .../custom_functions_in_data_mappings.json | 670 ++++++++++++++++++ .../source/custom_func_proj/Ballerina.toml | 4 + .../source/custom_func_proj/data_mappings.bal | 36 + .../search/source/custom_func_proj/main.bal | 5 + .../modelgenerator/commons/CommonUtils.java | 24 + 8 files changed, 761 insertions(+), 5 deletions(-) create mode 100644 flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/config/functions/custom_functions_in_data_mappings.json create mode 100644 flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/Ballerina.toml create mode 100644 flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/data_mappings.bal create mode 100644 flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/main.bal diff --git a/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/search/FunctionSearchCommand.java b/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/search/FunctionSearchCommand.java index 350e3951e6..3153eb0b59 100644 --- a/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/search/FunctionSearchCommand.java +++ b/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/search/FunctionSearchCommand.java @@ -86,9 +86,10 @@ class FunctionSearchCommand extends SearchCommand { private static final String FETCH_KEY = "functions"; private final List moduleNames; private final Document functionsDoc; + private final Document dataMappingsDoc; public FunctionSearchCommand(Project project, LineRange position, Map queryMap, - Document functionsDoc) { + Document functionsDoc, Document dataMappingsDoc) { super(project, position, queryMap); // Obtain the imported module names @@ -98,6 +99,7 @@ public FunctionSearchCommand(Project project, LineRange position, Map moduleDependency.descriptor().name().packageName().value()) .toList(); this.functionsDoc = functionsDoc; + this.dataMappingsDoc = dataMappingsDoc; // TODO: Use this method when https://github.com/ballerina-platform/ballerina-lang/issues/43695 is fixed // List moduleNames = semanticModel.moduleSymbols().stream() // .filter(symbol -> symbol.kind().equals(SymbolKind.MODULE)) @@ -195,17 +197,26 @@ private void buildProjectNodes() { List availableTools = new ArrayList<>(); for (Symbol symbol : functionSymbols) { FunctionSymbol functionSymbol = (FunctionSymbol) symbol; + // Skip NP functions from functions.bal if (functionsDoc != null && CommonUtils.isNaturalExpressionBodiedFunction(functionsDoc.syntaxTree(), functionSymbol)) { - // Skip NP functions + continue; + } + // Skip NP functions from data_mappings.bal + if (dataMappingsDoc != null + && CommonUtils.isNaturalExpressionBodiedFunction(dataMappingsDoc.syntaxTree(), functionSymbol)) { continue; } boolean isDataMappedFunction = false; Optional location = symbol.getLocation(); if (location.isPresent()) { - isDataMappedFunction = location.get().lineRange().fileName().equals(DATA_MAPPER_FILE_NAME); LineRange fnLineRange = location.get().lineRange(); + // Check if function is in data_mappings.bal and is a data mapping function + if (fnLineRange.fileName().equals(DATA_MAPPER_FILE_NAME) && dataMappingsDoc != null) { + isDataMappedFunction = CommonUtils.isDataMappingFunction( + dataMappingsDoc.syntaxTree(), functionSymbol); + } if (fnLineRange.fileName().equals(position.fileName()) && PositionUtil.isWithinLineRange(fnLineRange, position)) { continue; diff --git a/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/search/SearchCommand.java b/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/search/SearchCommand.java index e60d9cecf3..e1e950e57f 100644 --- a/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/search/SearchCommand.java +++ b/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/search/SearchCommand.java @@ -62,8 +62,13 @@ public abstract class SearchCommand { public static SearchCommand from(Kind kind, Project module, LineRange position, Map queryMap, Document functionsDoc) { + return from(kind, module, position, queryMap, functionsDoc, null); + } + + public static SearchCommand from(Kind kind, Project module, LineRange position, Map queryMap, + Document functionsDoc, Document dataMappingsDoc) { return switch (kind) { - case FUNCTION -> new FunctionSearchCommand(module, position, queryMap, functionsDoc); + case FUNCTION -> new FunctionSearchCommand(module, position, queryMap, functionsDoc, dataMappingsDoc); case CONNECTOR -> new ConnectorSearchCommand(module, position, queryMap); case NP_FUNCTION -> new NPFunctionSearchCommand(module, position, queryMap, functionsDoc); case TYPE -> new TypeSearchCommand(module, position, queryMap); diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/src/main/java/io/ballerina/flowmodelgenerator/extension/FlowModelGeneratorService.java b/flow-model-generator/modules/flow-model-generator-ls-extension/src/main/java/io/ballerina/flowmodelgenerator/extension/FlowModelGeneratorService.java index 134e8aff2c..c6833d879a 100644 --- a/flow-model-generator/modules/flow-model-generator-ls-extension/src/main/java/io/ballerina/flowmodelgenerator/extension/FlowModelGeneratorService.java +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/src/main/java/io/ballerina/flowmodelgenerator/extension/FlowModelGeneratorService.java @@ -680,9 +680,10 @@ public CompletableFuture search(SearchRequest r Path projectPath = workspaceManager.projectRoot(filePath); Optional functionsDoc = getDocumentFromFile(projectPath, "functions.bal"); + Optional dataMappingsDoc = getDocumentFromFile(projectPath, "data_mappings.bal"); SearchCommand command = SearchCommand.from(searchKind, project, position, request.queryMap(), - functionsDoc.orElse(null)); + functionsDoc.orElse(null), dataMappingsDoc.orElse(null)); response.setCategories(command.execute()); } catch (Throwable e) { response.setError(e); diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/config/functions/custom_functions_in_data_mappings.json b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/config/functions/custom_functions_in_data_mappings.json new file mode 100644 index 0000000000..aae8502d62 --- /dev/null +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/config/functions/custom_functions_in_data_mappings.json @@ -0,0 +1,670 @@ +{ + "description": "Functions in data_mappings.bal - only expression-bodied functions should be marked as data mapping functions", + "kind": "FUNCTION", + "source": "custom_func_proj/main.bal", + "position": { + "startLine": { + "line": 2, + "offset": 0 + }, + "endLine": { + "line": 4, + "offset": 1 + } + }, + "queryMap": {}, + "categories": [ + { + "metadata": { + "label": "Current Integration", + "description": "Functions defined within the current integration", + "keywords": [ + "Project", + "Local", + "Function" + ] + }, + "items": [ + { + "metadata": { + "label": "mapPersonToEmployee", + "data": { + "isDataMappedFunction": true, + "isAgentTool": false, + "isIsolatedFunction": true + } + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "test", + "module": "custom_func_proj", + "symbol": "mapPersonToEmployee", + "version": "0.1.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "customHelper", + "data": { + "isDataMappedFunction": false, + "isAgentTool": false, + "isIsolatedFunction": true + } + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "test", + "module": "custom_func_proj", + "symbol": "customHelper", + "version": "0.1.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "validateAge", + "data": { + "isDataMappedFunction": false, + "isAgentTool": false, + "isIsolatedFunction": true + } + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "test", + "module": "custom_func_proj", + "symbol": "validateAge", + "version": "0.1.0" + }, + "enabled": true + } + ] + }, + { + "metadata": { + "label": "Agent Tools", + "description": "Functions used as agent tools", + "keywords": [ + "Project", + "Local", + "Function" + ] + }, + "items": [] + }, + { + "metadata": { + "label": "Imported Functions", + "description": "Functions imported from other integrations", + "keywords": [ + "Imported", + "Function", + "Library" + ] + }, + "items": [ + { + "metadata": { + "label": "ai", + "description": "", + "keywords": [] + }, + "items": [ + { + "metadata": { + "label": "getTools", + "description": "Get the tools registered with the agent.\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "getTools", + "version": "1.1.1" + }, + "enabled": true + }, + { + "metadata": { + "label": "chunkDocumentRecursively", + "description": "Provides functionality to recursively chunk a text document using a configurable strategy.\n\nThe chunking process begins with the specified strategy and recursively falls back to \nfiner-grained strategies if the content exceeds the configured `maxChunkSize`. Overlapping content \nbetween chunks can be controlled using `maxOverlapSize`.\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "chunkDocumentRecursively", + "version": "1.1.1" + }, + "enabled": true + }, + { + "metadata": { + "label": "extractToolsFromOpenApiSpecFile", + "description": "Extracts the Http tools from the given OpenAPI specification file.\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "extractToolsFromOpenApiSpecFile", + "version": "1.1.1" + }, + "enabled": true + }, + { + "metadata": { + "label": "extractToolsFromOpenApiJsonSpec", + "description": "Extracts the Http tools from the given OpenAPI specification as a JSON \n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "extractToolsFromOpenApiJsonSpec", + "version": "1.1.1" + }, + "enabled": true + }, + { + "metadata": { + "label": "parseOpenApiSpec", + "description": "Parses the given OpenAPI specification as a JSON to a OpenApiSpec object.\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "parseOpenApiSpec", + "version": "1.1.1" + }, + "enabled": true + }, + { + "metadata": { + "label": "getDefaultModelProvider", + "description": "Creates a default model provider based on the provided `wso2ProviderConfig`.", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "getDefaultModelProvider", + "version": "1.1.1" + }, + "enabled": true + }, + { + "metadata": { + "label": "getDefaultEmbeddingProvider", + "description": "Creates a default embedding provider based on the provided `wso2ProviderConfig`.", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "getDefaultEmbeddingProvider", + "version": "1.1.1" + }, + "enabled": true + }, + { + "metadata": { + "label": "augmentUserQuery", + "description": "Augments the user's query with relevant context.\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "augmentUserQuery", + "version": "1.1.1" + }, + "enabled": true + }, + { + "metadata": { + "label": "executeTool", + "description": "Executes an AgentTool.\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "executeTool", + "version": "1.1.1" + }, + "enabled": true + }, + { + "metadata": { + "label": "getToolConfigs", + "description": "Generates a array of `ToolConfig` from the given list of function pointers.\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ai_1.1.1.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "ai", + "packageName": "ai", + "symbol": "getToolConfigs", + "version": "1.1.1" + }, + "enabled": true + } + ] + }, + { + "metadata": { + "label": "io", + "description": "", + "keywords": [] + }, + "items": [ + { + "metadata": { + "label": "fileReadBytes", + "description": "Read the entire file content as a byte array.\n```ballerina\nbyte[]|io:Error content = io:fileReadBytes(\"./resources/myfile.txt\");\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileReadBytes", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileReadBlocksAsStream", + "description": "Read the entire file content as a stream of blocks.\n```ballerina\nstream|io:Error content = io:fileReadBlocksAsStream(\"./resources/myfile.txt\", 1000);\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileReadBlocksAsStream", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileWriteBytes", + "description": "Write a set of bytes to a file.\n```ballerina\nbyte[] content = [60, 78, 39, 28];\nio:Error? result = io:fileWriteBytes(\"./resources/myfile.txt\", content);\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileWriteBytes", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileWriteBlocksFromStream", + "description": "Write a byte stream to a file.\n```ballerina\nbyte[] content = [[60, 78, 39, 28]];\nstream byteStream = content.toStream();\nio:Error? result = io:fileWriteBlocksFromStream(\"./resources/myfile.txt\", byteStream);\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileWriteBlocksFromStream", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileReadCsv", + "description": "Read file content as a CSV.\nWhen the expected data type is record[], the first entry of the csv file should contain matching headers.\n```ballerina\nstring[][]|io:Error content = io:fileReadCsv(\"./resources/myfile.csv\");\nrecord{}[]|io:Error content = io:fileReadCsv(\"./resources/myfile.csv\");\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileReadCsv", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileReadCsvAsStream", + "description": "Read file content as a CSV.\nWhen the expected data type is stream,\nthe first entry of the csv file should contain matching headers.\n```ballerina\nstream|io:Error content = io:fileReadCsvAsStream(\"./resources/myfile.csv\");\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileReadCsvAsStream", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileWriteCsv", + "description": "Write CSV content to a file.\nWhen the input is a record[] type in `OVERWRITE`, headers will be written to the CSV file by default.\nFor `APPEND`, order of the existing csv file is inferred using the headers and used as the order.\n```ballerina\ntype Coord record {int x;int y;};\nCoord[] contentRecord = [{x: 1,y: 2},{x: 1,y: 2}]\nstring[][] content = [[\"Anne\", \"Johnson\", \"SE\"], [\"John\", \"Cameron\", \"QA\"]];\nio:Error? result = io:fileWriteCsv(\"./resources/myfile.csv\", content);\nio:Error? resultRecord = io:fileWriteCsv(\"./resources/myfileRecord.csv\", contentRecord);\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileWriteCsv", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileWriteCsvFromStream", + "description": "Write CSV record stream to a file.\nWhen the input is a `stream` in `OVERWRITE`, headers will be written to the CSV file by default.\nFor `APPEND`, order of the existing csv file is inferred using the headers and used as the order.\n```ballerina\ntype Coord record {int x;int y;};\nCoord[] contentRecord = [{x: 1,y: 2},{x: 1,y: 2}]\nstring[][] content = [[\"Anne\", \"Johnson\", \"SE\"], [\"John\", \"Cameron\", \"QA\"]];\nstream stringStream = content.toStream();\nstream recordStream = contentRecord.toStream();\nio:Error? result = io:fileWriteCsvFromStream(\"./resources/myfile.csv\", stringStream);\nio:Error? resultRecord = io:fileWriteCsvFromStream(\"./resources/myfileRecord.csv\", recordStream);\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileWriteCsvFromStream", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileReadString", + "description": "Reads the entire file content as a `string`.\nThe resulting string output does not contain the terminal carriage (e.g., `\\r` or `\\n`).\n```ballerina\nstring|io:Error content = io:fileReadString(\"./resources/myfile.txt\");\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileReadString", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileReadLines", + "description": "Reads the entire file content as a list of lines.\nThe resulting string array does not contain the terminal carriage (e.g., `\\r` or `\\n`).\n```ballerina\nstring[]|io:Error content = io:fileReadLines(\"./resources/myfile.txt\");\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileReadLines", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileReadJson", + "description": "Reads file content as a JSON.\n```ballerina\njson|io:Error content = io:fileReadJson(\"./resources/myfile.json\");\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileReadJson", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileReadString", + "description": "Reads the entire file content as a `string`.\nThe resulting string output does not contain the terminal carriage (e.g., `\\r` or `\\n`).\n```ballerina\nstring|io:Error content = io:fileReadString(\"./resources/myfile.txt\");\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileReadString", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileWriteJson", + "description": "Write a JSON to a file.\n```ballerina\njson content = {\"name\": \"Anne\", \"age\": 30};\nio:Error? result = io:fileWriteJson(\"./resources/myfile.json\", content);\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileWriteJson", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "fileWriteString", + "description": "Write a string content to a file.\n```ballerina\nstring content = \"Hello Universe..!!\";\nio:Error? result = io:fileWriteString(\"./resources/myfile.txt\", content);\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "fileWriteString", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "print", + "description": "Prints `any`, `error`, or string templates (such as `The respective int value is ${val}`) value(s) to the `STDOUT`.\n```ballerina\nio:print(\"Start processing the CSV file from \", srcFileName);\n```\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "print", + "version": "1.8.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "println", + "description": "Prints `any`, `error` or string templates(such as `The respective int value is ${val}`) value(s) to the STDOUT\nfollowed by a new line.\n```ballerina\nio:println(\"Start processing the CSV file from \", srcFileName);\n```\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_io_1.8.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "io", + "packageName": "io", + "symbol": "println", + "version": "1.8.0" + }, + "enabled": true + } + ] + } + ] + }, + { + "metadata": { + "label": "Standard Library", + "description": "Components supported officially by Ballerina", + "keywords": [ + "Ballerina", + "Library" + ] + }, + "items": [ + { + "metadata": { + "label": "log", + "description": "", + "keywords": [] + }, + "items": [ + { + "metadata": { + "label": "printDebug", + "description": "Prints debug logs.\n```ballerina\nlog:printDebug(\"debug message\", id = 845315)\n```\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_log_2.12.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "log", + "packageName": "log", + "symbol": "printDebug", + "version": "2.12.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "printError", + "description": "Prints error logs.\n```ballerina\nerror e = error(\"error occurred\");\nlog:printError(\"error log with cause\", 'error = e, id = 845315);\n```\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_log_2.12.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "log", + "packageName": "log", + "symbol": "printError", + "version": "2.12.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "printInfo", + "description": "Prints info logs.\n```ballerina\nlog:printInfo(\"info message\", id = 845315)\n```\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_log_2.12.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "log", + "packageName": "log", + "symbol": "printInfo", + "version": "2.12.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "printWarn", + "description": "Prints warn logs.\n```ballerina\nlog:printWarn(\"warn message\", id = 845315)\n```\n", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_log_2.12.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "log", + "packageName": "log", + "symbol": "printWarn", + "version": "2.12.0" + }, + "enabled": true + } + ] + }, + { + "metadata": { + "label": "time", + "description": "", + "keywords": [] + }, + "items": [ + { + "metadata": { + "label": "utcFromString", + "description": "Converts from RFC 3339 timestamp (e.g., `2007-12-03T10:15:30.00Z`) to Utc.\n```ballerina\ntime:Utc|time:Error utc = time:utcFromString(\"2007-12-03T10:15:30.00Z\");\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_time_2.7.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "time", + "packageName": "time", + "symbol": "utcFromString", + "version": "2.7.0" + }, + "enabled": true + }, + { + "metadata": { + "label": "utcNow", + "description": "Returns the UTC representing the current time (current instant of the system clock in seconds from the epoch of `1970-01-01T00:00:00`).\n```ballerina\ntime:Utc utc = time:utcNow();\n```", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_time_2.7.0.png" + }, + "codedata": { + "node": "FUNCTION_CALL", + "org": "ballerina", + "module": "time", + "packageName": "time", + "symbol": "utcNow", + "version": "2.7.0" + }, + "enabled": true + } + ] + } + ] + } + ] +} diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/Ballerina.toml b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/Ballerina.toml new file mode 100644 index 0000000000..77931467b8 --- /dev/null +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "test" +name = "custom_func_proj" +version = "0.1.0" diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/data_mappings.bal b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/data_mappings.bal new file mode 100644 index 0000000000..a6f77d8791 --- /dev/null +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/data_mappings.bal @@ -0,0 +1,36 @@ +import ballerina/ai; + +type Person record { + string name; + int age; +}; + +type Employee record { + string fullName; + int age; +}; + +// This is a data mapping function (expression-bodied) +function mapPersonToEmployee(Person person) returns Employee => { + fullName: person.name, + age: person.age +}; + +// This is a custom function (regular function body), not a data mapping function +function customHelper(string input) returns string { + return "Processed: " + input; +} + +// This is another custom function (regular function body) +isolated function validateAge(int age) returns boolean { + return age >= 18; +} + +// This is a natural expression function - should be skipped entirely +function generateGreeting(string name) returns string|error => natural (check ai:getDefaultModelProvider()) { + **What to do** + Generate a personalized greeting for ${name} + + **Output** + string - A friendly greeting message +}; diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/main.bal b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/main.bal new file mode 100644 index 0000000000..a6bdad8c80 --- /dev/null +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/resources/search/source/custom_func_proj/main.bal @@ -0,0 +1,5 @@ +import ballerina/io; + +public function main() { + io:println("Hello World"); +} diff --git a/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/CommonUtils.java b/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/CommonUtils.java index 5227ea5c32..428d0a8e56 100644 --- a/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/CommonUtils.java +++ b/model-generator-commons/src/main/java/io/ballerina/modelgenerator/commons/CommonUtils.java @@ -875,6 +875,30 @@ public static boolean isNaturalExpressionBodiedFunction(SyntaxTree syntaxTree, F return BallerinaCompilerApi.getInstance().isNaturalExpressionBodiedFunction(functionDefNode); } + /** + * Checks if the given function is a data mapping function (expression-bodied function excluding natural + * expressions). + * + * @param syntaxTree the syntax tree + * @param functionSymbol the function symbol + * @return true if the function is a data mapping function, false otherwise + */ + public static boolean isDataMappingFunction(SyntaxTree syntaxTree, FunctionSymbol functionSymbol) { + if (functionSymbol.getLocation().isEmpty()) { + return false; + } + NonTerminalNode node = getNode(syntaxTree, functionSymbol.getLocation().get().textRange()); + if (node.kind() != SyntaxKind.FUNCTION_DEFINITION) { + return false; + } + FunctionDefinitionNode functionDefNode = (FunctionDefinitionNode) node; + // A function is a data mapping function if: + // 1. It has an expression function body + // 2. It's not a natural expression bodied function + return functionDefNode.functionBody().kind() == SyntaxKind.EXPRESSION_FUNCTION_BODY + && !BallerinaCompilerApi.getInstance().isNaturalExpressionBodiedFunction(functionDefNode); + } + public static String getClassType(String packageName, String clientName) { String importPrefix = packageName.substring(packageName.lastIndexOf('.') + 1); return String.format("%s:%s", importPrefix, clientName);