org.ballerinalang.langserver.completions.spi.LSCompletionProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.langserver.completions.spi.LSCompletionProvider.java

Source

/*
 *  Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package org.ballerinalang.langserver.completions.spi;

import org.antlr.v4.runtime.CommonToken;
import org.antlr.v4.runtime.Token;
import org.ballerinalang.compiler.CompilerPhase;
import org.ballerinalang.langserver.SnippetBlock;
import org.ballerinalang.langserver.common.UtilSymbolKeys;
import org.ballerinalang.langserver.common.utils.CommonUtil;
import org.ballerinalang.langserver.compiler.DocumentServiceKeys;
import org.ballerinalang.langserver.compiler.LSCompiler;
import org.ballerinalang.langserver.compiler.LSCompilerException;
import org.ballerinalang.langserver.compiler.LSContext;
import org.ballerinalang.langserver.compiler.LSPackageLoader;
import org.ballerinalang.langserver.compiler.common.modal.BallerinaPackage;
import org.ballerinalang.langserver.completions.CompletionKeys;
import org.ballerinalang.langserver.completions.LSCompletionException;
import org.ballerinalang.langserver.completions.LSCompletionProviderFactory;
import org.ballerinalang.langserver.completions.SymbolInfo;
import org.ballerinalang.langserver.completions.builder.BFunctionCompletionItemBuilder;
import org.ballerinalang.langserver.completions.builder.BTypeCompletionItemBuilder;
import org.ballerinalang.langserver.completions.builder.BVariableCompletionItemBuilder;
import org.ballerinalang.langserver.completions.util.ItemResolverConstants;
import org.ballerinalang.langserver.completions.util.Snippet;
import org.ballerinalang.langserver.completions.util.filters.DelimiterBasedContentFilter;
import org.ballerinalang.langserver.completions.util.filters.SymbolFilters;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.wso2.ballerinalang.compiler.parser.antlr4.BallerinaParser;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BConstantSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BPackageSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType;
import org.wso2.ballerinalang.compiler.tree.BLangFunction;
import org.wso2.ballerinalang.compiler.tree.BLangNode;
import org.wso2.ballerinalang.compiler.tree.BLangPackage;
import org.wso2.ballerinalang.compiler.tree.BLangService;
import org.wso2.ballerinalang.compiler.tree.BLangVariable;
import org.wso2.ballerinalang.compiler.tree.statements.BLangSimpleVariableDef;
import org.wso2.ballerinalang.compiler.tree.statements.BLangStatement;
import org.wso2.ballerinalang.compiler.tree.types.BLangFunctionTypeNode;
import org.wso2.ballerinalang.compiler.tree.types.BLangType;
import org.wso2.ballerinalang.compiler.tree.types.BLangUnionTypeNode;
import org.wso2.ballerinalang.compiler.tree.types.BLangUserDefinedType;
import org.wso2.ballerinalang.compiler.tree.types.BLangValueType;
import org.wso2.ballerinalang.util.Flags;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Interface for completion item providers.
 *
 * @since 0.995.0
 */
public abstract class LSCompletionProvider {

    protected List<Class> attachmentPoints = new ArrayList<>();

    /**
     * Get Completion items for the scope/ context.
     *
     * @param context Language Server Context
     * @return {@link List}     List of calculated Completion Items
     */
    public abstract List<CompletionItem> getCompletions(LSContext context);

    /**
     * Get the attachment points where the current provider attached to.
     *
     * @return {@link List}    List of attachment points
     */
    public List<Class> getAttachmentPoints() {
        return this.attachmentPoints;
    }

    /**
     * Get the Context Provider.
     * Ex: When a given scope is resolved then the context can be resolved by parsing a sub rule or token analyzing
     *
     * @param ctx Language Server Context
     * @return {@link Optional} Context Completion provider
     */
    public Optional<LSCompletionProvider> getContextProvider(LSContext ctx) {
        return Optional.empty();
    }

    /**
     * Populate the completion item list by considering the.
     *
     * @param symbolInfoList list of symbol information
     * @param context        Language server operation context
     * @return {@link List}     list of completion items
     */
    protected List<CompletionItem> getCompletionItemList(List<SymbolInfo> symbolInfoList, LSContext context) {
        List<CompletionItem> completionItems = new ArrayList<>();
        symbolInfoList.removeIf(CommonUtil.invalidSymbolsPredicate());
        symbolInfoList.forEach(symbolInfo -> {
            BSymbol bSymbol = symbolInfo.isCustomOperation() ? null : symbolInfo.getScopeEntry().symbol;
            if (CommonUtil.isValidInvokableSymbol(bSymbol) || symbolInfo.isCustomOperation()) {
                completionItems.add(populateBallerinaFunctionCompletionItem(symbolInfo));
            } else if (!(bSymbol instanceof BInvokableSymbol) && bSymbol instanceof BVarSymbol) {
                String typeName = symbolInfo.getScopeEntry().symbol.type.toString();
                completionItems.add(BVariableCompletionItemBuilder.build((BVarSymbol) bSymbol,
                        symbolInfo.getSymbolName(), typeName));
            } else if (bSymbol instanceof BTypeSymbol && !(bSymbol instanceof BPackageSymbol)) {
                // Here skip all the package symbols since the package is added separately
                completionItems
                        .add(BTypeCompletionItemBuilder.build((BTypeSymbol) bSymbol, symbolInfo.getSymbolName()));
            } else if (bSymbol instanceof BConstantSymbol) {
                completionItems.add(this.getBallerinaConstantCompletionItem(symbolInfo, context));
            }
        });
        return completionItems;
    }

    /**
     * Populate the completion item list by either list.
     *
     * @param list    Either List of completion items or symbol info
     * @param context LS Operation Context
     * @return {@link List}     Completion Items List
     */
    protected List<CompletionItem> getCompletionItemList(Either<List<CompletionItem>, List<SymbolInfo>> list,
            LSContext context) {
        List<CompletionItem> completionItems = new ArrayList<>();
        if (list.isLeft()) {
            completionItems.addAll(list.getLeft());
        } else {
            completionItems.addAll(this.getCompletionItemList(list.getRight(), context));
        }

        return completionItems;
    }

    /**
     * Check whether the token stream corresponds to a action invocation or a function invocation.
     *
     * @param context Completion operation context
     * @return {@link Boolean}      Whether invocation or Field Access
     */
    protected boolean isInvocationOrInteractionOrFieldAccess(LSContext context) {
        List<CommonToken> lhsTokens = context.get(CompletionKeys.LHS_TOKENS_KEY);
        if (lhsTokens == null) {
            return false;
        }
        List<CommonToken> lhsDefaultTokens = lhsTokens.stream()
                .filter(commonToken -> commonToken.getChannel() == Token.DEFAULT_CHANNEL)
                .collect(Collectors.toList());
        return !lhsDefaultTokens.isEmpty() && ((CommonUtil.getLastItem(lhsDefaultTokens)
                .getType() == BallerinaParser.COLON)
                || (CommonUtil.getLastItem(lhsDefaultTokens).getType() == BallerinaParser.DOT)
                || (CommonUtil.getLastItem(lhsDefaultTokens).getType() == BallerinaParser.RARROW)
                || (CommonUtil.getLastItem(lhsDefaultTokens).getType() == BallerinaParser.LARROW)
                || (CommonUtil.getLastItem(lhsDefaultTokens).getType() == BallerinaParser.NOT)
                || (lhsDefaultTokens.size() >= 2
                        && (lhsDefaultTokens.get(lhsDefaultTokens.size() - 2).getType() == BallerinaParser.COLON
                                || lhsDefaultTokens.get(lhsDefaultTokens.size() - 2)
                                        .getType() == BallerinaParser.DOT
                                || lhsDefaultTokens.get(lhsDefaultTokens.size() - 2)
                                        .getType() == BallerinaParser.RARROW
                                || lhsDefaultTokens.get(lhsDefaultTokens.size() - 2)
                                        .getType() == BallerinaParser.LARROW
                                || lhsDefaultTokens.get(lhsDefaultTokens.size() - 2)
                                        .getType() == BallerinaParser.NOT)));
    }

    /**
     * Get the basic types.
     *
     * @param visibleSymbols List of visible symbols
     * @return {@link List}     List of completion items
     */
    protected List<CompletionItem> getBasicTypes(List<SymbolInfo> visibleSymbols) {
        visibleSymbols.removeIf(CommonUtil.invalidSymbolsPredicate());
        List<CompletionItem> completionItems = new ArrayList<>();
        visibleSymbols.forEach(symbolInfo -> {
            BSymbol bSymbol = symbolInfo.getScopeEntry().symbol;
            if (bSymbol instanceof BTypeSymbol && !(bSymbol instanceof BPackageSymbol)) {
                completionItems
                        .add(BTypeCompletionItemBuilder.build((BTypeSymbol) bSymbol, symbolInfo.getSymbolName()));
            }
        });

        return completionItems;
    }

    /**
     * Get all the types in the Package with given name.
     *
     * @param visibleSymbols Visible Symbols
     * @param pkgName package name
     * @param ctx language server context
     * @return {@link List} list of Type completion items
     */
    protected List<CompletionItem> getTypesInPackage(List<SymbolInfo> visibleSymbols, String pkgName,
            LSContext ctx) {
        List<SymbolInfo> filteredList = new ArrayList<>();
        Optional<SymbolInfo> pkgSymbolInfo = visibleSymbols.stream().filter(symbolInfo -> {
            BSymbol symbol = symbolInfo.getScopeEntry().symbol;
            return symbol instanceof BPackageSymbol && symbolInfo.getSymbolName().equals(pkgName);
        }).findAny();
        pkgSymbolInfo.ifPresent(symbolInfo -> {
            BSymbol pkgSymbol = symbolInfo.getScopeEntry().symbol;
            pkgSymbol.scope.entries.forEach((name, scopeEntry) -> {
                if (scopeEntry.symbol instanceof BTypeSymbol) {
                    filteredList.add(new SymbolInfo(name.getValue(), scopeEntry));
                }
            });
        });

        return this.getCompletionItemList(filteredList, ctx);
    }

    /**
     * Add top level items to the given completionItems List.
     *
     * @param context LS Context
     * @return {@link List}     List of populated completion items
     */
    protected List<CompletionItem> addTopLevelItems(LSContext context) {
        ArrayList<CompletionItem> completionItems = new ArrayList<>();
        completionItems.add(getStaticItem(context, Snippet.KW_IMPORT));
        completionItems.add(getStaticItem(context, Snippet.DEF_FUNCTION));
        completionItems.add(getStaticItem(context, Snippet.DEF_MAIN_FUNCTION));
        completionItems.add(getStaticItem(context, Snippet.DEF_SERVICE));
        completionItems.add(getStaticItem(context, Snippet.DEF_SERVICE_WEBSOCKET));
        completionItems.add(getStaticItem(context, Snippet.DEF_SERVICE_WEBSUB));
        completionItems.add(getStaticItem(context, Snippet.DEF_SERVICE_GRPC));
        completionItems.add(getStaticItem(context, Snippet.DEF_ANNOTATION));
        completionItems.add(getStaticItem(context, Snippet.STMT_NAMESPACE_DECLARATION));
        completionItems.add(getStaticItem(context, Snippet.DEF_OBJECT_SNIPPET));
        completionItems.add(getStaticItem(context, Snippet.DEF_RECORD));
        completionItems.add(getStaticItem(context, Snippet.KW_TYPE));
        completionItems.add(getStaticItem(context, Snippet.KW_PUBLIC));
        completionItems.add(getStaticItem(context, Snippet.KW_FINAL));
        completionItems.add(getStaticItem(context, Snippet.KW_CONST));
        completionItems.add(getStaticItem(context, Snippet.DEF_ERROR));
        completionItems.add(getStaticItem(context, Snippet.KW_LISTENER));
        return completionItems;
    }

    /**
     * Get a static completion Item for the given snippet.
     *
     * @param ctx     Language Server Context
     * @param snippet Snippet to generate the static completion item
     * @return {@link CompletionItem} Generated static completion Item
     */
    protected CompletionItem getStaticItem(LSContext ctx, Snippet snippet) {
        return snippet.get().build(ctx);
    }

    /**
     * Check whether the given token is an access modifier token.
     *
     * @param tokenType Token type
     * @return {@link Boolean} Whether the token is an access modifier or not
     */
    protected boolean isAccessModifierToken(int tokenType) {
        return tokenType == BallerinaParser.PUBLIC || tokenType == BallerinaParser.CONST
                || tokenType == BallerinaParser.FINAL;
    }

    /**
     * Get the completion item for a package import.
     * If the package is already imported, additional text edit for the import statement will not be added.
     *
     * @param ctx LS Operation context
     * @return {@link List}     List of packages completion items
     */
    protected List<CompletionItem> getPackagesCompletionItems(LSContext ctx) {
        // First we include the packages from the imported list.
        List<String> populatedList = new ArrayList<>();
        String relativePath = ctx.get(DocumentServiceKeys.RELATIVE_FILE_PATH_KEY);
        BLangPackage pkg = ctx.get(DocumentServiceKeys.CURRENT_BLANG_PACKAGE_CONTEXT_KEY);
        BLangPackage srcOwnerPkg = CommonUtil.getSourceOwnerBLangPackage(relativePath, pkg);
        List<CompletionItem> completionItems = CommonUtil.getCurrentFileImports(srcOwnerPkg, ctx).stream()
                .map(bLangImportPackage -> {
                    String orgName = bLangImportPackage.orgName.toString();
                    String pkgName = bLangImportPackage.pkgNameComps.stream().map(id -> id.value)
                            .collect(Collectors.joining("."));
                    CompletionItem item = new CompletionItem();
                    item.setLabel(orgName + "/" + pkgName);
                    item.setInsertText(CommonUtil.getLastItem(bLangImportPackage.getPackageName()).value);
                    item.setDetail(ItemResolverConstants.PACKAGE_TYPE);
                    item.setKind(CompletionItemKind.Module);
                    populatedList.add(orgName + "/" + pkgName);
                    return item;
                }).collect(Collectors.toList());
        List<BallerinaPackage> packages = LSPackageLoader.getSdkPackages();
        packages.addAll(LSPackageLoader.getHomeRepoPackages());

        packages.forEach(ballerinaPackage -> {
            String name = ballerinaPackage.getPackageName();
            String orgName = ballerinaPackage.getOrgName();
            if (!populatedList.contains(orgName + "/" + name)) {
                CompletionItem item = new CompletionItem();
                item.setLabel(ballerinaPackage.getFullPackageNameAlias());
                item.setInsertText(name);
                item.setDetail(ItemResolverConstants.PACKAGE_TYPE);
                item.setKind(CompletionItemKind.Module);
                item.setAdditionalTextEdits(CommonUtil.getAutoImportTextEdits(ctx, orgName, name));
                completionItems.add(item);
            }
        });

        return completionItems;
    }

    /**
     * Get the completion items based on the delimiter token, as an example . and : .
     *
     * @param context Language Server Service Operation Context
     * @return {@link List} Completion Item List
     */
    protected List<CompletionItem> getDelimiterBasedCompletionItems(LSContext context) {
        Either<List<CompletionItem>, List<SymbolInfo>> itemList = SymbolFilters
                .get(DelimiterBasedContentFilter.class).filterItems(context);
        return this.getCompletionItemList(itemList, context);
    }

    /**
     * Predicate to filter the attached of Self, symbol.
     *
     * @return {@link Predicate} Symbol filter predicate
     */
    protected Predicate<SymbolInfo> attachedOrSelfKeywordFilter() {
        return symbolInfo -> {
            BSymbol bSymbol = symbolInfo.getScopeEntry().symbol;
            return (bSymbol instanceof BInvokableSymbol && ((bSymbol.flags & Flags.ATTACHED) == Flags.ATTACHED))
                    || (UtilSymbolKeys.SELF_KEYWORD_KEY.equals(bSymbol.getName().getValue())
                            && (bSymbol.owner.flags & Flags.RESOURCE) == Flags.RESOURCE);
        };
    }

    protected Optional<String> getSubRule(List<CommonToken> tokenList) {
        if (tokenList == null || tokenList.isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(tokenList.stream().map(CommonToken::getText).collect(Collectors.joining("")));
    }

    /**
     * Get the provider for the given key.
     *
     * @param providerKey key to get the provider
     * @return {@link LSCompletionProvider} Completion Provider
     */
    protected LSCompletionProvider getProvider(Class providerKey) {
        return LSCompletionProviderFactory.getInstance().getProvider(providerKey);
    }

    protected List<CompletionItem> getCompletionItemsAfterOnKeyword(LSContext ctx) {
        List<SymbolInfo> filtered = this.filterListenerVariables(ctx.get(CompletionKeys.VISIBLE_SYMBOLS_KEY));
        List<CompletionItem> completionItems = new ArrayList<>(this.getCompletionItemList(filtered, ctx));
        completionItems.add(Snippet.KW_NEW.get().build(ctx));

        return completionItems;
    }

    /**
     * Get the completions for the expression context in a variable definition.
     * Eg: Completion after the EQUAL sign of the variable definition
     * 
     * @param context Language Server context
     * @return {@link List} List of completion Items
     */
    protected List<CompletionItem> getVarDefExpressionCompletions(LSContext context) {
        List<CommonToken> lhsTokens = context.get(CompletionKeys.LHS_TOKENS_KEY);
        List<CompletionItem> completionItems = new ArrayList<>(this.getVarDefCompletions(context));
        int counter = 0;
        StringBuilder subRule = new StringBuilder("function testFunction () {" + CommonUtil.LINE_SEPARATOR + "\t");
        while (counter < lhsTokens.size()) {
            subRule.append(lhsTokens.get(counter).getText());
            if (lhsTokens.get(counter).getType() == BallerinaParser.ASSIGN) {
                subRule.append("0;");
                break;
            }
            counter++;
        }
        subRule.append(CommonUtil.LINE_SEPARATOR).append("}");

        try {
            Optional<BLangType> assignmentType = getAssignmentType(subRule.toString(), context);
            if (assignmentType.isPresent() && assignmentType.get() instanceof BLangFunctionTypeNode) {
                fillFunctionSnippet((BLangFunctionTypeNode) assignmentType.get(), context, completionItems);
                fillArrowFunctionSnippet((BLangFunctionTypeNode) assignmentType.get(), context, completionItems);
            }
        } catch (LSCompletionException ex) {
            // do nothing
        }

        return completionItems;
    }

    /**
     * Get the Resource Snippets.
     * 
     * @param ctx Language Server Context
     * @return {@link Optional} Completion List
     */
    protected List<CompletionItem> getResourceSnippets(LSContext ctx) {
        BLangNode symbolEnvNode = ctx.get(CompletionKeys.SCOPE_NODE_KEY);
        List<CompletionItem> items = new ArrayList<>();
        if (symbolEnvNode instanceof BLangService) {
            BLangService service = (BLangService) symbolEnvNode;
            String owner = service.listenerType.tsymbol.owner.name.value;
            String serviceTypeName = service.listenerType.tsymbol.name.value;
            // Only http, grpc have generic resource templates, others will have generic resource snippet
            switch (owner) {
            case "http":
                if ("Listener".equals(serviceTypeName)) {
                    items.add(Snippet.DEF_RESOURCE.get().build(ctx));
                    break;
                } else if ("WebSocketListener".equals(serviceTypeName)) {
                    addIfNotExists(Snippet.DEF_RESOURCE_WS_OPEN.get(), service, items, ctx);
                    addIfNotExists(Snippet.DEF_RESOURCE_WS_TEXT.get(), service, items, ctx);
                    addIfNotExists(Snippet.DEF_RESOURCE_WS_CLOSE.get(), service, items, ctx);
                    break;
                }
                return items;
            case "grpc":
                items.add(Snippet.DEF_RESOURCE_GRPC.get().build(ctx));
                break;
            case "websub":
                addIfNotExists(Snippet.DEF_RESOURCE_WEBSUB_INTENT.get(), service, items, ctx);
                addIfNotExists(Snippet.DEF_RESOURCE_WEBSUB_NOTIFY.get(), service, items, ctx);
                break;
            default:
                return items;
            }
        }
        return items;
    }

    // Private Methods

    /**
     * Populate the Ballerina Function Completion Item.
     *
     * @param symbolInfo - symbol information
     * @return completion item
     */
    private CompletionItem populateBallerinaFunctionCompletionItem(SymbolInfo symbolInfo) {
        if (symbolInfo.isCustomOperation()) {
            SymbolInfo.CustomOperationSignature signature = symbolInfo.getCustomOperationSignature();
            return BFunctionCompletionItemBuilder.build(null, signature.getLabel(), signature.getInsertText());
        }
        BSymbol bSymbol = symbolInfo.getScopeEntry().symbol;
        if (!(bSymbol instanceof BInvokableSymbol)) {
            return null;
        }
        return BFunctionCompletionItemBuilder.build((BInvokableSymbol) bSymbol);
    }

    /**
     * Get the Ballerina Constant Completion Item.
     *
     * @param symbolInfo symbol information
     * @param context    Language Server Operation Context
     * @return {@link CompletionItem}   completion item
     */
    private CompletionItem getBallerinaConstantCompletionItem(SymbolInfo symbolInfo, LSContext context) {
        BSymbol bSymbol = symbolInfo.getScopeEntry().symbol;
        if (!(bSymbol instanceof BConstantSymbol)) {
            return null;
        }

        CompletionItem completionItem = new CompletionItem();
        completionItem.setLabel(bSymbol.getName().getValue());
        completionItem.setInsertText(bSymbol.getName().getValue());
        completionItem.setDetail(CommonUtil.getBTypeName(((BConstantSymbol) bSymbol).literalValueType, context));
        completionItem.setDocumentation(ItemResolverConstants.CONSTANT_TYPE);
        completionItem.setKind(CompletionItemKind.Variable);

        return completionItem;
    }

    private List<SymbolInfo> filterListenerVariables(List<SymbolInfo> symbolInfos) {
        return symbolInfos.stream().filter(symbolInfo -> {
            BSymbol symbol = symbolInfo.getScopeEntry().symbol;
            return symbol instanceof BVarSymbol && CommonUtil.isListenerObject(symbol.type.tsymbol);
        }).collect(Collectors.toList());
    }

    private Optional<BLangType> getAssignmentType(String subRule, LSContext context) throws LSCompletionException {
        Optional<BLangPackage> bLangPackage;
        try {
            bLangPackage = LSCompiler.compileContent(subRule, CompilerPhase.CODE_ANALYZE).getBLangPackage();
        } catch (LSCompilerException e) {
            throw new LSCompletionException("Error while parsing the sub-rule");
        }

        if (!bLangPackage.isPresent()) {
            return Optional.empty();
        }

        BLangStatement evalStatement = bLangPackage.get().getFunctions().get(0).getBody().stmts.get(0);

        if (!(evalStatement instanceof BLangSimpleVariableDef)) {
            return Optional.empty();
        }

        BLangType typeNode = ((BLangSimpleVariableDef) evalStatement).getVariable().getTypeNode();

        return Optional.of(typeNode);
    }

    private void fillFunctionSnippet(BLangFunctionTypeNode functionTypeNode, LSContext context,
            List<CompletionItem> completionItems) throws LSCompletionException {

        List<BLangVariable> params = functionTypeNode.getParams();
        BLangType returnBLangType = functionTypeNode.getReturnTypeNode();
        String functionSignature = this.getFunctionSignature(params, returnBLangType);
        String body = this.getAnonFunctionSnippetBody(returnBLangType, params.size());
        String snippet = functionSignature + body;
        String label = this.convertToLabel(functionSignature);
        SnippetBlock snippetBlock = new SnippetBlock(label, snippet, ItemResolverConstants.SNIPPET_TYPE,
                SnippetBlock.SnippetType.SNIPPET);

        // Populate the anonymous function signature completion item
        completionItems.add(snippetBlock.build(context));
    }

    private String getFunctionSignature(List<BLangVariable> paramTypes, BLangType returnType)
            throws LSCompletionException {
        StringBuilder signature = new StringBuilder("function ");

        signature.append(this.getParamsSnippet(paramTypes, true));
        if (!(returnType.type instanceof BNilType)) {
            signature.append("returns (").append(this.getTypeName(returnType)).append(") ");
        }

        return signature.toString();
    }

    private void fillArrowFunctionSnippet(BLangFunctionTypeNode functionTypeNode, LSContext context,
            List<CompletionItem> completionItems) throws LSCompletionException {
        List<BLangVariable> params = functionTypeNode.getParams();
        BLangType returnBLangType = functionTypeNode.getReturnTypeNode();
        String paramSignature = this.getParamsSnippet(params, false);
        StringBuilder signature = new StringBuilder(paramSignature);

        signature.append(" => ").append("${");
        if (!(returnBLangType.type instanceof BNilType)) {
            signature.append(params.size() + 1).append(":")
                    .append(CommonUtil.getDefaultValueForType(returnBLangType.type));
        } else {
            signature.append(params.size() + 1);
        }
        signature.append("};");

        String label = "arrow function  " + this.convertToLabel(paramSignature);

        SnippetBlock snippetBlock = new SnippetBlock(label, signature.toString(),
                ItemResolverConstants.SNIPPET_TYPE, SnippetBlock.SnippetType.SNIPPET);

        // Populate the anonymous function signature completion item
        completionItems.add(snippetBlock.build(context));
    }

    private String getAnonFunctionSnippetBody(BLangType returnType, int numberOfParams) {
        StringBuilder body = new StringBuilder();
        if (!(returnType.type instanceof BNilType)) {
            body.append("{").append(CommonUtil.LINE_SEPARATOR).append("\t").append("return ")
                    .append(CommonUtil.getDefaultValueForType(returnType.type)).append(";")
                    .append(CommonUtil.LINE_SEPARATOR);
        } else {
            body.append("{").append(CommonUtil.LINE_SEPARATOR).append("\t${").append(numberOfParams + 1).append("}")
                    .append(CommonUtil.LINE_SEPARATOR);
        }

        body.append("};");

        return body.toString();
    }

    private String convertToLabel(String signature) {
        return signature.replaceAll("(\\$\\{\\d:)([a-zA-Z\\d]*:*[a-zA-Z\\d]*)(\\})", "$2")
                .replaceAll("(\\$\\{\\d\\})", "");
    }

    private String getParamsSnippet(List<BLangVariable> paramTypes, boolean withType) throws LSCompletionException {
        String paramName = "param";
        StringBuilder signature = new StringBuilder("(");
        List<String> params = IntStream.range(0, paramTypes.size()).mapToObj(index -> {
            try {
                int paramIndex = index + 1;
                String paramPlaceHolder = "${" + paramIndex + ":" + paramName + paramIndex + "}";
                if (withType) {
                    paramPlaceHolder = this.getTypeName(paramTypes.get(index).getTypeNode()) + " "
                            + paramPlaceHolder;
                }
                return paramPlaceHolder;
            } catch (LSCompletionException e) {
                return "";
            }
        }).collect(Collectors.toList());

        if (params.contains("")) {
            throw new LSCompletionException("Contains invalid parameter type");
        }

        signature.append(String.join(", ", params)).append(") ");

        return signature.toString();
    }

    private String getTypeName(BLangType bLangType) throws LSCompletionException {
        if (bLangType instanceof BLangValueType || bLangType instanceof BLangUnionTypeNode) {
            return bLangType.toString();
        } else if (bLangType instanceof BLangUserDefinedType) {
            BLangUserDefinedType userDefinedType = (BLangUserDefinedType) bLangType;
            String pkgAlias = userDefinedType.getPackageAlias().getValue();
            String typeName = userDefinedType.getTypeName().getValue();
            return pkgAlias.isEmpty() ? typeName : (pkgAlias + UtilSymbolKeys.PKG_DELIMITER_KEYWORD + typeName);
        } else {
            throw new LSCompletionException("Error identifying the type of anonymous function parameter");
        }
    }

    /**
     * Get variable definition context related completion items. This will extract the completion items analyzing the
     * variable definition context properties.
     *
     * @param context Completion context
     * @return {@link List}     List of resolved completion items
     */
    private List<CompletionItem> getVarDefCompletions(LSContext context) {
        ArrayList<CompletionItem> completionItems = new ArrayList<>();
        List<SymbolInfo> filteredList = context.get(CompletionKeys.VISIBLE_SYMBOLS_KEY);
        // Remove the functions without a receiver symbol, bTypes not being packages and attached functions
        filteredList.removeIf(symbolInfo -> {
            BSymbol bSymbol = symbolInfo.getScopeEntry().symbol;
            return (bSymbol instanceof BInvokableSymbol && ((BInvokableSymbol) bSymbol).receiverSymbol != null
                    && CommonUtil.isValidInvokableSymbol(bSymbol))
                    || ((bSymbol instanceof BTypeSymbol) && !(bSymbol instanceof BPackageSymbol))
                    || (bSymbol instanceof BInvokableSymbol
                            && ((bSymbol.flags & Flags.ATTACHED) == Flags.ATTACHED));
        });
        completionItems.addAll(getCompletionItemList(filteredList, context));
        // Add the packages completion items.
        completionItems.addAll(getPackagesCompletionItems(context));
        // Add the check keyword
        CompletionItem checkKeyword = Snippet.KW_CHECK.get().build(context);
        completionItems.add(checkKeyword);
        // Add the wait keyword
        CompletionItem waitKeyword = Snippet.KW_WAIT.get().build(context);
        completionItems.add(waitKeyword);
        // Add But keyword item
        CompletionItem butKeyword = Snippet.EXPR_MATCH.get().build(context);
        completionItems.add(butKeyword);
        // Add the trap expression keyword
        CompletionItem trapExpression = Snippet.STMT_TRAP.get().build(context);
        completionItems.add(trapExpression);

        return completionItems;
    }

    private void addIfNotExists(SnippetBlock snippet, BLangService service, List<CompletionItem> items,
            LSContext ctx) {
        boolean found = false;
        for (BLangFunction resource : service.getResources()) {
            if (snippet.getLabel().equals(resource.name.value + " " + ItemResolverConstants.RESOURCE)) {
                found = true;
            }
        }
        if (!found) {
            items.add(snippet.build(ctx));
        }
    }
}