1 module tooling.MergedRange; 2 3 import std.algorithm, std.array, std.range, std.file, std.stdio, std.exception, std.conv, std.typecons; 4 5 import tooling.Scanner; 6 import tooling.TreeRange; 7 import tooling.Tokenizer; 8 import tooling.TokenRange; 9 10 auto mergedRange(Token[][] tokensList) 11 { 12 struct Result 13 { 14 this(Token[][] tokensList) 15 { 16 tokenRanges_ = tokensList.map!(f => f.namespaceTokenRange).array; 17 rangeState_.length = tokenRanges_.length; 18 for (auto i=0; i<rangeState_.length; ++i) { 19 nextState(i); 20 } 21 pos_ = 0; 22 prependNewline_ = false; 23 } 24 25 bool empty() { return tokenRanges_.map!(r => r.empty).reduce!((e, v) => v && e); } 26 Token front() 27 { 28 if (prependNewline_) 29 { 30 auto t = tokenRanges_[pos_].front; 31 t.token_.precedingWhitespace_ = "\n" ~ t.token_.precedingWhitespace_; 32 return t.token_; 33 } 34 else 35 { 36 return tokenRanges_[pos_].front.token_; 37 } 38 } 39 40 Result save() { return this; } 41 42 private void nextPosition() 43 { 44 // If this token range (to this entity) is empty, switch to the next 45 // token range on the same namespace level. 46 if (!empty) 47 { 48 for (; tokenRanges_[pos_].empty; pos_ = (pos_+1) % tokenRanges_.length) 49 { 50 } 51 } 52 } 53 54 private void nextState(ulong pos) 55 { 56 rangeState_[pos] = RangeState(); 57 if (!tokenRanges_[pos].empty) 58 { 59 auto t = tokenRanges_[pos].front; 60 if (t.entity_) 61 { 62 if (t.entity_.type_ == "namespace") 63 { 64 if (t.token_.value == "namespace") 65 { 66 rangeState_[pos] = RangeState(t.entity_.name, "enter"); 67 } 68 else if (t.token_.value == "}") 69 { 70 rangeState_[pos] = RangeState(t.entity_.name, "exit"); 71 } 72 } 73 } 74 } 75 } 76 77 void popFront() 78 { 79 prependNewline_ = false; 80 if (!tokenRanges_[pos_].empty) 81 { 82 tokenRanges_[pos_].popFront; 83 } 84 nextPosition(); 85 if (!tokenRanges_[pos_].empty) 86 { 87 nextState(pos_); 88 if (!rangeState_[pos_].name.empty) 89 { 90 // Cycle over the states of all inputs. We want to find the next 91 // normal input and slice it in. 92 // Make sure to always use the input in given order. 93 auto states = iota(rangeState_.length) 94 .zip(rangeState_) 95 // .cycle(pos_+1) 96 // .take(rangeState_.length) 97 .array; 98 //std.stdio.writeln(states); 99 auto p = states.find!(t => t[1].name.empty); 100 if (!p.empty) 101 { 102 pos_ = p.front[0]; 103 prependNewline_ = true; 104 nextPosition(); 105 } 106 else 107 { 108 // no normal token waiting any longer 109 // if there is some namespace to enter, do so 110 p = states.find!(t => t[1].action == "enter"); 111 if (!p.empty) 112 { 113 pos_ = p.front[0]; 114 auto name = p.front[1].name; 115 namespaceStack_ ~= name; 116 117 // enhance all other inputs that are waiting at the same namespace 118 foreach (e; states) 119 { 120 if (e[0] != pos_ && e[1].name == name && e[1].action == "enter" && !tokenRanges_[e[0]].empty) 121 { 122 tokenRanges_[e[0]].popFront; 123 // consume the start of the namespace tokens, so we output 124 // them only once 125 while (!tokenRanges_[e[0]].empty) 126 { 127 auto value = tokenRanges_[e[0]].front.token_.value; 128 auto done = value == "{" || value == ";"; 129 tokenRanges_[e[0]].popFront; 130 if (done) 131 break; 132 } 133 nextState(e[0]); 134 } 135 } 136 } 137 else 138 { 139 // no namespace to enter any more, we need to get out of the namespace 140 auto name = namespaceStack_[$-1]; 141 namespaceStack_.popBack; 142 p = states.find!(t => t[1].name == name && t[1].action == "exit"); 143 if (p.empty) 144 { 145 throw new Exception("Missing closing token for namespace '" ~ name ~ "'"); 146 } 147 pos_ = p.front[0]; 148 149 // consume the namespace closings that are not needed 150 foreach (e; states) 151 { 152 if (e[0] != pos_ && e[1].name == name && e[1].action == "exit" && !tokenRanges_[e[0]].empty) 153 { 154 tokenRanges_[e[0]].popFront; 155 nextState(e[0]); 156 } 157 } 158 } 159 } 160 } 161 } 162 } 163 164 TokenRangeResult[] tokenRanges_; 165 alias RangeState = Tuple!(string, "name", string, "action"); 166 RangeState[] rangeState_; 167 string[] namespaceStack_; 168 ulong pos_; 169 bool prependNewline_; 170 } 171 172 return Result(tokensList); 173 } 174 175 auto mergedRange(string[] filenames) 176 { 177 auto tokensList = filenames.map!(f => f.readTokens).array; 178 return mergedRange(tokensList); 179 }