1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.googlejavaformat; 16 17 import com.google.common.base.MoreObjects; 18 import java.util.ArrayDeque; 19 import java.util.List; 20 21 /** A {@code DocBuilder} converts a sequence of {@link Op}s into a {@link Doc}. */ 22 public final class DocBuilder { 23 private final Doc.Level base = Doc.Level.make(Indent.Const.ZERO); 24 private final ArrayDeque<Doc.Level> stack = new ArrayDeque<>(); 25 26 /** 27 * A possibly earlier {@link Doc.Level} for appending text, à la Philip Wadler. 28 * 29 * <p>Processing {@link Doc}s presents a subtle problem. Suppose we have a {@link Doc} for to an 30 * assignment node, {@code a = b}, with an optional {@link Doc.Break} following the {@code =}. 31 * Suppose we have 5 characters to write it, so that we think we don't need the break. 32 * Unfortunately, this {@link Doc} lies in an expression statement {@link Doc} for the statement 33 * {@code a = b;} and this statement does not fit in 3 characters. This is why many formatters 34 * sometimes emit lines that are too long, or cheat by using a narrower line length to avoid such 35 * problems. 36 * 37 * <p>One solution to this problem is not to decide whether a {@link Doc.Level} should be broken 38 * until later (in this case, after the semicolon has been seen). A simpler approach is to rewrite 39 * the {@link Doc} as here, so that the semicolon moves inside the inner {@link Doc}, and we can 40 * decide whether to break that {@link Doc} without seeing later text. 41 */ 42 private Doc.Level appendLevel = base; 43 44 /** Start to build a {@code DocBuilder}. */ DocBuilder()45 public DocBuilder() { 46 stack.addLast(base); 47 } 48 49 /** 50 * Add a list of {@link Op}s to the {@link OpsBuilder}. 51 * 52 * @param ops the {@link Op}s 53 * @return the {@link OpsBuilder} 54 */ withOps(List<Op> ops)55 public DocBuilder withOps(List<Op> ops) { 56 for (Op op : ops) { 57 op.add(this); // These operations call the operations below to build the doc. 58 } 59 return this; 60 } 61 62 /** 63 * Open a new {@link Doc.Level}. 64 * 65 * @param plusIndent the extra indent for the {@link Doc.Level} 66 */ open(Indent plusIndent)67 void open(Indent plusIndent) { 68 Doc.Level level = Doc.Level.make(plusIndent); 69 stack.addLast(level); 70 } 71 72 /** Close the current {@link Doc.Level}. */ close()73 void close() { 74 Doc.Level top = stack.removeLast(); 75 stack.peekLast().add(top); 76 } 77 78 /** 79 * Add a {@link Doc} to the current {@link Doc.Level}. 80 * 81 * @param doc the {@link Doc} 82 */ add(Doc doc)83 void add(Doc doc) { 84 appendLevel.add(doc); 85 } 86 87 /** 88 * Add a {@link Doc.Break} to the current {@link Doc.Level}. 89 * 90 * @param breakDoc the {@link Doc.Break} 91 */ breakDoc(Doc.Break breakDoc)92 void breakDoc(Doc.Break breakDoc) { 93 appendLevel = stack.peekLast(); 94 appendLevel.add(breakDoc); 95 } 96 97 /** 98 * Return the {@link Doc}. 99 * 100 * @return the {@link Doc} 101 */ build()102 public Doc build() { 103 return base; 104 } 105 106 @Override toString()107 public String toString() { 108 return MoreObjects.toStringHelper(this) 109 .add("base", base) 110 .add("stack", stack) 111 .add("appendLevel", appendLevel) 112 .toString(); 113 } 114 } 115