org.ballerinalang.langserver.completions.BallerinaCustomErrorStrategy.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.langserver.completions.BallerinaCustomErrorStrategy.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;

import org.antlr.v4.runtime.InputMismatchException;
import org.antlr.v4.runtime.NoViableAltException;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.ballerinalang.langserver.DocumentServiceKeys;
import org.ballerinalang.langserver.LanguageServerContext;
import org.wso2.ballerinalang.compiler.parser.antlr4.BallerinaParser;
import org.wso2.ballerinalang.compiler.parser.antlr4.BallerinaParserErrorStrategy;

/**
 * Capture possible errors from source.
 */
public class BallerinaCustomErrorStrategy extends BallerinaParserErrorStrategy {
    private LanguageServerContext context;

    public BallerinaCustomErrorStrategy(LanguageServerContext context) {
        super(context.get(DocumentServiceKeys.COMPILER_CONTEXT_KEY), null);
        this.context = context;
    }

    @Override
    public void reportInputMismatch(Parser parser, InputMismatchException e) {
        fillContext(parser, e.getOffendingToken());
    }

    @Override
    public void reportMissingToken(Parser parser) {
        fillContext(parser, parser.getCurrentToken());
    }

    @Override
    public void reportNoViableAlternative(Parser parser, NoViableAltException e) {
        fillContext(parser, e.getOffendingToken());
    }

    @Override
    public void reportUnwantedToken(Parser parser) {
        fillContext(parser, parser.getCurrentToken());
    }

    private void fillContext(Parser parser, Token currentToken) {
        ParserRuleContext currentContext = parser.getContext();
        if (isCursorBetweenGivenTokenAndLastNonHiddenToken(currentToken, parser)) {
            this.context.put(DocumentServiceKeys.PARSER_RULE_CONTEXT_KEY, currentContext);
            this.context.put(DocumentServiceKeys.TOKEN_STREAM_KEY, parser.getTokenStream());
            this.context.put(DocumentServiceKeys.VOCABULARY_KEY, parser.getVocabulary());
            this.context.put(DocumentServiceKeys.TOKEN_INDEX_KEY, parser.getCurrentToken().getTokenIndex());
        }
    }

    /**
     * Checks whether cursor is within the whitespace region between current token to last token.
     * @param token Token to be evaluated
     * @param parser Parser Instance
     * @return true|false
     */
    private boolean isCursorBetweenGivenTokenAndLastNonHiddenToken(Token token, Parser parser) {
        this.setContextException(parser);
        boolean isCursorBetween = false;
        int line = this.context.get(DocumentServiceKeys.POSITION_KEY).getPosition().getLine();
        int character = this.context.get(DocumentServiceKeys.POSITION_KEY).getPosition().getCharacter();

        Token lastNonHiddenToken = null;
        for (int tokenIdx = token.getTokenIndex() - 1; tokenIdx >= 0; tokenIdx--) {
            Token lastToken = parser.getTokenStream().get(tokenIdx);
            if (lastToken.getChannel() != Token.HIDDEN_CHANNEL) {
                lastNonHiddenToken = lastToken;
                break;
            }
        }
        if (lastNonHiddenToken != null) {

            // Convert the token lines and char positions to zero based indexing
            int lastNonHiddenTokenLine = lastNonHiddenToken.getLine() - 1;
            int lastNonHiddenTokenChar = lastNonHiddenToken.getCharPositionInLine();
            int tokenLine = token.getLine() - 1;
            int tokenChar = token.getCharPositionInLine();

            if (line >= lastNonHiddenTokenLine && line <= tokenLine) {
                if (line == lastNonHiddenTokenLine) {
                    isCursorBetween = character >= (lastNonHiddenTokenChar + lastNonHiddenToken.getText().length());
                } else {
                    isCursorBetween = line != tokenLine || character <= tokenChar;
                }
            }
        }
        return isCursorBetween;
    }

    @Override
    protected void setContextException(Parser parser) {
        // Here the type of the exception is not important.
        InputMismatchException e = new InputMismatchException(parser);
        ParserRuleContext context = parser.getContext();
        // Note: Here we forcefully set the exception to null, in order to avoid the callable unit body being null at
        // the run time
        if (context instanceof BallerinaParser.CallableUnitBodyContext) {
            context.exception = null;
            return;
        }
        context.exception = e;
        // Note: Following check added, when the context is variable definition and the type name context is hit,
        // We need to set the error for the variable definition as well.
        if (context.getParent() instanceof BallerinaParser.VariableDefinitionStatementContext) {
            context.getParent().exception = e;
            return;
        }

        if (context instanceof BallerinaParser.NameReferenceContext) {
            setContextIfConnectorInit(context, e);
        }
    }

    /**
     * Check the context and identify if the particular context is a child of a connector init and set the exception.
     * @param context   current parser rule context
     * @param e         exception to set
     */
    private void setContextIfConnectorInit(ParserRuleContext context, InputMismatchException e) {
        ParserRuleContext connectorInitContext = context.getParent().getParent().getParent();
        if (connectorInitContext instanceof BallerinaParser.ConnectorInitExpressionContext) {
            ParserRuleContext tempContext = context;
            while (true) {
                tempContext.exception = e;
                tempContext = tempContext.getParent();
                if (tempContext.equals(connectorInitContext)) {
                    tempContext.getParent().exception = e;
                    break;
                }
            }
            connectorInitContext.getParent().exception = e;
        }
    }
}