blob: 2cd3de01c324d5ea37afe7aa23ccc6de84f4907d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.netbeans.modules.javascript2.editor.formatter;
import java.util.LinkedList;
import java.util.List;
import org.antlr.v4.runtime.ParserRuleContext;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.modules.javascript2.json.parser.JsonBaseVisitor;
import org.netbeans.modules.javascript2.json.parser.JsonParser;
import org.netbeans.modules.javascript2.lexer.api.JsTokenId;
import static org.netbeans.modules.javascript2.editor.formatter.TokenUtils.*;
/**
*
* @author Dusan Balek
*/
public class JsonFormatVisitor extends JsonBaseVisitor<Void> {
private final TokenUtils tokenUtils;
public JsonFormatVisitor(FormatTokenStream tokenStream, TokenSequence<? extends JsTokenId> ts, int formatFinish) {
this.tokenUtils = new TokenUtils(ts, tokenStream, formatFinish);
for (FormatToken token : tokenStream) {
if (token.getKind() == FormatToken.Kind.WHITESPACE) {
LinkedList<FormatToken> newTokens = new LinkedList<FormatToken>() {
@Override
public boolean add(FormatToken token) {
FormatToken last = isEmpty() ? null : getLast();
if (last != null) {
last.setNext(token);
token.setPrevious(last);
}
return super.add(token);
}
};
int start = 0;
for (int i = 0; i < token.getText().length(); i++) {
if (token.getText().charAt(i) == '\n') {
if (start > 0) {
newTokens.add(FormatToken.forAny(FormatToken.Kind.WHITESPACE, token.getOffset() + start, token.getText().subSequence(start, i), JsTokenId.WHITESPACE));
}
newTokens.add(FormatToken.forAny(FormatToken.Kind.EOL, token.getOffset() + i, token.getText().subSequence(start, i + 1), JsTokenId.EOL));
start = i + 1;
}
}
if (start > 0) {
newTokens.add(FormatToken.forAny(FormatToken.Kind.WHITESPACE, token.getOffset() + start, token.getText().subSequence(start, token.getText().length()), JsTokenId.WHITESPACE));
}
if (!newTokens.isEmpty()) {
FormatToken previous = token.previous();
if (previous != null) {
previous.setNext(newTokens.getFirst());
newTokens.getFirst().setPrevious(previous);
}
FormatToken next = token.next();
if (next != null) {
next.setPrevious(newTokens.getLast());
newTokens.getLast().setNext(next);
}
}
}
}
}
@Override
public Void visitObject(JsonParser.ObjectContext ctx) {
// indentation mark
FormatToken formatToken = tokenUtils.getPreviousToken(getStart(ctx), JsTokenId.BRACKET_LEFT_CURLY, true);
if (formatToken != null) {
appendTokenAfterLastVirtual(formatToken, FormatToken.forFormat(FormatToken.Kind.INDENTATION_INC));
appendTokenAfterLastVirtual(formatToken, FormatToken.forFormat(FormatToken.Kind.AFTER_LEFT_BRACE));
appendTokenAfterLastVirtual(formatToken, FormatToken.forFormat(FormatToken.Kind.AFTER_OBJECT_START));
FormatToken previous = formatToken.previous();
if (previous != null) {
appendToken(previous, FormatToken.forFormat(FormatToken.Kind.BEFORE_OBJECT));
}
}
int objectFinish = getFinish(ctx);
ctx.pair().stream().forEach((pair) -> {
// mark property end
FormatToken nextToken = tokenUtils.getNextToken(getFinish(pair), JsTokenId.OPERATOR_COMMA, objectFinish);
if (nextToken != null) {
appendTokenAfterLastVirtual(nextToken, FormatToken.forFormat(FormatToken.Kind.AFTER_PROPERTY), true);
}
});
// put indentation mark after non white token preceeding curly bracket
formatToken = tokenUtils.getPreviousNonWhiteToken(getFinish(ctx),
getStart(ctx), JsTokenId.BRACKET_RIGHT_CURLY, true);
if (formatToken != null) {
appendTokenAfterLastVirtual(formatToken, FormatToken.forFormat(FormatToken.Kind.BEFORE_OBJECT_END));
appendTokenAfterLastVirtual(formatToken, FormatToken.forFormat(FormatToken.Kind.BEFORE_RIGHT_BRACE));
appendTokenAfterLastVirtual(formatToken, FormatToken.forFormat(FormatToken.Kind.INDENTATION_DEC));
}
return super.visitObject(ctx);
}
@Override
public Void visitPair(JsonParser.PairContext ctx) {
FormatToken colon = tokenUtils.getNextToken(getFinish(ctx.key()),
JsTokenId.OPERATOR_COLON, getFinish(ctx));
if (colon != null) {
appendToken(colon, FormatToken.forFormat(FormatToken.Kind.AFTER_PROPERTY_OPERATOR));
FormatToken before = colon.previous();
if (before != null) {
appendTokenAfterLastVirtual(before, FormatToken.forFormat(FormatToken.Kind.BEFORE_PROPERTY_OPERATOR));
}
}
return super.visitPair(ctx);
}
@Override
public Void visitArray(JsonParser.ArrayContext ctx) {
int start = getStart(ctx);
int finish = getFinish(ctx);
FormatToken leftBracket = tokenUtils.getNextToken(start, JsTokenId.BRACKET_LEFT_BRACKET, finish);
if (leftBracket != null) {
if (leftBracket.previous() != null) {
// mark beginning of the array (see issue #250150)
appendToken(leftBracket.previous(), FormatToken.forFormat(FormatToken.Kind.BEFORE_ARRAY));
}
appendToken(leftBracket, FormatToken.forFormat(FormatToken.Kind.AFTER_ARRAY_LITERAL_START));
appendToken(leftBracket, FormatToken.forFormat(FormatToken.Kind.AFTER_ARRAY_LITERAL_BRACKET));
appendToken(leftBracket, FormatToken.forFormat(FormatToken.Kind.INDENTATION_INC));
FormatToken rightBracket = tokenUtils.getPreviousToken(finish, JsTokenId.BRACKET_RIGHT_BRACKET, start + 1);
if (rightBracket != null) {
FormatToken previous = rightBracket.previous();
if (previous != null) {
appendToken(previous, FormatToken.forFormat(FormatToken.Kind.BEFORE_ARRAY_LITERAL_END));
appendToken(previous, FormatToken.forFormat(FormatToken.Kind.BEFORE_ARRAY_LITERAL_BRACKET));
appendToken(previous, FormatToken.forFormat(FormatToken.Kind.INDENTATION_DEC));
}
}
}
List<JsonParser.ValueContext> items = ctx.value();
if (items != null && !items.isEmpty()) {
int prevItemFinish = start;
for (int i = 1; i < items.size(); i++) {
JsonParser.ValueContext prevItem = items.get(i - 1);
if (prevItem != null) {
prevItemFinish = getFinish(prevItem);
}
FormatToken comma = tokenUtils.getNextToken(prevItemFinish, JsTokenId.OPERATOR_COMMA, finish);
if (comma != null) {
prevItemFinish = comma.getOffset();
appendTokenAfterLastVirtual(comma,
FormatToken.forFormat(FormatToken.Kind.AFTER_ARRAY_LITERAL_ITEM));
}
}
}
return super.visitArray(ctx);
}
private int getStart(ParserRuleContext ctx) {
return ctx.getStart().getStartIndex();
}
private int getFinish(ParserRuleContext ctx) {
return ctx.getStop().getStopIndex();
}
}