1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | package Torello.Java; import java.util.stream.IntStream; class SetCodeIndentTabsPolicy { // Used by 'setCodeIndent_WithTabsPolicyRelative' // This needs to be a long-array, because there might be lines with lots of initial indentation. private static final char[] SPACES = new char[200]; static { java.util.Arrays.fill(SPACES, ' '); } static String run (String codeAsStr, int requestedIndent, int spacesPerTab) { char[] code = codeAsStr.toCharArray(); IntStream.Builder b = IntStream.builder(); // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // First find all of the line-breaks / new-lines. // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // // NOTE: If the first character is not a new-line, then the first line is presumed to begin // at String-index '-1' // // Afterwards, convert the Stream to 'nlPos' array. Build two other arrays if (code[0] != '\n') b.accept(-1); for (int i=0; i < code.length; i++) if (code[i] == '\n') b.accept(i); int[] nlPos = b.build().toArray(); int[] wsLen = new int[nlPos.length]; int[] fcPos = new int[nlPos.length]; int maxIndent = 0; int minIndent = Integer.MAX_VALUE; // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // Compute how much white-space is currently at the start of each line // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // // NOTE: Since this method calculates what the user is looking at in his code-editor, the // tabs-policy needs to be included in the calculation. // // Once the amount of white-space at the start of each line is computed, it will be easy // to shift the entire source-code left or right. Note that in the JavaDoc Upgrader Tool, // this is always shifted until the **LEAST INDENTED** line is indented by 1... // // REMEMBER: Shifting everything left must be a shift of an **EQUAL NUMBER OF SPACES** for // each line that is shifted. for (int i=0; i < wsLen.length; i++) { int END = (i == (nlPos.length - 1)) ? code.length : nlPos[i+1]; boolean hasCode = false; INNER: for (int j = (nlPos[i] + 1); j < END; j++) if (! Character.isWhitespace(code[j])) { fcPos[i] = j; hasCode = true; break INNER; } if (! hasCode) fcPos[i] = wsLen[i] = -1; else { // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // wsLen[i] = computeEffectiveLeadingWhiteSpace(code, nlPos[i] + 1, spacesPerTab); // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // // NOTE: The contents of everything within this 'else' branch is nothing more than // an inline block-copy of the method named in the comment above. Hopefully // inlining the method will speed this up a little bit. // // The values that would be passed before the method was inlined, here, are also // noted in the above comment. // // ALSO: The 'i' loop-variable was changed to a 'j' (to avoid conflicting with the // outer-loop 'i'). The "ret" was changed to "whiteSpaceChars" int whiteSpaceChars = 0; int relativeCount = 0; wsLen[i] = -1; EFFECTIVE_LEADING_WS: for (int j = (nlPos[i] + 1) /* lineFirstCharacterPos */; j < code.length; j++) if (! Character.isWhitespace(code[j])) { wsLen[j] = whiteSpaceChars; // return ret; break EFFECTIVE_LEADING_WS; } else switch (code[j]) { case ' ' : whiteSpaceChars++; relativeCount = (relativeCount + 1) % spacesPerTab; break; case '\t' : whiteSpaceChars += (spacesPerTab - relativeCount); relativeCount = 0; break; case '\r' : case '\f' : break; case '\n' : break EFFECTIVE_LEADING_WS; // return -1; default: throw new UnreachableError(); } // return -1; <== Not needed, the array-location is initialized to -1 } if (wsLen[i] == -1) continue; if (wsLen[i] > maxIndent) maxIndent = wsLen[i]; if (wsLen[i] < minIndent) minIndent = wsLen[i]; } // This is the amount of space to shift each line. int delta = requestedIndent - minIndent; // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // NOW: Rebuild the source-code string, making sure to shift each line. // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** StringBuilder sb = new StringBuilder(); for (int i=0; i < wsLen.length; i++) // The array "White-Space-Length" will have a '-1' if the entire line is nothing but // white-space. In such cases, simply append a '\n' - there is no reason to add extra // spaces. The code hilited just ignores it. if (wsLen[i] == -1) sb.append('\n'); // Otherwise append the leading white-space, and then the line-of-code. else { // First append the white-space at the beginning of the line. int numSpaces = wsLen[i] + delta; if (numSpaces > SPACES.length) throw new InternalError ("A Line of Code has more than 200 characters of leading white space"); sb.append(SPACES, 0, numSpaces); // Now append the line of code. Since there may be tabs after the first // non-white-space character, this is a little complicated... // // NOTE: This could be inlined, but this method just does too much... // // The char[]-Array 'code' has the code. The text of the source-code begins at // array-index 'First-Character-Position' (fcPos). This method needs the parameter // 'numSpaces' to make sure the tabs stay properly-relativised... sb.append(LOCAsStr.run(code, numSpaces, fcPos[i], spacesPerTab)); } // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // FINISHED: Return the Source-Code String // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** return sb.toString(); } } |