• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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