Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -38,13 +38,15 @@
import io.ballerina.compiler.api.symbols.TypeSymbol;
import io.ballerina.compiler.api.symbols.UnionTypeSymbol;
import io.ballerina.compiler.syntax.tree.ExpressionNode;
import io.ballerina.compiler.syntax.tree.ImportDeclarationNode;
import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode;
import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode;
import io.ballerina.compiler.syntax.tree.MappingFieldNode;
import io.ballerina.compiler.syntax.tree.MetadataNode;
import io.ballerina.compiler.syntax.tree.ModulePartNode;
import io.ballerina.compiler.syntax.tree.ModuleVariableDeclarationNode;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.NodeList;
import io.ballerina.compiler.syntax.tree.NodeParser;
import io.ballerina.compiler.syntax.tree.SpecificFieldNode;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
Expand Down Expand Up @@ -102,17 +104,20 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

import static io.ballerina.flowmodelgenerator.core.model.Property.CONFIG_VALUE_KEY;
import static io.ballerina.flowmodelgenerator.core.model.Property.CONFIG_VAR_DOC_KEY;
import static io.ballerina.flowmodelgenerator.core.model.Property.DEFAULT_VALUE_KEY;
import static io.ballerina.flowmodelgenerator.core.model.Property.VARIABLE_KEY;
import static io.ballerina.modelgenerator.commons.CommonUtils.importExists;

/**
* Provides extended services for viewing and editing Ballerina configuration variables.
Expand Down Expand Up @@ -140,6 +145,8 @@ public class ConfigEditorV2Service implements ExtendedLanguageServerService {
private static final String COLON_SPACE = ": ";
private static final String DOUBLE_QUOTE = "\"";
private static final String EQUALS_SIGN_SPACED = " = ";
private static final String LS = System.lineSeparator();
private static final String IMPORT_STMT_TEMPLATE = "%nimport %s/%s;%n";

// TOML and Config Statement Format Constants
private static final String CONFIG_STATEMENT_FORMAT = "configurable %s %s = %s;";
Expand Down Expand Up @@ -318,11 +325,13 @@ private Map<Path, List<TextEdit>> constructSourceTextEdits(Project rootProject,
SyntaxTree syntaxTree = document.get().syntaxTree();
ModulePartNode modulePartNode = syntaxTree.rootNode();
LinePosition startPos = LinePosition.from(modulePartNode.lineRange().endLine().line() + 1, 0);
textEdits.add(new TextEdit(CommonUtils.toRange(startPos), configStatement + System.lineSeparator()));
textEdits.add(new TextEdit(CommonUtils.toRange(startPos), configStatement + LS));
addImportEdits(variable, document.get(), textEdits);
} else if (isDelete) {
textEdits.add(new TextEdit(CommonUtils.toRange(lineRange), EMPTY_STRING));
} else {
textEdits.add(new TextEdit(CommonUtils.toRange(lineRange), configStatement));
addImportEdits(variable, document.get(), textEdits);
}

textEditsMap.put(variableFilePath, textEdits);
Expand All @@ -332,21 +341,61 @@ private Map<Path, List<TextEdit>> constructSourceTextEdits(Project rootProject,
}
}

/**
* Adds necessary import statements to the text edits for a configuration variable.
*
* @param node The configuration variable flow node.
* @param document The document containing the configuration variable.
* @param textEdits The list of text edits to which import statements will be added.
*/
private static void addImportEdits(FlowNode node, Document document, List<TextEdit> textEdits) {
Map<String, String> propImports = node.properties().get(Property.TYPE_KEY).imports();
if (propImports == null || propImports.isEmpty()) {
return;
}

ModulePartNode rootNode = document.syntaxTree().rootNode();
LineRange startLineRange = rootNode.lineRange();

Set<String> imports = new HashSet<>();
NodeList<ImportDeclarationNode> importNodes = rootNode.imports();
if (!importNodes.isEmpty()) {
ImportDeclarationNode lastImportNode = importNodes.get(importNodes.size() - 1);
startLineRange = lastImportNode.lineRange();
LinePosition linePosition = LinePosition.from(startLineRange.endLine().line() + 1, 0);
startLineRange = LineRange.from(startLineRange.fileName(), linePosition, linePosition);
}

propImports.values().forEach(moduleId -> {
String[] importParts = moduleId.split("/");
String orgName = importParts[0];
String moduleName = importParts[1].split(":")[0];
if (!importExists(rootNode, orgName, moduleName)) {
imports.add(String.format(IMPORT_STMT_TEMPLATE, orgName, moduleName));
}
});

if (!imports.isEmpty()) {
String importsStmts = String.join(LS, imports);
textEdits.addFirst(new TextEdit(CommonUtils.toRange(startLineRange), importsStmts));
}
}

/**
* Constructs the Ballerina source code statement for a configuration variable.
*/
private static String constructConfigStatement(FlowNode node) {
String defaultValue = node.properties().get(DEFAULT_VALUE_KEY).toSourceCode();
String variableDoc = node.properties().get(CONFIG_VAR_DOC_KEY).toSourceCode();
List<String> docLines = Arrays.stream(variableDoc.split(System.lineSeparator())).toList();
List<String> docLines = Arrays.stream(variableDoc.split(LS)).toList();

StringBuilder configStatementBuilder = new StringBuilder();
docLines.forEach(docLine -> {
if (!docLine.isBlank()) {
configStatementBuilder
.append(HASH_COMMENT_PREFIX)
.append(docLine)
.append(System.lineSeparator());
.append(LS);
}
}
);
Expand Down Expand Up @@ -933,7 +982,7 @@ private Map<Path, List<TextEdit>> constructConfigTomlTextEdits(Project project,
}
}
LinePosition startPos = LinePosition.from(0, 0);
textEdits.add(new TextEdit(CommonUtils.toRange(startPos), newContent + System.lineSeparator()));
textEdits.add(new TextEdit(CommonUtils.toRange(startPos), newContent + LS));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
{
"description": "Add config with its type being from an imported module",
"project": "config_update_test_project",
"request": {
"packageName": "config_tests/config_update_test_project",
"moduleName": "",
"configFilePath": "config.bal",
"configVariable": {
"id": "32790",
"metadata": {
"label": "Config",
"description": "Create a configurable variable"
},
"codedata": {
"node": "CONFIG_VARIABLE",
"lineRange": {
"fileName": "config.bal",
"startLine": {
"line": 1,
"offset": 0
},
"endLine": {
"line": 1,
"offset": 54
}
}
},
"returning": false,
"properties": {
"variable": {
"metadata": {
"label": "Variable Name",
"description": "Name of the variable"
},
"types": [
{
"fieldType": "IDENTIFIER",
"scope": "Global",
"selected": true
}
],
"optional": false,
"editable": true,
"advanced": false,
"hidden": false,
"modified": false,
"value": "token"
},
"type": {
"metadata": {
"label": "Variable Type",
"description": "Type of the variable"
},
"types": [
{
"fieldType": "TYPE",
"selected": true
}
],
"value": "http:BearerTokenConfig",
"placeholder": "var",
"optional": false,
"editable": true,
"advanced": false,
"hidden": false,
"modified": true,
"codedata": {},
"imports": {
"http": "ballerina/http:2.14.7"
}
},
"configValue": {
"metadata": {
"label": "Config Value",
"description": "Config value of the variable, to be used in Config.toml"
},
"types": [
{
"fieldType": "EXPRESSION",
"selected": true
}
],
"value": "",
"optional": true,
"editable": true,
"advanced": false,
"hidden": true,
"modified": false
},
"defaultValue": {
"metadata": {
"label": "Default Value",
"description": "Default value of the variable"
},
"types": [
{
"fieldType": "EXPRESSION",
"selected": true
}
],
"value": "",
"optional": true,
"editable": true,
"advanced": false,
"hidden": false,
"modified": true
},
"documentation": {
"metadata": {
"label": "Documentation",
"description": "Variable documentation in Markdown format"
},
"types": [
{
"fieldType": "TEXT",
"selected": true
}
],
"value": "",
"optional": true,
"editable": true,
"advanced": false,
"hidden": false,
"modified": false
}
},
"flags": 0
}
},
"response": {
"textEdits": {
"config.bal": [
{
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 5,
"character": 39
}
},
"newText": "\nimport ballerina/http;\n"
},
{
"range": {
"start": {
"line": 1,
"character": 0
},
"end": {
"line": 1,
"character": 54
}
},
"newText": "configurable http:BearerTokenConfig token = ?;"
}
]
}
}
}
Loading
Loading