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 :(before "End Primitive Recipe Declarations")
64 _BROWSE_TRACE,
65 :(before "End Primitive Recipe Numbers")
66 put(Recipe_ordinal, "$browse-trace", _BROWSE_TRACE);
67 :(before "End Primitive Recipe Checks")
68 case _BROWSE_TRACE: {
69 break;
70 }
71 :(before "End Primitive Recipe Implementations")
72 case _BROWSE_TRACE: {
73 start_trace_browser();
74 break;
75 }
76
77
78 :(after "Commandline Parsing")
79 if (argc == 3 && is_equal(argv[1], "browse-trace")) {
80 load_trace(argv[2]);
81 start_trace_browser();
82 return 0;
83 }
84
85 :(before "End Globals")
86 set<int> Visible;
87 int Top_of_screen = 0;
88 int Left_of_screen = 0;
89 int Last_printed_row = 0;
90 map<int, int> Trace_index;
91 string Current_search_pattern = "";
92
93 :(code)
94 void start_trace_browser() {
95 if (!Trace_stream) return;
96 cerr << "computing min depth to display\n";
97 int min_depth = 9999;
98 for (int i = 0; i < SIZE(Trace_stream->past_lines); ++i) {
99 ¦ trace_line& curr_line = Trace_stream->past_lines.at(i);
100 ¦ if (curr_line.depth < min_depth) min_depth = curr_line.depth;
101 }
102 cerr << "min depth is " << min_depth << '\n';
103 cerr << "computing lines to display\n";
104 for (int i = 0; i < SIZE(Trace_stream->past_lines); ++i) {
105 ¦ if (Trace_stream->past_lines.at(i).depth == min_depth)
106 ¦ ¦ Visible.insert(i);
107 }
108 tb_init();
109 Display_row = Display_column = 0;
110 Top_of_screen = 0;
111 refresh_screen_rows();
112 while (true) {
113 ¦ render();
114 ¦ int key = read_key();
115 ¦ if (key == 'q' || key == 'Q' || key == TB_KEY_CTRL_C) break;
116 ¦ if (key == 'j' || key == TB_KEY_ARROW_DOWN) {
117 ¦ ¦
118 ¦ ¦ if (Display_row < Last_printed_row) ++Display_row;
119 ¦ }
120 ¦ else if (key == 'k' || key == TB_KEY_ARROW_UP) {
121 ¦ ¦
122 ¦ ¦ if (Display_row > 0) --Display_row;
123 ¦ }
124 ¦ else if (key == 't') {
125 ¦ ¦
126 ¦ ¦ Display_row = 0;
127 ¦ }
128 ¦ else if (key == 'c') {
129 ¦ ¦
130 ¦ ¦ Display_row = tb_height()/2;
131 ¦ ¦ while (!contains_key(Trace_index, Display_row))
132 ¦ ¦ ¦ --Display_row;
133 ¦ }
134 ¦ else if (key == 'b') {
135 ¦ ¦
136 ¦ ¦ Display_row = tb_height()-1;
137 ¦ ¦ while (!contains_key(Trace_index, Display_row))
138 ¦ ¦ ¦ --Display_row;
139 ¦ }
140 ¦ else if (key == 'T') {
141 ¦ ¦
142 ¦ ¦ Top_of_screen = get(Trace_index, Display_row);
143 ¦ ¦ Display_row = 0;
144 ¦ ¦ refresh_screen_rows();
145 ¦ }
146 ¦ else if (key == 'h' || key == TB_KEY_ARROW_LEFT) {
147 ¦ ¦
148 ¦ ¦ if (Left_of_screen > 0) --Left_of_screen;
149 ¦ }
150 ¦ else if (key == 'l' || key == TB_KEY_ARROW_RIGHT) {
151 ¦ ¦
152 ¦ ¦ ++Left_of_screen;
153 ¦ }
154 ¦ else if (key == 'H') {
155 ¦ ¦
156 ¦ ¦ Left_of_screen -= (tb_width() - 5);
157 ¦ ¦ if (Left_of_screen < 0) Left_of_screen = 0;
158 ¦ }
159 ¦ else if (key == 'L') {
160 ¦ ¦
161 ¦ ¦ Left_of_screen += (tb_width() - 5);
162 ¦ }
163 ¦ else if (key == 'J' || key == TB_KEY_PGDN || key == TB_KEY_CTRL_F) {
164 ¦ ¦
165 ¦ ¦ if (Trace_index.find(tb_height()-1) != Trace_index.end()) {
166 ¦ ¦ ¦ Top_of_screen = get(Trace_index, tb_height()-1) + 1;
167 ¦ ¦ ¦ refresh_screen_rows();
168 ¦ ¦ }
169 ¦ }
170 ¦ else if (key == 'K' || key == TB_KEY_PGUP || key == TB_KEY_CTRL_B) {
171 ¦ ¦
172 ¦ ¦ for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) {
173 ¦ ¦ ¦ --Top_of_screen;
174 ¦ ¦ ¦ if (Top_of_screen <= 0) break;
175 ¦ ¦ ¦ while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen))
176 ¦ ¦ ¦ ¦ --Top_of_screen;
177 ¦ ¦ }
178 ¦ ¦ if (Top_of_screen >= 0)
179 ¦ ¦ ¦ refresh_screen_rows();
180 ¦ }
181 ¦ else if (key == 'g' || key == TB_KEY_HOME) {
182 ¦ ¦ ¦ Top_of_screen = 0;
183 ¦ ¦ ¦ Last_printed_row = 0;
184 ¦ ¦ ¦ Display_row = 0;
185 ¦ ¦ ¦ refresh_screen_rows();
186 ¦ }
187 ¦ else if (key == 'G' || key == TB_KEY_END) {
188 ¦ ¦
189 ¦ ¦ Top_of_screen = SIZE(Trace_stream->past_lines)-1;
190 ¦ ¦ for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) {
191 ¦ ¦ ¦ --Top_of_screen;
192 ¦ ¦ ¦ if (Top_of_screen <= 0) break;
193 ¦ ¦ ¦ while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen))
194 ¦ ¦ ¦ ¦ --Top_of_screen;
195 ¦ ¦ }
196 ¦ ¦ refresh_screen_rows();
197 ¦ ¦
198 ¦ ¦ Display_row = Last_printed_row;
199 ¦ ¦ refresh_screen_rows();
200 ¦ }
201 ¦ else if (key == TB_KEY_CARRIAGE_RETURN) {
202 ¦ ¦
203 ¦ ¦ assert(contains_key(Trace_index, Display_row));
204 ¦ ¦ int start_index = get(Trace_index, Display_row);
205 ¦ ¦ int index = 0;
206 ¦ ¦
207 ¦ ¦ int min_depth = 9999;
208 ¦ ¦ for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) {
209 ¦ ¦ ¦ if (contains_key(Visible, index)) break;
210 ¦ ¦ ¦ trace_line& curr_line = Trace_stream->past_lines.at(index);
211 ¦ ¦ ¦ assert(curr_line.depth > Trace_stream->past_lines.at(start_index).depth);
212 ¦ ¦ ¦ if (curr_line.depth < min_depth) min_depth = curr_line.depth;
213 ¦ ¦ }
214 ¦ ¦ int end_index = index;
215 ¦ ¦
216 ¦ ¦ for (index = start_index; index < end_index; ++index) {
217 ¦ ¦ ¦ trace_line& curr_line = Trace_stream->past_lines.at(index);
218 ¦ ¦ ¦ if (curr_line.depth == min_depth) {
219 ¦ ¦ ¦ ¦ Visible.insert(index);
220 ¦ ¦ ¦ }
221 ¦ ¦ }
222 ¦ ¦ refresh_screen_rows();
223 ¦ }
224 ¦ else if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) {
225 ¦ ¦
226 ¦ ¦ assert(contains_key(Trace_index, Display_row));
227 ¦ ¦ int start_index = get(Trace_index, Display_row);
228 ¦ ¦ int index = 0;
229 ¦ ¦
230 ¦ ¦ int initial_depth = Trace_stream->past_lines.at(start_index).depth;
231 ¦ ¦ for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) {
232 ¦ ¦ ¦ if (!contains_key(Visible, index)) continue;
233 ¦ ¦ ¦ trace_line& curr_line = Trace_stream->past_lines.at(index);
234 ¦ ¦ ¦ if (curr_line.depth <= initial_depth) break;
235 ¦ ¦ }
236 ¦ ¦ int end_index = index;
237 ¦ ¦
238 ¦ ¦ for (index = start_index+1; index < end_index; ++index) {
239 ¦ ¦ ¦ Visible.erase(index);
240 ¦ ¦ }
241 ¦ ¦ refresh_screen_rows();
242 ¦ }
243 ¦ else if (key == '/') {
244 ¦ ¦ if (start_search_editor())
245 ¦ ¦ ¦ search_next(Current_search_pattern);
246 ¦ }
247 ¦ else if (key == 'n') {
248 ¦ ¦ if (!Current_search_pattern.empty())
249 ¦ ¦ ¦ search_next(Current_search_pattern);
250 ¦ }
251 ¦ else if (key == 'N') {
252 ¦ ¦ if (!Current_search_pattern.empty())
253 ¦ ¦ ¦ search_previous(Current_search_pattern);
254 ¦ }
255 }
256 tb_shutdown();
257 }
258
259 bool start_search_editor() {
260 const int bottom_screen_line = tb_height()-1;
261
262 clear_line(bottom_screen_line);
263 tb_set_cursor(0, bottom_screen_line);
264 int col = 0;
265 tb_change_cell(col, bottom_screen_line, '/', TB_WHITE, TB_BLACK);
266 ++col;
267 tb_set_cursor(col, bottom_screen_line);
268 tb_present();
269 string pattern;
270 while (true) {
271 ¦ int key = read_key();
272 ¦ if (key == TB_KEY_ENTER) {
273 ¦ ¦ if (!pattern.empty())
274 ¦ ¦ ¦ Current_search_pattern = pattern;
275 ¦ ¦ return true;
276 ¦ }
277 ¦ else if (key == TB_KEY_ESC || key == TB_KEY_CTRL_C) {
278 ¦ ¦ return false;
279 ¦ }
280 ¦ else if (key == TB_KEY_ARROW_LEFT) {
281 ¦ ¦ if (col > 1) {
282 ¦ ¦ ¦ --col;
283 ¦ ¦ ¦ tb_set_cursor(col, bottom_screen_line);
284 ¦ ¦ ¦ tb_present();
285 ¦ ¦ }
286 ¦ }
287 ¦ else if (key == TB_KEY_ARROW_RIGHT) {
288 ¦ ¦ if (col-1 < SIZE(pattern)) {
289 ¦ ¦ ¦ ++col;
290 ¦ ¦ ¦ tb_set_cursor(col, bottom_screen_line);
291 ¦ ¦ ¦ tb_present();
292 ¦ ¦ }
293 ¦ }
294 ¦ else if (key == TB_KEY_HOME || key == TB_KEY_CTRL_A) {
295 ¦ ¦ col = 1;
296 ¦ ¦ tb_set_cursor(col, bottom_screen_line);
297 ¦ ¦ tb_present();
298 ¦ }
299 ¦ else if (key == TB_KEY_END || key == TB_KEY_CTRL_E) {
300 ¦ ¦ col = SIZE(pattern)+1;
301 ¦ ¦ tb_set_cursor(col, bottom_screen_line);
302 ¦ ¦ tb_present();
303 ¦ }
304 ¦ else if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) {
305 ¦ ¦ if (col > 1) {
306 ¦ ¦ ¦ --col;
307 ¦ ¦ ¦
308 ¦ ¦ ¦ pattern.erase(col-1, 1);
309 ¦ ¦ ¦
310 ¦ ¦ ¦ if (col > SIZE(pattern)) {
311 ¦ ¦ ¦ ¦ tb_change_cell(col, bottom_screen_line, ' ', TB_WHITE, TB_BLACK);
312 ¦ ¦ ¦ }
313 ¦ ¦ ¦ else {
314 ¦ ¦ ¦ ¦ assert(col <= SIZE(pattern));
315 ¦ ¦ ¦ ¦ for (int x = col; x < SIZE(pattern)+1; ++x)
316 ¦ ¦ ¦ ¦ ¦ tb_change_cell(x, bottom_screen_line, pattern.at(x-1), TB_WHITE, TB_BLACK);
317 ¦ ¦ ¦ ¦ tb_change_cell(SIZE(pattern)+1, bottom_screen_line, ' ', TB_WHITE, TB_BLACK);
318 ¦ ¦ ¦ }
319 ¦ ¦ ¦ tb_set_cursor(col, bottom_screen_line);
320 ¦ ¦ ¦ tb_present();
321 ¦ ¦ }
322 ¦ }
323 ¦ else if (key < 128) {
324 ¦ ¦
325 ¦ ¦ char c = static_cast<char>(key);
326 ¦ ¦ pattern.insert(col-1, 1, c);
327 ¦ ¦
328 ¦ ¦ for (int x = col; x < SIZE(pattern)+1; ++x)
329 ¦ ¦ ¦ tb_change_cell(x, bottom_screen_line, pattern.at(x-1), TB_WHITE, TB_BLACK);
330 ¦ ¦ ++col;
331 ¦ ¦ tb_set_cursor(col, bottom_screen_line);
332 ¦ ¦ tb_present();
333 ¦ }
334 }
335 }
336
337 void search_next(const string& pat) {
338 for (int trace_index = get(Trace_index, Display_row)+1; trace_index < SIZE(Trace_stream->past_lines); ++trace_index) {
339 ¦ if (!contains_key(Visible, trace_index)) continue;
340 ¦ const trace_line& line = Trace_stream->past_lines.at(trace_index);
341 ¦ if (line.label.find(pat) == string::npos && line.contents.find(pat) == string::npos) continue;
342 ¦ Top_of_screen = trace_index;
343 ¦ Display_row = 0;
344 ¦ refresh_screen_rows();
345 ¦ return;
346 }
347 }
348
349 void search_previous(const string& pat) {
350 for (int trace_index = get(Trace_index, Display_row)-1; trace_index >= 0; --trace_index) {
351 ¦ if (!contains_key(Visible, trace_index)) continue;
352 ¦ const trace_line& line = Trace_stream->past_lines.at(trace_index);
353 ¦ if (line.label.find(pat) == string::npos && line.contents.find(pat) == string::npos) continue;
354 ¦ Top_of_screen = trace_index;
355 ¦ Display_row = 0;
356 ¦ refresh_screen_rows();
357 ¦ return;
358 }
359 }
360
361 void clear_line(int screen_row) {
362 for (int col = 0; col < tb_width(); ++col)
363 ¦ tb_change_cell(col, screen_row, ' ', TB_WHITE, TB_BLACK);
364 }
365
366
367 void refresh_screen_rows() {
368 int screen_row = 0, index = 0;
369 Trace_index.clear();
370 for (screen_row = 0, index = Top_of_screen; screen_row < tb_height() && index < SIZE(Trace_stream->past_lines); ++screen_row, ++index) {
371 ¦
372 ¦ while (!contains_key(Visible, index)) {
373 ¦ ¦ ++index;
374 ¦ ¦ if (index >= SIZE(Trace_stream->past_lines)) goto done;
375 ¦ }
376 ¦ assert(index < SIZE(Trace_stream->past_lines));
377 ¦ put(Trace_index, screen_row, index);
378 }
379 done:;
380 }
381
382 void render() {
383 int screen_row = 0;
384 for (screen_row = 0; screen_row < tb_height(); ++screen_row) {
385 ¦ if (!contains_key(Trace_index, screen_row)) break;
386 ¦ trace_line& curr_line = Trace_stream->past_lines.at(get(Trace_index, screen_row));
387 ¦ ostringstream out;
388 ¦ out << std::setw(4) << curr_line.depth << ' ' << curr_line.label << ": " << curr_line.contents;
389 ¦ if (screen_row < tb_height()-1) {
390 ¦ ¦ int delta = lines_hidden(screen_row);
391 ¦ ¦
392 ¦ ¦ if (delta > 1) {
393 ¦ ¦ ¦ if (delta > 999) out << static_cast<char>(1);
394 ¦ ¦ ¦ out << " (" << delta << ")";
395 ¦ ¦ ¦ if (delta > 999) out << static_cast<char>(2);
396 ¦ ¦ }
397 ¦ }
398 ¦ render_line(screen_row, out.str(), screen_row == Display_row);
399 }
400
401 Last_printed_row = screen_row-1;
402 for (; screen_row < tb_height(); ++screen_row)
403 ¦ render_line(screen_row, "~", false);
404
405 tb_set_cursor(0, Display_row);
406 tb_present();
407 }
408
409 int lines_hidden(int screen_row) {
410 assert(contains_key(Trace_index, screen_row));
411 if (!contains_key(Trace_index, screen_row+1))
412 ¦ return SIZE(Trace_stream->past_lines) - get(Trace_index, screen_row);
413 else
414 ¦ return get(Trace_index, screen_row+1) - get(Trace_index, screen_row);
415 }
416
417 void render_line(int screen_row, const string& s, bool cursor_line) {
418 int col = 0;
419 int color = TB_WHITE;
420 int background_color = cursor_line ? 240 : TB_BLACK;
421 vector<pair<size_t, size_t> > highlight_ranges = find_all_occurrences(s, Current_search_pattern);
422 for (col = 0; col < tb_width() && col+Left_of_screen < SIZE(s); ++col) {
423 ¦ char c = s.at(col+Left_of_screen);
424 ¦ if (c == '\n') c = ';';
425 ¦
426 ¦ if (c == '\1') { color = 1; c = ' '; }
427 ¦ if (c == '\2') { color = TB_WHITE; c = ' '; }
428 ¦ if (in_range(highlight_ranges, col+Left_of_screen))
429 ¦ ¦ tb_change_cell(col, screen_row, c, TB_BLACK, 11);
430 ¦ else
431 ¦ ¦ tb_change_cell(col, screen_row, c, color, background_color);
432 }
433 for (; col < tb_width(); ++col)
434 ¦ tb_change_cell(col, screen_row, ' ', TB_WHITE, background_color);
435 }
436
437 vector<pair<size_t, size_t> > find_all_occurrences(const string& s, const string& pat) {
438 vector<pair<size_t, size_t> > result;
439 if (pat.empty()) return result;
440 size_t idx = 0;
441 while (true) {
442 ¦ size_t next_idx = s.find(pat, idx);
443 ¦ if (next_idx == string::npos) break;
444 ¦ result.push_back(pair<size_t, size_t>(next_idx, next_idx+SIZE(pat)));
445 ¦ idx = next_idx+SIZE(pat);
446 }
447 return result;
448 }
449
450 bool in_range(const vector<pair<size_t, size_t> >& highlight_ranges, size_t idx) {
451 for (int i = 0; i < SIZE(highlight_ranges); ++i) {
452 ¦ if (idx >= highlight_ranges.at(i).first && idx < highlight_ranges.at(i).second)
453 ¦ ¦ return true;
454 ¦ if (idx < highlight_ranges.at(i).second) break;
455 }
456 return false;
457 }
458
459 void load_trace(const char* filename) {
460 ifstream tin(filename);
461 if (!tin) {
462 ¦ cerr << "no such file: " << filename << '\n';
463 ¦ exit(1);
464 }
465 Trace_stream = new trace_stream;
466 while (has_data(tin)) {
467 ¦ tin >> std::noskipws;
468 ¦ ¦ skip_whitespace_but_not_newline(tin);
469 ¦ ¦ if (!isdigit(tin.peek())) {
470 ¦ ¦ ¦ string dummy;
471 ¦ ¦ ¦ getline(tin, dummy);
472 ¦ ¦ ¦ continue;
473 ¦ ¦ }
474 ¦ tin >> std::skipws;
475 ¦ int depth;
476 ¦ tin >> depth;
477 ¦ string label;
478 ¦ tin >> label;
479 ¦ if (*--label.end() == ':') label.erase(--label.end());
480 ¦ string line;
481 ¦ getline(tin, line);
482 ¦ Trace_stream->past_lines.push_back(trace_line(depth, label, line));
483 }
484 cerr << "lines read: " << Trace_stream->past_lines.size() << '\n';
485 }
486
487 int read_key() {
488 tb_event event;
489 do {
490 ¦ tb_poll_event(&event);
491 } while (event.type != TB_EVENT_KEY);
492 return event.key ? event.key : event.ch;
493 }