| /* |
| * 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(); |
| } |
| } |